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 },
105 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
106 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
107 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
108 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
109};
110use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
111use linked_editing_ranges::refresh_linked_ranges;
112use mouse_context_menu::MouseContextMenu;
113use persistence::DB;
114pub use proposed_changes_editor::{
115 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
116};
117use smallvec::smallvec;
118use std::iter::Peekable;
119use task::{ResolvedTask, TaskTemplate, TaskVariables};
120
121use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
122pub use lsp::CompletionContext;
123use lsp::{
124 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
125 InsertTextFormat, LanguageServerId, LanguageServerName,
126};
127
128use language::BufferSnapshot;
129use movement::TextLayoutDetails;
130pub use multi_buffer::{
131 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
132 ToOffset, ToPoint,
133};
134use multi_buffer::{
135 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
136 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
137};
138use project::{
139 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
140 project_settings::{GitGutterSetting, ProjectSettings},
141 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
142 PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
143};
144use rand::prelude::*;
145use rpc::{proto::*, ErrorExt};
146use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
147use selections_collection::{
148 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
149};
150use serde::{Deserialize, Serialize};
151use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
152use smallvec::SmallVec;
153use snippet::Snippet;
154use std::{
155 any::TypeId,
156 borrow::Cow,
157 cell::RefCell,
158 cmp::{self, Ordering, Reverse},
159 mem,
160 num::NonZeroU32,
161 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
162 path::{Path, PathBuf},
163 rc::Rc,
164 sync::Arc,
165 time::{Duration, Instant},
166};
167pub use sum_tree::Bias;
168use sum_tree::TreeMap;
169use text::{BufferId, OffsetUtf16, Rope};
170use theme::{
171 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
172 ThemeColors, ThemeSettings,
173};
174use ui::{
175 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
176 Tooltip,
177};
178use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
179use workspace::{
180 item::{ItemHandle, PreviewTabsSettings},
181 ItemId, RestoreOnStartupBehavior,
182};
183use workspace::{
184 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
185 WorkspaceSettings,
186};
187use workspace::{
188 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
189};
190use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
191
192use crate::hover_links::{find_url, find_url_from_range};
193use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
194
195pub const FILE_HEADER_HEIGHT: u32 = 2;
196pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
197pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
198pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
199const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
200const MAX_LINE_LEN: usize = 1024;
201const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
202const MAX_SELECTION_HISTORY_LEN: usize = 1024;
203pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
204#[doc(hidden)]
205pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
206
207pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
208pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
209pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
210
211pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
212pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
213
214const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
215 alt: true,
216 shift: true,
217 control: false,
218 platform: false,
219 function: false,
220};
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
223pub enum InlayId {
224 InlineCompletion(usize),
225 Hint(usize),
226}
227
228impl InlayId {
229 fn id(&self) -> usize {
230 match self {
231 Self::InlineCompletion(id) => *id,
232 Self::Hint(id) => *id,
233 }
234 }
235}
236
237enum DocumentHighlightRead {}
238enum DocumentHighlightWrite {}
239enum InputComposition {}
240enum SelectedTextHighlight {}
241
242#[derive(Debug, Copy, Clone, PartialEq, Eq)]
243pub enum Navigated {
244 Yes,
245 No,
246}
247
248impl Navigated {
249 pub fn from_bool(yes: bool) -> Navigated {
250 if yes {
251 Navigated::Yes
252 } else {
253 Navigated::No
254 }
255 }
256}
257
258#[derive(Debug, Clone, PartialEq, Eq)]
259enum DisplayDiffHunk {
260 Folded {
261 display_row: DisplayRow,
262 },
263 Unfolded {
264 is_created_file: bool,
265 diff_base_byte_range: Range<usize>,
266 display_row_range: Range<DisplayRow>,
267 multi_buffer_range: Range<Anchor>,
268 status: DiffHunkStatus,
269 },
270}
271
272pub fn init_settings(cx: &mut App) {
273 EditorSettings::register(cx);
274}
275
276pub fn init(cx: &mut App) {
277 init_settings(cx);
278
279 workspace::register_project_item::<Editor>(cx);
280 workspace::FollowableViewRegistry::register::<Editor>(cx);
281 workspace::register_serializable_item::<Editor>(cx);
282
283 cx.observe_new(
284 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
285 workspace.register_action(Editor::new_file);
286 workspace.register_action(Editor::new_file_vertical);
287 workspace.register_action(Editor::new_file_horizontal);
288 workspace.register_action(Editor::cancel_language_server_work);
289 },
290 )
291 .detach();
292
293 cx.on_action(move |_: &workspace::NewFile, cx| {
294 let app_state = workspace::AppState::global(cx);
295 if let Some(app_state) = app_state.upgrade() {
296 workspace::open_new(
297 Default::default(),
298 app_state,
299 cx,
300 |workspace, window, cx| {
301 Editor::new_file(workspace, &Default::default(), window, cx)
302 },
303 )
304 .detach();
305 }
306 });
307 cx.on_action(move |_: &workspace::NewWindow, cx| {
308 let app_state = workspace::AppState::global(cx);
309 if let Some(app_state) = app_state.upgrade() {
310 workspace::open_new(
311 Default::default(),
312 app_state,
313 cx,
314 |workspace, window, cx| {
315 cx.activate(true);
316 Editor::new_file(workspace, &Default::default(), window, cx)
317 },
318 )
319 .detach();
320 }
321 });
322}
323
324pub struct SearchWithinRange;
325
326trait InvalidationRegion {
327 fn ranges(&self) -> &[Range<Anchor>];
328}
329
330#[derive(Clone, Debug, PartialEq)]
331pub enum SelectPhase {
332 Begin {
333 position: DisplayPoint,
334 add: bool,
335 click_count: usize,
336 },
337 BeginColumnar {
338 position: DisplayPoint,
339 reset: bool,
340 goal_column: u32,
341 },
342 Extend {
343 position: DisplayPoint,
344 click_count: usize,
345 },
346 Update {
347 position: DisplayPoint,
348 goal_column: u32,
349 scroll_delta: gpui::Point<f32>,
350 },
351 End,
352}
353
354#[derive(Clone, Debug)]
355pub enum SelectMode {
356 Character,
357 Word(Range<Anchor>),
358 Line(Range<Anchor>),
359 All,
360}
361
362#[derive(Copy, Clone, PartialEq, Eq, Debug)]
363pub enum EditorMode {
364 SingleLine { auto_width: bool },
365 AutoHeight { max_lines: usize },
366 Full,
367}
368
369#[derive(Copy, Clone, Debug)]
370pub enum SoftWrap {
371 /// Prefer not to wrap at all.
372 ///
373 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
374 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
375 GitDiff,
376 /// Prefer a single line generally, unless an overly long line is encountered.
377 None,
378 /// Soft wrap lines that exceed the editor width.
379 EditorWidth,
380 /// Soft wrap lines at the preferred line length.
381 Column(u32),
382 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
383 Bounded(u32),
384}
385
386#[derive(Clone)]
387pub struct EditorStyle {
388 pub background: Hsla,
389 pub local_player: PlayerColor,
390 pub text: TextStyle,
391 pub scrollbar_width: Pixels,
392 pub syntax: Arc<SyntaxTheme>,
393 pub status: StatusColors,
394 pub inlay_hints_style: HighlightStyle,
395 pub inline_completion_styles: InlineCompletionStyles,
396 pub unnecessary_code_fade: f32,
397}
398
399impl Default for EditorStyle {
400 fn default() -> Self {
401 Self {
402 background: Hsla::default(),
403 local_player: PlayerColor::default(),
404 text: TextStyle::default(),
405 scrollbar_width: Pixels::default(),
406 syntax: Default::default(),
407 // HACK: Status colors don't have a real default.
408 // We should look into removing the status colors from the editor
409 // style and retrieve them directly from the theme.
410 status: StatusColors::dark(),
411 inlay_hints_style: HighlightStyle::default(),
412 inline_completion_styles: InlineCompletionStyles {
413 insertion: HighlightStyle::default(),
414 whitespace: HighlightStyle::default(),
415 },
416 unnecessary_code_fade: Default::default(),
417 }
418 }
419}
420
421pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
422 let show_background = language_settings::language_settings(None, None, cx)
423 .inlay_hints
424 .show_background;
425
426 HighlightStyle {
427 color: Some(cx.theme().status().hint),
428 background_color: show_background.then(|| cx.theme().status().hint_background),
429 ..HighlightStyle::default()
430 }
431}
432
433pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
434 InlineCompletionStyles {
435 insertion: HighlightStyle {
436 color: Some(cx.theme().status().predictive),
437 ..HighlightStyle::default()
438 },
439 whitespace: HighlightStyle {
440 background_color: Some(cx.theme().status().created_background),
441 ..HighlightStyle::default()
442 },
443 }
444}
445
446type CompletionId = usize;
447
448pub(crate) enum EditDisplayMode {
449 TabAccept,
450 DiffPopover,
451 Inline,
452}
453
454enum InlineCompletion {
455 Edit {
456 edits: Vec<(Range<Anchor>, String)>,
457 edit_preview: Option<EditPreview>,
458 display_mode: EditDisplayMode,
459 snapshot: BufferSnapshot,
460 },
461 Move {
462 target: Anchor,
463 snapshot: BufferSnapshot,
464 },
465}
466
467struct InlineCompletionState {
468 inlay_ids: Vec<InlayId>,
469 completion: InlineCompletion,
470 completion_id: Option<SharedString>,
471 invalidation_range: Range<Anchor>,
472}
473
474enum EditPredictionSettings {
475 Disabled,
476 Enabled {
477 show_in_menu: bool,
478 preview_requires_modifier: bool,
479 },
480}
481
482enum InlineCompletionHighlight {}
483
484#[derive(Debug, Clone)]
485struct InlineDiagnostic {
486 message: SharedString,
487 group_id: usize,
488 is_primary: bool,
489 start: Point,
490 severity: DiagnosticSeverity,
491}
492
493pub enum MenuInlineCompletionsPolicy {
494 Never,
495 ByProvider,
496}
497
498pub enum EditPredictionPreview {
499 /// Modifier is not pressed
500 Inactive { released_too_fast: bool },
501 /// Modifier pressed
502 Active {
503 since: Instant,
504 previous_scroll_position: Option<ScrollAnchor>,
505 },
506}
507
508impl EditPredictionPreview {
509 pub fn released_too_fast(&self) -> bool {
510 match self {
511 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
512 EditPredictionPreview::Active { .. } => false,
513 }
514 }
515
516 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
517 if let EditPredictionPreview::Active {
518 previous_scroll_position,
519 ..
520 } = self
521 {
522 *previous_scroll_position = scroll_position;
523 }
524 }
525}
526
527#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
528struct EditorActionId(usize);
529
530impl EditorActionId {
531 pub fn post_inc(&mut self) -> Self {
532 let answer = self.0;
533
534 *self = Self(answer + 1);
535
536 Self(answer)
537 }
538}
539
540// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
541// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
542
543type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
544type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
545
546#[derive(Default)]
547struct ScrollbarMarkerState {
548 scrollbar_size: Size<Pixels>,
549 dirty: bool,
550 markers: Arc<[PaintQuad]>,
551 pending_refresh: Option<Task<Result<()>>>,
552}
553
554impl ScrollbarMarkerState {
555 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
556 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
557 }
558}
559
560#[derive(Clone, Debug)]
561struct RunnableTasks {
562 templates: Vec<(TaskSourceKind, TaskTemplate)>,
563 offset: multi_buffer::Anchor,
564 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
565 column: u32,
566 // Values of all named captures, including those starting with '_'
567 extra_variables: HashMap<String, String>,
568 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
569 context_range: Range<BufferOffset>,
570}
571
572impl RunnableTasks {
573 fn resolve<'a>(
574 &'a self,
575 cx: &'a task::TaskContext,
576 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
577 self.templates.iter().filter_map(|(kind, template)| {
578 template
579 .resolve_task(&kind.to_id_base(), cx)
580 .map(|task| (kind.clone(), task))
581 })
582 }
583}
584
585#[derive(Clone)]
586struct ResolvedTasks {
587 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
588 position: Anchor,
589}
590#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
591struct BufferOffset(usize);
592
593// Addons allow storing per-editor state in other crates (e.g. Vim)
594pub trait Addon: 'static {
595 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
596
597 fn render_buffer_header_controls(
598 &self,
599 _: &ExcerptInfo,
600 _: &Window,
601 _: &App,
602 ) -> Option<AnyElement> {
603 None
604 }
605
606 fn to_any(&self) -> &dyn std::any::Any;
607}
608
609#[derive(Debug, Copy, Clone, PartialEq, Eq)]
610pub enum IsVimMode {
611 Yes,
612 No,
613}
614
615/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
616///
617/// See the [module level documentation](self) for more information.
618pub struct Editor {
619 focus_handle: FocusHandle,
620 last_focused_descendant: Option<WeakFocusHandle>,
621 /// The text buffer being edited
622 buffer: Entity<MultiBuffer>,
623 /// Map of how text in the buffer should be displayed.
624 /// Handles soft wraps, folds, fake inlay text insertions, etc.
625 pub display_map: Entity<DisplayMap>,
626 pub selections: SelectionsCollection,
627 pub scroll_manager: ScrollManager,
628 /// When inline assist editors are linked, they all render cursors because
629 /// typing enters text into each of them, even the ones that aren't focused.
630 pub(crate) show_cursor_when_unfocused: bool,
631 columnar_selection_tail: Option<Anchor>,
632 add_selections_state: Option<AddSelectionsState>,
633 select_next_state: Option<SelectNextState>,
634 select_prev_state: Option<SelectNextState>,
635 selection_history: SelectionHistory,
636 autoclose_regions: Vec<AutocloseRegion>,
637 snippet_stack: InvalidationStack<SnippetState>,
638 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
639 ime_transaction: Option<TransactionId>,
640 active_diagnostics: Option<ActiveDiagnosticGroup>,
641 show_inline_diagnostics: bool,
642 inline_diagnostics_update: Task<()>,
643 inline_diagnostics_enabled: bool,
644 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
645 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
646
647 // TODO: make this a access method
648 pub project: Option<Entity<Project>>,
649 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
650 completion_provider: Option<Box<dyn CompletionProvider>>,
651 collaboration_hub: Option<Box<dyn CollaborationHub>>,
652 blink_manager: Entity<BlinkManager>,
653 show_cursor_names: bool,
654 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
655 pub show_local_selections: bool,
656 mode: EditorMode,
657 show_breadcrumbs: bool,
658 show_gutter: bool,
659 show_scrollbars: bool,
660 show_line_numbers: Option<bool>,
661 use_relative_line_numbers: Option<bool>,
662 show_git_diff_gutter: Option<bool>,
663 show_code_actions: Option<bool>,
664 show_runnables: Option<bool>,
665 show_wrap_guides: Option<bool>,
666 show_indent_guides: Option<bool>,
667 placeholder_text: Option<Arc<str>>,
668 highlight_order: usize,
669 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
670 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
671 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
672 scrollbar_marker_state: ScrollbarMarkerState,
673 active_indent_guides_state: ActiveIndentGuidesState,
674 nav_history: Option<ItemNavHistory>,
675 context_menu: RefCell<Option<CodeContextMenu>>,
676 mouse_context_menu: Option<MouseContextMenu>,
677 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
678 signature_help_state: SignatureHelpState,
679 auto_signature_help: Option<bool>,
680 find_all_references_task_sources: Vec<Anchor>,
681 next_completion_id: CompletionId,
682 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
683 code_actions_task: Option<Task<Result<()>>>,
684 selection_highlight_task: Option<Task<()>>,
685 document_highlights_task: Option<Task<()>>,
686 linked_editing_range_task: Option<Task<Option<()>>>,
687 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
688 pending_rename: Option<RenameState>,
689 searchable: bool,
690 cursor_shape: CursorShape,
691 current_line_highlight: Option<CurrentLineHighlight>,
692 collapse_matches: bool,
693 autoindent_mode: Option<AutoindentMode>,
694 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
695 input_enabled: bool,
696 use_modal_editing: bool,
697 read_only: bool,
698 leader_peer_id: Option<PeerId>,
699 remote_id: Option<ViewId>,
700 hover_state: HoverState,
701 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
702 gutter_hovered: bool,
703 hovered_link_state: Option<HoveredLinkState>,
704 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
705 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
706 active_inline_completion: Option<InlineCompletionState>,
707 /// Used to prevent flickering as the user types while the menu is open
708 stale_inline_completion_in_menu: Option<InlineCompletionState>,
709 edit_prediction_settings: EditPredictionSettings,
710 inline_completions_hidden_for_vim_mode: bool,
711 show_inline_completions_override: Option<bool>,
712 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
713 edit_prediction_preview: EditPredictionPreview,
714 edit_prediction_indent_conflict: bool,
715 edit_prediction_requires_modifier_in_indent_conflict: bool,
716 inlay_hint_cache: InlayHintCache,
717 next_inlay_id: usize,
718 _subscriptions: Vec<Subscription>,
719 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
720 gutter_dimensions: GutterDimensions,
721 style: Option<EditorStyle>,
722 text_style_refinement: Option<TextStyleRefinement>,
723 next_editor_action_id: EditorActionId,
724 editor_actions:
725 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
726 use_autoclose: bool,
727 use_auto_surround: bool,
728 auto_replace_emoji_shortcode: bool,
729 jsx_tag_auto_close_enabled_in_any_buffer: bool,
730 show_git_blame_gutter: bool,
731 show_git_blame_inline: bool,
732 show_git_blame_inline_delay_task: Option<Task<()>>,
733 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
734 git_blame_inline_enabled: bool,
735 serialize_dirty_buffers: bool,
736 show_selection_menu: Option<bool>,
737 blame: Option<Entity<GitBlame>>,
738 blame_subscription: Option<Subscription>,
739 custom_context_menu: Option<
740 Box<
741 dyn 'static
742 + Fn(
743 &mut Self,
744 DisplayPoint,
745 &mut Window,
746 &mut Context<Self>,
747 ) -> Option<Entity<ui::ContextMenu>>,
748 >,
749 >,
750 last_bounds: Option<Bounds<Pixels>>,
751 last_position_map: Option<Rc<PositionMap>>,
752 expect_bounds_change: Option<Bounds<Pixels>>,
753 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
754 tasks_update_task: Option<Task<()>>,
755 in_project_search: bool,
756 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
757 breadcrumb_header: Option<String>,
758 focused_block: Option<FocusedBlock>,
759 next_scroll_position: NextScrollCursorCenterTopBottom,
760 addons: HashMap<TypeId, Box<dyn Addon>>,
761 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
762 load_diff_task: Option<Shared<Task<()>>>,
763 selection_mark_mode: bool,
764 toggle_fold_multiple_buffers: Task<()>,
765 _scroll_cursor_center_top_bottom_task: Task<()>,
766 serialize_selections: Task<()>,
767}
768
769#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
770enum NextScrollCursorCenterTopBottom {
771 #[default]
772 Center,
773 Top,
774 Bottom,
775}
776
777impl NextScrollCursorCenterTopBottom {
778 fn next(&self) -> Self {
779 match self {
780 Self::Center => Self::Top,
781 Self::Top => Self::Bottom,
782 Self::Bottom => Self::Center,
783 }
784 }
785}
786
787#[derive(Clone)]
788pub struct EditorSnapshot {
789 pub mode: EditorMode,
790 show_gutter: bool,
791 show_line_numbers: Option<bool>,
792 show_git_diff_gutter: Option<bool>,
793 show_code_actions: Option<bool>,
794 show_runnables: Option<bool>,
795 git_blame_gutter_max_author_length: Option<usize>,
796 pub display_snapshot: DisplaySnapshot,
797 pub placeholder_text: Option<Arc<str>>,
798 is_focused: bool,
799 scroll_anchor: ScrollAnchor,
800 ongoing_scroll: OngoingScroll,
801 current_line_highlight: CurrentLineHighlight,
802 gutter_hovered: bool,
803}
804
805const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
806
807#[derive(Default, Debug, Clone, Copy)]
808pub struct GutterDimensions {
809 pub left_padding: Pixels,
810 pub right_padding: Pixels,
811 pub width: Pixels,
812 pub margin: Pixels,
813 pub git_blame_entries_width: Option<Pixels>,
814}
815
816impl GutterDimensions {
817 /// The full width of the space taken up by the gutter.
818 pub fn full_width(&self) -> Pixels {
819 self.margin + self.width
820 }
821
822 /// The width of the space reserved for the fold indicators,
823 /// use alongside 'justify_end' and `gutter_width` to
824 /// right align content with the line numbers
825 pub fn fold_area_width(&self) -> Pixels {
826 self.margin + self.right_padding
827 }
828}
829
830#[derive(Debug)]
831pub struct RemoteSelection {
832 pub replica_id: ReplicaId,
833 pub selection: Selection<Anchor>,
834 pub cursor_shape: CursorShape,
835 pub peer_id: PeerId,
836 pub line_mode: bool,
837 pub participant_index: Option<ParticipantIndex>,
838 pub user_name: Option<SharedString>,
839}
840
841#[derive(Clone, Debug)]
842struct SelectionHistoryEntry {
843 selections: Arc<[Selection<Anchor>]>,
844 select_next_state: Option<SelectNextState>,
845 select_prev_state: Option<SelectNextState>,
846 add_selections_state: Option<AddSelectionsState>,
847}
848
849enum SelectionHistoryMode {
850 Normal,
851 Undoing,
852 Redoing,
853}
854
855#[derive(Clone, PartialEq, Eq, Hash)]
856struct HoveredCursor {
857 replica_id: u16,
858 selection_id: usize,
859}
860
861impl Default for SelectionHistoryMode {
862 fn default() -> Self {
863 Self::Normal
864 }
865}
866
867#[derive(Default)]
868struct SelectionHistory {
869 #[allow(clippy::type_complexity)]
870 selections_by_transaction:
871 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
872 mode: SelectionHistoryMode,
873 undo_stack: VecDeque<SelectionHistoryEntry>,
874 redo_stack: VecDeque<SelectionHistoryEntry>,
875}
876
877impl SelectionHistory {
878 fn insert_transaction(
879 &mut self,
880 transaction_id: TransactionId,
881 selections: Arc<[Selection<Anchor>]>,
882 ) {
883 self.selections_by_transaction
884 .insert(transaction_id, (selections, None));
885 }
886
887 #[allow(clippy::type_complexity)]
888 fn transaction(
889 &self,
890 transaction_id: TransactionId,
891 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
892 self.selections_by_transaction.get(&transaction_id)
893 }
894
895 #[allow(clippy::type_complexity)]
896 fn transaction_mut(
897 &mut self,
898 transaction_id: TransactionId,
899 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
900 self.selections_by_transaction.get_mut(&transaction_id)
901 }
902
903 fn push(&mut self, entry: SelectionHistoryEntry) {
904 if !entry.selections.is_empty() {
905 match self.mode {
906 SelectionHistoryMode::Normal => {
907 self.push_undo(entry);
908 self.redo_stack.clear();
909 }
910 SelectionHistoryMode::Undoing => self.push_redo(entry),
911 SelectionHistoryMode::Redoing => self.push_undo(entry),
912 }
913 }
914 }
915
916 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
917 if self
918 .undo_stack
919 .back()
920 .map_or(true, |e| e.selections != entry.selections)
921 {
922 self.undo_stack.push_back(entry);
923 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
924 self.undo_stack.pop_front();
925 }
926 }
927 }
928
929 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
930 if self
931 .redo_stack
932 .back()
933 .map_or(true, |e| e.selections != entry.selections)
934 {
935 self.redo_stack.push_back(entry);
936 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
937 self.redo_stack.pop_front();
938 }
939 }
940 }
941}
942
943struct RowHighlight {
944 index: usize,
945 range: Range<Anchor>,
946 color: Hsla,
947 should_autoscroll: bool,
948}
949
950#[derive(Clone, Debug)]
951struct AddSelectionsState {
952 above: bool,
953 stack: Vec<usize>,
954}
955
956#[derive(Clone)]
957struct SelectNextState {
958 query: AhoCorasick,
959 wordwise: bool,
960 done: bool,
961}
962
963impl std::fmt::Debug for SelectNextState {
964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
965 f.debug_struct(std::any::type_name::<Self>())
966 .field("wordwise", &self.wordwise)
967 .field("done", &self.done)
968 .finish()
969 }
970}
971
972#[derive(Debug)]
973struct AutocloseRegion {
974 selection_id: usize,
975 range: Range<Anchor>,
976 pair: BracketPair,
977}
978
979#[derive(Debug)]
980struct SnippetState {
981 ranges: Vec<Vec<Range<Anchor>>>,
982 active_index: usize,
983 choices: Vec<Option<Vec<String>>>,
984}
985
986#[doc(hidden)]
987pub struct RenameState {
988 pub range: Range<Anchor>,
989 pub old_name: Arc<str>,
990 pub editor: Entity<Editor>,
991 block_id: CustomBlockId,
992}
993
994struct InvalidationStack<T>(Vec<T>);
995
996struct RegisteredInlineCompletionProvider {
997 provider: Arc<dyn InlineCompletionProviderHandle>,
998 _subscription: Subscription,
999}
1000
1001#[derive(Debug, PartialEq, Eq)]
1002struct ActiveDiagnosticGroup {
1003 primary_range: Range<Anchor>,
1004 primary_message: String,
1005 group_id: usize,
1006 blocks: HashMap<CustomBlockId, Diagnostic>,
1007 is_valid: bool,
1008}
1009
1010#[derive(Serialize, Deserialize, Clone, Debug)]
1011pub struct ClipboardSelection {
1012 /// The number of bytes in this selection.
1013 pub len: usize,
1014 /// Whether this was a full-line selection.
1015 pub is_entire_line: bool,
1016 /// The indentation of the first line when this content was originally copied.
1017 pub first_line_indent: u32,
1018}
1019
1020#[derive(Debug)]
1021pub(crate) struct NavigationData {
1022 cursor_anchor: Anchor,
1023 cursor_position: Point,
1024 scroll_anchor: ScrollAnchor,
1025 scroll_top_row: u32,
1026}
1027
1028#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1029pub enum GotoDefinitionKind {
1030 Symbol,
1031 Declaration,
1032 Type,
1033 Implementation,
1034}
1035
1036#[derive(Debug, Clone)]
1037enum InlayHintRefreshReason {
1038 ModifiersChanged(bool),
1039 Toggle(bool),
1040 SettingsChange(InlayHintSettings),
1041 NewLinesShown,
1042 BufferEdited(HashSet<Arc<Language>>),
1043 RefreshRequested,
1044 ExcerptsRemoved(Vec<ExcerptId>),
1045}
1046
1047impl InlayHintRefreshReason {
1048 fn description(&self) -> &'static str {
1049 match self {
1050 Self::ModifiersChanged(_) => "modifiers changed",
1051 Self::Toggle(_) => "toggle",
1052 Self::SettingsChange(_) => "settings change",
1053 Self::NewLinesShown => "new lines shown",
1054 Self::BufferEdited(_) => "buffer edited",
1055 Self::RefreshRequested => "refresh requested",
1056 Self::ExcerptsRemoved(_) => "excerpts removed",
1057 }
1058 }
1059}
1060
1061pub enum FormatTarget {
1062 Buffers,
1063 Ranges(Vec<Range<MultiBufferPoint>>),
1064}
1065
1066pub(crate) struct FocusedBlock {
1067 id: BlockId,
1068 focus_handle: WeakFocusHandle,
1069}
1070
1071#[derive(Clone)]
1072enum JumpData {
1073 MultiBufferRow {
1074 row: MultiBufferRow,
1075 line_offset_from_top: u32,
1076 },
1077 MultiBufferPoint {
1078 excerpt_id: ExcerptId,
1079 position: Point,
1080 anchor: text::Anchor,
1081 line_offset_from_top: u32,
1082 },
1083}
1084
1085pub enum MultibufferSelectionMode {
1086 First,
1087 All,
1088}
1089
1090impl Editor {
1091 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1092 let buffer = cx.new(|cx| Buffer::local("", cx));
1093 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1094 Self::new(
1095 EditorMode::SingleLine { auto_width: false },
1096 buffer,
1097 None,
1098 false,
1099 window,
1100 cx,
1101 )
1102 }
1103
1104 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1105 let buffer = cx.new(|cx| Buffer::local("", cx));
1106 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1107 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1108 }
1109
1110 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1111 let buffer = cx.new(|cx| Buffer::local("", cx));
1112 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1113 Self::new(
1114 EditorMode::SingleLine { auto_width: true },
1115 buffer,
1116 None,
1117 false,
1118 window,
1119 cx,
1120 )
1121 }
1122
1123 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1124 let buffer = cx.new(|cx| Buffer::local("", cx));
1125 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1126 Self::new(
1127 EditorMode::AutoHeight { max_lines },
1128 buffer,
1129 None,
1130 false,
1131 window,
1132 cx,
1133 )
1134 }
1135
1136 pub fn for_buffer(
1137 buffer: Entity<Buffer>,
1138 project: Option<Entity<Project>>,
1139 window: &mut Window,
1140 cx: &mut Context<Self>,
1141 ) -> Self {
1142 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1143 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1144 }
1145
1146 pub fn for_multibuffer(
1147 buffer: Entity<MultiBuffer>,
1148 project: Option<Entity<Project>>,
1149 show_excerpt_controls: bool,
1150 window: &mut Window,
1151 cx: &mut Context<Self>,
1152 ) -> Self {
1153 Self::new(
1154 EditorMode::Full,
1155 buffer,
1156 project,
1157 show_excerpt_controls,
1158 window,
1159 cx,
1160 )
1161 }
1162
1163 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1164 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1165 let mut clone = Self::new(
1166 self.mode,
1167 self.buffer.clone(),
1168 self.project.clone(),
1169 show_excerpt_controls,
1170 window,
1171 cx,
1172 );
1173 self.display_map.update(cx, |display_map, cx| {
1174 let snapshot = display_map.snapshot(cx);
1175 clone.display_map.update(cx, |display_map, cx| {
1176 display_map.set_state(&snapshot, cx);
1177 });
1178 });
1179 clone.selections.clone_state(&self.selections);
1180 clone.scroll_manager.clone_state(&self.scroll_manager);
1181 clone.searchable = self.searchable;
1182 clone
1183 }
1184
1185 pub fn new(
1186 mode: EditorMode,
1187 buffer: Entity<MultiBuffer>,
1188 project: Option<Entity<Project>>,
1189 show_excerpt_controls: bool,
1190 window: &mut Window,
1191 cx: &mut Context<Self>,
1192 ) -> Self {
1193 let style = window.text_style();
1194 let font_size = style.font_size.to_pixels(window.rem_size());
1195 let editor = cx.entity().downgrade();
1196 let fold_placeholder = FoldPlaceholder {
1197 constrain_width: true,
1198 render: Arc::new(move |fold_id, fold_range, cx| {
1199 let editor = editor.clone();
1200 div()
1201 .id(fold_id)
1202 .bg(cx.theme().colors().ghost_element_background)
1203 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1204 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1205 .rounded_xs()
1206 .size_full()
1207 .cursor_pointer()
1208 .child("⋯")
1209 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1210 .on_click(move |_, _window, cx| {
1211 editor
1212 .update(cx, |editor, cx| {
1213 editor.unfold_ranges(
1214 &[fold_range.start..fold_range.end],
1215 true,
1216 false,
1217 cx,
1218 );
1219 cx.stop_propagation();
1220 })
1221 .ok();
1222 })
1223 .into_any()
1224 }),
1225 merge_adjacent: true,
1226 ..Default::default()
1227 };
1228 let display_map = cx.new(|cx| {
1229 DisplayMap::new(
1230 buffer.clone(),
1231 style.font(),
1232 font_size,
1233 None,
1234 show_excerpt_controls,
1235 FILE_HEADER_HEIGHT,
1236 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1237 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1238 fold_placeholder,
1239 cx,
1240 )
1241 });
1242
1243 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1244
1245 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1246
1247 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1248 .then(|| language_settings::SoftWrap::None);
1249
1250 let mut project_subscriptions = Vec::new();
1251 if mode == EditorMode::Full {
1252 if let Some(project) = project.as_ref() {
1253 project_subscriptions.push(cx.subscribe_in(
1254 project,
1255 window,
1256 |editor, _, event, window, cx| {
1257 if let project::Event::RefreshInlayHints = event {
1258 editor
1259 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1260 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1261 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1262 let focus_handle = editor.focus_handle(cx);
1263 if focus_handle.is_focused(window) {
1264 let snapshot = buffer.read(cx).snapshot();
1265 for (range, snippet) in snippet_edits {
1266 let editor_range =
1267 language::range_from_lsp(*range).to_offset(&snapshot);
1268 editor
1269 .insert_snippet(
1270 &[editor_range],
1271 snippet.clone(),
1272 window,
1273 cx,
1274 )
1275 .ok();
1276 }
1277 }
1278 }
1279 }
1280 },
1281 ));
1282 if let Some(task_inventory) = project
1283 .read(cx)
1284 .task_store()
1285 .read(cx)
1286 .task_inventory()
1287 .cloned()
1288 {
1289 project_subscriptions.push(cx.observe_in(
1290 &task_inventory,
1291 window,
1292 |editor, _, window, cx| {
1293 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1294 },
1295 ));
1296 }
1297 }
1298 }
1299
1300 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1301
1302 let inlay_hint_settings =
1303 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1304 let focus_handle = cx.focus_handle();
1305 cx.on_focus(&focus_handle, window, Self::handle_focus)
1306 .detach();
1307 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1308 .detach();
1309 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1310 .detach();
1311 cx.on_blur(&focus_handle, window, Self::handle_blur)
1312 .detach();
1313
1314 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1315 Some(false)
1316 } else {
1317 None
1318 };
1319
1320 let mut code_action_providers = Vec::new();
1321 let mut load_uncommitted_diff = None;
1322 if let Some(project) = project.clone() {
1323 load_uncommitted_diff = Some(
1324 get_uncommitted_diff_for_buffer(
1325 &project,
1326 buffer.read(cx).all_buffers(),
1327 buffer.clone(),
1328 cx,
1329 )
1330 .shared(),
1331 );
1332 code_action_providers.push(Rc::new(project) as Rc<_>);
1333 }
1334
1335 let mut this = Self {
1336 focus_handle,
1337 show_cursor_when_unfocused: false,
1338 last_focused_descendant: None,
1339 buffer: buffer.clone(),
1340 display_map: display_map.clone(),
1341 selections,
1342 scroll_manager: ScrollManager::new(cx),
1343 columnar_selection_tail: None,
1344 add_selections_state: None,
1345 select_next_state: None,
1346 select_prev_state: None,
1347 selection_history: Default::default(),
1348 autoclose_regions: Default::default(),
1349 snippet_stack: Default::default(),
1350 select_larger_syntax_node_stack: Vec::new(),
1351 ime_transaction: Default::default(),
1352 active_diagnostics: None,
1353 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1354 inline_diagnostics_update: Task::ready(()),
1355 inline_diagnostics: Vec::new(),
1356 soft_wrap_mode_override,
1357 completion_provider: project.clone().map(|project| Box::new(project) as _),
1358 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1359 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1360 project,
1361 blink_manager: blink_manager.clone(),
1362 show_local_selections: true,
1363 show_scrollbars: true,
1364 mode,
1365 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1366 show_gutter: mode == EditorMode::Full,
1367 show_line_numbers: None,
1368 use_relative_line_numbers: None,
1369 show_git_diff_gutter: None,
1370 show_code_actions: None,
1371 show_runnables: None,
1372 show_wrap_guides: None,
1373 show_indent_guides,
1374 placeholder_text: None,
1375 highlight_order: 0,
1376 highlighted_rows: HashMap::default(),
1377 background_highlights: Default::default(),
1378 gutter_highlights: TreeMap::default(),
1379 scrollbar_marker_state: ScrollbarMarkerState::default(),
1380 active_indent_guides_state: ActiveIndentGuidesState::default(),
1381 nav_history: None,
1382 context_menu: RefCell::new(None),
1383 mouse_context_menu: None,
1384 completion_tasks: Default::default(),
1385 signature_help_state: SignatureHelpState::default(),
1386 auto_signature_help: None,
1387 find_all_references_task_sources: Vec::new(),
1388 next_completion_id: 0,
1389 next_inlay_id: 0,
1390 code_action_providers,
1391 available_code_actions: Default::default(),
1392 code_actions_task: Default::default(),
1393 selection_highlight_task: Default::default(),
1394 document_highlights_task: Default::default(),
1395 linked_editing_range_task: Default::default(),
1396 pending_rename: Default::default(),
1397 searchable: true,
1398 cursor_shape: EditorSettings::get_global(cx)
1399 .cursor_shape
1400 .unwrap_or_default(),
1401 current_line_highlight: None,
1402 autoindent_mode: Some(AutoindentMode::EachLine),
1403 collapse_matches: false,
1404 workspace: None,
1405 input_enabled: true,
1406 use_modal_editing: mode == EditorMode::Full,
1407 read_only: false,
1408 use_autoclose: true,
1409 use_auto_surround: true,
1410 auto_replace_emoji_shortcode: false,
1411 jsx_tag_auto_close_enabled_in_any_buffer: false,
1412 leader_peer_id: None,
1413 remote_id: None,
1414 hover_state: Default::default(),
1415 pending_mouse_down: None,
1416 hovered_link_state: Default::default(),
1417 edit_prediction_provider: None,
1418 active_inline_completion: None,
1419 stale_inline_completion_in_menu: None,
1420 edit_prediction_preview: EditPredictionPreview::Inactive {
1421 released_too_fast: false,
1422 },
1423 inline_diagnostics_enabled: mode == EditorMode::Full,
1424 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1425
1426 gutter_hovered: false,
1427 pixel_position_of_newest_cursor: None,
1428 last_bounds: None,
1429 last_position_map: None,
1430 expect_bounds_change: None,
1431 gutter_dimensions: GutterDimensions::default(),
1432 style: None,
1433 show_cursor_names: false,
1434 hovered_cursors: Default::default(),
1435 next_editor_action_id: EditorActionId::default(),
1436 editor_actions: Rc::default(),
1437 inline_completions_hidden_for_vim_mode: false,
1438 show_inline_completions_override: None,
1439 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1440 edit_prediction_settings: EditPredictionSettings::Disabled,
1441 edit_prediction_indent_conflict: false,
1442 edit_prediction_requires_modifier_in_indent_conflict: true,
1443 custom_context_menu: None,
1444 show_git_blame_gutter: false,
1445 show_git_blame_inline: false,
1446 show_selection_menu: None,
1447 show_git_blame_inline_delay_task: None,
1448 git_blame_inline_tooltip: None,
1449 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1450 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1451 .session
1452 .restore_unsaved_buffers,
1453 blame: None,
1454 blame_subscription: None,
1455 tasks: Default::default(),
1456 _subscriptions: vec![
1457 cx.observe(&buffer, Self::on_buffer_changed),
1458 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1459 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1460 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1461 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1462 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1463 cx.observe_window_activation(window, |editor, window, cx| {
1464 let active = window.is_window_active();
1465 editor.blink_manager.update(cx, |blink_manager, cx| {
1466 if active {
1467 blink_manager.enable(cx);
1468 } else {
1469 blink_manager.disable(cx);
1470 }
1471 });
1472 }),
1473 ],
1474 tasks_update_task: None,
1475 linked_edit_ranges: Default::default(),
1476 in_project_search: false,
1477 previous_search_ranges: None,
1478 breadcrumb_header: None,
1479 focused_block: None,
1480 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1481 addons: HashMap::default(),
1482 registered_buffers: HashMap::default(),
1483 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1484 selection_mark_mode: false,
1485 toggle_fold_multiple_buffers: Task::ready(()),
1486 serialize_selections: Task::ready(()),
1487 text_style_refinement: None,
1488 load_diff_task: load_uncommitted_diff,
1489 };
1490 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1491 this._subscriptions.extend(project_subscriptions);
1492
1493 this.end_selection(window, cx);
1494 this.scroll_manager.show_scrollbar(window, cx);
1495 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1496
1497 if mode == EditorMode::Full {
1498 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1499 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1500
1501 if this.git_blame_inline_enabled {
1502 this.git_blame_inline_enabled = true;
1503 this.start_git_blame_inline(false, window, cx);
1504 }
1505
1506 if let Some(buffer) = buffer.read(cx).as_singleton() {
1507 if let Some(project) = this.project.as_ref() {
1508 let handle = project.update(cx, |project, cx| {
1509 project.register_buffer_with_language_servers(&buffer, cx)
1510 });
1511 this.registered_buffers
1512 .insert(buffer.read(cx).remote_id(), handle);
1513 }
1514 }
1515 }
1516
1517 this.report_editor_event("Editor Opened", None, cx);
1518 this
1519 }
1520
1521 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1522 self.mouse_context_menu
1523 .as_ref()
1524 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1525 }
1526
1527 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1528 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1529 }
1530
1531 fn key_context_internal(
1532 &self,
1533 has_active_edit_prediction: bool,
1534 window: &Window,
1535 cx: &App,
1536 ) -> KeyContext {
1537 let mut key_context = KeyContext::new_with_defaults();
1538 key_context.add("Editor");
1539 let mode = match self.mode {
1540 EditorMode::SingleLine { .. } => "single_line",
1541 EditorMode::AutoHeight { .. } => "auto_height",
1542 EditorMode::Full => "full",
1543 };
1544
1545 if EditorSettings::jupyter_enabled(cx) {
1546 key_context.add("jupyter");
1547 }
1548
1549 key_context.set("mode", mode);
1550 if self.pending_rename.is_some() {
1551 key_context.add("renaming");
1552 }
1553
1554 match self.context_menu.borrow().as_ref() {
1555 Some(CodeContextMenu::Completions(_)) => {
1556 key_context.add("menu");
1557 key_context.add("showing_completions");
1558 }
1559 Some(CodeContextMenu::CodeActions(_)) => {
1560 key_context.add("menu");
1561 key_context.add("showing_code_actions")
1562 }
1563 None => {}
1564 }
1565
1566 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1567 if !self.focus_handle(cx).contains_focused(window, cx)
1568 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1569 {
1570 for addon in self.addons.values() {
1571 addon.extend_key_context(&mut key_context, cx)
1572 }
1573 }
1574
1575 if let Some(extension) = self
1576 .buffer
1577 .read(cx)
1578 .as_singleton()
1579 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1580 {
1581 key_context.set("extension", extension.to_string());
1582 }
1583
1584 if has_active_edit_prediction {
1585 if self.edit_prediction_in_conflict() {
1586 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1587 } else {
1588 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1589 key_context.add("copilot_suggestion");
1590 }
1591 }
1592
1593 if self.selection_mark_mode {
1594 key_context.add("selection_mode");
1595 }
1596
1597 key_context
1598 }
1599
1600 pub fn edit_prediction_in_conflict(&self) -> bool {
1601 if !self.show_edit_predictions_in_menu() {
1602 return false;
1603 }
1604
1605 let showing_completions = self
1606 .context_menu
1607 .borrow()
1608 .as_ref()
1609 .map_or(false, |context| {
1610 matches!(context, CodeContextMenu::Completions(_))
1611 });
1612
1613 showing_completions
1614 || self.edit_prediction_requires_modifier()
1615 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1616 // bindings to insert tab characters.
1617 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1618 }
1619
1620 pub fn accept_edit_prediction_keybind(
1621 &self,
1622 window: &Window,
1623 cx: &App,
1624 ) -> AcceptEditPredictionBinding {
1625 let key_context = self.key_context_internal(true, window, cx);
1626 let in_conflict = self.edit_prediction_in_conflict();
1627
1628 AcceptEditPredictionBinding(
1629 window
1630 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1631 .into_iter()
1632 .filter(|binding| {
1633 !in_conflict
1634 || binding
1635 .keystrokes()
1636 .first()
1637 .map_or(false, |keystroke| keystroke.modifiers.modified())
1638 })
1639 .rev()
1640 .min_by_key(|binding| {
1641 binding
1642 .keystrokes()
1643 .first()
1644 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1645 }),
1646 )
1647 }
1648
1649 pub fn new_file(
1650 workspace: &mut Workspace,
1651 _: &workspace::NewFile,
1652 window: &mut Window,
1653 cx: &mut Context<Workspace>,
1654 ) {
1655 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1656 "Failed to create buffer",
1657 window,
1658 cx,
1659 |e, _, _| match e.error_code() {
1660 ErrorCode::RemoteUpgradeRequired => Some(format!(
1661 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1662 e.error_tag("required").unwrap_or("the latest version")
1663 )),
1664 _ => None,
1665 },
1666 );
1667 }
1668
1669 pub fn new_in_workspace(
1670 workspace: &mut Workspace,
1671 window: &mut Window,
1672 cx: &mut Context<Workspace>,
1673 ) -> Task<Result<Entity<Editor>>> {
1674 let project = workspace.project().clone();
1675 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1676
1677 cx.spawn_in(window, |workspace, mut cx| async move {
1678 let buffer = create.await?;
1679 workspace.update_in(&mut cx, |workspace, window, cx| {
1680 let editor =
1681 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1682 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1683 editor
1684 })
1685 })
1686 }
1687
1688 fn new_file_vertical(
1689 workspace: &mut Workspace,
1690 _: &workspace::NewFileSplitVertical,
1691 window: &mut Window,
1692 cx: &mut Context<Workspace>,
1693 ) {
1694 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1695 }
1696
1697 fn new_file_horizontal(
1698 workspace: &mut Workspace,
1699 _: &workspace::NewFileSplitHorizontal,
1700 window: &mut Window,
1701 cx: &mut Context<Workspace>,
1702 ) {
1703 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1704 }
1705
1706 fn new_file_in_direction(
1707 workspace: &mut Workspace,
1708 direction: SplitDirection,
1709 window: &mut Window,
1710 cx: &mut Context<Workspace>,
1711 ) {
1712 let project = workspace.project().clone();
1713 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1714
1715 cx.spawn_in(window, |workspace, mut cx| async move {
1716 let buffer = create.await?;
1717 workspace.update_in(&mut cx, move |workspace, window, cx| {
1718 workspace.split_item(
1719 direction,
1720 Box::new(
1721 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1722 ),
1723 window,
1724 cx,
1725 )
1726 })?;
1727 anyhow::Ok(())
1728 })
1729 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1730 match e.error_code() {
1731 ErrorCode::RemoteUpgradeRequired => Some(format!(
1732 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1733 e.error_tag("required").unwrap_or("the latest version")
1734 )),
1735 _ => None,
1736 }
1737 });
1738 }
1739
1740 pub fn leader_peer_id(&self) -> Option<PeerId> {
1741 self.leader_peer_id
1742 }
1743
1744 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1745 &self.buffer
1746 }
1747
1748 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1749 self.workspace.as_ref()?.0.upgrade()
1750 }
1751
1752 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1753 self.buffer().read(cx).title(cx)
1754 }
1755
1756 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1757 let git_blame_gutter_max_author_length = self
1758 .render_git_blame_gutter(cx)
1759 .then(|| {
1760 if let Some(blame) = self.blame.as_ref() {
1761 let max_author_length =
1762 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1763 Some(max_author_length)
1764 } else {
1765 None
1766 }
1767 })
1768 .flatten();
1769
1770 EditorSnapshot {
1771 mode: self.mode,
1772 show_gutter: self.show_gutter,
1773 show_line_numbers: self.show_line_numbers,
1774 show_git_diff_gutter: self.show_git_diff_gutter,
1775 show_code_actions: self.show_code_actions,
1776 show_runnables: self.show_runnables,
1777 git_blame_gutter_max_author_length,
1778 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1779 scroll_anchor: self.scroll_manager.anchor(),
1780 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1781 placeholder_text: self.placeholder_text.clone(),
1782 is_focused: self.focus_handle.is_focused(window),
1783 current_line_highlight: self
1784 .current_line_highlight
1785 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1786 gutter_hovered: self.gutter_hovered,
1787 }
1788 }
1789
1790 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1791 self.buffer.read(cx).language_at(point, cx)
1792 }
1793
1794 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1795 self.buffer.read(cx).read(cx).file_at(point).cloned()
1796 }
1797
1798 pub fn active_excerpt(
1799 &self,
1800 cx: &App,
1801 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1802 self.buffer
1803 .read(cx)
1804 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1805 }
1806
1807 pub fn mode(&self) -> EditorMode {
1808 self.mode
1809 }
1810
1811 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1812 self.collaboration_hub.as_deref()
1813 }
1814
1815 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1816 self.collaboration_hub = Some(hub);
1817 }
1818
1819 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1820 self.in_project_search = in_project_search;
1821 }
1822
1823 pub fn set_custom_context_menu(
1824 &mut self,
1825 f: impl 'static
1826 + Fn(
1827 &mut Self,
1828 DisplayPoint,
1829 &mut Window,
1830 &mut Context<Self>,
1831 ) -> Option<Entity<ui::ContextMenu>>,
1832 ) {
1833 self.custom_context_menu = Some(Box::new(f))
1834 }
1835
1836 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1837 self.completion_provider = provider;
1838 }
1839
1840 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1841 self.semantics_provider.clone()
1842 }
1843
1844 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1845 self.semantics_provider = provider;
1846 }
1847
1848 pub fn set_edit_prediction_provider<T>(
1849 &mut self,
1850 provider: Option<Entity<T>>,
1851 window: &mut Window,
1852 cx: &mut Context<Self>,
1853 ) where
1854 T: EditPredictionProvider,
1855 {
1856 self.edit_prediction_provider =
1857 provider.map(|provider| RegisteredInlineCompletionProvider {
1858 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1859 if this.focus_handle.is_focused(window) {
1860 this.update_visible_inline_completion(window, cx);
1861 }
1862 }),
1863 provider: Arc::new(provider),
1864 });
1865 self.update_edit_prediction_settings(cx);
1866 self.refresh_inline_completion(false, false, window, cx);
1867 }
1868
1869 pub fn placeholder_text(&self) -> Option<&str> {
1870 self.placeholder_text.as_deref()
1871 }
1872
1873 pub fn set_placeholder_text(
1874 &mut self,
1875 placeholder_text: impl Into<Arc<str>>,
1876 cx: &mut Context<Self>,
1877 ) {
1878 let placeholder_text = Some(placeholder_text.into());
1879 if self.placeholder_text != placeholder_text {
1880 self.placeholder_text = placeholder_text;
1881 cx.notify();
1882 }
1883 }
1884
1885 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1886 self.cursor_shape = cursor_shape;
1887
1888 // Disrupt blink for immediate user feedback that the cursor shape has changed
1889 self.blink_manager.update(cx, BlinkManager::show_cursor);
1890
1891 cx.notify();
1892 }
1893
1894 pub fn set_current_line_highlight(
1895 &mut self,
1896 current_line_highlight: Option<CurrentLineHighlight>,
1897 ) {
1898 self.current_line_highlight = current_line_highlight;
1899 }
1900
1901 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1902 self.collapse_matches = collapse_matches;
1903 }
1904
1905 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1906 let buffers = self.buffer.read(cx).all_buffers();
1907 let Some(project) = self.project.as_ref() else {
1908 return;
1909 };
1910 project.update(cx, |project, cx| {
1911 for buffer in buffers {
1912 self.registered_buffers
1913 .entry(buffer.read(cx).remote_id())
1914 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1915 }
1916 })
1917 }
1918
1919 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1920 if self.collapse_matches {
1921 return range.start..range.start;
1922 }
1923 range.clone()
1924 }
1925
1926 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1927 if self.display_map.read(cx).clip_at_line_ends != clip {
1928 self.display_map
1929 .update(cx, |map, _| map.clip_at_line_ends = clip);
1930 }
1931 }
1932
1933 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1934 self.input_enabled = input_enabled;
1935 }
1936
1937 pub fn set_inline_completions_hidden_for_vim_mode(
1938 &mut self,
1939 hidden: bool,
1940 window: &mut Window,
1941 cx: &mut Context<Self>,
1942 ) {
1943 if hidden != self.inline_completions_hidden_for_vim_mode {
1944 self.inline_completions_hidden_for_vim_mode = hidden;
1945 if hidden {
1946 self.update_visible_inline_completion(window, cx);
1947 } else {
1948 self.refresh_inline_completion(true, false, window, cx);
1949 }
1950 }
1951 }
1952
1953 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1954 self.menu_inline_completions_policy = value;
1955 }
1956
1957 pub fn set_autoindent(&mut self, autoindent: bool) {
1958 if autoindent {
1959 self.autoindent_mode = Some(AutoindentMode::EachLine);
1960 } else {
1961 self.autoindent_mode = None;
1962 }
1963 }
1964
1965 pub fn read_only(&self, cx: &App) -> bool {
1966 self.read_only || self.buffer.read(cx).read_only()
1967 }
1968
1969 pub fn set_read_only(&mut self, read_only: bool) {
1970 self.read_only = read_only;
1971 }
1972
1973 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1974 self.use_autoclose = autoclose;
1975 }
1976
1977 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1978 self.use_auto_surround = auto_surround;
1979 }
1980
1981 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1982 self.auto_replace_emoji_shortcode = auto_replace;
1983 }
1984
1985 pub fn toggle_edit_predictions(
1986 &mut self,
1987 _: &ToggleEditPrediction,
1988 window: &mut Window,
1989 cx: &mut Context<Self>,
1990 ) {
1991 if self.show_inline_completions_override.is_some() {
1992 self.set_show_edit_predictions(None, window, cx);
1993 } else {
1994 let show_edit_predictions = !self.edit_predictions_enabled();
1995 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1996 }
1997 }
1998
1999 pub fn set_show_edit_predictions(
2000 &mut self,
2001 show_edit_predictions: Option<bool>,
2002 window: &mut Window,
2003 cx: &mut Context<Self>,
2004 ) {
2005 self.show_inline_completions_override = show_edit_predictions;
2006 self.update_edit_prediction_settings(cx);
2007
2008 if let Some(false) = show_edit_predictions {
2009 self.discard_inline_completion(false, cx);
2010 } else {
2011 self.refresh_inline_completion(false, true, window, cx);
2012 }
2013 }
2014
2015 fn inline_completions_disabled_in_scope(
2016 &self,
2017 buffer: &Entity<Buffer>,
2018 buffer_position: language::Anchor,
2019 cx: &App,
2020 ) -> bool {
2021 let snapshot = buffer.read(cx).snapshot();
2022 let settings = snapshot.settings_at(buffer_position, cx);
2023
2024 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2025 return false;
2026 };
2027
2028 scope.override_name().map_or(false, |scope_name| {
2029 settings
2030 .edit_predictions_disabled_in
2031 .iter()
2032 .any(|s| s == scope_name)
2033 })
2034 }
2035
2036 pub fn set_use_modal_editing(&mut self, to: bool) {
2037 self.use_modal_editing = to;
2038 }
2039
2040 pub fn use_modal_editing(&self) -> bool {
2041 self.use_modal_editing
2042 }
2043
2044 fn selections_did_change(
2045 &mut self,
2046 local: bool,
2047 old_cursor_position: &Anchor,
2048 show_completions: bool,
2049 window: &mut Window,
2050 cx: &mut Context<Self>,
2051 ) {
2052 window.invalidate_character_coordinates();
2053
2054 // Copy selections to primary selection buffer
2055 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2056 if local {
2057 let selections = self.selections.all::<usize>(cx);
2058 let buffer_handle = self.buffer.read(cx).read(cx);
2059
2060 let mut text = String::new();
2061 for (index, selection) in selections.iter().enumerate() {
2062 let text_for_selection = buffer_handle
2063 .text_for_range(selection.start..selection.end)
2064 .collect::<String>();
2065
2066 text.push_str(&text_for_selection);
2067 if index != selections.len() - 1 {
2068 text.push('\n');
2069 }
2070 }
2071
2072 if !text.is_empty() {
2073 cx.write_to_primary(ClipboardItem::new_string(text));
2074 }
2075 }
2076
2077 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2078 self.buffer.update(cx, |buffer, cx| {
2079 buffer.set_active_selections(
2080 &self.selections.disjoint_anchors(),
2081 self.selections.line_mode,
2082 self.cursor_shape,
2083 cx,
2084 )
2085 });
2086 }
2087 let display_map = self
2088 .display_map
2089 .update(cx, |display_map, cx| display_map.snapshot(cx));
2090 let buffer = &display_map.buffer_snapshot;
2091 self.add_selections_state = None;
2092 self.select_next_state = None;
2093 self.select_prev_state = None;
2094 self.select_larger_syntax_node_stack.clear();
2095 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2096 self.snippet_stack
2097 .invalidate(&self.selections.disjoint_anchors(), buffer);
2098 self.take_rename(false, window, cx);
2099
2100 let new_cursor_position = self.selections.newest_anchor().head();
2101
2102 self.push_to_nav_history(
2103 *old_cursor_position,
2104 Some(new_cursor_position.to_point(buffer)),
2105 cx,
2106 );
2107
2108 if local {
2109 let new_cursor_position = self.selections.newest_anchor().head();
2110 let mut context_menu = self.context_menu.borrow_mut();
2111 let completion_menu = match context_menu.as_ref() {
2112 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2113 _ => {
2114 *context_menu = None;
2115 None
2116 }
2117 };
2118 if let Some(buffer_id) = new_cursor_position.buffer_id {
2119 if !self.registered_buffers.contains_key(&buffer_id) {
2120 if let Some(project) = self.project.as_ref() {
2121 project.update(cx, |project, cx| {
2122 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2123 return;
2124 };
2125 self.registered_buffers.insert(
2126 buffer_id,
2127 project.register_buffer_with_language_servers(&buffer, cx),
2128 );
2129 })
2130 }
2131 }
2132 }
2133
2134 if let Some(completion_menu) = completion_menu {
2135 let cursor_position = new_cursor_position.to_offset(buffer);
2136 let (word_range, kind) =
2137 buffer.surrounding_word(completion_menu.initial_position, true);
2138 if kind == Some(CharKind::Word)
2139 && word_range.to_inclusive().contains(&cursor_position)
2140 {
2141 let mut completion_menu = completion_menu.clone();
2142 drop(context_menu);
2143
2144 let query = Self::completion_query(buffer, cursor_position);
2145 cx.spawn(move |this, mut cx| async move {
2146 completion_menu
2147 .filter(query.as_deref(), cx.background_executor().clone())
2148 .await;
2149
2150 this.update(&mut cx, |this, cx| {
2151 let mut context_menu = this.context_menu.borrow_mut();
2152 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2153 else {
2154 return;
2155 };
2156
2157 if menu.id > completion_menu.id {
2158 return;
2159 }
2160
2161 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2162 drop(context_menu);
2163 cx.notify();
2164 })
2165 })
2166 .detach();
2167
2168 if show_completions {
2169 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2170 }
2171 } else {
2172 drop(context_menu);
2173 self.hide_context_menu(window, cx);
2174 }
2175 } else {
2176 drop(context_menu);
2177 }
2178
2179 hide_hover(self, cx);
2180
2181 if old_cursor_position.to_display_point(&display_map).row()
2182 != new_cursor_position.to_display_point(&display_map).row()
2183 {
2184 self.available_code_actions.take();
2185 }
2186 self.refresh_code_actions(window, cx);
2187 self.refresh_document_highlights(cx);
2188 self.refresh_selected_text_highlights(window, cx);
2189 refresh_matching_bracket_highlights(self, window, cx);
2190 self.update_visible_inline_completion(window, cx);
2191 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2192 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2193 if self.git_blame_inline_enabled {
2194 self.start_inline_blame_timer(window, cx);
2195 }
2196 }
2197
2198 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2199 cx.emit(EditorEvent::SelectionsChanged { local });
2200
2201 let selections = &self.selections.disjoint;
2202 if selections.len() == 1 {
2203 cx.emit(SearchEvent::ActiveMatchChanged)
2204 }
2205 if local
2206 && self.is_singleton(cx)
2207 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2208 {
2209 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2210 let background_executor = cx.background_executor().clone();
2211 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2212 let snapshot = self.buffer().read(cx).snapshot(cx);
2213 let selections = selections.clone();
2214 self.serialize_selections = cx.background_spawn(async move {
2215 background_executor.timer(Duration::from_millis(100)).await;
2216 let selections = selections
2217 .iter()
2218 .map(|selection| {
2219 (
2220 selection.start.to_offset(&snapshot),
2221 selection.end.to_offset(&snapshot),
2222 )
2223 })
2224 .collect();
2225 DB.save_editor_selections(editor_id, workspace_id, selections)
2226 .await
2227 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2228 .log_err();
2229 });
2230 }
2231 }
2232
2233 cx.notify();
2234 }
2235
2236 pub fn sync_selections(
2237 &mut self,
2238 other: Entity<Editor>,
2239 cx: &mut Context<Self>,
2240 ) -> gpui::Subscription {
2241 let other_selections = other.read(cx).selections.disjoint.to_vec();
2242 self.selections.change_with(cx, |selections| {
2243 selections.select_anchors(other_selections);
2244 });
2245
2246 let other_subscription =
2247 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2248 EditorEvent::SelectionsChanged { local: true } => {
2249 let other_selections = other.read(cx).selections.disjoint.to_vec();
2250 if other_selections.is_empty() {
2251 return;
2252 }
2253 this.selections.change_with(cx, |selections| {
2254 selections.select_anchors(other_selections);
2255 });
2256 }
2257 _ => {}
2258 });
2259
2260 let this_subscription =
2261 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2262 EditorEvent::SelectionsChanged { local: true } => {
2263 let these_selections = this.selections.disjoint.to_vec();
2264 if these_selections.is_empty() {
2265 return;
2266 }
2267 other.update(cx, |other_editor, cx| {
2268 other_editor.selections.change_with(cx, |selections| {
2269 selections.select_anchors(these_selections);
2270 })
2271 });
2272 }
2273 _ => {}
2274 });
2275
2276 Subscription::join(other_subscription, this_subscription)
2277 }
2278
2279 pub fn change_selections<R>(
2280 &mut self,
2281 autoscroll: Option<Autoscroll>,
2282 window: &mut Window,
2283 cx: &mut Context<Self>,
2284 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2285 ) -> R {
2286 self.change_selections_inner(autoscroll, true, window, cx, change)
2287 }
2288
2289 fn change_selections_inner<R>(
2290 &mut self,
2291 autoscroll: Option<Autoscroll>,
2292 request_completions: bool,
2293 window: &mut Window,
2294 cx: &mut Context<Self>,
2295 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2296 ) -> R {
2297 let old_cursor_position = self.selections.newest_anchor().head();
2298 self.push_to_selection_history();
2299
2300 let (changed, result) = self.selections.change_with(cx, change);
2301
2302 if changed {
2303 if let Some(autoscroll) = autoscroll {
2304 self.request_autoscroll(autoscroll, cx);
2305 }
2306 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2307
2308 if self.should_open_signature_help_automatically(
2309 &old_cursor_position,
2310 self.signature_help_state.backspace_pressed(),
2311 cx,
2312 ) {
2313 self.show_signature_help(&ShowSignatureHelp, window, cx);
2314 }
2315 self.signature_help_state.set_backspace_pressed(false);
2316 }
2317
2318 result
2319 }
2320
2321 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2322 where
2323 I: IntoIterator<Item = (Range<S>, T)>,
2324 S: ToOffset,
2325 T: Into<Arc<str>>,
2326 {
2327 if self.read_only(cx) {
2328 return;
2329 }
2330
2331 self.buffer
2332 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2333 }
2334
2335 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2336 where
2337 I: IntoIterator<Item = (Range<S>, T)>,
2338 S: ToOffset,
2339 T: Into<Arc<str>>,
2340 {
2341 if self.read_only(cx) {
2342 return;
2343 }
2344
2345 self.buffer.update(cx, |buffer, cx| {
2346 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2347 });
2348 }
2349
2350 pub fn edit_with_block_indent<I, S, T>(
2351 &mut self,
2352 edits: I,
2353 original_indent_columns: Vec<Option<u32>>,
2354 cx: &mut Context<Self>,
2355 ) where
2356 I: IntoIterator<Item = (Range<S>, T)>,
2357 S: ToOffset,
2358 T: Into<Arc<str>>,
2359 {
2360 if self.read_only(cx) {
2361 return;
2362 }
2363
2364 self.buffer.update(cx, |buffer, cx| {
2365 buffer.edit(
2366 edits,
2367 Some(AutoindentMode::Block {
2368 original_indent_columns,
2369 }),
2370 cx,
2371 )
2372 });
2373 }
2374
2375 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2376 self.hide_context_menu(window, cx);
2377
2378 match phase {
2379 SelectPhase::Begin {
2380 position,
2381 add,
2382 click_count,
2383 } => self.begin_selection(position, add, click_count, window, cx),
2384 SelectPhase::BeginColumnar {
2385 position,
2386 goal_column,
2387 reset,
2388 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2389 SelectPhase::Extend {
2390 position,
2391 click_count,
2392 } => self.extend_selection(position, click_count, window, cx),
2393 SelectPhase::Update {
2394 position,
2395 goal_column,
2396 scroll_delta,
2397 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2398 SelectPhase::End => self.end_selection(window, cx),
2399 }
2400 }
2401
2402 fn extend_selection(
2403 &mut self,
2404 position: DisplayPoint,
2405 click_count: usize,
2406 window: &mut Window,
2407 cx: &mut Context<Self>,
2408 ) {
2409 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2410 let tail = self.selections.newest::<usize>(cx).tail();
2411 self.begin_selection(position, false, click_count, window, cx);
2412
2413 let position = position.to_offset(&display_map, Bias::Left);
2414 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2415
2416 let mut pending_selection = self
2417 .selections
2418 .pending_anchor()
2419 .expect("extend_selection not called with pending selection");
2420 if position >= tail {
2421 pending_selection.start = tail_anchor;
2422 } else {
2423 pending_selection.end = tail_anchor;
2424 pending_selection.reversed = true;
2425 }
2426
2427 let mut pending_mode = self.selections.pending_mode().unwrap();
2428 match &mut pending_mode {
2429 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2430 _ => {}
2431 }
2432
2433 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2434 s.set_pending(pending_selection, pending_mode)
2435 });
2436 }
2437
2438 fn begin_selection(
2439 &mut self,
2440 position: DisplayPoint,
2441 add: bool,
2442 click_count: usize,
2443 window: &mut Window,
2444 cx: &mut Context<Self>,
2445 ) {
2446 if !self.focus_handle.is_focused(window) {
2447 self.last_focused_descendant = None;
2448 window.focus(&self.focus_handle);
2449 }
2450
2451 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2452 let buffer = &display_map.buffer_snapshot;
2453 let newest_selection = self.selections.newest_anchor().clone();
2454 let position = display_map.clip_point(position, Bias::Left);
2455
2456 let start;
2457 let end;
2458 let mode;
2459 let mut auto_scroll;
2460 match click_count {
2461 1 => {
2462 start = buffer.anchor_before(position.to_point(&display_map));
2463 end = start;
2464 mode = SelectMode::Character;
2465 auto_scroll = true;
2466 }
2467 2 => {
2468 let range = movement::surrounding_word(&display_map, position);
2469 start = buffer.anchor_before(range.start.to_point(&display_map));
2470 end = buffer.anchor_before(range.end.to_point(&display_map));
2471 mode = SelectMode::Word(start..end);
2472 auto_scroll = true;
2473 }
2474 3 => {
2475 let position = display_map
2476 .clip_point(position, Bias::Left)
2477 .to_point(&display_map);
2478 let line_start = display_map.prev_line_boundary(position).0;
2479 let next_line_start = buffer.clip_point(
2480 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2481 Bias::Left,
2482 );
2483 start = buffer.anchor_before(line_start);
2484 end = buffer.anchor_before(next_line_start);
2485 mode = SelectMode::Line(start..end);
2486 auto_scroll = true;
2487 }
2488 _ => {
2489 start = buffer.anchor_before(0);
2490 end = buffer.anchor_before(buffer.len());
2491 mode = SelectMode::All;
2492 auto_scroll = false;
2493 }
2494 }
2495 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2496
2497 let point_to_delete: Option<usize> = {
2498 let selected_points: Vec<Selection<Point>> =
2499 self.selections.disjoint_in_range(start..end, cx);
2500
2501 if !add || click_count > 1 {
2502 None
2503 } else if !selected_points.is_empty() {
2504 Some(selected_points[0].id)
2505 } else {
2506 let clicked_point_already_selected =
2507 self.selections.disjoint.iter().find(|selection| {
2508 selection.start.to_point(buffer) == start.to_point(buffer)
2509 || selection.end.to_point(buffer) == end.to_point(buffer)
2510 });
2511
2512 clicked_point_already_selected.map(|selection| selection.id)
2513 }
2514 };
2515
2516 let selections_count = self.selections.count();
2517
2518 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2519 if let Some(point_to_delete) = point_to_delete {
2520 s.delete(point_to_delete);
2521
2522 if selections_count == 1 {
2523 s.set_pending_anchor_range(start..end, mode);
2524 }
2525 } else {
2526 if !add {
2527 s.clear_disjoint();
2528 } else if click_count > 1 {
2529 s.delete(newest_selection.id)
2530 }
2531
2532 s.set_pending_anchor_range(start..end, mode);
2533 }
2534 });
2535 }
2536
2537 fn begin_columnar_selection(
2538 &mut self,
2539 position: DisplayPoint,
2540 goal_column: u32,
2541 reset: bool,
2542 window: &mut Window,
2543 cx: &mut Context<Self>,
2544 ) {
2545 if !self.focus_handle.is_focused(window) {
2546 self.last_focused_descendant = None;
2547 window.focus(&self.focus_handle);
2548 }
2549
2550 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2551
2552 if reset {
2553 let pointer_position = display_map
2554 .buffer_snapshot
2555 .anchor_before(position.to_point(&display_map));
2556
2557 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2558 s.clear_disjoint();
2559 s.set_pending_anchor_range(
2560 pointer_position..pointer_position,
2561 SelectMode::Character,
2562 );
2563 });
2564 }
2565
2566 let tail = self.selections.newest::<Point>(cx).tail();
2567 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2568
2569 if !reset {
2570 self.select_columns(
2571 tail.to_display_point(&display_map),
2572 position,
2573 goal_column,
2574 &display_map,
2575 window,
2576 cx,
2577 );
2578 }
2579 }
2580
2581 fn update_selection(
2582 &mut self,
2583 position: DisplayPoint,
2584 goal_column: u32,
2585 scroll_delta: gpui::Point<f32>,
2586 window: &mut Window,
2587 cx: &mut Context<Self>,
2588 ) {
2589 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2590
2591 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2592 let tail = tail.to_display_point(&display_map);
2593 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2594 } else if let Some(mut pending) = self.selections.pending_anchor() {
2595 let buffer = self.buffer.read(cx).snapshot(cx);
2596 let head;
2597 let tail;
2598 let mode = self.selections.pending_mode().unwrap();
2599 match &mode {
2600 SelectMode::Character => {
2601 head = position.to_point(&display_map);
2602 tail = pending.tail().to_point(&buffer);
2603 }
2604 SelectMode::Word(original_range) => {
2605 let original_display_range = original_range.start.to_display_point(&display_map)
2606 ..original_range.end.to_display_point(&display_map);
2607 let original_buffer_range = original_display_range.start.to_point(&display_map)
2608 ..original_display_range.end.to_point(&display_map);
2609 if movement::is_inside_word(&display_map, position)
2610 || original_display_range.contains(&position)
2611 {
2612 let word_range = movement::surrounding_word(&display_map, position);
2613 if word_range.start < original_display_range.start {
2614 head = word_range.start.to_point(&display_map);
2615 } else {
2616 head = word_range.end.to_point(&display_map);
2617 }
2618 } else {
2619 head = position.to_point(&display_map);
2620 }
2621
2622 if head <= original_buffer_range.start {
2623 tail = original_buffer_range.end;
2624 } else {
2625 tail = original_buffer_range.start;
2626 }
2627 }
2628 SelectMode::Line(original_range) => {
2629 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2630
2631 let position = display_map
2632 .clip_point(position, Bias::Left)
2633 .to_point(&display_map);
2634 let line_start = display_map.prev_line_boundary(position).0;
2635 let next_line_start = buffer.clip_point(
2636 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2637 Bias::Left,
2638 );
2639
2640 if line_start < original_range.start {
2641 head = line_start
2642 } else {
2643 head = next_line_start
2644 }
2645
2646 if head <= original_range.start {
2647 tail = original_range.end;
2648 } else {
2649 tail = original_range.start;
2650 }
2651 }
2652 SelectMode::All => {
2653 return;
2654 }
2655 };
2656
2657 if head < tail {
2658 pending.start = buffer.anchor_before(head);
2659 pending.end = buffer.anchor_before(tail);
2660 pending.reversed = true;
2661 } else {
2662 pending.start = buffer.anchor_before(tail);
2663 pending.end = buffer.anchor_before(head);
2664 pending.reversed = false;
2665 }
2666
2667 self.change_selections(None, window, cx, |s| {
2668 s.set_pending(pending, mode);
2669 });
2670 } else {
2671 log::error!("update_selection dispatched with no pending selection");
2672 return;
2673 }
2674
2675 self.apply_scroll_delta(scroll_delta, window, cx);
2676 cx.notify();
2677 }
2678
2679 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2680 self.columnar_selection_tail.take();
2681 if self.selections.pending_anchor().is_some() {
2682 let selections = self.selections.all::<usize>(cx);
2683 self.change_selections(None, window, cx, |s| {
2684 s.select(selections);
2685 s.clear_pending();
2686 });
2687 }
2688 }
2689
2690 fn select_columns(
2691 &mut self,
2692 tail: DisplayPoint,
2693 head: DisplayPoint,
2694 goal_column: u32,
2695 display_map: &DisplaySnapshot,
2696 window: &mut Window,
2697 cx: &mut Context<Self>,
2698 ) {
2699 let start_row = cmp::min(tail.row(), head.row());
2700 let end_row = cmp::max(tail.row(), head.row());
2701 let start_column = cmp::min(tail.column(), goal_column);
2702 let end_column = cmp::max(tail.column(), goal_column);
2703 let reversed = start_column < tail.column();
2704
2705 let selection_ranges = (start_row.0..=end_row.0)
2706 .map(DisplayRow)
2707 .filter_map(|row| {
2708 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2709 let start = display_map
2710 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2711 .to_point(display_map);
2712 let end = display_map
2713 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2714 .to_point(display_map);
2715 if reversed {
2716 Some(end..start)
2717 } else {
2718 Some(start..end)
2719 }
2720 } else {
2721 None
2722 }
2723 })
2724 .collect::<Vec<_>>();
2725
2726 self.change_selections(None, window, cx, |s| {
2727 s.select_ranges(selection_ranges);
2728 });
2729 cx.notify();
2730 }
2731
2732 pub fn has_pending_nonempty_selection(&self) -> bool {
2733 let pending_nonempty_selection = match self.selections.pending_anchor() {
2734 Some(Selection { start, end, .. }) => start != end,
2735 None => false,
2736 };
2737
2738 pending_nonempty_selection
2739 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2740 }
2741
2742 pub fn has_pending_selection(&self) -> bool {
2743 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2744 }
2745
2746 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2747 self.selection_mark_mode = false;
2748
2749 if self.clear_expanded_diff_hunks(cx) {
2750 cx.notify();
2751 return;
2752 }
2753 if self.dismiss_menus_and_popups(true, window, cx) {
2754 return;
2755 }
2756
2757 if self.mode == EditorMode::Full
2758 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2759 {
2760 return;
2761 }
2762
2763 cx.propagate();
2764 }
2765
2766 pub fn dismiss_menus_and_popups(
2767 &mut self,
2768 is_user_requested: bool,
2769 window: &mut Window,
2770 cx: &mut Context<Self>,
2771 ) -> bool {
2772 if self.take_rename(false, window, cx).is_some() {
2773 return true;
2774 }
2775
2776 if hide_hover(self, cx) {
2777 return true;
2778 }
2779
2780 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2781 return true;
2782 }
2783
2784 if self.hide_context_menu(window, cx).is_some() {
2785 return true;
2786 }
2787
2788 if self.mouse_context_menu.take().is_some() {
2789 return true;
2790 }
2791
2792 if is_user_requested && self.discard_inline_completion(true, cx) {
2793 return true;
2794 }
2795
2796 if self.snippet_stack.pop().is_some() {
2797 return true;
2798 }
2799
2800 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2801 self.dismiss_diagnostics(cx);
2802 return true;
2803 }
2804
2805 false
2806 }
2807
2808 fn linked_editing_ranges_for(
2809 &self,
2810 selection: Range<text::Anchor>,
2811 cx: &App,
2812 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2813 if self.linked_edit_ranges.is_empty() {
2814 return None;
2815 }
2816 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2817 selection.end.buffer_id.and_then(|end_buffer_id| {
2818 if selection.start.buffer_id != Some(end_buffer_id) {
2819 return None;
2820 }
2821 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2822 let snapshot = buffer.read(cx).snapshot();
2823 self.linked_edit_ranges
2824 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2825 .map(|ranges| (ranges, snapshot, buffer))
2826 })?;
2827 use text::ToOffset as TO;
2828 // find offset from the start of current range to current cursor position
2829 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2830
2831 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2832 let start_difference = start_offset - start_byte_offset;
2833 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2834 let end_difference = end_offset - start_byte_offset;
2835 // Current range has associated linked ranges.
2836 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2837 for range in linked_ranges.iter() {
2838 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2839 let end_offset = start_offset + end_difference;
2840 let start_offset = start_offset + start_difference;
2841 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2842 continue;
2843 }
2844 if self.selections.disjoint_anchor_ranges().any(|s| {
2845 if s.start.buffer_id != selection.start.buffer_id
2846 || s.end.buffer_id != selection.end.buffer_id
2847 {
2848 return false;
2849 }
2850 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2851 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2852 }) {
2853 continue;
2854 }
2855 let start = buffer_snapshot.anchor_after(start_offset);
2856 let end = buffer_snapshot.anchor_after(end_offset);
2857 linked_edits
2858 .entry(buffer.clone())
2859 .or_default()
2860 .push(start..end);
2861 }
2862 Some(linked_edits)
2863 }
2864
2865 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2866 let text: Arc<str> = text.into();
2867
2868 if self.read_only(cx) {
2869 return;
2870 }
2871
2872 let selections = self.selections.all_adjusted(cx);
2873 let mut bracket_inserted = false;
2874 let mut edits = Vec::new();
2875 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2876 let mut new_selections = Vec::with_capacity(selections.len());
2877 let mut new_autoclose_regions = Vec::new();
2878 let snapshot = self.buffer.read(cx).read(cx);
2879
2880 for (selection, autoclose_region) in
2881 self.selections_with_autoclose_regions(selections, &snapshot)
2882 {
2883 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2884 // Determine if the inserted text matches the opening or closing
2885 // bracket of any of this language's bracket pairs.
2886 let mut bracket_pair = None;
2887 let mut is_bracket_pair_start = false;
2888 let mut is_bracket_pair_end = false;
2889 if !text.is_empty() {
2890 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2891 // and they are removing the character that triggered IME popup.
2892 for (pair, enabled) in scope.brackets() {
2893 if !pair.close && !pair.surround {
2894 continue;
2895 }
2896
2897 if enabled && pair.start.ends_with(text.as_ref()) {
2898 let prefix_len = pair.start.len() - text.len();
2899 let preceding_text_matches_prefix = prefix_len == 0
2900 || (selection.start.column >= (prefix_len as u32)
2901 && snapshot.contains_str_at(
2902 Point::new(
2903 selection.start.row,
2904 selection.start.column - (prefix_len as u32),
2905 ),
2906 &pair.start[..prefix_len],
2907 ));
2908 if preceding_text_matches_prefix {
2909 bracket_pair = Some(pair.clone());
2910 is_bracket_pair_start = true;
2911 break;
2912 }
2913 }
2914 if pair.end.as_str() == text.as_ref() {
2915 bracket_pair = Some(pair.clone());
2916 is_bracket_pair_end = true;
2917 break;
2918 }
2919 }
2920 }
2921
2922 if let Some(bracket_pair) = bracket_pair {
2923 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2924 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2925 let auto_surround =
2926 self.use_auto_surround && snapshot_settings.use_auto_surround;
2927 if selection.is_empty() {
2928 if is_bracket_pair_start {
2929 // If the inserted text is a suffix of an opening bracket and the
2930 // selection is preceded by the rest of the opening bracket, then
2931 // insert the closing bracket.
2932 let following_text_allows_autoclose = snapshot
2933 .chars_at(selection.start)
2934 .next()
2935 .map_or(true, |c| scope.should_autoclose_before(c));
2936
2937 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2938 && bracket_pair.start.len() == 1
2939 {
2940 let target = bracket_pair.start.chars().next().unwrap();
2941 let current_line_count = snapshot
2942 .reversed_chars_at(selection.start)
2943 .take_while(|&c| c != '\n')
2944 .filter(|&c| c == target)
2945 .count();
2946 current_line_count % 2 == 1
2947 } else {
2948 false
2949 };
2950
2951 if autoclose
2952 && bracket_pair.close
2953 && following_text_allows_autoclose
2954 && !is_closing_quote
2955 {
2956 let anchor = snapshot.anchor_before(selection.end);
2957 new_selections.push((selection.map(|_| anchor), text.len()));
2958 new_autoclose_regions.push((
2959 anchor,
2960 text.len(),
2961 selection.id,
2962 bracket_pair.clone(),
2963 ));
2964 edits.push((
2965 selection.range(),
2966 format!("{}{}", text, bracket_pair.end).into(),
2967 ));
2968 bracket_inserted = true;
2969 continue;
2970 }
2971 }
2972
2973 if let Some(region) = autoclose_region {
2974 // If the selection is followed by an auto-inserted closing bracket,
2975 // then don't insert that closing bracket again; just move the selection
2976 // past the closing bracket.
2977 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2978 && text.as_ref() == region.pair.end.as_str();
2979 if should_skip {
2980 let anchor = snapshot.anchor_after(selection.end);
2981 new_selections
2982 .push((selection.map(|_| anchor), region.pair.end.len()));
2983 continue;
2984 }
2985 }
2986
2987 let always_treat_brackets_as_autoclosed = snapshot
2988 .language_settings_at(selection.start, cx)
2989 .always_treat_brackets_as_autoclosed;
2990 if always_treat_brackets_as_autoclosed
2991 && is_bracket_pair_end
2992 && snapshot.contains_str_at(selection.end, text.as_ref())
2993 {
2994 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2995 // and the inserted text is a closing bracket and the selection is followed
2996 // by the closing bracket then move the selection past the closing bracket.
2997 let anchor = snapshot.anchor_after(selection.end);
2998 new_selections.push((selection.map(|_| anchor), text.len()));
2999 continue;
3000 }
3001 }
3002 // If an opening bracket is 1 character long and is typed while
3003 // text is selected, then surround that text with the bracket pair.
3004 else if auto_surround
3005 && bracket_pair.surround
3006 && is_bracket_pair_start
3007 && bracket_pair.start.chars().count() == 1
3008 {
3009 edits.push((selection.start..selection.start, text.clone()));
3010 edits.push((
3011 selection.end..selection.end,
3012 bracket_pair.end.as_str().into(),
3013 ));
3014 bracket_inserted = true;
3015 new_selections.push((
3016 Selection {
3017 id: selection.id,
3018 start: snapshot.anchor_after(selection.start),
3019 end: snapshot.anchor_before(selection.end),
3020 reversed: selection.reversed,
3021 goal: selection.goal,
3022 },
3023 0,
3024 ));
3025 continue;
3026 }
3027 }
3028 }
3029
3030 if self.auto_replace_emoji_shortcode
3031 && selection.is_empty()
3032 && text.as_ref().ends_with(':')
3033 {
3034 if let Some(possible_emoji_short_code) =
3035 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3036 {
3037 if !possible_emoji_short_code.is_empty() {
3038 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3039 let emoji_shortcode_start = Point::new(
3040 selection.start.row,
3041 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3042 );
3043
3044 // Remove shortcode from buffer
3045 edits.push((
3046 emoji_shortcode_start..selection.start,
3047 "".to_string().into(),
3048 ));
3049 new_selections.push((
3050 Selection {
3051 id: selection.id,
3052 start: snapshot.anchor_after(emoji_shortcode_start),
3053 end: snapshot.anchor_before(selection.start),
3054 reversed: selection.reversed,
3055 goal: selection.goal,
3056 },
3057 0,
3058 ));
3059
3060 // Insert emoji
3061 let selection_start_anchor = snapshot.anchor_after(selection.start);
3062 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3063 edits.push((selection.start..selection.end, emoji.to_string().into()));
3064
3065 continue;
3066 }
3067 }
3068 }
3069 }
3070
3071 // If not handling any auto-close operation, then just replace the selected
3072 // text with the given input and move the selection to the end of the
3073 // newly inserted text.
3074 let anchor = snapshot.anchor_after(selection.end);
3075 if !self.linked_edit_ranges.is_empty() {
3076 let start_anchor = snapshot.anchor_before(selection.start);
3077
3078 let is_word_char = text.chars().next().map_or(true, |char| {
3079 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3080 classifier.is_word(char)
3081 });
3082
3083 if is_word_char {
3084 if let Some(ranges) = self
3085 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3086 {
3087 for (buffer, edits) in ranges {
3088 linked_edits
3089 .entry(buffer.clone())
3090 .or_default()
3091 .extend(edits.into_iter().map(|range| (range, text.clone())));
3092 }
3093 }
3094 }
3095 }
3096
3097 new_selections.push((selection.map(|_| anchor), 0));
3098 edits.push((selection.start..selection.end, text.clone()));
3099 }
3100
3101 drop(snapshot);
3102
3103 self.transact(window, cx, |this, window, cx| {
3104 let initial_buffer_versions =
3105 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3106
3107 this.buffer.update(cx, |buffer, cx| {
3108 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3109 });
3110 for (buffer, edits) in linked_edits {
3111 buffer.update(cx, |buffer, cx| {
3112 let snapshot = buffer.snapshot();
3113 let edits = edits
3114 .into_iter()
3115 .map(|(range, text)| {
3116 use text::ToPoint as TP;
3117 let end_point = TP::to_point(&range.end, &snapshot);
3118 let start_point = TP::to_point(&range.start, &snapshot);
3119 (start_point..end_point, text)
3120 })
3121 .sorted_by_key(|(range, _)| range.start)
3122 .collect::<Vec<_>>();
3123 buffer.edit(edits, None, cx);
3124 })
3125 }
3126 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3127 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3128 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3129 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3130 .zip(new_selection_deltas)
3131 .map(|(selection, delta)| Selection {
3132 id: selection.id,
3133 start: selection.start + delta,
3134 end: selection.end + delta,
3135 reversed: selection.reversed,
3136 goal: SelectionGoal::None,
3137 })
3138 .collect::<Vec<_>>();
3139
3140 let mut i = 0;
3141 for (position, delta, selection_id, pair) in new_autoclose_regions {
3142 let position = position.to_offset(&map.buffer_snapshot) + delta;
3143 let start = map.buffer_snapshot.anchor_before(position);
3144 let end = map.buffer_snapshot.anchor_after(position);
3145 while let Some(existing_state) = this.autoclose_regions.get(i) {
3146 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3147 Ordering::Less => i += 1,
3148 Ordering::Greater => break,
3149 Ordering::Equal => {
3150 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3151 Ordering::Less => i += 1,
3152 Ordering::Equal => break,
3153 Ordering::Greater => break,
3154 }
3155 }
3156 }
3157 }
3158 this.autoclose_regions.insert(
3159 i,
3160 AutocloseRegion {
3161 selection_id,
3162 range: start..end,
3163 pair,
3164 },
3165 );
3166 }
3167
3168 let had_active_inline_completion = this.has_active_inline_completion();
3169 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3170 s.select(new_selections)
3171 });
3172
3173 if !bracket_inserted {
3174 if let Some(on_type_format_task) =
3175 this.trigger_on_type_formatting(text.to_string(), window, cx)
3176 {
3177 on_type_format_task.detach_and_log_err(cx);
3178 }
3179 }
3180
3181 let editor_settings = EditorSettings::get_global(cx);
3182 if bracket_inserted
3183 && (editor_settings.auto_signature_help
3184 || editor_settings.show_signature_help_after_edits)
3185 {
3186 this.show_signature_help(&ShowSignatureHelp, window, cx);
3187 }
3188
3189 let trigger_in_words =
3190 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3191 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3192 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3193 this.refresh_inline_completion(true, false, window, cx);
3194 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3195 });
3196 }
3197
3198 fn find_possible_emoji_shortcode_at_position(
3199 snapshot: &MultiBufferSnapshot,
3200 position: Point,
3201 ) -> Option<String> {
3202 let mut chars = Vec::new();
3203 let mut found_colon = false;
3204 for char in snapshot.reversed_chars_at(position).take(100) {
3205 // Found a possible emoji shortcode in the middle of the buffer
3206 if found_colon {
3207 if char.is_whitespace() {
3208 chars.reverse();
3209 return Some(chars.iter().collect());
3210 }
3211 // If the previous character is not a whitespace, we are in the middle of a word
3212 // and we only want to complete the shortcode if the word is made up of other emojis
3213 let mut containing_word = String::new();
3214 for ch in snapshot
3215 .reversed_chars_at(position)
3216 .skip(chars.len() + 1)
3217 .take(100)
3218 {
3219 if ch.is_whitespace() {
3220 break;
3221 }
3222 containing_word.push(ch);
3223 }
3224 let containing_word = containing_word.chars().rev().collect::<String>();
3225 if util::word_consists_of_emojis(containing_word.as_str()) {
3226 chars.reverse();
3227 return Some(chars.iter().collect());
3228 }
3229 }
3230
3231 if char.is_whitespace() || !char.is_ascii() {
3232 return None;
3233 }
3234 if char == ':' {
3235 found_colon = true;
3236 } else {
3237 chars.push(char);
3238 }
3239 }
3240 // Found a possible emoji shortcode at the beginning of the buffer
3241 chars.reverse();
3242 Some(chars.iter().collect())
3243 }
3244
3245 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3246 self.transact(window, cx, |this, window, cx| {
3247 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3248 let selections = this.selections.all::<usize>(cx);
3249 let multi_buffer = this.buffer.read(cx);
3250 let buffer = multi_buffer.snapshot(cx);
3251 selections
3252 .iter()
3253 .map(|selection| {
3254 let start_point = selection.start.to_point(&buffer);
3255 let mut indent =
3256 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3257 indent.len = cmp::min(indent.len, start_point.column);
3258 let start = selection.start;
3259 let end = selection.end;
3260 let selection_is_empty = start == end;
3261 let language_scope = buffer.language_scope_at(start);
3262 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3263 &language_scope
3264 {
3265 let insert_extra_newline =
3266 insert_extra_newline_brackets(&buffer, start..end, language)
3267 || insert_extra_newline_tree_sitter(&buffer, start..end);
3268
3269 // Comment extension on newline is allowed only for cursor selections
3270 let comment_delimiter = maybe!({
3271 if !selection_is_empty {
3272 return None;
3273 }
3274
3275 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3276 return None;
3277 }
3278
3279 let delimiters = language.line_comment_prefixes();
3280 let max_len_of_delimiter =
3281 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3282 let (snapshot, range) =
3283 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3284
3285 let mut index_of_first_non_whitespace = 0;
3286 let comment_candidate = snapshot
3287 .chars_for_range(range)
3288 .skip_while(|c| {
3289 let should_skip = c.is_whitespace();
3290 if should_skip {
3291 index_of_first_non_whitespace += 1;
3292 }
3293 should_skip
3294 })
3295 .take(max_len_of_delimiter)
3296 .collect::<String>();
3297 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3298 comment_candidate.starts_with(comment_prefix.as_ref())
3299 })?;
3300 let cursor_is_placed_after_comment_marker =
3301 index_of_first_non_whitespace + comment_prefix.len()
3302 <= start_point.column as usize;
3303 if cursor_is_placed_after_comment_marker {
3304 Some(comment_prefix.clone())
3305 } else {
3306 None
3307 }
3308 });
3309 (comment_delimiter, insert_extra_newline)
3310 } else {
3311 (None, false)
3312 };
3313
3314 let capacity_for_delimiter = comment_delimiter
3315 .as_deref()
3316 .map(str::len)
3317 .unwrap_or_default();
3318 let mut new_text =
3319 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3320 new_text.push('\n');
3321 new_text.extend(indent.chars());
3322 if let Some(delimiter) = &comment_delimiter {
3323 new_text.push_str(delimiter);
3324 }
3325 if insert_extra_newline {
3326 new_text = new_text.repeat(2);
3327 }
3328
3329 let anchor = buffer.anchor_after(end);
3330 let new_selection = selection.map(|_| anchor);
3331 (
3332 (start..end, new_text),
3333 (insert_extra_newline, new_selection),
3334 )
3335 })
3336 .unzip()
3337 };
3338
3339 this.edit_with_autoindent(edits, cx);
3340 let buffer = this.buffer.read(cx).snapshot(cx);
3341 let new_selections = selection_fixup_info
3342 .into_iter()
3343 .map(|(extra_newline_inserted, new_selection)| {
3344 let mut cursor = new_selection.end.to_point(&buffer);
3345 if extra_newline_inserted {
3346 cursor.row -= 1;
3347 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3348 }
3349 new_selection.map(|_| cursor)
3350 })
3351 .collect();
3352
3353 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3354 s.select(new_selections)
3355 });
3356 this.refresh_inline_completion(true, false, window, cx);
3357 });
3358 }
3359
3360 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3361 let buffer = self.buffer.read(cx);
3362 let snapshot = buffer.snapshot(cx);
3363
3364 let mut edits = Vec::new();
3365 let mut rows = Vec::new();
3366
3367 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3368 let cursor = selection.head();
3369 let row = cursor.row;
3370
3371 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3372
3373 let newline = "\n".to_string();
3374 edits.push((start_of_line..start_of_line, newline));
3375
3376 rows.push(row + rows_inserted as u32);
3377 }
3378
3379 self.transact(window, cx, |editor, window, cx| {
3380 editor.edit(edits, cx);
3381
3382 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3383 let mut index = 0;
3384 s.move_cursors_with(|map, _, _| {
3385 let row = rows[index];
3386 index += 1;
3387
3388 let point = Point::new(row, 0);
3389 let boundary = map.next_line_boundary(point).1;
3390 let clipped = map.clip_point(boundary, Bias::Left);
3391
3392 (clipped, SelectionGoal::None)
3393 });
3394 });
3395
3396 let mut indent_edits = Vec::new();
3397 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3398 for row in rows {
3399 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3400 for (row, indent) in indents {
3401 if indent.len == 0 {
3402 continue;
3403 }
3404
3405 let text = match indent.kind {
3406 IndentKind::Space => " ".repeat(indent.len as usize),
3407 IndentKind::Tab => "\t".repeat(indent.len as usize),
3408 };
3409 let point = Point::new(row.0, 0);
3410 indent_edits.push((point..point, text));
3411 }
3412 }
3413 editor.edit(indent_edits, cx);
3414 });
3415 }
3416
3417 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3418 let buffer = self.buffer.read(cx);
3419 let snapshot = buffer.snapshot(cx);
3420
3421 let mut edits = Vec::new();
3422 let mut rows = Vec::new();
3423 let mut rows_inserted = 0;
3424
3425 for selection in self.selections.all_adjusted(cx) {
3426 let cursor = selection.head();
3427 let row = cursor.row;
3428
3429 let point = Point::new(row + 1, 0);
3430 let start_of_line = snapshot.clip_point(point, Bias::Left);
3431
3432 let newline = "\n".to_string();
3433 edits.push((start_of_line..start_of_line, newline));
3434
3435 rows_inserted += 1;
3436 rows.push(row + rows_inserted);
3437 }
3438
3439 self.transact(window, cx, |editor, window, cx| {
3440 editor.edit(edits, cx);
3441
3442 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3443 let mut index = 0;
3444 s.move_cursors_with(|map, _, _| {
3445 let row = rows[index];
3446 index += 1;
3447
3448 let point = Point::new(row, 0);
3449 let boundary = map.next_line_boundary(point).1;
3450 let clipped = map.clip_point(boundary, Bias::Left);
3451
3452 (clipped, SelectionGoal::None)
3453 });
3454 });
3455
3456 let mut indent_edits = Vec::new();
3457 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3458 for row in rows {
3459 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3460 for (row, indent) in indents {
3461 if indent.len == 0 {
3462 continue;
3463 }
3464
3465 let text = match indent.kind {
3466 IndentKind::Space => " ".repeat(indent.len as usize),
3467 IndentKind::Tab => "\t".repeat(indent.len as usize),
3468 };
3469 let point = Point::new(row.0, 0);
3470 indent_edits.push((point..point, text));
3471 }
3472 }
3473 editor.edit(indent_edits, cx);
3474 });
3475 }
3476
3477 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3478 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3479 original_indent_columns: Vec::new(),
3480 });
3481 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3482 }
3483
3484 fn insert_with_autoindent_mode(
3485 &mut self,
3486 text: &str,
3487 autoindent_mode: Option<AutoindentMode>,
3488 window: &mut Window,
3489 cx: &mut Context<Self>,
3490 ) {
3491 if self.read_only(cx) {
3492 return;
3493 }
3494
3495 let text: Arc<str> = text.into();
3496 self.transact(window, cx, |this, window, cx| {
3497 let old_selections = this.selections.all_adjusted(cx);
3498 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3499 let anchors = {
3500 let snapshot = buffer.read(cx);
3501 old_selections
3502 .iter()
3503 .map(|s| {
3504 let anchor = snapshot.anchor_after(s.head());
3505 s.map(|_| anchor)
3506 })
3507 .collect::<Vec<_>>()
3508 };
3509 buffer.edit(
3510 old_selections
3511 .iter()
3512 .map(|s| (s.start..s.end, text.clone())),
3513 autoindent_mode,
3514 cx,
3515 );
3516 anchors
3517 });
3518
3519 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3520 s.select_anchors(selection_anchors);
3521 });
3522
3523 cx.notify();
3524 });
3525 }
3526
3527 fn trigger_completion_on_input(
3528 &mut self,
3529 text: &str,
3530 trigger_in_words: bool,
3531 window: &mut Window,
3532 cx: &mut Context<Self>,
3533 ) {
3534 if self.is_completion_trigger(text, trigger_in_words, cx) {
3535 self.show_completions(
3536 &ShowCompletions {
3537 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3538 },
3539 window,
3540 cx,
3541 );
3542 } else {
3543 self.hide_context_menu(window, cx);
3544 }
3545 }
3546
3547 fn is_completion_trigger(
3548 &self,
3549 text: &str,
3550 trigger_in_words: bool,
3551 cx: &mut Context<Self>,
3552 ) -> bool {
3553 let position = self.selections.newest_anchor().head();
3554 let multibuffer = self.buffer.read(cx);
3555 let Some(buffer) = position
3556 .buffer_id
3557 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3558 else {
3559 return false;
3560 };
3561
3562 if let Some(completion_provider) = &self.completion_provider {
3563 completion_provider.is_completion_trigger(
3564 &buffer,
3565 position.text_anchor,
3566 text,
3567 trigger_in_words,
3568 cx,
3569 )
3570 } else {
3571 false
3572 }
3573 }
3574
3575 /// If any empty selections is touching the start of its innermost containing autoclose
3576 /// region, expand it to select the brackets.
3577 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3578 let selections = self.selections.all::<usize>(cx);
3579 let buffer = self.buffer.read(cx).read(cx);
3580 let new_selections = self
3581 .selections_with_autoclose_regions(selections, &buffer)
3582 .map(|(mut selection, region)| {
3583 if !selection.is_empty() {
3584 return selection;
3585 }
3586
3587 if let Some(region) = region {
3588 let mut range = region.range.to_offset(&buffer);
3589 if selection.start == range.start && range.start >= region.pair.start.len() {
3590 range.start -= region.pair.start.len();
3591 if buffer.contains_str_at(range.start, ®ion.pair.start)
3592 && buffer.contains_str_at(range.end, ®ion.pair.end)
3593 {
3594 range.end += region.pair.end.len();
3595 selection.start = range.start;
3596 selection.end = range.end;
3597
3598 return selection;
3599 }
3600 }
3601 }
3602
3603 let always_treat_brackets_as_autoclosed = buffer
3604 .language_settings_at(selection.start, cx)
3605 .always_treat_brackets_as_autoclosed;
3606
3607 if !always_treat_brackets_as_autoclosed {
3608 return selection;
3609 }
3610
3611 if let Some(scope) = buffer.language_scope_at(selection.start) {
3612 for (pair, enabled) in scope.brackets() {
3613 if !enabled || !pair.close {
3614 continue;
3615 }
3616
3617 if buffer.contains_str_at(selection.start, &pair.end) {
3618 let pair_start_len = pair.start.len();
3619 if buffer.contains_str_at(
3620 selection.start.saturating_sub(pair_start_len),
3621 &pair.start,
3622 ) {
3623 selection.start -= pair_start_len;
3624 selection.end += pair.end.len();
3625
3626 return selection;
3627 }
3628 }
3629 }
3630 }
3631
3632 selection
3633 })
3634 .collect();
3635
3636 drop(buffer);
3637 self.change_selections(None, window, cx, |selections| {
3638 selections.select(new_selections)
3639 });
3640 }
3641
3642 /// Iterate the given selections, and for each one, find the smallest surrounding
3643 /// autoclose region. This uses the ordering of the selections and the autoclose
3644 /// regions to avoid repeated comparisons.
3645 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3646 &'a self,
3647 selections: impl IntoIterator<Item = Selection<D>>,
3648 buffer: &'a MultiBufferSnapshot,
3649 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3650 let mut i = 0;
3651 let mut regions = self.autoclose_regions.as_slice();
3652 selections.into_iter().map(move |selection| {
3653 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3654
3655 let mut enclosing = None;
3656 while let Some(pair_state) = regions.get(i) {
3657 if pair_state.range.end.to_offset(buffer) < range.start {
3658 regions = ®ions[i + 1..];
3659 i = 0;
3660 } else if pair_state.range.start.to_offset(buffer) > range.end {
3661 break;
3662 } else {
3663 if pair_state.selection_id == selection.id {
3664 enclosing = Some(pair_state);
3665 }
3666 i += 1;
3667 }
3668 }
3669
3670 (selection, enclosing)
3671 })
3672 }
3673
3674 /// Remove any autoclose regions that no longer contain their selection.
3675 fn invalidate_autoclose_regions(
3676 &mut self,
3677 mut selections: &[Selection<Anchor>],
3678 buffer: &MultiBufferSnapshot,
3679 ) {
3680 self.autoclose_regions.retain(|state| {
3681 let mut i = 0;
3682 while let Some(selection) = selections.get(i) {
3683 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3684 selections = &selections[1..];
3685 continue;
3686 }
3687 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3688 break;
3689 }
3690 if selection.id == state.selection_id {
3691 return true;
3692 } else {
3693 i += 1;
3694 }
3695 }
3696 false
3697 });
3698 }
3699
3700 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3701 let offset = position.to_offset(buffer);
3702 let (word_range, kind) = buffer.surrounding_word(offset, true);
3703 if offset > word_range.start && kind == Some(CharKind::Word) {
3704 Some(
3705 buffer
3706 .text_for_range(word_range.start..offset)
3707 .collect::<String>(),
3708 )
3709 } else {
3710 None
3711 }
3712 }
3713
3714 pub fn toggle_inlay_hints(
3715 &mut self,
3716 _: &ToggleInlayHints,
3717 _: &mut Window,
3718 cx: &mut Context<Self>,
3719 ) {
3720 self.refresh_inlay_hints(
3721 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3722 cx,
3723 );
3724 }
3725
3726 pub fn inlay_hints_enabled(&self) -> bool {
3727 self.inlay_hint_cache.enabled
3728 }
3729
3730 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3731 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3732 return;
3733 }
3734
3735 let reason_description = reason.description();
3736 let ignore_debounce = matches!(
3737 reason,
3738 InlayHintRefreshReason::SettingsChange(_)
3739 | InlayHintRefreshReason::Toggle(_)
3740 | InlayHintRefreshReason::ExcerptsRemoved(_)
3741 | InlayHintRefreshReason::ModifiersChanged(_)
3742 );
3743 let (invalidate_cache, required_languages) = match reason {
3744 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3745 match self.inlay_hint_cache.modifiers_override(enabled) {
3746 Some(enabled) => {
3747 if enabled {
3748 (InvalidationStrategy::RefreshRequested, None)
3749 } else {
3750 self.splice_inlays(
3751 &self
3752 .visible_inlay_hints(cx)
3753 .iter()
3754 .map(|inlay| inlay.id)
3755 .collect::<Vec<InlayId>>(),
3756 Vec::new(),
3757 cx,
3758 );
3759 return;
3760 }
3761 }
3762 None => return,
3763 }
3764 }
3765 InlayHintRefreshReason::Toggle(enabled) => {
3766 if self.inlay_hint_cache.toggle(enabled) {
3767 if enabled {
3768 (InvalidationStrategy::RefreshRequested, None)
3769 } else {
3770 self.splice_inlays(
3771 &self
3772 .visible_inlay_hints(cx)
3773 .iter()
3774 .map(|inlay| inlay.id)
3775 .collect::<Vec<InlayId>>(),
3776 Vec::new(),
3777 cx,
3778 );
3779 return;
3780 }
3781 } else {
3782 return;
3783 }
3784 }
3785 InlayHintRefreshReason::SettingsChange(new_settings) => {
3786 match self.inlay_hint_cache.update_settings(
3787 &self.buffer,
3788 new_settings,
3789 self.visible_inlay_hints(cx),
3790 cx,
3791 ) {
3792 ControlFlow::Break(Some(InlaySplice {
3793 to_remove,
3794 to_insert,
3795 })) => {
3796 self.splice_inlays(&to_remove, to_insert, cx);
3797 return;
3798 }
3799 ControlFlow::Break(None) => return,
3800 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3801 }
3802 }
3803 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3804 if let Some(InlaySplice {
3805 to_remove,
3806 to_insert,
3807 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3808 {
3809 self.splice_inlays(&to_remove, to_insert, cx);
3810 }
3811 return;
3812 }
3813 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3814 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3815 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3816 }
3817 InlayHintRefreshReason::RefreshRequested => {
3818 (InvalidationStrategy::RefreshRequested, None)
3819 }
3820 };
3821
3822 if let Some(InlaySplice {
3823 to_remove,
3824 to_insert,
3825 }) = self.inlay_hint_cache.spawn_hint_refresh(
3826 reason_description,
3827 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3828 invalidate_cache,
3829 ignore_debounce,
3830 cx,
3831 ) {
3832 self.splice_inlays(&to_remove, to_insert, cx);
3833 }
3834 }
3835
3836 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3837 self.display_map
3838 .read(cx)
3839 .current_inlays()
3840 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3841 .cloned()
3842 .collect()
3843 }
3844
3845 pub fn excerpts_for_inlay_hints_query(
3846 &self,
3847 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3848 cx: &mut Context<Editor>,
3849 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3850 let Some(project) = self.project.as_ref() else {
3851 return HashMap::default();
3852 };
3853 let project = project.read(cx);
3854 let multi_buffer = self.buffer().read(cx);
3855 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3856 let multi_buffer_visible_start = self
3857 .scroll_manager
3858 .anchor()
3859 .anchor
3860 .to_point(&multi_buffer_snapshot);
3861 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3862 multi_buffer_visible_start
3863 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3864 Bias::Left,
3865 );
3866 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3867 multi_buffer_snapshot
3868 .range_to_buffer_ranges(multi_buffer_visible_range)
3869 .into_iter()
3870 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3871 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3872 let buffer_file = project::File::from_dyn(buffer.file())?;
3873 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3874 let worktree_entry = buffer_worktree
3875 .read(cx)
3876 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3877 if worktree_entry.is_ignored {
3878 return None;
3879 }
3880
3881 let language = buffer.language()?;
3882 if let Some(restrict_to_languages) = restrict_to_languages {
3883 if !restrict_to_languages.contains(language) {
3884 return None;
3885 }
3886 }
3887 Some((
3888 excerpt_id,
3889 (
3890 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3891 buffer.version().clone(),
3892 excerpt_visible_range,
3893 ),
3894 ))
3895 })
3896 .collect()
3897 }
3898
3899 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3900 TextLayoutDetails {
3901 text_system: window.text_system().clone(),
3902 editor_style: self.style.clone().unwrap(),
3903 rem_size: window.rem_size(),
3904 scroll_anchor: self.scroll_manager.anchor(),
3905 visible_rows: self.visible_line_count(),
3906 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3907 }
3908 }
3909
3910 pub fn splice_inlays(
3911 &self,
3912 to_remove: &[InlayId],
3913 to_insert: Vec<Inlay>,
3914 cx: &mut Context<Self>,
3915 ) {
3916 self.display_map.update(cx, |display_map, cx| {
3917 display_map.splice_inlays(to_remove, to_insert, cx)
3918 });
3919 cx.notify();
3920 }
3921
3922 fn trigger_on_type_formatting(
3923 &self,
3924 input: String,
3925 window: &mut Window,
3926 cx: &mut Context<Self>,
3927 ) -> Option<Task<Result<()>>> {
3928 if input.len() != 1 {
3929 return None;
3930 }
3931
3932 let project = self.project.as_ref()?;
3933 let position = self.selections.newest_anchor().head();
3934 let (buffer, buffer_position) = self
3935 .buffer
3936 .read(cx)
3937 .text_anchor_for_position(position, cx)?;
3938
3939 let settings = language_settings::language_settings(
3940 buffer
3941 .read(cx)
3942 .language_at(buffer_position)
3943 .map(|l| l.name()),
3944 buffer.read(cx).file(),
3945 cx,
3946 );
3947 if !settings.use_on_type_format {
3948 return None;
3949 }
3950
3951 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3952 // hence we do LSP request & edit on host side only — add formats to host's history.
3953 let push_to_lsp_host_history = true;
3954 // If this is not the host, append its history with new edits.
3955 let push_to_client_history = project.read(cx).is_via_collab();
3956
3957 let on_type_formatting = project.update(cx, |project, cx| {
3958 project.on_type_format(
3959 buffer.clone(),
3960 buffer_position,
3961 input,
3962 push_to_lsp_host_history,
3963 cx,
3964 )
3965 });
3966 Some(cx.spawn_in(window, |editor, mut cx| async move {
3967 if let Some(transaction) = on_type_formatting.await? {
3968 if push_to_client_history {
3969 buffer
3970 .update(&mut cx, |buffer, _| {
3971 buffer.push_transaction(transaction, Instant::now());
3972 })
3973 .ok();
3974 }
3975 editor.update(&mut cx, |editor, cx| {
3976 editor.refresh_document_highlights(cx);
3977 })?;
3978 }
3979 Ok(())
3980 }))
3981 }
3982
3983 pub fn show_completions(
3984 &mut self,
3985 options: &ShowCompletions,
3986 window: &mut Window,
3987 cx: &mut Context<Self>,
3988 ) {
3989 if self.pending_rename.is_some() {
3990 return;
3991 }
3992
3993 let Some(provider) = self.completion_provider.as_ref() else {
3994 return;
3995 };
3996
3997 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3998 return;
3999 }
4000
4001 let position = self.selections.newest_anchor().head();
4002 if position.diff_base_anchor.is_some() {
4003 return;
4004 }
4005 let (buffer, buffer_position) =
4006 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4007 output
4008 } else {
4009 return;
4010 };
4011 let show_completion_documentation = buffer
4012 .read(cx)
4013 .snapshot()
4014 .settings_at(buffer_position, cx)
4015 .show_completion_documentation;
4016
4017 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4018
4019 let trigger_kind = match &options.trigger {
4020 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4021 CompletionTriggerKind::TRIGGER_CHARACTER
4022 }
4023 _ => CompletionTriggerKind::INVOKED,
4024 };
4025 let completion_context = CompletionContext {
4026 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4027 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4028 Some(String::from(trigger))
4029 } else {
4030 None
4031 }
4032 }),
4033 trigger_kind,
4034 };
4035 let completions =
4036 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4037 let sort_completions = provider.sort_completions();
4038
4039 let id = post_inc(&mut self.next_completion_id);
4040 let task = cx.spawn_in(window, |editor, mut cx| {
4041 async move {
4042 editor.update(&mut cx, |this, _| {
4043 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4044 })?;
4045 let completions = completions.await.log_err();
4046 let menu = if let Some(completions) = completions {
4047 let mut menu = CompletionsMenu::new(
4048 id,
4049 sort_completions,
4050 show_completion_documentation,
4051 position,
4052 buffer.clone(),
4053 completions.into(),
4054 );
4055
4056 menu.filter(query.as_deref(), cx.background_executor().clone())
4057 .await;
4058
4059 menu.visible().then_some(menu)
4060 } else {
4061 None
4062 };
4063
4064 editor.update_in(&mut cx, |editor, window, cx| {
4065 match editor.context_menu.borrow().as_ref() {
4066 None => {}
4067 Some(CodeContextMenu::Completions(prev_menu)) => {
4068 if prev_menu.id > id {
4069 return;
4070 }
4071 }
4072 _ => return,
4073 }
4074
4075 if editor.focus_handle.is_focused(window) && menu.is_some() {
4076 let mut menu = menu.unwrap();
4077 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4078
4079 *editor.context_menu.borrow_mut() =
4080 Some(CodeContextMenu::Completions(menu));
4081
4082 if editor.show_edit_predictions_in_menu() {
4083 editor.update_visible_inline_completion(window, cx);
4084 } else {
4085 editor.discard_inline_completion(false, cx);
4086 }
4087
4088 cx.notify();
4089 } else if editor.completion_tasks.len() <= 1 {
4090 // If there are no more completion tasks and the last menu was
4091 // empty, we should hide it.
4092 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4093 // If it was already hidden and we don't show inline
4094 // completions in the menu, we should also show the
4095 // inline-completion when available.
4096 if was_hidden && editor.show_edit_predictions_in_menu() {
4097 editor.update_visible_inline_completion(window, cx);
4098 }
4099 }
4100 })?;
4101
4102 Ok::<_, anyhow::Error>(())
4103 }
4104 .log_err()
4105 });
4106
4107 self.completion_tasks.push((id, task));
4108 }
4109
4110 pub fn confirm_completion(
4111 &mut self,
4112 action: &ConfirmCompletion,
4113 window: &mut Window,
4114 cx: &mut Context<Self>,
4115 ) -> Option<Task<Result<()>>> {
4116 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4117 }
4118
4119 pub fn compose_completion(
4120 &mut self,
4121 action: &ComposeCompletion,
4122 window: &mut Window,
4123 cx: &mut Context<Self>,
4124 ) -> Option<Task<Result<()>>> {
4125 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4126 }
4127
4128 fn do_completion(
4129 &mut self,
4130 item_ix: Option<usize>,
4131 intent: CompletionIntent,
4132 window: &mut Window,
4133 cx: &mut Context<Editor>,
4134 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4135 use language::ToOffset as _;
4136
4137 let completions_menu =
4138 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4139 menu
4140 } else {
4141 return None;
4142 };
4143
4144 let entries = completions_menu.entries.borrow();
4145 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4146 if self.show_edit_predictions_in_menu() {
4147 self.discard_inline_completion(true, cx);
4148 }
4149 let candidate_id = mat.candidate_id;
4150 drop(entries);
4151
4152 let buffer_handle = completions_menu.buffer;
4153 let completion = completions_menu
4154 .completions
4155 .borrow()
4156 .get(candidate_id)?
4157 .clone();
4158 cx.stop_propagation();
4159
4160 let snippet;
4161 let text;
4162
4163 if completion.is_snippet() {
4164 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4165 text = snippet.as_ref().unwrap().text.clone();
4166 } else {
4167 snippet = None;
4168 text = completion.new_text.clone();
4169 };
4170 let selections = self.selections.all::<usize>(cx);
4171 let buffer = buffer_handle.read(cx);
4172 let old_range = completion.old_range.to_offset(buffer);
4173 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4174
4175 let newest_selection = self.selections.newest_anchor();
4176 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4177 return None;
4178 }
4179
4180 let lookbehind = newest_selection
4181 .start
4182 .text_anchor
4183 .to_offset(buffer)
4184 .saturating_sub(old_range.start);
4185 let lookahead = old_range
4186 .end
4187 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4188 let mut common_prefix_len = old_text
4189 .bytes()
4190 .zip(text.bytes())
4191 .take_while(|(a, b)| a == b)
4192 .count();
4193
4194 let snapshot = self.buffer.read(cx).snapshot(cx);
4195 let mut range_to_replace: Option<Range<isize>> = None;
4196 let mut ranges = Vec::new();
4197 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4198 for selection in &selections {
4199 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4200 let start = selection.start.saturating_sub(lookbehind);
4201 let end = selection.end + lookahead;
4202 if selection.id == newest_selection.id {
4203 range_to_replace = Some(
4204 ((start + common_prefix_len) as isize - selection.start as isize)
4205 ..(end as isize - selection.start as isize),
4206 );
4207 }
4208 ranges.push(start + common_prefix_len..end);
4209 } else {
4210 common_prefix_len = 0;
4211 ranges.clear();
4212 ranges.extend(selections.iter().map(|s| {
4213 if s.id == newest_selection.id {
4214 range_to_replace = Some(
4215 old_range.start.to_offset_utf16(&snapshot).0 as isize
4216 - selection.start as isize
4217 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4218 - selection.start as isize,
4219 );
4220 old_range.clone()
4221 } else {
4222 s.start..s.end
4223 }
4224 }));
4225 break;
4226 }
4227 if !self.linked_edit_ranges.is_empty() {
4228 let start_anchor = snapshot.anchor_before(selection.head());
4229 let end_anchor = snapshot.anchor_after(selection.tail());
4230 if let Some(ranges) = self
4231 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4232 {
4233 for (buffer, edits) in ranges {
4234 linked_edits.entry(buffer.clone()).or_default().extend(
4235 edits
4236 .into_iter()
4237 .map(|range| (range, text[common_prefix_len..].to_owned())),
4238 );
4239 }
4240 }
4241 }
4242 }
4243 let text = &text[common_prefix_len..];
4244
4245 cx.emit(EditorEvent::InputHandled {
4246 utf16_range_to_replace: range_to_replace,
4247 text: text.into(),
4248 });
4249
4250 self.transact(window, cx, |this, window, cx| {
4251 if let Some(mut snippet) = snippet {
4252 snippet.text = text.to_string();
4253 for tabstop in snippet
4254 .tabstops
4255 .iter_mut()
4256 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4257 {
4258 tabstop.start -= common_prefix_len as isize;
4259 tabstop.end -= common_prefix_len as isize;
4260 }
4261
4262 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4263 } else {
4264 this.buffer.update(cx, |buffer, cx| {
4265 buffer.edit(
4266 ranges.iter().map(|range| (range.clone(), text)),
4267 this.autoindent_mode.clone(),
4268 cx,
4269 );
4270 });
4271 }
4272 for (buffer, edits) in linked_edits {
4273 buffer.update(cx, |buffer, cx| {
4274 let snapshot = buffer.snapshot();
4275 let edits = edits
4276 .into_iter()
4277 .map(|(range, text)| {
4278 use text::ToPoint as TP;
4279 let end_point = TP::to_point(&range.end, &snapshot);
4280 let start_point = TP::to_point(&range.start, &snapshot);
4281 (start_point..end_point, text)
4282 })
4283 .sorted_by_key(|(range, _)| range.start)
4284 .collect::<Vec<_>>();
4285 buffer.edit(edits, None, cx);
4286 })
4287 }
4288
4289 this.refresh_inline_completion(true, false, window, cx);
4290 });
4291
4292 let show_new_completions_on_confirm = completion
4293 .confirm
4294 .as_ref()
4295 .map_or(false, |confirm| confirm(intent, window, cx));
4296 if show_new_completions_on_confirm {
4297 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4298 }
4299
4300 let provider = self.completion_provider.as_ref()?;
4301 drop(completion);
4302 let apply_edits = provider.apply_additional_edits_for_completion(
4303 buffer_handle,
4304 completions_menu.completions.clone(),
4305 candidate_id,
4306 true,
4307 cx,
4308 );
4309
4310 let editor_settings = EditorSettings::get_global(cx);
4311 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4312 // After the code completion is finished, users often want to know what signatures are needed.
4313 // so we should automatically call signature_help
4314 self.show_signature_help(&ShowSignatureHelp, window, cx);
4315 }
4316
4317 Some(cx.foreground_executor().spawn(async move {
4318 apply_edits.await?;
4319 Ok(())
4320 }))
4321 }
4322
4323 pub fn toggle_code_actions(
4324 &mut self,
4325 action: &ToggleCodeActions,
4326 window: &mut Window,
4327 cx: &mut Context<Self>,
4328 ) {
4329 let mut context_menu = self.context_menu.borrow_mut();
4330 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4331 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4332 // Toggle if we're selecting the same one
4333 *context_menu = None;
4334 cx.notify();
4335 return;
4336 } else {
4337 // Otherwise, clear it and start a new one
4338 *context_menu = None;
4339 cx.notify();
4340 }
4341 }
4342 drop(context_menu);
4343 let snapshot = self.snapshot(window, cx);
4344 let deployed_from_indicator = action.deployed_from_indicator;
4345 let mut task = self.code_actions_task.take();
4346 let action = action.clone();
4347 cx.spawn_in(window, |editor, mut cx| async move {
4348 while let Some(prev_task) = task {
4349 prev_task.await.log_err();
4350 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4351 }
4352
4353 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4354 if editor.focus_handle.is_focused(window) {
4355 let multibuffer_point = action
4356 .deployed_from_indicator
4357 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4358 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4359 let (buffer, buffer_row) = snapshot
4360 .buffer_snapshot
4361 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4362 .and_then(|(buffer_snapshot, range)| {
4363 editor
4364 .buffer
4365 .read(cx)
4366 .buffer(buffer_snapshot.remote_id())
4367 .map(|buffer| (buffer, range.start.row))
4368 })?;
4369 let (_, code_actions) = editor
4370 .available_code_actions
4371 .clone()
4372 .and_then(|(location, code_actions)| {
4373 let snapshot = location.buffer.read(cx).snapshot();
4374 let point_range = location.range.to_point(&snapshot);
4375 let point_range = point_range.start.row..=point_range.end.row;
4376 if point_range.contains(&buffer_row) {
4377 Some((location, code_actions))
4378 } else {
4379 None
4380 }
4381 })
4382 .unzip();
4383 let buffer_id = buffer.read(cx).remote_id();
4384 let tasks = editor
4385 .tasks
4386 .get(&(buffer_id, buffer_row))
4387 .map(|t| Arc::new(t.to_owned()));
4388 if tasks.is_none() && code_actions.is_none() {
4389 return None;
4390 }
4391
4392 editor.completion_tasks.clear();
4393 editor.discard_inline_completion(false, cx);
4394 let task_context =
4395 tasks
4396 .as_ref()
4397 .zip(editor.project.clone())
4398 .map(|(tasks, project)| {
4399 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4400 });
4401
4402 Some(cx.spawn_in(window, |editor, mut cx| async move {
4403 let task_context = match task_context {
4404 Some(task_context) => task_context.await,
4405 None => None,
4406 };
4407 let resolved_tasks =
4408 tasks.zip(task_context).map(|(tasks, task_context)| {
4409 Rc::new(ResolvedTasks {
4410 templates: tasks.resolve(&task_context).collect(),
4411 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4412 multibuffer_point.row,
4413 tasks.column,
4414 )),
4415 })
4416 });
4417 let spawn_straight_away = resolved_tasks
4418 .as_ref()
4419 .map_or(false, |tasks| tasks.templates.len() == 1)
4420 && code_actions
4421 .as_ref()
4422 .map_or(true, |actions| actions.is_empty());
4423 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4424 *editor.context_menu.borrow_mut() =
4425 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4426 buffer,
4427 actions: CodeActionContents {
4428 tasks: resolved_tasks,
4429 actions: code_actions,
4430 },
4431 selected_item: Default::default(),
4432 scroll_handle: UniformListScrollHandle::default(),
4433 deployed_from_indicator,
4434 }));
4435 if spawn_straight_away {
4436 if let Some(task) = editor.confirm_code_action(
4437 &ConfirmCodeAction { item_ix: Some(0) },
4438 window,
4439 cx,
4440 ) {
4441 cx.notify();
4442 return task;
4443 }
4444 }
4445 cx.notify();
4446 Task::ready(Ok(()))
4447 }) {
4448 task.await
4449 } else {
4450 Ok(())
4451 }
4452 }))
4453 } else {
4454 Some(Task::ready(Ok(())))
4455 }
4456 })?;
4457 if let Some(task) = spawned_test_task {
4458 task.await?;
4459 }
4460
4461 Ok::<_, anyhow::Error>(())
4462 })
4463 .detach_and_log_err(cx);
4464 }
4465
4466 pub fn confirm_code_action(
4467 &mut self,
4468 action: &ConfirmCodeAction,
4469 window: &mut Window,
4470 cx: &mut Context<Self>,
4471 ) -> Option<Task<Result<()>>> {
4472 let actions_menu =
4473 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4474 menu
4475 } else {
4476 return None;
4477 };
4478 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4479 let action = actions_menu.actions.get(action_ix)?;
4480 let title = action.label();
4481 let buffer = actions_menu.buffer;
4482 let workspace = self.workspace()?;
4483
4484 match action {
4485 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4486 workspace.update(cx, |workspace, cx| {
4487 workspace::tasks::schedule_resolved_task(
4488 workspace,
4489 task_source_kind,
4490 resolved_task,
4491 false,
4492 cx,
4493 );
4494
4495 Some(Task::ready(Ok(())))
4496 })
4497 }
4498 CodeActionsItem::CodeAction {
4499 excerpt_id,
4500 action,
4501 provider,
4502 } => {
4503 let apply_code_action =
4504 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4505 let workspace = workspace.downgrade();
4506 Some(cx.spawn_in(window, |editor, cx| async move {
4507 let project_transaction = apply_code_action.await?;
4508 Self::open_project_transaction(
4509 &editor,
4510 workspace,
4511 project_transaction,
4512 title,
4513 cx,
4514 )
4515 .await
4516 }))
4517 }
4518 }
4519 }
4520
4521 pub async fn open_project_transaction(
4522 this: &WeakEntity<Editor>,
4523 workspace: WeakEntity<Workspace>,
4524 transaction: ProjectTransaction,
4525 title: String,
4526 mut cx: AsyncWindowContext,
4527 ) -> Result<()> {
4528 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4529 cx.update(|_, cx| {
4530 entries.sort_unstable_by_key(|(buffer, _)| {
4531 buffer.read(cx).file().map(|f| f.path().clone())
4532 });
4533 })?;
4534
4535 // If the project transaction's edits are all contained within this editor, then
4536 // avoid opening a new editor to display them.
4537
4538 if let Some((buffer, transaction)) = entries.first() {
4539 if entries.len() == 1 {
4540 let excerpt = this.update(&mut cx, |editor, cx| {
4541 editor
4542 .buffer()
4543 .read(cx)
4544 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4545 })?;
4546 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4547 if excerpted_buffer == *buffer {
4548 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4549 let excerpt_range = excerpt_range.to_offset(buffer);
4550 buffer
4551 .edited_ranges_for_transaction::<usize>(transaction)
4552 .all(|range| {
4553 excerpt_range.start <= range.start
4554 && excerpt_range.end >= range.end
4555 })
4556 })?;
4557
4558 if all_edits_within_excerpt {
4559 return Ok(());
4560 }
4561 }
4562 }
4563 }
4564 } else {
4565 return Ok(());
4566 }
4567
4568 let mut ranges_to_highlight = Vec::new();
4569 let excerpt_buffer = cx.new(|cx| {
4570 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4571 for (buffer_handle, transaction) in &entries {
4572 let buffer = buffer_handle.read(cx);
4573 ranges_to_highlight.extend(
4574 multibuffer.push_excerpts_with_context_lines(
4575 buffer_handle.clone(),
4576 buffer
4577 .edited_ranges_for_transaction::<usize>(transaction)
4578 .collect(),
4579 DEFAULT_MULTIBUFFER_CONTEXT,
4580 cx,
4581 ),
4582 );
4583 }
4584 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4585 multibuffer
4586 })?;
4587
4588 workspace.update_in(&mut cx, |workspace, window, cx| {
4589 let project = workspace.project().clone();
4590 let editor = cx
4591 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4592 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4593 editor.update(cx, |editor, cx| {
4594 editor.highlight_background::<Self>(
4595 &ranges_to_highlight,
4596 |theme| theme.editor_highlighted_line_background,
4597 cx,
4598 );
4599 });
4600 })?;
4601
4602 Ok(())
4603 }
4604
4605 pub fn clear_code_action_providers(&mut self) {
4606 self.code_action_providers.clear();
4607 self.available_code_actions.take();
4608 }
4609
4610 pub fn add_code_action_provider(
4611 &mut self,
4612 provider: Rc<dyn CodeActionProvider>,
4613 window: &mut Window,
4614 cx: &mut Context<Self>,
4615 ) {
4616 if self
4617 .code_action_providers
4618 .iter()
4619 .any(|existing_provider| existing_provider.id() == provider.id())
4620 {
4621 return;
4622 }
4623
4624 self.code_action_providers.push(provider);
4625 self.refresh_code_actions(window, cx);
4626 }
4627
4628 pub fn remove_code_action_provider(
4629 &mut self,
4630 id: Arc<str>,
4631 window: &mut Window,
4632 cx: &mut Context<Self>,
4633 ) {
4634 self.code_action_providers
4635 .retain(|provider| provider.id() != id);
4636 self.refresh_code_actions(window, cx);
4637 }
4638
4639 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4640 let buffer = self.buffer.read(cx);
4641 let newest_selection = self.selections.newest_anchor().clone();
4642 if newest_selection.head().diff_base_anchor.is_some() {
4643 return None;
4644 }
4645 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4646 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4647 if start_buffer != end_buffer {
4648 return None;
4649 }
4650
4651 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4652 cx.background_executor()
4653 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4654 .await;
4655
4656 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4657 let providers = this.code_action_providers.clone();
4658 let tasks = this
4659 .code_action_providers
4660 .iter()
4661 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4662 .collect::<Vec<_>>();
4663 (providers, tasks)
4664 })?;
4665
4666 let mut actions = Vec::new();
4667 for (provider, provider_actions) in
4668 providers.into_iter().zip(future::join_all(tasks).await)
4669 {
4670 if let Some(provider_actions) = provider_actions.log_err() {
4671 actions.extend(provider_actions.into_iter().map(|action| {
4672 AvailableCodeAction {
4673 excerpt_id: newest_selection.start.excerpt_id,
4674 action,
4675 provider: provider.clone(),
4676 }
4677 }));
4678 }
4679 }
4680
4681 this.update(&mut cx, |this, cx| {
4682 this.available_code_actions = if actions.is_empty() {
4683 None
4684 } else {
4685 Some((
4686 Location {
4687 buffer: start_buffer,
4688 range: start..end,
4689 },
4690 actions.into(),
4691 ))
4692 };
4693 cx.notify();
4694 })
4695 }));
4696 None
4697 }
4698
4699 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4700 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4701 self.show_git_blame_inline = false;
4702
4703 self.show_git_blame_inline_delay_task =
4704 Some(cx.spawn_in(window, |this, mut cx| async move {
4705 cx.background_executor().timer(delay).await;
4706
4707 this.update(&mut cx, |this, cx| {
4708 this.show_git_blame_inline = true;
4709 cx.notify();
4710 })
4711 .log_err();
4712 }));
4713 }
4714 }
4715
4716 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4717 if self.pending_rename.is_some() {
4718 return None;
4719 }
4720
4721 let provider = self.semantics_provider.clone()?;
4722 let buffer = self.buffer.read(cx);
4723 let newest_selection = self.selections.newest_anchor().clone();
4724 let cursor_position = newest_selection.head();
4725 let (cursor_buffer, cursor_buffer_position) =
4726 buffer.text_anchor_for_position(cursor_position, cx)?;
4727 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4728 if cursor_buffer != tail_buffer {
4729 return None;
4730 }
4731 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4732 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4733 cx.background_executor()
4734 .timer(Duration::from_millis(debounce))
4735 .await;
4736
4737 let highlights = if let Some(highlights) = cx
4738 .update(|cx| {
4739 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4740 })
4741 .ok()
4742 .flatten()
4743 {
4744 highlights.await.log_err()
4745 } else {
4746 None
4747 };
4748
4749 if let Some(highlights) = highlights {
4750 this.update(&mut cx, |this, cx| {
4751 if this.pending_rename.is_some() {
4752 return;
4753 }
4754
4755 let buffer_id = cursor_position.buffer_id;
4756 let buffer = this.buffer.read(cx);
4757 if !buffer
4758 .text_anchor_for_position(cursor_position, cx)
4759 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4760 {
4761 return;
4762 }
4763
4764 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4765 let mut write_ranges = Vec::new();
4766 let mut read_ranges = Vec::new();
4767 for highlight in highlights {
4768 for (excerpt_id, excerpt_range) in
4769 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4770 {
4771 let start = highlight
4772 .range
4773 .start
4774 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4775 let end = highlight
4776 .range
4777 .end
4778 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4779 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4780 continue;
4781 }
4782
4783 let range = Anchor {
4784 buffer_id,
4785 excerpt_id,
4786 text_anchor: start,
4787 diff_base_anchor: None,
4788 }..Anchor {
4789 buffer_id,
4790 excerpt_id,
4791 text_anchor: end,
4792 diff_base_anchor: None,
4793 };
4794 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4795 write_ranges.push(range);
4796 } else {
4797 read_ranges.push(range);
4798 }
4799 }
4800 }
4801
4802 this.highlight_background::<DocumentHighlightRead>(
4803 &read_ranges,
4804 |theme| theme.editor_document_highlight_read_background,
4805 cx,
4806 );
4807 this.highlight_background::<DocumentHighlightWrite>(
4808 &write_ranges,
4809 |theme| theme.editor_document_highlight_write_background,
4810 cx,
4811 );
4812 cx.notify();
4813 })
4814 .log_err();
4815 }
4816 }));
4817 None
4818 }
4819
4820 pub fn refresh_selected_text_highlights(
4821 &mut self,
4822 window: &mut Window,
4823 cx: &mut Context<Editor>,
4824 ) {
4825 self.selection_highlight_task.take();
4826 if !EditorSettings::get_global(cx).selection_highlight {
4827 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4828 return;
4829 }
4830 if self.selections.count() != 1 || self.selections.line_mode {
4831 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4832 return;
4833 }
4834 let selection = self.selections.newest::<Point>(cx);
4835 if selection.is_empty() || selection.start.row != selection.end.row {
4836 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4837 return;
4838 }
4839 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4840 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4841 cx.background_executor()
4842 .timer(Duration::from_millis(debounce))
4843 .await;
4844 let Some(Some(matches_task)) = editor
4845 .update_in(&mut cx, |editor, _, cx| {
4846 if editor.selections.count() != 1 || editor.selections.line_mode {
4847 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4848 return None;
4849 }
4850 let selection = editor.selections.newest::<Point>(cx);
4851 if selection.is_empty() || selection.start.row != selection.end.row {
4852 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4853 return None;
4854 }
4855 let buffer = editor.buffer().read(cx).snapshot(cx);
4856 let query = buffer.text_for_range(selection.range()).collect::<String>();
4857 if query.trim().is_empty() {
4858 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4859 return None;
4860 }
4861 Some(cx.background_spawn(async move {
4862 let mut ranges = Vec::new();
4863 let selection_anchors = selection.range().to_anchors(&buffer);
4864 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4865 for (search_buffer, search_range, excerpt_id) in
4866 buffer.range_to_buffer_ranges(range)
4867 {
4868 ranges.extend(
4869 project::search::SearchQuery::text(
4870 query.clone(),
4871 false,
4872 false,
4873 false,
4874 Default::default(),
4875 Default::default(),
4876 None,
4877 )
4878 .unwrap()
4879 .search(search_buffer, Some(search_range.clone()))
4880 .await
4881 .into_iter()
4882 .filter_map(
4883 |match_range| {
4884 let start = search_buffer.anchor_after(
4885 search_range.start + match_range.start,
4886 );
4887 let end = search_buffer.anchor_before(
4888 search_range.start + match_range.end,
4889 );
4890 let range = Anchor::range_in_buffer(
4891 excerpt_id,
4892 search_buffer.remote_id(),
4893 start..end,
4894 );
4895 (range != selection_anchors).then_some(range)
4896 },
4897 ),
4898 );
4899 }
4900 }
4901 ranges
4902 }))
4903 })
4904 .log_err()
4905 else {
4906 return;
4907 };
4908 let matches = matches_task.await;
4909 editor
4910 .update_in(&mut cx, |editor, _, cx| {
4911 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4912 if !matches.is_empty() {
4913 editor.highlight_background::<SelectedTextHighlight>(
4914 &matches,
4915 |theme| theme.editor_document_highlight_bracket_background,
4916 cx,
4917 )
4918 }
4919 })
4920 .log_err();
4921 }));
4922 }
4923
4924 pub fn refresh_inline_completion(
4925 &mut self,
4926 debounce: bool,
4927 user_requested: bool,
4928 window: &mut Window,
4929 cx: &mut Context<Self>,
4930 ) -> Option<()> {
4931 let provider = self.edit_prediction_provider()?;
4932 let cursor = self.selections.newest_anchor().head();
4933 let (buffer, cursor_buffer_position) =
4934 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4935
4936 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4937 self.discard_inline_completion(false, cx);
4938 return None;
4939 }
4940
4941 if !user_requested
4942 && (!self.should_show_edit_predictions()
4943 || !self.is_focused(window)
4944 || buffer.read(cx).is_empty())
4945 {
4946 self.discard_inline_completion(false, cx);
4947 return None;
4948 }
4949
4950 self.update_visible_inline_completion(window, cx);
4951 provider.refresh(
4952 self.project.clone(),
4953 buffer,
4954 cursor_buffer_position,
4955 debounce,
4956 cx,
4957 );
4958 Some(())
4959 }
4960
4961 fn show_edit_predictions_in_menu(&self) -> bool {
4962 match self.edit_prediction_settings {
4963 EditPredictionSettings::Disabled => false,
4964 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4965 }
4966 }
4967
4968 pub fn edit_predictions_enabled(&self) -> bool {
4969 match self.edit_prediction_settings {
4970 EditPredictionSettings::Disabled => false,
4971 EditPredictionSettings::Enabled { .. } => true,
4972 }
4973 }
4974
4975 fn edit_prediction_requires_modifier(&self) -> bool {
4976 match self.edit_prediction_settings {
4977 EditPredictionSettings::Disabled => false,
4978 EditPredictionSettings::Enabled {
4979 preview_requires_modifier,
4980 ..
4981 } => preview_requires_modifier,
4982 }
4983 }
4984
4985 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
4986 if self.edit_prediction_provider.is_none() {
4987 self.edit_prediction_settings = EditPredictionSettings::Disabled;
4988 } else {
4989 let selection = self.selections.newest_anchor();
4990 let cursor = selection.head();
4991
4992 if let Some((buffer, cursor_buffer_position)) =
4993 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4994 {
4995 self.edit_prediction_settings =
4996 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
4997 }
4998 }
4999 }
5000
5001 fn edit_prediction_settings_at_position(
5002 &self,
5003 buffer: &Entity<Buffer>,
5004 buffer_position: language::Anchor,
5005 cx: &App,
5006 ) -> EditPredictionSettings {
5007 if self.mode != EditorMode::Full
5008 || !self.show_inline_completions_override.unwrap_or(true)
5009 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5010 {
5011 return EditPredictionSettings::Disabled;
5012 }
5013
5014 let buffer = buffer.read(cx);
5015
5016 let file = buffer.file();
5017
5018 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5019 return EditPredictionSettings::Disabled;
5020 };
5021
5022 let by_provider = matches!(
5023 self.menu_inline_completions_policy,
5024 MenuInlineCompletionsPolicy::ByProvider
5025 );
5026
5027 let show_in_menu = by_provider
5028 && self
5029 .edit_prediction_provider
5030 .as_ref()
5031 .map_or(false, |provider| {
5032 provider.provider.show_completions_in_menu()
5033 });
5034
5035 let preview_requires_modifier =
5036 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5037
5038 EditPredictionSettings::Enabled {
5039 show_in_menu,
5040 preview_requires_modifier,
5041 }
5042 }
5043
5044 fn should_show_edit_predictions(&self) -> bool {
5045 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5046 }
5047
5048 pub fn edit_prediction_preview_is_active(&self) -> bool {
5049 matches!(
5050 self.edit_prediction_preview,
5051 EditPredictionPreview::Active { .. }
5052 )
5053 }
5054
5055 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5056 let cursor = self.selections.newest_anchor().head();
5057 if let Some((buffer, cursor_position)) =
5058 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5059 {
5060 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5061 } else {
5062 false
5063 }
5064 }
5065
5066 fn edit_predictions_enabled_in_buffer(
5067 &self,
5068 buffer: &Entity<Buffer>,
5069 buffer_position: language::Anchor,
5070 cx: &App,
5071 ) -> bool {
5072 maybe!({
5073 let provider = self.edit_prediction_provider()?;
5074 if !provider.is_enabled(&buffer, buffer_position, cx) {
5075 return Some(false);
5076 }
5077 let buffer = buffer.read(cx);
5078 let Some(file) = buffer.file() else {
5079 return Some(true);
5080 };
5081 let settings = all_language_settings(Some(file), cx);
5082 Some(settings.edit_predictions_enabled_for_file(file, cx))
5083 })
5084 .unwrap_or(false)
5085 }
5086
5087 fn cycle_inline_completion(
5088 &mut self,
5089 direction: Direction,
5090 window: &mut Window,
5091 cx: &mut Context<Self>,
5092 ) -> Option<()> {
5093 let provider = self.edit_prediction_provider()?;
5094 let cursor = self.selections.newest_anchor().head();
5095 let (buffer, cursor_buffer_position) =
5096 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5097 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5098 return None;
5099 }
5100
5101 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5102 self.update_visible_inline_completion(window, cx);
5103
5104 Some(())
5105 }
5106
5107 pub fn show_inline_completion(
5108 &mut self,
5109 _: &ShowEditPrediction,
5110 window: &mut Window,
5111 cx: &mut Context<Self>,
5112 ) {
5113 if !self.has_active_inline_completion() {
5114 self.refresh_inline_completion(false, true, window, cx);
5115 return;
5116 }
5117
5118 self.update_visible_inline_completion(window, cx);
5119 }
5120
5121 pub fn display_cursor_names(
5122 &mut self,
5123 _: &DisplayCursorNames,
5124 window: &mut Window,
5125 cx: &mut Context<Self>,
5126 ) {
5127 self.show_cursor_names(window, cx);
5128 }
5129
5130 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5131 self.show_cursor_names = true;
5132 cx.notify();
5133 cx.spawn_in(window, |this, mut cx| async move {
5134 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5135 this.update(&mut cx, |this, cx| {
5136 this.show_cursor_names = false;
5137 cx.notify()
5138 })
5139 .ok()
5140 })
5141 .detach();
5142 }
5143
5144 pub fn next_edit_prediction(
5145 &mut self,
5146 _: &NextEditPrediction,
5147 window: &mut Window,
5148 cx: &mut Context<Self>,
5149 ) {
5150 if self.has_active_inline_completion() {
5151 self.cycle_inline_completion(Direction::Next, window, cx);
5152 } else {
5153 let is_copilot_disabled = self
5154 .refresh_inline_completion(false, true, window, cx)
5155 .is_none();
5156 if is_copilot_disabled {
5157 cx.propagate();
5158 }
5159 }
5160 }
5161
5162 pub fn previous_edit_prediction(
5163 &mut self,
5164 _: &PreviousEditPrediction,
5165 window: &mut Window,
5166 cx: &mut Context<Self>,
5167 ) {
5168 if self.has_active_inline_completion() {
5169 self.cycle_inline_completion(Direction::Prev, window, cx);
5170 } else {
5171 let is_copilot_disabled = self
5172 .refresh_inline_completion(false, true, window, cx)
5173 .is_none();
5174 if is_copilot_disabled {
5175 cx.propagate();
5176 }
5177 }
5178 }
5179
5180 pub fn accept_edit_prediction(
5181 &mut self,
5182 _: &AcceptEditPrediction,
5183 window: &mut Window,
5184 cx: &mut Context<Self>,
5185 ) {
5186 if self.show_edit_predictions_in_menu() {
5187 self.hide_context_menu(window, cx);
5188 }
5189
5190 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5191 return;
5192 };
5193
5194 self.report_inline_completion_event(
5195 active_inline_completion.completion_id.clone(),
5196 true,
5197 cx,
5198 );
5199
5200 match &active_inline_completion.completion {
5201 InlineCompletion::Move { target, .. } => {
5202 let target = *target;
5203
5204 if let Some(position_map) = &self.last_position_map {
5205 if position_map
5206 .visible_row_range
5207 .contains(&target.to_display_point(&position_map.snapshot).row())
5208 || !self.edit_prediction_requires_modifier()
5209 {
5210 self.unfold_ranges(&[target..target], true, false, cx);
5211 // Note that this is also done in vim's handler of the Tab action.
5212 self.change_selections(
5213 Some(Autoscroll::newest()),
5214 window,
5215 cx,
5216 |selections| {
5217 selections.select_anchor_ranges([target..target]);
5218 },
5219 );
5220 self.clear_row_highlights::<EditPredictionPreview>();
5221
5222 self.edit_prediction_preview
5223 .set_previous_scroll_position(None);
5224 } else {
5225 self.edit_prediction_preview
5226 .set_previous_scroll_position(Some(
5227 position_map.snapshot.scroll_anchor,
5228 ));
5229
5230 self.highlight_rows::<EditPredictionPreview>(
5231 target..target,
5232 cx.theme().colors().editor_highlighted_line_background,
5233 true,
5234 cx,
5235 );
5236 self.request_autoscroll(Autoscroll::fit(), cx);
5237 }
5238 }
5239 }
5240 InlineCompletion::Edit { edits, .. } => {
5241 if let Some(provider) = self.edit_prediction_provider() {
5242 provider.accept(cx);
5243 }
5244
5245 let snapshot = self.buffer.read(cx).snapshot(cx);
5246 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5247
5248 self.buffer.update(cx, |buffer, cx| {
5249 buffer.edit(edits.iter().cloned(), None, cx)
5250 });
5251
5252 self.change_selections(None, window, cx, |s| {
5253 s.select_anchor_ranges([last_edit_end..last_edit_end])
5254 });
5255
5256 self.update_visible_inline_completion(window, cx);
5257 if self.active_inline_completion.is_none() {
5258 self.refresh_inline_completion(true, true, window, cx);
5259 }
5260
5261 cx.notify();
5262 }
5263 }
5264
5265 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5266 }
5267
5268 pub fn accept_partial_inline_completion(
5269 &mut self,
5270 _: &AcceptPartialEditPrediction,
5271 window: &mut Window,
5272 cx: &mut Context<Self>,
5273 ) {
5274 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5275 return;
5276 };
5277 if self.selections.count() != 1 {
5278 return;
5279 }
5280
5281 self.report_inline_completion_event(
5282 active_inline_completion.completion_id.clone(),
5283 true,
5284 cx,
5285 );
5286
5287 match &active_inline_completion.completion {
5288 InlineCompletion::Move { target, .. } => {
5289 let target = *target;
5290 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5291 selections.select_anchor_ranges([target..target]);
5292 });
5293 }
5294 InlineCompletion::Edit { edits, .. } => {
5295 // Find an insertion that starts at the cursor position.
5296 let snapshot = self.buffer.read(cx).snapshot(cx);
5297 let cursor_offset = self.selections.newest::<usize>(cx).head();
5298 let insertion = edits.iter().find_map(|(range, text)| {
5299 let range = range.to_offset(&snapshot);
5300 if range.is_empty() && range.start == cursor_offset {
5301 Some(text)
5302 } else {
5303 None
5304 }
5305 });
5306
5307 if let Some(text) = insertion {
5308 let mut partial_completion = text
5309 .chars()
5310 .by_ref()
5311 .take_while(|c| c.is_alphabetic())
5312 .collect::<String>();
5313 if partial_completion.is_empty() {
5314 partial_completion = text
5315 .chars()
5316 .by_ref()
5317 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5318 .collect::<String>();
5319 }
5320
5321 cx.emit(EditorEvent::InputHandled {
5322 utf16_range_to_replace: None,
5323 text: partial_completion.clone().into(),
5324 });
5325
5326 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5327
5328 self.refresh_inline_completion(true, true, window, cx);
5329 cx.notify();
5330 } else {
5331 self.accept_edit_prediction(&Default::default(), window, cx);
5332 }
5333 }
5334 }
5335 }
5336
5337 fn discard_inline_completion(
5338 &mut self,
5339 should_report_inline_completion_event: bool,
5340 cx: &mut Context<Self>,
5341 ) -> bool {
5342 if should_report_inline_completion_event {
5343 let completion_id = self
5344 .active_inline_completion
5345 .as_ref()
5346 .and_then(|active_completion| active_completion.completion_id.clone());
5347
5348 self.report_inline_completion_event(completion_id, false, cx);
5349 }
5350
5351 if let Some(provider) = self.edit_prediction_provider() {
5352 provider.discard(cx);
5353 }
5354
5355 self.take_active_inline_completion(cx)
5356 }
5357
5358 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5359 let Some(provider) = self.edit_prediction_provider() else {
5360 return;
5361 };
5362
5363 let Some((_, buffer, _)) = self
5364 .buffer
5365 .read(cx)
5366 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5367 else {
5368 return;
5369 };
5370
5371 let extension = buffer
5372 .read(cx)
5373 .file()
5374 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5375
5376 let event_type = match accepted {
5377 true => "Edit Prediction Accepted",
5378 false => "Edit Prediction Discarded",
5379 };
5380 telemetry::event!(
5381 event_type,
5382 provider = provider.name(),
5383 prediction_id = id,
5384 suggestion_accepted = accepted,
5385 file_extension = extension,
5386 );
5387 }
5388
5389 pub fn has_active_inline_completion(&self) -> bool {
5390 self.active_inline_completion.is_some()
5391 }
5392
5393 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5394 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5395 return false;
5396 };
5397
5398 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5399 self.clear_highlights::<InlineCompletionHighlight>(cx);
5400 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5401 true
5402 }
5403
5404 /// Returns true when we're displaying the edit prediction popover below the cursor
5405 /// like we are not previewing and the LSP autocomplete menu is visible
5406 /// or we are in `when_holding_modifier` mode.
5407 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5408 if self.edit_prediction_preview_is_active()
5409 || !self.show_edit_predictions_in_menu()
5410 || !self.edit_predictions_enabled()
5411 {
5412 return false;
5413 }
5414
5415 if self.has_visible_completions_menu() {
5416 return true;
5417 }
5418
5419 has_completion && self.edit_prediction_requires_modifier()
5420 }
5421
5422 fn handle_modifiers_changed(
5423 &mut self,
5424 modifiers: Modifiers,
5425 position_map: &PositionMap,
5426 window: &mut Window,
5427 cx: &mut Context<Self>,
5428 ) {
5429 if self.show_edit_predictions_in_menu() {
5430 self.update_edit_prediction_preview(&modifiers, window, cx);
5431 }
5432
5433 self.update_selection_mode(&modifiers, position_map, window, cx);
5434
5435 let mouse_position = window.mouse_position();
5436 if !position_map.text_hitbox.is_hovered(window) {
5437 return;
5438 }
5439
5440 self.update_hovered_link(
5441 position_map.point_for_position(mouse_position),
5442 &position_map.snapshot,
5443 modifiers,
5444 window,
5445 cx,
5446 )
5447 }
5448
5449 fn update_selection_mode(
5450 &mut self,
5451 modifiers: &Modifiers,
5452 position_map: &PositionMap,
5453 window: &mut Window,
5454 cx: &mut Context<Self>,
5455 ) {
5456 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5457 return;
5458 }
5459
5460 let mouse_position = window.mouse_position();
5461 let point_for_position = position_map.point_for_position(mouse_position);
5462 let position = point_for_position.previous_valid;
5463
5464 self.select(
5465 SelectPhase::BeginColumnar {
5466 position,
5467 reset: false,
5468 goal_column: point_for_position.exact_unclipped.column(),
5469 },
5470 window,
5471 cx,
5472 );
5473 }
5474
5475 fn update_edit_prediction_preview(
5476 &mut self,
5477 modifiers: &Modifiers,
5478 window: &mut Window,
5479 cx: &mut Context<Self>,
5480 ) {
5481 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5482 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5483 return;
5484 };
5485
5486 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5487 if matches!(
5488 self.edit_prediction_preview,
5489 EditPredictionPreview::Inactive { .. }
5490 ) {
5491 self.edit_prediction_preview = EditPredictionPreview::Active {
5492 previous_scroll_position: None,
5493 since: Instant::now(),
5494 };
5495
5496 self.update_visible_inline_completion(window, cx);
5497 cx.notify();
5498 }
5499 } else if let EditPredictionPreview::Active {
5500 previous_scroll_position,
5501 since,
5502 } = self.edit_prediction_preview
5503 {
5504 if let (Some(previous_scroll_position), Some(position_map)) =
5505 (previous_scroll_position, self.last_position_map.as_ref())
5506 {
5507 self.set_scroll_position(
5508 previous_scroll_position
5509 .scroll_position(&position_map.snapshot.display_snapshot),
5510 window,
5511 cx,
5512 );
5513 }
5514
5515 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5516 released_too_fast: since.elapsed() < Duration::from_millis(200),
5517 };
5518 self.clear_row_highlights::<EditPredictionPreview>();
5519 self.update_visible_inline_completion(window, cx);
5520 cx.notify();
5521 }
5522 }
5523
5524 fn update_visible_inline_completion(
5525 &mut self,
5526 _window: &mut Window,
5527 cx: &mut Context<Self>,
5528 ) -> Option<()> {
5529 let selection = self.selections.newest_anchor();
5530 let cursor = selection.head();
5531 let multibuffer = self.buffer.read(cx).snapshot(cx);
5532 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5533 let excerpt_id = cursor.excerpt_id;
5534
5535 let show_in_menu = self.show_edit_predictions_in_menu();
5536 let completions_menu_has_precedence = !show_in_menu
5537 && (self.context_menu.borrow().is_some()
5538 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5539
5540 if completions_menu_has_precedence
5541 || !offset_selection.is_empty()
5542 || self
5543 .active_inline_completion
5544 .as_ref()
5545 .map_or(false, |completion| {
5546 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5547 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5548 !invalidation_range.contains(&offset_selection.head())
5549 })
5550 {
5551 self.discard_inline_completion(false, cx);
5552 return None;
5553 }
5554
5555 self.take_active_inline_completion(cx);
5556 let Some(provider) = self.edit_prediction_provider() else {
5557 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5558 return None;
5559 };
5560
5561 let (buffer, cursor_buffer_position) =
5562 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5563
5564 self.edit_prediction_settings =
5565 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5566
5567 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5568
5569 if self.edit_prediction_indent_conflict {
5570 let cursor_point = cursor.to_point(&multibuffer);
5571
5572 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5573
5574 if let Some((_, indent)) = indents.iter().next() {
5575 if indent.len == cursor_point.column {
5576 self.edit_prediction_indent_conflict = false;
5577 }
5578 }
5579 }
5580
5581 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5582 let edits = inline_completion
5583 .edits
5584 .into_iter()
5585 .flat_map(|(range, new_text)| {
5586 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5587 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5588 Some((start..end, new_text))
5589 })
5590 .collect::<Vec<_>>();
5591 if edits.is_empty() {
5592 return None;
5593 }
5594
5595 let first_edit_start = edits.first().unwrap().0.start;
5596 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5597 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5598
5599 let last_edit_end = edits.last().unwrap().0.end;
5600 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5601 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5602
5603 let cursor_row = cursor.to_point(&multibuffer).row;
5604
5605 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5606
5607 let mut inlay_ids = Vec::new();
5608 let invalidation_row_range;
5609 let move_invalidation_row_range = if cursor_row < edit_start_row {
5610 Some(cursor_row..edit_end_row)
5611 } else if cursor_row > edit_end_row {
5612 Some(edit_start_row..cursor_row)
5613 } else {
5614 None
5615 };
5616 let is_move =
5617 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5618 let completion = if is_move {
5619 invalidation_row_range =
5620 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5621 let target = first_edit_start;
5622 InlineCompletion::Move { target, snapshot }
5623 } else {
5624 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5625 && !self.inline_completions_hidden_for_vim_mode;
5626
5627 if show_completions_in_buffer {
5628 if edits
5629 .iter()
5630 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5631 {
5632 let mut inlays = Vec::new();
5633 for (range, new_text) in &edits {
5634 let inlay = Inlay::inline_completion(
5635 post_inc(&mut self.next_inlay_id),
5636 range.start,
5637 new_text.as_str(),
5638 );
5639 inlay_ids.push(inlay.id);
5640 inlays.push(inlay);
5641 }
5642
5643 self.splice_inlays(&[], inlays, cx);
5644 } else {
5645 let background_color = cx.theme().status().deleted_background;
5646 self.highlight_text::<InlineCompletionHighlight>(
5647 edits.iter().map(|(range, _)| range.clone()).collect(),
5648 HighlightStyle {
5649 background_color: Some(background_color),
5650 ..Default::default()
5651 },
5652 cx,
5653 );
5654 }
5655 }
5656
5657 invalidation_row_range = edit_start_row..edit_end_row;
5658
5659 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5660 if provider.show_tab_accept_marker() {
5661 EditDisplayMode::TabAccept
5662 } else {
5663 EditDisplayMode::Inline
5664 }
5665 } else {
5666 EditDisplayMode::DiffPopover
5667 };
5668
5669 InlineCompletion::Edit {
5670 edits,
5671 edit_preview: inline_completion.edit_preview,
5672 display_mode,
5673 snapshot,
5674 }
5675 };
5676
5677 let invalidation_range = multibuffer
5678 .anchor_before(Point::new(invalidation_row_range.start, 0))
5679 ..multibuffer.anchor_after(Point::new(
5680 invalidation_row_range.end,
5681 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5682 ));
5683
5684 self.stale_inline_completion_in_menu = None;
5685 self.active_inline_completion = Some(InlineCompletionState {
5686 inlay_ids,
5687 completion,
5688 completion_id: inline_completion.id,
5689 invalidation_range,
5690 });
5691
5692 cx.notify();
5693
5694 Some(())
5695 }
5696
5697 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5698 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5699 }
5700
5701 fn render_code_actions_indicator(
5702 &self,
5703 _style: &EditorStyle,
5704 row: DisplayRow,
5705 is_active: bool,
5706 cx: &mut Context<Self>,
5707 ) -> Option<IconButton> {
5708 if self.available_code_actions.is_some() {
5709 Some(
5710 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5711 .shape(ui::IconButtonShape::Square)
5712 .icon_size(IconSize::XSmall)
5713 .icon_color(Color::Muted)
5714 .toggle_state(is_active)
5715 .tooltip({
5716 let focus_handle = self.focus_handle.clone();
5717 move |window, cx| {
5718 Tooltip::for_action_in(
5719 "Toggle Code Actions",
5720 &ToggleCodeActions {
5721 deployed_from_indicator: None,
5722 },
5723 &focus_handle,
5724 window,
5725 cx,
5726 )
5727 }
5728 })
5729 .on_click(cx.listener(move |editor, _e, window, cx| {
5730 window.focus(&editor.focus_handle(cx));
5731 editor.toggle_code_actions(
5732 &ToggleCodeActions {
5733 deployed_from_indicator: Some(row),
5734 },
5735 window,
5736 cx,
5737 );
5738 })),
5739 )
5740 } else {
5741 None
5742 }
5743 }
5744
5745 fn clear_tasks(&mut self) {
5746 self.tasks.clear()
5747 }
5748
5749 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5750 if self.tasks.insert(key, value).is_some() {
5751 // This case should hopefully be rare, but just in case...
5752 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5753 }
5754 }
5755
5756 fn build_tasks_context(
5757 project: &Entity<Project>,
5758 buffer: &Entity<Buffer>,
5759 buffer_row: u32,
5760 tasks: &Arc<RunnableTasks>,
5761 cx: &mut Context<Self>,
5762 ) -> Task<Option<task::TaskContext>> {
5763 let position = Point::new(buffer_row, tasks.column);
5764 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5765 let location = Location {
5766 buffer: buffer.clone(),
5767 range: range_start..range_start,
5768 };
5769 // Fill in the environmental variables from the tree-sitter captures
5770 let mut captured_task_variables = TaskVariables::default();
5771 for (capture_name, value) in tasks.extra_variables.clone() {
5772 captured_task_variables.insert(
5773 task::VariableName::Custom(capture_name.into()),
5774 value.clone(),
5775 );
5776 }
5777 project.update(cx, |project, cx| {
5778 project.task_store().update(cx, |task_store, cx| {
5779 task_store.task_context_for_location(captured_task_variables, location, cx)
5780 })
5781 })
5782 }
5783
5784 pub fn spawn_nearest_task(
5785 &mut self,
5786 action: &SpawnNearestTask,
5787 window: &mut Window,
5788 cx: &mut Context<Self>,
5789 ) {
5790 let Some((workspace, _)) = self.workspace.clone() else {
5791 return;
5792 };
5793 let Some(project) = self.project.clone() else {
5794 return;
5795 };
5796
5797 // Try to find a closest, enclosing node using tree-sitter that has a
5798 // task
5799 let Some((buffer, buffer_row, tasks)) = self
5800 .find_enclosing_node_task(cx)
5801 // Or find the task that's closest in row-distance.
5802 .or_else(|| self.find_closest_task(cx))
5803 else {
5804 return;
5805 };
5806
5807 let reveal_strategy = action.reveal;
5808 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5809 cx.spawn_in(window, |_, mut cx| async move {
5810 let context = task_context.await?;
5811 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5812
5813 let resolved = resolved_task.resolved.as_mut()?;
5814 resolved.reveal = reveal_strategy;
5815
5816 workspace
5817 .update(&mut cx, |workspace, cx| {
5818 workspace::tasks::schedule_resolved_task(
5819 workspace,
5820 task_source_kind,
5821 resolved_task,
5822 false,
5823 cx,
5824 );
5825 })
5826 .ok()
5827 })
5828 .detach();
5829 }
5830
5831 fn find_closest_task(
5832 &mut self,
5833 cx: &mut Context<Self>,
5834 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5835 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5836
5837 let ((buffer_id, row), tasks) = self
5838 .tasks
5839 .iter()
5840 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5841
5842 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5843 let tasks = Arc::new(tasks.to_owned());
5844 Some((buffer, *row, tasks))
5845 }
5846
5847 fn find_enclosing_node_task(
5848 &mut self,
5849 cx: &mut Context<Self>,
5850 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5851 let snapshot = self.buffer.read(cx).snapshot(cx);
5852 let offset = self.selections.newest::<usize>(cx).head();
5853 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5854 let buffer_id = excerpt.buffer().remote_id();
5855
5856 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5857 let mut cursor = layer.node().walk();
5858
5859 while cursor.goto_first_child_for_byte(offset).is_some() {
5860 if cursor.node().end_byte() == offset {
5861 cursor.goto_next_sibling();
5862 }
5863 }
5864
5865 // Ascend to the smallest ancestor that contains the range and has a task.
5866 loop {
5867 let node = cursor.node();
5868 let node_range = node.byte_range();
5869 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5870
5871 // Check if this node contains our offset
5872 if node_range.start <= offset && node_range.end >= offset {
5873 // If it contains offset, check for task
5874 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5875 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5876 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5877 }
5878 }
5879
5880 if !cursor.goto_parent() {
5881 break;
5882 }
5883 }
5884 None
5885 }
5886
5887 fn render_run_indicator(
5888 &self,
5889 _style: &EditorStyle,
5890 is_active: bool,
5891 row: DisplayRow,
5892 cx: &mut Context<Self>,
5893 ) -> IconButton {
5894 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5895 .shape(ui::IconButtonShape::Square)
5896 .icon_size(IconSize::XSmall)
5897 .icon_color(Color::Muted)
5898 .toggle_state(is_active)
5899 .on_click(cx.listener(move |editor, _e, window, cx| {
5900 window.focus(&editor.focus_handle(cx));
5901 editor.toggle_code_actions(
5902 &ToggleCodeActions {
5903 deployed_from_indicator: Some(row),
5904 },
5905 window,
5906 cx,
5907 );
5908 }))
5909 }
5910
5911 pub fn context_menu_visible(&self) -> bool {
5912 !self.edit_prediction_preview_is_active()
5913 && self
5914 .context_menu
5915 .borrow()
5916 .as_ref()
5917 .map_or(false, |menu| menu.visible())
5918 }
5919
5920 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5921 self.context_menu
5922 .borrow()
5923 .as_ref()
5924 .map(|menu| menu.origin())
5925 }
5926
5927 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
5928 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
5929
5930 #[allow(clippy::too_many_arguments)]
5931 fn render_edit_prediction_popover(
5932 &mut self,
5933 text_bounds: &Bounds<Pixels>,
5934 content_origin: gpui::Point<Pixels>,
5935 editor_snapshot: &EditorSnapshot,
5936 visible_row_range: Range<DisplayRow>,
5937 scroll_top: f32,
5938 scroll_bottom: f32,
5939 line_layouts: &[LineWithInvisibles],
5940 line_height: Pixels,
5941 scroll_pixel_position: gpui::Point<Pixels>,
5942 newest_selection_head: Option<DisplayPoint>,
5943 editor_width: Pixels,
5944 style: &EditorStyle,
5945 window: &mut Window,
5946 cx: &mut App,
5947 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
5948 let active_inline_completion = self.active_inline_completion.as_ref()?;
5949
5950 if self.edit_prediction_visible_in_cursor_popover(true) {
5951 return None;
5952 }
5953
5954 match &active_inline_completion.completion {
5955 InlineCompletion::Move { target, .. } => {
5956 let target_display_point = target.to_display_point(editor_snapshot);
5957
5958 if self.edit_prediction_requires_modifier() {
5959 if !self.edit_prediction_preview_is_active() {
5960 return None;
5961 }
5962
5963 self.render_edit_prediction_modifier_jump_popover(
5964 text_bounds,
5965 content_origin,
5966 visible_row_range,
5967 line_layouts,
5968 line_height,
5969 scroll_pixel_position,
5970 newest_selection_head,
5971 target_display_point,
5972 window,
5973 cx,
5974 )
5975 } else {
5976 self.render_edit_prediction_eager_jump_popover(
5977 text_bounds,
5978 content_origin,
5979 editor_snapshot,
5980 visible_row_range,
5981 scroll_top,
5982 scroll_bottom,
5983 line_height,
5984 scroll_pixel_position,
5985 target_display_point,
5986 editor_width,
5987 window,
5988 cx,
5989 )
5990 }
5991 }
5992 InlineCompletion::Edit {
5993 display_mode: EditDisplayMode::Inline,
5994 ..
5995 } => None,
5996 InlineCompletion::Edit {
5997 display_mode: EditDisplayMode::TabAccept,
5998 edits,
5999 ..
6000 } => {
6001 let range = &edits.first()?.0;
6002 let target_display_point = range.end.to_display_point(editor_snapshot);
6003
6004 self.render_edit_prediction_end_of_line_popover(
6005 "Accept",
6006 editor_snapshot,
6007 visible_row_range,
6008 target_display_point,
6009 line_height,
6010 scroll_pixel_position,
6011 content_origin,
6012 editor_width,
6013 window,
6014 cx,
6015 )
6016 }
6017 InlineCompletion::Edit {
6018 edits,
6019 edit_preview,
6020 display_mode: EditDisplayMode::DiffPopover,
6021 snapshot,
6022 } => self.render_edit_prediction_diff_popover(
6023 text_bounds,
6024 content_origin,
6025 editor_snapshot,
6026 visible_row_range,
6027 line_layouts,
6028 line_height,
6029 scroll_pixel_position,
6030 newest_selection_head,
6031 editor_width,
6032 style,
6033 edits,
6034 edit_preview,
6035 snapshot,
6036 window,
6037 cx,
6038 ),
6039 }
6040 }
6041
6042 #[allow(clippy::too_many_arguments)]
6043 fn render_edit_prediction_modifier_jump_popover(
6044 &mut self,
6045 text_bounds: &Bounds<Pixels>,
6046 content_origin: gpui::Point<Pixels>,
6047 visible_row_range: Range<DisplayRow>,
6048 line_layouts: &[LineWithInvisibles],
6049 line_height: Pixels,
6050 scroll_pixel_position: gpui::Point<Pixels>,
6051 newest_selection_head: Option<DisplayPoint>,
6052 target_display_point: DisplayPoint,
6053 window: &mut Window,
6054 cx: &mut App,
6055 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6056 let scrolled_content_origin =
6057 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6058
6059 const SCROLL_PADDING_Y: Pixels = px(12.);
6060
6061 if target_display_point.row() < visible_row_range.start {
6062 return self.render_edit_prediction_scroll_popover(
6063 |_| SCROLL_PADDING_Y,
6064 IconName::ArrowUp,
6065 visible_row_range,
6066 line_layouts,
6067 newest_selection_head,
6068 scrolled_content_origin,
6069 window,
6070 cx,
6071 );
6072 } else if target_display_point.row() >= visible_row_range.end {
6073 return self.render_edit_prediction_scroll_popover(
6074 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6075 IconName::ArrowDown,
6076 visible_row_range,
6077 line_layouts,
6078 newest_selection_head,
6079 scrolled_content_origin,
6080 window,
6081 cx,
6082 );
6083 }
6084
6085 const POLE_WIDTH: Pixels = px(2.);
6086
6087 let line_layout =
6088 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6089 let target_column = target_display_point.column() as usize;
6090
6091 let target_x = line_layout.x_for_index(target_column);
6092 let target_y =
6093 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6094
6095 let flag_on_right = target_x < text_bounds.size.width / 2.;
6096
6097 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6098 border_color.l += 0.001;
6099
6100 let mut element = v_flex()
6101 .items_end()
6102 .when(flag_on_right, |el| el.items_start())
6103 .child(if flag_on_right {
6104 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6105 .rounded_bl(px(0.))
6106 .rounded_tl(px(0.))
6107 .border_l_2()
6108 .border_color(border_color)
6109 } else {
6110 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6111 .rounded_br(px(0.))
6112 .rounded_tr(px(0.))
6113 .border_r_2()
6114 .border_color(border_color)
6115 })
6116 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6117 .into_any();
6118
6119 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6120
6121 let mut origin = scrolled_content_origin + point(target_x, target_y)
6122 - point(
6123 if flag_on_right {
6124 POLE_WIDTH
6125 } else {
6126 size.width - POLE_WIDTH
6127 },
6128 size.height - line_height,
6129 );
6130
6131 origin.x = origin.x.max(content_origin.x);
6132
6133 element.prepaint_at(origin, window, cx);
6134
6135 Some((element, origin))
6136 }
6137
6138 #[allow(clippy::too_many_arguments)]
6139 fn render_edit_prediction_scroll_popover(
6140 &mut self,
6141 to_y: impl Fn(Size<Pixels>) -> Pixels,
6142 scroll_icon: IconName,
6143 visible_row_range: Range<DisplayRow>,
6144 line_layouts: &[LineWithInvisibles],
6145 newest_selection_head: Option<DisplayPoint>,
6146 scrolled_content_origin: gpui::Point<Pixels>,
6147 window: &mut Window,
6148 cx: &mut App,
6149 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6150 let mut element = self
6151 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6152 .into_any();
6153
6154 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6155
6156 let cursor = newest_selection_head?;
6157 let cursor_row_layout =
6158 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6159 let cursor_column = cursor.column() as usize;
6160
6161 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6162
6163 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6164
6165 element.prepaint_at(origin, window, cx);
6166 Some((element, origin))
6167 }
6168
6169 #[allow(clippy::too_many_arguments)]
6170 fn render_edit_prediction_eager_jump_popover(
6171 &mut self,
6172 text_bounds: &Bounds<Pixels>,
6173 content_origin: gpui::Point<Pixels>,
6174 editor_snapshot: &EditorSnapshot,
6175 visible_row_range: Range<DisplayRow>,
6176 scroll_top: f32,
6177 scroll_bottom: f32,
6178 line_height: Pixels,
6179 scroll_pixel_position: gpui::Point<Pixels>,
6180 target_display_point: DisplayPoint,
6181 editor_width: Pixels,
6182 window: &mut Window,
6183 cx: &mut App,
6184 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6185 if target_display_point.row().as_f32() < scroll_top {
6186 let mut element = self
6187 .render_edit_prediction_line_popover(
6188 "Jump to Edit",
6189 Some(IconName::ArrowUp),
6190 window,
6191 cx,
6192 )?
6193 .into_any();
6194
6195 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6196 let offset = point(
6197 (text_bounds.size.width - size.width) / 2.,
6198 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6199 );
6200
6201 let origin = text_bounds.origin + offset;
6202 element.prepaint_at(origin, window, cx);
6203 Some((element, origin))
6204 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6205 let mut element = self
6206 .render_edit_prediction_line_popover(
6207 "Jump to Edit",
6208 Some(IconName::ArrowDown),
6209 window,
6210 cx,
6211 )?
6212 .into_any();
6213
6214 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6215 let offset = point(
6216 (text_bounds.size.width - size.width) / 2.,
6217 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6218 );
6219
6220 let origin = text_bounds.origin + offset;
6221 element.prepaint_at(origin, window, cx);
6222 Some((element, origin))
6223 } else {
6224 self.render_edit_prediction_end_of_line_popover(
6225 "Jump to Edit",
6226 editor_snapshot,
6227 visible_row_range,
6228 target_display_point,
6229 line_height,
6230 scroll_pixel_position,
6231 content_origin,
6232 editor_width,
6233 window,
6234 cx,
6235 )
6236 }
6237 }
6238
6239 #[allow(clippy::too_many_arguments)]
6240 fn render_edit_prediction_end_of_line_popover(
6241 self: &mut Editor,
6242 label: &'static str,
6243 editor_snapshot: &EditorSnapshot,
6244 visible_row_range: Range<DisplayRow>,
6245 target_display_point: DisplayPoint,
6246 line_height: Pixels,
6247 scroll_pixel_position: gpui::Point<Pixels>,
6248 content_origin: gpui::Point<Pixels>,
6249 editor_width: Pixels,
6250 window: &mut Window,
6251 cx: &mut App,
6252 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6253 let target_line_end = DisplayPoint::new(
6254 target_display_point.row(),
6255 editor_snapshot.line_len(target_display_point.row()),
6256 );
6257
6258 let mut element = self
6259 .render_edit_prediction_line_popover(label, None, window, cx)?
6260 .into_any();
6261
6262 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6263
6264 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6265
6266 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6267 let mut origin = start_point
6268 + line_origin
6269 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6270 origin.x = origin.x.max(content_origin.x);
6271
6272 let max_x = content_origin.x + editor_width - size.width;
6273
6274 if origin.x > max_x {
6275 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6276
6277 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6278 origin.y += offset;
6279 IconName::ArrowUp
6280 } else {
6281 origin.y -= offset;
6282 IconName::ArrowDown
6283 };
6284
6285 element = self
6286 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6287 .into_any();
6288
6289 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6290
6291 origin.x = content_origin.x + editor_width - size.width - px(2.);
6292 }
6293
6294 element.prepaint_at(origin, window, cx);
6295 Some((element, origin))
6296 }
6297
6298 #[allow(clippy::too_many_arguments)]
6299 fn render_edit_prediction_diff_popover(
6300 self: &Editor,
6301 text_bounds: &Bounds<Pixels>,
6302 content_origin: gpui::Point<Pixels>,
6303 editor_snapshot: &EditorSnapshot,
6304 visible_row_range: Range<DisplayRow>,
6305 line_layouts: &[LineWithInvisibles],
6306 line_height: Pixels,
6307 scroll_pixel_position: gpui::Point<Pixels>,
6308 newest_selection_head: Option<DisplayPoint>,
6309 editor_width: Pixels,
6310 style: &EditorStyle,
6311 edits: &Vec<(Range<Anchor>, String)>,
6312 edit_preview: &Option<language::EditPreview>,
6313 snapshot: &language::BufferSnapshot,
6314 window: &mut Window,
6315 cx: &mut App,
6316 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6317 let edit_start = edits
6318 .first()
6319 .unwrap()
6320 .0
6321 .start
6322 .to_display_point(editor_snapshot);
6323 let edit_end = edits
6324 .last()
6325 .unwrap()
6326 .0
6327 .end
6328 .to_display_point(editor_snapshot);
6329
6330 let is_visible = visible_row_range.contains(&edit_start.row())
6331 || visible_row_range.contains(&edit_end.row());
6332 if !is_visible {
6333 return None;
6334 }
6335
6336 let highlighted_edits =
6337 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6338
6339 let styled_text = highlighted_edits.to_styled_text(&style.text);
6340 let line_count = highlighted_edits.text.lines().count();
6341
6342 const BORDER_WIDTH: Pixels = px(1.);
6343
6344 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6345 let has_keybind = keybind.is_some();
6346
6347 let mut element = h_flex()
6348 .items_start()
6349 .child(
6350 h_flex()
6351 .bg(cx.theme().colors().editor_background)
6352 .border(BORDER_WIDTH)
6353 .shadow_sm()
6354 .border_color(cx.theme().colors().border)
6355 .rounded_l_lg()
6356 .when(line_count > 1, |el| el.rounded_br_lg())
6357 .pr_1()
6358 .child(styled_text),
6359 )
6360 .child(
6361 h_flex()
6362 .h(line_height + BORDER_WIDTH * px(2.))
6363 .px_1p5()
6364 .gap_1()
6365 // Workaround: For some reason, there's a gap if we don't do this
6366 .ml(-BORDER_WIDTH)
6367 .shadow(smallvec![gpui::BoxShadow {
6368 color: gpui::black().opacity(0.05),
6369 offset: point(px(1.), px(1.)),
6370 blur_radius: px(2.),
6371 spread_radius: px(0.),
6372 }])
6373 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6374 .border(BORDER_WIDTH)
6375 .border_color(cx.theme().colors().border)
6376 .rounded_r_lg()
6377 .id("edit_prediction_diff_popover_keybind")
6378 .when(!has_keybind, |el| {
6379 let status_colors = cx.theme().status();
6380
6381 el.bg(status_colors.error_background)
6382 .border_color(status_colors.error.opacity(0.6))
6383 .child(Icon::new(IconName::Info).color(Color::Error))
6384 .cursor_default()
6385 .hoverable_tooltip(move |_window, cx| {
6386 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6387 })
6388 })
6389 .children(keybind),
6390 )
6391 .into_any();
6392
6393 let longest_row =
6394 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6395 let longest_line_width = if visible_row_range.contains(&longest_row) {
6396 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6397 } else {
6398 layout_line(
6399 longest_row,
6400 editor_snapshot,
6401 style,
6402 editor_width,
6403 |_| false,
6404 window,
6405 cx,
6406 )
6407 .width
6408 };
6409
6410 let viewport_bounds =
6411 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6412 right: -EditorElement::SCROLLBAR_WIDTH,
6413 ..Default::default()
6414 });
6415
6416 let x_after_longest =
6417 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6418 - scroll_pixel_position.x;
6419
6420 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6421
6422 // Fully visible if it can be displayed within the window (allow overlapping other
6423 // panes). However, this is only allowed if the popover starts within text_bounds.
6424 let can_position_to_the_right = x_after_longest < text_bounds.right()
6425 && x_after_longest + element_bounds.width < viewport_bounds.right();
6426
6427 let mut origin = if can_position_to_the_right {
6428 point(
6429 x_after_longest,
6430 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6431 - scroll_pixel_position.y,
6432 )
6433 } else {
6434 let cursor_row = newest_selection_head.map(|head| head.row());
6435 let above_edit = edit_start
6436 .row()
6437 .0
6438 .checked_sub(line_count as u32)
6439 .map(DisplayRow);
6440 let below_edit = Some(edit_end.row() + 1);
6441 let above_cursor =
6442 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6443 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6444
6445 // Place the edit popover adjacent to the edit if there is a location
6446 // available that is onscreen and does not obscure the cursor. Otherwise,
6447 // place it adjacent to the cursor.
6448 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6449 .into_iter()
6450 .flatten()
6451 .find(|&start_row| {
6452 let end_row = start_row + line_count as u32;
6453 visible_row_range.contains(&start_row)
6454 && visible_row_range.contains(&end_row)
6455 && cursor_row.map_or(true, |cursor_row| {
6456 !((start_row..end_row).contains(&cursor_row))
6457 })
6458 })?;
6459
6460 content_origin
6461 + point(
6462 -scroll_pixel_position.x,
6463 row_target.as_f32() * line_height - scroll_pixel_position.y,
6464 )
6465 };
6466
6467 origin.x -= BORDER_WIDTH;
6468
6469 window.defer_draw(element, origin, 1);
6470
6471 // Do not return an element, since it will already be drawn due to defer_draw.
6472 None
6473 }
6474
6475 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6476 px(30.)
6477 }
6478
6479 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6480 if self.read_only(cx) {
6481 cx.theme().players().read_only()
6482 } else {
6483 self.style.as_ref().unwrap().local_player
6484 }
6485 }
6486
6487 fn render_edit_prediction_accept_keybind(
6488 &self,
6489 window: &mut Window,
6490 cx: &App,
6491 ) -> Option<AnyElement> {
6492 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6493 let accept_keystroke = accept_binding.keystroke()?;
6494
6495 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6496
6497 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6498 Color::Accent
6499 } else {
6500 Color::Muted
6501 };
6502
6503 h_flex()
6504 .px_0p5()
6505 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6506 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6507 .text_size(TextSize::XSmall.rems(cx))
6508 .child(h_flex().children(ui::render_modifiers(
6509 &accept_keystroke.modifiers,
6510 PlatformStyle::platform(),
6511 Some(modifiers_color),
6512 Some(IconSize::XSmall.rems().into()),
6513 true,
6514 )))
6515 .when(is_platform_style_mac, |parent| {
6516 parent.child(accept_keystroke.key.clone())
6517 })
6518 .when(!is_platform_style_mac, |parent| {
6519 parent.child(
6520 Key::new(
6521 util::capitalize(&accept_keystroke.key),
6522 Some(Color::Default),
6523 )
6524 .size(Some(IconSize::XSmall.rems().into())),
6525 )
6526 })
6527 .into_any()
6528 .into()
6529 }
6530
6531 fn render_edit_prediction_line_popover(
6532 &self,
6533 label: impl Into<SharedString>,
6534 icon: Option<IconName>,
6535 window: &mut Window,
6536 cx: &App,
6537 ) -> Option<Stateful<Div>> {
6538 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6539
6540 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6541 let has_keybind = keybind.is_some();
6542
6543 let result = h_flex()
6544 .id("ep-line-popover")
6545 .py_0p5()
6546 .pl_1()
6547 .pr(padding_right)
6548 .gap_1()
6549 .rounded_md()
6550 .border_1()
6551 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6552 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6553 .shadow_sm()
6554 .when(!has_keybind, |el| {
6555 let status_colors = cx.theme().status();
6556
6557 el.bg(status_colors.error_background)
6558 .border_color(status_colors.error.opacity(0.6))
6559 .pl_2()
6560 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
6561 .cursor_default()
6562 .hoverable_tooltip(move |_window, cx| {
6563 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6564 })
6565 })
6566 .children(keybind)
6567 .child(
6568 Label::new(label)
6569 .size(LabelSize::Small)
6570 .when(!has_keybind, |el| {
6571 el.color(cx.theme().status().error.into()).strikethrough()
6572 }),
6573 )
6574 .when(!has_keybind, |el| {
6575 el.child(
6576 h_flex().ml_1().child(
6577 Icon::new(IconName::Info)
6578 .size(IconSize::Small)
6579 .color(cx.theme().status().error.into()),
6580 ),
6581 )
6582 })
6583 .when_some(icon, |element, icon| {
6584 element.child(
6585 div()
6586 .mt(px(1.5))
6587 .child(Icon::new(icon).size(IconSize::Small)),
6588 )
6589 });
6590
6591 Some(result)
6592 }
6593
6594 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6595 let accent_color = cx.theme().colors().text_accent;
6596 let editor_bg_color = cx.theme().colors().editor_background;
6597 editor_bg_color.blend(accent_color.opacity(0.1))
6598 }
6599
6600 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6601 let accent_color = cx.theme().colors().text_accent;
6602 let editor_bg_color = cx.theme().colors().editor_background;
6603 editor_bg_color.blend(accent_color.opacity(0.6))
6604 }
6605
6606 #[allow(clippy::too_many_arguments)]
6607 fn render_edit_prediction_cursor_popover(
6608 &self,
6609 min_width: Pixels,
6610 max_width: Pixels,
6611 cursor_point: Point,
6612 style: &EditorStyle,
6613 accept_keystroke: Option<&gpui::Keystroke>,
6614 _window: &Window,
6615 cx: &mut Context<Editor>,
6616 ) -> Option<AnyElement> {
6617 let provider = self.edit_prediction_provider.as_ref()?;
6618
6619 if provider.provider.needs_terms_acceptance(cx) {
6620 return Some(
6621 h_flex()
6622 .min_w(min_width)
6623 .flex_1()
6624 .px_2()
6625 .py_1()
6626 .gap_3()
6627 .elevation_2(cx)
6628 .hover(|style| style.bg(cx.theme().colors().element_hover))
6629 .id("accept-terms")
6630 .cursor_pointer()
6631 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6632 .on_click(cx.listener(|this, _event, window, cx| {
6633 cx.stop_propagation();
6634 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6635 window.dispatch_action(
6636 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6637 cx,
6638 );
6639 }))
6640 .child(
6641 h_flex()
6642 .flex_1()
6643 .gap_2()
6644 .child(Icon::new(IconName::ZedPredict))
6645 .child(Label::new("Accept Terms of Service"))
6646 .child(div().w_full())
6647 .child(
6648 Icon::new(IconName::ArrowUpRight)
6649 .color(Color::Muted)
6650 .size(IconSize::Small),
6651 )
6652 .into_any_element(),
6653 )
6654 .into_any(),
6655 );
6656 }
6657
6658 let is_refreshing = provider.provider.is_refreshing(cx);
6659
6660 fn pending_completion_container() -> Div {
6661 h_flex()
6662 .h_full()
6663 .flex_1()
6664 .gap_2()
6665 .child(Icon::new(IconName::ZedPredict))
6666 }
6667
6668 let completion = match &self.active_inline_completion {
6669 Some(prediction) => {
6670 if !self.has_visible_completions_menu() {
6671 const RADIUS: Pixels = px(6.);
6672 const BORDER_WIDTH: Pixels = px(1.);
6673
6674 return Some(
6675 h_flex()
6676 .elevation_2(cx)
6677 .border(BORDER_WIDTH)
6678 .border_color(cx.theme().colors().border)
6679 .when(accept_keystroke.is_none(), |el| {
6680 el.border_color(cx.theme().status().error)
6681 })
6682 .rounded(RADIUS)
6683 .rounded_tl(px(0.))
6684 .overflow_hidden()
6685 .child(div().px_1p5().child(match &prediction.completion {
6686 InlineCompletion::Move { target, snapshot } => {
6687 use text::ToPoint as _;
6688 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6689 {
6690 Icon::new(IconName::ZedPredictDown)
6691 } else {
6692 Icon::new(IconName::ZedPredictUp)
6693 }
6694 }
6695 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6696 }))
6697 .child(
6698 h_flex()
6699 .gap_1()
6700 .py_1()
6701 .px_2()
6702 .rounded_r(RADIUS - BORDER_WIDTH)
6703 .border_l_1()
6704 .border_color(cx.theme().colors().border)
6705 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6706 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6707 el.child(
6708 Label::new("Hold")
6709 .size(LabelSize::Small)
6710 .when(accept_keystroke.is_none(), |el| {
6711 el.strikethrough()
6712 })
6713 .line_height_style(LineHeightStyle::UiLabel),
6714 )
6715 })
6716 .id("edit_prediction_cursor_popover_keybind")
6717 .when(accept_keystroke.is_none(), |el| {
6718 let status_colors = cx.theme().status();
6719
6720 el.bg(status_colors.error_background)
6721 .border_color(status_colors.error.opacity(0.6))
6722 .child(Icon::new(IconName::Info).color(Color::Error))
6723 .cursor_default()
6724 .hoverable_tooltip(move |_window, cx| {
6725 cx.new(|_| MissingEditPredictionKeybindingTooltip)
6726 .into()
6727 })
6728 })
6729 .when_some(
6730 accept_keystroke.as_ref(),
6731 |el, accept_keystroke| {
6732 el.child(h_flex().children(ui::render_modifiers(
6733 &accept_keystroke.modifiers,
6734 PlatformStyle::platform(),
6735 Some(Color::Default),
6736 Some(IconSize::XSmall.rems().into()),
6737 false,
6738 )))
6739 },
6740 ),
6741 )
6742 .into_any(),
6743 );
6744 }
6745
6746 self.render_edit_prediction_cursor_popover_preview(
6747 prediction,
6748 cursor_point,
6749 style,
6750 cx,
6751 )?
6752 }
6753
6754 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6755 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6756 stale_completion,
6757 cursor_point,
6758 style,
6759 cx,
6760 )?,
6761
6762 None => {
6763 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6764 }
6765 },
6766
6767 None => pending_completion_container().child(Label::new("No Prediction")),
6768 };
6769
6770 let completion = if is_refreshing {
6771 completion
6772 .with_animation(
6773 "loading-completion",
6774 Animation::new(Duration::from_secs(2))
6775 .repeat()
6776 .with_easing(pulsating_between(0.4, 0.8)),
6777 |label, delta| label.opacity(delta),
6778 )
6779 .into_any_element()
6780 } else {
6781 completion.into_any_element()
6782 };
6783
6784 let has_completion = self.active_inline_completion.is_some();
6785
6786 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6787 Some(
6788 h_flex()
6789 .min_w(min_width)
6790 .max_w(max_width)
6791 .flex_1()
6792 .elevation_2(cx)
6793 .border_color(cx.theme().colors().border)
6794 .child(
6795 div()
6796 .flex_1()
6797 .py_1()
6798 .px_2()
6799 .overflow_hidden()
6800 .child(completion),
6801 )
6802 .when_some(accept_keystroke, |el, accept_keystroke| {
6803 if !accept_keystroke.modifiers.modified() {
6804 return el;
6805 }
6806
6807 el.child(
6808 h_flex()
6809 .h_full()
6810 .border_l_1()
6811 .rounded_r_lg()
6812 .border_color(cx.theme().colors().border)
6813 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6814 .gap_1()
6815 .py_1()
6816 .px_2()
6817 .child(
6818 h_flex()
6819 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6820 .when(is_platform_style_mac, |parent| parent.gap_1())
6821 .child(h_flex().children(ui::render_modifiers(
6822 &accept_keystroke.modifiers,
6823 PlatformStyle::platform(),
6824 Some(if !has_completion {
6825 Color::Muted
6826 } else {
6827 Color::Default
6828 }),
6829 None,
6830 false,
6831 ))),
6832 )
6833 .child(Label::new("Preview").into_any_element())
6834 .opacity(if has_completion { 1.0 } else { 0.4 }),
6835 )
6836 })
6837 .into_any(),
6838 )
6839 }
6840
6841 fn render_edit_prediction_cursor_popover_preview(
6842 &self,
6843 completion: &InlineCompletionState,
6844 cursor_point: Point,
6845 style: &EditorStyle,
6846 cx: &mut Context<Editor>,
6847 ) -> Option<Div> {
6848 use text::ToPoint as _;
6849
6850 fn render_relative_row_jump(
6851 prefix: impl Into<String>,
6852 current_row: u32,
6853 target_row: u32,
6854 ) -> Div {
6855 let (row_diff, arrow) = if target_row < current_row {
6856 (current_row - target_row, IconName::ArrowUp)
6857 } else {
6858 (target_row - current_row, IconName::ArrowDown)
6859 };
6860
6861 h_flex()
6862 .child(
6863 Label::new(format!("{}{}", prefix.into(), row_diff))
6864 .color(Color::Muted)
6865 .size(LabelSize::Small),
6866 )
6867 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6868 }
6869
6870 match &completion.completion {
6871 InlineCompletion::Move {
6872 target, snapshot, ..
6873 } => Some(
6874 h_flex()
6875 .px_2()
6876 .gap_2()
6877 .flex_1()
6878 .child(
6879 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6880 Icon::new(IconName::ZedPredictDown)
6881 } else {
6882 Icon::new(IconName::ZedPredictUp)
6883 },
6884 )
6885 .child(Label::new("Jump to Edit")),
6886 ),
6887
6888 InlineCompletion::Edit {
6889 edits,
6890 edit_preview,
6891 snapshot,
6892 display_mode: _,
6893 } => {
6894 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6895
6896 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6897 &snapshot,
6898 &edits,
6899 edit_preview.as_ref()?,
6900 true,
6901 cx,
6902 )
6903 .first_line_preview();
6904
6905 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6906 .with_default_highlights(&style.text, highlighted_edits.highlights);
6907
6908 let preview = h_flex()
6909 .gap_1()
6910 .min_w_16()
6911 .child(styled_text)
6912 .when(has_more_lines, |parent| parent.child("…"));
6913
6914 let left = if first_edit_row != cursor_point.row {
6915 render_relative_row_jump("", cursor_point.row, first_edit_row)
6916 .into_any_element()
6917 } else {
6918 Icon::new(IconName::ZedPredict).into_any_element()
6919 };
6920
6921 Some(
6922 h_flex()
6923 .h_full()
6924 .flex_1()
6925 .gap_2()
6926 .pr_1()
6927 .overflow_x_hidden()
6928 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6929 .child(left)
6930 .child(preview),
6931 )
6932 }
6933 }
6934 }
6935
6936 fn render_context_menu(
6937 &self,
6938 style: &EditorStyle,
6939 max_height_in_lines: u32,
6940 y_flipped: bool,
6941 window: &mut Window,
6942 cx: &mut Context<Editor>,
6943 ) -> Option<AnyElement> {
6944 let menu = self.context_menu.borrow();
6945 let menu = menu.as_ref()?;
6946 if !menu.visible() {
6947 return None;
6948 };
6949 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6950 }
6951
6952 fn render_context_menu_aside(
6953 &mut self,
6954 max_size: Size<Pixels>,
6955 window: &mut Window,
6956 cx: &mut Context<Editor>,
6957 ) -> Option<AnyElement> {
6958 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6959 if menu.visible() {
6960 menu.render_aside(self, max_size, window, cx)
6961 } else {
6962 None
6963 }
6964 })
6965 }
6966
6967 fn hide_context_menu(
6968 &mut self,
6969 window: &mut Window,
6970 cx: &mut Context<Self>,
6971 ) -> Option<CodeContextMenu> {
6972 cx.notify();
6973 self.completion_tasks.clear();
6974 let context_menu = self.context_menu.borrow_mut().take();
6975 self.stale_inline_completion_in_menu.take();
6976 self.update_visible_inline_completion(window, cx);
6977 context_menu
6978 }
6979
6980 fn show_snippet_choices(
6981 &mut self,
6982 choices: &Vec<String>,
6983 selection: Range<Anchor>,
6984 cx: &mut Context<Self>,
6985 ) {
6986 if selection.start.buffer_id.is_none() {
6987 return;
6988 }
6989 let buffer_id = selection.start.buffer_id.unwrap();
6990 let buffer = self.buffer().read(cx).buffer(buffer_id);
6991 let id = post_inc(&mut self.next_completion_id);
6992
6993 if let Some(buffer) = buffer {
6994 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6995 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6996 ));
6997 }
6998 }
6999
7000 pub fn insert_snippet(
7001 &mut self,
7002 insertion_ranges: &[Range<usize>],
7003 snippet: Snippet,
7004 window: &mut Window,
7005 cx: &mut Context<Self>,
7006 ) -> Result<()> {
7007 struct Tabstop<T> {
7008 is_end_tabstop: bool,
7009 ranges: Vec<Range<T>>,
7010 choices: Option<Vec<String>>,
7011 }
7012
7013 let tabstops = self.buffer.update(cx, |buffer, cx| {
7014 let snippet_text: Arc<str> = snippet.text.clone().into();
7015 buffer.edit(
7016 insertion_ranges
7017 .iter()
7018 .cloned()
7019 .map(|range| (range, snippet_text.clone())),
7020 Some(AutoindentMode::EachLine),
7021 cx,
7022 );
7023
7024 let snapshot = &*buffer.read(cx);
7025 let snippet = &snippet;
7026 snippet
7027 .tabstops
7028 .iter()
7029 .map(|tabstop| {
7030 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7031 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7032 });
7033 let mut tabstop_ranges = tabstop
7034 .ranges
7035 .iter()
7036 .flat_map(|tabstop_range| {
7037 let mut delta = 0_isize;
7038 insertion_ranges.iter().map(move |insertion_range| {
7039 let insertion_start = insertion_range.start as isize + delta;
7040 delta +=
7041 snippet.text.len() as isize - insertion_range.len() as isize;
7042
7043 let start = ((insertion_start + tabstop_range.start) as usize)
7044 .min(snapshot.len());
7045 let end = ((insertion_start + tabstop_range.end) as usize)
7046 .min(snapshot.len());
7047 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7048 })
7049 })
7050 .collect::<Vec<_>>();
7051 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7052
7053 Tabstop {
7054 is_end_tabstop,
7055 ranges: tabstop_ranges,
7056 choices: tabstop.choices.clone(),
7057 }
7058 })
7059 .collect::<Vec<_>>()
7060 });
7061 if let Some(tabstop) = tabstops.first() {
7062 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7063 s.select_ranges(tabstop.ranges.iter().cloned());
7064 });
7065
7066 if let Some(choices) = &tabstop.choices {
7067 if let Some(selection) = tabstop.ranges.first() {
7068 self.show_snippet_choices(choices, selection.clone(), cx)
7069 }
7070 }
7071
7072 // If we're already at the last tabstop and it's at the end of the snippet,
7073 // we're done, we don't need to keep the state around.
7074 if !tabstop.is_end_tabstop {
7075 let choices = tabstops
7076 .iter()
7077 .map(|tabstop| tabstop.choices.clone())
7078 .collect();
7079
7080 let ranges = tabstops
7081 .into_iter()
7082 .map(|tabstop| tabstop.ranges)
7083 .collect::<Vec<_>>();
7084
7085 self.snippet_stack.push(SnippetState {
7086 active_index: 0,
7087 ranges,
7088 choices,
7089 });
7090 }
7091
7092 // Check whether the just-entered snippet ends with an auto-closable bracket.
7093 if self.autoclose_regions.is_empty() {
7094 let snapshot = self.buffer.read(cx).snapshot(cx);
7095 for selection in &mut self.selections.all::<Point>(cx) {
7096 let selection_head = selection.head();
7097 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7098 continue;
7099 };
7100
7101 let mut bracket_pair = None;
7102 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7103 let prev_chars = snapshot
7104 .reversed_chars_at(selection_head)
7105 .collect::<String>();
7106 for (pair, enabled) in scope.brackets() {
7107 if enabled
7108 && pair.close
7109 && prev_chars.starts_with(pair.start.as_str())
7110 && next_chars.starts_with(pair.end.as_str())
7111 {
7112 bracket_pair = Some(pair.clone());
7113 break;
7114 }
7115 }
7116 if let Some(pair) = bracket_pair {
7117 let start = snapshot.anchor_after(selection_head);
7118 let end = snapshot.anchor_after(selection_head);
7119 self.autoclose_regions.push(AutocloseRegion {
7120 selection_id: selection.id,
7121 range: start..end,
7122 pair,
7123 });
7124 }
7125 }
7126 }
7127 }
7128 Ok(())
7129 }
7130
7131 pub fn move_to_next_snippet_tabstop(
7132 &mut self,
7133 window: &mut Window,
7134 cx: &mut Context<Self>,
7135 ) -> bool {
7136 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7137 }
7138
7139 pub fn move_to_prev_snippet_tabstop(
7140 &mut self,
7141 window: &mut Window,
7142 cx: &mut Context<Self>,
7143 ) -> bool {
7144 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7145 }
7146
7147 pub fn move_to_snippet_tabstop(
7148 &mut self,
7149 bias: Bias,
7150 window: &mut Window,
7151 cx: &mut Context<Self>,
7152 ) -> bool {
7153 if let Some(mut snippet) = self.snippet_stack.pop() {
7154 match bias {
7155 Bias::Left => {
7156 if snippet.active_index > 0 {
7157 snippet.active_index -= 1;
7158 } else {
7159 self.snippet_stack.push(snippet);
7160 return false;
7161 }
7162 }
7163 Bias::Right => {
7164 if snippet.active_index + 1 < snippet.ranges.len() {
7165 snippet.active_index += 1;
7166 } else {
7167 self.snippet_stack.push(snippet);
7168 return false;
7169 }
7170 }
7171 }
7172 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7173 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7174 s.select_anchor_ranges(current_ranges.iter().cloned())
7175 });
7176
7177 if let Some(choices) = &snippet.choices[snippet.active_index] {
7178 if let Some(selection) = current_ranges.first() {
7179 self.show_snippet_choices(&choices, selection.clone(), cx);
7180 }
7181 }
7182
7183 // If snippet state is not at the last tabstop, push it back on the stack
7184 if snippet.active_index + 1 < snippet.ranges.len() {
7185 self.snippet_stack.push(snippet);
7186 }
7187 return true;
7188 }
7189 }
7190
7191 false
7192 }
7193
7194 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7195 self.transact(window, cx, |this, window, cx| {
7196 this.select_all(&SelectAll, window, cx);
7197 this.insert("", window, cx);
7198 });
7199 }
7200
7201 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7202 self.transact(window, cx, |this, window, cx| {
7203 this.select_autoclose_pair(window, cx);
7204 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7205 if !this.linked_edit_ranges.is_empty() {
7206 let selections = this.selections.all::<MultiBufferPoint>(cx);
7207 let snapshot = this.buffer.read(cx).snapshot(cx);
7208
7209 for selection in selections.iter() {
7210 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7211 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7212 if selection_start.buffer_id != selection_end.buffer_id {
7213 continue;
7214 }
7215 if let Some(ranges) =
7216 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7217 {
7218 for (buffer, entries) in ranges {
7219 linked_ranges.entry(buffer).or_default().extend(entries);
7220 }
7221 }
7222 }
7223 }
7224
7225 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7226 if !this.selections.line_mode {
7227 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7228 for selection in &mut selections {
7229 if selection.is_empty() {
7230 let old_head = selection.head();
7231 let mut new_head =
7232 movement::left(&display_map, old_head.to_display_point(&display_map))
7233 .to_point(&display_map);
7234 if let Some((buffer, line_buffer_range)) = display_map
7235 .buffer_snapshot
7236 .buffer_line_for_row(MultiBufferRow(old_head.row))
7237 {
7238 let indent_size =
7239 buffer.indent_size_for_line(line_buffer_range.start.row);
7240 let indent_len = match indent_size.kind {
7241 IndentKind::Space => {
7242 buffer.settings_at(line_buffer_range.start, cx).tab_size
7243 }
7244 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7245 };
7246 if old_head.column <= indent_size.len && old_head.column > 0 {
7247 let indent_len = indent_len.get();
7248 new_head = cmp::min(
7249 new_head,
7250 MultiBufferPoint::new(
7251 old_head.row,
7252 ((old_head.column - 1) / indent_len) * indent_len,
7253 ),
7254 );
7255 }
7256 }
7257
7258 selection.set_head(new_head, SelectionGoal::None);
7259 }
7260 }
7261 }
7262
7263 this.signature_help_state.set_backspace_pressed(true);
7264 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7265 s.select(selections)
7266 });
7267 this.insert("", window, cx);
7268 let empty_str: Arc<str> = Arc::from("");
7269 for (buffer, edits) in linked_ranges {
7270 let snapshot = buffer.read(cx).snapshot();
7271 use text::ToPoint as TP;
7272
7273 let edits = edits
7274 .into_iter()
7275 .map(|range| {
7276 let end_point = TP::to_point(&range.end, &snapshot);
7277 let mut start_point = TP::to_point(&range.start, &snapshot);
7278
7279 if end_point == start_point {
7280 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7281 .saturating_sub(1);
7282 start_point =
7283 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7284 };
7285
7286 (start_point..end_point, empty_str.clone())
7287 })
7288 .sorted_by_key(|(range, _)| range.start)
7289 .collect::<Vec<_>>();
7290 buffer.update(cx, |this, cx| {
7291 this.edit(edits, None, cx);
7292 })
7293 }
7294 this.refresh_inline_completion(true, false, window, cx);
7295 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7296 });
7297 }
7298
7299 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7300 self.transact(window, cx, |this, window, cx| {
7301 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7302 let line_mode = s.line_mode;
7303 s.move_with(|map, selection| {
7304 if selection.is_empty() && !line_mode {
7305 let cursor = movement::right(map, selection.head());
7306 selection.end = cursor;
7307 selection.reversed = true;
7308 selection.goal = SelectionGoal::None;
7309 }
7310 })
7311 });
7312 this.insert("", window, cx);
7313 this.refresh_inline_completion(true, false, window, cx);
7314 });
7315 }
7316
7317 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7318 if self.move_to_prev_snippet_tabstop(window, cx) {
7319 return;
7320 }
7321
7322 self.outdent(&Outdent, window, cx);
7323 }
7324
7325 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7326 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7327 return;
7328 }
7329
7330 let mut selections = self.selections.all_adjusted(cx);
7331 let buffer = self.buffer.read(cx);
7332 let snapshot = buffer.snapshot(cx);
7333 let rows_iter = selections.iter().map(|s| s.head().row);
7334 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7335
7336 let mut edits = Vec::new();
7337 let mut prev_edited_row = 0;
7338 let mut row_delta = 0;
7339 for selection in &mut selections {
7340 if selection.start.row != prev_edited_row {
7341 row_delta = 0;
7342 }
7343 prev_edited_row = selection.end.row;
7344
7345 // If the selection is non-empty, then increase the indentation of the selected lines.
7346 if !selection.is_empty() {
7347 row_delta =
7348 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7349 continue;
7350 }
7351
7352 // If the selection is empty and the cursor is in the leading whitespace before the
7353 // suggested indentation, then auto-indent the line.
7354 let cursor = selection.head();
7355 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7356 if let Some(suggested_indent) =
7357 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7358 {
7359 if cursor.column < suggested_indent.len
7360 && cursor.column <= current_indent.len
7361 && current_indent.len <= suggested_indent.len
7362 {
7363 selection.start = Point::new(cursor.row, suggested_indent.len);
7364 selection.end = selection.start;
7365 if row_delta == 0 {
7366 edits.extend(Buffer::edit_for_indent_size_adjustment(
7367 cursor.row,
7368 current_indent,
7369 suggested_indent,
7370 ));
7371 row_delta = suggested_indent.len - current_indent.len;
7372 }
7373 continue;
7374 }
7375 }
7376
7377 // Otherwise, insert a hard or soft tab.
7378 let settings = buffer.language_settings_at(cursor, cx);
7379 let tab_size = if settings.hard_tabs {
7380 IndentSize::tab()
7381 } else {
7382 let tab_size = settings.tab_size.get();
7383 let char_column = snapshot
7384 .text_for_range(Point::new(cursor.row, 0)..cursor)
7385 .flat_map(str::chars)
7386 .count()
7387 + row_delta as usize;
7388 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7389 IndentSize::spaces(chars_to_next_tab_stop)
7390 };
7391 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7392 selection.end = selection.start;
7393 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7394 row_delta += tab_size.len;
7395 }
7396
7397 self.transact(window, cx, |this, window, cx| {
7398 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7399 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7400 s.select(selections)
7401 });
7402 this.refresh_inline_completion(true, false, window, cx);
7403 });
7404 }
7405
7406 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7407 if self.read_only(cx) {
7408 return;
7409 }
7410 let mut selections = self.selections.all::<Point>(cx);
7411 let mut prev_edited_row = 0;
7412 let mut row_delta = 0;
7413 let mut edits = Vec::new();
7414 let buffer = self.buffer.read(cx);
7415 let snapshot = buffer.snapshot(cx);
7416 for selection in &mut selections {
7417 if selection.start.row != prev_edited_row {
7418 row_delta = 0;
7419 }
7420 prev_edited_row = selection.end.row;
7421
7422 row_delta =
7423 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7424 }
7425
7426 self.transact(window, cx, |this, window, cx| {
7427 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7428 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7429 s.select(selections)
7430 });
7431 });
7432 }
7433
7434 fn indent_selection(
7435 buffer: &MultiBuffer,
7436 snapshot: &MultiBufferSnapshot,
7437 selection: &mut Selection<Point>,
7438 edits: &mut Vec<(Range<Point>, String)>,
7439 delta_for_start_row: u32,
7440 cx: &App,
7441 ) -> u32 {
7442 let settings = buffer.language_settings_at(selection.start, cx);
7443 let tab_size = settings.tab_size.get();
7444 let indent_kind = if settings.hard_tabs {
7445 IndentKind::Tab
7446 } else {
7447 IndentKind::Space
7448 };
7449 let mut start_row = selection.start.row;
7450 let mut end_row = selection.end.row + 1;
7451
7452 // If a selection ends at the beginning of a line, don't indent
7453 // that last line.
7454 if selection.end.column == 0 && selection.end.row > selection.start.row {
7455 end_row -= 1;
7456 }
7457
7458 // Avoid re-indenting a row that has already been indented by a
7459 // previous selection, but still update this selection's column
7460 // to reflect that indentation.
7461 if delta_for_start_row > 0 {
7462 start_row += 1;
7463 selection.start.column += delta_for_start_row;
7464 if selection.end.row == selection.start.row {
7465 selection.end.column += delta_for_start_row;
7466 }
7467 }
7468
7469 let mut delta_for_end_row = 0;
7470 let has_multiple_rows = start_row + 1 != end_row;
7471 for row in start_row..end_row {
7472 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7473 let indent_delta = match (current_indent.kind, indent_kind) {
7474 (IndentKind::Space, IndentKind::Space) => {
7475 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7476 IndentSize::spaces(columns_to_next_tab_stop)
7477 }
7478 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7479 (_, IndentKind::Tab) => IndentSize::tab(),
7480 };
7481
7482 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7483 0
7484 } else {
7485 selection.start.column
7486 };
7487 let row_start = Point::new(row, start);
7488 edits.push((
7489 row_start..row_start,
7490 indent_delta.chars().collect::<String>(),
7491 ));
7492
7493 // Update this selection's endpoints to reflect the indentation.
7494 if row == selection.start.row {
7495 selection.start.column += indent_delta.len;
7496 }
7497 if row == selection.end.row {
7498 selection.end.column += indent_delta.len;
7499 delta_for_end_row = indent_delta.len;
7500 }
7501 }
7502
7503 if selection.start.row == selection.end.row {
7504 delta_for_start_row + delta_for_end_row
7505 } else {
7506 delta_for_end_row
7507 }
7508 }
7509
7510 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7511 if self.read_only(cx) {
7512 return;
7513 }
7514 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7515 let selections = self.selections.all::<Point>(cx);
7516 let mut deletion_ranges = Vec::new();
7517 let mut last_outdent = None;
7518 {
7519 let buffer = self.buffer.read(cx);
7520 let snapshot = buffer.snapshot(cx);
7521 for selection in &selections {
7522 let settings = buffer.language_settings_at(selection.start, cx);
7523 let tab_size = settings.tab_size.get();
7524 let mut rows = selection.spanned_rows(false, &display_map);
7525
7526 // Avoid re-outdenting a row that has already been outdented by a
7527 // previous selection.
7528 if let Some(last_row) = last_outdent {
7529 if last_row == rows.start {
7530 rows.start = rows.start.next_row();
7531 }
7532 }
7533 let has_multiple_rows = rows.len() > 1;
7534 for row in rows.iter_rows() {
7535 let indent_size = snapshot.indent_size_for_line(row);
7536 if indent_size.len > 0 {
7537 let deletion_len = match indent_size.kind {
7538 IndentKind::Space => {
7539 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7540 if columns_to_prev_tab_stop == 0 {
7541 tab_size
7542 } else {
7543 columns_to_prev_tab_stop
7544 }
7545 }
7546 IndentKind::Tab => 1,
7547 };
7548 let start = if has_multiple_rows
7549 || deletion_len > selection.start.column
7550 || indent_size.len < selection.start.column
7551 {
7552 0
7553 } else {
7554 selection.start.column - deletion_len
7555 };
7556 deletion_ranges.push(
7557 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7558 );
7559 last_outdent = Some(row);
7560 }
7561 }
7562 }
7563 }
7564
7565 self.transact(window, cx, |this, window, cx| {
7566 this.buffer.update(cx, |buffer, cx| {
7567 let empty_str: Arc<str> = Arc::default();
7568 buffer.edit(
7569 deletion_ranges
7570 .into_iter()
7571 .map(|range| (range, empty_str.clone())),
7572 None,
7573 cx,
7574 );
7575 });
7576 let selections = this.selections.all::<usize>(cx);
7577 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7578 s.select(selections)
7579 });
7580 });
7581 }
7582
7583 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7584 if self.read_only(cx) {
7585 return;
7586 }
7587 let selections = self
7588 .selections
7589 .all::<usize>(cx)
7590 .into_iter()
7591 .map(|s| s.range());
7592
7593 self.transact(window, cx, |this, window, cx| {
7594 this.buffer.update(cx, |buffer, cx| {
7595 buffer.autoindent_ranges(selections, cx);
7596 });
7597 let selections = this.selections.all::<usize>(cx);
7598 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7599 s.select(selections)
7600 });
7601 });
7602 }
7603
7604 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7606 let selections = self.selections.all::<Point>(cx);
7607
7608 let mut new_cursors = Vec::new();
7609 let mut edit_ranges = Vec::new();
7610 let mut selections = selections.iter().peekable();
7611 while let Some(selection) = selections.next() {
7612 let mut rows = selection.spanned_rows(false, &display_map);
7613 let goal_display_column = selection.head().to_display_point(&display_map).column();
7614
7615 // Accumulate contiguous regions of rows that we want to delete.
7616 while let Some(next_selection) = selections.peek() {
7617 let next_rows = next_selection.spanned_rows(false, &display_map);
7618 if next_rows.start <= rows.end {
7619 rows.end = next_rows.end;
7620 selections.next().unwrap();
7621 } else {
7622 break;
7623 }
7624 }
7625
7626 let buffer = &display_map.buffer_snapshot;
7627 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7628 let edit_end;
7629 let cursor_buffer_row;
7630 if buffer.max_point().row >= rows.end.0 {
7631 // If there's a line after the range, delete the \n from the end of the row range
7632 // and position the cursor on the next line.
7633 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7634 cursor_buffer_row = rows.end;
7635 } else {
7636 // If there isn't a line after the range, delete the \n from the line before the
7637 // start of the row range and position the cursor there.
7638 edit_start = edit_start.saturating_sub(1);
7639 edit_end = buffer.len();
7640 cursor_buffer_row = rows.start.previous_row();
7641 }
7642
7643 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7644 *cursor.column_mut() =
7645 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7646
7647 new_cursors.push((
7648 selection.id,
7649 buffer.anchor_after(cursor.to_point(&display_map)),
7650 ));
7651 edit_ranges.push(edit_start..edit_end);
7652 }
7653
7654 self.transact(window, cx, |this, window, cx| {
7655 let buffer = this.buffer.update(cx, |buffer, cx| {
7656 let empty_str: Arc<str> = Arc::default();
7657 buffer.edit(
7658 edit_ranges
7659 .into_iter()
7660 .map(|range| (range, empty_str.clone())),
7661 None,
7662 cx,
7663 );
7664 buffer.snapshot(cx)
7665 });
7666 let new_selections = new_cursors
7667 .into_iter()
7668 .map(|(id, cursor)| {
7669 let cursor = cursor.to_point(&buffer);
7670 Selection {
7671 id,
7672 start: cursor,
7673 end: cursor,
7674 reversed: false,
7675 goal: SelectionGoal::None,
7676 }
7677 })
7678 .collect();
7679
7680 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7681 s.select(new_selections);
7682 });
7683 });
7684 }
7685
7686 pub fn join_lines_impl(
7687 &mut self,
7688 insert_whitespace: bool,
7689 window: &mut Window,
7690 cx: &mut Context<Self>,
7691 ) {
7692 if self.read_only(cx) {
7693 return;
7694 }
7695 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7696 for selection in self.selections.all::<Point>(cx) {
7697 let start = MultiBufferRow(selection.start.row);
7698 // Treat single line selections as if they include the next line. Otherwise this action
7699 // would do nothing for single line selections individual cursors.
7700 let end = if selection.start.row == selection.end.row {
7701 MultiBufferRow(selection.start.row + 1)
7702 } else {
7703 MultiBufferRow(selection.end.row)
7704 };
7705
7706 if let Some(last_row_range) = row_ranges.last_mut() {
7707 if start <= last_row_range.end {
7708 last_row_range.end = end;
7709 continue;
7710 }
7711 }
7712 row_ranges.push(start..end);
7713 }
7714
7715 let snapshot = self.buffer.read(cx).snapshot(cx);
7716 let mut cursor_positions = Vec::new();
7717 for row_range in &row_ranges {
7718 let anchor = snapshot.anchor_before(Point::new(
7719 row_range.end.previous_row().0,
7720 snapshot.line_len(row_range.end.previous_row()),
7721 ));
7722 cursor_positions.push(anchor..anchor);
7723 }
7724
7725 self.transact(window, cx, |this, window, cx| {
7726 for row_range in row_ranges.into_iter().rev() {
7727 for row in row_range.iter_rows().rev() {
7728 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7729 let next_line_row = row.next_row();
7730 let indent = snapshot.indent_size_for_line(next_line_row);
7731 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7732
7733 let replace =
7734 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7735 " "
7736 } else {
7737 ""
7738 };
7739
7740 this.buffer.update(cx, |buffer, cx| {
7741 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7742 });
7743 }
7744 }
7745
7746 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7747 s.select_anchor_ranges(cursor_positions)
7748 });
7749 });
7750 }
7751
7752 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7753 self.join_lines_impl(true, window, cx);
7754 }
7755
7756 pub fn sort_lines_case_sensitive(
7757 &mut self,
7758 _: &SortLinesCaseSensitive,
7759 window: &mut Window,
7760 cx: &mut Context<Self>,
7761 ) {
7762 self.manipulate_lines(window, cx, |lines| lines.sort())
7763 }
7764
7765 pub fn sort_lines_case_insensitive(
7766 &mut self,
7767 _: &SortLinesCaseInsensitive,
7768 window: &mut Window,
7769 cx: &mut Context<Self>,
7770 ) {
7771 self.manipulate_lines(window, cx, |lines| {
7772 lines.sort_by_key(|line| line.to_lowercase())
7773 })
7774 }
7775
7776 pub fn unique_lines_case_insensitive(
7777 &mut self,
7778 _: &UniqueLinesCaseInsensitive,
7779 window: &mut Window,
7780 cx: &mut Context<Self>,
7781 ) {
7782 self.manipulate_lines(window, cx, |lines| {
7783 let mut seen = HashSet::default();
7784 lines.retain(|line| seen.insert(line.to_lowercase()));
7785 })
7786 }
7787
7788 pub fn unique_lines_case_sensitive(
7789 &mut self,
7790 _: &UniqueLinesCaseSensitive,
7791 window: &mut Window,
7792 cx: &mut Context<Self>,
7793 ) {
7794 self.manipulate_lines(window, cx, |lines| {
7795 let mut seen = HashSet::default();
7796 lines.retain(|line| seen.insert(*line));
7797 })
7798 }
7799
7800 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7801 let Some(project) = self.project.clone() else {
7802 return;
7803 };
7804 self.reload(project, window, cx)
7805 .detach_and_notify_err(window, cx);
7806 }
7807
7808 pub fn restore_file(
7809 &mut self,
7810 _: &::git::RestoreFile,
7811 window: &mut Window,
7812 cx: &mut Context<Self>,
7813 ) {
7814 let mut buffer_ids = HashSet::default();
7815 let snapshot = self.buffer().read(cx).snapshot(cx);
7816 for selection in self.selections.all::<usize>(cx) {
7817 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7818 }
7819
7820 let buffer = self.buffer().read(cx);
7821 let ranges = buffer_ids
7822 .into_iter()
7823 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7824 .collect::<Vec<_>>();
7825
7826 self.restore_hunks_in_ranges(ranges, window, cx);
7827 }
7828
7829 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7830 let selections = self
7831 .selections
7832 .all(cx)
7833 .into_iter()
7834 .map(|s| s.range())
7835 .collect();
7836 self.restore_hunks_in_ranges(selections, window, cx);
7837 }
7838
7839 fn restore_hunks_in_ranges(
7840 &mut self,
7841 ranges: Vec<Range<Point>>,
7842 window: &mut Window,
7843 cx: &mut Context<Editor>,
7844 ) {
7845 let mut revert_changes = HashMap::default();
7846 let chunk_by = self
7847 .snapshot(window, cx)
7848 .hunks_for_ranges(ranges)
7849 .into_iter()
7850 .chunk_by(|hunk| hunk.buffer_id);
7851 for (buffer_id, hunks) in &chunk_by {
7852 let hunks = hunks.collect::<Vec<_>>();
7853 for hunk in &hunks {
7854 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7855 }
7856 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
7857 }
7858 drop(chunk_by);
7859 if !revert_changes.is_empty() {
7860 self.transact(window, cx, |editor, window, cx| {
7861 editor.restore(revert_changes, window, cx);
7862 });
7863 }
7864 }
7865
7866 pub fn open_active_item_in_terminal(
7867 &mut self,
7868 _: &OpenInTerminal,
7869 window: &mut Window,
7870 cx: &mut Context<Self>,
7871 ) {
7872 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7873 let project_path = buffer.read(cx).project_path(cx)?;
7874 let project = self.project.as_ref()?.read(cx);
7875 let entry = project.entry_for_path(&project_path, cx)?;
7876 let parent = match &entry.canonical_path {
7877 Some(canonical_path) => canonical_path.to_path_buf(),
7878 None => project.absolute_path(&project_path, cx)?,
7879 }
7880 .parent()?
7881 .to_path_buf();
7882 Some(parent)
7883 }) {
7884 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7885 }
7886 }
7887
7888 pub fn prepare_restore_change(
7889 &self,
7890 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7891 hunk: &MultiBufferDiffHunk,
7892 cx: &mut App,
7893 ) -> Option<()> {
7894 if hunk.is_created_file() {
7895 return None;
7896 }
7897 let buffer = self.buffer.read(cx);
7898 let diff = buffer.diff_for(hunk.buffer_id)?;
7899 let buffer = buffer.buffer(hunk.buffer_id)?;
7900 let buffer = buffer.read(cx);
7901 let original_text = diff
7902 .read(cx)
7903 .base_text()
7904 .as_rope()
7905 .slice(hunk.diff_base_byte_range.clone());
7906 let buffer_snapshot = buffer.snapshot();
7907 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7908 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7909 probe
7910 .0
7911 .start
7912 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7913 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7914 }) {
7915 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7916 Some(())
7917 } else {
7918 None
7919 }
7920 }
7921
7922 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7923 self.manipulate_lines(window, cx, |lines| lines.reverse())
7924 }
7925
7926 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7927 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7928 }
7929
7930 fn manipulate_lines<Fn>(
7931 &mut self,
7932 window: &mut Window,
7933 cx: &mut Context<Self>,
7934 mut callback: Fn,
7935 ) where
7936 Fn: FnMut(&mut Vec<&str>),
7937 {
7938 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7939 let buffer = self.buffer.read(cx).snapshot(cx);
7940
7941 let mut edits = Vec::new();
7942
7943 let selections = self.selections.all::<Point>(cx);
7944 let mut selections = selections.iter().peekable();
7945 let mut contiguous_row_selections = Vec::new();
7946 let mut new_selections = Vec::new();
7947 let mut added_lines = 0;
7948 let mut removed_lines = 0;
7949
7950 while let Some(selection) = selections.next() {
7951 let (start_row, end_row) = consume_contiguous_rows(
7952 &mut contiguous_row_selections,
7953 selection,
7954 &display_map,
7955 &mut selections,
7956 );
7957
7958 let start_point = Point::new(start_row.0, 0);
7959 let end_point = Point::new(
7960 end_row.previous_row().0,
7961 buffer.line_len(end_row.previous_row()),
7962 );
7963 let text = buffer
7964 .text_for_range(start_point..end_point)
7965 .collect::<String>();
7966
7967 let mut lines = text.split('\n').collect_vec();
7968
7969 let lines_before = lines.len();
7970 callback(&mut lines);
7971 let lines_after = lines.len();
7972
7973 edits.push((start_point..end_point, lines.join("\n")));
7974
7975 // Selections must change based on added and removed line count
7976 let start_row =
7977 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7978 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7979 new_selections.push(Selection {
7980 id: selection.id,
7981 start: start_row,
7982 end: end_row,
7983 goal: SelectionGoal::None,
7984 reversed: selection.reversed,
7985 });
7986
7987 if lines_after > lines_before {
7988 added_lines += lines_after - lines_before;
7989 } else if lines_before > lines_after {
7990 removed_lines += lines_before - lines_after;
7991 }
7992 }
7993
7994 self.transact(window, cx, |this, window, cx| {
7995 let buffer = this.buffer.update(cx, |buffer, cx| {
7996 buffer.edit(edits, None, cx);
7997 buffer.snapshot(cx)
7998 });
7999
8000 // Recalculate offsets on newly edited buffer
8001 let new_selections = new_selections
8002 .iter()
8003 .map(|s| {
8004 let start_point = Point::new(s.start.0, 0);
8005 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8006 Selection {
8007 id: s.id,
8008 start: buffer.point_to_offset(start_point),
8009 end: buffer.point_to_offset(end_point),
8010 goal: s.goal,
8011 reversed: s.reversed,
8012 }
8013 })
8014 .collect();
8015
8016 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8017 s.select(new_selections);
8018 });
8019
8020 this.request_autoscroll(Autoscroll::fit(), cx);
8021 });
8022 }
8023
8024 pub fn convert_to_upper_case(
8025 &mut self,
8026 _: &ConvertToUpperCase,
8027 window: &mut Window,
8028 cx: &mut Context<Self>,
8029 ) {
8030 self.manipulate_text(window, cx, |text| text.to_uppercase())
8031 }
8032
8033 pub fn convert_to_lower_case(
8034 &mut self,
8035 _: &ConvertToLowerCase,
8036 window: &mut Window,
8037 cx: &mut Context<Self>,
8038 ) {
8039 self.manipulate_text(window, cx, |text| text.to_lowercase())
8040 }
8041
8042 pub fn convert_to_title_case(
8043 &mut self,
8044 _: &ConvertToTitleCase,
8045 window: &mut Window,
8046 cx: &mut Context<Self>,
8047 ) {
8048 self.manipulate_text(window, cx, |text| {
8049 text.split('\n')
8050 .map(|line| line.to_case(Case::Title))
8051 .join("\n")
8052 })
8053 }
8054
8055 pub fn convert_to_snake_case(
8056 &mut self,
8057 _: &ConvertToSnakeCase,
8058 window: &mut Window,
8059 cx: &mut Context<Self>,
8060 ) {
8061 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8062 }
8063
8064 pub fn convert_to_kebab_case(
8065 &mut self,
8066 _: &ConvertToKebabCase,
8067 window: &mut Window,
8068 cx: &mut Context<Self>,
8069 ) {
8070 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8071 }
8072
8073 pub fn convert_to_upper_camel_case(
8074 &mut self,
8075 _: &ConvertToUpperCamelCase,
8076 window: &mut Window,
8077 cx: &mut Context<Self>,
8078 ) {
8079 self.manipulate_text(window, cx, |text| {
8080 text.split('\n')
8081 .map(|line| line.to_case(Case::UpperCamel))
8082 .join("\n")
8083 })
8084 }
8085
8086 pub fn convert_to_lower_camel_case(
8087 &mut self,
8088 _: &ConvertToLowerCamelCase,
8089 window: &mut Window,
8090 cx: &mut Context<Self>,
8091 ) {
8092 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8093 }
8094
8095 pub fn convert_to_opposite_case(
8096 &mut self,
8097 _: &ConvertToOppositeCase,
8098 window: &mut Window,
8099 cx: &mut Context<Self>,
8100 ) {
8101 self.manipulate_text(window, cx, |text| {
8102 text.chars()
8103 .fold(String::with_capacity(text.len()), |mut t, c| {
8104 if c.is_uppercase() {
8105 t.extend(c.to_lowercase());
8106 } else {
8107 t.extend(c.to_uppercase());
8108 }
8109 t
8110 })
8111 })
8112 }
8113
8114 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8115 where
8116 Fn: FnMut(&str) -> String,
8117 {
8118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8119 let buffer = self.buffer.read(cx).snapshot(cx);
8120
8121 let mut new_selections = Vec::new();
8122 let mut edits = Vec::new();
8123 let mut selection_adjustment = 0i32;
8124
8125 for selection in self.selections.all::<usize>(cx) {
8126 let selection_is_empty = selection.is_empty();
8127
8128 let (start, end) = if selection_is_empty {
8129 let word_range = movement::surrounding_word(
8130 &display_map,
8131 selection.start.to_display_point(&display_map),
8132 );
8133 let start = word_range.start.to_offset(&display_map, Bias::Left);
8134 let end = word_range.end.to_offset(&display_map, Bias::Left);
8135 (start, end)
8136 } else {
8137 (selection.start, selection.end)
8138 };
8139
8140 let text = buffer.text_for_range(start..end).collect::<String>();
8141 let old_length = text.len() as i32;
8142 let text = callback(&text);
8143
8144 new_selections.push(Selection {
8145 start: (start as i32 - selection_adjustment) as usize,
8146 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8147 goal: SelectionGoal::None,
8148 ..selection
8149 });
8150
8151 selection_adjustment += old_length - text.len() as i32;
8152
8153 edits.push((start..end, text));
8154 }
8155
8156 self.transact(window, cx, |this, window, cx| {
8157 this.buffer.update(cx, |buffer, cx| {
8158 buffer.edit(edits, None, cx);
8159 });
8160
8161 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8162 s.select(new_selections);
8163 });
8164
8165 this.request_autoscroll(Autoscroll::fit(), cx);
8166 });
8167 }
8168
8169 pub fn duplicate(
8170 &mut self,
8171 upwards: bool,
8172 whole_lines: bool,
8173 window: &mut Window,
8174 cx: &mut Context<Self>,
8175 ) {
8176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8177 let buffer = &display_map.buffer_snapshot;
8178 let selections = self.selections.all::<Point>(cx);
8179
8180 let mut edits = Vec::new();
8181 let mut selections_iter = selections.iter().peekable();
8182 while let Some(selection) = selections_iter.next() {
8183 let mut rows = selection.spanned_rows(false, &display_map);
8184 // duplicate line-wise
8185 if whole_lines || selection.start == selection.end {
8186 // Avoid duplicating the same lines twice.
8187 while let Some(next_selection) = selections_iter.peek() {
8188 let next_rows = next_selection.spanned_rows(false, &display_map);
8189 if next_rows.start < rows.end {
8190 rows.end = next_rows.end;
8191 selections_iter.next().unwrap();
8192 } else {
8193 break;
8194 }
8195 }
8196
8197 // Copy the text from the selected row region and splice it either at the start
8198 // or end of the region.
8199 let start = Point::new(rows.start.0, 0);
8200 let end = Point::new(
8201 rows.end.previous_row().0,
8202 buffer.line_len(rows.end.previous_row()),
8203 );
8204 let text = buffer
8205 .text_for_range(start..end)
8206 .chain(Some("\n"))
8207 .collect::<String>();
8208 let insert_location = if upwards {
8209 Point::new(rows.end.0, 0)
8210 } else {
8211 start
8212 };
8213 edits.push((insert_location..insert_location, text));
8214 } else {
8215 // duplicate character-wise
8216 let start = selection.start;
8217 let end = selection.end;
8218 let text = buffer.text_for_range(start..end).collect::<String>();
8219 edits.push((selection.end..selection.end, text));
8220 }
8221 }
8222
8223 self.transact(window, cx, |this, _, cx| {
8224 this.buffer.update(cx, |buffer, cx| {
8225 buffer.edit(edits, None, cx);
8226 });
8227
8228 this.request_autoscroll(Autoscroll::fit(), cx);
8229 });
8230 }
8231
8232 pub fn duplicate_line_up(
8233 &mut self,
8234 _: &DuplicateLineUp,
8235 window: &mut Window,
8236 cx: &mut Context<Self>,
8237 ) {
8238 self.duplicate(true, true, window, cx);
8239 }
8240
8241 pub fn duplicate_line_down(
8242 &mut self,
8243 _: &DuplicateLineDown,
8244 window: &mut Window,
8245 cx: &mut Context<Self>,
8246 ) {
8247 self.duplicate(false, true, window, cx);
8248 }
8249
8250 pub fn duplicate_selection(
8251 &mut self,
8252 _: &DuplicateSelection,
8253 window: &mut Window,
8254 cx: &mut Context<Self>,
8255 ) {
8256 self.duplicate(false, false, window, cx);
8257 }
8258
8259 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8260 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8261 let buffer = self.buffer.read(cx).snapshot(cx);
8262
8263 let mut edits = Vec::new();
8264 let mut unfold_ranges = Vec::new();
8265 let mut refold_creases = Vec::new();
8266
8267 let selections = self.selections.all::<Point>(cx);
8268 let mut selections = selections.iter().peekable();
8269 let mut contiguous_row_selections = Vec::new();
8270 let mut new_selections = Vec::new();
8271
8272 while let Some(selection) = selections.next() {
8273 // Find all the selections that span a contiguous row range
8274 let (start_row, end_row) = consume_contiguous_rows(
8275 &mut contiguous_row_selections,
8276 selection,
8277 &display_map,
8278 &mut selections,
8279 );
8280
8281 // Move the text spanned by the row range to be before the line preceding the row range
8282 if start_row.0 > 0 {
8283 let range_to_move = Point::new(
8284 start_row.previous_row().0,
8285 buffer.line_len(start_row.previous_row()),
8286 )
8287 ..Point::new(
8288 end_row.previous_row().0,
8289 buffer.line_len(end_row.previous_row()),
8290 );
8291 let insertion_point = display_map
8292 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8293 .0;
8294
8295 // Don't move lines across excerpts
8296 if buffer
8297 .excerpt_containing(insertion_point..range_to_move.end)
8298 .is_some()
8299 {
8300 let text = buffer
8301 .text_for_range(range_to_move.clone())
8302 .flat_map(|s| s.chars())
8303 .skip(1)
8304 .chain(['\n'])
8305 .collect::<String>();
8306
8307 edits.push((
8308 buffer.anchor_after(range_to_move.start)
8309 ..buffer.anchor_before(range_to_move.end),
8310 String::new(),
8311 ));
8312 let insertion_anchor = buffer.anchor_after(insertion_point);
8313 edits.push((insertion_anchor..insertion_anchor, text));
8314
8315 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8316
8317 // Move selections up
8318 new_selections.extend(contiguous_row_selections.drain(..).map(
8319 |mut selection| {
8320 selection.start.row -= row_delta;
8321 selection.end.row -= row_delta;
8322 selection
8323 },
8324 ));
8325
8326 // Move folds up
8327 unfold_ranges.push(range_to_move.clone());
8328 for fold in display_map.folds_in_range(
8329 buffer.anchor_before(range_to_move.start)
8330 ..buffer.anchor_after(range_to_move.end),
8331 ) {
8332 let mut start = fold.range.start.to_point(&buffer);
8333 let mut end = fold.range.end.to_point(&buffer);
8334 start.row -= row_delta;
8335 end.row -= row_delta;
8336 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8337 }
8338 }
8339 }
8340
8341 // If we didn't move line(s), preserve the existing selections
8342 new_selections.append(&mut contiguous_row_selections);
8343 }
8344
8345 self.transact(window, cx, |this, window, cx| {
8346 this.unfold_ranges(&unfold_ranges, true, true, cx);
8347 this.buffer.update(cx, |buffer, cx| {
8348 for (range, text) in edits {
8349 buffer.edit([(range, text)], None, cx);
8350 }
8351 });
8352 this.fold_creases(refold_creases, true, window, cx);
8353 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8354 s.select(new_selections);
8355 })
8356 });
8357 }
8358
8359 pub fn move_line_down(
8360 &mut self,
8361 _: &MoveLineDown,
8362 window: &mut Window,
8363 cx: &mut Context<Self>,
8364 ) {
8365 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8366 let buffer = self.buffer.read(cx).snapshot(cx);
8367
8368 let mut edits = Vec::new();
8369 let mut unfold_ranges = Vec::new();
8370 let mut refold_creases = Vec::new();
8371
8372 let selections = self.selections.all::<Point>(cx);
8373 let mut selections = selections.iter().peekable();
8374 let mut contiguous_row_selections = Vec::new();
8375 let mut new_selections = Vec::new();
8376
8377 while let Some(selection) = selections.next() {
8378 // Find all the selections that span a contiguous row range
8379 let (start_row, end_row) = consume_contiguous_rows(
8380 &mut contiguous_row_selections,
8381 selection,
8382 &display_map,
8383 &mut selections,
8384 );
8385
8386 // Move the text spanned by the row range to be after the last line of the row range
8387 if end_row.0 <= buffer.max_point().row {
8388 let range_to_move =
8389 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8390 let insertion_point = display_map
8391 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8392 .0;
8393
8394 // Don't move lines across excerpt boundaries
8395 if buffer
8396 .excerpt_containing(range_to_move.start..insertion_point)
8397 .is_some()
8398 {
8399 let mut text = String::from("\n");
8400 text.extend(buffer.text_for_range(range_to_move.clone()));
8401 text.pop(); // Drop trailing newline
8402 edits.push((
8403 buffer.anchor_after(range_to_move.start)
8404 ..buffer.anchor_before(range_to_move.end),
8405 String::new(),
8406 ));
8407 let insertion_anchor = buffer.anchor_after(insertion_point);
8408 edits.push((insertion_anchor..insertion_anchor, text));
8409
8410 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8411
8412 // Move selections down
8413 new_selections.extend(contiguous_row_selections.drain(..).map(
8414 |mut selection| {
8415 selection.start.row += row_delta;
8416 selection.end.row += row_delta;
8417 selection
8418 },
8419 ));
8420
8421 // Move folds down
8422 unfold_ranges.push(range_to_move.clone());
8423 for fold in display_map.folds_in_range(
8424 buffer.anchor_before(range_to_move.start)
8425 ..buffer.anchor_after(range_to_move.end),
8426 ) {
8427 let mut start = fold.range.start.to_point(&buffer);
8428 let mut end = fold.range.end.to_point(&buffer);
8429 start.row += row_delta;
8430 end.row += row_delta;
8431 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8432 }
8433 }
8434 }
8435
8436 // If we didn't move line(s), preserve the existing selections
8437 new_selections.append(&mut contiguous_row_selections);
8438 }
8439
8440 self.transact(window, cx, |this, window, cx| {
8441 this.unfold_ranges(&unfold_ranges, true, true, cx);
8442 this.buffer.update(cx, |buffer, cx| {
8443 for (range, text) in edits {
8444 buffer.edit([(range, text)], None, cx);
8445 }
8446 });
8447 this.fold_creases(refold_creases, true, window, cx);
8448 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8449 s.select(new_selections)
8450 });
8451 });
8452 }
8453
8454 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8455 let text_layout_details = &self.text_layout_details(window);
8456 self.transact(window, cx, |this, window, cx| {
8457 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8458 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8459 let line_mode = s.line_mode;
8460 s.move_with(|display_map, selection| {
8461 if !selection.is_empty() || line_mode {
8462 return;
8463 }
8464
8465 let mut head = selection.head();
8466 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8467 if head.column() == display_map.line_len(head.row()) {
8468 transpose_offset = display_map
8469 .buffer_snapshot
8470 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8471 }
8472
8473 if transpose_offset == 0 {
8474 return;
8475 }
8476
8477 *head.column_mut() += 1;
8478 head = display_map.clip_point(head, Bias::Right);
8479 let goal = SelectionGoal::HorizontalPosition(
8480 display_map
8481 .x_for_display_point(head, text_layout_details)
8482 .into(),
8483 );
8484 selection.collapse_to(head, goal);
8485
8486 let transpose_start = display_map
8487 .buffer_snapshot
8488 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8489 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8490 let transpose_end = display_map
8491 .buffer_snapshot
8492 .clip_offset(transpose_offset + 1, Bias::Right);
8493 if let Some(ch) =
8494 display_map.buffer_snapshot.chars_at(transpose_start).next()
8495 {
8496 edits.push((transpose_start..transpose_offset, String::new()));
8497 edits.push((transpose_end..transpose_end, ch.to_string()));
8498 }
8499 }
8500 });
8501 edits
8502 });
8503 this.buffer
8504 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8505 let selections = this.selections.all::<usize>(cx);
8506 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8507 s.select(selections);
8508 });
8509 });
8510 }
8511
8512 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8513 self.rewrap_impl(IsVimMode::No, cx)
8514 }
8515
8516 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
8517 let buffer = self.buffer.read(cx).snapshot(cx);
8518 let selections = self.selections.all::<Point>(cx);
8519 let mut selections = selections.iter().peekable();
8520
8521 let mut edits = Vec::new();
8522 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8523
8524 while let Some(selection) = selections.next() {
8525 let mut start_row = selection.start.row;
8526 let mut end_row = selection.end.row;
8527
8528 // Skip selections that overlap with a range that has already been rewrapped.
8529 let selection_range = start_row..end_row;
8530 if rewrapped_row_ranges
8531 .iter()
8532 .any(|range| range.overlaps(&selection_range))
8533 {
8534 continue;
8535 }
8536
8537 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
8538
8539 // Since not all lines in the selection may be at the same indent
8540 // level, choose the indent size that is the most common between all
8541 // of the lines.
8542 //
8543 // If there is a tie, we use the deepest indent.
8544 let (indent_size, indent_end) = {
8545 let mut indent_size_occurrences = HashMap::default();
8546 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8547
8548 for row in start_row..=end_row {
8549 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8550 rows_by_indent_size.entry(indent).or_default().push(row);
8551 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8552 }
8553
8554 let indent_size = indent_size_occurrences
8555 .into_iter()
8556 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8557 .map(|(indent, _)| indent)
8558 .unwrap_or_default();
8559 let row = rows_by_indent_size[&indent_size][0];
8560 let indent_end = Point::new(row, indent_size.len);
8561
8562 (indent_size, indent_end)
8563 };
8564
8565 let mut line_prefix = indent_size.chars().collect::<String>();
8566
8567 let mut inside_comment = false;
8568 if let Some(comment_prefix) =
8569 buffer
8570 .language_scope_at(selection.head())
8571 .and_then(|language| {
8572 language
8573 .line_comment_prefixes()
8574 .iter()
8575 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8576 .cloned()
8577 })
8578 {
8579 line_prefix.push_str(&comment_prefix);
8580 inside_comment = true;
8581 }
8582
8583 let language_settings = buffer.language_settings_at(selection.head(), cx);
8584 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8585 RewrapBehavior::InComments => inside_comment,
8586 RewrapBehavior::InSelections => !selection.is_empty(),
8587 RewrapBehavior::Anywhere => true,
8588 };
8589
8590 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
8591 if !should_rewrap {
8592 continue;
8593 }
8594
8595 if selection.is_empty() {
8596 'expand_upwards: while start_row > 0 {
8597 let prev_row = start_row - 1;
8598 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8599 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8600 {
8601 start_row = prev_row;
8602 } else {
8603 break 'expand_upwards;
8604 }
8605 }
8606
8607 'expand_downwards: while end_row < buffer.max_point().row {
8608 let next_row = end_row + 1;
8609 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8610 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8611 {
8612 end_row = next_row;
8613 } else {
8614 break 'expand_downwards;
8615 }
8616 }
8617 }
8618
8619 let start = Point::new(start_row, 0);
8620 let start_offset = start.to_offset(&buffer);
8621 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8622 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8623 let Some(lines_without_prefixes) = selection_text
8624 .lines()
8625 .map(|line| {
8626 line.strip_prefix(&line_prefix)
8627 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8628 .ok_or_else(|| {
8629 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8630 })
8631 })
8632 .collect::<Result<Vec<_>, _>>()
8633 .log_err()
8634 else {
8635 continue;
8636 };
8637
8638 let wrap_column = buffer
8639 .language_settings_at(Point::new(start_row, 0), cx)
8640 .preferred_line_length as usize;
8641 let wrapped_text = wrap_with_prefix(
8642 line_prefix,
8643 lines_without_prefixes.join(" "),
8644 wrap_column,
8645 tab_size,
8646 );
8647
8648 // TODO: should always use char-based diff while still supporting cursor behavior that
8649 // matches vim.
8650 let mut diff_options = DiffOptions::default();
8651 if is_vim_mode == IsVimMode::Yes {
8652 diff_options.max_word_diff_len = 0;
8653 diff_options.max_word_diff_line_count = 0;
8654 } else {
8655 diff_options.max_word_diff_len = usize::MAX;
8656 diff_options.max_word_diff_line_count = usize::MAX;
8657 }
8658
8659 for (old_range, new_text) in
8660 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8661 {
8662 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8663 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8664 edits.push((edit_start..edit_end, new_text));
8665 }
8666
8667 rewrapped_row_ranges.push(start_row..=end_row);
8668 }
8669
8670 self.buffer
8671 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8672 }
8673
8674 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8675 let mut text = String::new();
8676 let buffer = self.buffer.read(cx).snapshot(cx);
8677 let mut selections = self.selections.all::<Point>(cx);
8678 let mut clipboard_selections = Vec::with_capacity(selections.len());
8679 {
8680 let max_point = buffer.max_point();
8681 let mut is_first = true;
8682 for selection in &mut selections {
8683 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8684 if is_entire_line {
8685 selection.start = Point::new(selection.start.row, 0);
8686 if !selection.is_empty() && selection.end.column == 0 {
8687 selection.end = cmp::min(max_point, selection.end);
8688 } else {
8689 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8690 }
8691 selection.goal = SelectionGoal::None;
8692 }
8693 if is_first {
8694 is_first = false;
8695 } else {
8696 text += "\n";
8697 }
8698 let mut len = 0;
8699 for chunk in buffer.text_for_range(selection.start..selection.end) {
8700 text.push_str(chunk);
8701 len += chunk.len();
8702 }
8703 clipboard_selections.push(ClipboardSelection {
8704 len,
8705 is_entire_line,
8706 first_line_indent: buffer
8707 .indent_size_for_line(MultiBufferRow(selection.start.row))
8708 .len,
8709 });
8710 }
8711 }
8712
8713 self.transact(window, cx, |this, window, cx| {
8714 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8715 s.select(selections);
8716 });
8717 this.insert("", window, cx);
8718 });
8719 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8720 }
8721
8722 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8723 let item = self.cut_common(window, cx);
8724 cx.write_to_clipboard(item);
8725 }
8726
8727 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8728 self.change_selections(None, window, cx, |s| {
8729 s.move_with(|snapshot, sel| {
8730 if sel.is_empty() {
8731 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8732 }
8733 });
8734 });
8735 let item = self.cut_common(window, cx);
8736 cx.set_global(KillRing(item))
8737 }
8738
8739 pub fn kill_ring_yank(
8740 &mut self,
8741 _: &KillRingYank,
8742 window: &mut Window,
8743 cx: &mut Context<Self>,
8744 ) {
8745 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8746 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8747 (kill_ring.text().to_string(), kill_ring.metadata_json())
8748 } else {
8749 return;
8750 }
8751 } else {
8752 return;
8753 };
8754 self.do_paste(&text, metadata, false, window, cx);
8755 }
8756
8757 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8758 let selections = self.selections.all::<Point>(cx);
8759 let buffer = self.buffer.read(cx).read(cx);
8760 let mut text = String::new();
8761
8762 let mut clipboard_selections = Vec::with_capacity(selections.len());
8763 {
8764 let max_point = buffer.max_point();
8765 let mut is_first = true;
8766 for selection in selections.iter() {
8767 let mut start = selection.start;
8768 let mut end = selection.end;
8769 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8770 if is_entire_line {
8771 start = Point::new(start.row, 0);
8772 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8773 }
8774 if is_first {
8775 is_first = false;
8776 } else {
8777 text += "\n";
8778 }
8779 let mut len = 0;
8780 for chunk in buffer.text_for_range(start..end) {
8781 text.push_str(chunk);
8782 len += chunk.len();
8783 }
8784 clipboard_selections.push(ClipboardSelection {
8785 len,
8786 is_entire_line,
8787 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8788 });
8789 }
8790 }
8791
8792 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8793 text,
8794 clipboard_selections,
8795 ));
8796 }
8797
8798 pub fn do_paste(
8799 &mut self,
8800 text: &String,
8801 clipboard_selections: Option<Vec<ClipboardSelection>>,
8802 handle_entire_lines: bool,
8803 window: &mut Window,
8804 cx: &mut Context<Self>,
8805 ) {
8806 if self.read_only(cx) {
8807 return;
8808 }
8809
8810 let clipboard_text = Cow::Borrowed(text);
8811
8812 self.transact(window, cx, |this, window, cx| {
8813 if let Some(mut clipboard_selections) = clipboard_selections {
8814 let old_selections = this.selections.all::<usize>(cx);
8815 let all_selections_were_entire_line =
8816 clipboard_selections.iter().all(|s| s.is_entire_line);
8817 let first_selection_indent_column =
8818 clipboard_selections.first().map(|s| s.first_line_indent);
8819 if clipboard_selections.len() != old_selections.len() {
8820 clipboard_selections.drain(..);
8821 }
8822 let cursor_offset = this.selections.last::<usize>(cx).head();
8823 let mut auto_indent_on_paste = true;
8824
8825 this.buffer.update(cx, |buffer, cx| {
8826 let snapshot = buffer.read(cx);
8827 auto_indent_on_paste = snapshot
8828 .language_settings_at(cursor_offset, cx)
8829 .auto_indent_on_paste;
8830
8831 let mut start_offset = 0;
8832 let mut edits = Vec::new();
8833 let mut original_indent_columns = Vec::new();
8834 for (ix, selection) in old_selections.iter().enumerate() {
8835 let to_insert;
8836 let entire_line;
8837 let original_indent_column;
8838 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8839 let end_offset = start_offset + clipboard_selection.len;
8840 to_insert = &clipboard_text[start_offset..end_offset];
8841 entire_line = clipboard_selection.is_entire_line;
8842 start_offset = end_offset + 1;
8843 original_indent_column = Some(clipboard_selection.first_line_indent);
8844 } else {
8845 to_insert = clipboard_text.as_str();
8846 entire_line = all_selections_were_entire_line;
8847 original_indent_column = first_selection_indent_column
8848 }
8849
8850 // If the corresponding selection was empty when this slice of the
8851 // clipboard text was written, then the entire line containing the
8852 // selection was copied. If this selection is also currently empty,
8853 // then paste the line before the current line of the buffer.
8854 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8855 let column = selection.start.to_point(&snapshot).column as usize;
8856 let line_start = selection.start - column;
8857 line_start..line_start
8858 } else {
8859 selection.range()
8860 };
8861
8862 edits.push((range, to_insert));
8863 original_indent_columns.push(original_indent_column);
8864 }
8865 drop(snapshot);
8866
8867 buffer.edit(
8868 edits,
8869 if auto_indent_on_paste {
8870 Some(AutoindentMode::Block {
8871 original_indent_columns,
8872 })
8873 } else {
8874 None
8875 },
8876 cx,
8877 );
8878 });
8879
8880 let selections = this.selections.all::<usize>(cx);
8881 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8882 s.select(selections)
8883 });
8884 } else {
8885 this.insert(&clipboard_text, window, cx);
8886 }
8887 });
8888 }
8889
8890 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8891 if let Some(item) = cx.read_from_clipboard() {
8892 let entries = item.entries();
8893
8894 match entries.first() {
8895 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8896 // of all the pasted entries.
8897 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8898 .do_paste(
8899 clipboard_string.text(),
8900 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8901 true,
8902 window,
8903 cx,
8904 ),
8905 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8906 }
8907 }
8908 }
8909
8910 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8911 if self.read_only(cx) {
8912 return;
8913 }
8914
8915 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8916 if let Some((selections, _)) =
8917 self.selection_history.transaction(transaction_id).cloned()
8918 {
8919 self.change_selections(None, window, cx, |s| {
8920 s.select_anchors(selections.to_vec());
8921 });
8922 } else {
8923 log::error!(
8924 "No entry in selection_history found for undo. \
8925 This may correspond to a bug where undo does not update the selection. \
8926 If this is occurring, please add details to \
8927 https://github.com/zed-industries/zed/issues/22692"
8928 );
8929 }
8930 self.request_autoscroll(Autoscroll::fit(), cx);
8931 self.unmark_text(window, cx);
8932 self.refresh_inline_completion(true, false, window, cx);
8933 cx.emit(EditorEvent::Edited { transaction_id });
8934 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8935 }
8936 }
8937
8938 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8939 if self.read_only(cx) {
8940 return;
8941 }
8942
8943 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8944 if let Some((_, Some(selections))) =
8945 self.selection_history.transaction(transaction_id).cloned()
8946 {
8947 self.change_selections(None, window, cx, |s| {
8948 s.select_anchors(selections.to_vec());
8949 });
8950 } else {
8951 log::error!(
8952 "No entry in selection_history found for redo. \
8953 This may correspond to a bug where undo does not update the selection. \
8954 If this is occurring, please add details to \
8955 https://github.com/zed-industries/zed/issues/22692"
8956 );
8957 }
8958 self.request_autoscroll(Autoscroll::fit(), cx);
8959 self.unmark_text(window, cx);
8960 self.refresh_inline_completion(true, false, window, cx);
8961 cx.emit(EditorEvent::Edited { transaction_id });
8962 }
8963 }
8964
8965 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8966 self.buffer
8967 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8968 }
8969
8970 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8971 self.buffer
8972 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8973 }
8974
8975 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8976 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8977 let line_mode = s.line_mode;
8978 s.move_with(|map, selection| {
8979 let cursor = if selection.is_empty() && !line_mode {
8980 movement::left(map, selection.start)
8981 } else {
8982 selection.start
8983 };
8984 selection.collapse_to(cursor, SelectionGoal::None);
8985 });
8986 })
8987 }
8988
8989 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8990 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8991 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8992 })
8993 }
8994
8995 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8996 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8997 let line_mode = s.line_mode;
8998 s.move_with(|map, selection| {
8999 let cursor = if selection.is_empty() && !line_mode {
9000 movement::right(map, selection.end)
9001 } else {
9002 selection.end
9003 };
9004 selection.collapse_to(cursor, SelectionGoal::None)
9005 });
9006 })
9007 }
9008
9009 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9010 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9011 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9012 })
9013 }
9014
9015 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9016 if self.take_rename(true, window, cx).is_some() {
9017 return;
9018 }
9019
9020 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9021 cx.propagate();
9022 return;
9023 }
9024
9025 let text_layout_details = &self.text_layout_details(window);
9026 let selection_count = self.selections.count();
9027 let first_selection = self.selections.first_anchor();
9028
9029 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9030 let line_mode = s.line_mode;
9031 s.move_with(|map, selection| {
9032 if !selection.is_empty() && !line_mode {
9033 selection.goal = SelectionGoal::None;
9034 }
9035 let (cursor, goal) = movement::up(
9036 map,
9037 selection.start,
9038 selection.goal,
9039 false,
9040 text_layout_details,
9041 );
9042 selection.collapse_to(cursor, goal);
9043 });
9044 });
9045
9046 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9047 {
9048 cx.propagate();
9049 }
9050 }
9051
9052 pub fn move_up_by_lines(
9053 &mut self,
9054 action: &MoveUpByLines,
9055 window: &mut Window,
9056 cx: &mut Context<Self>,
9057 ) {
9058 if self.take_rename(true, window, cx).is_some() {
9059 return;
9060 }
9061
9062 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9063 cx.propagate();
9064 return;
9065 }
9066
9067 let text_layout_details = &self.text_layout_details(window);
9068
9069 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9070 let line_mode = s.line_mode;
9071 s.move_with(|map, selection| {
9072 if !selection.is_empty() && !line_mode {
9073 selection.goal = SelectionGoal::None;
9074 }
9075 let (cursor, goal) = movement::up_by_rows(
9076 map,
9077 selection.start,
9078 action.lines,
9079 selection.goal,
9080 false,
9081 text_layout_details,
9082 );
9083 selection.collapse_to(cursor, goal);
9084 });
9085 })
9086 }
9087
9088 pub fn move_down_by_lines(
9089 &mut self,
9090 action: &MoveDownByLines,
9091 window: &mut Window,
9092 cx: &mut Context<Self>,
9093 ) {
9094 if self.take_rename(true, window, cx).is_some() {
9095 return;
9096 }
9097
9098 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9099 cx.propagate();
9100 return;
9101 }
9102
9103 let text_layout_details = &self.text_layout_details(window);
9104
9105 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9106 let line_mode = s.line_mode;
9107 s.move_with(|map, selection| {
9108 if !selection.is_empty() && !line_mode {
9109 selection.goal = SelectionGoal::None;
9110 }
9111 let (cursor, goal) = movement::down_by_rows(
9112 map,
9113 selection.start,
9114 action.lines,
9115 selection.goal,
9116 false,
9117 text_layout_details,
9118 );
9119 selection.collapse_to(cursor, goal);
9120 });
9121 })
9122 }
9123
9124 pub fn select_down_by_lines(
9125 &mut self,
9126 action: &SelectDownByLines,
9127 window: &mut Window,
9128 cx: &mut Context<Self>,
9129 ) {
9130 let text_layout_details = &self.text_layout_details(window);
9131 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9132 s.move_heads_with(|map, head, goal| {
9133 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9134 })
9135 })
9136 }
9137
9138 pub fn select_up_by_lines(
9139 &mut self,
9140 action: &SelectUpByLines,
9141 window: &mut Window,
9142 cx: &mut Context<Self>,
9143 ) {
9144 let text_layout_details = &self.text_layout_details(window);
9145 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9146 s.move_heads_with(|map, head, goal| {
9147 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9148 })
9149 })
9150 }
9151
9152 pub fn select_page_up(
9153 &mut self,
9154 _: &SelectPageUp,
9155 window: &mut Window,
9156 cx: &mut Context<Self>,
9157 ) {
9158 let Some(row_count) = self.visible_row_count() else {
9159 return;
9160 };
9161
9162 let text_layout_details = &self.text_layout_details(window);
9163
9164 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9165 s.move_heads_with(|map, head, goal| {
9166 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9167 })
9168 })
9169 }
9170
9171 pub fn move_page_up(
9172 &mut self,
9173 action: &MovePageUp,
9174 window: &mut Window,
9175 cx: &mut Context<Self>,
9176 ) {
9177 if self.take_rename(true, window, cx).is_some() {
9178 return;
9179 }
9180
9181 if self
9182 .context_menu
9183 .borrow_mut()
9184 .as_mut()
9185 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9186 .unwrap_or(false)
9187 {
9188 return;
9189 }
9190
9191 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9192 cx.propagate();
9193 return;
9194 }
9195
9196 let Some(row_count) = self.visible_row_count() else {
9197 return;
9198 };
9199
9200 let autoscroll = if action.center_cursor {
9201 Autoscroll::center()
9202 } else {
9203 Autoscroll::fit()
9204 };
9205
9206 let text_layout_details = &self.text_layout_details(window);
9207
9208 self.change_selections(Some(autoscroll), window, cx, |s| {
9209 let line_mode = s.line_mode;
9210 s.move_with(|map, selection| {
9211 if !selection.is_empty() && !line_mode {
9212 selection.goal = SelectionGoal::None;
9213 }
9214 let (cursor, goal) = movement::up_by_rows(
9215 map,
9216 selection.end,
9217 row_count,
9218 selection.goal,
9219 false,
9220 text_layout_details,
9221 );
9222 selection.collapse_to(cursor, goal);
9223 });
9224 });
9225 }
9226
9227 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9228 let text_layout_details = &self.text_layout_details(window);
9229 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9230 s.move_heads_with(|map, head, goal| {
9231 movement::up(map, head, goal, false, text_layout_details)
9232 })
9233 })
9234 }
9235
9236 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9237 self.take_rename(true, window, cx);
9238
9239 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9240 cx.propagate();
9241 return;
9242 }
9243
9244 let text_layout_details = &self.text_layout_details(window);
9245 let selection_count = self.selections.count();
9246 let first_selection = self.selections.first_anchor();
9247
9248 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9249 let line_mode = s.line_mode;
9250 s.move_with(|map, selection| {
9251 if !selection.is_empty() && !line_mode {
9252 selection.goal = SelectionGoal::None;
9253 }
9254 let (cursor, goal) = movement::down(
9255 map,
9256 selection.end,
9257 selection.goal,
9258 false,
9259 text_layout_details,
9260 );
9261 selection.collapse_to(cursor, goal);
9262 });
9263 });
9264
9265 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9266 {
9267 cx.propagate();
9268 }
9269 }
9270
9271 pub fn select_page_down(
9272 &mut self,
9273 _: &SelectPageDown,
9274 window: &mut Window,
9275 cx: &mut Context<Self>,
9276 ) {
9277 let Some(row_count) = self.visible_row_count() else {
9278 return;
9279 };
9280
9281 let text_layout_details = &self.text_layout_details(window);
9282
9283 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9284 s.move_heads_with(|map, head, goal| {
9285 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9286 })
9287 })
9288 }
9289
9290 pub fn move_page_down(
9291 &mut self,
9292 action: &MovePageDown,
9293 window: &mut Window,
9294 cx: &mut Context<Self>,
9295 ) {
9296 if self.take_rename(true, window, cx).is_some() {
9297 return;
9298 }
9299
9300 if self
9301 .context_menu
9302 .borrow_mut()
9303 .as_mut()
9304 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9305 .unwrap_or(false)
9306 {
9307 return;
9308 }
9309
9310 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9311 cx.propagate();
9312 return;
9313 }
9314
9315 let Some(row_count) = self.visible_row_count() else {
9316 return;
9317 };
9318
9319 let autoscroll = if action.center_cursor {
9320 Autoscroll::center()
9321 } else {
9322 Autoscroll::fit()
9323 };
9324
9325 let text_layout_details = &self.text_layout_details(window);
9326 self.change_selections(Some(autoscroll), window, cx, |s| {
9327 let line_mode = s.line_mode;
9328 s.move_with(|map, selection| {
9329 if !selection.is_empty() && !line_mode {
9330 selection.goal = SelectionGoal::None;
9331 }
9332 let (cursor, goal) = movement::down_by_rows(
9333 map,
9334 selection.end,
9335 row_count,
9336 selection.goal,
9337 false,
9338 text_layout_details,
9339 );
9340 selection.collapse_to(cursor, goal);
9341 });
9342 });
9343 }
9344
9345 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9346 let text_layout_details = &self.text_layout_details(window);
9347 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9348 s.move_heads_with(|map, head, goal| {
9349 movement::down(map, head, goal, false, text_layout_details)
9350 })
9351 });
9352 }
9353
9354 pub fn context_menu_first(
9355 &mut self,
9356 _: &ContextMenuFirst,
9357 _window: &mut Window,
9358 cx: &mut Context<Self>,
9359 ) {
9360 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9361 context_menu.select_first(self.completion_provider.as_deref(), cx);
9362 }
9363 }
9364
9365 pub fn context_menu_prev(
9366 &mut self,
9367 _: &ContextMenuPrevious,
9368 _window: &mut Window,
9369 cx: &mut Context<Self>,
9370 ) {
9371 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9372 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9373 }
9374 }
9375
9376 pub fn context_menu_next(
9377 &mut self,
9378 _: &ContextMenuNext,
9379 _window: &mut Window,
9380 cx: &mut Context<Self>,
9381 ) {
9382 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9383 context_menu.select_next(self.completion_provider.as_deref(), cx);
9384 }
9385 }
9386
9387 pub fn context_menu_last(
9388 &mut self,
9389 _: &ContextMenuLast,
9390 _window: &mut Window,
9391 cx: &mut Context<Self>,
9392 ) {
9393 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9394 context_menu.select_last(self.completion_provider.as_deref(), cx);
9395 }
9396 }
9397
9398 pub fn move_to_previous_word_start(
9399 &mut self,
9400 _: &MoveToPreviousWordStart,
9401 window: &mut Window,
9402 cx: &mut Context<Self>,
9403 ) {
9404 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9405 s.move_cursors_with(|map, head, _| {
9406 (
9407 movement::previous_word_start(map, head),
9408 SelectionGoal::None,
9409 )
9410 });
9411 })
9412 }
9413
9414 pub fn move_to_previous_subword_start(
9415 &mut self,
9416 _: &MoveToPreviousSubwordStart,
9417 window: &mut Window,
9418 cx: &mut Context<Self>,
9419 ) {
9420 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9421 s.move_cursors_with(|map, head, _| {
9422 (
9423 movement::previous_subword_start(map, head),
9424 SelectionGoal::None,
9425 )
9426 });
9427 })
9428 }
9429
9430 pub fn select_to_previous_word_start(
9431 &mut self,
9432 _: &SelectToPreviousWordStart,
9433 window: &mut Window,
9434 cx: &mut Context<Self>,
9435 ) {
9436 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9437 s.move_heads_with(|map, head, _| {
9438 (
9439 movement::previous_word_start(map, head),
9440 SelectionGoal::None,
9441 )
9442 });
9443 })
9444 }
9445
9446 pub fn select_to_previous_subword_start(
9447 &mut self,
9448 _: &SelectToPreviousSubwordStart,
9449 window: &mut Window,
9450 cx: &mut Context<Self>,
9451 ) {
9452 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9453 s.move_heads_with(|map, head, _| {
9454 (
9455 movement::previous_subword_start(map, head),
9456 SelectionGoal::None,
9457 )
9458 });
9459 })
9460 }
9461
9462 pub fn delete_to_previous_word_start(
9463 &mut self,
9464 action: &DeleteToPreviousWordStart,
9465 window: &mut Window,
9466 cx: &mut Context<Self>,
9467 ) {
9468 self.transact(window, cx, |this, window, cx| {
9469 this.select_autoclose_pair(window, cx);
9470 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9471 let line_mode = s.line_mode;
9472 s.move_with(|map, selection| {
9473 if selection.is_empty() && !line_mode {
9474 let cursor = if action.ignore_newlines {
9475 movement::previous_word_start(map, selection.head())
9476 } else {
9477 movement::previous_word_start_or_newline(map, selection.head())
9478 };
9479 selection.set_head(cursor, SelectionGoal::None);
9480 }
9481 });
9482 });
9483 this.insert("", window, cx);
9484 });
9485 }
9486
9487 pub fn delete_to_previous_subword_start(
9488 &mut self,
9489 _: &DeleteToPreviousSubwordStart,
9490 window: &mut Window,
9491 cx: &mut Context<Self>,
9492 ) {
9493 self.transact(window, cx, |this, window, cx| {
9494 this.select_autoclose_pair(window, cx);
9495 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9496 let line_mode = s.line_mode;
9497 s.move_with(|map, selection| {
9498 if selection.is_empty() && !line_mode {
9499 let cursor = movement::previous_subword_start(map, selection.head());
9500 selection.set_head(cursor, SelectionGoal::None);
9501 }
9502 });
9503 });
9504 this.insert("", window, cx);
9505 });
9506 }
9507
9508 pub fn move_to_next_word_end(
9509 &mut self,
9510 _: &MoveToNextWordEnd,
9511 window: &mut Window,
9512 cx: &mut Context<Self>,
9513 ) {
9514 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9515 s.move_cursors_with(|map, head, _| {
9516 (movement::next_word_end(map, head), SelectionGoal::None)
9517 });
9518 })
9519 }
9520
9521 pub fn move_to_next_subword_end(
9522 &mut self,
9523 _: &MoveToNextSubwordEnd,
9524 window: &mut Window,
9525 cx: &mut Context<Self>,
9526 ) {
9527 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9528 s.move_cursors_with(|map, head, _| {
9529 (movement::next_subword_end(map, head), SelectionGoal::None)
9530 });
9531 })
9532 }
9533
9534 pub fn select_to_next_word_end(
9535 &mut self,
9536 _: &SelectToNextWordEnd,
9537 window: &mut Window,
9538 cx: &mut Context<Self>,
9539 ) {
9540 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9541 s.move_heads_with(|map, head, _| {
9542 (movement::next_word_end(map, head), SelectionGoal::None)
9543 });
9544 })
9545 }
9546
9547 pub fn select_to_next_subword_end(
9548 &mut self,
9549 _: &SelectToNextSubwordEnd,
9550 window: &mut Window,
9551 cx: &mut Context<Self>,
9552 ) {
9553 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9554 s.move_heads_with(|map, head, _| {
9555 (movement::next_subword_end(map, head), SelectionGoal::None)
9556 });
9557 })
9558 }
9559
9560 pub fn delete_to_next_word_end(
9561 &mut self,
9562 action: &DeleteToNextWordEnd,
9563 window: &mut Window,
9564 cx: &mut Context<Self>,
9565 ) {
9566 self.transact(window, cx, |this, window, cx| {
9567 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9568 let line_mode = s.line_mode;
9569 s.move_with(|map, selection| {
9570 if selection.is_empty() && !line_mode {
9571 let cursor = if action.ignore_newlines {
9572 movement::next_word_end(map, selection.head())
9573 } else {
9574 movement::next_word_end_or_newline(map, selection.head())
9575 };
9576 selection.set_head(cursor, SelectionGoal::None);
9577 }
9578 });
9579 });
9580 this.insert("", window, cx);
9581 });
9582 }
9583
9584 pub fn delete_to_next_subword_end(
9585 &mut self,
9586 _: &DeleteToNextSubwordEnd,
9587 window: &mut Window,
9588 cx: &mut Context<Self>,
9589 ) {
9590 self.transact(window, cx, |this, window, cx| {
9591 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9592 s.move_with(|map, selection| {
9593 if selection.is_empty() {
9594 let cursor = movement::next_subword_end(map, selection.head());
9595 selection.set_head(cursor, SelectionGoal::None);
9596 }
9597 });
9598 });
9599 this.insert("", window, cx);
9600 });
9601 }
9602
9603 pub fn move_to_beginning_of_line(
9604 &mut self,
9605 action: &MoveToBeginningOfLine,
9606 window: &mut Window,
9607 cx: &mut Context<Self>,
9608 ) {
9609 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9610 s.move_cursors_with(|map, head, _| {
9611 (
9612 movement::indented_line_beginning(
9613 map,
9614 head,
9615 action.stop_at_soft_wraps,
9616 action.stop_at_indent,
9617 ),
9618 SelectionGoal::None,
9619 )
9620 });
9621 })
9622 }
9623
9624 pub fn select_to_beginning_of_line(
9625 &mut self,
9626 action: &SelectToBeginningOfLine,
9627 window: &mut Window,
9628 cx: &mut Context<Self>,
9629 ) {
9630 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9631 s.move_heads_with(|map, head, _| {
9632 (
9633 movement::indented_line_beginning(
9634 map,
9635 head,
9636 action.stop_at_soft_wraps,
9637 action.stop_at_indent,
9638 ),
9639 SelectionGoal::None,
9640 )
9641 });
9642 });
9643 }
9644
9645 pub fn delete_to_beginning_of_line(
9646 &mut self,
9647 action: &DeleteToBeginningOfLine,
9648 window: &mut Window,
9649 cx: &mut Context<Self>,
9650 ) {
9651 self.transact(window, cx, |this, window, cx| {
9652 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9653 s.move_with(|_, selection| {
9654 selection.reversed = true;
9655 });
9656 });
9657
9658 this.select_to_beginning_of_line(
9659 &SelectToBeginningOfLine {
9660 stop_at_soft_wraps: false,
9661 stop_at_indent: action.stop_at_indent,
9662 },
9663 window,
9664 cx,
9665 );
9666 this.backspace(&Backspace, window, cx);
9667 });
9668 }
9669
9670 pub fn move_to_end_of_line(
9671 &mut self,
9672 action: &MoveToEndOfLine,
9673 window: &mut Window,
9674 cx: &mut Context<Self>,
9675 ) {
9676 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9677 s.move_cursors_with(|map, head, _| {
9678 (
9679 movement::line_end(map, head, action.stop_at_soft_wraps),
9680 SelectionGoal::None,
9681 )
9682 });
9683 })
9684 }
9685
9686 pub fn select_to_end_of_line(
9687 &mut self,
9688 action: &SelectToEndOfLine,
9689 window: &mut Window,
9690 cx: &mut Context<Self>,
9691 ) {
9692 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9693 s.move_heads_with(|map, head, _| {
9694 (
9695 movement::line_end(map, head, action.stop_at_soft_wraps),
9696 SelectionGoal::None,
9697 )
9698 });
9699 })
9700 }
9701
9702 pub fn delete_to_end_of_line(
9703 &mut self,
9704 _: &DeleteToEndOfLine,
9705 window: &mut Window,
9706 cx: &mut Context<Self>,
9707 ) {
9708 self.transact(window, cx, |this, window, cx| {
9709 this.select_to_end_of_line(
9710 &SelectToEndOfLine {
9711 stop_at_soft_wraps: false,
9712 },
9713 window,
9714 cx,
9715 );
9716 this.delete(&Delete, window, cx);
9717 });
9718 }
9719
9720 pub fn cut_to_end_of_line(
9721 &mut self,
9722 _: &CutToEndOfLine,
9723 window: &mut Window,
9724 cx: &mut Context<Self>,
9725 ) {
9726 self.transact(window, cx, |this, window, cx| {
9727 this.select_to_end_of_line(
9728 &SelectToEndOfLine {
9729 stop_at_soft_wraps: false,
9730 },
9731 window,
9732 cx,
9733 );
9734 this.cut(&Cut, window, cx);
9735 });
9736 }
9737
9738 pub fn move_to_start_of_paragraph(
9739 &mut self,
9740 _: &MoveToStartOfParagraph,
9741 window: &mut Window,
9742 cx: &mut Context<Self>,
9743 ) {
9744 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9745 cx.propagate();
9746 return;
9747 }
9748
9749 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9750 s.move_with(|map, selection| {
9751 selection.collapse_to(
9752 movement::start_of_paragraph(map, selection.head(), 1),
9753 SelectionGoal::None,
9754 )
9755 });
9756 })
9757 }
9758
9759 pub fn move_to_end_of_paragraph(
9760 &mut self,
9761 _: &MoveToEndOfParagraph,
9762 window: &mut Window,
9763 cx: &mut Context<Self>,
9764 ) {
9765 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9766 cx.propagate();
9767 return;
9768 }
9769
9770 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9771 s.move_with(|map, selection| {
9772 selection.collapse_to(
9773 movement::end_of_paragraph(map, selection.head(), 1),
9774 SelectionGoal::None,
9775 )
9776 });
9777 })
9778 }
9779
9780 pub fn select_to_start_of_paragraph(
9781 &mut self,
9782 _: &SelectToStartOfParagraph,
9783 window: &mut Window,
9784 cx: &mut Context<Self>,
9785 ) {
9786 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9787 cx.propagate();
9788 return;
9789 }
9790
9791 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9792 s.move_heads_with(|map, head, _| {
9793 (
9794 movement::start_of_paragraph(map, head, 1),
9795 SelectionGoal::None,
9796 )
9797 });
9798 })
9799 }
9800
9801 pub fn select_to_end_of_paragraph(
9802 &mut self,
9803 _: &SelectToEndOfParagraph,
9804 window: &mut Window,
9805 cx: &mut Context<Self>,
9806 ) {
9807 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9808 cx.propagate();
9809 return;
9810 }
9811
9812 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9813 s.move_heads_with(|map, head, _| {
9814 (
9815 movement::end_of_paragraph(map, head, 1),
9816 SelectionGoal::None,
9817 )
9818 });
9819 })
9820 }
9821
9822 pub fn move_to_start_of_excerpt(
9823 &mut self,
9824 _: &MoveToStartOfExcerpt,
9825 window: &mut Window,
9826 cx: &mut Context<Self>,
9827 ) {
9828 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9829 cx.propagate();
9830 return;
9831 }
9832
9833 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9834 s.move_with(|map, selection| {
9835 selection.collapse_to(
9836 movement::start_of_excerpt(
9837 map,
9838 selection.head(),
9839 workspace::searchable::Direction::Prev,
9840 ),
9841 SelectionGoal::None,
9842 )
9843 });
9844 })
9845 }
9846
9847 pub fn move_to_end_of_excerpt(
9848 &mut self,
9849 _: &MoveToEndOfExcerpt,
9850 window: &mut Window,
9851 cx: &mut Context<Self>,
9852 ) {
9853 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9854 cx.propagate();
9855 return;
9856 }
9857
9858 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9859 s.move_with(|map, selection| {
9860 selection.collapse_to(
9861 movement::end_of_excerpt(
9862 map,
9863 selection.head(),
9864 workspace::searchable::Direction::Next,
9865 ),
9866 SelectionGoal::None,
9867 )
9868 });
9869 })
9870 }
9871
9872 pub fn select_to_start_of_excerpt(
9873 &mut self,
9874 _: &SelectToStartOfExcerpt,
9875 window: &mut Window,
9876 cx: &mut Context<Self>,
9877 ) {
9878 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9879 cx.propagate();
9880 return;
9881 }
9882
9883 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9884 s.move_heads_with(|map, head, _| {
9885 (
9886 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9887 SelectionGoal::None,
9888 )
9889 });
9890 })
9891 }
9892
9893 pub fn select_to_end_of_excerpt(
9894 &mut self,
9895 _: &SelectToEndOfExcerpt,
9896 window: &mut Window,
9897 cx: &mut Context<Self>,
9898 ) {
9899 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9900 cx.propagate();
9901 return;
9902 }
9903
9904 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9905 s.move_heads_with(|map, head, _| {
9906 (
9907 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9908 SelectionGoal::None,
9909 )
9910 });
9911 })
9912 }
9913
9914 pub fn move_to_beginning(
9915 &mut self,
9916 _: &MoveToBeginning,
9917 window: &mut Window,
9918 cx: &mut Context<Self>,
9919 ) {
9920 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9921 cx.propagate();
9922 return;
9923 }
9924
9925 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9926 s.select_ranges(vec![0..0]);
9927 });
9928 }
9929
9930 pub fn select_to_beginning(
9931 &mut self,
9932 _: &SelectToBeginning,
9933 window: &mut Window,
9934 cx: &mut Context<Self>,
9935 ) {
9936 let mut selection = self.selections.last::<Point>(cx);
9937 selection.set_head(Point::zero(), SelectionGoal::None);
9938
9939 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9940 s.select(vec![selection]);
9941 });
9942 }
9943
9944 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9945 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9946 cx.propagate();
9947 return;
9948 }
9949
9950 let cursor = self.buffer.read(cx).read(cx).len();
9951 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9952 s.select_ranges(vec![cursor..cursor])
9953 });
9954 }
9955
9956 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9957 self.nav_history = nav_history;
9958 }
9959
9960 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9961 self.nav_history.as_ref()
9962 }
9963
9964 fn push_to_nav_history(
9965 &mut self,
9966 cursor_anchor: Anchor,
9967 new_position: Option<Point>,
9968 cx: &mut Context<Self>,
9969 ) {
9970 if let Some(nav_history) = self.nav_history.as_mut() {
9971 let buffer = self.buffer.read(cx).read(cx);
9972 let cursor_position = cursor_anchor.to_point(&buffer);
9973 let scroll_state = self.scroll_manager.anchor();
9974 let scroll_top_row = scroll_state.top_row(&buffer);
9975 drop(buffer);
9976
9977 if let Some(new_position) = new_position {
9978 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9979 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9980 return;
9981 }
9982 }
9983
9984 nav_history.push(
9985 Some(NavigationData {
9986 cursor_anchor,
9987 cursor_position,
9988 scroll_anchor: scroll_state,
9989 scroll_top_row,
9990 }),
9991 cx,
9992 );
9993 }
9994 }
9995
9996 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9997 let buffer = self.buffer.read(cx).snapshot(cx);
9998 let mut selection = self.selections.first::<usize>(cx);
9999 selection.set_head(buffer.len(), SelectionGoal::None);
10000 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10001 s.select(vec![selection]);
10002 });
10003 }
10004
10005 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10006 let end = self.buffer.read(cx).read(cx).len();
10007 self.change_selections(None, window, cx, |s| {
10008 s.select_ranges(vec![0..end]);
10009 });
10010 }
10011
10012 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10013 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10014 let mut selections = self.selections.all::<Point>(cx);
10015 let max_point = display_map.buffer_snapshot.max_point();
10016 for selection in &mut selections {
10017 let rows = selection.spanned_rows(true, &display_map);
10018 selection.start = Point::new(rows.start.0, 0);
10019 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10020 selection.reversed = false;
10021 }
10022 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10023 s.select(selections);
10024 });
10025 }
10026
10027 pub fn split_selection_into_lines(
10028 &mut self,
10029 _: &SplitSelectionIntoLines,
10030 window: &mut Window,
10031 cx: &mut Context<Self>,
10032 ) {
10033 let selections = self
10034 .selections
10035 .all::<Point>(cx)
10036 .into_iter()
10037 .map(|selection| selection.start..selection.end)
10038 .collect::<Vec<_>>();
10039 self.unfold_ranges(&selections, true, true, cx);
10040
10041 let mut new_selection_ranges = Vec::new();
10042 {
10043 let buffer = self.buffer.read(cx).read(cx);
10044 for selection in selections {
10045 for row in selection.start.row..selection.end.row {
10046 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10047 new_selection_ranges.push(cursor..cursor);
10048 }
10049
10050 let is_multiline_selection = selection.start.row != selection.end.row;
10051 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10052 // so this action feels more ergonomic when paired with other selection operations
10053 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10054 if !should_skip_last {
10055 new_selection_ranges.push(selection.end..selection.end);
10056 }
10057 }
10058 }
10059 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10060 s.select_ranges(new_selection_ranges);
10061 });
10062 }
10063
10064 pub fn add_selection_above(
10065 &mut self,
10066 _: &AddSelectionAbove,
10067 window: &mut Window,
10068 cx: &mut Context<Self>,
10069 ) {
10070 self.add_selection(true, window, cx);
10071 }
10072
10073 pub fn add_selection_below(
10074 &mut self,
10075 _: &AddSelectionBelow,
10076 window: &mut Window,
10077 cx: &mut Context<Self>,
10078 ) {
10079 self.add_selection(false, window, cx);
10080 }
10081
10082 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10083 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10084 let mut selections = self.selections.all::<Point>(cx);
10085 let text_layout_details = self.text_layout_details(window);
10086 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10087 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10088 let range = oldest_selection.display_range(&display_map).sorted();
10089
10090 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10091 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10092 let positions = start_x.min(end_x)..start_x.max(end_x);
10093
10094 selections.clear();
10095 let mut stack = Vec::new();
10096 for row in range.start.row().0..=range.end.row().0 {
10097 if let Some(selection) = self.selections.build_columnar_selection(
10098 &display_map,
10099 DisplayRow(row),
10100 &positions,
10101 oldest_selection.reversed,
10102 &text_layout_details,
10103 ) {
10104 stack.push(selection.id);
10105 selections.push(selection);
10106 }
10107 }
10108
10109 if above {
10110 stack.reverse();
10111 }
10112
10113 AddSelectionsState { above, stack }
10114 });
10115
10116 let last_added_selection = *state.stack.last().unwrap();
10117 let mut new_selections = Vec::new();
10118 if above == state.above {
10119 let end_row = if above {
10120 DisplayRow(0)
10121 } else {
10122 display_map.max_point().row()
10123 };
10124
10125 'outer: for selection in selections {
10126 if selection.id == last_added_selection {
10127 let range = selection.display_range(&display_map).sorted();
10128 debug_assert_eq!(range.start.row(), range.end.row());
10129 let mut row = range.start.row();
10130 let positions =
10131 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10132 px(start)..px(end)
10133 } else {
10134 let start_x =
10135 display_map.x_for_display_point(range.start, &text_layout_details);
10136 let end_x =
10137 display_map.x_for_display_point(range.end, &text_layout_details);
10138 start_x.min(end_x)..start_x.max(end_x)
10139 };
10140
10141 while row != end_row {
10142 if above {
10143 row.0 -= 1;
10144 } else {
10145 row.0 += 1;
10146 }
10147
10148 if let Some(new_selection) = self.selections.build_columnar_selection(
10149 &display_map,
10150 row,
10151 &positions,
10152 selection.reversed,
10153 &text_layout_details,
10154 ) {
10155 state.stack.push(new_selection.id);
10156 if above {
10157 new_selections.push(new_selection);
10158 new_selections.push(selection);
10159 } else {
10160 new_selections.push(selection);
10161 new_selections.push(new_selection);
10162 }
10163
10164 continue 'outer;
10165 }
10166 }
10167 }
10168
10169 new_selections.push(selection);
10170 }
10171 } else {
10172 new_selections = selections;
10173 new_selections.retain(|s| s.id != last_added_selection);
10174 state.stack.pop();
10175 }
10176
10177 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10178 s.select(new_selections);
10179 });
10180 if state.stack.len() > 1 {
10181 self.add_selections_state = Some(state);
10182 }
10183 }
10184
10185 pub fn select_next_match_internal(
10186 &mut self,
10187 display_map: &DisplaySnapshot,
10188 replace_newest: bool,
10189 autoscroll: Option<Autoscroll>,
10190 window: &mut Window,
10191 cx: &mut Context<Self>,
10192 ) -> Result<()> {
10193 fn select_next_match_ranges(
10194 this: &mut Editor,
10195 range: Range<usize>,
10196 replace_newest: bool,
10197 auto_scroll: Option<Autoscroll>,
10198 window: &mut Window,
10199 cx: &mut Context<Editor>,
10200 ) {
10201 this.unfold_ranges(&[range.clone()], false, true, cx);
10202 this.change_selections(auto_scroll, window, cx, |s| {
10203 if replace_newest {
10204 s.delete(s.newest_anchor().id);
10205 }
10206 s.insert_range(range.clone());
10207 });
10208 }
10209
10210 let buffer = &display_map.buffer_snapshot;
10211 let mut selections = self.selections.all::<usize>(cx);
10212 if let Some(mut select_next_state) = self.select_next_state.take() {
10213 let query = &select_next_state.query;
10214 if !select_next_state.done {
10215 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10216 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10217 let mut next_selected_range = None;
10218
10219 let bytes_after_last_selection =
10220 buffer.bytes_in_range(last_selection.end..buffer.len());
10221 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10222 let query_matches = query
10223 .stream_find_iter(bytes_after_last_selection)
10224 .map(|result| (last_selection.end, result))
10225 .chain(
10226 query
10227 .stream_find_iter(bytes_before_first_selection)
10228 .map(|result| (0, result)),
10229 );
10230
10231 for (start_offset, query_match) in query_matches {
10232 let query_match = query_match.unwrap(); // can only fail due to I/O
10233 let offset_range =
10234 start_offset + query_match.start()..start_offset + query_match.end();
10235 let display_range = offset_range.start.to_display_point(display_map)
10236 ..offset_range.end.to_display_point(display_map);
10237
10238 if !select_next_state.wordwise
10239 || (!movement::is_inside_word(display_map, display_range.start)
10240 && !movement::is_inside_word(display_map, display_range.end))
10241 {
10242 // TODO: This is n^2, because we might check all the selections
10243 if !selections
10244 .iter()
10245 .any(|selection| selection.range().overlaps(&offset_range))
10246 {
10247 next_selected_range = Some(offset_range);
10248 break;
10249 }
10250 }
10251 }
10252
10253 if let Some(next_selected_range) = next_selected_range {
10254 select_next_match_ranges(
10255 self,
10256 next_selected_range,
10257 replace_newest,
10258 autoscroll,
10259 window,
10260 cx,
10261 );
10262 } else {
10263 select_next_state.done = true;
10264 }
10265 }
10266
10267 self.select_next_state = Some(select_next_state);
10268 } else {
10269 let mut only_carets = true;
10270 let mut same_text_selected = true;
10271 let mut selected_text = None;
10272
10273 let mut selections_iter = selections.iter().peekable();
10274 while let Some(selection) = selections_iter.next() {
10275 if selection.start != selection.end {
10276 only_carets = false;
10277 }
10278
10279 if same_text_selected {
10280 if selected_text.is_none() {
10281 selected_text =
10282 Some(buffer.text_for_range(selection.range()).collect::<String>());
10283 }
10284
10285 if let Some(next_selection) = selections_iter.peek() {
10286 if next_selection.range().len() == selection.range().len() {
10287 let next_selected_text = buffer
10288 .text_for_range(next_selection.range())
10289 .collect::<String>();
10290 if Some(next_selected_text) != selected_text {
10291 same_text_selected = false;
10292 selected_text = None;
10293 }
10294 } else {
10295 same_text_selected = false;
10296 selected_text = None;
10297 }
10298 }
10299 }
10300 }
10301
10302 if only_carets {
10303 for selection in &mut selections {
10304 let word_range = movement::surrounding_word(
10305 display_map,
10306 selection.start.to_display_point(display_map),
10307 );
10308 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10309 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10310 selection.goal = SelectionGoal::None;
10311 selection.reversed = false;
10312 select_next_match_ranges(
10313 self,
10314 selection.start..selection.end,
10315 replace_newest,
10316 autoscroll,
10317 window,
10318 cx,
10319 );
10320 }
10321
10322 if selections.len() == 1 {
10323 let selection = selections
10324 .last()
10325 .expect("ensured that there's only one selection");
10326 let query = buffer
10327 .text_for_range(selection.start..selection.end)
10328 .collect::<String>();
10329 let is_empty = query.is_empty();
10330 let select_state = SelectNextState {
10331 query: AhoCorasick::new(&[query])?,
10332 wordwise: true,
10333 done: is_empty,
10334 };
10335 self.select_next_state = Some(select_state);
10336 } else {
10337 self.select_next_state = None;
10338 }
10339 } else if let Some(selected_text) = selected_text {
10340 self.select_next_state = Some(SelectNextState {
10341 query: AhoCorasick::new(&[selected_text])?,
10342 wordwise: false,
10343 done: false,
10344 });
10345 self.select_next_match_internal(
10346 display_map,
10347 replace_newest,
10348 autoscroll,
10349 window,
10350 cx,
10351 )?;
10352 }
10353 }
10354 Ok(())
10355 }
10356
10357 pub fn select_all_matches(
10358 &mut self,
10359 _action: &SelectAllMatches,
10360 window: &mut Window,
10361 cx: &mut Context<Self>,
10362 ) -> Result<()> {
10363 self.push_to_selection_history();
10364 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10365
10366 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10367 let Some(select_next_state) = self.select_next_state.as_mut() else {
10368 return Ok(());
10369 };
10370 if select_next_state.done {
10371 return Ok(());
10372 }
10373
10374 let mut new_selections = self.selections.all::<usize>(cx);
10375
10376 let buffer = &display_map.buffer_snapshot;
10377 let query_matches = select_next_state
10378 .query
10379 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10380
10381 for query_match in query_matches {
10382 let query_match = query_match.unwrap(); // can only fail due to I/O
10383 let offset_range = query_match.start()..query_match.end();
10384 let display_range = offset_range.start.to_display_point(&display_map)
10385 ..offset_range.end.to_display_point(&display_map);
10386
10387 if !select_next_state.wordwise
10388 || (!movement::is_inside_word(&display_map, display_range.start)
10389 && !movement::is_inside_word(&display_map, display_range.end))
10390 {
10391 self.selections.change_with(cx, |selections| {
10392 new_selections.push(Selection {
10393 id: selections.new_selection_id(),
10394 start: offset_range.start,
10395 end: offset_range.end,
10396 reversed: false,
10397 goal: SelectionGoal::None,
10398 });
10399 });
10400 }
10401 }
10402
10403 new_selections.sort_by_key(|selection| selection.start);
10404 let mut ix = 0;
10405 while ix + 1 < new_selections.len() {
10406 let current_selection = &new_selections[ix];
10407 let next_selection = &new_selections[ix + 1];
10408 if current_selection.range().overlaps(&next_selection.range()) {
10409 if current_selection.id < next_selection.id {
10410 new_selections.remove(ix + 1);
10411 } else {
10412 new_selections.remove(ix);
10413 }
10414 } else {
10415 ix += 1;
10416 }
10417 }
10418
10419 let reversed = self.selections.oldest::<usize>(cx).reversed;
10420
10421 for selection in new_selections.iter_mut() {
10422 selection.reversed = reversed;
10423 }
10424
10425 select_next_state.done = true;
10426 self.unfold_ranges(
10427 &new_selections
10428 .iter()
10429 .map(|selection| selection.range())
10430 .collect::<Vec<_>>(),
10431 false,
10432 false,
10433 cx,
10434 );
10435 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10436 selections.select(new_selections)
10437 });
10438
10439 Ok(())
10440 }
10441
10442 pub fn select_next(
10443 &mut self,
10444 action: &SelectNext,
10445 window: &mut Window,
10446 cx: &mut Context<Self>,
10447 ) -> Result<()> {
10448 self.push_to_selection_history();
10449 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10450 self.select_next_match_internal(
10451 &display_map,
10452 action.replace_newest,
10453 Some(Autoscroll::newest()),
10454 window,
10455 cx,
10456 )?;
10457 Ok(())
10458 }
10459
10460 pub fn select_previous(
10461 &mut self,
10462 action: &SelectPrevious,
10463 window: &mut Window,
10464 cx: &mut Context<Self>,
10465 ) -> Result<()> {
10466 self.push_to_selection_history();
10467 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10468 let buffer = &display_map.buffer_snapshot;
10469 let mut selections = self.selections.all::<usize>(cx);
10470 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10471 let query = &select_prev_state.query;
10472 if !select_prev_state.done {
10473 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10474 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10475 let mut next_selected_range = None;
10476 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10477 let bytes_before_last_selection =
10478 buffer.reversed_bytes_in_range(0..last_selection.start);
10479 let bytes_after_first_selection =
10480 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10481 let query_matches = query
10482 .stream_find_iter(bytes_before_last_selection)
10483 .map(|result| (last_selection.start, result))
10484 .chain(
10485 query
10486 .stream_find_iter(bytes_after_first_selection)
10487 .map(|result| (buffer.len(), result)),
10488 );
10489 for (end_offset, query_match) in query_matches {
10490 let query_match = query_match.unwrap(); // can only fail due to I/O
10491 let offset_range =
10492 end_offset - query_match.end()..end_offset - query_match.start();
10493 let display_range = offset_range.start.to_display_point(&display_map)
10494 ..offset_range.end.to_display_point(&display_map);
10495
10496 if !select_prev_state.wordwise
10497 || (!movement::is_inside_word(&display_map, display_range.start)
10498 && !movement::is_inside_word(&display_map, display_range.end))
10499 {
10500 next_selected_range = Some(offset_range);
10501 break;
10502 }
10503 }
10504
10505 if let Some(next_selected_range) = next_selected_range {
10506 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10507 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10508 if action.replace_newest {
10509 s.delete(s.newest_anchor().id);
10510 }
10511 s.insert_range(next_selected_range);
10512 });
10513 } else {
10514 select_prev_state.done = true;
10515 }
10516 }
10517
10518 self.select_prev_state = Some(select_prev_state);
10519 } else {
10520 let mut only_carets = true;
10521 let mut same_text_selected = true;
10522 let mut selected_text = None;
10523
10524 let mut selections_iter = selections.iter().peekable();
10525 while let Some(selection) = selections_iter.next() {
10526 if selection.start != selection.end {
10527 only_carets = false;
10528 }
10529
10530 if same_text_selected {
10531 if selected_text.is_none() {
10532 selected_text =
10533 Some(buffer.text_for_range(selection.range()).collect::<String>());
10534 }
10535
10536 if let Some(next_selection) = selections_iter.peek() {
10537 if next_selection.range().len() == selection.range().len() {
10538 let next_selected_text = buffer
10539 .text_for_range(next_selection.range())
10540 .collect::<String>();
10541 if Some(next_selected_text) != selected_text {
10542 same_text_selected = false;
10543 selected_text = None;
10544 }
10545 } else {
10546 same_text_selected = false;
10547 selected_text = None;
10548 }
10549 }
10550 }
10551 }
10552
10553 if only_carets {
10554 for selection in &mut selections {
10555 let word_range = movement::surrounding_word(
10556 &display_map,
10557 selection.start.to_display_point(&display_map),
10558 );
10559 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10560 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10561 selection.goal = SelectionGoal::None;
10562 selection.reversed = false;
10563 }
10564 if selections.len() == 1 {
10565 let selection = selections
10566 .last()
10567 .expect("ensured that there's only one selection");
10568 let query = buffer
10569 .text_for_range(selection.start..selection.end)
10570 .collect::<String>();
10571 let is_empty = query.is_empty();
10572 let select_state = SelectNextState {
10573 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10574 wordwise: true,
10575 done: is_empty,
10576 };
10577 self.select_prev_state = Some(select_state);
10578 } else {
10579 self.select_prev_state = None;
10580 }
10581
10582 self.unfold_ranges(
10583 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10584 false,
10585 true,
10586 cx,
10587 );
10588 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10589 s.select(selections);
10590 });
10591 } else if let Some(selected_text) = selected_text {
10592 self.select_prev_state = Some(SelectNextState {
10593 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10594 wordwise: false,
10595 done: false,
10596 });
10597 self.select_previous(action, window, cx)?;
10598 }
10599 }
10600 Ok(())
10601 }
10602
10603 pub fn toggle_comments(
10604 &mut self,
10605 action: &ToggleComments,
10606 window: &mut Window,
10607 cx: &mut Context<Self>,
10608 ) {
10609 if self.read_only(cx) {
10610 return;
10611 }
10612 let text_layout_details = &self.text_layout_details(window);
10613 self.transact(window, cx, |this, window, cx| {
10614 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10615 let mut edits = Vec::new();
10616 let mut selection_edit_ranges = Vec::new();
10617 let mut last_toggled_row = None;
10618 let snapshot = this.buffer.read(cx).read(cx);
10619 let empty_str: Arc<str> = Arc::default();
10620 let mut suffixes_inserted = Vec::new();
10621 let ignore_indent = action.ignore_indent;
10622
10623 fn comment_prefix_range(
10624 snapshot: &MultiBufferSnapshot,
10625 row: MultiBufferRow,
10626 comment_prefix: &str,
10627 comment_prefix_whitespace: &str,
10628 ignore_indent: bool,
10629 ) -> Range<Point> {
10630 let indent_size = if ignore_indent {
10631 0
10632 } else {
10633 snapshot.indent_size_for_line(row).len
10634 };
10635
10636 let start = Point::new(row.0, indent_size);
10637
10638 let mut line_bytes = snapshot
10639 .bytes_in_range(start..snapshot.max_point())
10640 .flatten()
10641 .copied();
10642
10643 // If this line currently begins with the line comment prefix, then record
10644 // the range containing the prefix.
10645 if line_bytes
10646 .by_ref()
10647 .take(comment_prefix.len())
10648 .eq(comment_prefix.bytes())
10649 {
10650 // Include any whitespace that matches the comment prefix.
10651 let matching_whitespace_len = line_bytes
10652 .zip(comment_prefix_whitespace.bytes())
10653 .take_while(|(a, b)| a == b)
10654 .count() as u32;
10655 let end = Point::new(
10656 start.row,
10657 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10658 );
10659 start..end
10660 } else {
10661 start..start
10662 }
10663 }
10664
10665 fn comment_suffix_range(
10666 snapshot: &MultiBufferSnapshot,
10667 row: MultiBufferRow,
10668 comment_suffix: &str,
10669 comment_suffix_has_leading_space: bool,
10670 ) -> Range<Point> {
10671 let end = Point::new(row.0, snapshot.line_len(row));
10672 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10673
10674 let mut line_end_bytes = snapshot
10675 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10676 .flatten()
10677 .copied();
10678
10679 let leading_space_len = if suffix_start_column > 0
10680 && line_end_bytes.next() == Some(b' ')
10681 && comment_suffix_has_leading_space
10682 {
10683 1
10684 } else {
10685 0
10686 };
10687
10688 // If this line currently begins with the line comment prefix, then record
10689 // the range containing the prefix.
10690 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10691 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10692 start..end
10693 } else {
10694 end..end
10695 }
10696 }
10697
10698 // TODO: Handle selections that cross excerpts
10699 for selection in &mut selections {
10700 let start_column = snapshot
10701 .indent_size_for_line(MultiBufferRow(selection.start.row))
10702 .len;
10703 let language = if let Some(language) =
10704 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10705 {
10706 language
10707 } else {
10708 continue;
10709 };
10710
10711 selection_edit_ranges.clear();
10712
10713 // If multiple selections contain a given row, avoid processing that
10714 // row more than once.
10715 let mut start_row = MultiBufferRow(selection.start.row);
10716 if last_toggled_row == Some(start_row) {
10717 start_row = start_row.next_row();
10718 }
10719 let end_row =
10720 if selection.end.row > selection.start.row && selection.end.column == 0 {
10721 MultiBufferRow(selection.end.row - 1)
10722 } else {
10723 MultiBufferRow(selection.end.row)
10724 };
10725 last_toggled_row = Some(end_row);
10726
10727 if start_row > end_row {
10728 continue;
10729 }
10730
10731 // If the language has line comments, toggle those.
10732 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10733
10734 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10735 if ignore_indent {
10736 full_comment_prefixes = full_comment_prefixes
10737 .into_iter()
10738 .map(|s| Arc::from(s.trim_end()))
10739 .collect();
10740 }
10741
10742 if !full_comment_prefixes.is_empty() {
10743 let first_prefix = full_comment_prefixes
10744 .first()
10745 .expect("prefixes is non-empty");
10746 let prefix_trimmed_lengths = full_comment_prefixes
10747 .iter()
10748 .map(|p| p.trim_end_matches(' ').len())
10749 .collect::<SmallVec<[usize; 4]>>();
10750
10751 let mut all_selection_lines_are_comments = true;
10752
10753 for row in start_row.0..=end_row.0 {
10754 let row = MultiBufferRow(row);
10755 if start_row < end_row && snapshot.is_line_blank(row) {
10756 continue;
10757 }
10758
10759 let prefix_range = full_comment_prefixes
10760 .iter()
10761 .zip(prefix_trimmed_lengths.iter().copied())
10762 .map(|(prefix, trimmed_prefix_len)| {
10763 comment_prefix_range(
10764 snapshot.deref(),
10765 row,
10766 &prefix[..trimmed_prefix_len],
10767 &prefix[trimmed_prefix_len..],
10768 ignore_indent,
10769 )
10770 })
10771 .max_by_key(|range| range.end.column - range.start.column)
10772 .expect("prefixes is non-empty");
10773
10774 if prefix_range.is_empty() {
10775 all_selection_lines_are_comments = false;
10776 }
10777
10778 selection_edit_ranges.push(prefix_range);
10779 }
10780
10781 if all_selection_lines_are_comments {
10782 edits.extend(
10783 selection_edit_ranges
10784 .iter()
10785 .cloned()
10786 .map(|range| (range, empty_str.clone())),
10787 );
10788 } else {
10789 let min_column = selection_edit_ranges
10790 .iter()
10791 .map(|range| range.start.column)
10792 .min()
10793 .unwrap_or(0);
10794 edits.extend(selection_edit_ranges.iter().map(|range| {
10795 let position = Point::new(range.start.row, min_column);
10796 (position..position, first_prefix.clone())
10797 }));
10798 }
10799 } else if let Some((full_comment_prefix, comment_suffix)) =
10800 language.block_comment_delimiters()
10801 {
10802 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10803 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10804 let prefix_range = comment_prefix_range(
10805 snapshot.deref(),
10806 start_row,
10807 comment_prefix,
10808 comment_prefix_whitespace,
10809 ignore_indent,
10810 );
10811 let suffix_range = comment_suffix_range(
10812 snapshot.deref(),
10813 end_row,
10814 comment_suffix.trim_start_matches(' '),
10815 comment_suffix.starts_with(' '),
10816 );
10817
10818 if prefix_range.is_empty() || suffix_range.is_empty() {
10819 edits.push((
10820 prefix_range.start..prefix_range.start,
10821 full_comment_prefix.clone(),
10822 ));
10823 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10824 suffixes_inserted.push((end_row, comment_suffix.len()));
10825 } else {
10826 edits.push((prefix_range, empty_str.clone()));
10827 edits.push((suffix_range, empty_str.clone()));
10828 }
10829 } else {
10830 continue;
10831 }
10832 }
10833
10834 drop(snapshot);
10835 this.buffer.update(cx, |buffer, cx| {
10836 buffer.edit(edits, None, cx);
10837 });
10838
10839 // Adjust selections so that they end before any comment suffixes that
10840 // were inserted.
10841 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10842 let mut selections = this.selections.all::<Point>(cx);
10843 let snapshot = this.buffer.read(cx).read(cx);
10844 for selection in &mut selections {
10845 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10846 match row.cmp(&MultiBufferRow(selection.end.row)) {
10847 Ordering::Less => {
10848 suffixes_inserted.next();
10849 continue;
10850 }
10851 Ordering::Greater => break,
10852 Ordering::Equal => {
10853 if selection.end.column == snapshot.line_len(row) {
10854 if selection.is_empty() {
10855 selection.start.column -= suffix_len as u32;
10856 }
10857 selection.end.column -= suffix_len as u32;
10858 }
10859 break;
10860 }
10861 }
10862 }
10863 }
10864
10865 drop(snapshot);
10866 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10867 s.select(selections)
10868 });
10869
10870 let selections = this.selections.all::<Point>(cx);
10871 let selections_on_single_row = selections.windows(2).all(|selections| {
10872 selections[0].start.row == selections[1].start.row
10873 && selections[0].end.row == selections[1].end.row
10874 && selections[0].start.row == selections[0].end.row
10875 });
10876 let selections_selecting = selections
10877 .iter()
10878 .any(|selection| selection.start != selection.end);
10879 let advance_downwards = action.advance_downwards
10880 && selections_on_single_row
10881 && !selections_selecting
10882 && !matches!(this.mode, EditorMode::SingleLine { .. });
10883
10884 if advance_downwards {
10885 let snapshot = this.buffer.read(cx).snapshot(cx);
10886
10887 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10888 s.move_cursors_with(|display_snapshot, display_point, _| {
10889 let mut point = display_point.to_point(display_snapshot);
10890 point.row += 1;
10891 point = snapshot.clip_point(point, Bias::Left);
10892 let display_point = point.to_display_point(display_snapshot);
10893 let goal = SelectionGoal::HorizontalPosition(
10894 display_snapshot
10895 .x_for_display_point(display_point, text_layout_details)
10896 .into(),
10897 );
10898 (display_point, goal)
10899 })
10900 });
10901 }
10902 });
10903 }
10904
10905 pub fn select_enclosing_symbol(
10906 &mut self,
10907 _: &SelectEnclosingSymbol,
10908 window: &mut Window,
10909 cx: &mut Context<Self>,
10910 ) {
10911 let buffer = self.buffer.read(cx).snapshot(cx);
10912 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10913
10914 fn update_selection(
10915 selection: &Selection<usize>,
10916 buffer_snap: &MultiBufferSnapshot,
10917 ) -> Option<Selection<usize>> {
10918 let cursor = selection.head();
10919 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10920 for symbol in symbols.iter().rev() {
10921 let start = symbol.range.start.to_offset(buffer_snap);
10922 let end = symbol.range.end.to_offset(buffer_snap);
10923 let new_range = start..end;
10924 if start < selection.start || end > selection.end {
10925 return Some(Selection {
10926 id: selection.id,
10927 start: new_range.start,
10928 end: new_range.end,
10929 goal: SelectionGoal::None,
10930 reversed: selection.reversed,
10931 });
10932 }
10933 }
10934 None
10935 }
10936
10937 let mut selected_larger_symbol = false;
10938 let new_selections = old_selections
10939 .iter()
10940 .map(|selection| match update_selection(selection, &buffer) {
10941 Some(new_selection) => {
10942 if new_selection.range() != selection.range() {
10943 selected_larger_symbol = true;
10944 }
10945 new_selection
10946 }
10947 None => selection.clone(),
10948 })
10949 .collect::<Vec<_>>();
10950
10951 if selected_larger_symbol {
10952 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10953 s.select(new_selections);
10954 });
10955 }
10956 }
10957
10958 pub fn select_larger_syntax_node(
10959 &mut self,
10960 _: &SelectLargerSyntaxNode,
10961 window: &mut Window,
10962 cx: &mut Context<Self>,
10963 ) {
10964 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10965 let buffer = self.buffer.read(cx).snapshot(cx);
10966 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10967
10968 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10969 let mut selected_larger_node = false;
10970 let new_selections = old_selections
10971 .iter()
10972 .map(|selection| {
10973 let old_range = selection.start..selection.end;
10974 let mut new_range = old_range.clone();
10975 let mut new_node = None;
10976 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10977 {
10978 new_node = Some(node);
10979 new_range = match containing_range {
10980 MultiOrSingleBufferOffsetRange::Single(_) => break,
10981 MultiOrSingleBufferOffsetRange::Multi(range) => range,
10982 };
10983 if !display_map.intersects_fold(new_range.start)
10984 && !display_map.intersects_fold(new_range.end)
10985 {
10986 break;
10987 }
10988 }
10989
10990 if let Some(node) = new_node {
10991 // Log the ancestor, to support using this action as a way to explore TreeSitter
10992 // nodes. Parent and grandparent are also logged because this operation will not
10993 // visit nodes that have the same range as their parent.
10994 log::info!("Node: {node:?}");
10995 let parent = node.parent();
10996 log::info!("Parent: {parent:?}");
10997 let grandparent = parent.and_then(|x| x.parent());
10998 log::info!("Grandparent: {grandparent:?}");
10999 }
11000
11001 selected_larger_node |= new_range != old_range;
11002 Selection {
11003 id: selection.id,
11004 start: new_range.start,
11005 end: new_range.end,
11006 goal: SelectionGoal::None,
11007 reversed: selection.reversed,
11008 }
11009 })
11010 .collect::<Vec<_>>();
11011
11012 if selected_larger_node {
11013 stack.push(old_selections);
11014 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11015 s.select(new_selections);
11016 });
11017 }
11018 self.select_larger_syntax_node_stack = stack;
11019 }
11020
11021 pub fn select_smaller_syntax_node(
11022 &mut self,
11023 _: &SelectSmallerSyntaxNode,
11024 window: &mut Window,
11025 cx: &mut Context<Self>,
11026 ) {
11027 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11028 if let Some(selections) = stack.pop() {
11029 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11030 s.select(selections.to_vec());
11031 });
11032 }
11033 self.select_larger_syntax_node_stack = stack;
11034 }
11035
11036 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11037 if !EditorSettings::get_global(cx).gutter.runnables {
11038 self.clear_tasks();
11039 return Task::ready(());
11040 }
11041 let project = self.project.as_ref().map(Entity::downgrade);
11042 cx.spawn_in(window, |this, mut cx| async move {
11043 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11044 let Some(project) = project.and_then(|p| p.upgrade()) else {
11045 return;
11046 };
11047 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11048 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11049 }) else {
11050 return;
11051 };
11052
11053 let hide_runnables = project
11054 .update(&mut cx, |project, cx| {
11055 // Do not display any test indicators in non-dev server remote projects.
11056 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11057 })
11058 .unwrap_or(true);
11059 if hide_runnables {
11060 return;
11061 }
11062 let new_rows =
11063 cx.background_spawn({
11064 let snapshot = display_snapshot.clone();
11065 async move {
11066 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11067 }
11068 })
11069 .await;
11070
11071 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11072 this.update(&mut cx, |this, _| {
11073 this.clear_tasks();
11074 for (key, value) in rows {
11075 this.insert_tasks(key, value);
11076 }
11077 })
11078 .ok();
11079 })
11080 }
11081 fn fetch_runnable_ranges(
11082 snapshot: &DisplaySnapshot,
11083 range: Range<Anchor>,
11084 ) -> Vec<language::RunnableRange> {
11085 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11086 }
11087
11088 fn runnable_rows(
11089 project: Entity<Project>,
11090 snapshot: DisplaySnapshot,
11091 runnable_ranges: Vec<RunnableRange>,
11092 mut cx: AsyncWindowContext,
11093 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11094 runnable_ranges
11095 .into_iter()
11096 .filter_map(|mut runnable| {
11097 let tasks = cx
11098 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11099 .ok()?;
11100 if tasks.is_empty() {
11101 return None;
11102 }
11103
11104 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11105
11106 let row = snapshot
11107 .buffer_snapshot
11108 .buffer_line_for_row(MultiBufferRow(point.row))?
11109 .1
11110 .start
11111 .row;
11112
11113 let context_range =
11114 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11115 Some((
11116 (runnable.buffer_id, row),
11117 RunnableTasks {
11118 templates: tasks,
11119 offset: snapshot
11120 .buffer_snapshot
11121 .anchor_before(runnable.run_range.start),
11122 context_range,
11123 column: point.column,
11124 extra_variables: runnable.extra_captures,
11125 },
11126 ))
11127 })
11128 .collect()
11129 }
11130
11131 fn templates_with_tags(
11132 project: &Entity<Project>,
11133 runnable: &mut Runnable,
11134 cx: &mut App,
11135 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11136 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11137 let (worktree_id, file) = project
11138 .buffer_for_id(runnable.buffer, cx)
11139 .and_then(|buffer| buffer.read(cx).file())
11140 .map(|file| (file.worktree_id(cx), file.clone()))
11141 .unzip();
11142
11143 (
11144 project.task_store().read(cx).task_inventory().cloned(),
11145 worktree_id,
11146 file,
11147 )
11148 });
11149
11150 let tags = mem::take(&mut runnable.tags);
11151 let mut tags: Vec<_> = tags
11152 .into_iter()
11153 .flat_map(|tag| {
11154 let tag = tag.0.clone();
11155 inventory
11156 .as_ref()
11157 .into_iter()
11158 .flat_map(|inventory| {
11159 inventory.read(cx).list_tasks(
11160 file.clone(),
11161 Some(runnable.language.clone()),
11162 worktree_id,
11163 cx,
11164 )
11165 })
11166 .filter(move |(_, template)| {
11167 template.tags.iter().any(|source_tag| source_tag == &tag)
11168 })
11169 })
11170 .sorted_by_key(|(kind, _)| kind.to_owned())
11171 .collect();
11172 if let Some((leading_tag_source, _)) = tags.first() {
11173 // Strongest source wins; if we have worktree tag binding, prefer that to
11174 // global and language bindings;
11175 // if we have a global binding, prefer that to language binding.
11176 let first_mismatch = tags
11177 .iter()
11178 .position(|(tag_source, _)| tag_source != leading_tag_source);
11179 if let Some(index) = first_mismatch {
11180 tags.truncate(index);
11181 }
11182 }
11183
11184 tags
11185 }
11186
11187 pub fn move_to_enclosing_bracket(
11188 &mut self,
11189 _: &MoveToEnclosingBracket,
11190 window: &mut Window,
11191 cx: &mut Context<Self>,
11192 ) {
11193 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11194 s.move_offsets_with(|snapshot, selection| {
11195 let Some(enclosing_bracket_ranges) =
11196 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11197 else {
11198 return;
11199 };
11200
11201 let mut best_length = usize::MAX;
11202 let mut best_inside = false;
11203 let mut best_in_bracket_range = false;
11204 let mut best_destination = None;
11205 for (open, close) in enclosing_bracket_ranges {
11206 let close = close.to_inclusive();
11207 let length = close.end() - open.start;
11208 let inside = selection.start >= open.end && selection.end <= *close.start();
11209 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11210 || close.contains(&selection.head());
11211
11212 // If best is next to a bracket and current isn't, skip
11213 if !in_bracket_range && best_in_bracket_range {
11214 continue;
11215 }
11216
11217 // Prefer smaller lengths unless best is inside and current isn't
11218 if length > best_length && (best_inside || !inside) {
11219 continue;
11220 }
11221
11222 best_length = length;
11223 best_inside = inside;
11224 best_in_bracket_range = in_bracket_range;
11225 best_destination = Some(
11226 if close.contains(&selection.start) && close.contains(&selection.end) {
11227 if inside {
11228 open.end
11229 } else {
11230 open.start
11231 }
11232 } else if inside {
11233 *close.start()
11234 } else {
11235 *close.end()
11236 },
11237 );
11238 }
11239
11240 if let Some(destination) = best_destination {
11241 selection.collapse_to(destination, SelectionGoal::None);
11242 }
11243 })
11244 });
11245 }
11246
11247 pub fn undo_selection(
11248 &mut self,
11249 _: &UndoSelection,
11250 window: &mut Window,
11251 cx: &mut Context<Self>,
11252 ) {
11253 self.end_selection(window, cx);
11254 self.selection_history.mode = SelectionHistoryMode::Undoing;
11255 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
11256 self.change_selections(None, window, cx, |s| {
11257 s.select_anchors(entry.selections.to_vec())
11258 });
11259 self.select_next_state = entry.select_next_state;
11260 self.select_prev_state = entry.select_prev_state;
11261 self.add_selections_state = entry.add_selections_state;
11262 self.request_autoscroll(Autoscroll::newest(), cx);
11263 }
11264 self.selection_history.mode = SelectionHistoryMode::Normal;
11265 }
11266
11267 pub fn redo_selection(
11268 &mut self,
11269 _: &RedoSelection,
11270 window: &mut Window,
11271 cx: &mut Context<Self>,
11272 ) {
11273 self.end_selection(window, cx);
11274 self.selection_history.mode = SelectionHistoryMode::Redoing;
11275 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11276 self.change_selections(None, window, cx, |s| {
11277 s.select_anchors(entry.selections.to_vec())
11278 });
11279 self.select_next_state = entry.select_next_state;
11280 self.select_prev_state = entry.select_prev_state;
11281 self.add_selections_state = entry.add_selections_state;
11282 self.request_autoscroll(Autoscroll::newest(), cx);
11283 }
11284 self.selection_history.mode = SelectionHistoryMode::Normal;
11285 }
11286
11287 pub fn expand_excerpts(
11288 &mut self,
11289 action: &ExpandExcerpts,
11290 _: &mut Window,
11291 cx: &mut Context<Self>,
11292 ) {
11293 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11294 }
11295
11296 pub fn expand_excerpts_down(
11297 &mut self,
11298 action: &ExpandExcerptsDown,
11299 _: &mut Window,
11300 cx: &mut Context<Self>,
11301 ) {
11302 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11303 }
11304
11305 pub fn expand_excerpts_up(
11306 &mut self,
11307 action: &ExpandExcerptsUp,
11308 _: &mut Window,
11309 cx: &mut Context<Self>,
11310 ) {
11311 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11312 }
11313
11314 pub fn expand_excerpts_for_direction(
11315 &mut self,
11316 lines: u32,
11317 direction: ExpandExcerptDirection,
11318
11319 cx: &mut Context<Self>,
11320 ) {
11321 let selections = self.selections.disjoint_anchors();
11322
11323 let lines = if lines == 0 {
11324 EditorSettings::get_global(cx).expand_excerpt_lines
11325 } else {
11326 lines
11327 };
11328
11329 self.buffer.update(cx, |buffer, cx| {
11330 let snapshot = buffer.snapshot(cx);
11331 let mut excerpt_ids = selections
11332 .iter()
11333 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11334 .collect::<Vec<_>>();
11335 excerpt_ids.sort();
11336 excerpt_ids.dedup();
11337 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11338 })
11339 }
11340
11341 pub fn expand_excerpt(
11342 &mut self,
11343 excerpt: ExcerptId,
11344 direction: ExpandExcerptDirection,
11345 cx: &mut Context<Self>,
11346 ) {
11347 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11348 self.buffer.update(cx, |buffer, cx| {
11349 buffer.expand_excerpts([excerpt], lines, direction, cx)
11350 })
11351 }
11352
11353 pub fn go_to_singleton_buffer_point(
11354 &mut self,
11355 point: Point,
11356 window: &mut Window,
11357 cx: &mut Context<Self>,
11358 ) {
11359 self.go_to_singleton_buffer_range(point..point, window, cx);
11360 }
11361
11362 pub fn go_to_singleton_buffer_range(
11363 &mut self,
11364 range: Range<Point>,
11365 window: &mut Window,
11366 cx: &mut Context<Self>,
11367 ) {
11368 let multibuffer = self.buffer().read(cx);
11369 let Some(buffer) = multibuffer.as_singleton() else {
11370 return;
11371 };
11372 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11373 return;
11374 };
11375 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11376 return;
11377 };
11378 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11379 s.select_anchor_ranges([start..end])
11380 });
11381 }
11382
11383 fn go_to_diagnostic(
11384 &mut self,
11385 _: &GoToDiagnostic,
11386 window: &mut Window,
11387 cx: &mut Context<Self>,
11388 ) {
11389 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11390 }
11391
11392 fn go_to_prev_diagnostic(
11393 &mut self,
11394 _: &GoToPreviousDiagnostic,
11395 window: &mut Window,
11396 cx: &mut Context<Self>,
11397 ) {
11398 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11399 }
11400
11401 pub fn go_to_diagnostic_impl(
11402 &mut self,
11403 direction: Direction,
11404 window: &mut Window,
11405 cx: &mut Context<Self>,
11406 ) {
11407 let buffer = self.buffer.read(cx).snapshot(cx);
11408 let selection = self.selections.newest::<usize>(cx);
11409
11410 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11411 if direction == Direction::Next {
11412 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11413 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11414 return;
11415 };
11416 self.activate_diagnostics(
11417 buffer_id,
11418 popover.local_diagnostic.diagnostic.group_id,
11419 window,
11420 cx,
11421 );
11422 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11423 let primary_range_start = active_diagnostics.primary_range.start;
11424 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11425 let mut new_selection = s.newest_anchor().clone();
11426 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11427 s.select_anchors(vec![new_selection.clone()]);
11428 });
11429 self.refresh_inline_completion(false, true, window, cx);
11430 }
11431 return;
11432 }
11433 }
11434
11435 let active_group_id = self
11436 .active_diagnostics
11437 .as_ref()
11438 .map(|active_group| active_group.group_id);
11439 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11440 active_diagnostics
11441 .primary_range
11442 .to_offset(&buffer)
11443 .to_inclusive()
11444 });
11445 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11446 if active_primary_range.contains(&selection.head()) {
11447 *active_primary_range.start()
11448 } else {
11449 selection.head()
11450 }
11451 } else {
11452 selection.head()
11453 };
11454
11455 let snapshot = self.snapshot(window, cx);
11456 let primary_diagnostics_before = buffer
11457 .diagnostics_in_range::<usize>(0..search_start)
11458 .filter(|entry| entry.diagnostic.is_primary)
11459 .filter(|entry| entry.range.start != entry.range.end)
11460 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11461 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11462 .collect::<Vec<_>>();
11463 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11464 primary_diagnostics_before
11465 .iter()
11466 .position(|entry| entry.diagnostic.group_id == active_group_id)
11467 });
11468
11469 let primary_diagnostics_after = buffer
11470 .diagnostics_in_range::<usize>(search_start..buffer.len())
11471 .filter(|entry| entry.diagnostic.is_primary)
11472 .filter(|entry| entry.range.start != entry.range.end)
11473 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11474 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11475 .collect::<Vec<_>>();
11476 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11477 primary_diagnostics_after
11478 .iter()
11479 .enumerate()
11480 .rev()
11481 .find_map(|(i, entry)| {
11482 if entry.diagnostic.group_id == active_group_id {
11483 Some(i)
11484 } else {
11485 None
11486 }
11487 })
11488 });
11489
11490 let next_primary_diagnostic = match direction {
11491 Direction::Prev => primary_diagnostics_before
11492 .iter()
11493 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11494 .rev()
11495 .next(),
11496 Direction::Next => primary_diagnostics_after
11497 .iter()
11498 .skip(
11499 last_same_group_diagnostic_after
11500 .map(|index| index + 1)
11501 .unwrap_or(0),
11502 )
11503 .next(),
11504 };
11505
11506 // Cycle around to the start of the buffer, potentially moving back to the start of
11507 // the currently active diagnostic.
11508 let cycle_around = || match direction {
11509 Direction::Prev => primary_diagnostics_after
11510 .iter()
11511 .rev()
11512 .chain(primary_diagnostics_before.iter().rev())
11513 .next(),
11514 Direction::Next => primary_diagnostics_before
11515 .iter()
11516 .chain(primary_diagnostics_after.iter())
11517 .next(),
11518 };
11519
11520 if let Some((primary_range, group_id)) = next_primary_diagnostic
11521 .or_else(cycle_around)
11522 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11523 {
11524 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11525 return;
11526 };
11527 self.activate_diagnostics(buffer_id, group_id, window, cx);
11528 if self.active_diagnostics.is_some() {
11529 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11530 s.select(vec![Selection {
11531 id: selection.id,
11532 start: primary_range.start,
11533 end: primary_range.start,
11534 reversed: false,
11535 goal: SelectionGoal::None,
11536 }]);
11537 });
11538 self.refresh_inline_completion(false, true, window, cx);
11539 }
11540 }
11541 }
11542
11543 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11544 let snapshot = self.snapshot(window, cx);
11545 let selection = self.selections.newest::<Point>(cx);
11546 self.go_to_hunk_after_or_before_position(
11547 &snapshot,
11548 selection.head(),
11549 Direction::Next,
11550 window,
11551 cx,
11552 );
11553 }
11554
11555 fn go_to_hunk_after_or_before_position(
11556 &mut self,
11557 snapshot: &EditorSnapshot,
11558 position: Point,
11559 direction: Direction,
11560 window: &mut Window,
11561 cx: &mut Context<Editor>,
11562 ) {
11563 let row = if direction == Direction::Next {
11564 self.hunk_after_position(snapshot, position)
11565 .map(|hunk| hunk.row_range.start)
11566 } else {
11567 self.hunk_before_position(snapshot, position)
11568 };
11569
11570 if let Some(row) = row {
11571 let destination = Point::new(row.0, 0);
11572 let autoscroll = Autoscroll::center();
11573
11574 self.unfold_ranges(&[destination..destination], false, false, cx);
11575 self.change_selections(Some(autoscroll), window, cx, |s| {
11576 s.select_ranges([destination..destination]);
11577 });
11578 }
11579 }
11580
11581 fn hunk_after_position(
11582 &mut self,
11583 snapshot: &EditorSnapshot,
11584 position: Point,
11585 ) -> Option<MultiBufferDiffHunk> {
11586 snapshot
11587 .buffer_snapshot
11588 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11589 .find(|hunk| hunk.row_range.start.0 > position.row)
11590 .or_else(|| {
11591 snapshot
11592 .buffer_snapshot
11593 .diff_hunks_in_range(Point::zero()..position)
11594 .find(|hunk| hunk.row_range.end.0 < position.row)
11595 })
11596 }
11597
11598 fn go_to_prev_hunk(
11599 &mut self,
11600 _: &GoToPreviousHunk,
11601 window: &mut Window,
11602 cx: &mut Context<Self>,
11603 ) {
11604 let snapshot = self.snapshot(window, cx);
11605 let selection = self.selections.newest::<Point>(cx);
11606 self.go_to_hunk_after_or_before_position(
11607 &snapshot,
11608 selection.head(),
11609 Direction::Prev,
11610 window,
11611 cx,
11612 );
11613 }
11614
11615 fn hunk_before_position(
11616 &mut self,
11617 snapshot: &EditorSnapshot,
11618 position: Point,
11619 ) -> Option<MultiBufferRow> {
11620 snapshot
11621 .buffer_snapshot
11622 .diff_hunk_before(position)
11623 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
11624 }
11625
11626 pub fn go_to_definition(
11627 &mut self,
11628 _: &GoToDefinition,
11629 window: &mut Window,
11630 cx: &mut Context<Self>,
11631 ) -> Task<Result<Navigated>> {
11632 let definition =
11633 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11634 cx.spawn_in(window, |editor, mut cx| async move {
11635 if definition.await? == Navigated::Yes {
11636 return Ok(Navigated::Yes);
11637 }
11638 match editor.update_in(&mut cx, |editor, window, cx| {
11639 editor.find_all_references(&FindAllReferences, window, cx)
11640 })? {
11641 Some(references) => references.await,
11642 None => Ok(Navigated::No),
11643 }
11644 })
11645 }
11646
11647 pub fn go_to_declaration(
11648 &mut self,
11649 _: &GoToDeclaration,
11650 window: &mut Window,
11651 cx: &mut Context<Self>,
11652 ) -> Task<Result<Navigated>> {
11653 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11654 }
11655
11656 pub fn go_to_declaration_split(
11657 &mut self,
11658 _: &GoToDeclaration,
11659 window: &mut Window,
11660 cx: &mut Context<Self>,
11661 ) -> Task<Result<Navigated>> {
11662 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11663 }
11664
11665 pub fn go_to_implementation(
11666 &mut self,
11667 _: &GoToImplementation,
11668 window: &mut Window,
11669 cx: &mut Context<Self>,
11670 ) -> Task<Result<Navigated>> {
11671 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11672 }
11673
11674 pub fn go_to_implementation_split(
11675 &mut self,
11676 _: &GoToImplementationSplit,
11677 window: &mut Window,
11678 cx: &mut Context<Self>,
11679 ) -> Task<Result<Navigated>> {
11680 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11681 }
11682
11683 pub fn go_to_type_definition(
11684 &mut self,
11685 _: &GoToTypeDefinition,
11686 window: &mut Window,
11687 cx: &mut Context<Self>,
11688 ) -> Task<Result<Navigated>> {
11689 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11690 }
11691
11692 pub fn go_to_definition_split(
11693 &mut self,
11694 _: &GoToDefinitionSplit,
11695 window: &mut Window,
11696 cx: &mut Context<Self>,
11697 ) -> Task<Result<Navigated>> {
11698 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11699 }
11700
11701 pub fn go_to_type_definition_split(
11702 &mut self,
11703 _: &GoToTypeDefinitionSplit,
11704 window: &mut Window,
11705 cx: &mut Context<Self>,
11706 ) -> Task<Result<Navigated>> {
11707 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11708 }
11709
11710 fn go_to_definition_of_kind(
11711 &mut self,
11712 kind: GotoDefinitionKind,
11713 split: bool,
11714 window: &mut Window,
11715 cx: &mut Context<Self>,
11716 ) -> Task<Result<Navigated>> {
11717 let Some(provider) = self.semantics_provider.clone() else {
11718 return Task::ready(Ok(Navigated::No));
11719 };
11720 let head = self.selections.newest::<usize>(cx).head();
11721 let buffer = self.buffer.read(cx);
11722 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11723 text_anchor
11724 } else {
11725 return Task::ready(Ok(Navigated::No));
11726 };
11727
11728 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11729 return Task::ready(Ok(Navigated::No));
11730 };
11731
11732 cx.spawn_in(window, |editor, mut cx| async move {
11733 let definitions = definitions.await?;
11734 let navigated = editor
11735 .update_in(&mut cx, |editor, window, cx| {
11736 editor.navigate_to_hover_links(
11737 Some(kind),
11738 definitions
11739 .into_iter()
11740 .filter(|location| {
11741 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11742 })
11743 .map(HoverLink::Text)
11744 .collect::<Vec<_>>(),
11745 split,
11746 window,
11747 cx,
11748 )
11749 })?
11750 .await?;
11751 anyhow::Ok(navigated)
11752 })
11753 }
11754
11755 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11756 let selection = self.selections.newest_anchor();
11757 let head = selection.head();
11758 let tail = selection.tail();
11759
11760 let Some((buffer, start_position)) =
11761 self.buffer.read(cx).text_anchor_for_position(head, cx)
11762 else {
11763 return;
11764 };
11765
11766 let end_position = if head != tail {
11767 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11768 return;
11769 };
11770 Some(pos)
11771 } else {
11772 None
11773 };
11774
11775 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11776 let url = if let Some(end_pos) = end_position {
11777 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11778 } else {
11779 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11780 };
11781
11782 if let Some(url) = url {
11783 editor.update(&mut cx, |_, cx| {
11784 cx.open_url(&url);
11785 })
11786 } else {
11787 Ok(())
11788 }
11789 });
11790
11791 url_finder.detach();
11792 }
11793
11794 pub fn open_selected_filename(
11795 &mut self,
11796 _: &OpenSelectedFilename,
11797 window: &mut Window,
11798 cx: &mut Context<Self>,
11799 ) {
11800 let Some(workspace) = self.workspace() else {
11801 return;
11802 };
11803
11804 let position = self.selections.newest_anchor().head();
11805
11806 let Some((buffer, buffer_position)) =
11807 self.buffer.read(cx).text_anchor_for_position(position, cx)
11808 else {
11809 return;
11810 };
11811
11812 let project = self.project.clone();
11813
11814 cx.spawn_in(window, |_, mut cx| async move {
11815 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
11816
11817 if let Some((_, path)) = result {
11818 workspace
11819 .update_in(&mut cx, |workspace, window, cx| {
11820 workspace.open_resolved_path(path, window, cx)
11821 })?
11822 .await?;
11823 }
11824 anyhow::Ok(())
11825 })
11826 .detach();
11827 }
11828
11829 pub(crate) fn navigate_to_hover_links(
11830 &mut self,
11831 kind: Option<GotoDefinitionKind>,
11832 mut definitions: Vec<HoverLink>,
11833 split: bool,
11834 window: &mut Window,
11835 cx: &mut Context<Editor>,
11836 ) -> Task<Result<Navigated>> {
11837 // If there is one definition, just open it directly
11838 if definitions.len() == 1 {
11839 let definition = definitions.pop().unwrap();
11840
11841 enum TargetTaskResult {
11842 Location(Option<Location>),
11843 AlreadyNavigated,
11844 }
11845
11846 let target_task = match definition {
11847 HoverLink::Text(link) => {
11848 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11849 }
11850 HoverLink::InlayHint(lsp_location, server_id) => {
11851 let computation =
11852 self.compute_target_location(lsp_location, server_id, window, cx);
11853 cx.background_spawn(async move {
11854 let location = computation.await?;
11855 Ok(TargetTaskResult::Location(location))
11856 })
11857 }
11858 HoverLink::Url(url) => {
11859 cx.open_url(&url);
11860 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11861 }
11862 HoverLink::File(path) => {
11863 if let Some(workspace) = self.workspace() {
11864 cx.spawn_in(window, |_, mut cx| async move {
11865 workspace
11866 .update_in(&mut cx, |workspace, window, cx| {
11867 workspace.open_resolved_path(path, window, cx)
11868 })?
11869 .await
11870 .map(|_| TargetTaskResult::AlreadyNavigated)
11871 })
11872 } else {
11873 Task::ready(Ok(TargetTaskResult::Location(None)))
11874 }
11875 }
11876 };
11877 cx.spawn_in(window, |editor, mut cx| async move {
11878 let target = match target_task.await.context("target resolution task")? {
11879 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11880 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11881 TargetTaskResult::Location(Some(target)) => target,
11882 };
11883
11884 editor.update_in(&mut cx, |editor, window, cx| {
11885 let Some(workspace) = editor.workspace() else {
11886 return Navigated::No;
11887 };
11888 let pane = workspace.read(cx).active_pane().clone();
11889
11890 let range = target.range.to_point(target.buffer.read(cx));
11891 let range = editor.range_for_match(&range);
11892 let range = collapse_multiline_range(range);
11893
11894 if !split
11895 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
11896 {
11897 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11898 } else {
11899 window.defer(cx, move |window, cx| {
11900 let target_editor: Entity<Self> =
11901 workspace.update(cx, |workspace, cx| {
11902 let pane = if split {
11903 workspace.adjacent_pane(window, cx)
11904 } else {
11905 workspace.active_pane().clone()
11906 };
11907
11908 workspace.open_project_item(
11909 pane,
11910 target.buffer.clone(),
11911 true,
11912 true,
11913 window,
11914 cx,
11915 )
11916 });
11917 target_editor.update(cx, |target_editor, cx| {
11918 // When selecting a definition in a different buffer, disable the nav history
11919 // to avoid creating a history entry at the previous cursor location.
11920 pane.update(cx, |pane, _| pane.disable_history());
11921 target_editor.go_to_singleton_buffer_range(range, window, cx);
11922 pane.update(cx, |pane, _| pane.enable_history());
11923 });
11924 });
11925 }
11926 Navigated::Yes
11927 })
11928 })
11929 } else if !definitions.is_empty() {
11930 cx.spawn_in(window, |editor, mut cx| async move {
11931 let (title, location_tasks, workspace) = editor
11932 .update_in(&mut cx, |editor, window, cx| {
11933 let tab_kind = match kind {
11934 Some(GotoDefinitionKind::Implementation) => "Implementations",
11935 _ => "Definitions",
11936 };
11937 let title = definitions
11938 .iter()
11939 .find_map(|definition| match definition {
11940 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11941 let buffer = origin.buffer.read(cx);
11942 format!(
11943 "{} for {}",
11944 tab_kind,
11945 buffer
11946 .text_for_range(origin.range.clone())
11947 .collect::<String>()
11948 )
11949 }),
11950 HoverLink::InlayHint(_, _) => None,
11951 HoverLink::Url(_) => None,
11952 HoverLink::File(_) => None,
11953 })
11954 .unwrap_or(tab_kind.to_string());
11955 let location_tasks = definitions
11956 .into_iter()
11957 .map(|definition| match definition {
11958 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11959 HoverLink::InlayHint(lsp_location, server_id) => editor
11960 .compute_target_location(lsp_location, server_id, window, cx),
11961 HoverLink::Url(_) => Task::ready(Ok(None)),
11962 HoverLink::File(_) => Task::ready(Ok(None)),
11963 })
11964 .collect::<Vec<_>>();
11965 (title, location_tasks, editor.workspace().clone())
11966 })
11967 .context("location tasks preparation")?;
11968
11969 let locations = future::join_all(location_tasks)
11970 .await
11971 .into_iter()
11972 .filter_map(|location| location.transpose())
11973 .collect::<Result<_>>()
11974 .context("location tasks")?;
11975
11976 let Some(workspace) = workspace else {
11977 return Ok(Navigated::No);
11978 };
11979 let opened = workspace
11980 .update_in(&mut cx, |workspace, window, cx| {
11981 Self::open_locations_in_multibuffer(
11982 workspace,
11983 locations,
11984 title,
11985 split,
11986 MultibufferSelectionMode::First,
11987 window,
11988 cx,
11989 )
11990 })
11991 .ok();
11992
11993 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11994 })
11995 } else {
11996 Task::ready(Ok(Navigated::No))
11997 }
11998 }
11999
12000 fn compute_target_location(
12001 &self,
12002 lsp_location: lsp::Location,
12003 server_id: LanguageServerId,
12004 window: &mut Window,
12005 cx: &mut Context<Self>,
12006 ) -> Task<anyhow::Result<Option<Location>>> {
12007 let Some(project) = self.project.clone() else {
12008 return Task::ready(Ok(None));
12009 };
12010
12011 cx.spawn_in(window, move |editor, mut cx| async move {
12012 let location_task = editor.update(&mut cx, |_, cx| {
12013 project.update(cx, |project, cx| {
12014 let language_server_name = project
12015 .language_server_statuses(cx)
12016 .find(|(id, _)| server_id == *id)
12017 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12018 language_server_name.map(|language_server_name| {
12019 project.open_local_buffer_via_lsp(
12020 lsp_location.uri.clone(),
12021 server_id,
12022 language_server_name,
12023 cx,
12024 )
12025 })
12026 })
12027 })?;
12028 let location = match location_task {
12029 Some(task) => Some({
12030 let target_buffer_handle = task.await.context("open local buffer")?;
12031 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12032 let target_start = target_buffer
12033 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12034 let target_end = target_buffer
12035 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12036 target_buffer.anchor_after(target_start)
12037 ..target_buffer.anchor_before(target_end)
12038 })?;
12039 Location {
12040 buffer: target_buffer_handle,
12041 range,
12042 }
12043 }),
12044 None => None,
12045 };
12046 Ok(location)
12047 })
12048 }
12049
12050 pub fn find_all_references(
12051 &mut self,
12052 _: &FindAllReferences,
12053 window: &mut Window,
12054 cx: &mut Context<Self>,
12055 ) -> Option<Task<Result<Navigated>>> {
12056 let selection = self.selections.newest::<usize>(cx);
12057 let multi_buffer = self.buffer.read(cx);
12058 let head = selection.head();
12059
12060 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12061 let head_anchor = multi_buffer_snapshot.anchor_at(
12062 head,
12063 if head < selection.tail() {
12064 Bias::Right
12065 } else {
12066 Bias::Left
12067 },
12068 );
12069
12070 match self
12071 .find_all_references_task_sources
12072 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12073 {
12074 Ok(_) => {
12075 log::info!(
12076 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12077 );
12078 return None;
12079 }
12080 Err(i) => {
12081 self.find_all_references_task_sources.insert(i, head_anchor);
12082 }
12083 }
12084
12085 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12086 let workspace = self.workspace()?;
12087 let project = workspace.read(cx).project().clone();
12088 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12089 Some(cx.spawn_in(window, |editor, mut cx| async move {
12090 let _cleanup = defer({
12091 let mut cx = cx.clone();
12092 move || {
12093 let _ = editor.update(&mut cx, |editor, _| {
12094 if let Ok(i) =
12095 editor
12096 .find_all_references_task_sources
12097 .binary_search_by(|anchor| {
12098 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12099 })
12100 {
12101 editor.find_all_references_task_sources.remove(i);
12102 }
12103 });
12104 }
12105 });
12106
12107 let locations = references.await?;
12108 if locations.is_empty() {
12109 return anyhow::Ok(Navigated::No);
12110 }
12111
12112 workspace.update_in(&mut cx, |workspace, window, cx| {
12113 let title = locations
12114 .first()
12115 .as_ref()
12116 .map(|location| {
12117 let buffer = location.buffer.read(cx);
12118 format!(
12119 "References to `{}`",
12120 buffer
12121 .text_for_range(location.range.clone())
12122 .collect::<String>()
12123 )
12124 })
12125 .unwrap();
12126 Self::open_locations_in_multibuffer(
12127 workspace,
12128 locations,
12129 title,
12130 false,
12131 MultibufferSelectionMode::First,
12132 window,
12133 cx,
12134 );
12135 Navigated::Yes
12136 })
12137 }))
12138 }
12139
12140 /// Opens a multibuffer with the given project locations in it
12141 pub fn open_locations_in_multibuffer(
12142 workspace: &mut Workspace,
12143 mut locations: Vec<Location>,
12144 title: String,
12145 split: bool,
12146 multibuffer_selection_mode: MultibufferSelectionMode,
12147 window: &mut Window,
12148 cx: &mut Context<Workspace>,
12149 ) {
12150 // If there are multiple definitions, open them in a multibuffer
12151 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12152 let mut locations = locations.into_iter().peekable();
12153 let mut ranges = Vec::new();
12154 let capability = workspace.project().read(cx).capability();
12155
12156 let excerpt_buffer = cx.new(|cx| {
12157 let mut multibuffer = MultiBuffer::new(capability);
12158 while let Some(location) = locations.next() {
12159 let buffer = location.buffer.read(cx);
12160 let mut ranges_for_buffer = Vec::new();
12161 let range = location.range.to_offset(buffer);
12162 ranges_for_buffer.push(range.clone());
12163
12164 while let Some(next_location) = locations.peek() {
12165 if next_location.buffer == location.buffer {
12166 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12167 locations.next();
12168 } else {
12169 break;
12170 }
12171 }
12172
12173 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12174 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12175 location.buffer.clone(),
12176 ranges_for_buffer,
12177 DEFAULT_MULTIBUFFER_CONTEXT,
12178 cx,
12179 ))
12180 }
12181
12182 multibuffer.with_title(title)
12183 });
12184
12185 let editor = cx.new(|cx| {
12186 Editor::for_multibuffer(
12187 excerpt_buffer,
12188 Some(workspace.project().clone()),
12189 true,
12190 window,
12191 cx,
12192 )
12193 });
12194 editor.update(cx, |editor, cx| {
12195 match multibuffer_selection_mode {
12196 MultibufferSelectionMode::First => {
12197 if let Some(first_range) = ranges.first() {
12198 editor.change_selections(None, window, cx, |selections| {
12199 selections.clear_disjoint();
12200 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12201 });
12202 }
12203 editor.highlight_background::<Self>(
12204 &ranges,
12205 |theme| theme.editor_highlighted_line_background,
12206 cx,
12207 );
12208 }
12209 MultibufferSelectionMode::All => {
12210 editor.change_selections(None, window, cx, |selections| {
12211 selections.clear_disjoint();
12212 selections.select_anchor_ranges(ranges);
12213 });
12214 }
12215 }
12216 editor.register_buffers_with_language_servers(cx);
12217 });
12218
12219 let item = Box::new(editor);
12220 let item_id = item.item_id();
12221
12222 if split {
12223 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12224 } else {
12225 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12226 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12227 pane.close_current_preview_item(window, cx)
12228 } else {
12229 None
12230 }
12231 });
12232 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12233 }
12234 workspace.active_pane().update(cx, |pane, cx| {
12235 pane.set_preview_item_id(Some(item_id), cx);
12236 });
12237 }
12238
12239 pub fn rename(
12240 &mut self,
12241 _: &Rename,
12242 window: &mut Window,
12243 cx: &mut Context<Self>,
12244 ) -> Option<Task<Result<()>>> {
12245 use language::ToOffset as _;
12246
12247 let provider = self.semantics_provider.clone()?;
12248 let selection = self.selections.newest_anchor().clone();
12249 let (cursor_buffer, cursor_buffer_position) = self
12250 .buffer
12251 .read(cx)
12252 .text_anchor_for_position(selection.head(), cx)?;
12253 let (tail_buffer, cursor_buffer_position_end) = self
12254 .buffer
12255 .read(cx)
12256 .text_anchor_for_position(selection.tail(), cx)?;
12257 if tail_buffer != cursor_buffer {
12258 return None;
12259 }
12260
12261 let snapshot = cursor_buffer.read(cx).snapshot();
12262 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12263 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12264 let prepare_rename = provider
12265 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12266 .unwrap_or_else(|| Task::ready(Ok(None)));
12267 drop(snapshot);
12268
12269 Some(cx.spawn_in(window, |this, mut cx| async move {
12270 let rename_range = if let Some(range) = prepare_rename.await? {
12271 Some(range)
12272 } else {
12273 this.update(&mut cx, |this, cx| {
12274 let buffer = this.buffer.read(cx).snapshot(cx);
12275 let mut buffer_highlights = this
12276 .document_highlights_for_position(selection.head(), &buffer)
12277 .filter(|highlight| {
12278 highlight.start.excerpt_id == selection.head().excerpt_id
12279 && highlight.end.excerpt_id == selection.head().excerpt_id
12280 });
12281 buffer_highlights
12282 .next()
12283 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12284 })?
12285 };
12286 if let Some(rename_range) = rename_range {
12287 this.update_in(&mut cx, |this, window, cx| {
12288 let snapshot = cursor_buffer.read(cx).snapshot();
12289 let rename_buffer_range = rename_range.to_offset(&snapshot);
12290 let cursor_offset_in_rename_range =
12291 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12292 let cursor_offset_in_rename_range_end =
12293 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12294
12295 this.take_rename(false, window, cx);
12296 let buffer = this.buffer.read(cx).read(cx);
12297 let cursor_offset = selection.head().to_offset(&buffer);
12298 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12299 let rename_end = rename_start + rename_buffer_range.len();
12300 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12301 let mut old_highlight_id = None;
12302 let old_name: Arc<str> = buffer
12303 .chunks(rename_start..rename_end, true)
12304 .map(|chunk| {
12305 if old_highlight_id.is_none() {
12306 old_highlight_id = chunk.syntax_highlight_id;
12307 }
12308 chunk.text
12309 })
12310 .collect::<String>()
12311 .into();
12312
12313 drop(buffer);
12314
12315 // Position the selection in the rename editor so that it matches the current selection.
12316 this.show_local_selections = false;
12317 let rename_editor = cx.new(|cx| {
12318 let mut editor = Editor::single_line(window, cx);
12319 editor.buffer.update(cx, |buffer, cx| {
12320 buffer.edit([(0..0, old_name.clone())], None, cx)
12321 });
12322 let rename_selection_range = match cursor_offset_in_rename_range
12323 .cmp(&cursor_offset_in_rename_range_end)
12324 {
12325 Ordering::Equal => {
12326 editor.select_all(&SelectAll, window, cx);
12327 return editor;
12328 }
12329 Ordering::Less => {
12330 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12331 }
12332 Ordering::Greater => {
12333 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12334 }
12335 };
12336 if rename_selection_range.end > old_name.len() {
12337 editor.select_all(&SelectAll, window, cx);
12338 } else {
12339 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12340 s.select_ranges([rename_selection_range]);
12341 });
12342 }
12343 editor
12344 });
12345 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12346 if e == &EditorEvent::Focused {
12347 cx.emit(EditorEvent::FocusedIn)
12348 }
12349 })
12350 .detach();
12351
12352 let write_highlights =
12353 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12354 let read_highlights =
12355 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12356 let ranges = write_highlights
12357 .iter()
12358 .flat_map(|(_, ranges)| ranges.iter())
12359 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12360 .cloned()
12361 .collect();
12362
12363 this.highlight_text::<Rename>(
12364 ranges,
12365 HighlightStyle {
12366 fade_out: Some(0.6),
12367 ..Default::default()
12368 },
12369 cx,
12370 );
12371 let rename_focus_handle = rename_editor.focus_handle(cx);
12372 window.focus(&rename_focus_handle);
12373 let block_id = this.insert_blocks(
12374 [BlockProperties {
12375 style: BlockStyle::Flex,
12376 placement: BlockPlacement::Below(range.start),
12377 height: 1,
12378 render: Arc::new({
12379 let rename_editor = rename_editor.clone();
12380 move |cx: &mut BlockContext| {
12381 let mut text_style = cx.editor_style.text.clone();
12382 if let Some(highlight_style) = old_highlight_id
12383 .and_then(|h| h.style(&cx.editor_style.syntax))
12384 {
12385 text_style = text_style.highlight(highlight_style);
12386 }
12387 div()
12388 .block_mouse_down()
12389 .pl(cx.anchor_x)
12390 .child(EditorElement::new(
12391 &rename_editor,
12392 EditorStyle {
12393 background: cx.theme().system().transparent,
12394 local_player: cx.editor_style.local_player,
12395 text: text_style,
12396 scrollbar_width: cx.editor_style.scrollbar_width,
12397 syntax: cx.editor_style.syntax.clone(),
12398 status: cx.editor_style.status.clone(),
12399 inlay_hints_style: HighlightStyle {
12400 font_weight: Some(FontWeight::BOLD),
12401 ..make_inlay_hints_style(cx.app)
12402 },
12403 inline_completion_styles: make_suggestion_styles(
12404 cx.app,
12405 ),
12406 ..EditorStyle::default()
12407 },
12408 ))
12409 .into_any_element()
12410 }
12411 }),
12412 priority: 0,
12413 }],
12414 Some(Autoscroll::fit()),
12415 cx,
12416 )[0];
12417 this.pending_rename = Some(RenameState {
12418 range,
12419 old_name,
12420 editor: rename_editor,
12421 block_id,
12422 });
12423 })?;
12424 }
12425
12426 Ok(())
12427 }))
12428 }
12429
12430 pub fn confirm_rename(
12431 &mut self,
12432 _: &ConfirmRename,
12433 window: &mut Window,
12434 cx: &mut Context<Self>,
12435 ) -> Option<Task<Result<()>>> {
12436 let rename = self.take_rename(false, window, cx)?;
12437 let workspace = self.workspace()?.downgrade();
12438 let (buffer, start) = self
12439 .buffer
12440 .read(cx)
12441 .text_anchor_for_position(rename.range.start, cx)?;
12442 let (end_buffer, _) = self
12443 .buffer
12444 .read(cx)
12445 .text_anchor_for_position(rename.range.end, cx)?;
12446 if buffer != end_buffer {
12447 return None;
12448 }
12449
12450 let old_name = rename.old_name;
12451 let new_name = rename.editor.read(cx).text(cx);
12452
12453 let rename = self.semantics_provider.as_ref()?.perform_rename(
12454 &buffer,
12455 start,
12456 new_name.clone(),
12457 cx,
12458 )?;
12459
12460 Some(cx.spawn_in(window, |editor, mut cx| async move {
12461 let project_transaction = rename.await?;
12462 Self::open_project_transaction(
12463 &editor,
12464 workspace,
12465 project_transaction,
12466 format!("Rename: {} → {}", old_name, new_name),
12467 cx.clone(),
12468 )
12469 .await?;
12470
12471 editor.update(&mut cx, |editor, cx| {
12472 editor.refresh_document_highlights(cx);
12473 })?;
12474 Ok(())
12475 }))
12476 }
12477
12478 fn take_rename(
12479 &mut self,
12480 moving_cursor: bool,
12481 window: &mut Window,
12482 cx: &mut Context<Self>,
12483 ) -> Option<RenameState> {
12484 let rename = self.pending_rename.take()?;
12485 if rename.editor.focus_handle(cx).is_focused(window) {
12486 window.focus(&self.focus_handle);
12487 }
12488
12489 self.remove_blocks(
12490 [rename.block_id].into_iter().collect(),
12491 Some(Autoscroll::fit()),
12492 cx,
12493 );
12494 self.clear_highlights::<Rename>(cx);
12495 self.show_local_selections = true;
12496
12497 if moving_cursor {
12498 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12499 editor.selections.newest::<usize>(cx).head()
12500 });
12501
12502 // Update the selection to match the position of the selection inside
12503 // the rename editor.
12504 let snapshot = self.buffer.read(cx).read(cx);
12505 let rename_range = rename.range.to_offset(&snapshot);
12506 let cursor_in_editor = snapshot
12507 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12508 .min(rename_range.end);
12509 drop(snapshot);
12510
12511 self.change_selections(None, window, cx, |s| {
12512 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12513 });
12514 } else {
12515 self.refresh_document_highlights(cx);
12516 }
12517
12518 Some(rename)
12519 }
12520
12521 pub fn pending_rename(&self) -> Option<&RenameState> {
12522 self.pending_rename.as_ref()
12523 }
12524
12525 fn format(
12526 &mut self,
12527 _: &Format,
12528 window: &mut Window,
12529 cx: &mut Context<Self>,
12530 ) -> Option<Task<Result<()>>> {
12531 let project = match &self.project {
12532 Some(project) => project.clone(),
12533 None => return None,
12534 };
12535
12536 Some(self.perform_format(
12537 project,
12538 FormatTrigger::Manual,
12539 FormatTarget::Buffers,
12540 window,
12541 cx,
12542 ))
12543 }
12544
12545 fn format_selections(
12546 &mut self,
12547 _: &FormatSelections,
12548 window: &mut Window,
12549 cx: &mut Context<Self>,
12550 ) -> Option<Task<Result<()>>> {
12551 let project = match &self.project {
12552 Some(project) => project.clone(),
12553 None => return None,
12554 };
12555
12556 let ranges = self
12557 .selections
12558 .all_adjusted(cx)
12559 .into_iter()
12560 .map(|selection| selection.range())
12561 .collect_vec();
12562
12563 Some(self.perform_format(
12564 project,
12565 FormatTrigger::Manual,
12566 FormatTarget::Ranges(ranges),
12567 window,
12568 cx,
12569 ))
12570 }
12571
12572 fn perform_format(
12573 &mut self,
12574 project: Entity<Project>,
12575 trigger: FormatTrigger,
12576 target: FormatTarget,
12577 window: &mut Window,
12578 cx: &mut Context<Self>,
12579 ) -> Task<Result<()>> {
12580 let buffer = self.buffer.clone();
12581 let (buffers, target) = match target {
12582 FormatTarget::Buffers => {
12583 let mut buffers = buffer.read(cx).all_buffers();
12584 if trigger == FormatTrigger::Save {
12585 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12586 }
12587 (buffers, LspFormatTarget::Buffers)
12588 }
12589 FormatTarget::Ranges(selection_ranges) => {
12590 let multi_buffer = buffer.read(cx);
12591 let snapshot = multi_buffer.read(cx);
12592 let mut buffers = HashSet::default();
12593 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12594 BTreeMap::new();
12595 for selection_range in selection_ranges {
12596 for (buffer, buffer_range, _) in
12597 snapshot.range_to_buffer_ranges(selection_range)
12598 {
12599 let buffer_id = buffer.remote_id();
12600 let start = buffer.anchor_before(buffer_range.start);
12601 let end = buffer.anchor_after(buffer_range.end);
12602 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12603 buffer_id_to_ranges
12604 .entry(buffer_id)
12605 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12606 .or_insert_with(|| vec![start..end]);
12607 }
12608 }
12609 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12610 }
12611 };
12612
12613 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12614 let format = project.update(cx, |project, cx| {
12615 project.format(buffers, target, true, trigger, cx)
12616 });
12617
12618 cx.spawn_in(window, |_, mut cx| async move {
12619 let transaction = futures::select_biased! {
12620 () = timeout => {
12621 log::warn!("timed out waiting for formatting");
12622 None
12623 }
12624 transaction = format.log_err().fuse() => transaction,
12625 };
12626
12627 buffer
12628 .update(&mut cx, |buffer, cx| {
12629 if let Some(transaction) = transaction {
12630 if !buffer.is_singleton() {
12631 buffer.push_transaction(&transaction.0, cx);
12632 }
12633 }
12634 cx.notify();
12635 })
12636 .ok();
12637
12638 Ok(())
12639 })
12640 }
12641
12642 fn organize_imports(
12643 &mut self,
12644 _: &OrganizeImports,
12645 window: &mut Window,
12646 cx: &mut Context<Self>,
12647 ) -> Option<Task<Result<()>>> {
12648 let project = match &self.project {
12649 Some(project) => project.clone(),
12650 None => return None,
12651 };
12652 Some(self.perform_code_action_kind(
12653 project,
12654 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
12655 window,
12656 cx,
12657 ))
12658 }
12659
12660 fn perform_code_action_kind(
12661 &mut self,
12662 project: Entity<Project>,
12663 kind: CodeActionKind,
12664 window: &mut Window,
12665 cx: &mut Context<Self>,
12666 ) -> Task<Result<()>> {
12667 let buffer = self.buffer.clone();
12668 let buffers = buffer.read(cx).all_buffers();
12669 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
12670 let apply_action = project.update(cx, |project, cx| {
12671 project.apply_code_action_kind(buffers, kind, true, cx)
12672 });
12673 cx.spawn_in(window, |_, mut cx| async move {
12674 let transaction = futures::select_biased! {
12675 () = timeout => {
12676 log::warn!("timed out waiting for executing code action");
12677 None
12678 }
12679 transaction = apply_action.log_err().fuse() => transaction,
12680 };
12681 buffer
12682 .update(&mut cx, |buffer, cx| {
12683 // check if we need this
12684 if let Some(transaction) = transaction {
12685 if !buffer.is_singleton() {
12686 buffer.push_transaction(&transaction.0, cx);
12687 }
12688 }
12689 cx.notify();
12690 })
12691 .ok();
12692 Ok(())
12693 })
12694 }
12695
12696 fn restart_language_server(
12697 &mut self,
12698 _: &RestartLanguageServer,
12699 _: &mut Window,
12700 cx: &mut Context<Self>,
12701 ) {
12702 if let Some(project) = self.project.clone() {
12703 self.buffer.update(cx, |multi_buffer, cx| {
12704 project.update(cx, |project, cx| {
12705 project.restart_language_servers_for_buffers(
12706 multi_buffer.all_buffers().into_iter().collect(),
12707 cx,
12708 );
12709 });
12710 })
12711 }
12712 }
12713
12714 fn cancel_language_server_work(
12715 workspace: &mut Workspace,
12716 _: &actions::CancelLanguageServerWork,
12717 _: &mut Window,
12718 cx: &mut Context<Workspace>,
12719 ) {
12720 let project = workspace.project();
12721 let buffers = workspace
12722 .active_item(cx)
12723 .and_then(|item| item.act_as::<Editor>(cx))
12724 .map_or(HashSet::default(), |editor| {
12725 editor.read(cx).buffer.read(cx).all_buffers()
12726 });
12727 project.update(cx, |project, cx| {
12728 project.cancel_language_server_work_for_buffers(buffers, cx);
12729 });
12730 }
12731
12732 fn show_character_palette(
12733 &mut self,
12734 _: &ShowCharacterPalette,
12735 window: &mut Window,
12736 _: &mut Context<Self>,
12737 ) {
12738 window.show_character_palette();
12739 }
12740
12741 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12742 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12743 let buffer = self.buffer.read(cx).snapshot(cx);
12744 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12745 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12746 let is_valid = buffer
12747 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12748 .any(|entry| {
12749 entry.diagnostic.is_primary
12750 && !entry.range.is_empty()
12751 && entry.range.start == primary_range_start
12752 && entry.diagnostic.message == active_diagnostics.primary_message
12753 });
12754
12755 if is_valid != active_diagnostics.is_valid {
12756 active_diagnostics.is_valid = is_valid;
12757 if is_valid {
12758 let mut new_styles = HashMap::default();
12759 for (block_id, diagnostic) in &active_diagnostics.blocks {
12760 new_styles.insert(
12761 *block_id,
12762 diagnostic_block_renderer(diagnostic.clone(), None, true),
12763 );
12764 }
12765 self.display_map.update(cx, |display_map, _cx| {
12766 display_map.replace_blocks(new_styles);
12767 });
12768 } else {
12769 self.dismiss_diagnostics(cx);
12770 }
12771 }
12772 }
12773 }
12774
12775 fn activate_diagnostics(
12776 &mut self,
12777 buffer_id: BufferId,
12778 group_id: usize,
12779 window: &mut Window,
12780 cx: &mut Context<Self>,
12781 ) {
12782 self.dismiss_diagnostics(cx);
12783 let snapshot = self.snapshot(window, cx);
12784 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
12785 let buffer = self.buffer.read(cx).snapshot(cx);
12786
12787 let mut primary_range = None;
12788 let mut primary_message = None;
12789 let diagnostic_group = buffer
12790 .diagnostic_group(buffer_id, group_id)
12791 .filter_map(|entry| {
12792 let start = entry.range.start;
12793 let end = entry.range.end;
12794 if snapshot.is_line_folded(MultiBufferRow(start.row))
12795 && (start.row == end.row
12796 || snapshot.is_line_folded(MultiBufferRow(end.row)))
12797 {
12798 return None;
12799 }
12800 if entry.diagnostic.is_primary {
12801 primary_range = Some(entry.range.clone());
12802 primary_message = Some(entry.diagnostic.message.clone());
12803 }
12804 Some(entry)
12805 })
12806 .collect::<Vec<_>>();
12807 let primary_range = primary_range?;
12808 let primary_message = primary_message?;
12809
12810 let blocks = display_map
12811 .insert_blocks(
12812 diagnostic_group.iter().map(|entry| {
12813 let diagnostic = entry.diagnostic.clone();
12814 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
12815 BlockProperties {
12816 style: BlockStyle::Fixed,
12817 placement: BlockPlacement::Below(
12818 buffer.anchor_after(entry.range.start),
12819 ),
12820 height: message_height,
12821 render: diagnostic_block_renderer(diagnostic, None, true),
12822 priority: 0,
12823 }
12824 }),
12825 cx,
12826 )
12827 .into_iter()
12828 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
12829 .collect();
12830
12831 Some(ActiveDiagnosticGroup {
12832 primary_range: buffer.anchor_before(primary_range.start)
12833 ..buffer.anchor_after(primary_range.end),
12834 primary_message,
12835 group_id,
12836 blocks,
12837 is_valid: true,
12838 })
12839 });
12840 }
12841
12842 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
12843 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
12844 self.display_map.update(cx, |display_map, cx| {
12845 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
12846 });
12847 cx.notify();
12848 }
12849 }
12850
12851 /// Disable inline diagnostics rendering for this editor.
12852 pub fn disable_inline_diagnostics(&mut self) {
12853 self.inline_diagnostics_enabled = false;
12854 self.inline_diagnostics_update = Task::ready(());
12855 self.inline_diagnostics.clear();
12856 }
12857
12858 pub fn inline_diagnostics_enabled(&self) -> bool {
12859 self.inline_diagnostics_enabled
12860 }
12861
12862 pub fn show_inline_diagnostics(&self) -> bool {
12863 self.show_inline_diagnostics
12864 }
12865
12866 pub fn toggle_inline_diagnostics(
12867 &mut self,
12868 _: &ToggleInlineDiagnostics,
12869 window: &mut Window,
12870 cx: &mut Context<'_, Editor>,
12871 ) {
12872 self.show_inline_diagnostics = !self.show_inline_diagnostics;
12873 self.refresh_inline_diagnostics(false, window, cx);
12874 }
12875
12876 fn refresh_inline_diagnostics(
12877 &mut self,
12878 debounce: bool,
12879 window: &mut Window,
12880 cx: &mut Context<Self>,
12881 ) {
12882 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12883 self.inline_diagnostics_update = Task::ready(());
12884 self.inline_diagnostics.clear();
12885 return;
12886 }
12887
12888 let debounce_ms = ProjectSettings::get_global(cx)
12889 .diagnostics
12890 .inline
12891 .update_debounce_ms;
12892 let debounce = if debounce && debounce_ms > 0 {
12893 Some(Duration::from_millis(debounce_ms))
12894 } else {
12895 None
12896 };
12897 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12898 if let Some(debounce) = debounce {
12899 cx.background_executor().timer(debounce).await;
12900 }
12901 let Some(snapshot) = editor
12902 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12903 .ok()
12904 else {
12905 return;
12906 };
12907
12908 let new_inline_diagnostics = cx
12909 .background_spawn(async move {
12910 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12911 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12912 let message = diagnostic_entry
12913 .diagnostic
12914 .message
12915 .split_once('\n')
12916 .map(|(line, _)| line)
12917 .map(SharedString::new)
12918 .unwrap_or_else(|| {
12919 SharedString::from(diagnostic_entry.diagnostic.message)
12920 });
12921 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12922 let (Ok(i) | Err(i)) = inline_diagnostics
12923 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12924 inline_diagnostics.insert(
12925 i,
12926 (
12927 start_anchor,
12928 InlineDiagnostic {
12929 message,
12930 group_id: diagnostic_entry.diagnostic.group_id,
12931 start: diagnostic_entry.range.start.to_point(&snapshot),
12932 is_primary: diagnostic_entry.diagnostic.is_primary,
12933 severity: diagnostic_entry.diagnostic.severity,
12934 },
12935 ),
12936 );
12937 }
12938 inline_diagnostics
12939 })
12940 .await;
12941
12942 editor
12943 .update(&mut cx, |editor, cx| {
12944 editor.inline_diagnostics = new_inline_diagnostics;
12945 cx.notify();
12946 })
12947 .ok();
12948 });
12949 }
12950
12951 pub fn set_selections_from_remote(
12952 &mut self,
12953 selections: Vec<Selection<Anchor>>,
12954 pending_selection: Option<Selection<Anchor>>,
12955 window: &mut Window,
12956 cx: &mut Context<Self>,
12957 ) {
12958 let old_cursor_position = self.selections.newest_anchor().head();
12959 self.selections.change_with(cx, |s| {
12960 s.select_anchors(selections);
12961 if let Some(pending_selection) = pending_selection {
12962 s.set_pending(pending_selection, SelectMode::Character);
12963 } else {
12964 s.clear_pending();
12965 }
12966 });
12967 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12968 }
12969
12970 fn push_to_selection_history(&mut self) {
12971 self.selection_history.push(SelectionHistoryEntry {
12972 selections: self.selections.disjoint_anchors(),
12973 select_next_state: self.select_next_state.clone(),
12974 select_prev_state: self.select_prev_state.clone(),
12975 add_selections_state: self.add_selections_state.clone(),
12976 });
12977 }
12978
12979 pub fn transact(
12980 &mut self,
12981 window: &mut Window,
12982 cx: &mut Context<Self>,
12983 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12984 ) -> Option<TransactionId> {
12985 self.start_transaction_at(Instant::now(), window, cx);
12986 update(self, window, cx);
12987 self.end_transaction_at(Instant::now(), cx)
12988 }
12989
12990 pub fn start_transaction_at(
12991 &mut self,
12992 now: Instant,
12993 window: &mut Window,
12994 cx: &mut Context<Self>,
12995 ) {
12996 self.end_selection(window, cx);
12997 if let Some(tx_id) = self
12998 .buffer
12999 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13000 {
13001 self.selection_history
13002 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13003 cx.emit(EditorEvent::TransactionBegun {
13004 transaction_id: tx_id,
13005 })
13006 }
13007 }
13008
13009 pub fn end_transaction_at(
13010 &mut self,
13011 now: Instant,
13012 cx: &mut Context<Self>,
13013 ) -> Option<TransactionId> {
13014 if let Some(transaction_id) = self
13015 .buffer
13016 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13017 {
13018 if let Some((_, end_selections)) =
13019 self.selection_history.transaction_mut(transaction_id)
13020 {
13021 *end_selections = Some(self.selections.disjoint_anchors());
13022 } else {
13023 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13024 }
13025
13026 cx.emit(EditorEvent::Edited { transaction_id });
13027 Some(transaction_id)
13028 } else {
13029 None
13030 }
13031 }
13032
13033 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13034 if self.selection_mark_mode {
13035 self.change_selections(None, window, cx, |s| {
13036 s.move_with(|_, sel| {
13037 sel.collapse_to(sel.head(), SelectionGoal::None);
13038 });
13039 })
13040 }
13041 self.selection_mark_mode = true;
13042 cx.notify();
13043 }
13044
13045 pub fn swap_selection_ends(
13046 &mut self,
13047 _: &actions::SwapSelectionEnds,
13048 window: &mut Window,
13049 cx: &mut Context<Self>,
13050 ) {
13051 self.change_selections(None, window, cx, |s| {
13052 s.move_with(|_, sel| {
13053 if sel.start != sel.end {
13054 sel.reversed = !sel.reversed
13055 }
13056 });
13057 });
13058 self.request_autoscroll(Autoscroll::newest(), cx);
13059 cx.notify();
13060 }
13061
13062 pub fn toggle_fold(
13063 &mut self,
13064 _: &actions::ToggleFold,
13065 window: &mut Window,
13066 cx: &mut Context<Self>,
13067 ) {
13068 if self.is_singleton(cx) {
13069 let selection = self.selections.newest::<Point>(cx);
13070
13071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13072 let range = if selection.is_empty() {
13073 let point = selection.head().to_display_point(&display_map);
13074 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13075 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13076 .to_point(&display_map);
13077 start..end
13078 } else {
13079 selection.range()
13080 };
13081 if display_map.folds_in_range(range).next().is_some() {
13082 self.unfold_lines(&Default::default(), window, cx)
13083 } else {
13084 self.fold(&Default::default(), window, cx)
13085 }
13086 } else {
13087 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13088 let buffer_ids: HashSet<_> = self
13089 .selections
13090 .disjoint_anchor_ranges()
13091 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13092 .collect();
13093
13094 let should_unfold = buffer_ids
13095 .iter()
13096 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13097
13098 for buffer_id in buffer_ids {
13099 if should_unfold {
13100 self.unfold_buffer(buffer_id, cx);
13101 } else {
13102 self.fold_buffer(buffer_id, cx);
13103 }
13104 }
13105 }
13106 }
13107
13108 pub fn toggle_fold_recursive(
13109 &mut self,
13110 _: &actions::ToggleFoldRecursive,
13111 window: &mut Window,
13112 cx: &mut Context<Self>,
13113 ) {
13114 let selection = self.selections.newest::<Point>(cx);
13115
13116 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13117 let range = if selection.is_empty() {
13118 let point = selection.head().to_display_point(&display_map);
13119 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13120 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13121 .to_point(&display_map);
13122 start..end
13123 } else {
13124 selection.range()
13125 };
13126 if display_map.folds_in_range(range).next().is_some() {
13127 self.unfold_recursive(&Default::default(), window, cx)
13128 } else {
13129 self.fold_recursive(&Default::default(), window, cx)
13130 }
13131 }
13132
13133 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13134 if self.is_singleton(cx) {
13135 let mut to_fold = Vec::new();
13136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13137 let selections = self.selections.all_adjusted(cx);
13138
13139 for selection in selections {
13140 let range = selection.range().sorted();
13141 let buffer_start_row = range.start.row;
13142
13143 if range.start.row != range.end.row {
13144 let mut found = false;
13145 let mut row = range.start.row;
13146 while row <= range.end.row {
13147 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13148 {
13149 found = true;
13150 row = crease.range().end.row + 1;
13151 to_fold.push(crease);
13152 } else {
13153 row += 1
13154 }
13155 }
13156 if found {
13157 continue;
13158 }
13159 }
13160
13161 for row in (0..=range.start.row).rev() {
13162 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13163 if crease.range().end.row >= buffer_start_row {
13164 to_fold.push(crease);
13165 if row <= range.start.row {
13166 break;
13167 }
13168 }
13169 }
13170 }
13171 }
13172
13173 self.fold_creases(to_fold, true, window, cx);
13174 } else {
13175 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13176 let buffer_ids = self
13177 .selections
13178 .disjoint_anchor_ranges()
13179 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13180 .collect::<HashSet<_>>();
13181 for buffer_id in buffer_ids {
13182 self.fold_buffer(buffer_id, cx);
13183 }
13184 }
13185 }
13186
13187 fn fold_at_level(
13188 &mut self,
13189 fold_at: &FoldAtLevel,
13190 window: &mut Window,
13191 cx: &mut Context<Self>,
13192 ) {
13193 if !self.buffer.read(cx).is_singleton() {
13194 return;
13195 }
13196
13197 let fold_at_level = fold_at.0;
13198 let snapshot = self.buffer.read(cx).snapshot(cx);
13199 let mut to_fold = Vec::new();
13200 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13201
13202 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13203 while start_row < end_row {
13204 match self
13205 .snapshot(window, cx)
13206 .crease_for_buffer_row(MultiBufferRow(start_row))
13207 {
13208 Some(crease) => {
13209 let nested_start_row = crease.range().start.row + 1;
13210 let nested_end_row = crease.range().end.row;
13211
13212 if current_level < fold_at_level {
13213 stack.push((nested_start_row, nested_end_row, current_level + 1));
13214 } else if current_level == fold_at_level {
13215 to_fold.push(crease);
13216 }
13217
13218 start_row = nested_end_row + 1;
13219 }
13220 None => start_row += 1,
13221 }
13222 }
13223 }
13224
13225 self.fold_creases(to_fold, true, window, cx);
13226 }
13227
13228 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13229 if self.buffer.read(cx).is_singleton() {
13230 let mut fold_ranges = Vec::new();
13231 let snapshot = self.buffer.read(cx).snapshot(cx);
13232
13233 for row in 0..snapshot.max_row().0 {
13234 if let Some(foldable_range) = self
13235 .snapshot(window, cx)
13236 .crease_for_buffer_row(MultiBufferRow(row))
13237 {
13238 fold_ranges.push(foldable_range);
13239 }
13240 }
13241
13242 self.fold_creases(fold_ranges, true, window, cx);
13243 } else {
13244 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13245 editor
13246 .update_in(&mut cx, |editor, _, cx| {
13247 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13248 editor.fold_buffer(buffer_id, cx);
13249 }
13250 })
13251 .ok();
13252 });
13253 }
13254 }
13255
13256 pub fn fold_function_bodies(
13257 &mut self,
13258 _: &actions::FoldFunctionBodies,
13259 window: &mut Window,
13260 cx: &mut Context<Self>,
13261 ) {
13262 let snapshot = self.buffer.read(cx).snapshot(cx);
13263
13264 let ranges = snapshot
13265 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13266 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13267 .collect::<Vec<_>>();
13268
13269 let creases = ranges
13270 .into_iter()
13271 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13272 .collect();
13273
13274 self.fold_creases(creases, true, window, cx);
13275 }
13276
13277 pub fn fold_recursive(
13278 &mut self,
13279 _: &actions::FoldRecursive,
13280 window: &mut Window,
13281 cx: &mut Context<Self>,
13282 ) {
13283 let mut to_fold = Vec::new();
13284 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13285 let selections = self.selections.all_adjusted(cx);
13286
13287 for selection in selections {
13288 let range = selection.range().sorted();
13289 let buffer_start_row = range.start.row;
13290
13291 if range.start.row != range.end.row {
13292 let mut found = false;
13293 for row in range.start.row..=range.end.row {
13294 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13295 found = true;
13296 to_fold.push(crease);
13297 }
13298 }
13299 if found {
13300 continue;
13301 }
13302 }
13303
13304 for row in (0..=range.start.row).rev() {
13305 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13306 if crease.range().end.row >= buffer_start_row {
13307 to_fold.push(crease);
13308 } else {
13309 break;
13310 }
13311 }
13312 }
13313 }
13314
13315 self.fold_creases(to_fold, true, window, cx);
13316 }
13317
13318 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13319 let buffer_row = fold_at.buffer_row;
13320 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13321
13322 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13323 let autoscroll = self
13324 .selections
13325 .all::<Point>(cx)
13326 .iter()
13327 .any(|selection| crease.range().overlaps(&selection.range()));
13328
13329 self.fold_creases(vec![crease], autoscroll, window, cx);
13330 }
13331 }
13332
13333 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13334 if self.is_singleton(cx) {
13335 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13336 let buffer = &display_map.buffer_snapshot;
13337 let selections = self.selections.all::<Point>(cx);
13338 let ranges = selections
13339 .iter()
13340 .map(|s| {
13341 let range = s.display_range(&display_map).sorted();
13342 let mut start = range.start.to_point(&display_map);
13343 let mut end = range.end.to_point(&display_map);
13344 start.column = 0;
13345 end.column = buffer.line_len(MultiBufferRow(end.row));
13346 start..end
13347 })
13348 .collect::<Vec<_>>();
13349
13350 self.unfold_ranges(&ranges, true, true, cx);
13351 } else {
13352 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13353 let buffer_ids = self
13354 .selections
13355 .disjoint_anchor_ranges()
13356 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13357 .collect::<HashSet<_>>();
13358 for buffer_id in buffer_ids {
13359 self.unfold_buffer(buffer_id, cx);
13360 }
13361 }
13362 }
13363
13364 pub fn unfold_recursive(
13365 &mut self,
13366 _: &UnfoldRecursive,
13367 _window: &mut Window,
13368 cx: &mut Context<Self>,
13369 ) {
13370 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13371 let selections = self.selections.all::<Point>(cx);
13372 let ranges = selections
13373 .iter()
13374 .map(|s| {
13375 let mut range = s.display_range(&display_map).sorted();
13376 *range.start.column_mut() = 0;
13377 *range.end.column_mut() = display_map.line_len(range.end.row());
13378 let start = range.start.to_point(&display_map);
13379 let end = range.end.to_point(&display_map);
13380 start..end
13381 })
13382 .collect::<Vec<_>>();
13383
13384 self.unfold_ranges(&ranges, true, true, cx);
13385 }
13386
13387 pub fn unfold_at(
13388 &mut self,
13389 unfold_at: &UnfoldAt,
13390 _window: &mut Window,
13391 cx: &mut Context<Self>,
13392 ) {
13393 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13394
13395 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13396 ..Point::new(
13397 unfold_at.buffer_row.0,
13398 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13399 );
13400
13401 let autoscroll = self
13402 .selections
13403 .all::<Point>(cx)
13404 .iter()
13405 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13406
13407 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13408 }
13409
13410 pub fn unfold_all(
13411 &mut self,
13412 _: &actions::UnfoldAll,
13413 _window: &mut Window,
13414 cx: &mut Context<Self>,
13415 ) {
13416 if self.buffer.read(cx).is_singleton() {
13417 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13418 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13419 } else {
13420 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13421 editor
13422 .update(&mut cx, |editor, cx| {
13423 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13424 editor.unfold_buffer(buffer_id, cx);
13425 }
13426 })
13427 .ok();
13428 });
13429 }
13430 }
13431
13432 pub fn fold_selected_ranges(
13433 &mut self,
13434 _: &FoldSelectedRanges,
13435 window: &mut Window,
13436 cx: &mut Context<Self>,
13437 ) {
13438 let selections = self.selections.all::<Point>(cx);
13439 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13440 let line_mode = self.selections.line_mode;
13441 let ranges = selections
13442 .into_iter()
13443 .map(|s| {
13444 if line_mode {
13445 let start = Point::new(s.start.row, 0);
13446 let end = Point::new(
13447 s.end.row,
13448 display_map
13449 .buffer_snapshot
13450 .line_len(MultiBufferRow(s.end.row)),
13451 );
13452 Crease::simple(start..end, display_map.fold_placeholder.clone())
13453 } else {
13454 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13455 }
13456 })
13457 .collect::<Vec<_>>();
13458 self.fold_creases(ranges, true, window, cx);
13459 }
13460
13461 pub fn fold_ranges<T: ToOffset + Clone>(
13462 &mut self,
13463 ranges: Vec<Range<T>>,
13464 auto_scroll: bool,
13465 window: &mut Window,
13466 cx: &mut Context<Self>,
13467 ) {
13468 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13469 let ranges = ranges
13470 .into_iter()
13471 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13472 .collect::<Vec<_>>();
13473 self.fold_creases(ranges, auto_scroll, window, cx);
13474 }
13475
13476 pub fn fold_creases<T: ToOffset + Clone>(
13477 &mut self,
13478 creases: Vec<Crease<T>>,
13479 auto_scroll: bool,
13480 window: &mut Window,
13481 cx: &mut Context<Self>,
13482 ) {
13483 if creases.is_empty() {
13484 return;
13485 }
13486
13487 let mut buffers_affected = HashSet::default();
13488 let multi_buffer = self.buffer().read(cx);
13489 for crease in &creases {
13490 if let Some((_, buffer, _)) =
13491 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13492 {
13493 buffers_affected.insert(buffer.read(cx).remote_id());
13494 };
13495 }
13496
13497 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13498
13499 if auto_scroll {
13500 self.request_autoscroll(Autoscroll::fit(), cx);
13501 }
13502
13503 cx.notify();
13504
13505 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13506 // Clear diagnostics block when folding a range that contains it.
13507 let snapshot = self.snapshot(window, cx);
13508 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13509 drop(snapshot);
13510 self.active_diagnostics = Some(active_diagnostics);
13511 self.dismiss_diagnostics(cx);
13512 } else {
13513 self.active_diagnostics = Some(active_diagnostics);
13514 }
13515 }
13516
13517 self.scrollbar_marker_state.dirty = true;
13518 }
13519
13520 /// Removes any folds whose ranges intersect any of the given ranges.
13521 pub fn unfold_ranges<T: ToOffset + Clone>(
13522 &mut self,
13523 ranges: &[Range<T>],
13524 inclusive: bool,
13525 auto_scroll: bool,
13526 cx: &mut Context<Self>,
13527 ) {
13528 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13529 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13530 });
13531 }
13532
13533 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13534 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13535 return;
13536 }
13537 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13538 self.display_map.update(cx, |display_map, cx| {
13539 display_map.fold_buffers([buffer_id], cx)
13540 });
13541 cx.emit(EditorEvent::BufferFoldToggled {
13542 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13543 folded: true,
13544 });
13545 cx.notify();
13546 }
13547
13548 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13549 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13550 return;
13551 }
13552 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13553 self.display_map.update(cx, |display_map, cx| {
13554 display_map.unfold_buffers([buffer_id], cx);
13555 });
13556 cx.emit(EditorEvent::BufferFoldToggled {
13557 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13558 folded: false,
13559 });
13560 cx.notify();
13561 }
13562
13563 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13564 self.display_map.read(cx).is_buffer_folded(buffer)
13565 }
13566
13567 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13568 self.display_map.read(cx).folded_buffers()
13569 }
13570
13571 /// Removes any folds with the given ranges.
13572 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13573 &mut self,
13574 ranges: &[Range<T>],
13575 type_id: TypeId,
13576 auto_scroll: bool,
13577 cx: &mut Context<Self>,
13578 ) {
13579 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13580 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13581 });
13582 }
13583
13584 fn remove_folds_with<T: ToOffset + Clone>(
13585 &mut self,
13586 ranges: &[Range<T>],
13587 auto_scroll: bool,
13588 cx: &mut Context<Self>,
13589 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13590 ) {
13591 if ranges.is_empty() {
13592 return;
13593 }
13594
13595 let mut buffers_affected = HashSet::default();
13596 let multi_buffer = self.buffer().read(cx);
13597 for range in ranges {
13598 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13599 buffers_affected.insert(buffer.read(cx).remote_id());
13600 };
13601 }
13602
13603 self.display_map.update(cx, update);
13604
13605 if auto_scroll {
13606 self.request_autoscroll(Autoscroll::fit(), cx);
13607 }
13608
13609 cx.notify();
13610 self.scrollbar_marker_state.dirty = true;
13611 self.active_indent_guides_state.dirty = true;
13612 }
13613
13614 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13615 self.display_map.read(cx).fold_placeholder.clone()
13616 }
13617
13618 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13619 self.buffer.update(cx, |buffer, cx| {
13620 buffer.set_all_diff_hunks_expanded(cx);
13621 });
13622 }
13623
13624 pub fn expand_all_diff_hunks(
13625 &mut self,
13626 _: &ExpandAllDiffHunks,
13627 _window: &mut Window,
13628 cx: &mut Context<Self>,
13629 ) {
13630 self.buffer.update(cx, |buffer, cx| {
13631 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13632 });
13633 }
13634
13635 pub fn toggle_selected_diff_hunks(
13636 &mut self,
13637 _: &ToggleSelectedDiffHunks,
13638 _window: &mut Window,
13639 cx: &mut Context<Self>,
13640 ) {
13641 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13642 self.toggle_diff_hunks_in_ranges(ranges, cx);
13643 }
13644
13645 pub fn diff_hunks_in_ranges<'a>(
13646 &'a self,
13647 ranges: &'a [Range<Anchor>],
13648 buffer: &'a MultiBufferSnapshot,
13649 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13650 ranges.iter().flat_map(move |range| {
13651 let end_excerpt_id = range.end.excerpt_id;
13652 let range = range.to_point(buffer);
13653 let mut peek_end = range.end;
13654 if range.end.row < buffer.max_row().0 {
13655 peek_end = Point::new(range.end.row + 1, 0);
13656 }
13657 buffer
13658 .diff_hunks_in_range(range.start..peek_end)
13659 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13660 })
13661 }
13662
13663 pub fn has_stageable_diff_hunks_in_ranges(
13664 &self,
13665 ranges: &[Range<Anchor>],
13666 snapshot: &MultiBufferSnapshot,
13667 ) -> bool {
13668 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13669 hunks.any(|hunk| hunk.status().has_secondary_hunk())
13670 }
13671
13672 pub fn toggle_staged_selected_diff_hunks(
13673 &mut self,
13674 _: &::git::ToggleStaged,
13675 _: &mut Window,
13676 cx: &mut Context<Self>,
13677 ) {
13678 let snapshot = self.buffer.read(cx).snapshot(cx);
13679 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13680 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13681 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13682 }
13683
13684 pub fn stage_and_next(
13685 &mut self,
13686 _: &::git::StageAndNext,
13687 window: &mut Window,
13688 cx: &mut Context<Self>,
13689 ) {
13690 self.do_stage_or_unstage_and_next(true, window, cx);
13691 }
13692
13693 pub fn unstage_and_next(
13694 &mut self,
13695 _: &::git::UnstageAndNext,
13696 window: &mut Window,
13697 cx: &mut Context<Self>,
13698 ) {
13699 self.do_stage_or_unstage_and_next(false, window, cx);
13700 }
13701
13702 pub fn stage_or_unstage_diff_hunks(
13703 &mut self,
13704 stage: bool,
13705 ranges: Vec<Range<Anchor>>,
13706 cx: &mut Context<Self>,
13707 ) {
13708 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
13709 cx.spawn(|this, mut cx| async move {
13710 task.await?;
13711 this.update(&mut cx, |this, cx| {
13712 let snapshot = this.buffer.read(cx).snapshot(cx);
13713 let chunk_by = this
13714 .diff_hunks_in_ranges(&ranges, &snapshot)
13715 .chunk_by(|hunk| hunk.buffer_id);
13716 for (buffer_id, hunks) in &chunk_by {
13717 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
13718 }
13719 })
13720 })
13721 .detach_and_log_err(cx);
13722 }
13723
13724 fn save_buffers_for_ranges_if_needed(
13725 &mut self,
13726 ranges: &[Range<Anchor>],
13727 cx: &mut Context<'_, Editor>,
13728 ) -> Task<Result<()>> {
13729 let multibuffer = self.buffer.read(cx);
13730 let snapshot = multibuffer.read(cx);
13731 let buffer_ids: HashSet<_> = ranges
13732 .iter()
13733 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
13734 .collect();
13735 drop(snapshot);
13736
13737 let mut buffers = HashSet::default();
13738 for buffer_id in buffer_ids {
13739 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
13740 let buffer = buffer_entity.read(cx);
13741 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
13742 {
13743 buffers.insert(buffer_entity);
13744 }
13745 }
13746 }
13747
13748 if let Some(project) = &self.project {
13749 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
13750 } else {
13751 Task::ready(Ok(()))
13752 }
13753 }
13754
13755 fn do_stage_or_unstage_and_next(
13756 &mut self,
13757 stage: bool,
13758 window: &mut Window,
13759 cx: &mut Context<Self>,
13760 ) {
13761 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13762
13763 if ranges.iter().any(|range| range.start != range.end) {
13764 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13765 return;
13766 }
13767
13768 let snapshot = self.snapshot(window, cx);
13769 let newest_range = self.selections.newest::<Point>(cx).range();
13770
13771 let run_twice = snapshot
13772 .hunks_for_ranges([newest_range])
13773 .first()
13774 .is_some_and(|hunk| {
13775 let next_line = Point::new(hunk.row_range.end.0 + 1, 0);
13776 self.hunk_after_position(&snapshot, next_line)
13777 .is_some_and(|other| other.row_range == hunk.row_range)
13778 });
13779
13780 if run_twice {
13781 self.go_to_next_hunk(&GoToHunk, window, cx);
13782 }
13783 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13784 self.go_to_next_hunk(&GoToHunk, window, cx);
13785 }
13786
13787 fn do_stage_or_unstage(
13788 &self,
13789 stage: bool,
13790 buffer_id: BufferId,
13791 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
13792 cx: &mut App,
13793 ) -> Option<()> {
13794 let project = self.project.as_ref()?;
13795 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
13796 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
13797 let buffer_snapshot = buffer.read(cx).snapshot();
13798 let file_exists = buffer_snapshot
13799 .file()
13800 .is_some_and(|file| file.disk_state().exists());
13801 diff.update(cx, |diff, cx| {
13802 diff.stage_or_unstage_hunks(
13803 stage,
13804 &hunks
13805 .map(|hunk| buffer_diff::DiffHunk {
13806 buffer_range: hunk.buffer_range,
13807 diff_base_byte_range: hunk.diff_base_byte_range,
13808 secondary_status: hunk.secondary_status,
13809 range: Point::zero()..Point::zero(), // unused
13810 })
13811 .collect::<Vec<_>>(),
13812 &buffer_snapshot,
13813 file_exists,
13814 cx,
13815 )
13816 });
13817 None
13818 }
13819
13820 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
13821 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13822 self.buffer
13823 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
13824 }
13825
13826 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
13827 self.buffer.update(cx, |buffer, cx| {
13828 let ranges = vec![Anchor::min()..Anchor::max()];
13829 if !buffer.all_diff_hunks_expanded()
13830 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
13831 {
13832 buffer.collapse_diff_hunks(ranges, cx);
13833 true
13834 } else {
13835 false
13836 }
13837 })
13838 }
13839
13840 fn toggle_diff_hunks_in_ranges(
13841 &mut self,
13842 ranges: Vec<Range<Anchor>>,
13843 cx: &mut Context<'_, Editor>,
13844 ) {
13845 self.buffer.update(cx, |buffer, cx| {
13846 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
13847 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
13848 })
13849 }
13850
13851 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
13852 self.buffer.update(cx, |buffer, cx| {
13853 let snapshot = buffer.snapshot(cx);
13854 let excerpt_id = range.end.excerpt_id;
13855 let point_range = range.to_point(&snapshot);
13856 let expand = !buffer.single_hunk_is_expanded(range, cx);
13857 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
13858 })
13859 }
13860
13861 pub(crate) fn apply_all_diff_hunks(
13862 &mut self,
13863 _: &ApplyAllDiffHunks,
13864 window: &mut Window,
13865 cx: &mut Context<Self>,
13866 ) {
13867 let buffers = self.buffer.read(cx).all_buffers();
13868 for branch_buffer in buffers {
13869 branch_buffer.update(cx, |branch_buffer, cx| {
13870 branch_buffer.merge_into_base(Vec::new(), cx);
13871 });
13872 }
13873
13874 if let Some(project) = self.project.clone() {
13875 self.save(true, project, window, cx).detach_and_log_err(cx);
13876 }
13877 }
13878
13879 pub(crate) fn apply_selected_diff_hunks(
13880 &mut self,
13881 _: &ApplyDiffHunk,
13882 window: &mut Window,
13883 cx: &mut Context<Self>,
13884 ) {
13885 let snapshot = self.snapshot(window, cx);
13886 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
13887 let mut ranges_by_buffer = HashMap::default();
13888 self.transact(window, cx, |editor, _window, cx| {
13889 for hunk in hunks {
13890 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13891 ranges_by_buffer
13892 .entry(buffer.clone())
13893 .or_insert_with(Vec::new)
13894 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13895 }
13896 }
13897
13898 for (buffer, ranges) in ranges_by_buffer {
13899 buffer.update(cx, |buffer, cx| {
13900 buffer.merge_into_base(ranges, cx);
13901 });
13902 }
13903 });
13904
13905 if let Some(project) = self.project.clone() {
13906 self.save(true, project, window, cx).detach_and_log_err(cx);
13907 }
13908 }
13909
13910 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13911 if hovered != self.gutter_hovered {
13912 self.gutter_hovered = hovered;
13913 cx.notify();
13914 }
13915 }
13916
13917 pub fn insert_blocks(
13918 &mut self,
13919 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13920 autoscroll: Option<Autoscroll>,
13921 cx: &mut Context<Self>,
13922 ) -> Vec<CustomBlockId> {
13923 let blocks = self
13924 .display_map
13925 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13926 if let Some(autoscroll) = autoscroll {
13927 self.request_autoscroll(autoscroll, cx);
13928 }
13929 cx.notify();
13930 blocks
13931 }
13932
13933 pub fn resize_blocks(
13934 &mut self,
13935 heights: HashMap<CustomBlockId, u32>,
13936 autoscroll: Option<Autoscroll>,
13937 cx: &mut Context<Self>,
13938 ) {
13939 self.display_map
13940 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13941 if let Some(autoscroll) = autoscroll {
13942 self.request_autoscroll(autoscroll, cx);
13943 }
13944 cx.notify();
13945 }
13946
13947 pub fn replace_blocks(
13948 &mut self,
13949 renderers: HashMap<CustomBlockId, RenderBlock>,
13950 autoscroll: Option<Autoscroll>,
13951 cx: &mut Context<Self>,
13952 ) {
13953 self.display_map
13954 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13955 if let Some(autoscroll) = autoscroll {
13956 self.request_autoscroll(autoscroll, cx);
13957 }
13958 cx.notify();
13959 }
13960
13961 pub fn remove_blocks(
13962 &mut self,
13963 block_ids: HashSet<CustomBlockId>,
13964 autoscroll: Option<Autoscroll>,
13965 cx: &mut Context<Self>,
13966 ) {
13967 self.display_map.update(cx, |display_map, cx| {
13968 display_map.remove_blocks(block_ids, cx)
13969 });
13970 if let Some(autoscroll) = autoscroll {
13971 self.request_autoscroll(autoscroll, cx);
13972 }
13973 cx.notify();
13974 }
13975
13976 pub fn row_for_block(
13977 &self,
13978 block_id: CustomBlockId,
13979 cx: &mut Context<Self>,
13980 ) -> Option<DisplayRow> {
13981 self.display_map
13982 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13983 }
13984
13985 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13986 self.focused_block = Some(focused_block);
13987 }
13988
13989 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13990 self.focused_block.take()
13991 }
13992
13993 pub fn insert_creases(
13994 &mut self,
13995 creases: impl IntoIterator<Item = Crease<Anchor>>,
13996 cx: &mut Context<Self>,
13997 ) -> Vec<CreaseId> {
13998 self.display_map
13999 .update(cx, |map, cx| map.insert_creases(creases, cx))
14000 }
14001
14002 pub fn remove_creases(
14003 &mut self,
14004 ids: impl IntoIterator<Item = CreaseId>,
14005 cx: &mut Context<Self>,
14006 ) {
14007 self.display_map
14008 .update(cx, |map, cx| map.remove_creases(ids, cx));
14009 }
14010
14011 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14012 self.display_map
14013 .update(cx, |map, cx| map.snapshot(cx))
14014 .longest_row()
14015 }
14016
14017 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14018 self.display_map
14019 .update(cx, |map, cx| map.snapshot(cx))
14020 .max_point()
14021 }
14022
14023 pub fn text(&self, cx: &App) -> String {
14024 self.buffer.read(cx).read(cx).text()
14025 }
14026
14027 pub fn is_empty(&self, cx: &App) -> bool {
14028 self.buffer.read(cx).read(cx).is_empty()
14029 }
14030
14031 pub fn text_option(&self, cx: &App) -> Option<String> {
14032 let text = self.text(cx);
14033 let text = text.trim();
14034
14035 if text.is_empty() {
14036 return None;
14037 }
14038
14039 Some(text.to_string())
14040 }
14041
14042 pub fn set_text(
14043 &mut self,
14044 text: impl Into<Arc<str>>,
14045 window: &mut Window,
14046 cx: &mut Context<Self>,
14047 ) {
14048 self.transact(window, cx, |this, _, cx| {
14049 this.buffer
14050 .read(cx)
14051 .as_singleton()
14052 .expect("you can only call set_text on editors for singleton buffers")
14053 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14054 });
14055 }
14056
14057 pub fn display_text(&self, cx: &mut App) -> String {
14058 self.display_map
14059 .update(cx, |map, cx| map.snapshot(cx))
14060 .text()
14061 }
14062
14063 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14064 let mut wrap_guides = smallvec::smallvec![];
14065
14066 if self.show_wrap_guides == Some(false) {
14067 return wrap_guides;
14068 }
14069
14070 let settings = self.buffer.read(cx).language_settings(cx);
14071 if settings.show_wrap_guides {
14072 match self.soft_wrap_mode(cx) {
14073 SoftWrap::Column(soft_wrap) => {
14074 wrap_guides.push((soft_wrap as usize, true));
14075 }
14076 SoftWrap::Bounded(soft_wrap) => {
14077 wrap_guides.push((soft_wrap as usize, true));
14078 }
14079 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14080 }
14081 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14082 }
14083
14084 wrap_guides
14085 }
14086
14087 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14088 let settings = self.buffer.read(cx).language_settings(cx);
14089 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14090 match mode {
14091 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14092 SoftWrap::None
14093 }
14094 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14095 language_settings::SoftWrap::PreferredLineLength => {
14096 SoftWrap::Column(settings.preferred_line_length)
14097 }
14098 language_settings::SoftWrap::Bounded => {
14099 SoftWrap::Bounded(settings.preferred_line_length)
14100 }
14101 }
14102 }
14103
14104 pub fn set_soft_wrap_mode(
14105 &mut self,
14106 mode: language_settings::SoftWrap,
14107
14108 cx: &mut Context<Self>,
14109 ) {
14110 self.soft_wrap_mode_override = Some(mode);
14111 cx.notify();
14112 }
14113
14114 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14115 self.text_style_refinement = Some(style);
14116 }
14117
14118 /// called by the Element so we know what style we were most recently rendered with.
14119 pub(crate) fn set_style(
14120 &mut self,
14121 style: EditorStyle,
14122 window: &mut Window,
14123 cx: &mut Context<Self>,
14124 ) {
14125 let rem_size = window.rem_size();
14126 self.display_map.update(cx, |map, cx| {
14127 map.set_font(
14128 style.text.font(),
14129 style.text.font_size.to_pixels(rem_size),
14130 cx,
14131 )
14132 });
14133 self.style = Some(style);
14134 }
14135
14136 pub fn style(&self) -> Option<&EditorStyle> {
14137 self.style.as_ref()
14138 }
14139
14140 // Called by the element. This method is not designed to be called outside of the editor
14141 // element's layout code because it does not notify when rewrapping is computed synchronously.
14142 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14143 self.display_map
14144 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14145 }
14146
14147 pub fn set_soft_wrap(&mut self) {
14148 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14149 }
14150
14151 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14152 if self.soft_wrap_mode_override.is_some() {
14153 self.soft_wrap_mode_override.take();
14154 } else {
14155 let soft_wrap = match self.soft_wrap_mode(cx) {
14156 SoftWrap::GitDiff => return,
14157 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14158 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14159 language_settings::SoftWrap::None
14160 }
14161 };
14162 self.soft_wrap_mode_override = Some(soft_wrap);
14163 }
14164 cx.notify();
14165 }
14166
14167 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14168 let Some(workspace) = self.workspace() else {
14169 return;
14170 };
14171 let fs = workspace.read(cx).app_state().fs.clone();
14172 let current_show = TabBarSettings::get_global(cx).show;
14173 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14174 setting.show = Some(!current_show);
14175 });
14176 }
14177
14178 pub fn toggle_indent_guides(
14179 &mut self,
14180 _: &ToggleIndentGuides,
14181 _: &mut Window,
14182 cx: &mut Context<Self>,
14183 ) {
14184 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14185 self.buffer
14186 .read(cx)
14187 .language_settings(cx)
14188 .indent_guides
14189 .enabled
14190 });
14191 self.show_indent_guides = Some(!currently_enabled);
14192 cx.notify();
14193 }
14194
14195 fn should_show_indent_guides(&self) -> Option<bool> {
14196 self.show_indent_guides
14197 }
14198
14199 pub fn toggle_line_numbers(
14200 &mut self,
14201 _: &ToggleLineNumbers,
14202 _: &mut Window,
14203 cx: &mut Context<Self>,
14204 ) {
14205 let mut editor_settings = EditorSettings::get_global(cx).clone();
14206 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14207 EditorSettings::override_global(editor_settings, cx);
14208 }
14209
14210 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14211 self.use_relative_line_numbers
14212 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14213 }
14214
14215 pub fn toggle_relative_line_numbers(
14216 &mut self,
14217 _: &ToggleRelativeLineNumbers,
14218 _: &mut Window,
14219 cx: &mut Context<Self>,
14220 ) {
14221 let is_relative = self.should_use_relative_line_numbers(cx);
14222 self.set_relative_line_number(Some(!is_relative), cx)
14223 }
14224
14225 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14226 self.use_relative_line_numbers = is_relative;
14227 cx.notify();
14228 }
14229
14230 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14231 self.show_gutter = show_gutter;
14232 cx.notify();
14233 }
14234
14235 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14236 self.show_scrollbars = show_scrollbars;
14237 cx.notify();
14238 }
14239
14240 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14241 self.show_line_numbers = Some(show_line_numbers);
14242 cx.notify();
14243 }
14244
14245 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14246 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14247 cx.notify();
14248 }
14249
14250 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14251 self.show_code_actions = Some(show_code_actions);
14252 cx.notify();
14253 }
14254
14255 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14256 self.show_runnables = Some(show_runnables);
14257 cx.notify();
14258 }
14259
14260 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14261 if self.display_map.read(cx).masked != masked {
14262 self.display_map.update(cx, |map, _| map.masked = masked);
14263 }
14264 cx.notify()
14265 }
14266
14267 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14268 self.show_wrap_guides = Some(show_wrap_guides);
14269 cx.notify();
14270 }
14271
14272 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14273 self.show_indent_guides = Some(show_indent_guides);
14274 cx.notify();
14275 }
14276
14277 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14278 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14279 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14280 if let Some(dir) = file.abs_path(cx).parent() {
14281 return Some(dir.to_owned());
14282 }
14283 }
14284
14285 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14286 return Some(project_path.path.to_path_buf());
14287 }
14288 }
14289
14290 None
14291 }
14292
14293 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14294 self.active_excerpt(cx)?
14295 .1
14296 .read(cx)
14297 .file()
14298 .and_then(|f| f.as_local())
14299 }
14300
14301 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14302 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14303 let buffer = buffer.read(cx);
14304 if let Some(project_path) = buffer.project_path(cx) {
14305 let project = self.project.as_ref()?.read(cx);
14306 project.absolute_path(&project_path, cx)
14307 } else {
14308 buffer
14309 .file()
14310 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14311 }
14312 })
14313 }
14314
14315 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14316 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14317 let project_path = buffer.read(cx).project_path(cx)?;
14318 let project = self.project.as_ref()?.read(cx);
14319 let entry = project.entry_for_path(&project_path, cx)?;
14320 let path = entry.path.to_path_buf();
14321 Some(path)
14322 })
14323 }
14324
14325 pub fn reveal_in_finder(
14326 &mut self,
14327 _: &RevealInFileManager,
14328 _window: &mut Window,
14329 cx: &mut Context<Self>,
14330 ) {
14331 if let Some(target) = self.target_file(cx) {
14332 cx.reveal_path(&target.abs_path(cx));
14333 }
14334 }
14335
14336 pub fn copy_path(
14337 &mut self,
14338 _: &zed_actions::workspace::CopyPath,
14339 _window: &mut Window,
14340 cx: &mut Context<Self>,
14341 ) {
14342 if let Some(path) = self.target_file_abs_path(cx) {
14343 if let Some(path) = path.to_str() {
14344 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14345 }
14346 }
14347 }
14348
14349 pub fn copy_relative_path(
14350 &mut self,
14351 _: &zed_actions::workspace::CopyRelativePath,
14352 _window: &mut Window,
14353 cx: &mut Context<Self>,
14354 ) {
14355 if let Some(path) = self.target_file_path(cx) {
14356 if let Some(path) = path.to_str() {
14357 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14358 }
14359 }
14360 }
14361
14362 pub fn copy_file_name_without_extension(
14363 &mut self,
14364 _: &CopyFileNameWithoutExtension,
14365 _: &mut Window,
14366 cx: &mut Context<Self>,
14367 ) {
14368 if let Some(file) = self.target_file(cx) {
14369 if let Some(file_stem) = file.path().file_stem() {
14370 if let Some(name) = file_stem.to_str() {
14371 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14372 }
14373 }
14374 }
14375 }
14376
14377 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14378 if let Some(file) = self.target_file(cx) {
14379 if let Some(file_name) = file.path().file_name() {
14380 if let Some(name) = file_name.to_str() {
14381 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14382 }
14383 }
14384 }
14385 }
14386
14387 pub fn toggle_git_blame(
14388 &mut self,
14389 _: &ToggleGitBlame,
14390 window: &mut Window,
14391 cx: &mut Context<Self>,
14392 ) {
14393 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14394
14395 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14396 self.start_git_blame(true, window, cx);
14397 }
14398
14399 cx.notify();
14400 }
14401
14402 pub fn toggle_git_blame_inline(
14403 &mut self,
14404 _: &ToggleGitBlameInline,
14405 window: &mut Window,
14406 cx: &mut Context<Self>,
14407 ) {
14408 self.toggle_git_blame_inline_internal(true, window, cx);
14409 cx.notify();
14410 }
14411
14412 pub fn git_blame_inline_enabled(&self) -> bool {
14413 self.git_blame_inline_enabled
14414 }
14415
14416 pub fn toggle_selection_menu(
14417 &mut self,
14418 _: &ToggleSelectionMenu,
14419 _: &mut Window,
14420 cx: &mut Context<Self>,
14421 ) {
14422 self.show_selection_menu = self
14423 .show_selection_menu
14424 .map(|show_selections_menu| !show_selections_menu)
14425 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14426
14427 cx.notify();
14428 }
14429
14430 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14431 self.show_selection_menu
14432 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14433 }
14434
14435 fn start_git_blame(
14436 &mut self,
14437 user_triggered: bool,
14438 window: &mut Window,
14439 cx: &mut Context<Self>,
14440 ) {
14441 if let Some(project) = self.project.as_ref() {
14442 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14443 return;
14444 };
14445
14446 if buffer.read(cx).file().is_none() {
14447 return;
14448 }
14449
14450 let focused = self.focus_handle(cx).contains_focused(window, cx);
14451
14452 let project = project.clone();
14453 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14454 self.blame_subscription =
14455 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14456 self.blame = Some(blame);
14457 }
14458 }
14459
14460 fn toggle_git_blame_inline_internal(
14461 &mut self,
14462 user_triggered: bool,
14463 window: &mut Window,
14464 cx: &mut Context<Self>,
14465 ) {
14466 if self.git_blame_inline_enabled {
14467 self.git_blame_inline_enabled = false;
14468 self.show_git_blame_inline = false;
14469 self.show_git_blame_inline_delay_task.take();
14470 } else {
14471 self.git_blame_inline_enabled = true;
14472 self.start_git_blame_inline(user_triggered, window, cx);
14473 }
14474
14475 cx.notify();
14476 }
14477
14478 fn start_git_blame_inline(
14479 &mut self,
14480 user_triggered: bool,
14481 window: &mut Window,
14482 cx: &mut Context<Self>,
14483 ) {
14484 self.start_git_blame(user_triggered, window, cx);
14485
14486 if ProjectSettings::get_global(cx)
14487 .git
14488 .inline_blame_delay()
14489 .is_some()
14490 {
14491 self.start_inline_blame_timer(window, cx);
14492 } else {
14493 self.show_git_blame_inline = true
14494 }
14495 }
14496
14497 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14498 self.blame.as_ref()
14499 }
14500
14501 pub fn show_git_blame_gutter(&self) -> bool {
14502 self.show_git_blame_gutter
14503 }
14504
14505 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14506 self.show_git_blame_gutter && self.has_blame_entries(cx)
14507 }
14508
14509 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14510 self.show_git_blame_inline
14511 && (self.focus_handle.is_focused(window)
14512 || self
14513 .git_blame_inline_tooltip
14514 .as_ref()
14515 .and_then(|t| t.upgrade())
14516 .is_some())
14517 && !self.newest_selection_head_on_empty_line(cx)
14518 && self.has_blame_entries(cx)
14519 }
14520
14521 fn has_blame_entries(&self, cx: &App) -> bool {
14522 self.blame()
14523 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14524 }
14525
14526 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14527 let cursor_anchor = self.selections.newest_anchor().head();
14528
14529 let snapshot = self.buffer.read(cx).snapshot(cx);
14530 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14531
14532 snapshot.line_len(buffer_row) == 0
14533 }
14534
14535 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14536 let buffer_and_selection = maybe!({
14537 let selection = self.selections.newest::<Point>(cx);
14538 let selection_range = selection.range();
14539
14540 let multi_buffer = self.buffer().read(cx);
14541 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14542 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14543
14544 let (buffer, range, _) = if selection.reversed {
14545 buffer_ranges.first()
14546 } else {
14547 buffer_ranges.last()
14548 }?;
14549
14550 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14551 ..text::ToPoint::to_point(&range.end, &buffer).row;
14552 Some((
14553 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14554 selection,
14555 ))
14556 });
14557
14558 let Some((buffer, selection)) = buffer_and_selection else {
14559 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14560 };
14561
14562 let Some(project) = self.project.as_ref() else {
14563 return Task::ready(Err(anyhow!("editor does not have project")));
14564 };
14565
14566 project.update(cx, |project, cx| {
14567 project.get_permalink_to_line(&buffer, selection, cx)
14568 })
14569 }
14570
14571 pub fn copy_permalink_to_line(
14572 &mut self,
14573 _: &CopyPermalinkToLine,
14574 window: &mut Window,
14575 cx: &mut Context<Self>,
14576 ) {
14577 let permalink_task = self.get_permalink_to_line(cx);
14578 let workspace = self.workspace();
14579
14580 cx.spawn_in(window, |_, mut cx| async move {
14581 match permalink_task.await {
14582 Ok(permalink) => {
14583 cx.update(|_, cx| {
14584 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14585 })
14586 .ok();
14587 }
14588 Err(err) => {
14589 let message = format!("Failed to copy permalink: {err}");
14590
14591 Err::<(), anyhow::Error>(err).log_err();
14592
14593 if let Some(workspace) = workspace {
14594 workspace
14595 .update_in(&mut cx, |workspace, _, cx| {
14596 struct CopyPermalinkToLine;
14597
14598 workspace.show_toast(
14599 Toast::new(
14600 NotificationId::unique::<CopyPermalinkToLine>(),
14601 message,
14602 ),
14603 cx,
14604 )
14605 })
14606 .ok();
14607 }
14608 }
14609 }
14610 })
14611 .detach();
14612 }
14613
14614 pub fn copy_file_location(
14615 &mut self,
14616 _: &CopyFileLocation,
14617 _: &mut Window,
14618 cx: &mut Context<Self>,
14619 ) {
14620 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14621 if let Some(file) = self.target_file(cx) {
14622 if let Some(path) = file.path().to_str() {
14623 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14624 }
14625 }
14626 }
14627
14628 pub fn open_permalink_to_line(
14629 &mut self,
14630 _: &OpenPermalinkToLine,
14631 window: &mut Window,
14632 cx: &mut Context<Self>,
14633 ) {
14634 let permalink_task = self.get_permalink_to_line(cx);
14635 let workspace = self.workspace();
14636
14637 cx.spawn_in(window, |_, mut cx| async move {
14638 match permalink_task.await {
14639 Ok(permalink) => {
14640 cx.update(|_, cx| {
14641 cx.open_url(permalink.as_ref());
14642 })
14643 .ok();
14644 }
14645 Err(err) => {
14646 let message = format!("Failed to open permalink: {err}");
14647
14648 Err::<(), anyhow::Error>(err).log_err();
14649
14650 if let Some(workspace) = workspace {
14651 workspace
14652 .update(&mut cx, |workspace, cx| {
14653 struct OpenPermalinkToLine;
14654
14655 workspace.show_toast(
14656 Toast::new(
14657 NotificationId::unique::<OpenPermalinkToLine>(),
14658 message,
14659 ),
14660 cx,
14661 )
14662 })
14663 .ok();
14664 }
14665 }
14666 }
14667 })
14668 .detach();
14669 }
14670
14671 pub fn insert_uuid_v4(
14672 &mut self,
14673 _: &InsertUuidV4,
14674 window: &mut Window,
14675 cx: &mut Context<Self>,
14676 ) {
14677 self.insert_uuid(UuidVersion::V4, window, cx);
14678 }
14679
14680 pub fn insert_uuid_v7(
14681 &mut self,
14682 _: &InsertUuidV7,
14683 window: &mut Window,
14684 cx: &mut Context<Self>,
14685 ) {
14686 self.insert_uuid(UuidVersion::V7, window, cx);
14687 }
14688
14689 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14690 self.transact(window, cx, |this, window, cx| {
14691 let edits = this
14692 .selections
14693 .all::<Point>(cx)
14694 .into_iter()
14695 .map(|selection| {
14696 let uuid = match version {
14697 UuidVersion::V4 => uuid::Uuid::new_v4(),
14698 UuidVersion::V7 => uuid::Uuid::now_v7(),
14699 };
14700
14701 (selection.range(), uuid.to_string())
14702 });
14703 this.edit(edits, cx);
14704 this.refresh_inline_completion(true, false, window, cx);
14705 });
14706 }
14707
14708 pub fn open_selections_in_multibuffer(
14709 &mut self,
14710 _: &OpenSelectionsInMultibuffer,
14711 window: &mut Window,
14712 cx: &mut Context<Self>,
14713 ) {
14714 let multibuffer = self.buffer.read(cx);
14715
14716 let Some(buffer) = multibuffer.as_singleton() else {
14717 return;
14718 };
14719
14720 let Some(workspace) = self.workspace() else {
14721 return;
14722 };
14723
14724 let locations = self
14725 .selections
14726 .disjoint_anchors()
14727 .iter()
14728 .map(|range| Location {
14729 buffer: buffer.clone(),
14730 range: range.start.text_anchor..range.end.text_anchor,
14731 })
14732 .collect::<Vec<_>>();
14733
14734 let title = multibuffer.title(cx).to_string();
14735
14736 cx.spawn_in(window, |_, mut cx| async move {
14737 workspace.update_in(&mut cx, |workspace, window, cx| {
14738 Self::open_locations_in_multibuffer(
14739 workspace,
14740 locations,
14741 format!("Selections for '{title}'"),
14742 false,
14743 MultibufferSelectionMode::All,
14744 window,
14745 cx,
14746 );
14747 })
14748 })
14749 .detach();
14750 }
14751
14752 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14753 /// last highlight added will be used.
14754 ///
14755 /// If the range ends at the beginning of a line, then that line will not be highlighted.
14756 pub fn highlight_rows<T: 'static>(
14757 &mut self,
14758 range: Range<Anchor>,
14759 color: Hsla,
14760 should_autoscroll: bool,
14761 cx: &mut Context<Self>,
14762 ) {
14763 let snapshot = self.buffer().read(cx).snapshot(cx);
14764 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14765 let ix = row_highlights.binary_search_by(|highlight| {
14766 Ordering::Equal
14767 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
14768 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
14769 });
14770
14771 if let Err(mut ix) = ix {
14772 let index = post_inc(&mut self.highlight_order);
14773
14774 // If this range intersects with the preceding highlight, then merge it with
14775 // the preceding highlight. Otherwise insert a new highlight.
14776 let mut merged = false;
14777 if ix > 0 {
14778 let prev_highlight = &mut row_highlights[ix - 1];
14779 if prev_highlight
14780 .range
14781 .end
14782 .cmp(&range.start, &snapshot)
14783 .is_ge()
14784 {
14785 ix -= 1;
14786 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
14787 prev_highlight.range.end = range.end;
14788 }
14789 merged = true;
14790 prev_highlight.index = index;
14791 prev_highlight.color = color;
14792 prev_highlight.should_autoscroll = should_autoscroll;
14793 }
14794 }
14795
14796 if !merged {
14797 row_highlights.insert(
14798 ix,
14799 RowHighlight {
14800 range: range.clone(),
14801 index,
14802 color,
14803 should_autoscroll,
14804 },
14805 );
14806 }
14807
14808 // If any of the following highlights intersect with this one, merge them.
14809 while let Some(next_highlight) = row_highlights.get(ix + 1) {
14810 let highlight = &row_highlights[ix];
14811 if next_highlight
14812 .range
14813 .start
14814 .cmp(&highlight.range.end, &snapshot)
14815 .is_le()
14816 {
14817 if next_highlight
14818 .range
14819 .end
14820 .cmp(&highlight.range.end, &snapshot)
14821 .is_gt()
14822 {
14823 row_highlights[ix].range.end = next_highlight.range.end;
14824 }
14825 row_highlights.remove(ix + 1);
14826 } else {
14827 break;
14828 }
14829 }
14830 }
14831 }
14832
14833 /// Remove any highlighted row ranges of the given type that intersect the
14834 /// given ranges.
14835 pub fn remove_highlighted_rows<T: 'static>(
14836 &mut self,
14837 ranges_to_remove: Vec<Range<Anchor>>,
14838 cx: &mut Context<Self>,
14839 ) {
14840 let snapshot = self.buffer().read(cx).snapshot(cx);
14841 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14842 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
14843 row_highlights.retain(|highlight| {
14844 while let Some(range_to_remove) = ranges_to_remove.peek() {
14845 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
14846 Ordering::Less | Ordering::Equal => {
14847 ranges_to_remove.next();
14848 }
14849 Ordering::Greater => {
14850 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
14851 Ordering::Less | Ordering::Equal => {
14852 return false;
14853 }
14854 Ordering::Greater => break,
14855 }
14856 }
14857 }
14858 }
14859
14860 true
14861 })
14862 }
14863
14864 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
14865 pub fn clear_row_highlights<T: 'static>(&mut self) {
14866 self.highlighted_rows.remove(&TypeId::of::<T>());
14867 }
14868
14869 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14870 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14871 self.highlighted_rows
14872 .get(&TypeId::of::<T>())
14873 .map_or(&[] as &[_], |vec| vec.as_slice())
14874 .iter()
14875 .map(|highlight| (highlight.range.clone(), highlight.color))
14876 }
14877
14878 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14879 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14880 /// Allows to ignore certain kinds of highlights.
14881 pub fn highlighted_display_rows(
14882 &self,
14883 window: &mut Window,
14884 cx: &mut App,
14885 ) -> BTreeMap<DisplayRow, Background> {
14886 let snapshot = self.snapshot(window, cx);
14887 let mut used_highlight_orders = HashMap::default();
14888 self.highlighted_rows
14889 .iter()
14890 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14891 .fold(
14892 BTreeMap::<DisplayRow, Background>::new(),
14893 |mut unique_rows, highlight| {
14894 let start = highlight.range.start.to_display_point(&snapshot);
14895 let end = highlight.range.end.to_display_point(&snapshot);
14896 let start_row = start.row().0;
14897 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14898 && end.column() == 0
14899 {
14900 end.row().0.saturating_sub(1)
14901 } else {
14902 end.row().0
14903 };
14904 for row in start_row..=end_row {
14905 let used_index =
14906 used_highlight_orders.entry(row).or_insert(highlight.index);
14907 if highlight.index >= *used_index {
14908 *used_index = highlight.index;
14909 unique_rows.insert(DisplayRow(row), highlight.color.into());
14910 }
14911 }
14912 unique_rows
14913 },
14914 )
14915 }
14916
14917 pub fn highlighted_display_row_for_autoscroll(
14918 &self,
14919 snapshot: &DisplaySnapshot,
14920 ) -> Option<DisplayRow> {
14921 self.highlighted_rows
14922 .values()
14923 .flat_map(|highlighted_rows| highlighted_rows.iter())
14924 .filter_map(|highlight| {
14925 if highlight.should_autoscroll {
14926 Some(highlight.range.start.to_display_point(snapshot).row())
14927 } else {
14928 None
14929 }
14930 })
14931 .min()
14932 }
14933
14934 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14935 self.highlight_background::<SearchWithinRange>(
14936 ranges,
14937 |colors| colors.editor_document_highlight_read_background,
14938 cx,
14939 )
14940 }
14941
14942 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14943 self.breadcrumb_header = Some(new_header);
14944 }
14945
14946 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14947 self.clear_background_highlights::<SearchWithinRange>(cx);
14948 }
14949
14950 pub fn highlight_background<T: 'static>(
14951 &mut self,
14952 ranges: &[Range<Anchor>],
14953 color_fetcher: fn(&ThemeColors) -> Hsla,
14954 cx: &mut Context<Self>,
14955 ) {
14956 self.background_highlights
14957 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14958 self.scrollbar_marker_state.dirty = true;
14959 cx.notify();
14960 }
14961
14962 pub fn clear_background_highlights<T: 'static>(
14963 &mut self,
14964 cx: &mut Context<Self>,
14965 ) -> Option<BackgroundHighlight> {
14966 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14967 if !text_highlights.1.is_empty() {
14968 self.scrollbar_marker_state.dirty = true;
14969 cx.notify();
14970 }
14971 Some(text_highlights)
14972 }
14973
14974 pub fn highlight_gutter<T: 'static>(
14975 &mut self,
14976 ranges: &[Range<Anchor>],
14977 color_fetcher: fn(&App) -> Hsla,
14978 cx: &mut Context<Self>,
14979 ) {
14980 self.gutter_highlights
14981 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14982 cx.notify();
14983 }
14984
14985 pub fn clear_gutter_highlights<T: 'static>(
14986 &mut self,
14987 cx: &mut Context<Self>,
14988 ) -> Option<GutterHighlight> {
14989 cx.notify();
14990 self.gutter_highlights.remove(&TypeId::of::<T>())
14991 }
14992
14993 #[cfg(feature = "test-support")]
14994 pub fn all_text_background_highlights(
14995 &self,
14996 window: &mut Window,
14997 cx: &mut Context<Self>,
14998 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14999 let snapshot = self.snapshot(window, cx);
15000 let buffer = &snapshot.buffer_snapshot;
15001 let start = buffer.anchor_before(0);
15002 let end = buffer.anchor_after(buffer.len());
15003 let theme = cx.theme().colors();
15004 self.background_highlights_in_range(start..end, &snapshot, theme)
15005 }
15006
15007 #[cfg(feature = "test-support")]
15008 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15009 let snapshot = self.buffer().read(cx).snapshot(cx);
15010
15011 let highlights = self
15012 .background_highlights
15013 .get(&TypeId::of::<items::BufferSearchHighlights>());
15014
15015 if let Some((_color, ranges)) = highlights {
15016 ranges
15017 .iter()
15018 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15019 .collect_vec()
15020 } else {
15021 vec![]
15022 }
15023 }
15024
15025 fn document_highlights_for_position<'a>(
15026 &'a self,
15027 position: Anchor,
15028 buffer: &'a MultiBufferSnapshot,
15029 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15030 let read_highlights = self
15031 .background_highlights
15032 .get(&TypeId::of::<DocumentHighlightRead>())
15033 .map(|h| &h.1);
15034 let write_highlights = self
15035 .background_highlights
15036 .get(&TypeId::of::<DocumentHighlightWrite>())
15037 .map(|h| &h.1);
15038 let left_position = position.bias_left(buffer);
15039 let right_position = position.bias_right(buffer);
15040 read_highlights
15041 .into_iter()
15042 .chain(write_highlights)
15043 .flat_map(move |ranges| {
15044 let start_ix = match ranges.binary_search_by(|probe| {
15045 let cmp = probe.end.cmp(&left_position, buffer);
15046 if cmp.is_ge() {
15047 Ordering::Greater
15048 } else {
15049 Ordering::Less
15050 }
15051 }) {
15052 Ok(i) | Err(i) => i,
15053 };
15054
15055 ranges[start_ix..]
15056 .iter()
15057 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15058 })
15059 }
15060
15061 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15062 self.background_highlights
15063 .get(&TypeId::of::<T>())
15064 .map_or(false, |(_, highlights)| !highlights.is_empty())
15065 }
15066
15067 pub fn background_highlights_in_range(
15068 &self,
15069 search_range: Range<Anchor>,
15070 display_snapshot: &DisplaySnapshot,
15071 theme: &ThemeColors,
15072 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15073 let mut results = Vec::new();
15074 for (color_fetcher, ranges) in self.background_highlights.values() {
15075 let color = color_fetcher(theme);
15076 let start_ix = match ranges.binary_search_by(|probe| {
15077 let cmp = probe
15078 .end
15079 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15080 if cmp.is_gt() {
15081 Ordering::Greater
15082 } else {
15083 Ordering::Less
15084 }
15085 }) {
15086 Ok(i) | Err(i) => i,
15087 };
15088 for range in &ranges[start_ix..] {
15089 if range
15090 .start
15091 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15092 .is_ge()
15093 {
15094 break;
15095 }
15096
15097 let start = range.start.to_display_point(display_snapshot);
15098 let end = range.end.to_display_point(display_snapshot);
15099 results.push((start..end, color))
15100 }
15101 }
15102 results
15103 }
15104
15105 pub fn background_highlight_row_ranges<T: 'static>(
15106 &self,
15107 search_range: Range<Anchor>,
15108 display_snapshot: &DisplaySnapshot,
15109 count: usize,
15110 ) -> Vec<RangeInclusive<DisplayPoint>> {
15111 let mut results = Vec::new();
15112 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15113 return vec![];
15114 };
15115
15116 let start_ix = match ranges.binary_search_by(|probe| {
15117 let cmp = probe
15118 .end
15119 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15120 if cmp.is_gt() {
15121 Ordering::Greater
15122 } else {
15123 Ordering::Less
15124 }
15125 }) {
15126 Ok(i) | Err(i) => i,
15127 };
15128 let mut push_region = |start: Option<Point>, end: Option<Point>| {
15129 if let (Some(start_display), Some(end_display)) = (start, end) {
15130 results.push(
15131 start_display.to_display_point(display_snapshot)
15132 ..=end_display.to_display_point(display_snapshot),
15133 );
15134 }
15135 };
15136 let mut start_row: Option<Point> = None;
15137 let mut end_row: Option<Point> = None;
15138 if ranges.len() > count {
15139 return Vec::new();
15140 }
15141 for range in &ranges[start_ix..] {
15142 if range
15143 .start
15144 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15145 .is_ge()
15146 {
15147 break;
15148 }
15149 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
15150 if let Some(current_row) = &end_row {
15151 if end.row == current_row.row {
15152 continue;
15153 }
15154 }
15155 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
15156 if start_row.is_none() {
15157 assert_eq!(end_row, None);
15158 start_row = Some(start);
15159 end_row = Some(end);
15160 continue;
15161 }
15162 if let Some(current_end) = end_row.as_mut() {
15163 if start.row > current_end.row + 1 {
15164 push_region(start_row, end_row);
15165 start_row = Some(start);
15166 end_row = Some(end);
15167 } else {
15168 // Merge two hunks.
15169 *current_end = end;
15170 }
15171 } else {
15172 unreachable!();
15173 }
15174 }
15175 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15176 push_region(start_row, end_row);
15177 results
15178 }
15179
15180 pub fn gutter_highlights_in_range(
15181 &self,
15182 search_range: Range<Anchor>,
15183 display_snapshot: &DisplaySnapshot,
15184 cx: &App,
15185 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15186 let mut results = Vec::new();
15187 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15188 let color = color_fetcher(cx);
15189 let start_ix = match ranges.binary_search_by(|probe| {
15190 let cmp = probe
15191 .end
15192 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15193 if cmp.is_gt() {
15194 Ordering::Greater
15195 } else {
15196 Ordering::Less
15197 }
15198 }) {
15199 Ok(i) | Err(i) => i,
15200 };
15201 for range in &ranges[start_ix..] {
15202 if range
15203 .start
15204 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15205 .is_ge()
15206 {
15207 break;
15208 }
15209
15210 let start = range.start.to_display_point(display_snapshot);
15211 let end = range.end.to_display_point(display_snapshot);
15212 results.push((start..end, color))
15213 }
15214 }
15215 results
15216 }
15217
15218 /// Get the text ranges corresponding to the redaction query
15219 pub fn redacted_ranges(
15220 &self,
15221 search_range: Range<Anchor>,
15222 display_snapshot: &DisplaySnapshot,
15223 cx: &App,
15224 ) -> Vec<Range<DisplayPoint>> {
15225 display_snapshot
15226 .buffer_snapshot
15227 .redacted_ranges(search_range, |file| {
15228 if let Some(file) = file {
15229 file.is_private()
15230 && EditorSettings::get(
15231 Some(SettingsLocation {
15232 worktree_id: file.worktree_id(cx),
15233 path: file.path().as_ref(),
15234 }),
15235 cx,
15236 )
15237 .redact_private_values
15238 } else {
15239 false
15240 }
15241 })
15242 .map(|range| {
15243 range.start.to_display_point(display_snapshot)
15244 ..range.end.to_display_point(display_snapshot)
15245 })
15246 .collect()
15247 }
15248
15249 pub fn highlight_text<T: 'static>(
15250 &mut self,
15251 ranges: Vec<Range<Anchor>>,
15252 style: HighlightStyle,
15253 cx: &mut Context<Self>,
15254 ) {
15255 self.display_map.update(cx, |map, _| {
15256 map.highlight_text(TypeId::of::<T>(), ranges, style)
15257 });
15258 cx.notify();
15259 }
15260
15261 pub(crate) fn highlight_inlays<T: 'static>(
15262 &mut self,
15263 highlights: Vec<InlayHighlight>,
15264 style: HighlightStyle,
15265 cx: &mut Context<Self>,
15266 ) {
15267 self.display_map.update(cx, |map, _| {
15268 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15269 });
15270 cx.notify();
15271 }
15272
15273 pub fn text_highlights<'a, T: 'static>(
15274 &'a self,
15275 cx: &'a App,
15276 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15277 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15278 }
15279
15280 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15281 let cleared = self
15282 .display_map
15283 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15284 if cleared {
15285 cx.notify();
15286 }
15287 }
15288
15289 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15290 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15291 && self.focus_handle.is_focused(window)
15292 }
15293
15294 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15295 self.show_cursor_when_unfocused = is_enabled;
15296 cx.notify();
15297 }
15298
15299 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15300 cx.notify();
15301 }
15302
15303 fn on_buffer_event(
15304 &mut self,
15305 multibuffer: &Entity<MultiBuffer>,
15306 event: &multi_buffer::Event,
15307 window: &mut Window,
15308 cx: &mut Context<Self>,
15309 ) {
15310 match event {
15311 multi_buffer::Event::Edited {
15312 singleton_buffer_edited,
15313 edited_buffer: buffer_edited,
15314 } => {
15315 self.scrollbar_marker_state.dirty = true;
15316 self.active_indent_guides_state.dirty = true;
15317 self.refresh_active_diagnostics(cx);
15318 self.refresh_code_actions(window, cx);
15319 if self.has_active_inline_completion() {
15320 self.update_visible_inline_completion(window, cx);
15321 }
15322 if let Some(buffer) = buffer_edited {
15323 let buffer_id = buffer.read(cx).remote_id();
15324 if !self.registered_buffers.contains_key(&buffer_id) {
15325 if let Some(project) = self.project.as_ref() {
15326 project.update(cx, |project, cx| {
15327 self.registered_buffers.insert(
15328 buffer_id,
15329 project.register_buffer_with_language_servers(&buffer, cx),
15330 );
15331 })
15332 }
15333 }
15334 }
15335 cx.emit(EditorEvent::BufferEdited);
15336 cx.emit(SearchEvent::MatchesInvalidated);
15337 if *singleton_buffer_edited {
15338 if let Some(project) = &self.project {
15339 #[allow(clippy::mutable_key_type)]
15340 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15341 multibuffer
15342 .all_buffers()
15343 .into_iter()
15344 .filter_map(|buffer| {
15345 buffer.update(cx, |buffer, cx| {
15346 let language = buffer.language()?;
15347 let should_discard = project.update(cx, |project, cx| {
15348 project.is_local()
15349 && !project.has_language_servers_for(buffer, cx)
15350 });
15351 should_discard.not().then_some(language.clone())
15352 })
15353 })
15354 .collect::<HashSet<_>>()
15355 });
15356 if !languages_affected.is_empty() {
15357 self.refresh_inlay_hints(
15358 InlayHintRefreshReason::BufferEdited(languages_affected),
15359 cx,
15360 );
15361 }
15362 }
15363 }
15364
15365 let Some(project) = &self.project else { return };
15366 let (telemetry, is_via_ssh) = {
15367 let project = project.read(cx);
15368 let telemetry = project.client().telemetry().clone();
15369 let is_via_ssh = project.is_via_ssh();
15370 (telemetry, is_via_ssh)
15371 };
15372 refresh_linked_ranges(self, window, cx);
15373 telemetry.log_edit_event("editor", is_via_ssh);
15374 }
15375 multi_buffer::Event::ExcerptsAdded {
15376 buffer,
15377 predecessor,
15378 excerpts,
15379 } => {
15380 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15381 let buffer_id = buffer.read(cx).remote_id();
15382 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15383 if let Some(project) = &self.project {
15384 get_uncommitted_diff_for_buffer(
15385 project,
15386 [buffer.clone()],
15387 self.buffer.clone(),
15388 cx,
15389 )
15390 .detach();
15391 }
15392 }
15393 cx.emit(EditorEvent::ExcerptsAdded {
15394 buffer: buffer.clone(),
15395 predecessor: *predecessor,
15396 excerpts: excerpts.clone(),
15397 });
15398 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15399 }
15400 multi_buffer::Event::ExcerptsRemoved { ids } => {
15401 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15402 let buffer = self.buffer.read(cx);
15403 self.registered_buffers
15404 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15405 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15406 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15407 }
15408 multi_buffer::Event::ExcerptsEdited {
15409 excerpt_ids,
15410 buffer_ids,
15411 } => {
15412 self.display_map.update(cx, |map, cx| {
15413 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15414 });
15415 cx.emit(EditorEvent::ExcerptsEdited {
15416 ids: excerpt_ids.clone(),
15417 })
15418 }
15419 multi_buffer::Event::ExcerptsExpanded { ids } => {
15420 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15421 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15422 }
15423 multi_buffer::Event::Reparsed(buffer_id) => {
15424 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15425 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15426
15427 cx.emit(EditorEvent::Reparsed(*buffer_id));
15428 }
15429 multi_buffer::Event::DiffHunksToggled => {
15430 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15431 }
15432 multi_buffer::Event::LanguageChanged(buffer_id) => {
15433 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15434 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15435 cx.emit(EditorEvent::Reparsed(*buffer_id));
15436 cx.notify();
15437 }
15438 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15439 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15440 multi_buffer::Event::FileHandleChanged
15441 | multi_buffer::Event::Reloaded
15442 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
15443 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15444 multi_buffer::Event::DiagnosticsUpdated => {
15445 self.refresh_active_diagnostics(cx);
15446 self.refresh_inline_diagnostics(true, window, cx);
15447 self.scrollbar_marker_state.dirty = true;
15448 cx.notify();
15449 }
15450 _ => {}
15451 };
15452 }
15453
15454 fn on_display_map_changed(
15455 &mut self,
15456 _: Entity<DisplayMap>,
15457 _: &mut Window,
15458 cx: &mut Context<Self>,
15459 ) {
15460 cx.notify();
15461 }
15462
15463 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15464 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15465 self.update_edit_prediction_settings(cx);
15466 self.refresh_inline_completion(true, false, window, cx);
15467 self.refresh_inlay_hints(
15468 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15469 self.selections.newest_anchor().head(),
15470 &self.buffer.read(cx).snapshot(cx),
15471 cx,
15472 )),
15473 cx,
15474 );
15475
15476 let old_cursor_shape = self.cursor_shape;
15477
15478 {
15479 let editor_settings = EditorSettings::get_global(cx);
15480 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15481 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15482 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15483 }
15484
15485 if old_cursor_shape != self.cursor_shape {
15486 cx.emit(EditorEvent::CursorShapeChanged);
15487 }
15488
15489 let project_settings = ProjectSettings::get_global(cx);
15490 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15491
15492 if self.mode == EditorMode::Full {
15493 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15494 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15495 if self.show_inline_diagnostics != show_inline_diagnostics {
15496 self.show_inline_diagnostics = show_inline_diagnostics;
15497 self.refresh_inline_diagnostics(false, window, cx);
15498 }
15499
15500 if self.git_blame_inline_enabled != inline_blame_enabled {
15501 self.toggle_git_blame_inline_internal(false, window, cx);
15502 }
15503 }
15504
15505 cx.notify();
15506 }
15507
15508 pub fn set_searchable(&mut self, searchable: bool) {
15509 self.searchable = searchable;
15510 }
15511
15512 pub fn searchable(&self) -> bool {
15513 self.searchable
15514 }
15515
15516 fn open_proposed_changes_editor(
15517 &mut self,
15518 _: &OpenProposedChangesEditor,
15519 window: &mut Window,
15520 cx: &mut Context<Self>,
15521 ) {
15522 let Some(workspace) = self.workspace() else {
15523 cx.propagate();
15524 return;
15525 };
15526
15527 let selections = self.selections.all::<usize>(cx);
15528 let multi_buffer = self.buffer.read(cx);
15529 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15530 let mut new_selections_by_buffer = HashMap::default();
15531 for selection in selections {
15532 for (buffer, range, _) in
15533 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15534 {
15535 let mut range = range.to_point(buffer);
15536 range.start.column = 0;
15537 range.end.column = buffer.line_len(range.end.row);
15538 new_selections_by_buffer
15539 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15540 .or_insert(Vec::new())
15541 .push(range)
15542 }
15543 }
15544
15545 let proposed_changes_buffers = new_selections_by_buffer
15546 .into_iter()
15547 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15548 .collect::<Vec<_>>();
15549 let proposed_changes_editor = cx.new(|cx| {
15550 ProposedChangesEditor::new(
15551 "Proposed changes",
15552 proposed_changes_buffers,
15553 self.project.clone(),
15554 window,
15555 cx,
15556 )
15557 });
15558
15559 window.defer(cx, move |window, cx| {
15560 workspace.update(cx, |workspace, cx| {
15561 workspace.active_pane().update(cx, |pane, cx| {
15562 pane.add_item(
15563 Box::new(proposed_changes_editor),
15564 true,
15565 true,
15566 None,
15567 window,
15568 cx,
15569 );
15570 });
15571 });
15572 });
15573 }
15574
15575 pub fn open_excerpts_in_split(
15576 &mut self,
15577 _: &OpenExcerptsSplit,
15578 window: &mut Window,
15579 cx: &mut Context<Self>,
15580 ) {
15581 self.open_excerpts_common(None, true, window, cx)
15582 }
15583
15584 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15585 self.open_excerpts_common(None, false, window, cx)
15586 }
15587
15588 fn open_excerpts_common(
15589 &mut self,
15590 jump_data: Option<JumpData>,
15591 split: bool,
15592 window: &mut Window,
15593 cx: &mut Context<Self>,
15594 ) {
15595 let Some(workspace) = self.workspace() else {
15596 cx.propagate();
15597 return;
15598 };
15599
15600 if self.buffer.read(cx).is_singleton() {
15601 cx.propagate();
15602 return;
15603 }
15604
15605 let mut new_selections_by_buffer = HashMap::default();
15606 match &jump_data {
15607 Some(JumpData::MultiBufferPoint {
15608 excerpt_id,
15609 position,
15610 anchor,
15611 line_offset_from_top,
15612 }) => {
15613 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15614 if let Some(buffer) = multi_buffer_snapshot
15615 .buffer_id_for_excerpt(*excerpt_id)
15616 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15617 {
15618 let buffer_snapshot = buffer.read(cx).snapshot();
15619 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15620 language::ToPoint::to_point(anchor, &buffer_snapshot)
15621 } else {
15622 buffer_snapshot.clip_point(*position, Bias::Left)
15623 };
15624 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15625 new_selections_by_buffer.insert(
15626 buffer,
15627 (
15628 vec![jump_to_offset..jump_to_offset],
15629 Some(*line_offset_from_top),
15630 ),
15631 );
15632 }
15633 }
15634 Some(JumpData::MultiBufferRow {
15635 row,
15636 line_offset_from_top,
15637 }) => {
15638 let point = MultiBufferPoint::new(row.0, 0);
15639 if let Some((buffer, buffer_point, _)) =
15640 self.buffer.read(cx).point_to_buffer_point(point, cx)
15641 {
15642 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15643 new_selections_by_buffer
15644 .entry(buffer)
15645 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15646 .0
15647 .push(buffer_offset..buffer_offset)
15648 }
15649 }
15650 None => {
15651 let selections = self.selections.all::<usize>(cx);
15652 let multi_buffer = self.buffer.read(cx);
15653 for selection in selections {
15654 for (snapshot, range, _, anchor) in multi_buffer
15655 .snapshot(cx)
15656 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15657 {
15658 if let Some(anchor) = anchor {
15659 // selection is in a deleted hunk
15660 let Some(buffer_id) = anchor.buffer_id else {
15661 continue;
15662 };
15663 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15664 continue;
15665 };
15666 let offset = text::ToOffset::to_offset(
15667 &anchor.text_anchor,
15668 &buffer_handle.read(cx).snapshot(),
15669 );
15670 let range = offset..offset;
15671 new_selections_by_buffer
15672 .entry(buffer_handle)
15673 .or_insert((Vec::new(), None))
15674 .0
15675 .push(range)
15676 } else {
15677 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15678 else {
15679 continue;
15680 };
15681 new_selections_by_buffer
15682 .entry(buffer_handle)
15683 .or_insert((Vec::new(), None))
15684 .0
15685 .push(range)
15686 }
15687 }
15688 }
15689 }
15690 }
15691
15692 if new_selections_by_buffer.is_empty() {
15693 return;
15694 }
15695
15696 // We defer the pane interaction because we ourselves are a workspace item
15697 // and activating a new item causes the pane to call a method on us reentrantly,
15698 // which panics if we're on the stack.
15699 window.defer(cx, move |window, cx| {
15700 workspace.update(cx, |workspace, cx| {
15701 let pane = if split {
15702 workspace.adjacent_pane(window, cx)
15703 } else {
15704 workspace.active_pane().clone()
15705 };
15706
15707 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15708 let editor = buffer
15709 .read(cx)
15710 .file()
15711 .is_none()
15712 .then(|| {
15713 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15714 // so `workspace.open_project_item` will never find them, always opening a new editor.
15715 // Instead, we try to activate the existing editor in the pane first.
15716 let (editor, pane_item_index) =
15717 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15718 let editor = item.downcast::<Editor>()?;
15719 let singleton_buffer =
15720 editor.read(cx).buffer().read(cx).as_singleton()?;
15721 if singleton_buffer == buffer {
15722 Some((editor, i))
15723 } else {
15724 None
15725 }
15726 })?;
15727 pane.update(cx, |pane, cx| {
15728 pane.activate_item(pane_item_index, true, true, window, cx)
15729 });
15730 Some(editor)
15731 })
15732 .flatten()
15733 .unwrap_or_else(|| {
15734 workspace.open_project_item::<Self>(
15735 pane.clone(),
15736 buffer,
15737 true,
15738 true,
15739 window,
15740 cx,
15741 )
15742 });
15743
15744 editor.update(cx, |editor, cx| {
15745 let autoscroll = match scroll_offset {
15746 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15747 None => Autoscroll::newest(),
15748 };
15749 let nav_history = editor.nav_history.take();
15750 editor.change_selections(Some(autoscroll), window, cx, |s| {
15751 s.select_ranges(ranges);
15752 });
15753 editor.nav_history = nav_history;
15754 });
15755 }
15756 })
15757 });
15758 }
15759
15760 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
15761 let snapshot = self.buffer.read(cx).read(cx);
15762 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
15763 Some(
15764 ranges
15765 .iter()
15766 .map(move |range| {
15767 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
15768 })
15769 .collect(),
15770 )
15771 }
15772
15773 fn selection_replacement_ranges(
15774 &self,
15775 range: Range<OffsetUtf16>,
15776 cx: &mut App,
15777 ) -> Vec<Range<OffsetUtf16>> {
15778 let selections = self.selections.all::<OffsetUtf16>(cx);
15779 let newest_selection = selections
15780 .iter()
15781 .max_by_key(|selection| selection.id)
15782 .unwrap();
15783 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
15784 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
15785 let snapshot = self.buffer.read(cx).read(cx);
15786 selections
15787 .into_iter()
15788 .map(|mut selection| {
15789 selection.start.0 =
15790 (selection.start.0 as isize).saturating_add(start_delta) as usize;
15791 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
15792 snapshot.clip_offset_utf16(selection.start, Bias::Left)
15793 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
15794 })
15795 .collect()
15796 }
15797
15798 fn report_editor_event(
15799 &self,
15800 event_type: &'static str,
15801 file_extension: Option<String>,
15802 cx: &App,
15803 ) {
15804 if cfg!(any(test, feature = "test-support")) {
15805 return;
15806 }
15807
15808 let Some(project) = &self.project else { return };
15809
15810 // If None, we are in a file without an extension
15811 let file = self
15812 .buffer
15813 .read(cx)
15814 .as_singleton()
15815 .and_then(|b| b.read(cx).file());
15816 let file_extension = file_extension.or(file
15817 .as_ref()
15818 .and_then(|file| Path::new(file.file_name(cx)).extension())
15819 .and_then(|e| e.to_str())
15820 .map(|a| a.to_string()));
15821
15822 let vim_mode = cx
15823 .global::<SettingsStore>()
15824 .raw_user_settings()
15825 .get("vim_mode")
15826 == Some(&serde_json::Value::Bool(true));
15827
15828 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
15829 let copilot_enabled = edit_predictions_provider
15830 == language::language_settings::EditPredictionProvider::Copilot;
15831 let copilot_enabled_for_language = self
15832 .buffer
15833 .read(cx)
15834 .language_settings(cx)
15835 .show_edit_predictions;
15836
15837 let project = project.read(cx);
15838 telemetry::event!(
15839 event_type,
15840 file_extension,
15841 vim_mode,
15842 copilot_enabled,
15843 copilot_enabled_for_language,
15844 edit_predictions_provider,
15845 is_via_ssh = project.is_via_ssh(),
15846 );
15847 }
15848
15849 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
15850 /// with each line being an array of {text, highlight} objects.
15851 fn copy_highlight_json(
15852 &mut self,
15853 _: &CopyHighlightJson,
15854 window: &mut Window,
15855 cx: &mut Context<Self>,
15856 ) {
15857 #[derive(Serialize)]
15858 struct Chunk<'a> {
15859 text: String,
15860 highlight: Option<&'a str>,
15861 }
15862
15863 let snapshot = self.buffer.read(cx).snapshot(cx);
15864 let range = self
15865 .selected_text_range(false, window, cx)
15866 .and_then(|selection| {
15867 if selection.range.is_empty() {
15868 None
15869 } else {
15870 Some(selection.range)
15871 }
15872 })
15873 .unwrap_or_else(|| 0..snapshot.len());
15874
15875 let chunks = snapshot.chunks(range, true);
15876 let mut lines = Vec::new();
15877 let mut line: VecDeque<Chunk> = VecDeque::new();
15878
15879 let Some(style) = self.style.as_ref() else {
15880 return;
15881 };
15882
15883 for chunk in chunks {
15884 let highlight = chunk
15885 .syntax_highlight_id
15886 .and_then(|id| id.name(&style.syntax));
15887 let mut chunk_lines = chunk.text.split('\n').peekable();
15888 while let Some(text) = chunk_lines.next() {
15889 let mut merged_with_last_token = false;
15890 if let Some(last_token) = line.back_mut() {
15891 if last_token.highlight == highlight {
15892 last_token.text.push_str(text);
15893 merged_with_last_token = true;
15894 }
15895 }
15896
15897 if !merged_with_last_token {
15898 line.push_back(Chunk {
15899 text: text.into(),
15900 highlight,
15901 });
15902 }
15903
15904 if chunk_lines.peek().is_some() {
15905 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15906 line.pop_front();
15907 }
15908 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15909 line.pop_back();
15910 }
15911
15912 lines.push(mem::take(&mut line));
15913 }
15914 }
15915 }
15916
15917 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15918 return;
15919 };
15920 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15921 }
15922
15923 pub fn open_context_menu(
15924 &mut self,
15925 _: &OpenContextMenu,
15926 window: &mut Window,
15927 cx: &mut Context<Self>,
15928 ) {
15929 self.request_autoscroll(Autoscroll::newest(), cx);
15930 let position = self.selections.newest_display(cx).start;
15931 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15932 }
15933
15934 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15935 &self.inlay_hint_cache
15936 }
15937
15938 pub fn replay_insert_event(
15939 &mut self,
15940 text: &str,
15941 relative_utf16_range: Option<Range<isize>>,
15942 window: &mut Window,
15943 cx: &mut Context<Self>,
15944 ) {
15945 if !self.input_enabled {
15946 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15947 return;
15948 }
15949 if let Some(relative_utf16_range) = relative_utf16_range {
15950 let selections = self.selections.all::<OffsetUtf16>(cx);
15951 self.change_selections(None, window, cx, |s| {
15952 let new_ranges = selections.into_iter().map(|range| {
15953 let start = OffsetUtf16(
15954 range
15955 .head()
15956 .0
15957 .saturating_add_signed(relative_utf16_range.start),
15958 );
15959 let end = OffsetUtf16(
15960 range
15961 .head()
15962 .0
15963 .saturating_add_signed(relative_utf16_range.end),
15964 );
15965 start..end
15966 });
15967 s.select_ranges(new_ranges);
15968 });
15969 }
15970
15971 self.handle_input(text, window, cx);
15972 }
15973
15974 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15975 let Some(provider) = self.semantics_provider.as_ref() else {
15976 return false;
15977 };
15978
15979 let mut supports = false;
15980 self.buffer().update(cx, |this, cx| {
15981 this.for_each_buffer(|buffer| {
15982 supports |= provider.supports_inlay_hints(buffer, cx);
15983 });
15984 });
15985
15986 supports
15987 }
15988
15989 pub fn is_focused(&self, window: &Window) -> bool {
15990 self.focus_handle.is_focused(window)
15991 }
15992
15993 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15994 cx.emit(EditorEvent::Focused);
15995
15996 if let Some(descendant) = self
15997 .last_focused_descendant
15998 .take()
15999 .and_then(|descendant| descendant.upgrade())
16000 {
16001 window.focus(&descendant);
16002 } else {
16003 if let Some(blame) = self.blame.as_ref() {
16004 blame.update(cx, GitBlame::focus)
16005 }
16006
16007 self.blink_manager.update(cx, BlinkManager::enable);
16008 self.show_cursor_names(window, cx);
16009 self.buffer.update(cx, |buffer, cx| {
16010 buffer.finalize_last_transaction(cx);
16011 if self.leader_peer_id.is_none() {
16012 buffer.set_active_selections(
16013 &self.selections.disjoint_anchors(),
16014 self.selections.line_mode,
16015 self.cursor_shape,
16016 cx,
16017 );
16018 }
16019 });
16020 }
16021 }
16022
16023 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16024 cx.emit(EditorEvent::FocusedIn)
16025 }
16026
16027 fn handle_focus_out(
16028 &mut self,
16029 event: FocusOutEvent,
16030 _window: &mut Window,
16031 cx: &mut Context<Self>,
16032 ) {
16033 if event.blurred != self.focus_handle {
16034 self.last_focused_descendant = Some(event.blurred);
16035 }
16036 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16037 }
16038
16039 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16040 self.blink_manager.update(cx, BlinkManager::disable);
16041 self.buffer
16042 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16043
16044 if let Some(blame) = self.blame.as_ref() {
16045 blame.update(cx, GitBlame::blur)
16046 }
16047 if !self.hover_state.focused(window, cx) {
16048 hide_hover(self, cx);
16049 }
16050 if !self
16051 .context_menu
16052 .borrow()
16053 .as_ref()
16054 .is_some_and(|context_menu| context_menu.focused(window, cx))
16055 {
16056 self.hide_context_menu(window, cx);
16057 }
16058 self.discard_inline_completion(false, cx);
16059 cx.emit(EditorEvent::Blurred);
16060 cx.notify();
16061 }
16062
16063 pub fn register_action<A: Action>(
16064 &mut self,
16065 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16066 ) -> Subscription {
16067 let id = self.next_editor_action_id.post_inc();
16068 let listener = Arc::new(listener);
16069 self.editor_actions.borrow_mut().insert(
16070 id,
16071 Box::new(move |window, _| {
16072 let listener = listener.clone();
16073 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16074 let action = action.downcast_ref().unwrap();
16075 if phase == DispatchPhase::Bubble {
16076 listener(action, window, cx)
16077 }
16078 })
16079 }),
16080 );
16081
16082 let editor_actions = self.editor_actions.clone();
16083 Subscription::new(move || {
16084 editor_actions.borrow_mut().remove(&id);
16085 })
16086 }
16087
16088 pub fn file_header_size(&self) -> u32 {
16089 FILE_HEADER_HEIGHT
16090 }
16091
16092 pub fn restore(
16093 &mut self,
16094 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16095 window: &mut Window,
16096 cx: &mut Context<Self>,
16097 ) {
16098 let workspace = self.workspace();
16099 let project = self.project.as_ref();
16100 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16101 let mut tasks = Vec::new();
16102 for (buffer_id, changes) in revert_changes {
16103 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16104 buffer.update(cx, |buffer, cx| {
16105 buffer.edit(
16106 changes
16107 .into_iter()
16108 .map(|(range, text)| (range, text.to_string())),
16109 None,
16110 cx,
16111 );
16112 });
16113
16114 if let Some(project) =
16115 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16116 {
16117 project.update(cx, |project, cx| {
16118 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16119 })
16120 }
16121 }
16122 }
16123 tasks
16124 });
16125 cx.spawn_in(window, |_, mut cx| async move {
16126 for (buffer, task) in save_tasks {
16127 let result = task.await;
16128 if result.is_err() {
16129 let Some(path) = buffer
16130 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
16131 .ok()
16132 else {
16133 continue;
16134 };
16135 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
16136 let Some(task) = cx
16137 .update_window_entity(&workspace, |workspace, window, cx| {
16138 workspace
16139 .open_path_preview(path, None, false, false, false, window, cx)
16140 })
16141 .ok()
16142 else {
16143 continue;
16144 };
16145 task.await.log_err();
16146 }
16147 }
16148 }
16149 })
16150 .detach();
16151 self.change_selections(None, window, cx, |selections| selections.refresh());
16152 }
16153
16154 pub fn to_pixel_point(
16155 &self,
16156 source: multi_buffer::Anchor,
16157 editor_snapshot: &EditorSnapshot,
16158 window: &mut Window,
16159 ) -> Option<gpui::Point<Pixels>> {
16160 let source_point = source.to_display_point(editor_snapshot);
16161 self.display_to_pixel_point(source_point, editor_snapshot, window)
16162 }
16163
16164 pub fn display_to_pixel_point(
16165 &self,
16166 source: DisplayPoint,
16167 editor_snapshot: &EditorSnapshot,
16168 window: &mut Window,
16169 ) -> Option<gpui::Point<Pixels>> {
16170 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16171 let text_layout_details = self.text_layout_details(window);
16172 let scroll_top = text_layout_details
16173 .scroll_anchor
16174 .scroll_position(editor_snapshot)
16175 .y;
16176
16177 if source.row().as_f32() < scroll_top.floor() {
16178 return None;
16179 }
16180 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16181 let source_y = line_height * (source.row().as_f32() - scroll_top);
16182 Some(gpui::Point::new(source_x, source_y))
16183 }
16184
16185 pub fn has_visible_completions_menu(&self) -> bool {
16186 !self.edit_prediction_preview_is_active()
16187 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16188 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16189 })
16190 }
16191
16192 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16193 self.addons
16194 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16195 }
16196
16197 pub fn unregister_addon<T: Addon>(&mut self) {
16198 self.addons.remove(&std::any::TypeId::of::<T>());
16199 }
16200
16201 pub fn addon<T: Addon>(&self) -> Option<&T> {
16202 let type_id = std::any::TypeId::of::<T>();
16203 self.addons
16204 .get(&type_id)
16205 .and_then(|item| item.to_any().downcast_ref::<T>())
16206 }
16207
16208 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16209 let text_layout_details = self.text_layout_details(window);
16210 let style = &text_layout_details.editor_style;
16211 let font_id = window.text_system().resolve_font(&style.text.font());
16212 let font_size = style.text.font_size.to_pixels(window.rem_size());
16213 let line_height = style.text.line_height_in_pixels(window.rem_size());
16214 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16215
16216 gpui::Size::new(em_width, line_height)
16217 }
16218
16219 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16220 self.load_diff_task.clone()
16221 }
16222
16223 fn read_selections_from_db(
16224 &mut self,
16225 item_id: u64,
16226 workspace_id: WorkspaceId,
16227 window: &mut Window,
16228 cx: &mut Context<Editor>,
16229 ) {
16230 if !self.is_singleton(cx)
16231 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16232 {
16233 return;
16234 }
16235 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16236 return;
16237 };
16238 if selections.is_empty() {
16239 return;
16240 }
16241
16242 let snapshot = self.buffer.read(cx).snapshot(cx);
16243 self.change_selections(None, window, cx, |s| {
16244 s.select_ranges(selections.into_iter().map(|(start, end)| {
16245 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16246 }));
16247 });
16248 }
16249}
16250
16251fn insert_extra_newline_brackets(
16252 buffer: &MultiBufferSnapshot,
16253 range: Range<usize>,
16254 language: &language::LanguageScope,
16255) -> bool {
16256 let leading_whitespace_len = buffer
16257 .reversed_chars_at(range.start)
16258 .take_while(|c| c.is_whitespace() && *c != '\n')
16259 .map(|c| c.len_utf8())
16260 .sum::<usize>();
16261 let trailing_whitespace_len = buffer
16262 .chars_at(range.end)
16263 .take_while(|c| c.is_whitespace() && *c != '\n')
16264 .map(|c| c.len_utf8())
16265 .sum::<usize>();
16266 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16267
16268 language.brackets().any(|(pair, enabled)| {
16269 let pair_start = pair.start.trim_end();
16270 let pair_end = pair.end.trim_start();
16271
16272 enabled
16273 && pair.newline
16274 && buffer.contains_str_at(range.end, pair_end)
16275 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16276 })
16277}
16278
16279fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16280 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16281 [(buffer, range, _)] => (*buffer, range.clone()),
16282 _ => return false,
16283 };
16284 let pair = {
16285 let mut result: Option<BracketMatch> = None;
16286
16287 for pair in buffer
16288 .all_bracket_ranges(range.clone())
16289 .filter(move |pair| {
16290 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16291 })
16292 {
16293 let len = pair.close_range.end - pair.open_range.start;
16294
16295 if let Some(existing) = &result {
16296 let existing_len = existing.close_range.end - existing.open_range.start;
16297 if len > existing_len {
16298 continue;
16299 }
16300 }
16301
16302 result = Some(pair);
16303 }
16304
16305 result
16306 };
16307 let Some(pair) = pair else {
16308 return false;
16309 };
16310 pair.newline_only
16311 && buffer
16312 .chars_for_range(pair.open_range.end..range.start)
16313 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16314 .all(|c| c.is_whitespace() && c != '\n')
16315}
16316
16317fn get_uncommitted_diff_for_buffer(
16318 project: &Entity<Project>,
16319 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16320 buffer: Entity<MultiBuffer>,
16321 cx: &mut App,
16322) -> Task<()> {
16323 let mut tasks = Vec::new();
16324 project.update(cx, |project, cx| {
16325 for buffer in buffers {
16326 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16327 }
16328 });
16329 cx.spawn(|mut cx| async move {
16330 let diffs = future::join_all(tasks).await;
16331 buffer
16332 .update(&mut cx, |buffer, cx| {
16333 for diff in diffs.into_iter().flatten() {
16334 buffer.add_diff(diff, cx);
16335 }
16336 })
16337 .ok();
16338 })
16339}
16340
16341fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16342 let tab_size = tab_size.get() as usize;
16343 let mut width = offset;
16344
16345 for ch in text.chars() {
16346 width += if ch == '\t' {
16347 tab_size - (width % tab_size)
16348 } else {
16349 1
16350 };
16351 }
16352
16353 width - offset
16354}
16355
16356#[cfg(test)]
16357mod tests {
16358 use super::*;
16359
16360 #[test]
16361 fn test_string_size_with_expanded_tabs() {
16362 let nz = |val| NonZeroU32::new(val).unwrap();
16363 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16364 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16365 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16366 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16367 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16368 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16369 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16370 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16371 }
16372}
16373
16374/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16375struct WordBreakingTokenizer<'a> {
16376 input: &'a str,
16377}
16378
16379impl<'a> WordBreakingTokenizer<'a> {
16380 fn new(input: &'a str) -> Self {
16381 Self { input }
16382 }
16383}
16384
16385fn is_char_ideographic(ch: char) -> bool {
16386 use unicode_script::Script::*;
16387 use unicode_script::UnicodeScript;
16388 matches!(ch.script(), Han | Tangut | Yi)
16389}
16390
16391fn is_grapheme_ideographic(text: &str) -> bool {
16392 text.chars().any(is_char_ideographic)
16393}
16394
16395fn is_grapheme_whitespace(text: &str) -> bool {
16396 text.chars().any(|x| x.is_whitespace())
16397}
16398
16399fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16400 text.chars().next().map_or(false, |ch| {
16401 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16402 })
16403}
16404
16405#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16406struct WordBreakToken<'a> {
16407 token: &'a str,
16408 grapheme_len: usize,
16409 is_whitespace: bool,
16410}
16411
16412impl<'a> Iterator for WordBreakingTokenizer<'a> {
16413 /// Yields a span, the count of graphemes in the token, and whether it was
16414 /// whitespace. Note that it also breaks at word boundaries.
16415 type Item = WordBreakToken<'a>;
16416
16417 fn next(&mut self) -> Option<Self::Item> {
16418 use unicode_segmentation::UnicodeSegmentation;
16419 if self.input.is_empty() {
16420 return None;
16421 }
16422
16423 let mut iter = self.input.graphemes(true).peekable();
16424 let mut offset = 0;
16425 let mut graphemes = 0;
16426 if let Some(first_grapheme) = iter.next() {
16427 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16428 offset += first_grapheme.len();
16429 graphemes += 1;
16430 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16431 if let Some(grapheme) = iter.peek().copied() {
16432 if should_stay_with_preceding_ideograph(grapheme) {
16433 offset += grapheme.len();
16434 graphemes += 1;
16435 }
16436 }
16437 } else {
16438 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16439 let mut next_word_bound = words.peek().copied();
16440 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16441 next_word_bound = words.next();
16442 }
16443 while let Some(grapheme) = iter.peek().copied() {
16444 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16445 break;
16446 };
16447 if is_grapheme_whitespace(grapheme) != is_whitespace {
16448 break;
16449 };
16450 offset += grapheme.len();
16451 graphemes += 1;
16452 iter.next();
16453 }
16454 }
16455 let token = &self.input[..offset];
16456 self.input = &self.input[offset..];
16457 if is_whitespace {
16458 Some(WordBreakToken {
16459 token: " ",
16460 grapheme_len: 1,
16461 is_whitespace: true,
16462 })
16463 } else {
16464 Some(WordBreakToken {
16465 token,
16466 grapheme_len: graphemes,
16467 is_whitespace: false,
16468 })
16469 }
16470 } else {
16471 None
16472 }
16473 }
16474}
16475
16476#[test]
16477fn test_word_breaking_tokenizer() {
16478 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16479 ("", &[]),
16480 (" ", &[(" ", 1, true)]),
16481 ("Ʒ", &[("Ʒ", 1, false)]),
16482 ("Ǽ", &[("Ǽ", 1, false)]),
16483 ("⋑", &[("⋑", 1, false)]),
16484 ("⋑⋑", &[("⋑⋑", 2, false)]),
16485 (
16486 "原理,进而",
16487 &[
16488 ("原", 1, false),
16489 ("理,", 2, false),
16490 ("进", 1, false),
16491 ("而", 1, false),
16492 ],
16493 ),
16494 (
16495 "hello world",
16496 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16497 ),
16498 (
16499 "hello, world",
16500 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16501 ),
16502 (
16503 " hello world",
16504 &[
16505 (" ", 1, true),
16506 ("hello", 5, false),
16507 (" ", 1, true),
16508 ("world", 5, false),
16509 ],
16510 ),
16511 (
16512 "这是什么 \n 钢笔",
16513 &[
16514 ("这", 1, false),
16515 ("是", 1, false),
16516 ("什", 1, false),
16517 ("么", 1, false),
16518 (" ", 1, true),
16519 ("钢", 1, false),
16520 ("笔", 1, false),
16521 ],
16522 ),
16523 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16524 ];
16525
16526 for (input, result) in tests {
16527 assert_eq!(
16528 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16529 result
16530 .iter()
16531 .copied()
16532 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16533 token,
16534 grapheme_len,
16535 is_whitespace,
16536 })
16537 .collect::<Vec<_>>()
16538 );
16539 }
16540}
16541
16542fn wrap_with_prefix(
16543 line_prefix: String,
16544 unwrapped_text: String,
16545 wrap_column: usize,
16546 tab_size: NonZeroU32,
16547) -> String {
16548 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16549 let mut wrapped_text = String::new();
16550 let mut current_line = line_prefix.clone();
16551
16552 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16553 let mut current_line_len = line_prefix_len;
16554 for WordBreakToken {
16555 token,
16556 grapheme_len,
16557 is_whitespace,
16558 } in tokenizer
16559 {
16560 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16561 wrapped_text.push_str(current_line.trim_end());
16562 wrapped_text.push('\n');
16563 current_line.truncate(line_prefix.len());
16564 current_line_len = line_prefix_len;
16565 if !is_whitespace {
16566 current_line.push_str(token);
16567 current_line_len += grapheme_len;
16568 }
16569 } else if !is_whitespace {
16570 current_line.push_str(token);
16571 current_line_len += grapheme_len;
16572 } else if current_line_len != line_prefix_len {
16573 current_line.push(' ');
16574 current_line_len += 1;
16575 }
16576 }
16577
16578 if !current_line.is_empty() {
16579 wrapped_text.push_str(¤t_line);
16580 }
16581 wrapped_text
16582}
16583
16584#[test]
16585fn test_wrap_with_prefix() {
16586 assert_eq!(
16587 wrap_with_prefix(
16588 "# ".to_string(),
16589 "abcdefg".to_string(),
16590 4,
16591 NonZeroU32::new(4).unwrap()
16592 ),
16593 "# abcdefg"
16594 );
16595 assert_eq!(
16596 wrap_with_prefix(
16597 "".to_string(),
16598 "\thello world".to_string(),
16599 8,
16600 NonZeroU32::new(4).unwrap()
16601 ),
16602 "hello\nworld"
16603 );
16604 assert_eq!(
16605 wrap_with_prefix(
16606 "// ".to_string(),
16607 "xx \nyy zz aa bb cc".to_string(),
16608 12,
16609 NonZeroU32::new(4).unwrap()
16610 ),
16611 "// xx yy zz\n// aa bb cc"
16612 );
16613 assert_eq!(
16614 wrap_with_prefix(
16615 String::new(),
16616 "这是什么 \n 钢笔".to_string(),
16617 3,
16618 NonZeroU32::new(4).unwrap()
16619 ),
16620 "这是什\n么 钢\n笔"
16621 );
16622}
16623
16624pub trait CollaborationHub {
16625 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16626 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16627 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16628}
16629
16630impl CollaborationHub for Entity<Project> {
16631 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16632 self.read(cx).collaborators()
16633 }
16634
16635 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16636 self.read(cx).user_store().read(cx).participant_indices()
16637 }
16638
16639 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16640 let this = self.read(cx);
16641 let user_ids = this.collaborators().values().map(|c| c.user_id);
16642 this.user_store().read_with(cx, |user_store, cx| {
16643 user_store.participant_names(user_ids, cx)
16644 })
16645 }
16646}
16647
16648pub trait SemanticsProvider {
16649 fn hover(
16650 &self,
16651 buffer: &Entity<Buffer>,
16652 position: text::Anchor,
16653 cx: &mut App,
16654 ) -> Option<Task<Vec<project::Hover>>>;
16655
16656 fn inlay_hints(
16657 &self,
16658 buffer_handle: Entity<Buffer>,
16659 range: Range<text::Anchor>,
16660 cx: &mut App,
16661 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16662
16663 fn resolve_inlay_hint(
16664 &self,
16665 hint: InlayHint,
16666 buffer_handle: Entity<Buffer>,
16667 server_id: LanguageServerId,
16668 cx: &mut App,
16669 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16670
16671 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16672
16673 fn document_highlights(
16674 &self,
16675 buffer: &Entity<Buffer>,
16676 position: text::Anchor,
16677 cx: &mut App,
16678 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16679
16680 fn definitions(
16681 &self,
16682 buffer: &Entity<Buffer>,
16683 position: text::Anchor,
16684 kind: GotoDefinitionKind,
16685 cx: &mut App,
16686 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16687
16688 fn range_for_rename(
16689 &self,
16690 buffer: &Entity<Buffer>,
16691 position: text::Anchor,
16692 cx: &mut App,
16693 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16694
16695 fn perform_rename(
16696 &self,
16697 buffer: &Entity<Buffer>,
16698 position: text::Anchor,
16699 new_name: String,
16700 cx: &mut App,
16701 ) -> Option<Task<Result<ProjectTransaction>>>;
16702}
16703
16704pub trait CompletionProvider {
16705 fn completions(
16706 &self,
16707 buffer: &Entity<Buffer>,
16708 buffer_position: text::Anchor,
16709 trigger: CompletionContext,
16710 window: &mut Window,
16711 cx: &mut Context<Editor>,
16712 ) -> Task<Result<Vec<Completion>>>;
16713
16714 fn resolve_completions(
16715 &self,
16716 buffer: Entity<Buffer>,
16717 completion_indices: Vec<usize>,
16718 completions: Rc<RefCell<Box<[Completion]>>>,
16719 cx: &mut Context<Editor>,
16720 ) -> Task<Result<bool>>;
16721
16722 fn apply_additional_edits_for_completion(
16723 &self,
16724 _buffer: Entity<Buffer>,
16725 _completions: Rc<RefCell<Box<[Completion]>>>,
16726 _completion_index: usize,
16727 _push_to_history: bool,
16728 _cx: &mut Context<Editor>,
16729 ) -> Task<Result<Option<language::Transaction>>> {
16730 Task::ready(Ok(None))
16731 }
16732
16733 fn is_completion_trigger(
16734 &self,
16735 buffer: &Entity<Buffer>,
16736 position: language::Anchor,
16737 text: &str,
16738 trigger_in_words: bool,
16739 cx: &mut Context<Editor>,
16740 ) -> bool;
16741
16742 fn sort_completions(&self) -> bool {
16743 true
16744 }
16745}
16746
16747pub trait CodeActionProvider {
16748 fn id(&self) -> Arc<str>;
16749
16750 fn code_actions(
16751 &self,
16752 buffer: &Entity<Buffer>,
16753 range: Range<text::Anchor>,
16754 window: &mut Window,
16755 cx: &mut App,
16756 ) -> Task<Result<Vec<CodeAction>>>;
16757
16758 fn apply_code_action(
16759 &self,
16760 buffer_handle: Entity<Buffer>,
16761 action: CodeAction,
16762 excerpt_id: ExcerptId,
16763 push_to_history: bool,
16764 window: &mut Window,
16765 cx: &mut App,
16766 ) -> Task<Result<ProjectTransaction>>;
16767}
16768
16769impl CodeActionProvider for Entity<Project> {
16770 fn id(&self) -> Arc<str> {
16771 "project".into()
16772 }
16773
16774 fn code_actions(
16775 &self,
16776 buffer: &Entity<Buffer>,
16777 range: Range<text::Anchor>,
16778 _window: &mut Window,
16779 cx: &mut App,
16780 ) -> Task<Result<Vec<CodeAction>>> {
16781 self.update(cx, |project, cx| {
16782 project.code_actions(buffer, range, None, cx)
16783 })
16784 }
16785
16786 fn apply_code_action(
16787 &self,
16788 buffer_handle: Entity<Buffer>,
16789 action: CodeAction,
16790 _excerpt_id: ExcerptId,
16791 push_to_history: bool,
16792 _window: &mut Window,
16793 cx: &mut App,
16794 ) -> Task<Result<ProjectTransaction>> {
16795 self.update(cx, |project, cx| {
16796 project.apply_code_action(buffer_handle, action, push_to_history, cx)
16797 })
16798 }
16799}
16800
16801fn snippet_completions(
16802 project: &Project,
16803 buffer: &Entity<Buffer>,
16804 buffer_position: text::Anchor,
16805 cx: &mut App,
16806) -> Task<Result<Vec<Completion>>> {
16807 let language = buffer.read(cx).language_at(buffer_position);
16808 let language_name = language.as_ref().map(|language| language.lsp_id());
16809 let snippet_store = project.snippets().read(cx);
16810 let snippets = snippet_store.snippets_for(language_name, cx);
16811
16812 if snippets.is_empty() {
16813 return Task::ready(Ok(vec![]));
16814 }
16815 let snapshot = buffer.read(cx).text_snapshot();
16816 let chars: String = snapshot
16817 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
16818 .collect();
16819
16820 let scope = language.map(|language| language.default_scope());
16821 let executor = cx.background_executor().clone();
16822
16823 cx.background_spawn(async move {
16824 let classifier = CharClassifier::new(scope).for_completion(true);
16825 let mut last_word = chars
16826 .chars()
16827 .take_while(|c| classifier.is_word(*c))
16828 .collect::<String>();
16829 last_word = last_word.chars().rev().collect();
16830
16831 if last_word.is_empty() {
16832 return Ok(vec![]);
16833 }
16834
16835 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
16836 let to_lsp = |point: &text::Anchor| {
16837 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
16838 point_to_lsp(end)
16839 };
16840 let lsp_end = to_lsp(&buffer_position);
16841
16842 let candidates = snippets
16843 .iter()
16844 .enumerate()
16845 .flat_map(|(ix, snippet)| {
16846 snippet
16847 .prefix
16848 .iter()
16849 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
16850 })
16851 .collect::<Vec<StringMatchCandidate>>();
16852
16853 let mut matches = fuzzy::match_strings(
16854 &candidates,
16855 &last_word,
16856 last_word.chars().any(|c| c.is_uppercase()),
16857 100,
16858 &Default::default(),
16859 executor,
16860 )
16861 .await;
16862
16863 // Remove all candidates where the query's start does not match the start of any word in the candidate
16864 if let Some(query_start) = last_word.chars().next() {
16865 matches.retain(|string_match| {
16866 split_words(&string_match.string).any(|word| {
16867 // Check that the first codepoint of the word as lowercase matches the first
16868 // codepoint of the query as lowercase
16869 word.chars()
16870 .flat_map(|codepoint| codepoint.to_lowercase())
16871 .zip(query_start.to_lowercase())
16872 .all(|(word_cp, query_cp)| word_cp == query_cp)
16873 })
16874 });
16875 }
16876
16877 let matched_strings = matches
16878 .into_iter()
16879 .map(|m| m.string)
16880 .collect::<HashSet<_>>();
16881
16882 let result: Vec<Completion> = snippets
16883 .into_iter()
16884 .filter_map(|snippet| {
16885 let matching_prefix = snippet
16886 .prefix
16887 .iter()
16888 .find(|prefix| matched_strings.contains(*prefix))?;
16889 let start = as_offset - last_word.len();
16890 let start = snapshot.anchor_before(start);
16891 let range = start..buffer_position;
16892 let lsp_start = to_lsp(&start);
16893 let lsp_range = lsp::Range {
16894 start: lsp_start,
16895 end: lsp_end,
16896 };
16897 Some(Completion {
16898 old_range: range,
16899 new_text: snippet.body.clone(),
16900 resolved: false,
16901 label: CodeLabel {
16902 text: matching_prefix.clone(),
16903 runs: vec![],
16904 filter_range: 0..matching_prefix.len(),
16905 },
16906 server_id: LanguageServerId(usize::MAX),
16907 documentation: snippet
16908 .description
16909 .clone()
16910 .map(|description| CompletionDocumentation::SingleLine(description.into())),
16911 lsp_completion: lsp::CompletionItem {
16912 label: snippet.prefix.first().unwrap().clone(),
16913 kind: Some(CompletionItemKind::SNIPPET),
16914 label_details: snippet.description.as_ref().map(|description| {
16915 lsp::CompletionItemLabelDetails {
16916 detail: Some(description.clone()),
16917 description: None,
16918 }
16919 }),
16920 insert_text_format: Some(InsertTextFormat::SNIPPET),
16921 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
16922 lsp::InsertReplaceEdit {
16923 new_text: snippet.body.clone(),
16924 insert: lsp_range,
16925 replace: lsp_range,
16926 },
16927 )),
16928 filter_text: Some(snippet.body.clone()),
16929 sort_text: Some(char::MAX.to_string()),
16930 ..Default::default()
16931 },
16932 confirm: None,
16933 })
16934 })
16935 .collect();
16936
16937 Ok(result)
16938 })
16939}
16940
16941impl CompletionProvider for Entity<Project> {
16942 fn completions(
16943 &self,
16944 buffer: &Entity<Buffer>,
16945 buffer_position: text::Anchor,
16946 options: CompletionContext,
16947 _window: &mut Window,
16948 cx: &mut Context<Editor>,
16949 ) -> Task<Result<Vec<Completion>>> {
16950 self.update(cx, |project, cx| {
16951 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16952 let project_completions = project.completions(buffer, buffer_position, options, cx);
16953 cx.background_spawn(async move {
16954 let mut completions = project_completions.await?;
16955 let snippets_completions = snippets.await?;
16956 completions.extend(snippets_completions);
16957 Ok(completions)
16958 })
16959 })
16960 }
16961
16962 fn resolve_completions(
16963 &self,
16964 buffer: Entity<Buffer>,
16965 completion_indices: Vec<usize>,
16966 completions: Rc<RefCell<Box<[Completion]>>>,
16967 cx: &mut Context<Editor>,
16968 ) -> Task<Result<bool>> {
16969 self.update(cx, |project, cx| {
16970 project.lsp_store().update(cx, |lsp_store, cx| {
16971 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16972 })
16973 })
16974 }
16975
16976 fn apply_additional_edits_for_completion(
16977 &self,
16978 buffer: Entity<Buffer>,
16979 completions: Rc<RefCell<Box<[Completion]>>>,
16980 completion_index: usize,
16981 push_to_history: bool,
16982 cx: &mut Context<Editor>,
16983 ) -> Task<Result<Option<language::Transaction>>> {
16984 self.update(cx, |project, cx| {
16985 project.lsp_store().update(cx, |lsp_store, cx| {
16986 lsp_store.apply_additional_edits_for_completion(
16987 buffer,
16988 completions,
16989 completion_index,
16990 push_to_history,
16991 cx,
16992 )
16993 })
16994 })
16995 }
16996
16997 fn is_completion_trigger(
16998 &self,
16999 buffer: &Entity<Buffer>,
17000 position: language::Anchor,
17001 text: &str,
17002 trigger_in_words: bool,
17003 cx: &mut Context<Editor>,
17004 ) -> bool {
17005 let mut chars = text.chars();
17006 let char = if let Some(char) = chars.next() {
17007 char
17008 } else {
17009 return false;
17010 };
17011 if chars.next().is_some() {
17012 return false;
17013 }
17014
17015 let buffer = buffer.read(cx);
17016 let snapshot = buffer.snapshot();
17017 if !snapshot.settings_at(position, cx).show_completions_on_input {
17018 return false;
17019 }
17020 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17021 if trigger_in_words && classifier.is_word(char) {
17022 return true;
17023 }
17024
17025 buffer.completion_triggers().contains(text)
17026 }
17027}
17028
17029impl SemanticsProvider for Entity<Project> {
17030 fn hover(
17031 &self,
17032 buffer: &Entity<Buffer>,
17033 position: text::Anchor,
17034 cx: &mut App,
17035 ) -> Option<Task<Vec<project::Hover>>> {
17036 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17037 }
17038
17039 fn document_highlights(
17040 &self,
17041 buffer: &Entity<Buffer>,
17042 position: text::Anchor,
17043 cx: &mut App,
17044 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
17045 Some(self.update(cx, |project, cx| {
17046 project.document_highlights(buffer, position, cx)
17047 }))
17048 }
17049
17050 fn definitions(
17051 &self,
17052 buffer: &Entity<Buffer>,
17053 position: text::Anchor,
17054 kind: GotoDefinitionKind,
17055 cx: &mut App,
17056 ) -> Option<Task<Result<Vec<LocationLink>>>> {
17057 Some(self.update(cx, |project, cx| match kind {
17058 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
17059 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
17060 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
17061 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
17062 }))
17063 }
17064
17065 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
17066 // TODO: make this work for remote projects
17067 self.update(cx, |this, cx| {
17068 buffer.update(cx, |buffer, cx| {
17069 this.any_language_server_supports_inlay_hints(buffer, cx)
17070 })
17071 })
17072 }
17073
17074 fn inlay_hints(
17075 &self,
17076 buffer_handle: Entity<Buffer>,
17077 range: Range<text::Anchor>,
17078 cx: &mut App,
17079 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
17080 Some(self.update(cx, |project, cx| {
17081 project.inlay_hints(buffer_handle, range, cx)
17082 }))
17083 }
17084
17085 fn resolve_inlay_hint(
17086 &self,
17087 hint: InlayHint,
17088 buffer_handle: Entity<Buffer>,
17089 server_id: LanguageServerId,
17090 cx: &mut App,
17091 ) -> Option<Task<anyhow::Result<InlayHint>>> {
17092 Some(self.update(cx, |project, cx| {
17093 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
17094 }))
17095 }
17096
17097 fn range_for_rename(
17098 &self,
17099 buffer: &Entity<Buffer>,
17100 position: text::Anchor,
17101 cx: &mut App,
17102 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
17103 Some(self.update(cx, |project, cx| {
17104 let buffer = buffer.clone();
17105 let task = project.prepare_rename(buffer.clone(), position, cx);
17106 cx.spawn(|_, mut cx| async move {
17107 Ok(match task.await? {
17108 PrepareRenameResponse::Success(range) => Some(range),
17109 PrepareRenameResponse::InvalidPosition => None,
17110 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
17111 // Fallback on using TreeSitter info to determine identifier range
17112 buffer.update(&mut cx, |buffer, _| {
17113 let snapshot = buffer.snapshot();
17114 let (range, kind) = snapshot.surrounding_word(position);
17115 if kind != Some(CharKind::Word) {
17116 return None;
17117 }
17118 Some(
17119 snapshot.anchor_before(range.start)
17120 ..snapshot.anchor_after(range.end),
17121 )
17122 })?
17123 }
17124 })
17125 })
17126 }))
17127 }
17128
17129 fn perform_rename(
17130 &self,
17131 buffer: &Entity<Buffer>,
17132 position: text::Anchor,
17133 new_name: String,
17134 cx: &mut App,
17135 ) -> Option<Task<Result<ProjectTransaction>>> {
17136 Some(self.update(cx, |project, cx| {
17137 project.perform_rename(buffer.clone(), position, new_name, cx)
17138 }))
17139 }
17140}
17141
17142fn inlay_hint_settings(
17143 location: Anchor,
17144 snapshot: &MultiBufferSnapshot,
17145 cx: &mut Context<Editor>,
17146) -> InlayHintSettings {
17147 let file = snapshot.file_at(location);
17148 let language = snapshot.language_at(location).map(|l| l.name());
17149 language_settings(language, file, cx).inlay_hints
17150}
17151
17152fn consume_contiguous_rows(
17153 contiguous_row_selections: &mut Vec<Selection<Point>>,
17154 selection: &Selection<Point>,
17155 display_map: &DisplaySnapshot,
17156 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
17157) -> (MultiBufferRow, MultiBufferRow) {
17158 contiguous_row_selections.push(selection.clone());
17159 let start_row = MultiBufferRow(selection.start.row);
17160 let mut end_row = ending_row(selection, display_map);
17161
17162 while let Some(next_selection) = selections.peek() {
17163 if next_selection.start.row <= end_row.0 {
17164 end_row = ending_row(next_selection, display_map);
17165 contiguous_row_selections.push(selections.next().unwrap().clone());
17166 } else {
17167 break;
17168 }
17169 }
17170 (start_row, end_row)
17171}
17172
17173fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17174 if next_selection.end.column > 0 || next_selection.is_empty() {
17175 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17176 } else {
17177 MultiBufferRow(next_selection.end.row)
17178 }
17179}
17180
17181impl EditorSnapshot {
17182 pub fn remote_selections_in_range<'a>(
17183 &'a self,
17184 range: &'a Range<Anchor>,
17185 collaboration_hub: &dyn CollaborationHub,
17186 cx: &'a App,
17187 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17188 let participant_names = collaboration_hub.user_names(cx);
17189 let participant_indices = collaboration_hub.user_participant_indices(cx);
17190 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17191 let collaborators_by_replica_id = collaborators_by_peer_id
17192 .iter()
17193 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17194 .collect::<HashMap<_, _>>();
17195 self.buffer_snapshot
17196 .selections_in_range(range, false)
17197 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17198 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17199 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17200 let user_name = participant_names.get(&collaborator.user_id).cloned();
17201 Some(RemoteSelection {
17202 replica_id,
17203 selection,
17204 cursor_shape,
17205 line_mode,
17206 participant_index,
17207 peer_id: collaborator.peer_id,
17208 user_name,
17209 })
17210 })
17211 }
17212
17213 pub fn hunks_for_ranges(
17214 &self,
17215 ranges: impl IntoIterator<Item = Range<Point>>,
17216 ) -> Vec<MultiBufferDiffHunk> {
17217 let mut hunks = Vec::new();
17218 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17219 HashMap::default();
17220 for query_range in ranges {
17221 let query_rows =
17222 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17223 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17224 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17225 ) {
17226 // Include deleted hunks that are adjacent to the query range, because
17227 // otherwise they would be missed.
17228 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
17229 if hunk.status().is_deleted() {
17230 intersects_range |= hunk.row_range.start == query_rows.end;
17231 intersects_range |= hunk.row_range.end == query_rows.start;
17232 }
17233 if intersects_range {
17234 if !processed_buffer_rows
17235 .entry(hunk.buffer_id)
17236 .or_default()
17237 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17238 {
17239 continue;
17240 }
17241 hunks.push(hunk);
17242 }
17243 }
17244 }
17245
17246 hunks
17247 }
17248
17249 fn display_diff_hunks_for_rows<'a>(
17250 &'a self,
17251 display_rows: Range<DisplayRow>,
17252 folded_buffers: &'a HashSet<BufferId>,
17253 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17254 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17255 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17256
17257 self.buffer_snapshot
17258 .diff_hunks_in_range(buffer_start..buffer_end)
17259 .filter_map(|hunk| {
17260 if folded_buffers.contains(&hunk.buffer_id) {
17261 return None;
17262 }
17263
17264 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17265 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17266
17267 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17268 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17269
17270 let display_hunk = if hunk_display_start.column() != 0 {
17271 DisplayDiffHunk::Folded {
17272 display_row: hunk_display_start.row(),
17273 }
17274 } else {
17275 let mut end_row = hunk_display_end.row();
17276 if hunk_display_end.column() > 0 {
17277 end_row.0 += 1;
17278 }
17279 let is_created_file = hunk.is_created_file();
17280 DisplayDiffHunk::Unfolded {
17281 status: hunk.status(),
17282 diff_base_byte_range: hunk.diff_base_byte_range,
17283 display_row_range: hunk_display_start.row()..end_row,
17284 multi_buffer_range: Anchor::range_in_buffer(
17285 hunk.excerpt_id,
17286 hunk.buffer_id,
17287 hunk.buffer_range,
17288 ),
17289 is_created_file,
17290 }
17291 };
17292
17293 Some(display_hunk)
17294 })
17295 }
17296
17297 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17298 self.display_snapshot.buffer_snapshot.language_at(position)
17299 }
17300
17301 pub fn is_focused(&self) -> bool {
17302 self.is_focused
17303 }
17304
17305 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17306 self.placeholder_text.as_ref()
17307 }
17308
17309 pub fn scroll_position(&self) -> gpui::Point<f32> {
17310 self.scroll_anchor.scroll_position(&self.display_snapshot)
17311 }
17312
17313 fn gutter_dimensions(
17314 &self,
17315 font_id: FontId,
17316 font_size: Pixels,
17317 max_line_number_width: Pixels,
17318 cx: &App,
17319 ) -> Option<GutterDimensions> {
17320 if !self.show_gutter {
17321 return None;
17322 }
17323
17324 let descent = cx.text_system().descent(font_id, font_size);
17325 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17326 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17327
17328 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17329 matches!(
17330 ProjectSettings::get_global(cx).git.git_gutter,
17331 Some(GitGutterSetting::TrackedFiles)
17332 )
17333 });
17334 let gutter_settings = EditorSettings::get_global(cx).gutter;
17335 let show_line_numbers = self
17336 .show_line_numbers
17337 .unwrap_or(gutter_settings.line_numbers);
17338 let line_gutter_width = if show_line_numbers {
17339 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17340 let min_width_for_number_on_gutter = em_advance * 4.0;
17341 max_line_number_width.max(min_width_for_number_on_gutter)
17342 } else {
17343 0.0.into()
17344 };
17345
17346 let show_code_actions = self
17347 .show_code_actions
17348 .unwrap_or(gutter_settings.code_actions);
17349
17350 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17351
17352 let git_blame_entries_width =
17353 self.git_blame_gutter_max_author_length
17354 .map(|max_author_length| {
17355 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17356
17357 /// The number of characters to dedicate to gaps and margins.
17358 const SPACING_WIDTH: usize = 4;
17359
17360 let max_char_count = max_author_length
17361 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17362 + ::git::SHORT_SHA_LENGTH
17363 + MAX_RELATIVE_TIMESTAMP.len()
17364 + SPACING_WIDTH;
17365
17366 em_advance * max_char_count
17367 });
17368
17369 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17370 left_padding += if show_code_actions || show_runnables {
17371 em_width * 3.0
17372 } else if show_git_gutter && show_line_numbers {
17373 em_width * 2.0
17374 } else if show_git_gutter || show_line_numbers {
17375 em_width
17376 } else {
17377 px(0.)
17378 };
17379
17380 let right_padding = if gutter_settings.folds && show_line_numbers {
17381 em_width * 4.0
17382 } else if gutter_settings.folds {
17383 em_width * 3.0
17384 } else if show_line_numbers {
17385 em_width
17386 } else {
17387 px(0.)
17388 };
17389
17390 Some(GutterDimensions {
17391 left_padding,
17392 right_padding,
17393 width: line_gutter_width + left_padding + right_padding,
17394 margin: -descent,
17395 git_blame_entries_width,
17396 })
17397 }
17398
17399 pub fn render_crease_toggle(
17400 &self,
17401 buffer_row: MultiBufferRow,
17402 row_contains_cursor: bool,
17403 editor: Entity<Editor>,
17404 window: &mut Window,
17405 cx: &mut App,
17406 ) -> Option<AnyElement> {
17407 let folded = self.is_line_folded(buffer_row);
17408 let mut is_foldable = false;
17409
17410 if let Some(crease) = self
17411 .crease_snapshot
17412 .query_row(buffer_row, &self.buffer_snapshot)
17413 {
17414 is_foldable = true;
17415 match crease {
17416 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17417 if let Some(render_toggle) = render_toggle {
17418 let toggle_callback =
17419 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17420 if folded {
17421 editor.update(cx, |editor, cx| {
17422 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17423 });
17424 } else {
17425 editor.update(cx, |editor, cx| {
17426 editor.unfold_at(
17427 &crate::UnfoldAt { buffer_row },
17428 window,
17429 cx,
17430 )
17431 });
17432 }
17433 });
17434 return Some((render_toggle)(
17435 buffer_row,
17436 folded,
17437 toggle_callback,
17438 window,
17439 cx,
17440 ));
17441 }
17442 }
17443 }
17444 }
17445
17446 is_foldable |= self.starts_indent(buffer_row);
17447
17448 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17449 Some(
17450 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17451 .toggle_state(folded)
17452 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17453 if folded {
17454 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17455 } else {
17456 this.fold_at(&FoldAt { buffer_row }, window, cx);
17457 }
17458 }))
17459 .into_any_element(),
17460 )
17461 } else {
17462 None
17463 }
17464 }
17465
17466 pub fn render_crease_trailer(
17467 &self,
17468 buffer_row: MultiBufferRow,
17469 window: &mut Window,
17470 cx: &mut App,
17471 ) -> Option<AnyElement> {
17472 let folded = self.is_line_folded(buffer_row);
17473 if let Crease::Inline { render_trailer, .. } = self
17474 .crease_snapshot
17475 .query_row(buffer_row, &self.buffer_snapshot)?
17476 {
17477 let render_trailer = render_trailer.as_ref()?;
17478 Some(render_trailer(buffer_row, folded, window, cx))
17479 } else {
17480 None
17481 }
17482 }
17483}
17484
17485impl Deref for EditorSnapshot {
17486 type Target = DisplaySnapshot;
17487
17488 fn deref(&self) -> &Self::Target {
17489 &self.display_snapshot
17490 }
17491}
17492
17493#[derive(Clone, Debug, PartialEq, Eq)]
17494pub enum EditorEvent {
17495 InputIgnored {
17496 text: Arc<str>,
17497 },
17498 InputHandled {
17499 utf16_range_to_replace: Option<Range<isize>>,
17500 text: Arc<str>,
17501 },
17502 ExcerptsAdded {
17503 buffer: Entity<Buffer>,
17504 predecessor: ExcerptId,
17505 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17506 },
17507 ExcerptsRemoved {
17508 ids: Vec<ExcerptId>,
17509 },
17510 BufferFoldToggled {
17511 ids: Vec<ExcerptId>,
17512 folded: bool,
17513 },
17514 ExcerptsEdited {
17515 ids: Vec<ExcerptId>,
17516 },
17517 ExcerptsExpanded {
17518 ids: Vec<ExcerptId>,
17519 },
17520 BufferEdited,
17521 Edited {
17522 transaction_id: clock::Lamport,
17523 },
17524 Reparsed(BufferId),
17525 Focused,
17526 FocusedIn,
17527 Blurred,
17528 DirtyChanged,
17529 Saved,
17530 TitleChanged,
17531 DiffBaseChanged,
17532 SelectionsChanged {
17533 local: bool,
17534 },
17535 ScrollPositionChanged {
17536 local: bool,
17537 autoscroll: bool,
17538 },
17539 Closed,
17540 TransactionUndone {
17541 transaction_id: clock::Lamport,
17542 },
17543 TransactionBegun {
17544 transaction_id: clock::Lamport,
17545 },
17546 Reloaded,
17547 CursorShapeChanged,
17548}
17549
17550impl EventEmitter<EditorEvent> for Editor {}
17551
17552impl Focusable for Editor {
17553 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17554 self.focus_handle.clone()
17555 }
17556}
17557
17558impl Render for Editor {
17559 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17560 let settings = ThemeSettings::get_global(cx);
17561
17562 let mut text_style = match self.mode {
17563 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17564 color: cx.theme().colors().editor_foreground,
17565 font_family: settings.ui_font.family.clone(),
17566 font_features: settings.ui_font.features.clone(),
17567 font_fallbacks: settings.ui_font.fallbacks.clone(),
17568 font_size: rems(0.875).into(),
17569 font_weight: settings.ui_font.weight,
17570 line_height: relative(settings.buffer_line_height.value()),
17571 ..Default::default()
17572 },
17573 EditorMode::Full => TextStyle {
17574 color: cx.theme().colors().editor_foreground,
17575 font_family: settings.buffer_font.family.clone(),
17576 font_features: settings.buffer_font.features.clone(),
17577 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17578 font_size: settings.buffer_font_size(cx).into(),
17579 font_weight: settings.buffer_font.weight,
17580 line_height: relative(settings.buffer_line_height.value()),
17581 ..Default::default()
17582 },
17583 };
17584 if let Some(text_style_refinement) = &self.text_style_refinement {
17585 text_style.refine(text_style_refinement)
17586 }
17587
17588 let background = match self.mode {
17589 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17590 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17591 EditorMode::Full => cx.theme().colors().editor_background,
17592 };
17593
17594 EditorElement::new(
17595 &cx.entity(),
17596 EditorStyle {
17597 background,
17598 local_player: cx.theme().players().local(),
17599 text: text_style,
17600 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17601 syntax: cx.theme().syntax().clone(),
17602 status: cx.theme().status().clone(),
17603 inlay_hints_style: make_inlay_hints_style(cx),
17604 inline_completion_styles: make_suggestion_styles(cx),
17605 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17606 },
17607 )
17608 }
17609}
17610
17611impl EntityInputHandler for Editor {
17612 fn text_for_range(
17613 &mut self,
17614 range_utf16: Range<usize>,
17615 adjusted_range: &mut Option<Range<usize>>,
17616 _: &mut Window,
17617 cx: &mut Context<Self>,
17618 ) -> Option<String> {
17619 let snapshot = self.buffer.read(cx).read(cx);
17620 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17621 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17622 if (start.0..end.0) != range_utf16 {
17623 adjusted_range.replace(start.0..end.0);
17624 }
17625 Some(snapshot.text_for_range(start..end).collect())
17626 }
17627
17628 fn selected_text_range(
17629 &mut self,
17630 ignore_disabled_input: bool,
17631 _: &mut Window,
17632 cx: &mut Context<Self>,
17633 ) -> Option<UTF16Selection> {
17634 // Prevent the IME menu from appearing when holding down an alphabetic key
17635 // while input is disabled.
17636 if !ignore_disabled_input && !self.input_enabled {
17637 return None;
17638 }
17639
17640 let selection = self.selections.newest::<OffsetUtf16>(cx);
17641 let range = selection.range();
17642
17643 Some(UTF16Selection {
17644 range: range.start.0..range.end.0,
17645 reversed: selection.reversed,
17646 })
17647 }
17648
17649 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17650 let snapshot = self.buffer.read(cx).read(cx);
17651 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17652 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17653 }
17654
17655 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17656 self.clear_highlights::<InputComposition>(cx);
17657 self.ime_transaction.take();
17658 }
17659
17660 fn replace_text_in_range(
17661 &mut self,
17662 range_utf16: Option<Range<usize>>,
17663 text: &str,
17664 window: &mut Window,
17665 cx: &mut Context<Self>,
17666 ) {
17667 if !self.input_enabled {
17668 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17669 return;
17670 }
17671
17672 self.transact(window, cx, |this, window, cx| {
17673 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17674 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17675 Some(this.selection_replacement_ranges(range_utf16, cx))
17676 } else {
17677 this.marked_text_ranges(cx)
17678 };
17679
17680 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17681 let newest_selection_id = this.selections.newest_anchor().id;
17682 this.selections
17683 .all::<OffsetUtf16>(cx)
17684 .iter()
17685 .zip(ranges_to_replace.iter())
17686 .find_map(|(selection, range)| {
17687 if selection.id == newest_selection_id {
17688 Some(
17689 (range.start.0 as isize - selection.head().0 as isize)
17690 ..(range.end.0 as isize - selection.head().0 as isize),
17691 )
17692 } else {
17693 None
17694 }
17695 })
17696 });
17697
17698 cx.emit(EditorEvent::InputHandled {
17699 utf16_range_to_replace: range_to_replace,
17700 text: text.into(),
17701 });
17702
17703 if let Some(new_selected_ranges) = new_selected_ranges {
17704 this.change_selections(None, window, cx, |selections| {
17705 selections.select_ranges(new_selected_ranges)
17706 });
17707 this.backspace(&Default::default(), window, cx);
17708 }
17709
17710 this.handle_input(text, window, cx);
17711 });
17712
17713 if let Some(transaction) = self.ime_transaction {
17714 self.buffer.update(cx, |buffer, cx| {
17715 buffer.group_until_transaction(transaction, cx);
17716 });
17717 }
17718
17719 self.unmark_text(window, cx);
17720 }
17721
17722 fn replace_and_mark_text_in_range(
17723 &mut self,
17724 range_utf16: Option<Range<usize>>,
17725 text: &str,
17726 new_selected_range_utf16: Option<Range<usize>>,
17727 window: &mut Window,
17728 cx: &mut Context<Self>,
17729 ) {
17730 if !self.input_enabled {
17731 return;
17732 }
17733
17734 let transaction = self.transact(window, cx, |this, window, cx| {
17735 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17736 let snapshot = this.buffer.read(cx).read(cx);
17737 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17738 for marked_range in &mut marked_ranges {
17739 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17740 marked_range.start.0 += relative_range_utf16.start;
17741 marked_range.start =
17742 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
17743 marked_range.end =
17744 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
17745 }
17746 }
17747 Some(marked_ranges)
17748 } else if let Some(range_utf16) = range_utf16 {
17749 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17750 Some(this.selection_replacement_ranges(range_utf16, cx))
17751 } else {
17752 None
17753 };
17754
17755 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
17756 let newest_selection_id = this.selections.newest_anchor().id;
17757 this.selections
17758 .all::<OffsetUtf16>(cx)
17759 .iter()
17760 .zip(ranges_to_replace.iter())
17761 .find_map(|(selection, range)| {
17762 if selection.id == newest_selection_id {
17763 Some(
17764 (range.start.0 as isize - selection.head().0 as isize)
17765 ..(range.end.0 as isize - selection.head().0 as isize),
17766 )
17767 } else {
17768 None
17769 }
17770 })
17771 });
17772
17773 cx.emit(EditorEvent::InputHandled {
17774 utf16_range_to_replace: range_to_replace,
17775 text: text.into(),
17776 });
17777
17778 if let Some(ranges) = ranges_to_replace {
17779 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
17780 }
17781
17782 let marked_ranges = {
17783 let snapshot = this.buffer.read(cx).read(cx);
17784 this.selections
17785 .disjoint_anchors()
17786 .iter()
17787 .map(|selection| {
17788 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
17789 })
17790 .collect::<Vec<_>>()
17791 };
17792
17793 if text.is_empty() {
17794 this.unmark_text(window, cx);
17795 } else {
17796 this.highlight_text::<InputComposition>(
17797 marked_ranges.clone(),
17798 HighlightStyle {
17799 underline: Some(UnderlineStyle {
17800 thickness: px(1.),
17801 color: None,
17802 wavy: false,
17803 }),
17804 ..Default::default()
17805 },
17806 cx,
17807 );
17808 }
17809
17810 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
17811 let use_autoclose = this.use_autoclose;
17812 let use_auto_surround = this.use_auto_surround;
17813 this.set_use_autoclose(false);
17814 this.set_use_auto_surround(false);
17815 this.handle_input(text, window, cx);
17816 this.set_use_autoclose(use_autoclose);
17817 this.set_use_auto_surround(use_auto_surround);
17818
17819 if let Some(new_selected_range) = new_selected_range_utf16 {
17820 let snapshot = this.buffer.read(cx).read(cx);
17821 let new_selected_ranges = marked_ranges
17822 .into_iter()
17823 .map(|marked_range| {
17824 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
17825 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
17826 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
17827 snapshot.clip_offset_utf16(new_start, Bias::Left)
17828 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
17829 })
17830 .collect::<Vec<_>>();
17831
17832 drop(snapshot);
17833 this.change_selections(None, window, cx, |selections| {
17834 selections.select_ranges(new_selected_ranges)
17835 });
17836 }
17837 });
17838
17839 self.ime_transaction = self.ime_transaction.or(transaction);
17840 if let Some(transaction) = self.ime_transaction {
17841 self.buffer.update(cx, |buffer, cx| {
17842 buffer.group_until_transaction(transaction, cx);
17843 });
17844 }
17845
17846 if self.text_highlights::<InputComposition>(cx).is_none() {
17847 self.ime_transaction.take();
17848 }
17849 }
17850
17851 fn bounds_for_range(
17852 &mut self,
17853 range_utf16: Range<usize>,
17854 element_bounds: gpui::Bounds<Pixels>,
17855 window: &mut Window,
17856 cx: &mut Context<Self>,
17857 ) -> Option<gpui::Bounds<Pixels>> {
17858 let text_layout_details = self.text_layout_details(window);
17859 let gpui::Size {
17860 width: em_width,
17861 height: line_height,
17862 } = self.character_size(window);
17863
17864 let snapshot = self.snapshot(window, cx);
17865 let scroll_position = snapshot.scroll_position();
17866 let scroll_left = scroll_position.x * em_width;
17867
17868 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
17869 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
17870 + self.gutter_dimensions.width
17871 + self.gutter_dimensions.margin;
17872 let y = line_height * (start.row().as_f32() - scroll_position.y);
17873
17874 Some(Bounds {
17875 origin: element_bounds.origin + point(x, y),
17876 size: size(em_width, line_height),
17877 })
17878 }
17879
17880 fn character_index_for_point(
17881 &mut self,
17882 point: gpui::Point<Pixels>,
17883 _window: &mut Window,
17884 _cx: &mut Context<Self>,
17885 ) -> Option<usize> {
17886 let position_map = self.last_position_map.as_ref()?;
17887 if !position_map.text_hitbox.contains(&point) {
17888 return None;
17889 }
17890 let display_point = position_map.point_for_position(point).previous_valid;
17891 let anchor = position_map
17892 .snapshot
17893 .display_point_to_anchor(display_point, Bias::Left);
17894 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
17895 Some(utf16_offset.0)
17896 }
17897}
17898
17899trait SelectionExt {
17900 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
17901 fn spanned_rows(
17902 &self,
17903 include_end_if_at_line_start: bool,
17904 map: &DisplaySnapshot,
17905 ) -> Range<MultiBufferRow>;
17906}
17907
17908impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
17909 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
17910 let start = self
17911 .start
17912 .to_point(&map.buffer_snapshot)
17913 .to_display_point(map);
17914 let end = self
17915 .end
17916 .to_point(&map.buffer_snapshot)
17917 .to_display_point(map);
17918 if self.reversed {
17919 end..start
17920 } else {
17921 start..end
17922 }
17923 }
17924
17925 fn spanned_rows(
17926 &self,
17927 include_end_if_at_line_start: bool,
17928 map: &DisplaySnapshot,
17929 ) -> Range<MultiBufferRow> {
17930 let start = self.start.to_point(&map.buffer_snapshot);
17931 let mut end = self.end.to_point(&map.buffer_snapshot);
17932 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
17933 end.row -= 1;
17934 }
17935
17936 let buffer_start = map.prev_line_boundary(start).0;
17937 let buffer_end = map.next_line_boundary(end).0;
17938 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
17939 }
17940}
17941
17942impl<T: InvalidationRegion> InvalidationStack<T> {
17943 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
17944 where
17945 S: Clone + ToOffset,
17946 {
17947 while let Some(region) = self.last() {
17948 let all_selections_inside_invalidation_ranges =
17949 if selections.len() == region.ranges().len() {
17950 selections
17951 .iter()
17952 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
17953 .all(|(selection, invalidation_range)| {
17954 let head = selection.head().to_offset(buffer);
17955 invalidation_range.start <= head && invalidation_range.end >= head
17956 })
17957 } else {
17958 false
17959 };
17960
17961 if all_selections_inside_invalidation_ranges {
17962 break;
17963 } else {
17964 self.pop();
17965 }
17966 }
17967 }
17968}
17969
17970impl<T> Default for InvalidationStack<T> {
17971 fn default() -> Self {
17972 Self(Default::default())
17973 }
17974}
17975
17976impl<T> Deref for InvalidationStack<T> {
17977 type Target = Vec<T>;
17978
17979 fn deref(&self) -> &Self::Target {
17980 &self.0
17981 }
17982}
17983
17984impl<T> DerefMut for InvalidationStack<T> {
17985 fn deref_mut(&mut self) -> &mut Self::Target {
17986 &mut self.0
17987 }
17988}
17989
17990impl InvalidationRegion for SnippetState {
17991 fn ranges(&self) -> &[Range<Anchor>] {
17992 &self.ranges[self.active_index]
17993 }
17994}
17995
17996pub fn diagnostic_block_renderer(
17997 diagnostic: Diagnostic,
17998 max_message_rows: Option<u8>,
17999 allow_closing: bool,
18000) -> RenderBlock {
18001 let (text_without_backticks, code_ranges) =
18002 highlight_diagnostic_message(&diagnostic, max_message_rows);
18003
18004 Arc::new(move |cx: &mut BlockContext| {
18005 let group_id: SharedString = cx.block_id.to_string().into();
18006
18007 let mut text_style = cx.window.text_style().clone();
18008 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18009 let theme_settings = ThemeSettings::get_global(cx);
18010 text_style.font_family = theme_settings.buffer_font.family.clone();
18011 text_style.font_style = theme_settings.buffer_font.style;
18012 text_style.font_features = theme_settings.buffer_font.features.clone();
18013 text_style.font_weight = theme_settings.buffer_font.weight;
18014
18015 let multi_line_diagnostic = diagnostic.message.contains('\n');
18016
18017 let buttons = |diagnostic: &Diagnostic| {
18018 if multi_line_diagnostic {
18019 v_flex()
18020 } else {
18021 h_flex()
18022 }
18023 .when(allow_closing, |div| {
18024 div.children(diagnostic.is_primary.then(|| {
18025 IconButton::new("close-block", IconName::XCircle)
18026 .icon_color(Color::Muted)
18027 .size(ButtonSize::Compact)
18028 .style(ButtonStyle::Transparent)
18029 .visible_on_hover(group_id.clone())
18030 .on_click(move |_click, window, cx| {
18031 window.dispatch_action(Box::new(Cancel), cx)
18032 })
18033 .tooltip(|window, cx| {
18034 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18035 })
18036 }))
18037 })
18038 .child(
18039 IconButton::new("copy-block", IconName::Copy)
18040 .icon_color(Color::Muted)
18041 .size(ButtonSize::Compact)
18042 .style(ButtonStyle::Transparent)
18043 .visible_on_hover(group_id.clone())
18044 .on_click({
18045 let message = diagnostic.message.clone();
18046 move |_click, _, cx| {
18047 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
18048 }
18049 })
18050 .tooltip(Tooltip::text("Copy diagnostic message")),
18051 )
18052 };
18053
18054 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
18055 AvailableSpace::min_size(),
18056 cx.window,
18057 cx.app,
18058 );
18059
18060 h_flex()
18061 .id(cx.block_id)
18062 .group(group_id.clone())
18063 .relative()
18064 .size_full()
18065 .block_mouse_down()
18066 .pl(cx.gutter_dimensions.width)
18067 .w(cx.max_width - cx.gutter_dimensions.full_width())
18068 .child(
18069 div()
18070 .flex()
18071 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
18072 .flex_shrink(),
18073 )
18074 .child(buttons(&diagnostic))
18075 .child(div().flex().flex_shrink_0().child(
18076 StyledText::new(text_without_backticks.clone()).with_default_highlights(
18077 &text_style,
18078 code_ranges.iter().map(|range| {
18079 (
18080 range.clone(),
18081 HighlightStyle {
18082 font_weight: Some(FontWeight::BOLD),
18083 ..Default::default()
18084 },
18085 )
18086 }),
18087 ),
18088 ))
18089 .into_any_element()
18090 })
18091}
18092
18093fn inline_completion_edit_text(
18094 current_snapshot: &BufferSnapshot,
18095 edits: &[(Range<Anchor>, String)],
18096 edit_preview: &EditPreview,
18097 include_deletions: bool,
18098 cx: &App,
18099) -> HighlightedText {
18100 let edits = edits
18101 .iter()
18102 .map(|(anchor, text)| {
18103 (
18104 anchor.start.text_anchor..anchor.end.text_anchor,
18105 text.clone(),
18106 )
18107 })
18108 .collect::<Vec<_>>();
18109
18110 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
18111}
18112
18113pub fn highlight_diagnostic_message(
18114 diagnostic: &Diagnostic,
18115 mut max_message_rows: Option<u8>,
18116) -> (SharedString, Vec<Range<usize>>) {
18117 let mut text_without_backticks = String::new();
18118 let mut code_ranges = Vec::new();
18119
18120 if let Some(source) = &diagnostic.source {
18121 text_without_backticks.push_str(source);
18122 code_ranges.push(0..source.len());
18123 text_without_backticks.push_str(": ");
18124 }
18125
18126 let mut prev_offset = 0;
18127 let mut in_code_block = false;
18128 let has_row_limit = max_message_rows.is_some();
18129 let mut newline_indices = diagnostic
18130 .message
18131 .match_indices('\n')
18132 .filter(|_| has_row_limit)
18133 .map(|(ix, _)| ix)
18134 .fuse()
18135 .peekable();
18136
18137 for (quote_ix, _) in diagnostic
18138 .message
18139 .match_indices('`')
18140 .chain([(diagnostic.message.len(), "")])
18141 {
18142 let mut first_newline_ix = None;
18143 let mut last_newline_ix = None;
18144 while let Some(newline_ix) = newline_indices.peek() {
18145 if *newline_ix < quote_ix {
18146 if first_newline_ix.is_none() {
18147 first_newline_ix = Some(*newline_ix);
18148 }
18149 last_newline_ix = Some(*newline_ix);
18150
18151 if let Some(rows_left) = &mut max_message_rows {
18152 if *rows_left == 0 {
18153 break;
18154 } else {
18155 *rows_left -= 1;
18156 }
18157 }
18158 let _ = newline_indices.next();
18159 } else {
18160 break;
18161 }
18162 }
18163 let prev_len = text_without_backticks.len();
18164 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18165 text_without_backticks.push_str(new_text);
18166 if in_code_block {
18167 code_ranges.push(prev_len..text_without_backticks.len());
18168 }
18169 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18170 in_code_block = !in_code_block;
18171 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18172 text_without_backticks.push_str("...");
18173 break;
18174 }
18175 }
18176
18177 (text_without_backticks.into(), code_ranges)
18178}
18179
18180fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18181 match severity {
18182 DiagnosticSeverity::ERROR => colors.error,
18183 DiagnosticSeverity::WARNING => colors.warning,
18184 DiagnosticSeverity::INFORMATION => colors.info,
18185 DiagnosticSeverity::HINT => colors.info,
18186 _ => colors.ignored,
18187 }
18188}
18189
18190pub fn styled_runs_for_code_label<'a>(
18191 label: &'a CodeLabel,
18192 syntax_theme: &'a theme::SyntaxTheme,
18193) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18194 let fade_out = HighlightStyle {
18195 fade_out: Some(0.35),
18196 ..Default::default()
18197 };
18198
18199 let mut prev_end = label.filter_range.end;
18200 label
18201 .runs
18202 .iter()
18203 .enumerate()
18204 .flat_map(move |(ix, (range, highlight_id))| {
18205 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18206 style
18207 } else {
18208 return Default::default();
18209 };
18210 let mut muted_style = style;
18211 muted_style.highlight(fade_out);
18212
18213 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18214 if range.start >= label.filter_range.end {
18215 if range.start > prev_end {
18216 runs.push((prev_end..range.start, fade_out));
18217 }
18218 runs.push((range.clone(), muted_style));
18219 } else if range.end <= label.filter_range.end {
18220 runs.push((range.clone(), style));
18221 } else {
18222 runs.push((range.start..label.filter_range.end, style));
18223 runs.push((label.filter_range.end..range.end, muted_style));
18224 }
18225 prev_end = cmp::max(prev_end, range.end);
18226
18227 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18228 runs.push((prev_end..label.text.len(), fade_out));
18229 }
18230
18231 runs
18232 })
18233}
18234
18235pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18236 let mut prev_index = 0;
18237 let mut prev_codepoint: Option<char> = None;
18238 text.char_indices()
18239 .chain([(text.len(), '\0')])
18240 .filter_map(move |(index, codepoint)| {
18241 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18242 let is_boundary = index == text.len()
18243 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18244 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18245 if is_boundary {
18246 let chunk = &text[prev_index..index];
18247 prev_index = index;
18248 Some(chunk)
18249 } else {
18250 None
18251 }
18252 })
18253}
18254
18255pub trait RangeToAnchorExt: Sized {
18256 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18257
18258 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18259 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18260 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18261 }
18262}
18263
18264impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18265 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18266 let start_offset = self.start.to_offset(snapshot);
18267 let end_offset = self.end.to_offset(snapshot);
18268 if start_offset == end_offset {
18269 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18270 } else {
18271 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18272 }
18273 }
18274}
18275
18276pub trait RowExt {
18277 fn as_f32(&self) -> f32;
18278
18279 fn next_row(&self) -> Self;
18280
18281 fn previous_row(&self) -> Self;
18282
18283 fn minus(&self, other: Self) -> u32;
18284}
18285
18286impl RowExt for DisplayRow {
18287 fn as_f32(&self) -> f32 {
18288 self.0 as f32
18289 }
18290
18291 fn next_row(&self) -> Self {
18292 Self(self.0 + 1)
18293 }
18294
18295 fn previous_row(&self) -> Self {
18296 Self(self.0.saturating_sub(1))
18297 }
18298
18299 fn minus(&self, other: Self) -> u32 {
18300 self.0 - other.0
18301 }
18302}
18303
18304impl RowExt for MultiBufferRow {
18305 fn as_f32(&self) -> f32 {
18306 self.0 as f32
18307 }
18308
18309 fn next_row(&self) -> Self {
18310 Self(self.0 + 1)
18311 }
18312
18313 fn previous_row(&self) -> Self {
18314 Self(self.0.saturating_sub(1))
18315 }
18316
18317 fn minus(&self, other: Self) -> u32 {
18318 self.0 - other.0
18319 }
18320}
18321
18322trait RowRangeExt {
18323 type Row;
18324
18325 fn len(&self) -> usize;
18326
18327 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18328}
18329
18330impl RowRangeExt for Range<MultiBufferRow> {
18331 type Row = MultiBufferRow;
18332
18333 fn len(&self) -> usize {
18334 (self.end.0 - self.start.0) as usize
18335 }
18336
18337 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18338 (self.start.0..self.end.0).map(MultiBufferRow)
18339 }
18340}
18341
18342impl RowRangeExt for Range<DisplayRow> {
18343 type Row = DisplayRow;
18344
18345 fn len(&self) -> usize {
18346 (self.end.0 - self.start.0) as usize
18347 }
18348
18349 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18350 (self.start.0..self.end.0).map(DisplayRow)
18351 }
18352}
18353
18354/// If select range has more than one line, we
18355/// just point the cursor to range.start.
18356fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18357 if range.start.row == range.end.row {
18358 range
18359 } else {
18360 range.start..range.start
18361 }
18362}
18363pub struct KillRing(ClipboardItem);
18364impl Global for KillRing {}
18365
18366const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18367
18368fn all_edits_insertions_or_deletions(
18369 edits: &Vec<(Range<Anchor>, String)>,
18370 snapshot: &MultiBufferSnapshot,
18371) -> bool {
18372 let mut all_insertions = true;
18373 let mut all_deletions = true;
18374
18375 for (range, new_text) in edits.iter() {
18376 let range_is_empty = range.to_offset(&snapshot).is_empty();
18377 let text_is_empty = new_text.is_empty();
18378
18379 if range_is_empty != text_is_empty {
18380 if range_is_empty {
18381 all_deletions = false;
18382 } else {
18383 all_insertions = false;
18384 }
18385 } else {
18386 return false;
18387 }
18388
18389 if !all_insertions && !all_deletions {
18390 return false;
18391 }
18392 }
18393 all_insertions || all_deletions
18394}
18395
18396struct MissingEditPredictionKeybindingTooltip;
18397
18398impl Render for MissingEditPredictionKeybindingTooltip {
18399 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18400 ui::tooltip_container(window, cx, |container, _, cx| {
18401 container
18402 .flex_shrink_0()
18403 .max_w_80()
18404 .min_h(rems_from_px(124.))
18405 .justify_between()
18406 .child(
18407 v_flex()
18408 .flex_1()
18409 .text_ui_sm(cx)
18410 .child(Label::new("Conflict with Accept Keybinding"))
18411 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
18412 )
18413 .child(
18414 h_flex()
18415 .pb_1()
18416 .gap_1()
18417 .items_end()
18418 .w_full()
18419 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
18420 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
18421 }))
18422 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
18423 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
18424 })),
18425 )
18426 })
18427 }
18428}