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 if buffer.read(cx).is_singleton() {
1254 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1255 cx.emit(EditorEvent::TitleChanged);
1256 }));
1257 }
1258 project_subscriptions.push(cx.subscribe_in(
1259 project,
1260 window,
1261 |editor, _, event, window, cx| {
1262 if let project::Event::RefreshInlayHints = event {
1263 editor
1264 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1265 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1266 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1267 let focus_handle = editor.focus_handle(cx);
1268 if focus_handle.is_focused(window) {
1269 let snapshot = buffer.read(cx).snapshot();
1270 for (range, snippet) in snippet_edits {
1271 let editor_range =
1272 language::range_from_lsp(*range).to_offset(&snapshot);
1273 editor
1274 .insert_snippet(
1275 &[editor_range],
1276 snippet.clone(),
1277 window,
1278 cx,
1279 )
1280 .ok();
1281 }
1282 }
1283 }
1284 }
1285 },
1286 ));
1287 if let Some(task_inventory) = project
1288 .read(cx)
1289 .task_store()
1290 .read(cx)
1291 .task_inventory()
1292 .cloned()
1293 {
1294 project_subscriptions.push(cx.observe_in(
1295 &task_inventory,
1296 window,
1297 |editor, _, window, cx| {
1298 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1299 },
1300 ));
1301 }
1302 }
1303 }
1304
1305 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1306
1307 let inlay_hint_settings =
1308 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1309 let focus_handle = cx.focus_handle();
1310 cx.on_focus(&focus_handle, window, Self::handle_focus)
1311 .detach();
1312 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1313 .detach();
1314 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1315 .detach();
1316 cx.on_blur(&focus_handle, window, Self::handle_blur)
1317 .detach();
1318
1319 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1320 Some(false)
1321 } else {
1322 None
1323 };
1324
1325 let mut code_action_providers = Vec::new();
1326 let mut load_uncommitted_diff = None;
1327 if let Some(project) = project.clone() {
1328 load_uncommitted_diff = Some(
1329 get_uncommitted_diff_for_buffer(
1330 &project,
1331 buffer.read(cx).all_buffers(),
1332 buffer.clone(),
1333 cx,
1334 )
1335 .shared(),
1336 );
1337 code_action_providers.push(Rc::new(project) as Rc<_>);
1338 }
1339
1340 let mut this = Self {
1341 focus_handle,
1342 show_cursor_when_unfocused: false,
1343 last_focused_descendant: None,
1344 buffer: buffer.clone(),
1345 display_map: display_map.clone(),
1346 selections,
1347 scroll_manager: ScrollManager::new(cx),
1348 columnar_selection_tail: None,
1349 add_selections_state: None,
1350 select_next_state: None,
1351 select_prev_state: None,
1352 selection_history: Default::default(),
1353 autoclose_regions: Default::default(),
1354 snippet_stack: Default::default(),
1355 select_larger_syntax_node_stack: Vec::new(),
1356 ime_transaction: Default::default(),
1357 active_diagnostics: None,
1358 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1359 inline_diagnostics_update: Task::ready(()),
1360 inline_diagnostics: Vec::new(),
1361 soft_wrap_mode_override,
1362 completion_provider: project.clone().map(|project| Box::new(project) as _),
1363 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1364 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1365 project,
1366 blink_manager: blink_manager.clone(),
1367 show_local_selections: true,
1368 show_scrollbars: true,
1369 mode,
1370 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1371 show_gutter: mode == EditorMode::Full,
1372 show_line_numbers: None,
1373 use_relative_line_numbers: None,
1374 show_git_diff_gutter: None,
1375 show_code_actions: None,
1376 show_runnables: None,
1377 show_wrap_guides: None,
1378 show_indent_guides,
1379 placeholder_text: None,
1380 highlight_order: 0,
1381 highlighted_rows: HashMap::default(),
1382 background_highlights: Default::default(),
1383 gutter_highlights: TreeMap::default(),
1384 scrollbar_marker_state: ScrollbarMarkerState::default(),
1385 active_indent_guides_state: ActiveIndentGuidesState::default(),
1386 nav_history: None,
1387 context_menu: RefCell::new(None),
1388 mouse_context_menu: None,
1389 completion_tasks: Default::default(),
1390 signature_help_state: SignatureHelpState::default(),
1391 auto_signature_help: None,
1392 find_all_references_task_sources: Vec::new(),
1393 next_completion_id: 0,
1394 next_inlay_id: 0,
1395 code_action_providers,
1396 available_code_actions: Default::default(),
1397 code_actions_task: Default::default(),
1398 selection_highlight_task: Default::default(),
1399 document_highlights_task: Default::default(),
1400 linked_editing_range_task: Default::default(),
1401 pending_rename: Default::default(),
1402 searchable: true,
1403 cursor_shape: EditorSettings::get_global(cx)
1404 .cursor_shape
1405 .unwrap_or_default(),
1406 current_line_highlight: None,
1407 autoindent_mode: Some(AutoindentMode::EachLine),
1408 collapse_matches: false,
1409 workspace: None,
1410 input_enabled: true,
1411 use_modal_editing: mode == EditorMode::Full,
1412 read_only: false,
1413 use_autoclose: true,
1414 use_auto_surround: true,
1415 auto_replace_emoji_shortcode: false,
1416 jsx_tag_auto_close_enabled_in_any_buffer: false,
1417 leader_peer_id: None,
1418 remote_id: None,
1419 hover_state: Default::default(),
1420 pending_mouse_down: None,
1421 hovered_link_state: Default::default(),
1422 edit_prediction_provider: None,
1423 active_inline_completion: None,
1424 stale_inline_completion_in_menu: None,
1425 edit_prediction_preview: EditPredictionPreview::Inactive {
1426 released_too_fast: false,
1427 },
1428 inline_diagnostics_enabled: mode == EditorMode::Full,
1429 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1430
1431 gutter_hovered: false,
1432 pixel_position_of_newest_cursor: None,
1433 last_bounds: None,
1434 last_position_map: None,
1435 expect_bounds_change: None,
1436 gutter_dimensions: GutterDimensions::default(),
1437 style: None,
1438 show_cursor_names: false,
1439 hovered_cursors: Default::default(),
1440 next_editor_action_id: EditorActionId::default(),
1441 editor_actions: Rc::default(),
1442 inline_completions_hidden_for_vim_mode: false,
1443 show_inline_completions_override: None,
1444 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1445 edit_prediction_settings: EditPredictionSettings::Disabled,
1446 edit_prediction_indent_conflict: false,
1447 edit_prediction_requires_modifier_in_indent_conflict: true,
1448 custom_context_menu: None,
1449 show_git_blame_gutter: false,
1450 show_git_blame_inline: false,
1451 show_selection_menu: None,
1452 show_git_blame_inline_delay_task: None,
1453 git_blame_inline_tooltip: None,
1454 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1455 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1456 .session
1457 .restore_unsaved_buffers,
1458 blame: None,
1459 blame_subscription: None,
1460 tasks: Default::default(),
1461 _subscriptions: vec![
1462 cx.observe(&buffer, Self::on_buffer_changed),
1463 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1464 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1465 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1466 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1467 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1468 cx.observe_window_activation(window, |editor, window, cx| {
1469 let active = window.is_window_active();
1470 editor.blink_manager.update(cx, |blink_manager, cx| {
1471 if active {
1472 blink_manager.enable(cx);
1473 } else {
1474 blink_manager.disable(cx);
1475 }
1476 });
1477 }),
1478 ],
1479 tasks_update_task: None,
1480 linked_edit_ranges: Default::default(),
1481 in_project_search: false,
1482 previous_search_ranges: None,
1483 breadcrumb_header: None,
1484 focused_block: None,
1485 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1486 addons: HashMap::default(),
1487 registered_buffers: HashMap::default(),
1488 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1489 selection_mark_mode: false,
1490 toggle_fold_multiple_buffers: Task::ready(()),
1491 serialize_selections: Task::ready(()),
1492 text_style_refinement: None,
1493 load_diff_task: load_uncommitted_diff,
1494 };
1495 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1496 this._subscriptions.extend(project_subscriptions);
1497
1498 this.end_selection(window, cx);
1499 this.scroll_manager.show_scrollbar(window, cx);
1500 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1501
1502 if mode == EditorMode::Full {
1503 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1504 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1505
1506 if this.git_blame_inline_enabled {
1507 this.git_blame_inline_enabled = true;
1508 this.start_git_blame_inline(false, window, cx);
1509 }
1510
1511 if let Some(buffer) = buffer.read(cx).as_singleton() {
1512 if let Some(project) = this.project.as_ref() {
1513 let handle = project.update(cx, |project, cx| {
1514 project.register_buffer_with_language_servers(&buffer, cx)
1515 });
1516 this.registered_buffers
1517 .insert(buffer.read(cx).remote_id(), handle);
1518 }
1519 }
1520 }
1521
1522 this.report_editor_event("Editor Opened", None, cx);
1523 this
1524 }
1525
1526 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1527 self.mouse_context_menu
1528 .as_ref()
1529 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1530 }
1531
1532 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1533 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1534 }
1535
1536 fn key_context_internal(
1537 &self,
1538 has_active_edit_prediction: bool,
1539 window: &Window,
1540 cx: &App,
1541 ) -> KeyContext {
1542 let mut key_context = KeyContext::new_with_defaults();
1543 key_context.add("Editor");
1544 let mode = match self.mode {
1545 EditorMode::SingleLine { .. } => "single_line",
1546 EditorMode::AutoHeight { .. } => "auto_height",
1547 EditorMode::Full => "full",
1548 };
1549
1550 if EditorSettings::jupyter_enabled(cx) {
1551 key_context.add("jupyter");
1552 }
1553
1554 key_context.set("mode", mode);
1555 if self.pending_rename.is_some() {
1556 key_context.add("renaming");
1557 }
1558
1559 match self.context_menu.borrow().as_ref() {
1560 Some(CodeContextMenu::Completions(_)) => {
1561 key_context.add("menu");
1562 key_context.add("showing_completions");
1563 }
1564 Some(CodeContextMenu::CodeActions(_)) => {
1565 key_context.add("menu");
1566 key_context.add("showing_code_actions")
1567 }
1568 None => {}
1569 }
1570
1571 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1572 if !self.focus_handle(cx).contains_focused(window, cx)
1573 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1574 {
1575 for addon in self.addons.values() {
1576 addon.extend_key_context(&mut key_context, cx)
1577 }
1578 }
1579
1580 if let Some(extension) = self
1581 .buffer
1582 .read(cx)
1583 .as_singleton()
1584 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1585 {
1586 key_context.set("extension", extension.to_string());
1587 }
1588
1589 if has_active_edit_prediction {
1590 if self.edit_prediction_in_conflict() {
1591 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1592 } else {
1593 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1594 key_context.add("copilot_suggestion");
1595 }
1596 }
1597
1598 if self.selection_mark_mode {
1599 key_context.add("selection_mode");
1600 }
1601
1602 key_context
1603 }
1604
1605 pub fn edit_prediction_in_conflict(&self) -> bool {
1606 if !self.show_edit_predictions_in_menu() {
1607 return false;
1608 }
1609
1610 let showing_completions = self
1611 .context_menu
1612 .borrow()
1613 .as_ref()
1614 .map_or(false, |context| {
1615 matches!(context, CodeContextMenu::Completions(_))
1616 });
1617
1618 showing_completions
1619 || self.edit_prediction_requires_modifier()
1620 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1621 // bindings to insert tab characters.
1622 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1623 }
1624
1625 pub fn accept_edit_prediction_keybind(
1626 &self,
1627 window: &Window,
1628 cx: &App,
1629 ) -> AcceptEditPredictionBinding {
1630 let key_context = self.key_context_internal(true, window, cx);
1631 let in_conflict = self.edit_prediction_in_conflict();
1632
1633 AcceptEditPredictionBinding(
1634 window
1635 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1636 .into_iter()
1637 .filter(|binding| {
1638 !in_conflict
1639 || binding
1640 .keystrokes()
1641 .first()
1642 .map_or(false, |keystroke| keystroke.modifiers.modified())
1643 })
1644 .rev()
1645 .min_by_key(|binding| {
1646 binding
1647 .keystrokes()
1648 .first()
1649 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1650 }),
1651 )
1652 }
1653
1654 pub fn new_file(
1655 workspace: &mut Workspace,
1656 _: &workspace::NewFile,
1657 window: &mut Window,
1658 cx: &mut Context<Workspace>,
1659 ) {
1660 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1661 "Failed to create buffer",
1662 window,
1663 cx,
1664 |e, _, _| match e.error_code() {
1665 ErrorCode::RemoteUpgradeRequired => Some(format!(
1666 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1667 e.error_tag("required").unwrap_or("the latest version")
1668 )),
1669 _ => None,
1670 },
1671 );
1672 }
1673
1674 pub fn new_in_workspace(
1675 workspace: &mut Workspace,
1676 window: &mut Window,
1677 cx: &mut Context<Workspace>,
1678 ) -> Task<Result<Entity<Editor>>> {
1679 let project = workspace.project().clone();
1680 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1681
1682 cx.spawn_in(window, |workspace, mut cx| async move {
1683 let buffer = create.await?;
1684 workspace.update_in(&mut cx, |workspace, window, cx| {
1685 let editor =
1686 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1687 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1688 editor
1689 })
1690 })
1691 }
1692
1693 fn new_file_vertical(
1694 workspace: &mut Workspace,
1695 _: &workspace::NewFileSplitVertical,
1696 window: &mut Window,
1697 cx: &mut Context<Workspace>,
1698 ) {
1699 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1700 }
1701
1702 fn new_file_horizontal(
1703 workspace: &mut Workspace,
1704 _: &workspace::NewFileSplitHorizontal,
1705 window: &mut Window,
1706 cx: &mut Context<Workspace>,
1707 ) {
1708 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1709 }
1710
1711 fn new_file_in_direction(
1712 workspace: &mut Workspace,
1713 direction: SplitDirection,
1714 window: &mut Window,
1715 cx: &mut Context<Workspace>,
1716 ) {
1717 let project = workspace.project().clone();
1718 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1719
1720 cx.spawn_in(window, |workspace, mut cx| async move {
1721 let buffer = create.await?;
1722 workspace.update_in(&mut cx, move |workspace, window, cx| {
1723 workspace.split_item(
1724 direction,
1725 Box::new(
1726 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1727 ),
1728 window,
1729 cx,
1730 )
1731 })?;
1732 anyhow::Ok(())
1733 })
1734 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1735 match e.error_code() {
1736 ErrorCode::RemoteUpgradeRequired => Some(format!(
1737 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1738 e.error_tag("required").unwrap_or("the latest version")
1739 )),
1740 _ => None,
1741 }
1742 });
1743 }
1744
1745 pub fn leader_peer_id(&self) -> Option<PeerId> {
1746 self.leader_peer_id
1747 }
1748
1749 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1750 &self.buffer
1751 }
1752
1753 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1754 self.workspace.as_ref()?.0.upgrade()
1755 }
1756
1757 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1758 self.buffer().read(cx).title(cx)
1759 }
1760
1761 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1762 let git_blame_gutter_max_author_length = self
1763 .render_git_blame_gutter(cx)
1764 .then(|| {
1765 if let Some(blame) = self.blame.as_ref() {
1766 let max_author_length =
1767 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1768 Some(max_author_length)
1769 } else {
1770 None
1771 }
1772 })
1773 .flatten();
1774
1775 EditorSnapshot {
1776 mode: self.mode,
1777 show_gutter: self.show_gutter,
1778 show_line_numbers: self.show_line_numbers,
1779 show_git_diff_gutter: self.show_git_diff_gutter,
1780 show_code_actions: self.show_code_actions,
1781 show_runnables: self.show_runnables,
1782 git_blame_gutter_max_author_length,
1783 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1784 scroll_anchor: self.scroll_manager.anchor(),
1785 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1786 placeholder_text: self.placeholder_text.clone(),
1787 is_focused: self.focus_handle.is_focused(window),
1788 current_line_highlight: self
1789 .current_line_highlight
1790 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1791 gutter_hovered: self.gutter_hovered,
1792 }
1793 }
1794
1795 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1796 self.buffer.read(cx).language_at(point, cx)
1797 }
1798
1799 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1800 self.buffer.read(cx).read(cx).file_at(point).cloned()
1801 }
1802
1803 pub fn active_excerpt(
1804 &self,
1805 cx: &App,
1806 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1807 self.buffer
1808 .read(cx)
1809 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1810 }
1811
1812 pub fn mode(&self) -> EditorMode {
1813 self.mode
1814 }
1815
1816 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1817 self.collaboration_hub.as_deref()
1818 }
1819
1820 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1821 self.collaboration_hub = Some(hub);
1822 }
1823
1824 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1825 self.in_project_search = in_project_search;
1826 }
1827
1828 pub fn set_custom_context_menu(
1829 &mut self,
1830 f: impl 'static
1831 + Fn(
1832 &mut Self,
1833 DisplayPoint,
1834 &mut Window,
1835 &mut Context<Self>,
1836 ) -> Option<Entity<ui::ContextMenu>>,
1837 ) {
1838 self.custom_context_menu = Some(Box::new(f))
1839 }
1840
1841 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1842 self.completion_provider = provider;
1843 }
1844
1845 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1846 self.semantics_provider.clone()
1847 }
1848
1849 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1850 self.semantics_provider = provider;
1851 }
1852
1853 pub fn set_edit_prediction_provider<T>(
1854 &mut self,
1855 provider: Option<Entity<T>>,
1856 window: &mut Window,
1857 cx: &mut Context<Self>,
1858 ) where
1859 T: EditPredictionProvider,
1860 {
1861 self.edit_prediction_provider =
1862 provider.map(|provider| RegisteredInlineCompletionProvider {
1863 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1864 if this.focus_handle.is_focused(window) {
1865 this.update_visible_inline_completion(window, cx);
1866 }
1867 }),
1868 provider: Arc::new(provider),
1869 });
1870 self.update_edit_prediction_settings(cx);
1871 self.refresh_inline_completion(false, false, window, cx);
1872 }
1873
1874 pub fn placeholder_text(&self) -> Option<&str> {
1875 self.placeholder_text.as_deref()
1876 }
1877
1878 pub fn set_placeholder_text(
1879 &mut self,
1880 placeholder_text: impl Into<Arc<str>>,
1881 cx: &mut Context<Self>,
1882 ) {
1883 let placeholder_text = Some(placeholder_text.into());
1884 if self.placeholder_text != placeholder_text {
1885 self.placeholder_text = placeholder_text;
1886 cx.notify();
1887 }
1888 }
1889
1890 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1891 self.cursor_shape = cursor_shape;
1892
1893 // Disrupt blink for immediate user feedback that the cursor shape has changed
1894 self.blink_manager.update(cx, BlinkManager::show_cursor);
1895
1896 cx.notify();
1897 }
1898
1899 pub fn set_current_line_highlight(
1900 &mut self,
1901 current_line_highlight: Option<CurrentLineHighlight>,
1902 ) {
1903 self.current_line_highlight = current_line_highlight;
1904 }
1905
1906 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1907 self.collapse_matches = collapse_matches;
1908 }
1909
1910 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1911 let buffers = self.buffer.read(cx).all_buffers();
1912 let Some(project) = self.project.as_ref() else {
1913 return;
1914 };
1915 project.update(cx, |project, cx| {
1916 for buffer in buffers {
1917 self.registered_buffers
1918 .entry(buffer.read(cx).remote_id())
1919 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1920 }
1921 })
1922 }
1923
1924 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1925 if self.collapse_matches {
1926 return range.start..range.start;
1927 }
1928 range.clone()
1929 }
1930
1931 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1932 if self.display_map.read(cx).clip_at_line_ends != clip {
1933 self.display_map
1934 .update(cx, |map, _| map.clip_at_line_ends = clip);
1935 }
1936 }
1937
1938 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1939 self.input_enabled = input_enabled;
1940 }
1941
1942 pub fn set_inline_completions_hidden_for_vim_mode(
1943 &mut self,
1944 hidden: bool,
1945 window: &mut Window,
1946 cx: &mut Context<Self>,
1947 ) {
1948 if hidden != self.inline_completions_hidden_for_vim_mode {
1949 self.inline_completions_hidden_for_vim_mode = hidden;
1950 if hidden {
1951 self.update_visible_inline_completion(window, cx);
1952 } else {
1953 self.refresh_inline_completion(true, false, window, cx);
1954 }
1955 }
1956 }
1957
1958 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1959 self.menu_inline_completions_policy = value;
1960 }
1961
1962 pub fn set_autoindent(&mut self, autoindent: bool) {
1963 if autoindent {
1964 self.autoindent_mode = Some(AutoindentMode::EachLine);
1965 } else {
1966 self.autoindent_mode = None;
1967 }
1968 }
1969
1970 pub fn read_only(&self, cx: &App) -> bool {
1971 self.read_only || self.buffer.read(cx).read_only()
1972 }
1973
1974 pub fn set_read_only(&mut self, read_only: bool) {
1975 self.read_only = read_only;
1976 }
1977
1978 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1979 self.use_autoclose = autoclose;
1980 }
1981
1982 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1983 self.use_auto_surround = auto_surround;
1984 }
1985
1986 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1987 self.auto_replace_emoji_shortcode = auto_replace;
1988 }
1989
1990 pub fn toggle_edit_predictions(
1991 &mut self,
1992 _: &ToggleEditPrediction,
1993 window: &mut Window,
1994 cx: &mut Context<Self>,
1995 ) {
1996 if self.show_inline_completions_override.is_some() {
1997 self.set_show_edit_predictions(None, window, cx);
1998 } else {
1999 let show_edit_predictions = !self.edit_predictions_enabled();
2000 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2001 }
2002 }
2003
2004 pub fn set_show_edit_predictions(
2005 &mut self,
2006 show_edit_predictions: Option<bool>,
2007 window: &mut Window,
2008 cx: &mut Context<Self>,
2009 ) {
2010 self.show_inline_completions_override = show_edit_predictions;
2011 self.update_edit_prediction_settings(cx);
2012
2013 if let Some(false) = show_edit_predictions {
2014 self.discard_inline_completion(false, cx);
2015 } else {
2016 self.refresh_inline_completion(false, true, window, cx);
2017 }
2018 }
2019
2020 fn inline_completions_disabled_in_scope(
2021 &self,
2022 buffer: &Entity<Buffer>,
2023 buffer_position: language::Anchor,
2024 cx: &App,
2025 ) -> bool {
2026 let snapshot = buffer.read(cx).snapshot();
2027 let settings = snapshot.settings_at(buffer_position, cx);
2028
2029 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2030 return false;
2031 };
2032
2033 scope.override_name().map_or(false, |scope_name| {
2034 settings
2035 .edit_predictions_disabled_in
2036 .iter()
2037 .any(|s| s == scope_name)
2038 })
2039 }
2040
2041 pub fn set_use_modal_editing(&mut self, to: bool) {
2042 self.use_modal_editing = to;
2043 }
2044
2045 pub fn use_modal_editing(&self) -> bool {
2046 self.use_modal_editing
2047 }
2048
2049 fn selections_did_change(
2050 &mut self,
2051 local: bool,
2052 old_cursor_position: &Anchor,
2053 show_completions: bool,
2054 window: &mut Window,
2055 cx: &mut Context<Self>,
2056 ) {
2057 window.invalidate_character_coordinates();
2058
2059 // Copy selections to primary selection buffer
2060 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2061 if local {
2062 let selections = self.selections.all::<usize>(cx);
2063 let buffer_handle = self.buffer.read(cx).read(cx);
2064
2065 let mut text = String::new();
2066 for (index, selection) in selections.iter().enumerate() {
2067 let text_for_selection = buffer_handle
2068 .text_for_range(selection.start..selection.end)
2069 .collect::<String>();
2070
2071 text.push_str(&text_for_selection);
2072 if index != selections.len() - 1 {
2073 text.push('\n');
2074 }
2075 }
2076
2077 if !text.is_empty() {
2078 cx.write_to_primary(ClipboardItem::new_string(text));
2079 }
2080 }
2081
2082 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2083 self.buffer.update(cx, |buffer, cx| {
2084 buffer.set_active_selections(
2085 &self.selections.disjoint_anchors(),
2086 self.selections.line_mode,
2087 self.cursor_shape,
2088 cx,
2089 )
2090 });
2091 }
2092 let display_map = self
2093 .display_map
2094 .update(cx, |display_map, cx| display_map.snapshot(cx));
2095 let buffer = &display_map.buffer_snapshot;
2096 self.add_selections_state = None;
2097 self.select_next_state = None;
2098 self.select_prev_state = None;
2099 self.select_larger_syntax_node_stack.clear();
2100 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2101 self.snippet_stack
2102 .invalidate(&self.selections.disjoint_anchors(), buffer);
2103 self.take_rename(false, window, cx);
2104
2105 let new_cursor_position = self.selections.newest_anchor().head();
2106
2107 self.push_to_nav_history(
2108 *old_cursor_position,
2109 Some(new_cursor_position.to_point(buffer)),
2110 cx,
2111 );
2112
2113 if local {
2114 let new_cursor_position = self.selections.newest_anchor().head();
2115 let mut context_menu = self.context_menu.borrow_mut();
2116 let completion_menu = match context_menu.as_ref() {
2117 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2118 _ => {
2119 *context_menu = None;
2120 None
2121 }
2122 };
2123 if let Some(buffer_id) = new_cursor_position.buffer_id {
2124 if !self.registered_buffers.contains_key(&buffer_id) {
2125 if let Some(project) = self.project.as_ref() {
2126 project.update(cx, |project, cx| {
2127 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2128 return;
2129 };
2130 self.registered_buffers.insert(
2131 buffer_id,
2132 project.register_buffer_with_language_servers(&buffer, cx),
2133 );
2134 })
2135 }
2136 }
2137 }
2138
2139 if let Some(completion_menu) = completion_menu {
2140 let cursor_position = new_cursor_position.to_offset(buffer);
2141 let (word_range, kind) =
2142 buffer.surrounding_word(completion_menu.initial_position, true);
2143 if kind == Some(CharKind::Word)
2144 && word_range.to_inclusive().contains(&cursor_position)
2145 {
2146 let mut completion_menu = completion_menu.clone();
2147 drop(context_menu);
2148
2149 let query = Self::completion_query(buffer, cursor_position);
2150 cx.spawn(move |this, mut cx| async move {
2151 completion_menu
2152 .filter(query.as_deref(), cx.background_executor().clone())
2153 .await;
2154
2155 this.update(&mut cx, |this, cx| {
2156 let mut context_menu = this.context_menu.borrow_mut();
2157 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2158 else {
2159 return;
2160 };
2161
2162 if menu.id > completion_menu.id {
2163 return;
2164 }
2165
2166 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2167 drop(context_menu);
2168 cx.notify();
2169 })
2170 })
2171 .detach();
2172
2173 if show_completions {
2174 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2175 }
2176 } else {
2177 drop(context_menu);
2178 self.hide_context_menu(window, cx);
2179 }
2180 } else {
2181 drop(context_menu);
2182 }
2183
2184 hide_hover(self, cx);
2185
2186 if old_cursor_position.to_display_point(&display_map).row()
2187 != new_cursor_position.to_display_point(&display_map).row()
2188 {
2189 self.available_code_actions.take();
2190 }
2191 self.refresh_code_actions(window, cx);
2192 self.refresh_document_highlights(cx);
2193 self.refresh_selected_text_highlights(window, cx);
2194 refresh_matching_bracket_highlights(self, window, cx);
2195 self.update_visible_inline_completion(window, cx);
2196 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2197 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2198 if self.git_blame_inline_enabled {
2199 self.start_inline_blame_timer(window, cx);
2200 }
2201 }
2202
2203 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2204 cx.emit(EditorEvent::SelectionsChanged { local });
2205
2206 let selections = &self.selections.disjoint;
2207 if selections.len() == 1 {
2208 cx.emit(SearchEvent::ActiveMatchChanged)
2209 }
2210 if local
2211 && self.is_singleton(cx)
2212 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2213 {
2214 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2215 let background_executor = cx.background_executor().clone();
2216 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2217 let snapshot = self.buffer().read(cx).snapshot(cx);
2218 let selections = selections.clone();
2219 self.serialize_selections = cx.background_spawn(async move {
2220 background_executor.timer(Duration::from_millis(100)).await;
2221 let selections = selections
2222 .iter()
2223 .map(|selection| {
2224 (
2225 selection.start.to_offset(&snapshot),
2226 selection.end.to_offset(&snapshot),
2227 )
2228 })
2229 .collect();
2230 DB.save_editor_selections(editor_id, workspace_id, selections)
2231 .await
2232 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2233 .log_err();
2234 });
2235 }
2236 }
2237
2238 cx.notify();
2239 }
2240
2241 pub fn sync_selections(
2242 &mut self,
2243 other: Entity<Editor>,
2244 cx: &mut Context<Self>,
2245 ) -> gpui::Subscription {
2246 let other_selections = other.read(cx).selections.disjoint.to_vec();
2247 self.selections.change_with(cx, |selections| {
2248 selections.select_anchors(other_selections);
2249 });
2250
2251 let other_subscription =
2252 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2253 EditorEvent::SelectionsChanged { local: true } => {
2254 let other_selections = other.read(cx).selections.disjoint.to_vec();
2255 if other_selections.is_empty() {
2256 return;
2257 }
2258 this.selections.change_with(cx, |selections| {
2259 selections.select_anchors(other_selections);
2260 });
2261 }
2262 _ => {}
2263 });
2264
2265 let this_subscription =
2266 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2267 EditorEvent::SelectionsChanged { local: true } => {
2268 let these_selections = this.selections.disjoint.to_vec();
2269 if these_selections.is_empty() {
2270 return;
2271 }
2272 other.update(cx, |other_editor, cx| {
2273 other_editor.selections.change_with(cx, |selections| {
2274 selections.select_anchors(these_selections);
2275 })
2276 });
2277 }
2278 _ => {}
2279 });
2280
2281 Subscription::join(other_subscription, this_subscription)
2282 }
2283
2284 pub fn change_selections<R>(
2285 &mut self,
2286 autoscroll: Option<Autoscroll>,
2287 window: &mut Window,
2288 cx: &mut Context<Self>,
2289 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2290 ) -> R {
2291 self.change_selections_inner(autoscroll, true, window, cx, change)
2292 }
2293
2294 fn change_selections_inner<R>(
2295 &mut self,
2296 autoscroll: Option<Autoscroll>,
2297 request_completions: bool,
2298 window: &mut Window,
2299 cx: &mut Context<Self>,
2300 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2301 ) -> R {
2302 let old_cursor_position = self.selections.newest_anchor().head();
2303 self.push_to_selection_history();
2304
2305 let (changed, result) = self.selections.change_with(cx, change);
2306
2307 if changed {
2308 if let Some(autoscroll) = autoscroll {
2309 self.request_autoscroll(autoscroll, cx);
2310 }
2311 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2312
2313 if self.should_open_signature_help_automatically(
2314 &old_cursor_position,
2315 self.signature_help_state.backspace_pressed(),
2316 cx,
2317 ) {
2318 self.show_signature_help(&ShowSignatureHelp, window, cx);
2319 }
2320 self.signature_help_state.set_backspace_pressed(false);
2321 }
2322
2323 result
2324 }
2325
2326 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2327 where
2328 I: IntoIterator<Item = (Range<S>, T)>,
2329 S: ToOffset,
2330 T: Into<Arc<str>>,
2331 {
2332 if self.read_only(cx) {
2333 return;
2334 }
2335
2336 self.buffer
2337 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2338 }
2339
2340 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2341 where
2342 I: IntoIterator<Item = (Range<S>, T)>,
2343 S: ToOffset,
2344 T: Into<Arc<str>>,
2345 {
2346 if self.read_only(cx) {
2347 return;
2348 }
2349
2350 self.buffer.update(cx, |buffer, cx| {
2351 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2352 });
2353 }
2354
2355 pub fn edit_with_block_indent<I, S, T>(
2356 &mut self,
2357 edits: I,
2358 original_indent_columns: Vec<Option<u32>>,
2359 cx: &mut Context<Self>,
2360 ) where
2361 I: IntoIterator<Item = (Range<S>, T)>,
2362 S: ToOffset,
2363 T: Into<Arc<str>>,
2364 {
2365 if self.read_only(cx) {
2366 return;
2367 }
2368
2369 self.buffer.update(cx, |buffer, cx| {
2370 buffer.edit(
2371 edits,
2372 Some(AutoindentMode::Block {
2373 original_indent_columns,
2374 }),
2375 cx,
2376 )
2377 });
2378 }
2379
2380 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2381 self.hide_context_menu(window, cx);
2382
2383 match phase {
2384 SelectPhase::Begin {
2385 position,
2386 add,
2387 click_count,
2388 } => self.begin_selection(position, add, click_count, window, cx),
2389 SelectPhase::BeginColumnar {
2390 position,
2391 goal_column,
2392 reset,
2393 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2394 SelectPhase::Extend {
2395 position,
2396 click_count,
2397 } => self.extend_selection(position, click_count, window, cx),
2398 SelectPhase::Update {
2399 position,
2400 goal_column,
2401 scroll_delta,
2402 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2403 SelectPhase::End => self.end_selection(window, cx),
2404 }
2405 }
2406
2407 fn extend_selection(
2408 &mut self,
2409 position: DisplayPoint,
2410 click_count: usize,
2411 window: &mut Window,
2412 cx: &mut Context<Self>,
2413 ) {
2414 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2415 let tail = self.selections.newest::<usize>(cx).tail();
2416 self.begin_selection(position, false, click_count, window, cx);
2417
2418 let position = position.to_offset(&display_map, Bias::Left);
2419 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2420
2421 let mut pending_selection = self
2422 .selections
2423 .pending_anchor()
2424 .expect("extend_selection not called with pending selection");
2425 if position >= tail {
2426 pending_selection.start = tail_anchor;
2427 } else {
2428 pending_selection.end = tail_anchor;
2429 pending_selection.reversed = true;
2430 }
2431
2432 let mut pending_mode = self.selections.pending_mode().unwrap();
2433 match &mut pending_mode {
2434 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2435 _ => {}
2436 }
2437
2438 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2439 s.set_pending(pending_selection, pending_mode)
2440 });
2441 }
2442
2443 fn begin_selection(
2444 &mut self,
2445 position: DisplayPoint,
2446 add: bool,
2447 click_count: usize,
2448 window: &mut Window,
2449 cx: &mut Context<Self>,
2450 ) {
2451 if !self.focus_handle.is_focused(window) {
2452 self.last_focused_descendant = None;
2453 window.focus(&self.focus_handle);
2454 }
2455
2456 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2457 let buffer = &display_map.buffer_snapshot;
2458 let newest_selection = self.selections.newest_anchor().clone();
2459 let position = display_map.clip_point(position, Bias::Left);
2460
2461 let start;
2462 let end;
2463 let mode;
2464 let mut auto_scroll;
2465 match click_count {
2466 1 => {
2467 start = buffer.anchor_before(position.to_point(&display_map));
2468 end = start;
2469 mode = SelectMode::Character;
2470 auto_scroll = true;
2471 }
2472 2 => {
2473 let range = movement::surrounding_word(&display_map, position);
2474 start = buffer.anchor_before(range.start.to_point(&display_map));
2475 end = buffer.anchor_before(range.end.to_point(&display_map));
2476 mode = SelectMode::Word(start..end);
2477 auto_scroll = true;
2478 }
2479 3 => {
2480 let position = display_map
2481 .clip_point(position, Bias::Left)
2482 .to_point(&display_map);
2483 let line_start = display_map.prev_line_boundary(position).0;
2484 let next_line_start = buffer.clip_point(
2485 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2486 Bias::Left,
2487 );
2488 start = buffer.anchor_before(line_start);
2489 end = buffer.anchor_before(next_line_start);
2490 mode = SelectMode::Line(start..end);
2491 auto_scroll = true;
2492 }
2493 _ => {
2494 start = buffer.anchor_before(0);
2495 end = buffer.anchor_before(buffer.len());
2496 mode = SelectMode::All;
2497 auto_scroll = false;
2498 }
2499 }
2500 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2501
2502 let point_to_delete: Option<usize> = {
2503 let selected_points: Vec<Selection<Point>> =
2504 self.selections.disjoint_in_range(start..end, cx);
2505
2506 if !add || click_count > 1 {
2507 None
2508 } else if !selected_points.is_empty() {
2509 Some(selected_points[0].id)
2510 } else {
2511 let clicked_point_already_selected =
2512 self.selections.disjoint.iter().find(|selection| {
2513 selection.start.to_point(buffer) == start.to_point(buffer)
2514 || selection.end.to_point(buffer) == end.to_point(buffer)
2515 });
2516
2517 clicked_point_already_selected.map(|selection| selection.id)
2518 }
2519 };
2520
2521 let selections_count = self.selections.count();
2522
2523 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2524 if let Some(point_to_delete) = point_to_delete {
2525 s.delete(point_to_delete);
2526
2527 if selections_count == 1 {
2528 s.set_pending_anchor_range(start..end, mode);
2529 }
2530 } else {
2531 if !add {
2532 s.clear_disjoint();
2533 } else if click_count > 1 {
2534 s.delete(newest_selection.id)
2535 }
2536
2537 s.set_pending_anchor_range(start..end, mode);
2538 }
2539 });
2540 }
2541
2542 fn begin_columnar_selection(
2543 &mut self,
2544 position: DisplayPoint,
2545 goal_column: u32,
2546 reset: bool,
2547 window: &mut Window,
2548 cx: &mut Context<Self>,
2549 ) {
2550 if !self.focus_handle.is_focused(window) {
2551 self.last_focused_descendant = None;
2552 window.focus(&self.focus_handle);
2553 }
2554
2555 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2556
2557 if reset {
2558 let pointer_position = display_map
2559 .buffer_snapshot
2560 .anchor_before(position.to_point(&display_map));
2561
2562 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2563 s.clear_disjoint();
2564 s.set_pending_anchor_range(
2565 pointer_position..pointer_position,
2566 SelectMode::Character,
2567 );
2568 });
2569 }
2570
2571 let tail = self.selections.newest::<Point>(cx).tail();
2572 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2573
2574 if !reset {
2575 self.select_columns(
2576 tail.to_display_point(&display_map),
2577 position,
2578 goal_column,
2579 &display_map,
2580 window,
2581 cx,
2582 );
2583 }
2584 }
2585
2586 fn update_selection(
2587 &mut self,
2588 position: DisplayPoint,
2589 goal_column: u32,
2590 scroll_delta: gpui::Point<f32>,
2591 window: &mut Window,
2592 cx: &mut Context<Self>,
2593 ) {
2594 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2595
2596 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2597 let tail = tail.to_display_point(&display_map);
2598 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2599 } else if let Some(mut pending) = self.selections.pending_anchor() {
2600 let buffer = self.buffer.read(cx).snapshot(cx);
2601 let head;
2602 let tail;
2603 let mode = self.selections.pending_mode().unwrap();
2604 match &mode {
2605 SelectMode::Character => {
2606 head = position.to_point(&display_map);
2607 tail = pending.tail().to_point(&buffer);
2608 }
2609 SelectMode::Word(original_range) => {
2610 let original_display_range = original_range.start.to_display_point(&display_map)
2611 ..original_range.end.to_display_point(&display_map);
2612 let original_buffer_range = original_display_range.start.to_point(&display_map)
2613 ..original_display_range.end.to_point(&display_map);
2614 if movement::is_inside_word(&display_map, position)
2615 || original_display_range.contains(&position)
2616 {
2617 let word_range = movement::surrounding_word(&display_map, position);
2618 if word_range.start < original_display_range.start {
2619 head = word_range.start.to_point(&display_map);
2620 } else {
2621 head = word_range.end.to_point(&display_map);
2622 }
2623 } else {
2624 head = position.to_point(&display_map);
2625 }
2626
2627 if head <= original_buffer_range.start {
2628 tail = original_buffer_range.end;
2629 } else {
2630 tail = original_buffer_range.start;
2631 }
2632 }
2633 SelectMode::Line(original_range) => {
2634 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2635
2636 let position = display_map
2637 .clip_point(position, Bias::Left)
2638 .to_point(&display_map);
2639 let line_start = display_map.prev_line_boundary(position).0;
2640 let next_line_start = buffer.clip_point(
2641 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2642 Bias::Left,
2643 );
2644
2645 if line_start < original_range.start {
2646 head = line_start
2647 } else {
2648 head = next_line_start
2649 }
2650
2651 if head <= original_range.start {
2652 tail = original_range.end;
2653 } else {
2654 tail = original_range.start;
2655 }
2656 }
2657 SelectMode::All => {
2658 return;
2659 }
2660 };
2661
2662 if head < tail {
2663 pending.start = buffer.anchor_before(head);
2664 pending.end = buffer.anchor_before(tail);
2665 pending.reversed = true;
2666 } else {
2667 pending.start = buffer.anchor_before(tail);
2668 pending.end = buffer.anchor_before(head);
2669 pending.reversed = false;
2670 }
2671
2672 self.change_selections(None, window, cx, |s| {
2673 s.set_pending(pending, mode);
2674 });
2675 } else {
2676 log::error!("update_selection dispatched with no pending selection");
2677 return;
2678 }
2679
2680 self.apply_scroll_delta(scroll_delta, window, cx);
2681 cx.notify();
2682 }
2683
2684 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2685 self.columnar_selection_tail.take();
2686 if self.selections.pending_anchor().is_some() {
2687 let selections = self.selections.all::<usize>(cx);
2688 self.change_selections(None, window, cx, |s| {
2689 s.select(selections);
2690 s.clear_pending();
2691 });
2692 }
2693 }
2694
2695 fn select_columns(
2696 &mut self,
2697 tail: DisplayPoint,
2698 head: DisplayPoint,
2699 goal_column: u32,
2700 display_map: &DisplaySnapshot,
2701 window: &mut Window,
2702 cx: &mut Context<Self>,
2703 ) {
2704 let start_row = cmp::min(tail.row(), head.row());
2705 let end_row = cmp::max(tail.row(), head.row());
2706 let start_column = cmp::min(tail.column(), goal_column);
2707 let end_column = cmp::max(tail.column(), goal_column);
2708 let reversed = start_column < tail.column();
2709
2710 let selection_ranges = (start_row.0..=end_row.0)
2711 .map(DisplayRow)
2712 .filter_map(|row| {
2713 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2714 let start = display_map
2715 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2716 .to_point(display_map);
2717 let end = display_map
2718 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2719 .to_point(display_map);
2720 if reversed {
2721 Some(end..start)
2722 } else {
2723 Some(start..end)
2724 }
2725 } else {
2726 None
2727 }
2728 })
2729 .collect::<Vec<_>>();
2730
2731 self.change_selections(None, window, cx, |s| {
2732 s.select_ranges(selection_ranges);
2733 });
2734 cx.notify();
2735 }
2736
2737 pub fn has_pending_nonempty_selection(&self) -> bool {
2738 let pending_nonempty_selection = match self.selections.pending_anchor() {
2739 Some(Selection { start, end, .. }) => start != end,
2740 None => false,
2741 };
2742
2743 pending_nonempty_selection
2744 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2745 }
2746
2747 pub fn has_pending_selection(&self) -> bool {
2748 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2749 }
2750
2751 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2752 self.selection_mark_mode = false;
2753
2754 if self.clear_expanded_diff_hunks(cx) {
2755 cx.notify();
2756 return;
2757 }
2758 if self.dismiss_menus_and_popups(true, window, cx) {
2759 return;
2760 }
2761
2762 if self.mode == EditorMode::Full
2763 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2764 {
2765 return;
2766 }
2767
2768 cx.propagate();
2769 }
2770
2771 pub fn dismiss_menus_and_popups(
2772 &mut self,
2773 is_user_requested: bool,
2774 window: &mut Window,
2775 cx: &mut Context<Self>,
2776 ) -> bool {
2777 if self.take_rename(false, window, cx).is_some() {
2778 return true;
2779 }
2780
2781 if hide_hover(self, cx) {
2782 return true;
2783 }
2784
2785 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2786 return true;
2787 }
2788
2789 if self.hide_context_menu(window, cx).is_some() {
2790 return true;
2791 }
2792
2793 if self.mouse_context_menu.take().is_some() {
2794 return true;
2795 }
2796
2797 if is_user_requested && self.discard_inline_completion(true, cx) {
2798 return true;
2799 }
2800
2801 if self.snippet_stack.pop().is_some() {
2802 return true;
2803 }
2804
2805 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2806 self.dismiss_diagnostics(cx);
2807 return true;
2808 }
2809
2810 false
2811 }
2812
2813 fn linked_editing_ranges_for(
2814 &self,
2815 selection: Range<text::Anchor>,
2816 cx: &App,
2817 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2818 if self.linked_edit_ranges.is_empty() {
2819 return None;
2820 }
2821 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2822 selection.end.buffer_id.and_then(|end_buffer_id| {
2823 if selection.start.buffer_id != Some(end_buffer_id) {
2824 return None;
2825 }
2826 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2827 let snapshot = buffer.read(cx).snapshot();
2828 self.linked_edit_ranges
2829 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2830 .map(|ranges| (ranges, snapshot, buffer))
2831 })?;
2832 use text::ToOffset as TO;
2833 // find offset from the start of current range to current cursor position
2834 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2835
2836 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2837 let start_difference = start_offset - start_byte_offset;
2838 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2839 let end_difference = end_offset - start_byte_offset;
2840 // Current range has associated linked ranges.
2841 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2842 for range in linked_ranges.iter() {
2843 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2844 let end_offset = start_offset + end_difference;
2845 let start_offset = start_offset + start_difference;
2846 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2847 continue;
2848 }
2849 if self.selections.disjoint_anchor_ranges().any(|s| {
2850 if s.start.buffer_id != selection.start.buffer_id
2851 || s.end.buffer_id != selection.end.buffer_id
2852 {
2853 return false;
2854 }
2855 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2856 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2857 }) {
2858 continue;
2859 }
2860 let start = buffer_snapshot.anchor_after(start_offset);
2861 let end = buffer_snapshot.anchor_after(end_offset);
2862 linked_edits
2863 .entry(buffer.clone())
2864 .or_default()
2865 .push(start..end);
2866 }
2867 Some(linked_edits)
2868 }
2869
2870 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2871 let text: Arc<str> = text.into();
2872
2873 if self.read_only(cx) {
2874 return;
2875 }
2876
2877 let selections = self.selections.all_adjusted(cx);
2878 let mut bracket_inserted = false;
2879 let mut edits = Vec::new();
2880 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2881 let mut new_selections = Vec::with_capacity(selections.len());
2882 let mut new_autoclose_regions = Vec::new();
2883 let snapshot = self.buffer.read(cx).read(cx);
2884
2885 for (selection, autoclose_region) in
2886 self.selections_with_autoclose_regions(selections, &snapshot)
2887 {
2888 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2889 // Determine if the inserted text matches the opening or closing
2890 // bracket of any of this language's bracket pairs.
2891 let mut bracket_pair = None;
2892 let mut is_bracket_pair_start = false;
2893 let mut is_bracket_pair_end = false;
2894 if !text.is_empty() {
2895 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2896 // and they are removing the character that triggered IME popup.
2897 for (pair, enabled) in scope.brackets() {
2898 if !pair.close && !pair.surround {
2899 continue;
2900 }
2901
2902 if enabled && pair.start.ends_with(text.as_ref()) {
2903 let prefix_len = pair.start.len() - text.len();
2904 let preceding_text_matches_prefix = prefix_len == 0
2905 || (selection.start.column >= (prefix_len as u32)
2906 && snapshot.contains_str_at(
2907 Point::new(
2908 selection.start.row,
2909 selection.start.column - (prefix_len as u32),
2910 ),
2911 &pair.start[..prefix_len],
2912 ));
2913 if preceding_text_matches_prefix {
2914 bracket_pair = Some(pair.clone());
2915 is_bracket_pair_start = true;
2916 break;
2917 }
2918 }
2919 if pair.end.as_str() == text.as_ref() {
2920 bracket_pair = Some(pair.clone());
2921 is_bracket_pair_end = true;
2922 break;
2923 }
2924 }
2925 }
2926
2927 if let Some(bracket_pair) = bracket_pair {
2928 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2929 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2930 let auto_surround =
2931 self.use_auto_surround && snapshot_settings.use_auto_surround;
2932 if selection.is_empty() {
2933 if is_bracket_pair_start {
2934 // If the inserted text is a suffix of an opening bracket and the
2935 // selection is preceded by the rest of the opening bracket, then
2936 // insert the closing bracket.
2937 let following_text_allows_autoclose = snapshot
2938 .chars_at(selection.start)
2939 .next()
2940 .map_or(true, |c| scope.should_autoclose_before(c));
2941
2942 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2943 && bracket_pair.start.len() == 1
2944 {
2945 let target = bracket_pair.start.chars().next().unwrap();
2946 let current_line_count = snapshot
2947 .reversed_chars_at(selection.start)
2948 .take_while(|&c| c != '\n')
2949 .filter(|&c| c == target)
2950 .count();
2951 current_line_count % 2 == 1
2952 } else {
2953 false
2954 };
2955
2956 if autoclose
2957 && bracket_pair.close
2958 && following_text_allows_autoclose
2959 && !is_closing_quote
2960 {
2961 let anchor = snapshot.anchor_before(selection.end);
2962 new_selections.push((selection.map(|_| anchor), text.len()));
2963 new_autoclose_regions.push((
2964 anchor,
2965 text.len(),
2966 selection.id,
2967 bracket_pair.clone(),
2968 ));
2969 edits.push((
2970 selection.range(),
2971 format!("{}{}", text, bracket_pair.end).into(),
2972 ));
2973 bracket_inserted = true;
2974 continue;
2975 }
2976 }
2977
2978 if let Some(region) = autoclose_region {
2979 // If the selection is followed by an auto-inserted closing bracket,
2980 // then don't insert that closing bracket again; just move the selection
2981 // past the closing bracket.
2982 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2983 && text.as_ref() == region.pair.end.as_str();
2984 if should_skip {
2985 let anchor = snapshot.anchor_after(selection.end);
2986 new_selections
2987 .push((selection.map(|_| anchor), region.pair.end.len()));
2988 continue;
2989 }
2990 }
2991
2992 let always_treat_brackets_as_autoclosed = snapshot
2993 .language_settings_at(selection.start, cx)
2994 .always_treat_brackets_as_autoclosed;
2995 if always_treat_brackets_as_autoclosed
2996 && is_bracket_pair_end
2997 && snapshot.contains_str_at(selection.end, text.as_ref())
2998 {
2999 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3000 // and the inserted text is a closing bracket and the selection is followed
3001 // by the closing bracket then move the selection past the closing bracket.
3002 let anchor = snapshot.anchor_after(selection.end);
3003 new_selections.push((selection.map(|_| anchor), text.len()));
3004 continue;
3005 }
3006 }
3007 // If an opening bracket is 1 character long and is typed while
3008 // text is selected, then surround that text with the bracket pair.
3009 else if auto_surround
3010 && bracket_pair.surround
3011 && is_bracket_pair_start
3012 && bracket_pair.start.chars().count() == 1
3013 {
3014 edits.push((selection.start..selection.start, text.clone()));
3015 edits.push((
3016 selection.end..selection.end,
3017 bracket_pair.end.as_str().into(),
3018 ));
3019 bracket_inserted = true;
3020 new_selections.push((
3021 Selection {
3022 id: selection.id,
3023 start: snapshot.anchor_after(selection.start),
3024 end: snapshot.anchor_before(selection.end),
3025 reversed: selection.reversed,
3026 goal: selection.goal,
3027 },
3028 0,
3029 ));
3030 continue;
3031 }
3032 }
3033 }
3034
3035 if self.auto_replace_emoji_shortcode
3036 && selection.is_empty()
3037 && text.as_ref().ends_with(':')
3038 {
3039 if let Some(possible_emoji_short_code) =
3040 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3041 {
3042 if !possible_emoji_short_code.is_empty() {
3043 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3044 let emoji_shortcode_start = Point::new(
3045 selection.start.row,
3046 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3047 );
3048
3049 // Remove shortcode from buffer
3050 edits.push((
3051 emoji_shortcode_start..selection.start,
3052 "".to_string().into(),
3053 ));
3054 new_selections.push((
3055 Selection {
3056 id: selection.id,
3057 start: snapshot.anchor_after(emoji_shortcode_start),
3058 end: snapshot.anchor_before(selection.start),
3059 reversed: selection.reversed,
3060 goal: selection.goal,
3061 },
3062 0,
3063 ));
3064
3065 // Insert emoji
3066 let selection_start_anchor = snapshot.anchor_after(selection.start);
3067 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3068 edits.push((selection.start..selection.end, emoji.to_string().into()));
3069
3070 continue;
3071 }
3072 }
3073 }
3074 }
3075
3076 // If not handling any auto-close operation, then just replace the selected
3077 // text with the given input and move the selection to the end of the
3078 // newly inserted text.
3079 let anchor = snapshot.anchor_after(selection.end);
3080 if !self.linked_edit_ranges.is_empty() {
3081 let start_anchor = snapshot.anchor_before(selection.start);
3082
3083 let is_word_char = text.chars().next().map_or(true, |char| {
3084 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3085 classifier.is_word(char)
3086 });
3087
3088 if is_word_char {
3089 if let Some(ranges) = self
3090 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3091 {
3092 for (buffer, edits) in ranges {
3093 linked_edits
3094 .entry(buffer.clone())
3095 .or_default()
3096 .extend(edits.into_iter().map(|range| (range, text.clone())));
3097 }
3098 }
3099 }
3100 }
3101
3102 new_selections.push((selection.map(|_| anchor), 0));
3103 edits.push((selection.start..selection.end, text.clone()));
3104 }
3105
3106 drop(snapshot);
3107
3108 self.transact(window, cx, |this, window, cx| {
3109 let initial_buffer_versions =
3110 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3111
3112 this.buffer.update(cx, |buffer, cx| {
3113 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3114 });
3115 for (buffer, edits) in linked_edits {
3116 buffer.update(cx, |buffer, cx| {
3117 let snapshot = buffer.snapshot();
3118 let edits = edits
3119 .into_iter()
3120 .map(|(range, text)| {
3121 use text::ToPoint as TP;
3122 let end_point = TP::to_point(&range.end, &snapshot);
3123 let start_point = TP::to_point(&range.start, &snapshot);
3124 (start_point..end_point, text)
3125 })
3126 .sorted_by_key(|(range, _)| range.start)
3127 .collect::<Vec<_>>();
3128 buffer.edit(edits, None, cx);
3129 })
3130 }
3131 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3132 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3133 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3134 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3135 .zip(new_selection_deltas)
3136 .map(|(selection, delta)| Selection {
3137 id: selection.id,
3138 start: selection.start + delta,
3139 end: selection.end + delta,
3140 reversed: selection.reversed,
3141 goal: SelectionGoal::None,
3142 })
3143 .collect::<Vec<_>>();
3144
3145 let mut i = 0;
3146 for (position, delta, selection_id, pair) in new_autoclose_regions {
3147 let position = position.to_offset(&map.buffer_snapshot) + delta;
3148 let start = map.buffer_snapshot.anchor_before(position);
3149 let end = map.buffer_snapshot.anchor_after(position);
3150 while let Some(existing_state) = this.autoclose_regions.get(i) {
3151 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3152 Ordering::Less => i += 1,
3153 Ordering::Greater => break,
3154 Ordering::Equal => {
3155 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3156 Ordering::Less => i += 1,
3157 Ordering::Equal => break,
3158 Ordering::Greater => break,
3159 }
3160 }
3161 }
3162 }
3163 this.autoclose_regions.insert(
3164 i,
3165 AutocloseRegion {
3166 selection_id,
3167 range: start..end,
3168 pair,
3169 },
3170 );
3171 }
3172
3173 let had_active_inline_completion = this.has_active_inline_completion();
3174 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3175 s.select(new_selections)
3176 });
3177
3178 if !bracket_inserted {
3179 if let Some(on_type_format_task) =
3180 this.trigger_on_type_formatting(text.to_string(), window, cx)
3181 {
3182 on_type_format_task.detach_and_log_err(cx);
3183 }
3184 }
3185
3186 let editor_settings = EditorSettings::get_global(cx);
3187 if bracket_inserted
3188 && (editor_settings.auto_signature_help
3189 || editor_settings.show_signature_help_after_edits)
3190 {
3191 this.show_signature_help(&ShowSignatureHelp, window, cx);
3192 }
3193
3194 let trigger_in_words =
3195 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3196 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3197 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3198 this.refresh_inline_completion(true, false, window, cx);
3199 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3200 });
3201 }
3202
3203 fn find_possible_emoji_shortcode_at_position(
3204 snapshot: &MultiBufferSnapshot,
3205 position: Point,
3206 ) -> Option<String> {
3207 let mut chars = Vec::new();
3208 let mut found_colon = false;
3209 for char in snapshot.reversed_chars_at(position).take(100) {
3210 // Found a possible emoji shortcode in the middle of the buffer
3211 if found_colon {
3212 if char.is_whitespace() {
3213 chars.reverse();
3214 return Some(chars.iter().collect());
3215 }
3216 // If the previous character is not a whitespace, we are in the middle of a word
3217 // and we only want to complete the shortcode if the word is made up of other emojis
3218 let mut containing_word = String::new();
3219 for ch in snapshot
3220 .reversed_chars_at(position)
3221 .skip(chars.len() + 1)
3222 .take(100)
3223 {
3224 if ch.is_whitespace() {
3225 break;
3226 }
3227 containing_word.push(ch);
3228 }
3229 let containing_word = containing_word.chars().rev().collect::<String>();
3230 if util::word_consists_of_emojis(containing_word.as_str()) {
3231 chars.reverse();
3232 return Some(chars.iter().collect());
3233 }
3234 }
3235
3236 if char.is_whitespace() || !char.is_ascii() {
3237 return None;
3238 }
3239 if char == ':' {
3240 found_colon = true;
3241 } else {
3242 chars.push(char);
3243 }
3244 }
3245 // Found a possible emoji shortcode at the beginning of the buffer
3246 chars.reverse();
3247 Some(chars.iter().collect())
3248 }
3249
3250 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3251 self.transact(window, cx, |this, window, cx| {
3252 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3253 let selections = this.selections.all::<usize>(cx);
3254 let multi_buffer = this.buffer.read(cx);
3255 let buffer = multi_buffer.snapshot(cx);
3256 selections
3257 .iter()
3258 .map(|selection| {
3259 let start_point = selection.start.to_point(&buffer);
3260 let mut indent =
3261 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3262 indent.len = cmp::min(indent.len, start_point.column);
3263 let start = selection.start;
3264 let end = selection.end;
3265 let selection_is_empty = start == end;
3266 let language_scope = buffer.language_scope_at(start);
3267 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3268 &language_scope
3269 {
3270 let insert_extra_newline =
3271 insert_extra_newline_brackets(&buffer, start..end, language)
3272 || insert_extra_newline_tree_sitter(&buffer, start..end);
3273
3274 // Comment extension on newline is allowed only for cursor selections
3275 let comment_delimiter = maybe!({
3276 if !selection_is_empty {
3277 return None;
3278 }
3279
3280 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3281 return None;
3282 }
3283
3284 let delimiters = language.line_comment_prefixes();
3285 let max_len_of_delimiter =
3286 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3287 let (snapshot, range) =
3288 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3289
3290 let mut index_of_first_non_whitespace = 0;
3291 let comment_candidate = snapshot
3292 .chars_for_range(range)
3293 .skip_while(|c| {
3294 let should_skip = c.is_whitespace();
3295 if should_skip {
3296 index_of_first_non_whitespace += 1;
3297 }
3298 should_skip
3299 })
3300 .take(max_len_of_delimiter)
3301 .collect::<String>();
3302 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3303 comment_candidate.starts_with(comment_prefix.as_ref())
3304 })?;
3305 let cursor_is_placed_after_comment_marker =
3306 index_of_first_non_whitespace + comment_prefix.len()
3307 <= start_point.column as usize;
3308 if cursor_is_placed_after_comment_marker {
3309 Some(comment_prefix.clone())
3310 } else {
3311 None
3312 }
3313 });
3314 (comment_delimiter, insert_extra_newline)
3315 } else {
3316 (None, false)
3317 };
3318
3319 let capacity_for_delimiter = comment_delimiter
3320 .as_deref()
3321 .map(str::len)
3322 .unwrap_or_default();
3323 let mut new_text =
3324 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3325 new_text.push('\n');
3326 new_text.extend(indent.chars());
3327 if let Some(delimiter) = &comment_delimiter {
3328 new_text.push_str(delimiter);
3329 }
3330 if insert_extra_newline {
3331 new_text = new_text.repeat(2);
3332 }
3333
3334 let anchor = buffer.anchor_after(end);
3335 let new_selection = selection.map(|_| anchor);
3336 (
3337 (start..end, new_text),
3338 (insert_extra_newline, new_selection),
3339 )
3340 })
3341 .unzip()
3342 };
3343
3344 this.edit_with_autoindent(edits, cx);
3345 let buffer = this.buffer.read(cx).snapshot(cx);
3346 let new_selections = selection_fixup_info
3347 .into_iter()
3348 .map(|(extra_newline_inserted, new_selection)| {
3349 let mut cursor = new_selection.end.to_point(&buffer);
3350 if extra_newline_inserted {
3351 cursor.row -= 1;
3352 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3353 }
3354 new_selection.map(|_| cursor)
3355 })
3356 .collect();
3357
3358 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3359 s.select(new_selections)
3360 });
3361 this.refresh_inline_completion(true, false, window, cx);
3362 });
3363 }
3364
3365 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3366 let buffer = self.buffer.read(cx);
3367 let snapshot = buffer.snapshot(cx);
3368
3369 let mut edits = Vec::new();
3370 let mut rows = Vec::new();
3371
3372 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3373 let cursor = selection.head();
3374 let row = cursor.row;
3375
3376 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3377
3378 let newline = "\n".to_string();
3379 edits.push((start_of_line..start_of_line, newline));
3380
3381 rows.push(row + rows_inserted as u32);
3382 }
3383
3384 self.transact(window, cx, |editor, window, cx| {
3385 editor.edit(edits, cx);
3386
3387 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3388 let mut index = 0;
3389 s.move_cursors_with(|map, _, _| {
3390 let row = rows[index];
3391 index += 1;
3392
3393 let point = Point::new(row, 0);
3394 let boundary = map.next_line_boundary(point).1;
3395 let clipped = map.clip_point(boundary, Bias::Left);
3396
3397 (clipped, SelectionGoal::None)
3398 });
3399 });
3400
3401 let mut indent_edits = Vec::new();
3402 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3403 for row in rows {
3404 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3405 for (row, indent) in indents {
3406 if indent.len == 0 {
3407 continue;
3408 }
3409
3410 let text = match indent.kind {
3411 IndentKind::Space => " ".repeat(indent.len as usize),
3412 IndentKind::Tab => "\t".repeat(indent.len as usize),
3413 };
3414 let point = Point::new(row.0, 0);
3415 indent_edits.push((point..point, text));
3416 }
3417 }
3418 editor.edit(indent_edits, cx);
3419 });
3420 }
3421
3422 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3423 let buffer = self.buffer.read(cx);
3424 let snapshot = buffer.snapshot(cx);
3425
3426 let mut edits = Vec::new();
3427 let mut rows = Vec::new();
3428 let mut rows_inserted = 0;
3429
3430 for selection in self.selections.all_adjusted(cx) {
3431 let cursor = selection.head();
3432 let row = cursor.row;
3433
3434 let point = Point::new(row + 1, 0);
3435 let start_of_line = snapshot.clip_point(point, Bias::Left);
3436
3437 let newline = "\n".to_string();
3438 edits.push((start_of_line..start_of_line, newline));
3439
3440 rows_inserted += 1;
3441 rows.push(row + rows_inserted);
3442 }
3443
3444 self.transact(window, cx, |editor, window, cx| {
3445 editor.edit(edits, cx);
3446
3447 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3448 let mut index = 0;
3449 s.move_cursors_with(|map, _, _| {
3450 let row = rows[index];
3451 index += 1;
3452
3453 let point = Point::new(row, 0);
3454 let boundary = map.next_line_boundary(point).1;
3455 let clipped = map.clip_point(boundary, Bias::Left);
3456
3457 (clipped, SelectionGoal::None)
3458 });
3459 });
3460
3461 let mut indent_edits = Vec::new();
3462 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3463 for row in rows {
3464 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3465 for (row, indent) in indents {
3466 if indent.len == 0 {
3467 continue;
3468 }
3469
3470 let text = match indent.kind {
3471 IndentKind::Space => " ".repeat(indent.len as usize),
3472 IndentKind::Tab => "\t".repeat(indent.len as usize),
3473 };
3474 let point = Point::new(row.0, 0);
3475 indent_edits.push((point..point, text));
3476 }
3477 }
3478 editor.edit(indent_edits, cx);
3479 });
3480 }
3481
3482 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3483 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3484 original_indent_columns: Vec::new(),
3485 });
3486 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3487 }
3488
3489 fn insert_with_autoindent_mode(
3490 &mut self,
3491 text: &str,
3492 autoindent_mode: Option<AutoindentMode>,
3493 window: &mut Window,
3494 cx: &mut Context<Self>,
3495 ) {
3496 if self.read_only(cx) {
3497 return;
3498 }
3499
3500 let text: Arc<str> = text.into();
3501 self.transact(window, cx, |this, window, cx| {
3502 let old_selections = this.selections.all_adjusted(cx);
3503 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3504 let anchors = {
3505 let snapshot = buffer.read(cx);
3506 old_selections
3507 .iter()
3508 .map(|s| {
3509 let anchor = snapshot.anchor_after(s.head());
3510 s.map(|_| anchor)
3511 })
3512 .collect::<Vec<_>>()
3513 };
3514 buffer.edit(
3515 old_selections
3516 .iter()
3517 .map(|s| (s.start..s.end, text.clone())),
3518 autoindent_mode,
3519 cx,
3520 );
3521 anchors
3522 });
3523
3524 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3525 s.select_anchors(selection_anchors);
3526 });
3527
3528 cx.notify();
3529 });
3530 }
3531
3532 fn trigger_completion_on_input(
3533 &mut self,
3534 text: &str,
3535 trigger_in_words: bool,
3536 window: &mut Window,
3537 cx: &mut Context<Self>,
3538 ) {
3539 if self.is_completion_trigger(text, trigger_in_words, cx) {
3540 self.show_completions(
3541 &ShowCompletions {
3542 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3543 },
3544 window,
3545 cx,
3546 );
3547 } else {
3548 self.hide_context_menu(window, cx);
3549 }
3550 }
3551
3552 fn is_completion_trigger(
3553 &self,
3554 text: &str,
3555 trigger_in_words: bool,
3556 cx: &mut Context<Self>,
3557 ) -> bool {
3558 let position = self.selections.newest_anchor().head();
3559 let multibuffer = self.buffer.read(cx);
3560 let Some(buffer) = position
3561 .buffer_id
3562 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3563 else {
3564 return false;
3565 };
3566
3567 if let Some(completion_provider) = &self.completion_provider {
3568 completion_provider.is_completion_trigger(
3569 &buffer,
3570 position.text_anchor,
3571 text,
3572 trigger_in_words,
3573 cx,
3574 )
3575 } else {
3576 false
3577 }
3578 }
3579
3580 /// If any empty selections is touching the start of its innermost containing autoclose
3581 /// region, expand it to select the brackets.
3582 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3583 let selections = self.selections.all::<usize>(cx);
3584 let buffer = self.buffer.read(cx).read(cx);
3585 let new_selections = self
3586 .selections_with_autoclose_regions(selections, &buffer)
3587 .map(|(mut selection, region)| {
3588 if !selection.is_empty() {
3589 return selection;
3590 }
3591
3592 if let Some(region) = region {
3593 let mut range = region.range.to_offset(&buffer);
3594 if selection.start == range.start && range.start >= region.pair.start.len() {
3595 range.start -= region.pair.start.len();
3596 if buffer.contains_str_at(range.start, ®ion.pair.start)
3597 && buffer.contains_str_at(range.end, ®ion.pair.end)
3598 {
3599 range.end += region.pair.end.len();
3600 selection.start = range.start;
3601 selection.end = range.end;
3602
3603 return selection;
3604 }
3605 }
3606 }
3607
3608 let always_treat_brackets_as_autoclosed = buffer
3609 .language_settings_at(selection.start, cx)
3610 .always_treat_brackets_as_autoclosed;
3611
3612 if !always_treat_brackets_as_autoclosed {
3613 return selection;
3614 }
3615
3616 if let Some(scope) = buffer.language_scope_at(selection.start) {
3617 for (pair, enabled) in scope.brackets() {
3618 if !enabled || !pair.close {
3619 continue;
3620 }
3621
3622 if buffer.contains_str_at(selection.start, &pair.end) {
3623 let pair_start_len = pair.start.len();
3624 if buffer.contains_str_at(
3625 selection.start.saturating_sub(pair_start_len),
3626 &pair.start,
3627 ) {
3628 selection.start -= pair_start_len;
3629 selection.end += pair.end.len();
3630
3631 return selection;
3632 }
3633 }
3634 }
3635 }
3636
3637 selection
3638 })
3639 .collect();
3640
3641 drop(buffer);
3642 self.change_selections(None, window, cx, |selections| {
3643 selections.select(new_selections)
3644 });
3645 }
3646
3647 /// Iterate the given selections, and for each one, find the smallest surrounding
3648 /// autoclose region. This uses the ordering of the selections and the autoclose
3649 /// regions to avoid repeated comparisons.
3650 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3651 &'a self,
3652 selections: impl IntoIterator<Item = Selection<D>>,
3653 buffer: &'a MultiBufferSnapshot,
3654 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3655 let mut i = 0;
3656 let mut regions = self.autoclose_regions.as_slice();
3657 selections.into_iter().map(move |selection| {
3658 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3659
3660 let mut enclosing = None;
3661 while let Some(pair_state) = regions.get(i) {
3662 if pair_state.range.end.to_offset(buffer) < range.start {
3663 regions = ®ions[i + 1..];
3664 i = 0;
3665 } else if pair_state.range.start.to_offset(buffer) > range.end {
3666 break;
3667 } else {
3668 if pair_state.selection_id == selection.id {
3669 enclosing = Some(pair_state);
3670 }
3671 i += 1;
3672 }
3673 }
3674
3675 (selection, enclosing)
3676 })
3677 }
3678
3679 /// Remove any autoclose regions that no longer contain their selection.
3680 fn invalidate_autoclose_regions(
3681 &mut self,
3682 mut selections: &[Selection<Anchor>],
3683 buffer: &MultiBufferSnapshot,
3684 ) {
3685 self.autoclose_regions.retain(|state| {
3686 let mut i = 0;
3687 while let Some(selection) = selections.get(i) {
3688 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3689 selections = &selections[1..];
3690 continue;
3691 }
3692 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3693 break;
3694 }
3695 if selection.id == state.selection_id {
3696 return true;
3697 } else {
3698 i += 1;
3699 }
3700 }
3701 false
3702 });
3703 }
3704
3705 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3706 let offset = position.to_offset(buffer);
3707 let (word_range, kind) = buffer.surrounding_word(offset, true);
3708 if offset > word_range.start && kind == Some(CharKind::Word) {
3709 Some(
3710 buffer
3711 .text_for_range(word_range.start..offset)
3712 .collect::<String>(),
3713 )
3714 } else {
3715 None
3716 }
3717 }
3718
3719 pub fn toggle_inlay_hints(
3720 &mut self,
3721 _: &ToggleInlayHints,
3722 _: &mut Window,
3723 cx: &mut Context<Self>,
3724 ) {
3725 self.refresh_inlay_hints(
3726 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3727 cx,
3728 );
3729 }
3730
3731 pub fn inlay_hints_enabled(&self) -> bool {
3732 self.inlay_hint_cache.enabled
3733 }
3734
3735 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3736 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3737 return;
3738 }
3739
3740 let reason_description = reason.description();
3741 let ignore_debounce = matches!(
3742 reason,
3743 InlayHintRefreshReason::SettingsChange(_)
3744 | InlayHintRefreshReason::Toggle(_)
3745 | InlayHintRefreshReason::ExcerptsRemoved(_)
3746 | InlayHintRefreshReason::ModifiersChanged(_)
3747 );
3748 let (invalidate_cache, required_languages) = match reason {
3749 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3750 match self.inlay_hint_cache.modifiers_override(enabled) {
3751 Some(enabled) => {
3752 if enabled {
3753 (InvalidationStrategy::RefreshRequested, None)
3754 } else {
3755 self.splice_inlays(
3756 &self
3757 .visible_inlay_hints(cx)
3758 .iter()
3759 .map(|inlay| inlay.id)
3760 .collect::<Vec<InlayId>>(),
3761 Vec::new(),
3762 cx,
3763 );
3764 return;
3765 }
3766 }
3767 None => return,
3768 }
3769 }
3770 InlayHintRefreshReason::Toggle(enabled) => {
3771 if self.inlay_hint_cache.toggle(enabled) {
3772 if enabled {
3773 (InvalidationStrategy::RefreshRequested, None)
3774 } else {
3775 self.splice_inlays(
3776 &self
3777 .visible_inlay_hints(cx)
3778 .iter()
3779 .map(|inlay| inlay.id)
3780 .collect::<Vec<InlayId>>(),
3781 Vec::new(),
3782 cx,
3783 );
3784 return;
3785 }
3786 } else {
3787 return;
3788 }
3789 }
3790 InlayHintRefreshReason::SettingsChange(new_settings) => {
3791 match self.inlay_hint_cache.update_settings(
3792 &self.buffer,
3793 new_settings,
3794 self.visible_inlay_hints(cx),
3795 cx,
3796 ) {
3797 ControlFlow::Break(Some(InlaySplice {
3798 to_remove,
3799 to_insert,
3800 })) => {
3801 self.splice_inlays(&to_remove, to_insert, cx);
3802 return;
3803 }
3804 ControlFlow::Break(None) => return,
3805 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3806 }
3807 }
3808 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3809 if let Some(InlaySplice {
3810 to_remove,
3811 to_insert,
3812 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3813 {
3814 self.splice_inlays(&to_remove, to_insert, cx);
3815 }
3816 return;
3817 }
3818 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3819 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3820 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3821 }
3822 InlayHintRefreshReason::RefreshRequested => {
3823 (InvalidationStrategy::RefreshRequested, None)
3824 }
3825 };
3826
3827 if let Some(InlaySplice {
3828 to_remove,
3829 to_insert,
3830 }) = self.inlay_hint_cache.spawn_hint_refresh(
3831 reason_description,
3832 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3833 invalidate_cache,
3834 ignore_debounce,
3835 cx,
3836 ) {
3837 self.splice_inlays(&to_remove, to_insert, cx);
3838 }
3839 }
3840
3841 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3842 self.display_map
3843 .read(cx)
3844 .current_inlays()
3845 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3846 .cloned()
3847 .collect()
3848 }
3849
3850 pub fn excerpts_for_inlay_hints_query(
3851 &self,
3852 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3853 cx: &mut Context<Editor>,
3854 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3855 let Some(project) = self.project.as_ref() else {
3856 return HashMap::default();
3857 };
3858 let project = project.read(cx);
3859 let multi_buffer = self.buffer().read(cx);
3860 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3861 let multi_buffer_visible_start = self
3862 .scroll_manager
3863 .anchor()
3864 .anchor
3865 .to_point(&multi_buffer_snapshot);
3866 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3867 multi_buffer_visible_start
3868 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3869 Bias::Left,
3870 );
3871 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3872 multi_buffer_snapshot
3873 .range_to_buffer_ranges(multi_buffer_visible_range)
3874 .into_iter()
3875 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3876 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3877 let buffer_file = project::File::from_dyn(buffer.file())?;
3878 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3879 let worktree_entry = buffer_worktree
3880 .read(cx)
3881 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3882 if worktree_entry.is_ignored {
3883 return None;
3884 }
3885
3886 let language = buffer.language()?;
3887 if let Some(restrict_to_languages) = restrict_to_languages {
3888 if !restrict_to_languages.contains(language) {
3889 return None;
3890 }
3891 }
3892 Some((
3893 excerpt_id,
3894 (
3895 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3896 buffer.version().clone(),
3897 excerpt_visible_range,
3898 ),
3899 ))
3900 })
3901 .collect()
3902 }
3903
3904 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3905 TextLayoutDetails {
3906 text_system: window.text_system().clone(),
3907 editor_style: self.style.clone().unwrap(),
3908 rem_size: window.rem_size(),
3909 scroll_anchor: self.scroll_manager.anchor(),
3910 visible_rows: self.visible_line_count(),
3911 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3912 }
3913 }
3914
3915 pub fn splice_inlays(
3916 &self,
3917 to_remove: &[InlayId],
3918 to_insert: Vec<Inlay>,
3919 cx: &mut Context<Self>,
3920 ) {
3921 self.display_map.update(cx, |display_map, cx| {
3922 display_map.splice_inlays(to_remove, to_insert, cx)
3923 });
3924 cx.notify();
3925 }
3926
3927 fn trigger_on_type_formatting(
3928 &self,
3929 input: String,
3930 window: &mut Window,
3931 cx: &mut Context<Self>,
3932 ) -> Option<Task<Result<()>>> {
3933 if input.len() != 1 {
3934 return None;
3935 }
3936
3937 let project = self.project.as_ref()?;
3938 let position = self.selections.newest_anchor().head();
3939 let (buffer, buffer_position) = self
3940 .buffer
3941 .read(cx)
3942 .text_anchor_for_position(position, cx)?;
3943
3944 let settings = language_settings::language_settings(
3945 buffer
3946 .read(cx)
3947 .language_at(buffer_position)
3948 .map(|l| l.name()),
3949 buffer.read(cx).file(),
3950 cx,
3951 );
3952 if !settings.use_on_type_format {
3953 return None;
3954 }
3955
3956 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3957 // hence we do LSP request & edit on host side only — add formats to host's history.
3958 let push_to_lsp_host_history = true;
3959 // If this is not the host, append its history with new edits.
3960 let push_to_client_history = project.read(cx).is_via_collab();
3961
3962 let on_type_formatting = project.update(cx, |project, cx| {
3963 project.on_type_format(
3964 buffer.clone(),
3965 buffer_position,
3966 input,
3967 push_to_lsp_host_history,
3968 cx,
3969 )
3970 });
3971 Some(cx.spawn_in(window, |editor, mut cx| async move {
3972 if let Some(transaction) = on_type_formatting.await? {
3973 if push_to_client_history {
3974 buffer
3975 .update(&mut cx, |buffer, _| {
3976 buffer.push_transaction(transaction, Instant::now());
3977 })
3978 .ok();
3979 }
3980 editor.update(&mut cx, |editor, cx| {
3981 editor.refresh_document_highlights(cx);
3982 })?;
3983 }
3984 Ok(())
3985 }))
3986 }
3987
3988 pub fn show_completions(
3989 &mut self,
3990 options: &ShowCompletions,
3991 window: &mut Window,
3992 cx: &mut Context<Self>,
3993 ) {
3994 if self.pending_rename.is_some() {
3995 return;
3996 }
3997
3998 let Some(provider) = self.completion_provider.as_ref() else {
3999 return;
4000 };
4001
4002 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4003 return;
4004 }
4005
4006 let position = self.selections.newest_anchor().head();
4007 if position.diff_base_anchor.is_some() {
4008 return;
4009 }
4010 let (buffer, buffer_position) =
4011 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4012 output
4013 } else {
4014 return;
4015 };
4016 let show_completion_documentation = buffer
4017 .read(cx)
4018 .snapshot()
4019 .settings_at(buffer_position, cx)
4020 .show_completion_documentation;
4021
4022 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4023
4024 let trigger_kind = match &options.trigger {
4025 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4026 CompletionTriggerKind::TRIGGER_CHARACTER
4027 }
4028 _ => CompletionTriggerKind::INVOKED,
4029 };
4030 let completion_context = CompletionContext {
4031 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4032 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4033 Some(String::from(trigger))
4034 } else {
4035 None
4036 }
4037 }),
4038 trigger_kind,
4039 };
4040 let completions =
4041 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4042 let sort_completions = provider.sort_completions();
4043
4044 let id = post_inc(&mut self.next_completion_id);
4045 let task = cx.spawn_in(window, |editor, mut cx| {
4046 async move {
4047 editor.update(&mut cx, |this, _| {
4048 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4049 })?;
4050 let completions = completions.await.log_err();
4051 let menu = if let Some(completions) = completions {
4052 let mut menu = CompletionsMenu::new(
4053 id,
4054 sort_completions,
4055 show_completion_documentation,
4056 position,
4057 buffer.clone(),
4058 completions.into(),
4059 );
4060
4061 menu.filter(query.as_deref(), cx.background_executor().clone())
4062 .await;
4063
4064 menu.visible().then_some(menu)
4065 } else {
4066 None
4067 };
4068
4069 editor.update_in(&mut cx, |editor, window, cx| {
4070 match editor.context_menu.borrow().as_ref() {
4071 None => {}
4072 Some(CodeContextMenu::Completions(prev_menu)) => {
4073 if prev_menu.id > id {
4074 return;
4075 }
4076 }
4077 _ => return,
4078 }
4079
4080 if editor.focus_handle.is_focused(window) && menu.is_some() {
4081 let mut menu = menu.unwrap();
4082 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4083
4084 *editor.context_menu.borrow_mut() =
4085 Some(CodeContextMenu::Completions(menu));
4086
4087 if editor.show_edit_predictions_in_menu() {
4088 editor.update_visible_inline_completion(window, cx);
4089 } else {
4090 editor.discard_inline_completion(false, cx);
4091 }
4092
4093 cx.notify();
4094 } else if editor.completion_tasks.len() <= 1 {
4095 // If there are no more completion tasks and the last menu was
4096 // empty, we should hide it.
4097 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4098 // If it was already hidden and we don't show inline
4099 // completions in the menu, we should also show the
4100 // inline-completion when available.
4101 if was_hidden && editor.show_edit_predictions_in_menu() {
4102 editor.update_visible_inline_completion(window, cx);
4103 }
4104 }
4105 })?;
4106
4107 Ok::<_, anyhow::Error>(())
4108 }
4109 .log_err()
4110 });
4111
4112 self.completion_tasks.push((id, task));
4113 }
4114
4115 pub fn confirm_completion(
4116 &mut self,
4117 action: &ConfirmCompletion,
4118 window: &mut Window,
4119 cx: &mut Context<Self>,
4120 ) -> Option<Task<Result<()>>> {
4121 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4122 }
4123
4124 pub fn compose_completion(
4125 &mut self,
4126 action: &ComposeCompletion,
4127 window: &mut Window,
4128 cx: &mut Context<Self>,
4129 ) -> Option<Task<Result<()>>> {
4130 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4131 }
4132
4133 fn do_completion(
4134 &mut self,
4135 item_ix: Option<usize>,
4136 intent: CompletionIntent,
4137 window: &mut Window,
4138 cx: &mut Context<Editor>,
4139 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4140 use language::ToOffset as _;
4141
4142 let completions_menu =
4143 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4144 menu
4145 } else {
4146 return None;
4147 };
4148
4149 let entries = completions_menu.entries.borrow();
4150 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4151 if self.show_edit_predictions_in_menu() {
4152 self.discard_inline_completion(true, cx);
4153 }
4154 let candidate_id = mat.candidate_id;
4155 drop(entries);
4156
4157 let buffer_handle = completions_menu.buffer;
4158 let completion = completions_menu
4159 .completions
4160 .borrow()
4161 .get(candidate_id)?
4162 .clone();
4163 cx.stop_propagation();
4164
4165 let snippet;
4166 let text;
4167
4168 if completion.is_snippet() {
4169 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4170 text = snippet.as_ref().unwrap().text.clone();
4171 } else {
4172 snippet = None;
4173 text = completion.new_text.clone();
4174 };
4175 let selections = self.selections.all::<usize>(cx);
4176 let buffer = buffer_handle.read(cx);
4177 let old_range = completion.old_range.to_offset(buffer);
4178 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4179
4180 let newest_selection = self.selections.newest_anchor();
4181 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4182 return None;
4183 }
4184
4185 let lookbehind = newest_selection
4186 .start
4187 .text_anchor
4188 .to_offset(buffer)
4189 .saturating_sub(old_range.start);
4190 let lookahead = old_range
4191 .end
4192 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4193 let mut common_prefix_len = old_text
4194 .bytes()
4195 .zip(text.bytes())
4196 .take_while(|(a, b)| a == b)
4197 .count();
4198
4199 let snapshot = self.buffer.read(cx).snapshot(cx);
4200 let mut range_to_replace: Option<Range<isize>> = None;
4201 let mut ranges = Vec::new();
4202 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4203 for selection in &selections {
4204 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4205 let start = selection.start.saturating_sub(lookbehind);
4206 let end = selection.end + lookahead;
4207 if selection.id == newest_selection.id {
4208 range_to_replace = Some(
4209 ((start + common_prefix_len) as isize - selection.start as isize)
4210 ..(end as isize - selection.start as isize),
4211 );
4212 }
4213 ranges.push(start + common_prefix_len..end);
4214 } else {
4215 common_prefix_len = 0;
4216 ranges.clear();
4217 ranges.extend(selections.iter().map(|s| {
4218 if s.id == newest_selection.id {
4219 range_to_replace = Some(
4220 old_range.start.to_offset_utf16(&snapshot).0 as isize
4221 - selection.start as isize
4222 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4223 - selection.start as isize,
4224 );
4225 old_range.clone()
4226 } else {
4227 s.start..s.end
4228 }
4229 }));
4230 break;
4231 }
4232 if !self.linked_edit_ranges.is_empty() {
4233 let start_anchor = snapshot.anchor_before(selection.head());
4234 let end_anchor = snapshot.anchor_after(selection.tail());
4235 if let Some(ranges) = self
4236 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4237 {
4238 for (buffer, edits) in ranges {
4239 linked_edits.entry(buffer.clone()).or_default().extend(
4240 edits
4241 .into_iter()
4242 .map(|range| (range, text[common_prefix_len..].to_owned())),
4243 );
4244 }
4245 }
4246 }
4247 }
4248 let text = &text[common_prefix_len..];
4249
4250 cx.emit(EditorEvent::InputHandled {
4251 utf16_range_to_replace: range_to_replace,
4252 text: text.into(),
4253 });
4254
4255 self.transact(window, cx, |this, window, cx| {
4256 if let Some(mut snippet) = snippet {
4257 snippet.text = text.to_string();
4258 for tabstop in snippet
4259 .tabstops
4260 .iter_mut()
4261 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4262 {
4263 tabstop.start -= common_prefix_len as isize;
4264 tabstop.end -= common_prefix_len as isize;
4265 }
4266
4267 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4268 } else {
4269 this.buffer.update(cx, |buffer, cx| {
4270 buffer.edit(
4271 ranges.iter().map(|range| (range.clone(), text)),
4272 this.autoindent_mode.clone(),
4273 cx,
4274 );
4275 });
4276 }
4277 for (buffer, edits) in linked_edits {
4278 buffer.update(cx, |buffer, cx| {
4279 let snapshot = buffer.snapshot();
4280 let edits = edits
4281 .into_iter()
4282 .map(|(range, text)| {
4283 use text::ToPoint as TP;
4284 let end_point = TP::to_point(&range.end, &snapshot);
4285 let start_point = TP::to_point(&range.start, &snapshot);
4286 (start_point..end_point, text)
4287 })
4288 .sorted_by_key(|(range, _)| range.start)
4289 .collect::<Vec<_>>();
4290 buffer.edit(edits, None, cx);
4291 })
4292 }
4293
4294 this.refresh_inline_completion(true, false, window, cx);
4295 });
4296
4297 let show_new_completions_on_confirm = completion
4298 .confirm
4299 .as_ref()
4300 .map_or(false, |confirm| confirm(intent, window, cx));
4301 if show_new_completions_on_confirm {
4302 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4303 }
4304
4305 let provider = self.completion_provider.as_ref()?;
4306 drop(completion);
4307 let apply_edits = provider.apply_additional_edits_for_completion(
4308 buffer_handle,
4309 completions_menu.completions.clone(),
4310 candidate_id,
4311 true,
4312 cx,
4313 );
4314
4315 let editor_settings = EditorSettings::get_global(cx);
4316 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4317 // After the code completion is finished, users often want to know what signatures are needed.
4318 // so we should automatically call signature_help
4319 self.show_signature_help(&ShowSignatureHelp, window, cx);
4320 }
4321
4322 Some(cx.foreground_executor().spawn(async move {
4323 apply_edits.await?;
4324 Ok(())
4325 }))
4326 }
4327
4328 pub fn toggle_code_actions(
4329 &mut self,
4330 action: &ToggleCodeActions,
4331 window: &mut Window,
4332 cx: &mut Context<Self>,
4333 ) {
4334 let mut context_menu = self.context_menu.borrow_mut();
4335 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4336 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4337 // Toggle if we're selecting the same one
4338 *context_menu = None;
4339 cx.notify();
4340 return;
4341 } else {
4342 // Otherwise, clear it and start a new one
4343 *context_menu = None;
4344 cx.notify();
4345 }
4346 }
4347 drop(context_menu);
4348 let snapshot = self.snapshot(window, cx);
4349 let deployed_from_indicator = action.deployed_from_indicator;
4350 let mut task = self.code_actions_task.take();
4351 let action = action.clone();
4352 cx.spawn_in(window, |editor, mut cx| async move {
4353 while let Some(prev_task) = task {
4354 prev_task.await.log_err();
4355 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4356 }
4357
4358 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4359 if editor.focus_handle.is_focused(window) {
4360 let multibuffer_point = action
4361 .deployed_from_indicator
4362 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4363 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4364 let (buffer, buffer_row) = snapshot
4365 .buffer_snapshot
4366 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4367 .and_then(|(buffer_snapshot, range)| {
4368 editor
4369 .buffer
4370 .read(cx)
4371 .buffer(buffer_snapshot.remote_id())
4372 .map(|buffer| (buffer, range.start.row))
4373 })?;
4374 let (_, code_actions) = editor
4375 .available_code_actions
4376 .clone()
4377 .and_then(|(location, code_actions)| {
4378 let snapshot = location.buffer.read(cx).snapshot();
4379 let point_range = location.range.to_point(&snapshot);
4380 let point_range = point_range.start.row..=point_range.end.row;
4381 if point_range.contains(&buffer_row) {
4382 Some((location, code_actions))
4383 } else {
4384 None
4385 }
4386 })
4387 .unzip();
4388 let buffer_id = buffer.read(cx).remote_id();
4389 let tasks = editor
4390 .tasks
4391 .get(&(buffer_id, buffer_row))
4392 .map(|t| Arc::new(t.to_owned()));
4393 if tasks.is_none() && code_actions.is_none() {
4394 return None;
4395 }
4396
4397 editor.completion_tasks.clear();
4398 editor.discard_inline_completion(false, cx);
4399 let task_context =
4400 tasks
4401 .as_ref()
4402 .zip(editor.project.clone())
4403 .map(|(tasks, project)| {
4404 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4405 });
4406
4407 Some(cx.spawn_in(window, |editor, mut cx| async move {
4408 let task_context = match task_context {
4409 Some(task_context) => task_context.await,
4410 None => None,
4411 };
4412 let resolved_tasks =
4413 tasks.zip(task_context).map(|(tasks, task_context)| {
4414 Rc::new(ResolvedTasks {
4415 templates: tasks.resolve(&task_context).collect(),
4416 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4417 multibuffer_point.row,
4418 tasks.column,
4419 )),
4420 })
4421 });
4422 let spawn_straight_away = resolved_tasks
4423 .as_ref()
4424 .map_or(false, |tasks| tasks.templates.len() == 1)
4425 && code_actions
4426 .as_ref()
4427 .map_or(true, |actions| actions.is_empty());
4428 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4429 *editor.context_menu.borrow_mut() =
4430 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4431 buffer,
4432 actions: CodeActionContents {
4433 tasks: resolved_tasks,
4434 actions: code_actions,
4435 },
4436 selected_item: Default::default(),
4437 scroll_handle: UniformListScrollHandle::default(),
4438 deployed_from_indicator,
4439 }));
4440 if spawn_straight_away {
4441 if let Some(task) = editor.confirm_code_action(
4442 &ConfirmCodeAction { item_ix: Some(0) },
4443 window,
4444 cx,
4445 ) {
4446 cx.notify();
4447 return task;
4448 }
4449 }
4450 cx.notify();
4451 Task::ready(Ok(()))
4452 }) {
4453 task.await
4454 } else {
4455 Ok(())
4456 }
4457 }))
4458 } else {
4459 Some(Task::ready(Ok(())))
4460 }
4461 })?;
4462 if let Some(task) = spawned_test_task {
4463 task.await?;
4464 }
4465
4466 Ok::<_, anyhow::Error>(())
4467 })
4468 .detach_and_log_err(cx);
4469 }
4470
4471 pub fn confirm_code_action(
4472 &mut self,
4473 action: &ConfirmCodeAction,
4474 window: &mut Window,
4475 cx: &mut Context<Self>,
4476 ) -> Option<Task<Result<()>>> {
4477 let actions_menu =
4478 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4479 menu
4480 } else {
4481 return None;
4482 };
4483 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4484 let action = actions_menu.actions.get(action_ix)?;
4485 let title = action.label();
4486 let buffer = actions_menu.buffer;
4487 let workspace = self.workspace()?;
4488
4489 match action {
4490 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4491 workspace.update(cx, |workspace, cx| {
4492 workspace::tasks::schedule_resolved_task(
4493 workspace,
4494 task_source_kind,
4495 resolved_task,
4496 false,
4497 cx,
4498 );
4499
4500 Some(Task::ready(Ok(())))
4501 })
4502 }
4503 CodeActionsItem::CodeAction {
4504 excerpt_id,
4505 action,
4506 provider,
4507 } => {
4508 let apply_code_action =
4509 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4510 let workspace = workspace.downgrade();
4511 Some(cx.spawn_in(window, |editor, cx| async move {
4512 let project_transaction = apply_code_action.await?;
4513 Self::open_project_transaction(
4514 &editor,
4515 workspace,
4516 project_transaction,
4517 title,
4518 cx,
4519 )
4520 .await
4521 }))
4522 }
4523 }
4524 }
4525
4526 pub async fn open_project_transaction(
4527 this: &WeakEntity<Editor>,
4528 workspace: WeakEntity<Workspace>,
4529 transaction: ProjectTransaction,
4530 title: String,
4531 mut cx: AsyncWindowContext,
4532 ) -> Result<()> {
4533 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4534 cx.update(|_, cx| {
4535 entries.sort_unstable_by_key(|(buffer, _)| {
4536 buffer.read(cx).file().map(|f| f.path().clone())
4537 });
4538 })?;
4539
4540 // If the project transaction's edits are all contained within this editor, then
4541 // avoid opening a new editor to display them.
4542
4543 if let Some((buffer, transaction)) = entries.first() {
4544 if entries.len() == 1 {
4545 let excerpt = this.update(&mut cx, |editor, cx| {
4546 editor
4547 .buffer()
4548 .read(cx)
4549 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4550 })?;
4551 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4552 if excerpted_buffer == *buffer {
4553 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4554 let excerpt_range = excerpt_range.to_offset(buffer);
4555 buffer
4556 .edited_ranges_for_transaction::<usize>(transaction)
4557 .all(|range| {
4558 excerpt_range.start <= range.start
4559 && excerpt_range.end >= range.end
4560 })
4561 })?;
4562
4563 if all_edits_within_excerpt {
4564 return Ok(());
4565 }
4566 }
4567 }
4568 }
4569 } else {
4570 return Ok(());
4571 }
4572
4573 let mut ranges_to_highlight = Vec::new();
4574 let excerpt_buffer = cx.new(|cx| {
4575 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4576 for (buffer_handle, transaction) in &entries {
4577 let buffer = buffer_handle.read(cx);
4578 ranges_to_highlight.extend(
4579 multibuffer.push_excerpts_with_context_lines(
4580 buffer_handle.clone(),
4581 buffer
4582 .edited_ranges_for_transaction::<usize>(transaction)
4583 .collect(),
4584 DEFAULT_MULTIBUFFER_CONTEXT,
4585 cx,
4586 ),
4587 );
4588 }
4589 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4590 multibuffer
4591 })?;
4592
4593 workspace.update_in(&mut cx, |workspace, window, cx| {
4594 let project = workspace.project().clone();
4595 let editor = cx
4596 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4597 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4598 editor.update(cx, |editor, cx| {
4599 editor.highlight_background::<Self>(
4600 &ranges_to_highlight,
4601 |theme| theme.editor_highlighted_line_background,
4602 cx,
4603 );
4604 });
4605 })?;
4606
4607 Ok(())
4608 }
4609
4610 pub fn clear_code_action_providers(&mut self) {
4611 self.code_action_providers.clear();
4612 self.available_code_actions.take();
4613 }
4614
4615 pub fn add_code_action_provider(
4616 &mut self,
4617 provider: Rc<dyn CodeActionProvider>,
4618 window: &mut Window,
4619 cx: &mut Context<Self>,
4620 ) {
4621 if self
4622 .code_action_providers
4623 .iter()
4624 .any(|existing_provider| existing_provider.id() == provider.id())
4625 {
4626 return;
4627 }
4628
4629 self.code_action_providers.push(provider);
4630 self.refresh_code_actions(window, cx);
4631 }
4632
4633 pub fn remove_code_action_provider(
4634 &mut self,
4635 id: Arc<str>,
4636 window: &mut Window,
4637 cx: &mut Context<Self>,
4638 ) {
4639 self.code_action_providers
4640 .retain(|provider| provider.id() != id);
4641 self.refresh_code_actions(window, cx);
4642 }
4643
4644 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4645 let buffer = self.buffer.read(cx);
4646 let newest_selection = self.selections.newest_anchor().clone();
4647 if newest_selection.head().diff_base_anchor.is_some() {
4648 return None;
4649 }
4650 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4651 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4652 if start_buffer != end_buffer {
4653 return None;
4654 }
4655
4656 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4657 cx.background_executor()
4658 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4659 .await;
4660
4661 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4662 let providers = this.code_action_providers.clone();
4663 let tasks = this
4664 .code_action_providers
4665 .iter()
4666 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4667 .collect::<Vec<_>>();
4668 (providers, tasks)
4669 })?;
4670
4671 let mut actions = Vec::new();
4672 for (provider, provider_actions) in
4673 providers.into_iter().zip(future::join_all(tasks).await)
4674 {
4675 if let Some(provider_actions) = provider_actions.log_err() {
4676 actions.extend(provider_actions.into_iter().map(|action| {
4677 AvailableCodeAction {
4678 excerpt_id: newest_selection.start.excerpt_id,
4679 action,
4680 provider: provider.clone(),
4681 }
4682 }));
4683 }
4684 }
4685
4686 this.update(&mut cx, |this, cx| {
4687 this.available_code_actions = if actions.is_empty() {
4688 None
4689 } else {
4690 Some((
4691 Location {
4692 buffer: start_buffer,
4693 range: start..end,
4694 },
4695 actions.into(),
4696 ))
4697 };
4698 cx.notify();
4699 })
4700 }));
4701 None
4702 }
4703
4704 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4705 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4706 self.show_git_blame_inline = false;
4707
4708 self.show_git_blame_inline_delay_task =
4709 Some(cx.spawn_in(window, |this, mut cx| async move {
4710 cx.background_executor().timer(delay).await;
4711
4712 this.update(&mut cx, |this, cx| {
4713 this.show_git_blame_inline = true;
4714 cx.notify();
4715 })
4716 .log_err();
4717 }));
4718 }
4719 }
4720
4721 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4722 if self.pending_rename.is_some() {
4723 return None;
4724 }
4725
4726 let provider = self.semantics_provider.clone()?;
4727 let buffer = self.buffer.read(cx);
4728 let newest_selection = self.selections.newest_anchor().clone();
4729 let cursor_position = newest_selection.head();
4730 let (cursor_buffer, cursor_buffer_position) =
4731 buffer.text_anchor_for_position(cursor_position, cx)?;
4732 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4733 if cursor_buffer != tail_buffer {
4734 return None;
4735 }
4736 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4737 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4738 cx.background_executor()
4739 .timer(Duration::from_millis(debounce))
4740 .await;
4741
4742 let highlights = if let Some(highlights) = cx
4743 .update(|cx| {
4744 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4745 })
4746 .ok()
4747 .flatten()
4748 {
4749 highlights.await.log_err()
4750 } else {
4751 None
4752 };
4753
4754 if let Some(highlights) = highlights {
4755 this.update(&mut cx, |this, cx| {
4756 if this.pending_rename.is_some() {
4757 return;
4758 }
4759
4760 let buffer_id = cursor_position.buffer_id;
4761 let buffer = this.buffer.read(cx);
4762 if !buffer
4763 .text_anchor_for_position(cursor_position, cx)
4764 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4765 {
4766 return;
4767 }
4768
4769 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4770 let mut write_ranges = Vec::new();
4771 let mut read_ranges = Vec::new();
4772 for highlight in highlights {
4773 for (excerpt_id, excerpt_range) in
4774 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4775 {
4776 let start = highlight
4777 .range
4778 .start
4779 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4780 let end = highlight
4781 .range
4782 .end
4783 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4784 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4785 continue;
4786 }
4787
4788 let range = Anchor {
4789 buffer_id,
4790 excerpt_id,
4791 text_anchor: start,
4792 diff_base_anchor: None,
4793 }..Anchor {
4794 buffer_id,
4795 excerpt_id,
4796 text_anchor: end,
4797 diff_base_anchor: None,
4798 };
4799 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4800 write_ranges.push(range);
4801 } else {
4802 read_ranges.push(range);
4803 }
4804 }
4805 }
4806
4807 this.highlight_background::<DocumentHighlightRead>(
4808 &read_ranges,
4809 |theme| theme.editor_document_highlight_read_background,
4810 cx,
4811 );
4812 this.highlight_background::<DocumentHighlightWrite>(
4813 &write_ranges,
4814 |theme| theme.editor_document_highlight_write_background,
4815 cx,
4816 );
4817 cx.notify();
4818 })
4819 .log_err();
4820 }
4821 }));
4822 None
4823 }
4824
4825 pub fn refresh_selected_text_highlights(
4826 &mut self,
4827 window: &mut Window,
4828 cx: &mut Context<Editor>,
4829 ) {
4830 self.selection_highlight_task.take();
4831 if !EditorSettings::get_global(cx).selection_highlight {
4832 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4833 return;
4834 }
4835 if self.selections.count() != 1 || self.selections.line_mode {
4836 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4837 return;
4838 }
4839 let selection = self.selections.newest::<Point>(cx);
4840 if selection.is_empty() || selection.start.row != selection.end.row {
4841 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4842 return;
4843 }
4844 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4845 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4846 cx.background_executor()
4847 .timer(Duration::from_millis(debounce))
4848 .await;
4849 let Some(Some(matches_task)) = editor
4850 .update_in(&mut cx, |editor, _, cx| {
4851 if editor.selections.count() != 1 || editor.selections.line_mode {
4852 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4853 return None;
4854 }
4855 let selection = editor.selections.newest::<Point>(cx);
4856 if selection.is_empty() || selection.start.row != selection.end.row {
4857 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4858 return None;
4859 }
4860 let buffer = editor.buffer().read(cx).snapshot(cx);
4861 let query = buffer.text_for_range(selection.range()).collect::<String>();
4862 if query.trim().is_empty() {
4863 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4864 return None;
4865 }
4866 Some(cx.background_spawn(async move {
4867 let mut ranges = Vec::new();
4868 let selection_anchors = selection.range().to_anchors(&buffer);
4869 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4870 for (search_buffer, search_range, excerpt_id) in
4871 buffer.range_to_buffer_ranges(range)
4872 {
4873 ranges.extend(
4874 project::search::SearchQuery::text(
4875 query.clone(),
4876 false,
4877 false,
4878 false,
4879 Default::default(),
4880 Default::default(),
4881 None,
4882 )
4883 .unwrap()
4884 .search(search_buffer, Some(search_range.clone()))
4885 .await
4886 .into_iter()
4887 .filter_map(
4888 |match_range| {
4889 let start = search_buffer.anchor_after(
4890 search_range.start + match_range.start,
4891 );
4892 let end = search_buffer.anchor_before(
4893 search_range.start + match_range.end,
4894 );
4895 let range = Anchor::range_in_buffer(
4896 excerpt_id,
4897 search_buffer.remote_id(),
4898 start..end,
4899 );
4900 (range != selection_anchors).then_some(range)
4901 },
4902 ),
4903 );
4904 }
4905 }
4906 ranges
4907 }))
4908 })
4909 .log_err()
4910 else {
4911 return;
4912 };
4913 let matches = matches_task.await;
4914 editor
4915 .update_in(&mut cx, |editor, _, cx| {
4916 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4917 if !matches.is_empty() {
4918 editor.highlight_background::<SelectedTextHighlight>(
4919 &matches,
4920 |theme| theme.editor_document_highlight_bracket_background,
4921 cx,
4922 )
4923 }
4924 })
4925 .log_err();
4926 }));
4927 }
4928
4929 pub fn refresh_inline_completion(
4930 &mut self,
4931 debounce: bool,
4932 user_requested: bool,
4933 window: &mut Window,
4934 cx: &mut Context<Self>,
4935 ) -> Option<()> {
4936 let provider = self.edit_prediction_provider()?;
4937 let cursor = self.selections.newest_anchor().head();
4938 let (buffer, cursor_buffer_position) =
4939 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4940
4941 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4942 self.discard_inline_completion(false, cx);
4943 return None;
4944 }
4945
4946 if !user_requested
4947 && (!self.should_show_edit_predictions()
4948 || !self.is_focused(window)
4949 || buffer.read(cx).is_empty())
4950 {
4951 self.discard_inline_completion(false, cx);
4952 return None;
4953 }
4954
4955 self.update_visible_inline_completion(window, cx);
4956 provider.refresh(
4957 self.project.clone(),
4958 buffer,
4959 cursor_buffer_position,
4960 debounce,
4961 cx,
4962 );
4963 Some(())
4964 }
4965
4966 fn show_edit_predictions_in_menu(&self) -> bool {
4967 match self.edit_prediction_settings {
4968 EditPredictionSettings::Disabled => false,
4969 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4970 }
4971 }
4972
4973 pub fn edit_predictions_enabled(&self) -> bool {
4974 match self.edit_prediction_settings {
4975 EditPredictionSettings::Disabled => false,
4976 EditPredictionSettings::Enabled { .. } => true,
4977 }
4978 }
4979
4980 fn edit_prediction_requires_modifier(&self) -> bool {
4981 match self.edit_prediction_settings {
4982 EditPredictionSettings::Disabled => false,
4983 EditPredictionSettings::Enabled {
4984 preview_requires_modifier,
4985 ..
4986 } => preview_requires_modifier,
4987 }
4988 }
4989
4990 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
4991 if self.edit_prediction_provider.is_none() {
4992 self.edit_prediction_settings = EditPredictionSettings::Disabled;
4993 } else {
4994 let selection = self.selections.newest_anchor();
4995 let cursor = selection.head();
4996
4997 if let Some((buffer, cursor_buffer_position)) =
4998 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4999 {
5000 self.edit_prediction_settings =
5001 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5002 }
5003 }
5004 }
5005
5006 fn edit_prediction_settings_at_position(
5007 &self,
5008 buffer: &Entity<Buffer>,
5009 buffer_position: language::Anchor,
5010 cx: &App,
5011 ) -> EditPredictionSettings {
5012 if self.mode != EditorMode::Full
5013 || !self.show_inline_completions_override.unwrap_or(true)
5014 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5015 {
5016 return EditPredictionSettings::Disabled;
5017 }
5018
5019 let buffer = buffer.read(cx);
5020
5021 let file = buffer.file();
5022
5023 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5024 return EditPredictionSettings::Disabled;
5025 };
5026
5027 let by_provider = matches!(
5028 self.menu_inline_completions_policy,
5029 MenuInlineCompletionsPolicy::ByProvider
5030 );
5031
5032 let show_in_menu = by_provider
5033 && self
5034 .edit_prediction_provider
5035 .as_ref()
5036 .map_or(false, |provider| {
5037 provider.provider.show_completions_in_menu()
5038 });
5039
5040 let preview_requires_modifier =
5041 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5042
5043 EditPredictionSettings::Enabled {
5044 show_in_menu,
5045 preview_requires_modifier,
5046 }
5047 }
5048
5049 fn should_show_edit_predictions(&self) -> bool {
5050 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5051 }
5052
5053 pub fn edit_prediction_preview_is_active(&self) -> bool {
5054 matches!(
5055 self.edit_prediction_preview,
5056 EditPredictionPreview::Active { .. }
5057 )
5058 }
5059
5060 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5061 let cursor = self.selections.newest_anchor().head();
5062 if let Some((buffer, cursor_position)) =
5063 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5064 {
5065 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5066 } else {
5067 false
5068 }
5069 }
5070
5071 fn edit_predictions_enabled_in_buffer(
5072 &self,
5073 buffer: &Entity<Buffer>,
5074 buffer_position: language::Anchor,
5075 cx: &App,
5076 ) -> bool {
5077 maybe!({
5078 let provider = self.edit_prediction_provider()?;
5079 if !provider.is_enabled(&buffer, buffer_position, cx) {
5080 return Some(false);
5081 }
5082 let buffer = buffer.read(cx);
5083 let Some(file) = buffer.file() else {
5084 return Some(true);
5085 };
5086 let settings = all_language_settings(Some(file), cx);
5087 Some(settings.edit_predictions_enabled_for_file(file, cx))
5088 })
5089 .unwrap_or(false)
5090 }
5091
5092 fn cycle_inline_completion(
5093 &mut self,
5094 direction: Direction,
5095 window: &mut Window,
5096 cx: &mut Context<Self>,
5097 ) -> Option<()> {
5098 let provider = self.edit_prediction_provider()?;
5099 let cursor = self.selections.newest_anchor().head();
5100 let (buffer, cursor_buffer_position) =
5101 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5102 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5103 return None;
5104 }
5105
5106 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5107 self.update_visible_inline_completion(window, cx);
5108
5109 Some(())
5110 }
5111
5112 pub fn show_inline_completion(
5113 &mut self,
5114 _: &ShowEditPrediction,
5115 window: &mut Window,
5116 cx: &mut Context<Self>,
5117 ) {
5118 if !self.has_active_inline_completion() {
5119 self.refresh_inline_completion(false, true, window, cx);
5120 return;
5121 }
5122
5123 self.update_visible_inline_completion(window, cx);
5124 }
5125
5126 pub fn display_cursor_names(
5127 &mut self,
5128 _: &DisplayCursorNames,
5129 window: &mut Window,
5130 cx: &mut Context<Self>,
5131 ) {
5132 self.show_cursor_names(window, cx);
5133 }
5134
5135 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5136 self.show_cursor_names = true;
5137 cx.notify();
5138 cx.spawn_in(window, |this, mut cx| async move {
5139 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5140 this.update(&mut cx, |this, cx| {
5141 this.show_cursor_names = false;
5142 cx.notify()
5143 })
5144 .ok()
5145 })
5146 .detach();
5147 }
5148
5149 pub fn next_edit_prediction(
5150 &mut self,
5151 _: &NextEditPrediction,
5152 window: &mut Window,
5153 cx: &mut Context<Self>,
5154 ) {
5155 if self.has_active_inline_completion() {
5156 self.cycle_inline_completion(Direction::Next, window, cx);
5157 } else {
5158 let is_copilot_disabled = self
5159 .refresh_inline_completion(false, true, window, cx)
5160 .is_none();
5161 if is_copilot_disabled {
5162 cx.propagate();
5163 }
5164 }
5165 }
5166
5167 pub fn previous_edit_prediction(
5168 &mut self,
5169 _: &PreviousEditPrediction,
5170 window: &mut Window,
5171 cx: &mut Context<Self>,
5172 ) {
5173 if self.has_active_inline_completion() {
5174 self.cycle_inline_completion(Direction::Prev, window, cx);
5175 } else {
5176 let is_copilot_disabled = self
5177 .refresh_inline_completion(false, true, window, cx)
5178 .is_none();
5179 if is_copilot_disabled {
5180 cx.propagate();
5181 }
5182 }
5183 }
5184
5185 pub fn accept_edit_prediction(
5186 &mut self,
5187 _: &AcceptEditPrediction,
5188 window: &mut Window,
5189 cx: &mut Context<Self>,
5190 ) {
5191 if self.show_edit_predictions_in_menu() {
5192 self.hide_context_menu(window, cx);
5193 }
5194
5195 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5196 return;
5197 };
5198
5199 self.report_inline_completion_event(
5200 active_inline_completion.completion_id.clone(),
5201 true,
5202 cx,
5203 );
5204
5205 match &active_inline_completion.completion {
5206 InlineCompletion::Move { target, .. } => {
5207 let target = *target;
5208
5209 if let Some(position_map) = &self.last_position_map {
5210 if position_map
5211 .visible_row_range
5212 .contains(&target.to_display_point(&position_map.snapshot).row())
5213 || !self.edit_prediction_requires_modifier()
5214 {
5215 self.unfold_ranges(&[target..target], true, false, cx);
5216 // Note that this is also done in vim's handler of the Tab action.
5217 self.change_selections(
5218 Some(Autoscroll::newest()),
5219 window,
5220 cx,
5221 |selections| {
5222 selections.select_anchor_ranges([target..target]);
5223 },
5224 );
5225 self.clear_row_highlights::<EditPredictionPreview>();
5226
5227 self.edit_prediction_preview
5228 .set_previous_scroll_position(None);
5229 } else {
5230 self.edit_prediction_preview
5231 .set_previous_scroll_position(Some(
5232 position_map.snapshot.scroll_anchor,
5233 ));
5234
5235 self.highlight_rows::<EditPredictionPreview>(
5236 target..target,
5237 cx.theme().colors().editor_highlighted_line_background,
5238 true,
5239 cx,
5240 );
5241 self.request_autoscroll(Autoscroll::fit(), cx);
5242 }
5243 }
5244 }
5245 InlineCompletion::Edit { edits, .. } => {
5246 if let Some(provider) = self.edit_prediction_provider() {
5247 provider.accept(cx);
5248 }
5249
5250 let snapshot = self.buffer.read(cx).snapshot(cx);
5251 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5252
5253 self.buffer.update(cx, |buffer, cx| {
5254 buffer.edit(edits.iter().cloned(), None, cx)
5255 });
5256
5257 self.change_selections(None, window, cx, |s| {
5258 s.select_anchor_ranges([last_edit_end..last_edit_end])
5259 });
5260
5261 self.update_visible_inline_completion(window, cx);
5262 if self.active_inline_completion.is_none() {
5263 self.refresh_inline_completion(true, true, window, cx);
5264 }
5265
5266 cx.notify();
5267 }
5268 }
5269
5270 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5271 }
5272
5273 pub fn accept_partial_inline_completion(
5274 &mut self,
5275 _: &AcceptPartialEditPrediction,
5276 window: &mut Window,
5277 cx: &mut Context<Self>,
5278 ) {
5279 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5280 return;
5281 };
5282 if self.selections.count() != 1 {
5283 return;
5284 }
5285
5286 self.report_inline_completion_event(
5287 active_inline_completion.completion_id.clone(),
5288 true,
5289 cx,
5290 );
5291
5292 match &active_inline_completion.completion {
5293 InlineCompletion::Move { target, .. } => {
5294 let target = *target;
5295 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5296 selections.select_anchor_ranges([target..target]);
5297 });
5298 }
5299 InlineCompletion::Edit { edits, .. } => {
5300 // Find an insertion that starts at the cursor position.
5301 let snapshot = self.buffer.read(cx).snapshot(cx);
5302 let cursor_offset = self.selections.newest::<usize>(cx).head();
5303 let insertion = edits.iter().find_map(|(range, text)| {
5304 let range = range.to_offset(&snapshot);
5305 if range.is_empty() && range.start == cursor_offset {
5306 Some(text)
5307 } else {
5308 None
5309 }
5310 });
5311
5312 if let Some(text) = insertion {
5313 let mut partial_completion = text
5314 .chars()
5315 .by_ref()
5316 .take_while(|c| c.is_alphabetic())
5317 .collect::<String>();
5318 if partial_completion.is_empty() {
5319 partial_completion = text
5320 .chars()
5321 .by_ref()
5322 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5323 .collect::<String>();
5324 }
5325
5326 cx.emit(EditorEvent::InputHandled {
5327 utf16_range_to_replace: None,
5328 text: partial_completion.clone().into(),
5329 });
5330
5331 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5332
5333 self.refresh_inline_completion(true, true, window, cx);
5334 cx.notify();
5335 } else {
5336 self.accept_edit_prediction(&Default::default(), window, cx);
5337 }
5338 }
5339 }
5340 }
5341
5342 fn discard_inline_completion(
5343 &mut self,
5344 should_report_inline_completion_event: bool,
5345 cx: &mut Context<Self>,
5346 ) -> bool {
5347 if should_report_inline_completion_event {
5348 let completion_id = self
5349 .active_inline_completion
5350 .as_ref()
5351 .and_then(|active_completion| active_completion.completion_id.clone());
5352
5353 self.report_inline_completion_event(completion_id, false, cx);
5354 }
5355
5356 if let Some(provider) = self.edit_prediction_provider() {
5357 provider.discard(cx);
5358 }
5359
5360 self.take_active_inline_completion(cx)
5361 }
5362
5363 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5364 let Some(provider) = self.edit_prediction_provider() else {
5365 return;
5366 };
5367
5368 let Some((_, buffer, _)) = self
5369 .buffer
5370 .read(cx)
5371 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5372 else {
5373 return;
5374 };
5375
5376 let extension = buffer
5377 .read(cx)
5378 .file()
5379 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5380
5381 let event_type = match accepted {
5382 true => "Edit Prediction Accepted",
5383 false => "Edit Prediction Discarded",
5384 };
5385 telemetry::event!(
5386 event_type,
5387 provider = provider.name(),
5388 prediction_id = id,
5389 suggestion_accepted = accepted,
5390 file_extension = extension,
5391 );
5392 }
5393
5394 pub fn has_active_inline_completion(&self) -> bool {
5395 self.active_inline_completion.is_some()
5396 }
5397
5398 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5399 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5400 return false;
5401 };
5402
5403 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5404 self.clear_highlights::<InlineCompletionHighlight>(cx);
5405 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5406 true
5407 }
5408
5409 /// Returns true when we're displaying the edit prediction popover below the cursor
5410 /// like we are not previewing and the LSP autocomplete menu is visible
5411 /// or we are in `when_holding_modifier` mode.
5412 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5413 if self.edit_prediction_preview_is_active()
5414 || !self.show_edit_predictions_in_menu()
5415 || !self.edit_predictions_enabled()
5416 {
5417 return false;
5418 }
5419
5420 if self.has_visible_completions_menu() {
5421 return true;
5422 }
5423
5424 has_completion && self.edit_prediction_requires_modifier()
5425 }
5426
5427 fn handle_modifiers_changed(
5428 &mut self,
5429 modifiers: Modifiers,
5430 position_map: &PositionMap,
5431 window: &mut Window,
5432 cx: &mut Context<Self>,
5433 ) {
5434 if self.show_edit_predictions_in_menu() {
5435 self.update_edit_prediction_preview(&modifiers, window, cx);
5436 }
5437
5438 self.update_selection_mode(&modifiers, position_map, window, cx);
5439
5440 let mouse_position = window.mouse_position();
5441 if !position_map.text_hitbox.is_hovered(window) {
5442 return;
5443 }
5444
5445 self.update_hovered_link(
5446 position_map.point_for_position(mouse_position),
5447 &position_map.snapshot,
5448 modifiers,
5449 window,
5450 cx,
5451 )
5452 }
5453
5454 fn update_selection_mode(
5455 &mut self,
5456 modifiers: &Modifiers,
5457 position_map: &PositionMap,
5458 window: &mut Window,
5459 cx: &mut Context<Self>,
5460 ) {
5461 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5462 return;
5463 }
5464
5465 let mouse_position = window.mouse_position();
5466 let point_for_position = position_map.point_for_position(mouse_position);
5467 let position = point_for_position.previous_valid;
5468
5469 self.select(
5470 SelectPhase::BeginColumnar {
5471 position,
5472 reset: false,
5473 goal_column: point_for_position.exact_unclipped.column(),
5474 },
5475 window,
5476 cx,
5477 );
5478 }
5479
5480 fn update_edit_prediction_preview(
5481 &mut self,
5482 modifiers: &Modifiers,
5483 window: &mut Window,
5484 cx: &mut Context<Self>,
5485 ) {
5486 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5487 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5488 return;
5489 };
5490
5491 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5492 if matches!(
5493 self.edit_prediction_preview,
5494 EditPredictionPreview::Inactive { .. }
5495 ) {
5496 self.edit_prediction_preview = EditPredictionPreview::Active {
5497 previous_scroll_position: None,
5498 since: Instant::now(),
5499 };
5500
5501 self.update_visible_inline_completion(window, cx);
5502 cx.notify();
5503 }
5504 } else if let EditPredictionPreview::Active {
5505 previous_scroll_position,
5506 since,
5507 } = self.edit_prediction_preview
5508 {
5509 if let (Some(previous_scroll_position), Some(position_map)) =
5510 (previous_scroll_position, self.last_position_map.as_ref())
5511 {
5512 self.set_scroll_position(
5513 previous_scroll_position
5514 .scroll_position(&position_map.snapshot.display_snapshot),
5515 window,
5516 cx,
5517 );
5518 }
5519
5520 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5521 released_too_fast: since.elapsed() < Duration::from_millis(200),
5522 };
5523 self.clear_row_highlights::<EditPredictionPreview>();
5524 self.update_visible_inline_completion(window, cx);
5525 cx.notify();
5526 }
5527 }
5528
5529 fn update_visible_inline_completion(
5530 &mut self,
5531 _window: &mut Window,
5532 cx: &mut Context<Self>,
5533 ) -> Option<()> {
5534 let selection = self.selections.newest_anchor();
5535 let cursor = selection.head();
5536 let multibuffer = self.buffer.read(cx).snapshot(cx);
5537 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5538 let excerpt_id = cursor.excerpt_id;
5539
5540 let show_in_menu = self.show_edit_predictions_in_menu();
5541 let completions_menu_has_precedence = !show_in_menu
5542 && (self.context_menu.borrow().is_some()
5543 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5544
5545 if completions_menu_has_precedence
5546 || !offset_selection.is_empty()
5547 || self
5548 .active_inline_completion
5549 .as_ref()
5550 .map_or(false, |completion| {
5551 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5552 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5553 !invalidation_range.contains(&offset_selection.head())
5554 })
5555 {
5556 self.discard_inline_completion(false, cx);
5557 return None;
5558 }
5559
5560 self.take_active_inline_completion(cx);
5561 let Some(provider) = self.edit_prediction_provider() else {
5562 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5563 return None;
5564 };
5565
5566 let (buffer, cursor_buffer_position) =
5567 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5568
5569 self.edit_prediction_settings =
5570 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5571
5572 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5573
5574 if self.edit_prediction_indent_conflict {
5575 let cursor_point = cursor.to_point(&multibuffer);
5576
5577 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5578
5579 if let Some((_, indent)) = indents.iter().next() {
5580 if indent.len == cursor_point.column {
5581 self.edit_prediction_indent_conflict = false;
5582 }
5583 }
5584 }
5585
5586 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5587 let edits = inline_completion
5588 .edits
5589 .into_iter()
5590 .flat_map(|(range, new_text)| {
5591 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5592 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5593 Some((start..end, new_text))
5594 })
5595 .collect::<Vec<_>>();
5596 if edits.is_empty() {
5597 return None;
5598 }
5599
5600 let first_edit_start = edits.first().unwrap().0.start;
5601 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5602 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5603
5604 let last_edit_end = edits.last().unwrap().0.end;
5605 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5606 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5607
5608 let cursor_row = cursor.to_point(&multibuffer).row;
5609
5610 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5611
5612 let mut inlay_ids = Vec::new();
5613 let invalidation_row_range;
5614 let move_invalidation_row_range = if cursor_row < edit_start_row {
5615 Some(cursor_row..edit_end_row)
5616 } else if cursor_row > edit_end_row {
5617 Some(edit_start_row..cursor_row)
5618 } else {
5619 None
5620 };
5621 let is_move =
5622 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5623 let completion = if is_move {
5624 invalidation_row_range =
5625 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5626 let target = first_edit_start;
5627 InlineCompletion::Move { target, snapshot }
5628 } else {
5629 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5630 && !self.inline_completions_hidden_for_vim_mode;
5631
5632 if show_completions_in_buffer {
5633 if edits
5634 .iter()
5635 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5636 {
5637 let mut inlays = Vec::new();
5638 for (range, new_text) in &edits {
5639 let inlay = Inlay::inline_completion(
5640 post_inc(&mut self.next_inlay_id),
5641 range.start,
5642 new_text.as_str(),
5643 );
5644 inlay_ids.push(inlay.id);
5645 inlays.push(inlay);
5646 }
5647
5648 self.splice_inlays(&[], inlays, cx);
5649 } else {
5650 let background_color = cx.theme().status().deleted_background;
5651 self.highlight_text::<InlineCompletionHighlight>(
5652 edits.iter().map(|(range, _)| range.clone()).collect(),
5653 HighlightStyle {
5654 background_color: Some(background_color),
5655 ..Default::default()
5656 },
5657 cx,
5658 );
5659 }
5660 }
5661
5662 invalidation_row_range = edit_start_row..edit_end_row;
5663
5664 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5665 if provider.show_tab_accept_marker() {
5666 EditDisplayMode::TabAccept
5667 } else {
5668 EditDisplayMode::Inline
5669 }
5670 } else {
5671 EditDisplayMode::DiffPopover
5672 };
5673
5674 InlineCompletion::Edit {
5675 edits,
5676 edit_preview: inline_completion.edit_preview,
5677 display_mode,
5678 snapshot,
5679 }
5680 };
5681
5682 let invalidation_range = multibuffer
5683 .anchor_before(Point::new(invalidation_row_range.start, 0))
5684 ..multibuffer.anchor_after(Point::new(
5685 invalidation_row_range.end,
5686 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5687 ));
5688
5689 self.stale_inline_completion_in_menu = None;
5690 self.active_inline_completion = Some(InlineCompletionState {
5691 inlay_ids,
5692 completion,
5693 completion_id: inline_completion.id,
5694 invalidation_range,
5695 });
5696
5697 cx.notify();
5698
5699 Some(())
5700 }
5701
5702 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5703 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5704 }
5705
5706 fn render_code_actions_indicator(
5707 &self,
5708 _style: &EditorStyle,
5709 row: DisplayRow,
5710 is_active: bool,
5711 cx: &mut Context<Self>,
5712 ) -> Option<IconButton> {
5713 if self.available_code_actions.is_some() {
5714 Some(
5715 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5716 .shape(ui::IconButtonShape::Square)
5717 .icon_size(IconSize::XSmall)
5718 .icon_color(Color::Muted)
5719 .toggle_state(is_active)
5720 .tooltip({
5721 let focus_handle = self.focus_handle.clone();
5722 move |window, cx| {
5723 Tooltip::for_action_in(
5724 "Toggle Code Actions",
5725 &ToggleCodeActions {
5726 deployed_from_indicator: None,
5727 },
5728 &focus_handle,
5729 window,
5730 cx,
5731 )
5732 }
5733 })
5734 .on_click(cx.listener(move |editor, _e, window, cx| {
5735 window.focus(&editor.focus_handle(cx));
5736 editor.toggle_code_actions(
5737 &ToggleCodeActions {
5738 deployed_from_indicator: Some(row),
5739 },
5740 window,
5741 cx,
5742 );
5743 })),
5744 )
5745 } else {
5746 None
5747 }
5748 }
5749
5750 fn clear_tasks(&mut self) {
5751 self.tasks.clear()
5752 }
5753
5754 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5755 if self.tasks.insert(key, value).is_some() {
5756 // This case should hopefully be rare, but just in case...
5757 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5758 }
5759 }
5760
5761 fn build_tasks_context(
5762 project: &Entity<Project>,
5763 buffer: &Entity<Buffer>,
5764 buffer_row: u32,
5765 tasks: &Arc<RunnableTasks>,
5766 cx: &mut Context<Self>,
5767 ) -> Task<Option<task::TaskContext>> {
5768 let position = Point::new(buffer_row, tasks.column);
5769 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5770 let location = Location {
5771 buffer: buffer.clone(),
5772 range: range_start..range_start,
5773 };
5774 // Fill in the environmental variables from the tree-sitter captures
5775 let mut captured_task_variables = TaskVariables::default();
5776 for (capture_name, value) in tasks.extra_variables.clone() {
5777 captured_task_variables.insert(
5778 task::VariableName::Custom(capture_name.into()),
5779 value.clone(),
5780 );
5781 }
5782 project.update(cx, |project, cx| {
5783 project.task_store().update(cx, |task_store, cx| {
5784 task_store.task_context_for_location(captured_task_variables, location, cx)
5785 })
5786 })
5787 }
5788
5789 pub fn spawn_nearest_task(
5790 &mut self,
5791 action: &SpawnNearestTask,
5792 window: &mut Window,
5793 cx: &mut Context<Self>,
5794 ) {
5795 let Some((workspace, _)) = self.workspace.clone() else {
5796 return;
5797 };
5798 let Some(project) = self.project.clone() else {
5799 return;
5800 };
5801
5802 // Try to find a closest, enclosing node using tree-sitter that has a
5803 // task
5804 let Some((buffer, buffer_row, tasks)) = self
5805 .find_enclosing_node_task(cx)
5806 // Or find the task that's closest in row-distance.
5807 .or_else(|| self.find_closest_task(cx))
5808 else {
5809 return;
5810 };
5811
5812 let reveal_strategy = action.reveal;
5813 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5814 cx.spawn_in(window, |_, mut cx| async move {
5815 let context = task_context.await?;
5816 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5817
5818 let resolved = resolved_task.resolved.as_mut()?;
5819 resolved.reveal = reveal_strategy;
5820
5821 workspace
5822 .update(&mut cx, |workspace, cx| {
5823 workspace::tasks::schedule_resolved_task(
5824 workspace,
5825 task_source_kind,
5826 resolved_task,
5827 false,
5828 cx,
5829 );
5830 })
5831 .ok()
5832 })
5833 .detach();
5834 }
5835
5836 fn find_closest_task(
5837 &mut self,
5838 cx: &mut Context<Self>,
5839 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5840 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5841
5842 let ((buffer_id, row), tasks) = self
5843 .tasks
5844 .iter()
5845 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5846
5847 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5848 let tasks = Arc::new(tasks.to_owned());
5849 Some((buffer, *row, tasks))
5850 }
5851
5852 fn find_enclosing_node_task(
5853 &mut self,
5854 cx: &mut Context<Self>,
5855 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5856 let snapshot = self.buffer.read(cx).snapshot(cx);
5857 let offset = self.selections.newest::<usize>(cx).head();
5858 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5859 let buffer_id = excerpt.buffer().remote_id();
5860
5861 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5862 let mut cursor = layer.node().walk();
5863
5864 while cursor.goto_first_child_for_byte(offset).is_some() {
5865 if cursor.node().end_byte() == offset {
5866 cursor.goto_next_sibling();
5867 }
5868 }
5869
5870 // Ascend to the smallest ancestor that contains the range and has a task.
5871 loop {
5872 let node = cursor.node();
5873 let node_range = node.byte_range();
5874 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5875
5876 // Check if this node contains our offset
5877 if node_range.start <= offset && node_range.end >= offset {
5878 // If it contains offset, check for task
5879 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5880 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5881 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5882 }
5883 }
5884
5885 if !cursor.goto_parent() {
5886 break;
5887 }
5888 }
5889 None
5890 }
5891
5892 fn render_run_indicator(
5893 &self,
5894 _style: &EditorStyle,
5895 is_active: bool,
5896 row: DisplayRow,
5897 cx: &mut Context<Self>,
5898 ) -> IconButton {
5899 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5900 .shape(ui::IconButtonShape::Square)
5901 .icon_size(IconSize::XSmall)
5902 .icon_color(Color::Muted)
5903 .toggle_state(is_active)
5904 .on_click(cx.listener(move |editor, _e, window, cx| {
5905 window.focus(&editor.focus_handle(cx));
5906 editor.toggle_code_actions(
5907 &ToggleCodeActions {
5908 deployed_from_indicator: Some(row),
5909 },
5910 window,
5911 cx,
5912 );
5913 }))
5914 }
5915
5916 pub fn context_menu_visible(&self) -> bool {
5917 !self.edit_prediction_preview_is_active()
5918 && self
5919 .context_menu
5920 .borrow()
5921 .as_ref()
5922 .map_or(false, |menu| menu.visible())
5923 }
5924
5925 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5926 self.context_menu
5927 .borrow()
5928 .as_ref()
5929 .map(|menu| menu.origin())
5930 }
5931
5932 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
5933 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
5934
5935 #[allow(clippy::too_many_arguments)]
5936 fn render_edit_prediction_popover(
5937 &mut self,
5938 text_bounds: &Bounds<Pixels>,
5939 content_origin: gpui::Point<Pixels>,
5940 editor_snapshot: &EditorSnapshot,
5941 visible_row_range: Range<DisplayRow>,
5942 scroll_top: f32,
5943 scroll_bottom: f32,
5944 line_layouts: &[LineWithInvisibles],
5945 line_height: Pixels,
5946 scroll_pixel_position: gpui::Point<Pixels>,
5947 newest_selection_head: Option<DisplayPoint>,
5948 editor_width: Pixels,
5949 style: &EditorStyle,
5950 window: &mut Window,
5951 cx: &mut App,
5952 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
5953 let active_inline_completion = self.active_inline_completion.as_ref()?;
5954
5955 if self.edit_prediction_visible_in_cursor_popover(true) {
5956 return None;
5957 }
5958
5959 match &active_inline_completion.completion {
5960 InlineCompletion::Move { target, .. } => {
5961 let target_display_point = target.to_display_point(editor_snapshot);
5962
5963 if self.edit_prediction_requires_modifier() {
5964 if !self.edit_prediction_preview_is_active() {
5965 return None;
5966 }
5967
5968 self.render_edit_prediction_modifier_jump_popover(
5969 text_bounds,
5970 content_origin,
5971 visible_row_range,
5972 line_layouts,
5973 line_height,
5974 scroll_pixel_position,
5975 newest_selection_head,
5976 target_display_point,
5977 window,
5978 cx,
5979 )
5980 } else {
5981 self.render_edit_prediction_eager_jump_popover(
5982 text_bounds,
5983 content_origin,
5984 editor_snapshot,
5985 visible_row_range,
5986 scroll_top,
5987 scroll_bottom,
5988 line_height,
5989 scroll_pixel_position,
5990 target_display_point,
5991 editor_width,
5992 window,
5993 cx,
5994 )
5995 }
5996 }
5997 InlineCompletion::Edit {
5998 display_mode: EditDisplayMode::Inline,
5999 ..
6000 } => None,
6001 InlineCompletion::Edit {
6002 display_mode: EditDisplayMode::TabAccept,
6003 edits,
6004 ..
6005 } => {
6006 let range = &edits.first()?.0;
6007 let target_display_point = range.end.to_display_point(editor_snapshot);
6008
6009 self.render_edit_prediction_end_of_line_popover(
6010 "Accept",
6011 editor_snapshot,
6012 visible_row_range,
6013 target_display_point,
6014 line_height,
6015 scroll_pixel_position,
6016 content_origin,
6017 editor_width,
6018 window,
6019 cx,
6020 )
6021 }
6022 InlineCompletion::Edit {
6023 edits,
6024 edit_preview,
6025 display_mode: EditDisplayMode::DiffPopover,
6026 snapshot,
6027 } => self.render_edit_prediction_diff_popover(
6028 text_bounds,
6029 content_origin,
6030 editor_snapshot,
6031 visible_row_range,
6032 line_layouts,
6033 line_height,
6034 scroll_pixel_position,
6035 newest_selection_head,
6036 editor_width,
6037 style,
6038 edits,
6039 edit_preview,
6040 snapshot,
6041 window,
6042 cx,
6043 ),
6044 }
6045 }
6046
6047 #[allow(clippy::too_many_arguments)]
6048 fn render_edit_prediction_modifier_jump_popover(
6049 &mut self,
6050 text_bounds: &Bounds<Pixels>,
6051 content_origin: gpui::Point<Pixels>,
6052 visible_row_range: Range<DisplayRow>,
6053 line_layouts: &[LineWithInvisibles],
6054 line_height: Pixels,
6055 scroll_pixel_position: gpui::Point<Pixels>,
6056 newest_selection_head: Option<DisplayPoint>,
6057 target_display_point: DisplayPoint,
6058 window: &mut Window,
6059 cx: &mut App,
6060 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6061 let scrolled_content_origin =
6062 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6063
6064 const SCROLL_PADDING_Y: Pixels = px(12.);
6065
6066 if target_display_point.row() < visible_row_range.start {
6067 return self.render_edit_prediction_scroll_popover(
6068 |_| SCROLL_PADDING_Y,
6069 IconName::ArrowUp,
6070 visible_row_range,
6071 line_layouts,
6072 newest_selection_head,
6073 scrolled_content_origin,
6074 window,
6075 cx,
6076 );
6077 } else if target_display_point.row() >= visible_row_range.end {
6078 return self.render_edit_prediction_scroll_popover(
6079 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6080 IconName::ArrowDown,
6081 visible_row_range,
6082 line_layouts,
6083 newest_selection_head,
6084 scrolled_content_origin,
6085 window,
6086 cx,
6087 );
6088 }
6089
6090 const POLE_WIDTH: Pixels = px(2.);
6091
6092 let line_layout =
6093 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6094 let target_column = target_display_point.column() as usize;
6095
6096 let target_x = line_layout.x_for_index(target_column);
6097 let target_y =
6098 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6099
6100 let flag_on_right = target_x < text_bounds.size.width / 2.;
6101
6102 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6103 border_color.l += 0.001;
6104
6105 let mut element = v_flex()
6106 .items_end()
6107 .when(flag_on_right, |el| el.items_start())
6108 .child(if flag_on_right {
6109 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6110 .rounded_bl(px(0.))
6111 .rounded_tl(px(0.))
6112 .border_l_2()
6113 .border_color(border_color)
6114 } else {
6115 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6116 .rounded_br(px(0.))
6117 .rounded_tr(px(0.))
6118 .border_r_2()
6119 .border_color(border_color)
6120 })
6121 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6122 .into_any();
6123
6124 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6125
6126 let mut origin = scrolled_content_origin + point(target_x, target_y)
6127 - point(
6128 if flag_on_right {
6129 POLE_WIDTH
6130 } else {
6131 size.width - POLE_WIDTH
6132 },
6133 size.height - line_height,
6134 );
6135
6136 origin.x = origin.x.max(content_origin.x);
6137
6138 element.prepaint_at(origin, window, cx);
6139
6140 Some((element, origin))
6141 }
6142
6143 #[allow(clippy::too_many_arguments)]
6144 fn render_edit_prediction_scroll_popover(
6145 &mut self,
6146 to_y: impl Fn(Size<Pixels>) -> Pixels,
6147 scroll_icon: IconName,
6148 visible_row_range: Range<DisplayRow>,
6149 line_layouts: &[LineWithInvisibles],
6150 newest_selection_head: Option<DisplayPoint>,
6151 scrolled_content_origin: gpui::Point<Pixels>,
6152 window: &mut Window,
6153 cx: &mut App,
6154 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6155 let mut element = self
6156 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6157 .into_any();
6158
6159 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6160
6161 let cursor = newest_selection_head?;
6162 let cursor_row_layout =
6163 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6164 let cursor_column = cursor.column() as usize;
6165
6166 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6167
6168 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6169
6170 element.prepaint_at(origin, window, cx);
6171 Some((element, origin))
6172 }
6173
6174 #[allow(clippy::too_many_arguments)]
6175 fn render_edit_prediction_eager_jump_popover(
6176 &mut self,
6177 text_bounds: &Bounds<Pixels>,
6178 content_origin: gpui::Point<Pixels>,
6179 editor_snapshot: &EditorSnapshot,
6180 visible_row_range: Range<DisplayRow>,
6181 scroll_top: f32,
6182 scroll_bottom: f32,
6183 line_height: Pixels,
6184 scroll_pixel_position: gpui::Point<Pixels>,
6185 target_display_point: DisplayPoint,
6186 editor_width: Pixels,
6187 window: &mut Window,
6188 cx: &mut App,
6189 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6190 if target_display_point.row().as_f32() < scroll_top {
6191 let mut element = self
6192 .render_edit_prediction_line_popover(
6193 "Jump to Edit",
6194 Some(IconName::ArrowUp),
6195 window,
6196 cx,
6197 )?
6198 .into_any();
6199
6200 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6201 let offset = point(
6202 (text_bounds.size.width - size.width) / 2.,
6203 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6204 );
6205
6206 let origin = text_bounds.origin + offset;
6207 element.prepaint_at(origin, window, cx);
6208 Some((element, origin))
6209 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6210 let mut element = self
6211 .render_edit_prediction_line_popover(
6212 "Jump to Edit",
6213 Some(IconName::ArrowDown),
6214 window,
6215 cx,
6216 )?
6217 .into_any();
6218
6219 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6220 let offset = point(
6221 (text_bounds.size.width - size.width) / 2.,
6222 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6223 );
6224
6225 let origin = text_bounds.origin + offset;
6226 element.prepaint_at(origin, window, cx);
6227 Some((element, origin))
6228 } else {
6229 self.render_edit_prediction_end_of_line_popover(
6230 "Jump to Edit",
6231 editor_snapshot,
6232 visible_row_range,
6233 target_display_point,
6234 line_height,
6235 scroll_pixel_position,
6236 content_origin,
6237 editor_width,
6238 window,
6239 cx,
6240 )
6241 }
6242 }
6243
6244 #[allow(clippy::too_many_arguments)]
6245 fn render_edit_prediction_end_of_line_popover(
6246 self: &mut Editor,
6247 label: &'static str,
6248 editor_snapshot: &EditorSnapshot,
6249 visible_row_range: Range<DisplayRow>,
6250 target_display_point: DisplayPoint,
6251 line_height: Pixels,
6252 scroll_pixel_position: gpui::Point<Pixels>,
6253 content_origin: gpui::Point<Pixels>,
6254 editor_width: Pixels,
6255 window: &mut Window,
6256 cx: &mut App,
6257 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6258 let target_line_end = DisplayPoint::new(
6259 target_display_point.row(),
6260 editor_snapshot.line_len(target_display_point.row()),
6261 );
6262
6263 let mut element = self
6264 .render_edit_prediction_line_popover(label, None, window, cx)?
6265 .into_any();
6266
6267 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6268
6269 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6270
6271 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6272 let mut origin = start_point
6273 + line_origin
6274 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6275 origin.x = origin.x.max(content_origin.x);
6276
6277 let max_x = content_origin.x + editor_width - size.width;
6278
6279 if origin.x > max_x {
6280 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6281
6282 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6283 origin.y += offset;
6284 IconName::ArrowUp
6285 } else {
6286 origin.y -= offset;
6287 IconName::ArrowDown
6288 };
6289
6290 element = self
6291 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6292 .into_any();
6293
6294 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6295
6296 origin.x = content_origin.x + editor_width - size.width - px(2.);
6297 }
6298
6299 element.prepaint_at(origin, window, cx);
6300 Some((element, origin))
6301 }
6302
6303 #[allow(clippy::too_many_arguments)]
6304 fn render_edit_prediction_diff_popover(
6305 self: &Editor,
6306 text_bounds: &Bounds<Pixels>,
6307 content_origin: gpui::Point<Pixels>,
6308 editor_snapshot: &EditorSnapshot,
6309 visible_row_range: Range<DisplayRow>,
6310 line_layouts: &[LineWithInvisibles],
6311 line_height: Pixels,
6312 scroll_pixel_position: gpui::Point<Pixels>,
6313 newest_selection_head: Option<DisplayPoint>,
6314 editor_width: Pixels,
6315 style: &EditorStyle,
6316 edits: &Vec<(Range<Anchor>, String)>,
6317 edit_preview: &Option<language::EditPreview>,
6318 snapshot: &language::BufferSnapshot,
6319 window: &mut Window,
6320 cx: &mut App,
6321 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6322 let edit_start = edits
6323 .first()
6324 .unwrap()
6325 .0
6326 .start
6327 .to_display_point(editor_snapshot);
6328 let edit_end = edits
6329 .last()
6330 .unwrap()
6331 .0
6332 .end
6333 .to_display_point(editor_snapshot);
6334
6335 let is_visible = visible_row_range.contains(&edit_start.row())
6336 || visible_row_range.contains(&edit_end.row());
6337 if !is_visible {
6338 return None;
6339 }
6340
6341 let highlighted_edits =
6342 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6343
6344 let styled_text = highlighted_edits.to_styled_text(&style.text);
6345 let line_count = highlighted_edits.text.lines().count();
6346
6347 const BORDER_WIDTH: Pixels = px(1.);
6348
6349 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6350 let has_keybind = keybind.is_some();
6351
6352 let mut element = h_flex()
6353 .items_start()
6354 .child(
6355 h_flex()
6356 .bg(cx.theme().colors().editor_background)
6357 .border(BORDER_WIDTH)
6358 .shadow_sm()
6359 .border_color(cx.theme().colors().border)
6360 .rounded_l_lg()
6361 .when(line_count > 1, |el| el.rounded_br_lg())
6362 .pr_1()
6363 .child(styled_text),
6364 )
6365 .child(
6366 h_flex()
6367 .h(line_height + BORDER_WIDTH * px(2.))
6368 .px_1p5()
6369 .gap_1()
6370 // Workaround: For some reason, there's a gap if we don't do this
6371 .ml(-BORDER_WIDTH)
6372 .shadow(smallvec![gpui::BoxShadow {
6373 color: gpui::black().opacity(0.05),
6374 offset: point(px(1.), px(1.)),
6375 blur_radius: px(2.),
6376 spread_radius: px(0.),
6377 }])
6378 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6379 .border(BORDER_WIDTH)
6380 .border_color(cx.theme().colors().border)
6381 .rounded_r_lg()
6382 .id("edit_prediction_diff_popover_keybind")
6383 .when(!has_keybind, |el| {
6384 let status_colors = cx.theme().status();
6385
6386 el.bg(status_colors.error_background)
6387 .border_color(status_colors.error.opacity(0.6))
6388 .child(Icon::new(IconName::Info).color(Color::Error))
6389 .cursor_default()
6390 .hoverable_tooltip(move |_window, cx| {
6391 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6392 })
6393 })
6394 .children(keybind),
6395 )
6396 .into_any();
6397
6398 let longest_row =
6399 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6400 let longest_line_width = if visible_row_range.contains(&longest_row) {
6401 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6402 } else {
6403 layout_line(
6404 longest_row,
6405 editor_snapshot,
6406 style,
6407 editor_width,
6408 |_| false,
6409 window,
6410 cx,
6411 )
6412 .width
6413 };
6414
6415 let viewport_bounds =
6416 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6417 right: -EditorElement::SCROLLBAR_WIDTH,
6418 ..Default::default()
6419 });
6420
6421 let x_after_longest =
6422 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6423 - scroll_pixel_position.x;
6424
6425 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6426
6427 // Fully visible if it can be displayed within the window (allow overlapping other
6428 // panes). However, this is only allowed if the popover starts within text_bounds.
6429 let can_position_to_the_right = x_after_longest < text_bounds.right()
6430 && x_after_longest + element_bounds.width < viewport_bounds.right();
6431
6432 let mut origin = if can_position_to_the_right {
6433 point(
6434 x_after_longest,
6435 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6436 - scroll_pixel_position.y,
6437 )
6438 } else {
6439 let cursor_row = newest_selection_head.map(|head| head.row());
6440 let above_edit = edit_start
6441 .row()
6442 .0
6443 .checked_sub(line_count as u32)
6444 .map(DisplayRow);
6445 let below_edit = Some(edit_end.row() + 1);
6446 let above_cursor =
6447 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6448 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6449
6450 // Place the edit popover adjacent to the edit if there is a location
6451 // available that is onscreen and does not obscure the cursor. Otherwise,
6452 // place it adjacent to the cursor.
6453 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6454 .into_iter()
6455 .flatten()
6456 .find(|&start_row| {
6457 let end_row = start_row + line_count as u32;
6458 visible_row_range.contains(&start_row)
6459 && visible_row_range.contains(&end_row)
6460 && cursor_row.map_or(true, |cursor_row| {
6461 !((start_row..end_row).contains(&cursor_row))
6462 })
6463 })?;
6464
6465 content_origin
6466 + point(
6467 -scroll_pixel_position.x,
6468 row_target.as_f32() * line_height - scroll_pixel_position.y,
6469 )
6470 };
6471
6472 origin.x -= BORDER_WIDTH;
6473
6474 window.defer_draw(element, origin, 1);
6475
6476 // Do not return an element, since it will already be drawn due to defer_draw.
6477 None
6478 }
6479
6480 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6481 px(30.)
6482 }
6483
6484 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6485 if self.read_only(cx) {
6486 cx.theme().players().read_only()
6487 } else {
6488 self.style.as_ref().unwrap().local_player
6489 }
6490 }
6491
6492 fn render_edit_prediction_accept_keybind(
6493 &self,
6494 window: &mut Window,
6495 cx: &App,
6496 ) -> Option<AnyElement> {
6497 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6498 let accept_keystroke = accept_binding.keystroke()?;
6499
6500 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6501
6502 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6503 Color::Accent
6504 } else {
6505 Color::Muted
6506 };
6507
6508 h_flex()
6509 .px_0p5()
6510 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6511 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6512 .text_size(TextSize::XSmall.rems(cx))
6513 .child(h_flex().children(ui::render_modifiers(
6514 &accept_keystroke.modifiers,
6515 PlatformStyle::platform(),
6516 Some(modifiers_color),
6517 Some(IconSize::XSmall.rems().into()),
6518 true,
6519 )))
6520 .when(is_platform_style_mac, |parent| {
6521 parent.child(accept_keystroke.key.clone())
6522 })
6523 .when(!is_platform_style_mac, |parent| {
6524 parent.child(
6525 Key::new(
6526 util::capitalize(&accept_keystroke.key),
6527 Some(Color::Default),
6528 )
6529 .size(Some(IconSize::XSmall.rems().into())),
6530 )
6531 })
6532 .into_any()
6533 .into()
6534 }
6535
6536 fn render_edit_prediction_line_popover(
6537 &self,
6538 label: impl Into<SharedString>,
6539 icon: Option<IconName>,
6540 window: &mut Window,
6541 cx: &App,
6542 ) -> Option<Stateful<Div>> {
6543 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6544
6545 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6546 let has_keybind = keybind.is_some();
6547
6548 let result = h_flex()
6549 .id("ep-line-popover")
6550 .py_0p5()
6551 .pl_1()
6552 .pr(padding_right)
6553 .gap_1()
6554 .rounded_md()
6555 .border_1()
6556 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6557 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6558 .shadow_sm()
6559 .when(!has_keybind, |el| {
6560 let status_colors = cx.theme().status();
6561
6562 el.bg(status_colors.error_background)
6563 .border_color(status_colors.error.opacity(0.6))
6564 .pl_2()
6565 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
6566 .cursor_default()
6567 .hoverable_tooltip(move |_window, cx| {
6568 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6569 })
6570 })
6571 .children(keybind)
6572 .child(
6573 Label::new(label)
6574 .size(LabelSize::Small)
6575 .when(!has_keybind, |el| {
6576 el.color(cx.theme().status().error.into()).strikethrough()
6577 }),
6578 )
6579 .when(!has_keybind, |el| {
6580 el.child(
6581 h_flex().ml_1().child(
6582 Icon::new(IconName::Info)
6583 .size(IconSize::Small)
6584 .color(cx.theme().status().error.into()),
6585 ),
6586 )
6587 })
6588 .when_some(icon, |element, icon| {
6589 element.child(
6590 div()
6591 .mt(px(1.5))
6592 .child(Icon::new(icon).size(IconSize::Small)),
6593 )
6594 });
6595
6596 Some(result)
6597 }
6598
6599 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6600 let accent_color = cx.theme().colors().text_accent;
6601 let editor_bg_color = cx.theme().colors().editor_background;
6602 editor_bg_color.blend(accent_color.opacity(0.1))
6603 }
6604
6605 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6606 let accent_color = cx.theme().colors().text_accent;
6607 let editor_bg_color = cx.theme().colors().editor_background;
6608 editor_bg_color.blend(accent_color.opacity(0.6))
6609 }
6610
6611 #[allow(clippy::too_many_arguments)]
6612 fn render_edit_prediction_cursor_popover(
6613 &self,
6614 min_width: Pixels,
6615 max_width: Pixels,
6616 cursor_point: Point,
6617 style: &EditorStyle,
6618 accept_keystroke: Option<&gpui::Keystroke>,
6619 _window: &Window,
6620 cx: &mut Context<Editor>,
6621 ) -> Option<AnyElement> {
6622 let provider = self.edit_prediction_provider.as_ref()?;
6623
6624 if provider.provider.needs_terms_acceptance(cx) {
6625 return Some(
6626 h_flex()
6627 .min_w(min_width)
6628 .flex_1()
6629 .px_2()
6630 .py_1()
6631 .gap_3()
6632 .elevation_2(cx)
6633 .hover(|style| style.bg(cx.theme().colors().element_hover))
6634 .id("accept-terms")
6635 .cursor_pointer()
6636 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6637 .on_click(cx.listener(|this, _event, window, cx| {
6638 cx.stop_propagation();
6639 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6640 window.dispatch_action(
6641 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6642 cx,
6643 );
6644 }))
6645 .child(
6646 h_flex()
6647 .flex_1()
6648 .gap_2()
6649 .child(Icon::new(IconName::ZedPredict))
6650 .child(Label::new("Accept Terms of Service"))
6651 .child(div().w_full())
6652 .child(
6653 Icon::new(IconName::ArrowUpRight)
6654 .color(Color::Muted)
6655 .size(IconSize::Small),
6656 )
6657 .into_any_element(),
6658 )
6659 .into_any(),
6660 );
6661 }
6662
6663 let is_refreshing = provider.provider.is_refreshing(cx);
6664
6665 fn pending_completion_container() -> Div {
6666 h_flex()
6667 .h_full()
6668 .flex_1()
6669 .gap_2()
6670 .child(Icon::new(IconName::ZedPredict))
6671 }
6672
6673 let completion = match &self.active_inline_completion {
6674 Some(prediction) => {
6675 if !self.has_visible_completions_menu() {
6676 const RADIUS: Pixels = px(6.);
6677 const BORDER_WIDTH: Pixels = px(1.);
6678
6679 return Some(
6680 h_flex()
6681 .elevation_2(cx)
6682 .border(BORDER_WIDTH)
6683 .border_color(cx.theme().colors().border)
6684 .when(accept_keystroke.is_none(), |el| {
6685 el.border_color(cx.theme().status().error)
6686 })
6687 .rounded(RADIUS)
6688 .rounded_tl(px(0.))
6689 .overflow_hidden()
6690 .child(div().px_1p5().child(match &prediction.completion {
6691 InlineCompletion::Move { target, snapshot } => {
6692 use text::ToPoint as _;
6693 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6694 {
6695 Icon::new(IconName::ZedPredictDown)
6696 } else {
6697 Icon::new(IconName::ZedPredictUp)
6698 }
6699 }
6700 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6701 }))
6702 .child(
6703 h_flex()
6704 .gap_1()
6705 .py_1()
6706 .px_2()
6707 .rounded_r(RADIUS - BORDER_WIDTH)
6708 .border_l_1()
6709 .border_color(cx.theme().colors().border)
6710 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6711 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6712 el.child(
6713 Label::new("Hold")
6714 .size(LabelSize::Small)
6715 .when(accept_keystroke.is_none(), |el| {
6716 el.strikethrough()
6717 })
6718 .line_height_style(LineHeightStyle::UiLabel),
6719 )
6720 })
6721 .id("edit_prediction_cursor_popover_keybind")
6722 .when(accept_keystroke.is_none(), |el| {
6723 let status_colors = cx.theme().status();
6724
6725 el.bg(status_colors.error_background)
6726 .border_color(status_colors.error.opacity(0.6))
6727 .child(Icon::new(IconName::Info).color(Color::Error))
6728 .cursor_default()
6729 .hoverable_tooltip(move |_window, cx| {
6730 cx.new(|_| MissingEditPredictionKeybindingTooltip)
6731 .into()
6732 })
6733 })
6734 .when_some(
6735 accept_keystroke.as_ref(),
6736 |el, accept_keystroke| {
6737 el.child(h_flex().children(ui::render_modifiers(
6738 &accept_keystroke.modifiers,
6739 PlatformStyle::platform(),
6740 Some(Color::Default),
6741 Some(IconSize::XSmall.rems().into()),
6742 false,
6743 )))
6744 },
6745 ),
6746 )
6747 .into_any(),
6748 );
6749 }
6750
6751 self.render_edit_prediction_cursor_popover_preview(
6752 prediction,
6753 cursor_point,
6754 style,
6755 cx,
6756 )?
6757 }
6758
6759 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6760 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6761 stale_completion,
6762 cursor_point,
6763 style,
6764 cx,
6765 )?,
6766
6767 None => {
6768 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6769 }
6770 },
6771
6772 None => pending_completion_container().child(Label::new("No Prediction")),
6773 };
6774
6775 let completion = if is_refreshing {
6776 completion
6777 .with_animation(
6778 "loading-completion",
6779 Animation::new(Duration::from_secs(2))
6780 .repeat()
6781 .with_easing(pulsating_between(0.4, 0.8)),
6782 |label, delta| label.opacity(delta),
6783 )
6784 .into_any_element()
6785 } else {
6786 completion.into_any_element()
6787 };
6788
6789 let has_completion = self.active_inline_completion.is_some();
6790
6791 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6792 Some(
6793 h_flex()
6794 .min_w(min_width)
6795 .max_w(max_width)
6796 .flex_1()
6797 .elevation_2(cx)
6798 .border_color(cx.theme().colors().border)
6799 .child(
6800 div()
6801 .flex_1()
6802 .py_1()
6803 .px_2()
6804 .overflow_hidden()
6805 .child(completion),
6806 )
6807 .when_some(accept_keystroke, |el, accept_keystroke| {
6808 if !accept_keystroke.modifiers.modified() {
6809 return el;
6810 }
6811
6812 el.child(
6813 h_flex()
6814 .h_full()
6815 .border_l_1()
6816 .rounded_r_lg()
6817 .border_color(cx.theme().colors().border)
6818 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6819 .gap_1()
6820 .py_1()
6821 .px_2()
6822 .child(
6823 h_flex()
6824 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6825 .when(is_platform_style_mac, |parent| parent.gap_1())
6826 .child(h_flex().children(ui::render_modifiers(
6827 &accept_keystroke.modifiers,
6828 PlatformStyle::platform(),
6829 Some(if !has_completion {
6830 Color::Muted
6831 } else {
6832 Color::Default
6833 }),
6834 None,
6835 false,
6836 ))),
6837 )
6838 .child(Label::new("Preview").into_any_element())
6839 .opacity(if has_completion { 1.0 } else { 0.4 }),
6840 )
6841 })
6842 .into_any(),
6843 )
6844 }
6845
6846 fn render_edit_prediction_cursor_popover_preview(
6847 &self,
6848 completion: &InlineCompletionState,
6849 cursor_point: Point,
6850 style: &EditorStyle,
6851 cx: &mut Context<Editor>,
6852 ) -> Option<Div> {
6853 use text::ToPoint as _;
6854
6855 fn render_relative_row_jump(
6856 prefix: impl Into<String>,
6857 current_row: u32,
6858 target_row: u32,
6859 ) -> Div {
6860 let (row_diff, arrow) = if target_row < current_row {
6861 (current_row - target_row, IconName::ArrowUp)
6862 } else {
6863 (target_row - current_row, IconName::ArrowDown)
6864 };
6865
6866 h_flex()
6867 .child(
6868 Label::new(format!("{}{}", prefix.into(), row_diff))
6869 .color(Color::Muted)
6870 .size(LabelSize::Small),
6871 )
6872 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6873 }
6874
6875 match &completion.completion {
6876 InlineCompletion::Move {
6877 target, snapshot, ..
6878 } => Some(
6879 h_flex()
6880 .px_2()
6881 .gap_2()
6882 .flex_1()
6883 .child(
6884 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6885 Icon::new(IconName::ZedPredictDown)
6886 } else {
6887 Icon::new(IconName::ZedPredictUp)
6888 },
6889 )
6890 .child(Label::new("Jump to Edit")),
6891 ),
6892
6893 InlineCompletion::Edit {
6894 edits,
6895 edit_preview,
6896 snapshot,
6897 display_mode: _,
6898 } => {
6899 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6900
6901 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6902 &snapshot,
6903 &edits,
6904 edit_preview.as_ref()?,
6905 true,
6906 cx,
6907 )
6908 .first_line_preview();
6909
6910 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6911 .with_default_highlights(&style.text, highlighted_edits.highlights);
6912
6913 let preview = h_flex()
6914 .gap_1()
6915 .min_w_16()
6916 .child(styled_text)
6917 .when(has_more_lines, |parent| parent.child("…"));
6918
6919 let left = if first_edit_row != cursor_point.row {
6920 render_relative_row_jump("", cursor_point.row, first_edit_row)
6921 .into_any_element()
6922 } else {
6923 Icon::new(IconName::ZedPredict).into_any_element()
6924 };
6925
6926 Some(
6927 h_flex()
6928 .h_full()
6929 .flex_1()
6930 .gap_2()
6931 .pr_1()
6932 .overflow_x_hidden()
6933 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6934 .child(left)
6935 .child(preview),
6936 )
6937 }
6938 }
6939 }
6940
6941 fn render_context_menu(
6942 &self,
6943 style: &EditorStyle,
6944 max_height_in_lines: u32,
6945 y_flipped: bool,
6946 window: &mut Window,
6947 cx: &mut Context<Editor>,
6948 ) -> Option<AnyElement> {
6949 let menu = self.context_menu.borrow();
6950 let menu = menu.as_ref()?;
6951 if !menu.visible() {
6952 return None;
6953 };
6954 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6955 }
6956
6957 fn render_context_menu_aside(
6958 &mut self,
6959 max_size: Size<Pixels>,
6960 window: &mut Window,
6961 cx: &mut Context<Editor>,
6962 ) -> Option<AnyElement> {
6963 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6964 if menu.visible() {
6965 menu.render_aside(self, max_size, window, cx)
6966 } else {
6967 None
6968 }
6969 })
6970 }
6971
6972 fn hide_context_menu(
6973 &mut self,
6974 window: &mut Window,
6975 cx: &mut Context<Self>,
6976 ) -> Option<CodeContextMenu> {
6977 cx.notify();
6978 self.completion_tasks.clear();
6979 let context_menu = self.context_menu.borrow_mut().take();
6980 self.stale_inline_completion_in_menu.take();
6981 self.update_visible_inline_completion(window, cx);
6982 context_menu
6983 }
6984
6985 fn show_snippet_choices(
6986 &mut self,
6987 choices: &Vec<String>,
6988 selection: Range<Anchor>,
6989 cx: &mut Context<Self>,
6990 ) {
6991 if selection.start.buffer_id.is_none() {
6992 return;
6993 }
6994 let buffer_id = selection.start.buffer_id.unwrap();
6995 let buffer = self.buffer().read(cx).buffer(buffer_id);
6996 let id = post_inc(&mut self.next_completion_id);
6997
6998 if let Some(buffer) = buffer {
6999 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7000 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7001 ));
7002 }
7003 }
7004
7005 pub fn insert_snippet(
7006 &mut self,
7007 insertion_ranges: &[Range<usize>],
7008 snippet: Snippet,
7009 window: &mut Window,
7010 cx: &mut Context<Self>,
7011 ) -> Result<()> {
7012 struct Tabstop<T> {
7013 is_end_tabstop: bool,
7014 ranges: Vec<Range<T>>,
7015 choices: Option<Vec<String>>,
7016 }
7017
7018 let tabstops = self.buffer.update(cx, |buffer, cx| {
7019 let snippet_text: Arc<str> = snippet.text.clone().into();
7020 buffer.edit(
7021 insertion_ranges
7022 .iter()
7023 .cloned()
7024 .map(|range| (range, snippet_text.clone())),
7025 Some(AutoindentMode::EachLine),
7026 cx,
7027 );
7028
7029 let snapshot = &*buffer.read(cx);
7030 let snippet = &snippet;
7031 snippet
7032 .tabstops
7033 .iter()
7034 .map(|tabstop| {
7035 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7036 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7037 });
7038 let mut tabstop_ranges = tabstop
7039 .ranges
7040 .iter()
7041 .flat_map(|tabstop_range| {
7042 let mut delta = 0_isize;
7043 insertion_ranges.iter().map(move |insertion_range| {
7044 let insertion_start = insertion_range.start as isize + delta;
7045 delta +=
7046 snippet.text.len() as isize - insertion_range.len() as isize;
7047
7048 let start = ((insertion_start + tabstop_range.start) as usize)
7049 .min(snapshot.len());
7050 let end = ((insertion_start + tabstop_range.end) as usize)
7051 .min(snapshot.len());
7052 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7053 })
7054 })
7055 .collect::<Vec<_>>();
7056 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7057
7058 Tabstop {
7059 is_end_tabstop,
7060 ranges: tabstop_ranges,
7061 choices: tabstop.choices.clone(),
7062 }
7063 })
7064 .collect::<Vec<_>>()
7065 });
7066 if let Some(tabstop) = tabstops.first() {
7067 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7068 s.select_ranges(tabstop.ranges.iter().cloned());
7069 });
7070
7071 if let Some(choices) = &tabstop.choices {
7072 if let Some(selection) = tabstop.ranges.first() {
7073 self.show_snippet_choices(choices, selection.clone(), cx)
7074 }
7075 }
7076
7077 // If we're already at the last tabstop and it's at the end of the snippet,
7078 // we're done, we don't need to keep the state around.
7079 if !tabstop.is_end_tabstop {
7080 let choices = tabstops
7081 .iter()
7082 .map(|tabstop| tabstop.choices.clone())
7083 .collect();
7084
7085 let ranges = tabstops
7086 .into_iter()
7087 .map(|tabstop| tabstop.ranges)
7088 .collect::<Vec<_>>();
7089
7090 self.snippet_stack.push(SnippetState {
7091 active_index: 0,
7092 ranges,
7093 choices,
7094 });
7095 }
7096
7097 // Check whether the just-entered snippet ends with an auto-closable bracket.
7098 if self.autoclose_regions.is_empty() {
7099 let snapshot = self.buffer.read(cx).snapshot(cx);
7100 for selection in &mut self.selections.all::<Point>(cx) {
7101 let selection_head = selection.head();
7102 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7103 continue;
7104 };
7105
7106 let mut bracket_pair = None;
7107 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7108 let prev_chars = snapshot
7109 .reversed_chars_at(selection_head)
7110 .collect::<String>();
7111 for (pair, enabled) in scope.brackets() {
7112 if enabled
7113 && pair.close
7114 && prev_chars.starts_with(pair.start.as_str())
7115 && next_chars.starts_with(pair.end.as_str())
7116 {
7117 bracket_pair = Some(pair.clone());
7118 break;
7119 }
7120 }
7121 if let Some(pair) = bracket_pair {
7122 let start = snapshot.anchor_after(selection_head);
7123 let end = snapshot.anchor_after(selection_head);
7124 self.autoclose_regions.push(AutocloseRegion {
7125 selection_id: selection.id,
7126 range: start..end,
7127 pair,
7128 });
7129 }
7130 }
7131 }
7132 }
7133 Ok(())
7134 }
7135
7136 pub fn move_to_next_snippet_tabstop(
7137 &mut self,
7138 window: &mut Window,
7139 cx: &mut Context<Self>,
7140 ) -> bool {
7141 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7142 }
7143
7144 pub fn move_to_prev_snippet_tabstop(
7145 &mut self,
7146 window: &mut Window,
7147 cx: &mut Context<Self>,
7148 ) -> bool {
7149 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7150 }
7151
7152 pub fn move_to_snippet_tabstop(
7153 &mut self,
7154 bias: Bias,
7155 window: &mut Window,
7156 cx: &mut Context<Self>,
7157 ) -> bool {
7158 if let Some(mut snippet) = self.snippet_stack.pop() {
7159 match bias {
7160 Bias::Left => {
7161 if snippet.active_index > 0 {
7162 snippet.active_index -= 1;
7163 } else {
7164 self.snippet_stack.push(snippet);
7165 return false;
7166 }
7167 }
7168 Bias::Right => {
7169 if snippet.active_index + 1 < snippet.ranges.len() {
7170 snippet.active_index += 1;
7171 } else {
7172 self.snippet_stack.push(snippet);
7173 return false;
7174 }
7175 }
7176 }
7177 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7178 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7179 s.select_anchor_ranges(current_ranges.iter().cloned())
7180 });
7181
7182 if let Some(choices) = &snippet.choices[snippet.active_index] {
7183 if let Some(selection) = current_ranges.first() {
7184 self.show_snippet_choices(&choices, selection.clone(), cx);
7185 }
7186 }
7187
7188 // If snippet state is not at the last tabstop, push it back on the stack
7189 if snippet.active_index + 1 < snippet.ranges.len() {
7190 self.snippet_stack.push(snippet);
7191 }
7192 return true;
7193 }
7194 }
7195
7196 false
7197 }
7198
7199 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7200 self.transact(window, cx, |this, window, cx| {
7201 this.select_all(&SelectAll, window, cx);
7202 this.insert("", window, cx);
7203 });
7204 }
7205
7206 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7207 self.transact(window, cx, |this, window, cx| {
7208 this.select_autoclose_pair(window, cx);
7209 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7210 if !this.linked_edit_ranges.is_empty() {
7211 let selections = this.selections.all::<MultiBufferPoint>(cx);
7212 let snapshot = this.buffer.read(cx).snapshot(cx);
7213
7214 for selection in selections.iter() {
7215 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7216 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7217 if selection_start.buffer_id != selection_end.buffer_id {
7218 continue;
7219 }
7220 if let Some(ranges) =
7221 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7222 {
7223 for (buffer, entries) in ranges {
7224 linked_ranges.entry(buffer).or_default().extend(entries);
7225 }
7226 }
7227 }
7228 }
7229
7230 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7231 if !this.selections.line_mode {
7232 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7233 for selection in &mut selections {
7234 if selection.is_empty() {
7235 let old_head = selection.head();
7236 let mut new_head =
7237 movement::left(&display_map, old_head.to_display_point(&display_map))
7238 .to_point(&display_map);
7239 if let Some((buffer, line_buffer_range)) = display_map
7240 .buffer_snapshot
7241 .buffer_line_for_row(MultiBufferRow(old_head.row))
7242 {
7243 let indent_size =
7244 buffer.indent_size_for_line(line_buffer_range.start.row);
7245 let indent_len = match indent_size.kind {
7246 IndentKind::Space => {
7247 buffer.settings_at(line_buffer_range.start, cx).tab_size
7248 }
7249 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7250 };
7251 if old_head.column <= indent_size.len && old_head.column > 0 {
7252 let indent_len = indent_len.get();
7253 new_head = cmp::min(
7254 new_head,
7255 MultiBufferPoint::new(
7256 old_head.row,
7257 ((old_head.column - 1) / indent_len) * indent_len,
7258 ),
7259 );
7260 }
7261 }
7262
7263 selection.set_head(new_head, SelectionGoal::None);
7264 }
7265 }
7266 }
7267
7268 this.signature_help_state.set_backspace_pressed(true);
7269 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7270 s.select(selections)
7271 });
7272 this.insert("", window, cx);
7273 let empty_str: Arc<str> = Arc::from("");
7274 for (buffer, edits) in linked_ranges {
7275 let snapshot = buffer.read(cx).snapshot();
7276 use text::ToPoint as TP;
7277
7278 let edits = edits
7279 .into_iter()
7280 .map(|range| {
7281 let end_point = TP::to_point(&range.end, &snapshot);
7282 let mut start_point = TP::to_point(&range.start, &snapshot);
7283
7284 if end_point == start_point {
7285 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7286 .saturating_sub(1);
7287 start_point =
7288 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7289 };
7290
7291 (start_point..end_point, empty_str.clone())
7292 })
7293 .sorted_by_key(|(range, _)| range.start)
7294 .collect::<Vec<_>>();
7295 buffer.update(cx, |this, cx| {
7296 this.edit(edits, None, cx);
7297 })
7298 }
7299 this.refresh_inline_completion(true, false, window, cx);
7300 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7301 });
7302 }
7303
7304 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7305 self.transact(window, cx, |this, window, cx| {
7306 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7307 let line_mode = s.line_mode;
7308 s.move_with(|map, selection| {
7309 if selection.is_empty() && !line_mode {
7310 let cursor = movement::right(map, selection.head());
7311 selection.end = cursor;
7312 selection.reversed = true;
7313 selection.goal = SelectionGoal::None;
7314 }
7315 })
7316 });
7317 this.insert("", window, cx);
7318 this.refresh_inline_completion(true, false, window, cx);
7319 });
7320 }
7321
7322 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7323 if self.move_to_prev_snippet_tabstop(window, cx) {
7324 return;
7325 }
7326
7327 self.outdent(&Outdent, window, cx);
7328 }
7329
7330 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7331 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7332 return;
7333 }
7334
7335 let mut selections = self.selections.all_adjusted(cx);
7336 let buffer = self.buffer.read(cx);
7337 let snapshot = buffer.snapshot(cx);
7338 let rows_iter = selections.iter().map(|s| s.head().row);
7339 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7340
7341 let mut edits = Vec::new();
7342 let mut prev_edited_row = 0;
7343 let mut row_delta = 0;
7344 for selection in &mut selections {
7345 if selection.start.row != prev_edited_row {
7346 row_delta = 0;
7347 }
7348 prev_edited_row = selection.end.row;
7349
7350 // If the selection is non-empty, then increase the indentation of the selected lines.
7351 if !selection.is_empty() {
7352 row_delta =
7353 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7354 continue;
7355 }
7356
7357 // If the selection is empty and the cursor is in the leading whitespace before the
7358 // suggested indentation, then auto-indent the line.
7359 let cursor = selection.head();
7360 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7361 if let Some(suggested_indent) =
7362 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7363 {
7364 if cursor.column < suggested_indent.len
7365 && cursor.column <= current_indent.len
7366 && current_indent.len <= suggested_indent.len
7367 {
7368 selection.start = Point::new(cursor.row, suggested_indent.len);
7369 selection.end = selection.start;
7370 if row_delta == 0 {
7371 edits.extend(Buffer::edit_for_indent_size_adjustment(
7372 cursor.row,
7373 current_indent,
7374 suggested_indent,
7375 ));
7376 row_delta = suggested_indent.len - current_indent.len;
7377 }
7378 continue;
7379 }
7380 }
7381
7382 // Otherwise, insert a hard or soft tab.
7383 let settings = buffer.language_settings_at(cursor, cx);
7384 let tab_size = if settings.hard_tabs {
7385 IndentSize::tab()
7386 } else {
7387 let tab_size = settings.tab_size.get();
7388 let char_column = snapshot
7389 .text_for_range(Point::new(cursor.row, 0)..cursor)
7390 .flat_map(str::chars)
7391 .count()
7392 + row_delta as usize;
7393 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7394 IndentSize::spaces(chars_to_next_tab_stop)
7395 };
7396 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7397 selection.end = selection.start;
7398 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7399 row_delta += tab_size.len;
7400 }
7401
7402 self.transact(window, cx, |this, window, cx| {
7403 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7404 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7405 s.select(selections)
7406 });
7407 this.refresh_inline_completion(true, false, window, cx);
7408 });
7409 }
7410
7411 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7412 if self.read_only(cx) {
7413 return;
7414 }
7415 let mut selections = self.selections.all::<Point>(cx);
7416 let mut prev_edited_row = 0;
7417 let mut row_delta = 0;
7418 let mut edits = Vec::new();
7419 let buffer = self.buffer.read(cx);
7420 let snapshot = buffer.snapshot(cx);
7421 for selection in &mut selections {
7422 if selection.start.row != prev_edited_row {
7423 row_delta = 0;
7424 }
7425 prev_edited_row = selection.end.row;
7426
7427 row_delta =
7428 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7429 }
7430
7431 self.transact(window, cx, |this, window, cx| {
7432 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7433 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7434 s.select(selections)
7435 });
7436 });
7437 }
7438
7439 fn indent_selection(
7440 buffer: &MultiBuffer,
7441 snapshot: &MultiBufferSnapshot,
7442 selection: &mut Selection<Point>,
7443 edits: &mut Vec<(Range<Point>, String)>,
7444 delta_for_start_row: u32,
7445 cx: &App,
7446 ) -> u32 {
7447 let settings = buffer.language_settings_at(selection.start, cx);
7448 let tab_size = settings.tab_size.get();
7449 let indent_kind = if settings.hard_tabs {
7450 IndentKind::Tab
7451 } else {
7452 IndentKind::Space
7453 };
7454 let mut start_row = selection.start.row;
7455 let mut end_row = selection.end.row + 1;
7456
7457 // If a selection ends at the beginning of a line, don't indent
7458 // that last line.
7459 if selection.end.column == 0 && selection.end.row > selection.start.row {
7460 end_row -= 1;
7461 }
7462
7463 // Avoid re-indenting a row that has already been indented by a
7464 // previous selection, but still update this selection's column
7465 // to reflect that indentation.
7466 if delta_for_start_row > 0 {
7467 start_row += 1;
7468 selection.start.column += delta_for_start_row;
7469 if selection.end.row == selection.start.row {
7470 selection.end.column += delta_for_start_row;
7471 }
7472 }
7473
7474 let mut delta_for_end_row = 0;
7475 let has_multiple_rows = start_row + 1 != end_row;
7476 for row in start_row..end_row {
7477 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7478 let indent_delta = match (current_indent.kind, indent_kind) {
7479 (IndentKind::Space, IndentKind::Space) => {
7480 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7481 IndentSize::spaces(columns_to_next_tab_stop)
7482 }
7483 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7484 (_, IndentKind::Tab) => IndentSize::tab(),
7485 };
7486
7487 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7488 0
7489 } else {
7490 selection.start.column
7491 };
7492 let row_start = Point::new(row, start);
7493 edits.push((
7494 row_start..row_start,
7495 indent_delta.chars().collect::<String>(),
7496 ));
7497
7498 // Update this selection's endpoints to reflect the indentation.
7499 if row == selection.start.row {
7500 selection.start.column += indent_delta.len;
7501 }
7502 if row == selection.end.row {
7503 selection.end.column += indent_delta.len;
7504 delta_for_end_row = indent_delta.len;
7505 }
7506 }
7507
7508 if selection.start.row == selection.end.row {
7509 delta_for_start_row + delta_for_end_row
7510 } else {
7511 delta_for_end_row
7512 }
7513 }
7514
7515 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7516 if self.read_only(cx) {
7517 return;
7518 }
7519 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7520 let selections = self.selections.all::<Point>(cx);
7521 let mut deletion_ranges = Vec::new();
7522 let mut last_outdent = None;
7523 {
7524 let buffer = self.buffer.read(cx);
7525 let snapshot = buffer.snapshot(cx);
7526 for selection in &selections {
7527 let settings = buffer.language_settings_at(selection.start, cx);
7528 let tab_size = settings.tab_size.get();
7529 let mut rows = selection.spanned_rows(false, &display_map);
7530
7531 // Avoid re-outdenting a row that has already been outdented by a
7532 // previous selection.
7533 if let Some(last_row) = last_outdent {
7534 if last_row == rows.start {
7535 rows.start = rows.start.next_row();
7536 }
7537 }
7538 let has_multiple_rows = rows.len() > 1;
7539 for row in rows.iter_rows() {
7540 let indent_size = snapshot.indent_size_for_line(row);
7541 if indent_size.len > 0 {
7542 let deletion_len = match indent_size.kind {
7543 IndentKind::Space => {
7544 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7545 if columns_to_prev_tab_stop == 0 {
7546 tab_size
7547 } else {
7548 columns_to_prev_tab_stop
7549 }
7550 }
7551 IndentKind::Tab => 1,
7552 };
7553 let start = if has_multiple_rows
7554 || deletion_len > selection.start.column
7555 || indent_size.len < selection.start.column
7556 {
7557 0
7558 } else {
7559 selection.start.column - deletion_len
7560 };
7561 deletion_ranges.push(
7562 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7563 );
7564 last_outdent = Some(row);
7565 }
7566 }
7567 }
7568 }
7569
7570 self.transact(window, cx, |this, window, cx| {
7571 this.buffer.update(cx, |buffer, cx| {
7572 let empty_str: Arc<str> = Arc::default();
7573 buffer.edit(
7574 deletion_ranges
7575 .into_iter()
7576 .map(|range| (range, empty_str.clone())),
7577 None,
7578 cx,
7579 );
7580 });
7581 let selections = this.selections.all::<usize>(cx);
7582 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7583 s.select(selections)
7584 });
7585 });
7586 }
7587
7588 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7589 if self.read_only(cx) {
7590 return;
7591 }
7592 let selections = self
7593 .selections
7594 .all::<usize>(cx)
7595 .into_iter()
7596 .map(|s| s.range());
7597
7598 self.transact(window, cx, |this, window, cx| {
7599 this.buffer.update(cx, |buffer, cx| {
7600 buffer.autoindent_ranges(selections, cx);
7601 });
7602 let selections = this.selections.all::<usize>(cx);
7603 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7604 s.select(selections)
7605 });
7606 });
7607 }
7608
7609 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7611 let selections = self.selections.all::<Point>(cx);
7612
7613 let mut new_cursors = Vec::new();
7614 let mut edit_ranges = Vec::new();
7615 let mut selections = selections.iter().peekable();
7616 while let Some(selection) = selections.next() {
7617 let mut rows = selection.spanned_rows(false, &display_map);
7618 let goal_display_column = selection.head().to_display_point(&display_map).column();
7619
7620 // Accumulate contiguous regions of rows that we want to delete.
7621 while let Some(next_selection) = selections.peek() {
7622 let next_rows = next_selection.spanned_rows(false, &display_map);
7623 if next_rows.start <= rows.end {
7624 rows.end = next_rows.end;
7625 selections.next().unwrap();
7626 } else {
7627 break;
7628 }
7629 }
7630
7631 let buffer = &display_map.buffer_snapshot;
7632 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7633 let edit_end;
7634 let cursor_buffer_row;
7635 if buffer.max_point().row >= rows.end.0 {
7636 // If there's a line after the range, delete the \n from the end of the row range
7637 // and position the cursor on the next line.
7638 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7639 cursor_buffer_row = rows.end;
7640 } else {
7641 // If there isn't a line after the range, delete the \n from the line before the
7642 // start of the row range and position the cursor there.
7643 edit_start = edit_start.saturating_sub(1);
7644 edit_end = buffer.len();
7645 cursor_buffer_row = rows.start.previous_row();
7646 }
7647
7648 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7649 *cursor.column_mut() =
7650 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7651
7652 new_cursors.push((
7653 selection.id,
7654 buffer.anchor_after(cursor.to_point(&display_map)),
7655 ));
7656 edit_ranges.push(edit_start..edit_end);
7657 }
7658
7659 self.transact(window, cx, |this, window, cx| {
7660 let buffer = this.buffer.update(cx, |buffer, cx| {
7661 let empty_str: Arc<str> = Arc::default();
7662 buffer.edit(
7663 edit_ranges
7664 .into_iter()
7665 .map(|range| (range, empty_str.clone())),
7666 None,
7667 cx,
7668 );
7669 buffer.snapshot(cx)
7670 });
7671 let new_selections = new_cursors
7672 .into_iter()
7673 .map(|(id, cursor)| {
7674 let cursor = cursor.to_point(&buffer);
7675 Selection {
7676 id,
7677 start: cursor,
7678 end: cursor,
7679 reversed: false,
7680 goal: SelectionGoal::None,
7681 }
7682 })
7683 .collect();
7684
7685 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7686 s.select(new_selections);
7687 });
7688 });
7689 }
7690
7691 pub fn join_lines_impl(
7692 &mut self,
7693 insert_whitespace: bool,
7694 window: &mut Window,
7695 cx: &mut Context<Self>,
7696 ) {
7697 if self.read_only(cx) {
7698 return;
7699 }
7700 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7701 for selection in self.selections.all::<Point>(cx) {
7702 let start = MultiBufferRow(selection.start.row);
7703 // Treat single line selections as if they include the next line. Otherwise this action
7704 // would do nothing for single line selections individual cursors.
7705 let end = if selection.start.row == selection.end.row {
7706 MultiBufferRow(selection.start.row + 1)
7707 } else {
7708 MultiBufferRow(selection.end.row)
7709 };
7710
7711 if let Some(last_row_range) = row_ranges.last_mut() {
7712 if start <= last_row_range.end {
7713 last_row_range.end = end;
7714 continue;
7715 }
7716 }
7717 row_ranges.push(start..end);
7718 }
7719
7720 let snapshot = self.buffer.read(cx).snapshot(cx);
7721 let mut cursor_positions = Vec::new();
7722 for row_range in &row_ranges {
7723 let anchor = snapshot.anchor_before(Point::new(
7724 row_range.end.previous_row().0,
7725 snapshot.line_len(row_range.end.previous_row()),
7726 ));
7727 cursor_positions.push(anchor..anchor);
7728 }
7729
7730 self.transact(window, cx, |this, window, cx| {
7731 for row_range in row_ranges.into_iter().rev() {
7732 for row in row_range.iter_rows().rev() {
7733 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7734 let next_line_row = row.next_row();
7735 let indent = snapshot.indent_size_for_line(next_line_row);
7736 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7737
7738 let replace =
7739 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7740 " "
7741 } else {
7742 ""
7743 };
7744
7745 this.buffer.update(cx, |buffer, cx| {
7746 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7747 });
7748 }
7749 }
7750
7751 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7752 s.select_anchor_ranges(cursor_positions)
7753 });
7754 });
7755 }
7756
7757 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7758 self.join_lines_impl(true, window, cx);
7759 }
7760
7761 pub fn sort_lines_case_sensitive(
7762 &mut self,
7763 _: &SortLinesCaseSensitive,
7764 window: &mut Window,
7765 cx: &mut Context<Self>,
7766 ) {
7767 self.manipulate_lines(window, cx, |lines| lines.sort())
7768 }
7769
7770 pub fn sort_lines_case_insensitive(
7771 &mut self,
7772 _: &SortLinesCaseInsensitive,
7773 window: &mut Window,
7774 cx: &mut Context<Self>,
7775 ) {
7776 self.manipulate_lines(window, cx, |lines| {
7777 lines.sort_by_key(|line| line.to_lowercase())
7778 })
7779 }
7780
7781 pub fn unique_lines_case_insensitive(
7782 &mut self,
7783 _: &UniqueLinesCaseInsensitive,
7784 window: &mut Window,
7785 cx: &mut Context<Self>,
7786 ) {
7787 self.manipulate_lines(window, cx, |lines| {
7788 let mut seen = HashSet::default();
7789 lines.retain(|line| seen.insert(line.to_lowercase()));
7790 })
7791 }
7792
7793 pub fn unique_lines_case_sensitive(
7794 &mut self,
7795 _: &UniqueLinesCaseSensitive,
7796 window: &mut Window,
7797 cx: &mut Context<Self>,
7798 ) {
7799 self.manipulate_lines(window, cx, |lines| {
7800 let mut seen = HashSet::default();
7801 lines.retain(|line| seen.insert(*line));
7802 })
7803 }
7804
7805 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7806 let Some(project) = self.project.clone() else {
7807 return;
7808 };
7809 self.reload(project, window, cx)
7810 .detach_and_notify_err(window, cx);
7811 }
7812
7813 pub fn restore_file(
7814 &mut self,
7815 _: &::git::RestoreFile,
7816 window: &mut Window,
7817 cx: &mut Context<Self>,
7818 ) {
7819 let mut buffer_ids = HashSet::default();
7820 let snapshot = self.buffer().read(cx).snapshot(cx);
7821 for selection in self.selections.all::<usize>(cx) {
7822 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7823 }
7824
7825 let buffer = self.buffer().read(cx);
7826 let ranges = buffer_ids
7827 .into_iter()
7828 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7829 .collect::<Vec<_>>();
7830
7831 self.restore_hunks_in_ranges(ranges, window, cx);
7832 }
7833
7834 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7835 let selections = self
7836 .selections
7837 .all(cx)
7838 .into_iter()
7839 .map(|s| s.range())
7840 .collect();
7841 self.restore_hunks_in_ranges(selections, window, cx);
7842 }
7843
7844 fn restore_hunks_in_ranges(
7845 &mut self,
7846 ranges: Vec<Range<Point>>,
7847 window: &mut Window,
7848 cx: &mut Context<Editor>,
7849 ) {
7850 let mut revert_changes = HashMap::default();
7851 let chunk_by = self
7852 .snapshot(window, cx)
7853 .hunks_for_ranges(ranges)
7854 .into_iter()
7855 .chunk_by(|hunk| hunk.buffer_id);
7856 for (buffer_id, hunks) in &chunk_by {
7857 let hunks = hunks.collect::<Vec<_>>();
7858 for hunk in &hunks {
7859 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7860 }
7861 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
7862 }
7863 drop(chunk_by);
7864 if !revert_changes.is_empty() {
7865 self.transact(window, cx, |editor, window, cx| {
7866 editor.restore(revert_changes, window, cx);
7867 });
7868 }
7869 }
7870
7871 pub fn open_active_item_in_terminal(
7872 &mut self,
7873 _: &OpenInTerminal,
7874 window: &mut Window,
7875 cx: &mut Context<Self>,
7876 ) {
7877 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7878 let project_path = buffer.read(cx).project_path(cx)?;
7879 let project = self.project.as_ref()?.read(cx);
7880 let entry = project.entry_for_path(&project_path, cx)?;
7881 let parent = match &entry.canonical_path {
7882 Some(canonical_path) => canonical_path.to_path_buf(),
7883 None => project.absolute_path(&project_path, cx)?,
7884 }
7885 .parent()?
7886 .to_path_buf();
7887 Some(parent)
7888 }) {
7889 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7890 }
7891 }
7892
7893 pub fn prepare_restore_change(
7894 &self,
7895 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7896 hunk: &MultiBufferDiffHunk,
7897 cx: &mut App,
7898 ) -> Option<()> {
7899 if hunk.is_created_file() {
7900 return None;
7901 }
7902 let buffer = self.buffer.read(cx);
7903 let diff = buffer.diff_for(hunk.buffer_id)?;
7904 let buffer = buffer.buffer(hunk.buffer_id)?;
7905 let buffer = buffer.read(cx);
7906 let original_text = diff
7907 .read(cx)
7908 .base_text()
7909 .as_rope()
7910 .slice(hunk.diff_base_byte_range.clone());
7911 let buffer_snapshot = buffer.snapshot();
7912 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7913 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7914 probe
7915 .0
7916 .start
7917 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7918 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7919 }) {
7920 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7921 Some(())
7922 } else {
7923 None
7924 }
7925 }
7926
7927 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7928 self.manipulate_lines(window, cx, |lines| lines.reverse())
7929 }
7930
7931 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7932 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7933 }
7934
7935 fn manipulate_lines<Fn>(
7936 &mut self,
7937 window: &mut Window,
7938 cx: &mut Context<Self>,
7939 mut callback: Fn,
7940 ) where
7941 Fn: FnMut(&mut Vec<&str>),
7942 {
7943 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7944 let buffer = self.buffer.read(cx).snapshot(cx);
7945
7946 let mut edits = Vec::new();
7947
7948 let selections = self.selections.all::<Point>(cx);
7949 let mut selections = selections.iter().peekable();
7950 let mut contiguous_row_selections = Vec::new();
7951 let mut new_selections = Vec::new();
7952 let mut added_lines = 0;
7953 let mut removed_lines = 0;
7954
7955 while let Some(selection) = selections.next() {
7956 let (start_row, end_row) = consume_contiguous_rows(
7957 &mut contiguous_row_selections,
7958 selection,
7959 &display_map,
7960 &mut selections,
7961 );
7962
7963 let start_point = Point::new(start_row.0, 0);
7964 let end_point = Point::new(
7965 end_row.previous_row().0,
7966 buffer.line_len(end_row.previous_row()),
7967 );
7968 let text = buffer
7969 .text_for_range(start_point..end_point)
7970 .collect::<String>();
7971
7972 let mut lines = text.split('\n').collect_vec();
7973
7974 let lines_before = lines.len();
7975 callback(&mut lines);
7976 let lines_after = lines.len();
7977
7978 edits.push((start_point..end_point, lines.join("\n")));
7979
7980 // Selections must change based on added and removed line count
7981 let start_row =
7982 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7983 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7984 new_selections.push(Selection {
7985 id: selection.id,
7986 start: start_row,
7987 end: end_row,
7988 goal: SelectionGoal::None,
7989 reversed: selection.reversed,
7990 });
7991
7992 if lines_after > lines_before {
7993 added_lines += lines_after - lines_before;
7994 } else if lines_before > lines_after {
7995 removed_lines += lines_before - lines_after;
7996 }
7997 }
7998
7999 self.transact(window, cx, |this, window, cx| {
8000 let buffer = this.buffer.update(cx, |buffer, cx| {
8001 buffer.edit(edits, None, cx);
8002 buffer.snapshot(cx)
8003 });
8004
8005 // Recalculate offsets on newly edited buffer
8006 let new_selections = new_selections
8007 .iter()
8008 .map(|s| {
8009 let start_point = Point::new(s.start.0, 0);
8010 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8011 Selection {
8012 id: s.id,
8013 start: buffer.point_to_offset(start_point),
8014 end: buffer.point_to_offset(end_point),
8015 goal: s.goal,
8016 reversed: s.reversed,
8017 }
8018 })
8019 .collect();
8020
8021 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8022 s.select(new_selections);
8023 });
8024
8025 this.request_autoscroll(Autoscroll::fit(), cx);
8026 });
8027 }
8028
8029 pub fn convert_to_upper_case(
8030 &mut self,
8031 _: &ConvertToUpperCase,
8032 window: &mut Window,
8033 cx: &mut Context<Self>,
8034 ) {
8035 self.manipulate_text(window, cx, |text| text.to_uppercase())
8036 }
8037
8038 pub fn convert_to_lower_case(
8039 &mut self,
8040 _: &ConvertToLowerCase,
8041 window: &mut Window,
8042 cx: &mut Context<Self>,
8043 ) {
8044 self.manipulate_text(window, cx, |text| text.to_lowercase())
8045 }
8046
8047 pub fn convert_to_title_case(
8048 &mut self,
8049 _: &ConvertToTitleCase,
8050 window: &mut Window,
8051 cx: &mut Context<Self>,
8052 ) {
8053 self.manipulate_text(window, cx, |text| {
8054 text.split('\n')
8055 .map(|line| line.to_case(Case::Title))
8056 .join("\n")
8057 })
8058 }
8059
8060 pub fn convert_to_snake_case(
8061 &mut self,
8062 _: &ConvertToSnakeCase,
8063 window: &mut Window,
8064 cx: &mut Context<Self>,
8065 ) {
8066 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8067 }
8068
8069 pub fn convert_to_kebab_case(
8070 &mut self,
8071 _: &ConvertToKebabCase,
8072 window: &mut Window,
8073 cx: &mut Context<Self>,
8074 ) {
8075 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8076 }
8077
8078 pub fn convert_to_upper_camel_case(
8079 &mut self,
8080 _: &ConvertToUpperCamelCase,
8081 window: &mut Window,
8082 cx: &mut Context<Self>,
8083 ) {
8084 self.manipulate_text(window, cx, |text| {
8085 text.split('\n')
8086 .map(|line| line.to_case(Case::UpperCamel))
8087 .join("\n")
8088 })
8089 }
8090
8091 pub fn convert_to_lower_camel_case(
8092 &mut self,
8093 _: &ConvertToLowerCamelCase,
8094 window: &mut Window,
8095 cx: &mut Context<Self>,
8096 ) {
8097 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8098 }
8099
8100 pub fn convert_to_opposite_case(
8101 &mut self,
8102 _: &ConvertToOppositeCase,
8103 window: &mut Window,
8104 cx: &mut Context<Self>,
8105 ) {
8106 self.manipulate_text(window, cx, |text| {
8107 text.chars()
8108 .fold(String::with_capacity(text.len()), |mut t, c| {
8109 if c.is_uppercase() {
8110 t.extend(c.to_lowercase());
8111 } else {
8112 t.extend(c.to_uppercase());
8113 }
8114 t
8115 })
8116 })
8117 }
8118
8119 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8120 where
8121 Fn: FnMut(&str) -> String,
8122 {
8123 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8124 let buffer = self.buffer.read(cx).snapshot(cx);
8125
8126 let mut new_selections = Vec::new();
8127 let mut edits = Vec::new();
8128 let mut selection_adjustment = 0i32;
8129
8130 for selection in self.selections.all::<usize>(cx) {
8131 let selection_is_empty = selection.is_empty();
8132
8133 let (start, end) = if selection_is_empty {
8134 let word_range = movement::surrounding_word(
8135 &display_map,
8136 selection.start.to_display_point(&display_map),
8137 );
8138 let start = word_range.start.to_offset(&display_map, Bias::Left);
8139 let end = word_range.end.to_offset(&display_map, Bias::Left);
8140 (start, end)
8141 } else {
8142 (selection.start, selection.end)
8143 };
8144
8145 let text = buffer.text_for_range(start..end).collect::<String>();
8146 let old_length = text.len() as i32;
8147 let text = callback(&text);
8148
8149 new_selections.push(Selection {
8150 start: (start as i32 - selection_adjustment) as usize,
8151 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8152 goal: SelectionGoal::None,
8153 ..selection
8154 });
8155
8156 selection_adjustment += old_length - text.len() as i32;
8157
8158 edits.push((start..end, text));
8159 }
8160
8161 self.transact(window, cx, |this, window, cx| {
8162 this.buffer.update(cx, |buffer, cx| {
8163 buffer.edit(edits, None, cx);
8164 });
8165
8166 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8167 s.select(new_selections);
8168 });
8169
8170 this.request_autoscroll(Autoscroll::fit(), cx);
8171 });
8172 }
8173
8174 pub fn duplicate(
8175 &mut self,
8176 upwards: bool,
8177 whole_lines: bool,
8178 window: &mut Window,
8179 cx: &mut Context<Self>,
8180 ) {
8181 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8182 let buffer = &display_map.buffer_snapshot;
8183 let selections = self.selections.all::<Point>(cx);
8184
8185 let mut edits = Vec::new();
8186 let mut selections_iter = selections.iter().peekable();
8187 while let Some(selection) = selections_iter.next() {
8188 let mut rows = selection.spanned_rows(false, &display_map);
8189 // duplicate line-wise
8190 if whole_lines || selection.start == selection.end {
8191 // Avoid duplicating the same lines twice.
8192 while let Some(next_selection) = selections_iter.peek() {
8193 let next_rows = next_selection.spanned_rows(false, &display_map);
8194 if next_rows.start < rows.end {
8195 rows.end = next_rows.end;
8196 selections_iter.next().unwrap();
8197 } else {
8198 break;
8199 }
8200 }
8201
8202 // Copy the text from the selected row region and splice it either at the start
8203 // or end of the region.
8204 let start = Point::new(rows.start.0, 0);
8205 let end = Point::new(
8206 rows.end.previous_row().0,
8207 buffer.line_len(rows.end.previous_row()),
8208 );
8209 let text = buffer
8210 .text_for_range(start..end)
8211 .chain(Some("\n"))
8212 .collect::<String>();
8213 let insert_location = if upwards {
8214 Point::new(rows.end.0, 0)
8215 } else {
8216 start
8217 };
8218 edits.push((insert_location..insert_location, text));
8219 } else {
8220 // duplicate character-wise
8221 let start = selection.start;
8222 let end = selection.end;
8223 let text = buffer.text_for_range(start..end).collect::<String>();
8224 edits.push((selection.end..selection.end, text));
8225 }
8226 }
8227
8228 self.transact(window, cx, |this, _, cx| {
8229 this.buffer.update(cx, |buffer, cx| {
8230 buffer.edit(edits, None, cx);
8231 });
8232
8233 this.request_autoscroll(Autoscroll::fit(), cx);
8234 });
8235 }
8236
8237 pub fn duplicate_line_up(
8238 &mut self,
8239 _: &DuplicateLineUp,
8240 window: &mut Window,
8241 cx: &mut Context<Self>,
8242 ) {
8243 self.duplicate(true, true, window, cx);
8244 }
8245
8246 pub fn duplicate_line_down(
8247 &mut self,
8248 _: &DuplicateLineDown,
8249 window: &mut Window,
8250 cx: &mut Context<Self>,
8251 ) {
8252 self.duplicate(false, true, window, cx);
8253 }
8254
8255 pub fn duplicate_selection(
8256 &mut self,
8257 _: &DuplicateSelection,
8258 window: &mut Window,
8259 cx: &mut Context<Self>,
8260 ) {
8261 self.duplicate(false, false, window, cx);
8262 }
8263
8264 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8265 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8266 let buffer = self.buffer.read(cx).snapshot(cx);
8267
8268 let mut edits = Vec::new();
8269 let mut unfold_ranges = Vec::new();
8270 let mut refold_creases = Vec::new();
8271
8272 let selections = self.selections.all::<Point>(cx);
8273 let mut selections = selections.iter().peekable();
8274 let mut contiguous_row_selections = Vec::new();
8275 let mut new_selections = Vec::new();
8276
8277 while let Some(selection) = selections.next() {
8278 // Find all the selections that span a contiguous row range
8279 let (start_row, end_row) = consume_contiguous_rows(
8280 &mut contiguous_row_selections,
8281 selection,
8282 &display_map,
8283 &mut selections,
8284 );
8285
8286 // Move the text spanned by the row range to be before the line preceding the row range
8287 if start_row.0 > 0 {
8288 let range_to_move = Point::new(
8289 start_row.previous_row().0,
8290 buffer.line_len(start_row.previous_row()),
8291 )
8292 ..Point::new(
8293 end_row.previous_row().0,
8294 buffer.line_len(end_row.previous_row()),
8295 );
8296 let insertion_point = display_map
8297 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8298 .0;
8299
8300 // Don't move lines across excerpts
8301 if buffer
8302 .excerpt_containing(insertion_point..range_to_move.end)
8303 .is_some()
8304 {
8305 let text = buffer
8306 .text_for_range(range_to_move.clone())
8307 .flat_map(|s| s.chars())
8308 .skip(1)
8309 .chain(['\n'])
8310 .collect::<String>();
8311
8312 edits.push((
8313 buffer.anchor_after(range_to_move.start)
8314 ..buffer.anchor_before(range_to_move.end),
8315 String::new(),
8316 ));
8317 let insertion_anchor = buffer.anchor_after(insertion_point);
8318 edits.push((insertion_anchor..insertion_anchor, text));
8319
8320 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8321
8322 // Move selections up
8323 new_selections.extend(contiguous_row_selections.drain(..).map(
8324 |mut selection| {
8325 selection.start.row -= row_delta;
8326 selection.end.row -= row_delta;
8327 selection
8328 },
8329 ));
8330
8331 // Move folds up
8332 unfold_ranges.push(range_to_move.clone());
8333 for fold in display_map.folds_in_range(
8334 buffer.anchor_before(range_to_move.start)
8335 ..buffer.anchor_after(range_to_move.end),
8336 ) {
8337 let mut start = fold.range.start.to_point(&buffer);
8338 let mut end = fold.range.end.to_point(&buffer);
8339 start.row -= row_delta;
8340 end.row -= row_delta;
8341 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8342 }
8343 }
8344 }
8345
8346 // If we didn't move line(s), preserve the existing selections
8347 new_selections.append(&mut contiguous_row_selections);
8348 }
8349
8350 self.transact(window, cx, |this, window, cx| {
8351 this.unfold_ranges(&unfold_ranges, true, true, cx);
8352 this.buffer.update(cx, |buffer, cx| {
8353 for (range, text) in edits {
8354 buffer.edit([(range, text)], None, cx);
8355 }
8356 });
8357 this.fold_creases(refold_creases, true, window, cx);
8358 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8359 s.select(new_selections);
8360 })
8361 });
8362 }
8363
8364 pub fn move_line_down(
8365 &mut self,
8366 _: &MoveLineDown,
8367 window: &mut Window,
8368 cx: &mut Context<Self>,
8369 ) {
8370 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8371 let buffer = self.buffer.read(cx).snapshot(cx);
8372
8373 let mut edits = Vec::new();
8374 let mut unfold_ranges = Vec::new();
8375 let mut refold_creases = Vec::new();
8376
8377 let selections = self.selections.all::<Point>(cx);
8378 let mut selections = selections.iter().peekable();
8379 let mut contiguous_row_selections = Vec::new();
8380 let mut new_selections = Vec::new();
8381
8382 while let Some(selection) = selections.next() {
8383 // Find all the selections that span a contiguous row range
8384 let (start_row, end_row) = consume_contiguous_rows(
8385 &mut contiguous_row_selections,
8386 selection,
8387 &display_map,
8388 &mut selections,
8389 );
8390
8391 // Move the text spanned by the row range to be after the last line of the row range
8392 if end_row.0 <= buffer.max_point().row {
8393 let range_to_move =
8394 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8395 let insertion_point = display_map
8396 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8397 .0;
8398
8399 // Don't move lines across excerpt boundaries
8400 if buffer
8401 .excerpt_containing(range_to_move.start..insertion_point)
8402 .is_some()
8403 {
8404 let mut text = String::from("\n");
8405 text.extend(buffer.text_for_range(range_to_move.clone()));
8406 text.pop(); // Drop trailing newline
8407 edits.push((
8408 buffer.anchor_after(range_to_move.start)
8409 ..buffer.anchor_before(range_to_move.end),
8410 String::new(),
8411 ));
8412 let insertion_anchor = buffer.anchor_after(insertion_point);
8413 edits.push((insertion_anchor..insertion_anchor, text));
8414
8415 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8416
8417 // Move selections down
8418 new_selections.extend(contiguous_row_selections.drain(..).map(
8419 |mut selection| {
8420 selection.start.row += row_delta;
8421 selection.end.row += row_delta;
8422 selection
8423 },
8424 ));
8425
8426 // Move folds down
8427 unfold_ranges.push(range_to_move.clone());
8428 for fold in display_map.folds_in_range(
8429 buffer.anchor_before(range_to_move.start)
8430 ..buffer.anchor_after(range_to_move.end),
8431 ) {
8432 let mut start = fold.range.start.to_point(&buffer);
8433 let mut end = fold.range.end.to_point(&buffer);
8434 start.row += row_delta;
8435 end.row += row_delta;
8436 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8437 }
8438 }
8439 }
8440
8441 // If we didn't move line(s), preserve the existing selections
8442 new_selections.append(&mut contiguous_row_selections);
8443 }
8444
8445 self.transact(window, cx, |this, window, cx| {
8446 this.unfold_ranges(&unfold_ranges, true, true, cx);
8447 this.buffer.update(cx, |buffer, cx| {
8448 for (range, text) in edits {
8449 buffer.edit([(range, text)], None, cx);
8450 }
8451 });
8452 this.fold_creases(refold_creases, true, window, cx);
8453 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8454 s.select(new_selections)
8455 });
8456 });
8457 }
8458
8459 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8460 let text_layout_details = &self.text_layout_details(window);
8461 self.transact(window, cx, |this, window, cx| {
8462 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8463 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8464 let line_mode = s.line_mode;
8465 s.move_with(|display_map, selection| {
8466 if !selection.is_empty() || line_mode {
8467 return;
8468 }
8469
8470 let mut head = selection.head();
8471 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8472 if head.column() == display_map.line_len(head.row()) {
8473 transpose_offset = display_map
8474 .buffer_snapshot
8475 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8476 }
8477
8478 if transpose_offset == 0 {
8479 return;
8480 }
8481
8482 *head.column_mut() += 1;
8483 head = display_map.clip_point(head, Bias::Right);
8484 let goal = SelectionGoal::HorizontalPosition(
8485 display_map
8486 .x_for_display_point(head, text_layout_details)
8487 .into(),
8488 );
8489 selection.collapse_to(head, goal);
8490
8491 let transpose_start = display_map
8492 .buffer_snapshot
8493 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8494 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8495 let transpose_end = display_map
8496 .buffer_snapshot
8497 .clip_offset(transpose_offset + 1, Bias::Right);
8498 if let Some(ch) =
8499 display_map.buffer_snapshot.chars_at(transpose_start).next()
8500 {
8501 edits.push((transpose_start..transpose_offset, String::new()));
8502 edits.push((transpose_end..transpose_end, ch.to_string()));
8503 }
8504 }
8505 });
8506 edits
8507 });
8508 this.buffer
8509 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8510 let selections = this.selections.all::<usize>(cx);
8511 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8512 s.select(selections);
8513 });
8514 });
8515 }
8516
8517 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8518 self.rewrap_impl(IsVimMode::No, cx)
8519 }
8520
8521 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
8522 let buffer = self.buffer.read(cx).snapshot(cx);
8523 let selections = self.selections.all::<Point>(cx);
8524 let mut selections = selections.iter().peekable();
8525
8526 let mut edits = Vec::new();
8527 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8528
8529 while let Some(selection) = selections.next() {
8530 let mut start_row = selection.start.row;
8531 let mut end_row = selection.end.row;
8532
8533 // Skip selections that overlap with a range that has already been rewrapped.
8534 let selection_range = start_row..end_row;
8535 if rewrapped_row_ranges
8536 .iter()
8537 .any(|range| range.overlaps(&selection_range))
8538 {
8539 continue;
8540 }
8541
8542 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
8543
8544 // Since not all lines in the selection may be at the same indent
8545 // level, choose the indent size that is the most common between all
8546 // of the lines.
8547 //
8548 // If there is a tie, we use the deepest indent.
8549 let (indent_size, indent_end) = {
8550 let mut indent_size_occurrences = HashMap::default();
8551 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8552
8553 for row in start_row..=end_row {
8554 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8555 rows_by_indent_size.entry(indent).or_default().push(row);
8556 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8557 }
8558
8559 let indent_size = indent_size_occurrences
8560 .into_iter()
8561 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8562 .map(|(indent, _)| indent)
8563 .unwrap_or_default();
8564 let row = rows_by_indent_size[&indent_size][0];
8565 let indent_end = Point::new(row, indent_size.len);
8566
8567 (indent_size, indent_end)
8568 };
8569
8570 let mut line_prefix = indent_size.chars().collect::<String>();
8571
8572 let mut inside_comment = false;
8573 if let Some(comment_prefix) =
8574 buffer
8575 .language_scope_at(selection.head())
8576 .and_then(|language| {
8577 language
8578 .line_comment_prefixes()
8579 .iter()
8580 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8581 .cloned()
8582 })
8583 {
8584 line_prefix.push_str(&comment_prefix);
8585 inside_comment = true;
8586 }
8587
8588 let language_settings = buffer.language_settings_at(selection.head(), cx);
8589 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8590 RewrapBehavior::InComments => inside_comment,
8591 RewrapBehavior::InSelections => !selection.is_empty(),
8592 RewrapBehavior::Anywhere => true,
8593 };
8594
8595 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
8596 if !should_rewrap {
8597 continue;
8598 }
8599
8600 if selection.is_empty() {
8601 'expand_upwards: while start_row > 0 {
8602 let prev_row = start_row - 1;
8603 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8604 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8605 {
8606 start_row = prev_row;
8607 } else {
8608 break 'expand_upwards;
8609 }
8610 }
8611
8612 'expand_downwards: while end_row < buffer.max_point().row {
8613 let next_row = end_row + 1;
8614 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8615 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8616 {
8617 end_row = next_row;
8618 } else {
8619 break 'expand_downwards;
8620 }
8621 }
8622 }
8623
8624 let start = Point::new(start_row, 0);
8625 let start_offset = start.to_offset(&buffer);
8626 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8627 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8628 let Some(lines_without_prefixes) = selection_text
8629 .lines()
8630 .map(|line| {
8631 line.strip_prefix(&line_prefix)
8632 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8633 .ok_or_else(|| {
8634 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8635 })
8636 })
8637 .collect::<Result<Vec<_>, _>>()
8638 .log_err()
8639 else {
8640 continue;
8641 };
8642
8643 let wrap_column = buffer
8644 .language_settings_at(Point::new(start_row, 0), cx)
8645 .preferred_line_length as usize;
8646 let wrapped_text = wrap_with_prefix(
8647 line_prefix,
8648 lines_without_prefixes.join(" "),
8649 wrap_column,
8650 tab_size,
8651 );
8652
8653 // TODO: should always use char-based diff while still supporting cursor behavior that
8654 // matches vim.
8655 let mut diff_options = DiffOptions::default();
8656 if is_vim_mode == IsVimMode::Yes {
8657 diff_options.max_word_diff_len = 0;
8658 diff_options.max_word_diff_line_count = 0;
8659 } else {
8660 diff_options.max_word_diff_len = usize::MAX;
8661 diff_options.max_word_diff_line_count = usize::MAX;
8662 }
8663
8664 for (old_range, new_text) in
8665 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8666 {
8667 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8668 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8669 edits.push((edit_start..edit_end, new_text));
8670 }
8671
8672 rewrapped_row_ranges.push(start_row..=end_row);
8673 }
8674
8675 self.buffer
8676 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8677 }
8678
8679 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8680 let mut text = String::new();
8681 let buffer = self.buffer.read(cx).snapshot(cx);
8682 let mut selections = self.selections.all::<Point>(cx);
8683 let mut clipboard_selections = Vec::with_capacity(selections.len());
8684 {
8685 let max_point = buffer.max_point();
8686 let mut is_first = true;
8687 for selection in &mut selections {
8688 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8689 if is_entire_line {
8690 selection.start = Point::new(selection.start.row, 0);
8691 if !selection.is_empty() && selection.end.column == 0 {
8692 selection.end = cmp::min(max_point, selection.end);
8693 } else {
8694 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8695 }
8696 selection.goal = SelectionGoal::None;
8697 }
8698 if is_first {
8699 is_first = false;
8700 } else {
8701 text += "\n";
8702 }
8703 let mut len = 0;
8704 for chunk in buffer.text_for_range(selection.start..selection.end) {
8705 text.push_str(chunk);
8706 len += chunk.len();
8707 }
8708 clipboard_selections.push(ClipboardSelection {
8709 len,
8710 is_entire_line,
8711 first_line_indent: buffer
8712 .indent_size_for_line(MultiBufferRow(selection.start.row))
8713 .len,
8714 });
8715 }
8716 }
8717
8718 self.transact(window, cx, |this, window, cx| {
8719 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8720 s.select(selections);
8721 });
8722 this.insert("", window, cx);
8723 });
8724 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8725 }
8726
8727 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8728 let item = self.cut_common(window, cx);
8729 cx.write_to_clipboard(item);
8730 }
8731
8732 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8733 self.change_selections(None, window, cx, |s| {
8734 s.move_with(|snapshot, sel| {
8735 if sel.is_empty() {
8736 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8737 }
8738 });
8739 });
8740 let item = self.cut_common(window, cx);
8741 cx.set_global(KillRing(item))
8742 }
8743
8744 pub fn kill_ring_yank(
8745 &mut self,
8746 _: &KillRingYank,
8747 window: &mut Window,
8748 cx: &mut Context<Self>,
8749 ) {
8750 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8751 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8752 (kill_ring.text().to_string(), kill_ring.metadata_json())
8753 } else {
8754 return;
8755 }
8756 } else {
8757 return;
8758 };
8759 self.do_paste(&text, metadata, false, window, cx);
8760 }
8761
8762 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8763 let selections = self.selections.all::<Point>(cx);
8764 let buffer = self.buffer.read(cx).read(cx);
8765 let mut text = String::new();
8766
8767 let mut clipboard_selections = Vec::with_capacity(selections.len());
8768 {
8769 let max_point = buffer.max_point();
8770 let mut is_first = true;
8771 for selection in selections.iter() {
8772 let mut start = selection.start;
8773 let mut end = selection.end;
8774 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8775 if is_entire_line {
8776 start = Point::new(start.row, 0);
8777 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8778 }
8779 if is_first {
8780 is_first = false;
8781 } else {
8782 text += "\n";
8783 }
8784 let mut len = 0;
8785 for chunk in buffer.text_for_range(start..end) {
8786 text.push_str(chunk);
8787 len += chunk.len();
8788 }
8789 clipboard_selections.push(ClipboardSelection {
8790 len,
8791 is_entire_line,
8792 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8793 });
8794 }
8795 }
8796
8797 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8798 text,
8799 clipboard_selections,
8800 ));
8801 }
8802
8803 pub fn do_paste(
8804 &mut self,
8805 text: &String,
8806 clipboard_selections: Option<Vec<ClipboardSelection>>,
8807 handle_entire_lines: bool,
8808 window: &mut Window,
8809 cx: &mut Context<Self>,
8810 ) {
8811 if self.read_only(cx) {
8812 return;
8813 }
8814
8815 let clipboard_text = Cow::Borrowed(text);
8816
8817 self.transact(window, cx, |this, window, cx| {
8818 if let Some(mut clipboard_selections) = clipboard_selections {
8819 let old_selections = this.selections.all::<usize>(cx);
8820 let all_selections_were_entire_line =
8821 clipboard_selections.iter().all(|s| s.is_entire_line);
8822 let first_selection_indent_column =
8823 clipboard_selections.first().map(|s| s.first_line_indent);
8824 if clipboard_selections.len() != old_selections.len() {
8825 clipboard_selections.drain(..);
8826 }
8827 let cursor_offset = this.selections.last::<usize>(cx).head();
8828 let mut auto_indent_on_paste = true;
8829
8830 this.buffer.update(cx, |buffer, cx| {
8831 let snapshot = buffer.read(cx);
8832 auto_indent_on_paste = snapshot
8833 .language_settings_at(cursor_offset, cx)
8834 .auto_indent_on_paste;
8835
8836 let mut start_offset = 0;
8837 let mut edits = Vec::new();
8838 let mut original_indent_columns = Vec::new();
8839 for (ix, selection) in old_selections.iter().enumerate() {
8840 let to_insert;
8841 let entire_line;
8842 let original_indent_column;
8843 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8844 let end_offset = start_offset + clipboard_selection.len;
8845 to_insert = &clipboard_text[start_offset..end_offset];
8846 entire_line = clipboard_selection.is_entire_line;
8847 start_offset = end_offset + 1;
8848 original_indent_column = Some(clipboard_selection.first_line_indent);
8849 } else {
8850 to_insert = clipboard_text.as_str();
8851 entire_line = all_selections_were_entire_line;
8852 original_indent_column = first_selection_indent_column
8853 }
8854
8855 // If the corresponding selection was empty when this slice of the
8856 // clipboard text was written, then the entire line containing the
8857 // selection was copied. If this selection is also currently empty,
8858 // then paste the line before the current line of the buffer.
8859 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8860 let column = selection.start.to_point(&snapshot).column as usize;
8861 let line_start = selection.start - column;
8862 line_start..line_start
8863 } else {
8864 selection.range()
8865 };
8866
8867 edits.push((range, to_insert));
8868 original_indent_columns.push(original_indent_column);
8869 }
8870 drop(snapshot);
8871
8872 buffer.edit(
8873 edits,
8874 if auto_indent_on_paste {
8875 Some(AutoindentMode::Block {
8876 original_indent_columns,
8877 })
8878 } else {
8879 None
8880 },
8881 cx,
8882 );
8883 });
8884
8885 let selections = this.selections.all::<usize>(cx);
8886 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8887 s.select(selections)
8888 });
8889 } else {
8890 this.insert(&clipboard_text, window, cx);
8891 }
8892 });
8893 }
8894
8895 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8896 if let Some(item) = cx.read_from_clipboard() {
8897 let entries = item.entries();
8898
8899 match entries.first() {
8900 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8901 // of all the pasted entries.
8902 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8903 .do_paste(
8904 clipboard_string.text(),
8905 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8906 true,
8907 window,
8908 cx,
8909 ),
8910 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8911 }
8912 }
8913 }
8914
8915 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8916 if self.read_only(cx) {
8917 return;
8918 }
8919
8920 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8921 if let Some((selections, _)) =
8922 self.selection_history.transaction(transaction_id).cloned()
8923 {
8924 self.change_selections(None, window, cx, |s| {
8925 s.select_anchors(selections.to_vec());
8926 });
8927 } else {
8928 log::error!(
8929 "No entry in selection_history found for undo. \
8930 This may correspond to a bug where undo does not update the selection. \
8931 If this is occurring, please add details to \
8932 https://github.com/zed-industries/zed/issues/22692"
8933 );
8934 }
8935 self.request_autoscroll(Autoscroll::fit(), cx);
8936 self.unmark_text(window, cx);
8937 self.refresh_inline_completion(true, false, window, cx);
8938 cx.emit(EditorEvent::Edited { transaction_id });
8939 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8940 }
8941 }
8942
8943 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8944 if self.read_only(cx) {
8945 return;
8946 }
8947
8948 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8949 if let Some((_, Some(selections))) =
8950 self.selection_history.transaction(transaction_id).cloned()
8951 {
8952 self.change_selections(None, window, cx, |s| {
8953 s.select_anchors(selections.to_vec());
8954 });
8955 } else {
8956 log::error!(
8957 "No entry in selection_history found for redo. \
8958 This may correspond to a bug where undo does not update the selection. \
8959 If this is occurring, please add details to \
8960 https://github.com/zed-industries/zed/issues/22692"
8961 );
8962 }
8963 self.request_autoscroll(Autoscroll::fit(), cx);
8964 self.unmark_text(window, cx);
8965 self.refresh_inline_completion(true, false, window, cx);
8966 cx.emit(EditorEvent::Edited { transaction_id });
8967 }
8968 }
8969
8970 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8971 self.buffer
8972 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8973 }
8974
8975 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8976 self.buffer
8977 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8978 }
8979
8980 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8981 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8982 let line_mode = s.line_mode;
8983 s.move_with(|map, selection| {
8984 let cursor = if selection.is_empty() && !line_mode {
8985 movement::left(map, selection.start)
8986 } else {
8987 selection.start
8988 };
8989 selection.collapse_to(cursor, SelectionGoal::None);
8990 });
8991 })
8992 }
8993
8994 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8995 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8996 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8997 })
8998 }
8999
9000 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9001 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9002 let line_mode = s.line_mode;
9003 s.move_with(|map, selection| {
9004 let cursor = if selection.is_empty() && !line_mode {
9005 movement::right(map, selection.end)
9006 } else {
9007 selection.end
9008 };
9009 selection.collapse_to(cursor, SelectionGoal::None)
9010 });
9011 })
9012 }
9013
9014 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9015 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9016 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9017 })
9018 }
9019
9020 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9021 if self.take_rename(true, window, cx).is_some() {
9022 return;
9023 }
9024
9025 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9026 cx.propagate();
9027 return;
9028 }
9029
9030 let text_layout_details = &self.text_layout_details(window);
9031 let selection_count = self.selections.count();
9032 let first_selection = self.selections.first_anchor();
9033
9034 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9035 let line_mode = s.line_mode;
9036 s.move_with(|map, selection| {
9037 if !selection.is_empty() && !line_mode {
9038 selection.goal = SelectionGoal::None;
9039 }
9040 let (cursor, goal) = movement::up(
9041 map,
9042 selection.start,
9043 selection.goal,
9044 false,
9045 text_layout_details,
9046 );
9047 selection.collapse_to(cursor, goal);
9048 });
9049 });
9050
9051 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9052 {
9053 cx.propagate();
9054 }
9055 }
9056
9057 pub fn move_up_by_lines(
9058 &mut self,
9059 action: &MoveUpByLines,
9060 window: &mut Window,
9061 cx: &mut Context<Self>,
9062 ) {
9063 if self.take_rename(true, window, cx).is_some() {
9064 return;
9065 }
9066
9067 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9068 cx.propagate();
9069 return;
9070 }
9071
9072 let text_layout_details = &self.text_layout_details(window);
9073
9074 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9075 let line_mode = s.line_mode;
9076 s.move_with(|map, selection| {
9077 if !selection.is_empty() && !line_mode {
9078 selection.goal = SelectionGoal::None;
9079 }
9080 let (cursor, goal) = movement::up_by_rows(
9081 map,
9082 selection.start,
9083 action.lines,
9084 selection.goal,
9085 false,
9086 text_layout_details,
9087 );
9088 selection.collapse_to(cursor, goal);
9089 });
9090 })
9091 }
9092
9093 pub fn move_down_by_lines(
9094 &mut self,
9095 action: &MoveDownByLines,
9096 window: &mut Window,
9097 cx: &mut Context<Self>,
9098 ) {
9099 if self.take_rename(true, window, cx).is_some() {
9100 return;
9101 }
9102
9103 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9104 cx.propagate();
9105 return;
9106 }
9107
9108 let text_layout_details = &self.text_layout_details(window);
9109
9110 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9111 let line_mode = s.line_mode;
9112 s.move_with(|map, selection| {
9113 if !selection.is_empty() && !line_mode {
9114 selection.goal = SelectionGoal::None;
9115 }
9116 let (cursor, goal) = movement::down_by_rows(
9117 map,
9118 selection.start,
9119 action.lines,
9120 selection.goal,
9121 false,
9122 text_layout_details,
9123 );
9124 selection.collapse_to(cursor, goal);
9125 });
9126 })
9127 }
9128
9129 pub fn select_down_by_lines(
9130 &mut self,
9131 action: &SelectDownByLines,
9132 window: &mut Window,
9133 cx: &mut Context<Self>,
9134 ) {
9135 let text_layout_details = &self.text_layout_details(window);
9136 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9137 s.move_heads_with(|map, head, goal| {
9138 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9139 })
9140 })
9141 }
9142
9143 pub fn select_up_by_lines(
9144 &mut self,
9145 action: &SelectUpByLines,
9146 window: &mut Window,
9147 cx: &mut Context<Self>,
9148 ) {
9149 let text_layout_details = &self.text_layout_details(window);
9150 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9151 s.move_heads_with(|map, head, goal| {
9152 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9153 })
9154 })
9155 }
9156
9157 pub fn select_page_up(
9158 &mut self,
9159 _: &SelectPageUp,
9160 window: &mut Window,
9161 cx: &mut Context<Self>,
9162 ) {
9163 let Some(row_count) = self.visible_row_count() else {
9164 return;
9165 };
9166
9167 let text_layout_details = &self.text_layout_details(window);
9168
9169 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9170 s.move_heads_with(|map, head, goal| {
9171 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9172 })
9173 })
9174 }
9175
9176 pub fn move_page_up(
9177 &mut self,
9178 action: &MovePageUp,
9179 window: &mut Window,
9180 cx: &mut Context<Self>,
9181 ) {
9182 if self.take_rename(true, window, cx).is_some() {
9183 return;
9184 }
9185
9186 if self
9187 .context_menu
9188 .borrow_mut()
9189 .as_mut()
9190 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9191 .unwrap_or(false)
9192 {
9193 return;
9194 }
9195
9196 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9197 cx.propagate();
9198 return;
9199 }
9200
9201 let Some(row_count) = self.visible_row_count() else {
9202 return;
9203 };
9204
9205 let autoscroll = if action.center_cursor {
9206 Autoscroll::center()
9207 } else {
9208 Autoscroll::fit()
9209 };
9210
9211 let text_layout_details = &self.text_layout_details(window);
9212
9213 self.change_selections(Some(autoscroll), window, cx, |s| {
9214 let line_mode = s.line_mode;
9215 s.move_with(|map, selection| {
9216 if !selection.is_empty() && !line_mode {
9217 selection.goal = SelectionGoal::None;
9218 }
9219 let (cursor, goal) = movement::up_by_rows(
9220 map,
9221 selection.end,
9222 row_count,
9223 selection.goal,
9224 false,
9225 text_layout_details,
9226 );
9227 selection.collapse_to(cursor, goal);
9228 });
9229 });
9230 }
9231
9232 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9233 let text_layout_details = &self.text_layout_details(window);
9234 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9235 s.move_heads_with(|map, head, goal| {
9236 movement::up(map, head, goal, false, text_layout_details)
9237 })
9238 })
9239 }
9240
9241 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9242 self.take_rename(true, window, cx);
9243
9244 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9245 cx.propagate();
9246 return;
9247 }
9248
9249 let text_layout_details = &self.text_layout_details(window);
9250 let selection_count = self.selections.count();
9251 let first_selection = self.selections.first_anchor();
9252
9253 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9254 let line_mode = s.line_mode;
9255 s.move_with(|map, selection| {
9256 if !selection.is_empty() && !line_mode {
9257 selection.goal = SelectionGoal::None;
9258 }
9259 let (cursor, goal) = movement::down(
9260 map,
9261 selection.end,
9262 selection.goal,
9263 false,
9264 text_layout_details,
9265 );
9266 selection.collapse_to(cursor, goal);
9267 });
9268 });
9269
9270 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9271 {
9272 cx.propagate();
9273 }
9274 }
9275
9276 pub fn select_page_down(
9277 &mut self,
9278 _: &SelectPageDown,
9279 window: &mut Window,
9280 cx: &mut Context<Self>,
9281 ) {
9282 let Some(row_count) = self.visible_row_count() else {
9283 return;
9284 };
9285
9286 let text_layout_details = &self.text_layout_details(window);
9287
9288 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9289 s.move_heads_with(|map, head, goal| {
9290 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9291 })
9292 })
9293 }
9294
9295 pub fn move_page_down(
9296 &mut self,
9297 action: &MovePageDown,
9298 window: &mut Window,
9299 cx: &mut Context<Self>,
9300 ) {
9301 if self.take_rename(true, window, cx).is_some() {
9302 return;
9303 }
9304
9305 if self
9306 .context_menu
9307 .borrow_mut()
9308 .as_mut()
9309 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9310 .unwrap_or(false)
9311 {
9312 return;
9313 }
9314
9315 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9316 cx.propagate();
9317 return;
9318 }
9319
9320 let Some(row_count) = self.visible_row_count() else {
9321 return;
9322 };
9323
9324 let autoscroll = if action.center_cursor {
9325 Autoscroll::center()
9326 } else {
9327 Autoscroll::fit()
9328 };
9329
9330 let text_layout_details = &self.text_layout_details(window);
9331 self.change_selections(Some(autoscroll), window, cx, |s| {
9332 let line_mode = s.line_mode;
9333 s.move_with(|map, selection| {
9334 if !selection.is_empty() && !line_mode {
9335 selection.goal = SelectionGoal::None;
9336 }
9337 let (cursor, goal) = movement::down_by_rows(
9338 map,
9339 selection.end,
9340 row_count,
9341 selection.goal,
9342 false,
9343 text_layout_details,
9344 );
9345 selection.collapse_to(cursor, goal);
9346 });
9347 });
9348 }
9349
9350 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9351 let text_layout_details = &self.text_layout_details(window);
9352 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9353 s.move_heads_with(|map, head, goal| {
9354 movement::down(map, head, goal, false, text_layout_details)
9355 })
9356 });
9357 }
9358
9359 pub fn context_menu_first(
9360 &mut self,
9361 _: &ContextMenuFirst,
9362 _window: &mut Window,
9363 cx: &mut Context<Self>,
9364 ) {
9365 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9366 context_menu.select_first(self.completion_provider.as_deref(), cx);
9367 }
9368 }
9369
9370 pub fn context_menu_prev(
9371 &mut self,
9372 _: &ContextMenuPrevious,
9373 _window: &mut Window,
9374 cx: &mut Context<Self>,
9375 ) {
9376 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9377 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9378 }
9379 }
9380
9381 pub fn context_menu_next(
9382 &mut self,
9383 _: &ContextMenuNext,
9384 _window: &mut Window,
9385 cx: &mut Context<Self>,
9386 ) {
9387 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9388 context_menu.select_next(self.completion_provider.as_deref(), cx);
9389 }
9390 }
9391
9392 pub fn context_menu_last(
9393 &mut self,
9394 _: &ContextMenuLast,
9395 _window: &mut Window,
9396 cx: &mut Context<Self>,
9397 ) {
9398 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9399 context_menu.select_last(self.completion_provider.as_deref(), cx);
9400 }
9401 }
9402
9403 pub fn move_to_previous_word_start(
9404 &mut self,
9405 _: &MoveToPreviousWordStart,
9406 window: &mut Window,
9407 cx: &mut Context<Self>,
9408 ) {
9409 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9410 s.move_cursors_with(|map, head, _| {
9411 (
9412 movement::previous_word_start(map, head),
9413 SelectionGoal::None,
9414 )
9415 });
9416 })
9417 }
9418
9419 pub fn move_to_previous_subword_start(
9420 &mut self,
9421 _: &MoveToPreviousSubwordStart,
9422 window: &mut Window,
9423 cx: &mut Context<Self>,
9424 ) {
9425 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9426 s.move_cursors_with(|map, head, _| {
9427 (
9428 movement::previous_subword_start(map, head),
9429 SelectionGoal::None,
9430 )
9431 });
9432 })
9433 }
9434
9435 pub fn select_to_previous_word_start(
9436 &mut self,
9437 _: &SelectToPreviousWordStart,
9438 window: &mut Window,
9439 cx: &mut Context<Self>,
9440 ) {
9441 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9442 s.move_heads_with(|map, head, _| {
9443 (
9444 movement::previous_word_start(map, head),
9445 SelectionGoal::None,
9446 )
9447 });
9448 })
9449 }
9450
9451 pub fn select_to_previous_subword_start(
9452 &mut self,
9453 _: &SelectToPreviousSubwordStart,
9454 window: &mut Window,
9455 cx: &mut Context<Self>,
9456 ) {
9457 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9458 s.move_heads_with(|map, head, _| {
9459 (
9460 movement::previous_subword_start(map, head),
9461 SelectionGoal::None,
9462 )
9463 });
9464 })
9465 }
9466
9467 pub fn delete_to_previous_word_start(
9468 &mut self,
9469 action: &DeleteToPreviousWordStart,
9470 window: &mut Window,
9471 cx: &mut Context<Self>,
9472 ) {
9473 self.transact(window, cx, |this, window, cx| {
9474 this.select_autoclose_pair(window, cx);
9475 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9476 let line_mode = s.line_mode;
9477 s.move_with(|map, selection| {
9478 if selection.is_empty() && !line_mode {
9479 let cursor = if action.ignore_newlines {
9480 movement::previous_word_start(map, selection.head())
9481 } else {
9482 movement::previous_word_start_or_newline(map, selection.head())
9483 };
9484 selection.set_head(cursor, SelectionGoal::None);
9485 }
9486 });
9487 });
9488 this.insert("", window, cx);
9489 });
9490 }
9491
9492 pub fn delete_to_previous_subword_start(
9493 &mut self,
9494 _: &DeleteToPreviousSubwordStart,
9495 window: &mut Window,
9496 cx: &mut Context<Self>,
9497 ) {
9498 self.transact(window, cx, |this, window, cx| {
9499 this.select_autoclose_pair(window, cx);
9500 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9501 let line_mode = s.line_mode;
9502 s.move_with(|map, selection| {
9503 if selection.is_empty() && !line_mode {
9504 let cursor = movement::previous_subword_start(map, selection.head());
9505 selection.set_head(cursor, SelectionGoal::None);
9506 }
9507 });
9508 });
9509 this.insert("", window, cx);
9510 });
9511 }
9512
9513 pub fn move_to_next_word_end(
9514 &mut self,
9515 _: &MoveToNextWordEnd,
9516 window: &mut Window,
9517 cx: &mut Context<Self>,
9518 ) {
9519 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9520 s.move_cursors_with(|map, head, _| {
9521 (movement::next_word_end(map, head), SelectionGoal::None)
9522 });
9523 })
9524 }
9525
9526 pub fn move_to_next_subword_end(
9527 &mut self,
9528 _: &MoveToNextSubwordEnd,
9529 window: &mut Window,
9530 cx: &mut Context<Self>,
9531 ) {
9532 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9533 s.move_cursors_with(|map, head, _| {
9534 (movement::next_subword_end(map, head), SelectionGoal::None)
9535 });
9536 })
9537 }
9538
9539 pub fn select_to_next_word_end(
9540 &mut self,
9541 _: &SelectToNextWordEnd,
9542 window: &mut Window,
9543 cx: &mut Context<Self>,
9544 ) {
9545 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9546 s.move_heads_with(|map, head, _| {
9547 (movement::next_word_end(map, head), SelectionGoal::None)
9548 });
9549 })
9550 }
9551
9552 pub fn select_to_next_subword_end(
9553 &mut self,
9554 _: &SelectToNextSubwordEnd,
9555 window: &mut Window,
9556 cx: &mut Context<Self>,
9557 ) {
9558 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9559 s.move_heads_with(|map, head, _| {
9560 (movement::next_subword_end(map, head), SelectionGoal::None)
9561 });
9562 })
9563 }
9564
9565 pub fn delete_to_next_word_end(
9566 &mut self,
9567 action: &DeleteToNextWordEnd,
9568 window: &mut Window,
9569 cx: &mut Context<Self>,
9570 ) {
9571 self.transact(window, cx, |this, window, cx| {
9572 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9573 let line_mode = s.line_mode;
9574 s.move_with(|map, selection| {
9575 if selection.is_empty() && !line_mode {
9576 let cursor = if action.ignore_newlines {
9577 movement::next_word_end(map, selection.head())
9578 } else {
9579 movement::next_word_end_or_newline(map, selection.head())
9580 };
9581 selection.set_head(cursor, SelectionGoal::None);
9582 }
9583 });
9584 });
9585 this.insert("", window, cx);
9586 });
9587 }
9588
9589 pub fn delete_to_next_subword_end(
9590 &mut self,
9591 _: &DeleteToNextSubwordEnd,
9592 window: &mut Window,
9593 cx: &mut Context<Self>,
9594 ) {
9595 self.transact(window, cx, |this, window, cx| {
9596 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9597 s.move_with(|map, selection| {
9598 if selection.is_empty() {
9599 let cursor = movement::next_subword_end(map, selection.head());
9600 selection.set_head(cursor, SelectionGoal::None);
9601 }
9602 });
9603 });
9604 this.insert("", window, cx);
9605 });
9606 }
9607
9608 pub fn move_to_beginning_of_line(
9609 &mut self,
9610 action: &MoveToBeginningOfLine,
9611 window: &mut Window,
9612 cx: &mut Context<Self>,
9613 ) {
9614 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9615 s.move_cursors_with(|map, head, _| {
9616 (
9617 movement::indented_line_beginning(
9618 map,
9619 head,
9620 action.stop_at_soft_wraps,
9621 action.stop_at_indent,
9622 ),
9623 SelectionGoal::None,
9624 )
9625 });
9626 })
9627 }
9628
9629 pub fn select_to_beginning_of_line(
9630 &mut self,
9631 action: &SelectToBeginningOfLine,
9632 window: &mut Window,
9633 cx: &mut Context<Self>,
9634 ) {
9635 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9636 s.move_heads_with(|map, head, _| {
9637 (
9638 movement::indented_line_beginning(
9639 map,
9640 head,
9641 action.stop_at_soft_wraps,
9642 action.stop_at_indent,
9643 ),
9644 SelectionGoal::None,
9645 )
9646 });
9647 });
9648 }
9649
9650 pub fn delete_to_beginning_of_line(
9651 &mut self,
9652 action: &DeleteToBeginningOfLine,
9653 window: &mut Window,
9654 cx: &mut Context<Self>,
9655 ) {
9656 self.transact(window, cx, |this, window, cx| {
9657 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9658 s.move_with(|_, selection| {
9659 selection.reversed = true;
9660 });
9661 });
9662
9663 this.select_to_beginning_of_line(
9664 &SelectToBeginningOfLine {
9665 stop_at_soft_wraps: false,
9666 stop_at_indent: action.stop_at_indent,
9667 },
9668 window,
9669 cx,
9670 );
9671 this.backspace(&Backspace, window, cx);
9672 });
9673 }
9674
9675 pub fn move_to_end_of_line(
9676 &mut self,
9677 action: &MoveToEndOfLine,
9678 window: &mut Window,
9679 cx: &mut Context<Self>,
9680 ) {
9681 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9682 s.move_cursors_with(|map, head, _| {
9683 (
9684 movement::line_end(map, head, action.stop_at_soft_wraps),
9685 SelectionGoal::None,
9686 )
9687 });
9688 })
9689 }
9690
9691 pub fn select_to_end_of_line(
9692 &mut self,
9693 action: &SelectToEndOfLine,
9694 window: &mut Window,
9695 cx: &mut Context<Self>,
9696 ) {
9697 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9698 s.move_heads_with(|map, head, _| {
9699 (
9700 movement::line_end(map, head, action.stop_at_soft_wraps),
9701 SelectionGoal::None,
9702 )
9703 });
9704 })
9705 }
9706
9707 pub fn delete_to_end_of_line(
9708 &mut self,
9709 _: &DeleteToEndOfLine,
9710 window: &mut Window,
9711 cx: &mut Context<Self>,
9712 ) {
9713 self.transact(window, cx, |this, window, cx| {
9714 this.select_to_end_of_line(
9715 &SelectToEndOfLine {
9716 stop_at_soft_wraps: false,
9717 },
9718 window,
9719 cx,
9720 );
9721 this.delete(&Delete, window, cx);
9722 });
9723 }
9724
9725 pub fn cut_to_end_of_line(
9726 &mut self,
9727 _: &CutToEndOfLine,
9728 window: &mut Window,
9729 cx: &mut Context<Self>,
9730 ) {
9731 self.transact(window, cx, |this, window, cx| {
9732 this.select_to_end_of_line(
9733 &SelectToEndOfLine {
9734 stop_at_soft_wraps: false,
9735 },
9736 window,
9737 cx,
9738 );
9739 this.cut(&Cut, window, cx);
9740 });
9741 }
9742
9743 pub fn move_to_start_of_paragraph(
9744 &mut self,
9745 _: &MoveToStartOfParagraph,
9746 window: &mut Window,
9747 cx: &mut Context<Self>,
9748 ) {
9749 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9750 cx.propagate();
9751 return;
9752 }
9753
9754 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9755 s.move_with(|map, selection| {
9756 selection.collapse_to(
9757 movement::start_of_paragraph(map, selection.head(), 1),
9758 SelectionGoal::None,
9759 )
9760 });
9761 })
9762 }
9763
9764 pub fn move_to_end_of_paragraph(
9765 &mut self,
9766 _: &MoveToEndOfParagraph,
9767 window: &mut Window,
9768 cx: &mut Context<Self>,
9769 ) {
9770 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9771 cx.propagate();
9772 return;
9773 }
9774
9775 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9776 s.move_with(|map, selection| {
9777 selection.collapse_to(
9778 movement::end_of_paragraph(map, selection.head(), 1),
9779 SelectionGoal::None,
9780 )
9781 });
9782 })
9783 }
9784
9785 pub fn select_to_start_of_paragraph(
9786 &mut self,
9787 _: &SelectToStartOfParagraph,
9788 window: &mut Window,
9789 cx: &mut Context<Self>,
9790 ) {
9791 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9792 cx.propagate();
9793 return;
9794 }
9795
9796 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9797 s.move_heads_with(|map, head, _| {
9798 (
9799 movement::start_of_paragraph(map, head, 1),
9800 SelectionGoal::None,
9801 )
9802 });
9803 })
9804 }
9805
9806 pub fn select_to_end_of_paragraph(
9807 &mut self,
9808 _: &SelectToEndOfParagraph,
9809 window: &mut Window,
9810 cx: &mut Context<Self>,
9811 ) {
9812 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9813 cx.propagate();
9814 return;
9815 }
9816
9817 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9818 s.move_heads_with(|map, head, _| {
9819 (
9820 movement::end_of_paragraph(map, head, 1),
9821 SelectionGoal::None,
9822 )
9823 });
9824 })
9825 }
9826
9827 pub fn move_to_start_of_excerpt(
9828 &mut self,
9829 _: &MoveToStartOfExcerpt,
9830 window: &mut Window,
9831 cx: &mut Context<Self>,
9832 ) {
9833 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9834 cx.propagate();
9835 return;
9836 }
9837
9838 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9839 s.move_with(|map, selection| {
9840 selection.collapse_to(
9841 movement::start_of_excerpt(
9842 map,
9843 selection.head(),
9844 workspace::searchable::Direction::Prev,
9845 ),
9846 SelectionGoal::None,
9847 )
9848 });
9849 })
9850 }
9851
9852 pub fn move_to_end_of_excerpt(
9853 &mut self,
9854 _: &MoveToEndOfExcerpt,
9855 window: &mut Window,
9856 cx: &mut Context<Self>,
9857 ) {
9858 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9859 cx.propagate();
9860 return;
9861 }
9862
9863 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9864 s.move_with(|map, selection| {
9865 selection.collapse_to(
9866 movement::end_of_excerpt(
9867 map,
9868 selection.head(),
9869 workspace::searchable::Direction::Next,
9870 ),
9871 SelectionGoal::None,
9872 )
9873 });
9874 })
9875 }
9876
9877 pub fn select_to_start_of_excerpt(
9878 &mut self,
9879 _: &SelectToStartOfExcerpt,
9880 window: &mut Window,
9881 cx: &mut Context<Self>,
9882 ) {
9883 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9884 cx.propagate();
9885 return;
9886 }
9887
9888 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9889 s.move_heads_with(|map, head, _| {
9890 (
9891 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9892 SelectionGoal::None,
9893 )
9894 });
9895 })
9896 }
9897
9898 pub fn select_to_end_of_excerpt(
9899 &mut self,
9900 _: &SelectToEndOfExcerpt,
9901 window: &mut Window,
9902 cx: &mut Context<Self>,
9903 ) {
9904 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9905 cx.propagate();
9906 return;
9907 }
9908
9909 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9910 s.move_heads_with(|map, head, _| {
9911 (
9912 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9913 SelectionGoal::None,
9914 )
9915 });
9916 })
9917 }
9918
9919 pub fn move_to_beginning(
9920 &mut self,
9921 _: &MoveToBeginning,
9922 window: &mut Window,
9923 cx: &mut Context<Self>,
9924 ) {
9925 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9926 cx.propagate();
9927 return;
9928 }
9929
9930 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9931 s.select_ranges(vec![0..0]);
9932 });
9933 }
9934
9935 pub fn select_to_beginning(
9936 &mut self,
9937 _: &SelectToBeginning,
9938 window: &mut Window,
9939 cx: &mut Context<Self>,
9940 ) {
9941 let mut selection = self.selections.last::<Point>(cx);
9942 selection.set_head(Point::zero(), SelectionGoal::None);
9943
9944 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9945 s.select(vec![selection]);
9946 });
9947 }
9948
9949 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9950 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9951 cx.propagate();
9952 return;
9953 }
9954
9955 let cursor = self.buffer.read(cx).read(cx).len();
9956 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9957 s.select_ranges(vec![cursor..cursor])
9958 });
9959 }
9960
9961 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9962 self.nav_history = nav_history;
9963 }
9964
9965 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9966 self.nav_history.as_ref()
9967 }
9968
9969 fn push_to_nav_history(
9970 &mut self,
9971 cursor_anchor: Anchor,
9972 new_position: Option<Point>,
9973 cx: &mut Context<Self>,
9974 ) {
9975 if let Some(nav_history) = self.nav_history.as_mut() {
9976 let buffer = self.buffer.read(cx).read(cx);
9977 let cursor_position = cursor_anchor.to_point(&buffer);
9978 let scroll_state = self.scroll_manager.anchor();
9979 let scroll_top_row = scroll_state.top_row(&buffer);
9980 drop(buffer);
9981
9982 if let Some(new_position) = new_position {
9983 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9984 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9985 return;
9986 }
9987 }
9988
9989 nav_history.push(
9990 Some(NavigationData {
9991 cursor_anchor,
9992 cursor_position,
9993 scroll_anchor: scroll_state,
9994 scroll_top_row,
9995 }),
9996 cx,
9997 );
9998 }
9999 }
10000
10001 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10002 let buffer = self.buffer.read(cx).snapshot(cx);
10003 let mut selection = self.selections.first::<usize>(cx);
10004 selection.set_head(buffer.len(), SelectionGoal::None);
10005 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10006 s.select(vec![selection]);
10007 });
10008 }
10009
10010 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10011 let end = self.buffer.read(cx).read(cx).len();
10012 self.change_selections(None, window, cx, |s| {
10013 s.select_ranges(vec![0..end]);
10014 });
10015 }
10016
10017 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10018 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10019 let mut selections = self.selections.all::<Point>(cx);
10020 let max_point = display_map.buffer_snapshot.max_point();
10021 for selection in &mut selections {
10022 let rows = selection.spanned_rows(true, &display_map);
10023 selection.start = Point::new(rows.start.0, 0);
10024 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10025 selection.reversed = false;
10026 }
10027 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10028 s.select(selections);
10029 });
10030 }
10031
10032 pub fn split_selection_into_lines(
10033 &mut self,
10034 _: &SplitSelectionIntoLines,
10035 window: &mut Window,
10036 cx: &mut Context<Self>,
10037 ) {
10038 let selections = self
10039 .selections
10040 .all::<Point>(cx)
10041 .into_iter()
10042 .map(|selection| selection.start..selection.end)
10043 .collect::<Vec<_>>();
10044 self.unfold_ranges(&selections, true, true, cx);
10045
10046 let mut new_selection_ranges = Vec::new();
10047 {
10048 let buffer = self.buffer.read(cx).read(cx);
10049 for selection in selections {
10050 for row in selection.start.row..selection.end.row {
10051 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10052 new_selection_ranges.push(cursor..cursor);
10053 }
10054
10055 let is_multiline_selection = selection.start.row != selection.end.row;
10056 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10057 // so this action feels more ergonomic when paired with other selection operations
10058 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10059 if !should_skip_last {
10060 new_selection_ranges.push(selection.end..selection.end);
10061 }
10062 }
10063 }
10064 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10065 s.select_ranges(new_selection_ranges);
10066 });
10067 }
10068
10069 pub fn add_selection_above(
10070 &mut self,
10071 _: &AddSelectionAbove,
10072 window: &mut Window,
10073 cx: &mut Context<Self>,
10074 ) {
10075 self.add_selection(true, window, cx);
10076 }
10077
10078 pub fn add_selection_below(
10079 &mut self,
10080 _: &AddSelectionBelow,
10081 window: &mut Window,
10082 cx: &mut Context<Self>,
10083 ) {
10084 self.add_selection(false, window, cx);
10085 }
10086
10087 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10088 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10089 let mut selections = self.selections.all::<Point>(cx);
10090 let text_layout_details = self.text_layout_details(window);
10091 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10092 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10093 let range = oldest_selection.display_range(&display_map).sorted();
10094
10095 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10096 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10097 let positions = start_x.min(end_x)..start_x.max(end_x);
10098
10099 selections.clear();
10100 let mut stack = Vec::new();
10101 for row in range.start.row().0..=range.end.row().0 {
10102 if let Some(selection) = self.selections.build_columnar_selection(
10103 &display_map,
10104 DisplayRow(row),
10105 &positions,
10106 oldest_selection.reversed,
10107 &text_layout_details,
10108 ) {
10109 stack.push(selection.id);
10110 selections.push(selection);
10111 }
10112 }
10113
10114 if above {
10115 stack.reverse();
10116 }
10117
10118 AddSelectionsState { above, stack }
10119 });
10120
10121 let last_added_selection = *state.stack.last().unwrap();
10122 let mut new_selections = Vec::new();
10123 if above == state.above {
10124 let end_row = if above {
10125 DisplayRow(0)
10126 } else {
10127 display_map.max_point().row()
10128 };
10129
10130 'outer: for selection in selections {
10131 if selection.id == last_added_selection {
10132 let range = selection.display_range(&display_map).sorted();
10133 debug_assert_eq!(range.start.row(), range.end.row());
10134 let mut row = range.start.row();
10135 let positions =
10136 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10137 px(start)..px(end)
10138 } else {
10139 let start_x =
10140 display_map.x_for_display_point(range.start, &text_layout_details);
10141 let end_x =
10142 display_map.x_for_display_point(range.end, &text_layout_details);
10143 start_x.min(end_x)..start_x.max(end_x)
10144 };
10145
10146 while row != end_row {
10147 if above {
10148 row.0 -= 1;
10149 } else {
10150 row.0 += 1;
10151 }
10152
10153 if let Some(new_selection) = self.selections.build_columnar_selection(
10154 &display_map,
10155 row,
10156 &positions,
10157 selection.reversed,
10158 &text_layout_details,
10159 ) {
10160 state.stack.push(new_selection.id);
10161 if above {
10162 new_selections.push(new_selection);
10163 new_selections.push(selection);
10164 } else {
10165 new_selections.push(selection);
10166 new_selections.push(new_selection);
10167 }
10168
10169 continue 'outer;
10170 }
10171 }
10172 }
10173
10174 new_selections.push(selection);
10175 }
10176 } else {
10177 new_selections = selections;
10178 new_selections.retain(|s| s.id != last_added_selection);
10179 state.stack.pop();
10180 }
10181
10182 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10183 s.select(new_selections);
10184 });
10185 if state.stack.len() > 1 {
10186 self.add_selections_state = Some(state);
10187 }
10188 }
10189
10190 pub fn select_next_match_internal(
10191 &mut self,
10192 display_map: &DisplaySnapshot,
10193 replace_newest: bool,
10194 autoscroll: Option<Autoscroll>,
10195 window: &mut Window,
10196 cx: &mut Context<Self>,
10197 ) -> Result<()> {
10198 fn select_next_match_ranges(
10199 this: &mut Editor,
10200 range: Range<usize>,
10201 replace_newest: bool,
10202 auto_scroll: Option<Autoscroll>,
10203 window: &mut Window,
10204 cx: &mut Context<Editor>,
10205 ) {
10206 this.unfold_ranges(&[range.clone()], false, true, cx);
10207 this.change_selections(auto_scroll, window, cx, |s| {
10208 if replace_newest {
10209 s.delete(s.newest_anchor().id);
10210 }
10211 s.insert_range(range.clone());
10212 });
10213 }
10214
10215 let buffer = &display_map.buffer_snapshot;
10216 let mut selections = self.selections.all::<usize>(cx);
10217 if let Some(mut select_next_state) = self.select_next_state.take() {
10218 let query = &select_next_state.query;
10219 if !select_next_state.done {
10220 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10221 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10222 let mut next_selected_range = None;
10223
10224 let bytes_after_last_selection =
10225 buffer.bytes_in_range(last_selection.end..buffer.len());
10226 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10227 let query_matches = query
10228 .stream_find_iter(bytes_after_last_selection)
10229 .map(|result| (last_selection.end, result))
10230 .chain(
10231 query
10232 .stream_find_iter(bytes_before_first_selection)
10233 .map(|result| (0, result)),
10234 );
10235
10236 for (start_offset, query_match) in query_matches {
10237 let query_match = query_match.unwrap(); // can only fail due to I/O
10238 let offset_range =
10239 start_offset + query_match.start()..start_offset + query_match.end();
10240 let display_range = offset_range.start.to_display_point(display_map)
10241 ..offset_range.end.to_display_point(display_map);
10242
10243 if !select_next_state.wordwise
10244 || (!movement::is_inside_word(display_map, display_range.start)
10245 && !movement::is_inside_word(display_map, display_range.end))
10246 {
10247 // TODO: This is n^2, because we might check all the selections
10248 if !selections
10249 .iter()
10250 .any(|selection| selection.range().overlaps(&offset_range))
10251 {
10252 next_selected_range = Some(offset_range);
10253 break;
10254 }
10255 }
10256 }
10257
10258 if let Some(next_selected_range) = next_selected_range {
10259 select_next_match_ranges(
10260 self,
10261 next_selected_range,
10262 replace_newest,
10263 autoscroll,
10264 window,
10265 cx,
10266 );
10267 } else {
10268 select_next_state.done = true;
10269 }
10270 }
10271
10272 self.select_next_state = Some(select_next_state);
10273 } else {
10274 let mut only_carets = true;
10275 let mut same_text_selected = true;
10276 let mut selected_text = None;
10277
10278 let mut selections_iter = selections.iter().peekable();
10279 while let Some(selection) = selections_iter.next() {
10280 if selection.start != selection.end {
10281 only_carets = false;
10282 }
10283
10284 if same_text_selected {
10285 if selected_text.is_none() {
10286 selected_text =
10287 Some(buffer.text_for_range(selection.range()).collect::<String>());
10288 }
10289
10290 if let Some(next_selection) = selections_iter.peek() {
10291 if next_selection.range().len() == selection.range().len() {
10292 let next_selected_text = buffer
10293 .text_for_range(next_selection.range())
10294 .collect::<String>();
10295 if Some(next_selected_text) != selected_text {
10296 same_text_selected = false;
10297 selected_text = None;
10298 }
10299 } else {
10300 same_text_selected = false;
10301 selected_text = None;
10302 }
10303 }
10304 }
10305 }
10306
10307 if only_carets {
10308 for selection in &mut selections {
10309 let word_range = movement::surrounding_word(
10310 display_map,
10311 selection.start.to_display_point(display_map),
10312 );
10313 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10314 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10315 selection.goal = SelectionGoal::None;
10316 selection.reversed = false;
10317 select_next_match_ranges(
10318 self,
10319 selection.start..selection.end,
10320 replace_newest,
10321 autoscroll,
10322 window,
10323 cx,
10324 );
10325 }
10326
10327 if selections.len() == 1 {
10328 let selection = selections
10329 .last()
10330 .expect("ensured that there's only one selection");
10331 let query = buffer
10332 .text_for_range(selection.start..selection.end)
10333 .collect::<String>();
10334 let is_empty = query.is_empty();
10335 let select_state = SelectNextState {
10336 query: AhoCorasick::new(&[query])?,
10337 wordwise: true,
10338 done: is_empty,
10339 };
10340 self.select_next_state = Some(select_state);
10341 } else {
10342 self.select_next_state = None;
10343 }
10344 } else if let Some(selected_text) = selected_text {
10345 self.select_next_state = Some(SelectNextState {
10346 query: AhoCorasick::new(&[selected_text])?,
10347 wordwise: false,
10348 done: false,
10349 });
10350 self.select_next_match_internal(
10351 display_map,
10352 replace_newest,
10353 autoscroll,
10354 window,
10355 cx,
10356 )?;
10357 }
10358 }
10359 Ok(())
10360 }
10361
10362 pub fn select_all_matches(
10363 &mut self,
10364 _action: &SelectAllMatches,
10365 window: &mut Window,
10366 cx: &mut Context<Self>,
10367 ) -> Result<()> {
10368 self.push_to_selection_history();
10369 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10370
10371 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10372 let Some(select_next_state) = self.select_next_state.as_mut() else {
10373 return Ok(());
10374 };
10375 if select_next_state.done {
10376 return Ok(());
10377 }
10378
10379 let mut new_selections = self.selections.all::<usize>(cx);
10380
10381 let buffer = &display_map.buffer_snapshot;
10382 let query_matches = select_next_state
10383 .query
10384 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10385
10386 for query_match in query_matches {
10387 let query_match = query_match.unwrap(); // can only fail due to I/O
10388 let offset_range = query_match.start()..query_match.end();
10389 let display_range = offset_range.start.to_display_point(&display_map)
10390 ..offset_range.end.to_display_point(&display_map);
10391
10392 if !select_next_state.wordwise
10393 || (!movement::is_inside_word(&display_map, display_range.start)
10394 && !movement::is_inside_word(&display_map, display_range.end))
10395 {
10396 self.selections.change_with(cx, |selections| {
10397 new_selections.push(Selection {
10398 id: selections.new_selection_id(),
10399 start: offset_range.start,
10400 end: offset_range.end,
10401 reversed: false,
10402 goal: SelectionGoal::None,
10403 });
10404 });
10405 }
10406 }
10407
10408 new_selections.sort_by_key(|selection| selection.start);
10409 let mut ix = 0;
10410 while ix + 1 < new_selections.len() {
10411 let current_selection = &new_selections[ix];
10412 let next_selection = &new_selections[ix + 1];
10413 if current_selection.range().overlaps(&next_selection.range()) {
10414 if current_selection.id < next_selection.id {
10415 new_selections.remove(ix + 1);
10416 } else {
10417 new_selections.remove(ix);
10418 }
10419 } else {
10420 ix += 1;
10421 }
10422 }
10423
10424 let reversed = self.selections.oldest::<usize>(cx).reversed;
10425
10426 for selection in new_selections.iter_mut() {
10427 selection.reversed = reversed;
10428 }
10429
10430 select_next_state.done = true;
10431 self.unfold_ranges(
10432 &new_selections
10433 .iter()
10434 .map(|selection| selection.range())
10435 .collect::<Vec<_>>(),
10436 false,
10437 false,
10438 cx,
10439 );
10440 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10441 selections.select(new_selections)
10442 });
10443
10444 Ok(())
10445 }
10446
10447 pub fn select_next(
10448 &mut self,
10449 action: &SelectNext,
10450 window: &mut Window,
10451 cx: &mut Context<Self>,
10452 ) -> Result<()> {
10453 self.push_to_selection_history();
10454 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10455 self.select_next_match_internal(
10456 &display_map,
10457 action.replace_newest,
10458 Some(Autoscroll::newest()),
10459 window,
10460 cx,
10461 )?;
10462 Ok(())
10463 }
10464
10465 pub fn select_previous(
10466 &mut self,
10467 action: &SelectPrevious,
10468 window: &mut Window,
10469 cx: &mut Context<Self>,
10470 ) -> Result<()> {
10471 self.push_to_selection_history();
10472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10473 let buffer = &display_map.buffer_snapshot;
10474 let mut selections = self.selections.all::<usize>(cx);
10475 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10476 let query = &select_prev_state.query;
10477 if !select_prev_state.done {
10478 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10479 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10480 let mut next_selected_range = None;
10481 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10482 let bytes_before_last_selection =
10483 buffer.reversed_bytes_in_range(0..last_selection.start);
10484 let bytes_after_first_selection =
10485 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10486 let query_matches = query
10487 .stream_find_iter(bytes_before_last_selection)
10488 .map(|result| (last_selection.start, result))
10489 .chain(
10490 query
10491 .stream_find_iter(bytes_after_first_selection)
10492 .map(|result| (buffer.len(), result)),
10493 );
10494 for (end_offset, query_match) in query_matches {
10495 let query_match = query_match.unwrap(); // can only fail due to I/O
10496 let offset_range =
10497 end_offset - query_match.end()..end_offset - query_match.start();
10498 let display_range = offset_range.start.to_display_point(&display_map)
10499 ..offset_range.end.to_display_point(&display_map);
10500
10501 if !select_prev_state.wordwise
10502 || (!movement::is_inside_word(&display_map, display_range.start)
10503 && !movement::is_inside_word(&display_map, display_range.end))
10504 {
10505 next_selected_range = Some(offset_range);
10506 break;
10507 }
10508 }
10509
10510 if let Some(next_selected_range) = next_selected_range {
10511 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10512 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10513 if action.replace_newest {
10514 s.delete(s.newest_anchor().id);
10515 }
10516 s.insert_range(next_selected_range);
10517 });
10518 } else {
10519 select_prev_state.done = true;
10520 }
10521 }
10522
10523 self.select_prev_state = Some(select_prev_state);
10524 } else {
10525 let mut only_carets = true;
10526 let mut same_text_selected = true;
10527 let mut selected_text = None;
10528
10529 let mut selections_iter = selections.iter().peekable();
10530 while let Some(selection) = selections_iter.next() {
10531 if selection.start != selection.end {
10532 only_carets = false;
10533 }
10534
10535 if same_text_selected {
10536 if selected_text.is_none() {
10537 selected_text =
10538 Some(buffer.text_for_range(selection.range()).collect::<String>());
10539 }
10540
10541 if let Some(next_selection) = selections_iter.peek() {
10542 if next_selection.range().len() == selection.range().len() {
10543 let next_selected_text = buffer
10544 .text_for_range(next_selection.range())
10545 .collect::<String>();
10546 if Some(next_selected_text) != selected_text {
10547 same_text_selected = false;
10548 selected_text = None;
10549 }
10550 } else {
10551 same_text_selected = false;
10552 selected_text = None;
10553 }
10554 }
10555 }
10556 }
10557
10558 if only_carets {
10559 for selection in &mut selections {
10560 let word_range = movement::surrounding_word(
10561 &display_map,
10562 selection.start.to_display_point(&display_map),
10563 );
10564 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10565 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10566 selection.goal = SelectionGoal::None;
10567 selection.reversed = false;
10568 }
10569 if selections.len() == 1 {
10570 let selection = selections
10571 .last()
10572 .expect("ensured that there's only one selection");
10573 let query = buffer
10574 .text_for_range(selection.start..selection.end)
10575 .collect::<String>();
10576 let is_empty = query.is_empty();
10577 let select_state = SelectNextState {
10578 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10579 wordwise: true,
10580 done: is_empty,
10581 };
10582 self.select_prev_state = Some(select_state);
10583 } else {
10584 self.select_prev_state = None;
10585 }
10586
10587 self.unfold_ranges(
10588 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10589 false,
10590 true,
10591 cx,
10592 );
10593 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10594 s.select(selections);
10595 });
10596 } else if let Some(selected_text) = selected_text {
10597 self.select_prev_state = Some(SelectNextState {
10598 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10599 wordwise: false,
10600 done: false,
10601 });
10602 self.select_previous(action, window, cx)?;
10603 }
10604 }
10605 Ok(())
10606 }
10607
10608 pub fn toggle_comments(
10609 &mut self,
10610 action: &ToggleComments,
10611 window: &mut Window,
10612 cx: &mut Context<Self>,
10613 ) {
10614 if self.read_only(cx) {
10615 return;
10616 }
10617 let text_layout_details = &self.text_layout_details(window);
10618 self.transact(window, cx, |this, window, cx| {
10619 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10620 let mut edits = Vec::new();
10621 let mut selection_edit_ranges = Vec::new();
10622 let mut last_toggled_row = None;
10623 let snapshot = this.buffer.read(cx).read(cx);
10624 let empty_str: Arc<str> = Arc::default();
10625 let mut suffixes_inserted = Vec::new();
10626 let ignore_indent = action.ignore_indent;
10627
10628 fn comment_prefix_range(
10629 snapshot: &MultiBufferSnapshot,
10630 row: MultiBufferRow,
10631 comment_prefix: &str,
10632 comment_prefix_whitespace: &str,
10633 ignore_indent: bool,
10634 ) -> Range<Point> {
10635 let indent_size = if ignore_indent {
10636 0
10637 } else {
10638 snapshot.indent_size_for_line(row).len
10639 };
10640
10641 let start = Point::new(row.0, indent_size);
10642
10643 let mut line_bytes = snapshot
10644 .bytes_in_range(start..snapshot.max_point())
10645 .flatten()
10646 .copied();
10647
10648 // If this line currently begins with the line comment prefix, then record
10649 // the range containing the prefix.
10650 if line_bytes
10651 .by_ref()
10652 .take(comment_prefix.len())
10653 .eq(comment_prefix.bytes())
10654 {
10655 // Include any whitespace that matches the comment prefix.
10656 let matching_whitespace_len = line_bytes
10657 .zip(comment_prefix_whitespace.bytes())
10658 .take_while(|(a, b)| a == b)
10659 .count() as u32;
10660 let end = Point::new(
10661 start.row,
10662 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10663 );
10664 start..end
10665 } else {
10666 start..start
10667 }
10668 }
10669
10670 fn comment_suffix_range(
10671 snapshot: &MultiBufferSnapshot,
10672 row: MultiBufferRow,
10673 comment_suffix: &str,
10674 comment_suffix_has_leading_space: bool,
10675 ) -> Range<Point> {
10676 let end = Point::new(row.0, snapshot.line_len(row));
10677 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10678
10679 let mut line_end_bytes = snapshot
10680 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10681 .flatten()
10682 .copied();
10683
10684 let leading_space_len = if suffix_start_column > 0
10685 && line_end_bytes.next() == Some(b' ')
10686 && comment_suffix_has_leading_space
10687 {
10688 1
10689 } else {
10690 0
10691 };
10692
10693 // If this line currently begins with the line comment prefix, then record
10694 // the range containing the prefix.
10695 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10696 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10697 start..end
10698 } else {
10699 end..end
10700 }
10701 }
10702
10703 // TODO: Handle selections that cross excerpts
10704 for selection in &mut selections {
10705 let start_column = snapshot
10706 .indent_size_for_line(MultiBufferRow(selection.start.row))
10707 .len;
10708 let language = if let Some(language) =
10709 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10710 {
10711 language
10712 } else {
10713 continue;
10714 };
10715
10716 selection_edit_ranges.clear();
10717
10718 // If multiple selections contain a given row, avoid processing that
10719 // row more than once.
10720 let mut start_row = MultiBufferRow(selection.start.row);
10721 if last_toggled_row == Some(start_row) {
10722 start_row = start_row.next_row();
10723 }
10724 let end_row =
10725 if selection.end.row > selection.start.row && selection.end.column == 0 {
10726 MultiBufferRow(selection.end.row - 1)
10727 } else {
10728 MultiBufferRow(selection.end.row)
10729 };
10730 last_toggled_row = Some(end_row);
10731
10732 if start_row > end_row {
10733 continue;
10734 }
10735
10736 // If the language has line comments, toggle those.
10737 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10738
10739 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10740 if ignore_indent {
10741 full_comment_prefixes = full_comment_prefixes
10742 .into_iter()
10743 .map(|s| Arc::from(s.trim_end()))
10744 .collect();
10745 }
10746
10747 if !full_comment_prefixes.is_empty() {
10748 let first_prefix = full_comment_prefixes
10749 .first()
10750 .expect("prefixes is non-empty");
10751 let prefix_trimmed_lengths = full_comment_prefixes
10752 .iter()
10753 .map(|p| p.trim_end_matches(' ').len())
10754 .collect::<SmallVec<[usize; 4]>>();
10755
10756 let mut all_selection_lines_are_comments = true;
10757
10758 for row in start_row.0..=end_row.0 {
10759 let row = MultiBufferRow(row);
10760 if start_row < end_row && snapshot.is_line_blank(row) {
10761 continue;
10762 }
10763
10764 let prefix_range = full_comment_prefixes
10765 .iter()
10766 .zip(prefix_trimmed_lengths.iter().copied())
10767 .map(|(prefix, trimmed_prefix_len)| {
10768 comment_prefix_range(
10769 snapshot.deref(),
10770 row,
10771 &prefix[..trimmed_prefix_len],
10772 &prefix[trimmed_prefix_len..],
10773 ignore_indent,
10774 )
10775 })
10776 .max_by_key(|range| range.end.column - range.start.column)
10777 .expect("prefixes is non-empty");
10778
10779 if prefix_range.is_empty() {
10780 all_selection_lines_are_comments = false;
10781 }
10782
10783 selection_edit_ranges.push(prefix_range);
10784 }
10785
10786 if all_selection_lines_are_comments {
10787 edits.extend(
10788 selection_edit_ranges
10789 .iter()
10790 .cloned()
10791 .map(|range| (range, empty_str.clone())),
10792 );
10793 } else {
10794 let min_column = selection_edit_ranges
10795 .iter()
10796 .map(|range| range.start.column)
10797 .min()
10798 .unwrap_or(0);
10799 edits.extend(selection_edit_ranges.iter().map(|range| {
10800 let position = Point::new(range.start.row, min_column);
10801 (position..position, first_prefix.clone())
10802 }));
10803 }
10804 } else if let Some((full_comment_prefix, comment_suffix)) =
10805 language.block_comment_delimiters()
10806 {
10807 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10808 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10809 let prefix_range = comment_prefix_range(
10810 snapshot.deref(),
10811 start_row,
10812 comment_prefix,
10813 comment_prefix_whitespace,
10814 ignore_indent,
10815 );
10816 let suffix_range = comment_suffix_range(
10817 snapshot.deref(),
10818 end_row,
10819 comment_suffix.trim_start_matches(' '),
10820 comment_suffix.starts_with(' '),
10821 );
10822
10823 if prefix_range.is_empty() || suffix_range.is_empty() {
10824 edits.push((
10825 prefix_range.start..prefix_range.start,
10826 full_comment_prefix.clone(),
10827 ));
10828 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10829 suffixes_inserted.push((end_row, comment_suffix.len()));
10830 } else {
10831 edits.push((prefix_range, empty_str.clone()));
10832 edits.push((suffix_range, empty_str.clone()));
10833 }
10834 } else {
10835 continue;
10836 }
10837 }
10838
10839 drop(snapshot);
10840 this.buffer.update(cx, |buffer, cx| {
10841 buffer.edit(edits, None, cx);
10842 });
10843
10844 // Adjust selections so that they end before any comment suffixes that
10845 // were inserted.
10846 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10847 let mut selections = this.selections.all::<Point>(cx);
10848 let snapshot = this.buffer.read(cx).read(cx);
10849 for selection in &mut selections {
10850 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10851 match row.cmp(&MultiBufferRow(selection.end.row)) {
10852 Ordering::Less => {
10853 suffixes_inserted.next();
10854 continue;
10855 }
10856 Ordering::Greater => break,
10857 Ordering::Equal => {
10858 if selection.end.column == snapshot.line_len(row) {
10859 if selection.is_empty() {
10860 selection.start.column -= suffix_len as u32;
10861 }
10862 selection.end.column -= suffix_len as u32;
10863 }
10864 break;
10865 }
10866 }
10867 }
10868 }
10869
10870 drop(snapshot);
10871 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10872 s.select(selections)
10873 });
10874
10875 let selections = this.selections.all::<Point>(cx);
10876 let selections_on_single_row = selections.windows(2).all(|selections| {
10877 selections[0].start.row == selections[1].start.row
10878 && selections[0].end.row == selections[1].end.row
10879 && selections[0].start.row == selections[0].end.row
10880 });
10881 let selections_selecting = selections
10882 .iter()
10883 .any(|selection| selection.start != selection.end);
10884 let advance_downwards = action.advance_downwards
10885 && selections_on_single_row
10886 && !selections_selecting
10887 && !matches!(this.mode, EditorMode::SingleLine { .. });
10888
10889 if advance_downwards {
10890 let snapshot = this.buffer.read(cx).snapshot(cx);
10891
10892 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10893 s.move_cursors_with(|display_snapshot, display_point, _| {
10894 let mut point = display_point.to_point(display_snapshot);
10895 point.row += 1;
10896 point = snapshot.clip_point(point, Bias::Left);
10897 let display_point = point.to_display_point(display_snapshot);
10898 let goal = SelectionGoal::HorizontalPosition(
10899 display_snapshot
10900 .x_for_display_point(display_point, text_layout_details)
10901 .into(),
10902 );
10903 (display_point, goal)
10904 })
10905 });
10906 }
10907 });
10908 }
10909
10910 pub fn select_enclosing_symbol(
10911 &mut self,
10912 _: &SelectEnclosingSymbol,
10913 window: &mut Window,
10914 cx: &mut Context<Self>,
10915 ) {
10916 let buffer = self.buffer.read(cx).snapshot(cx);
10917 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10918
10919 fn update_selection(
10920 selection: &Selection<usize>,
10921 buffer_snap: &MultiBufferSnapshot,
10922 ) -> Option<Selection<usize>> {
10923 let cursor = selection.head();
10924 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10925 for symbol in symbols.iter().rev() {
10926 let start = symbol.range.start.to_offset(buffer_snap);
10927 let end = symbol.range.end.to_offset(buffer_snap);
10928 let new_range = start..end;
10929 if start < selection.start || end > selection.end {
10930 return Some(Selection {
10931 id: selection.id,
10932 start: new_range.start,
10933 end: new_range.end,
10934 goal: SelectionGoal::None,
10935 reversed: selection.reversed,
10936 });
10937 }
10938 }
10939 None
10940 }
10941
10942 let mut selected_larger_symbol = false;
10943 let new_selections = old_selections
10944 .iter()
10945 .map(|selection| match update_selection(selection, &buffer) {
10946 Some(new_selection) => {
10947 if new_selection.range() != selection.range() {
10948 selected_larger_symbol = true;
10949 }
10950 new_selection
10951 }
10952 None => selection.clone(),
10953 })
10954 .collect::<Vec<_>>();
10955
10956 if selected_larger_symbol {
10957 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10958 s.select(new_selections);
10959 });
10960 }
10961 }
10962
10963 pub fn select_larger_syntax_node(
10964 &mut self,
10965 _: &SelectLargerSyntaxNode,
10966 window: &mut Window,
10967 cx: &mut Context<Self>,
10968 ) {
10969 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10970 let buffer = self.buffer.read(cx).snapshot(cx);
10971 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10972
10973 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10974 let mut selected_larger_node = false;
10975 let new_selections = old_selections
10976 .iter()
10977 .map(|selection| {
10978 let old_range = selection.start..selection.end;
10979 let mut new_range = old_range.clone();
10980 let mut new_node = None;
10981 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10982 {
10983 new_node = Some(node);
10984 new_range = match containing_range {
10985 MultiOrSingleBufferOffsetRange::Single(_) => break,
10986 MultiOrSingleBufferOffsetRange::Multi(range) => range,
10987 };
10988 if !display_map.intersects_fold(new_range.start)
10989 && !display_map.intersects_fold(new_range.end)
10990 {
10991 break;
10992 }
10993 }
10994
10995 if let Some(node) = new_node {
10996 // Log the ancestor, to support using this action as a way to explore TreeSitter
10997 // nodes. Parent and grandparent are also logged because this operation will not
10998 // visit nodes that have the same range as their parent.
10999 log::info!("Node: {node:?}");
11000 let parent = node.parent();
11001 log::info!("Parent: {parent:?}");
11002 let grandparent = parent.and_then(|x| x.parent());
11003 log::info!("Grandparent: {grandparent:?}");
11004 }
11005
11006 selected_larger_node |= new_range != old_range;
11007 Selection {
11008 id: selection.id,
11009 start: new_range.start,
11010 end: new_range.end,
11011 goal: SelectionGoal::None,
11012 reversed: selection.reversed,
11013 }
11014 })
11015 .collect::<Vec<_>>();
11016
11017 if selected_larger_node {
11018 stack.push(old_selections);
11019 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11020 s.select(new_selections);
11021 });
11022 }
11023 self.select_larger_syntax_node_stack = stack;
11024 }
11025
11026 pub fn select_smaller_syntax_node(
11027 &mut self,
11028 _: &SelectSmallerSyntaxNode,
11029 window: &mut Window,
11030 cx: &mut Context<Self>,
11031 ) {
11032 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11033 if let Some(selections) = stack.pop() {
11034 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11035 s.select(selections.to_vec());
11036 });
11037 }
11038 self.select_larger_syntax_node_stack = stack;
11039 }
11040
11041 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11042 if !EditorSettings::get_global(cx).gutter.runnables {
11043 self.clear_tasks();
11044 return Task::ready(());
11045 }
11046 let project = self.project.as_ref().map(Entity::downgrade);
11047 cx.spawn_in(window, |this, mut cx| async move {
11048 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11049 let Some(project) = project.and_then(|p| p.upgrade()) else {
11050 return;
11051 };
11052 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11053 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11054 }) else {
11055 return;
11056 };
11057
11058 let hide_runnables = project
11059 .update(&mut cx, |project, cx| {
11060 // Do not display any test indicators in non-dev server remote projects.
11061 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11062 })
11063 .unwrap_or(true);
11064 if hide_runnables {
11065 return;
11066 }
11067 let new_rows =
11068 cx.background_spawn({
11069 let snapshot = display_snapshot.clone();
11070 async move {
11071 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11072 }
11073 })
11074 .await;
11075
11076 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11077 this.update(&mut cx, |this, _| {
11078 this.clear_tasks();
11079 for (key, value) in rows {
11080 this.insert_tasks(key, value);
11081 }
11082 })
11083 .ok();
11084 })
11085 }
11086 fn fetch_runnable_ranges(
11087 snapshot: &DisplaySnapshot,
11088 range: Range<Anchor>,
11089 ) -> Vec<language::RunnableRange> {
11090 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11091 }
11092
11093 fn runnable_rows(
11094 project: Entity<Project>,
11095 snapshot: DisplaySnapshot,
11096 runnable_ranges: Vec<RunnableRange>,
11097 mut cx: AsyncWindowContext,
11098 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11099 runnable_ranges
11100 .into_iter()
11101 .filter_map(|mut runnable| {
11102 let tasks = cx
11103 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11104 .ok()?;
11105 if tasks.is_empty() {
11106 return None;
11107 }
11108
11109 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11110
11111 let row = snapshot
11112 .buffer_snapshot
11113 .buffer_line_for_row(MultiBufferRow(point.row))?
11114 .1
11115 .start
11116 .row;
11117
11118 let context_range =
11119 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11120 Some((
11121 (runnable.buffer_id, row),
11122 RunnableTasks {
11123 templates: tasks,
11124 offset: snapshot
11125 .buffer_snapshot
11126 .anchor_before(runnable.run_range.start),
11127 context_range,
11128 column: point.column,
11129 extra_variables: runnable.extra_captures,
11130 },
11131 ))
11132 })
11133 .collect()
11134 }
11135
11136 fn templates_with_tags(
11137 project: &Entity<Project>,
11138 runnable: &mut Runnable,
11139 cx: &mut App,
11140 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11141 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11142 let (worktree_id, file) = project
11143 .buffer_for_id(runnable.buffer, cx)
11144 .and_then(|buffer| buffer.read(cx).file())
11145 .map(|file| (file.worktree_id(cx), file.clone()))
11146 .unzip();
11147
11148 (
11149 project.task_store().read(cx).task_inventory().cloned(),
11150 worktree_id,
11151 file,
11152 )
11153 });
11154
11155 let tags = mem::take(&mut runnable.tags);
11156 let mut tags: Vec<_> = tags
11157 .into_iter()
11158 .flat_map(|tag| {
11159 let tag = tag.0.clone();
11160 inventory
11161 .as_ref()
11162 .into_iter()
11163 .flat_map(|inventory| {
11164 inventory.read(cx).list_tasks(
11165 file.clone(),
11166 Some(runnable.language.clone()),
11167 worktree_id,
11168 cx,
11169 )
11170 })
11171 .filter(move |(_, template)| {
11172 template.tags.iter().any(|source_tag| source_tag == &tag)
11173 })
11174 })
11175 .sorted_by_key(|(kind, _)| kind.to_owned())
11176 .collect();
11177 if let Some((leading_tag_source, _)) = tags.first() {
11178 // Strongest source wins; if we have worktree tag binding, prefer that to
11179 // global and language bindings;
11180 // if we have a global binding, prefer that to language binding.
11181 let first_mismatch = tags
11182 .iter()
11183 .position(|(tag_source, _)| tag_source != leading_tag_source);
11184 if let Some(index) = first_mismatch {
11185 tags.truncate(index);
11186 }
11187 }
11188
11189 tags
11190 }
11191
11192 pub fn move_to_enclosing_bracket(
11193 &mut self,
11194 _: &MoveToEnclosingBracket,
11195 window: &mut Window,
11196 cx: &mut Context<Self>,
11197 ) {
11198 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11199 s.move_offsets_with(|snapshot, selection| {
11200 let Some(enclosing_bracket_ranges) =
11201 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11202 else {
11203 return;
11204 };
11205
11206 let mut best_length = usize::MAX;
11207 let mut best_inside = false;
11208 let mut best_in_bracket_range = false;
11209 let mut best_destination = None;
11210 for (open, close) in enclosing_bracket_ranges {
11211 let close = close.to_inclusive();
11212 let length = close.end() - open.start;
11213 let inside = selection.start >= open.end && selection.end <= *close.start();
11214 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11215 || close.contains(&selection.head());
11216
11217 // If best is next to a bracket and current isn't, skip
11218 if !in_bracket_range && best_in_bracket_range {
11219 continue;
11220 }
11221
11222 // Prefer smaller lengths unless best is inside and current isn't
11223 if length > best_length && (best_inside || !inside) {
11224 continue;
11225 }
11226
11227 best_length = length;
11228 best_inside = inside;
11229 best_in_bracket_range = in_bracket_range;
11230 best_destination = Some(
11231 if close.contains(&selection.start) && close.contains(&selection.end) {
11232 if inside {
11233 open.end
11234 } else {
11235 open.start
11236 }
11237 } else if inside {
11238 *close.start()
11239 } else {
11240 *close.end()
11241 },
11242 );
11243 }
11244
11245 if let Some(destination) = best_destination {
11246 selection.collapse_to(destination, SelectionGoal::None);
11247 }
11248 })
11249 });
11250 }
11251
11252 pub fn undo_selection(
11253 &mut self,
11254 _: &UndoSelection,
11255 window: &mut Window,
11256 cx: &mut Context<Self>,
11257 ) {
11258 self.end_selection(window, cx);
11259 self.selection_history.mode = SelectionHistoryMode::Undoing;
11260 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
11261 self.change_selections(None, window, cx, |s| {
11262 s.select_anchors(entry.selections.to_vec())
11263 });
11264 self.select_next_state = entry.select_next_state;
11265 self.select_prev_state = entry.select_prev_state;
11266 self.add_selections_state = entry.add_selections_state;
11267 self.request_autoscroll(Autoscroll::newest(), cx);
11268 }
11269 self.selection_history.mode = SelectionHistoryMode::Normal;
11270 }
11271
11272 pub fn redo_selection(
11273 &mut self,
11274 _: &RedoSelection,
11275 window: &mut Window,
11276 cx: &mut Context<Self>,
11277 ) {
11278 self.end_selection(window, cx);
11279 self.selection_history.mode = SelectionHistoryMode::Redoing;
11280 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11281 self.change_selections(None, window, cx, |s| {
11282 s.select_anchors(entry.selections.to_vec())
11283 });
11284 self.select_next_state = entry.select_next_state;
11285 self.select_prev_state = entry.select_prev_state;
11286 self.add_selections_state = entry.add_selections_state;
11287 self.request_autoscroll(Autoscroll::newest(), cx);
11288 }
11289 self.selection_history.mode = SelectionHistoryMode::Normal;
11290 }
11291
11292 pub fn expand_excerpts(
11293 &mut self,
11294 action: &ExpandExcerpts,
11295 _: &mut Window,
11296 cx: &mut Context<Self>,
11297 ) {
11298 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11299 }
11300
11301 pub fn expand_excerpts_down(
11302 &mut self,
11303 action: &ExpandExcerptsDown,
11304 _: &mut Window,
11305 cx: &mut Context<Self>,
11306 ) {
11307 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11308 }
11309
11310 pub fn expand_excerpts_up(
11311 &mut self,
11312 action: &ExpandExcerptsUp,
11313 _: &mut Window,
11314 cx: &mut Context<Self>,
11315 ) {
11316 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11317 }
11318
11319 pub fn expand_excerpts_for_direction(
11320 &mut self,
11321 lines: u32,
11322 direction: ExpandExcerptDirection,
11323
11324 cx: &mut Context<Self>,
11325 ) {
11326 let selections = self.selections.disjoint_anchors();
11327
11328 let lines = if lines == 0 {
11329 EditorSettings::get_global(cx).expand_excerpt_lines
11330 } else {
11331 lines
11332 };
11333
11334 self.buffer.update(cx, |buffer, cx| {
11335 let snapshot = buffer.snapshot(cx);
11336 let mut excerpt_ids = selections
11337 .iter()
11338 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11339 .collect::<Vec<_>>();
11340 excerpt_ids.sort();
11341 excerpt_ids.dedup();
11342 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11343 })
11344 }
11345
11346 pub fn expand_excerpt(
11347 &mut self,
11348 excerpt: ExcerptId,
11349 direction: ExpandExcerptDirection,
11350 cx: &mut Context<Self>,
11351 ) {
11352 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11353 self.buffer.update(cx, |buffer, cx| {
11354 buffer.expand_excerpts([excerpt], lines, direction, cx)
11355 })
11356 }
11357
11358 pub fn go_to_singleton_buffer_point(
11359 &mut self,
11360 point: Point,
11361 window: &mut Window,
11362 cx: &mut Context<Self>,
11363 ) {
11364 self.go_to_singleton_buffer_range(point..point, window, cx);
11365 }
11366
11367 pub fn go_to_singleton_buffer_range(
11368 &mut self,
11369 range: Range<Point>,
11370 window: &mut Window,
11371 cx: &mut Context<Self>,
11372 ) {
11373 let multibuffer = self.buffer().read(cx);
11374 let Some(buffer) = multibuffer.as_singleton() else {
11375 return;
11376 };
11377 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11378 return;
11379 };
11380 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11381 return;
11382 };
11383 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11384 s.select_anchor_ranges([start..end])
11385 });
11386 }
11387
11388 fn go_to_diagnostic(
11389 &mut self,
11390 _: &GoToDiagnostic,
11391 window: &mut Window,
11392 cx: &mut Context<Self>,
11393 ) {
11394 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11395 }
11396
11397 fn go_to_prev_diagnostic(
11398 &mut self,
11399 _: &GoToPreviousDiagnostic,
11400 window: &mut Window,
11401 cx: &mut Context<Self>,
11402 ) {
11403 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11404 }
11405
11406 pub fn go_to_diagnostic_impl(
11407 &mut self,
11408 direction: Direction,
11409 window: &mut Window,
11410 cx: &mut Context<Self>,
11411 ) {
11412 let buffer = self.buffer.read(cx).snapshot(cx);
11413 let selection = self.selections.newest::<usize>(cx);
11414
11415 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11416 if direction == Direction::Next {
11417 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11418 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11419 return;
11420 };
11421 self.activate_diagnostics(
11422 buffer_id,
11423 popover.local_diagnostic.diagnostic.group_id,
11424 window,
11425 cx,
11426 );
11427 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11428 let primary_range_start = active_diagnostics.primary_range.start;
11429 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11430 let mut new_selection = s.newest_anchor().clone();
11431 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11432 s.select_anchors(vec![new_selection.clone()]);
11433 });
11434 self.refresh_inline_completion(false, true, window, cx);
11435 }
11436 return;
11437 }
11438 }
11439
11440 let active_group_id = self
11441 .active_diagnostics
11442 .as_ref()
11443 .map(|active_group| active_group.group_id);
11444 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11445 active_diagnostics
11446 .primary_range
11447 .to_offset(&buffer)
11448 .to_inclusive()
11449 });
11450 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11451 if active_primary_range.contains(&selection.head()) {
11452 *active_primary_range.start()
11453 } else {
11454 selection.head()
11455 }
11456 } else {
11457 selection.head()
11458 };
11459
11460 let snapshot = self.snapshot(window, cx);
11461 let primary_diagnostics_before = buffer
11462 .diagnostics_in_range::<usize>(0..search_start)
11463 .filter(|entry| entry.diagnostic.is_primary)
11464 .filter(|entry| entry.range.start != entry.range.end)
11465 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11466 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11467 .collect::<Vec<_>>();
11468 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11469 primary_diagnostics_before
11470 .iter()
11471 .position(|entry| entry.diagnostic.group_id == active_group_id)
11472 });
11473
11474 let primary_diagnostics_after = buffer
11475 .diagnostics_in_range::<usize>(search_start..buffer.len())
11476 .filter(|entry| entry.diagnostic.is_primary)
11477 .filter(|entry| entry.range.start != entry.range.end)
11478 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11479 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11480 .collect::<Vec<_>>();
11481 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11482 primary_diagnostics_after
11483 .iter()
11484 .enumerate()
11485 .rev()
11486 .find_map(|(i, entry)| {
11487 if entry.diagnostic.group_id == active_group_id {
11488 Some(i)
11489 } else {
11490 None
11491 }
11492 })
11493 });
11494
11495 let next_primary_diagnostic = match direction {
11496 Direction::Prev => primary_diagnostics_before
11497 .iter()
11498 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11499 .rev()
11500 .next(),
11501 Direction::Next => primary_diagnostics_after
11502 .iter()
11503 .skip(
11504 last_same_group_diagnostic_after
11505 .map(|index| index + 1)
11506 .unwrap_or(0),
11507 )
11508 .next(),
11509 };
11510
11511 // Cycle around to the start of the buffer, potentially moving back to the start of
11512 // the currently active diagnostic.
11513 let cycle_around = || match direction {
11514 Direction::Prev => primary_diagnostics_after
11515 .iter()
11516 .rev()
11517 .chain(primary_diagnostics_before.iter().rev())
11518 .next(),
11519 Direction::Next => primary_diagnostics_before
11520 .iter()
11521 .chain(primary_diagnostics_after.iter())
11522 .next(),
11523 };
11524
11525 if let Some((primary_range, group_id)) = next_primary_diagnostic
11526 .or_else(cycle_around)
11527 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11528 {
11529 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11530 return;
11531 };
11532 self.activate_diagnostics(buffer_id, group_id, window, cx);
11533 if self.active_diagnostics.is_some() {
11534 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11535 s.select(vec![Selection {
11536 id: selection.id,
11537 start: primary_range.start,
11538 end: primary_range.start,
11539 reversed: false,
11540 goal: SelectionGoal::None,
11541 }]);
11542 });
11543 self.refresh_inline_completion(false, true, window, cx);
11544 }
11545 }
11546 }
11547
11548 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11549 let snapshot = self.snapshot(window, cx);
11550 let selection = self.selections.newest::<Point>(cx);
11551 self.go_to_hunk_after_or_before_position(
11552 &snapshot,
11553 selection.head(),
11554 Direction::Next,
11555 window,
11556 cx,
11557 );
11558 }
11559
11560 fn go_to_hunk_after_or_before_position(
11561 &mut self,
11562 snapshot: &EditorSnapshot,
11563 position: Point,
11564 direction: Direction,
11565 window: &mut Window,
11566 cx: &mut Context<Editor>,
11567 ) {
11568 let row = if direction == Direction::Next {
11569 self.hunk_after_position(snapshot, position)
11570 .map(|hunk| hunk.row_range.start)
11571 } else {
11572 self.hunk_before_position(snapshot, position)
11573 };
11574
11575 if let Some(row) = row {
11576 let destination = Point::new(row.0, 0);
11577 let autoscroll = Autoscroll::center();
11578
11579 self.unfold_ranges(&[destination..destination], false, false, cx);
11580 self.change_selections(Some(autoscroll), window, cx, |s| {
11581 s.select_ranges([destination..destination]);
11582 });
11583 }
11584 }
11585
11586 fn hunk_after_position(
11587 &mut self,
11588 snapshot: &EditorSnapshot,
11589 position: Point,
11590 ) -> Option<MultiBufferDiffHunk> {
11591 snapshot
11592 .buffer_snapshot
11593 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11594 .find(|hunk| hunk.row_range.start.0 > position.row)
11595 .or_else(|| {
11596 snapshot
11597 .buffer_snapshot
11598 .diff_hunks_in_range(Point::zero()..position)
11599 .find(|hunk| hunk.row_range.end.0 < position.row)
11600 })
11601 }
11602
11603 fn go_to_prev_hunk(
11604 &mut self,
11605 _: &GoToPreviousHunk,
11606 window: &mut Window,
11607 cx: &mut Context<Self>,
11608 ) {
11609 let snapshot = self.snapshot(window, cx);
11610 let selection = self.selections.newest::<Point>(cx);
11611 self.go_to_hunk_after_or_before_position(
11612 &snapshot,
11613 selection.head(),
11614 Direction::Prev,
11615 window,
11616 cx,
11617 );
11618 }
11619
11620 fn hunk_before_position(
11621 &mut self,
11622 snapshot: &EditorSnapshot,
11623 position: Point,
11624 ) -> Option<MultiBufferRow> {
11625 snapshot
11626 .buffer_snapshot
11627 .diff_hunk_before(position)
11628 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
11629 }
11630
11631 pub fn go_to_definition(
11632 &mut self,
11633 _: &GoToDefinition,
11634 window: &mut Window,
11635 cx: &mut Context<Self>,
11636 ) -> Task<Result<Navigated>> {
11637 let definition =
11638 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11639 cx.spawn_in(window, |editor, mut cx| async move {
11640 if definition.await? == Navigated::Yes {
11641 return Ok(Navigated::Yes);
11642 }
11643 match editor.update_in(&mut cx, |editor, window, cx| {
11644 editor.find_all_references(&FindAllReferences, window, cx)
11645 })? {
11646 Some(references) => references.await,
11647 None => Ok(Navigated::No),
11648 }
11649 })
11650 }
11651
11652 pub fn go_to_declaration(
11653 &mut self,
11654 _: &GoToDeclaration,
11655 window: &mut Window,
11656 cx: &mut Context<Self>,
11657 ) -> Task<Result<Navigated>> {
11658 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11659 }
11660
11661 pub fn go_to_declaration_split(
11662 &mut self,
11663 _: &GoToDeclaration,
11664 window: &mut Window,
11665 cx: &mut Context<Self>,
11666 ) -> Task<Result<Navigated>> {
11667 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11668 }
11669
11670 pub fn go_to_implementation(
11671 &mut self,
11672 _: &GoToImplementation,
11673 window: &mut Window,
11674 cx: &mut Context<Self>,
11675 ) -> Task<Result<Navigated>> {
11676 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11677 }
11678
11679 pub fn go_to_implementation_split(
11680 &mut self,
11681 _: &GoToImplementationSplit,
11682 window: &mut Window,
11683 cx: &mut Context<Self>,
11684 ) -> Task<Result<Navigated>> {
11685 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11686 }
11687
11688 pub fn go_to_type_definition(
11689 &mut self,
11690 _: &GoToTypeDefinition,
11691 window: &mut Window,
11692 cx: &mut Context<Self>,
11693 ) -> Task<Result<Navigated>> {
11694 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11695 }
11696
11697 pub fn go_to_definition_split(
11698 &mut self,
11699 _: &GoToDefinitionSplit,
11700 window: &mut Window,
11701 cx: &mut Context<Self>,
11702 ) -> Task<Result<Navigated>> {
11703 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11704 }
11705
11706 pub fn go_to_type_definition_split(
11707 &mut self,
11708 _: &GoToTypeDefinitionSplit,
11709 window: &mut Window,
11710 cx: &mut Context<Self>,
11711 ) -> Task<Result<Navigated>> {
11712 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11713 }
11714
11715 fn go_to_definition_of_kind(
11716 &mut self,
11717 kind: GotoDefinitionKind,
11718 split: bool,
11719 window: &mut Window,
11720 cx: &mut Context<Self>,
11721 ) -> Task<Result<Navigated>> {
11722 let Some(provider) = self.semantics_provider.clone() else {
11723 return Task::ready(Ok(Navigated::No));
11724 };
11725 let head = self.selections.newest::<usize>(cx).head();
11726 let buffer = self.buffer.read(cx);
11727 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11728 text_anchor
11729 } else {
11730 return Task::ready(Ok(Navigated::No));
11731 };
11732
11733 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11734 return Task::ready(Ok(Navigated::No));
11735 };
11736
11737 cx.spawn_in(window, |editor, mut cx| async move {
11738 let definitions = definitions.await?;
11739 let navigated = editor
11740 .update_in(&mut cx, |editor, window, cx| {
11741 editor.navigate_to_hover_links(
11742 Some(kind),
11743 definitions
11744 .into_iter()
11745 .filter(|location| {
11746 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11747 })
11748 .map(HoverLink::Text)
11749 .collect::<Vec<_>>(),
11750 split,
11751 window,
11752 cx,
11753 )
11754 })?
11755 .await?;
11756 anyhow::Ok(navigated)
11757 })
11758 }
11759
11760 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11761 let selection = self.selections.newest_anchor();
11762 let head = selection.head();
11763 let tail = selection.tail();
11764
11765 let Some((buffer, start_position)) =
11766 self.buffer.read(cx).text_anchor_for_position(head, cx)
11767 else {
11768 return;
11769 };
11770
11771 let end_position = if head != tail {
11772 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11773 return;
11774 };
11775 Some(pos)
11776 } else {
11777 None
11778 };
11779
11780 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11781 let url = if let Some(end_pos) = end_position {
11782 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11783 } else {
11784 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11785 };
11786
11787 if let Some(url) = url {
11788 editor.update(&mut cx, |_, cx| {
11789 cx.open_url(&url);
11790 })
11791 } else {
11792 Ok(())
11793 }
11794 });
11795
11796 url_finder.detach();
11797 }
11798
11799 pub fn open_selected_filename(
11800 &mut self,
11801 _: &OpenSelectedFilename,
11802 window: &mut Window,
11803 cx: &mut Context<Self>,
11804 ) {
11805 let Some(workspace) = self.workspace() else {
11806 return;
11807 };
11808
11809 let position = self.selections.newest_anchor().head();
11810
11811 let Some((buffer, buffer_position)) =
11812 self.buffer.read(cx).text_anchor_for_position(position, cx)
11813 else {
11814 return;
11815 };
11816
11817 let project = self.project.clone();
11818
11819 cx.spawn_in(window, |_, mut cx| async move {
11820 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
11821
11822 if let Some((_, path)) = result {
11823 workspace
11824 .update_in(&mut cx, |workspace, window, cx| {
11825 workspace.open_resolved_path(path, window, cx)
11826 })?
11827 .await?;
11828 }
11829 anyhow::Ok(())
11830 })
11831 .detach();
11832 }
11833
11834 pub(crate) fn navigate_to_hover_links(
11835 &mut self,
11836 kind: Option<GotoDefinitionKind>,
11837 mut definitions: Vec<HoverLink>,
11838 split: bool,
11839 window: &mut Window,
11840 cx: &mut Context<Editor>,
11841 ) -> Task<Result<Navigated>> {
11842 // If there is one definition, just open it directly
11843 if definitions.len() == 1 {
11844 let definition = definitions.pop().unwrap();
11845
11846 enum TargetTaskResult {
11847 Location(Option<Location>),
11848 AlreadyNavigated,
11849 }
11850
11851 let target_task = match definition {
11852 HoverLink::Text(link) => {
11853 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11854 }
11855 HoverLink::InlayHint(lsp_location, server_id) => {
11856 let computation =
11857 self.compute_target_location(lsp_location, server_id, window, cx);
11858 cx.background_spawn(async move {
11859 let location = computation.await?;
11860 Ok(TargetTaskResult::Location(location))
11861 })
11862 }
11863 HoverLink::Url(url) => {
11864 cx.open_url(&url);
11865 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11866 }
11867 HoverLink::File(path) => {
11868 if let Some(workspace) = self.workspace() {
11869 cx.spawn_in(window, |_, mut cx| async move {
11870 workspace
11871 .update_in(&mut cx, |workspace, window, cx| {
11872 workspace.open_resolved_path(path, window, cx)
11873 })?
11874 .await
11875 .map(|_| TargetTaskResult::AlreadyNavigated)
11876 })
11877 } else {
11878 Task::ready(Ok(TargetTaskResult::Location(None)))
11879 }
11880 }
11881 };
11882 cx.spawn_in(window, |editor, mut cx| async move {
11883 let target = match target_task.await.context("target resolution task")? {
11884 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11885 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11886 TargetTaskResult::Location(Some(target)) => target,
11887 };
11888
11889 editor.update_in(&mut cx, |editor, window, cx| {
11890 let Some(workspace) = editor.workspace() else {
11891 return Navigated::No;
11892 };
11893 let pane = workspace.read(cx).active_pane().clone();
11894
11895 let range = target.range.to_point(target.buffer.read(cx));
11896 let range = editor.range_for_match(&range);
11897 let range = collapse_multiline_range(range);
11898
11899 if !split
11900 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
11901 {
11902 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11903 } else {
11904 window.defer(cx, move |window, cx| {
11905 let target_editor: Entity<Self> =
11906 workspace.update(cx, |workspace, cx| {
11907 let pane = if split {
11908 workspace.adjacent_pane(window, cx)
11909 } else {
11910 workspace.active_pane().clone()
11911 };
11912
11913 workspace.open_project_item(
11914 pane,
11915 target.buffer.clone(),
11916 true,
11917 true,
11918 window,
11919 cx,
11920 )
11921 });
11922 target_editor.update(cx, |target_editor, cx| {
11923 // When selecting a definition in a different buffer, disable the nav history
11924 // to avoid creating a history entry at the previous cursor location.
11925 pane.update(cx, |pane, _| pane.disable_history());
11926 target_editor.go_to_singleton_buffer_range(range, window, cx);
11927 pane.update(cx, |pane, _| pane.enable_history());
11928 });
11929 });
11930 }
11931 Navigated::Yes
11932 })
11933 })
11934 } else if !definitions.is_empty() {
11935 cx.spawn_in(window, |editor, mut cx| async move {
11936 let (title, location_tasks, workspace) = editor
11937 .update_in(&mut cx, |editor, window, cx| {
11938 let tab_kind = match kind {
11939 Some(GotoDefinitionKind::Implementation) => "Implementations",
11940 _ => "Definitions",
11941 };
11942 let title = definitions
11943 .iter()
11944 .find_map(|definition| match definition {
11945 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11946 let buffer = origin.buffer.read(cx);
11947 format!(
11948 "{} for {}",
11949 tab_kind,
11950 buffer
11951 .text_for_range(origin.range.clone())
11952 .collect::<String>()
11953 )
11954 }),
11955 HoverLink::InlayHint(_, _) => None,
11956 HoverLink::Url(_) => None,
11957 HoverLink::File(_) => None,
11958 })
11959 .unwrap_or(tab_kind.to_string());
11960 let location_tasks = definitions
11961 .into_iter()
11962 .map(|definition| match definition {
11963 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11964 HoverLink::InlayHint(lsp_location, server_id) => editor
11965 .compute_target_location(lsp_location, server_id, window, cx),
11966 HoverLink::Url(_) => Task::ready(Ok(None)),
11967 HoverLink::File(_) => Task::ready(Ok(None)),
11968 })
11969 .collect::<Vec<_>>();
11970 (title, location_tasks, editor.workspace().clone())
11971 })
11972 .context("location tasks preparation")?;
11973
11974 let locations = future::join_all(location_tasks)
11975 .await
11976 .into_iter()
11977 .filter_map(|location| location.transpose())
11978 .collect::<Result<_>>()
11979 .context("location tasks")?;
11980
11981 let Some(workspace) = workspace else {
11982 return Ok(Navigated::No);
11983 };
11984 let opened = workspace
11985 .update_in(&mut cx, |workspace, window, cx| {
11986 Self::open_locations_in_multibuffer(
11987 workspace,
11988 locations,
11989 title,
11990 split,
11991 MultibufferSelectionMode::First,
11992 window,
11993 cx,
11994 )
11995 })
11996 .ok();
11997
11998 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11999 })
12000 } else {
12001 Task::ready(Ok(Navigated::No))
12002 }
12003 }
12004
12005 fn compute_target_location(
12006 &self,
12007 lsp_location: lsp::Location,
12008 server_id: LanguageServerId,
12009 window: &mut Window,
12010 cx: &mut Context<Self>,
12011 ) -> Task<anyhow::Result<Option<Location>>> {
12012 let Some(project) = self.project.clone() else {
12013 return Task::ready(Ok(None));
12014 };
12015
12016 cx.spawn_in(window, move |editor, mut cx| async move {
12017 let location_task = editor.update(&mut cx, |_, cx| {
12018 project.update(cx, |project, cx| {
12019 let language_server_name = project
12020 .language_server_statuses(cx)
12021 .find(|(id, _)| server_id == *id)
12022 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12023 language_server_name.map(|language_server_name| {
12024 project.open_local_buffer_via_lsp(
12025 lsp_location.uri.clone(),
12026 server_id,
12027 language_server_name,
12028 cx,
12029 )
12030 })
12031 })
12032 })?;
12033 let location = match location_task {
12034 Some(task) => Some({
12035 let target_buffer_handle = task.await.context("open local buffer")?;
12036 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12037 let target_start = target_buffer
12038 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12039 let target_end = target_buffer
12040 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12041 target_buffer.anchor_after(target_start)
12042 ..target_buffer.anchor_before(target_end)
12043 })?;
12044 Location {
12045 buffer: target_buffer_handle,
12046 range,
12047 }
12048 }),
12049 None => None,
12050 };
12051 Ok(location)
12052 })
12053 }
12054
12055 pub fn find_all_references(
12056 &mut self,
12057 _: &FindAllReferences,
12058 window: &mut Window,
12059 cx: &mut Context<Self>,
12060 ) -> Option<Task<Result<Navigated>>> {
12061 let selection = self.selections.newest::<usize>(cx);
12062 let multi_buffer = self.buffer.read(cx);
12063 let head = selection.head();
12064
12065 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12066 let head_anchor = multi_buffer_snapshot.anchor_at(
12067 head,
12068 if head < selection.tail() {
12069 Bias::Right
12070 } else {
12071 Bias::Left
12072 },
12073 );
12074
12075 match self
12076 .find_all_references_task_sources
12077 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12078 {
12079 Ok(_) => {
12080 log::info!(
12081 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12082 );
12083 return None;
12084 }
12085 Err(i) => {
12086 self.find_all_references_task_sources.insert(i, head_anchor);
12087 }
12088 }
12089
12090 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12091 let workspace = self.workspace()?;
12092 let project = workspace.read(cx).project().clone();
12093 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12094 Some(cx.spawn_in(window, |editor, mut cx| async move {
12095 let _cleanup = defer({
12096 let mut cx = cx.clone();
12097 move || {
12098 let _ = editor.update(&mut cx, |editor, _| {
12099 if let Ok(i) =
12100 editor
12101 .find_all_references_task_sources
12102 .binary_search_by(|anchor| {
12103 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12104 })
12105 {
12106 editor.find_all_references_task_sources.remove(i);
12107 }
12108 });
12109 }
12110 });
12111
12112 let locations = references.await?;
12113 if locations.is_empty() {
12114 return anyhow::Ok(Navigated::No);
12115 }
12116
12117 workspace.update_in(&mut cx, |workspace, window, cx| {
12118 let title = locations
12119 .first()
12120 .as_ref()
12121 .map(|location| {
12122 let buffer = location.buffer.read(cx);
12123 format!(
12124 "References to `{}`",
12125 buffer
12126 .text_for_range(location.range.clone())
12127 .collect::<String>()
12128 )
12129 })
12130 .unwrap();
12131 Self::open_locations_in_multibuffer(
12132 workspace,
12133 locations,
12134 title,
12135 false,
12136 MultibufferSelectionMode::First,
12137 window,
12138 cx,
12139 );
12140 Navigated::Yes
12141 })
12142 }))
12143 }
12144
12145 /// Opens a multibuffer with the given project locations in it
12146 pub fn open_locations_in_multibuffer(
12147 workspace: &mut Workspace,
12148 mut locations: Vec<Location>,
12149 title: String,
12150 split: bool,
12151 multibuffer_selection_mode: MultibufferSelectionMode,
12152 window: &mut Window,
12153 cx: &mut Context<Workspace>,
12154 ) {
12155 // If there are multiple definitions, open them in a multibuffer
12156 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12157 let mut locations = locations.into_iter().peekable();
12158 let mut ranges = Vec::new();
12159 let capability = workspace.project().read(cx).capability();
12160
12161 let excerpt_buffer = cx.new(|cx| {
12162 let mut multibuffer = MultiBuffer::new(capability);
12163 while let Some(location) = locations.next() {
12164 let buffer = location.buffer.read(cx);
12165 let mut ranges_for_buffer = Vec::new();
12166 let range = location.range.to_offset(buffer);
12167 ranges_for_buffer.push(range.clone());
12168
12169 while let Some(next_location) = locations.peek() {
12170 if next_location.buffer == location.buffer {
12171 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12172 locations.next();
12173 } else {
12174 break;
12175 }
12176 }
12177
12178 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12179 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12180 location.buffer.clone(),
12181 ranges_for_buffer,
12182 DEFAULT_MULTIBUFFER_CONTEXT,
12183 cx,
12184 ))
12185 }
12186
12187 multibuffer.with_title(title)
12188 });
12189
12190 let editor = cx.new(|cx| {
12191 Editor::for_multibuffer(
12192 excerpt_buffer,
12193 Some(workspace.project().clone()),
12194 true,
12195 window,
12196 cx,
12197 )
12198 });
12199 editor.update(cx, |editor, cx| {
12200 match multibuffer_selection_mode {
12201 MultibufferSelectionMode::First => {
12202 if let Some(first_range) = ranges.first() {
12203 editor.change_selections(None, window, cx, |selections| {
12204 selections.clear_disjoint();
12205 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12206 });
12207 }
12208 editor.highlight_background::<Self>(
12209 &ranges,
12210 |theme| theme.editor_highlighted_line_background,
12211 cx,
12212 );
12213 }
12214 MultibufferSelectionMode::All => {
12215 editor.change_selections(None, window, cx, |selections| {
12216 selections.clear_disjoint();
12217 selections.select_anchor_ranges(ranges);
12218 });
12219 }
12220 }
12221 editor.register_buffers_with_language_servers(cx);
12222 });
12223
12224 let item = Box::new(editor);
12225 let item_id = item.item_id();
12226
12227 if split {
12228 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12229 } else {
12230 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12231 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12232 pane.close_current_preview_item(window, cx)
12233 } else {
12234 None
12235 }
12236 });
12237 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12238 }
12239 workspace.active_pane().update(cx, |pane, cx| {
12240 pane.set_preview_item_id(Some(item_id), cx);
12241 });
12242 }
12243
12244 pub fn rename(
12245 &mut self,
12246 _: &Rename,
12247 window: &mut Window,
12248 cx: &mut Context<Self>,
12249 ) -> Option<Task<Result<()>>> {
12250 use language::ToOffset as _;
12251
12252 let provider = self.semantics_provider.clone()?;
12253 let selection = self.selections.newest_anchor().clone();
12254 let (cursor_buffer, cursor_buffer_position) = self
12255 .buffer
12256 .read(cx)
12257 .text_anchor_for_position(selection.head(), cx)?;
12258 let (tail_buffer, cursor_buffer_position_end) = self
12259 .buffer
12260 .read(cx)
12261 .text_anchor_for_position(selection.tail(), cx)?;
12262 if tail_buffer != cursor_buffer {
12263 return None;
12264 }
12265
12266 let snapshot = cursor_buffer.read(cx).snapshot();
12267 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12268 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12269 let prepare_rename = provider
12270 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12271 .unwrap_or_else(|| Task::ready(Ok(None)));
12272 drop(snapshot);
12273
12274 Some(cx.spawn_in(window, |this, mut cx| async move {
12275 let rename_range = if let Some(range) = prepare_rename.await? {
12276 Some(range)
12277 } else {
12278 this.update(&mut cx, |this, cx| {
12279 let buffer = this.buffer.read(cx).snapshot(cx);
12280 let mut buffer_highlights = this
12281 .document_highlights_for_position(selection.head(), &buffer)
12282 .filter(|highlight| {
12283 highlight.start.excerpt_id == selection.head().excerpt_id
12284 && highlight.end.excerpt_id == selection.head().excerpt_id
12285 });
12286 buffer_highlights
12287 .next()
12288 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12289 })?
12290 };
12291 if let Some(rename_range) = rename_range {
12292 this.update_in(&mut cx, |this, window, cx| {
12293 let snapshot = cursor_buffer.read(cx).snapshot();
12294 let rename_buffer_range = rename_range.to_offset(&snapshot);
12295 let cursor_offset_in_rename_range =
12296 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12297 let cursor_offset_in_rename_range_end =
12298 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12299
12300 this.take_rename(false, window, cx);
12301 let buffer = this.buffer.read(cx).read(cx);
12302 let cursor_offset = selection.head().to_offset(&buffer);
12303 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12304 let rename_end = rename_start + rename_buffer_range.len();
12305 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12306 let mut old_highlight_id = None;
12307 let old_name: Arc<str> = buffer
12308 .chunks(rename_start..rename_end, true)
12309 .map(|chunk| {
12310 if old_highlight_id.is_none() {
12311 old_highlight_id = chunk.syntax_highlight_id;
12312 }
12313 chunk.text
12314 })
12315 .collect::<String>()
12316 .into();
12317
12318 drop(buffer);
12319
12320 // Position the selection in the rename editor so that it matches the current selection.
12321 this.show_local_selections = false;
12322 let rename_editor = cx.new(|cx| {
12323 let mut editor = Editor::single_line(window, cx);
12324 editor.buffer.update(cx, |buffer, cx| {
12325 buffer.edit([(0..0, old_name.clone())], None, cx)
12326 });
12327 let rename_selection_range = match cursor_offset_in_rename_range
12328 .cmp(&cursor_offset_in_rename_range_end)
12329 {
12330 Ordering::Equal => {
12331 editor.select_all(&SelectAll, window, cx);
12332 return editor;
12333 }
12334 Ordering::Less => {
12335 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12336 }
12337 Ordering::Greater => {
12338 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12339 }
12340 };
12341 if rename_selection_range.end > old_name.len() {
12342 editor.select_all(&SelectAll, window, cx);
12343 } else {
12344 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12345 s.select_ranges([rename_selection_range]);
12346 });
12347 }
12348 editor
12349 });
12350 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12351 if e == &EditorEvent::Focused {
12352 cx.emit(EditorEvent::FocusedIn)
12353 }
12354 })
12355 .detach();
12356
12357 let write_highlights =
12358 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12359 let read_highlights =
12360 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12361 let ranges = write_highlights
12362 .iter()
12363 .flat_map(|(_, ranges)| ranges.iter())
12364 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12365 .cloned()
12366 .collect();
12367
12368 this.highlight_text::<Rename>(
12369 ranges,
12370 HighlightStyle {
12371 fade_out: Some(0.6),
12372 ..Default::default()
12373 },
12374 cx,
12375 );
12376 let rename_focus_handle = rename_editor.focus_handle(cx);
12377 window.focus(&rename_focus_handle);
12378 let block_id = this.insert_blocks(
12379 [BlockProperties {
12380 style: BlockStyle::Flex,
12381 placement: BlockPlacement::Below(range.start),
12382 height: 1,
12383 render: Arc::new({
12384 let rename_editor = rename_editor.clone();
12385 move |cx: &mut BlockContext| {
12386 let mut text_style = cx.editor_style.text.clone();
12387 if let Some(highlight_style) = old_highlight_id
12388 .and_then(|h| h.style(&cx.editor_style.syntax))
12389 {
12390 text_style = text_style.highlight(highlight_style);
12391 }
12392 div()
12393 .block_mouse_down()
12394 .pl(cx.anchor_x)
12395 .child(EditorElement::new(
12396 &rename_editor,
12397 EditorStyle {
12398 background: cx.theme().system().transparent,
12399 local_player: cx.editor_style.local_player,
12400 text: text_style,
12401 scrollbar_width: cx.editor_style.scrollbar_width,
12402 syntax: cx.editor_style.syntax.clone(),
12403 status: cx.editor_style.status.clone(),
12404 inlay_hints_style: HighlightStyle {
12405 font_weight: Some(FontWeight::BOLD),
12406 ..make_inlay_hints_style(cx.app)
12407 },
12408 inline_completion_styles: make_suggestion_styles(
12409 cx.app,
12410 ),
12411 ..EditorStyle::default()
12412 },
12413 ))
12414 .into_any_element()
12415 }
12416 }),
12417 priority: 0,
12418 }],
12419 Some(Autoscroll::fit()),
12420 cx,
12421 )[0];
12422 this.pending_rename = Some(RenameState {
12423 range,
12424 old_name,
12425 editor: rename_editor,
12426 block_id,
12427 });
12428 })?;
12429 }
12430
12431 Ok(())
12432 }))
12433 }
12434
12435 pub fn confirm_rename(
12436 &mut self,
12437 _: &ConfirmRename,
12438 window: &mut Window,
12439 cx: &mut Context<Self>,
12440 ) -> Option<Task<Result<()>>> {
12441 let rename = self.take_rename(false, window, cx)?;
12442 let workspace = self.workspace()?.downgrade();
12443 let (buffer, start) = self
12444 .buffer
12445 .read(cx)
12446 .text_anchor_for_position(rename.range.start, cx)?;
12447 let (end_buffer, _) = self
12448 .buffer
12449 .read(cx)
12450 .text_anchor_for_position(rename.range.end, cx)?;
12451 if buffer != end_buffer {
12452 return None;
12453 }
12454
12455 let old_name = rename.old_name;
12456 let new_name = rename.editor.read(cx).text(cx);
12457
12458 let rename = self.semantics_provider.as_ref()?.perform_rename(
12459 &buffer,
12460 start,
12461 new_name.clone(),
12462 cx,
12463 )?;
12464
12465 Some(cx.spawn_in(window, |editor, mut cx| async move {
12466 let project_transaction = rename.await?;
12467 Self::open_project_transaction(
12468 &editor,
12469 workspace,
12470 project_transaction,
12471 format!("Rename: {} → {}", old_name, new_name),
12472 cx.clone(),
12473 )
12474 .await?;
12475
12476 editor.update(&mut cx, |editor, cx| {
12477 editor.refresh_document_highlights(cx);
12478 })?;
12479 Ok(())
12480 }))
12481 }
12482
12483 fn take_rename(
12484 &mut self,
12485 moving_cursor: bool,
12486 window: &mut Window,
12487 cx: &mut Context<Self>,
12488 ) -> Option<RenameState> {
12489 let rename = self.pending_rename.take()?;
12490 if rename.editor.focus_handle(cx).is_focused(window) {
12491 window.focus(&self.focus_handle);
12492 }
12493
12494 self.remove_blocks(
12495 [rename.block_id].into_iter().collect(),
12496 Some(Autoscroll::fit()),
12497 cx,
12498 );
12499 self.clear_highlights::<Rename>(cx);
12500 self.show_local_selections = true;
12501
12502 if moving_cursor {
12503 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12504 editor.selections.newest::<usize>(cx).head()
12505 });
12506
12507 // Update the selection to match the position of the selection inside
12508 // the rename editor.
12509 let snapshot = self.buffer.read(cx).read(cx);
12510 let rename_range = rename.range.to_offset(&snapshot);
12511 let cursor_in_editor = snapshot
12512 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12513 .min(rename_range.end);
12514 drop(snapshot);
12515
12516 self.change_selections(None, window, cx, |s| {
12517 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12518 });
12519 } else {
12520 self.refresh_document_highlights(cx);
12521 }
12522
12523 Some(rename)
12524 }
12525
12526 pub fn pending_rename(&self) -> Option<&RenameState> {
12527 self.pending_rename.as_ref()
12528 }
12529
12530 fn format(
12531 &mut self,
12532 _: &Format,
12533 window: &mut Window,
12534 cx: &mut Context<Self>,
12535 ) -> Option<Task<Result<()>>> {
12536 let project = match &self.project {
12537 Some(project) => project.clone(),
12538 None => return None,
12539 };
12540
12541 Some(self.perform_format(
12542 project,
12543 FormatTrigger::Manual,
12544 FormatTarget::Buffers,
12545 window,
12546 cx,
12547 ))
12548 }
12549
12550 fn format_selections(
12551 &mut self,
12552 _: &FormatSelections,
12553 window: &mut Window,
12554 cx: &mut Context<Self>,
12555 ) -> Option<Task<Result<()>>> {
12556 let project = match &self.project {
12557 Some(project) => project.clone(),
12558 None => return None,
12559 };
12560
12561 let ranges = self
12562 .selections
12563 .all_adjusted(cx)
12564 .into_iter()
12565 .map(|selection| selection.range())
12566 .collect_vec();
12567
12568 Some(self.perform_format(
12569 project,
12570 FormatTrigger::Manual,
12571 FormatTarget::Ranges(ranges),
12572 window,
12573 cx,
12574 ))
12575 }
12576
12577 fn perform_format(
12578 &mut self,
12579 project: Entity<Project>,
12580 trigger: FormatTrigger,
12581 target: FormatTarget,
12582 window: &mut Window,
12583 cx: &mut Context<Self>,
12584 ) -> Task<Result<()>> {
12585 let buffer = self.buffer.clone();
12586 let (buffers, target) = match target {
12587 FormatTarget::Buffers => {
12588 let mut buffers = buffer.read(cx).all_buffers();
12589 if trigger == FormatTrigger::Save {
12590 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12591 }
12592 (buffers, LspFormatTarget::Buffers)
12593 }
12594 FormatTarget::Ranges(selection_ranges) => {
12595 let multi_buffer = buffer.read(cx);
12596 let snapshot = multi_buffer.read(cx);
12597 let mut buffers = HashSet::default();
12598 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12599 BTreeMap::new();
12600 for selection_range in selection_ranges {
12601 for (buffer, buffer_range, _) in
12602 snapshot.range_to_buffer_ranges(selection_range)
12603 {
12604 let buffer_id = buffer.remote_id();
12605 let start = buffer.anchor_before(buffer_range.start);
12606 let end = buffer.anchor_after(buffer_range.end);
12607 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12608 buffer_id_to_ranges
12609 .entry(buffer_id)
12610 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12611 .or_insert_with(|| vec![start..end]);
12612 }
12613 }
12614 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12615 }
12616 };
12617
12618 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12619 let format = project.update(cx, |project, cx| {
12620 project.format(buffers, target, true, trigger, cx)
12621 });
12622
12623 cx.spawn_in(window, |_, mut cx| async move {
12624 let transaction = futures::select_biased! {
12625 () = timeout => {
12626 log::warn!("timed out waiting for formatting");
12627 None
12628 }
12629 transaction = format.log_err().fuse() => transaction,
12630 };
12631
12632 buffer
12633 .update(&mut cx, |buffer, cx| {
12634 if let Some(transaction) = transaction {
12635 if !buffer.is_singleton() {
12636 buffer.push_transaction(&transaction.0, cx);
12637 }
12638 }
12639 cx.notify();
12640 })
12641 .ok();
12642
12643 Ok(())
12644 })
12645 }
12646
12647 fn organize_imports(
12648 &mut self,
12649 _: &OrganizeImports,
12650 window: &mut Window,
12651 cx: &mut Context<Self>,
12652 ) -> Option<Task<Result<()>>> {
12653 let project = match &self.project {
12654 Some(project) => project.clone(),
12655 None => return None,
12656 };
12657 Some(self.perform_code_action_kind(
12658 project,
12659 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
12660 window,
12661 cx,
12662 ))
12663 }
12664
12665 fn perform_code_action_kind(
12666 &mut self,
12667 project: Entity<Project>,
12668 kind: CodeActionKind,
12669 window: &mut Window,
12670 cx: &mut Context<Self>,
12671 ) -> Task<Result<()>> {
12672 let buffer = self.buffer.clone();
12673 let buffers = buffer.read(cx).all_buffers();
12674 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
12675 let apply_action = project.update(cx, |project, cx| {
12676 project.apply_code_action_kind(buffers, kind, true, cx)
12677 });
12678 cx.spawn_in(window, |_, mut cx| async move {
12679 let transaction = futures::select_biased! {
12680 () = timeout => {
12681 log::warn!("timed out waiting for executing code action");
12682 None
12683 }
12684 transaction = apply_action.log_err().fuse() => transaction,
12685 };
12686 buffer
12687 .update(&mut cx, |buffer, cx| {
12688 // check if we need this
12689 if let Some(transaction) = transaction {
12690 if !buffer.is_singleton() {
12691 buffer.push_transaction(&transaction.0, cx);
12692 }
12693 }
12694 cx.notify();
12695 })
12696 .ok();
12697 Ok(())
12698 })
12699 }
12700
12701 fn restart_language_server(
12702 &mut self,
12703 _: &RestartLanguageServer,
12704 _: &mut Window,
12705 cx: &mut Context<Self>,
12706 ) {
12707 if let Some(project) = self.project.clone() {
12708 self.buffer.update(cx, |multi_buffer, cx| {
12709 project.update(cx, |project, cx| {
12710 project.restart_language_servers_for_buffers(
12711 multi_buffer.all_buffers().into_iter().collect(),
12712 cx,
12713 );
12714 });
12715 })
12716 }
12717 }
12718
12719 fn cancel_language_server_work(
12720 workspace: &mut Workspace,
12721 _: &actions::CancelLanguageServerWork,
12722 _: &mut Window,
12723 cx: &mut Context<Workspace>,
12724 ) {
12725 let project = workspace.project();
12726 let buffers = workspace
12727 .active_item(cx)
12728 .and_then(|item| item.act_as::<Editor>(cx))
12729 .map_or(HashSet::default(), |editor| {
12730 editor.read(cx).buffer.read(cx).all_buffers()
12731 });
12732 project.update(cx, |project, cx| {
12733 project.cancel_language_server_work_for_buffers(buffers, cx);
12734 });
12735 }
12736
12737 fn show_character_palette(
12738 &mut self,
12739 _: &ShowCharacterPalette,
12740 window: &mut Window,
12741 _: &mut Context<Self>,
12742 ) {
12743 window.show_character_palette();
12744 }
12745
12746 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12747 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12748 let buffer = self.buffer.read(cx).snapshot(cx);
12749 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12750 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12751 let is_valid = buffer
12752 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12753 .any(|entry| {
12754 entry.diagnostic.is_primary
12755 && !entry.range.is_empty()
12756 && entry.range.start == primary_range_start
12757 && entry.diagnostic.message == active_diagnostics.primary_message
12758 });
12759
12760 if is_valid != active_diagnostics.is_valid {
12761 active_diagnostics.is_valid = is_valid;
12762 if is_valid {
12763 let mut new_styles = HashMap::default();
12764 for (block_id, diagnostic) in &active_diagnostics.blocks {
12765 new_styles.insert(
12766 *block_id,
12767 diagnostic_block_renderer(diagnostic.clone(), None, true),
12768 );
12769 }
12770 self.display_map.update(cx, |display_map, _cx| {
12771 display_map.replace_blocks(new_styles);
12772 });
12773 } else {
12774 self.dismiss_diagnostics(cx);
12775 }
12776 }
12777 }
12778 }
12779
12780 fn activate_diagnostics(
12781 &mut self,
12782 buffer_id: BufferId,
12783 group_id: usize,
12784 window: &mut Window,
12785 cx: &mut Context<Self>,
12786 ) {
12787 self.dismiss_diagnostics(cx);
12788 let snapshot = self.snapshot(window, cx);
12789 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
12790 let buffer = self.buffer.read(cx).snapshot(cx);
12791
12792 let mut primary_range = None;
12793 let mut primary_message = None;
12794 let diagnostic_group = buffer
12795 .diagnostic_group(buffer_id, group_id)
12796 .filter_map(|entry| {
12797 let start = entry.range.start;
12798 let end = entry.range.end;
12799 if snapshot.is_line_folded(MultiBufferRow(start.row))
12800 && (start.row == end.row
12801 || snapshot.is_line_folded(MultiBufferRow(end.row)))
12802 {
12803 return None;
12804 }
12805 if entry.diagnostic.is_primary {
12806 primary_range = Some(entry.range.clone());
12807 primary_message = Some(entry.diagnostic.message.clone());
12808 }
12809 Some(entry)
12810 })
12811 .collect::<Vec<_>>();
12812 let primary_range = primary_range?;
12813 let primary_message = primary_message?;
12814
12815 let blocks = display_map
12816 .insert_blocks(
12817 diagnostic_group.iter().map(|entry| {
12818 let diagnostic = entry.diagnostic.clone();
12819 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
12820 BlockProperties {
12821 style: BlockStyle::Fixed,
12822 placement: BlockPlacement::Below(
12823 buffer.anchor_after(entry.range.start),
12824 ),
12825 height: message_height,
12826 render: diagnostic_block_renderer(diagnostic, None, true),
12827 priority: 0,
12828 }
12829 }),
12830 cx,
12831 )
12832 .into_iter()
12833 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
12834 .collect();
12835
12836 Some(ActiveDiagnosticGroup {
12837 primary_range: buffer.anchor_before(primary_range.start)
12838 ..buffer.anchor_after(primary_range.end),
12839 primary_message,
12840 group_id,
12841 blocks,
12842 is_valid: true,
12843 })
12844 });
12845 }
12846
12847 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
12848 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
12849 self.display_map.update(cx, |display_map, cx| {
12850 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
12851 });
12852 cx.notify();
12853 }
12854 }
12855
12856 /// Disable inline diagnostics rendering for this editor.
12857 pub fn disable_inline_diagnostics(&mut self) {
12858 self.inline_diagnostics_enabled = false;
12859 self.inline_diagnostics_update = Task::ready(());
12860 self.inline_diagnostics.clear();
12861 }
12862
12863 pub fn inline_diagnostics_enabled(&self) -> bool {
12864 self.inline_diagnostics_enabled
12865 }
12866
12867 pub fn show_inline_diagnostics(&self) -> bool {
12868 self.show_inline_diagnostics
12869 }
12870
12871 pub fn toggle_inline_diagnostics(
12872 &mut self,
12873 _: &ToggleInlineDiagnostics,
12874 window: &mut Window,
12875 cx: &mut Context<'_, Editor>,
12876 ) {
12877 self.show_inline_diagnostics = !self.show_inline_diagnostics;
12878 self.refresh_inline_diagnostics(false, window, cx);
12879 }
12880
12881 fn refresh_inline_diagnostics(
12882 &mut self,
12883 debounce: bool,
12884 window: &mut Window,
12885 cx: &mut Context<Self>,
12886 ) {
12887 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12888 self.inline_diagnostics_update = Task::ready(());
12889 self.inline_diagnostics.clear();
12890 return;
12891 }
12892
12893 let debounce_ms = ProjectSettings::get_global(cx)
12894 .diagnostics
12895 .inline
12896 .update_debounce_ms;
12897 let debounce = if debounce && debounce_ms > 0 {
12898 Some(Duration::from_millis(debounce_ms))
12899 } else {
12900 None
12901 };
12902 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12903 if let Some(debounce) = debounce {
12904 cx.background_executor().timer(debounce).await;
12905 }
12906 let Some(snapshot) = editor
12907 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12908 .ok()
12909 else {
12910 return;
12911 };
12912
12913 let new_inline_diagnostics = cx
12914 .background_spawn(async move {
12915 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12916 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12917 let message = diagnostic_entry
12918 .diagnostic
12919 .message
12920 .split_once('\n')
12921 .map(|(line, _)| line)
12922 .map(SharedString::new)
12923 .unwrap_or_else(|| {
12924 SharedString::from(diagnostic_entry.diagnostic.message)
12925 });
12926 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12927 let (Ok(i) | Err(i)) = inline_diagnostics
12928 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12929 inline_diagnostics.insert(
12930 i,
12931 (
12932 start_anchor,
12933 InlineDiagnostic {
12934 message,
12935 group_id: diagnostic_entry.diagnostic.group_id,
12936 start: diagnostic_entry.range.start.to_point(&snapshot),
12937 is_primary: diagnostic_entry.diagnostic.is_primary,
12938 severity: diagnostic_entry.diagnostic.severity,
12939 },
12940 ),
12941 );
12942 }
12943 inline_diagnostics
12944 })
12945 .await;
12946
12947 editor
12948 .update(&mut cx, |editor, cx| {
12949 editor.inline_diagnostics = new_inline_diagnostics;
12950 cx.notify();
12951 })
12952 .ok();
12953 });
12954 }
12955
12956 pub fn set_selections_from_remote(
12957 &mut self,
12958 selections: Vec<Selection<Anchor>>,
12959 pending_selection: Option<Selection<Anchor>>,
12960 window: &mut Window,
12961 cx: &mut Context<Self>,
12962 ) {
12963 let old_cursor_position = self.selections.newest_anchor().head();
12964 self.selections.change_with(cx, |s| {
12965 s.select_anchors(selections);
12966 if let Some(pending_selection) = pending_selection {
12967 s.set_pending(pending_selection, SelectMode::Character);
12968 } else {
12969 s.clear_pending();
12970 }
12971 });
12972 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12973 }
12974
12975 fn push_to_selection_history(&mut self) {
12976 self.selection_history.push(SelectionHistoryEntry {
12977 selections: self.selections.disjoint_anchors(),
12978 select_next_state: self.select_next_state.clone(),
12979 select_prev_state: self.select_prev_state.clone(),
12980 add_selections_state: self.add_selections_state.clone(),
12981 });
12982 }
12983
12984 pub fn transact(
12985 &mut self,
12986 window: &mut Window,
12987 cx: &mut Context<Self>,
12988 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12989 ) -> Option<TransactionId> {
12990 self.start_transaction_at(Instant::now(), window, cx);
12991 update(self, window, cx);
12992 self.end_transaction_at(Instant::now(), cx)
12993 }
12994
12995 pub fn start_transaction_at(
12996 &mut self,
12997 now: Instant,
12998 window: &mut Window,
12999 cx: &mut Context<Self>,
13000 ) {
13001 self.end_selection(window, cx);
13002 if let Some(tx_id) = self
13003 .buffer
13004 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13005 {
13006 self.selection_history
13007 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13008 cx.emit(EditorEvent::TransactionBegun {
13009 transaction_id: tx_id,
13010 })
13011 }
13012 }
13013
13014 pub fn end_transaction_at(
13015 &mut self,
13016 now: Instant,
13017 cx: &mut Context<Self>,
13018 ) -> Option<TransactionId> {
13019 if let Some(transaction_id) = self
13020 .buffer
13021 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13022 {
13023 if let Some((_, end_selections)) =
13024 self.selection_history.transaction_mut(transaction_id)
13025 {
13026 *end_selections = Some(self.selections.disjoint_anchors());
13027 } else {
13028 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13029 }
13030
13031 cx.emit(EditorEvent::Edited { transaction_id });
13032 Some(transaction_id)
13033 } else {
13034 None
13035 }
13036 }
13037
13038 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13039 if self.selection_mark_mode {
13040 self.change_selections(None, window, cx, |s| {
13041 s.move_with(|_, sel| {
13042 sel.collapse_to(sel.head(), SelectionGoal::None);
13043 });
13044 })
13045 }
13046 self.selection_mark_mode = true;
13047 cx.notify();
13048 }
13049
13050 pub fn swap_selection_ends(
13051 &mut self,
13052 _: &actions::SwapSelectionEnds,
13053 window: &mut Window,
13054 cx: &mut Context<Self>,
13055 ) {
13056 self.change_selections(None, window, cx, |s| {
13057 s.move_with(|_, sel| {
13058 if sel.start != sel.end {
13059 sel.reversed = !sel.reversed
13060 }
13061 });
13062 });
13063 self.request_autoscroll(Autoscroll::newest(), cx);
13064 cx.notify();
13065 }
13066
13067 pub fn toggle_fold(
13068 &mut self,
13069 _: &actions::ToggleFold,
13070 window: &mut Window,
13071 cx: &mut Context<Self>,
13072 ) {
13073 if self.is_singleton(cx) {
13074 let selection = self.selections.newest::<Point>(cx);
13075
13076 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13077 let range = if selection.is_empty() {
13078 let point = selection.head().to_display_point(&display_map);
13079 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13080 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13081 .to_point(&display_map);
13082 start..end
13083 } else {
13084 selection.range()
13085 };
13086 if display_map.folds_in_range(range).next().is_some() {
13087 self.unfold_lines(&Default::default(), window, cx)
13088 } else {
13089 self.fold(&Default::default(), window, cx)
13090 }
13091 } else {
13092 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13093 let buffer_ids: HashSet<_> = self
13094 .selections
13095 .disjoint_anchor_ranges()
13096 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13097 .collect();
13098
13099 let should_unfold = buffer_ids
13100 .iter()
13101 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13102
13103 for buffer_id in buffer_ids {
13104 if should_unfold {
13105 self.unfold_buffer(buffer_id, cx);
13106 } else {
13107 self.fold_buffer(buffer_id, cx);
13108 }
13109 }
13110 }
13111 }
13112
13113 pub fn toggle_fold_recursive(
13114 &mut self,
13115 _: &actions::ToggleFoldRecursive,
13116 window: &mut Window,
13117 cx: &mut Context<Self>,
13118 ) {
13119 let selection = self.selections.newest::<Point>(cx);
13120
13121 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13122 let range = if selection.is_empty() {
13123 let point = selection.head().to_display_point(&display_map);
13124 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13125 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13126 .to_point(&display_map);
13127 start..end
13128 } else {
13129 selection.range()
13130 };
13131 if display_map.folds_in_range(range).next().is_some() {
13132 self.unfold_recursive(&Default::default(), window, cx)
13133 } else {
13134 self.fold_recursive(&Default::default(), window, cx)
13135 }
13136 }
13137
13138 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13139 if self.is_singleton(cx) {
13140 let mut to_fold = Vec::new();
13141 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13142 let selections = self.selections.all_adjusted(cx);
13143
13144 for selection in selections {
13145 let range = selection.range().sorted();
13146 let buffer_start_row = range.start.row;
13147
13148 if range.start.row != range.end.row {
13149 let mut found = false;
13150 let mut row = range.start.row;
13151 while row <= range.end.row {
13152 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13153 {
13154 found = true;
13155 row = crease.range().end.row + 1;
13156 to_fold.push(crease);
13157 } else {
13158 row += 1
13159 }
13160 }
13161 if found {
13162 continue;
13163 }
13164 }
13165
13166 for row in (0..=range.start.row).rev() {
13167 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13168 if crease.range().end.row >= buffer_start_row {
13169 to_fold.push(crease);
13170 if row <= range.start.row {
13171 break;
13172 }
13173 }
13174 }
13175 }
13176 }
13177
13178 self.fold_creases(to_fold, true, window, cx);
13179 } else {
13180 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13181 let buffer_ids = self
13182 .selections
13183 .disjoint_anchor_ranges()
13184 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13185 .collect::<HashSet<_>>();
13186 for buffer_id in buffer_ids {
13187 self.fold_buffer(buffer_id, cx);
13188 }
13189 }
13190 }
13191
13192 fn fold_at_level(
13193 &mut self,
13194 fold_at: &FoldAtLevel,
13195 window: &mut Window,
13196 cx: &mut Context<Self>,
13197 ) {
13198 if !self.buffer.read(cx).is_singleton() {
13199 return;
13200 }
13201
13202 let fold_at_level = fold_at.0;
13203 let snapshot = self.buffer.read(cx).snapshot(cx);
13204 let mut to_fold = Vec::new();
13205 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13206
13207 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13208 while start_row < end_row {
13209 match self
13210 .snapshot(window, cx)
13211 .crease_for_buffer_row(MultiBufferRow(start_row))
13212 {
13213 Some(crease) => {
13214 let nested_start_row = crease.range().start.row + 1;
13215 let nested_end_row = crease.range().end.row;
13216
13217 if current_level < fold_at_level {
13218 stack.push((nested_start_row, nested_end_row, current_level + 1));
13219 } else if current_level == fold_at_level {
13220 to_fold.push(crease);
13221 }
13222
13223 start_row = nested_end_row + 1;
13224 }
13225 None => start_row += 1,
13226 }
13227 }
13228 }
13229
13230 self.fold_creases(to_fold, true, window, cx);
13231 }
13232
13233 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13234 if self.buffer.read(cx).is_singleton() {
13235 let mut fold_ranges = Vec::new();
13236 let snapshot = self.buffer.read(cx).snapshot(cx);
13237
13238 for row in 0..snapshot.max_row().0 {
13239 if let Some(foldable_range) = self
13240 .snapshot(window, cx)
13241 .crease_for_buffer_row(MultiBufferRow(row))
13242 {
13243 fold_ranges.push(foldable_range);
13244 }
13245 }
13246
13247 self.fold_creases(fold_ranges, true, window, cx);
13248 } else {
13249 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13250 editor
13251 .update_in(&mut cx, |editor, _, cx| {
13252 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13253 editor.fold_buffer(buffer_id, cx);
13254 }
13255 })
13256 .ok();
13257 });
13258 }
13259 }
13260
13261 pub fn fold_function_bodies(
13262 &mut self,
13263 _: &actions::FoldFunctionBodies,
13264 window: &mut Window,
13265 cx: &mut Context<Self>,
13266 ) {
13267 let snapshot = self.buffer.read(cx).snapshot(cx);
13268
13269 let ranges = snapshot
13270 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13271 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13272 .collect::<Vec<_>>();
13273
13274 let creases = ranges
13275 .into_iter()
13276 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13277 .collect();
13278
13279 self.fold_creases(creases, true, window, cx);
13280 }
13281
13282 pub fn fold_recursive(
13283 &mut self,
13284 _: &actions::FoldRecursive,
13285 window: &mut Window,
13286 cx: &mut Context<Self>,
13287 ) {
13288 let mut to_fold = Vec::new();
13289 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13290 let selections = self.selections.all_adjusted(cx);
13291
13292 for selection in selections {
13293 let range = selection.range().sorted();
13294 let buffer_start_row = range.start.row;
13295
13296 if range.start.row != range.end.row {
13297 let mut found = false;
13298 for row in range.start.row..=range.end.row {
13299 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13300 found = true;
13301 to_fold.push(crease);
13302 }
13303 }
13304 if found {
13305 continue;
13306 }
13307 }
13308
13309 for row in (0..=range.start.row).rev() {
13310 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13311 if crease.range().end.row >= buffer_start_row {
13312 to_fold.push(crease);
13313 } else {
13314 break;
13315 }
13316 }
13317 }
13318 }
13319
13320 self.fold_creases(to_fold, true, window, cx);
13321 }
13322
13323 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13324 let buffer_row = fold_at.buffer_row;
13325 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13326
13327 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13328 let autoscroll = self
13329 .selections
13330 .all::<Point>(cx)
13331 .iter()
13332 .any(|selection| crease.range().overlaps(&selection.range()));
13333
13334 self.fold_creases(vec![crease], autoscroll, window, cx);
13335 }
13336 }
13337
13338 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13339 if self.is_singleton(cx) {
13340 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13341 let buffer = &display_map.buffer_snapshot;
13342 let selections = self.selections.all::<Point>(cx);
13343 let ranges = selections
13344 .iter()
13345 .map(|s| {
13346 let range = s.display_range(&display_map).sorted();
13347 let mut start = range.start.to_point(&display_map);
13348 let mut end = range.end.to_point(&display_map);
13349 start.column = 0;
13350 end.column = buffer.line_len(MultiBufferRow(end.row));
13351 start..end
13352 })
13353 .collect::<Vec<_>>();
13354
13355 self.unfold_ranges(&ranges, true, true, cx);
13356 } else {
13357 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13358 let buffer_ids = self
13359 .selections
13360 .disjoint_anchor_ranges()
13361 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13362 .collect::<HashSet<_>>();
13363 for buffer_id in buffer_ids {
13364 self.unfold_buffer(buffer_id, cx);
13365 }
13366 }
13367 }
13368
13369 pub fn unfold_recursive(
13370 &mut self,
13371 _: &UnfoldRecursive,
13372 _window: &mut Window,
13373 cx: &mut Context<Self>,
13374 ) {
13375 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13376 let selections = self.selections.all::<Point>(cx);
13377 let ranges = selections
13378 .iter()
13379 .map(|s| {
13380 let mut range = s.display_range(&display_map).sorted();
13381 *range.start.column_mut() = 0;
13382 *range.end.column_mut() = display_map.line_len(range.end.row());
13383 let start = range.start.to_point(&display_map);
13384 let end = range.end.to_point(&display_map);
13385 start..end
13386 })
13387 .collect::<Vec<_>>();
13388
13389 self.unfold_ranges(&ranges, true, true, cx);
13390 }
13391
13392 pub fn unfold_at(
13393 &mut self,
13394 unfold_at: &UnfoldAt,
13395 _window: &mut Window,
13396 cx: &mut Context<Self>,
13397 ) {
13398 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13399
13400 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13401 ..Point::new(
13402 unfold_at.buffer_row.0,
13403 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13404 );
13405
13406 let autoscroll = self
13407 .selections
13408 .all::<Point>(cx)
13409 .iter()
13410 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13411
13412 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13413 }
13414
13415 pub fn unfold_all(
13416 &mut self,
13417 _: &actions::UnfoldAll,
13418 _window: &mut Window,
13419 cx: &mut Context<Self>,
13420 ) {
13421 if self.buffer.read(cx).is_singleton() {
13422 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13423 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13424 } else {
13425 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13426 editor
13427 .update(&mut cx, |editor, cx| {
13428 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13429 editor.unfold_buffer(buffer_id, cx);
13430 }
13431 })
13432 .ok();
13433 });
13434 }
13435 }
13436
13437 pub fn fold_selected_ranges(
13438 &mut self,
13439 _: &FoldSelectedRanges,
13440 window: &mut Window,
13441 cx: &mut Context<Self>,
13442 ) {
13443 let selections = self.selections.all::<Point>(cx);
13444 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13445 let line_mode = self.selections.line_mode;
13446 let ranges = selections
13447 .into_iter()
13448 .map(|s| {
13449 if line_mode {
13450 let start = Point::new(s.start.row, 0);
13451 let end = Point::new(
13452 s.end.row,
13453 display_map
13454 .buffer_snapshot
13455 .line_len(MultiBufferRow(s.end.row)),
13456 );
13457 Crease::simple(start..end, display_map.fold_placeholder.clone())
13458 } else {
13459 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13460 }
13461 })
13462 .collect::<Vec<_>>();
13463 self.fold_creases(ranges, true, window, cx);
13464 }
13465
13466 pub fn fold_ranges<T: ToOffset + Clone>(
13467 &mut self,
13468 ranges: Vec<Range<T>>,
13469 auto_scroll: bool,
13470 window: &mut Window,
13471 cx: &mut Context<Self>,
13472 ) {
13473 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13474 let ranges = ranges
13475 .into_iter()
13476 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13477 .collect::<Vec<_>>();
13478 self.fold_creases(ranges, auto_scroll, window, cx);
13479 }
13480
13481 pub fn fold_creases<T: ToOffset + Clone>(
13482 &mut self,
13483 creases: Vec<Crease<T>>,
13484 auto_scroll: bool,
13485 window: &mut Window,
13486 cx: &mut Context<Self>,
13487 ) {
13488 if creases.is_empty() {
13489 return;
13490 }
13491
13492 let mut buffers_affected = HashSet::default();
13493 let multi_buffer = self.buffer().read(cx);
13494 for crease in &creases {
13495 if let Some((_, buffer, _)) =
13496 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13497 {
13498 buffers_affected.insert(buffer.read(cx).remote_id());
13499 };
13500 }
13501
13502 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13503
13504 if auto_scroll {
13505 self.request_autoscroll(Autoscroll::fit(), cx);
13506 }
13507
13508 cx.notify();
13509
13510 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13511 // Clear diagnostics block when folding a range that contains it.
13512 let snapshot = self.snapshot(window, cx);
13513 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13514 drop(snapshot);
13515 self.active_diagnostics = Some(active_diagnostics);
13516 self.dismiss_diagnostics(cx);
13517 } else {
13518 self.active_diagnostics = Some(active_diagnostics);
13519 }
13520 }
13521
13522 self.scrollbar_marker_state.dirty = true;
13523 }
13524
13525 /// Removes any folds whose ranges intersect any of the given ranges.
13526 pub fn unfold_ranges<T: ToOffset + Clone>(
13527 &mut self,
13528 ranges: &[Range<T>],
13529 inclusive: bool,
13530 auto_scroll: bool,
13531 cx: &mut Context<Self>,
13532 ) {
13533 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13534 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13535 });
13536 }
13537
13538 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13539 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13540 return;
13541 }
13542 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13543 self.display_map.update(cx, |display_map, cx| {
13544 display_map.fold_buffers([buffer_id], cx)
13545 });
13546 cx.emit(EditorEvent::BufferFoldToggled {
13547 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13548 folded: true,
13549 });
13550 cx.notify();
13551 }
13552
13553 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13554 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13555 return;
13556 }
13557 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13558 self.display_map.update(cx, |display_map, cx| {
13559 display_map.unfold_buffers([buffer_id], cx);
13560 });
13561 cx.emit(EditorEvent::BufferFoldToggled {
13562 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13563 folded: false,
13564 });
13565 cx.notify();
13566 }
13567
13568 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13569 self.display_map.read(cx).is_buffer_folded(buffer)
13570 }
13571
13572 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13573 self.display_map.read(cx).folded_buffers()
13574 }
13575
13576 /// Removes any folds with the given ranges.
13577 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13578 &mut self,
13579 ranges: &[Range<T>],
13580 type_id: TypeId,
13581 auto_scroll: bool,
13582 cx: &mut Context<Self>,
13583 ) {
13584 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13585 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13586 });
13587 }
13588
13589 fn remove_folds_with<T: ToOffset + Clone>(
13590 &mut self,
13591 ranges: &[Range<T>],
13592 auto_scroll: bool,
13593 cx: &mut Context<Self>,
13594 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13595 ) {
13596 if ranges.is_empty() {
13597 return;
13598 }
13599
13600 let mut buffers_affected = HashSet::default();
13601 let multi_buffer = self.buffer().read(cx);
13602 for range in ranges {
13603 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13604 buffers_affected.insert(buffer.read(cx).remote_id());
13605 };
13606 }
13607
13608 self.display_map.update(cx, update);
13609
13610 if auto_scroll {
13611 self.request_autoscroll(Autoscroll::fit(), cx);
13612 }
13613
13614 cx.notify();
13615 self.scrollbar_marker_state.dirty = true;
13616 self.active_indent_guides_state.dirty = true;
13617 }
13618
13619 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13620 self.display_map.read(cx).fold_placeholder.clone()
13621 }
13622
13623 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13624 self.buffer.update(cx, |buffer, cx| {
13625 buffer.set_all_diff_hunks_expanded(cx);
13626 });
13627 }
13628
13629 pub fn expand_all_diff_hunks(
13630 &mut self,
13631 _: &ExpandAllDiffHunks,
13632 _window: &mut Window,
13633 cx: &mut Context<Self>,
13634 ) {
13635 self.buffer.update(cx, |buffer, cx| {
13636 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13637 });
13638 }
13639
13640 pub fn toggle_selected_diff_hunks(
13641 &mut self,
13642 _: &ToggleSelectedDiffHunks,
13643 _window: &mut Window,
13644 cx: &mut Context<Self>,
13645 ) {
13646 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13647 self.toggle_diff_hunks_in_ranges(ranges, cx);
13648 }
13649
13650 pub fn diff_hunks_in_ranges<'a>(
13651 &'a self,
13652 ranges: &'a [Range<Anchor>],
13653 buffer: &'a MultiBufferSnapshot,
13654 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13655 ranges.iter().flat_map(move |range| {
13656 let end_excerpt_id = range.end.excerpt_id;
13657 let range = range.to_point(buffer);
13658 let mut peek_end = range.end;
13659 if range.end.row < buffer.max_row().0 {
13660 peek_end = Point::new(range.end.row + 1, 0);
13661 }
13662 buffer
13663 .diff_hunks_in_range(range.start..peek_end)
13664 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13665 })
13666 }
13667
13668 pub fn has_stageable_diff_hunks_in_ranges(
13669 &self,
13670 ranges: &[Range<Anchor>],
13671 snapshot: &MultiBufferSnapshot,
13672 ) -> bool {
13673 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13674 hunks.any(|hunk| hunk.status().has_secondary_hunk())
13675 }
13676
13677 pub fn toggle_staged_selected_diff_hunks(
13678 &mut self,
13679 _: &::git::ToggleStaged,
13680 _: &mut Window,
13681 cx: &mut Context<Self>,
13682 ) {
13683 let snapshot = self.buffer.read(cx).snapshot(cx);
13684 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13685 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13686 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13687 }
13688
13689 pub fn stage_and_next(
13690 &mut self,
13691 _: &::git::StageAndNext,
13692 window: &mut Window,
13693 cx: &mut Context<Self>,
13694 ) {
13695 self.do_stage_or_unstage_and_next(true, window, cx);
13696 }
13697
13698 pub fn unstage_and_next(
13699 &mut self,
13700 _: &::git::UnstageAndNext,
13701 window: &mut Window,
13702 cx: &mut Context<Self>,
13703 ) {
13704 self.do_stage_or_unstage_and_next(false, window, cx);
13705 }
13706
13707 pub fn stage_or_unstage_diff_hunks(
13708 &mut self,
13709 stage: bool,
13710 ranges: Vec<Range<Anchor>>,
13711 cx: &mut Context<Self>,
13712 ) {
13713 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
13714 cx.spawn(|this, mut cx| async move {
13715 task.await?;
13716 this.update(&mut cx, |this, cx| {
13717 let snapshot = this.buffer.read(cx).snapshot(cx);
13718 let chunk_by = this
13719 .diff_hunks_in_ranges(&ranges, &snapshot)
13720 .chunk_by(|hunk| hunk.buffer_id);
13721 for (buffer_id, hunks) in &chunk_by {
13722 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
13723 }
13724 })
13725 })
13726 .detach_and_log_err(cx);
13727 }
13728
13729 fn save_buffers_for_ranges_if_needed(
13730 &mut self,
13731 ranges: &[Range<Anchor>],
13732 cx: &mut Context<'_, Editor>,
13733 ) -> Task<Result<()>> {
13734 let multibuffer = self.buffer.read(cx);
13735 let snapshot = multibuffer.read(cx);
13736 let buffer_ids: HashSet<_> = ranges
13737 .iter()
13738 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
13739 .collect();
13740 drop(snapshot);
13741
13742 let mut buffers = HashSet::default();
13743 for buffer_id in buffer_ids {
13744 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
13745 let buffer = buffer_entity.read(cx);
13746 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
13747 {
13748 buffers.insert(buffer_entity);
13749 }
13750 }
13751 }
13752
13753 if let Some(project) = &self.project {
13754 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
13755 } else {
13756 Task::ready(Ok(()))
13757 }
13758 }
13759
13760 fn do_stage_or_unstage_and_next(
13761 &mut self,
13762 stage: bool,
13763 window: &mut Window,
13764 cx: &mut Context<Self>,
13765 ) {
13766 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13767
13768 if ranges.iter().any(|range| range.start != range.end) {
13769 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13770 return;
13771 }
13772
13773 let snapshot = self.snapshot(window, cx);
13774 let newest_range = self.selections.newest::<Point>(cx).range();
13775
13776 let run_twice = snapshot
13777 .hunks_for_ranges([newest_range])
13778 .first()
13779 .is_some_and(|hunk| {
13780 let next_line = Point::new(hunk.row_range.end.0 + 1, 0);
13781 self.hunk_after_position(&snapshot, next_line)
13782 .is_some_and(|other| other.row_range == hunk.row_range)
13783 });
13784
13785 if run_twice {
13786 self.go_to_next_hunk(&GoToHunk, window, cx);
13787 }
13788 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13789 self.go_to_next_hunk(&GoToHunk, window, cx);
13790 }
13791
13792 fn do_stage_or_unstage(
13793 &self,
13794 stage: bool,
13795 buffer_id: BufferId,
13796 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
13797 cx: &mut App,
13798 ) -> Option<()> {
13799 let project = self.project.as_ref()?;
13800 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
13801 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
13802 let buffer_snapshot = buffer.read(cx).snapshot();
13803 let file_exists = buffer_snapshot
13804 .file()
13805 .is_some_and(|file| file.disk_state().exists());
13806 diff.update(cx, |diff, cx| {
13807 diff.stage_or_unstage_hunks(
13808 stage,
13809 &hunks
13810 .map(|hunk| buffer_diff::DiffHunk {
13811 buffer_range: hunk.buffer_range,
13812 diff_base_byte_range: hunk.diff_base_byte_range,
13813 secondary_status: hunk.secondary_status,
13814 range: Point::zero()..Point::zero(), // unused
13815 })
13816 .collect::<Vec<_>>(),
13817 &buffer_snapshot,
13818 file_exists,
13819 cx,
13820 )
13821 });
13822 None
13823 }
13824
13825 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
13826 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13827 self.buffer
13828 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
13829 }
13830
13831 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
13832 self.buffer.update(cx, |buffer, cx| {
13833 let ranges = vec![Anchor::min()..Anchor::max()];
13834 if !buffer.all_diff_hunks_expanded()
13835 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
13836 {
13837 buffer.collapse_diff_hunks(ranges, cx);
13838 true
13839 } else {
13840 false
13841 }
13842 })
13843 }
13844
13845 fn toggle_diff_hunks_in_ranges(
13846 &mut self,
13847 ranges: Vec<Range<Anchor>>,
13848 cx: &mut Context<'_, Editor>,
13849 ) {
13850 self.buffer.update(cx, |buffer, cx| {
13851 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
13852 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
13853 })
13854 }
13855
13856 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
13857 self.buffer.update(cx, |buffer, cx| {
13858 let snapshot = buffer.snapshot(cx);
13859 let excerpt_id = range.end.excerpt_id;
13860 let point_range = range.to_point(&snapshot);
13861 let expand = !buffer.single_hunk_is_expanded(range, cx);
13862 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
13863 })
13864 }
13865
13866 pub(crate) fn apply_all_diff_hunks(
13867 &mut self,
13868 _: &ApplyAllDiffHunks,
13869 window: &mut Window,
13870 cx: &mut Context<Self>,
13871 ) {
13872 let buffers = self.buffer.read(cx).all_buffers();
13873 for branch_buffer in buffers {
13874 branch_buffer.update(cx, |branch_buffer, cx| {
13875 branch_buffer.merge_into_base(Vec::new(), cx);
13876 });
13877 }
13878
13879 if let Some(project) = self.project.clone() {
13880 self.save(true, project, window, cx).detach_and_log_err(cx);
13881 }
13882 }
13883
13884 pub(crate) fn apply_selected_diff_hunks(
13885 &mut self,
13886 _: &ApplyDiffHunk,
13887 window: &mut Window,
13888 cx: &mut Context<Self>,
13889 ) {
13890 let snapshot = self.snapshot(window, cx);
13891 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
13892 let mut ranges_by_buffer = HashMap::default();
13893 self.transact(window, cx, |editor, _window, cx| {
13894 for hunk in hunks {
13895 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13896 ranges_by_buffer
13897 .entry(buffer.clone())
13898 .or_insert_with(Vec::new)
13899 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13900 }
13901 }
13902
13903 for (buffer, ranges) in ranges_by_buffer {
13904 buffer.update(cx, |buffer, cx| {
13905 buffer.merge_into_base(ranges, cx);
13906 });
13907 }
13908 });
13909
13910 if let Some(project) = self.project.clone() {
13911 self.save(true, project, window, cx).detach_and_log_err(cx);
13912 }
13913 }
13914
13915 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13916 if hovered != self.gutter_hovered {
13917 self.gutter_hovered = hovered;
13918 cx.notify();
13919 }
13920 }
13921
13922 pub fn insert_blocks(
13923 &mut self,
13924 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13925 autoscroll: Option<Autoscroll>,
13926 cx: &mut Context<Self>,
13927 ) -> Vec<CustomBlockId> {
13928 let blocks = self
13929 .display_map
13930 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13931 if let Some(autoscroll) = autoscroll {
13932 self.request_autoscroll(autoscroll, cx);
13933 }
13934 cx.notify();
13935 blocks
13936 }
13937
13938 pub fn resize_blocks(
13939 &mut self,
13940 heights: HashMap<CustomBlockId, u32>,
13941 autoscroll: Option<Autoscroll>,
13942 cx: &mut Context<Self>,
13943 ) {
13944 self.display_map
13945 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13946 if let Some(autoscroll) = autoscroll {
13947 self.request_autoscroll(autoscroll, cx);
13948 }
13949 cx.notify();
13950 }
13951
13952 pub fn replace_blocks(
13953 &mut self,
13954 renderers: HashMap<CustomBlockId, RenderBlock>,
13955 autoscroll: Option<Autoscroll>,
13956 cx: &mut Context<Self>,
13957 ) {
13958 self.display_map
13959 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13960 if let Some(autoscroll) = autoscroll {
13961 self.request_autoscroll(autoscroll, cx);
13962 }
13963 cx.notify();
13964 }
13965
13966 pub fn remove_blocks(
13967 &mut self,
13968 block_ids: HashSet<CustomBlockId>,
13969 autoscroll: Option<Autoscroll>,
13970 cx: &mut Context<Self>,
13971 ) {
13972 self.display_map.update(cx, |display_map, cx| {
13973 display_map.remove_blocks(block_ids, cx)
13974 });
13975 if let Some(autoscroll) = autoscroll {
13976 self.request_autoscroll(autoscroll, cx);
13977 }
13978 cx.notify();
13979 }
13980
13981 pub fn row_for_block(
13982 &self,
13983 block_id: CustomBlockId,
13984 cx: &mut Context<Self>,
13985 ) -> Option<DisplayRow> {
13986 self.display_map
13987 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13988 }
13989
13990 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13991 self.focused_block = Some(focused_block);
13992 }
13993
13994 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13995 self.focused_block.take()
13996 }
13997
13998 pub fn insert_creases(
13999 &mut self,
14000 creases: impl IntoIterator<Item = Crease<Anchor>>,
14001 cx: &mut Context<Self>,
14002 ) -> Vec<CreaseId> {
14003 self.display_map
14004 .update(cx, |map, cx| map.insert_creases(creases, cx))
14005 }
14006
14007 pub fn remove_creases(
14008 &mut self,
14009 ids: impl IntoIterator<Item = CreaseId>,
14010 cx: &mut Context<Self>,
14011 ) {
14012 self.display_map
14013 .update(cx, |map, cx| map.remove_creases(ids, cx));
14014 }
14015
14016 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14017 self.display_map
14018 .update(cx, |map, cx| map.snapshot(cx))
14019 .longest_row()
14020 }
14021
14022 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14023 self.display_map
14024 .update(cx, |map, cx| map.snapshot(cx))
14025 .max_point()
14026 }
14027
14028 pub fn text(&self, cx: &App) -> String {
14029 self.buffer.read(cx).read(cx).text()
14030 }
14031
14032 pub fn is_empty(&self, cx: &App) -> bool {
14033 self.buffer.read(cx).read(cx).is_empty()
14034 }
14035
14036 pub fn text_option(&self, cx: &App) -> Option<String> {
14037 let text = self.text(cx);
14038 let text = text.trim();
14039
14040 if text.is_empty() {
14041 return None;
14042 }
14043
14044 Some(text.to_string())
14045 }
14046
14047 pub fn set_text(
14048 &mut self,
14049 text: impl Into<Arc<str>>,
14050 window: &mut Window,
14051 cx: &mut Context<Self>,
14052 ) {
14053 self.transact(window, cx, |this, _, cx| {
14054 this.buffer
14055 .read(cx)
14056 .as_singleton()
14057 .expect("you can only call set_text on editors for singleton buffers")
14058 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14059 });
14060 }
14061
14062 pub fn display_text(&self, cx: &mut App) -> String {
14063 self.display_map
14064 .update(cx, |map, cx| map.snapshot(cx))
14065 .text()
14066 }
14067
14068 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14069 let mut wrap_guides = smallvec::smallvec![];
14070
14071 if self.show_wrap_guides == Some(false) {
14072 return wrap_guides;
14073 }
14074
14075 let settings = self.buffer.read(cx).language_settings(cx);
14076 if settings.show_wrap_guides {
14077 match self.soft_wrap_mode(cx) {
14078 SoftWrap::Column(soft_wrap) => {
14079 wrap_guides.push((soft_wrap as usize, true));
14080 }
14081 SoftWrap::Bounded(soft_wrap) => {
14082 wrap_guides.push((soft_wrap as usize, true));
14083 }
14084 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14085 }
14086 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14087 }
14088
14089 wrap_guides
14090 }
14091
14092 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14093 let settings = self.buffer.read(cx).language_settings(cx);
14094 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14095 match mode {
14096 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14097 SoftWrap::None
14098 }
14099 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14100 language_settings::SoftWrap::PreferredLineLength => {
14101 SoftWrap::Column(settings.preferred_line_length)
14102 }
14103 language_settings::SoftWrap::Bounded => {
14104 SoftWrap::Bounded(settings.preferred_line_length)
14105 }
14106 }
14107 }
14108
14109 pub fn set_soft_wrap_mode(
14110 &mut self,
14111 mode: language_settings::SoftWrap,
14112
14113 cx: &mut Context<Self>,
14114 ) {
14115 self.soft_wrap_mode_override = Some(mode);
14116 cx.notify();
14117 }
14118
14119 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14120 self.text_style_refinement = Some(style);
14121 }
14122
14123 /// called by the Element so we know what style we were most recently rendered with.
14124 pub(crate) fn set_style(
14125 &mut self,
14126 style: EditorStyle,
14127 window: &mut Window,
14128 cx: &mut Context<Self>,
14129 ) {
14130 let rem_size = window.rem_size();
14131 self.display_map.update(cx, |map, cx| {
14132 map.set_font(
14133 style.text.font(),
14134 style.text.font_size.to_pixels(rem_size),
14135 cx,
14136 )
14137 });
14138 self.style = Some(style);
14139 }
14140
14141 pub fn style(&self) -> Option<&EditorStyle> {
14142 self.style.as_ref()
14143 }
14144
14145 // Called by the element. This method is not designed to be called outside of the editor
14146 // element's layout code because it does not notify when rewrapping is computed synchronously.
14147 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14148 self.display_map
14149 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14150 }
14151
14152 pub fn set_soft_wrap(&mut self) {
14153 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14154 }
14155
14156 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14157 if self.soft_wrap_mode_override.is_some() {
14158 self.soft_wrap_mode_override.take();
14159 } else {
14160 let soft_wrap = match self.soft_wrap_mode(cx) {
14161 SoftWrap::GitDiff => return,
14162 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14163 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14164 language_settings::SoftWrap::None
14165 }
14166 };
14167 self.soft_wrap_mode_override = Some(soft_wrap);
14168 }
14169 cx.notify();
14170 }
14171
14172 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14173 let Some(workspace) = self.workspace() else {
14174 return;
14175 };
14176 let fs = workspace.read(cx).app_state().fs.clone();
14177 let current_show = TabBarSettings::get_global(cx).show;
14178 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14179 setting.show = Some(!current_show);
14180 });
14181 }
14182
14183 pub fn toggle_indent_guides(
14184 &mut self,
14185 _: &ToggleIndentGuides,
14186 _: &mut Window,
14187 cx: &mut Context<Self>,
14188 ) {
14189 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14190 self.buffer
14191 .read(cx)
14192 .language_settings(cx)
14193 .indent_guides
14194 .enabled
14195 });
14196 self.show_indent_guides = Some(!currently_enabled);
14197 cx.notify();
14198 }
14199
14200 fn should_show_indent_guides(&self) -> Option<bool> {
14201 self.show_indent_guides
14202 }
14203
14204 pub fn toggle_line_numbers(
14205 &mut self,
14206 _: &ToggleLineNumbers,
14207 _: &mut Window,
14208 cx: &mut Context<Self>,
14209 ) {
14210 let mut editor_settings = EditorSettings::get_global(cx).clone();
14211 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14212 EditorSettings::override_global(editor_settings, cx);
14213 }
14214
14215 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14216 self.use_relative_line_numbers
14217 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14218 }
14219
14220 pub fn toggle_relative_line_numbers(
14221 &mut self,
14222 _: &ToggleRelativeLineNumbers,
14223 _: &mut Window,
14224 cx: &mut Context<Self>,
14225 ) {
14226 let is_relative = self.should_use_relative_line_numbers(cx);
14227 self.set_relative_line_number(Some(!is_relative), cx)
14228 }
14229
14230 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14231 self.use_relative_line_numbers = is_relative;
14232 cx.notify();
14233 }
14234
14235 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14236 self.show_gutter = show_gutter;
14237 cx.notify();
14238 }
14239
14240 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14241 self.show_scrollbars = show_scrollbars;
14242 cx.notify();
14243 }
14244
14245 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14246 self.show_line_numbers = Some(show_line_numbers);
14247 cx.notify();
14248 }
14249
14250 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14251 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14252 cx.notify();
14253 }
14254
14255 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14256 self.show_code_actions = Some(show_code_actions);
14257 cx.notify();
14258 }
14259
14260 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14261 self.show_runnables = Some(show_runnables);
14262 cx.notify();
14263 }
14264
14265 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14266 if self.display_map.read(cx).masked != masked {
14267 self.display_map.update(cx, |map, _| map.masked = masked);
14268 }
14269 cx.notify()
14270 }
14271
14272 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14273 self.show_wrap_guides = Some(show_wrap_guides);
14274 cx.notify();
14275 }
14276
14277 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14278 self.show_indent_guides = Some(show_indent_guides);
14279 cx.notify();
14280 }
14281
14282 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14283 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14284 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14285 if let Some(dir) = file.abs_path(cx).parent() {
14286 return Some(dir.to_owned());
14287 }
14288 }
14289
14290 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14291 return Some(project_path.path.to_path_buf());
14292 }
14293 }
14294
14295 None
14296 }
14297
14298 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14299 self.active_excerpt(cx)?
14300 .1
14301 .read(cx)
14302 .file()
14303 .and_then(|f| f.as_local())
14304 }
14305
14306 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14307 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14308 let buffer = buffer.read(cx);
14309 if let Some(project_path) = buffer.project_path(cx) {
14310 let project = self.project.as_ref()?.read(cx);
14311 project.absolute_path(&project_path, cx)
14312 } else {
14313 buffer
14314 .file()
14315 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14316 }
14317 })
14318 }
14319
14320 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14321 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14322 let project_path = buffer.read(cx).project_path(cx)?;
14323 let project = self.project.as_ref()?.read(cx);
14324 let entry = project.entry_for_path(&project_path, cx)?;
14325 let path = entry.path.to_path_buf();
14326 Some(path)
14327 })
14328 }
14329
14330 pub fn reveal_in_finder(
14331 &mut self,
14332 _: &RevealInFileManager,
14333 _window: &mut Window,
14334 cx: &mut Context<Self>,
14335 ) {
14336 if let Some(target) = self.target_file(cx) {
14337 cx.reveal_path(&target.abs_path(cx));
14338 }
14339 }
14340
14341 pub fn copy_path(
14342 &mut self,
14343 _: &zed_actions::workspace::CopyPath,
14344 _window: &mut Window,
14345 cx: &mut Context<Self>,
14346 ) {
14347 if let Some(path) = self.target_file_abs_path(cx) {
14348 if let Some(path) = path.to_str() {
14349 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14350 }
14351 }
14352 }
14353
14354 pub fn copy_relative_path(
14355 &mut self,
14356 _: &zed_actions::workspace::CopyRelativePath,
14357 _window: &mut Window,
14358 cx: &mut Context<Self>,
14359 ) {
14360 if let Some(path) = self.target_file_path(cx) {
14361 if let Some(path) = path.to_str() {
14362 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14363 }
14364 }
14365 }
14366
14367 pub fn copy_file_name_without_extension(
14368 &mut self,
14369 _: &CopyFileNameWithoutExtension,
14370 _: &mut Window,
14371 cx: &mut Context<Self>,
14372 ) {
14373 if let Some(file) = self.target_file(cx) {
14374 if let Some(file_stem) = file.path().file_stem() {
14375 if let Some(name) = file_stem.to_str() {
14376 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14377 }
14378 }
14379 }
14380 }
14381
14382 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14383 if let Some(file) = self.target_file(cx) {
14384 if let Some(file_name) = file.path().file_name() {
14385 if let Some(name) = file_name.to_str() {
14386 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14387 }
14388 }
14389 }
14390 }
14391
14392 pub fn toggle_git_blame(
14393 &mut self,
14394 _: &ToggleGitBlame,
14395 window: &mut Window,
14396 cx: &mut Context<Self>,
14397 ) {
14398 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14399
14400 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14401 self.start_git_blame(true, window, cx);
14402 }
14403
14404 cx.notify();
14405 }
14406
14407 pub fn toggle_git_blame_inline(
14408 &mut self,
14409 _: &ToggleGitBlameInline,
14410 window: &mut Window,
14411 cx: &mut Context<Self>,
14412 ) {
14413 self.toggle_git_blame_inline_internal(true, window, cx);
14414 cx.notify();
14415 }
14416
14417 pub fn git_blame_inline_enabled(&self) -> bool {
14418 self.git_blame_inline_enabled
14419 }
14420
14421 pub fn toggle_selection_menu(
14422 &mut self,
14423 _: &ToggleSelectionMenu,
14424 _: &mut Window,
14425 cx: &mut Context<Self>,
14426 ) {
14427 self.show_selection_menu = self
14428 .show_selection_menu
14429 .map(|show_selections_menu| !show_selections_menu)
14430 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14431
14432 cx.notify();
14433 }
14434
14435 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14436 self.show_selection_menu
14437 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14438 }
14439
14440 fn start_git_blame(
14441 &mut self,
14442 user_triggered: bool,
14443 window: &mut Window,
14444 cx: &mut Context<Self>,
14445 ) {
14446 if let Some(project) = self.project.as_ref() {
14447 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14448 return;
14449 };
14450
14451 if buffer.read(cx).file().is_none() {
14452 return;
14453 }
14454
14455 let focused = self.focus_handle(cx).contains_focused(window, cx);
14456
14457 let project = project.clone();
14458 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14459 self.blame_subscription =
14460 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14461 self.blame = Some(blame);
14462 }
14463 }
14464
14465 fn toggle_git_blame_inline_internal(
14466 &mut self,
14467 user_triggered: bool,
14468 window: &mut Window,
14469 cx: &mut Context<Self>,
14470 ) {
14471 if self.git_blame_inline_enabled {
14472 self.git_blame_inline_enabled = false;
14473 self.show_git_blame_inline = false;
14474 self.show_git_blame_inline_delay_task.take();
14475 } else {
14476 self.git_blame_inline_enabled = true;
14477 self.start_git_blame_inline(user_triggered, window, cx);
14478 }
14479
14480 cx.notify();
14481 }
14482
14483 fn start_git_blame_inline(
14484 &mut self,
14485 user_triggered: bool,
14486 window: &mut Window,
14487 cx: &mut Context<Self>,
14488 ) {
14489 self.start_git_blame(user_triggered, window, cx);
14490
14491 if ProjectSettings::get_global(cx)
14492 .git
14493 .inline_blame_delay()
14494 .is_some()
14495 {
14496 self.start_inline_blame_timer(window, cx);
14497 } else {
14498 self.show_git_blame_inline = true
14499 }
14500 }
14501
14502 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14503 self.blame.as_ref()
14504 }
14505
14506 pub fn show_git_blame_gutter(&self) -> bool {
14507 self.show_git_blame_gutter
14508 }
14509
14510 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14511 self.show_git_blame_gutter && self.has_blame_entries(cx)
14512 }
14513
14514 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14515 self.show_git_blame_inline
14516 && (self.focus_handle.is_focused(window)
14517 || self
14518 .git_blame_inline_tooltip
14519 .as_ref()
14520 .and_then(|t| t.upgrade())
14521 .is_some())
14522 && !self.newest_selection_head_on_empty_line(cx)
14523 && self.has_blame_entries(cx)
14524 }
14525
14526 fn has_blame_entries(&self, cx: &App) -> bool {
14527 self.blame()
14528 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14529 }
14530
14531 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14532 let cursor_anchor = self.selections.newest_anchor().head();
14533
14534 let snapshot = self.buffer.read(cx).snapshot(cx);
14535 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14536
14537 snapshot.line_len(buffer_row) == 0
14538 }
14539
14540 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14541 let buffer_and_selection = maybe!({
14542 let selection = self.selections.newest::<Point>(cx);
14543 let selection_range = selection.range();
14544
14545 let multi_buffer = self.buffer().read(cx);
14546 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14547 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14548
14549 let (buffer, range, _) = if selection.reversed {
14550 buffer_ranges.first()
14551 } else {
14552 buffer_ranges.last()
14553 }?;
14554
14555 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14556 ..text::ToPoint::to_point(&range.end, &buffer).row;
14557 Some((
14558 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14559 selection,
14560 ))
14561 });
14562
14563 let Some((buffer, selection)) = buffer_and_selection else {
14564 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14565 };
14566
14567 let Some(project) = self.project.as_ref() else {
14568 return Task::ready(Err(anyhow!("editor does not have project")));
14569 };
14570
14571 project.update(cx, |project, cx| {
14572 project.get_permalink_to_line(&buffer, selection, cx)
14573 })
14574 }
14575
14576 pub fn copy_permalink_to_line(
14577 &mut self,
14578 _: &CopyPermalinkToLine,
14579 window: &mut Window,
14580 cx: &mut Context<Self>,
14581 ) {
14582 let permalink_task = self.get_permalink_to_line(cx);
14583 let workspace = self.workspace();
14584
14585 cx.spawn_in(window, |_, mut cx| async move {
14586 match permalink_task.await {
14587 Ok(permalink) => {
14588 cx.update(|_, cx| {
14589 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14590 })
14591 .ok();
14592 }
14593 Err(err) => {
14594 let message = format!("Failed to copy permalink: {err}");
14595
14596 Err::<(), anyhow::Error>(err).log_err();
14597
14598 if let Some(workspace) = workspace {
14599 workspace
14600 .update_in(&mut cx, |workspace, _, cx| {
14601 struct CopyPermalinkToLine;
14602
14603 workspace.show_toast(
14604 Toast::new(
14605 NotificationId::unique::<CopyPermalinkToLine>(),
14606 message,
14607 ),
14608 cx,
14609 )
14610 })
14611 .ok();
14612 }
14613 }
14614 }
14615 })
14616 .detach();
14617 }
14618
14619 pub fn copy_file_location(
14620 &mut self,
14621 _: &CopyFileLocation,
14622 _: &mut Window,
14623 cx: &mut Context<Self>,
14624 ) {
14625 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14626 if let Some(file) = self.target_file(cx) {
14627 if let Some(path) = file.path().to_str() {
14628 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14629 }
14630 }
14631 }
14632
14633 pub fn open_permalink_to_line(
14634 &mut self,
14635 _: &OpenPermalinkToLine,
14636 window: &mut Window,
14637 cx: &mut Context<Self>,
14638 ) {
14639 let permalink_task = self.get_permalink_to_line(cx);
14640 let workspace = self.workspace();
14641
14642 cx.spawn_in(window, |_, mut cx| async move {
14643 match permalink_task.await {
14644 Ok(permalink) => {
14645 cx.update(|_, cx| {
14646 cx.open_url(permalink.as_ref());
14647 })
14648 .ok();
14649 }
14650 Err(err) => {
14651 let message = format!("Failed to open permalink: {err}");
14652
14653 Err::<(), anyhow::Error>(err).log_err();
14654
14655 if let Some(workspace) = workspace {
14656 workspace
14657 .update(&mut cx, |workspace, cx| {
14658 struct OpenPermalinkToLine;
14659
14660 workspace.show_toast(
14661 Toast::new(
14662 NotificationId::unique::<OpenPermalinkToLine>(),
14663 message,
14664 ),
14665 cx,
14666 )
14667 })
14668 .ok();
14669 }
14670 }
14671 }
14672 })
14673 .detach();
14674 }
14675
14676 pub fn insert_uuid_v4(
14677 &mut self,
14678 _: &InsertUuidV4,
14679 window: &mut Window,
14680 cx: &mut Context<Self>,
14681 ) {
14682 self.insert_uuid(UuidVersion::V4, window, cx);
14683 }
14684
14685 pub fn insert_uuid_v7(
14686 &mut self,
14687 _: &InsertUuidV7,
14688 window: &mut Window,
14689 cx: &mut Context<Self>,
14690 ) {
14691 self.insert_uuid(UuidVersion::V7, window, cx);
14692 }
14693
14694 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14695 self.transact(window, cx, |this, window, cx| {
14696 let edits = this
14697 .selections
14698 .all::<Point>(cx)
14699 .into_iter()
14700 .map(|selection| {
14701 let uuid = match version {
14702 UuidVersion::V4 => uuid::Uuid::new_v4(),
14703 UuidVersion::V7 => uuid::Uuid::now_v7(),
14704 };
14705
14706 (selection.range(), uuid.to_string())
14707 });
14708 this.edit(edits, cx);
14709 this.refresh_inline_completion(true, false, window, cx);
14710 });
14711 }
14712
14713 pub fn open_selections_in_multibuffer(
14714 &mut self,
14715 _: &OpenSelectionsInMultibuffer,
14716 window: &mut Window,
14717 cx: &mut Context<Self>,
14718 ) {
14719 let multibuffer = self.buffer.read(cx);
14720
14721 let Some(buffer) = multibuffer.as_singleton() else {
14722 return;
14723 };
14724
14725 let Some(workspace) = self.workspace() else {
14726 return;
14727 };
14728
14729 let locations = self
14730 .selections
14731 .disjoint_anchors()
14732 .iter()
14733 .map(|range| Location {
14734 buffer: buffer.clone(),
14735 range: range.start.text_anchor..range.end.text_anchor,
14736 })
14737 .collect::<Vec<_>>();
14738
14739 let title = multibuffer.title(cx).to_string();
14740
14741 cx.spawn_in(window, |_, mut cx| async move {
14742 workspace.update_in(&mut cx, |workspace, window, cx| {
14743 Self::open_locations_in_multibuffer(
14744 workspace,
14745 locations,
14746 format!("Selections for '{title}'"),
14747 false,
14748 MultibufferSelectionMode::All,
14749 window,
14750 cx,
14751 );
14752 })
14753 })
14754 .detach();
14755 }
14756
14757 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14758 /// last highlight added will be used.
14759 ///
14760 /// If the range ends at the beginning of a line, then that line will not be highlighted.
14761 pub fn highlight_rows<T: 'static>(
14762 &mut self,
14763 range: Range<Anchor>,
14764 color: Hsla,
14765 should_autoscroll: bool,
14766 cx: &mut Context<Self>,
14767 ) {
14768 let snapshot = self.buffer().read(cx).snapshot(cx);
14769 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14770 let ix = row_highlights.binary_search_by(|highlight| {
14771 Ordering::Equal
14772 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
14773 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
14774 });
14775
14776 if let Err(mut ix) = ix {
14777 let index = post_inc(&mut self.highlight_order);
14778
14779 // If this range intersects with the preceding highlight, then merge it with
14780 // the preceding highlight. Otherwise insert a new highlight.
14781 let mut merged = false;
14782 if ix > 0 {
14783 let prev_highlight = &mut row_highlights[ix - 1];
14784 if prev_highlight
14785 .range
14786 .end
14787 .cmp(&range.start, &snapshot)
14788 .is_ge()
14789 {
14790 ix -= 1;
14791 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
14792 prev_highlight.range.end = range.end;
14793 }
14794 merged = true;
14795 prev_highlight.index = index;
14796 prev_highlight.color = color;
14797 prev_highlight.should_autoscroll = should_autoscroll;
14798 }
14799 }
14800
14801 if !merged {
14802 row_highlights.insert(
14803 ix,
14804 RowHighlight {
14805 range: range.clone(),
14806 index,
14807 color,
14808 should_autoscroll,
14809 },
14810 );
14811 }
14812
14813 // If any of the following highlights intersect with this one, merge them.
14814 while let Some(next_highlight) = row_highlights.get(ix + 1) {
14815 let highlight = &row_highlights[ix];
14816 if next_highlight
14817 .range
14818 .start
14819 .cmp(&highlight.range.end, &snapshot)
14820 .is_le()
14821 {
14822 if next_highlight
14823 .range
14824 .end
14825 .cmp(&highlight.range.end, &snapshot)
14826 .is_gt()
14827 {
14828 row_highlights[ix].range.end = next_highlight.range.end;
14829 }
14830 row_highlights.remove(ix + 1);
14831 } else {
14832 break;
14833 }
14834 }
14835 }
14836 }
14837
14838 /// Remove any highlighted row ranges of the given type that intersect the
14839 /// given ranges.
14840 pub fn remove_highlighted_rows<T: 'static>(
14841 &mut self,
14842 ranges_to_remove: Vec<Range<Anchor>>,
14843 cx: &mut Context<Self>,
14844 ) {
14845 let snapshot = self.buffer().read(cx).snapshot(cx);
14846 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14847 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
14848 row_highlights.retain(|highlight| {
14849 while let Some(range_to_remove) = ranges_to_remove.peek() {
14850 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
14851 Ordering::Less | Ordering::Equal => {
14852 ranges_to_remove.next();
14853 }
14854 Ordering::Greater => {
14855 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
14856 Ordering::Less | Ordering::Equal => {
14857 return false;
14858 }
14859 Ordering::Greater => break,
14860 }
14861 }
14862 }
14863 }
14864
14865 true
14866 })
14867 }
14868
14869 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
14870 pub fn clear_row_highlights<T: 'static>(&mut self) {
14871 self.highlighted_rows.remove(&TypeId::of::<T>());
14872 }
14873
14874 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14875 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14876 self.highlighted_rows
14877 .get(&TypeId::of::<T>())
14878 .map_or(&[] as &[_], |vec| vec.as_slice())
14879 .iter()
14880 .map(|highlight| (highlight.range.clone(), highlight.color))
14881 }
14882
14883 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14884 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14885 /// Allows to ignore certain kinds of highlights.
14886 pub fn highlighted_display_rows(
14887 &self,
14888 window: &mut Window,
14889 cx: &mut App,
14890 ) -> BTreeMap<DisplayRow, Background> {
14891 let snapshot = self.snapshot(window, cx);
14892 let mut used_highlight_orders = HashMap::default();
14893 self.highlighted_rows
14894 .iter()
14895 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14896 .fold(
14897 BTreeMap::<DisplayRow, Background>::new(),
14898 |mut unique_rows, highlight| {
14899 let start = highlight.range.start.to_display_point(&snapshot);
14900 let end = highlight.range.end.to_display_point(&snapshot);
14901 let start_row = start.row().0;
14902 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14903 && end.column() == 0
14904 {
14905 end.row().0.saturating_sub(1)
14906 } else {
14907 end.row().0
14908 };
14909 for row in start_row..=end_row {
14910 let used_index =
14911 used_highlight_orders.entry(row).or_insert(highlight.index);
14912 if highlight.index >= *used_index {
14913 *used_index = highlight.index;
14914 unique_rows.insert(DisplayRow(row), highlight.color.into());
14915 }
14916 }
14917 unique_rows
14918 },
14919 )
14920 }
14921
14922 pub fn highlighted_display_row_for_autoscroll(
14923 &self,
14924 snapshot: &DisplaySnapshot,
14925 ) -> Option<DisplayRow> {
14926 self.highlighted_rows
14927 .values()
14928 .flat_map(|highlighted_rows| highlighted_rows.iter())
14929 .filter_map(|highlight| {
14930 if highlight.should_autoscroll {
14931 Some(highlight.range.start.to_display_point(snapshot).row())
14932 } else {
14933 None
14934 }
14935 })
14936 .min()
14937 }
14938
14939 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14940 self.highlight_background::<SearchWithinRange>(
14941 ranges,
14942 |colors| colors.editor_document_highlight_read_background,
14943 cx,
14944 )
14945 }
14946
14947 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14948 self.breadcrumb_header = Some(new_header);
14949 }
14950
14951 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14952 self.clear_background_highlights::<SearchWithinRange>(cx);
14953 }
14954
14955 pub fn highlight_background<T: 'static>(
14956 &mut self,
14957 ranges: &[Range<Anchor>],
14958 color_fetcher: fn(&ThemeColors) -> Hsla,
14959 cx: &mut Context<Self>,
14960 ) {
14961 self.background_highlights
14962 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14963 self.scrollbar_marker_state.dirty = true;
14964 cx.notify();
14965 }
14966
14967 pub fn clear_background_highlights<T: 'static>(
14968 &mut self,
14969 cx: &mut Context<Self>,
14970 ) -> Option<BackgroundHighlight> {
14971 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14972 if !text_highlights.1.is_empty() {
14973 self.scrollbar_marker_state.dirty = true;
14974 cx.notify();
14975 }
14976 Some(text_highlights)
14977 }
14978
14979 pub fn highlight_gutter<T: 'static>(
14980 &mut self,
14981 ranges: &[Range<Anchor>],
14982 color_fetcher: fn(&App) -> Hsla,
14983 cx: &mut Context<Self>,
14984 ) {
14985 self.gutter_highlights
14986 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14987 cx.notify();
14988 }
14989
14990 pub fn clear_gutter_highlights<T: 'static>(
14991 &mut self,
14992 cx: &mut Context<Self>,
14993 ) -> Option<GutterHighlight> {
14994 cx.notify();
14995 self.gutter_highlights.remove(&TypeId::of::<T>())
14996 }
14997
14998 #[cfg(feature = "test-support")]
14999 pub fn all_text_background_highlights(
15000 &self,
15001 window: &mut Window,
15002 cx: &mut Context<Self>,
15003 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15004 let snapshot = self.snapshot(window, cx);
15005 let buffer = &snapshot.buffer_snapshot;
15006 let start = buffer.anchor_before(0);
15007 let end = buffer.anchor_after(buffer.len());
15008 let theme = cx.theme().colors();
15009 self.background_highlights_in_range(start..end, &snapshot, theme)
15010 }
15011
15012 #[cfg(feature = "test-support")]
15013 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15014 let snapshot = self.buffer().read(cx).snapshot(cx);
15015
15016 let highlights = self
15017 .background_highlights
15018 .get(&TypeId::of::<items::BufferSearchHighlights>());
15019
15020 if let Some((_color, ranges)) = highlights {
15021 ranges
15022 .iter()
15023 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15024 .collect_vec()
15025 } else {
15026 vec![]
15027 }
15028 }
15029
15030 fn document_highlights_for_position<'a>(
15031 &'a self,
15032 position: Anchor,
15033 buffer: &'a MultiBufferSnapshot,
15034 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15035 let read_highlights = self
15036 .background_highlights
15037 .get(&TypeId::of::<DocumentHighlightRead>())
15038 .map(|h| &h.1);
15039 let write_highlights = self
15040 .background_highlights
15041 .get(&TypeId::of::<DocumentHighlightWrite>())
15042 .map(|h| &h.1);
15043 let left_position = position.bias_left(buffer);
15044 let right_position = position.bias_right(buffer);
15045 read_highlights
15046 .into_iter()
15047 .chain(write_highlights)
15048 .flat_map(move |ranges| {
15049 let start_ix = match ranges.binary_search_by(|probe| {
15050 let cmp = probe.end.cmp(&left_position, buffer);
15051 if cmp.is_ge() {
15052 Ordering::Greater
15053 } else {
15054 Ordering::Less
15055 }
15056 }) {
15057 Ok(i) | Err(i) => i,
15058 };
15059
15060 ranges[start_ix..]
15061 .iter()
15062 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15063 })
15064 }
15065
15066 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15067 self.background_highlights
15068 .get(&TypeId::of::<T>())
15069 .map_or(false, |(_, highlights)| !highlights.is_empty())
15070 }
15071
15072 pub fn background_highlights_in_range(
15073 &self,
15074 search_range: Range<Anchor>,
15075 display_snapshot: &DisplaySnapshot,
15076 theme: &ThemeColors,
15077 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15078 let mut results = Vec::new();
15079 for (color_fetcher, ranges) in self.background_highlights.values() {
15080 let color = color_fetcher(theme);
15081 let start_ix = match ranges.binary_search_by(|probe| {
15082 let cmp = probe
15083 .end
15084 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15085 if cmp.is_gt() {
15086 Ordering::Greater
15087 } else {
15088 Ordering::Less
15089 }
15090 }) {
15091 Ok(i) | Err(i) => i,
15092 };
15093 for range in &ranges[start_ix..] {
15094 if range
15095 .start
15096 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15097 .is_ge()
15098 {
15099 break;
15100 }
15101
15102 let start = range.start.to_display_point(display_snapshot);
15103 let end = range.end.to_display_point(display_snapshot);
15104 results.push((start..end, color))
15105 }
15106 }
15107 results
15108 }
15109
15110 pub fn background_highlight_row_ranges<T: 'static>(
15111 &self,
15112 search_range: Range<Anchor>,
15113 display_snapshot: &DisplaySnapshot,
15114 count: usize,
15115 ) -> Vec<RangeInclusive<DisplayPoint>> {
15116 let mut results = Vec::new();
15117 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15118 return vec![];
15119 };
15120
15121 let start_ix = match ranges.binary_search_by(|probe| {
15122 let cmp = probe
15123 .end
15124 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15125 if cmp.is_gt() {
15126 Ordering::Greater
15127 } else {
15128 Ordering::Less
15129 }
15130 }) {
15131 Ok(i) | Err(i) => i,
15132 };
15133 let mut push_region = |start: Option<Point>, end: Option<Point>| {
15134 if let (Some(start_display), Some(end_display)) = (start, end) {
15135 results.push(
15136 start_display.to_display_point(display_snapshot)
15137 ..=end_display.to_display_point(display_snapshot),
15138 );
15139 }
15140 };
15141 let mut start_row: Option<Point> = None;
15142 let mut end_row: Option<Point> = None;
15143 if ranges.len() > count {
15144 return Vec::new();
15145 }
15146 for range in &ranges[start_ix..] {
15147 if range
15148 .start
15149 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15150 .is_ge()
15151 {
15152 break;
15153 }
15154 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
15155 if let Some(current_row) = &end_row {
15156 if end.row == current_row.row {
15157 continue;
15158 }
15159 }
15160 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
15161 if start_row.is_none() {
15162 assert_eq!(end_row, None);
15163 start_row = Some(start);
15164 end_row = Some(end);
15165 continue;
15166 }
15167 if let Some(current_end) = end_row.as_mut() {
15168 if start.row > current_end.row + 1 {
15169 push_region(start_row, end_row);
15170 start_row = Some(start);
15171 end_row = Some(end);
15172 } else {
15173 // Merge two hunks.
15174 *current_end = end;
15175 }
15176 } else {
15177 unreachable!();
15178 }
15179 }
15180 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15181 push_region(start_row, end_row);
15182 results
15183 }
15184
15185 pub fn gutter_highlights_in_range(
15186 &self,
15187 search_range: Range<Anchor>,
15188 display_snapshot: &DisplaySnapshot,
15189 cx: &App,
15190 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15191 let mut results = Vec::new();
15192 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15193 let color = color_fetcher(cx);
15194 let start_ix = match ranges.binary_search_by(|probe| {
15195 let cmp = probe
15196 .end
15197 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15198 if cmp.is_gt() {
15199 Ordering::Greater
15200 } else {
15201 Ordering::Less
15202 }
15203 }) {
15204 Ok(i) | Err(i) => i,
15205 };
15206 for range in &ranges[start_ix..] {
15207 if range
15208 .start
15209 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15210 .is_ge()
15211 {
15212 break;
15213 }
15214
15215 let start = range.start.to_display_point(display_snapshot);
15216 let end = range.end.to_display_point(display_snapshot);
15217 results.push((start..end, color))
15218 }
15219 }
15220 results
15221 }
15222
15223 /// Get the text ranges corresponding to the redaction query
15224 pub fn redacted_ranges(
15225 &self,
15226 search_range: Range<Anchor>,
15227 display_snapshot: &DisplaySnapshot,
15228 cx: &App,
15229 ) -> Vec<Range<DisplayPoint>> {
15230 display_snapshot
15231 .buffer_snapshot
15232 .redacted_ranges(search_range, |file| {
15233 if let Some(file) = file {
15234 file.is_private()
15235 && EditorSettings::get(
15236 Some(SettingsLocation {
15237 worktree_id: file.worktree_id(cx),
15238 path: file.path().as_ref(),
15239 }),
15240 cx,
15241 )
15242 .redact_private_values
15243 } else {
15244 false
15245 }
15246 })
15247 .map(|range| {
15248 range.start.to_display_point(display_snapshot)
15249 ..range.end.to_display_point(display_snapshot)
15250 })
15251 .collect()
15252 }
15253
15254 pub fn highlight_text<T: 'static>(
15255 &mut self,
15256 ranges: Vec<Range<Anchor>>,
15257 style: HighlightStyle,
15258 cx: &mut Context<Self>,
15259 ) {
15260 self.display_map.update(cx, |map, _| {
15261 map.highlight_text(TypeId::of::<T>(), ranges, style)
15262 });
15263 cx.notify();
15264 }
15265
15266 pub(crate) fn highlight_inlays<T: 'static>(
15267 &mut self,
15268 highlights: Vec<InlayHighlight>,
15269 style: HighlightStyle,
15270 cx: &mut Context<Self>,
15271 ) {
15272 self.display_map.update(cx, |map, _| {
15273 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15274 });
15275 cx.notify();
15276 }
15277
15278 pub fn text_highlights<'a, T: 'static>(
15279 &'a self,
15280 cx: &'a App,
15281 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15282 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15283 }
15284
15285 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15286 let cleared = self
15287 .display_map
15288 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15289 if cleared {
15290 cx.notify();
15291 }
15292 }
15293
15294 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15295 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15296 && self.focus_handle.is_focused(window)
15297 }
15298
15299 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15300 self.show_cursor_when_unfocused = is_enabled;
15301 cx.notify();
15302 }
15303
15304 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15305 cx.notify();
15306 }
15307
15308 fn on_buffer_event(
15309 &mut self,
15310 multibuffer: &Entity<MultiBuffer>,
15311 event: &multi_buffer::Event,
15312 window: &mut Window,
15313 cx: &mut Context<Self>,
15314 ) {
15315 match event {
15316 multi_buffer::Event::Edited {
15317 singleton_buffer_edited,
15318 edited_buffer: buffer_edited,
15319 } => {
15320 self.scrollbar_marker_state.dirty = true;
15321 self.active_indent_guides_state.dirty = true;
15322 self.refresh_active_diagnostics(cx);
15323 self.refresh_code_actions(window, cx);
15324 if self.has_active_inline_completion() {
15325 self.update_visible_inline_completion(window, cx);
15326 }
15327 if let Some(buffer) = buffer_edited {
15328 let buffer_id = buffer.read(cx).remote_id();
15329 if !self.registered_buffers.contains_key(&buffer_id) {
15330 if let Some(project) = self.project.as_ref() {
15331 project.update(cx, |project, cx| {
15332 self.registered_buffers.insert(
15333 buffer_id,
15334 project.register_buffer_with_language_servers(&buffer, cx),
15335 );
15336 })
15337 }
15338 }
15339 }
15340 cx.emit(EditorEvent::BufferEdited);
15341 cx.emit(SearchEvent::MatchesInvalidated);
15342 if *singleton_buffer_edited {
15343 if let Some(project) = &self.project {
15344 #[allow(clippy::mutable_key_type)]
15345 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15346 multibuffer
15347 .all_buffers()
15348 .into_iter()
15349 .filter_map(|buffer| {
15350 buffer.update(cx, |buffer, cx| {
15351 let language = buffer.language()?;
15352 let should_discard = project.update(cx, |project, cx| {
15353 project.is_local()
15354 && !project.has_language_servers_for(buffer, cx)
15355 });
15356 should_discard.not().then_some(language.clone())
15357 })
15358 })
15359 .collect::<HashSet<_>>()
15360 });
15361 if !languages_affected.is_empty() {
15362 self.refresh_inlay_hints(
15363 InlayHintRefreshReason::BufferEdited(languages_affected),
15364 cx,
15365 );
15366 }
15367 }
15368 }
15369
15370 let Some(project) = &self.project else { return };
15371 let (telemetry, is_via_ssh) = {
15372 let project = project.read(cx);
15373 let telemetry = project.client().telemetry().clone();
15374 let is_via_ssh = project.is_via_ssh();
15375 (telemetry, is_via_ssh)
15376 };
15377 refresh_linked_ranges(self, window, cx);
15378 telemetry.log_edit_event("editor", is_via_ssh);
15379 }
15380 multi_buffer::Event::ExcerptsAdded {
15381 buffer,
15382 predecessor,
15383 excerpts,
15384 } => {
15385 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15386 let buffer_id = buffer.read(cx).remote_id();
15387 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15388 if let Some(project) = &self.project {
15389 get_uncommitted_diff_for_buffer(
15390 project,
15391 [buffer.clone()],
15392 self.buffer.clone(),
15393 cx,
15394 )
15395 .detach();
15396 }
15397 }
15398 cx.emit(EditorEvent::ExcerptsAdded {
15399 buffer: buffer.clone(),
15400 predecessor: *predecessor,
15401 excerpts: excerpts.clone(),
15402 });
15403 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15404 }
15405 multi_buffer::Event::ExcerptsRemoved { ids } => {
15406 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15407 let buffer = self.buffer.read(cx);
15408 self.registered_buffers
15409 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15410 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15411 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15412 }
15413 multi_buffer::Event::ExcerptsEdited {
15414 excerpt_ids,
15415 buffer_ids,
15416 } => {
15417 self.display_map.update(cx, |map, cx| {
15418 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15419 });
15420 cx.emit(EditorEvent::ExcerptsEdited {
15421 ids: excerpt_ids.clone(),
15422 })
15423 }
15424 multi_buffer::Event::ExcerptsExpanded { ids } => {
15425 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15426 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15427 }
15428 multi_buffer::Event::Reparsed(buffer_id) => {
15429 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15430 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15431
15432 cx.emit(EditorEvent::Reparsed(*buffer_id));
15433 }
15434 multi_buffer::Event::DiffHunksToggled => {
15435 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15436 }
15437 multi_buffer::Event::LanguageChanged(buffer_id) => {
15438 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15439 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15440 cx.emit(EditorEvent::Reparsed(*buffer_id));
15441 cx.notify();
15442 }
15443 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15444 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15445 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
15446 cx.emit(EditorEvent::TitleChanged)
15447 }
15448 // multi_buffer::Event::DiffBaseChanged => {
15449 // self.scrollbar_marker_state.dirty = true;
15450 // cx.emit(EditorEvent::DiffBaseChanged);
15451 // cx.notify();
15452 // }
15453 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15454 multi_buffer::Event::DiagnosticsUpdated => {
15455 self.refresh_active_diagnostics(cx);
15456 self.refresh_inline_diagnostics(true, window, cx);
15457 self.scrollbar_marker_state.dirty = true;
15458 cx.notify();
15459 }
15460 _ => {}
15461 };
15462 }
15463
15464 fn on_display_map_changed(
15465 &mut self,
15466 _: Entity<DisplayMap>,
15467 _: &mut Window,
15468 cx: &mut Context<Self>,
15469 ) {
15470 cx.notify();
15471 }
15472
15473 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15474 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15475 self.update_edit_prediction_settings(cx);
15476 self.refresh_inline_completion(true, false, window, cx);
15477 self.refresh_inlay_hints(
15478 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15479 self.selections.newest_anchor().head(),
15480 &self.buffer.read(cx).snapshot(cx),
15481 cx,
15482 )),
15483 cx,
15484 );
15485
15486 let old_cursor_shape = self.cursor_shape;
15487
15488 {
15489 let editor_settings = EditorSettings::get_global(cx);
15490 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15491 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15492 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15493 }
15494
15495 if old_cursor_shape != self.cursor_shape {
15496 cx.emit(EditorEvent::CursorShapeChanged);
15497 }
15498
15499 let project_settings = ProjectSettings::get_global(cx);
15500 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15501
15502 if self.mode == EditorMode::Full {
15503 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15504 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15505 if self.show_inline_diagnostics != show_inline_diagnostics {
15506 self.show_inline_diagnostics = show_inline_diagnostics;
15507 self.refresh_inline_diagnostics(false, window, cx);
15508 }
15509
15510 if self.git_blame_inline_enabled != inline_blame_enabled {
15511 self.toggle_git_blame_inline_internal(false, window, cx);
15512 }
15513 }
15514
15515 cx.notify();
15516 }
15517
15518 pub fn set_searchable(&mut self, searchable: bool) {
15519 self.searchable = searchable;
15520 }
15521
15522 pub fn searchable(&self) -> bool {
15523 self.searchable
15524 }
15525
15526 fn open_proposed_changes_editor(
15527 &mut self,
15528 _: &OpenProposedChangesEditor,
15529 window: &mut Window,
15530 cx: &mut Context<Self>,
15531 ) {
15532 let Some(workspace) = self.workspace() else {
15533 cx.propagate();
15534 return;
15535 };
15536
15537 let selections = self.selections.all::<usize>(cx);
15538 let multi_buffer = self.buffer.read(cx);
15539 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15540 let mut new_selections_by_buffer = HashMap::default();
15541 for selection in selections {
15542 for (buffer, range, _) in
15543 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15544 {
15545 let mut range = range.to_point(buffer);
15546 range.start.column = 0;
15547 range.end.column = buffer.line_len(range.end.row);
15548 new_selections_by_buffer
15549 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15550 .or_insert(Vec::new())
15551 .push(range)
15552 }
15553 }
15554
15555 let proposed_changes_buffers = new_selections_by_buffer
15556 .into_iter()
15557 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15558 .collect::<Vec<_>>();
15559 let proposed_changes_editor = cx.new(|cx| {
15560 ProposedChangesEditor::new(
15561 "Proposed changes",
15562 proposed_changes_buffers,
15563 self.project.clone(),
15564 window,
15565 cx,
15566 )
15567 });
15568
15569 window.defer(cx, move |window, cx| {
15570 workspace.update(cx, |workspace, cx| {
15571 workspace.active_pane().update(cx, |pane, cx| {
15572 pane.add_item(
15573 Box::new(proposed_changes_editor),
15574 true,
15575 true,
15576 None,
15577 window,
15578 cx,
15579 );
15580 });
15581 });
15582 });
15583 }
15584
15585 pub fn open_excerpts_in_split(
15586 &mut self,
15587 _: &OpenExcerptsSplit,
15588 window: &mut Window,
15589 cx: &mut Context<Self>,
15590 ) {
15591 self.open_excerpts_common(None, true, window, cx)
15592 }
15593
15594 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15595 self.open_excerpts_common(None, false, window, cx)
15596 }
15597
15598 fn open_excerpts_common(
15599 &mut self,
15600 jump_data: Option<JumpData>,
15601 split: bool,
15602 window: &mut Window,
15603 cx: &mut Context<Self>,
15604 ) {
15605 let Some(workspace) = self.workspace() else {
15606 cx.propagate();
15607 return;
15608 };
15609
15610 if self.buffer.read(cx).is_singleton() {
15611 cx.propagate();
15612 return;
15613 }
15614
15615 let mut new_selections_by_buffer = HashMap::default();
15616 match &jump_data {
15617 Some(JumpData::MultiBufferPoint {
15618 excerpt_id,
15619 position,
15620 anchor,
15621 line_offset_from_top,
15622 }) => {
15623 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15624 if let Some(buffer) = multi_buffer_snapshot
15625 .buffer_id_for_excerpt(*excerpt_id)
15626 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15627 {
15628 let buffer_snapshot = buffer.read(cx).snapshot();
15629 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15630 language::ToPoint::to_point(anchor, &buffer_snapshot)
15631 } else {
15632 buffer_snapshot.clip_point(*position, Bias::Left)
15633 };
15634 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15635 new_selections_by_buffer.insert(
15636 buffer,
15637 (
15638 vec![jump_to_offset..jump_to_offset],
15639 Some(*line_offset_from_top),
15640 ),
15641 );
15642 }
15643 }
15644 Some(JumpData::MultiBufferRow {
15645 row,
15646 line_offset_from_top,
15647 }) => {
15648 let point = MultiBufferPoint::new(row.0, 0);
15649 if let Some((buffer, buffer_point, _)) =
15650 self.buffer.read(cx).point_to_buffer_point(point, cx)
15651 {
15652 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15653 new_selections_by_buffer
15654 .entry(buffer)
15655 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15656 .0
15657 .push(buffer_offset..buffer_offset)
15658 }
15659 }
15660 None => {
15661 let selections = self.selections.all::<usize>(cx);
15662 let multi_buffer = self.buffer.read(cx);
15663 for selection in selections {
15664 for (snapshot, range, _, anchor) in multi_buffer
15665 .snapshot(cx)
15666 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15667 {
15668 if let Some(anchor) = anchor {
15669 // selection is in a deleted hunk
15670 let Some(buffer_id) = anchor.buffer_id else {
15671 continue;
15672 };
15673 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15674 continue;
15675 };
15676 let offset = text::ToOffset::to_offset(
15677 &anchor.text_anchor,
15678 &buffer_handle.read(cx).snapshot(),
15679 );
15680 let range = offset..offset;
15681 new_selections_by_buffer
15682 .entry(buffer_handle)
15683 .or_insert((Vec::new(), None))
15684 .0
15685 .push(range)
15686 } else {
15687 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15688 else {
15689 continue;
15690 };
15691 new_selections_by_buffer
15692 .entry(buffer_handle)
15693 .or_insert((Vec::new(), None))
15694 .0
15695 .push(range)
15696 }
15697 }
15698 }
15699 }
15700 }
15701
15702 if new_selections_by_buffer.is_empty() {
15703 return;
15704 }
15705
15706 // We defer the pane interaction because we ourselves are a workspace item
15707 // and activating a new item causes the pane to call a method on us reentrantly,
15708 // which panics if we're on the stack.
15709 window.defer(cx, move |window, cx| {
15710 workspace.update(cx, |workspace, cx| {
15711 let pane = if split {
15712 workspace.adjacent_pane(window, cx)
15713 } else {
15714 workspace.active_pane().clone()
15715 };
15716
15717 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15718 let editor = buffer
15719 .read(cx)
15720 .file()
15721 .is_none()
15722 .then(|| {
15723 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15724 // so `workspace.open_project_item` will never find them, always opening a new editor.
15725 // Instead, we try to activate the existing editor in the pane first.
15726 let (editor, pane_item_index) =
15727 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15728 let editor = item.downcast::<Editor>()?;
15729 let singleton_buffer =
15730 editor.read(cx).buffer().read(cx).as_singleton()?;
15731 if singleton_buffer == buffer {
15732 Some((editor, i))
15733 } else {
15734 None
15735 }
15736 })?;
15737 pane.update(cx, |pane, cx| {
15738 pane.activate_item(pane_item_index, true, true, window, cx)
15739 });
15740 Some(editor)
15741 })
15742 .flatten()
15743 .unwrap_or_else(|| {
15744 workspace.open_project_item::<Self>(
15745 pane.clone(),
15746 buffer,
15747 true,
15748 true,
15749 window,
15750 cx,
15751 )
15752 });
15753
15754 editor.update(cx, |editor, cx| {
15755 let autoscroll = match scroll_offset {
15756 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15757 None => Autoscroll::newest(),
15758 };
15759 let nav_history = editor.nav_history.take();
15760 editor.change_selections(Some(autoscroll), window, cx, |s| {
15761 s.select_ranges(ranges);
15762 });
15763 editor.nav_history = nav_history;
15764 });
15765 }
15766 })
15767 });
15768 }
15769
15770 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
15771 let snapshot = self.buffer.read(cx).read(cx);
15772 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
15773 Some(
15774 ranges
15775 .iter()
15776 .map(move |range| {
15777 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
15778 })
15779 .collect(),
15780 )
15781 }
15782
15783 fn selection_replacement_ranges(
15784 &self,
15785 range: Range<OffsetUtf16>,
15786 cx: &mut App,
15787 ) -> Vec<Range<OffsetUtf16>> {
15788 let selections = self.selections.all::<OffsetUtf16>(cx);
15789 let newest_selection = selections
15790 .iter()
15791 .max_by_key(|selection| selection.id)
15792 .unwrap();
15793 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
15794 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
15795 let snapshot = self.buffer.read(cx).read(cx);
15796 selections
15797 .into_iter()
15798 .map(|mut selection| {
15799 selection.start.0 =
15800 (selection.start.0 as isize).saturating_add(start_delta) as usize;
15801 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
15802 snapshot.clip_offset_utf16(selection.start, Bias::Left)
15803 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
15804 })
15805 .collect()
15806 }
15807
15808 fn report_editor_event(
15809 &self,
15810 event_type: &'static str,
15811 file_extension: Option<String>,
15812 cx: &App,
15813 ) {
15814 if cfg!(any(test, feature = "test-support")) {
15815 return;
15816 }
15817
15818 let Some(project) = &self.project else { return };
15819
15820 // If None, we are in a file without an extension
15821 let file = self
15822 .buffer
15823 .read(cx)
15824 .as_singleton()
15825 .and_then(|b| b.read(cx).file());
15826 let file_extension = file_extension.or(file
15827 .as_ref()
15828 .and_then(|file| Path::new(file.file_name(cx)).extension())
15829 .and_then(|e| e.to_str())
15830 .map(|a| a.to_string()));
15831
15832 let vim_mode = cx
15833 .global::<SettingsStore>()
15834 .raw_user_settings()
15835 .get("vim_mode")
15836 == Some(&serde_json::Value::Bool(true));
15837
15838 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
15839 let copilot_enabled = edit_predictions_provider
15840 == language::language_settings::EditPredictionProvider::Copilot;
15841 let copilot_enabled_for_language = self
15842 .buffer
15843 .read(cx)
15844 .language_settings(cx)
15845 .show_edit_predictions;
15846
15847 let project = project.read(cx);
15848 telemetry::event!(
15849 event_type,
15850 file_extension,
15851 vim_mode,
15852 copilot_enabled,
15853 copilot_enabled_for_language,
15854 edit_predictions_provider,
15855 is_via_ssh = project.is_via_ssh(),
15856 );
15857 }
15858
15859 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
15860 /// with each line being an array of {text, highlight} objects.
15861 fn copy_highlight_json(
15862 &mut self,
15863 _: &CopyHighlightJson,
15864 window: &mut Window,
15865 cx: &mut Context<Self>,
15866 ) {
15867 #[derive(Serialize)]
15868 struct Chunk<'a> {
15869 text: String,
15870 highlight: Option<&'a str>,
15871 }
15872
15873 let snapshot = self.buffer.read(cx).snapshot(cx);
15874 let range = self
15875 .selected_text_range(false, window, cx)
15876 .and_then(|selection| {
15877 if selection.range.is_empty() {
15878 None
15879 } else {
15880 Some(selection.range)
15881 }
15882 })
15883 .unwrap_or_else(|| 0..snapshot.len());
15884
15885 let chunks = snapshot.chunks(range, true);
15886 let mut lines = Vec::new();
15887 let mut line: VecDeque<Chunk> = VecDeque::new();
15888
15889 let Some(style) = self.style.as_ref() else {
15890 return;
15891 };
15892
15893 for chunk in chunks {
15894 let highlight = chunk
15895 .syntax_highlight_id
15896 .and_then(|id| id.name(&style.syntax));
15897 let mut chunk_lines = chunk.text.split('\n').peekable();
15898 while let Some(text) = chunk_lines.next() {
15899 let mut merged_with_last_token = false;
15900 if let Some(last_token) = line.back_mut() {
15901 if last_token.highlight == highlight {
15902 last_token.text.push_str(text);
15903 merged_with_last_token = true;
15904 }
15905 }
15906
15907 if !merged_with_last_token {
15908 line.push_back(Chunk {
15909 text: text.into(),
15910 highlight,
15911 });
15912 }
15913
15914 if chunk_lines.peek().is_some() {
15915 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15916 line.pop_front();
15917 }
15918 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15919 line.pop_back();
15920 }
15921
15922 lines.push(mem::take(&mut line));
15923 }
15924 }
15925 }
15926
15927 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15928 return;
15929 };
15930 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15931 }
15932
15933 pub fn open_context_menu(
15934 &mut self,
15935 _: &OpenContextMenu,
15936 window: &mut Window,
15937 cx: &mut Context<Self>,
15938 ) {
15939 self.request_autoscroll(Autoscroll::newest(), cx);
15940 let position = self.selections.newest_display(cx).start;
15941 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15942 }
15943
15944 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15945 &self.inlay_hint_cache
15946 }
15947
15948 pub fn replay_insert_event(
15949 &mut self,
15950 text: &str,
15951 relative_utf16_range: Option<Range<isize>>,
15952 window: &mut Window,
15953 cx: &mut Context<Self>,
15954 ) {
15955 if !self.input_enabled {
15956 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15957 return;
15958 }
15959 if let Some(relative_utf16_range) = relative_utf16_range {
15960 let selections = self.selections.all::<OffsetUtf16>(cx);
15961 self.change_selections(None, window, cx, |s| {
15962 let new_ranges = selections.into_iter().map(|range| {
15963 let start = OffsetUtf16(
15964 range
15965 .head()
15966 .0
15967 .saturating_add_signed(relative_utf16_range.start),
15968 );
15969 let end = OffsetUtf16(
15970 range
15971 .head()
15972 .0
15973 .saturating_add_signed(relative_utf16_range.end),
15974 );
15975 start..end
15976 });
15977 s.select_ranges(new_ranges);
15978 });
15979 }
15980
15981 self.handle_input(text, window, cx);
15982 }
15983
15984 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15985 let Some(provider) = self.semantics_provider.as_ref() else {
15986 return false;
15987 };
15988
15989 let mut supports = false;
15990 self.buffer().update(cx, |this, cx| {
15991 this.for_each_buffer(|buffer| {
15992 supports |= provider.supports_inlay_hints(buffer, cx);
15993 });
15994 });
15995
15996 supports
15997 }
15998
15999 pub fn is_focused(&self, window: &Window) -> bool {
16000 self.focus_handle.is_focused(window)
16001 }
16002
16003 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16004 cx.emit(EditorEvent::Focused);
16005
16006 if let Some(descendant) = self
16007 .last_focused_descendant
16008 .take()
16009 .and_then(|descendant| descendant.upgrade())
16010 {
16011 window.focus(&descendant);
16012 } else {
16013 if let Some(blame) = self.blame.as_ref() {
16014 blame.update(cx, GitBlame::focus)
16015 }
16016
16017 self.blink_manager.update(cx, BlinkManager::enable);
16018 self.show_cursor_names(window, cx);
16019 self.buffer.update(cx, |buffer, cx| {
16020 buffer.finalize_last_transaction(cx);
16021 if self.leader_peer_id.is_none() {
16022 buffer.set_active_selections(
16023 &self.selections.disjoint_anchors(),
16024 self.selections.line_mode,
16025 self.cursor_shape,
16026 cx,
16027 );
16028 }
16029 });
16030 }
16031 }
16032
16033 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16034 cx.emit(EditorEvent::FocusedIn)
16035 }
16036
16037 fn handle_focus_out(
16038 &mut self,
16039 event: FocusOutEvent,
16040 _window: &mut Window,
16041 cx: &mut Context<Self>,
16042 ) {
16043 if event.blurred != self.focus_handle {
16044 self.last_focused_descendant = Some(event.blurred);
16045 }
16046 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16047 }
16048
16049 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16050 self.blink_manager.update(cx, BlinkManager::disable);
16051 self.buffer
16052 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16053
16054 if let Some(blame) = self.blame.as_ref() {
16055 blame.update(cx, GitBlame::blur)
16056 }
16057 if !self.hover_state.focused(window, cx) {
16058 hide_hover(self, cx);
16059 }
16060 if !self
16061 .context_menu
16062 .borrow()
16063 .as_ref()
16064 .is_some_and(|context_menu| context_menu.focused(window, cx))
16065 {
16066 self.hide_context_menu(window, cx);
16067 }
16068 self.discard_inline_completion(false, cx);
16069 cx.emit(EditorEvent::Blurred);
16070 cx.notify();
16071 }
16072
16073 pub fn register_action<A: Action>(
16074 &mut self,
16075 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16076 ) -> Subscription {
16077 let id = self.next_editor_action_id.post_inc();
16078 let listener = Arc::new(listener);
16079 self.editor_actions.borrow_mut().insert(
16080 id,
16081 Box::new(move |window, _| {
16082 let listener = listener.clone();
16083 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16084 let action = action.downcast_ref().unwrap();
16085 if phase == DispatchPhase::Bubble {
16086 listener(action, window, cx)
16087 }
16088 })
16089 }),
16090 );
16091
16092 let editor_actions = self.editor_actions.clone();
16093 Subscription::new(move || {
16094 editor_actions.borrow_mut().remove(&id);
16095 })
16096 }
16097
16098 pub fn file_header_size(&self) -> u32 {
16099 FILE_HEADER_HEIGHT
16100 }
16101
16102 pub fn restore(
16103 &mut self,
16104 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16105 window: &mut Window,
16106 cx: &mut Context<Self>,
16107 ) {
16108 let workspace = self.workspace();
16109 let project = self.project.as_ref();
16110 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16111 let mut tasks = Vec::new();
16112 for (buffer_id, changes) in revert_changes {
16113 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16114 buffer.update(cx, |buffer, cx| {
16115 buffer.edit(
16116 changes
16117 .into_iter()
16118 .map(|(range, text)| (range, text.to_string())),
16119 None,
16120 cx,
16121 );
16122 });
16123
16124 if let Some(project) =
16125 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16126 {
16127 project.update(cx, |project, cx| {
16128 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16129 })
16130 }
16131 }
16132 }
16133 tasks
16134 });
16135 cx.spawn_in(window, |_, mut cx| async move {
16136 for (buffer, task) in save_tasks {
16137 let result = task.await;
16138 if result.is_err() {
16139 let Some(path) = buffer
16140 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
16141 .ok()
16142 else {
16143 continue;
16144 };
16145 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
16146 let Some(task) = cx
16147 .update_window_entity(&workspace, |workspace, window, cx| {
16148 workspace
16149 .open_path_preview(path, None, false, false, false, window, cx)
16150 })
16151 .ok()
16152 else {
16153 continue;
16154 };
16155 task.await.log_err();
16156 }
16157 }
16158 }
16159 })
16160 .detach();
16161 self.change_selections(None, window, cx, |selections| selections.refresh());
16162 }
16163
16164 pub fn to_pixel_point(
16165 &self,
16166 source: multi_buffer::Anchor,
16167 editor_snapshot: &EditorSnapshot,
16168 window: &mut Window,
16169 ) -> Option<gpui::Point<Pixels>> {
16170 let source_point = source.to_display_point(editor_snapshot);
16171 self.display_to_pixel_point(source_point, editor_snapshot, window)
16172 }
16173
16174 pub fn display_to_pixel_point(
16175 &self,
16176 source: DisplayPoint,
16177 editor_snapshot: &EditorSnapshot,
16178 window: &mut Window,
16179 ) -> Option<gpui::Point<Pixels>> {
16180 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16181 let text_layout_details = self.text_layout_details(window);
16182 let scroll_top = text_layout_details
16183 .scroll_anchor
16184 .scroll_position(editor_snapshot)
16185 .y;
16186
16187 if source.row().as_f32() < scroll_top.floor() {
16188 return None;
16189 }
16190 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16191 let source_y = line_height * (source.row().as_f32() - scroll_top);
16192 Some(gpui::Point::new(source_x, source_y))
16193 }
16194
16195 pub fn has_visible_completions_menu(&self) -> bool {
16196 !self.edit_prediction_preview_is_active()
16197 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16198 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16199 })
16200 }
16201
16202 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16203 self.addons
16204 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16205 }
16206
16207 pub fn unregister_addon<T: Addon>(&mut self) {
16208 self.addons.remove(&std::any::TypeId::of::<T>());
16209 }
16210
16211 pub fn addon<T: Addon>(&self) -> Option<&T> {
16212 let type_id = std::any::TypeId::of::<T>();
16213 self.addons
16214 .get(&type_id)
16215 .and_then(|item| item.to_any().downcast_ref::<T>())
16216 }
16217
16218 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16219 let text_layout_details = self.text_layout_details(window);
16220 let style = &text_layout_details.editor_style;
16221 let font_id = window.text_system().resolve_font(&style.text.font());
16222 let font_size = style.text.font_size.to_pixels(window.rem_size());
16223 let line_height = style.text.line_height_in_pixels(window.rem_size());
16224 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16225
16226 gpui::Size::new(em_width, line_height)
16227 }
16228
16229 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16230 self.load_diff_task.clone()
16231 }
16232
16233 fn read_selections_from_db(
16234 &mut self,
16235 item_id: u64,
16236 workspace_id: WorkspaceId,
16237 window: &mut Window,
16238 cx: &mut Context<Editor>,
16239 ) {
16240 if !self.is_singleton(cx)
16241 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16242 {
16243 return;
16244 }
16245 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16246 return;
16247 };
16248 if selections.is_empty() {
16249 return;
16250 }
16251
16252 let snapshot = self.buffer.read(cx).snapshot(cx);
16253 self.change_selections(None, window, cx, |s| {
16254 s.select_ranges(selections.into_iter().map(|(start, end)| {
16255 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16256 }));
16257 });
16258 }
16259}
16260
16261fn insert_extra_newline_brackets(
16262 buffer: &MultiBufferSnapshot,
16263 range: Range<usize>,
16264 language: &language::LanguageScope,
16265) -> bool {
16266 let leading_whitespace_len = buffer
16267 .reversed_chars_at(range.start)
16268 .take_while(|c| c.is_whitespace() && *c != '\n')
16269 .map(|c| c.len_utf8())
16270 .sum::<usize>();
16271 let trailing_whitespace_len = buffer
16272 .chars_at(range.end)
16273 .take_while(|c| c.is_whitespace() && *c != '\n')
16274 .map(|c| c.len_utf8())
16275 .sum::<usize>();
16276 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16277
16278 language.brackets().any(|(pair, enabled)| {
16279 let pair_start = pair.start.trim_end();
16280 let pair_end = pair.end.trim_start();
16281
16282 enabled
16283 && pair.newline
16284 && buffer.contains_str_at(range.end, pair_end)
16285 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16286 })
16287}
16288
16289fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16290 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16291 [(buffer, range, _)] => (*buffer, range.clone()),
16292 _ => return false,
16293 };
16294 let pair = {
16295 let mut result: Option<BracketMatch> = None;
16296
16297 for pair in buffer
16298 .all_bracket_ranges(range.clone())
16299 .filter(move |pair| {
16300 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16301 })
16302 {
16303 let len = pair.close_range.end - pair.open_range.start;
16304
16305 if let Some(existing) = &result {
16306 let existing_len = existing.close_range.end - existing.open_range.start;
16307 if len > existing_len {
16308 continue;
16309 }
16310 }
16311
16312 result = Some(pair);
16313 }
16314
16315 result
16316 };
16317 let Some(pair) = pair else {
16318 return false;
16319 };
16320 pair.newline_only
16321 && buffer
16322 .chars_for_range(pair.open_range.end..range.start)
16323 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16324 .all(|c| c.is_whitespace() && c != '\n')
16325}
16326
16327fn get_uncommitted_diff_for_buffer(
16328 project: &Entity<Project>,
16329 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16330 buffer: Entity<MultiBuffer>,
16331 cx: &mut App,
16332) -> Task<()> {
16333 let mut tasks = Vec::new();
16334 project.update(cx, |project, cx| {
16335 for buffer in buffers {
16336 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16337 }
16338 });
16339 cx.spawn(|mut cx| async move {
16340 let diffs = future::join_all(tasks).await;
16341 buffer
16342 .update(&mut cx, |buffer, cx| {
16343 for diff in diffs.into_iter().flatten() {
16344 buffer.add_diff(diff, cx);
16345 }
16346 })
16347 .ok();
16348 })
16349}
16350
16351fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16352 let tab_size = tab_size.get() as usize;
16353 let mut width = offset;
16354
16355 for ch in text.chars() {
16356 width += if ch == '\t' {
16357 tab_size - (width % tab_size)
16358 } else {
16359 1
16360 };
16361 }
16362
16363 width - offset
16364}
16365
16366#[cfg(test)]
16367mod tests {
16368 use super::*;
16369
16370 #[test]
16371 fn test_string_size_with_expanded_tabs() {
16372 let nz = |val| NonZeroU32::new(val).unwrap();
16373 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16374 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16375 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16376 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16377 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16378 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16379 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16380 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16381 }
16382}
16383
16384/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16385struct WordBreakingTokenizer<'a> {
16386 input: &'a str,
16387}
16388
16389impl<'a> WordBreakingTokenizer<'a> {
16390 fn new(input: &'a str) -> Self {
16391 Self { input }
16392 }
16393}
16394
16395fn is_char_ideographic(ch: char) -> bool {
16396 use unicode_script::Script::*;
16397 use unicode_script::UnicodeScript;
16398 matches!(ch.script(), Han | Tangut | Yi)
16399}
16400
16401fn is_grapheme_ideographic(text: &str) -> bool {
16402 text.chars().any(is_char_ideographic)
16403}
16404
16405fn is_grapheme_whitespace(text: &str) -> bool {
16406 text.chars().any(|x| x.is_whitespace())
16407}
16408
16409fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16410 text.chars().next().map_or(false, |ch| {
16411 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16412 })
16413}
16414
16415#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16416struct WordBreakToken<'a> {
16417 token: &'a str,
16418 grapheme_len: usize,
16419 is_whitespace: bool,
16420}
16421
16422impl<'a> Iterator for WordBreakingTokenizer<'a> {
16423 /// Yields a span, the count of graphemes in the token, and whether it was
16424 /// whitespace. Note that it also breaks at word boundaries.
16425 type Item = WordBreakToken<'a>;
16426
16427 fn next(&mut self) -> Option<Self::Item> {
16428 use unicode_segmentation::UnicodeSegmentation;
16429 if self.input.is_empty() {
16430 return None;
16431 }
16432
16433 let mut iter = self.input.graphemes(true).peekable();
16434 let mut offset = 0;
16435 let mut graphemes = 0;
16436 if let Some(first_grapheme) = iter.next() {
16437 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16438 offset += first_grapheme.len();
16439 graphemes += 1;
16440 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16441 if let Some(grapheme) = iter.peek().copied() {
16442 if should_stay_with_preceding_ideograph(grapheme) {
16443 offset += grapheme.len();
16444 graphemes += 1;
16445 }
16446 }
16447 } else {
16448 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16449 let mut next_word_bound = words.peek().copied();
16450 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16451 next_word_bound = words.next();
16452 }
16453 while let Some(grapheme) = iter.peek().copied() {
16454 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16455 break;
16456 };
16457 if is_grapheme_whitespace(grapheme) != is_whitespace {
16458 break;
16459 };
16460 offset += grapheme.len();
16461 graphemes += 1;
16462 iter.next();
16463 }
16464 }
16465 let token = &self.input[..offset];
16466 self.input = &self.input[offset..];
16467 if is_whitespace {
16468 Some(WordBreakToken {
16469 token: " ",
16470 grapheme_len: 1,
16471 is_whitespace: true,
16472 })
16473 } else {
16474 Some(WordBreakToken {
16475 token,
16476 grapheme_len: graphemes,
16477 is_whitespace: false,
16478 })
16479 }
16480 } else {
16481 None
16482 }
16483 }
16484}
16485
16486#[test]
16487fn test_word_breaking_tokenizer() {
16488 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16489 ("", &[]),
16490 (" ", &[(" ", 1, true)]),
16491 ("Ʒ", &[("Ʒ", 1, false)]),
16492 ("Ǽ", &[("Ǽ", 1, false)]),
16493 ("⋑", &[("⋑", 1, false)]),
16494 ("⋑⋑", &[("⋑⋑", 2, false)]),
16495 (
16496 "原理,进而",
16497 &[
16498 ("原", 1, false),
16499 ("理,", 2, false),
16500 ("进", 1, false),
16501 ("而", 1, false),
16502 ],
16503 ),
16504 (
16505 "hello world",
16506 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16507 ),
16508 (
16509 "hello, world",
16510 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16511 ),
16512 (
16513 " hello world",
16514 &[
16515 (" ", 1, true),
16516 ("hello", 5, false),
16517 (" ", 1, true),
16518 ("world", 5, false),
16519 ],
16520 ),
16521 (
16522 "这是什么 \n 钢笔",
16523 &[
16524 ("这", 1, false),
16525 ("是", 1, false),
16526 ("什", 1, false),
16527 ("么", 1, false),
16528 (" ", 1, true),
16529 ("钢", 1, false),
16530 ("笔", 1, false),
16531 ],
16532 ),
16533 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16534 ];
16535
16536 for (input, result) in tests {
16537 assert_eq!(
16538 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16539 result
16540 .iter()
16541 .copied()
16542 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16543 token,
16544 grapheme_len,
16545 is_whitespace,
16546 })
16547 .collect::<Vec<_>>()
16548 );
16549 }
16550}
16551
16552fn wrap_with_prefix(
16553 line_prefix: String,
16554 unwrapped_text: String,
16555 wrap_column: usize,
16556 tab_size: NonZeroU32,
16557) -> String {
16558 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16559 let mut wrapped_text = String::new();
16560 let mut current_line = line_prefix.clone();
16561
16562 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16563 let mut current_line_len = line_prefix_len;
16564 for WordBreakToken {
16565 token,
16566 grapheme_len,
16567 is_whitespace,
16568 } in tokenizer
16569 {
16570 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16571 wrapped_text.push_str(current_line.trim_end());
16572 wrapped_text.push('\n');
16573 current_line.truncate(line_prefix.len());
16574 current_line_len = line_prefix_len;
16575 if !is_whitespace {
16576 current_line.push_str(token);
16577 current_line_len += grapheme_len;
16578 }
16579 } else if !is_whitespace {
16580 current_line.push_str(token);
16581 current_line_len += grapheme_len;
16582 } else if current_line_len != line_prefix_len {
16583 current_line.push(' ');
16584 current_line_len += 1;
16585 }
16586 }
16587
16588 if !current_line.is_empty() {
16589 wrapped_text.push_str(¤t_line);
16590 }
16591 wrapped_text
16592}
16593
16594#[test]
16595fn test_wrap_with_prefix() {
16596 assert_eq!(
16597 wrap_with_prefix(
16598 "# ".to_string(),
16599 "abcdefg".to_string(),
16600 4,
16601 NonZeroU32::new(4).unwrap()
16602 ),
16603 "# abcdefg"
16604 );
16605 assert_eq!(
16606 wrap_with_prefix(
16607 "".to_string(),
16608 "\thello world".to_string(),
16609 8,
16610 NonZeroU32::new(4).unwrap()
16611 ),
16612 "hello\nworld"
16613 );
16614 assert_eq!(
16615 wrap_with_prefix(
16616 "// ".to_string(),
16617 "xx \nyy zz aa bb cc".to_string(),
16618 12,
16619 NonZeroU32::new(4).unwrap()
16620 ),
16621 "// xx yy zz\n// aa bb cc"
16622 );
16623 assert_eq!(
16624 wrap_with_prefix(
16625 String::new(),
16626 "这是什么 \n 钢笔".to_string(),
16627 3,
16628 NonZeroU32::new(4).unwrap()
16629 ),
16630 "这是什\n么 钢\n笔"
16631 );
16632}
16633
16634pub trait CollaborationHub {
16635 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16636 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16637 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16638}
16639
16640impl CollaborationHub for Entity<Project> {
16641 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16642 self.read(cx).collaborators()
16643 }
16644
16645 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16646 self.read(cx).user_store().read(cx).participant_indices()
16647 }
16648
16649 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16650 let this = self.read(cx);
16651 let user_ids = this.collaborators().values().map(|c| c.user_id);
16652 this.user_store().read_with(cx, |user_store, cx| {
16653 user_store.participant_names(user_ids, cx)
16654 })
16655 }
16656}
16657
16658pub trait SemanticsProvider {
16659 fn hover(
16660 &self,
16661 buffer: &Entity<Buffer>,
16662 position: text::Anchor,
16663 cx: &mut App,
16664 ) -> Option<Task<Vec<project::Hover>>>;
16665
16666 fn inlay_hints(
16667 &self,
16668 buffer_handle: Entity<Buffer>,
16669 range: Range<text::Anchor>,
16670 cx: &mut App,
16671 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16672
16673 fn resolve_inlay_hint(
16674 &self,
16675 hint: InlayHint,
16676 buffer_handle: Entity<Buffer>,
16677 server_id: LanguageServerId,
16678 cx: &mut App,
16679 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16680
16681 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16682
16683 fn document_highlights(
16684 &self,
16685 buffer: &Entity<Buffer>,
16686 position: text::Anchor,
16687 cx: &mut App,
16688 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16689
16690 fn definitions(
16691 &self,
16692 buffer: &Entity<Buffer>,
16693 position: text::Anchor,
16694 kind: GotoDefinitionKind,
16695 cx: &mut App,
16696 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16697
16698 fn range_for_rename(
16699 &self,
16700 buffer: &Entity<Buffer>,
16701 position: text::Anchor,
16702 cx: &mut App,
16703 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16704
16705 fn perform_rename(
16706 &self,
16707 buffer: &Entity<Buffer>,
16708 position: text::Anchor,
16709 new_name: String,
16710 cx: &mut App,
16711 ) -> Option<Task<Result<ProjectTransaction>>>;
16712}
16713
16714pub trait CompletionProvider {
16715 fn completions(
16716 &self,
16717 buffer: &Entity<Buffer>,
16718 buffer_position: text::Anchor,
16719 trigger: CompletionContext,
16720 window: &mut Window,
16721 cx: &mut Context<Editor>,
16722 ) -> Task<Result<Vec<Completion>>>;
16723
16724 fn resolve_completions(
16725 &self,
16726 buffer: Entity<Buffer>,
16727 completion_indices: Vec<usize>,
16728 completions: Rc<RefCell<Box<[Completion]>>>,
16729 cx: &mut Context<Editor>,
16730 ) -> Task<Result<bool>>;
16731
16732 fn apply_additional_edits_for_completion(
16733 &self,
16734 _buffer: Entity<Buffer>,
16735 _completions: Rc<RefCell<Box<[Completion]>>>,
16736 _completion_index: usize,
16737 _push_to_history: bool,
16738 _cx: &mut Context<Editor>,
16739 ) -> Task<Result<Option<language::Transaction>>> {
16740 Task::ready(Ok(None))
16741 }
16742
16743 fn is_completion_trigger(
16744 &self,
16745 buffer: &Entity<Buffer>,
16746 position: language::Anchor,
16747 text: &str,
16748 trigger_in_words: bool,
16749 cx: &mut Context<Editor>,
16750 ) -> bool;
16751
16752 fn sort_completions(&self) -> bool {
16753 true
16754 }
16755}
16756
16757pub trait CodeActionProvider {
16758 fn id(&self) -> Arc<str>;
16759
16760 fn code_actions(
16761 &self,
16762 buffer: &Entity<Buffer>,
16763 range: Range<text::Anchor>,
16764 window: &mut Window,
16765 cx: &mut App,
16766 ) -> Task<Result<Vec<CodeAction>>>;
16767
16768 fn apply_code_action(
16769 &self,
16770 buffer_handle: Entity<Buffer>,
16771 action: CodeAction,
16772 excerpt_id: ExcerptId,
16773 push_to_history: bool,
16774 window: &mut Window,
16775 cx: &mut App,
16776 ) -> Task<Result<ProjectTransaction>>;
16777}
16778
16779impl CodeActionProvider for Entity<Project> {
16780 fn id(&self) -> Arc<str> {
16781 "project".into()
16782 }
16783
16784 fn code_actions(
16785 &self,
16786 buffer: &Entity<Buffer>,
16787 range: Range<text::Anchor>,
16788 _window: &mut Window,
16789 cx: &mut App,
16790 ) -> Task<Result<Vec<CodeAction>>> {
16791 self.update(cx, |project, cx| {
16792 project.code_actions(buffer, range, None, cx)
16793 })
16794 }
16795
16796 fn apply_code_action(
16797 &self,
16798 buffer_handle: Entity<Buffer>,
16799 action: CodeAction,
16800 _excerpt_id: ExcerptId,
16801 push_to_history: bool,
16802 _window: &mut Window,
16803 cx: &mut App,
16804 ) -> Task<Result<ProjectTransaction>> {
16805 self.update(cx, |project, cx| {
16806 project.apply_code_action(buffer_handle, action, push_to_history, cx)
16807 })
16808 }
16809}
16810
16811fn snippet_completions(
16812 project: &Project,
16813 buffer: &Entity<Buffer>,
16814 buffer_position: text::Anchor,
16815 cx: &mut App,
16816) -> Task<Result<Vec<Completion>>> {
16817 let language = buffer.read(cx).language_at(buffer_position);
16818 let language_name = language.as_ref().map(|language| language.lsp_id());
16819 let snippet_store = project.snippets().read(cx);
16820 let snippets = snippet_store.snippets_for(language_name, cx);
16821
16822 if snippets.is_empty() {
16823 return Task::ready(Ok(vec![]));
16824 }
16825 let snapshot = buffer.read(cx).text_snapshot();
16826 let chars: String = snapshot
16827 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
16828 .collect();
16829
16830 let scope = language.map(|language| language.default_scope());
16831 let executor = cx.background_executor().clone();
16832
16833 cx.background_spawn(async move {
16834 let classifier = CharClassifier::new(scope).for_completion(true);
16835 let mut last_word = chars
16836 .chars()
16837 .take_while(|c| classifier.is_word(*c))
16838 .collect::<String>();
16839 last_word = last_word.chars().rev().collect();
16840
16841 if last_word.is_empty() {
16842 return Ok(vec![]);
16843 }
16844
16845 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
16846 let to_lsp = |point: &text::Anchor| {
16847 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
16848 point_to_lsp(end)
16849 };
16850 let lsp_end = to_lsp(&buffer_position);
16851
16852 let candidates = snippets
16853 .iter()
16854 .enumerate()
16855 .flat_map(|(ix, snippet)| {
16856 snippet
16857 .prefix
16858 .iter()
16859 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
16860 })
16861 .collect::<Vec<StringMatchCandidate>>();
16862
16863 let mut matches = fuzzy::match_strings(
16864 &candidates,
16865 &last_word,
16866 last_word.chars().any(|c| c.is_uppercase()),
16867 100,
16868 &Default::default(),
16869 executor,
16870 )
16871 .await;
16872
16873 // Remove all candidates where the query's start does not match the start of any word in the candidate
16874 if let Some(query_start) = last_word.chars().next() {
16875 matches.retain(|string_match| {
16876 split_words(&string_match.string).any(|word| {
16877 // Check that the first codepoint of the word as lowercase matches the first
16878 // codepoint of the query as lowercase
16879 word.chars()
16880 .flat_map(|codepoint| codepoint.to_lowercase())
16881 .zip(query_start.to_lowercase())
16882 .all(|(word_cp, query_cp)| word_cp == query_cp)
16883 })
16884 });
16885 }
16886
16887 let matched_strings = matches
16888 .into_iter()
16889 .map(|m| m.string)
16890 .collect::<HashSet<_>>();
16891
16892 let result: Vec<Completion> = snippets
16893 .into_iter()
16894 .filter_map(|snippet| {
16895 let matching_prefix = snippet
16896 .prefix
16897 .iter()
16898 .find(|prefix| matched_strings.contains(*prefix))?;
16899 let start = as_offset - last_word.len();
16900 let start = snapshot.anchor_before(start);
16901 let range = start..buffer_position;
16902 let lsp_start = to_lsp(&start);
16903 let lsp_range = lsp::Range {
16904 start: lsp_start,
16905 end: lsp_end,
16906 };
16907 Some(Completion {
16908 old_range: range,
16909 new_text: snippet.body.clone(),
16910 resolved: false,
16911 label: CodeLabel {
16912 text: matching_prefix.clone(),
16913 runs: vec![],
16914 filter_range: 0..matching_prefix.len(),
16915 },
16916 server_id: LanguageServerId(usize::MAX),
16917 documentation: snippet
16918 .description
16919 .clone()
16920 .map(|description| CompletionDocumentation::SingleLine(description.into())),
16921 lsp_completion: lsp::CompletionItem {
16922 label: snippet.prefix.first().unwrap().clone(),
16923 kind: Some(CompletionItemKind::SNIPPET),
16924 label_details: snippet.description.as_ref().map(|description| {
16925 lsp::CompletionItemLabelDetails {
16926 detail: Some(description.clone()),
16927 description: None,
16928 }
16929 }),
16930 insert_text_format: Some(InsertTextFormat::SNIPPET),
16931 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
16932 lsp::InsertReplaceEdit {
16933 new_text: snippet.body.clone(),
16934 insert: lsp_range,
16935 replace: lsp_range,
16936 },
16937 )),
16938 filter_text: Some(snippet.body.clone()),
16939 sort_text: Some(char::MAX.to_string()),
16940 ..Default::default()
16941 },
16942 confirm: None,
16943 })
16944 })
16945 .collect();
16946
16947 Ok(result)
16948 })
16949}
16950
16951impl CompletionProvider for Entity<Project> {
16952 fn completions(
16953 &self,
16954 buffer: &Entity<Buffer>,
16955 buffer_position: text::Anchor,
16956 options: CompletionContext,
16957 _window: &mut Window,
16958 cx: &mut Context<Editor>,
16959 ) -> Task<Result<Vec<Completion>>> {
16960 self.update(cx, |project, cx| {
16961 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16962 let project_completions = project.completions(buffer, buffer_position, options, cx);
16963 cx.background_spawn(async move {
16964 let mut completions = project_completions.await?;
16965 let snippets_completions = snippets.await?;
16966 completions.extend(snippets_completions);
16967 Ok(completions)
16968 })
16969 })
16970 }
16971
16972 fn resolve_completions(
16973 &self,
16974 buffer: Entity<Buffer>,
16975 completion_indices: Vec<usize>,
16976 completions: Rc<RefCell<Box<[Completion]>>>,
16977 cx: &mut Context<Editor>,
16978 ) -> Task<Result<bool>> {
16979 self.update(cx, |project, cx| {
16980 project.lsp_store().update(cx, |lsp_store, cx| {
16981 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16982 })
16983 })
16984 }
16985
16986 fn apply_additional_edits_for_completion(
16987 &self,
16988 buffer: Entity<Buffer>,
16989 completions: Rc<RefCell<Box<[Completion]>>>,
16990 completion_index: usize,
16991 push_to_history: bool,
16992 cx: &mut Context<Editor>,
16993 ) -> Task<Result<Option<language::Transaction>>> {
16994 self.update(cx, |project, cx| {
16995 project.lsp_store().update(cx, |lsp_store, cx| {
16996 lsp_store.apply_additional_edits_for_completion(
16997 buffer,
16998 completions,
16999 completion_index,
17000 push_to_history,
17001 cx,
17002 )
17003 })
17004 })
17005 }
17006
17007 fn is_completion_trigger(
17008 &self,
17009 buffer: &Entity<Buffer>,
17010 position: language::Anchor,
17011 text: &str,
17012 trigger_in_words: bool,
17013 cx: &mut Context<Editor>,
17014 ) -> bool {
17015 let mut chars = text.chars();
17016 let char = if let Some(char) = chars.next() {
17017 char
17018 } else {
17019 return false;
17020 };
17021 if chars.next().is_some() {
17022 return false;
17023 }
17024
17025 let buffer = buffer.read(cx);
17026 let snapshot = buffer.snapshot();
17027 if !snapshot.settings_at(position, cx).show_completions_on_input {
17028 return false;
17029 }
17030 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17031 if trigger_in_words && classifier.is_word(char) {
17032 return true;
17033 }
17034
17035 buffer.completion_triggers().contains(text)
17036 }
17037}
17038
17039impl SemanticsProvider for Entity<Project> {
17040 fn hover(
17041 &self,
17042 buffer: &Entity<Buffer>,
17043 position: text::Anchor,
17044 cx: &mut App,
17045 ) -> Option<Task<Vec<project::Hover>>> {
17046 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17047 }
17048
17049 fn document_highlights(
17050 &self,
17051 buffer: &Entity<Buffer>,
17052 position: text::Anchor,
17053 cx: &mut App,
17054 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
17055 Some(self.update(cx, |project, cx| {
17056 project.document_highlights(buffer, position, cx)
17057 }))
17058 }
17059
17060 fn definitions(
17061 &self,
17062 buffer: &Entity<Buffer>,
17063 position: text::Anchor,
17064 kind: GotoDefinitionKind,
17065 cx: &mut App,
17066 ) -> Option<Task<Result<Vec<LocationLink>>>> {
17067 Some(self.update(cx, |project, cx| match kind {
17068 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
17069 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
17070 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
17071 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
17072 }))
17073 }
17074
17075 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
17076 // TODO: make this work for remote projects
17077 self.update(cx, |this, cx| {
17078 buffer.update(cx, |buffer, cx| {
17079 this.any_language_server_supports_inlay_hints(buffer, cx)
17080 })
17081 })
17082 }
17083
17084 fn inlay_hints(
17085 &self,
17086 buffer_handle: Entity<Buffer>,
17087 range: Range<text::Anchor>,
17088 cx: &mut App,
17089 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
17090 Some(self.update(cx, |project, cx| {
17091 project.inlay_hints(buffer_handle, range, cx)
17092 }))
17093 }
17094
17095 fn resolve_inlay_hint(
17096 &self,
17097 hint: InlayHint,
17098 buffer_handle: Entity<Buffer>,
17099 server_id: LanguageServerId,
17100 cx: &mut App,
17101 ) -> Option<Task<anyhow::Result<InlayHint>>> {
17102 Some(self.update(cx, |project, cx| {
17103 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
17104 }))
17105 }
17106
17107 fn range_for_rename(
17108 &self,
17109 buffer: &Entity<Buffer>,
17110 position: text::Anchor,
17111 cx: &mut App,
17112 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
17113 Some(self.update(cx, |project, cx| {
17114 let buffer = buffer.clone();
17115 let task = project.prepare_rename(buffer.clone(), position, cx);
17116 cx.spawn(|_, mut cx| async move {
17117 Ok(match task.await? {
17118 PrepareRenameResponse::Success(range) => Some(range),
17119 PrepareRenameResponse::InvalidPosition => None,
17120 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
17121 // Fallback on using TreeSitter info to determine identifier range
17122 buffer.update(&mut cx, |buffer, _| {
17123 let snapshot = buffer.snapshot();
17124 let (range, kind) = snapshot.surrounding_word(position);
17125 if kind != Some(CharKind::Word) {
17126 return None;
17127 }
17128 Some(
17129 snapshot.anchor_before(range.start)
17130 ..snapshot.anchor_after(range.end),
17131 )
17132 })?
17133 }
17134 })
17135 })
17136 }))
17137 }
17138
17139 fn perform_rename(
17140 &self,
17141 buffer: &Entity<Buffer>,
17142 position: text::Anchor,
17143 new_name: String,
17144 cx: &mut App,
17145 ) -> Option<Task<Result<ProjectTransaction>>> {
17146 Some(self.update(cx, |project, cx| {
17147 project.perform_rename(buffer.clone(), position, new_name, cx)
17148 }))
17149 }
17150}
17151
17152fn inlay_hint_settings(
17153 location: Anchor,
17154 snapshot: &MultiBufferSnapshot,
17155 cx: &mut Context<Editor>,
17156) -> InlayHintSettings {
17157 let file = snapshot.file_at(location);
17158 let language = snapshot.language_at(location).map(|l| l.name());
17159 language_settings(language, file, cx).inlay_hints
17160}
17161
17162fn consume_contiguous_rows(
17163 contiguous_row_selections: &mut Vec<Selection<Point>>,
17164 selection: &Selection<Point>,
17165 display_map: &DisplaySnapshot,
17166 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
17167) -> (MultiBufferRow, MultiBufferRow) {
17168 contiguous_row_selections.push(selection.clone());
17169 let start_row = MultiBufferRow(selection.start.row);
17170 let mut end_row = ending_row(selection, display_map);
17171
17172 while let Some(next_selection) = selections.peek() {
17173 if next_selection.start.row <= end_row.0 {
17174 end_row = ending_row(next_selection, display_map);
17175 contiguous_row_selections.push(selections.next().unwrap().clone());
17176 } else {
17177 break;
17178 }
17179 }
17180 (start_row, end_row)
17181}
17182
17183fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17184 if next_selection.end.column > 0 || next_selection.is_empty() {
17185 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17186 } else {
17187 MultiBufferRow(next_selection.end.row)
17188 }
17189}
17190
17191impl EditorSnapshot {
17192 pub fn remote_selections_in_range<'a>(
17193 &'a self,
17194 range: &'a Range<Anchor>,
17195 collaboration_hub: &dyn CollaborationHub,
17196 cx: &'a App,
17197 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17198 let participant_names = collaboration_hub.user_names(cx);
17199 let participant_indices = collaboration_hub.user_participant_indices(cx);
17200 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17201 let collaborators_by_replica_id = collaborators_by_peer_id
17202 .iter()
17203 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17204 .collect::<HashMap<_, _>>();
17205 self.buffer_snapshot
17206 .selections_in_range(range, false)
17207 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17208 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17209 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17210 let user_name = participant_names.get(&collaborator.user_id).cloned();
17211 Some(RemoteSelection {
17212 replica_id,
17213 selection,
17214 cursor_shape,
17215 line_mode,
17216 participant_index,
17217 peer_id: collaborator.peer_id,
17218 user_name,
17219 })
17220 })
17221 }
17222
17223 pub fn hunks_for_ranges(
17224 &self,
17225 ranges: impl IntoIterator<Item = Range<Point>>,
17226 ) -> Vec<MultiBufferDiffHunk> {
17227 let mut hunks = Vec::new();
17228 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17229 HashMap::default();
17230 for query_range in ranges {
17231 let query_rows =
17232 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17233 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17234 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17235 ) {
17236 // Include deleted hunks that are adjacent to the query range, because
17237 // otherwise they would be missed.
17238 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
17239 if hunk.status().is_deleted() {
17240 intersects_range |= hunk.row_range.start == query_rows.end;
17241 intersects_range |= hunk.row_range.end == query_rows.start;
17242 }
17243 if intersects_range {
17244 if !processed_buffer_rows
17245 .entry(hunk.buffer_id)
17246 .or_default()
17247 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17248 {
17249 continue;
17250 }
17251 hunks.push(hunk);
17252 }
17253 }
17254 }
17255
17256 hunks
17257 }
17258
17259 fn display_diff_hunks_for_rows<'a>(
17260 &'a self,
17261 display_rows: Range<DisplayRow>,
17262 folded_buffers: &'a HashSet<BufferId>,
17263 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17264 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17265 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17266
17267 self.buffer_snapshot
17268 .diff_hunks_in_range(buffer_start..buffer_end)
17269 .filter_map(|hunk| {
17270 if folded_buffers.contains(&hunk.buffer_id) {
17271 return None;
17272 }
17273
17274 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17275 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17276
17277 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17278 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17279
17280 let display_hunk = if hunk_display_start.column() != 0 {
17281 DisplayDiffHunk::Folded {
17282 display_row: hunk_display_start.row(),
17283 }
17284 } else {
17285 let mut end_row = hunk_display_end.row();
17286 if hunk_display_end.column() > 0 {
17287 end_row.0 += 1;
17288 }
17289 let is_created_file = hunk.is_created_file();
17290 DisplayDiffHunk::Unfolded {
17291 status: hunk.status(),
17292 diff_base_byte_range: hunk.diff_base_byte_range,
17293 display_row_range: hunk_display_start.row()..end_row,
17294 multi_buffer_range: Anchor::range_in_buffer(
17295 hunk.excerpt_id,
17296 hunk.buffer_id,
17297 hunk.buffer_range,
17298 ),
17299 is_created_file,
17300 }
17301 };
17302
17303 Some(display_hunk)
17304 })
17305 }
17306
17307 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17308 self.display_snapshot.buffer_snapshot.language_at(position)
17309 }
17310
17311 pub fn is_focused(&self) -> bool {
17312 self.is_focused
17313 }
17314
17315 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17316 self.placeholder_text.as_ref()
17317 }
17318
17319 pub fn scroll_position(&self) -> gpui::Point<f32> {
17320 self.scroll_anchor.scroll_position(&self.display_snapshot)
17321 }
17322
17323 fn gutter_dimensions(
17324 &self,
17325 font_id: FontId,
17326 font_size: Pixels,
17327 max_line_number_width: Pixels,
17328 cx: &App,
17329 ) -> Option<GutterDimensions> {
17330 if !self.show_gutter {
17331 return None;
17332 }
17333
17334 let descent = cx.text_system().descent(font_id, font_size);
17335 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17336 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17337
17338 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17339 matches!(
17340 ProjectSettings::get_global(cx).git.git_gutter,
17341 Some(GitGutterSetting::TrackedFiles)
17342 )
17343 });
17344 let gutter_settings = EditorSettings::get_global(cx).gutter;
17345 let show_line_numbers = self
17346 .show_line_numbers
17347 .unwrap_or(gutter_settings.line_numbers);
17348 let line_gutter_width = if show_line_numbers {
17349 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17350 let min_width_for_number_on_gutter = em_advance * 4.0;
17351 max_line_number_width.max(min_width_for_number_on_gutter)
17352 } else {
17353 0.0.into()
17354 };
17355
17356 let show_code_actions = self
17357 .show_code_actions
17358 .unwrap_or(gutter_settings.code_actions);
17359
17360 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17361
17362 let git_blame_entries_width =
17363 self.git_blame_gutter_max_author_length
17364 .map(|max_author_length| {
17365 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17366
17367 /// The number of characters to dedicate to gaps and margins.
17368 const SPACING_WIDTH: usize = 4;
17369
17370 let max_char_count = max_author_length
17371 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17372 + ::git::SHORT_SHA_LENGTH
17373 + MAX_RELATIVE_TIMESTAMP.len()
17374 + SPACING_WIDTH;
17375
17376 em_advance * max_char_count
17377 });
17378
17379 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17380 left_padding += if show_code_actions || show_runnables {
17381 em_width * 3.0
17382 } else if show_git_gutter && show_line_numbers {
17383 em_width * 2.0
17384 } else if show_git_gutter || show_line_numbers {
17385 em_width
17386 } else {
17387 px(0.)
17388 };
17389
17390 let right_padding = if gutter_settings.folds && show_line_numbers {
17391 em_width * 4.0
17392 } else if gutter_settings.folds {
17393 em_width * 3.0
17394 } else if show_line_numbers {
17395 em_width
17396 } else {
17397 px(0.)
17398 };
17399
17400 Some(GutterDimensions {
17401 left_padding,
17402 right_padding,
17403 width: line_gutter_width + left_padding + right_padding,
17404 margin: -descent,
17405 git_blame_entries_width,
17406 })
17407 }
17408
17409 pub fn render_crease_toggle(
17410 &self,
17411 buffer_row: MultiBufferRow,
17412 row_contains_cursor: bool,
17413 editor: Entity<Editor>,
17414 window: &mut Window,
17415 cx: &mut App,
17416 ) -> Option<AnyElement> {
17417 let folded = self.is_line_folded(buffer_row);
17418 let mut is_foldable = false;
17419
17420 if let Some(crease) = self
17421 .crease_snapshot
17422 .query_row(buffer_row, &self.buffer_snapshot)
17423 {
17424 is_foldable = true;
17425 match crease {
17426 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17427 if let Some(render_toggle) = render_toggle {
17428 let toggle_callback =
17429 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17430 if folded {
17431 editor.update(cx, |editor, cx| {
17432 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17433 });
17434 } else {
17435 editor.update(cx, |editor, cx| {
17436 editor.unfold_at(
17437 &crate::UnfoldAt { buffer_row },
17438 window,
17439 cx,
17440 )
17441 });
17442 }
17443 });
17444 return Some((render_toggle)(
17445 buffer_row,
17446 folded,
17447 toggle_callback,
17448 window,
17449 cx,
17450 ));
17451 }
17452 }
17453 }
17454 }
17455
17456 is_foldable |= self.starts_indent(buffer_row);
17457
17458 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17459 Some(
17460 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17461 .toggle_state(folded)
17462 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17463 if folded {
17464 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17465 } else {
17466 this.fold_at(&FoldAt { buffer_row }, window, cx);
17467 }
17468 }))
17469 .into_any_element(),
17470 )
17471 } else {
17472 None
17473 }
17474 }
17475
17476 pub fn render_crease_trailer(
17477 &self,
17478 buffer_row: MultiBufferRow,
17479 window: &mut Window,
17480 cx: &mut App,
17481 ) -> Option<AnyElement> {
17482 let folded = self.is_line_folded(buffer_row);
17483 if let Crease::Inline { render_trailer, .. } = self
17484 .crease_snapshot
17485 .query_row(buffer_row, &self.buffer_snapshot)?
17486 {
17487 let render_trailer = render_trailer.as_ref()?;
17488 Some(render_trailer(buffer_row, folded, window, cx))
17489 } else {
17490 None
17491 }
17492 }
17493}
17494
17495impl Deref for EditorSnapshot {
17496 type Target = DisplaySnapshot;
17497
17498 fn deref(&self) -> &Self::Target {
17499 &self.display_snapshot
17500 }
17501}
17502
17503#[derive(Clone, Debug, PartialEq, Eq)]
17504pub enum EditorEvent {
17505 InputIgnored {
17506 text: Arc<str>,
17507 },
17508 InputHandled {
17509 utf16_range_to_replace: Option<Range<isize>>,
17510 text: Arc<str>,
17511 },
17512 ExcerptsAdded {
17513 buffer: Entity<Buffer>,
17514 predecessor: ExcerptId,
17515 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17516 },
17517 ExcerptsRemoved {
17518 ids: Vec<ExcerptId>,
17519 },
17520 BufferFoldToggled {
17521 ids: Vec<ExcerptId>,
17522 folded: bool,
17523 },
17524 ExcerptsEdited {
17525 ids: Vec<ExcerptId>,
17526 },
17527 ExcerptsExpanded {
17528 ids: Vec<ExcerptId>,
17529 },
17530 BufferEdited,
17531 Edited {
17532 transaction_id: clock::Lamport,
17533 },
17534 Reparsed(BufferId),
17535 Focused,
17536 FocusedIn,
17537 Blurred,
17538 DirtyChanged,
17539 Saved,
17540 TitleChanged,
17541 DiffBaseChanged,
17542 SelectionsChanged {
17543 local: bool,
17544 },
17545 ScrollPositionChanged {
17546 local: bool,
17547 autoscroll: bool,
17548 },
17549 Closed,
17550 TransactionUndone {
17551 transaction_id: clock::Lamport,
17552 },
17553 TransactionBegun {
17554 transaction_id: clock::Lamport,
17555 },
17556 Reloaded,
17557 CursorShapeChanged,
17558}
17559
17560impl EventEmitter<EditorEvent> for Editor {}
17561
17562impl Focusable for Editor {
17563 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17564 self.focus_handle.clone()
17565 }
17566}
17567
17568impl Render for Editor {
17569 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17570 let settings = ThemeSettings::get_global(cx);
17571
17572 let mut text_style = match self.mode {
17573 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17574 color: cx.theme().colors().editor_foreground,
17575 font_family: settings.ui_font.family.clone(),
17576 font_features: settings.ui_font.features.clone(),
17577 font_fallbacks: settings.ui_font.fallbacks.clone(),
17578 font_size: rems(0.875).into(),
17579 font_weight: settings.ui_font.weight,
17580 line_height: relative(settings.buffer_line_height.value()),
17581 ..Default::default()
17582 },
17583 EditorMode::Full => TextStyle {
17584 color: cx.theme().colors().editor_foreground,
17585 font_family: settings.buffer_font.family.clone(),
17586 font_features: settings.buffer_font.features.clone(),
17587 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17588 font_size: settings.buffer_font_size(cx).into(),
17589 font_weight: settings.buffer_font.weight,
17590 line_height: relative(settings.buffer_line_height.value()),
17591 ..Default::default()
17592 },
17593 };
17594 if let Some(text_style_refinement) = &self.text_style_refinement {
17595 text_style.refine(text_style_refinement)
17596 }
17597
17598 let background = match self.mode {
17599 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17600 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17601 EditorMode::Full => cx.theme().colors().editor_background,
17602 };
17603
17604 EditorElement::new(
17605 &cx.entity(),
17606 EditorStyle {
17607 background,
17608 local_player: cx.theme().players().local(),
17609 text: text_style,
17610 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17611 syntax: cx.theme().syntax().clone(),
17612 status: cx.theme().status().clone(),
17613 inlay_hints_style: make_inlay_hints_style(cx),
17614 inline_completion_styles: make_suggestion_styles(cx),
17615 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17616 },
17617 )
17618 }
17619}
17620
17621impl EntityInputHandler for Editor {
17622 fn text_for_range(
17623 &mut self,
17624 range_utf16: Range<usize>,
17625 adjusted_range: &mut Option<Range<usize>>,
17626 _: &mut Window,
17627 cx: &mut Context<Self>,
17628 ) -> Option<String> {
17629 let snapshot = self.buffer.read(cx).read(cx);
17630 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17631 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17632 if (start.0..end.0) != range_utf16 {
17633 adjusted_range.replace(start.0..end.0);
17634 }
17635 Some(snapshot.text_for_range(start..end).collect())
17636 }
17637
17638 fn selected_text_range(
17639 &mut self,
17640 ignore_disabled_input: bool,
17641 _: &mut Window,
17642 cx: &mut Context<Self>,
17643 ) -> Option<UTF16Selection> {
17644 // Prevent the IME menu from appearing when holding down an alphabetic key
17645 // while input is disabled.
17646 if !ignore_disabled_input && !self.input_enabled {
17647 return None;
17648 }
17649
17650 let selection = self.selections.newest::<OffsetUtf16>(cx);
17651 let range = selection.range();
17652
17653 Some(UTF16Selection {
17654 range: range.start.0..range.end.0,
17655 reversed: selection.reversed,
17656 })
17657 }
17658
17659 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17660 let snapshot = self.buffer.read(cx).read(cx);
17661 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17662 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17663 }
17664
17665 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17666 self.clear_highlights::<InputComposition>(cx);
17667 self.ime_transaction.take();
17668 }
17669
17670 fn replace_text_in_range(
17671 &mut self,
17672 range_utf16: Option<Range<usize>>,
17673 text: &str,
17674 window: &mut Window,
17675 cx: &mut Context<Self>,
17676 ) {
17677 if !self.input_enabled {
17678 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17679 return;
17680 }
17681
17682 self.transact(window, cx, |this, window, cx| {
17683 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17684 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17685 Some(this.selection_replacement_ranges(range_utf16, cx))
17686 } else {
17687 this.marked_text_ranges(cx)
17688 };
17689
17690 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17691 let newest_selection_id = this.selections.newest_anchor().id;
17692 this.selections
17693 .all::<OffsetUtf16>(cx)
17694 .iter()
17695 .zip(ranges_to_replace.iter())
17696 .find_map(|(selection, range)| {
17697 if selection.id == newest_selection_id {
17698 Some(
17699 (range.start.0 as isize - selection.head().0 as isize)
17700 ..(range.end.0 as isize - selection.head().0 as isize),
17701 )
17702 } else {
17703 None
17704 }
17705 })
17706 });
17707
17708 cx.emit(EditorEvent::InputHandled {
17709 utf16_range_to_replace: range_to_replace,
17710 text: text.into(),
17711 });
17712
17713 if let Some(new_selected_ranges) = new_selected_ranges {
17714 this.change_selections(None, window, cx, |selections| {
17715 selections.select_ranges(new_selected_ranges)
17716 });
17717 this.backspace(&Default::default(), window, cx);
17718 }
17719
17720 this.handle_input(text, window, cx);
17721 });
17722
17723 if let Some(transaction) = self.ime_transaction {
17724 self.buffer.update(cx, |buffer, cx| {
17725 buffer.group_until_transaction(transaction, cx);
17726 });
17727 }
17728
17729 self.unmark_text(window, cx);
17730 }
17731
17732 fn replace_and_mark_text_in_range(
17733 &mut self,
17734 range_utf16: Option<Range<usize>>,
17735 text: &str,
17736 new_selected_range_utf16: Option<Range<usize>>,
17737 window: &mut Window,
17738 cx: &mut Context<Self>,
17739 ) {
17740 if !self.input_enabled {
17741 return;
17742 }
17743
17744 let transaction = self.transact(window, cx, |this, window, cx| {
17745 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17746 let snapshot = this.buffer.read(cx).read(cx);
17747 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17748 for marked_range in &mut marked_ranges {
17749 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17750 marked_range.start.0 += relative_range_utf16.start;
17751 marked_range.start =
17752 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
17753 marked_range.end =
17754 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
17755 }
17756 }
17757 Some(marked_ranges)
17758 } else if let Some(range_utf16) = range_utf16 {
17759 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17760 Some(this.selection_replacement_ranges(range_utf16, cx))
17761 } else {
17762 None
17763 };
17764
17765 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
17766 let newest_selection_id = this.selections.newest_anchor().id;
17767 this.selections
17768 .all::<OffsetUtf16>(cx)
17769 .iter()
17770 .zip(ranges_to_replace.iter())
17771 .find_map(|(selection, range)| {
17772 if selection.id == newest_selection_id {
17773 Some(
17774 (range.start.0 as isize - selection.head().0 as isize)
17775 ..(range.end.0 as isize - selection.head().0 as isize),
17776 )
17777 } else {
17778 None
17779 }
17780 })
17781 });
17782
17783 cx.emit(EditorEvent::InputHandled {
17784 utf16_range_to_replace: range_to_replace,
17785 text: text.into(),
17786 });
17787
17788 if let Some(ranges) = ranges_to_replace {
17789 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
17790 }
17791
17792 let marked_ranges = {
17793 let snapshot = this.buffer.read(cx).read(cx);
17794 this.selections
17795 .disjoint_anchors()
17796 .iter()
17797 .map(|selection| {
17798 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
17799 })
17800 .collect::<Vec<_>>()
17801 };
17802
17803 if text.is_empty() {
17804 this.unmark_text(window, cx);
17805 } else {
17806 this.highlight_text::<InputComposition>(
17807 marked_ranges.clone(),
17808 HighlightStyle {
17809 underline: Some(UnderlineStyle {
17810 thickness: px(1.),
17811 color: None,
17812 wavy: false,
17813 }),
17814 ..Default::default()
17815 },
17816 cx,
17817 );
17818 }
17819
17820 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
17821 let use_autoclose = this.use_autoclose;
17822 let use_auto_surround = this.use_auto_surround;
17823 this.set_use_autoclose(false);
17824 this.set_use_auto_surround(false);
17825 this.handle_input(text, window, cx);
17826 this.set_use_autoclose(use_autoclose);
17827 this.set_use_auto_surround(use_auto_surround);
17828
17829 if let Some(new_selected_range) = new_selected_range_utf16 {
17830 let snapshot = this.buffer.read(cx).read(cx);
17831 let new_selected_ranges = marked_ranges
17832 .into_iter()
17833 .map(|marked_range| {
17834 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
17835 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
17836 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
17837 snapshot.clip_offset_utf16(new_start, Bias::Left)
17838 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
17839 })
17840 .collect::<Vec<_>>();
17841
17842 drop(snapshot);
17843 this.change_selections(None, window, cx, |selections| {
17844 selections.select_ranges(new_selected_ranges)
17845 });
17846 }
17847 });
17848
17849 self.ime_transaction = self.ime_transaction.or(transaction);
17850 if let Some(transaction) = self.ime_transaction {
17851 self.buffer.update(cx, |buffer, cx| {
17852 buffer.group_until_transaction(transaction, cx);
17853 });
17854 }
17855
17856 if self.text_highlights::<InputComposition>(cx).is_none() {
17857 self.ime_transaction.take();
17858 }
17859 }
17860
17861 fn bounds_for_range(
17862 &mut self,
17863 range_utf16: Range<usize>,
17864 element_bounds: gpui::Bounds<Pixels>,
17865 window: &mut Window,
17866 cx: &mut Context<Self>,
17867 ) -> Option<gpui::Bounds<Pixels>> {
17868 let text_layout_details = self.text_layout_details(window);
17869 let gpui::Size {
17870 width: em_width,
17871 height: line_height,
17872 } = self.character_size(window);
17873
17874 let snapshot = self.snapshot(window, cx);
17875 let scroll_position = snapshot.scroll_position();
17876 let scroll_left = scroll_position.x * em_width;
17877
17878 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
17879 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
17880 + self.gutter_dimensions.width
17881 + self.gutter_dimensions.margin;
17882 let y = line_height * (start.row().as_f32() - scroll_position.y);
17883
17884 Some(Bounds {
17885 origin: element_bounds.origin + point(x, y),
17886 size: size(em_width, line_height),
17887 })
17888 }
17889
17890 fn character_index_for_point(
17891 &mut self,
17892 point: gpui::Point<Pixels>,
17893 _window: &mut Window,
17894 _cx: &mut Context<Self>,
17895 ) -> Option<usize> {
17896 let position_map = self.last_position_map.as_ref()?;
17897 if !position_map.text_hitbox.contains(&point) {
17898 return None;
17899 }
17900 let display_point = position_map.point_for_position(point).previous_valid;
17901 let anchor = position_map
17902 .snapshot
17903 .display_point_to_anchor(display_point, Bias::Left);
17904 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
17905 Some(utf16_offset.0)
17906 }
17907}
17908
17909trait SelectionExt {
17910 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
17911 fn spanned_rows(
17912 &self,
17913 include_end_if_at_line_start: bool,
17914 map: &DisplaySnapshot,
17915 ) -> Range<MultiBufferRow>;
17916}
17917
17918impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
17919 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
17920 let start = self
17921 .start
17922 .to_point(&map.buffer_snapshot)
17923 .to_display_point(map);
17924 let end = self
17925 .end
17926 .to_point(&map.buffer_snapshot)
17927 .to_display_point(map);
17928 if self.reversed {
17929 end..start
17930 } else {
17931 start..end
17932 }
17933 }
17934
17935 fn spanned_rows(
17936 &self,
17937 include_end_if_at_line_start: bool,
17938 map: &DisplaySnapshot,
17939 ) -> Range<MultiBufferRow> {
17940 let start = self.start.to_point(&map.buffer_snapshot);
17941 let mut end = self.end.to_point(&map.buffer_snapshot);
17942 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
17943 end.row -= 1;
17944 }
17945
17946 let buffer_start = map.prev_line_boundary(start).0;
17947 let buffer_end = map.next_line_boundary(end).0;
17948 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
17949 }
17950}
17951
17952impl<T: InvalidationRegion> InvalidationStack<T> {
17953 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
17954 where
17955 S: Clone + ToOffset,
17956 {
17957 while let Some(region) = self.last() {
17958 let all_selections_inside_invalidation_ranges =
17959 if selections.len() == region.ranges().len() {
17960 selections
17961 .iter()
17962 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
17963 .all(|(selection, invalidation_range)| {
17964 let head = selection.head().to_offset(buffer);
17965 invalidation_range.start <= head && invalidation_range.end >= head
17966 })
17967 } else {
17968 false
17969 };
17970
17971 if all_selections_inside_invalidation_ranges {
17972 break;
17973 } else {
17974 self.pop();
17975 }
17976 }
17977 }
17978}
17979
17980impl<T> Default for InvalidationStack<T> {
17981 fn default() -> Self {
17982 Self(Default::default())
17983 }
17984}
17985
17986impl<T> Deref for InvalidationStack<T> {
17987 type Target = Vec<T>;
17988
17989 fn deref(&self) -> &Self::Target {
17990 &self.0
17991 }
17992}
17993
17994impl<T> DerefMut for InvalidationStack<T> {
17995 fn deref_mut(&mut self) -> &mut Self::Target {
17996 &mut self.0
17997 }
17998}
17999
18000impl InvalidationRegion for SnippetState {
18001 fn ranges(&self) -> &[Range<Anchor>] {
18002 &self.ranges[self.active_index]
18003 }
18004}
18005
18006pub fn diagnostic_block_renderer(
18007 diagnostic: Diagnostic,
18008 max_message_rows: Option<u8>,
18009 allow_closing: bool,
18010) -> RenderBlock {
18011 let (text_without_backticks, code_ranges) =
18012 highlight_diagnostic_message(&diagnostic, max_message_rows);
18013
18014 Arc::new(move |cx: &mut BlockContext| {
18015 let group_id: SharedString = cx.block_id.to_string().into();
18016
18017 let mut text_style = cx.window.text_style().clone();
18018 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18019 let theme_settings = ThemeSettings::get_global(cx);
18020 text_style.font_family = theme_settings.buffer_font.family.clone();
18021 text_style.font_style = theme_settings.buffer_font.style;
18022 text_style.font_features = theme_settings.buffer_font.features.clone();
18023 text_style.font_weight = theme_settings.buffer_font.weight;
18024
18025 let multi_line_diagnostic = diagnostic.message.contains('\n');
18026
18027 let buttons = |diagnostic: &Diagnostic| {
18028 if multi_line_diagnostic {
18029 v_flex()
18030 } else {
18031 h_flex()
18032 }
18033 .when(allow_closing, |div| {
18034 div.children(diagnostic.is_primary.then(|| {
18035 IconButton::new("close-block", IconName::XCircle)
18036 .icon_color(Color::Muted)
18037 .size(ButtonSize::Compact)
18038 .style(ButtonStyle::Transparent)
18039 .visible_on_hover(group_id.clone())
18040 .on_click(move |_click, window, cx| {
18041 window.dispatch_action(Box::new(Cancel), cx)
18042 })
18043 .tooltip(|window, cx| {
18044 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18045 })
18046 }))
18047 })
18048 .child(
18049 IconButton::new("copy-block", IconName::Copy)
18050 .icon_color(Color::Muted)
18051 .size(ButtonSize::Compact)
18052 .style(ButtonStyle::Transparent)
18053 .visible_on_hover(group_id.clone())
18054 .on_click({
18055 let message = diagnostic.message.clone();
18056 move |_click, _, cx| {
18057 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
18058 }
18059 })
18060 .tooltip(Tooltip::text("Copy diagnostic message")),
18061 )
18062 };
18063
18064 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
18065 AvailableSpace::min_size(),
18066 cx.window,
18067 cx.app,
18068 );
18069
18070 h_flex()
18071 .id(cx.block_id)
18072 .group(group_id.clone())
18073 .relative()
18074 .size_full()
18075 .block_mouse_down()
18076 .pl(cx.gutter_dimensions.width)
18077 .w(cx.max_width - cx.gutter_dimensions.full_width())
18078 .child(
18079 div()
18080 .flex()
18081 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
18082 .flex_shrink(),
18083 )
18084 .child(buttons(&diagnostic))
18085 .child(div().flex().flex_shrink_0().child(
18086 StyledText::new(text_without_backticks.clone()).with_default_highlights(
18087 &text_style,
18088 code_ranges.iter().map(|range| {
18089 (
18090 range.clone(),
18091 HighlightStyle {
18092 font_weight: Some(FontWeight::BOLD),
18093 ..Default::default()
18094 },
18095 )
18096 }),
18097 ),
18098 ))
18099 .into_any_element()
18100 })
18101}
18102
18103fn inline_completion_edit_text(
18104 current_snapshot: &BufferSnapshot,
18105 edits: &[(Range<Anchor>, String)],
18106 edit_preview: &EditPreview,
18107 include_deletions: bool,
18108 cx: &App,
18109) -> HighlightedText {
18110 let edits = edits
18111 .iter()
18112 .map(|(anchor, text)| {
18113 (
18114 anchor.start.text_anchor..anchor.end.text_anchor,
18115 text.clone(),
18116 )
18117 })
18118 .collect::<Vec<_>>();
18119
18120 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
18121}
18122
18123pub fn highlight_diagnostic_message(
18124 diagnostic: &Diagnostic,
18125 mut max_message_rows: Option<u8>,
18126) -> (SharedString, Vec<Range<usize>>) {
18127 let mut text_without_backticks = String::new();
18128 let mut code_ranges = Vec::new();
18129
18130 if let Some(source) = &diagnostic.source {
18131 text_without_backticks.push_str(source);
18132 code_ranges.push(0..source.len());
18133 text_without_backticks.push_str(": ");
18134 }
18135
18136 let mut prev_offset = 0;
18137 let mut in_code_block = false;
18138 let has_row_limit = max_message_rows.is_some();
18139 let mut newline_indices = diagnostic
18140 .message
18141 .match_indices('\n')
18142 .filter(|_| has_row_limit)
18143 .map(|(ix, _)| ix)
18144 .fuse()
18145 .peekable();
18146
18147 for (quote_ix, _) in diagnostic
18148 .message
18149 .match_indices('`')
18150 .chain([(diagnostic.message.len(), "")])
18151 {
18152 let mut first_newline_ix = None;
18153 let mut last_newline_ix = None;
18154 while let Some(newline_ix) = newline_indices.peek() {
18155 if *newline_ix < quote_ix {
18156 if first_newline_ix.is_none() {
18157 first_newline_ix = Some(*newline_ix);
18158 }
18159 last_newline_ix = Some(*newline_ix);
18160
18161 if let Some(rows_left) = &mut max_message_rows {
18162 if *rows_left == 0 {
18163 break;
18164 } else {
18165 *rows_left -= 1;
18166 }
18167 }
18168 let _ = newline_indices.next();
18169 } else {
18170 break;
18171 }
18172 }
18173 let prev_len = text_without_backticks.len();
18174 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18175 text_without_backticks.push_str(new_text);
18176 if in_code_block {
18177 code_ranges.push(prev_len..text_without_backticks.len());
18178 }
18179 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18180 in_code_block = !in_code_block;
18181 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18182 text_without_backticks.push_str("...");
18183 break;
18184 }
18185 }
18186
18187 (text_without_backticks.into(), code_ranges)
18188}
18189
18190fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18191 match severity {
18192 DiagnosticSeverity::ERROR => colors.error,
18193 DiagnosticSeverity::WARNING => colors.warning,
18194 DiagnosticSeverity::INFORMATION => colors.info,
18195 DiagnosticSeverity::HINT => colors.info,
18196 _ => colors.ignored,
18197 }
18198}
18199
18200pub fn styled_runs_for_code_label<'a>(
18201 label: &'a CodeLabel,
18202 syntax_theme: &'a theme::SyntaxTheme,
18203) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18204 let fade_out = HighlightStyle {
18205 fade_out: Some(0.35),
18206 ..Default::default()
18207 };
18208
18209 let mut prev_end = label.filter_range.end;
18210 label
18211 .runs
18212 .iter()
18213 .enumerate()
18214 .flat_map(move |(ix, (range, highlight_id))| {
18215 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18216 style
18217 } else {
18218 return Default::default();
18219 };
18220 let mut muted_style = style;
18221 muted_style.highlight(fade_out);
18222
18223 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18224 if range.start >= label.filter_range.end {
18225 if range.start > prev_end {
18226 runs.push((prev_end..range.start, fade_out));
18227 }
18228 runs.push((range.clone(), muted_style));
18229 } else if range.end <= label.filter_range.end {
18230 runs.push((range.clone(), style));
18231 } else {
18232 runs.push((range.start..label.filter_range.end, style));
18233 runs.push((label.filter_range.end..range.end, muted_style));
18234 }
18235 prev_end = cmp::max(prev_end, range.end);
18236
18237 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18238 runs.push((prev_end..label.text.len(), fade_out));
18239 }
18240
18241 runs
18242 })
18243}
18244
18245pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18246 let mut prev_index = 0;
18247 let mut prev_codepoint: Option<char> = None;
18248 text.char_indices()
18249 .chain([(text.len(), '\0')])
18250 .filter_map(move |(index, codepoint)| {
18251 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18252 let is_boundary = index == text.len()
18253 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18254 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18255 if is_boundary {
18256 let chunk = &text[prev_index..index];
18257 prev_index = index;
18258 Some(chunk)
18259 } else {
18260 None
18261 }
18262 })
18263}
18264
18265pub trait RangeToAnchorExt: Sized {
18266 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18267
18268 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18269 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18270 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18271 }
18272}
18273
18274impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18275 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18276 let start_offset = self.start.to_offset(snapshot);
18277 let end_offset = self.end.to_offset(snapshot);
18278 if start_offset == end_offset {
18279 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18280 } else {
18281 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18282 }
18283 }
18284}
18285
18286pub trait RowExt {
18287 fn as_f32(&self) -> f32;
18288
18289 fn next_row(&self) -> Self;
18290
18291 fn previous_row(&self) -> Self;
18292
18293 fn minus(&self, other: Self) -> u32;
18294}
18295
18296impl RowExt for DisplayRow {
18297 fn as_f32(&self) -> f32 {
18298 self.0 as f32
18299 }
18300
18301 fn next_row(&self) -> Self {
18302 Self(self.0 + 1)
18303 }
18304
18305 fn previous_row(&self) -> Self {
18306 Self(self.0.saturating_sub(1))
18307 }
18308
18309 fn minus(&self, other: Self) -> u32 {
18310 self.0 - other.0
18311 }
18312}
18313
18314impl RowExt for MultiBufferRow {
18315 fn as_f32(&self) -> f32 {
18316 self.0 as f32
18317 }
18318
18319 fn next_row(&self) -> Self {
18320 Self(self.0 + 1)
18321 }
18322
18323 fn previous_row(&self) -> Self {
18324 Self(self.0.saturating_sub(1))
18325 }
18326
18327 fn minus(&self, other: Self) -> u32 {
18328 self.0 - other.0
18329 }
18330}
18331
18332trait RowRangeExt {
18333 type Row;
18334
18335 fn len(&self) -> usize;
18336
18337 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18338}
18339
18340impl RowRangeExt for Range<MultiBufferRow> {
18341 type Row = MultiBufferRow;
18342
18343 fn len(&self) -> usize {
18344 (self.end.0 - self.start.0) as usize
18345 }
18346
18347 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18348 (self.start.0..self.end.0).map(MultiBufferRow)
18349 }
18350}
18351
18352impl RowRangeExt for Range<DisplayRow> {
18353 type Row = DisplayRow;
18354
18355 fn len(&self) -> usize {
18356 (self.end.0 - self.start.0) as usize
18357 }
18358
18359 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18360 (self.start.0..self.end.0).map(DisplayRow)
18361 }
18362}
18363
18364/// If select range has more than one line, we
18365/// just point the cursor to range.start.
18366fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18367 if range.start.row == range.end.row {
18368 range
18369 } else {
18370 range.start..range.start
18371 }
18372}
18373pub struct KillRing(ClipboardItem);
18374impl Global for KillRing {}
18375
18376const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18377
18378fn all_edits_insertions_or_deletions(
18379 edits: &Vec<(Range<Anchor>, String)>,
18380 snapshot: &MultiBufferSnapshot,
18381) -> bool {
18382 let mut all_insertions = true;
18383 let mut all_deletions = true;
18384
18385 for (range, new_text) in edits.iter() {
18386 let range_is_empty = range.to_offset(&snapshot).is_empty();
18387 let text_is_empty = new_text.is_empty();
18388
18389 if range_is_empty != text_is_empty {
18390 if range_is_empty {
18391 all_deletions = false;
18392 } else {
18393 all_insertions = false;
18394 }
18395 } else {
18396 return false;
18397 }
18398
18399 if !all_insertions && !all_deletions {
18400 return false;
18401 }
18402 }
18403 all_insertions || all_deletions
18404}
18405
18406struct MissingEditPredictionKeybindingTooltip;
18407
18408impl Render for MissingEditPredictionKeybindingTooltip {
18409 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18410 ui::tooltip_container(window, cx, |container, _, cx| {
18411 container
18412 .flex_shrink_0()
18413 .max_w_80()
18414 .min_h(rems_from_px(124.))
18415 .justify_between()
18416 .child(
18417 v_flex()
18418 .flex_1()
18419 .text_ui_sm(cx)
18420 .child(Label::new("Conflict with Accept Keybinding"))
18421 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
18422 )
18423 .child(
18424 h_flex()
18425 .pb_1()
18426 .gap_1()
18427 .items_end()
18428 .w_full()
18429 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
18430 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
18431 }))
18432 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
18433 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
18434 })),
18435 )
18436 })
18437 }
18438}