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 linked_editing_ranges;
32mod lsp_ext;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod proposed_changes_editor;
37mod rust_analyzer_ext;
38pub mod scroll;
39mod selections_collection;
40pub mod tasks;
41
42#[cfg(test)]
43mod editor_tests;
44#[cfg(test)]
45mod inline_completion_tests;
46mod signature_help;
47#[cfg(any(test, feature = "test-support"))]
48pub mod test;
49
50pub(crate) use actions::*;
51pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use buffer_diff::DiffHunkStatus;
56use client::{Collaborator, ParticipantIndex};
57use clock::ReplicaId;
58use collections::{BTreeMap, HashMap, HashSet, VecDeque};
59use convert_case::{Case, Casing};
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
64};
65pub use editor_settings_controls::*;
66use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::{
71 future::{self, Shared},
72 FutureExt,
73};
74use fuzzy::StringMatchCandidate;
75
76use ::git::Restore;
77use code_context_menus::{
78 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
79 CompletionsMenu, ContextMenuOrigin,
80};
81use git::blame::GitBlame;
82use gpui::{
83 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
84 AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
85 ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity, EntityInputHandler,
86 EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
87 HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
88 ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, Task,
89 TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
90 WeakEntity, WeakFocusHandle, Window,
91};
92use highlight_matching_bracket::refresh_matching_bracket_highlights;
93use hover_popover::{hide_hover, HoverState};
94use indent_guides::ActiveIndentGuidesState;
95use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
96pub use inline_completion::Direction;
97use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
98pub use items::MAX_TAB_TITLE_LEN;
99use itertools::Itertools;
100use language::{
101 language_settings::{
102 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
103 },
104 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
105 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
106 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
107 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
108};
109use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
110use linked_editing_ranges::refresh_linked_ranges;
111use mouse_context_menu::MouseContextMenu;
112use persistence::DB;
113pub use proposed_changes_editor::{
114 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
115};
116use smallvec::smallvec;
117use std::iter::Peekable;
118use task::{ResolvedTask, TaskTemplate, TaskVariables};
119
120use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
121pub use lsp::CompletionContext;
122use lsp::{
123 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
124 InsertTextFormat, LanguageServerId, LanguageServerName,
125};
126
127use language::BufferSnapshot;
128use movement::TextLayoutDetails;
129pub use multi_buffer::{
130 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
131 ToOffset, ToPoint,
132};
133use multi_buffer::{
134 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
135 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
136};
137use project::{
138 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
139 project_settings::{GitGutterSetting, ProjectSettings},
140 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
141 PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
142};
143use rand::prelude::*;
144use rpc::{proto::*, ErrorExt};
145use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
146use selections_collection::{
147 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
148};
149use serde::{Deserialize, Serialize};
150use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
151use smallvec::SmallVec;
152use snippet::Snippet;
153use std::{
154 any::TypeId,
155 borrow::Cow,
156 cell::RefCell,
157 cmp::{self, Ordering, Reverse},
158 mem,
159 num::NonZeroU32,
160 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
161 path::{Path, PathBuf},
162 rc::Rc,
163 sync::Arc,
164 time::{Duration, Instant},
165};
166pub use sum_tree::Bias;
167use sum_tree::TreeMap;
168use text::{BufferId, OffsetUtf16, Rope};
169use theme::{
170 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
171 ThemeColors, ThemeSettings,
172};
173use ui::{
174 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
175 Tooltip,
176};
177use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
178use workspace::{
179 item::{ItemHandle, PreviewTabsSettings},
180 ItemId, RestoreOnStartupBehavior,
181};
182use workspace::{
183 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
184 WorkspaceSettings,
185};
186use workspace::{
187 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
188};
189use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
190
191use crate::hover_links::{find_url, find_url_from_range};
192use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
193
194pub const FILE_HEADER_HEIGHT: u32 = 2;
195pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
196pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
197pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
198const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
199const MAX_LINE_LEN: usize = 1024;
200const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
201const MAX_SELECTION_HISTORY_LEN: usize = 1024;
202pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
203#[doc(hidden)]
204pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
205
206pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
207pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
208pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
209
210pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
211pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
212
213const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
214 alt: true,
215 shift: true,
216 control: false,
217 platform: false,
218 function: false,
219};
220
221#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
222pub enum InlayId {
223 InlineCompletion(usize),
224 Hint(usize),
225}
226
227impl InlayId {
228 fn id(&self) -> usize {
229 match self {
230 Self::InlineCompletion(id) => *id,
231 Self::Hint(id) => *id,
232 }
233 }
234}
235
236enum DocumentHighlightRead {}
237enum DocumentHighlightWrite {}
238enum InputComposition {}
239enum SelectedTextHighlight {}
240
241#[derive(Debug, Copy, Clone, PartialEq, Eq)]
242pub enum Navigated {
243 Yes,
244 No,
245}
246
247impl Navigated {
248 pub fn from_bool(yes: bool) -> Navigated {
249 if yes {
250 Navigated::Yes
251 } else {
252 Navigated::No
253 }
254 }
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
258enum DisplayDiffHunk {
259 Folded {
260 display_row: DisplayRow,
261 },
262 Unfolded {
263 is_created_file: bool,
264 diff_base_byte_range: Range<usize>,
265 display_row_range: Range<DisplayRow>,
266 multi_buffer_range: Range<Anchor>,
267 status: DiffHunkStatus,
268 },
269}
270
271pub fn init_settings(cx: &mut App) {
272 EditorSettings::register(cx);
273}
274
275pub fn init(cx: &mut App) {
276 init_settings(cx);
277
278 workspace::register_project_item::<Editor>(cx);
279 workspace::FollowableViewRegistry::register::<Editor>(cx);
280 workspace::register_serializable_item::<Editor>(cx);
281
282 cx.observe_new(
283 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
284 workspace.register_action(Editor::new_file);
285 workspace.register_action(Editor::new_file_vertical);
286 workspace.register_action(Editor::new_file_horizontal);
287 workspace.register_action(Editor::cancel_language_server_work);
288 },
289 )
290 .detach();
291
292 cx.on_action(move |_: &workspace::NewFile, cx| {
293 let app_state = workspace::AppState::global(cx);
294 if let Some(app_state) = app_state.upgrade() {
295 workspace::open_new(
296 Default::default(),
297 app_state,
298 cx,
299 |workspace, window, cx| {
300 Editor::new_file(workspace, &Default::default(), window, cx)
301 },
302 )
303 .detach();
304 }
305 });
306 cx.on_action(move |_: &workspace::NewWindow, cx| {
307 let app_state = workspace::AppState::global(cx);
308 if let Some(app_state) = app_state.upgrade() {
309 workspace::open_new(
310 Default::default(),
311 app_state,
312 cx,
313 |workspace, window, cx| {
314 cx.activate(true);
315 Editor::new_file(workspace, &Default::default(), window, cx)
316 },
317 )
318 .detach();
319 }
320 });
321}
322
323pub struct SearchWithinRange;
324
325trait InvalidationRegion {
326 fn ranges(&self) -> &[Range<Anchor>];
327}
328
329#[derive(Clone, Debug, PartialEq)]
330pub enum SelectPhase {
331 Begin {
332 position: DisplayPoint,
333 add: bool,
334 click_count: usize,
335 },
336 BeginColumnar {
337 position: DisplayPoint,
338 reset: bool,
339 goal_column: u32,
340 },
341 Extend {
342 position: DisplayPoint,
343 click_count: usize,
344 },
345 Update {
346 position: DisplayPoint,
347 goal_column: u32,
348 scroll_delta: gpui::Point<f32>,
349 },
350 End,
351}
352
353#[derive(Clone, Debug)]
354pub enum SelectMode {
355 Character,
356 Word(Range<Anchor>),
357 Line(Range<Anchor>),
358 All,
359}
360
361#[derive(Copy, Clone, PartialEq, Eq, Debug)]
362pub enum EditorMode {
363 SingleLine { auto_width: bool },
364 AutoHeight { max_lines: usize },
365 Full,
366}
367
368#[derive(Copy, Clone, Debug)]
369pub enum SoftWrap {
370 /// Prefer not to wrap at all.
371 ///
372 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
373 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
374 GitDiff,
375 /// Prefer a single line generally, unless an overly long line is encountered.
376 None,
377 /// Soft wrap lines that exceed the editor width.
378 EditorWidth,
379 /// Soft wrap lines at the preferred line length.
380 Column(u32),
381 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
382 Bounded(u32),
383}
384
385#[derive(Clone)]
386pub struct EditorStyle {
387 pub background: Hsla,
388 pub local_player: PlayerColor,
389 pub text: TextStyle,
390 pub scrollbar_width: Pixels,
391 pub syntax: Arc<SyntaxTheme>,
392 pub status: StatusColors,
393 pub inlay_hints_style: HighlightStyle,
394 pub inline_completion_styles: InlineCompletionStyles,
395 pub unnecessary_code_fade: f32,
396}
397
398impl Default for EditorStyle {
399 fn default() -> Self {
400 Self {
401 background: Hsla::default(),
402 local_player: PlayerColor::default(),
403 text: TextStyle::default(),
404 scrollbar_width: Pixels::default(),
405 syntax: Default::default(),
406 // HACK: Status colors don't have a real default.
407 // We should look into removing the status colors from the editor
408 // style and retrieve them directly from the theme.
409 status: StatusColors::dark(),
410 inlay_hints_style: HighlightStyle::default(),
411 inline_completion_styles: InlineCompletionStyles {
412 insertion: HighlightStyle::default(),
413 whitespace: HighlightStyle::default(),
414 },
415 unnecessary_code_fade: Default::default(),
416 }
417 }
418}
419
420pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
421 let show_background = language_settings::language_settings(None, None, cx)
422 .inlay_hints
423 .show_background;
424
425 HighlightStyle {
426 color: Some(cx.theme().status().hint),
427 background_color: show_background.then(|| cx.theme().status().hint_background),
428 ..HighlightStyle::default()
429 }
430}
431
432pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
433 InlineCompletionStyles {
434 insertion: HighlightStyle {
435 color: Some(cx.theme().status().predictive),
436 ..HighlightStyle::default()
437 },
438 whitespace: HighlightStyle {
439 background_color: Some(cx.theme().status().created_background),
440 ..HighlightStyle::default()
441 },
442 }
443}
444
445type CompletionId = usize;
446
447pub(crate) enum EditDisplayMode {
448 TabAccept,
449 DiffPopover,
450 Inline,
451}
452
453enum InlineCompletion {
454 Edit {
455 edits: Vec<(Range<Anchor>, String)>,
456 edit_preview: Option<EditPreview>,
457 display_mode: EditDisplayMode,
458 snapshot: BufferSnapshot,
459 },
460 Move {
461 target: Anchor,
462 snapshot: BufferSnapshot,
463 },
464}
465
466struct InlineCompletionState {
467 inlay_ids: Vec<InlayId>,
468 completion: InlineCompletion,
469 completion_id: Option<SharedString>,
470 invalidation_range: Range<Anchor>,
471}
472
473enum EditPredictionSettings {
474 Disabled,
475 Enabled {
476 show_in_menu: bool,
477 preview_requires_modifier: bool,
478 },
479}
480
481enum InlineCompletionHighlight {}
482
483#[derive(Debug, Clone)]
484struct InlineDiagnostic {
485 message: SharedString,
486 group_id: usize,
487 is_primary: bool,
488 start: Point,
489 severity: DiagnosticSeverity,
490}
491
492pub enum MenuInlineCompletionsPolicy {
493 Never,
494 ByProvider,
495}
496
497pub enum EditPredictionPreview {
498 /// Modifier is not pressed
499 Inactive { released_too_fast: bool },
500 /// Modifier pressed
501 Active {
502 since: Instant,
503 previous_scroll_position: Option<ScrollAnchor>,
504 },
505}
506
507impl EditPredictionPreview {
508 pub fn released_too_fast(&self) -> bool {
509 match self {
510 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
511 EditPredictionPreview::Active { .. } => false,
512 }
513 }
514
515 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
516 if let EditPredictionPreview::Active {
517 previous_scroll_position,
518 ..
519 } = self
520 {
521 *previous_scroll_position = scroll_position;
522 }
523 }
524}
525
526#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
527struct EditorActionId(usize);
528
529impl EditorActionId {
530 pub fn post_inc(&mut self) -> Self {
531 let answer = self.0;
532
533 *self = Self(answer + 1);
534
535 Self(answer)
536 }
537}
538
539// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
540// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
541
542type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
543type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
544
545#[derive(Default)]
546struct ScrollbarMarkerState {
547 scrollbar_size: Size<Pixels>,
548 dirty: bool,
549 markers: Arc<[PaintQuad]>,
550 pending_refresh: Option<Task<Result<()>>>,
551}
552
553impl ScrollbarMarkerState {
554 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
555 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
556 }
557}
558
559#[derive(Clone, Debug)]
560struct RunnableTasks {
561 templates: Vec<(TaskSourceKind, TaskTemplate)>,
562 offset: multi_buffer::Anchor,
563 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
564 column: u32,
565 // Values of all named captures, including those starting with '_'
566 extra_variables: HashMap<String, String>,
567 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
568 context_range: Range<BufferOffset>,
569}
570
571impl RunnableTasks {
572 fn resolve<'a>(
573 &'a self,
574 cx: &'a task::TaskContext,
575 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
576 self.templates.iter().filter_map(|(kind, template)| {
577 template
578 .resolve_task(&kind.to_id_base(), cx)
579 .map(|task| (kind.clone(), task))
580 })
581 }
582}
583
584#[derive(Clone)]
585struct ResolvedTasks {
586 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
587 position: Anchor,
588}
589#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
590struct BufferOffset(usize);
591
592// Addons allow storing per-editor state in other crates (e.g. Vim)
593pub trait Addon: 'static {
594 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
595
596 fn render_buffer_header_controls(
597 &self,
598 _: &ExcerptInfo,
599 _: &Window,
600 _: &App,
601 ) -> Option<AnyElement> {
602 None
603 }
604
605 fn to_any(&self) -> &dyn std::any::Any;
606}
607
608/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
609///
610/// See the [module level documentation](self) for more information.
611pub struct Editor {
612 focus_handle: FocusHandle,
613 last_focused_descendant: Option<WeakFocusHandle>,
614 /// The text buffer being edited
615 buffer: Entity<MultiBuffer>,
616 /// Map of how text in the buffer should be displayed.
617 /// Handles soft wraps, folds, fake inlay text insertions, etc.
618 pub display_map: Entity<DisplayMap>,
619 pub selections: SelectionsCollection,
620 pub scroll_manager: ScrollManager,
621 /// When inline assist editors are linked, they all render cursors because
622 /// typing enters text into each of them, even the ones that aren't focused.
623 pub(crate) show_cursor_when_unfocused: bool,
624 columnar_selection_tail: Option<Anchor>,
625 add_selections_state: Option<AddSelectionsState>,
626 select_next_state: Option<SelectNextState>,
627 select_prev_state: Option<SelectNextState>,
628 selection_history: SelectionHistory,
629 autoclose_regions: Vec<AutocloseRegion>,
630 snippet_stack: InvalidationStack<SnippetState>,
631 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
632 ime_transaction: Option<TransactionId>,
633 active_diagnostics: Option<ActiveDiagnosticGroup>,
634 show_inline_diagnostics: bool,
635 inline_diagnostics_update: Task<()>,
636 inline_diagnostics_enabled: bool,
637 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
638 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
639 hard_wrap: Option<usize>,
640
641 // TODO: make this a access method
642 pub project: Option<Entity<Project>>,
643 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
644 completion_provider: Option<Box<dyn CompletionProvider>>,
645 collaboration_hub: Option<Box<dyn CollaborationHub>>,
646 blink_manager: Entity<BlinkManager>,
647 show_cursor_names: bool,
648 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
649 pub show_local_selections: bool,
650 mode: EditorMode,
651 show_breadcrumbs: bool,
652 show_gutter: bool,
653 show_scrollbars: bool,
654 show_line_numbers: Option<bool>,
655 use_relative_line_numbers: Option<bool>,
656 show_git_diff_gutter: Option<bool>,
657 show_code_actions: Option<bool>,
658 show_runnables: Option<bool>,
659 show_wrap_guides: Option<bool>,
660 show_indent_guides: Option<bool>,
661 placeholder_text: Option<Arc<str>>,
662 highlight_order: usize,
663 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
664 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
665 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
666 scrollbar_marker_state: ScrollbarMarkerState,
667 active_indent_guides_state: ActiveIndentGuidesState,
668 nav_history: Option<ItemNavHistory>,
669 context_menu: RefCell<Option<CodeContextMenu>>,
670 mouse_context_menu: Option<MouseContextMenu>,
671 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
672 signature_help_state: SignatureHelpState,
673 auto_signature_help: Option<bool>,
674 find_all_references_task_sources: Vec<Anchor>,
675 next_completion_id: CompletionId,
676 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
677 code_actions_task: Option<Task<Result<()>>>,
678 selection_highlight_task: Option<Task<()>>,
679 document_highlights_task: Option<Task<()>>,
680 linked_editing_range_task: Option<Task<Option<()>>>,
681 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
682 pending_rename: Option<RenameState>,
683 searchable: bool,
684 cursor_shape: CursorShape,
685 current_line_highlight: Option<CurrentLineHighlight>,
686 collapse_matches: bool,
687 autoindent_mode: Option<AutoindentMode>,
688 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
689 input_enabled: bool,
690 use_modal_editing: bool,
691 read_only: bool,
692 leader_peer_id: Option<PeerId>,
693 remote_id: Option<ViewId>,
694 hover_state: HoverState,
695 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
696 gutter_hovered: bool,
697 hovered_link_state: Option<HoveredLinkState>,
698 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
699 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
700 active_inline_completion: Option<InlineCompletionState>,
701 /// Used to prevent flickering as the user types while the menu is open
702 stale_inline_completion_in_menu: Option<InlineCompletionState>,
703 edit_prediction_settings: EditPredictionSettings,
704 inline_completions_hidden_for_vim_mode: bool,
705 show_inline_completions_override: Option<bool>,
706 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
707 edit_prediction_preview: EditPredictionPreview,
708 edit_prediction_indent_conflict: bool,
709 edit_prediction_requires_modifier_in_indent_conflict: bool,
710 inlay_hint_cache: InlayHintCache,
711 next_inlay_id: usize,
712 _subscriptions: Vec<Subscription>,
713 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
714 gutter_dimensions: GutterDimensions,
715 style: Option<EditorStyle>,
716 text_style_refinement: Option<TextStyleRefinement>,
717 next_editor_action_id: EditorActionId,
718 editor_actions:
719 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
720 use_autoclose: bool,
721 use_auto_surround: bool,
722 auto_replace_emoji_shortcode: bool,
723 show_git_blame_gutter: bool,
724 show_git_blame_inline: bool,
725 show_git_blame_inline_delay_task: Option<Task<()>>,
726 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
727 git_blame_inline_enabled: bool,
728 serialize_dirty_buffers: bool,
729 show_selection_menu: Option<bool>,
730 blame: Option<Entity<GitBlame>>,
731 blame_subscription: Option<Subscription>,
732 custom_context_menu: Option<
733 Box<
734 dyn 'static
735 + Fn(
736 &mut Self,
737 DisplayPoint,
738 &mut Window,
739 &mut Context<Self>,
740 ) -> Option<Entity<ui::ContextMenu>>,
741 >,
742 >,
743 last_bounds: Option<Bounds<Pixels>>,
744 last_position_map: Option<Rc<PositionMap>>,
745 expect_bounds_change: Option<Bounds<Pixels>>,
746 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
747 tasks_update_task: Option<Task<()>>,
748 in_project_search: bool,
749 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
750 breadcrumb_header: Option<String>,
751 focused_block: Option<FocusedBlock>,
752 next_scroll_position: NextScrollCursorCenterTopBottom,
753 addons: HashMap<TypeId, Box<dyn Addon>>,
754 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
755 load_diff_task: Option<Shared<Task<()>>>,
756 selection_mark_mode: bool,
757 toggle_fold_multiple_buffers: Task<()>,
758 _scroll_cursor_center_top_bottom_task: Task<()>,
759 serialize_selections: Task<()>,
760}
761
762#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
763enum NextScrollCursorCenterTopBottom {
764 #[default]
765 Center,
766 Top,
767 Bottom,
768}
769
770impl NextScrollCursorCenterTopBottom {
771 fn next(&self) -> Self {
772 match self {
773 Self::Center => Self::Top,
774 Self::Top => Self::Bottom,
775 Self::Bottom => Self::Center,
776 }
777 }
778}
779
780#[derive(Clone)]
781pub struct EditorSnapshot {
782 pub mode: EditorMode,
783 show_gutter: bool,
784 show_line_numbers: Option<bool>,
785 show_git_diff_gutter: Option<bool>,
786 show_code_actions: Option<bool>,
787 show_runnables: Option<bool>,
788 git_blame_gutter_max_author_length: Option<usize>,
789 pub display_snapshot: DisplaySnapshot,
790 pub placeholder_text: Option<Arc<str>>,
791 is_focused: bool,
792 scroll_anchor: ScrollAnchor,
793 ongoing_scroll: OngoingScroll,
794 current_line_highlight: CurrentLineHighlight,
795 gutter_hovered: bool,
796}
797
798const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
799
800#[derive(Default, Debug, Clone, Copy)]
801pub struct GutterDimensions {
802 pub left_padding: Pixels,
803 pub right_padding: Pixels,
804 pub width: Pixels,
805 pub margin: Pixels,
806 pub git_blame_entries_width: Option<Pixels>,
807}
808
809impl GutterDimensions {
810 /// The full width of the space taken up by the gutter.
811 pub fn full_width(&self) -> Pixels {
812 self.margin + self.width
813 }
814
815 /// The width of the space reserved for the fold indicators,
816 /// use alongside 'justify_end' and `gutter_width` to
817 /// right align content with the line numbers
818 pub fn fold_area_width(&self) -> Pixels {
819 self.margin + self.right_padding
820 }
821}
822
823#[derive(Debug)]
824pub struct RemoteSelection {
825 pub replica_id: ReplicaId,
826 pub selection: Selection<Anchor>,
827 pub cursor_shape: CursorShape,
828 pub peer_id: PeerId,
829 pub line_mode: bool,
830 pub participant_index: Option<ParticipantIndex>,
831 pub user_name: Option<SharedString>,
832}
833
834#[derive(Clone, Debug)]
835struct SelectionHistoryEntry {
836 selections: Arc<[Selection<Anchor>]>,
837 select_next_state: Option<SelectNextState>,
838 select_prev_state: Option<SelectNextState>,
839 add_selections_state: Option<AddSelectionsState>,
840}
841
842enum SelectionHistoryMode {
843 Normal,
844 Undoing,
845 Redoing,
846}
847
848#[derive(Clone, PartialEq, Eq, Hash)]
849struct HoveredCursor {
850 replica_id: u16,
851 selection_id: usize,
852}
853
854impl Default for SelectionHistoryMode {
855 fn default() -> Self {
856 Self::Normal
857 }
858}
859
860#[derive(Default)]
861struct SelectionHistory {
862 #[allow(clippy::type_complexity)]
863 selections_by_transaction:
864 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
865 mode: SelectionHistoryMode,
866 undo_stack: VecDeque<SelectionHistoryEntry>,
867 redo_stack: VecDeque<SelectionHistoryEntry>,
868}
869
870impl SelectionHistory {
871 fn insert_transaction(
872 &mut self,
873 transaction_id: TransactionId,
874 selections: Arc<[Selection<Anchor>]>,
875 ) {
876 self.selections_by_transaction
877 .insert(transaction_id, (selections, None));
878 }
879
880 #[allow(clippy::type_complexity)]
881 fn transaction(
882 &self,
883 transaction_id: TransactionId,
884 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
885 self.selections_by_transaction.get(&transaction_id)
886 }
887
888 #[allow(clippy::type_complexity)]
889 fn transaction_mut(
890 &mut self,
891 transaction_id: TransactionId,
892 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
893 self.selections_by_transaction.get_mut(&transaction_id)
894 }
895
896 fn push(&mut self, entry: SelectionHistoryEntry) {
897 if !entry.selections.is_empty() {
898 match self.mode {
899 SelectionHistoryMode::Normal => {
900 self.push_undo(entry);
901 self.redo_stack.clear();
902 }
903 SelectionHistoryMode::Undoing => self.push_redo(entry),
904 SelectionHistoryMode::Redoing => self.push_undo(entry),
905 }
906 }
907 }
908
909 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
910 if self
911 .undo_stack
912 .back()
913 .map_or(true, |e| e.selections != entry.selections)
914 {
915 self.undo_stack.push_back(entry);
916 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
917 self.undo_stack.pop_front();
918 }
919 }
920 }
921
922 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
923 if self
924 .redo_stack
925 .back()
926 .map_or(true, |e| e.selections != entry.selections)
927 {
928 self.redo_stack.push_back(entry);
929 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
930 self.redo_stack.pop_front();
931 }
932 }
933 }
934}
935
936struct RowHighlight {
937 index: usize,
938 range: Range<Anchor>,
939 color: Hsla,
940 should_autoscroll: bool,
941}
942
943#[derive(Clone, Debug)]
944struct AddSelectionsState {
945 above: bool,
946 stack: Vec<usize>,
947}
948
949#[derive(Clone)]
950struct SelectNextState {
951 query: AhoCorasick,
952 wordwise: bool,
953 done: bool,
954}
955
956impl std::fmt::Debug for SelectNextState {
957 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
958 f.debug_struct(std::any::type_name::<Self>())
959 .field("wordwise", &self.wordwise)
960 .field("done", &self.done)
961 .finish()
962 }
963}
964
965#[derive(Debug)]
966struct AutocloseRegion {
967 selection_id: usize,
968 range: Range<Anchor>,
969 pair: BracketPair,
970}
971
972#[derive(Debug)]
973struct SnippetState {
974 ranges: Vec<Vec<Range<Anchor>>>,
975 active_index: usize,
976 choices: Vec<Option<Vec<String>>>,
977}
978
979#[doc(hidden)]
980pub struct RenameState {
981 pub range: Range<Anchor>,
982 pub old_name: Arc<str>,
983 pub editor: Entity<Editor>,
984 block_id: CustomBlockId,
985}
986
987struct InvalidationStack<T>(Vec<T>);
988
989struct RegisteredInlineCompletionProvider {
990 provider: Arc<dyn InlineCompletionProviderHandle>,
991 _subscription: Subscription,
992}
993
994#[derive(Debug, PartialEq, Eq)]
995struct ActiveDiagnosticGroup {
996 primary_range: Range<Anchor>,
997 primary_message: String,
998 group_id: usize,
999 blocks: HashMap<CustomBlockId, Diagnostic>,
1000 is_valid: bool,
1001}
1002
1003#[derive(Serialize, Deserialize, Clone, Debug)]
1004pub struct ClipboardSelection {
1005 /// The number of bytes in this selection.
1006 pub len: usize,
1007 /// Whether this was a full-line selection.
1008 pub is_entire_line: bool,
1009 /// The indentation of the first line when this content was originally copied.
1010 pub first_line_indent: u32,
1011}
1012
1013#[derive(Debug)]
1014pub(crate) struct NavigationData {
1015 cursor_anchor: Anchor,
1016 cursor_position: Point,
1017 scroll_anchor: ScrollAnchor,
1018 scroll_top_row: u32,
1019}
1020
1021#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1022pub enum GotoDefinitionKind {
1023 Symbol,
1024 Declaration,
1025 Type,
1026 Implementation,
1027}
1028
1029#[derive(Debug, Clone)]
1030enum InlayHintRefreshReason {
1031 ModifiersChanged(bool),
1032 Toggle(bool),
1033 SettingsChange(InlayHintSettings),
1034 NewLinesShown,
1035 BufferEdited(HashSet<Arc<Language>>),
1036 RefreshRequested,
1037 ExcerptsRemoved(Vec<ExcerptId>),
1038}
1039
1040impl InlayHintRefreshReason {
1041 fn description(&self) -> &'static str {
1042 match self {
1043 Self::ModifiersChanged(_) => "modifiers changed",
1044 Self::Toggle(_) => "toggle",
1045 Self::SettingsChange(_) => "settings change",
1046 Self::NewLinesShown => "new lines shown",
1047 Self::BufferEdited(_) => "buffer edited",
1048 Self::RefreshRequested => "refresh requested",
1049 Self::ExcerptsRemoved(_) => "excerpts removed",
1050 }
1051 }
1052}
1053
1054pub enum FormatTarget {
1055 Buffers,
1056 Ranges(Vec<Range<MultiBufferPoint>>),
1057}
1058
1059pub(crate) struct FocusedBlock {
1060 id: BlockId,
1061 focus_handle: WeakFocusHandle,
1062}
1063
1064#[derive(Clone)]
1065enum JumpData {
1066 MultiBufferRow {
1067 row: MultiBufferRow,
1068 line_offset_from_top: u32,
1069 },
1070 MultiBufferPoint {
1071 excerpt_id: ExcerptId,
1072 position: Point,
1073 anchor: text::Anchor,
1074 line_offset_from_top: u32,
1075 },
1076}
1077
1078pub enum MultibufferSelectionMode {
1079 First,
1080 All,
1081}
1082
1083impl Editor {
1084 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1085 let buffer = cx.new(|cx| Buffer::local("", cx));
1086 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1087 Self::new(
1088 EditorMode::SingleLine { auto_width: false },
1089 buffer,
1090 None,
1091 false,
1092 window,
1093 cx,
1094 )
1095 }
1096
1097 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1098 let buffer = cx.new(|cx| Buffer::local("", cx));
1099 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1100 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1101 }
1102
1103 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1104 let buffer = cx.new(|cx| Buffer::local("", cx));
1105 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1106 Self::new(
1107 EditorMode::SingleLine { auto_width: true },
1108 buffer,
1109 None,
1110 false,
1111 window,
1112 cx,
1113 )
1114 }
1115
1116 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1117 let buffer = cx.new(|cx| Buffer::local("", cx));
1118 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1119 Self::new(
1120 EditorMode::AutoHeight { max_lines },
1121 buffer,
1122 None,
1123 false,
1124 window,
1125 cx,
1126 )
1127 }
1128
1129 pub fn for_buffer(
1130 buffer: Entity<Buffer>,
1131 project: Option<Entity<Project>>,
1132 window: &mut Window,
1133 cx: &mut Context<Self>,
1134 ) -> Self {
1135 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1136 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1137 }
1138
1139 pub fn for_multibuffer(
1140 buffer: Entity<MultiBuffer>,
1141 project: Option<Entity<Project>>,
1142 show_excerpt_controls: bool,
1143 window: &mut Window,
1144 cx: &mut Context<Self>,
1145 ) -> Self {
1146 Self::new(
1147 EditorMode::Full,
1148 buffer,
1149 project,
1150 show_excerpt_controls,
1151 window,
1152 cx,
1153 )
1154 }
1155
1156 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1157 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1158 let mut clone = Self::new(
1159 self.mode,
1160 self.buffer.clone(),
1161 self.project.clone(),
1162 show_excerpt_controls,
1163 window,
1164 cx,
1165 );
1166 self.display_map.update(cx, |display_map, cx| {
1167 let snapshot = display_map.snapshot(cx);
1168 clone.display_map.update(cx, |display_map, cx| {
1169 display_map.set_state(&snapshot, cx);
1170 });
1171 });
1172 clone.selections.clone_state(&self.selections);
1173 clone.scroll_manager.clone_state(&self.scroll_manager);
1174 clone.searchable = self.searchable;
1175 clone
1176 }
1177
1178 pub fn new(
1179 mode: EditorMode,
1180 buffer: Entity<MultiBuffer>,
1181 project: Option<Entity<Project>>,
1182 show_excerpt_controls: bool,
1183 window: &mut Window,
1184 cx: &mut Context<Self>,
1185 ) -> Self {
1186 let style = window.text_style();
1187 let font_size = style.font_size.to_pixels(window.rem_size());
1188 let editor = cx.entity().downgrade();
1189 let fold_placeholder = FoldPlaceholder {
1190 constrain_width: true,
1191 render: Arc::new(move |fold_id, fold_range, cx| {
1192 let editor = editor.clone();
1193 div()
1194 .id(fold_id)
1195 .bg(cx.theme().colors().ghost_element_background)
1196 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1197 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1198 .rounded_sm()
1199 .size_full()
1200 .cursor_pointer()
1201 .child("⋯")
1202 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1203 .on_click(move |_, _window, cx| {
1204 editor
1205 .update(cx, |editor, cx| {
1206 editor.unfold_ranges(
1207 &[fold_range.start..fold_range.end],
1208 true,
1209 false,
1210 cx,
1211 );
1212 cx.stop_propagation();
1213 })
1214 .ok();
1215 })
1216 .into_any()
1217 }),
1218 merge_adjacent: true,
1219 ..Default::default()
1220 };
1221 let display_map = cx.new(|cx| {
1222 DisplayMap::new(
1223 buffer.clone(),
1224 style.font(),
1225 font_size,
1226 None,
1227 show_excerpt_controls,
1228 FILE_HEADER_HEIGHT,
1229 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1230 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1231 fold_placeholder,
1232 cx,
1233 )
1234 });
1235
1236 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1237
1238 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1239
1240 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1241 .then(|| language_settings::SoftWrap::None);
1242
1243 let mut project_subscriptions = Vec::new();
1244 if mode == EditorMode::Full {
1245 if let Some(project) = project.as_ref() {
1246 project_subscriptions.push(cx.subscribe_in(
1247 project,
1248 window,
1249 |editor, _, event, window, cx| {
1250 if let project::Event::RefreshInlayHints = event {
1251 editor
1252 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1253 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1254 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1255 let focus_handle = editor.focus_handle(cx);
1256 if focus_handle.is_focused(window) {
1257 let snapshot = buffer.read(cx).snapshot();
1258 for (range, snippet) in snippet_edits {
1259 let editor_range =
1260 language::range_from_lsp(*range).to_offset(&snapshot);
1261 editor
1262 .insert_snippet(
1263 &[editor_range],
1264 snippet.clone(),
1265 window,
1266 cx,
1267 )
1268 .ok();
1269 }
1270 }
1271 }
1272 }
1273 },
1274 ));
1275 if let Some(task_inventory) = project
1276 .read(cx)
1277 .task_store()
1278 .read(cx)
1279 .task_inventory()
1280 .cloned()
1281 {
1282 project_subscriptions.push(cx.observe_in(
1283 &task_inventory,
1284 window,
1285 |editor, _, window, cx| {
1286 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1287 },
1288 ));
1289 }
1290 }
1291 }
1292
1293 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1294
1295 let inlay_hint_settings =
1296 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1297 let focus_handle = cx.focus_handle();
1298 cx.on_focus(&focus_handle, window, Self::handle_focus)
1299 .detach();
1300 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1301 .detach();
1302 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1303 .detach();
1304 cx.on_blur(&focus_handle, window, Self::handle_blur)
1305 .detach();
1306
1307 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1308 Some(false)
1309 } else {
1310 None
1311 };
1312
1313 let mut code_action_providers = Vec::new();
1314 let mut load_uncommitted_diff = None;
1315 if let Some(project) = project.clone() {
1316 load_uncommitted_diff = Some(
1317 get_uncommitted_diff_for_buffer(
1318 &project,
1319 buffer.read(cx).all_buffers(),
1320 buffer.clone(),
1321 cx,
1322 )
1323 .shared(),
1324 );
1325 code_action_providers.push(Rc::new(project) as Rc<_>);
1326 }
1327
1328 let mut this = Self {
1329 focus_handle,
1330 show_cursor_when_unfocused: false,
1331 last_focused_descendant: None,
1332 buffer: buffer.clone(),
1333 display_map: display_map.clone(),
1334 selections,
1335 scroll_manager: ScrollManager::new(cx),
1336 columnar_selection_tail: None,
1337 add_selections_state: None,
1338 select_next_state: None,
1339 select_prev_state: None,
1340 selection_history: Default::default(),
1341 autoclose_regions: Default::default(),
1342 snippet_stack: Default::default(),
1343 select_larger_syntax_node_stack: Vec::new(),
1344 ime_transaction: Default::default(),
1345 active_diagnostics: None,
1346 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1347 inline_diagnostics_update: Task::ready(()),
1348 inline_diagnostics: Vec::new(),
1349 soft_wrap_mode_override,
1350 hard_wrap: None,
1351 completion_provider: project.clone().map(|project| Box::new(project) as _),
1352 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1353 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1354 project,
1355 blink_manager: blink_manager.clone(),
1356 show_local_selections: true,
1357 show_scrollbars: true,
1358 mode,
1359 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1360 show_gutter: mode == EditorMode::Full,
1361 show_line_numbers: None,
1362 use_relative_line_numbers: None,
1363 show_git_diff_gutter: None,
1364 show_code_actions: None,
1365 show_runnables: None,
1366 show_wrap_guides: None,
1367 show_indent_guides,
1368 placeholder_text: None,
1369 highlight_order: 0,
1370 highlighted_rows: HashMap::default(),
1371 background_highlights: Default::default(),
1372 gutter_highlights: TreeMap::default(),
1373 scrollbar_marker_state: ScrollbarMarkerState::default(),
1374 active_indent_guides_state: ActiveIndentGuidesState::default(),
1375 nav_history: None,
1376 context_menu: RefCell::new(None),
1377 mouse_context_menu: None,
1378 completion_tasks: Default::default(),
1379 signature_help_state: SignatureHelpState::default(),
1380 auto_signature_help: None,
1381 find_all_references_task_sources: Vec::new(),
1382 next_completion_id: 0,
1383 next_inlay_id: 0,
1384 code_action_providers,
1385 available_code_actions: Default::default(),
1386 code_actions_task: Default::default(),
1387 selection_highlight_task: Default::default(),
1388 document_highlights_task: Default::default(),
1389 linked_editing_range_task: Default::default(),
1390 pending_rename: Default::default(),
1391 searchable: true,
1392 cursor_shape: EditorSettings::get_global(cx)
1393 .cursor_shape
1394 .unwrap_or_default(),
1395 current_line_highlight: None,
1396 autoindent_mode: Some(AutoindentMode::EachLine),
1397 collapse_matches: false,
1398 workspace: None,
1399 input_enabled: true,
1400 use_modal_editing: mode == EditorMode::Full,
1401 read_only: false,
1402 use_autoclose: true,
1403 use_auto_surround: true,
1404 auto_replace_emoji_shortcode: false,
1405 leader_peer_id: None,
1406 remote_id: None,
1407 hover_state: Default::default(),
1408 pending_mouse_down: None,
1409 hovered_link_state: Default::default(),
1410 edit_prediction_provider: None,
1411 active_inline_completion: None,
1412 stale_inline_completion_in_menu: None,
1413 edit_prediction_preview: EditPredictionPreview::Inactive {
1414 released_too_fast: false,
1415 },
1416 inline_diagnostics_enabled: mode == EditorMode::Full,
1417 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1418
1419 gutter_hovered: false,
1420 pixel_position_of_newest_cursor: None,
1421 last_bounds: None,
1422 last_position_map: None,
1423 expect_bounds_change: None,
1424 gutter_dimensions: GutterDimensions::default(),
1425 style: None,
1426 show_cursor_names: false,
1427 hovered_cursors: Default::default(),
1428 next_editor_action_id: EditorActionId::default(),
1429 editor_actions: Rc::default(),
1430 inline_completions_hidden_for_vim_mode: false,
1431 show_inline_completions_override: None,
1432 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1433 edit_prediction_settings: EditPredictionSettings::Disabled,
1434 edit_prediction_indent_conflict: false,
1435 edit_prediction_requires_modifier_in_indent_conflict: true,
1436 custom_context_menu: None,
1437 show_git_blame_gutter: false,
1438 show_git_blame_inline: false,
1439 show_selection_menu: None,
1440 show_git_blame_inline_delay_task: None,
1441 git_blame_inline_tooltip: None,
1442 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1443 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1444 .session
1445 .restore_unsaved_buffers,
1446 blame: None,
1447 blame_subscription: None,
1448 tasks: Default::default(),
1449 _subscriptions: vec![
1450 cx.observe(&buffer, Self::on_buffer_changed),
1451 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1452 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1453 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1454 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1455 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1456 cx.observe_window_activation(window, |editor, window, cx| {
1457 let active = window.is_window_active();
1458 editor.blink_manager.update(cx, |blink_manager, cx| {
1459 if active {
1460 blink_manager.enable(cx);
1461 } else {
1462 blink_manager.disable(cx);
1463 }
1464 });
1465 }),
1466 ],
1467 tasks_update_task: None,
1468 linked_edit_ranges: Default::default(),
1469 in_project_search: false,
1470 previous_search_ranges: None,
1471 breadcrumb_header: None,
1472 focused_block: None,
1473 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1474 addons: HashMap::default(),
1475 registered_buffers: HashMap::default(),
1476 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1477 selection_mark_mode: false,
1478 toggle_fold_multiple_buffers: Task::ready(()),
1479 serialize_selections: Task::ready(()),
1480 text_style_refinement: None,
1481 load_diff_task: load_uncommitted_diff,
1482 };
1483 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1484 this._subscriptions.extend(project_subscriptions);
1485
1486 this.end_selection(window, cx);
1487 this.scroll_manager.show_scrollbar(window, cx);
1488
1489 if mode == EditorMode::Full {
1490 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1491 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1492
1493 if this.git_blame_inline_enabled {
1494 this.git_blame_inline_enabled = true;
1495 this.start_git_blame_inline(false, window, cx);
1496 }
1497
1498 if let Some(buffer) = buffer.read(cx).as_singleton() {
1499 if let Some(project) = this.project.as_ref() {
1500 let handle = project.update(cx, |project, cx| {
1501 project.register_buffer_with_language_servers(&buffer, cx)
1502 });
1503 this.registered_buffers
1504 .insert(buffer.read(cx).remote_id(), handle);
1505 }
1506 }
1507 }
1508
1509 this.report_editor_event("Editor Opened", None, cx);
1510 this
1511 }
1512
1513 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1514 self.mouse_context_menu
1515 .as_ref()
1516 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1517 }
1518
1519 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1520 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1521 }
1522
1523 fn key_context_internal(
1524 &self,
1525 has_active_edit_prediction: bool,
1526 window: &Window,
1527 cx: &App,
1528 ) -> KeyContext {
1529 let mut key_context = KeyContext::new_with_defaults();
1530 key_context.add("Editor");
1531 let mode = match self.mode {
1532 EditorMode::SingleLine { .. } => "single_line",
1533 EditorMode::AutoHeight { .. } => "auto_height",
1534 EditorMode::Full => "full",
1535 };
1536
1537 if EditorSettings::jupyter_enabled(cx) {
1538 key_context.add("jupyter");
1539 }
1540
1541 key_context.set("mode", mode);
1542 if self.pending_rename.is_some() {
1543 key_context.add("renaming");
1544 }
1545
1546 match self.context_menu.borrow().as_ref() {
1547 Some(CodeContextMenu::Completions(_)) => {
1548 key_context.add("menu");
1549 key_context.add("showing_completions");
1550 }
1551 Some(CodeContextMenu::CodeActions(_)) => {
1552 key_context.add("menu");
1553 key_context.add("showing_code_actions")
1554 }
1555 None => {}
1556 }
1557
1558 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1559 if !self.focus_handle(cx).contains_focused(window, cx)
1560 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1561 {
1562 for addon in self.addons.values() {
1563 addon.extend_key_context(&mut key_context, cx)
1564 }
1565 }
1566
1567 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1568 if let Some(extension) = singleton_buffer
1569 .read(cx)
1570 .file()
1571 .and_then(|file| file.path().extension()?.to_str())
1572 {
1573 key_context.set("extension", extension.to_string());
1574 }
1575 } else {
1576 key_context.add("multibuffer");
1577 }
1578
1579 if has_active_edit_prediction {
1580 if self.edit_prediction_in_conflict() {
1581 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1582 } else {
1583 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1584 key_context.add("copilot_suggestion");
1585 }
1586 }
1587
1588 if self.selection_mark_mode {
1589 key_context.add("selection_mode");
1590 }
1591
1592 key_context
1593 }
1594
1595 pub fn edit_prediction_in_conflict(&self) -> bool {
1596 if !self.show_edit_predictions_in_menu() {
1597 return false;
1598 }
1599
1600 let showing_completions = self
1601 .context_menu
1602 .borrow()
1603 .as_ref()
1604 .map_or(false, |context| {
1605 matches!(context, CodeContextMenu::Completions(_))
1606 });
1607
1608 showing_completions
1609 || self.edit_prediction_requires_modifier()
1610 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1611 // bindings to insert tab characters.
1612 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1613 }
1614
1615 pub fn accept_edit_prediction_keybind(
1616 &self,
1617 window: &Window,
1618 cx: &App,
1619 ) -> AcceptEditPredictionBinding {
1620 let key_context = self.key_context_internal(true, window, cx);
1621 let in_conflict = self.edit_prediction_in_conflict();
1622
1623 AcceptEditPredictionBinding(
1624 window
1625 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1626 .into_iter()
1627 .filter(|binding| {
1628 !in_conflict
1629 || binding
1630 .keystrokes()
1631 .first()
1632 .map_or(false, |keystroke| keystroke.modifiers.modified())
1633 })
1634 .rev()
1635 .min_by_key(|binding| {
1636 binding
1637 .keystrokes()
1638 .first()
1639 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1640 }),
1641 )
1642 }
1643
1644 pub fn new_file(
1645 workspace: &mut Workspace,
1646 _: &workspace::NewFile,
1647 window: &mut Window,
1648 cx: &mut Context<Workspace>,
1649 ) {
1650 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1651 "Failed to create buffer",
1652 window,
1653 cx,
1654 |e, _, _| match e.error_code() {
1655 ErrorCode::RemoteUpgradeRequired => Some(format!(
1656 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1657 e.error_tag("required").unwrap_or("the latest version")
1658 )),
1659 _ => None,
1660 },
1661 );
1662 }
1663
1664 pub fn new_in_workspace(
1665 workspace: &mut Workspace,
1666 window: &mut Window,
1667 cx: &mut Context<Workspace>,
1668 ) -> Task<Result<Entity<Editor>>> {
1669 let project = workspace.project().clone();
1670 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1671
1672 cx.spawn_in(window, |workspace, mut cx| async move {
1673 let buffer = create.await?;
1674 workspace.update_in(&mut cx, |workspace, window, cx| {
1675 let editor =
1676 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1677 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1678 editor
1679 })
1680 })
1681 }
1682
1683 fn new_file_vertical(
1684 workspace: &mut Workspace,
1685 _: &workspace::NewFileSplitVertical,
1686 window: &mut Window,
1687 cx: &mut Context<Workspace>,
1688 ) {
1689 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1690 }
1691
1692 fn new_file_horizontal(
1693 workspace: &mut Workspace,
1694 _: &workspace::NewFileSplitHorizontal,
1695 window: &mut Window,
1696 cx: &mut Context<Workspace>,
1697 ) {
1698 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1699 }
1700
1701 fn new_file_in_direction(
1702 workspace: &mut Workspace,
1703 direction: SplitDirection,
1704 window: &mut Window,
1705 cx: &mut Context<Workspace>,
1706 ) {
1707 let project = workspace.project().clone();
1708 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1709
1710 cx.spawn_in(window, |workspace, mut cx| async move {
1711 let buffer = create.await?;
1712 workspace.update_in(&mut cx, move |workspace, window, cx| {
1713 workspace.split_item(
1714 direction,
1715 Box::new(
1716 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1717 ),
1718 window,
1719 cx,
1720 )
1721 })?;
1722 anyhow::Ok(())
1723 })
1724 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1725 match e.error_code() {
1726 ErrorCode::RemoteUpgradeRequired => Some(format!(
1727 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1728 e.error_tag("required").unwrap_or("the latest version")
1729 )),
1730 _ => None,
1731 }
1732 });
1733 }
1734
1735 pub fn leader_peer_id(&self) -> Option<PeerId> {
1736 self.leader_peer_id
1737 }
1738
1739 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1740 &self.buffer
1741 }
1742
1743 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1744 self.workspace.as_ref()?.0.upgrade()
1745 }
1746
1747 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1748 self.buffer().read(cx).title(cx)
1749 }
1750
1751 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1752 let git_blame_gutter_max_author_length = self
1753 .render_git_blame_gutter(cx)
1754 .then(|| {
1755 if let Some(blame) = self.blame.as_ref() {
1756 let max_author_length =
1757 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1758 Some(max_author_length)
1759 } else {
1760 None
1761 }
1762 })
1763 .flatten();
1764
1765 EditorSnapshot {
1766 mode: self.mode,
1767 show_gutter: self.show_gutter,
1768 show_line_numbers: self.show_line_numbers,
1769 show_git_diff_gutter: self.show_git_diff_gutter,
1770 show_code_actions: self.show_code_actions,
1771 show_runnables: self.show_runnables,
1772 git_blame_gutter_max_author_length,
1773 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1774 scroll_anchor: self.scroll_manager.anchor(),
1775 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1776 placeholder_text: self.placeholder_text.clone(),
1777 is_focused: self.focus_handle.is_focused(window),
1778 current_line_highlight: self
1779 .current_line_highlight
1780 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1781 gutter_hovered: self.gutter_hovered,
1782 }
1783 }
1784
1785 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1786 self.buffer.read(cx).language_at(point, cx)
1787 }
1788
1789 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1790 self.buffer.read(cx).read(cx).file_at(point).cloned()
1791 }
1792
1793 pub fn active_excerpt(
1794 &self,
1795 cx: &App,
1796 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1797 self.buffer
1798 .read(cx)
1799 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1800 }
1801
1802 pub fn mode(&self) -> EditorMode {
1803 self.mode
1804 }
1805
1806 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1807 self.collaboration_hub.as_deref()
1808 }
1809
1810 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1811 self.collaboration_hub = Some(hub);
1812 }
1813
1814 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1815 self.in_project_search = in_project_search;
1816 }
1817
1818 pub fn set_custom_context_menu(
1819 &mut self,
1820 f: impl 'static
1821 + Fn(
1822 &mut Self,
1823 DisplayPoint,
1824 &mut Window,
1825 &mut Context<Self>,
1826 ) -> Option<Entity<ui::ContextMenu>>,
1827 ) {
1828 self.custom_context_menu = Some(Box::new(f))
1829 }
1830
1831 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1832 self.completion_provider = provider;
1833 }
1834
1835 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1836 self.semantics_provider.clone()
1837 }
1838
1839 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1840 self.semantics_provider = provider;
1841 }
1842
1843 pub fn set_edit_prediction_provider<T>(
1844 &mut self,
1845 provider: Option<Entity<T>>,
1846 window: &mut Window,
1847 cx: &mut Context<Self>,
1848 ) where
1849 T: EditPredictionProvider,
1850 {
1851 self.edit_prediction_provider =
1852 provider.map(|provider| RegisteredInlineCompletionProvider {
1853 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1854 if this.focus_handle.is_focused(window) {
1855 this.update_visible_inline_completion(window, cx);
1856 }
1857 }),
1858 provider: Arc::new(provider),
1859 });
1860 self.update_edit_prediction_settings(cx);
1861 self.refresh_inline_completion(false, false, window, cx);
1862 }
1863
1864 pub fn placeholder_text(&self) -> Option<&str> {
1865 self.placeholder_text.as_deref()
1866 }
1867
1868 pub fn set_placeholder_text(
1869 &mut self,
1870 placeholder_text: impl Into<Arc<str>>,
1871 cx: &mut Context<Self>,
1872 ) {
1873 let placeholder_text = Some(placeholder_text.into());
1874 if self.placeholder_text != placeholder_text {
1875 self.placeholder_text = placeholder_text;
1876 cx.notify();
1877 }
1878 }
1879
1880 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1881 self.cursor_shape = cursor_shape;
1882
1883 // Disrupt blink for immediate user feedback that the cursor shape has changed
1884 self.blink_manager.update(cx, BlinkManager::show_cursor);
1885
1886 cx.notify();
1887 }
1888
1889 pub fn set_current_line_highlight(
1890 &mut self,
1891 current_line_highlight: Option<CurrentLineHighlight>,
1892 ) {
1893 self.current_line_highlight = current_line_highlight;
1894 }
1895
1896 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1897 self.collapse_matches = collapse_matches;
1898 }
1899
1900 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1901 let buffers = self.buffer.read(cx).all_buffers();
1902 let Some(project) = self.project.as_ref() else {
1903 return;
1904 };
1905 project.update(cx, |project, cx| {
1906 for buffer in buffers {
1907 self.registered_buffers
1908 .entry(buffer.read(cx).remote_id())
1909 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1910 }
1911 })
1912 }
1913
1914 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1915 if self.collapse_matches {
1916 return range.start..range.start;
1917 }
1918 range.clone()
1919 }
1920
1921 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1922 if self.display_map.read(cx).clip_at_line_ends != clip {
1923 self.display_map
1924 .update(cx, |map, _| map.clip_at_line_ends = clip);
1925 }
1926 }
1927
1928 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1929 self.input_enabled = input_enabled;
1930 }
1931
1932 pub fn set_inline_completions_hidden_for_vim_mode(
1933 &mut self,
1934 hidden: bool,
1935 window: &mut Window,
1936 cx: &mut Context<Self>,
1937 ) {
1938 if hidden != self.inline_completions_hidden_for_vim_mode {
1939 self.inline_completions_hidden_for_vim_mode = hidden;
1940 if hidden {
1941 self.update_visible_inline_completion(window, cx);
1942 } else {
1943 self.refresh_inline_completion(true, false, window, cx);
1944 }
1945 }
1946 }
1947
1948 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1949 self.menu_inline_completions_policy = value;
1950 }
1951
1952 pub fn set_autoindent(&mut self, autoindent: bool) {
1953 if autoindent {
1954 self.autoindent_mode = Some(AutoindentMode::EachLine);
1955 } else {
1956 self.autoindent_mode = None;
1957 }
1958 }
1959
1960 pub fn read_only(&self, cx: &App) -> bool {
1961 self.read_only || self.buffer.read(cx).read_only()
1962 }
1963
1964 pub fn set_read_only(&mut self, read_only: bool) {
1965 self.read_only = read_only;
1966 }
1967
1968 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1969 self.use_autoclose = autoclose;
1970 }
1971
1972 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1973 self.use_auto_surround = auto_surround;
1974 }
1975
1976 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1977 self.auto_replace_emoji_shortcode = auto_replace;
1978 }
1979
1980 pub fn toggle_edit_predictions(
1981 &mut self,
1982 _: &ToggleEditPrediction,
1983 window: &mut Window,
1984 cx: &mut Context<Self>,
1985 ) {
1986 if self.show_inline_completions_override.is_some() {
1987 self.set_show_edit_predictions(None, window, cx);
1988 } else {
1989 let show_edit_predictions = !self.edit_predictions_enabled();
1990 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1991 }
1992 }
1993
1994 pub fn set_show_edit_predictions(
1995 &mut self,
1996 show_edit_predictions: Option<bool>,
1997 window: &mut Window,
1998 cx: &mut Context<Self>,
1999 ) {
2000 self.show_inline_completions_override = show_edit_predictions;
2001 self.update_edit_prediction_settings(cx);
2002
2003 if let Some(false) = show_edit_predictions {
2004 self.discard_inline_completion(false, cx);
2005 } else {
2006 self.refresh_inline_completion(false, true, window, cx);
2007 }
2008 }
2009
2010 fn inline_completions_disabled_in_scope(
2011 &self,
2012 buffer: &Entity<Buffer>,
2013 buffer_position: language::Anchor,
2014 cx: &App,
2015 ) -> bool {
2016 let snapshot = buffer.read(cx).snapshot();
2017 let settings = snapshot.settings_at(buffer_position, cx);
2018
2019 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2020 return false;
2021 };
2022
2023 scope.override_name().map_or(false, |scope_name| {
2024 settings
2025 .edit_predictions_disabled_in
2026 .iter()
2027 .any(|s| s == scope_name)
2028 })
2029 }
2030
2031 pub fn set_use_modal_editing(&mut self, to: bool) {
2032 self.use_modal_editing = to;
2033 }
2034
2035 pub fn use_modal_editing(&self) -> bool {
2036 self.use_modal_editing
2037 }
2038
2039 fn selections_did_change(
2040 &mut self,
2041 local: bool,
2042 old_cursor_position: &Anchor,
2043 show_completions: bool,
2044 window: &mut Window,
2045 cx: &mut Context<Self>,
2046 ) {
2047 window.invalidate_character_coordinates();
2048
2049 // Copy selections to primary selection buffer
2050 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2051 if local {
2052 let selections = self.selections.all::<usize>(cx);
2053 let buffer_handle = self.buffer.read(cx).read(cx);
2054
2055 let mut text = String::new();
2056 for (index, selection) in selections.iter().enumerate() {
2057 let text_for_selection = buffer_handle
2058 .text_for_range(selection.start..selection.end)
2059 .collect::<String>();
2060
2061 text.push_str(&text_for_selection);
2062 if index != selections.len() - 1 {
2063 text.push('\n');
2064 }
2065 }
2066
2067 if !text.is_empty() {
2068 cx.write_to_primary(ClipboardItem::new_string(text));
2069 }
2070 }
2071
2072 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2073 self.buffer.update(cx, |buffer, cx| {
2074 buffer.set_active_selections(
2075 &self.selections.disjoint_anchors(),
2076 self.selections.line_mode,
2077 self.cursor_shape,
2078 cx,
2079 )
2080 });
2081 }
2082 let display_map = self
2083 .display_map
2084 .update(cx, |display_map, cx| display_map.snapshot(cx));
2085 let buffer = &display_map.buffer_snapshot;
2086 self.add_selections_state = None;
2087 self.select_next_state = None;
2088 self.select_prev_state = None;
2089 self.select_larger_syntax_node_stack.clear();
2090 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2091 self.snippet_stack
2092 .invalidate(&self.selections.disjoint_anchors(), buffer);
2093 self.take_rename(false, window, cx);
2094
2095 let new_cursor_position = self.selections.newest_anchor().head();
2096
2097 self.push_to_nav_history(
2098 *old_cursor_position,
2099 Some(new_cursor_position.to_point(buffer)),
2100 cx,
2101 );
2102
2103 if local {
2104 let new_cursor_position = self.selections.newest_anchor().head();
2105 let mut context_menu = self.context_menu.borrow_mut();
2106 let completion_menu = match context_menu.as_ref() {
2107 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2108 _ => {
2109 *context_menu = None;
2110 None
2111 }
2112 };
2113 if let Some(buffer_id) = new_cursor_position.buffer_id {
2114 if !self.registered_buffers.contains_key(&buffer_id) {
2115 if let Some(project) = self.project.as_ref() {
2116 project.update(cx, |project, cx| {
2117 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2118 return;
2119 };
2120 self.registered_buffers.insert(
2121 buffer_id,
2122 project.register_buffer_with_language_servers(&buffer, cx),
2123 );
2124 })
2125 }
2126 }
2127 }
2128
2129 if let Some(completion_menu) = completion_menu {
2130 let cursor_position = new_cursor_position.to_offset(buffer);
2131 let (word_range, kind) =
2132 buffer.surrounding_word(completion_menu.initial_position, true);
2133 if kind == Some(CharKind::Word)
2134 && word_range.to_inclusive().contains(&cursor_position)
2135 {
2136 let mut completion_menu = completion_menu.clone();
2137 drop(context_menu);
2138
2139 let query = Self::completion_query(buffer, cursor_position);
2140 cx.spawn(move |this, mut cx| async move {
2141 completion_menu
2142 .filter(query.as_deref(), cx.background_executor().clone())
2143 .await;
2144
2145 this.update(&mut cx, |this, cx| {
2146 let mut context_menu = this.context_menu.borrow_mut();
2147 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2148 else {
2149 return;
2150 };
2151
2152 if menu.id > completion_menu.id {
2153 return;
2154 }
2155
2156 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2157 drop(context_menu);
2158 cx.notify();
2159 })
2160 })
2161 .detach();
2162
2163 if show_completions {
2164 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2165 }
2166 } else {
2167 drop(context_menu);
2168 self.hide_context_menu(window, cx);
2169 }
2170 } else {
2171 drop(context_menu);
2172 }
2173
2174 hide_hover(self, cx);
2175
2176 if old_cursor_position.to_display_point(&display_map).row()
2177 != new_cursor_position.to_display_point(&display_map).row()
2178 {
2179 self.available_code_actions.take();
2180 }
2181 self.refresh_code_actions(window, cx);
2182 self.refresh_document_highlights(cx);
2183 self.refresh_selected_text_highlights(window, cx);
2184 refresh_matching_bracket_highlights(self, window, cx);
2185 self.update_visible_inline_completion(window, cx);
2186 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2187 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2188 if self.git_blame_inline_enabled {
2189 self.start_inline_blame_timer(window, cx);
2190 }
2191 }
2192
2193 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2194 cx.emit(EditorEvent::SelectionsChanged { local });
2195
2196 let selections = &self.selections.disjoint;
2197 if selections.len() == 1 {
2198 cx.emit(SearchEvent::ActiveMatchChanged)
2199 }
2200 if local
2201 && self.is_singleton(cx)
2202 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2203 {
2204 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2205 let background_executor = cx.background_executor().clone();
2206 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2207 let snapshot = self.buffer().read(cx).snapshot(cx);
2208 let selections = selections.clone();
2209 self.serialize_selections = cx.background_spawn(async move {
2210 background_executor.timer(Duration::from_millis(100)).await;
2211 let selections = selections
2212 .iter()
2213 .map(|selection| {
2214 (
2215 selection.start.to_offset(&snapshot),
2216 selection.end.to_offset(&snapshot),
2217 )
2218 })
2219 .collect();
2220 DB.save_editor_selections(editor_id, workspace_id, selections)
2221 .await
2222 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2223 .log_err();
2224 });
2225 }
2226 }
2227
2228 cx.notify();
2229 }
2230
2231 pub fn sync_selections(
2232 &mut self,
2233 other: Entity<Editor>,
2234 cx: &mut Context<Self>,
2235 ) -> gpui::Subscription {
2236 let other_selections = other.read(cx).selections.disjoint.to_vec();
2237 self.selections.change_with(cx, |selections| {
2238 selections.select_anchors(other_selections);
2239 });
2240
2241 let other_subscription =
2242 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2243 EditorEvent::SelectionsChanged { local: true } => {
2244 let other_selections = other.read(cx).selections.disjoint.to_vec();
2245 if other_selections.is_empty() {
2246 return;
2247 }
2248 this.selections.change_with(cx, |selections| {
2249 selections.select_anchors(other_selections);
2250 });
2251 }
2252 _ => {}
2253 });
2254
2255 let this_subscription =
2256 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2257 EditorEvent::SelectionsChanged { local: true } => {
2258 let these_selections = this.selections.disjoint.to_vec();
2259 if these_selections.is_empty() {
2260 return;
2261 }
2262 other.update(cx, |other_editor, cx| {
2263 other_editor.selections.change_with(cx, |selections| {
2264 selections.select_anchors(these_selections);
2265 })
2266 });
2267 }
2268 _ => {}
2269 });
2270
2271 Subscription::join(other_subscription, this_subscription)
2272 }
2273
2274 pub fn change_selections<R>(
2275 &mut self,
2276 autoscroll: Option<Autoscroll>,
2277 window: &mut Window,
2278 cx: &mut Context<Self>,
2279 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2280 ) -> R {
2281 self.change_selections_inner(autoscroll, true, window, cx, change)
2282 }
2283
2284 fn change_selections_inner<R>(
2285 &mut self,
2286 autoscroll: Option<Autoscroll>,
2287 request_completions: bool,
2288 window: &mut Window,
2289 cx: &mut Context<Self>,
2290 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2291 ) -> R {
2292 let old_cursor_position = self.selections.newest_anchor().head();
2293 self.push_to_selection_history();
2294
2295 let (changed, result) = self.selections.change_with(cx, change);
2296
2297 if changed {
2298 if let Some(autoscroll) = autoscroll {
2299 self.request_autoscroll(autoscroll, cx);
2300 }
2301 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2302
2303 if self.should_open_signature_help_automatically(
2304 &old_cursor_position,
2305 self.signature_help_state.backspace_pressed(),
2306 cx,
2307 ) {
2308 self.show_signature_help(&ShowSignatureHelp, window, cx);
2309 }
2310 self.signature_help_state.set_backspace_pressed(false);
2311 }
2312
2313 result
2314 }
2315
2316 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2317 where
2318 I: IntoIterator<Item = (Range<S>, T)>,
2319 S: ToOffset,
2320 T: Into<Arc<str>>,
2321 {
2322 if self.read_only(cx) {
2323 return;
2324 }
2325
2326 self.buffer
2327 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2328 }
2329
2330 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2331 where
2332 I: IntoIterator<Item = (Range<S>, T)>,
2333 S: ToOffset,
2334 T: Into<Arc<str>>,
2335 {
2336 if self.read_only(cx) {
2337 return;
2338 }
2339
2340 self.buffer.update(cx, |buffer, cx| {
2341 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2342 });
2343 }
2344
2345 pub fn edit_with_block_indent<I, S, T>(
2346 &mut self,
2347 edits: I,
2348 original_indent_columns: Vec<Option<u32>>,
2349 cx: &mut Context<Self>,
2350 ) where
2351 I: IntoIterator<Item = (Range<S>, T)>,
2352 S: ToOffset,
2353 T: Into<Arc<str>>,
2354 {
2355 if self.read_only(cx) {
2356 return;
2357 }
2358
2359 self.buffer.update(cx, |buffer, cx| {
2360 buffer.edit(
2361 edits,
2362 Some(AutoindentMode::Block {
2363 original_indent_columns,
2364 }),
2365 cx,
2366 )
2367 });
2368 }
2369
2370 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2371 self.hide_context_menu(window, cx);
2372
2373 match phase {
2374 SelectPhase::Begin {
2375 position,
2376 add,
2377 click_count,
2378 } => self.begin_selection(position, add, click_count, window, cx),
2379 SelectPhase::BeginColumnar {
2380 position,
2381 goal_column,
2382 reset,
2383 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2384 SelectPhase::Extend {
2385 position,
2386 click_count,
2387 } => self.extend_selection(position, click_count, window, cx),
2388 SelectPhase::Update {
2389 position,
2390 goal_column,
2391 scroll_delta,
2392 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2393 SelectPhase::End => self.end_selection(window, cx),
2394 }
2395 }
2396
2397 fn extend_selection(
2398 &mut self,
2399 position: DisplayPoint,
2400 click_count: usize,
2401 window: &mut Window,
2402 cx: &mut Context<Self>,
2403 ) {
2404 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2405 let tail = self.selections.newest::<usize>(cx).tail();
2406 self.begin_selection(position, false, click_count, window, cx);
2407
2408 let position = position.to_offset(&display_map, Bias::Left);
2409 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2410
2411 let mut pending_selection = self
2412 .selections
2413 .pending_anchor()
2414 .expect("extend_selection not called with pending selection");
2415 if position >= tail {
2416 pending_selection.start = tail_anchor;
2417 } else {
2418 pending_selection.end = tail_anchor;
2419 pending_selection.reversed = true;
2420 }
2421
2422 let mut pending_mode = self.selections.pending_mode().unwrap();
2423 match &mut pending_mode {
2424 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2425 _ => {}
2426 }
2427
2428 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2429 s.set_pending(pending_selection, pending_mode)
2430 });
2431 }
2432
2433 fn begin_selection(
2434 &mut self,
2435 position: DisplayPoint,
2436 add: bool,
2437 click_count: usize,
2438 window: &mut Window,
2439 cx: &mut Context<Self>,
2440 ) {
2441 if !self.focus_handle.is_focused(window) {
2442 self.last_focused_descendant = None;
2443 window.focus(&self.focus_handle);
2444 }
2445
2446 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2447 let buffer = &display_map.buffer_snapshot;
2448 let newest_selection = self.selections.newest_anchor().clone();
2449 let position = display_map.clip_point(position, Bias::Left);
2450
2451 let start;
2452 let end;
2453 let mode;
2454 let mut auto_scroll;
2455 match click_count {
2456 1 => {
2457 start = buffer.anchor_before(position.to_point(&display_map));
2458 end = start;
2459 mode = SelectMode::Character;
2460 auto_scroll = true;
2461 }
2462 2 => {
2463 let range = movement::surrounding_word(&display_map, position);
2464 start = buffer.anchor_before(range.start.to_point(&display_map));
2465 end = buffer.anchor_before(range.end.to_point(&display_map));
2466 mode = SelectMode::Word(start..end);
2467 auto_scroll = true;
2468 }
2469 3 => {
2470 let position = display_map
2471 .clip_point(position, Bias::Left)
2472 .to_point(&display_map);
2473 let line_start = display_map.prev_line_boundary(position).0;
2474 let next_line_start = buffer.clip_point(
2475 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2476 Bias::Left,
2477 );
2478 start = buffer.anchor_before(line_start);
2479 end = buffer.anchor_before(next_line_start);
2480 mode = SelectMode::Line(start..end);
2481 auto_scroll = true;
2482 }
2483 _ => {
2484 start = buffer.anchor_before(0);
2485 end = buffer.anchor_before(buffer.len());
2486 mode = SelectMode::All;
2487 auto_scroll = false;
2488 }
2489 }
2490 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2491
2492 let point_to_delete: Option<usize> = {
2493 let selected_points: Vec<Selection<Point>> =
2494 self.selections.disjoint_in_range(start..end, cx);
2495
2496 if !add || click_count > 1 {
2497 None
2498 } else if !selected_points.is_empty() {
2499 Some(selected_points[0].id)
2500 } else {
2501 let clicked_point_already_selected =
2502 self.selections.disjoint.iter().find(|selection| {
2503 selection.start.to_point(buffer) == start.to_point(buffer)
2504 || selection.end.to_point(buffer) == end.to_point(buffer)
2505 });
2506
2507 clicked_point_already_selected.map(|selection| selection.id)
2508 }
2509 };
2510
2511 let selections_count = self.selections.count();
2512
2513 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2514 if let Some(point_to_delete) = point_to_delete {
2515 s.delete(point_to_delete);
2516
2517 if selections_count == 1 {
2518 s.set_pending_anchor_range(start..end, mode);
2519 }
2520 } else {
2521 if !add {
2522 s.clear_disjoint();
2523 } else if click_count > 1 {
2524 s.delete(newest_selection.id)
2525 }
2526
2527 s.set_pending_anchor_range(start..end, mode);
2528 }
2529 });
2530 }
2531
2532 fn begin_columnar_selection(
2533 &mut self,
2534 position: DisplayPoint,
2535 goal_column: u32,
2536 reset: bool,
2537 window: &mut Window,
2538 cx: &mut Context<Self>,
2539 ) {
2540 if !self.focus_handle.is_focused(window) {
2541 self.last_focused_descendant = None;
2542 window.focus(&self.focus_handle);
2543 }
2544
2545 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2546
2547 if reset {
2548 let pointer_position = display_map
2549 .buffer_snapshot
2550 .anchor_before(position.to_point(&display_map));
2551
2552 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2553 s.clear_disjoint();
2554 s.set_pending_anchor_range(
2555 pointer_position..pointer_position,
2556 SelectMode::Character,
2557 );
2558 });
2559 }
2560
2561 let tail = self.selections.newest::<Point>(cx).tail();
2562 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2563
2564 if !reset {
2565 self.select_columns(
2566 tail.to_display_point(&display_map),
2567 position,
2568 goal_column,
2569 &display_map,
2570 window,
2571 cx,
2572 );
2573 }
2574 }
2575
2576 fn update_selection(
2577 &mut self,
2578 position: DisplayPoint,
2579 goal_column: u32,
2580 scroll_delta: gpui::Point<f32>,
2581 window: &mut Window,
2582 cx: &mut Context<Self>,
2583 ) {
2584 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2585
2586 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2587 let tail = tail.to_display_point(&display_map);
2588 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2589 } else if let Some(mut pending) = self.selections.pending_anchor() {
2590 let buffer = self.buffer.read(cx).snapshot(cx);
2591 let head;
2592 let tail;
2593 let mode = self.selections.pending_mode().unwrap();
2594 match &mode {
2595 SelectMode::Character => {
2596 head = position.to_point(&display_map);
2597 tail = pending.tail().to_point(&buffer);
2598 }
2599 SelectMode::Word(original_range) => {
2600 let original_display_range = original_range.start.to_display_point(&display_map)
2601 ..original_range.end.to_display_point(&display_map);
2602 let original_buffer_range = original_display_range.start.to_point(&display_map)
2603 ..original_display_range.end.to_point(&display_map);
2604 if movement::is_inside_word(&display_map, position)
2605 || original_display_range.contains(&position)
2606 {
2607 let word_range = movement::surrounding_word(&display_map, position);
2608 if word_range.start < original_display_range.start {
2609 head = word_range.start.to_point(&display_map);
2610 } else {
2611 head = word_range.end.to_point(&display_map);
2612 }
2613 } else {
2614 head = position.to_point(&display_map);
2615 }
2616
2617 if head <= original_buffer_range.start {
2618 tail = original_buffer_range.end;
2619 } else {
2620 tail = original_buffer_range.start;
2621 }
2622 }
2623 SelectMode::Line(original_range) => {
2624 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2625
2626 let position = display_map
2627 .clip_point(position, Bias::Left)
2628 .to_point(&display_map);
2629 let line_start = display_map.prev_line_boundary(position).0;
2630 let next_line_start = buffer.clip_point(
2631 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2632 Bias::Left,
2633 );
2634
2635 if line_start < original_range.start {
2636 head = line_start
2637 } else {
2638 head = next_line_start
2639 }
2640
2641 if head <= original_range.start {
2642 tail = original_range.end;
2643 } else {
2644 tail = original_range.start;
2645 }
2646 }
2647 SelectMode::All => {
2648 return;
2649 }
2650 };
2651
2652 if head < tail {
2653 pending.start = buffer.anchor_before(head);
2654 pending.end = buffer.anchor_before(tail);
2655 pending.reversed = true;
2656 } else {
2657 pending.start = buffer.anchor_before(tail);
2658 pending.end = buffer.anchor_before(head);
2659 pending.reversed = false;
2660 }
2661
2662 self.change_selections(None, window, cx, |s| {
2663 s.set_pending(pending, mode);
2664 });
2665 } else {
2666 log::error!("update_selection dispatched with no pending selection");
2667 return;
2668 }
2669
2670 self.apply_scroll_delta(scroll_delta, window, cx);
2671 cx.notify();
2672 }
2673
2674 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2675 self.columnar_selection_tail.take();
2676 if self.selections.pending_anchor().is_some() {
2677 let selections = self.selections.all::<usize>(cx);
2678 self.change_selections(None, window, cx, |s| {
2679 s.select(selections);
2680 s.clear_pending();
2681 });
2682 }
2683 }
2684
2685 fn select_columns(
2686 &mut self,
2687 tail: DisplayPoint,
2688 head: DisplayPoint,
2689 goal_column: u32,
2690 display_map: &DisplaySnapshot,
2691 window: &mut Window,
2692 cx: &mut Context<Self>,
2693 ) {
2694 let start_row = cmp::min(tail.row(), head.row());
2695 let end_row = cmp::max(tail.row(), head.row());
2696 let start_column = cmp::min(tail.column(), goal_column);
2697 let end_column = cmp::max(tail.column(), goal_column);
2698 let reversed = start_column < tail.column();
2699
2700 let selection_ranges = (start_row.0..=end_row.0)
2701 .map(DisplayRow)
2702 .filter_map(|row| {
2703 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2704 let start = display_map
2705 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2706 .to_point(display_map);
2707 let end = display_map
2708 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2709 .to_point(display_map);
2710 if reversed {
2711 Some(end..start)
2712 } else {
2713 Some(start..end)
2714 }
2715 } else {
2716 None
2717 }
2718 })
2719 .collect::<Vec<_>>();
2720
2721 self.change_selections(None, window, cx, |s| {
2722 s.select_ranges(selection_ranges);
2723 });
2724 cx.notify();
2725 }
2726
2727 pub fn has_pending_nonempty_selection(&self) -> bool {
2728 let pending_nonempty_selection = match self.selections.pending_anchor() {
2729 Some(Selection { start, end, .. }) => start != end,
2730 None => false,
2731 };
2732
2733 pending_nonempty_selection
2734 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2735 }
2736
2737 pub fn has_pending_selection(&self) -> bool {
2738 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2739 }
2740
2741 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2742 self.selection_mark_mode = false;
2743
2744 if self.clear_expanded_diff_hunks(cx) {
2745 cx.notify();
2746 return;
2747 }
2748 if self.dismiss_menus_and_popups(true, window, cx) {
2749 return;
2750 }
2751
2752 if self.mode == EditorMode::Full
2753 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2754 {
2755 return;
2756 }
2757
2758 cx.propagate();
2759 }
2760
2761 pub fn dismiss_menus_and_popups(
2762 &mut self,
2763 is_user_requested: bool,
2764 window: &mut Window,
2765 cx: &mut Context<Self>,
2766 ) -> bool {
2767 if self.take_rename(false, window, cx).is_some() {
2768 return true;
2769 }
2770
2771 if hide_hover(self, cx) {
2772 return true;
2773 }
2774
2775 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2776 return true;
2777 }
2778
2779 if self.hide_context_menu(window, cx).is_some() {
2780 return true;
2781 }
2782
2783 if self.mouse_context_menu.take().is_some() {
2784 return true;
2785 }
2786
2787 if is_user_requested && self.discard_inline_completion(true, cx) {
2788 return true;
2789 }
2790
2791 if self.snippet_stack.pop().is_some() {
2792 return true;
2793 }
2794
2795 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2796 self.dismiss_diagnostics(cx);
2797 return true;
2798 }
2799
2800 false
2801 }
2802
2803 fn linked_editing_ranges_for(
2804 &self,
2805 selection: Range<text::Anchor>,
2806 cx: &App,
2807 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2808 if self.linked_edit_ranges.is_empty() {
2809 return None;
2810 }
2811 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2812 selection.end.buffer_id.and_then(|end_buffer_id| {
2813 if selection.start.buffer_id != Some(end_buffer_id) {
2814 return None;
2815 }
2816 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2817 let snapshot = buffer.read(cx).snapshot();
2818 self.linked_edit_ranges
2819 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2820 .map(|ranges| (ranges, snapshot, buffer))
2821 })?;
2822 use text::ToOffset as TO;
2823 // find offset from the start of current range to current cursor position
2824 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2825
2826 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2827 let start_difference = start_offset - start_byte_offset;
2828 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2829 let end_difference = end_offset - start_byte_offset;
2830 // Current range has associated linked ranges.
2831 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2832 for range in linked_ranges.iter() {
2833 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2834 let end_offset = start_offset + end_difference;
2835 let start_offset = start_offset + start_difference;
2836 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2837 continue;
2838 }
2839 if self.selections.disjoint_anchor_ranges().any(|s| {
2840 if s.start.buffer_id != selection.start.buffer_id
2841 || s.end.buffer_id != selection.end.buffer_id
2842 {
2843 return false;
2844 }
2845 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2846 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2847 }) {
2848 continue;
2849 }
2850 let start = buffer_snapshot.anchor_after(start_offset);
2851 let end = buffer_snapshot.anchor_after(end_offset);
2852 linked_edits
2853 .entry(buffer.clone())
2854 .or_default()
2855 .push(start..end);
2856 }
2857 Some(linked_edits)
2858 }
2859
2860 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2861 let text: Arc<str> = text.into();
2862
2863 if self.read_only(cx) {
2864 return;
2865 }
2866
2867 let selections = self.selections.all_adjusted(cx);
2868 let mut bracket_inserted = false;
2869 let mut edits = Vec::new();
2870 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2871 let mut new_selections = Vec::with_capacity(selections.len());
2872 let mut new_autoclose_regions = Vec::new();
2873 let snapshot = self.buffer.read(cx).read(cx);
2874
2875 for (selection, autoclose_region) in
2876 self.selections_with_autoclose_regions(selections, &snapshot)
2877 {
2878 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2879 // Determine if the inserted text matches the opening or closing
2880 // bracket of any of this language's bracket pairs.
2881 let mut bracket_pair = None;
2882 let mut is_bracket_pair_start = false;
2883 let mut is_bracket_pair_end = false;
2884 if !text.is_empty() {
2885 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2886 // and they are removing the character that triggered IME popup.
2887 for (pair, enabled) in scope.brackets() {
2888 if !pair.close && !pair.surround {
2889 continue;
2890 }
2891
2892 if enabled && pair.start.ends_with(text.as_ref()) {
2893 let prefix_len = pair.start.len() - text.len();
2894 let preceding_text_matches_prefix = prefix_len == 0
2895 || (selection.start.column >= (prefix_len as u32)
2896 && snapshot.contains_str_at(
2897 Point::new(
2898 selection.start.row,
2899 selection.start.column - (prefix_len as u32),
2900 ),
2901 &pair.start[..prefix_len],
2902 ));
2903 if preceding_text_matches_prefix {
2904 bracket_pair = Some(pair.clone());
2905 is_bracket_pair_start = true;
2906 break;
2907 }
2908 }
2909 if pair.end.as_str() == text.as_ref() {
2910 bracket_pair = Some(pair.clone());
2911 is_bracket_pair_end = true;
2912 break;
2913 }
2914 }
2915 }
2916
2917 if let Some(bracket_pair) = bracket_pair {
2918 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2919 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2920 let auto_surround =
2921 self.use_auto_surround && snapshot_settings.use_auto_surround;
2922 if selection.is_empty() {
2923 if is_bracket_pair_start {
2924 // If the inserted text is a suffix of an opening bracket and the
2925 // selection is preceded by the rest of the opening bracket, then
2926 // insert the closing bracket.
2927 let following_text_allows_autoclose = snapshot
2928 .chars_at(selection.start)
2929 .next()
2930 .map_or(true, |c| scope.should_autoclose_before(c));
2931
2932 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2933 && bracket_pair.start.len() == 1
2934 {
2935 let target = bracket_pair.start.chars().next().unwrap();
2936 let current_line_count = snapshot
2937 .reversed_chars_at(selection.start)
2938 .take_while(|&c| c != '\n')
2939 .filter(|&c| c == target)
2940 .count();
2941 current_line_count % 2 == 1
2942 } else {
2943 false
2944 };
2945
2946 if autoclose
2947 && bracket_pair.close
2948 && following_text_allows_autoclose
2949 && !is_closing_quote
2950 {
2951 let anchor = snapshot.anchor_before(selection.end);
2952 new_selections.push((selection.map(|_| anchor), text.len()));
2953 new_autoclose_regions.push((
2954 anchor,
2955 text.len(),
2956 selection.id,
2957 bracket_pair.clone(),
2958 ));
2959 edits.push((
2960 selection.range(),
2961 format!("{}{}", text, bracket_pair.end).into(),
2962 ));
2963 bracket_inserted = true;
2964 continue;
2965 }
2966 }
2967
2968 if let Some(region) = autoclose_region {
2969 // If the selection is followed by an auto-inserted closing bracket,
2970 // then don't insert that closing bracket again; just move the selection
2971 // past the closing bracket.
2972 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2973 && text.as_ref() == region.pair.end.as_str();
2974 if should_skip {
2975 let anchor = snapshot.anchor_after(selection.end);
2976 new_selections
2977 .push((selection.map(|_| anchor), region.pair.end.len()));
2978 continue;
2979 }
2980 }
2981
2982 let always_treat_brackets_as_autoclosed = snapshot
2983 .language_settings_at(selection.start, cx)
2984 .always_treat_brackets_as_autoclosed;
2985 if always_treat_brackets_as_autoclosed
2986 && is_bracket_pair_end
2987 && snapshot.contains_str_at(selection.end, text.as_ref())
2988 {
2989 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2990 // and the inserted text is a closing bracket and the selection is followed
2991 // by the closing bracket then move the selection past the closing bracket.
2992 let anchor = snapshot.anchor_after(selection.end);
2993 new_selections.push((selection.map(|_| anchor), text.len()));
2994 continue;
2995 }
2996 }
2997 // If an opening bracket is 1 character long and is typed while
2998 // text is selected, then surround that text with the bracket pair.
2999 else if auto_surround
3000 && bracket_pair.surround
3001 && is_bracket_pair_start
3002 && bracket_pair.start.chars().count() == 1
3003 {
3004 edits.push((selection.start..selection.start, text.clone()));
3005 edits.push((
3006 selection.end..selection.end,
3007 bracket_pair.end.as_str().into(),
3008 ));
3009 bracket_inserted = true;
3010 new_selections.push((
3011 Selection {
3012 id: selection.id,
3013 start: snapshot.anchor_after(selection.start),
3014 end: snapshot.anchor_before(selection.end),
3015 reversed: selection.reversed,
3016 goal: selection.goal,
3017 },
3018 0,
3019 ));
3020 continue;
3021 }
3022 }
3023 }
3024
3025 if self.auto_replace_emoji_shortcode
3026 && selection.is_empty()
3027 && text.as_ref().ends_with(':')
3028 {
3029 if let Some(possible_emoji_short_code) =
3030 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3031 {
3032 if !possible_emoji_short_code.is_empty() {
3033 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3034 let emoji_shortcode_start = Point::new(
3035 selection.start.row,
3036 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3037 );
3038
3039 // Remove shortcode from buffer
3040 edits.push((
3041 emoji_shortcode_start..selection.start,
3042 "".to_string().into(),
3043 ));
3044 new_selections.push((
3045 Selection {
3046 id: selection.id,
3047 start: snapshot.anchor_after(emoji_shortcode_start),
3048 end: snapshot.anchor_before(selection.start),
3049 reversed: selection.reversed,
3050 goal: selection.goal,
3051 },
3052 0,
3053 ));
3054
3055 // Insert emoji
3056 let selection_start_anchor = snapshot.anchor_after(selection.start);
3057 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3058 edits.push((selection.start..selection.end, emoji.to_string().into()));
3059
3060 continue;
3061 }
3062 }
3063 }
3064 }
3065
3066 // If not handling any auto-close operation, then just replace the selected
3067 // text with the given input and move the selection to the end of the
3068 // newly inserted text.
3069 let anchor = snapshot.anchor_after(selection.end);
3070 if !self.linked_edit_ranges.is_empty() {
3071 let start_anchor = snapshot.anchor_before(selection.start);
3072
3073 let is_word_char = text.chars().next().map_or(true, |char| {
3074 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3075 classifier.is_word(char)
3076 });
3077
3078 if is_word_char {
3079 if let Some(ranges) = self
3080 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3081 {
3082 for (buffer, edits) in ranges {
3083 linked_edits
3084 .entry(buffer.clone())
3085 .or_default()
3086 .extend(edits.into_iter().map(|range| (range, text.clone())));
3087 }
3088 }
3089 }
3090 }
3091
3092 new_selections.push((selection.map(|_| anchor), 0));
3093 edits.push((selection.start..selection.end, text.clone()));
3094 }
3095
3096 drop(snapshot);
3097
3098 self.transact(window, cx, |this, window, cx| {
3099 this.buffer.update(cx, |buffer, cx| {
3100 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3101 });
3102 for (buffer, edits) in linked_edits {
3103 buffer.update(cx, |buffer, cx| {
3104 let snapshot = buffer.snapshot();
3105 let edits = edits
3106 .into_iter()
3107 .map(|(range, text)| {
3108 use text::ToPoint as TP;
3109 let end_point = TP::to_point(&range.end, &snapshot);
3110 let start_point = TP::to_point(&range.start, &snapshot);
3111 (start_point..end_point, text)
3112 })
3113 .sorted_by_key(|(range, _)| range.start)
3114 .collect::<Vec<_>>();
3115 buffer.edit(edits, None, cx);
3116 })
3117 }
3118 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3119 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3120 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3121 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3122 .zip(new_selection_deltas)
3123 .map(|(selection, delta)| Selection {
3124 id: selection.id,
3125 start: selection.start + delta,
3126 end: selection.end + delta,
3127 reversed: selection.reversed,
3128 goal: SelectionGoal::None,
3129 })
3130 .collect::<Vec<_>>();
3131
3132 let mut i = 0;
3133 for (position, delta, selection_id, pair) in new_autoclose_regions {
3134 let position = position.to_offset(&map.buffer_snapshot) + delta;
3135 let start = map.buffer_snapshot.anchor_before(position);
3136 let end = map.buffer_snapshot.anchor_after(position);
3137 while let Some(existing_state) = this.autoclose_regions.get(i) {
3138 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3139 Ordering::Less => i += 1,
3140 Ordering::Greater => break,
3141 Ordering::Equal => {
3142 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3143 Ordering::Less => i += 1,
3144 Ordering::Equal => break,
3145 Ordering::Greater => break,
3146 }
3147 }
3148 }
3149 }
3150 this.autoclose_regions.insert(
3151 i,
3152 AutocloseRegion {
3153 selection_id,
3154 range: start..end,
3155 pair,
3156 },
3157 );
3158 }
3159
3160 let had_active_inline_completion = this.has_active_inline_completion();
3161 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3162 s.select(new_selections)
3163 });
3164
3165 if !bracket_inserted {
3166 if let Some(on_type_format_task) =
3167 this.trigger_on_type_formatting(text.to_string(), window, cx)
3168 {
3169 on_type_format_task.detach_and_log_err(cx);
3170 }
3171 }
3172
3173 let editor_settings = EditorSettings::get_global(cx);
3174 if bracket_inserted
3175 && (editor_settings.auto_signature_help
3176 || editor_settings.show_signature_help_after_edits)
3177 {
3178 this.show_signature_help(&ShowSignatureHelp, window, cx);
3179 }
3180
3181 let trigger_in_words =
3182 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3183 if this.hard_wrap.is_some() {
3184 let latest: Range<Point> = this.selections.newest(cx).range();
3185 if latest.is_empty()
3186 && this
3187 .buffer()
3188 .read(cx)
3189 .snapshot(cx)
3190 .line_len(MultiBufferRow(latest.start.row))
3191 == latest.start.column
3192 {
3193 this.rewrap_impl(true, cx)
3194 }
3195 }
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 });
3200 }
3201
3202 fn find_possible_emoji_shortcode_at_position(
3203 snapshot: &MultiBufferSnapshot,
3204 position: Point,
3205 ) -> Option<String> {
3206 let mut chars = Vec::new();
3207 let mut found_colon = false;
3208 for char in snapshot.reversed_chars_at(position).take(100) {
3209 // Found a possible emoji shortcode in the middle of the buffer
3210 if found_colon {
3211 if char.is_whitespace() {
3212 chars.reverse();
3213 return Some(chars.iter().collect());
3214 }
3215 // If the previous character is not a whitespace, we are in the middle of a word
3216 // and we only want to complete the shortcode if the word is made up of other emojis
3217 let mut containing_word = String::new();
3218 for ch in snapshot
3219 .reversed_chars_at(position)
3220 .skip(chars.len() + 1)
3221 .take(100)
3222 {
3223 if ch.is_whitespace() {
3224 break;
3225 }
3226 containing_word.push(ch);
3227 }
3228 let containing_word = containing_word.chars().rev().collect::<String>();
3229 if util::word_consists_of_emojis(containing_word.as_str()) {
3230 chars.reverse();
3231 return Some(chars.iter().collect());
3232 }
3233 }
3234
3235 if char.is_whitespace() || !char.is_ascii() {
3236 return None;
3237 }
3238 if char == ':' {
3239 found_colon = true;
3240 } else {
3241 chars.push(char);
3242 }
3243 }
3244 // Found a possible emoji shortcode at the beginning of the buffer
3245 chars.reverse();
3246 Some(chars.iter().collect())
3247 }
3248
3249 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3250 self.transact(window, cx, |this, window, cx| {
3251 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3252 let selections = this.selections.all::<usize>(cx);
3253 let multi_buffer = this.buffer.read(cx);
3254 let buffer = multi_buffer.snapshot(cx);
3255 selections
3256 .iter()
3257 .map(|selection| {
3258 let start_point = selection.start.to_point(&buffer);
3259 let mut indent =
3260 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3261 indent.len = cmp::min(indent.len, start_point.column);
3262 let start = selection.start;
3263 let end = selection.end;
3264 let selection_is_empty = start == end;
3265 let language_scope = buffer.language_scope_at(start);
3266 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3267 &language_scope
3268 {
3269 let insert_extra_newline =
3270 insert_extra_newline_brackets(&buffer, start..end, language)
3271 || insert_extra_newline_tree_sitter(&buffer, start..end);
3272
3273 // Comment extension on newline is allowed only for cursor selections
3274 let comment_delimiter = maybe!({
3275 if !selection_is_empty {
3276 return None;
3277 }
3278
3279 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3280 return None;
3281 }
3282
3283 let delimiters = language.line_comment_prefixes();
3284 let max_len_of_delimiter =
3285 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3286 let (snapshot, range) =
3287 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3288
3289 let mut index_of_first_non_whitespace = 0;
3290 let comment_candidate = snapshot
3291 .chars_for_range(range)
3292 .skip_while(|c| {
3293 let should_skip = c.is_whitespace();
3294 if should_skip {
3295 index_of_first_non_whitespace += 1;
3296 }
3297 should_skip
3298 })
3299 .take(max_len_of_delimiter)
3300 .collect::<String>();
3301 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3302 comment_candidate.starts_with(comment_prefix.as_ref())
3303 })?;
3304 let cursor_is_placed_after_comment_marker =
3305 index_of_first_non_whitespace + comment_prefix.len()
3306 <= start_point.column as usize;
3307 if cursor_is_placed_after_comment_marker {
3308 Some(comment_prefix.clone())
3309 } else {
3310 None
3311 }
3312 });
3313 (comment_delimiter, insert_extra_newline)
3314 } else {
3315 (None, false)
3316 };
3317
3318 let capacity_for_delimiter = comment_delimiter
3319 .as_deref()
3320 .map(str::len)
3321 .unwrap_or_default();
3322 let mut new_text =
3323 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3324 new_text.push('\n');
3325 new_text.extend(indent.chars());
3326 if let Some(delimiter) = &comment_delimiter {
3327 new_text.push_str(delimiter);
3328 }
3329 if insert_extra_newline {
3330 new_text = new_text.repeat(2);
3331 }
3332
3333 let anchor = buffer.anchor_after(end);
3334 let new_selection = selection.map(|_| anchor);
3335 (
3336 (start..end, new_text),
3337 (insert_extra_newline, new_selection),
3338 )
3339 })
3340 .unzip()
3341 };
3342
3343 this.edit_with_autoindent(edits, cx);
3344 let buffer = this.buffer.read(cx).snapshot(cx);
3345 let new_selections = selection_fixup_info
3346 .into_iter()
3347 .map(|(extra_newline_inserted, new_selection)| {
3348 let mut cursor = new_selection.end.to_point(&buffer);
3349 if extra_newline_inserted {
3350 cursor.row -= 1;
3351 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3352 }
3353 new_selection.map(|_| cursor)
3354 })
3355 .collect();
3356
3357 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3358 s.select(new_selections)
3359 });
3360 this.refresh_inline_completion(true, false, window, cx);
3361 });
3362 }
3363
3364 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3365 let buffer = self.buffer.read(cx);
3366 let snapshot = buffer.snapshot(cx);
3367
3368 let mut edits = Vec::new();
3369 let mut rows = Vec::new();
3370
3371 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3372 let cursor = selection.head();
3373 let row = cursor.row;
3374
3375 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3376
3377 let newline = "\n".to_string();
3378 edits.push((start_of_line..start_of_line, newline));
3379
3380 rows.push(row + rows_inserted as u32);
3381 }
3382
3383 self.transact(window, cx, |editor, window, cx| {
3384 editor.edit(edits, cx);
3385
3386 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3387 let mut index = 0;
3388 s.move_cursors_with(|map, _, _| {
3389 let row = rows[index];
3390 index += 1;
3391
3392 let point = Point::new(row, 0);
3393 let boundary = map.next_line_boundary(point).1;
3394 let clipped = map.clip_point(boundary, Bias::Left);
3395
3396 (clipped, SelectionGoal::None)
3397 });
3398 });
3399
3400 let mut indent_edits = Vec::new();
3401 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3402 for row in rows {
3403 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3404 for (row, indent) in indents {
3405 if indent.len == 0 {
3406 continue;
3407 }
3408
3409 let text = match indent.kind {
3410 IndentKind::Space => " ".repeat(indent.len as usize),
3411 IndentKind::Tab => "\t".repeat(indent.len as usize),
3412 };
3413 let point = Point::new(row.0, 0);
3414 indent_edits.push((point..point, text));
3415 }
3416 }
3417 editor.edit(indent_edits, cx);
3418 });
3419 }
3420
3421 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3422 let buffer = self.buffer.read(cx);
3423 let snapshot = buffer.snapshot(cx);
3424
3425 let mut edits = Vec::new();
3426 let mut rows = Vec::new();
3427 let mut rows_inserted = 0;
3428
3429 for selection in self.selections.all_adjusted(cx) {
3430 let cursor = selection.head();
3431 let row = cursor.row;
3432
3433 let point = Point::new(row + 1, 0);
3434 let start_of_line = snapshot.clip_point(point, Bias::Left);
3435
3436 let newline = "\n".to_string();
3437 edits.push((start_of_line..start_of_line, newline));
3438
3439 rows_inserted += 1;
3440 rows.push(row + rows_inserted);
3441 }
3442
3443 self.transact(window, cx, |editor, window, cx| {
3444 editor.edit(edits, cx);
3445
3446 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3447 let mut index = 0;
3448 s.move_cursors_with(|map, _, _| {
3449 let row = rows[index];
3450 index += 1;
3451
3452 let point = Point::new(row, 0);
3453 let boundary = map.next_line_boundary(point).1;
3454 let clipped = map.clip_point(boundary, Bias::Left);
3455
3456 (clipped, SelectionGoal::None)
3457 });
3458 });
3459
3460 let mut indent_edits = Vec::new();
3461 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3462 for row in rows {
3463 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3464 for (row, indent) in indents {
3465 if indent.len == 0 {
3466 continue;
3467 }
3468
3469 let text = match indent.kind {
3470 IndentKind::Space => " ".repeat(indent.len as usize),
3471 IndentKind::Tab => "\t".repeat(indent.len as usize),
3472 };
3473 let point = Point::new(row.0, 0);
3474 indent_edits.push((point..point, text));
3475 }
3476 }
3477 editor.edit(indent_edits, cx);
3478 });
3479 }
3480
3481 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3482 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3483 original_indent_columns: Vec::new(),
3484 });
3485 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3486 }
3487
3488 fn insert_with_autoindent_mode(
3489 &mut self,
3490 text: &str,
3491 autoindent_mode: Option<AutoindentMode>,
3492 window: &mut Window,
3493 cx: &mut Context<Self>,
3494 ) {
3495 if self.read_only(cx) {
3496 return;
3497 }
3498
3499 let text: Arc<str> = text.into();
3500 self.transact(window, cx, |this, window, cx| {
3501 let old_selections = this.selections.all_adjusted(cx);
3502 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3503 let anchors = {
3504 let snapshot = buffer.read(cx);
3505 old_selections
3506 .iter()
3507 .map(|s| {
3508 let anchor = snapshot.anchor_after(s.head());
3509 s.map(|_| anchor)
3510 })
3511 .collect::<Vec<_>>()
3512 };
3513 buffer.edit(
3514 old_selections
3515 .iter()
3516 .map(|s| (s.start..s.end, text.clone())),
3517 autoindent_mode,
3518 cx,
3519 );
3520 anchors
3521 });
3522
3523 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3524 s.select_anchors(selection_anchors);
3525 });
3526
3527 cx.notify();
3528 });
3529 }
3530
3531 fn trigger_completion_on_input(
3532 &mut self,
3533 text: &str,
3534 trigger_in_words: bool,
3535 window: &mut Window,
3536 cx: &mut Context<Self>,
3537 ) {
3538 if self.is_completion_trigger(text, trigger_in_words, cx) {
3539 self.show_completions(
3540 &ShowCompletions {
3541 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3542 },
3543 window,
3544 cx,
3545 );
3546 } else {
3547 self.hide_context_menu(window, cx);
3548 }
3549 }
3550
3551 fn is_completion_trigger(
3552 &self,
3553 text: &str,
3554 trigger_in_words: bool,
3555 cx: &mut Context<Self>,
3556 ) -> bool {
3557 let position = self.selections.newest_anchor().head();
3558 let multibuffer = self.buffer.read(cx);
3559 let Some(buffer) = position
3560 .buffer_id
3561 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3562 else {
3563 return false;
3564 };
3565
3566 if let Some(completion_provider) = &self.completion_provider {
3567 completion_provider.is_completion_trigger(
3568 &buffer,
3569 position.text_anchor,
3570 text,
3571 trigger_in_words,
3572 cx,
3573 )
3574 } else {
3575 false
3576 }
3577 }
3578
3579 /// If any empty selections is touching the start of its innermost containing autoclose
3580 /// region, expand it to select the brackets.
3581 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3582 let selections = self.selections.all::<usize>(cx);
3583 let buffer = self.buffer.read(cx).read(cx);
3584 let new_selections = self
3585 .selections_with_autoclose_regions(selections, &buffer)
3586 .map(|(mut selection, region)| {
3587 if !selection.is_empty() {
3588 return selection;
3589 }
3590
3591 if let Some(region) = region {
3592 let mut range = region.range.to_offset(&buffer);
3593 if selection.start == range.start && range.start >= region.pair.start.len() {
3594 range.start -= region.pair.start.len();
3595 if buffer.contains_str_at(range.start, ®ion.pair.start)
3596 && buffer.contains_str_at(range.end, ®ion.pair.end)
3597 {
3598 range.end += region.pair.end.len();
3599 selection.start = range.start;
3600 selection.end = range.end;
3601
3602 return selection;
3603 }
3604 }
3605 }
3606
3607 let always_treat_brackets_as_autoclosed = buffer
3608 .language_settings_at(selection.start, cx)
3609 .always_treat_brackets_as_autoclosed;
3610
3611 if !always_treat_brackets_as_autoclosed {
3612 return selection;
3613 }
3614
3615 if let Some(scope) = buffer.language_scope_at(selection.start) {
3616 for (pair, enabled) in scope.brackets() {
3617 if !enabled || !pair.close {
3618 continue;
3619 }
3620
3621 if buffer.contains_str_at(selection.start, &pair.end) {
3622 let pair_start_len = pair.start.len();
3623 if buffer.contains_str_at(
3624 selection.start.saturating_sub(pair_start_len),
3625 &pair.start,
3626 ) {
3627 selection.start -= pair_start_len;
3628 selection.end += pair.end.len();
3629
3630 return selection;
3631 }
3632 }
3633 }
3634 }
3635
3636 selection
3637 })
3638 .collect();
3639
3640 drop(buffer);
3641 self.change_selections(None, window, cx, |selections| {
3642 selections.select(new_selections)
3643 });
3644 }
3645
3646 /// Iterate the given selections, and for each one, find the smallest surrounding
3647 /// autoclose region. This uses the ordering of the selections and the autoclose
3648 /// regions to avoid repeated comparisons.
3649 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3650 &'a self,
3651 selections: impl IntoIterator<Item = Selection<D>>,
3652 buffer: &'a MultiBufferSnapshot,
3653 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3654 let mut i = 0;
3655 let mut regions = self.autoclose_regions.as_slice();
3656 selections.into_iter().map(move |selection| {
3657 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3658
3659 let mut enclosing = None;
3660 while let Some(pair_state) = regions.get(i) {
3661 if pair_state.range.end.to_offset(buffer) < range.start {
3662 regions = ®ions[i + 1..];
3663 i = 0;
3664 } else if pair_state.range.start.to_offset(buffer) > range.end {
3665 break;
3666 } else {
3667 if pair_state.selection_id == selection.id {
3668 enclosing = Some(pair_state);
3669 }
3670 i += 1;
3671 }
3672 }
3673
3674 (selection, enclosing)
3675 })
3676 }
3677
3678 /// Remove any autoclose regions that no longer contain their selection.
3679 fn invalidate_autoclose_regions(
3680 &mut self,
3681 mut selections: &[Selection<Anchor>],
3682 buffer: &MultiBufferSnapshot,
3683 ) {
3684 self.autoclose_regions.retain(|state| {
3685 let mut i = 0;
3686 while let Some(selection) = selections.get(i) {
3687 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3688 selections = &selections[1..];
3689 continue;
3690 }
3691 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3692 break;
3693 }
3694 if selection.id == state.selection_id {
3695 return true;
3696 } else {
3697 i += 1;
3698 }
3699 }
3700 false
3701 });
3702 }
3703
3704 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3705 let offset = position.to_offset(buffer);
3706 let (word_range, kind) = buffer.surrounding_word(offset, true);
3707 if offset > word_range.start && kind == Some(CharKind::Word) {
3708 Some(
3709 buffer
3710 .text_for_range(word_range.start..offset)
3711 .collect::<String>(),
3712 )
3713 } else {
3714 None
3715 }
3716 }
3717
3718 pub fn toggle_inlay_hints(
3719 &mut self,
3720 _: &ToggleInlayHints,
3721 _: &mut Window,
3722 cx: &mut Context<Self>,
3723 ) {
3724 self.refresh_inlay_hints(
3725 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3726 cx,
3727 );
3728 }
3729
3730 pub fn inlay_hints_enabled(&self) -> bool {
3731 self.inlay_hint_cache.enabled
3732 }
3733
3734 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3735 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3736 return;
3737 }
3738
3739 let reason_description = reason.description();
3740 let ignore_debounce = matches!(
3741 reason,
3742 InlayHintRefreshReason::SettingsChange(_)
3743 | InlayHintRefreshReason::Toggle(_)
3744 | InlayHintRefreshReason::ExcerptsRemoved(_)
3745 | InlayHintRefreshReason::ModifiersChanged(_)
3746 );
3747 let (invalidate_cache, required_languages) = match reason {
3748 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3749 match self.inlay_hint_cache.modifiers_override(enabled) {
3750 Some(enabled) => {
3751 if enabled {
3752 (InvalidationStrategy::RefreshRequested, None)
3753 } else {
3754 self.splice_inlays(
3755 &self
3756 .visible_inlay_hints(cx)
3757 .iter()
3758 .map(|inlay| inlay.id)
3759 .collect::<Vec<InlayId>>(),
3760 Vec::new(),
3761 cx,
3762 );
3763 return;
3764 }
3765 }
3766 None => return,
3767 }
3768 }
3769 InlayHintRefreshReason::Toggle(enabled) => {
3770 if self.inlay_hint_cache.toggle(enabled) {
3771 if enabled {
3772 (InvalidationStrategy::RefreshRequested, None)
3773 } else {
3774 self.splice_inlays(
3775 &self
3776 .visible_inlay_hints(cx)
3777 .iter()
3778 .map(|inlay| inlay.id)
3779 .collect::<Vec<InlayId>>(),
3780 Vec::new(),
3781 cx,
3782 );
3783 return;
3784 }
3785 } else {
3786 return;
3787 }
3788 }
3789 InlayHintRefreshReason::SettingsChange(new_settings) => {
3790 match self.inlay_hint_cache.update_settings(
3791 &self.buffer,
3792 new_settings,
3793 self.visible_inlay_hints(cx),
3794 cx,
3795 ) {
3796 ControlFlow::Break(Some(InlaySplice {
3797 to_remove,
3798 to_insert,
3799 })) => {
3800 self.splice_inlays(&to_remove, to_insert, cx);
3801 return;
3802 }
3803 ControlFlow::Break(None) => return,
3804 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3805 }
3806 }
3807 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3808 if let Some(InlaySplice {
3809 to_remove,
3810 to_insert,
3811 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3812 {
3813 self.splice_inlays(&to_remove, to_insert, cx);
3814 }
3815 return;
3816 }
3817 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3818 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3819 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3820 }
3821 InlayHintRefreshReason::RefreshRequested => {
3822 (InvalidationStrategy::RefreshRequested, None)
3823 }
3824 };
3825
3826 if let Some(InlaySplice {
3827 to_remove,
3828 to_insert,
3829 }) = self.inlay_hint_cache.spawn_hint_refresh(
3830 reason_description,
3831 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3832 invalidate_cache,
3833 ignore_debounce,
3834 cx,
3835 ) {
3836 self.splice_inlays(&to_remove, to_insert, cx);
3837 }
3838 }
3839
3840 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3841 self.display_map
3842 .read(cx)
3843 .current_inlays()
3844 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3845 .cloned()
3846 .collect()
3847 }
3848
3849 pub fn excerpts_for_inlay_hints_query(
3850 &self,
3851 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3852 cx: &mut Context<Editor>,
3853 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3854 let Some(project) = self.project.as_ref() else {
3855 return HashMap::default();
3856 };
3857 let project = project.read(cx);
3858 let multi_buffer = self.buffer().read(cx);
3859 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3860 let multi_buffer_visible_start = self
3861 .scroll_manager
3862 .anchor()
3863 .anchor
3864 .to_point(&multi_buffer_snapshot);
3865 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3866 multi_buffer_visible_start
3867 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3868 Bias::Left,
3869 );
3870 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3871 multi_buffer_snapshot
3872 .range_to_buffer_ranges(multi_buffer_visible_range)
3873 .into_iter()
3874 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3875 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3876 let buffer_file = project::File::from_dyn(buffer.file())?;
3877 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3878 let worktree_entry = buffer_worktree
3879 .read(cx)
3880 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3881 if worktree_entry.is_ignored {
3882 return None;
3883 }
3884
3885 let language = buffer.language()?;
3886 if let Some(restrict_to_languages) = restrict_to_languages {
3887 if !restrict_to_languages.contains(language) {
3888 return None;
3889 }
3890 }
3891 Some((
3892 excerpt_id,
3893 (
3894 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3895 buffer.version().clone(),
3896 excerpt_visible_range,
3897 ),
3898 ))
3899 })
3900 .collect()
3901 }
3902
3903 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3904 TextLayoutDetails {
3905 text_system: window.text_system().clone(),
3906 editor_style: self.style.clone().unwrap(),
3907 rem_size: window.rem_size(),
3908 scroll_anchor: self.scroll_manager.anchor(),
3909 visible_rows: self.visible_line_count(),
3910 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3911 }
3912 }
3913
3914 pub fn splice_inlays(
3915 &self,
3916 to_remove: &[InlayId],
3917 to_insert: Vec<Inlay>,
3918 cx: &mut Context<Self>,
3919 ) {
3920 self.display_map.update(cx, |display_map, cx| {
3921 display_map.splice_inlays(to_remove, to_insert, cx)
3922 });
3923 cx.notify();
3924 }
3925
3926 fn trigger_on_type_formatting(
3927 &self,
3928 input: String,
3929 window: &mut Window,
3930 cx: &mut Context<Self>,
3931 ) -> Option<Task<Result<()>>> {
3932 if input.len() != 1 {
3933 return None;
3934 }
3935
3936 let project = self.project.as_ref()?;
3937 let position = self.selections.newest_anchor().head();
3938 let (buffer, buffer_position) = self
3939 .buffer
3940 .read(cx)
3941 .text_anchor_for_position(position, cx)?;
3942
3943 let settings = language_settings::language_settings(
3944 buffer
3945 .read(cx)
3946 .language_at(buffer_position)
3947 .map(|l| l.name()),
3948 buffer.read(cx).file(),
3949 cx,
3950 );
3951 if !settings.use_on_type_format {
3952 return None;
3953 }
3954
3955 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3956 // hence we do LSP request & edit on host side only — add formats to host's history.
3957 let push_to_lsp_host_history = true;
3958 // If this is not the host, append its history with new edits.
3959 let push_to_client_history = project.read(cx).is_via_collab();
3960
3961 let on_type_formatting = project.update(cx, |project, cx| {
3962 project.on_type_format(
3963 buffer.clone(),
3964 buffer_position,
3965 input,
3966 push_to_lsp_host_history,
3967 cx,
3968 )
3969 });
3970 Some(cx.spawn_in(window, |editor, mut cx| async move {
3971 if let Some(transaction) = on_type_formatting.await? {
3972 if push_to_client_history {
3973 buffer
3974 .update(&mut cx, |buffer, _| {
3975 buffer.push_transaction(transaction, Instant::now());
3976 })
3977 .ok();
3978 }
3979 editor.update(&mut cx, |editor, cx| {
3980 editor.refresh_document_highlights(cx);
3981 })?;
3982 }
3983 Ok(())
3984 }))
3985 }
3986
3987 pub fn show_completions(
3988 &mut self,
3989 options: &ShowCompletions,
3990 window: &mut Window,
3991 cx: &mut Context<Self>,
3992 ) {
3993 if self.pending_rename.is_some() {
3994 return;
3995 }
3996
3997 let Some(provider) = self.completion_provider.as_ref() else {
3998 return;
3999 };
4000
4001 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4002 return;
4003 }
4004
4005 let position = self.selections.newest_anchor().head();
4006 if position.diff_base_anchor.is_some() {
4007 return;
4008 }
4009 let (buffer, buffer_position) =
4010 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4011 output
4012 } else {
4013 return;
4014 };
4015 let show_completion_documentation = buffer
4016 .read(cx)
4017 .snapshot()
4018 .settings_at(buffer_position, cx)
4019 .show_completion_documentation;
4020
4021 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4022
4023 let trigger_kind = match &options.trigger {
4024 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4025 CompletionTriggerKind::TRIGGER_CHARACTER
4026 }
4027 _ => CompletionTriggerKind::INVOKED,
4028 };
4029 let completion_context = CompletionContext {
4030 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4031 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4032 Some(String::from(trigger))
4033 } else {
4034 None
4035 }
4036 }),
4037 trigger_kind,
4038 };
4039 let completions =
4040 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4041 let sort_completions = provider.sort_completions();
4042
4043 let id = post_inc(&mut self.next_completion_id);
4044 let task = cx.spawn_in(window, |editor, mut cx| {
4045 async move {
4046 editor.update(&mut cx, |this, _| {
4047 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4048 })?;
4049 let completions = completions.await.log_err();
4050 let menu = if let Some(completions) = completions {
4051 let mut menu = CompletionsMenu::new(
4052 id,
4053 sort_completions,
4054 show_completion_documentation,
4055 position,
4056 buffer.clone(),
4057 completions.into(),
4058 );
4059
4060 menu.filter(query.as_deref(), cx.background_executor().clone())
4061 .await;
4062
4063 menu.visible().then_some(menu)
4064 } else {
4065 None
4066 };
4067
4068 editor.update_in(&mut cx, |editor, window, cx| {
4069 match editor.context_menu.borrow().as_ref() {
4070 None => {}
4071 Some(CodeContextMenu::Completions(prev_menu)) => {
4072 if prev_menu.id > id {
4073 return;
4074 }
4075 }
4076 _ => return,
4077 }
4078
4079 if editor.focus_handle.is_focused(window) && menu.is_some() {
4080 let mut menu = menu.unwrap();
4081 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4082
4083 *editor.context_menu.borrow_mut() =
4084 Some(CodeContextMenu::Completions(menu));
4085
4086 if editor.show_edit_predictions_in_menu() {
4087 editor.update_visible_inline_completion(window, cx);
4088 } else {
4089 editor.discard_inline_completion(false, cx);
4090 }
4091
4092 cx.notify();
4093 } else if editor.completion_tasks.len() <= 1 {
4094 // If there are no more completion tasks and the last menu was
4095 // empty, we should hide it.
4096 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4097 // If it was already hidden and we don't show inline
4098 // completions in the menu, we should also show the
4099 // inline-completion when available.
4100 if was_hidden && editor.show_edit_predictions_in_menu() {
4101 editor.update_visible_inline_completion(window, cx);
4102 }
4103 }
4104 })?;
4105
4106 Ok::<_, anyhow::Error>(())
4107 }
4108 .log_err()
4109 });
4110
4111 self.completion_tasks.push((id, task));
4112 }
4113
4114 pub fn confirm_completion(
4115 &mut self,
4116 action: &ConfirmCompletion,
4117 window: &mut Window,
4118 cx: &mut Context<Self>,
4119 ) -> Option<Task<Result<()>>> {
4120 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4121 }
4122
4123 pub fn compose_completion(
4124 &mut self,
4125 action: &ComposeCompletion,
4126 window: &mut Window,
4127 cx: &mut Context<Self>,
4128 ) -> Option<Task<Result<()>>> {
4129 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4130 }
4131
4132 fn do_completion(
4133 &mut self,
4134 item_ix: Option<usize>,
4135 intent: CompletionIntent,
4136 window: &mut Window,
4137 cx: &mut Context<Editor>,
4138 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4139 use language::ToOffset as _;
4140
4141 let completions_menu =
4142 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4143 menu
4144 } else {
4145 return None;
4146 };
4147
4148 let entries = completions_menu.entries.borrow();
4149 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4150 if self.show_edit_predictions_in_menu() {
4151 self.discard_inline_completion(true, cx);
4152 }
4153 let candidate_id = mat.candidate_id;
4154 drop(entries);
4155
4156 let buffer_handle = completions_menu.buffer;
4157 let completion = completions_menu
4158 .completions
4159 .borrow()
4160 .get(candidate_id)?
4161 .clone();
4162 cx.stop_propagation();
4163
4164 let snippet;
4165 let text;
4166
4167 if completion.is_snippet() {
4168 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4169 text = snippet.as_ref().unwrap().text.clone();
4170 } else {
4171 snippet = None;
4172 text = completion.new_text.clone();
4173 };
4174 let selections = self.selections.all::<usize>(cx);
4175 let buffer = buffer_handle.read(cx);
4176 let old_range = completion.old_range.to_offset(buffer);
4177 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4178
4179 let newest_selection = self.selections.newest_anchor();
4180 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4181 return None;
4182 }
4183
4184 let lookbehind = newest_selection
4185 .start
4186 .text_anchor
4187 .to_offset(buffer)
4188 .saturating_sub(old_range.start);
4189 let lookahead = old_range
4190 .end
4191 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4192 let mut common_prefix_len = old_text
4193 .bytes()
4194 .zip(text.bytes())
4195 .take_while(|(a, b)| a == b)
4196 .count();
4197
4198 let snapshot = self.buffer.read(cx).snapshot(cx);
4199 let mut range_to_replace: Option<Range<isize>> = None;
4200 let mut ranges = Vec::new();
4201 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4202 for selection in &selections {
4203 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4204 let start = selection.start.saturating_sub(lookbehind);
4205 let end = selection.end + lookahead;
4206 if selection.id == newest_selection.id {
4207 range_to_replace = Some(
4208 ((start + common_prefix_len) as isize - selection.start as isize)
4209 ..(end as isize - selection.start as isize),
4210 );
4211 }
4212 ranges.push(start + common_prefix_len..end);
4213 } else {
4214 common_prefix_len = 0;
4215 ranges.clear();
4216 ranges.extend(selections.iter().map(|s| {
4217 if s.id == newest_selection.id {
4218 range_to_replace = Some(
4219 old_range.start.to_offset_utf16(&snapshot).0 as isize
4220 - selection.start as isize
4221 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4222 - selection.start as isize,
4223 );
4224 old_range.clone()
4225 } else {
4226 s.start..s.end
4227 }
4228 }));
4229 break;
4230 }
4231 if !self.linked_edit_ranges.is_empty() {
4232 let start_anchor = snapshot.anchor_before(selection.head());
4233 let end_anchor = snapshot.anchor_after(selection.tail());
4234 if let Some(ranges) = self
4235 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4236 {
4237 for (buffer, edits) in ranges {
4238 linked_edits.entry(buffer.clone()).or_default().extend(
4239 edits
4240 .into_iter()
4241 .map(|range| (range, text[common_prefix_len..].to_owned())),
4242 );
4243 }
4244 }
4245 }
4246 }
4247 let text = &text[common_prefix_len..];
4248
4249 cx.emit(EditorEvent::InputHandled {
4250 utf16_range_to_replace: range_to_replace,
4251 text: text.into(),
4252 });
4253
4254 self.transact(window, cx, |this, window, cx| {
4255 if let Some(mut snippet) = snippet {
4256 snippet.text = text.to_string();
4257 for tabstop in snippet
4258 .tabstops
4259 .iter_mut()
4260 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4261 {
4262 tabstop.start -= common_prefix_len as isize;
4263 tabstop.end -= common_prefix_len as isize;
4264 }
4265
4266 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4267 } else {
4268 this.buffer.update(cx, |buffer, cx| {
4269 buffer.edit(
4270 ranges.iter().map(|range| (range.clone(), text)),
4271 this.autoindent_mode.clone(),
4272 cx,
4273 );
4274 });
4275 }
4276 for (buffer, edits) in linked_edits {
4277 buffer.update(cx, |buffer, cx| {
4278 let snapshot = buffer.snapshot();
4279 let edits = edits
4280 .into_iter()
4281 .map(|(range, text)| {
4282 use text::ToPoint as TP;
4283 let end_point = TP::to_point(&range.end, &snapshot);
4284 let start_point = TP::to_point(&range.start, &snapshot);
4285 (start_point..end_point, text)
4286 })
4287 .sorted_by_key(|(range, _)| range.start)
4288 .collect::<Vec<_>>();
4289 buffer.edit(edits, None, cx);
4290 })
4291 }
4292
4293 this.refresh_inline_completion(true, false, window, cx);
4294 });
4295
4296 let show_new_completions_on_confirm = completion
4297 .confirm
4298 .as_ref()
4299 .map_or(false, |confirm| confirm(intent, window, cx));
4300 if show_new_completions_on_confirm {
4301 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4302 }
4303
4304 let provider = self.completion_provider.as_ref()?;
4305 drop(completion);
4306 let apply_edits = provider.apply_additional_edits_for_completion(
4307 buffer_handle,
4308 completions_menu.completions.clone(),
4309 candidate_id,
4310 true,
4311 cx,
4312 );
4313
4314 let editor_settings = EditorSettings::get_global(cx);
4315 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4316 // After the code completion is finished, users often want to know what signatures are needed.
4317 // so we should automatically call signature_help
4318 self.show_signature_help(&ShowSignatureHelp, window, cx);
4319 }
4320
4321 Some(cx.foreground_executor().spawn(async move {
4322 apply_edits.await?;
4323 Ok(())
4324 }))
4325 }
4326
4327 pub fn toggle_code_actions(
4328 &mut self,
4329 action: &ToggleCodeActions,
4330 window: &mut Window,
4331 cx: &mut Context<Self>,
4332 ) {
4333 let mut context_menu = self.context_menu.borrow_mut();
4334 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4335 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4336 // Toggle if we're selecting the same one
4337 *context_menu = None;
4338 cx.notify();
4339 return;
4340 } else {
4341 // Otherwise, clear it and start a new one
4342 *context_menu = None;
4343 cx.notify();
4344 }
4345 }
4346 drop(context_menu);
4347 let snapshot = self.snapshot(window, cx);
4348 let deployed_from_indicator = action.deployed_from_indicator;
4349 let mut task = self.code_actions_task.take();
4350 let action = action.clone();
4351 cx.spawn_in(window, |editor, mut cx| async move {
4352 while let Some(prev_task) = task {
4353 prev_task.await.log_err();
4354 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4355 }
4356
4357 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4358 if editor.focus_handle.is_focused(window) {
4359 let multibuffer_point = action
4360 .deployed_from_indicator
4361 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4362 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4363 let (buffer, buffer_row) = snapshot
4364 .buffer_snapshot
4365 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4366 .and_then(|(buffer_snapshot, range)| {
4367 editor
4368 .buffer
4369 .read(cx)
4370 .buffer(buffer_snapshot.remote_id())
4371 .map(|buffer| (buffer, range.start.row))
4372 })?;
4373 let (_, code_actions) = editor
4374 .available_code_actions
4375 .clone()
4376 .and_then(|(location, code_actions)| {
4377 let snapshot = location.buffer.read(cx).snapshot();
4378 let point_range = location.range.to_point(&snapshot);
4379 let point_range = point_range.start.row..=point_range.end.row;
4380 if point_range.contains(&buffer_row) {
4381 Some((location, code_actions))
4382 } else {
4383 None
4384 }
4385 })
4386 .unzip();
4387 let buffer_id = buffer.read(cx).remote_id();
4388 let tasks = editor
4389 .tasks
4390 .get(&(buffer_id, buffer_row))
4391 .map(|t| Arc::new(t.to_owned()));
4392 if tasks.is_none() && code_actions.is_none() {
4393 return None;
4394 }
4395
4396 editor.completion_tasks.clear();
4397 editor.discard_inline_completion(false, cx);
4398 let task_context =
4399 tasks
4400 .as_ref()
4401 .zip(editor.project.clone())
4402 .map(|(tasks, project)| {
4403 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4404 });
4405
4406 Some(cx.spawn_in(window, |editor, mut cx| async move {
4407 let task_context = match task_context {
4408 Some(task_context) => task_context.await,
4409 None => None,
4410 };
4411 let resolved_tasks =
4412 tasks.zip(task_context).map(|(tasks, task_context)| {
4413 Rc::new(ResolvedTasks {
4414 templates: tasks.resolve(&task_context).collect(),
4415 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4416 multibuffer_point.row,
4417 tasks.column,
4418 )),
4419 })
4420 });
4421 let spawn_straight_away = resolved_tasks
4422 .as_ref()
4423 .map_or(false, |tasks| tasks.templates.len() == 1)
4424 && code_actions
4425 .as_ref()
4426 .map_or(true, |actions| actions.is_empty());
4427 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4428 *editor.context_menu.borrow_mut() =
4429 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4430 buffer,
4431 actions: CodeActionContents {
4432 tasks: resolved_tasks,
4433 actions: code_actions,
4434 },
4435 selected_item: Default::default(),
4436 scroll_handle: UniformListScrollHandle::default(),
4437 deployed_from_indicator,
4438 }));
4439 if spawn_straight_away {
4440 if let Some(task) = editor.confirm_code_action(
4441 &ConfirmCodeAction { item_ix: Some(0) },
4442 window,
4443 cx,
4444 ) {
4445 cx.notify();
4446 return task;
4447 }
4448 }
4449 cx.notify();
4450 Task::ready(Ok(()))
4451 }) {
4452 task.await
4453 } else {
4454 Ok(())
4455 }
4456 }))
4457 } else {
4458 Some(Task::ready(Ok(())))
4459 }
4460 })?;
4461 if let Some(task) = spawned_test_task {
4462 task.await?;
4463 }
4464
4465 Ok::<_, anyhow::Error>(())
4466 })
4467 .detach_and_log_err(cx);
4468 }
4469
4470 pub fn confirm_code_action(
4471 &mut self,
4472 action: &ConfirmCodeAction,
4473 window: &mut Window,
4474 cx: &mut Context<Self>,
4475 ) -> Option<Task<Result<()>>> {
4476 let actions_menu =
4477 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4478 menu
4479 } else {
4480 return None;
4481 };
4482 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4483 let action = actions_menu.actions.get(action_ix)?;
4484 let title = action.label();
4485 let buffer = actions_menu.buffer;
4486 let workspace = self.workspace()?;
4487
4488 match action {
4489 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4490 workspace.update(cx, |workspace, cx| {
4491 workspace::tasks::schedule_resolved_task(
4492 workspace,
4493 task_source_kind,
4494 resolved_task,
4495 false,
4496 cx,
4497 );
4498
4499 Some(Task::ready(Ok(())))
4500 })
4501 }
4502 CodeActionsItem::CodeAction {
4503 excerpt_id,
4504 action,
4505 provider,
4506 } => {
4507 let apply_code_action =
4508 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4509 let workspace = workspace.downgrade();
4510 Some(cx.spawn_in(window, |editor, cx| async move {
4511 let project_transaction = apply_code_action.await?;
4512 Self::open_project_transaction(
4513 &editor,
4514 workspace,
4515 project_transaction,
4516 title,
4517 cx,
4518 )
4519 .await
4520 }))
4521 }
4522 }
4523 }
4524
4525 pub async fn open_project_transaction(
4526 this: &WeakEntity<Editor>,
4527 workspace: WeakEntity<Workspace>,
4528 transaction: ProjectTransaction,
4529 title: String,
4530 mut cx: AsyncWindowContext,
4531 ) -> Result<()> {
4532 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4533 cx.update(|_, cx| {
4534 entries.sort_unstable_by_key(|(buffer, _)| {
4535 buffer.read(cx).file().map(|f| f.path().clone())
4536 });
4537 })?;
4538
4539 // If the project transaction's edits are all contained within this editor, then
4540 // avoid opening a new editor to display them.
4541
4542 if let Some((buffer, transaction)) = entries.first() {
4543 if entries.len() == 1 {
4544 let excerpt = this.update(&mut cx, |editor, cx| {
4545 editor
4546 .buffer()
4547 .read(cx)
4548 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4549 })?;
4550 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4551 if excerpted_buffer == *buffer {
4552 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4553 let excerpt_range = excerpt_range.to_offset(buffer);
4554 buffer
4555 .edited_ranges_for_transaction::<usize>(transaction)
4556 .all(|range| {
4557 excerpt_range.start <= range.start
4558 && excerpt_range.end >= range.end
4559 })
4560 })?;
4561
4562 if all_edits_within_excerpt {
4563 return Ok(());
4564 }
4565 }
4566 }
4567 }
4568 } else {
4569 return Ok(());
4570 }
4571
4572 let mut ranges_to_highlight = Vec::new();
4573 let excerpt_buffer = cx.new(|cx| {
4574 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4575 for (buffer_handle, transaction) in &entries {
4576 let buffer = buffer_handle.read(cx);
4577 ranges_to_highlight.extend(
4578 multibuffer.push_excerpts_with_context_lines(
4579 buffer_handle.clone(),
4580 buffer
4581 .edited_ranges_for_transaction::<usize>(transaction)
4582 .collect(),
4583 DEFAULT_MULTIBUFFER_CONTEXT,
4584 cx,
4585 ),
4586 );
4587 }
4588 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4589 multibuffer
4590 })?;
4591
4592 workspace.update_in(&mut cx, |workspace, window, cx| {
4593 let project = workspace.project().clone();
4594 let editor = cx
4595 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4596 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4597 editor.update(cx, |editor, cx| {
4598 editor.highlight_background::<Self>(
4599 &ranges_to_highlight,
4600 |theme| theme.editor_highlighted_line_background,
4601 cx,
4602 );
4603 });
4604 })?;
4605
4606 Ok(())
4607 }
4608
4609 pub fn clear_code_action_providers(&mut self) {
4610 self.code_action_providers.clear();
4611 self.available_code_actions.take();
4612 }
4613
4614 pub fn add_code_action_provider(
4615 &mut self,
4616 provider: Rc<dyn CodeActionProvider>,
4617 window: &mut Window,
4618 cx: &mut Context<Self>,
4619 ) {
4620 if self
4621 .code_action_providers
4622 .iter()
4623 .any(|existing_provider| existing_provider.id() == provider.id())
4624 {
4625 return;
4626 }
4627
4628 self.code_action_providers.push(provider);
4629 self.refresh_code_actions(window, cx);
4630 }
4631
4632 pub fn remove_code_action_provider(
4633 &mut self,
4634 id: Arc<str>,
4635 window: &mut Window,
4636 cx: &mut Context<Self>,
4637 ) {
4638 self.code_action_providers
4639 .retain(|provider| provider.id() != id);
4640 self.refresh_code_actions(window, cx);
4641 }
4642
4643 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4644 let buffer = self.buffer.read(cx);
4645 let newest_selection = self.selections.newest_anchor().clone();
4646 if newest_selection.head().diff_base_anchor.is_some() {
4647 return None;
4648 }
4649 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4650 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4651 if start_buffer != end_buffer {
4652 return None;
4653 }
4654
4655 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4656 cx.background_executor()
4657 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4658 .await;
4659
4660 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4661 let providers = this.code_action_providers.clone();
4662 let tasks = this
4663 .code_action_providers
4664 .iter()
4665 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4666 .collect::<Vec<_>>();
4667 (providers, tasks)
4668 })?;
4669
4670 let mut actions = Vec::new();
4671 for (provider, provider_actions) in
4672 providers.into_iter().zip(future::join_all(tasks).await)
4673 {
4674 if let Some(provider_actions) = provider_actions.log_err() {
4675 actions.extend(provider_actions.into_iter().map(|action| {
4676 AvailableCodeAction {
4677 excerpt_id: newest_selection.start.excerpt_id,
4678 action,
4679 provider: provider.clone(),
4680 }
4681 }));
4682 }
4683 }
4684
4685 this.update(&mut cx, |this, cx| {
4686 this.available_code_actions = if actions.is_empty() {
4687 None
4688 } else {
4689 Some((
4690 Location {
4691 buffer: start_buffer,
4692 range: start..end,
4693 },
4694 actions.into(),
4695 ))
4696 };
4697 cx.notify();
4698 })
4699 }));
4700 None
4701 }
4702
4703 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4704 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4705 self.show_git_blame_inline = false;
4706
4707 self.show_git_blame_inline_delay_task =
4708 Some(cx.spawn_in(window, |this, mut cx| async move {
4709 cx.background_executor().timer(delay).await;
4710
4711 this.update(&mut cx, |this, cx| {
4712 this.show_git_blame_inline = true;
4713 cx.notify();
4714 })
4715 .log_err();
4716 }));
4717 }
4718 }
4719
4720 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4721 if self.pending_rename.is_some() {
4722 return None;
4723 }
4724
4725 let provider = self.semantics_provider.clone()?;
4726 let buffer = self.buffer.read(cx);
4727 let newest_selection = self.selections.newest_anchor().clone();
4728 let cursor_position = newest_selection.head();
4729 let (cursor_buffer, cursor_buffer_position) =
4730 buffer.text_anchor_for_position(cursor_position, cx)?;
4731 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4732 if cursor_buffer != tail_buffer {
4733 return None;
4734 }
4735 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4736 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4737 cx.background_executor()
4738 .timer(Duration::from_millis(debounce))
4739 .await;
4740
4741 let highlights = if let Some(highlights) = cx
4742 .update(|cx| {
4743 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4744 })
4745 .ok()
4746 .flatten()
4747 {
4748 highlights.await.log_err()
4749 } else {
4750 None
4751 };
4752
4753 if let Some(highlights) = highlights {
4754 this.update(&mut cx, |this, cx| {
4755 if this.pending_rename.is_some() {
4756 return;
4757 }
4758
4759 let buffer_id = cursor_position.buffer_id;
4760 let buffer = this.buffer.read(cx);
4761 if !buffer
4762 .text_anchor_for_position(cursor_position, cx)
4763 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4764 {
4765 return;
4766 }
4767
4768 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4769 let mut write_ranges = Vec::new();
4770 let mut read_ranges = Vec::new();
4771 for highlight in highlights {
4772 for (excerpt_id, excerpt_range) in
4773 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4774 {
4775 let start = highlight
4776 .range
4777 .start
4778 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4779 let end = highlight
4780 .range
4781 .end
4782 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4783 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4784 continue;
4785 }
4786
4787 let range = Anchor {
4788 buffer_id,
4789 excerpt_id,
4790 text_anchor: start,
4791 diff_base_anchor: None,
4792 }..Anchor {
4793 buffer_id,
4794 excerpt_id,
4795 text_anchor: end,
4796 diff_base_anchor: None,
4797 };
4798 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4799 write_ranges.push(range);
4800 } else {
4801 read_ranges.push(range);
4802 }
4803 }
4804 }
4805
4806 this.highlight_background::<DocumentHighlightRead>(
4807 &read_ranges,
4808 |theme| theme.editor_document_highlight_read_background,
4809 cx,
4810 );
4811 this.highlight_background::<DocumentHighlightWrite>(
4812 &write_ranges,
4813 |theme| theme.editor_document_highlight_write_background,
4814 cx,
4815 );
4816 cx.notify();
4817 })
4818 .log_err();
4819 }
4820 }));
4821 None
4822 }
4823
4824 pub fn refresh_selected_text_highlights(
4825 &mut self,
4826 window: &mut Window,
4827 cx: &mut Context<Editor>,
4828 ) {
4829 self.selection_highlight_task.take();
4830 if !EditorSettings::get_global(cx).selection_highlight {
4831 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4832 return;
4833 }
4834 if self.selections.count() != 1 || self.selections.line_mode {
4835 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4836 return;
4837 }
4838 let selection = self.selections.newest::<Point>(cx);
4839 if selection.is_empty() || selection.start.row != selection.end.row {
4840 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4841 return;
4842 }
4843 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4844 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4845 cx.background_executor()
4846 .timer(Duration::from_millis(debounce))
4847 .await;
4848 let Some(Some(matches_task)) = editor
4849 .update_in(&mut cx, |editor, _, cx| {
4850 if editor.selections.count() != 1 || editor.selections.line_mode {
4851 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4852 return None;
4853 }
4854 let selection = editor.selections.newest::<Point>(cx);
4855 if selection.is_empty() || selection.start.row != selection.end.row {
4856 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4857 return None;
4858 }
4859 let buffer = editor.buffer().read(cx).snapshot(cx);
4860 let query = buffer.text_for_range(selection.range()).collect::<String>();
4861 if query.trim().is_empty() {
4862 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4863 return None;
4864 }
4865 Some(cx.background_spawn(async move {
4866 let mut ranges = Vec::new();
4867 let selection_anchors = selection.range().to_anchors(&buffer);
4868 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4869 for (search_buffer, search_range, excerpt_id) in
4870 buffer.range_to_buffer_ranges(range)
4871 {
4872 ranges.extend(
4873 project::search::SearchQuery::text(
4874 query.clone(),
4875 false,
4876 false,
4877 false,
4878 Default::default(),
4879 Default::default(),
4880 None,
4881 )
4882 .unwrap()
4883 .search(search_buffer, Some(search_range.clone()))
4884 .await
4885 .into_iter()
4886 .filter_map(
4887 |match_range| {
4888 let start = search_buffer.anchor_after(
4889 search_range.start + match_range.start,
4890 );
4891 let end = search_buffer.anchor_before(
4892 search_range.start + match_range.end,
4893 );
4894 let range = Anchor::range_in_buffer(
4895 excerpt_id,
4896 search_buffer.remote_id(),
4897 start..end,
4898 );
4899 (range != selection_anchors).then_some(range)
4900 },
4901 ),
4902 );
4903 }
4904 }
4905 ranges
4906 }))
4907 })
4908 .log_err()
4909 else {
4910 return;
4911 };
4912 let matches = matches_task.await;
4913 editor
4914 .update_in(&mut cx, |editor, _, cx| {
4915 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4916 if !matches.is_empty() {
4917 editor.highlight_background::<SelectedTextHighlight>(
4918 &matches,
4919 |theme| theme.editor_document_highlight_bracket_background,
4920 cx,
4921 )
4922 }
4923 })
4924 .log_err();
4925 }));
4926 }
4927
4928 pub fn refresh_inline_completion(
4929 &mut self,
4930 debounce: bool,
4931 user_requested: bool,
4932 window: &mut Window,
4933 cx: &mut Context<Self>,
4934 ) -> Option<()> {
4935 let provider = self.edit_prediction_provider()?;
4936 let cursor = self.selections.newest_anchor().head();
4937 let (buffer, cursor_buffer_position) =
4938 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4939
4940 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4941 self.discard_inline_completion(false, cx);
4942 return None;
4943 }
4944
4945 if !user_requested
4946 && (!self.should_show_edit_predictions()
4947 || !self.is_focused(window)
4948 || buffer.read(cx).is_empty())
4949 {
4950 self.discard_inline_completion(false, cx);
4951 return None;
4952 }
4953
4954 self.update_visible_inline_completion(window, cx);
4955 provider.refresh(
4956 self.project.clone(),
4957 buffer,
4958 cursor_buffer_position,
4959 debounce,
4960 cx,
4961 );
4962 Some(())
4963 }
4964
4965 fn show_edit_predictions_in_menu(&self) -> bool {
4966 match self.edit_prediction_settings {
4967 EditPredictionSettings::Disabled => false,
4968 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4969 }
4970 }
4971
4972 pub fn edit_predictions_enabled(&self) -> bool {
4973 match self.edit_prediction_settings {
4974 EditPredictionSettings::Disabled => false,
4975 EditPredictionSettings::Enabled { .. } => true,
4976 }
4977 }
4978
4979 fn edit_prediction_requires_modifier(&self) -> bool {
4980 match self.edit_prediction_settings {
4981 EditPredictionSettings::Disabled => false,
4982 EditPredictionSettings::Enabled {
4983 preview_requires_modifier,
4984 ..
4985 } => preview_requires_modifier,
4986 }
4987 }
4988
4989 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
4990 if self.edit_prediction_provider.is_none() {
4991 self.edit_prediction_settings = EditPredictionSettings::Disabled;
4992 } else {
4993 let selection = self.selections.newest_anchor();
4994 let cursor = selection.head();
4995
4996 if let Some((buffer, cursor_buffer_position)) =
4997 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4998 {
4999 self.edit_prediction_settings =
5000 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5001 }
5002 }
5003 }
5004
5005 fn edit_prediction_settings_at_position(
5006 &self,
5007 buffer: &Entity<Buffer>,
5008 buffer_position: language::Anchor,
5009 cx: &App,
5010 ) -> EditPredictionSettings {
5011 if self.mode != EditorMode::Full
5012 || !self.show_inline_completions_override.unwrap_or(true)
5013 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5014 {
5015 return EditPredictionSettings::Disabled;
5016 }
5017
5018 let buffer = buffer.read(cx);
5019
5020 let file = buffer.file();
5021
5022 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5023 return EditPredictionSettings::Disabled;
5024 };
5025
5026 let by_provider = matches!(
5027 self.menu_inline_completions_policy,
5028 MenuInlineCompletionsPolicy::ByProvider
5029 );
5030
5031 let show_in_menu = by_provider
5032 && self
5033 .edit_prediction_provider
5034 .as_ref()
5035 .map_or(false, |provider| {
5036 provider.provider.show_completions_in_menu()
5037 });
5038
5039 let preview_requires_modifier =
5040 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5041
5042 EditPredictionSettings::Enabled {
5043 show_in_menu,
5044 preview_requires_modifier,
5045 }
5046 }
5047
5048 fn should_show_edit_predictions(&self) -> bool {
5049 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5050 }
5051
5052 pub fn edit_prediction_preview_is_active(&self) -> bool {
5053 matches!(
5054 self.edit_prediction_preview,
5055 EditPredictionPreview::Active { .. }
5056 )
5057 }
5058
5059 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5060 let cursor = self.selections.newest_anchor().head();
5061 if let Some((buffer, cursor_position)) =
5062 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5063 {
5064 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5065 } else {
5066 false
5067 }
5068 }
5069
5070 fn edit_predictions_enabled_in_buffer(
5071 &self,
5072 buffer: &Entity<Buffer>,
5073 buffer_position: language::Anchor,
5074 cx: &App,
5075 ) -> bool {
5076 maybe!({
5077 let provider = self.edit_prediction_provider()?;
5078 if !provider.is_enabled(&buffer, buffer_position, cx) {
5079 return Some(false);
5080 }
5081 let buffer = buffer.read(cx);
5082 let Some(file) = buffer.file() else {
5083 return Some(true);
5084 };
5085 let settings = all_language_settings(Some(file), cx);
5086 Some(settings.edit_predictions_enabled_for_file(file, cx))
5087 })
5088 .unwrap_or(false)
5089 }
5090
5091 fn cycle_inline_completion(
5092 &mut self,
5093 direction: Direction,
5094 window: &mut Window,
5095 cx: &mut Context<Self>,
5096 ) -> Option<()> {
5097 let provider = self.edit_prediction_provider()?;
5098 let cursor = self.selections.newest_anchor().head();
5099 let (buffer, cursor_buffer_position) =
5100 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5101 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5102 return None;
5103 }
5104
5105 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5106 self.update_visible_inline_completion(window, cx);
5107
5108 Some(())
5109 }
5110
5111 pub fn show_inline_completion(
5112 &mut self,
5113 _: &ShowEditPrediction,
5114 window: &mut Window,
5115 cx: &mut Context<Self>,
5116 ) {
5117 if !self.has_active_inline_completion() {
5118 self.refresh_inline_completion(false, true, window, cx);
5119 return;
5120 }
5121
5122 self.update_visible_inline_completion(window, cx);
5123 }
5124
5125 pub fn display_cursor_names(
5126 &mut self,
5127 _: &DisplayCursorNames,
5128 window: &mut Window,
5129 cx: &mut Context<Self>,
5130 ) {
5131 self.show_cursor_names(window, cx);
5132 }
5133
5134 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5135 self.show_cursor_names = true;
5136 cx.notify();
5137 cx.spawn_in(window, |this, mut cx| async move {
5138 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5139 this.update(&mut cx, |this, cx| {
5140 this.show_cursor_names = false;
5141 cx.notify()
5142 })
5143 .ok()
5144 })
5145 .detach();
5146 }
5147
5148 pub fn next_edit_prediction(
5149 &mut self,
5150 _: &NextEditPrediction,
5151 window: &mut Window,
5152 cx: &mut Context<Self>,
5153 ) {
5154 if self.has_active_inline_completion() {
5155 self.cycle_inline_completion(Direction::Next, window, cx);
5156 } else {
5157 let is_copilot_disabled = self
5158 .refresh_inline_completion(false, true, window, cx)
5159 .is_none();
5160 if is_copilot_disabled {
5161 cx.propagate();
5162 }
5163 }
5164 }
5165
5166 pub fn previous_edit_prediction(
5167 &mut self,
5168 _: &PreviousEditPrediction,
5169 window: &mut Window,
5170 cx: &mut Context<Self>,
5171 ) {
5172 if self.has_active_inline_completion() {
5173 self.cycle_inline_completion(Direction::Prev, window, cx);
5174 } else {
5175 let is_copilot_disabled = self
5176 .refresh_inline_completion(false, true, window, cx)
5177 .is_none();
5178 if is_copilot_disabled {
5179 cx.propagate();
5180 }
5181 }
5182 }
5183
5184 pub fn accept_edit_prediction(
5185 &mut self,
5186 _: &AcceptEditPrediction,
5187 window: &mut Window,
5188 cx: &mut Context<Self>,
5189 ) {
5190 if self.show_edit_predictions_in_menu() {
5191 self.hide_context_menu(window, cx);
5192 }
5193
5194 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5195 return;
5196 };
5197
5198 self.report_inline_completion_event(
5199 active_inline_completion.completion_id.clone(),
5200 true,
5201 cx,
5202 );
5203
5204 match &active_inline_completion.completion {
5205 InlineCompletion::Move { target, .. } => {
5206 let target = *target;
5207
5208 if let Some(position_map) = &self.last_position_map {
5209 if position_map
5210 .visible_row_range
5211 .contains(&target.to_display_point(&position_map.snapshot).row())
5212 || !self.edit_prediction_requires_modifier()
5213 {
5214 self.unfold_ranges(&[target..target], true, false, cx);
5215 // Note that this is also done in vim's handler of the Tab action.
5216 self.change_selections(
5217 Some(Autoscroll::newest()),
5218 window,
5219 cx,
5220 |selections| {
5221 selections.select_anchor_ranges([target..target]);
5222 },
5223 );
5224 self.clear_row_highlights::<EditPredictionPreview>();
5225
5226 self.edit_prediction_preview
5227 .set_previous_scroll_position(None);
5228 } else {
5229 self.edit_prediction_preview
5230 .set_previous_scroll_position(Some(
5231 position_map.snapshot.scroll_anchor,
5232 ));
5233
5234 self.highlight_rows::<EditPredictionPreview>(
5235 target..target,
5236 cx.theme().colors().editor_highlighted_line_background,
5237 true,
5238 cx,
5239 );
5240 self.request_autoscroll(Autoscroll::fit(), cx);
5241 }
5242 }
5243 }
5244 InlineCompletion::Edit { edits, .. } => {
5245 if let Some(provider) = self.edit_prediction_provider() {
5246 provider.accept(cx);
5247 }
5248
5249 let snapshot = self.buffer.read(cx).snapshot(cx);
5250 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5251
5252 self.buffer.update(cx, |buffer, cx| {
5253 buffer.edit(edits.iter().cloned(), None, cx)
5254 });
5255
5256 self.change_selections(None, window, cx, |s| {
5257 s.select_anchor_ranges([last_edit_end..last_edit_end])
5258 });
5259
5260 self.update_visible_inline_completion(window, cx);
5261 if self.active_inline_completion.is_none() {
5262 self.refresh_inline_completion(true, true, window, cx);
5263 }
5264
5265 cx.notify();
5266 }
5267 }
5268
5269 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5270 }
5271
5272 pub fn accept_partial_inline_completion(
5273 &mut self,
5274 _: &AcceptPartialEditPrediction,
5275 window: &mut Window,
5276 cx: &mut Context<Self>,
5277 ) {
5278 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5279 return;
5280 };
5281 if self.selections.count() != 1 {
5282 return;
5283 }
5284
5285 self.report_inline_completion_event(
5286 active_inline_completion.completion_id.clone(),
5287 true,
5288 cx,
5289 );
5290
5291 match &active_inline_completion.completion {
5292 InlineCompletion::Move { target, .. } => {
5293 let target = *target;
5294 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5295 selections.select_anchor_ranges([target..target]);
5296 });
5297 }
5298 InlineCompletion::Edit { edits, .. } => {
5299 // Find an insertion that starts at the cursor position.
5300 let snapshot = self.buffer.read(cx).snapshot(cx);
5301 let cursor_offset = self.selections.newest::<usize>(cx).head();
5302 let insertion = edits.iter().find_map(|(range, text)| {
5303 let range = range.to_offset(&snapshot);
5304 if range.is_empty() && range.start == cursor_offset {
5305 Some(text)
5306 } else {
5307 None
5308 }
5309 });
5310
5311 if let Some(text) = insertion {
5312 let mut partial_completion = text
5313 .chars()
5314 .by_ref()
5315 .take_while(|c| c.is_alphabetic())
5316 .collect::<String>();
5317 if partial_completion.is_empty() {
5318 partial_completion = text
5319 .chars()
5320 .by_ref()
5321 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5322 .collect::<String>();
5323 }
5324
5325 cx.emit(EditorEvent::InputHandled {
5326 utf16_range_to_replace: None,
5327 text: partial_completion.clone().into(),
5328 });
5329
5330 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5331
5332 self.refresh_inline_completion(true, true, window, cx);
5333 cx.notify();
5334 } else {
5335 self.accept_edit_prediction(&Default::default(), window, cx);
5336 }
5337 }
5338 }
5339 }
5340
5341 fn discard_inline_completion(
5342 &mut self,
5343 should_report_inline_completion_event: bool,
5344 cx: &mut Context<Self>,
5345 ) -> bool {
5346 if should_report_inline_completion_event {
5347 let completion_id = self
5348 .active_inline_completion
5349 .as_ref()
5350 .and_then(|active_completion| active_completion.completion_id.clone());
5351
5352 self.report_inline_completion_event(completion_id, false, cx);
5353 }
5354
5355 if let Some(provider) = self.edit_prediction_provider() {
5356 provider.discard(cx);
5357 }
5358
5359 self.take_active_inline_completion(cx)
5360 }
5361
5362 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5363 let Some(provider) = self.edit_prediction_provider() else {
5364 return;
5365 };
5366
5367 let Some((_, buffer, _)) = self
5368 .buffer
5369 .read(cx)
5370 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5371 else {
5372 return;
5373 };
5374
5375 let extension = buffer
5376 .read(cx)
5377 .file()
5378 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5379
5380 let event_type = match accepted {
5381 true => "Edit Prediction Accepted",
5382 false => "Edit Prediction Discarded",
5383 };
5384 telemetry::event!(
5385 event_type,
5386 provider = provider.name(),
5387 prediction_id = id,
5388 suggestion_accepted = accepted,
5389 file_extension = extension,
5390 );
5391 }
5392
5393 pub fn has_active_inline_completion(&self) -> bool {
5394 self.active_inline_completion.is_some()
5395 }
5396
5397 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5398 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5399 return false;
5400 };
5401
5402 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5403 self.clear_highlights::<InlineCompletionHighlight>(cx);
5404 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5405 true
5406 }
5407
5408 /// Returns true when we're displaying the edit prediction popover below the cursor
5409 /// like we are not previewing and the LSP autocomplete menu is visible
5410 /// or we are in `when_holding_modifier` mode.
5411 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5412 if self.edit_prediction_preview_is_active()
5413 || !self.show_edit_predictions_in_menu()
5414 || !self.edit_predictions_enabled()
5415 {
5416 return false;
5417 }
5418
5419 if self.has_visible_completions_menu() {
5420 return true;
5421 }
5422
5423 has_completion && self.edit_prediction_requires_modifier()
5424 }
5425
5426 fn handle_modifiers_changed(
5427 &mut self,
5428 modifiers: Modifiers,
5429 position_map: &PositionMap,
5430 window: &mut Window,
5431 cx: &mut Context<Self>,
5432 ) {
5433 if self.show_edit_predictions_in_menu() {
5434 self.update_edit_prediction_preview(&modifiers, window, cx);
5435 }
5436
5437 self.update_selection_mode(&modifiers, position_map, window, cx);
5438
5439 let mouse_position = window.mouse_position();
5440 if !position_map.text_hitbox.is_hovered(window) {
5441 return;
5442 }
5443
5444 self.update_hovered_link(
5445 position_map.point_for_position(mouse_position),
5446 &position_map.snapshot,
5447 modifiers,
5448 window,
5449 cx,
5450 )
5451 }
5452
5453 fn update_selection_mode(
5454 &mut self,
5455 modifiers: &Modifiers,
5456 position_map: &PositionMap,
5457 window: &mut Window,
5458 cx: &mut Context<Self>,
5459 ) {
5460 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5461 return;
5462 }
5463
5464 let mouse_position = window.mouse_position();
5465 let point_for_position = position_map.point_for_position(mouse_position);
5466 let position = point_for_position.previous_valid;
5467
5468 self.select(
5469 SelectPhase::BeginColumnar {
5470 position,
5471 reset: false,
5472 goal_column: point_for_position.exact_unclipped.column(),
5473 },
5474 window,
5475 cx,
5476 );
5477 }
5478
5479 fn update_edit_prediction_preview(
5480 &mut self,
5481 modifiers: &Modifiers,
5482 window: &mut Window,
5483 cx: &mut Context<Self>,
5484 ) {
5485 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5486 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5487 return;
5488 };
5489
5490 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5491 if matches!(
5492 self.edit_prediction_preview,
5493 EditPredictionPreview::Inactive { .. }
5494 ) {
5495 self.edit_prediction_preview = EditPredictionPreview::Active {
5496 previous_scroll_position: None,
5497 since: Instant::now(),
5498 };
5499
5500 self.update_visible_inline_completion(window, cx);
5501 cx.notify();
5502 }
5503 } else if let EditPredictionPreview::Active {
5504 previous_scroll_position,
5505 since,
5506 } = self.edit_prediction_preview
5507 {
5508 if let (Some(previous_scroll_position), Some(position_map)) =
5509 (previous_scroll_position, self.last_position_map.as_ref())
5510 {
5511 self.set_scroll_position(
5512 previous_scroll_position
5513 .scroll_position(&position_map.snapshot.display_snapshot),
5514 window,
5515 cx,
5516 );
5517 }
5518
5519 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5520 released_too_fast: since.elapsed() < Duration::from_millis(200),
5521 };
5522 self.clear_row_highlights::<EditPredictionPreview>();
5523 self.update_visible_inline_completion(window, cx);
5524 cx.notify();
5525 }
5526 }
5527
5528 fn update_visible_inline_completion(
5529 &mut self,
5530 _window: &mut Window,
5531 cx: &mut Context<Self>,
5532 ) -> Option<()> {
5533 let selection = self.selections.newest_anchor();
5534 let cursor = selection.head();
5535 let multibuffer = self.buffer.read(cx).snapshot(cx);
5536 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5537 let excerpt_id = cursor.excerpt_id;
5538
5539 let show_in_menu = self.show_edit_predictions_in_menu();
5540 let completions_menu_has_precedence = !show_in_menu
5541 && (self.context_menu.borrow().is_some()
5542 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5543
5544 if completions_menu_has_precedence
5545 || !offset_selection.is_empty()
5546 || self
5547 .active_inline_completion
5548 .as_ref()
5549 .map_or(false, |completion| {
5550 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5551 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5552 !invalidation_range.contains(&offset_selection.head())
5553 })
5554 {
5555 self.discard_inline_completion(false, cx);
5556 return None;
5557 }
5558
5559 self.take_active_inline_completion(cx);
5560 let Some(provider) = self.edit_prediction_provider() else {
5561 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5562 return None;
5563 };
5564
5565 let (buffer, cursor_buffer_position) =
5566 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5567
5568 self.edit_prediction_settings =
5569 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5570
5571 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5572
5573 if self.edit_prediction_indent_conflict {
5574 let cursor_point = cursor.to_point(&multibuffer);
5575
5576 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5577
5578 if let Some((_, indent)) = indents.iter().next() {
5579 if indent.len == cursor_point.column {
5580 self.edit_prediction_indent_conflict = false;
5581 }
5582 }
5583 }
5584
5585 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5586 let edits = inline_completion
5587 .edits
5588 .into_iter()
5589 .flat_map(|(range, new_text)| {
5590 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5591 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5592 Some((start..end, new_text))
5593 })
5594 .collect::<Vec<_>>();
5595 if edits.is_empty() {
5596 return None;
5597 }
5598
5599 let first_edit_start = edits.first().unwrap().0.start;
5600 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5601 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5602
5603 let last_edit_end = edits.last().unwrap().0.end;
5604 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5605 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5606
5607 let cursor_row = cursor.to_point(&multibuffer).row;
5608
5609 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5610
5611 let mut inlay_ids = Vec::new();
5612 let invalidation_row_range;
5613 let move_invalidation_row_range = if cursor_row < edit_start_row {
5614 Some(cursor_row..edit_end_row)
5615 } else if cursor_row > edit_end_row {
5616 Some(edit_start_row..cursor_row)
5617 } else {
5618 None
5619 };
5620 let is_move =
5621 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5622 let completion = if is_move {
5623 invalidation_row_range =
5624 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5625 let target = first_edit_start;
5626 InlineCompletion::Move { target, snapshot }
5627 } else {
5628 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5629 && !self.inline_completions_hidden_for_vim_mode;
5630
5631 if show_completions_in_buffer {
5632 if edits
5633 .iter()
5634 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5635 {
5636 let mut inlays = Vec::new();
5637 for (range, new_text) in &edits {
5638 let inlay = Inlay::inline_completion(
5639 post_inc(&mut self.next_inlay_id),
5640 range.start,
5641 new_text.as_str(),
5642 );
5643 inlay_ids.push(inlay.id);
5644 inlays.push(inlay);
5645 }
5646
5647 self.splice_inlays(&[], inlays, cx);
5648 } else {
5649 let background_color = cx.theme().status().deleted_background;
5650 self.highlight_text::<InlineCompletionHighlight>(
5651 edits.iter().map(|(range, _)| range.clone()).collect(),
5652 HighlightStyle {
5653 background_color: Some(background_color),
5654 ..Default::default()
5655 },
5656 cx,
5657 );
5658 }
5659 }
5660
5661 invalidation_row_range = edit_start_row..edit_end_row;
5662
5663 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5664 if provider.show_tab_accept_marker() {
5665 EditDisplayMode::TabAccept
5666 } else {
5667 EditDisplayMode::Inline
5668 }
5669 } else {
5670 EditDisplayMode::DiffPopover
5671 };
5672
5673 InlineCompletion::Edit {
5674 edits,
5675 edit_preview: inline_completion.edit_preview,
5676 display_mode,
5677 snapshot,
5678 }
5679 };
5680
5681 let invalidation_range = multibuffer
5682 .anchor_before(Point::new(invalidation_row_range.start, 0))
5683 ..multibuffer.anchor_after(Point::new(
5684 invalidation_row_range.end,
5685 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5686 ));
5687
5688 self.stale_inline_completion_in_menu = None;
5689 self.active_inline_completion = Some(InlineCompletionState {
5690 inlay_ids,
5691 completion,
5692 completion_id: inline_completion.id,
5693 invalidation_range,
5694 });
5695
5696 cx.notify();
5697
5698 Some(())
5699 }
5700
5701 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5702 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5703 }
5704
5705 fn render_code_actions_indicator(
5706 &self,
5707 _style: &EditorStyle,
5708 row: DisplayRow,
5709 is_active: bool,
5710 cx: &mut Context<Self>,
5711 ) -> Option<IconButton> {
5712 if self.available_code_actions.is_some() {
5713 Some(
5714 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5715 .shape(ui::IconButtonShape::Square)
5716 .icon_size(IconSize::XSmall)
5717 .icon_color(Color::Muted)
5718 .toggle_state(is_active)
5719 .tooltip({
5720 let focus_handle = self.focus_handle.clone();
5721 move |window, cx| {
5722 Tooltip::for_action_in(
5723 "Toggle Code Actions",
5724 &ToggleCodeActions {
5725 deployed_from_indicator: None,
5726 },
5727 &focus_handle,
5728 window,
5729 cx,
5730 )
5731 }
5732 })
5733 .on_click(cx.listener(move |editor, _e, window, cx| {
5734 window.focus(&editor.focus_handle(cx));
5735 editor.toggle_code_actions(
5736 &ToggleCodeActions {
5737 deployed_from_indicator: Some(row),
5738 },
5739 window,
5740 cx,
5741 );
5742 })),
5743 )
5744 } else {
5745 None
5746 }
5747 }
5748
5749 fn clear_tasks(&mut self) {
5750 self.tasks.clear()
5751 }
5752
5753 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5754 if self.tasks.insert(key, value).is_some() {
5755 // This case should hopefully be rare, but just in case...
5756 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5757 }
5758 }
5759
5760 fn build_tasks_context(
5761 project: &Entity<Project>,
5762 buffer: &Entity<Buffer>,
5763 buffer_row: u32,
5764 tasks: &Arc<RunnableTasks>,
5765 cx: &mut Context<Self>,
5766 ) -> Task<Option<task::TaskContext>> {
5767 let position = Point::new(buffer_row, tasks.column);
5768 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5769 let location = Location {
5770 buffer: buffer.clone(),
5771 range: range_start..range_start,
5772 };
5773 // Fill in the environmental variables from the tree-sitter captures
5774 let mut captured_task_variables = TaskVariables::default();
5775 for (capture_name, value) in tasks.extra_variables.clone() {
5776 captured_task_variables.insert(
5777 task::VariableName::Custom(capture_name.into()),
5778 value.clone(),
5779 );
5780 }
5781 project.update(cx, |project, cx| {
5782 project.task_store().update(cx, |task_store, cx| {
5783 task_store.task_context_for_location(captured_task_variables, location, cx)
5784 })
5785 })
5786 }
5787
5788 pub fn spawn_nearest_task(
5789 &mut self,
5790 action: &SpawnNearestTask,
5791 window: &mut Window,
5792 cx: &mut Context<Self>,
5793 ) {
5794 let Some((workspace, _)) = self.workspace.clone() else {
5795 return;
5796 };
5797 let Some(project) = self.project.clone() else {
5798 return;
5799 };
5800
5801 // Try to find a closest, enclosing node using tree-sitter that has a
5802 // task
5803 let Some((buffer, buffer_row, tasks)) = self
5804 .find_enclosing_node_task(cx)
5805 // Or find the task that's closest in row-distance.
5806 .or_else(|| self.find_closest_task(cx))
5807 else {
5808 return;
5809 };
5810
5811 let reveal_strategy = action.reveal;
5812 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5813 cx.spawn_in(window, |_, mut cx| async move {
5814 let context = task_context.await?;
5815 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5816
5817 let resolved = resolved_task.resolved.as_mut()?;
5818 resolved.reveal = reveal_strategy;
5819
5820 workspace
5821 .update(&mut cx, |workspace, cx| {
5822 workspace::tasks::schedule_resolved_task(
5823 workspace,
5824 task_source_kind,
5825 resolved_task,
5826 false,
5827 cx,
5828 );
5829 })
5830 .ok()
5831 })
5832 .detach();
5833 }
5834
5835 fn find_closest_task(
5836 &mut self,
5837 cx: &mut Context<Self>,
5838 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5839 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5840
5841 let ((buffer_id, row), tasks) = self
5842 .tasks
5843 .iter()
5844 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5845
5846 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5847 let tasks = Arc::new(tasks.to_owned());
5848 Some((buffer, *row, tasks))
5849 }
5850
5851 fn find_enclosing_node_task(
5852 &mut self,
5853 cx: &mut Context<Self>,
5854 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5855 let snapshot = self.buffer.read(cx).snapshot(cx);
5856 let offset = self.selections.newest::<usize>(cx).head();
5857 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5858 let buffer_id = excerpt.buffer().remote_id();
5859
5860 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5861 let mut cursor = layer.node().walk();
5862
5863 while cursor.goto_first_child_for_byte(offset).is_some() {
5864 if cursor.node().end_byte() == offset {
5865 cursor.goto_next_sibling();
5866 }
5867 }
5868
5869 // Ascend to the smallest ancestor that contains the range and has a task.
5870 loop {
5871 let node = cursor.node();
5872 let node_range = node.byte_range();
5873 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5874
5875 // Check if this node contains our offset
5876 if node_range.start <= offset && node_range.end >= offset {
5877 // If it contains offset, check for task
5878 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5879 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5880 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5881 }
5882 }
5883
5884 if !cursor.goto_parent() {
5885 break;
5886 }
5887 }
5888 None
5889 }
5890
5891 fn render_run_indicator(
5892 &self,
5893 _style: &EditorStyle,
5894 is_active: bool,
5895 row: DisplayRow,
5896 cx: &mut Context<Self>,
5897 ) -> IconButton {
5898 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5899 .shape(ui::IconButtonShape::Square)
5900 .icon_size(IconSize::XSmall)
5901 .icon_color(Color::Muted)
5902 .toggle_state(is_active)
5903 .on_click(cx.listener(move |editor, _e, window, cx| {
5904 window.focus(&editor.focus_handle(cx));
5905 editor.toggle_code_actions(
5906 &ToggleCodeActions {
5907 deployed_from_indicator: Some(row),
5908 },
5909 window,
5910 cx,
5911 );
5912 }))
5913 }
5914
5915 pub fn context_menu_visible(&self) -> bool {
5916 !self.edit_prediction_preview_is_active()
5917 && self
5918 .context_menu
5919 .borrow()
5920 .as_ref()
5921 .map_or(false, |menu| menu.visible())
5922 }
5923
5924 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5925 self.context_menu
5926 .borrow()
5927 .as_ref()
5928 .map(|menu| menu.origin())
5929 }
5930
5931 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
5932 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
5933
5934 #[allow(clippy::too_many_arguments)]
5935 fn render_edit_prediction_popover(
5936 &mut self,
5937 text_bounds: &Bounds<Pixels>,
5938 content_origin: gpui::Point<Pixels>,
5939 editor_snapshot: &EditorSnapshot,
5940 visible_row_range: Range<DisplayRow>,
5941 scroll_top: f32,
5942 scroll_bottom: f32,
5943 line_layouts: &[LineWithInvisibles],
5944 line_height: Pixels,
5945 scroll_pixel_position: gpui::Point<Pixels>,
5946 newest_selection_head: Option<DisplayPoint>,
5947 editor_width: Pixels,
5948 style: &EditorStyle,
5949 window: &mut Window,
5950 cx: &mut App,
5951 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
5952 let active_inline_completion = self.active_inline_completion.as_ref()?;
5953
5954 if self.edit_prediction_visible_in_cursor_popover(true) {
5955 return None;
5956 }
5957
5958 match &active_inline_completion.completion {
5959 InlineCompletion::Move { target, .. } => {
5960 let target_display_point = target.to_display_point(editor_snapshot);
5961
5962 if self.edit_prediction_requires_modifier() {
5963 if !self.edit_prediction_preview_is_active() {
5964 return None;
5965 }
5966
5967 self.render_edit_prediction_modifier_jump_popover(
5968 text_bounds,
5969 content_origin,
5970 visible_row_range,
5971 line_layouts,
5972 line_height,
5973 scroll_pixel_position,
5974 newest_selection_head,
5975 target_display_point,
5976 window,
5977 cx,
5978 )
5979 } else {
5980 self.render_edit_prediction_eager_jump_popover(
5981 text_bounds,
5982 content_origin,
5983 editor_snapshot,
5984 visible_row_range,
5985 scroll_top,
5986 scroll_bottom,
5987 line_height,
5988 scroll_pixel_position,
5989 target_display_point,
5990 editor_width,
5991 window,
5992 cx,
5993 )
5994 }
5995 }
5996 InlineCompletion::Edit {
5997 display_mode: EditDisplayMode::Inline,
5998 ..
5999 } => None,
6000 InlineCompletion::Edit {
6001 display_mode: EditDisplayMode::TabAccept,
6002 edits,
6003 ..
6004 } => {
6005 let range = &edits.first()?.0;
6006 let target_display_point = range.end.to_display_point(editor_snapshot);
6007
6008 self.render_edit_prediction_end_of_line_popover(
6009 "Accept",
6010 editor_snapshot,
6011 visible_row_range,
6012 target_display_point,
6013 line_height,
6014 scroll_pixel_position,
6015 content_origin,
6016 editor_width,
6017 window,
6018 cx,
6019 )
6020 }
6021 InlineCompletion::Edit {
6022 edits,
6023 edit_preview,
6024 display_mode: EditDisplayMode::DiffPopover,
6025 snapshot,
6026 } => self.render_edit_prediction_diff_popover(
6027 text_bounds,
6028 content_origin,
6029 editor_snapshot,
6030 visible_row_range,
6031 line_layouts,
6032 line_height,
6033 scroll_pixel_position,
6034 newest_selection_head,
6035 editor_width,
6036 style,
6037 edits,
6038 edit_preview,
6039 snapshot,
6040 window,
6041 cx,
6042 ),
6043 }
6044 }
6045
6046 #[allow(clippy::too_many_arguments)]
6047 fn render_edit_prediction_modifier_jump_popover(
6048 &mut self,
6049 text_bounds: &Bounds<Pixels>,
6050 content_origin: gpui::Point<Pixels>,
6051 visible_row_range: Range<DisplayRow>,
6052 line_layouts: &[LineWithInvisibles],
6053 line_height: Pixels,
6054 scroll_pixel_position: gpui::Point<Pixels>,
6055 newest_selection_head: Option<DisplayPoint>,
6056 target_display_point: DisplayPoint,
6057 window: &mut Window,
6058 cx: &mut App,
6059 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6060 let scrolled_content_origin =
6061 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6062
6063 const SCROLL_PADDING_Y: Pixels = px(12.);
6064
6065 if target_display_point.row() < visible_row_range.start {
6066 return self.render_edit_prediction_scroll_popover(
6067 |_| SCROLL_PADDING_Y,
6068 IconName::ArrowUp,
6069 visible_row_range,
6070 line_layouts,
6071 newest_selection_head,
6072 scrolled_content_origin,
6073 window,
6074 cx,
6075 );
6076 } else if target_display_point.row() >= visible_row_range.end {
6077 return self.render_edit_prediction_scroll_popover(
6078 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6079 IconName::ArrowDown,
6080 visible_row_range,
6081 line_layouts,
6082 newest_selection_head,
6083 scrolled_content_origin,
6084 window,
6085 cx,
6086 );
6087 }
6088
6089 const POLE_WIDTH: Pixels = px(2.);
6090
6091 let line_layout =
6092 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6093 let target_column = target_display_point.column() as usize;
6094
6095 let target_x = line_layout.x_for_index(target_column);
6096 let target_y =
6097 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6098
6099 let flag_on_right = target_x < text_bounds.size.width / 2.;
6100
6101 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6102 border_color.l += 0.001;
6103
6104 let mut element = v_flex()
6105 .items_end()
6106 .when(flag_on_right, |el| el.items_start())
6107 .child(if flag_on_right {
6108 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6109 .rounded_bl(px(0.))
6110 .rounded_tl(px(0.))
6111 .border_l_2()
6112 .border_color(border_color)
6113 } else {
6114 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6115 .rounded_br(px(0.))
6116 .rounded_tr(px(0.))
6117 .border_r_2()
6118 .border_color(border_color)
6119 })
6120 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6121 .into_any();
6122
6123 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6124
6125 let mut origin = scrolled_content_origin + point(target_x, target_y)
6126 - point(
6127 if flag_on_right {
6128 POLE_WIDTH
6129 } else {
6130 size.width - POLE_WIDTH
6131 },
6132 size.height - line_height,
6133 );
6134
6135 origin.x = origin.x.max(content_origin.x);
6136
6137 element.prepaint_at(origin, window, cx);
6138
6139 Some((element, origin))
6140 }
6141
6142 #[allow(clippy::too_many_arguments)]
6143 fn render_edit_prediction_scroll_popover(
6144 &mut self,
6145 to_y: impl Fn(Size<Pixels>) -> Pixels,
6146 scroll_icon: IconName,
6147 visible_row_range: Range<DisplayRow>,
6148 line_layouts: &[LineWithInvisibles],
6149 newest_selection_head: Option<DisplayPoint>,
6150 scrolled_content_origin: gpui::Point<Pixels>,
6151 window: &mut Window,
6152 cx: &mut App,
6153 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6154 let mut element = self
6155 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6156 .into_any();
6157
6158 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6159
6160 let cursor = newest_selection_head?;
6161 let cursor_row_layout =
6162 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6163 let cursor_column = cursor.column() as usize;
6164
6165 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6166
6167 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6168
6169 element.prepaint_at(origin, window, cx);
6170 Some((element, origin))
6171 }
6172
6173 #[allow(clippy::too_many_arguments)]
6174 fn render_edit_prediction_eager_jump_popover(
6175 &mut self,
6176 text_bounds: &Bounds<Pixels>,
6177 content_origin: gpui::Point<Pixels>,
6178 editor_snapshot: &EditorSnapshot,
6179 visible_row_range: Range<DisplayRow>,
6180 scroll_top: f32,
6181 scroll_bottom: f32,
6182 line_height: Pixels,
6183 scroll_pixel_position: gpui::Point<Pixels>,
6184 target_display_point: DisplayPoint,
6185 editor_width: Pixels,
6186 window: &mut Window,
6187 cx: &mut App,
6188 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6189 if target_display_point.row().as_f32() < scroll_top {
6190 let mut element = self
6191 .render_edit_prediction_line_popover(
6192 "Jump to Edit",
6193 Some(IconName::ArrowUp),
6194 window,
6195 cx,
6196 )?
6197 .into_any();
6198
6199 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6200 let offset = point(
6201 (text_bounds.size.width - size.width) / 2.,
6202 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6203 );
6204
6205 let origin = text_bounds.origin + offset;
6206 element.prepaint_at(origin, window, cx);
6207 Some((element, origin))
6208 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6209 let mut element = self
6210 .render_edit_prediction_line_popover(
6211 "Jump to Edit",
6212 Some(IconName::ArrowDown),
6213 window,
6214 cx,
6215 )?
6216 .into_any();
6217
6218 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6219 let offset = point(
6220 (text_bounds.size.width - size.width) / 2.,
6221 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6222 );
6223
6224 let origin = text_bounds.origin + offset;
6225 element.prepaint_at(origin, window, cx);
6226 Some((element, origin))
6227 } else {
6228 self.render_edit_prediction_end_of_line_popover(
6229 "Jump to Edit",
6230 editor_snapshot,
6231 visible_row_range,
6232 target_display_point,
6233 line_height,
6234 scroll_pixel_position,
6235 content_origin,
6236 editor_width,
6237 window,
6238 cx,
6239 )
6240 }
6241 }
6242
6243 #[allow(clippy::too_many_arguments)]
6244 fn render_edit_prediction_end_of_line_popover(
6245 self: &mut Editor,
6246 label: &'static str,
6247 editor_snapshot: &EditorSnapshot,
6248 visible_row_range: Range<DisplayRow>,
6249 target_display_point: DisplayPoint,
6250 line_height: Pixels,
6251 scroll_pixel_position: gpui::Point<Pixels>,
6252 content_origin: gpui::Point<Pixels>,
6253 editor_width: Pixels,
6254 window: &mut Window,
6255 cx: &mut App,
6256 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6257 let target_line_end = DisplayPoint::new(
6258 target_display_point.row(),
6259 editor_snapshot.line_len(target_display_point.row()),
6260 );
6261
6262 let mut element = self
6263 .render_edit_prediction_line_popover(label, None, window, cx)?
6264 .into_any();
6265
6266 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6267
6268 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6269
6270 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6271 let mut origin = start_point
6272 + line_origin
6273 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6274 origin.x = origin.x.max(content_origin.x);
6275
6276 let max_x = content_origin.x + editor_width - size.width;
6277
6278 if origin.x > max_x {
6279 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6280
6281 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6282 origin.y += offset;
6283 IconName::ArrowUp
6284 } else {
6285 origin.y -= offset;
6286 IconName::ArrowDown
6287 };
6288
6289 element = self
6290 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6291 .into_any();
6292
6293 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6294
6295 origin.x = content_origin.x + editor_width - size.width - px(2.);
6296 }
6297
6298 element.prepaint_at(origin, window, cx);
6299 Some((element, origin))
6300 }
6301
6302 #[allow(clippy::too_many_arguments)]
6303 fn render_edit_prediction_diff_popover(
6304 self: &Editor,
6305 text_bounds: &Bounds<Pixels>,
6306 content_origin: gpui::Point<Pixels>,
6307 editor_snapshot: &EditorSnapshot,
6308 visible_row_range: Range<DisplayRow>,
6309 line_layouts: &[LineWithInvisibles],
6310 line_height: Pixels,
6311 scroll_pixel_position: gpui::Point<Pixels>,
6312 newest_selection_head: Option<DisplayPoint>,
6313 editor_width: Pixels,
6314 style: &EditorStyle,
6315 edits: &Vec<(Range<Anchor>, String)>,
6316 edit_preview: &Option<language::EditPreview>,
6317 snapshot: &language::BufferSnapshot,
6318 window: &mut Window,
6319 cx: &mut App,
6320 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6321 let edit_start = edits
6322 .first()
6323 .unwrap()
6324 .0
6325 .start
6326 .to_display_point(editor_snapshot);
6327 let edit_end = edits
6328 .last()
6329 .unwrap()
6330 .0
6331 .end
6332 .to_display_point(editor_snapshot);
6333
6334 let is_visible = visible_row_range.contains(&edit_start.row())
6335 || visible_row_range.contains(&edit_end.row());
6336 if !is_visible {
6337 return None;
6338 }
6339
6340 let highlighted_edits =
6341 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6342
6343 let styled_text = highlighted_edits.to_styled_text(&style.text);
6344 let line_count = highlighted_edits.text.lines().count();
6345
6346 const BORDER_WIDTH: Pixels = px(1.);
6347
6348 let mut element = h_flex()
6349 .items_start()
6350 .child(
6351 h_flex()
6352 .bg(cx.theme().colors().editor_background)
6353 .border(BORDER_WIDTH)
6354 .shadow_sm()
6355 .border_color(cx.theme().colors().border)
6356 .rounded_l_lg()
6357 .when(line_count > 1, |el| el.rounded_br_lg())
6358 .pr_1()
6359 .child(styled_text),
6360 )
6361 .child(
6362 h_flex()
6363 .h(line_height + BORDER_WIDTH * px(2.))
6364 .px_1p5()
6365 .gap_1()
6366 // Workaround: For some reason, there's a gap if we don't do this
6367 .ml(-BORDER_WIDTH)
6368 .shadow(smallvec![gpui::BoxShadow {
6369 color: gpui::black().opacity(0.05),
6370 offset: point(px(1.), px(1.)),
6371 blur_radius: px(2.),
6372 spread_radius: px(0.),
6373 }])
6374 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6375 .border(BORDER_WIDTH)
6376 .border_color(cx.theme().colors().border)
6377 .rounded_r_lg()
6378 .children(self.render_edit_prediction_accept_keybind(window, cx)),
6379 )
6380 .into_any();
6381
6382 let longest_row =
6383 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6384 let longest_line_width = if visible_row_range.contains(&longest_row) {
6385 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6386 } else {
6387 layout_line(
6388 longest_row,
6389 editor_snapshot,
6390 style,
6391 editor_width,
6392 |_| false,
6393 window,
6394 cx,
6395 )
6396 .width
6397 };
6398
6399 let viewport_bounds =
6400 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6401 right: -EditorElement::SCROLLBAR_WIDTH,
6402 ..Default::default()
6403 });
6404
6405 let x_after_longest =
6406 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6407 - scroll_pixel_position.x;
6408
6409 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6410
6411 // Fully visible if it can be displayed within the window (allow overlapping other
6412 // panes). However, this is only allowed if the popover starts within text_bounds.
6413 let can_position_to_the_right = x_after_longest < text_bounds.right()
6414 && x_after_longest + element_bounds.width < viewport_bounds.right();
6415
6416 let mut origin = if can_position_to_the_right {
6417 point(
6418 x_after_longest,
6419 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6420 - scroll_pixel_position.y,
6421 )
6422 } else {
6423 let cursor_row = newest_selection_head.map(|head| head.row());
6424 let above_edit = edit_start
6425 .row()
6426 .0
6427 .checked_sub(line_count as u32)
6428 .map(DisplayRow);
6429 let below_edit = Some(edit_end.row() + 1);
6430 let above_cursor =
6431 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6432 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6433
6434 // Place the edit popover adjacent to the edit if there is a location
6435 // available that is onscreen and does not obscure the cursor. Otherwise,
6436 // place it adjacent to the cursor.
6437 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6438 .into_iter()
6439 .flatten()
6440 .find(|&start_row| {
6441 let end_row = start_row + line_count as u32;
6442 visible_row_range.contains(&start_row)
6443 && visible_row_range.contains(&end_row)
6444 && cursor_row.map_or(true, |cursor_row| {
6445 !((start_row..end_row).contains(&cursor_row))
6446 })
6447 })?;
6448
6449 content_origin
6450 + point(
6451 -scroll_pixel_position.x,
6452 row_target.as_f32() * line_height - scroll_pixel_position.y,
6453 )
6454 };
6455
6456 origin.x -= BORDER_WIDTH;
6457
6458 window.defer_draw(element, origin, 1);
6459
6460 // Do not return an element, since it will already be drawn due to defer_draw.
6461 None
6462 }
6463
6464 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6465 px(30.)
6466 }
6467
6468 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6469 if self.read_only(cx) {
6470 cx.theme().players().read_only()
6471 } else {
6472 self.style.as_ref().unwrap().local_player
6473 }
6474 }
6475
6476 fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
6477 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6478 let accept_keystroke = accept_binding.keystroke()?;
6479
6480 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6481
6482 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6483 Color::Accent
6484 } else {
6485 Color::Muted
6486 };
6487
6488 h_flex()
6489 .px_0p5()
6490 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6491 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6492 .text_size(TextSize::XSmall.rems(cx))
6493 .child(h_flex().children(ui::render_modifiers(
6494 &accept_keystroke.modifiers,
6495 PlatformStyle::platform(),
6496 Some(modifiers_color),
6497 Some(IconSize::XSmall.rems().into()),
6498 true,
6499 )))
6500 .when(is_platform_style_mac, |parent| {
6501 parent.child(accept_keystroke.key.clone())
6502 })
6503 .when(!is_platform_style_mac, |parent| {
6504 parent.child(
6505 Key::new(
6506 util::capitalize(&accept_keystroke.key),
6507 Some(Color::Default),
6508 )
6509 .size(Some(IconSize::XSmall.rems().into())),
6510 )
6511 })
6512 .into()
6513 }
6514
6515 fn render_edit_prediction_line_popover(
6516 &self,
6517 label: impl Into<SharedString>,
6518 icon: Option<IconName>,
6519 window: &mut Window,
6520 cx: &App,
6521 ) -> Option<Div> {
6522 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6523
6524 let result = h_flex()
6525 .py_0p5()
6526 .pl_1()
6527 .pr(padding_right)
6528 .gap_1()
6529 .rounded(px(6.))
6530 .border_1()
6531 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6532 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6533 .shadow_sm()
6534 .children(self.render_edit_prediction_accept_keybind(window, cx))
6535 .child(Label::new(label).size(LabelSize::Small))
6536 .when_some(icon, |element, icon| {
6537 element.child(
6538 div()
6539 .mt(px(1.5))
6540 .child(Icon::new(icon).size(IconSize::Small)),
6541 )
6542 });
6543
6544 Some(result)
6545 }
6546
6547 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6548 let accent_color = cx.theme().colors().text_accent;
6549 let editor_bg_color = cx.theme().colors().editor_background;
6550 editor_bg_color.blend(accent_color.opacity(0.1))
6551 }
6552
6553 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6554 let accent_color = cx.theme().colors().text_accent;
6555 let editor_bg_color = cx.theme().colors().editor_background;
6556 editor_bg_color.blend(accent_color.opacity(0.6))
6557 }
6558
6559 #[allow(clippy::too_many_arguments)]
6560 fn render_edit_prediction_cursor_popover(
6561 &self,
6562 min_width: Pixels,
6563 max_width: Pixels,
6564 cursor_point: Point,
6565 style: &EditorStyle,
6566 accept_keystroke: Option<&gpui::Keystroke>,
6567 _window: &Window,
6568 cx: &mut Context<Editor>,
6569 ) -> Option<AnyElement> {
6570 let provider = self.edit_prediction_provider.as_ref()?;
6571
6572 if provider.provider.needs_terms_acceptance(cx) {
6573 return Some(
6574 h_flex()
6575 .min_w(min_width)
6576 .flex_1()
6577 .px_2()
6578 .py_1()
6579 .gap_3()
6580 .elevation_2(cx)
6581 .hover(|style| style.bg(cx.theme().colors().element_hover))
6582 .id("accept-terms")
6583 .cursor_pointer()
6584 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6585 .on_click(cx.listener(|this, _event, window, cx| {
6586 cx.stop_propagation();
6587 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6588 window.dispatch_action(
6589 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6590 cx,
6591 );
6592 }))
6593 .child(
6594 h_flex()
6595 .flex_1()
6596 .gap_2()
6597 .child(Icon::new(IconName::ZedPredict))
6598 .child(Label::new("Accept Terms of Service"))
6599 .child(div().w_full())
6600 .child(
6601 Icon::new(IconName::ArrowUpRight)
6602 .color(Color::Muted)
6603 .size(IconSize::Small),
6604 )
6605 .into_any_element(),
6606 )
6607 .into_any(),
6608 );
6609 }
6610
6611 let is_refreshing = provider.provider.is_refreshing(cx);
6612
6613 fn pending_completion_container() -> Div {
6614 h_flex()
6615 .h_full()
6616 .flex_1()
6617 .gap_2()
6618 .child(Icon::new(IconName::ZedPredict))
6619 }
6620
6621 let completion = match &self.active_inline_completion {
6622 Some(prediction) => {
6623 if !self.has_visible_completions_menu() {
6624 const RADIUS: Pixels = px(6.);
6625 const BORDER_WIDTH: Pixels = px(1.);
6626
6627 return Some(
6628 h_flex()
6629 .elevation_2(cx)
6630 .border(BORDER_WIDTH)
6631 .border_color(cx.theme().colors().border)
6632 .rounded(RADIUS)
6633 .rounded_tl(px(0.))
6634 .overflow_hidden()
6635 .child(div().px_1p5().child(match &prediction.completion {
6636 InlineCompletion::Move { target, snapshot } => {
6637 use text::ToPoint as _;
6638 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6639 {
6640 Icon::new(IconName::ZedPredictDown)
6641 } else {
6642 Icon::new(IconName::ZedPredictUp)
6643 }
6644 }
6645 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6646 }))
6647 .child(
6648 h_flex()
6649 .gap_1()
6650 .py_1()
6651 .px_2()
6652 .rounded_r(RADIUS - BORDER_WIDTH)
6653 .border_l_1()
6654 .border_color(cx.theme().colors().border)
6655 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6656 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6657 el.child(
6658 Label::new("Hold")
6659 .size(LabelSize::Small)
6660 .line_height_style(LineHeightStyle::UiLabel),
6661 )
6662 })
6663 .child(h_flex().children(ui::render_modifiers(
6664 &accept_keystroke?.modifiers,
6665 PlatformStyle::platform(),
6666 Some(Color::Default),
6667 Some(IconSize::XSmall.rems().into()),
6668 false,
6669 ))),
6670 )
6671 .into_any(),
6672 );
6673 }
6674
6675 self.render_edit_prediction_cursor_popover_preview(
6676 prediction,
6677 cursor_point,
6678 style,
6679 cx,
6680 )?
6681 }
6682
6683 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6684 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6685 stale_completion,
6686 cursor_point,
6687 style,
6688 cx,
6689 )?,
6690
6691 None => {
6692 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6693 }
6694 },
6695
6696 None => pending_completion_container().child(Label::new("No Prediction")),
6697 };
6698
6699 let completion = if is_refreshing {
6700 completion
6701 .with_animation(
6702 "loading-completion",
6703 Animation::new(Duration::from_secs(2))
6704 .repeat()
6705 .with_easing(pulsating_between(0.4, 0.8)),
6706 |label, delta| label.opacity(delta),
6707 )
6708 .into_any_element()
6709 } else {
6710 completion.into_any_element()
6711 };
6712
6713 let has_completion = self.active_inline_completion.is_some();
6714
6715 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6716 Some(
6717 h_flex()
6718 .min_w(min_width)
6719 .max_w(max_width)
6720 .flex_1()
6721 .elevation_2(cx)
6722 .border_color(cx.theme().colors().border)
6723 .child(
6724 div()
6725 .flex_1()
6726 .py_1()
6727 .px_2()
6728 .overflow_hidden()
6729 .child(completion),
6730 )
6731 .when_some(accept_keystroke, |el, accept_keystroke| {
6732 if !accept_keystroke.modifiers.modified() {
6733 return el;
6734 }
6735
6736 el.child(
6737 h_flex()
6738 .h_full()
6739 .border_l_1()
6740 .rounded_r_lg()
6741 .border_color(cx.theme().colors().border)
6742 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6743 .gap_1()
6744 .py_1()
6745 .px_2()
6746 .child(
6747 h_flex()
6748 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6749 .when(is_platform_style_mac, |parent| parent.gap_1())
6750 .child(h_flex().children(ui::render_modifiers(
6751 &accept_keystroke.modifiers,
6752 PlatformStyle::platform(),
6753 Some(if !has_completion {
6754 Color::Muted
6755 } else {
6756 Color::Default
6757 }),
6758 None,
6759 false,
6760 ))),
6761 )
6762 .child(Label::new("Preview").into_any_element())
6763 .opacity(if has_completion { 1.0 } else { 0.4 }),
6764 )
6765 })
6766 .into_any(),
6767 )
6768 }
6769
6770 fn render_edit_prediction_cursor_popover_preview(
6771 &self,
6772 completion: &InlineCompletionState,
6773 cursor_point: Point,
6774 style: &EditorStyle,
6775 cx: &mut Context<Editor>,
6776 ) -> Option<Div> {
6777 use text::ToPoint as _;
6778
6779 fn render_relative_row_jump(
6780 prefix: impl Into<String>,
6781 current_row: u32,
6782 target_row: u32,
6783 ) -> Div {
6784 let (row_diff, arrow) = if target_row < current_row {
6785 (current_row - target_row, IconName::ArrowUp)
6786 } else {
6787 (target_row - current_row, IconName::ArrowDown)
6788 };
6789
6790 h_flex()
6791 .child(
6792 Label::new(format!("{}{}", prefix.into(), row_diff))
6793 .color(Color::Muted)
6794 .size(LabelSize::Small),
6795 )
6796 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6797 }
6798
6799 match &completion.completion {
6800 InlineCompletion::Move {
6801 target, snapshot, ..
6802 } => Some(
6803 h_flex()
6804 .px_2()
6805 .gap_2()
6806 .flex_1()
6807 .child(
6808 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6809 Icon::new(IconName::ZedPredictDown)
6810 } else {
6811 Icon::new(IconName::ZedPredictUp)
6812 },
6813 )
6814 .child(Label::new("Jump to Edit")),
6815 ),
6816
6817 InlineCompletion::Edit {
6818 edits,
6819 edit_preview,
6820 snapshot,
6821 display_mode: _,
6822 } => {
6823 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6824
6825 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6826 &snapshot,
6827 &edits,
6828 edit_preview.as_ref()?,
6829 true,
6830 cx,
6831 )
6832 .first_line_preview();
6833
6834 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6835 .with_default_highlights(&style.text, highlighted_edits.highlights);
6836
6837 let preview = h_flex()
6838 .gap_1()
6839 .min_w_16()
6840 .child(styled_text)
6841 .when(has_more_lines, |parent| parent.child("…"));
6842
6843 let left = if first_edit_row != cursor_point.row {
6844 render_relative_row_jump("", cursor_point.row, first_edit_row)
6845 .into_any_element()
6846 } else {
6847 Icon::new(IconName::ZedPredict).into_any_element()
6848 };
6849
6850 Some(
6851 h_flex()
6852 .h_full()
6853 .flex_1()
6854 .gap_2()
6855 .pr_1()
6856 .overflow_x_hidden()
6857 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6858 .child(left)
6859 .child(preview),
6860 )
6861 }
6862 }
6863 }
6864
6865 fn render_context_menu(
6866 &self,
6867 style: &EditorStyle,
6868 max_height_in_lines: u32,
6869 y_flipped: bool,
6870 window: &mut Window,
6871 cx: &mut Context<Editor>,
6872 ) -> Option<AnyElement> {
6873 let menu = self.context_menu.borrow();
6874 let menu = menu.as_ref()?;
6875 if !menu.visible() {
6876 return None;
6877 };
6878 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6879 }
6880
6881 fn render_context_menu_aside(
6882 &mut self,
6883 max_size: Size<Pixels>,
6884 window: &mut Window,
6885 cx: &mut Context<Editor>,
6886 ) -> Option<AnyElement> {
6887 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6888 if menu.visible() {
6889 menu.render_aside(self, max_size, window, cx)
6890 } else {
6891 None
6892 }
6893 })
6894 }
6895
6896 fn hide_context_menu(
6897 &mut self,
6898 window: &mut Window,
6899 cx: &mut Context<Self>,
6900 ) -> Option<CodeContextMenu> {
6901 cx.notify();
6902 self.completion_tasks.clear();
6903 let context_menu = self.context_menu.borrow_mut().take();
6904 self.stale_inline_completion_in_menu.take();
6905 self.update_visible_inline_completion(window, cx);
6906 context_menu
6907 }
6908
6909 fn show_snippet_choices(
6910 &mut self,
6911 choices: &Vec<String>,
6912 selection: Range<Anchor>,
6913 cx: &mut Context<Self>,
6914 ) {
6915 if selection.start.buffer_id.is_none() {
6916 return;
6917 }
6918 let buffer_id = selection.start.buffer_id.unwrap();
6919 let buffer = self.buffer().read(cx).buffer(buffer_id);
6920 let id = post_inc(&mut self.next_completion_id);
6921
6922 if let Some(buffer) = buffer {
6923 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6924 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6925 ));
6926 }
6927 }
6928
6929 pub fn insert_snippet(
6930 &mut self,
6931 insertion_ranges: &[Range<usize>],
6932 snippet: Snippet,
6933 window: &mut Window,
6934 cx: &mut Context<Self>,
6935 ) -> Result<()> {
6936 struct Tabstop<T> {
6937 is_end_tabstop: bool,
6938 ranges: Vec<Range<T>>,
6939 choices: Option<Vec<String>>,
6940 }
6941
6942 let tabstops = self.buffer.update(cx, |buffer, cx| {
6943 let snippet_text: Arc<str> = snippet.text.clone().into();
6944 buffer.edit(
6945 insertion_ranges
6946 .iter()
6947 .cloned()
6948 .map(|range| (range, snippet_text.clone())),
6949 Some(AutoindentMode::EachLine),
6950 cx,
6951 );
6952
6953 let snapshot = &*buffer.read(cx);
6954 let snippet = &snippet;
6955 snippet
6956 .tabstops
6957 .iter()
6958 .map(|tabstop| {
6959 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6960 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6961 });
6962 let mut tabstop_ranges = tabstop
6963 .ranges
6964 .iter()
6965 .flat_map(|tabstop_range| {
6966 let mut delta = 0_isize;
6967 insertion_ranges.iter().map(move |insertion_range| {
6968 let insertion_start = insertion_range.start as isize + delta;
6969 delta +=
6970 snippet.text.len() as isize - insertion_range.len() as isize;
6971
6972 let start = ((insertion_start + tabstop_range.start) as usize)
6973 .min(snapshot.len());
6974 let end = ((insertion_start + tabstop_range.end) as usize)
6975 .min(snapshot.len());
6976 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6977 })
6978 })
6979 .collect::<Vec<_>>();
6980 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6981
6982 Tabstop {
6983 is_end_tabstop,
6984 ranges: tabstop_ranges,
6985 choices: tabstop.choices.clone(),
6986 }
6987 })
6988 .collect::<Vec<_>>()
6989 });
6990 if let Some(tabstop) = tabstops.first() {
6991 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6992 s.select_ranges(tabstop.ranges.iter().cloned());
6993 });
6994
6995 if let Some(choices) = &tabstop.choices {
6996 if let Some(selection) = tabstop.ranges.first() {
6997 self.show_snippet_choices(choices, selection.clone(), cx)
6998 }
6999 }
7000
7001 // If we're already at the last tabstop and it's at the end of the snippet,
7002 // we're done, we don't need to keep the state around.
7003 if !tabstop.is_end_tabstop {
7004 let choices = tabstops
7005 .iter()
7006 .map(|tabstop| tabstop.choices.clone())
7007 .collect();
7008
7009 let ranges = tabstops
7010 .into_iter()
7011 .map(|tabstop| tabstop.ranges)
7012 .collect::<Vec<_>>();
7013
7014 self.snippet_stack.push(SnippetState {
7015 active_index: 0,
7016 ranges,
7017 choices,
7018 });
7019 }
7020
7021 // Check whether the just-entered snippet ends with an auto-closable bracket.
7022 if self.autoclose_regions.is_empty() {
7023 let snapshot = self.buffer.read(cx).snapshot(cx);
7024 for selection in &mut self.selections.all::<Point>(cx) {
7025 let selection_head = selection.head();
7026 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7027 continue;
7028 };
7029
7030 let mut bracket_pair = None;
7031 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7032 let prev_chars = snapshot
7033 .reversed_chars_at(selection_head)
7034 .collect::<String>();
7035 for (pair, enabled) in scope.brackets() {
7036 if enabled
7037 && pair.close
7038 && prev_chars.starts_with(pair.start.as_str())
7039 && next_chars.starts_with(pair.end.as_str())
7040 {
7041 bracket_pair = Some(pair.clone());
7042 break;
7043 }
7044 }
7045 if let Some(pair) = bracket_pair {
7046 let start = snapshot.anchor_after(selection_head);
7047 let end = snapshot.anchor_after(selection_head);
7048 self.autoclose_regions.push(AutocloseRegion {
7049 selection_id: selection.id,
7050 range: start..end,
7051 pair,
7052 });
7053 }
7054 }
7055 }
7056 }
7057 Ok(())
7058 }
7059
7060 pub fn move_to_next_snippet_tabstop(
7061 &mut self,
7062 window: &mut Window,
7063 cx: &mut Context<Self>,
7064 ) -> bool {
7065 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7066 }
7067
7068 pub fn move_to_prev_snippet_tabstop(
7069 &mut self,
7070 window: &mut Window,
7071 cx: &mut Context<Self>,
7072 ) -> bool {
7073 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7074 }
7075
7076 pub fn move_to_snippet_tabstop(
7077 &mut self,
7078 bias: Bias,
7079 window: &mut Window,
7080 cx: &mut Context<Self>,
7081 ) -> bool {
7082 if let Some(mut snippet) = self.snippet_stack.pop() {
7083 match bias {
7084 Bias::Left => {
7085 if snippet.active_index > 0 {
7086 snippet.active_index -= 1;
7087 } else {
7088 self.snippet_stack.push(snippet);
7089 return false;
7090 }
7091 }
7092 Bias::Right => {
7093 if snippet.active_index + 1 < snippet.ranges.len() {
7094 snippet.active_index += 1;
7095 } else {
7096 self.snippet_stack.push(snippet);
7097 return false;
7098 }
7099 }
7100 }
7101 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7102 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7103 s.select_anchor_ranges(current_ranges.iter().cloned())
7104 });
7105
7106 if let Some(choices) = &snippet.choices[snippet.active_index] {
7107 if let Some(selection) = current_ranges.first() {
7108 self.show_snippet_choices(&choices, selection.clone(), cx);
7109 }
7110 }
7111
7112 // If snippet state is not at the last tabstop, push it back on the stack
7113 if snippet.active_index + 1 < snippet.ranges.len() {
7114 self.snippet_stack.push(snippet);
7115 }
7116 return true;
7117 }
7118 }
7119
7120 false
7121 }
7122
7123 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7124 self.transact(window, cx, |this, window, cx| {
7125 this.select_all(&SelectAll, window, cx);
7126 this.insert("", window, cx);
7127 });
7128 }
7129
7130 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7131 self.transact(window, cx, |this, window, cx| {
7132 this.select_autoclose_pair(window, cx);
7133 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7134 if !this.linked_edit_ranges.is_empty() {
7135 let selections = this.selections.all::<MultiBufferPoint>(cx);
7136 let snapshot = this.buffer.read(cx).snapshot(cx);
7137
7138 for selection in selections.iter() {
7139 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7140 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7141 if selection_start.buffer_id != selection_end.buffer_id {
7142 continue;
7143 }
7144 if let Some(ranges) =
7145 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7146 {
7147 for (buffer, entries) in ranges {
7148 linked_ranges.entry(buffer).or_default().extend(entries);
7149 }
7150 }
7151 }
7152 }
7153
7154 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7155 if !this.selections.line_mode {
7156 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7157 for selection in &mut selections {
7158 if selection.is_empty() {
7159 let old_head = selection.head();
7160 let mut new_head =
7161 movement::left(&display_map, old_head.to_display_point(&display_map))
7162 .to_point(&display_map);
7163 if let Some((buffer, line_buffer_range)) = display_map
7164 .buffer_snapshot
7165 .buffer_line_for_row(MultiBufferRow(old_head.row))
7166 {
7167 let indent_size =
7168 buffer.indent_size_for_line(line_buffer_range.start.row);
7169 let indent_len = match indent_size.kind {
7170 IndentKind::Space => {
7171 buffer.settings_at(line_buffer_range.start, cx).tab_size
7172 }
7173 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7174 };
7175 if old_head.column <= indent_size.len && old_head.column > 0 {
7176 let indent_len = indent_len.get();
7177 new_head = cmp::min(
7178 new_head,
7179 MultiBufferPoint::new(
7180 old_head.row,
7181 ((old_head.column - 1) / indent_len) * indent_len,
7182 ),
7183 );
7184 }
7185 }
7186
7187 selection.set_head(new_head, SelectionGoal::None);
7188 }
7189 }
7190 }
7191
7192 this.signature_help_state.set_backspace_pressed(true);
7193 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7194 s.select(selections)
7195 });
7196 this.insert("", window, cx);
7197 let empty_str: Arc<str> = Arc::from("");
7198 for (buffer, edits) in linked_ranges {
7199 let snapshot = buffer.read(cx).snapshot();
7200 use text::ToPoint as TP;
7201
7202 let edits = edits
7203 .into_iter()
7204 .map(|range| {
7205 let end_point = TP::to_point(&range.end, &snapshot);
7206 let mut start_point = TP::to_point(&range.start, &snapshot);
7207
7208 if end_point == start_point {
7209 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7210 .saturating_sub(1);
7211 start_point =
7212 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7213 };
7214
7215 (start_point..end_point, empty_str.clone())
7216 })
7217 .sorted_by_key(|(range, _)| range.start)
7218 .collect::<Vec<_>>();
7219 buffer.update(cx, |this, cx| {
7220 this.edit(edits, None, cx);
7221 })
7222 }
7223 this.refresh_inline_completion(true, false, window, cx);
7224 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7225 });
7226 }
7227
7228 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7229 self.transact(window, cx, |this, window, cx| {
7230 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7231 let line_mode = s.line_mode;
7232 s.move_with(|map, selection| {
7233 if selection.is_empty() && !line_mode {
7234 let cursor = movement::right(map, selection.head());
7235 selection.end = cursor;
7236 selection.reversed = true;
7237 selection.goal = SelectionGoal::None;
7238 }
7239 })
7240 });
7241 this.insert("", window, cx);
7242 this.refresh_inline_completion(true, false, window, cx);
7243 });
7244 }
7245
7246 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7247 if self.move_to_prev_snippet_tabstop(window, cx) {
7248 return;
7249 }
7250
7251 self.outdent(&Outdent, window, cx);
7252 }
7253
7254 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7255 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7256 return;
7257 }
7258
7259 let mut selections = self.selections.all_adjusted(cx);
7260 let buffer = self.buffer.read(cx);
7261 let snapshot = buffer.snapshot(cx);
7262 let rows_iter = selections.iter().map(|s| s.head().row);
7263 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7264
7265 let mut edits = Vec::new();
7266 let mut prev_edited_row = 0;
7267 let mut row_delta = 0;
7268 for selection in &mut selections {
7269 if selection.start.row != prev_edited_row {
7270 row_delta = 0;
7271 }
7272 prev_edited_row = selection.end.row;
7273
7274 // If the selection is non-empty, then increase the indentation of the selected lines.
7275 if !selection.is_empty() {
7276 row_delta =
7277 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7278 continue;
7279 }
7280
7281 // If the selection is empty and the cursor is in the leading whitespace before the
7282 // suggested indentation, then auto-indent the line.
7283 let cursor = selection.head();
7284 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7285 if let Some(suggested_indent) =
7286 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7287 {
7288 if cursor.column < suggested_indent.len
7289 && cursor.column <= current_indent.len
7290 && current_indent.len <= suggested_indent.len
7291 {
7292 selection.start = Point::new(cursor.row, suggested_indent.len);
7293 selection.end = selection.start;
7294 if row_delta == 0 {
7295 edits.extend(Buffer::edit_for_indent_size_adjustment(
7296 cursor.row,
7297 current_indent,
7298 suggested_indent,
7299 ));
7300 row_delta = suggested_indent.len - current_indent.len;
7301 }
7302 continue;
7303 }
7304 }
7305
7306 // Otherwise, insert a hard or soft tab.
7307 let settings = buffer.language_settings_at(cursor, cx);
7308 let tab_size = if settings.hard_tabs {
7309 IndentSize::tab()
7310 } else {
7311 let tab_size = settings.tab_size.get();
7312 let char_column = snapshot
7313 .text_for_range(Point::new(cursor.row, 0)..cursor)
7314 .flat_map(str::chars)
7315 .count()
7316 + row_delta as usize;
7317 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7318 IndentSize::spaces(chars_to_next_tab_stop)
7319 };
7320 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7321 selection.end = selection.start;
7322 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7323 row_delta += tab_size.len;
7324 }
7325
7326 self.transact(window, cx, |this, window, cx| {
7327 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7328 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7329 s.select(selections)
7330 });
7331 this.refresh_inline_completion(true, false, window, cx);
7332 });
7333 }
7334
7335 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7336 if self.read_only(cx) {
7337 return;
7338 }
7339 let mut selections = self.selections.all::<Point>(cx);
7340 let mut prev_edited_row = 0;
7341 let mut row_delta = 0;
7342 let mut edits = Vec::new();
7343 let buffer = self.buffer.read(cx);
7344 let snapshot = buffer.snapshot(cx);
7345 for selection in &mut selections {
7346 if selection.start.row != prev_edited_row {
7347 row_delta = 0;
7348 }
7349 prev_edited_row = selection.end.row;
7350
7351 row_delta =
7352 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7353 }
7354
7355 self.transact(window, cx, |this, window, cx| {
7356 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7357 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7358 s.select(selections)
7359 });
7360 });
7361 }
7362
7363 fn indent_selection(
7364 buffer: &MultiBuffer,
7365 snapshot: &MultiBufferSnapshot,
7366 selection: &mut Selection<Point>,
7367 edits: &mut Vec<(Range<Point>, String)>,
7368 delta_for_start_row: u32,
7369 cx: &App,
7370 ) -> u32 {
7371 let settings = buffer.language_settings_at(selection.start, cx);
7372 let tab_size = settings.tab_size.get();
7373 let indent_kind = if settings.hard_tabs {
7374 IndentKind::Tab
7375 } else {
7376 IndentKind::Space
7377 };
7378 let mut start_row = selection.start.row;
7379 let mut end_row = selection.end.row + 1;
7380
7381 // If a selection ends at the beginning of a line, don't indent
7382 // that last line.
7383 if selection.end.column == 0 && selection.end.row > selection.start.row {
7384 end_row -= 1;
7385 }
7386
7387 // Avoid re-indenting a row that has already been indented by a
7388 // previous selection, but still update this selection's column
7389 // to reflect that indentation.
7390 if delta_for_start_row > 0 {
7391 start_row += 1;
7392 selection.start.column += delta_for_start_row;
7393 if selection.end.row == selection.start.row {
7394 selection.end.column += delta_for_start_row;
7395 }
7396 }
7397
7398 let mut delta_for_end_row = 0;
7399 let has_multiple_rows = start_row + 1 != end_row;
7400 for row in start_row..end_row {
7401 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7402 let indent_delta = match (current_indent.kind, indent_kind) {
7403 (IndentKind::Space, IndentKind::Space) => {
7404 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7405 IndentSize::spaces(columns_to_next_tab_stop)
7406 }
7407 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7408 (_, IndentKind::Tab) => IndentSize::tab(),
7409 };
7410
7411 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7412 0
7413 } else {
7414 selection.start.column
7415 };
7416 let row_start = Point::new(row, start);
7417 edits.push((
7418 row_start..row_start,
7419 indent_delta.chars().collect::<String>(),
7420 ));
7421
7422 // Update this selection's endpoints to reflect the indentation.
7423 if row == selection.start.row {
7424 selection.start.column += indent_delta.len;
7425 }
7426 if row == selection.end.row {
7427 selection.end.column += indent_delta.len;
7428 delta_for_end_row = indent_delta.len;
7429 }
7430 }
7431
7432 if selection.start.row == selection.end.row {
7433 delta_for_start_row + delta_for_end_row
7434 } else {
7435 delta_for_end_row
7436 }
7437 }
7438
7439 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7440 if self.read_only(cx) {
7441 return;
7442 }
7443 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7444 let selections = self.selections.all::<Point>(cx);
7445 let mut deletion_ranges = Vec::new();
7446 let mut last_outdent = None;
7447 {
7448 let buffer = self.buffer.read(cx);
7449 let snapshot = buffer.snapshot(cx);
7450 for selection in &selections {
7451 let settings = buffer.language_settings_at(selection.start, cx);
7452 let tab_size = settings.tab_size.get();
7453 let mut rows = selection.spanned_rows(false, &display_map);
7454
7455 // Avoid re-outdenting a row that has already been outdented by a
7456 // previous selection.
7457 if let Some(last_row) = last_outdent {
7458 if last_row == rows.start {
7459 rows.start = rows.start.next_row();
7460 }
7461 }
7462 let has_multiple_rows = rows.len() > 1;
7463 for row in rows.iter_rows() {
7464 let indent_size = snapshot.indent_size_for_line(row);
7465 if indent_size.len > 0 {
7466 let deletion_len = match indent_size.kind {
7467 IndentKind::Space => {
7468 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7469 if columns_to_prev_tab_stop == 0 {
7470 tab_size
7471 } else {
7472 columns_to_prev_tab_stop
7473 }
7474 }
7475 IndentKind::Tab => 1,
7476 };
7477 let start = if has_multiple_rows
7478 || deletion_len > selection.start.column
7479 || indent_size.len < selection.start.column
7480 {
7481 0
7482 } else {
7483 selection.start.column - deletion_len
7484 };
7485 deletion_ranges.push(
7486 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7487 );
7488 last_outdent = Some(row);
7489 }
7490 }
7491 }
7492 }
7493
7494 self.transact(window, cx, |this, window, cx| {
7495 this.buffer.update(cx, |buffer, cx| {
7496 let empty_str: Arc<str> = Arc::default();
7497 buffer.edit(
7498 deletion_ranges
7499 .into_iter()
7500 .map(|range| (range, empty_str.clone())),
7501 None,
7502 cx,
7503 );
7504 });
7505 let selections = this.selections.all::<usize>(cx);
7506 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7507 s.select(selections)
7508 });
7509 });
7510 }
7511
7512 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7513 if self.read_only(cx) {
7514 return;
7515 }
7516 let selections = self
7517 .selections
7518 .all::<usize>(cx)
7519 .into_iter()
7520 .map(|s| s.range());
7521
7522 self.transact(window, cx, |this, window, cx| {
7523 this.buffer.update(cx, |buffer, cx| {
7524 buffer.autoindent_ranges(selections, cx);
7525 });
7526 let selections = this.selections.all::<usize>(cx);
7527 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7528 s.select(selections)
7529 });
7530 });
7531 }
7532
7533 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7534 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7535 let selections = self.selections.all::<Point>(cx);
7536
7537 let mut new_cursors = Vec::new();
7538 let mut edit_ranges = Vec::new();
7539 let mut selections = selections.iter().peekable();
7540 while let Some(selection) = selections.next() {
7541 let mut rows = selection.spanned_rows(false, &display_map);
7542 let goal_display_column = selection.head().to_display_point(&display_map).column();
7543
7544 // Accumulate contiguous regions of rows that we want to delete.
7545 while let Some(next_selection) = selections.peek() {
7546 let next_rows = next_selection.spanned_rows(false, &display_map);
7547 if next_rows.start <= rows.end {
7548 rows.end = next_rows.end;
7549 selections.next().unwrap();
7550 } else {
7551 break;
7552 }
7553 }
7554
7555 let buffer = &display_map.buffer_snapshot;
7556 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7557 let edit_end;
7558 let cursor_buffer_row;
7559 if buffer.max_point().row >= rows.end.0 {
7560 // If there's a line after the range, delete the \n from the end of the row range
7561 // and position the cursor on the next line.
7562 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7563 cursor_buffer_row = rows.end;
7564 } else {
7565 // If there isn't a line after the range, delete the \n from the line before the
7566 // start of the row range and position the cursor there.
7567 edit_start = edit_start.saturating_sub(1);
7568 edit_end = buffer.len();
7569 cursor_buffer_row = rows.start.previous_row();
7570 }
7571
7572 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7573 *cursor.column_mut() =
7574 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7575
7576 new_cursors.push((
7577 selection.id,
7578 buffer.anchor_after(cursor.to_point(&display_map)),
7579 ));
7580 edit_ranges.push(edit_start..edit_end);
7581 }
7582
7583 self.transact(window, cx, |this, window, cx| {
7584 let buffer = this.buffer.update(cx, |buffer, cx| {
7585 let empty_str: Arc<str> = Arc::default();
7586 buffer.edit(
7587 edit_ranges
7588 .into_iter()
7589 .map(|range| (range, empty_str.clone())),
7590 None,
7591 cx,
7592 );
7593 buffer.snapshot(cx)
7594 });
7595 let new_selections = new_cursors
7596 .into_iter()
7597 .map(|(id, cursor)| {
7598 let cursor = cursor.to_point(&buffer);
7599 Selection {
7600 id,
7601 start: cursor,
7602 end: cursor,
7603 reversed: false,
7604 goal: SelectionGoal::None,
7605 }
7606 })
7607 .collect();
7608
7609 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7610 s.select(new_selections);
7611 });
7612 });
7613 }
7614
7615 pub fn join_lines_impl(
7616 &mut self,
7617 insert_whitespace: bool,
7618 window: &mut Window,
7619 cx: &mut Context<Self>,
7620 ) {
7621 if self.read_only(cx) {
7622 return;
7623 }
7624 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7625 for selection in self.selections.all::<Point>(cx) {
7626 let start = MultiBufferRow(selection.start.row);
7627 // Treat single line selections as if they include the next line. Otherwise this action
7628 // would do nothing for single line selections individual cursors.
7629 let end = if selection.start.row == selection.end.row {
7630 MultiBufferRow(selection.start.row + 1)
7631 } else {
7632 MultiBufferRow(selection.end.row)
7633 };
7634
7635 if let Some(last_row_range) = row_ranges.last_mut() {
7636 if start <= last_row_range.end {
7637 last_row_range.end = end;
7638 continue;
7639 }
7640 }
7641 row_ranges.push(start..end);
7642 }
7643
7644 let snapshot = self.buffer.read(cx).snapshot(cx);
7645 let mut cursor_positions = Vec::new();
7646 for row_range in &row_ranges {
7647 let anchor = snapshot.anchor_before(Point::new(
7648 row_range.end.previous_row().0,
7649 snapshot.line_len(row_range.end.previous_row()),
7650 ));
7651 cursor_positions.push(anchor..anchor);
7652 }
7653
7654 self.transact(window, cx, |this, window, cx| {
7655 for row_range in row_ranges.into_iter().rev() {
7656 for row in row_range.iter_rows().rev() {
7657 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7658 let next_line_row = row.next_row();
7659 let indent = snapshot.indent_size_for_line(next_line_row);
7660 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7661
7662 let replace =
7663 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7664 " "
7665 } else {
7666 ""
7667 };
7668
7669 this.buffer.update(cx, |buffer, cx| {
7670 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7671 });
7672 }
7673 }
7674
7675 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7676 s.select_anchor_ranges(cursor_positions)
7677 });
7678 });
7679 }
7680
7681 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7682 self.join_lines_impl(true, window, cx);
7683 }
7684
7685 pub fn sort_lines_case_sensitive(
7686 &mut self,
7687 _: &SortLinesCaseSensitive,
7688 window: &mut Window,
7689 cx: &mut Context<Self>,
7690 ) {
7691 self.manipulate_lines(window, cx, |lines| lines.sort())
7692 }
7693
7694 pub fn sort_lines_case_insensitive(
7695 &mut self,
7696 _: &SortLinesCaseInsensitive,
7697 window: &mut Window,
7698 cx: &mut Context<Self>,
7699 ) {
7700 self.manipulate_lines(window, cx, |lines| {
7701 lines.sort_by_key(|line| line.to_lowercase())
7702 })
7703 }
7704
7705 pub fn unique_lines_case_insensitive(
7706 &mut self,
7707 _: &UniqueLinesCaseInsensitive,
7708 window: &mut Window,
7709 cx: &mut Context<Self>,
7710 ) {
7711 self.manipulate_lines(window, cx, |lines| {
7712 let mut seen = HashSet::default();
7713 lines.retain(|line| seen.insert(line.to_lowercase()));
7714 })
7715 }
7716
7717 pub fn unique_lines_case_sensitive(
7718 &mut self,
7719 _: &UniqueLinesCaseSensitive,
7720 window: &mut Window,
7721 cx: &mut Context<Self>,
7722 ) {
7723 self.manipulate_lines(window, cx, |lines| {
7724 let mut seen = HashSet::default();
7725 lines.retain(|line| seen.insert(*line));
7726 })
7727 }
7728
7729 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7730 let Some(project) = self.project.clone() else {
7731 return;
7732 };
7733 self.reload(project, window, cx)
7734 .detach_and_notify_err(window, cx);
7735 }
7736
7737 pub fn restore_file(
7738 &mut self,
7739 _: &::git::RestoreFile,
7740 window: &mut Window,
7741 cx: &mut Context<Self>,
7742 ) {
7743 let mut buffer_ids = HashSet::default();
7744 let snapshot = self.buffer().read(cx).snapshot(cx);
7745 for selection in self.selections.all::<usize>(cx) {
7746 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7747 }
7748
7749 let buffer = self.buffer().read(cx);
7750 let ranges = buffer_ids
7751 .into_iter()
7752 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7753 .collect::<Vec<_>>();
7754
7755 self.restore_hunks_in_ranges(ranges, window, cx);
7756 }
7757
7758 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7759 let selections = self
7760 .selections
7761 .all(cx)
7762 .into_iter()
7763 .map(|s| s.range())
7764 .collect();
7765 self.restore_hunks_in_ranges(selections, window, cx);
7766 }
7767
7768 fn restore_hunks_in_ranges(
7769 &mut self,
7770 ranges: Vec<Range<Point>>,
7771 window: &mut Window,
7772 cx: &mut Context<Editor>,
7773 ) {
7774 let mut revert_changes = HashMap::default();
7775 let chunk_by = self
7776 .snapshot(window, cx)
7777 .hunks_for_ranges(ranges)
7778 .into_iter()
7779 .chunk_by(|hunk| hunk.buffer_id);
7780 for (buffer_id, hunks) in &chunk_by {
7781 let hunks = hunks.collect::<Vec<_>>();
7782 for hunk in &hunks {
7783 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7784 }
7785 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
7786 }
7787 drop(chunk_by);
7788 if !revert_changes.is_empty() {
7789 self.transact(window, cx, |editor, window, cx| {
7790 editor.restore(revert_changes, window, cx);
7791 });
7792 }
7793 }
7794
7795 pub fn open_active_item_in_terminal(
7796 &mut self,
7797 _: &OpenInTerminal,
7798 window: &mut Window,
7799 cx: &mut Context<Self>,
7800 ) {
7801 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7802 let project_path = buffer.read(cx).project_path(cx)?;
7803 let project = self.project.as_ref()?.read(cx);
7804 let entry = project.entry_for_path(&project_path, cx)?;
7805 let parent = match &entry.canonical_path {
7806 Some(canonical_path) => canonical_path.to_path_buf(),
7807 None => project.absolute_path(&project_path, cx)?,
7808 }
7809 .parent()?
7810 .to_path_buf();
7811 Some(parent)
7812 }) {
7813 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7814 }
7815 }
7816
7817 pub fn prepare_restore_change(
7818 &self,
7819 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7820 hunk: &MultiBufferDiffHunk,
7821 cx: &mut App,
7822 ) -> Option<()> {
7823 if hunk.is_created_file() {
7824 return None;
7825 }
7826 let buffer = self.buffer.read(cx);
7827 let diff = buffer.diff_for(hunk.buffer_id)?;
7828 let buffer = buffer.buffer(hunk.buffer_id)?;
7829 let buffer = buffer.read(cx);
7830 let original_text = diff
7831 .read(cx)
7832 .base_text()
7833 .as_rope()
7834 .slice(hunk.diff_base_byte_range.clone());
7835 let buffer_snapshot = buffer.snapshot();
7836 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7837 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7838 probe
7839 .0
7840 .start
7841 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7842 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7843 }) {
7844 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7845 Some(())
7846 } else {
7847 None
7848 }
7849 }
7850
7851 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7852 self.manipulate_lines(window, cx, |lines| lines.reverse())
7853 }
7854
7855 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7856 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7857 }
7858
7859 fn manipulate_lines<Fn>(
7860 &mut self,
7861 window: &mut Window,
7862 cx: &mut Context<Self>,
7863 mut callback: Fn,
7864 ) where
7865 Fn: FnMut(&mut Vec<&str>),
7866 {
7867 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7868 let buffer = self.buffer.read(cx).snapshot(cx);
7869
7870 let mut edits = Vec::new();
7871
7872 let selections = self.selections.all::<Point>(cx);
7873 let mut selections = selections.iter().peekable();
7874 let mut contiguous_row_selections = Vec::new();
7875 let mut new_selections = Vec::new();
7876 let mut added_lines = 0;
7877 let mut removed_lines = 0;
7878
7879 while let Some(selection) = selections.next() {
7880 let (start_row, end_row) = consume_contiguous_rows(
7881 &mut contiguous_row_selections,
7882 selection,
7883 &display_map,
7884 &mut selections,
7885 );
7886
7887 let start_point = Point::new(start_row.0, 0);
7888 let end_point = Point::new(
7889 end_row.previous_row().0,
7890 buffer.line_len(end_row.previous_row()),
7891 );
7892 let text = buffer
7893 .text_for_range(start_point..end_point)
7894 .collect::<String>();
7895
7896 let mut lines = text.split('\n').collect_vec();
7897
7898 let lines_before = lines.len();
7899 callback(&mut lines);
7900 let lines_after = lines.len();
7901
7902 edits.push((start_point..end_point, lines.join("\n")));
7903
7904 // Selections must change based on added and removed line count
7905 let start_row =
7906 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7907 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7908 new_selections.push(Selection {
7909 id: selection.id,
7910 start: start_row,
7911 end: end_row,
7912 goal: SelectionGoal::None,
7913 reversed: selection.reversed,
7914 });
7915
7916 if lines_after > lines_before {
7917 added_lines += lines_after - lines_before;
7918 } else if lines_before > lines_after {
7919 removed_lines += lines_before - lines_after;
7920 }
7921 }
7922
7923 self.transact(window, cx, |this, window, cx| {
7924 let buffer = this.buffer.update(cx, |buffer, cx| {
7925 buffer.edit(edits, None, cx);
7926 buffer.snapshot(cx)
7927 });
7928
7929 // Recalculate offsets on newly edited buffer
7930 let new_selections = new_selections
7931 .iter()
7932 .map(|s| {
7933 let start_point = Point::new(s.start.0, 0);
7934 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7935 Selection {
7936 id: s.id,
7937 start: buffer.point_to_offset(start_point),
7938 end: buffer.point_to_offset(end_point),
7939 goal: s.goal,
7940 reversed: s.reversed,
7941 }
7942 })
7943 .collect();
7944
7945 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7946 s.select(new_selections);
7947 });
7948
7949 this.request_autoscroll(Autoscroll::fit(), cx);
7950 });
7951 }
7952
7953 pub fn convert_to_upper_case(
7954 &mut self,
7955 _: &ConvertToUpperCase,
7956 window: &mut Window,
7957 cx: &mut Context<Self>,
7958 ) {
7959 self.manipulate_text(window, cx, |text| text.to_uppercase())
7960 }
7961
7962 pub fn convert_to_lower_case(
7963 &mut self,
7964 _: &ConvertToLowerCase,
7965 window: &mut Window,
7966 cx: &mut Context<Self>,
7967 ) {
7968 self.manipulate_text(window, cx, |text| text.to_lowercase())
7969 }
7970
7971 pub fn convert_to_title_case(
7972 &mut self,
7973 _: &ConvertToTitleCase,
7974 window: &mut Window,
7975 cx: &mut Context<Self>,
7976 ) {
7977 self.manipulate_text(window, cx, |text| {
7978 text.split('\n')
7979 .map(|line| line.to_case(Case::Title))
7980 .join("\n")
7981 })
7982 }
7983
7984 pub fn convert_to_snake_case(
7985 &mut self,
7986 _: &ConvertToSnakeCase,
7987 window: &mut Window,
7988 cx: &mut Context<Self>,
7989 ) {
7990 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7991 }
7992
7993 pub fn convert_to_kebab_case(
7994 &mut self,
7995 _: &ConvertToKebabCase,
7996 window: &mut Window,
7997 cx: &mut Context<Self>,
7998 ) {
7999 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8000 }
8001
8002 pub fn convert_to_upper_camel_case(
8003 &mut self,
8004 _: &ConvertToUpperCamelCase,
8005 window: &mut Window,
8006 cx: &mut Context<Self>,
8007 ) {
8008 self.manipulate_text(window, cx, |text| {
8009 text.split('\n')
8010 .map(|line| line.to_case(Case::UpperCamel))
8011 .join("\n")
8012 })
8013 }
8014
8015 pub fn convert_to_lower_camel_case(
8016 &mut self,
8017 _: &ConvertToLowerCamelCase,
8018 window: &mut Window,
8019 cx: &mut Context<Self>,
8020 ) {
8021 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8022 }
8023
8024 pub fn convert_to_opposite_case(
8025 &mut self,
8026 _: &ConvertToOppositeCase,
8027 window: &mut Window,
8028 cx: &mut Context<Self>,
8029 ) {
8030 self.manipulate_text(window, cx, |text| {
8031 text.chars()
8032 .fold(String::with_capacity(text.len()), |mut t, c| {
8033 if c.is_uppercase() {
8034 t.extend(c.to_lowercase());
8035 } else {
8036 t.extend(c.to_uppercase());
8037 }
8038 t
8039 })
8040 })
8041 }
8042
8043 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8044 where
8045 Fn: FnMut(&str) -> String,
8046 {
8047 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8048 let buffer = self.buffer.read(cx).snapshot(cx);
8049
8050 let mut new_selections = Vec::new();
8051 let mut edits = Vec::new();
8052 let mut selection_adjustment = 0i32;
8053
8054 for selection in self.selections.all::<usize>(cx) {
8055 let selection_is_empty = selection.is_empty();
8056
8057 let (start, end) = if selection_is_empty {
8058 let word_range = movement::surrounding_word(
8059 &display_map,
8060 selection.start.to_display_point(&display_map),
8061 );
8062 let start = word_range.start.to_offset(&display_map, Bias::Left);
8063 let end = word_range.end.to_offset(&display_map, Bias::Left);
8064 (start, end)
8065 } else {
8066 (selection.start, selection.end)
8067 };
8068
8069 let text = buffer.text_for_range(start..end).collect::<String>();
8070 let old_length = text.len() as i32;
8071 let text = callback(&text);
8072
8073 new_selections.push(Selection {
8074 start: (start as i32 - selection_adjustment) as usize,
8075 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8076 goal: SelectionGoal::None,
8077 ..selection
8078 });
8079
8080 selection_adjustment += old_length - text.len() as i32;
8081
8082 edits.push((start..end, text));
8083 }
8084
8085 self.transact(window, cx, |this, window, cx| {
8086 this.buffer.update(cx, |buffer, cx| {
8087 buffer.edit(edits, None, cx);
8088 });
8089
8090 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8091 s.select(new_selections);
8092 });
8093
8094 this.request_autoscroll(Autoscroll::fit(), cx);
8095 });
8096 }
8097
8098 pub fn duplicate(
8099 &mut self,
8100 upwards: bool,
8101 whole_lines: bool,
8102 window: &mut Window,
8103 cx: &mut Context<Self>,
8104 ) {
8105 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8106 let buffer = &display_map.buffer_snapshot;
8107 let selections = self.selections.all::<Point>(cx);
8108
8109 let mut edits = Vec::new();
8110 let mut selections_iter = selections.iter().peekable();
8111 while let Some(selection) = selections_iter.next() {
8112 let mut rows = selection.spanned_rows(false, &display_map);
8113 // duplicate line-wise
8114 if whole_lines || selection.start == selection.end {
8115 // Avoid duplicating the same lines twice.
8116 while let Some(next_selection) = selections_iter.peek() {
8117 let next_rows = next_selection.spanned_rows(false, &display_map);
8118 if next_rows.start < rows.end {
8119 rows.end = next_rows.end;
8120 selections_iter.next().unwrap();
8121 } else {
8122 break;
8123 }
8124 }
8125
8126 // Copy the text from the selected row region and splice it either at the start
8127 // or end of the region.
8128 let start = Point::new(rows.start.0, 0);
8129 let end = Point::new(
8130 rows.end.previous_row().0,
8131 buffer.line_len(rows.end.previous_row()),
8132 );
8133 let text = buffer
8134 .text_for_range(start..end)
8135 .chain(Some("\n"))
8136 .collect::<String>();
8137 let insert_location = if upwards {
8138 Point::new(rows.end.0, 0)
8139 } else {
8140 start
8141 };
8142 edits.push((insert_location..insert_location, text));
8143 } else {
8144 // duplicate character-wise
8145 let start = selection.start;
8146 let end = selection.end;
8147 let text = buffer.text_for_range(start..end).collect::<String>();
8148 edits.push((selection.end..selection.end, text));
8149 }
8150 }
8151
8152 self.transact(window, cx, |this, _, cx| {
8153 this.buffer.update(cx, |buffer, cx| {
8154 buffer.edit(edits, None, cx);
8155 });
8156
8157 this.request_autoscroll(Autoscroll::fit(), cx);
8158 });
8159 }
8160
8161 pub fn duplicate_line_up(
8162 &mut self,
8163 _: &DuplicateLineUp,
8164 window: &mut Window,
8165 cx: &mut Context<Self>,
8166 ) {
8167 self.duplicate(true, true, window, cx);
8168 }
8169
8170 pub fn duplicate_line_down(
8171 &mut self,
8172 _: &DuplicateLineDown,
8173 window: &mut Window,
8174 cx: &mut Context<Self>,
8175 ) {
8176 self.duplicate(false, true, window, cx);
8177 }
8178
8179 pub fn duplicate_selection(
8180 &mut self,
8181 _: &DuplicateSelection,
8182 window: &mut Window,
8183 cx: &mut Context<Self>,
8184 ) {
8185 self.duplicate(false, false, window, cx);
8186 }
8187
8188 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8189 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8190 let buffer = self.buffer.read(cx).snapshot(cx);
8191
8192 let mut edits = Vec::new();
8193 let mut unfold_ranges = Vec::new();
8194 let mut refold_creases = Vec::new();
8195
8196 let selections = self.selections.all::<Point>(cx);
8197 let mut selections = selections.iter().peekable();
8198 let mut contiguous_row_selections = Vec::new();
8199 let mut new_selections = Vec::new();
8200
8201 while let Some(selection) = selections.next() {
8202 // Find all the selections that span a contiguous row range
8203 let (start_row, end_row) = consume_contiguous_rows(
8204 &mut contiguous_row_selections,
8205 selection,
8206 &display_map,
8207 &mut selections,
8208 );
8209
8210 // Move the text spanned by the row range to be before the line preceding the row range
8211 if start_row.0 > 0 {
8212 let range_to_move = Point::new(
8213 start_row.previous_row().0,
8214 buffer.line_len(start_row.previous_row()),
8215 )
8216 ..Point::new(
8217 end_row.previous_row().0,
8218 buffer.line_len(end_row.previous_row()),
8219 );
8220 let insertion_point = display_map
8221 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8222 .0;
8223
8224 // Don't move lines across excerpts
8225 if buffer
8226 .excerpt_containing(insertion_point..range_to_move.end)
8227 .is_some()
8228 {
8229 let text = buffer
8230 .text_for_range(range_to_move.clone())
8231 .flat_map(|s| s.chars())
8232 .skip(1)
8233 .chain(['\n'])
8234 .collect::<String>();
8235
8236 edits.push((
8237 buffer.anchor_after(range_to_move.start)
8238 ..buffer.anchor_before(range_to_move.end),
8239 String::new(),
8240 ));
8241 let insertion_anchor = buffer.anchor_after(insertion_point);
8242 edits.push((insertion_anchor..insertion_anchor, text));
8243
8244 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8245
8246 // Move selections up
8247 new_selections.extend(contiguous_row_selections.drain(..).map(
8248 |mut selection| {
8249 selection.start.row -= row_delta;
8250 selection.end.row -= row_delta;
8251 selection
8252 },
8253 ));
8254
8255 // Move folds up
8256 unfold_ranges.push(range_to_move.clone());
8257 for fold in display_map.folds_in_range(
8258 buffer.anchor_before(range_to_move.start)
8259 ..buffer.anchor_after(range_to_move.end),
8260 ) {
8261 let mut start = fold.range.start.to_point(&buffer);
8262 let mut end = fold.range.end.to_point(&buffer);
8263 start.row -= row_delta;
8264 end.row -= row_delta;
8265 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8266 }
8267 }
8268 }
8269
8270 // If we didn't move line(s), preserve the existing selections
8271 new_selections.append(&mut contiguous_row_selections);
8272 }
8273
8274 self.transact(window, cx, |this, window, cx| {
8275 this.unfold_ranges(&unfold_ranges, true, true, cx);
8276 this.buffer.update(cx, |buffer, cx| {
8277 for (range, text) in edits {
8278 buffer.edit([(range, text)], None, cx);
8279 }
8280 });
8281 this.fold_creases(refold_creases, true, window, cx);
8282 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8283 s.select(new_selections);
8284 })
8285 });
8286 }
8287
8288 pub fn move_line_down(
8289 &mut self,
8290 _: &MoveLineDown,
8291 window: &mut Window,
8292 cx: &mut Context<Self>,
8293 ) {
8294 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8295 let buffer = self.buffer.read(cx).snapshot(cx);
8296
8297 let mut edits = Vec::new();
8298 let mut unfold_ranges = Vec::new();
8299 let mut refold_creases = Vec::new();
8300
8301 let selections = self.selections.all::<Point>(cx);
8302 let mut selections = selections.iter().peekable();
8303 let mut contiguous_row_selections = Vec::new();
8304 let mut new_selections = Vec::new();
8305
8306 while let Some(selection) = selections.next() {
8307 // Find all the selections that span a contiguous row range
8308 let (start_row, end_row) = consume_contiguous_rows(
8309 &mut contiguous_row_selections,
8310 selection,
8311 &display_map,
8312 &mut selections,
8313 );
8314
8315 // Move the text spanned by the row range to be after the last line of the row range
8316 if end_row.0 <= buffer.max_point().row {
8317 let range_to_move =
8318 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8319 let insertion_point = display_map
8320 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8321 .0;
8322
8323 // Don't move lines across excerpt boundaries
8324 if buffer
8325 .excerpt_containing(range_to_move.start..insertion_point)
8326 .is_some()
8327 {
8328 let mut text = String::from("\n");
8329 text.extend(buffer.text_for_range(range_to_move.clone()));
8330 text.pop(); // Drop trailing newline
8331 edits.push((
8332 buffer.anchor_after(range_to_move.start)
8333 ..buffer.anchor_before(range_to_move.end),
8334 String::new(),
8335 ));
8336 let insertion_anchor = buffer.anchor_after(insertion_point);
8337 edits.push((insertion_anchor..insertion_anchor, text));
8338
8339 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8340
8341 // Move selections down
8342 new_selections.extend(contiguous_row_selections.drain(..).map(
8343 |mut selection| {
8344 selection.start.row += row_delta;
8345 selection.end.row += row_delta;
8346 selection
8347 },
8348 ));
8349
8350 // Move folds down
8351 unfold_ranges.push(range_to_move.clone());
8352 for fold in display_map.folds_in_range(
8353 buffer.anchor_before(range_to_move.start)
8354 ..buffer.anchor_after(range_to_move.end),
8355 ) {
8356 let mut start = fold.range.start.to_point(&buffer);
8357 let mut end = fold.range.end.to_point(&buffer);
8358 start.row += row_delta;
8359 end.row += row_delta;
8360 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8361 }
8362 }
8363 }
8364
8365 // If we didn't move line(s), preserve the existing selections
8366 new_selections.append(&mut contiguous_row_selections);
8367 }
8368
8369 self.transact(window, cx, |this, window, cx| {
8370 this.unfold_ranges(&unfold_ranges, true, true, cx);
8371 this.buffer.update(cx, |buffer, cx| {
8372 for (range, text) in edits {
8373 buffer.edit([(range, text)], None, cx);
8374 }
8375 });
8376 this.fold_creases(refold_creases, true, window, cx);
8377 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8378 s.select(new_selections)
8379 });
8380 });
8381 }
8382
8383 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8384 let text_layout_details = &self.text_layout_details(window);
8385 self.transact(window, cx, |this, window, cx| {
8386 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8387 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8388 let line_mode = s.line_mode;
8389 s.move_with(|display_map, selection| {
8390 if !selection.is_empty() || line_mode {
8391 return;
8392 }
8393
8394 let mut head = selection.head();
8395 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8396 if head.column() == display_map.line_len(head.row()) {
8397 transpose_offset = display_map
8398 .buffer_snapshot
8399 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8400 }
8401
8402 if transpose_offset == 0 {
8403 return;
8404 }
8405
8406 *head.column_mut() += 1;
8407 head = display_map.clip_point(head, Bias::Right);
8408 let goal = SelectionGoal::HorizontalPosition(
8409 display_map
8410 .x_for_display_point(head, text_layout_details)
8411 .into(),
8412 );
8413 selection.collapse_to(head, goal);
8414
8415 let transpose_start = display_map
8416 .buffer_snapshot
8417 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8418 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8419 let transpose_end = display_map
8420 .buffer_snapshot
8421 .clip_offset(transpose_offset + 1, Bias::Right);
8422 if let Some(ch) =
8423 display_map.buffer_snapshot.chars_at(transpose_start).next()
8424 {
8425 edits.push((transpose_start..transpose_offset, String::new()));
8426 edits.push((transpose_end..transpose_end, ch.to_string()));
8427 }
8428 }
8429 });
8430 edits
8431 });
8432 this.buffer
8433 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8434 let selections = this.selections.all::<usize>(cx);
8435 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8436 s.select(selections);
8437 });
8438 });
8439 }
8440
8441 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8442 self.rewrap_impl(false, cx)
8443 }
8444
8445 pub fn rewrap_impl(&mut self, override_language_settings: bool, cx: &mut Context<Self>) {
8446 let buffer = self.buffer.read(cx).snapshot(cx);
8447 let selections = self.selections.all::<Point>(cx);
8448 let mut selections = selections.iter().peekable();
8449
8450 let mut edits = Vec::new();
8451 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8452
8453 while let Some(selection) = selections.next() {
8454 let mut start_row = selection.start.row;
8455 let mut end_row = selection.end.row;
8456
8457 // Skip selections that overlap with a range that has already been rewrapped.
8458 let selection_range = start_row..end_row;
8459 if rewrapped_row_ranges
8460 .iter()
8461 .any(|range| range.overlaps(&selection_range))
8462 {
8463 continue;
8464 }
8465
8466 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
8467
8468 // Since not all lines in the selection may be at the same indent
8469 // level, choose the indent size that is the most common between all
8470 // of the lines.
8471 //
8472 // If there is a tie, we use the deepest indent.
8473 let (indent_size, indent_end) = {
8474 let mut indent_size_occurrences = HashMap::default();
8475 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8476
8477 for row in start_row..=end_row {
8478 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8479 rows_by_indent_size.entry(indent).or_default().push(row);
8480 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8481 }
8482
8483 let indent_size = indent_size_occurrences
8484 .into_iter()
8485 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8486 .map(|(indent, _)| indent)
8487 .unwrap_or_default();
8488 let row = rows_by_indent_size[&indent_size][0];
8489 let indent_end = Point::new(row, indent_size.len);
8490
8491 (indent_size, indent_end)
8492 };
8493
8494 let mut line_prefix = indent_size.chars().collect::<String>();
8495
8496 let mut inside_comment = false;
8497 if let Some(comment_prefix) =
8498 buffer
8499 .language_scope_at(selection.head())
8500 .and_then(|language| {
8501 language
8502 .line_comment_prefixes()
8503 .iter()
8504 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8505 .cloned()
8506 })
8507 {
8508 line_prefix.push_str(&comment_prefix);
8509 inside_comment = true;
8510 }
8511
8512 let language_settings = buffer.language_settings_at(selection.head(), cx);
8513 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8514 RewrapBehavior::InComments => inside_comment,
8515 RewrapBehavior::InSelections => !selection.is_empty(),
8516 RewrapBehavior::Anywhere => true,
8517 };
8518
8519 let should_rewrap = override_language_settings
8520 || allow_rewrap_based_on_language
8521 || self.hard_wrap.is_some();
8522 if !should_rewrap {
8523 continue;
8524 }
8525
8526 if selection.is_empty() {
8527 'expand_upwards: while start_row > 0 {
8528 let prev_row = start_row - 1;
8529 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8530 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8531 {
8532 start_row = prev_row;
8533 } else {
8534 break 'expand_upwards;
8535 }
8536 }
8537
8538 'expand_downwards: while end_row < buffer.max_point().row {
8539 let next_row = end_row + 1;
8540 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8541 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8542 {
8543 end_row = next_row;
8544 } else {
8545 break 'expand_downwards;
8546 }
8547 }
8548 }
8549
8550 let start = Point::new(start_row, 0);
8551 let start_offset = start.to_offset(&buffer);
8552 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8553 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8554 let Some(lines_without_prefixes) = selection_text
8555 .lines()
8556 .map(|line| {
8557 line.strip_prefix(&line_prefix)
8558 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8559 .ok_or_else(|| {
8560 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8561 })
8562 })
8563 .collect::<Result<Vec<_>, _>>()
8564 .log_err()
8565 else {
8566 continue;
8567 };
8568
8569 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
8570 buffer
8571 .language_settings_at(Point::new(start_row, 0), cx)
8572 .preferred_line_length as usize
8573 });
8574 let wrapped_text = wrap_with_prefix(
8575 line_prefix,
8576 lines_without_prefixes.join(" "),
8577 wrap_column,
8578 tab_size,
8579 );
8580
8581 // TODO: should always use char-based diff while still supporting cursor behavior that
8582 // matches vim.
8583 let mut diff_options = DiffOptions::default();
8584 if override_language_settings {
8585 diff_options.max_word_diff_len = 0;
8586 diff_options.max_word_diff_line_count = 0;
8587 } else {
8588 diff_options.max_word_diff_len = usize::MAX;
8589 diff_options.max_word_diff_line_count = usize::MAX;
8590 }
8591
8592 for (old_range, new_text) in
8593 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8594 {
8595 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8596 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8597 edits.push((edit_start..edit_end, new_text));
8598 }
8599
8600 rewrapped_row_ranges.push(start_row..=end_row);
8601 }
8602
8603 self.buffer
8604 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8605 }
8606
8607 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8608 let mut text = String::new();
8609 let buffer = self.buffer.read(cx).snapshot(cx);
8610 let mut selections = self.selections.all::<Point>(cx);
8611 let mut clipboard_selections = Vec::with_capacity(selections.len());
8612 {
8613 let max_point = buffer.max_point();
8614 let mut is_first = true;
8615 for selection in &mut selections {
8616 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8617 if is_entire_line {
8618 selection.start = Point::new(selection.start.row, 0);
8619 if !selection.is_empty() && selection.end.column == 0 {
8620 selection.end = cmp::min(max_point, selection.end);
8621 } else {
8622 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8623 }
8624 selection.goal = SelectionGoal::None;
8625 }
8626 if is_first {
8627 is_first = false;
8628 } else {
8629 text += "\n";
8630 }
8631 let mut len = 0;
8632 for chunk in buffer.text_for_range(selection.start..selection.end) {
8633 text.push_str(chunk);
8634 len += chunk.len();
8635 }
8636 clipboard_selections.push(ClipboardSelection {
8637 len,
8638 is_entire_line,
8639 first_line_indent: buffer
8640 .indent_size_for_line(MultiBufferRow(selection.start.row))
8641 .len,
8642 });
8643 }
8644 }
8645
8646 self.transact(window, cx, |this, window, cx| {
8647 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8648 s.select(selections);
8649 });
8650 this.insert("", window, cx);
8651 });
8652 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8653 }
8654
8655 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8656 let item = self.cut_common(window, cx);
8657 cx.write_to_clipboard(item);
8658 }
8659
8660 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8661 self.change_selections(None, window, cx, |s| {
8662 s.move_with(|snapshot, sel| {
8663 if sel.is_empty() {
8664 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8665 }
8666 });
8667 });
8668 let item = self.cut_common(window, cx);
8669 cx.set_global(KillRing(item))
8670 }
8671
8672 pub fn kill_ring_yank(
8673 &mut self,
8674 _: &KillRingYank,
8675 window: &mut Window,
8676 cx: &mut Context<Self>,
8677 ) {
8678 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8679 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8680 (kill_ring.text().to_string(), kill_ring.metadata_json())
8681 } else {
8682 return;
8683 }
8684 } else {
8685 return;
8686 };
8687 self.do_paste(&text, metadata, false, window, cx);
8688 }
8689
8690 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8691 let selections = self.selections.all::<Point>(cx);
8692 let buffer = self.buffer.read(cx).read(cx);
8693 let mut text = String::new();
8694
8695 let mut clipboard_selections = Vec::with_capacity(selections.len());
8696 {
8697 let max_point = buffer.max_point();
8698 let mut is_first = true;
8699 for selection in selections.iter() {
8700 let mut start = selection.start;
8701 let mut end = selection.end;
8702 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8703 if is_entire_line {
8704 start = Point::new(start.row, 0);
8705 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8706 }
8707 if is_first {
8708 is_first = false;
8709 } else {
8710 text += "\n";
8711 }
8712 let mut len = 0;
8713 for chunk in buffer.text_for_range(start..end) {
8714 text.push_str(chunk);
8715 len += chunk.len();
8716 }
8717 clipboard_selections.push(ClipboardSelection {
8718 len,
8719 is_entire_line,
8720 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8721 });
8722 }
8723 }
8724
8725 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8726 text,
8727 clipboard_selections,
8728 ));
8729 }
8730
8731 pub fn do_paste(
8732 &mut self,
8733 text: &String,
8734 clipboard_selections: Option<Vec<ClipboardSelection>>,
8735 handle_entire_lines: bool,
8736 window: &mut Window,
8737 cx: &mut Context<Self>,
8738 ) {
8739 if self.read_only(cx) {
8740 return;
8741 }
8742
8743 let clipboard_text = Cow::Borrowed(text);
8744
8745 self.transact(window, cx, |this, window, cx| {
8746 if let Some(mut clipboard_selections) = clipboard_selections {
8747 let old_selections = this.selections.all::<usize>(cx);
8748 let all_selections_were_entire_line =
8749 clipboard_selections.iter().all(|s| s.is_entire_line);
8750 let first_selection_indent_column =
8751 clipboard_selections.first().map(|s| s.first_line_indent);
8752 if clipboard_selections.len() != old_selections.len() {
8753 clipboard_selections.drain(..);
8754 }
8755 let cursor_offset = this.selections.last::<usize>(cx).head();
8756 let mut auto_indent_on_paste = true;
8757
8758 this.buffer.update(cx, |buffer, cx| {
8759 let snapshot = buffer.read(cx);
8760 auto_indent_on_paste = snapshot
8761 .language_settings_at(cursor_offset, cx)
8762 .auto_indent_on_paste;
8763
8764 let mut start_offset = 0;
8765 let mut edits = Vec::new();
8766 let mut original_indent_columns = Vec::new();
8767 for (ix, selection) in old_selections.iter().enumerate() {
8768 let to_insert;
8769 let entire_line;
8770 let original_indent_column;
8771 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8772 let end_offset = start_offset + clipboard_selection.len;
8773 to_insert = &clipboard_text[start_offset..end_offset];
8774 entire_line = clipboard_selection.is_entire_line;
8775 start_offset = end_offset + 1;
8776 original_indent_column = Some(clipboard_selection.first_line_indent);
8777 } else {
8778 to_insert = clipboard_text.as_str();
8779 entire_line = all_selections_were_entire_line;
8780 original_indent_column = first_selection_indent_column
8781 }
8782
8783 // If the corresponding selection was empty when this slice of the
8784 // clipboard text was written, then the entire line containing the
8785 // selection was copied. If this selection is also currently empty,
8786 // then paste the line before the current line of the buffer.
8787 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8788 let column = selection.start.to_point(&snapshot).column as usize;
8789 let line_start = selection.start - column;
8790 line_start..line_start
8791 } else {
8792 selection.range()
8793 };
8794
8795 edits.push((range, to_insert));
8796 original_indent_columns.push(original_indent_column);
8797 }
8798 drop(snapshot);
8799
8800 buffer.edit(
8801 edits,
8802 if auto_indent_on_paste {
8803 Some(AutoindentMode::Block {
8804 original_indent_columns,
8805 })
8806 } else {
8807 None
8808 },
8809 cx,
8810 );
8811 });
8812
8813 let selections = this.selections.all::<usize>(cx);
8814 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8815 s.select(selections)
8816 });
8817 } else {
8818 this.insert(&clipboard_text, window, cx);
8819 }
8820 });
8821 }
8822
8823 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8824 if let Some(item) = cx.read_from_clipboard() {
8825 let entries = item.entries();
8826
8827 match entries.first() {
8828 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8829 // of all the pasted entries.
8830 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8831 .do_paste(
8832 clipboard_string.text(),
8833 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8834 true,
8835 window,
8836 cx,
8837 ),
8838 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8839 }
8840 }
8841 }
8842
8843 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8844 if self.read_only(cx) {
8845 return;
8846 }
8847
8848 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8849 if let Some((selections, _)) =
8850 self.selection_history.transaction(transaction_id).cloned()
8851 {
8852 self.change_selections(None, window, cx, |s| {
8853 s.select_anchors(selections.to_vec());
8854 });
8855 } else {
8856 log::error!(
8857 "No entry in selection_history found for undo. \
8858 This may correspond to a bug where undo does not update the selection. \
8859 If this is occurring, please add details to \
8860 https://github.com/zed-industries/zed/issues/22692"
8861 );
8862 }
8863 self.request_autoscroll(Autoscroll::fit(), cx);
8864 self.unmark_text(window, cx);
8865 self.refresh_inline_completion(true, false, window, cx);
8866 cx.emit(EditorEvent::Edited { transaction_id });
8867 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8868 }
8869 }
8870
8871 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8872 if self.read_only(cx) {
8873 return;
8874 }
8875
8876 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8877 if let Some((_, Some(selections))) =
8878 self.selection_history.transaction(transaction_id).cloned()
8879 {
8880 self.change_selections(None, window, cx, |s| {
8881 s.select_anchors(selections.to_vec());
8882 });
8883 } else {
8884 log::error!(
8885 "No entry in selection_history found for redo. \
8886 This may correspond to a bug where undo does not update the selection. \
8887 If this is occurring, please add details to \
8888 https://github.com/zed-industries/zed/issues/22692"
8889 );
8890 }
8891 self.request_autoscroll(Autoscroll::fit(), cx);
8892 self.unmark_text(window, cx);
8893 self.refresh_inline_completion(true, false, window, cx);
8894 cx.emit(EditorEvent::Edited { transaction_id });
8895 }
8896 }
8897
8898 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8899 self.buffer
8900 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8901 }
8902
8903 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8904 self.buffer
8905 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8906 }
8907
8908 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8909 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8910 let line_mode = s.line_mode;
8911 s.move_with(|map, selection| {
8912 let cursor = if selection.is_empty() && !line_mode {
8913 movement::left(map, selection.start)
8914 } else {
8915 selection.start
8916 };
8917 selection.collapse_to(cursor, SelectionGoal::None);
8918 });
8919 })
8920 }
8921
8922 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8923 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8924 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8925 })
8926 }
8927
8928 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8929 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8930 let line_mode = s.line_mode;
8931 s.move_with(|map, selection| {
8932 let cursor = if selection.is_empty() && !line_mode {
8933 movement::right(map, selection.end)
8934 } else {
8935 selection.end
8936 };
8937 selection.collapse_to(cursor, SelectionGoal::None)
8938 });
8939 })
8940 }
8941
8942 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8943 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8944 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8945 })
8946 }
8947
8948 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8949 if self.take_rename(true, window, cx).is_some() {
8950 return;
8951 }
8952
8953 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8954 cx.propagate();
8955 return;
8956 }
8957
8958 let text_layout_details = &self.text_layout_details(window);
8959 let selection_count = self.selections.count();
8960 let first_selection = self.selections.first_anchor();
8961
8962 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8963 let line_mode = s.line_mode;
8964 s.move_with(|map, selection| {
8965 if !selection.is_empty() && !line_mode {
8966 selection.goal = SelectionGoal::None;
8967 }
8968 let (cursor, goal) = movement::up(
8969 map,
8970 selection.start,
8971 selection.goal,
8972 false,
8973 text_layout_details,
8974 );
8975 selection.collapse_to(cursor, goal);
8976 });
8977 });
8978
8979 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8980 {
8981 cx.propagate();
8982 }
8983 }
8984
8985 pub fn move_up_by_lines(
8986 &mut self,
8987 action: &MoveUpByLines,
8988 window: &mut Window,
8989 cx: &mut Context<Self>,
8990 ) {
8991 if self.take_rename(true, window, cx).is_some() {
8992 return;
8993 }
8994
8995 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8996 cx.propagate();
8997 return;
8998 }
8999
9000 let text_layout_details = &self.text_layout_details(window);
9001
9002 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9003 let line_mode = s.line_mode;
9004 s.move_with(|map, selection| {
9005 if !selection.is_empty() && !line_mode {
9006 selection.goal = SelectionGoal::None;
9007 }
9008 let (cursor, goal) = movement::up_by_rows(
9009 map,
9010 selection.start,
9011 action.lines,
9012 selection.goal,
9013 false,
9014 text_layout_details,
9015 );
9016 selection.collapse_to(cursor, goal);
9017 });
9018 })
9019 }
9020
9021 pub fn move_down_by_lines(
9022 &mut self,
9023 action: &MoveDownByLines,
9024 window: &mut Window,
9025 cx: &mut Context<Self>,
9026 ) {
9027 if self.take_rename(true, window, cx).is_some() {
9028 return;
9029 }
9030
9031 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9032 cx.propagate();
9033 return;
9034 }
9035
9036 let text_layout_details = &self.text_layout_details(window);
9037
9038 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9039 let line_mode = s.line_mode;
9040 s.move_with(|map, selection| {
9041 if !selection.is_empty() && !line_mode {
9042 selection.goal = SelectionGoal::None;
9043 }
9044 let (cursor, goal) = movement::down_by_rows(
9045 map,
9046 selection.start,
9047 action.lines,
9048 selection.goal,
9049 false,
9050 text_layout_details,
9051 );
9052 selection.collapse_to(cursor, goal);
9053 });
9054 })
9055 }
9056
9057 pub fn select_down_by_lines(
9058 &mut self,
9059 action: &SelectDownByLines,
9060 window: &mut Window,
9061 cx: &mut Context<Self>,
9062 ) {
9063 let text_layout_details = &self.text_layout_details(window);
9064 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9065 s.move_heads_with(|map, head, goal| {
9066 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9067 })
9068 })
9069 }
9070
9071 pub fn select_up_by_lines(
9072 &mut self,
9073 action: &SelectUpByLines,
9074 window: &mut Window,
9075 cx: &mut Context<Self>,
9076 ) {
9077 let text_layout_details = &self.text_layout_details(window);
9078 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9079 s.move_heads_with(|map, head, goal| {
9080 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9081 })
9082 })
9083 }
9084
9085 pub fn select_page_up(
9086 &mut self,
9087 _: &SelectPageUp,
9088 window: &mut Window,
9089 cx: &mut Context<Self>,
9090 ) {
9091 let Some(row_count) = self.visible_row_count() else {
9092 return;
9093 };
9094
9095 let text_layout_details = &self.text_layout_details(window);
9096
9097 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9098 s.move_heads_with(|map, head, goal| {
9099 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9100 })
9101 })
9102 }
9103
9104 pub fn move_page_up(
9105 &mut self,
9106 action: &MovePageUp,
9107 window: &mut Window,
9108 cx: &mut Context<Self>,
9109 ) {
9110 if self.take_rename(true, window, cx).is_some() {
9111 return;
9112 }
9113
9114 if self
9115 .context_menu
9116 .borrow_mut()
9117 .as_mut()
9118 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9119 .unwrap_or(false)
9120 {
9121 return;
9122 }
9123
9124 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9125 cx.propagate();
9126 return;
9127 }
9128
9129 let Some(row_count) = self.visible_row_count() else {
9130 return;
9131 };
9132
9133 let autoscroll = if action.center_cursor {
9134 Autoscroll::center()
9135 } else {
9136 Autoscroll::fit()
9137 };
9138
9139 let text_layout_details = &self.text_layout_details(window);
9140
9141 self.change_selections(Some(autoscroll), window, cx, |s| {
9142 let line_mode = s.line_mode;
9143 s.move_with(|map, selection| {
9144 if !selection.is_empty() && !line_mode {
9145 selection.goal = SelectionGoal::None;
9146 }
9147 let (cursor, goal) = movement::up_by_rows(
9148 map,
9149 selection.end,
9150 row_count,
9151 selection.goal,
9152 false,
9153 text_layout_details,
9154 );
9155 selection.collapse_to(cursor, goal);
9156 });
9157 });
9158 }
9159
9160 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9161 let text_layout_details = &self.text_layout_details(window);
9162 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9163 s.move_heads_with(|map, head, goal| {
9164 movement::up(map, head, goal, false, text_layout_details)
9165 })
9166 })
9167 }
9168
9169 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9170 self.take_rename(true, window, cx);
9171
9172 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9173 cx.propagate();
9174 return;
9175 }
9176
9177 let text_layout_details = &self.text_layout_details(window);
9178 let selection_count = self.selections.count();
9179 let first_selection = self.selections.first_anchor();
9180
9181 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9182 let line_mode = s.line_mode;
9183 s.move_with(|map, selection| {
9184 if !selection.is_empty() && !line_mode {
9185 selection.goal = SelectionGoal::None;
9186 }
9187 let (cursor, goal) = movement::down(
9188 map,
9189 selection.end,
9190 selection.goal,
9191 false,
9192 text_layout_details,
9193 );
9194 selection.collapse_to(cursor, goal);
9195 });
9196 });
9197
9198 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9199 {
9200 cx.propagate();
9201 }
9202 }
9203
9204 pub fn select_page_down(
9205 &mut self,
9206 _: &SelectPageDown,
9207 window: &mut Window,
9208 cx: &mut Context<Self>,
9209 ) {
9210 let Some(row_count) = self.visible_row_count() else {
9211 return;
9212 };
9213
9214 let text_layout_details = &self.text_layout_details(window);
9215
9216 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9217 s.move_heads_with(|map, head, goal| {
9218 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9219 })
9220 })
9221 }
9222
9223 pub fn move_page_down(
9224 &mut self,
9225 action: &MovePageDown,
9226 window: &mut Window,
9227 cx: &mut Context<Self>,
9228 ) {
9229 if self.take_rename(true, window, cx).is_some() {
9230 return;
9231 }
9232
9233 if self
9234 .context_menu
9235 .borrow_mut()
9236 .as_mut()
9237 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9238 .unwrap_or(false)
9239 {
9240 return;
9241 }
9242
9243 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9244 cx.propagate();
9245 return;
9246 }
9247
9248 let Some(row_count) = self.visible_row_count() else {
9249 return;
9250 };
9251
9252 let autoscroll = if action.center_cursor {
9253 Autoscroll::center()
9254 } else {
9255 Autoscroll::fit()
9256 };
9257
9258 let text_layout_details = &self.text_layout_details(window);
9259 self.change_selections(Some(autoscroll), window, cx, |s| {
9260 let line_mode = s.line_mode;
9261 s.move_with(|map, selection| {
9262 if !selection.is_empty() && !line_mode {
9263 selection.goal = SelectionGoal::None;
9264 }
9265 let (cursor, goal) = movement::down_by_rows(
9266 map,
9267 selection.end,
9268 row_count,
9269 selection.goal,
9270 false,
9271 text_layout_details,
9272 );
9273 selection.collapse_to(cursor, goal);
9274 });
9275 });
9276 }
9277
9278 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9279 let text_layout_details = &self.text_layout_details(window);
9280 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9281 s.move_heads_with(|map, head, goal| {
9282 movement::down(map, head, goal, false, text_layout_details)
9283 })
9284 });
9285 }
9286
9287 pub fn context_menu_first(
9288 &mut self,
9289 _: &ContextMenuFirst,
9290 _window: &mut Window,
9291 cx: &mut Context<Self>,
9292 ) {
9293 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9294 context_menu.select_first(self.completion_provider.as_deref(), cx);
9295 }
9296 }
9297
9298 pub fn context_menu_prev(
9299 &mut self,
9300 _: &ContextMenuPrevious,
9301 _window: &mut Window,
9302 cx: &mut Context<Self>,
9303 ) {
9304 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9305 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9306 }
9307 }
9308
9309 pub fn context_menu_next(
9310 &mut self,
9311 _: &ContextMenuNext,
9312 _window: &mut Window,
9313 cx: &mut Context<Self>,
9314 ) {
9315 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9316 context_menu.select_next(self.completion_provider.as_deref(), cx);
9317 }
9318 }
9319
9320 pub fn context_menu_last(
9321 &mut self,
9322 _: &ContextMenuLast,
9323 _window: &mut Window,
9324 cx: &mut Context<Self>,
9325 ) {
9326 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9327 context_menu.select_last(self.completion_provider.as_deref(), cx);
9328 }
9329 }
9330
9331 pub fn move_to_previous_word_start(
9332 &mut self,
9333 _: &MoveToPreviousWordStart,
9334 window: &mut Window,
9335 cx: &mut Context<Self>,
9336 ) {
9337 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9338 s.move_cursors_with(|map, head, _| {
9339 (
9340 movement::previous_word_start(map, head),
9341 SelectionGoal::None,
9342 )
9343 });
9344 })
9345 }
9346
9347 pub fn move_to_previous_subword_start(
9348 &mut self,
9349 _: &MoveToPreviousSubwordStart,
9350 window: &mut Window,
9351 cx: &mut Context<Self>,
9352 ) {
9353 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9354 s.move_cursors_with(|map, head, _| {
9355 (
9356 movement::previous_subword_start(map, head),
9357 SelectionGoal::None,
9358 )
9359 });
9360 })
9361 }
9362
9363 pub fn select_to_previous_word_start(
9364 &mut self,
9365 _: &SelectToPreviousWordStart,
9366 window: &mut Window,
9367 cx: &mut Context<Self>,
9368 ) {
9369 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9370 s.move_heads_with(|map, head, _| {
9371 (
9372 movement::previous_word_start(map, head),
9373 SelectionGoal::None,
9374 )
9375 });
9376 })
9377 }
9378
9379 pub fn select_to_previous_subword_start(
9380 &mut self,
9381 _: &SelectToPreviousSubwordStart,
9382 window: &mut Window,
9383 cx: &mut Context<Self>,
9384 ) {
9385 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9386 s.move_heads_with(|map, head, _| {
9387 (
9388 movement::previous_subword_start(map, head),
9389 SelectionGoal::None,
9390 )
9391 });
9392 })
9393 }
9394
9395 pub fn delete_to_previous_word_start(
9396 &mut self,
9397 action: &DeleteToPreviousWordStart,
9398 window: &mut Window,
9399 cx: &mut Context<Self>,
9400 ) {
9401 self.transact(window, cx, |this, window, cx| {
9402 this.select_autoclose_pair(window, cx);
9403 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9404 let line_mode = s.line_mode;
9405 s.move_with(|map, selection| {
9406 if selection.is_empty() && !line_mode {
9407 let cursor = if action.ignore_newlines {
9408 movement::previous_word_start(map, selection.head())
9409 } else {
9410 movement::previous_word_start_or_newline(map, selection.head())
9411 };
9412 selection.set_head(cursor, SelectionGoal::None);
9413 }
9414 });
9415 });
9416 this.insert("", window, cx);
9417 });
9418 }
9419
9420 pub fn delete_to_previous_subword_start(
9421 &mut self,
9422 _: &DeleteToPreviousSubwordStart,
9423 window: &mut Window,
9424 cx: &mut Context<Self>,
9425 ) {
9426 self.transact(window, cx, |this, window, cx| {
9427 this.select_autoclose_pair(window, cx);
9428 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9429 let line_mode = s.line_mode;
9430 s.move_with(|map, selection| {
9431 if selection.is_empty() && !line_mode {
9432 let cursor = movement::previous_subword_start(map, selection.head());
9433 selection.set_head(cursor, SelectionGoal::None);
9434 }
9435 });
9436 });
9437 this.insert("", window, cx);
9438 });
9439 }
9440
9441 pub fn move_to_next_word_end(
9442 &mut self,
9443 _: &MoveToNextWordEnd,
9444 window: &mut Window,
9445 cx: &mut Context<Self>,
9446 ) {
9447 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9448 s.move_cursors_with(|map, head, _| {
9449 (movement::next_word_end(map, head), SelectionGoal::None)
9450 });
9451 })
9452 }
9453
9454 pub fn move_to_next_subword_end(
9455 &mut self,
9456 _: &MoveToNextSubwordEnd,
9457 window: &mut Window,
9458 cx: &mut Context<Self>,
9459 ) {
9460 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9461 s.move_cursors_with(|map, head, _| {
9462 (movement::next_subword_end(map, head), SelectionGoal::None)
9463 });
9464 })
9465 }
9466
9467 pub fn select_to_next_word_end(
9468 &mut self,
9469 _: &SelectToNextWordEnd,
9470 window: &mut Window,
9471 cx: &mut Context<Self>,
9472 ) {
9473 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9474 s.move_heads_with(|map, head, _| {
9475 (movement::next_word_end(map, head), SelectionGoal::None)
9476 });
9477 })
9478 }
9479
9480 pub fn select_to_next_subword_end(
9481 &mut self,
9482 _: &SelectToNextSubwordEnd,
9483 window: &mut Window,
9484 cx: &mut Context<Self>,
9485 ) {
9486 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9487 s.move_heads_with(|map, head, _| {
9488 (movement::next_subword_end(map, head), SelectionGoal::None)
9489 });
9490 })
9491 }
9492
9493 pub fn delete_to_next_word_end(
9494 &mut self,
9495 action: &DeleteToNextWordEnd,
9496 window: &mut Window,
9497 cx: &mut Context<Self>,
9498 ) {
9499 self.transact(window, cx, |this, 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 = if action.ignore_newlines {
9505 movement::next_word_end(map, selection.head())
9506 } else {
9507 movement::next_word_end_or_newline(map, selection.head())
9508 };
9509 selection.set_head(cursor, SelectionGoal::None);
9510 }
9511 });
9512 });
9513 this.insert("", window, cx);
9514 });
9515 }
9516
9517 pub fn delete_to_next_subword_end(
9518 &mut self,
9519 _: &DeleteToNextSubwordEnd,
9520 window: &mut Window,
9521 cx: &mut Context<Self>,
9522 ) {
9523 self.transact(window, cx, |this, window, cx| {
9524 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9525 s.move_with(|map, selection| {
9526 if selection.is_empty() {
9527 let cursor = movement::next_subword_end(map, selection.head());
9528 selection.set_head(cursor, SelectionGoal::None);
9529 }
9530 });
9531 });
9532 this.insert("", window, cx);
9533 });
9534 }
9535
9536 pub fn move_to_beginning_of_line(
9537 &mut self,
9538 action: &MoveToBeginningOfLine,
9539 window: &mut Window,
9540 cx: &mut Context<Self>,
9541 ) {
9542 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9543 s.move_cursors_with(|map, head, _| {
9544 (
9545 movement::indented_line_beginning(
9546 map,
9547 head,
9548 action.stop_at_soft_wraps,
9549 action.stop_at_indent,
9550 ),
9551 SelectionGoal::None,
9552 )
9553 });
9554 })
9555 }
9556
9557 pub fn select_to_beginning_of_line(
9558 &mut self,
9559 action: &SelectToBeginningOfLine,
9560 window: &mut Window,
9561 cx: &mut Context<Self>,
9562 ) {
9563 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9564 s.move_heads_with(|map, head, _| {
9565 (
9566 movement::indented_line_beginning(
9567 map,
9568 head,
9569 action.stop_at_soft_wraps,
9570 action.stop_at_indent,
9571 ),
9572 SelectionGoal::None,
9573 )
9574 });
9575 });
9576 }
9577
9578 pub fn delete_to_beginning_of_line(
9579 &mut self,
9580 action: &DeleteToBeginningOfLine,
9581 window: &mut Window,
9582 cx: &mut Context<Self>,
9583 ) {
9584 self.transact(window, cx, |this, window, cx| {
9585 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9586 s.move_with(|_, selection| {
9587 selection.reversed = true;
9588 });
9589 });
9590
9591 this.select_to_beginning_of_line(
9592 &SelectToBeginningOfLine {
9593 stop_at_soft_wraps: false,
9594 stop_at_indent: action.stop_at_indent,
9595 },
9596 window,
9597 cx,
9598 );
9599 this.backspace(&Backspace, window, cx);
9600 });
9601 }
9602
9603 pub fn move_to_end_of_line(
9604 &mut self,
9605 action: &MoveToEndOfLine,
9606 window: &mut Window,
9607 cx: &mut Context<Self>,
9608 ) {
9609 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9610 s.move_cursors_with(|map, head, _| {
9611 (
9612 movement::line_end(map, head, action.stop_at_soft_wraps),
9613 SelectionGoal::None,
9614 )
9615 });
9616 })
9617 }
9618
9619 pub fn select_to_end_of_line(
9620 &mut self,
9621 action: &SelectToEndOfLine,
9622 window: &mut Window,
9623 cx: &mut Context<Self>,
9624 ) {
9625 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9626 s.move_heads_with(|map, head, _| {
9627 (
9628 movement::line_end(map, head, action.stop_at_soft_wraps),
9629 SelectionGoal::None,
9630 )
9631 });
9632 })
9633 }
9634
9635 pub fn delete_to_end_of_line(
9636 &mut self,
9637 _: &DeleteToEndOfLine,
9638 window: &mut Window,
9639 cx: &mut Context<Self>,
9640 ) {
9641 self.transact(window, cx, |this, window, cx| {
9642 this.select_to_end_of_line(
9643 &SelectToEndOfLine {
9644 stop_at_soft_wraps: false,
9645 },
9646 window,
9647 cx,
9648 );
9649 this.delete(&Delete, window, cx);
9650 });
9651 }
9652
9653 pub fn cut_to_end_of_line(
9654 &mut self,
9655 _: &CutToEndOfLine,
9656 window: &mut Window,
9657 cx: &mut Context<Self>,
9658 ) {
9659 self.transact(window, cx, |this, window, cx| {
9660 this.select_to_end_of_line(
9661 &SelectToEndOfLine {
9662 stop_at_soft_wraps: false,
9663 },
9664 window,
9665 cx,
9666 );
9667 this.cut(&Cut, window, cx);
9668 });
9669 }
9670
9671 pub fn move_to_start_of_paragraph(
9672 &mut self,
9673 _: &MoveToStartOfParagraph,
9674 window: &mut Window,
9675 cx: &mut Context<Self>,
9676 ) {
9677 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9678 cx.propagate();
9679 return;
9680 }
9681
9682 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9683 s.move_with(|map, selection| {
9684 selection.collapse_to(
9685 movement::start_of_paragraph(map, selection.head(), 1),
9686 SelectionGoal::None,
9687 )
9688 });
9689 })
9690 }
9691
9692 pub fn move_to_end_of_paragraph(
9693 &mut self,
9694 _: &MoveToEndOfParagraph,
9695 window: &mut Window,
9696 cx: &mut Context<Self>,
9697 ) {
9698 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9699 cx.propagate();
9700 return;
9701 }
9702
9703 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9704 s.move_with(|map, selection| {
9705 selection.collapse_to(
9706 movement::end_of_paragraph(map, selection.head(), 1),
9707 SelectionGoal::None,
9708 )
9709 });
9710 })
9711 }
9712
9713 pub fn select_to_start_of_paragraph(
9714 &mut self,
9715 _: &SelectToStartOfParagraph,
9716 window: &mut Window,
9717 cx: &mut Context<Self>,
9718 ) {
9719 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9720 cx.propagate();
9721 return;
9722 }
9723
9724 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9725 s.move_heads_with(|map, head, _| {
9726 (
9727 movement::start_of_paragraph(map, head, 1),
9728 SelectionGoal::None,
9729 )
9730 });
9731 })
9732 }
9733
9734 pub fn select_to_end_of_paragraph(
9735 &mut self,
9736 _: &SelectToEndOfParagraph,
9737 window: &mut Window,
9738 cx: &mut Context<Self>,
9739 ) {
9740 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9741 cx.propagate();
9742 return;
9743 }
9744
9745 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9746 s.move_heads_with(|map, head, _| {
9747 (
9748 movement::end_of_paragraph(map, head, 1),
9749 SelectionGoal::None,
9750 )
9751 });
9752 })
9753 }
9754
9755 pub fn move_to_start_of_excerpt(
9756 &mut self,
9757 _: &MoveToStartOfExcerpt,
9758 window: &mut Window,
9759 cx: &mut Context<Self>,
9760 ) {
9761 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9762 cx.propagate();
9763 return;
9764 }
9765
9766 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9767 s.move_with(|map, selection| {
9768 selection.collapse_to(
9769 movement::start_of_excerpt(
9770 map,
9771 selection.head(),
9772 workspace::searchable::Direction::Prev,
9773 ),
9774 SelectionGoal::None,
9775 )
9776 });
9777 })
9778 }
9779
9780 pub fn move_to_start_of_next_excerpt(
9781 &mut self,
9782 _: &MoveToStartOfNextExcerpt,
9783 window: &mut Window,
9784 cx: &mut Context<Self>,
9785 ) {
9786 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9787 cx.propagate();
9788 return;
9789 }
9790
9791 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9792 s.move_with(|map, selection| {
9793 selection.collapse_to(
9794 movement::start_of_excerpt(
9795 map,
9796 selection.head(),
9797 workspace::searchable::Direction::Next,
9798 ),
9799 SelectionGoal::None,
9800 )
9801 });
9802 })
9803 }
9804
9805 pub fn move_to_end_of_excerpt(
9806 &mut self,
9807 _: &MoveToEndOfExcerpt,
9808 window: &mut Window,
9809 cx: &mut Context<Self>,
9810 ) {
9811 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9812 cx.propagate();
9813 return;
9814 }
9815
9816 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9817 s.move_with(|map, selection| {
9818 selection.collapse_to(
9819 movement::end_of_excerpt(
9820 map,
9821 selection.head(),
9822 workspace::searchable::Direction::Next,
9823 ),
9824 SelectionGoal::None,
9825 )
9826 });
9827 })
9828 }
9829
9830 pub fn move_to_end_of_previous_excerpt(
9831 &mut self,
9832 _: &MoveToEndOfPreviousExcerpt,
9833 window: &mut Window,
9834 cx: &mut Context<Self>,
9835 ) {
9836 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9837 cx.propagate();
9838 return;
9839 }
9840
9841 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9842 s.move_with(|map, selection| {
9843 selection.collapse_to(
9844 movement::end_of_excerpt(
9845 map,
9846 selection.head(),
9847 workspace::searchable::Direction::Prev,
9848 ),
9849 SelectionGoal::None,
9850 )
9851 });
9852 })
9853 }
9854
9855 pub fn select_to_start_of_excerpt(
9856 &mut self,
9857 _: &SelectToStartOfExcerpt,
9858 window: &mut Window,
9859 cx: &mut Context<Self>,
9860 ) {
9861 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9862 cx.propagate();
9863 return;
9864 }
9865
9866 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9867 s.move_heads_with(|map, head, _| {
9868 (
9869 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9870 SelectionGoal::None,
9871 )
9872 });
9873 })
9874 }
9875
9876 pub fn select_to_start_of_next_excerpt(
9877 &mut self,
9878 _: &SelectToStartOfNextExcerpt,
9879 window: &mut Window,
9880 cx: &mut Context<Self>,
9881 ) {
9882 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9883 cx.propagate();
9884 return;
9885 }
9886
9887 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9888 s.move_heads_with(|map, head, _| {
9889 (
9890 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
9891 SelectionGoal::None,
9892 )
9893 });
9894 })
9895 }
9896
9897 pub fn select_to_end_of_excerpt(
9898 &mut self,
9899 _: &SelectToEndOfExcerpt,
9900 window: &mut Window,
9901 cx: &mut Context<Self>,
9902 ) {
9903 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9904 cx.propagate();
9905 return;
9906 }
9907
9908 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9909 s.move_heads_with(|map, head, _| {
9910 (
9911 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9912 SelectionGoal::None,
9913 )
9914 });
9915 })
9916 }
9917
9918 pub fn select_to_end_of_previous_excerpt(
9919 &mut self,
9920 _: &SelectToEndOfPreviousExcerpt,
9921 window: &mut Window,
9922 cx: &mut Context<Self>,
9923 ) {
9924 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9925 cx.propagate();
9926 return;
9927 }
9928
9929 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9930 s.move_heads_with(|map, head, _| {
9931 (
9932 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9933 SelectionGoal::None,
9934 )
9935 });
9936 })
9937 }
9938
9939 pub fn move_to_beginning(
9940 &mut self,
9941 _: &MoveToBeginning,
9942 window: &mut Window,
9943 cx: &mut Context<Self>,
9944 ) {
9945 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9946 cx.propagate();
9947 return;
9948 }
9949
9950 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9951 s.select_ranges(vec![0..0]);
9952 });
9953 }
9954
9955 pub fn select_to_beginning(
9956 &mut self,
9957 _: &SelectToBeginning,
9958 window: &mut Window,
9959 cx: &mut Context<Self>,
9960 ) {
9961 let mut selection = self.selections.last::<Point>(cx);
9962 selection.set_head(Point::zero(), SelectionGoal::None);
9963
9964 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9965 s.select(vec![selection]);
9966 });
9967 }
9968
9969 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9970 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9971 cx.propagate();
9972 return;
9973 }
9974
9975 let cursor = self.buffer.read(cx).read(cx).len();
9976 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9977 s.select_ranges(vec![cursor..cursor])
9978 });
9979 }
9980
9981 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9982 self.nav_history = nav_history;
9983 }
9984
9985 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9986 self.nav_history.as_ref()
9987 }
9988
9989 fn push_to_nav_history(
9990 &mut self,
9991 cursor_anchor: Anchor,
9992 new_position: Option<Point>,
9993 cx: &mut Context<Self>,
9994 ) {
9995 if let Some(nav_history) = self.nav_history.as_mut() {
9996 let buffer = self.buffer.read(cx).read(cx);
9997 let cursor_position = cursor_anchor.to_point(&buffer);
9998 let scroll_state = self.scroll_manager.anchor();
9999 let scroll_top_row = scroll_state.top_row(&buffer);
10000 drop(buffer);
10001
10002 if let Some(new_position) = new_position {
10003 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10004 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10005 return;
10006 }
10007 }
10008
10009 nav_history.push(
10010 Some(NavigationData {
10011 cursor_anchor,
10012 cursor_position,
10013 scroll_anchor: scroll_state,
10014 scroll_top_row,
10015 }),
10016 cx,
10017 );
10018 }
10019 }
10020
10021 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10022 let buffer = self.buffer.read(cx).snapshot(cx);
10023 let mut selection = self.selections.first::<usize>(cx);
10024 selection.set_head(buffer.len(), SelectionGoal::None);
10025 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10026 s.select(vec![selection]);
10027 });
10028 }
10029
10030 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10031 let end = self.buffer.read(cx).read(cx).len();
10032 self.change_selections(None, window, cx, |s| {
10033 s.select_ranges(vec![0..end]);
10034 });
10035 }
10036
10037 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10038 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10039 let mut selections = self.selections.all::<Point>(cx);
10040 let max_point = display_map.buffer_snapshot.max_point();
10041 for selection in &mut selections {
10042 let rows = selection.spanned_rows(true, &display_map);
10043 selection.start = Point::new(rows.start.0, 0);
10044 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10045 selection.reversed = false;
10046 }
10047 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10048 s.select(selections);
10049 });
10050 }
10051
10052 pub fn split_selection_into_lines(
10053 &mut self,
10054 _: &SplitSelectionIntoLines,
10055 window: &mut Window,
10056 cx: &mut Context<Self>,
10057 ) {
10058 let selections = self
10059 .selections
10060 .all::<Point>(cx)
10061 .into_iter()
10062 .map(|selection| selection.start..selection.end)
10063 .collect::<Vec<_>>();
10064 self.unfold_ranges(&selections, true, true, cx);
10065
10066 let mut new_selection_ranges = Vec::new();
10067 {
10068 let buffer = self.buffer.read(cx).read(cx);
10069 for selection in selections {
10070 for row in selection.start.row..selection.end.row {
10071 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10072 new_selection_ranges.push(cursor..cursor);
10073 }
10074
10075 let is_multiline_selection = selection.start.row != selection.end.row;
10076 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10077 // so this action feels more ergonomic when paired with other selection operations
10078 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10079 if !should_skip_last {
10080 new_selection_ranges.push(selection.end..selection.end);
10081 }
10082 }
10083 }
10084 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10085 s.select_ranges(new_selection_ranges);
10086 });
10087 }
10088
10089 pub fn add_selection_above(
10090 &mut self,
10091 _: &AddSelectionAbove,
10092 window: &mut Window,
10093 cx: &mut Context<Self>,
10094 ) {
10095 self.add_selection(true, window, cx);
10096 }
10097
10098 pub fn add_selection_below(
10099 &mut self,
10100 _: &AddSelectionBelow,
10101 window: &mut Window,
10102 cx: &mut Context<Self>,
10103 ) {
10104 self.add_selection(false, window, cx);
10105 }
10106
10107 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10108 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10109 let mut selections = self.selections.all::<Point>(cx);
10110 let text_layout_details = self.text_layout_details(window);
10111 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10112 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10113 let range = oldest_selection.display_range(&display_map).sorted();
10114
10115 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10116 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10117 let positions = start_x.min(end_x)..start_x.max(end_x);
10118
10119 selections.clear();
10120 let mut stack = Vec::new();
10121 for row in range.start.row().0..=range.end.row().0 {
10122 if let Some(selection) = self.selections.build_columnar_selection(
10123 &display_map,
10124 DisplayRow(row),
10125 &positions,
10126 oldest_selection.reversed,
10127 &text_layout_details,
10128 ) {
10129 stack.push(selection.id);
10130 selections.push(selection);
10131 }
10132 }
10133
10134 if above {
10135 stack.reverse();
10136 }
10137
10138 AddSelectionsState { above, stack }
10139 });
10140
10141 let last_added_selection = *state.stack.last().unwrap();
10142 let mut new_selections = Vec::new();
10143 if above == state.above {
10144 let end_row = if above {
10145 DisplayRow(0)
10146 } else {
10147 display_map.max_point().row()
10148 };
10149
10150 'outer: for selection in selections {
10151 if selection.id == last_added_selection {
10152 let range = selection.display_range(&display_map).sorted();
10153 debug_assert_eq!(range.start.row(), range.end.row());
10154 let mut row = range.start.row();
10155 let positions =
10156 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10157 px(start)..px(end)
10158 } else {
10159 let start_x =
10160 display_map.x_for_display_point(range.start, &text_layout_details);
10161 let end_x =
10162 display_map.x_for_display_point(range.end, &text_layout_details);
10163 start_x.min(end_x)..start_x.max(end_x)
10164 };
10165
10166 while row != end_row {
10167 if above {
10168 row.0 -= 1;
10169 } else {
10170 row.0 += 1;
10171 }
10172
10173 if let Some(new_selection) = self.selections.build_columnar_selection(
10174 &display_map,
10175 row,
10176 &positions,
10177 selection.reversed,
10178 &text_layout_details,
10179 ) {
10180 state.stack.push(new_selection.id);
10181 if above {
10182 new_selections.push(new_selection);
10183 new_selections.push(selection);
10184 } else {
10185 new_selections.push(selection);
10186 new_selections.push(new_selection);
10187 }
10188
10189 continue 'outer;
10190 }
10191 }
10192 }
10193
10194 new_selections.push(selection);
10195 }
10196 } else {
10197 new_selections = selections;
10198 new_selections.retain(|s| s.id != last_added_selection);
10199 state.stack.pop();
10200 }
10201
10202 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10203 s.select(new_selections);
10204 });
10205 if state.stack.len() > 1 {
10206 self.add_selections_state = Some(state);
10207 }
10208 }
10209
10210 pub fn select_next_match_internal(
10211 &mut self,
10212 display_map: &DisplaySnapshot,
10213 replace_newest: bool,
10214 autoscroll: Option<Autoscroll>,
10215 window: &mut Window,
10216 cx: &mut Context<Self>,
10217 ) -> Result<()> {
10218 fn select_next_match_ranges(
10219 this: &mut Editor,
10220 range: Range<usize>,
10221 replace_newest: bool,
10222 auto_scroll: Option<Autoscroll>,
10223 window: &mut Window,
10224 cx: &mut Context<Editor>,
10225 ) {
10226 this.unfold_ranges(&[range.clone()], false, true, cx);
10227 this.change_selections(auto_scroll, window, cx, |s| {
10228 if replace_newest {
10229 s.delete(s.newest_anchor().id);
10230 }
10231 s.insert_range(range.clone());
10232 });
10233 }
10234
10235 let buffer = &display_map.buffer_snapshot;
10236 let mut selections = self.selections.all::<usize>(cx);
10237 if let Some(mut select_next_state) = self.select_next_state.take() {
10238 let query = &select_next_state.query;
10239 if !select_next_state.done {
10240 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10241 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10242 let mut next_selected_range = None;
10243
10244 let bytes_after_last_selection =
10245 buffer.bytes_in_range(last_selection.end..buffer.len());
10246 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10247 let query_matches = query
10248 .stream_find_iter(bytes_after_last_selection)
10249 .map(|result| (last_selection.end, result))
10250 .chain(
10251 query
10252 .stream_find_iter(bytes_before_first_selection)
10253 .map(|result| (0, result)),
10254 );
10255
10256 for (start_offset, query_match) in query_matches {
10257 let query_match = query_match.unwrap(); // can only fail due to I/O
10258 let offset_range =
10259 start_offset + query_match.start()..start_offset + query_match.end();
10260 let display_range = offset_range.start.to_display_point(display_map)
10261 ..offset_range.end.to_display_point(display_map);
10262
10263 if !select_next_state.wordwise
10264 || (!movement::is_inside_word(display_map, display_range.start)
10265 && !movement::is_inside_word(display_map, display_range.end))
10266 {
10267 // TODO: This is n^2, because we might check all the selections
10268 if !selections
10269 .iter()
10270 .any(|selection| selection.range().overlaps(&offset_range))
10271 {
10272 next_selected_range = Some(offset_range);
10273 break;
10274 }
10275 }
10276 }
10277
10278 if let Some(next_selected_range) = next_selected_range {
10279 select_next_match_ranges(
10280 self,
10281 next_selected_range,
10282 replace_newest,
10283 autoscroll,
10284 window,
10285 cx,
10286 );
10287 } else {
10288 select_next_state.done = true;
10289 }
10290 }
10291
10292 self.select_next_state = Some(select_next_state);
10293 } else {
10294 let mut only_carets = true;
10295 let mut same_text_selected = true;
10296 let mut selected_text = None;
10297
10298 let mut selections_iter = selections.iter().peekable();
10299 while let Some(selection) = selections_iter.next() {
10300 if selection.start != selection.end {
10301 only_carets = false;
10302 }
10303
10304 if same_text_selected {
10305 if selected_text.is_none() {
10306 selected_text =
10307 Some(buffer.text_for_range(selection.range()).collect::<String>());
10308 }
10309
10310 if let Some(next_selection) = selections_iter.peek() {
10311 if next_selection.range().len() == selection.range().len() {
10312 let next_selected_text = buffer
10313 .text_for_range(next_selection.range())
10314 .collect::<String>();
10315 if Some(next_selected_text) != selected_text {
10316 same_text_selected = false;
10317 selected_text = None;
10318 }
10319 } else {
10320 same_text_selected = false;
10321 selected_text = None;
10322 }
10323 }
10324 }
10325 }
10326
10327 if only_carets {
10328 for selection in &mut selections {
10329 let word_range = movement::surrounding_word(
10330 display_map,
10331 selection.start.to_display_point(display_map),
10332 );
10333 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10334 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10335 selection.goal = SelectionGoal::None;
10336 selection.reversed = false;
10337 select_next_match_ranges(
10338 self,
10339 selection.start..selection.end,
10340 replace_newest,
10341 autoscroll,
10342 window,
10343 cx,
10344 );
10345 }
10346
10347 if selections.len() == 1 {
10348 let selection = selections
10349 .last()
10350 .expect("ensured that there's only one selection");
10351 let query = buffer
10352 .text_for_range(selection.start..selection.end)
10353 .collect::<String>();
10354 let is_empty = query.is_empty();
10355 let select_state = SelectNextState {
10356 query: AhoCorasick::new(&[query])?,
10357 wordwise: true,
10358 done: is_empty,
10359 };
10360 self.select_next_state = Some(select_state);
10361 } else {
10362 self.select_next_state = None;
10363 }
10364 } else if let Some(selected_text) = selected_text {
10365 self.select_next_state = Some(SelectNextState {
10366 query: AhoCorasick::new(&[selected_text])?,
10367 wordwise: false,
10368 done: false,
10369 });
10370 self.select_next_match_internal(
10371 display_map,
10372 replace_newest,
10373 autoscroll,
10374 window,
10375 cx,
10376 )?;
10377 }
10378 }
10379 Ok(())
10380 }
10381
10382 pub fn select_all_matches(
10383 &mut self,
10384 _action: &SelectAllMatches,
10385 window: &mut Window,
10386 cx: &mut Context<Self>,
10387 ) -> Result<()> {
10388 self.push_to_selection_history();
10389 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10390
10391 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10392 let Some(select_next_state) = self.select_next_state.as_mut() else {
10393 return Ok(());
10394 };
10395 if select_next_state.done {
10396 return Ok(());
10397 }
10398
10399 let mut new_selections = self.selections.all::<usize>(cx);
10400
10401 let buffer = &display_map.buffer_snapshot;
10402 let query_matches = select_next_state
10403 .query
10404 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10405
10406 for query_match in query_matches {
10407 let query_match = query_match.unwrap(); // can only fail due to I/O
10408 let offset_range = query_match.start()..query_match.end();
10409 let display_range = offset_range.start.to_display_point(&display_map)
10410 ..offset_range.end.to_display_point(&display_map);
10411
10412 if !select_next_state.wordwise
10413 || (!movement::is_inside_word(&display_map, display_range.start)
10414 && !movement::is_inside_word(&display_map, display_range.end))
10415 {
10416 self.selections.change_with(cx, |selections| {
10417 new_selections.push(Selection {
10418 id: selections.new_selection_id(),
10419 start: offset_range.start,
10420 end: offset_range.end,
10421 reversed: false,
10422 goal: SelectionGoal::None,
10423 });
10424 });
10425 }
10426 }
10427
10428 new_selections.sort_by_key(|selection| selection.start);
10429 let mut ix = 0;
10430 while ix + 1 < new_selections.len() {
10431 let current_selection = &new_selections[ix];
10432 let next_selection = &new_selections[ix + 1];
10433 if current_selection.range().overlaps(&next_selection.range()) {
10434 if current_selection.id < next_selection.id {
10435 new_selections.remove(ix + 1);
10436 } else {
10437 new_selections.remove(ix);
10438 }
10439 } else {
10440 ix += 1;
10441 }
10442 }
10443
10444 let reversed = self.selections.oldest::<usize>(cx).reversed;
10445
10446 for selection in new_selections.iter_mut() {
10447 selection.reversed = reversed;
10448 }
10449
10450 select_next_state.done = true;
10451 self.unfold_ranges(
10452 &new_selections
10453 .iter()
10454 .map(|selection| selection.range())
10455 .collect::<Vec<_>>(),
10456 false,
10457 false,
10458 cx,
10459 );
10460 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10461 selections.select(new_selections)
10462 });
10463
10464 Ok(())
10465 }
10466
10467 pub fn select_next(
10468 &mut self,
10469 action: &SelectNext,
10470 window: &mut Window,
10471 cx: &mut Context<Self>,
10472 ) -> Result<()> {
10473 self.push_to_selection_history();
10474 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10475 self.select_next_match_internal(
10476 &display_map,
10477 action.replace_newest,
10478 Some(Autoscroll::newest()),
10479 window,
10480 cx,
10481 )?;
10482 Ok(())
10483 }
10484
10485 pub fn select_previous(
10486 &mut self,
10487 action: &SelectPrevious,
10488 window: &mut Window,
10489 cx: &mut Context<Self>,
10490 ) -> Result<()> {
10491 self.push_to_selection_history();
10492 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10493 let buffer = &display_map.buffer_snapshot;
10494 let mut selections = self.selections.all::<usize>(cx);
10495 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10496 let query = &select_prev_state.query;
10497 if !select_prev_state.done {
10498 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10499 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10500 let mut next_selected_range = None;
10501 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10502 let bytes_before_last_selection =
10503 buffer.reversed_bytes_in_range(0..last_selection.start);
10504 let bytes_after_first_selection =
10505 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10506 let query_matches = query
10507 .stream_find_iter(bytes_before_last_selection)
10508 .map(|result| (last_selection.start, result))
10509 .chain(
10510 query
10511 .stream_find_iter(bytes_after_first_selection)
10512 .map(|result| (buffer.len(), result)),
10513 );
10514 for (end_offset, query_match) in query_matches {
10515 let query_match = query_match.unwrap(); // can only fail due to I/O
10516 let offset_range =
10517 end_offset - query_match.end()..end_offset - query_match.start();
10518 let display_range = offset_range.start.to_display_point(&display_map)
10519 ..offset_range.end.to_display_point(&display_map);
10520
10521 if !select_prev_state.wordwise
10522 || (!movement::is_inside_word(&display_map, display_range.start)
10523 && !movement::is_inside_word(&display_map, display_range.end))
10524 {
10525 next_selected_range = Some(offset_range);
10526 break;
10527 }
10528 }
10529
10530 if let Some(next_selected_range) = next_selected_range {
10531 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10532 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10533 if action.replace_newest {
10534 s.delete(s.newest_anchor().id);
10535 }
10536 s.insert_range(next_selected_range);
10537 });
10538 } else {
10539 select_prev_state.done = true;
10540 }
10541 }
10542
10543 self.select_prev_state = Some(select_prev_state);
10544 } else {
10545 let mut only_carets = true;
10546 let mut same_text_selected = true;
10547 let mut selected_text = None;
10548
10549 let mut selections_iter = selections.iter().peekable();
10550 while let Some(selection) = selections_iter.next() {
10551 if selection.start != selection.end {
10552 only_carets = false;
10553 }
10554
10555 if same_text_selected {
10556 if selected_text.is_none() {
10557 selected_text =
10558 Some(buffer.text_for_range(selection.range()).collect::<String>());
10559 }
10560
10561 if let Some(next_selection) = selections_iter.peek() {
10562 if next_selection.range().len() == selection.range().len() {
10563 let next_selected_text = buffer
10564 .text_for_range(next_selection.range())
10565 .collect::<String>();
10566 if Some(next_selected_text) != selected_text {
10567 same_text_selected = false;
10568 selected_text = None;
10569 }
10570 } else {
10571 same_text_selected = false;
10572 selected_text = None;
10573 }
10574 }
10575 }
10576 }
10577
10578 if only_carets {
10579 for selection in &mut selections {
10580 let word_range = movement::surrounding_word(
10581 &display_map,
10582 selection.start.to_display_point(&display_map),
10583 );
10584 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10585 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10586 selection.goal = SelectionGoal::None;
10587 selection.reversed = false;
10588 }
10589 if selections.len() == 1 {
10590 let selection = selections
10591 .last()
10592 .expect("ensured that there's only one selection");
10593 let query = buffer
10594 .text_for_range(selection.start..selection.end)
10595 .collect::<String>();
10596 let is_empty = query.is_empty();
10597 let select_state = SelectNextState {
10598 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10599 wordwise: true,
10600 done: is_empty,
10601 };
10602 self.select_prev_state = Some(select_state);
10603 } else {
10604 self.select_prev_state = None;
10605 }
10606
10607 self.unfold_ranges(
10608 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10609 false,
10610 true,
10611 cx,
10612 );
10613 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10614 s.select(selections);
10615 });
10616 } else if let Some(selected_text) = selected_text {
10617 self.select_prev_state = Some(SelectNextState {
10618 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10619 wordwise: false,
10620 done: false,
10621 });
10622 self.select_previous(action, window, cx)?;
10623 }
10624 }
10625 Ok(())
10626 }
10627
10628 pub fn toggle_comments(
10629 &mut self,
10630 action: &ToggleComments,
10631 window: &mut Window,
10632 cx: &mut Context<Self>,
10633 ) {
10634 if self.read_only(cx) {
10635 return;
10636 }
10637 let text_layout_details = &self.text_layout_details(window);
10638 self.transact(window, cx, |this, window, cx| {
10639 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10640 let mut edits = Vec::new();
10641 let mut selection_edit_ranges = Vec::new();
10642 let mut last_toggled_row = None;
10643 let snapshot = this.buffer.read(cx).read(cx);
10644 let empty_str: Arc<str> = Arc::default();
10645 let mut suffixes_inserted = Vec::new();
10646 let ignore_indent = action.ignore_indent;
10647
10648 fn comment_prefix_range(
10649 snapshot: &MultiBufferSnapshot,
10650 row: MultiBufferRow,
10651 comment_prefix: &str,
10652 comment_prefix_whitespace: &str,
10653 ignore_indent: bool,
10654 ) -> Range<Point> {
10655 let indent_size = if ignore_indent {
10656 0
10657 } else {
10658 snapshot.indent_size_for_line(row).len
10659 };
10660
10661 let start = Point::new(row.0, indent_size);
10662
10663 let mut line_bytes = snapshot
10664 .bytes_in_range(start..snapshot.max_point())
10665 .flatten()
10666 .copied();
10667
10668 // If this line currently begins with the line comment prefix, then record
10669 // the range containing the prefix.
10670 if line_bytes
10671 .by_ref()
10672 .take(comment_prefix.len())
10673 .eq(comment_prefix.bytes())
10674 {
10675 // Include any whitespace that matches the comment prefix.
10676 let matching_whitespace_len = line_bytes
10677 .zip(comment_prefix_whitespace.bytes())
10678 .take_while(|(a, b)| a == b)
10679 .count() as u32;
10680 let end = Point::new(
10681 start.row,
10682 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10683 );
10684 start..end
10685 } else {
10686 start..start
10687 }
10688 }
10689
10690 fn comment_suffix_range(
10691 snapshot: &MultiBufferSnapshot,
10692 row: MultiBufferRow,
10693 comment_suffix: &str,
10694 comment_suffix_has_leading_space: bool,
10695 ) -> Range<Point> {
10696 let end = Point::new(row.0, snapshot.line_len(row));
10697 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10698
10699 let mut line_end_bytes = snapshot
10700 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10701 .flatten()
10702 .copied();
10703
10704 let leading_space_len = if suffix_start_column > 0
10705 && line_end_bytes.next() == Some(b' ')
10706 && comment_suffix_has_leading_space
10707 {
10708 1
10709 } else {
10710 0
10711 };
10712
10713 // If this line currently begins with the line comment prefix, then record
10714 // the range containing the prefix.
10715 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10716 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10717 start..end
10718 } else {
10719 end..end
10720 }
10721 }
10722
10723 // TODO: Handle selections that cross excerpts
10724 for selection in &mut selections {
10725 let start_column = snapshot
10726 .indent_size_for_line(MultiBufferRow(selection.start.row))
10727 .len;
10728 let language = if let Some(language) =
10729 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10730 {
10731 language
10732 } else {
10733 continue;
10734 };
10735
10736 selection_edit_ranges.clear();
10737
10738 // If multiple selections contain a given row, avoid processing that
10739 // row more than once.
10740 let mut start_row = MultiBufferRow(selection.start.row);
10741 if last_toggled_row == Some(start_row) {
10742 start_row = start_row.next_row();
10743 }
10744 let end_row =
10745 if selection.end.row > selection.start.row && selection.end.column == 0 {
10746 MultiBufferRow(selection.end.row - 1)
10747 } else {
10748 MultiBufferRow(selection.end.row)
10749 };
10750 last_toggled_row = Some(end_row);
10751
10752 if start_row > end_row {
10753 continue;
10754 }
10755
10756 // If the language has line comments, toggle those.
10757 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10758
10759 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10760 if ignore_indent {
10761 full_comment_prefixes = full_comment_prefixes
10762 .into_iter()
10763 .map(|s| Arc::from(s.trim_end()))
10764 .collect();
10765 }
10766
10767 if !full_comment_prefixes.is_empty() {
10768 let first_prefix = full_comment_prefixes
10769 .first()
10770 .expect("prefixes is non-empty");
10771 let prefix_trimmed_lengths = full_comment_prefixes
10772 .iter()
10773 .map(|p| p.trim_end_matches(' ').len())
10774 .collect::<SmallVec<[usize; 4]>>();
10775
10776 let mut all_selection_lines_are_comments = true;
10777
10778 for row in start_row.0..=end_row.0 {
10779 let row = MultiBufferRow(row);
10780 if start_row < end_row && snapshot.is_line_blank(row) {
10781 continue;
10782 }
10783
10784 let prefix_range = full_comment_prefixes
10785 .iter()
10786 .zip(prefix_trimmed_lengths.iter().copied())
10787 .map(|(prefix, trimmed_prefix_len)| {
10788 comment_prefix_range(
10789 snapshot.deref(),
10790 row,
10791 &prefix[..trimmed_prefix_len],
10792 &prefix[trimmed_prefix_len..],
10793 ignore_indent,
10794 )
10795 })
10796 .max_by_key(|range| range.end.column - range.start.column)
10797 .expect("prefixes is non-empty");
10798
10799 if prefix_range.is_empty() {
10800 all_selection_lines_are_comments = false;
10801 }
10802
10803 selection_edit_ranges.push(prefix_range);
10804 }
10805
10806 if all_selection_lines_are_comments {
10807 edits.extend(
10808 selection_edit_ranges
10809 .iter()
10810 .cloned()
10811 .map(|range| (range, empty_str.clone())),
10812 );
10813 } else {
10814 let min_column = selection_edit_ranges
10815 .iter()
10816 .map(|range| range.start.column)
10817 .min()
10818 .unwrap_or(0);
10819 edits.extend(selection_edit_ranges.iter().map(|range| {
10820 let position = Point::new(range.start.row, min_column);
10821 (position..position, first_prefix.clone())
10822 }));
10823 }
10824 } else if let Some((full_comment_prefix, comment_suffix)) =
10825 language.block_comment_delimiters()
10826 {
10827 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10828 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10829 let prefix_range = comment_prefix_range(
10830 snapshot.deref(),
10831 start_row,
10832 comment_prefix,
10833 comment_prefix_whitespace,
10834 ignore_indent,
10835 );
10836 let suffix_range = comment_suffix_range(
10837 snapshot.deref(),
10838 end_row,
10839 comment_suffix.trim_start_matches(' '),
10840 comment_suffix.starts_with(' '),
10841 );
10842
10843 if prefix_range.is_empty() || suffix_range.is_empty() {
10844 edits.push((
10845 prefix_range.start..prefix_range.start,
10846 full_comment_prefix.clone(),
10847 ));
10848 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10849 suffixes_inserted.push((end_row, comment_suffix.len()));
10850 } else {
10851 edits.push((prefix_range, empty_str.clone()));
10852 edits.push((suffix_range, empty_str.clone()));
10853 }
10854 } else {
10855 continue;
10856 }
10857 }
10858
10859 drop(snapshot);
10860 this.buffer.update(cx, |buffer, cx| {
10861 buffer.edit(edits, None, cx);
10862 });
10863
10864 // Adjust selections so that they end before any comment suffixes that
10865 // were inserted.
10866 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10867 let mut selections = this.selections.all::<Point>(cx);
10868 let snapshot = this.buffer.read(cx).read(cx);
10869 for selection in &mut selections {
10870 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10871 match row.cmp(&MultiBufferRow(selection.end.row)) {
10872 Ordering::Less => {
10873 suffixes_inserted.next();
10874 continue;
10875 }
10876 Ordering::Greater => break,
10877 Ordering::Equal => {
10878 if selection.end.column == snapshot.line_len(row) {
10879 if selection.is_empty() {
10880 selection.start.column -= suffix_len as u32;
10881 }
10882 selection.end.column -= suffix_len as u32;
10883 }
10884 break;
10885 }
10886 }
10887 }
10888 }
10889
10890 drop(snapshot);
10891 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10892 s.select(selections)
10893 });
10894
10895 let selections = this.selections.all::<Point>(cx);
10896 let selections_on_single_row = selections.windows(2).all(|selections| {
10897 selections[0].start.row == selections[1].start.row
10898 && selections[0].end.row == selections[1].end.row
10899 && selections[0].start.row == selections[0].end.row
10900 });
10901 let selections_selecting = selections
10902 .iter()
10903 .any(|selection| selection.start != selection.end);
10904 let advance_downwards = action.advance_downwards
10905 && selections_on_single_row
10906 && !selections_selecting
10907 && !matches!(this.mode, EditorMode::SingleLine { .. });
10908
10909 if advance_downwards {
10910 let snapshot = this.buffer.read(cx).snapshot(cx);
10911
10912 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10913 s.move_cursors_with(|display_snapshot, display_point, _| {
10914 let mut point = display_point.to_point(display_snapshot);
10915 point.row += 1;
10916 point = snapshot.clip_point(point, Bias::Left);
10917 let display_point = point.to_display_point(display_snapshot);
10918 let goal = SelectionGoal::HorizontalPosition(
10919 display_snapshot
10920 .x_for_display_point(display_point, text_layout_details)
10921 .into(),
10922 );
10923 (display_point, goal)
10924 })
10925 });
10926 }
10927 });
10928 }
10929
10930 pub fn select_enclosing_symbol(
10931 &mut self,
10932 _: &SelectEnclosingSymbol,
10933 window: &mut Window,
10934 cx: &mut Context<Self>,
10935 ) {
10936 let buffer = self.buffer.read(cx).snapshot(cx);
10937 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10938
10939 fn update_selection(
10940 selection: &Selection<usize>,
10941 buffer_snap: &MultiBufferSnapshot,
10942 ) -> Option<Selection<usize>> {
10943 let cursor = selection.head();
10944 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10945 for symbol in symbols.iter().rev() {
10946 let start = symbol.range.start.to_offset(buffer_snap);
10947 let end = symbol.range.end.to_offset(buffer_snap);
10948 let new_range = start..end;
10949 if start < selection.start || end > selection.end {
10950 return Some(Selection {
10951 id: selection.id,
10952 start: new_range.start,
10953 end: new_range.end,
10954 goal: SelectionGoal::None,
10955 reversed: selection.reversed,
10956 });
10957 }
10958 }
10959 None
10960 }
10961
10962 let mut selected_larger_symbol = false;
10963 let new_selections = old_selections
10964 .iter()
10965 .map(|selection| match update_selection(selection, &buffer) {
10966 Some(new_selection) => {
10967 if new_selection.range() != selection.range() {
10968 selected_larger_symbol = true;
10969 }
10970 new_selection
10971 }
10972 None => selection.clone(),
10973 })
10974 .collect::<Vec<_>>();
10975
10976 if selected_larger_symbol {
10977 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10978 s.select(new_selections);
10979 });
10980 }
10981 }
10982
10983 pub fn select_larger_syntax_node(
10984 &mut self,
10985 _: &SelectLargerSyntaxNode,
10986 window: &mut Window,
10987 cx: &mut Context<Self>,
10988 ) {
10989 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10990 let buffer = self.buffer.read(cx).snapshot(cx);
10991 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10992
10993 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10994 let mut selected_larger_node = false;
10995 let new_selections = old_selections
10996 .iter()
10997 .map(|selection| {
10998 let old_range = selection.start..selection.end;
10999 let mut new_range = old_range.clone();
11000 let mut new_node = None;
11001 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11002 {
11003 new_node = Some(node);
11004 new_range = match containing_range {
11005 MultiOrSingleBufferOffsetRange::Single(_) => break,
11006 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11007 };
11008 if !display_map.intersects_fold(new_range.start)
11009 && !display_map.intersects_fold(new_range.end)
11010 {
11011 break;
11012 }
11013 }
11014
11015 if let Some(node) = new_node {
11016 // Log the ancestor, to support using this action as a way to explore TreeSitter
11017 // nodes. Parent and grandparent are also logged because this operation will not
11018 // visit nodes that have the same range as their parent.
11019 log::info!("Node: {node:?}");
11020 let parent = node.parent();
11021 log::info!("Parent: {parent:?}");
11022 let grandparent = parent.and_then(|x| x.parent());
11023 log::info!("Grandparent: {grandparent:?}");
11024 }
11025
11026 selected_larger_node |= new_range != old_range;
11027 Selection {
11028 id: selection.id,
11029 start: new_range.start,
11030 end: new_range.end,
11031 goal: SelectionGoal::None,
11032 reversed: selection.reversed,
11033 }
11034 })
11035 .collect::<Vec<_>>();
11036
11037 if selected_larger_node {
11038 stack.push(old_selections);
11039 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11040 s.select(new_selections);
11041 });
11042 }
11043 self.select_larger_syntax_node_stack = stack;
11044 }
11045
11046 pub fn select_smaller_syntax_node(
11047 &mut self,
11048 _: &SelectSmallerSyntaxNode,
11049 window: &mut Window,
11050 cx: &mut Context<Self>,
11051 ) {
11052 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11053 if let Some(selections) = stack.pop() {
11054 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11055 s.select(selections.to_vec());
11056 });
11057 }
11058 self.select_larger_syntax_node_stack = stack;
11059 }
11060
11061 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11062 if !EditorSettings::get_global(cx).gutter.runnables {
11063 self.clear_tasks();
11064 return Task::ready(());
11065 }
11066 let project = self.project.as_ref().map(Entity::downgrade);
11067 cx.spawn_in(window, |this, mut cx| async move {
11068 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11069 let Some(project) = project.and_then(|p| p.upgrade()) else {
11070 return;
11071 };
11072 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11073 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11074 }) else {
11075 return;
11076 };
11077
11078 let hide_runnables = project
11079 .update(&mut cx, |project, cx| {
11080 // Do not display any test indicators in non-dev server remote projects.
11081 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11082 })
11083 .unwrap_or(true);
11084 if hide_runnables {
11085 return;
11086 }
11087 let new_rows =
11088 cx.background_spawn({
11089 let snapshot = display_snapshot.clone();
11090 async move {
11091 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11092 }
11093 })
11094 .await;
11095
11096 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11097 this.update(&mut cx, |this, _| {
11098 this.clear_tasks();
11099 for (key, value) in rows {
11100 this.insert_tasks(key, value);
11101 }
11102 })
11103 .ok();
11104 })
11105 }
11106 fn fetch_runnable_ranges(
11107 snapshot: &DisplaySnapshot,
11108 range: Range<Anchor>,
11109 ) -> Vec<language::RunnableRange> {
11110 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11111 }
11112
11113 fn runnable_rows(
11114 project: Entity<Project>,
11115 snapshot: DisplaySnapshot,
11116 runnable_ranges: Vec<RunnableRange>,
11117 mut cx: AsyncWindowContext,
11118 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11119 runnable_ranges
11120 .into_iter()
11121 .filter_map(|mut runnable| {
11122 let tasks = cx
11123 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11124 .ok()?;
11125 if tasks.is_empty() {
11126 return None;
11127 }
11128
11129 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11130
11131 let row = snapshot
11132 .buffer_snapshot
11133 .buffer_line_for_row(MultiBufferRow(point.row))?
11134 .1
11135 .start
11136 .row;
11137
11138 let context_range =
11139 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11140 Some((
11141 (runnable.buffer_id, row),
11142 RunnableTasks {
11143 templates: tasks,
11144 offset: snapshot
11145 .buffer_snapshot
11146 .anchor_before(runnable.run_range.start),
11147 context_range,
11148 column: point.column,
11149 extra_variables: runnable.extra_captures,
11150 },
11151 ))
11152 })
11153 .collect()
11154 }
11155
11156 fn templates_with_tags(
11157 project: &Entity<Project>,
11158 runnable: &mut Runnable,
11159 cx: &mut App,
11160 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11161 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11162 let (worktree_id, file) = project
11163 .buffer_for_id(runnable.buffer, cx)
11164 .and_then(|buffer| buffer.read(cx).file())
11165 .map(|file| (file.worktree_id(cx), file.clone()))
11166 .unzip();
11167
11168 (
11169 project.task_store().read(cx).task_inventory().cloned(),
11170 worktree_id,
11171 file,
11172 )
11173 });
11174
11175 let tags = mem::take(&mut runnable.tags);
11176 let mut tags: Vec<_> = tags
11177 .into_iter()
11178 .flat_map(|tag| {
11179 let tag = tag.0.clone();
11180 inventory
11181 .as_ref()
11182 .into_iter()
11183 .flat_map(|inventory| {
11184 inventory.read(cx).list_tasks(
11185 file.clone(),
11186 Some(runnable.language.clone()),
11187 worktree_id,
11188 cx,
11189 )
11190 })
11191 .filter(move |(_, template)| {
11192 template.tags.iter().any(|source_tag| source_tag == &tag)
11193 })
11194 })
11195 .sorted_by_key(|(kind, _)| kind.to_owned())
11196 .collect();
11197 if let Some((leading_tag_source, _)) = tags.first() {
11198 // Strongest source wins; if we have worktree tag binding, prefer that to
11199 // global and language bindings;
11200 // if we have a global binding, prefer that to language binding.
11201 let first_mismatch = tags
11202 .iter()
11203 .position(|(tag_source, _)| tag_source != leading_tag_source);
11204 if let Some(index) = first_mismatch {
11205 tags.truncate(index);
11206 }
11207 }
11208
11209 tags
11210 }
11211
11212 pub fn move_to_enclosing_bracket(
11213 &mut self,
11214 _: &MoveToEnclosingBracket,
11215 window: &mut Window,
11216 cx: &mut Context<Self>,
11217 ) {
11218 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11219 s.move_offsets_with(|snapshot, selection| {
11220 let Some(enclosing_bracket_ranges) =
11221 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11222 else {
11223 return;
11224 };
11225
11226 let mut best_length = usize::MAX;
11227 let mut best_inside = false;
11228 let mut best_in_bracket_range = false;
11229 let mut best_destination = None;
11230 for (open, close) in enclosing_bracket_ranges {
11231 let close = close.to_inclusive();
11232 let length = close.end() - open.start;
11233 let inside = selection.start >= open.end && selection.end <= *close.start();
11234 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11235 || close.contains(&selection.head());
11236
11237 // If best is next to a bracket and current isn't, skip
11238 if !in_bracket_range && best_in_bracket_range {
11239 continue;
11240 }
11241
11242 // Prefer smaller lengths unless best is inside and current isn't
11243 if length > best_length && (best_inside || !inside) {
11244 continue;
11245 }
11246
11247 best_length = length;
11248 best_inside = inside;
11249 best_in_bracket_range = in_bracket_range;
11250 best_destination = Some(
11251 if close.contains(&selection.start) && close.contains(&selection.end) {
11252 if inside {
11253 open.end
11254 } else {
11255 open.start
11256 }
11257 } else if inside {
11258 *close.start()
11259 } else {
11260 *close.end()
11261 },
11262 );
11263 }
11264
11265 if let Some(destination) = best_destination {
11266 selection.collapse_to(destination, SelectionGoal::None);
11267 }
11268 })
11269 });
11270 }
11271
11272 pub fn undo_selection(
11273 &mut self,
11274 _: &UndoSelection,
11275 window: &mut Window,
11276 cx: &mut Context<Self>,
11277 ) {
11278 self.end_selection(window, cx);
11279 self.selection_history.mode = SelectionHistoryMode::Undoing;
11280 if let Some(entry) = self.selection_history.undo_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 redo_selection(
11293 &mut self,
11294 _: &RedoSelection,
11295 window: &mut Window,
11296 cx: &mut Context<Self>,
11297 ) {
11298 self.end_selection(window, cx);
11299 self.selection_history.mode = SelectionHistoryMode::Redoing;
11300 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11301 self.change_selections(None, window, cx, |s| {
11302 s.select_anchors(entry.selections.to_vec())
11303 });
11304 self.select_next_state = entry.select_next_state;
11305 self.select_prev_state = entry.select_prev_state;
11306 self.add_selections_state = entry.add_selections_state;
11307 self.request_autoscroll(Autoscroll::newest(), cx);
11308 }
11309 self.selection_history.mode = SelectionHistoryMode::Normal;
11310 }
11311
11312 pub fn expand_excerpts(
11313 &mut self,
11314 action: &ExpandExcerpts,
11315 _: &mut Window,
11316 cx: &mut Context<Self>,
11317 ) {
11318 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11319 }
11320
11321 pub fn expand_excerpts_down(
11322 &mut self,
11323 action: &ExpandExcerptsDown,
11324 _: &mut Window,
11325 cx: &mut Context<Self>,
11326 ) {
11327 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11328 }
11329
11330 pub fn expand_excerpts_up(
11331 &mut self,
11332 action: &ExpandExcerptsUp,
11333 _: &mut Window,
11334 cx: &mut Context<Self>,
11335 ) {
11336 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11337 }
11338
11339 pub fn expand_excerpts_for_direction(
11340 &mut self,
11341 lines: u32,
11342 direction: ExpandExcerptDirection,
11343
11344 cx: &mut Context<Self>,
11345 ) {
11346 let selections = self.selections.disjoint_anchors();
11347
11348 let lines = if lines == 0 {
11349 EditorSettings::get_global(cx).expand_excerpt_lines
11350 } else {
11351 lines
11352 };
11353
11354 self.buffer.update(cx, |buffer, cx| {
11355 let snapshot = buffer.snapshot(cx);
11356 let mut excerpt_ids = selections
11357 .iter()
11358 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11359 .collect::<Vec<_>>();
11360 excerpt_ids.sort();
11361 excerpt_ids.dedup();
11362 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11363 })
11364 }
11365
11366 pub fn expand_excerpt(
11367 &mut self,
11368 excerpt: ExcerptId,
11369 direction: ExpandExcerptDirection,
11370 cx: &mut Context<Self>,
11371 ) {
11372 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11373 self.buffer.update(cx, |buffer, cx| {
11374 buffer.expand_excerpts([excerpt], lines, direction, cx)
11375 })
11376 }
11377
11378 pub fn go_to_singleton_buffer_point(
11379 &mut self,
11380 point: Point,
11381 window: &mut Window,
11382 cx: &mut Context<Self>,
11383 ) {
11384 self.go_to_singleton_buffer_range(point..point, window, cx);
11385 }
11386
11387 pub fn go_to_singleton_buffer_range(
11388 &mut self,
11389 range: Range<Point>,
11390 window: &mut Window,
11391 cx: &mut Context<Self>,
11392 ) {
11393 let multibuffer = self.buffer().read(cx);
11394 let Some(buffer) = multibuffer.as_singleton() else {
11395 return;
11396 };
11397 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11398 return;
11399 };
11400 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11401 return;
11402 };
11403 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11404 s.select_anchor_ranges([start..end])
11405 });
11406 }
11407
11408 fn go_to_diagnostic(
11409 &mut self,
11410 _: &GoToDiagnostic,
11411 window: &mut Window,
11412 cx: &mut Context<Self>,
11413 ) {
11414 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11415 }
11416
11417 fn go_to_prev_diagnostic(
11418 &mut self,
11419 _: &GoToPreviousDiagnostic,
11420 window: &mut Window,
11421 cx: &mut Context<Self>,
11422 ) {
11423 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11424 }
11425
11426 pub fn go_to_diagnostic_impl(
11427 &mut self,
11428 direction: Direction,
11429 window: &mut Window,
11430 cx: &mut Context<Self>,
11431 ) {
11432 let buffer = self.buffer.read(cx).snapshot(cx);
11433 let selection = self.selections.newest::<usize>(cx);
11434
11435 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11436 if direction == Direction::Next {
11437 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11438 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11439 return;
11440 };
11441 self.activate_diagnostics(
11442 buffer_id,
11443 popover.local_diagnostic.diagnostic.group_id,
11444 window,
11445 cx,
11446 );
11447 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11448 let primary_range_start = active_diagnostics.primary_range.start;
11449 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11450 let mut new_selection = s.newest_anchor().clone();
11451 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11452 s.select_anchors(vec![new_selection.clone()]);
11453 });
11454 self.refresh_inline_completion(false, true, window, cx);
11455 }
11456 return;
11457 }
11458 }
11459
11460 let active_group_id = self
11461 .active_diagnostics
11462 .as_ref()
11463 .map(|active_group| active_group.group_id);
11464 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11465 active_diagnostics
11466 .primary_range
11467 .to_offset(&buffer)
11468 .to_inclusive()
11469 });
11470 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11471 if active_primary_range.contains(&selection.head()) {
11472 *active_primary_range.start()
11473 } else {
11474 selection.head()
11475 }
11476 } else {
11477 selection.head()
11478 };
11479
11480 let snapshot = self.snapshot(window, cx);
11481 let primary_diagnostics_before = buffer
11482 .diagnostics_in_range::<usize>(0..search_start)
11483 .filter(|entry| entry.diagnostic.is_primary)
11484 .filter(|entry| entry.range.start != entry.range.end)
11485 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11486 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11487 .collect::<Vec<_>>();
11488 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11489 primary_diagnostics_before
11490 .iter()
11491 .position(|entry| entry.diagnostic.group_id == active_group_id)
11492 });
11493
11494 let primary_diagnostics_after = buffer
11495 .diagnostics_in_range::<usize>(search_start..buffer.len())
11496 .filter(|entry| entry.diagnostic.is_primary)
11497 .filter(|entry| entry.range.start != entry.range.end)
11498 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11499 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11500 .collect::<Vec<_>>();
11501 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11502 primary_diagnostics_after
11503 .iter()
11504 .enumerate()
11505 .rev()
11506 .find_map(|(i, entry)| {
11507 if entry.diagnostic.group_id == active_group_id {
11508 Some(i)
11509 } else {
11510 None
11511 }
11512 })
11513 });
11514
11515 let next_primary_diagnostic = match direction {
11516 Direction::Prev => primary_diagnostics_before
11517 .iter()
11518 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11519 .rev()
11520 .next(),
11521 Direction::Next => primary_diagnostics_after
11522 .iter()
11523 .skip(
11524 last_same_group_diagnostic_after
11525 .map(|index| index + 1)
11526 .unwrap_or(0),
11527 )
11528 .next(),
11529 };
11530
11531 // Cycle around to the start of the buffer, potentially moving back to the start of
11532 // the currently active diagnostic.
11533 let cycle_around = || match direction {
11534 Direction::Prev => primary_diagnostics_after
11535 .iter()
11536 .rev()
11537 .chain(primary_diagnostics_before.iter().rev())
11538 .next(),
11539 Direction::Next => primary_diagnostics_before
11540 .iter()
11541 .chain(primary_diagnostics_after.iter())
11542 .next(),
11543 };
11544
11545 if let Some((primary_range, group_id)) = next_primary_diagnostic
11546 .or_else(cycle_around)
11547 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11548 {
11549 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11550 return;
11551 };
11552 self.activate_diagnostics(buffer_id, group_id, window, cx);
11553 if self.active_diagnostics.is_some() {
11554 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11555 s.select(vec![Selection {
11556 id: selection.id,
11557 start: primary_range.start,
11558 end: primary_range.start,
11559 reversed: false,
11560 goal: SelectionGoal::None,
11561 }]);
11562 });
11563 self.refresh_inline_completion(false, true, window, cx);
11564 }
11565 }
11566 }
11567
11568 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11569 let snapshot = self.snapshot(window, cx);
11570 let selection = self.selections.newest::<Point>(cx);
11571 self.go_to_hunk_before_or_after_position(
11572 &snapshot,
11573 selection.head(),
11574 Direction::Next,
11575 window,
11576 cx,
11577 );
11578 }
11579
11580 fn go_to_hunk_before_or_after_position(
11581 &mut self,
11582 snapshot: &EditorSnapshot,
11583 position: Point,
11584 direction: Direction,
11585 window: &mut Window,
11586 cx: &mut Context<Editor>,
11587 ) {
11588 let row = if direction == Direction::Next {
11589 self.hunk_after_position(snapshot, position)
11590 .map(|hunk| hunk.row_range.start)
11591 } else {
11592 self.hunk_before_position(snapshot, position)
11593 };
11594
11595 if let Some(row) = row {
11596 let destination = Point::new(row.0, 0);
11597 let autoscroll = Autoscroll::center();
11598
11599 self.unfold_ranges(&[destination..destination], false, false, cx);
11600 self.change_selections(Some(autoscroll), window, cx, |s| {
11601 s.select_ranges([destination..destination]);
11602 });
11603 }
11604 }
11605
11606 fn hunk_after_position(
11607 &mut self,
11608 snapshot: &EditorSnapshot,
11609 position: Point,
11610 ) -> Option<MultiBufferDiffHunk> {
11611 snapshot
11612 .buffer_snapshot
11613 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11614 .find(|hunk| hunk.row_range.start.0 > position.row)
11615 .or_else(|| {
11616 snapshot
11617 .buffer_snapshot
11618 .diff_hunks_in_range(Point::zero()..position)
11619 .find(|hunk| hunk.row_range.end.0 < position.row)
11620 })
11621 }
11622
11623 fn go_to_prev_hunk(
11624 &mut self,
11625 _: &GoToPreviousHunk,
11626 window: &mut Window,
11627 cx: &mut Context<Self>,
11628 ) {
11629 let snapshot = self.snapshot(window, cx);
11630 let selection = self.selections.newest::<Point>(cx);
11631 self.go_to_hunk_before_or_after_position(
11632 &snapshot,
11633 selection.head(),
11634 Direction::Prev,
11635 window,
11636 cx,
11637 );
11638 }
11639
11640 fn hunk_before_position(
11641 &mut self,
11642 snapshot: &EditorSnapshot,
11643 position: Point,
11644 ) -> Option<MultiBufferRow> {
11645 snapshot
11646 .buffer_snapshot
11647 .diff_hunk_before(position)
11648 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
11649 }
11650
11651 pub fn go_to_definition(
11652 &mut self,
11653 _: &GoToDefinition,
11654 window: &mut Window,
11655 cx: &mut Context<Self>,
11656 ) -> Task<Result<Navigated>> {
11657 let definition =
11658 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11659 cx.spawn_in(window, |editor, mut cx| async move {
11660 if definition.await? == Navigated::Yes {
11661 return Ok(Navigated::Yes);
11662 }
11663 match editor.update_in(&mut cx, |editor, window, cx| {
11664 editor.find_all_references(&FindAllReferences, window, cx)
11665 })? {
11666 Some(references) => references.await,
11667 None => Ok(Navigated::No),
11668 }
11669 })
11670 }
11671
11672 pub fn go_to_declaration(
11673 &mut self,
11674 _: &GoToDeclaration,
11675 window: &mut Window,
11676 cx: &mut Context<Self>,
11677 ) -> Task<Result<Navigated>> {
11678 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11679 }
11680
11681 pub fn go_to_declaration_split(
11682 &mut self,
11683 _: &GoToDeclaration,
11684 window: &mut Window,
11685 cx: &mut Context<Self>,
11686 ) -> Task<Result<Navigated>> {
11687 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11688 }
11689
11690 pub fn go_to_implementation(
11691 &mut self,
11692 _: &GoToImplementation,
11693 window: &mut Window,
11694 cx: &mut Context<Self>,
11695 ) -> Task<Result<Navigated>> {
11696 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11697 }
11698
11699 pub fn go_to_implementation_split(
11700 &mut self,
11701 _: &GoToImplementationSplit,
11702 window: &mut Window,
11703 cx: &mut Context<Self>,
11704 ) -> Task<Result<Navigated>> {
11705 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11706 }
11707
11708 pub fn go_to_type_definition(
11709 &mut self,
11710 _: &GoToTypeDefinition,
11711 window: &mut Window,
11712 cx: &mut Context<Self>,
11713 ) -> Task<Result<Navigated>> {
11714 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11715 }
11716
11717 pub fn go_to_definition_split(
11718 &mut self,
11719 _: &GoToDefinitionSplit,
11720 window: &mut Window,
11721 cx: &mut Context<Self>,
11722 ) -> Task<Result<Navigated>> {
11723 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11724 }
11725
11726 pub fn go_to_type_definition_split(
11727 &mut self,
11728 _: &GoToTypeDefinitionSplit,
11729 window: &mut Window,
11730 cx: &mut Context<Self>,
11731 ) -> Task<Result<Navigated>> {
11732 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11733 }
11734
11735 fn go_to_definition_of_kind(
11736 &mut self,
11737 kind: GotoDefinitionKind,
11738 split: bool,
11739 window: &mut Window,
11740 cx: &mut Context<Self>,
11741 ) -> Task<Result<Navigated>> {
11742 let Some(provider) = self.semantics_provider.clone() else {
11743 return Task::ready(Ok(Navigated::No));
11744 };
11745 let head = self.selections.newest::<usize>(cx).head();
11746 let buffer = self.buffer.read(cx);
11747 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11748 text_anchor
11749 } else {
11750 return Task::ready(Ok(Navigated::No));
11751 };
11752
11753 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11754 return Task::ready(Ok(Navigated::No));
11755 };
11756
11757 cx.spawn_in(window, |editor, mut cx| async move {
11758 let definitions = definitions.await?;
11759 let navigated = editor
11760 .update_in(&mut cx, |editor, window, cx| {
11761 editor.navigate_to_hover_links(
11762 Some(kind),
11763 definitions
11764 .into_iter()
11765 .filter(|location| {
11766 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11767 })
11768 .map(HoverLink::Text)
11769 .collect::<Vec<_>>(),
11770 split,
11771 window,
11772 cx,
11773 )
11774 })?
11775 .await?;
11776 anyhow::Ok(navigated)
11777 })
11778 }
11779
11780 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11781 let selection = self.selections.newest_anchor();
11782 let head = selection.head();
11783 let tail = selection.tail();
11784
11785 let Some((buffer, start_position)) =
11786 self.buffer.read(cx).text_anchor_for_position(head, cx)
11787 else {
11788 return;
11789 };
11790
11791 let end_position = if head != tail {
11792 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11793 return;
11794 };
11795 Some(pos)
11796 } else {
11797 None
11798 };
11799
11800 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11801 let url = if let Some(end_pos) = end_position {
11802 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11803 } else {
11804 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11805 };
11806
11807 if let Some(url) = url {
11808 editor.update(&mut cx, |_, cx| {
11809 cx.open_url(&url);
11810 })
11811 } else {
11812 Ok(())
11813 }
11814 });
11815
11816 url_finder.detach();
11817 }
11818
11819 pub fn open_selected_filename(
11820 &mut self,
11821 _: &OpenSelectedFilename,
11822 window: &mut Window,
11823 cx: &mut Context<Self>,
11824 ) {
11825 let Some(workspace) = self.workspace() else {
11826 return;
11827 };
11828
11829 let position = self.selections.newest_anchor().head();
11830
11831 let Some((buffer, buffer_position)) =
11832 self.buffer.read(cx).text_anchor_for_position(position, cx)
11833 else {
11834 return;
11835 };
11836
11837 let project = self.project.clone();
11838
11839 cx.spawn_in(window, |_, mut cx| async move {
11840 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
11841
11842 if let Some((_, path)) = result {
11843 workspace
11844 .update_in(&mut cx, |workspace, window, cx| {
11845 workspace.open_resolved_path(path, window, cx)
11846 })?
11847 .await?;
11848 }
11849 anyhow::Ok(())
11850 })
11851 .detach();
11852 }
11853
11854 pub(crate) fn navigate_to_hover_links(
11855 &mut self,
11856 kind: Option<GotoDefinitionKind>,
11857 mut definitions: Vec<HoverLink>,
11858 split: bool,
11859 window: &mut Window,
11860 cx: &mut Context<Editor>,
11861 ) -> Task<Result<Navigated>> {
11862 // If there is one definition, just open it directly
11863 if definitions.len() == 1 {
11864 let definition = definitions.pop().unwrap();
11865
11866 enum TargetTaskResult {
11867 Location(Option<Location>),
11868 AlreadyNavigated,
11869 }
11870
11871 let target_task = match definition {
11872 HoverLink::Text(link) => {
11873 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11874 }
11875 HoverLink::InlayHint(lsp_location, server_id) => {
11876 let computation =
11877 self.compute_target_location(lsp_location, server_id, window, cx);
11878 cx.background_spawn(async move {
11879 let location = computation.await?;
11880 Ok(TargetTaskResult::Location(location))
11881 })
11882 }
11883 HoverLink::Url(url) => {
11884 cx.open_url(&url);
11885 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11886 }
11887 HoverLink::File(path) => {
11888 if let Some(workspace) = self.workspace() {
11889 cx.spawn_in(window, |_, mut cx| async move {
11890 workspace
11891 .update_in(&mut cx, |workspace, window, cx| {
11892 workspace.open_resolved_path(path, window, cx)
11893 })?
11894 .await
11895 .map(|_| TargetTaskResult::AlreadyNavigated)
11896 })
11897 } else {
11898 Task::ready(Ok(TargetTaskResult::Location(None)))
11899 }
11900 }
11901 };
11902 cx.spawn_in(window, |editor, mut cx| async move {
11903 let target = match target_task.await.context("target resolution task")? {
11904 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11905 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11906 TargetTaskResult::Location(Some(target)) => target,
11907 };
11908
11909 editor.update_in(&mut cx, |editor, window, cx| {
11910 let Some(workspace) = editor.workspace() else {
11911 return Navigated::No;
11912 };
11913 let pane = workspace.read(cx).active_pane().clone();
11914
11915 let range = target.range.to_point(target.buffer.read(cx));
11916 let range = editor.range_for_match(&range);
11917 let range = collapse_multiline_range(range);
11918
11919 if !split
11920 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
11921 {
11922 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11923 } else {
11924 window.defer(cx, move |window, cx| {
11925 let target_editor: Entity<Self> =
11926 workspace.update(cx, |workspace, cx| {
11927 let pane = if split {
11928 workspace.adjacent_pane(window, cx)
11929 } else {
11930 workspace.active_pane().clone()
11931 };
11932
11933 workspace.open_project_item(
11934 pane,
11935 target.buffer.clone(),
11936 true,
11937 true,
11938 window,
11939 cx,
11940 )
11941 });
11942 target_editor.update(cx, |target_editor, cx| {
11943 // When selecting a definition in a different buffer, disable the nav history
11944 // to avoid creating a history entry at the previous cursor location.
11945 pane.update(cx, |pane, _| pane.disable_history());
11946 target_editor.go_to_singleton_buffer_range(range, window, cx);
11947 pane.update(cx, |pane, _| pane.enable_history());
11948 });
11949 });
11950 }
11951 Navigated::Yes
11952 })
11953 })
11954 } else if !definitions.is_empty() {
11955 cx.spawn_in(window, |editor, mut cx| async move {
11956 let (title, location_tasks, workspace) = editor
11957 .update_in(&mut cx, |editor, window, cx| {
11958 let tab_kind = match kind {
11959 Some(GotoDefinitionKind::Implementation) => "Implementations",
11960 _ => "Definitions",
11961 };
11962 let title = definitions
11963 .iter()
11964 .find_map(|definition| match definition {
11965 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11966 let buffer = origin.buffer.read(cx);
11967 format!(
11968 "{} for {}",
11969 tab_kind,
11970 buffer
11971 .text_for_range(origin.range.clone())
11972 .collect::<String>()
11973 )
11974 }),
11975 HoverLink::InlayHint(_, _) => None,
11976 HoverLink::Url(_) => None,
11977 HoverLink::File(_) => None,
11978 })
11979 .unwrap_or(tab_kind.to_string());
11980 let location_tasks = definitions
11981 .into_iter()
11982 .map(|definition| match definition {
11983 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11984 HoverLink::InlayHint(lsp_location, server_id) => editor
11985 .compute_target_location(lsp_location, server_id, window, cx),
11986 HoverLink::Url(_) => Task::ready(Ok(None)),
11987 HoverLink::File(_) => Task::ready(Ok(None)),
11988 })
11989 .collect::<Vec<_>>();
11990 (title, location_tasks, editor.workspace().clone())
11991 })
11992 .context("location tasks preparation")?;
11993
11994 let locations = future::join_all(location_tasks)
11995 .await
11996 .into_iter()
11997 .filter_map(|location| location.transpose())
11998 .collect::<Result<_>>()
11999 .context("location tasks")?;
12000
12001 let Some(workspace) = workspace else {
12002 return Ok(Navigated::No);
12003 };
12004 let opened = workspace
12005 .update_in(&mut cx, |workspace, window, cx| {
12006 Self::open_locations_in_multibuffer(
12007 workspace,
12008 locations,
12009 title,
12010 split,
12011 MultibufferSelectionMode::First,
12012 window,
12013 cx,
12014 )
12015 })
12016 .ok();
12017
12018 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12019 })
12020 } else {
12021 Task::ready(Ok(Navigated::No))
12022 }
12023 }
12024
12025 fn compute_target_location(
12026 &self,
12027 lsp_location: lsp::Location,
12028 server_id: LanguageServerId,
12029 window: &mut Window,
12030 cx: &mut Context<Self>,
12031 ) -> Task<anyhow::Result<Option<Location>>> {
12032 let Some(project) = self.project.clone() else {
12033 return Task::ready(Ok(None));
12034 };
12035
12036 cx.spawn_in(window, move |editor, mut cx| async move {
12037 let location_task = editor.update(&mut cx, |_, cx| {
12038 project.update(cx, |project, cx| {
12039 let language_server_name = project
12040 .language_server_statuses(cx)
12041 .find(|(id, _)| server_id == *id)
12042 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12043 language_server_name.map(|language_server_name| {
12044 project.open_local_buffer_via_lsp(
12045 lsp_location.uri.clone(),
12046 server_id,
12047 language_server_name,
12048 cx,
12049 )
12050 })
12051 })
12052 })?;
12053 let location = match location_task {
12054 Some(task) => Some({
12055 let target_buffer_handle = task.await.context("open local buffer")?;
12056 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12057 let target_start = target_buffer
12058 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12059 let target_end = target_buffer
12060 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12061 target_buffer.anchor_after(target_start)
12062 ..target_buffer.anchor_before(target_end)
12063 })?;
12064 Location {
12065 buffer: target_buffer_handle,
12066 range,
12067 }
12068 }),
12069 None => None,
12070 };
12071 Ok(location)
12072 })
12073 }
12074
12075 pub fn find_all_references(
12076 &mut self,
12077 _: &FindAllReferences,
12078 window: &mut Window,
12079 cx: &mut Context<Self>,
12080 ) -> Option<Task<Result<Navigated>>> {
12081 let selection = self.selections.newest::<usize>(cx);
12082 let multi_buffer = self.buffer.read(cx);
12083 let head = selection.head();
12084
12085 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12086 let head_anchor = multi_buffer_snapshot.anchor_at(
12087 head,
12088 if head < selection.tail() {
12089 Bias::Right
12090 } else {
12091 Bias::Left
12092 },
12093 );
12094
12095 match self
12096 .find_all_references_task_sources
12097 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12098 {
12099 Ok(_) => {
12100 log::info!(
12101 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12102 );
12103 return None;
12104 }
12105 Err(i) => {
12106 self.find_all_references_task_sources.insert(i, head_anchor);
12107 }
12108 }
12109
12110 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12111 let workspace = self.workspace()?;
12112 let project = workspace.read(cx).project().clone();
12113 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12114 Some(cx.spawn_in(window, |editor, mut cx| async move {
12115 let _cleanup = defer({
12116 let mut cx = cx.clone();
12117 move || {
12118 let _ = editor.update(&mut cx, |editor, _| {
12119 if let Ok(i) =
12120 editor
12121 .find_all_references_task_sources
12122 .binary_search_by(|anchor| {
12123 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12124 })
12125 {
12126 editor.find_all_references_task_sources.remove(i);
12127 }
12128 });
12129 }
12130 });
12131
12132 let locations = references.await?;
12133 if locations.is_empty() {
12134 return anyhow::Ok(Navigated::No);
12135 }
12136
12137 workspace.update_in(&mut cx, |workspace, window, cx| {
12138 let title = locations
12139 .first()
12140 .as_ref()
12141 .map(|location| {
12142 let buffer = location.buffer.read(cx);
12143 format!(
12144 "References to `{}`",
12145 buffer
12146 .text_for_range(location.range.clone())
12147 .collect::<String>()
12148 )
12149 })
12150 .unwrap();
12151 Self::open_locations_in_multibuffer(
12152 workspace,
12153 locations,
12154 title,
12155 false,
12156 MultibufferSelectionMode::First,
12157 window,
12158 cx,
12159 );
12160 Navigated::Yes
12161 })
12162 }))
12163 }
12164
12165 /// Opens a multibuffer with the given project locations in it
12166 pub fn open_locations_in_multibuffer(
12167 workspace: &mut Workspace,
12168 mut locations: Vec<Location>,
12169 title: String,
12170 split: bool,
12171 multibuffer_selection_mode: MultibufferSelectionMode,
12172 window: &mut Window,
12173 cx: &mut Context<Workspace>,
12174 ) {
12175 // If there are multiple definitions, open them in a multibuffer
12176 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12177 let mut locations = locations.into_iter().peekable();
12178 let mut ranges = Vec::new();
12179 let capability = workspace.project().read(cx).capability();
12180
12181 let excerpt_buffer = cx.new(|cx| {
12182 let mut multibuffer = MultiBuffer::new(capability);
12183 while let Some(location) = locations.next() {
12184 let buffer = location.buffer.read(cx);
12185 let mut ranges_for_buffer = Vec::new();
12186 let range = location.range.to_offset(buffer);
12187 ranges_for_buffer.push(range.clone());
12188
12189 while let Some(next_location) = locations.peek() {
12190 if next_location.buffer == location.buffer {
12191 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12192 locations.next();
12193 } else {
12194 break;
12195 }
12196 }
12197
12198 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12199 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12200 location.buffer.clone(),
12201 ranges_for_buffer,
12202 DEFAULT_MULTIBUFFER_CONTEXT,
12203 cx,
12204 ))
12205 }
12206
12207 multibuffer.with_title(title)
12208 });
12209
12210 let editor = cx.new(|cx| {
12211 Editor::for_multibuffer(
12212 excerpt_buffer,
12213 Some(workspace.project().clone()),
12214 true,
12215 window,
12216 cx,
12217 )
12218 });
12219 editor.update(cx, |editor, cx| {
12220 match multibuffer_selection_mode {
12221 MultibufferSelectionMode::First => {
12222 if let Some(first_range) = ranges.first() {
12223 editor.change_selections(None, window, cx, |selections| {
12224 selections.clear_disjoint();
12225 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12226 });
12227 }
12228 editor.highlight_background::<Self>(
12229 &ranges,
12230 |theme| theme.editor_highlighted_line_background,
12231 cx,
12232 );
12233 }
12234 MultibufferSelectionMode::All => {
12235 editor.change_selections(None, window, cx, |selections| {
12236 selections.clear_disjoint();
12237 selections.select_anchor_ranges(ranges);
12238 });
12239 }
12240 }
12241 editor.register_buffers_with_language_servers(cx);
12242 });
12243
12244 let item = Box::new(editor);
12245 let item_id = item.item_id();
12246
12247 if split {
12248 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12249 } else {
12250 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12251 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12252 pane.close_current_preview_item(window, cx)
12253 } else {
12254 None
12255 }
12256 });
12257 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12258 }
12259 workspace.active_pane().update(cx, |pane, cx| {
12260 pane.set_preview_item_id(Some(item_id), cx);
12261 });
12262 }
12263
12264 pub fn rename(
12265 &mut self,
12266 _: &Rename,
12267 window: &mut Window,
12268 cx: &mut Context<Self>,
12269 ) -> Option<Task<Result<()>>> {
12270 use language::ToOffset as _;
12271
12272 let provider = self.semantics_provider.clone()?;
12273 let selection = self.selections.newest_anchor().clone();
12274 let (cursor_buffer, cursor_buffer_position) = self
12275 .buffer
12276 .read(cx)
12277 .text_anchor_for_position(selection.head(), cx)?;
12278 let (tail_buffer, cursor_buffer_position_end) = self
12279 .buffer
12280 .read(cx)
12281 .text_anchor_for_position(selection.tail(), cx)?;
12282 if tail_buffer != cursor_buffer {
12283 return None;
12284 }
12285
12286 let snapshot = cursor_buffer.read(cx).snapshot();
12287 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12288 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12289 let prepare_rename = provider
12290 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12291 .unwrap_or_else(|| Task::ready(Ok(None)));
12292 drop(snapshot);
12293
12294 Some(cx.spawn_in(window, |this, mut cx| async move {
12295 let rename_range = if let Some(range) = prepare_rename.await? {
12296 Some(range)
12297 } else {
12298 this.update(&mut cx, |this, cx| {
12299 let buffer = this.buffer.read(cx).snapshot(cx);
12300 let mut buffer_highlights = this
12301 .document_highlights_for_position(selection.head(), &buffer)
12302 .filter(|highlight| {
12303 highlight.start.excerpt_id == selection.head().excerpt_id
12304 && highlight.end.excerpt_id == selection.head().excerpt_id
12305 });
12306 buffer_highlights
12307 .next()
12308 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12309 })?
12310 };
12311 if let Some(rename_range) = rename_range {
12312 this.update_in(&mut cx, |this, window, cx| {
12313 let snapshot = cursor_buffer.read(cx).snapshot();
12314 let rename_buffer_range = rename_range.to_offset(&snapshot);
12315 let cursor_offset_in_rename_range =
12316 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12317 let cursor_offset_in_rename_range_end =
12318 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12319
12320 this.take_rename(false, window, cx);
12321 let buffer = this.buffer.read(cx).read(cx);
12322 let cursor_offset = selection.head().to_offset(&buffer);
12323 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12324 let rename_end = rename_start + rename_buffer_range.len();
12325 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12326 let mut old_highlight_id = None;
12327 let old_name: Arc<str> = buffer
12328 .chunks(rename_start..rename_end, true)
12329 .map(|chunk| {
12330 if old_highlight_id.is_none() {
12331 old_highlight_id = chunk.syntax_highlight_id;
12332 }
12333 chunk.text
12334 })
12335 .collect::<String>()
12336 .into();
12337
12338 drop(buffer);
12339
12340 // Position the selection in the rename editor so that it matches the current selection.
12341 this.show_local_selections = false;
12342 let rename_editor = cx.new(|cx| {
12343 let mut editor = Editor::single_line(window, cx);
12344 editor.buffer.update(cx, |buffer, cx| {
12345 buffer.edit([(0..0, old_name.clone())], None, cx)
12346 });
12347 let rename_selection_range = match cursor_offset_in_rename_range
12348 .cmp(&cursor_offset_in_rename_range_end)
12349 {
12350 Ordering::Equal => {
12351 editor.select_all(&SelectAll, window, cx);
12352 return editor;
12353 }
12354 Ordering::Less => {
12355 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12356 }
12357 Ordering::Greater => {
12358 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12359 }
12360 };
12361 if rename_selection_range.end > old_name.len() {
12362 editor.select_all(&SelectAll, window, cx);
12363 } else {
12364 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12365 s.select_ranges([rename_selection_range]);
12366 });
12367 }
12368 editor
12369 });
12370 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12371 if e == &EditorEvent::Focused {
12372 cx.emit(EditorEvent::FocusedIn)
12373 }
12374 })
12375 .detach();
12376
12377 let write_highlights =
12378 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12379 let read_highlights =
12380 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12381 let ranges = write_highlights
12382 .iter()
12383 .flat_map(|(_, ranges)| ranges.iter())
12384 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12385 .cloned()
12386 .collect();
12387
12388 this.highlight_text::<Rename>(
12389 ranges,
12390 HighlightStyle {
12391 fade_out: Some(0.6),
12392 ..Default::default()
12393 },
12394 cx,
12395 );
12396 let rename_focus_handle = rename_editor.focus_handle(cx);
12397 window.focus(&rename_focus_handle);
12398 let block_id = this.insert_blocks(
12399 [BlockProperties {
12400 style: BlockStyle::Flex,
12401 placement: BlockPlacement::Below(range.start),
12402 height: 1,
12403 render: Arc::new({
12404 let rename_editor = rename_editor.clone();
12405 move |cx: &mut BlockContext| {
12406 let mut text_style = cx.editor_style.text.clone();
12407 if let Some(highlight_style) = old_highlight_id
12408 .and_then(|h| h.style(&cx.editor_style.syntax))
12409 {
12410 text_style = text_style.highlight(highlight_style);
12411 }
12412 div()
12413 .block_mouse_down()
12414 .pl(cx.anchor_x)
12415 .child(EditorElement::new(
12416 &rename_editor,
12417 EditorStyle {
12418 background: cx.theme().system().transparent,
12419 local_player: cx.editor_style.local_player,
12420 text: text_style,
12421 scrollbar_width: cx.editor_style.scrollbar_width,
12422 syntax: cx.editor_style.syntax.clone(),
12423 status: cx.editor_style.status.clone(),
12424 inlay_hints_style: HighlightStyle {
12425 font_weight: Some(FontWeight::BOLD),
12426 ..make_inlay_hints_style(cx.app)
12427 },
12428 inline_completion_styles: make_suggestion_styles(
12429 cx.app,
12430 ),
12431 ..EditorStyle::default()
12432 },
12433 ))
12434 .into_any_element()
12435 }
12436 }),
12437 priority: 0,
12438 }],
12439 Some(Autoscroll::fit()),
12440 cx,
12441 )[0];
12442 this.pending_rename = Some(RenameState {
12443 range,
12444 old_name,
12445 editor: rename_editor,
12446 block_id,
12447 });
12448 })?;
12449 }
12450
12451 Ok(())
12452 }))
12453 }
12454
12455 pub fn confirm_rename(
12456 &mut self,
12457 _: &ConfirmRename,
12458 window: &mut Window,
12459 cx: &mut Context<Self>,
12460 ) -> Option<Task<Result<()>>> {
12461 let rename = self.take_rename(false, window, cx)?;
12462 let workspace = self.workspace()?.downgrade();
12463 let (buffer, start) = self
12464 .buffer
12465 .read(cx)
12466 .text_anchor_for_position(rename.range.start, cx)?;
12467 let (end_buffer, _) = self
12468 .buffer
12469 .read(cx)
12470 .text_anchor_for_position(rename.range.end, cx)?;
12471 if buffer != end_buffer {
12472 return None;
12473 }
12474
12475 let old_name = rename.old_name;
12476 let new_name = rename.editor.read(cx).text(cx);
12477
12478 let rename = self.semantics_provider.as_ref()?.perform_rename(
12479 &buffer,
12480 start,
12481 new_name.clone(),
12482 cx,
12483 )?;
12484
12485 Some(cx.spawn_in(window, |editor, mut cx| async move {
12486 let project_transaction = rename.await?;
12487 Self::open_project_transaction(
12488 &editor,
12489 workspace,
12490 project_transaction,
12491 format!("Rename: {} → {}", old_name, new_name),
12492 cx.clone(),
12493 )
12494 .await?;
12495
12496 editor.update(&mut cx, |editor, cx| {
12497 editor.refresh_document_highlights(cx);
12498 })?;
12499 Ok(())
12500 }))
12501 }
12502
12503 fn take_rename(
12504 &mut self,
12505 moving_cursor: bool,
12506 window: &mut Window,
12507 cx: &mut Context<Self>,
12508 ) -> Option<RenameState> {
12509 let rename = self.pending_rename.take()?;
12510 if rename.editor.focus_handle(cx).is_focused(window) {
12511 window.focus(&self.focus_handle);
12512 }
12513
12514 self.remove_blocks(
12515 [rename.block_id].into_iter().collect(),
12516 Some(Autoscroll::fit()),
12517 cx,
12518 );
12519 self.clear_highlights::<Rename>(cx);
12520 self.show_local_selections = true;
12521
12522 if moving_cursor {
12523 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12524 editor.selections.newest::<usize>(cx).head()
12525 });
12526
12527 // Update the selection to match the position of the selection inside
12528 // the rename editor.
12529 let snapshot = self.buffer.read(cx).read(cx);
12530 let rename_range = rename.range.to_offset(&snapshot);
12531 let cursor_in_editor = snapshot
12532 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12533 .min(rename_range.end);
12534 drop(snapshot);
12535
12536 self.change_selections(None, window, cx, |s| {
12537 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12538 });
12539 } else {
12540 self.refresh_document_highlights(cx);
12541 }
12542
12543 Some(rename)
12544 }
12545
12546 pub fn pending_rename(&self) -> Option<&RenameState> {
12547 self.pending_rename.as_ref()
12548 }
12549
12550 fn format(
12551 &mut self,
12552 _: &Format,
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 Some(self.perform_format(
12562 project,
12563 FormatTrigger::Manual,
12564 FormatTarget::Buffers,
12565 window,
12566 cx,
12567 ))
12568 }
12569
12570 fn format_selections(
12571 &mut self,
12572 _: &FormatSelections,
12573 window: &mut Window,
12574 cx: &mut Context<Self>,
12575 ) -> Option<Task<Result<()>>> {
12576 let project = match &self.project {
12577 Some(project) => project.clone(),
12578 None => return None,
12579 };
12580
12581 let ranges = self
12582 .selections
12583 .all_adjusted(cx)
12584 .into_iter()
12585 .map(|selection| selection.range())
12586 .collect_vec();
12587
12588 Some(self.perform_format(
12589 project,
12590 FormatTrigger::Manual,
12591 FormatTarget::Ranges(ranges),
12592 window,
12593 cx,
12594 ))
12595 }
12596
12597 fn perform_format(
12598 &mut self,
12599 project: Entity<Project>,
12600 trigger: FormatTrigger,
12601 target: FormatTarget,
12602 window: &mut Window,
12603 cx: &mut Context<Self>,
12604 ) -> Task<Result<()>> {
12605 let buffer = self.buffer.clone();
12606 let (buffers, target) = match target {
12607 FormatTarget::Buffers => {
12608 let mut buffers = buffer.read(cx).all_buffers();
12609 if trigger == FormatTrigger::Save {
12610 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12611 }
12612 (buffers, LspFormatTarget::Buffers)
12613 }
12614 FormatTarget::Ranges(selection_ranges) => {
12615 let multi_buffer = buffer.read(cx);
12616 let snapshot = multi_buffer.read(cx);
12617 let mut buffers = HashSet::default();
12618 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12619 BTreeMap::new();
12620 for selection_range in selection_ranges {
12621 for (buffer, buffer_range, _) in
12622 snapshot.range_to_buffer_ranges(selection_range)
12623 {
12624 let buffer_id = buffer.remote_id();
12625 let start = buffer.anchor_before(buffer_range.start);
12626 let end = buffer.anchor_after(buffer_range.end);
12627 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12628 buffer_id_to_ranges
12629 .entry(buffer_id)
12630 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12631 .or_insert_with(|| vec![start..end]);
12632 }
12633 }
12634 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12635 }
12636 };
12637
12638 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12639 let format = project.update(cx, |project, cx| {
12640 project.format(buffers, target, true, trigger, cx)
12641 });
12642
12643 cx.spawn_in(window, |_, mut cx| async move {
12644 let transaction = futures::select_biased! {
12645 () = timeout => {
12646 log::warn!("timed out waiting for formatting");
12647 None
12648 }
12649 transaction = format.log_err().fuse() => transaction,
12650 };
12651
12652 buffer
12653 .update(&mut cx, |buffer, cx| {
12654 if let Some(transaction) = transaction {
12655 if !buffer.is_singleton() {
12656 buffer.push_transaction(&transaction.0, cx);
12657 }
12658 }
12659 cx.notify();
12660 })
12661 .ok();
12662
12663 Ok(())
12664 })
12665 }
12666
12667 fn organize_imports(
12668 &mut self,
12669 _: &OrganizeImports,
12670 window: &mut Window,
12671 cx: &mut Context<Self>,
12672 ) -> Option<Task<Result<()>>> {
12673 let project = match &self.project {
12674 Some(project) => project.clone(),
12675 None => return None,
12676 };
12677 Some(self.perform_code_action_kind(
12678 project,
12679 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
12680 window,
12681 cx,
12682 ))
12683 }
12684
12685 fn perform_code_action_kind(
12686 &mut self,
12687 project: Entity<Project>,
12688 kind: CodeActionKind,
12689 window: &mut Window,
12690 cx: &mut Context<Self>,
12691 ) -> Task<Result<()>> {
12692 let buffer = self.buffer.clone();
12693 let buffers = buffer.read(cx).all_buffers();
12694 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
12695 let apply_action = project.update(cx, |project, cx| {
12696 project.apply_code_action_kind(buffers, kind, true, cx)
12697 });
12698 cx.spawn_in(window, |_, mut cx| async move {
12699 let transaction = futures::select_biased! {
12700 () = timeout => {
12701 log::warn!("timed out waiting for executing code action");
12702 None
12703 }
12704 transaction = apply_action.log_err().fuse() => transaction,
12705 };
12706 buffer
12707 .update(&mut cx, |buffer, cx| {
12708 // check if we need this
12709 if let Some(transaction) = transaction {
12710 if !buffer.is_singleton() {
12711 buffer.push_transaction(&transaction.0, cx);
12712 }
12713 }
12714 cx.notify();
12715 })
12716 .ok();
12717 Ok(())
12718 })
12719 }
12720
12721 fn restart_language_server(
12722 &mut self,
12723 _: &RestartLanguageServer,
12724 _: &mut Window,
12725 cx: &mut Context<Self>,
12726 ) {
12727 if let Some(project) = self.project.clone() {
12728 self.buffer.update(cx, |multi_buffer, cx| {
12729 project.update(cx, |project, cx| {
12730 project.restart_language_servers_for_buffers(
12731 multi_buffer.all_buffers().into_iter().collect(),
12732 cx,
12733 );
12734 });
12735 })
12736 }
12737 }
12738
12739 fn cancel_language_server_work(
12740 workspace: &mut Workspace,
12741 _: &actions::CancelLanguageServerWork,
12742 _: &mut Window,
12743 cx: &mut Context<Workspace>,
12744 ) {
12745 let project = workspace.project();
12746 let buffers = workspace
12747 .active_item(cx)
12748 .and_then(|item| item.act_as::<Editor>(cx))
12749 .map_or(HashSet::default(), |editor| {
12750 editor.read(cx).buffer.read(cx).all_buffers()
12751 });
12752 project.update(cx, |project, cx| {
12753 project.cancel_language_server_work_for_buffers(buffers, cx);
12754 });
12755 }
12756
12757 fn show_character_palette(
12758 &mut self,
12759 _: &ShowCharacterPalette,
12760 window: &mut Window,
12761 _: &mut Context<Self>,
12762 ) {
12763 window.show_character_palette();
12764 }
12765
12766 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12767 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12768 let buffer = self.buffer.read(cx).snapshot(cx);
12769 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12770 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12771 let is_valid = buffer
12772 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12773 .any(|entry| {
12774 entry.diagnostic.is_primary
12775 && !entry.range.is_empty()
12776 && entry.range.start == primary_range_start
12777 && entry.diagnostic.message == active_diagnostics.primary_message
12778 });
12779
12780 if is_valid != active_diagnostics.is_valid {
12781 active_diagnostics.is_valid = is_valid;
12782 if is_valid {
12783 let mut new_styles = HashMap::default();
12784 for (block_id, diagnostic) in &active_diagnostics.blocks {
12785 new_styles.insert(
12786 *block_id,
12787 diagnostic_block_renderer(diagnostic.clone(), None, true),
12788 );
12789 }
12790 self.display_map.update(cx, |display_map, _cx| {
12791 display_map.replace_blocks(new_styles);
12792 });
12793 } else {
12794 self.dismiss_diagnostics(cx);
12795 }
12796 }
12797 }
12798 }
12799
12800 fn activate_diagnostics(
12801 &mut self,
12802 buffer_id: BufferId,
12803 group_id: usize,
12804 window: &mut Window,
12805 cx: &mut Context<Self>,
12806 ) {
12807 self.dismiss_diagnostics(cx);
12808 let snapshot = self.snapshot(window, cx);
12809 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
12810 let buffer = self.buffer.read(cx).snapshot(cx);
12811
12812 let mut primary_range = None;
12813 let mut primary_message = None;
12814 let diagnostic_group = buffer
12815 .diagnostic_group(buffer_id, group_id)
12816 .filter_map(|entry| {
12817 let start = entry.range.start;
12818 let end = entry.range.end;
12819 if snapshot.is_line_folded(MultiBufferRow(start.row))
12820 && (start.row == end.row
12821 || snapshot.is_line_folded(MultiBufferRow(end.row)))
12822 {
12823 return None;
12824 }
12825 if entry.diagnostic.is_primary {
12826 primary_range = Some(entry.range.clone());
12827 primary_message = Some(entry.diagnostic.message.clone());
12828 }
12829 Some(entry)
12830 })
12831 .collect::<Vec<_>>();
12832 let primary_range = primary_range?;
12833 let primary_message = primary_message?;
12834
12835 let blocks = display_map
12836 .insert_blocks(
12837 diagnostic_group.iter().map(|entry| {
12838 let diagnostic = entry.diagnostic.clone();
12839 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
12840 BlockProperties {
12841 style: BlockStyle::Fixed,
12842 placement: BlockPlacement::Below(
12843 buffer.anchor_after(entry.range.start),
12844 ),
12845 height: message_height,
12846 render: diagnostic_block_renderer(diagnostic, None, true),
12847 priority: 0,
12848 }
12849 }),
12850 cx,
12851 )
12852 .into_iter()
12853 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
12854 .collect();
12855
12856 Some(ActiveDiagnosticGroup {
12857 primary_range: buffer.anchor_before(primary_range.start)
12858 ..buffer.anchor_after(primary_range.end),
12859 primary_message,
12860 group_id,
12861 blocks,
12862 is_valid: true,
12863 })
12864 });
12865 }
12866
12867 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
12868 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
12869 self.display_map.update(cx, |display_map, cx| {
12870 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
12871 });
12872 cx.notify();
12873 }
12874 }
12875
12876 /// Disable inline diagnostics rendering for this editor.
12877 pub fn disable_inline_diagnostics(&mut self) {
12878 self.inline_diagnostics_enabled = false;
12879 self.inline_diagnostics_update = Task::ready(());
12880 self.inline_diagnostics.clear();
12881 }
12882
12883 pub fn inline_diagnostics_enabled(&self) -> bool {
12884 self.inline_diagnostics_enabled
12885 }
12886
12887 pub fn show_inline_diagnostics(&self) -> bool {
12888 self.show_inline_diagnostics
12889 }
12890
12891 pub fn toggle_inline_diagnostics(
12892 &mut self,
12893 _: &ToggleInlineDiagnostics,
12894 window: &mut Window,
12895 cx: &mut Context<'_, Editor>,
12896 ) {
12897 self.show_inline_diagnostics = !self.show_inline_diagnostics;
12898 self.refresh_inline_diagnostics(false, window, cx);
12899 }
12900
12901 fn refresh_inline_diagnostics(
12902 &mut self,
12903 debounce: bool,
12904 window: &mut Window,
12905 cx: &mut Context<Self>,
12906 ) {
12907 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12908 self.inline_diagnostics_update = Task::ready(());
12909 self.inline_diagnostics.clear();
12910 return;
12911 }
12912
12913 let debounce_ms = ProjectSettings::get_global(cx)
12914 .diagnostics
12915 .inline
12916 .update_debounce_ms;
12917 let debounce = if debounce && debounce_ms > 0 {
12918 Some(Duration::from_millis(debounce_ms))
12919 } else {
12920 None
12921 };
12922 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12923 if let Some(debounce) = debounce {
12924 cx.background_executor().timer(debounce).await;
12925 }
12926 let Some(snapshot) = editor
12927 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12928 .ok()
12929 else {
12930 return;
12931 };
12932
12933 let new_inline_diagnostics = cx
12934 .background_spawn(async move {
12935 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12936 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12937 let message = diagnostic_entry
12938 .diagnostic
12939 .message
12940 .split_once('\n')
12941 .map(|(line, _)| line)
12942 .map(SharedString::new)
12943 .unwrap_or_else(|| {
12944 SharedString::from(diagnostic_entry.diagnostic.message)
12945 });
12946 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12947 let (Ok(i) | Err(i)) = inline_diagnostics
12948 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12949 inline_diagnostics.insert(
12950 i,
12951 (
12952 start_anchor,
12953 InlineDiagnostic {
12954 message,
12955 group_id: diagnostic_entry.diagnostic.group_id,
12956 start: diagnostic_entry.range.start.to_point(&snapshot),
12957 is_primary: diagnostic_entry.diagnostic.is_primary,
12958 severity: diagnostic_entry.diagnostic.severity,
12959 },
12960 ),
12961 );
12962 }
12963 inline_diagnostics
12964 })
12965 .await;
12966
12967 editor
12968 .update(&mut cx, |editor, cx| {
12969 editor.inline_diagnostics = new_inline_diagnostics;
12970 cx.notify();
12971 })
12972 .ok();
12973 });
12974 }
12975
12976 pub fn set_selections_from_remote(
12977 &mut self,
12978 selections: Vec<Selection<Anchor>>,
12979 pending_selection: Option<Selection<Anchor>>,
12980 window: &mut Window,
12981 cx: &mut Context<Self>,
12982 ) {
12983 let old_cursor_position = self.selections.newest_anchor().head();
12984 self.selections.change_with(cx, |s| {
12985 s.select_anchors(selections);
12986 if let Some(pending_selection) = pending_selection {
12987 s.set_pending(pending_selection, SelectMode::Character);
12988 } else {
12989 s.clear_pending();
12990 }
12991 });
12992 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12993 }
12994
12995 fn push_to_selection_history(&mut self) {
12996 self.selection_history.push(SelectionHistoryEntry {
12997 selections: self.selections.disjoint_anchors(),
12998 select_next_state: self.select_next_state.clone(),
12999 select_prev_state: self.select_prev_state.clone(),
13000 add_selections_state: self.add_selections_state.clone(),
13001 });
13002 }
13003
13004 pub fn transact(
13005 &mut self,
13006 window: &mut Window,
13007 cx: &mut Context<Self>,
13008 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13009 ) -> Option<TransactionId> {
13010 self.start_transaction_at(Instant::now(), window, cx);
13011 update(self, window, cx);
13012 self.end_transaction_at(Instant::now(), cx)
13013 }
13014
13015 pub fn start_transaction_at(
13016 &mut self,
13017 now: Instant,
13018 window: &mut Window,
13019 cx: &mut Context<Self>,
13020 ) {
13021 self.end_selection(window, cx);
13022 if let Some(tx_id) = self
13023 .buffer
13024 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13025 {
13026 self.selection_history
13027 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13028 cx.emit(EditorEvent::TransactionBegun {
13029 transaction_id: tx_id,
13030 })
13031 }
13032 }
13033
13034 pub fn end_transaction_at(
13035 &mut self,
13036 now: Instant,
13037 cx: &mut Context<Self>,
13038 ) -> Option<TransactionId> {
13039 if let Some(transaction_id) = self
13040 .buffer
13041 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13042 {
13043 if let Some((_, end_selections)) =
13044 self.selection_history.transaction_mut(transaction_id)
13045 {
13046 *end_selections = Some(self.selections.disjoint_anchors());
13047 } else {
13048 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13049 }
13050
13051 cx.emit(EditorEvent::Edited { transaction_id });
13052 Some(transaction_id)
13053 } else {
13054 None
13055 }
13056 }
13057
13058 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13059 if self.selection_mark_mode {
13060 self.change_selections(None, window, cx, |s| {
13061 s.move_with(|_, sel| {
13062 sel.collapse_to(sel.head(), SelectionGoal::None);
13063 });
13064 })
13065 }
13066 self.selection_mark_mode = true;
13067 cx.notify();
13068 }
13069
13070 pub fn swap_selection_ends(
13071 &mut self,
13072 _: &actions::SwapSelectionEnds,
13073 window: &mut Window,
13074 cx: &mut Context<Self>,
13075 ) {
13076 self.change_selections(None, window, cx, |s| {
13077 s.move_with(|_, sel| {
13078 if sel.start != sel.end {
13079 sel.reversed = !sel.reversed
13080 }
13081 });
13082 });
13083 self.request_autoscroll(Autoscroll::newest(), cx);
13084 cx.notify();
13085 }
13086
13087 pub fn toggle_fold(
13088 &mut self,
13089 _: &actions::ToggleFold,
13090 window: &mut Window,
13091 cx: &mut Context<Self>,
13092 ) {
13093 if self.is_singleton(cx) {
13094 let selection = self.selections.newest::<Point>(cx);
13095
13096 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13097 let range = if selection.is_empty() {
13098 let point = selection.head().to_display_point(&display_map);
13099 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13100 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13101 .to_point(&display_map);
13102 start..end
13103 } else {
13104 selection.range()
13105 };
13106 if display_map.folds_in_range(range).next().is_some() {
13107 self.unfold_lines(&Default::default(), window, cx)
13108 } else {
13109 self.fold(&Default::default(), window, cx)
13110 }
13111 } else {
13112 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13113 let buffer_ids: HashSet<_> = self
13114 .selections
13115 .disjoint_anchor_ranges()
13116 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13117 .collect();
13118
13119 let should_unfold = buffer_ids
13120 .iter()
13121 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13122
13123 for buffer_id in buffer_ids {
13124 if should_unfold {
13125 self.unfold_buffer(buffer_id, cx);
13126 } else {
13127 self.fold_buffer(buffer_id, cx);
13128 }
13129 }
13130 }
13131 }
13132
13133 pub fn toggle_fold_recursive(
13134 &mut self,
13135 _: &actions::ToggleFoldRecursive,
13136 window: &mut Window,
13137 cx: &mut Context<Self>,
13138 ) {
13139 let selection = self.selections.newest::<Point>(cx);
13140
13141 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13142 let range = if selection.is_empty() {
13143 let point = selection.head().to_display_point(&display_map);
13144 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13145 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13146 .to_point(&display_map);
13147 start..end
13148 } else {
13149 selection.range()
13150 };
13151 if display_map.folds_in_range(range).next().is_some() {
13152 self.unfold_recursive(&Default::default(), window, cx)
13153 } else {
13154 self.fold_recursive(&Default::default(), window, cx)
13155 }
13156 }
13157
13158 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13159 if self.is_singleton(cx) {
13160 let mut to_fold = Vec::new();
13161 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13162 let selections = self.selections.all_adjusted(cx);
13163
13164 for selection in selections {
13165 let range = selection.range().sorted();
13166 let buffer_start_row = range.start.row;
13167
13168 if range.start.row != range.end.row {
13169 let mut found = false;
13170 let mut row = range.start.row;
13171 while row <= range.end.row {
13172 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13173 {
13174 found = true;
13175 row = crease.range().end.row + 1;
13176 to_fold.push(crease);
13177 } else {
13178 row += 1
13179 }
13180 }
13181 if found {
13182 continue;
13183 }
13184 }
13185
13186 for row in (0..=range.start.row).rev() {
13187 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13188 if crease.range().end.row >= buffer_start_row {
13189 to_fold.push(crease);
13190 if row <= range.start.row {
13191 break;
13192 }
13193 }
13194 }
13195 }
13196 }
13197
13198 self.fold_creases(to_fold, true, window, cx);
13199 } else {
13200 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13201 let buffer_ids = self
13202 .selections
13203 .disjoint_anchor_ranges()
13204 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13205 .collect::<HashSet<_>>();
13206 for buffer_id in buffer_ids {
13207 self.fold_buffer(buffer_id, cx);
13208 }
13209 }
13210 }
13211
13212 fn fold_at_level(
13213 &mut self,
13214 fold_at: &FoldAtLevel,
13215 window: &mut Window,
13216 cx: &mut Context<Self>,
13217 ) {
13218 if !self.buffer.read(cx).is_singleton() {
13219 return;
13220 }
13221
13222 let fold_at_level = fold_at.0;
13223 let snapshot = self.buffer.read(cx).snapshot(cx);
13224 let mut to_fold = Vec::new();
13225 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13226
13227 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13228 while start_row < end_row {
13229 match self
13230 .snapshot(window, cx)
13231 .crease_for_buffer_row(MultiBufferRow(start_row))
13232 {
13233 Some(crease) => {
13234 let nested_start_row = crease.range().start.row + 1;
13235 let nested_end_row = crease.range().end.row;
13236
13237 if current_level < fold_at_level {
13238 stack.push((nested_start_row, nested_end_row, current_level + 1));
13239 } else if current_level == fold_at_level {
13240 to_fold.push(crease);
13241 }
13242
13243 start_row = nested_end_row + 1;
13244 }
13245 None => start_row += 1,
13246 }
13247 }
13248 }
13249
13250 self.fold_creases(to_fold, true, window, cx);
13251 }
13252
13253 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13254 if self.buffer.read(cx).is_singleton() {
13255 let mut fold_ranges = Vec::new();
13256 let snapshot = self.buffer.read(cx).snapshot(cx);
13257
13258 for row in 0..snapshot.max_row().0 {
13259 if let Some(foldable_range) = self
13260 .snapshot(window, cx)
13261 .crease_for_buffer_row(MultiBufferRow(row))
13262 {
13263 fold_ranges.push(foldable_range);
13264 }
13265 }
13266
13267 self.fold_creases(fold_ranges, true, window, cx);
13268 } else {
13269 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13270 editor
13271 .update_in(&mut cx, |editor, _, cx| {
13272 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13273 editor.fold_buffer(buffer_id, cx);
13274 }
13275 })
13276 .ok();
13277 });
13278 }
13279 }
13280
13281 pub fn fold_function_bodies(
13282 &mut self,
13283 _: &actions::FoldFunctionBodies,
13284 window: &mut Window,
13285 cx: &mut Context<Self>,
13286 ) {
13287 let snapshot = self.buffer.read(cx).snapshot(cx);
13288
13289 let ranges = snapshot
13290 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13291 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13292 .collect::<Vec<_>>();
13293
13294 let creases = ranges
13295 .into_iter()
13296 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13297 .collect();
13298
13299 self.fold_creases(creases, true, window, cx);
13300 }
13301
13302 pub fn fold_recursive(
13303 &mut self,
13304 _: &actions::FoldRecursive,
13305 window: &mut Window,
13306 cx: &mut Context<Self>,
13307 ) {
13308 let mut to_fold = Vec::new();
13309 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13310 let selections = self.selections.all_adjusted(cx);
13311
13312 for selection in selections {
13313 let range = selection.range().sorted();
13314 let buffer_start_row = range.start.row;
13315
13316 if range.start.row != range.end.row {
13317 let mut found = false;
13318 for row in range.start.row..=range.end.row {
13319 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13320 found = true;
13321 to_fold.push(crease);
13322 }
13323 }
13324 if found {
13325 continue;
13326 }
13327 }
13328
13329 for row in (0..=range.start.row).rev() {
13330 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13331 if crease.range().end.row >= buffer_start_row {
13332 to_fold.push(crease);
13333 } else {
13334 break;
13335 }
13336 }
13337 }
13338 }
13339
13340 self.fold_creases(to_fold, true, window, cx);
13341 }
13342
13343 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13344 let buffer_row = fold_at.buffer_row;
13345 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13346
13347 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13348 let autoscroll = self
13349 .selections
13350 .all::<Point>(cx)
13351 .iter()
13352 .any(|selection| crease.range().overlaps(&selection.range()));
13353
13354 self.fold_creases(vec![crease], autoscroll, window, cx);
13355 }
13356 }
13357
13358 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13359 if self.is_singleton(cx) {
13360 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13361 let buffer = &display_map.buffer_snapshot;
13362 let selections = self.selections.all::<Point>(cx);
13363 let ranges = selections
13364 .iter()
13365 .map(|s| {
13366 let range = s.display_range(&display_map).sorted();
13367 let mut start = range.start.to_point(&display_map);
13368 let mut end = range.end.to_point(&display_map);
13369 start.column = 0;
13370 end.column = buffer.line_len(MultiBufferRow(end.row));
13371 start..end
13372 })
13373 .collect::<Vec<_>>();
13374
13375 self.unfold_ranges(&ranges, true, true, cx);
13376 } else {
13377 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13378 let buffer_ids = self
13379 .selections
13380 .disjoint_anchor_ranges()
13381 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13382 .collect::<HashSet<_>>();
13383 for buffer_id in buffer_ids {
13384 self.unfold_buffer(buffer_id, cx);
13385 }
13386 }
13387 }
13388
13389 pub fn unfold_recursive(
13390 &mut self,
13391 _: &UnfoldRecursive,
13392 _window: &mut Window,
13393 cx: &mut Context<Self>,
13394 ) {
13395 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13396 let selections = self.selections.all::<Point>(cx);
13397 let ranges = selections
13398 .iter()
13399 .map(|s| {
13400 let mut range = s.display_range(&display_map).sorted();
13401 *range.start.column_mut() = 0;
13402 *range.end.column_mut() = display_map.line_len(range.end.row());
13403 let start = range.start.to_point(&display_map);
13404 let end = range.end.to_point(&display_map);
13405 start..end
13406 })
13407 .collect::<Vec<_>>();
13408
13409 self.unfold_ranges(&ranges, true, true, cx);
13410 }
13411
13412 pub fn unfold_at(
13413 &mut self,
13414 unfold_at: &UnfoldAt,
13415 _window: &mut Window,
13416 cx: &mut Context<Self>,
13417 ) {
13418 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13419
13420 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13421 ..Point::new(
13422 unfold_at.buffer_row.0,
13423 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13424 );
13425
13426 let autoscroll = self
13427 .selections
13428 .all::<Point>(cx)
13429 .iter()
13430 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13431
13432 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13433 }
13434
13435 pub fn unfold_all(
13436 &mut self,
13437 _: &actions::UnfoldAll,
13438 _window: &mut Window,
13439 cx: &mut Context<Self>,
13440 ) {
13441 if self.buffer.read(cx).is_singleton() {
13442 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13443 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13444 } else {
13445 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13446 editor
13447 .update(&mut cx, |editor, cx| {
13448 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13449 editor.unfold_buffer(buffer_id, cx);
13450 }
13451 })
13452 .ok();
13453 });
13454 }
13455 }
13456
13457 pub fn fold_selected_ranges(
13458 &mut self,
13459 _: &FoldSelectedRanges,
13460 window: &mut Window,
13461 cx: &mut Context<Self>,
13462 ) {
13463 let selections = self.selections.all::<Point>(cx);
13464 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13465 let line_mode = self.selections.line_mode;
13466 let ranges = selections
13467 .into_iter()
13468 .map(|s| {
13469 if line_mode {
13470 let start = Point::new(s.start.row, 0);
13471 let end = Point::new(
13472 s.end.row,
13473 display_map
13474 .buffer_snapshot
13475 .line_len(MultiBufferRow(s.end.row)),
13476 );
13477 Crease::simple(start..end, display_map.fold_placeholder.clone())
13478 } else {
13479 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13480 }
13481 })
13482 .collect::<Vec<_>>();
13483 self.fold_creases(ranges, true, window, cx);
13484 }
13485
13486 pub fn fold_ranges<T: ToOffset + Clone>(
13487 &mut self,
13488 ranges: Vec<Range<T>>,
13489 auto_scroll: bool,
13490 window: &mut Window,
13491 cx: &mut Context<Self>,
13492 ) {
13493 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13494 let ranges = ranges
13495 .into_iter()
13496 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13497 .collect::<Vec<_>>();
13498 self.fold_creases(ranges, auto_scroll, window, cx);
13499 }
13500
13501 pub fn fold_creases<T: ToOffset + Clone>(
13502 &mut self,
13503 creases: Vec<Crease<T>>,
13504 auto_scroll: bool,
13505 window: &mut Window,
13506 cx: &mut Context<Self>,
13507 ) {
13508 if creases.is_empty() {
13509 return;
13510 }
13511
13512 let mut buffers_affected = HashSet::default();
13513 let multi_buffer = self.buffer().read(cx);
13514 for crease in &creases {
13515 if let Some((_, buffer, _)) =
13516 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13517 {
13518 buffers_affected.insert(buffer.read(cx).remote_id());
13519 };
13520 }
13521
13522 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13523
13524 if auto_scroll {
13525 self.request_autoscroll(Autoscroll::fit(), cx);
13526 }
13527
13528 cx.notify();
13529
13530 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13531 // Clear diagnostics block when folding a range that contains it.
13532 let snapshot = self.snapshot(window, cx);
13533 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13534 drop(snapshot);
13535 self.active_diagnostics = Some(active_diagnostics);
13536 self.dismiss_diagnostics(cx);
13537 } else {
13538 self.active_diagnostics = Some(active_diagnostics);
13539 }
13540 }
13541
13542 self.scrollbar_marker_state.dirty = true;
13543 }
13544
13545 /// Removes any folds whose ranges intersect any of the given ranges.
13546 pub fn unfold_ranges<T: ToOffset + Clone>(
13547 &mut self,
13548 ranges: &[Range<T>],
13549 inclusive: bool,
13550 auto_scroll: bool,
13551 cx: &mut Context<Self>,
13552 ) {
13553 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13554 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13555 });
13556 }
13557
13558 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13559 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13560 return;
13561 }
13562 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13563 self.display_map.update(cx, |display_map, cx| {
13564 display_map.fold_buffers([buffer_id], cx)
13565 });
13566 cx.emit(EditorEvent::BufferFoldToggled {
13567 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13568 folded: true,
13569 });
13570 cx.notify();
13571 }
13572
13573 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13574 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13575 return;
13576 }
13577 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13578 self.display_map.update(cx, |display_map, cx| {
13579 display_map.unfold_buffers([buffer_id], cx);
13580 });
13581 cx.emit(EditorEvent::BufferFoldToggled {
13582 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13583 folded: false,
13584 });
13585 cx.notify();
13586 }
13587
13588 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13589 self.display_map.read(cx).is_buffer_folded(buffer)
13590 }
13591
13592 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13593 self.display_map.read(cx).folded_buffers()
13594 }
13595
13596 /// Removes any folds with the given ranges.
13597 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13598 &mut self,
13599 ranges: &[Range<T>],
13600 type_id: TypeId,
13601 auto_scroll: bool,
13602 cx: &mut Context<Self>,
13603 ) {
13604 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13605 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13606 });
13607 }
13608
13609 fn remove_folds_with<T: ToOffset + Clone>(
13610 &mut self,
13611 ranges: &[Range<T>],
13612 auto_scroll: bool,
13613 cx: &mut Context<Self>,
13614 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13615 ) {
13616 if ranges.is_empty() {
13617 return;
13618 }
13619
13620 let mut buffers_affected = HashSet::default();
13621 let multi_buffer = self.buffer().read(cx);
13622 for range in ranges {
13623 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13624 buffers_affected.insert(buffer.read(cx).remote_id());
13625 };
13626 }
13627
13628 self.display_map.update(cx, update);
13629
13630 if auto_scroll {
13631 self.request_autoscroll(Autoscroll::fit(), cx);
13632 }
13633
13634 cx.notify();
13635 self.scrollbar_marker_state.dirty = true;
13636 self.active_indent_guides_state.dirty = true;
13637 }
13638
13639 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13640 self.display_map.read(cx).fold_placeholder.clone()
13641 }
13642
13643 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13644 self.buffer.update(cx, |buffer, cx| {
13645 buffer.set_all_diff_hunks_expanded(cx);
13646 });
13647 }
13648
13649 pub fn expand_all_diff_hunks(
13650 &mut self,
13651 _: &ExpandAllDiffHunks,
13652 _window: &mut Window,
13653 cx: &mut Context<Self>,
13654 ) {
13655 self.buffer.update(cx, |buffer, cx| {
13656 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13657 });
13658 }
13659
13660 pub fn toggle_selected_diff_hunks(
13661 &mut self,
13662 _: &ToggleSelectedDiffHunks,
13663 _window: &mut Window,
13664 cx: &mut Context<Self>,
13665 ) {
13666 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13667 self.toggle_diff_hunks_in_ranges(ranges, cx);
13668 }
13669
13670 pub fn diff_hunks_in_ranges<'a>(
13671 &'a self,
13672 ranges: &'a [Range<Anchor>],
13673 buffer: &'a MultiBufferSnapshot,
13674 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13675 ranges.iter().flat_map(move |range| {
13676 let end_excerpt_id = range.end.excerpt_id;
13677 let range = range.to_point(buffer);
13678 let mut peek_end = range.end;
13679 if range.end.row < buffer.max_row().0 {
13680 peek_end = Point::new(range.end.row + 1, 0);
13681 }
13682 buffer
13683 .diff_hunks_in_range(range.start..peek_end)
13684 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13685 })
13686 }
13687
13688 pub fn has_stageable_diff_hunks_in_ranges(
13689 &self,
13690 ranges: &[Range<Anchor>],
13691 snapshot: &MultiBufferSnapshot,
13692 ) -> bool {
13693 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13694 hunks.any(|hunk| hunk.status().has_secondary_hunk())
13695 }
13696
13697 pub fn toggle_staged_selected_diff_hunks(
13698 &mut self,
13699 _: &::git::ToggleStaged,
13700 _: &mut Window,
13701 cx: &mut Context<Self>,
13702 ) {
13703 let snapshot = self.buffer.read(cx).snapshot(cx);
13704 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13705 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13706 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13707 }
13708
13709 pub fn stage_and_next(
13710 &mut self,
13711 _: &::git::StageAndNext,
13712 window: &mut Window,
13713 cx: &mut Context<Self>,
13714 ) {
13715 self.do_stage_or_unstage_and_next(true, window, cx);
13716 }
13717
13718 pub fn unstage_and_next(
13719 &mut self,
13720 _: &::git::UnstageAndNext,
13721 window: &mut Window,
13722 cx: &mut Context<Self>,
13723 ) {
13724 self.do_stage_or_unstage_and_next(false, window, cx);
13725 }
13726
13727 pub fn stage_or_unstage_diff_hunks(
13728 &mut self,
13729 stage: bool,
13730 ranges: Vec<Range<Anchor>>,
13731 cx: &mut Context<Self>,
13732 ) {
13733 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
13734 cx.spawn(|this, mut cx| async move {
13735 task.await?;
13736 this.update(&mut cx, |this, cx| {
13737 let snapshot = this.buffer.read(cx).snapshot(cx);
13738 let chunk_by = this
13739 .diff_hunks_in_ranges(&ranges, &snapshot)
13740 .chunk_by(|hunk| hunk.buffer_id);
13741 for (buffer_id, hunks) in &chunk_by {
13742 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
13743 }
13744 })
13745 })
13746 .detach_and_log_err(cx);
13747 }
13748
13749 fn save_buffers_for_ranges_if_needed(
13750 &mut self,
13751 ranges: &[Range<Anchor>],
13752 cx: &mut Context<'_, Editor>,
13753 ) -> Task<Result<()>> {
13754 let multibuffer = self.buffer.read(cx);
13755 let snapshot = multibuffer.read(cx);
13756 let buffer_ids: HashSet<_> = ranges
13757 .iter()
13758 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
13759 .collect();
13760 drop(snapshot);
13761
13762 let mut buffers = HashSet::default();
13763 for buffer_id in buffer_ids {
13764 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
13765 let buffer = buffer_entity.read(cx);
13766 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
13767 {
13768 buffers.insert(buffer_entity);
13769 }
13770 }
13771 }
13772
13773 if let Some(project) = &self.project {
13774 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
13775 } else {
13776 Task::ready(Ok(()))
13777 }
13778 }
13779
13780 fn do_stage_or_unstage_and_next(
13781 &mut self,
13782 stage: bool,
13783 window: &mut Window,
13784 cx: &mut Context<Self>,
13785 ) {
13786 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13787
13788 if ranges.iter().any(|range| range.start != range.end) {
13789 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13790 return;
13791 }
13792
13793 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13794 let snapshot = self.snapshot(window, cx);
13795 let position = self.selections.newest::<Point>(cx).head();
13796 let mut row = snapshot
13797 .buffer_snapshot
13798 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
13799 .find(|hunk| hunk.row_range.start.0 > position.row)
13800 .map(|hunk| hunk.row_range.start);
13801
13802 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
13803 // Outside of the project diff editor, wrap around to the beginning.
13804 if !all_diff_hunks_expanded {
13805 row = row.or_else(|| {
13806 snapshot
13807 .buffer_snapshot
13808 .diff_hunks_in_range(Point::zero()..position)
13809 .find(|hunk| hunk.row_range.end.0 < position.row)
13810 .map(|hunk| hunk.row_range.start)
13811 });
13812 }
13813
13814 if let Some(row) = row {
13815 let destination = Point::new(row.0, 0);
13816 let autoscroll = Autoscroll::center();
13817
13818 self.unfold_ranges(&[destination..destination], false, false, cx);
13819 self.change_selections(Some(autoscroll), window, cx, |s| {
13820 s.select_ranges([destination..destination]);
13821 });
13822 } else if all_diff_hunks_expanded {
13823 window.dispatch_action(::git::ExpandCommitEditor.boxed_clone(), cx);
13824 }
13825 }
13826
13827 fn do_stage_or_unstage(
13828 &self,
13829 stage: bool,
13830 buffer_id: BufferId,
13831 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
13832 cx: &mut App,
13833 ) -> Option<()> {
13834 let project = self.project.as_ref()?;
13835 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
13836 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
13837 let buffer_snapshot = buffer.read(cx).snapshot();
13838 let file_exists = buffer_snapshot
13839 .file()
13840 .is_some_and(|file| file.disk_state().exists());
13841 diff.update(cx, |diff, cx| {
13842 diff.stage_or_unstage_hunks(
13843 stage,
13844 &hunks
13845 .map(|hunk| buffer_diff::DiffHunk {
13846 buffer_range: hunk.buffer_range,
13847 diff_base_byte_range: hunk.diff_base_byte_range,
13848 secondary_status: hunk.secondary_status,
13849 range: Point::zero()..Point::zero(), // unused
13850 })
13851 .collect::<Vec<_>>(),
13852 &buffer_snapshot,
13853 file_exists,
13854 cx,
13855 )
13856 });
13857 None
13858 }
13859
13860 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
13861 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13862 self.buffer
13863 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
13864 }
13865
13866 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
13867 self.buffer.update(cx, |buffer, cx| {
13868 let ranges = vec![Anchor::min()..Anchor::max()];
13869 if !buffer.all_diff_hunks_expanded()
13870 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
13871 {
13872 buffer.collapse_diff_hunks(ranges, cx);
13873 true
13874 } else {
13875 false
13876 }
13877 })
13878 }
13879
13880 fn toggle_diff_hunks_in_ranges(
13881 &mut self,
13882 ranges: Vec<Range<Anchor>>,
13883 cx: &mut Context<'_, Editor>,
13884 ) {
13885 self.buffer.update(cx, |buffer, cx| {
13886 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
13887 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
13888 })
13889 }
13890
13891 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
13892 self.buffer.update(cx, |buffer, cx| {
13893 let snapshot = buffer.snapshot(cx);
13894 let excerpt_id = range.end.excerpt_id;
13895 let point_range = range.to_point(&snapshot);
13896 let expand = !buffer.single_hunk_is_expanded(range, cx);
13897 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
13898 })
13899 }
13900
13901 pub(crate) fn apply_all_diff_hunks(
13902 &mut self,
13903 _: &ApplyAllDiffHunks,
13904 window: &mut Window,
13905 cx: &mut Context<Self>,
13906 ) {
13907 let buffers = self.buffer.read(cx).all_buffers();
13908 for branch_buffer in buffers {
13909 branch_buffer.update(cx, |branch_buffer, cx| {
13910 branch_buffer.merge_into_base(Vec::new(), cx);
13911 });
13912 }
13913
13914 if let Some(project) = self.project.clone() {
13915 self.save(true, project, window, cx).detach_and_log_err(cx);
13916 }
13917 }
13918
13919 pub(crate) fn apply_selected_diff_hunks(
13920 &mut self,
13921 _: &ApplyDiffHunk,
13922 window: &mut Window,
13923 cx: &mut Context<Self>,
13924 ) {
13925 let snapshot = self.snapshot(window, cx);
13926 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
13927 let mut ranges_by_buffer = HashMap::default();
13928 self.transact(window, cx, |editor, _window, cx| {
13929 for hunk in hunks {
13930 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13931 ranges_by_buffer
13932 .entry(buffer.clone())
13933 .or_insert_with(Vec::new)
13934 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13935 }
13936 }
13937
13938 for (buffer, ranges) in ranges_by_buffer {
13939 buffer.update(cx, |buffer, cx| {
13940 buffer.merge_into_base(ranges, cx);
13941 });
13942 }
13943 });
13944
13945 if let Some(project) = self.project.clone() {
13946 self.save(true, project, window, cx).detach_and_log_err(cx);
13947 }
13948 }
13949
13950 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13951 if hovered != self.gutter_hovered {
13952 self.gutter_hovered = hovered;
13953 cx.notify();
13954 }
13955 }
13956
13957 pub fn insert_blocks(
13958 &mut self,
13959 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13960 autoscroll: Option<Autoscroll>,
13961 cx: &mut Context<Self>,
13962 ) -> Vec<CustomBlockId> {
13963 let blocks = self
13964 .display_map
13965 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13966 if let Some(autoscroll) = autoscroll {
13967 self.request_autoscroll(autoscroll, cx);
13968 }
13969 cx.notify();
13970 blocks
13971 }
13972
13973 pub fn resize_blocks(
13974 &mut self,
13975 heights: HashMap<CustomBlockId, u32>,
13976 autoscroll: Option<Autoscroll>,
13977 cx: &mut Context<Self>,
13978 ) {
13979 self.display_map
13980 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13981 if let Some(autoscroll) = autoscroll {
13982 self.request_autoscroll(autoscroll, cx);
13983 }
13984 cx.notify();
13985 }
13986
13987 pub fn replace_blocks(
13988 &mut self,
13989 renderers: HashMap<CustomBlockId, RenderBlock>,
13990 autoscroll: Option<Autoscroll>,
13991 cx: &mut Context<Self>,
13992 ) {
13993 self.display_map
13994 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13995 if let Some(autoscroll) = autoscroll {
13996 self.request_autoscroll(autoscroll, cx);
13997 }
13998 cx.notify();
13999 }
14000
14001 pub fn remove_blocks(
14002 &mut self,
14003 block_ids: HashSet<CustomBlockId>,
14004 autoscroll: Option<Autoscroll>,
14005 cx: &mut Context<Self>,
14006 ) {
14007 self.display_map.update(cx, |display_map, cx| {
14008 display_map.remove_blocks(block_ids, cx)
14009 });
14010 if let Some(autoscroll) = autoscroll {
14011 self.request_autoscroll(autoscroll, cx);
14012 }
14013 cx.notify();
14014 }
14015
14016 pub fn row_for_block(
14017 &self,
14018 block_id: CustomBlockId,
14019 cx: &mut Context<Self>,
14020 ) -> Option<DisplayRow> {
14021 self.display_map
14022 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14023 }
14024
14025 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14026 self.focused_block = Some(focused_block);
14027 }
14028
14029 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14030 self.focused_block.take()
14031 }
14032
14033 pub fn insert_creases(
14034 &mut self,
14035 creases: impl IntoIterator<Item = Crease<Anchor>>,
14036 cx: &mut Context<Self>,
14037 ) -> Vec<CreaseId> {
14038 self.display_map
14039 .update(cx, |map, cx| map.insert_creases(creases, cx))
14040 }
14041
14042 pub fn remove_creases(
14043 &mut self,
14044 ids: impl IntoIterator<Item = CreaseId>,
14045 cx: &mut Context<Self>,
14046 ) {
14047 self.display_map
14048 .update(cx, |map, cx| map.remove_creases(ids, cx));
14049 }
14050
14051 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14052 self.display_map
14053 .update(cx, |map, cx| map.snapshot(cx))
14054 .longest_row()
14055 }
14056
14057 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14058 self.display_map
14059 .update(cx, |map, cx| map.snapshot(cx))
14060 .max_point()
14061 }
14062
14063 pub fn text(&self, cx: &App) -> String {
14064 self.buffer.read(cx).read(cx).text()
14065 }
14066
14067 pub fn is_empty(&self, cx: &App) -> bool {
14068 self.buffer.read(cx).read(cx).is_empty()
14069 }
14070
14071 pub fn text_option(&self, cx: &App) -> Option<String> {
14072 let text = self.text(cx);
14073 let text = text.trim();
14074
14075 if text.is_empty() {
14076 return None;
14077 }
14078
14079 Some(text.to_string())
14080 }
14081
14082 pub fn set_text(
14083 &mut self,
14084 text: impl Into<Arc<str>>,
14085 window: &mut Window,
14086 cx: &mut Context<Self>,
14087 ) {
14088 self.transact(window, cx, |this, _, cx| {
14089 this.buffer
14090 .read(cx)
14091 .as_singleton()
14092 .expect("you can only call set_text on editors for singleton buffers")
14093 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14094 });
14095 }
14096
14097 pub fn display_text(&self, cx: &mut App) -> String {
14098 self.display_map
14099 .update(cx, |map, cx| map.snapshot(cx))
14100 .text()
14101 }
14102
14103 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14104 let mut wrap_guides = smallvec::smallvec![];
14105
14106 if self.show_wrap_guides == Some(false) {
14107 return wrap_guides;
14108 }
14109
14110 let settings = self.buffer.read(cx).language_settings(cx);
14111 if settings.show_wrap_guides {
14112 match self.soft_wrap_mode(cx) {
14113 SoftWrap::Column(soft_wrap) => {
14114 wrap_guides.push((soft_wrap as usize, true));
14115 }
14116 SoftWrap::Bounded(soft_wrap) => {
14117 wrap_guides.push((soft_wrap as usize, true));
14118 }
14119 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14120 }
14121 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14122 }
14123
14124 wrap_guides
14125 }
14126
14127 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14128 let settings = self.buffer.read(cx).language_settings(cx);
14129 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14130 match mode {
14131 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14132 SoftWrap::None
14133 }
14134 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14135 language_settings::SoftWrap::PreferredLineLength => {
14136 SoftWrap::Column(settings.preferred_line_length)
14137 }
14138 language_settings::SoftWrap::Bounded => {
14139 SoftWrap::Bounded(settings.preferred_line_length)
14140 }
14141 }
14142 }
14143
14144 pub fn set_soft_wrap_mode(
14145 &mut self,
14146 mode: language_settings::SoftWrap,
14147
14148 cx: &mut Context<Self>,
14149 ) {
14150 self.soft_wrap_mode_override = Some(mode);
14151 cx.notify();
14152 }
14153
14154 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
14155 self.hard_wrap = hard_wrap;
14156 cx.notify();
14157 }
14158
14159 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14160 self.text_style_refinement = Some(style);
14161 }
14162
14163 /// called by the Element so we know what style we were most recently rendered with.
14164 pub(crate) fn set_style(
14165 &mut self,
14166 style: EditorStyle,
14167 window: &mut Window,
14168 cx: &mut Context<Self>,
14169 ) {
14170 let rem_size = window.rem_size();
14171 self.display_map.update(cx, |map, cx| {
14172 map.set_font(
14173 style.text.font(),
14174 style.text.font_size.to_pixels(rem_size),
14175 cx,
14176 )
14177 });
14178 self.style = Some(style);
14179 }
14180
14181 pub fn style(&self) -> Option<&EditorStyle> {
14182 self.style.as_ref()
14183 }
14184
14185 // Called by the element. This method is not designed to be called outside of the editor
14186 // element's layout code because it does not notify when rewrapping is computed synchronously.
14187 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14188 self.display_map
14189 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14190 }
14191
14192 pub fn set_soft_wrap(&mut self) {
14193 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14194 }
14195
14196 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14197 if self.soft_wrap_mode_override.is_some() {
14198 self.soft_wrap_mode_override.take();
14199 } else {
14200 let soft_wrap = match self.soft_wrap_mode(cx) {
14201 SoftWrap::GitDiff => return,
14202 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14203 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14204 language_settings::SoftWrap::None
14205 }
14206 };
14207 self.soft_wrap_mode_override = Some(soft_wrap);
14208 }
14209 cx.notify();
14210 }
14211
14212 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14213 let Some(workspace) = self.workspace() else {
14214 return;
14215 };
14216 let fs = workspace.read(cx).app_state().fs.clone();
14217 let current_show = TabBarSettings::get_global(cx).show;
14218 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14219 setting.show = Some(!current_show);
14220 });
14221 }
14222
14223 pub fn toggle_indent_guides(
14224 &mut self,
14225 _: &ToggleIndentGuides,
14226 _: &mut Window,
14227 cx: &mut Context<Self>,
14228 ) {
14229 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14230 self.buffer
14231 .read(cx)
14232 .language_settings(cx)
14233 .indent_guides
14234 .enabled
14235 });
14236 self.show_indent_guides = Some(!currently_enabled);
14237 cx.notify();
14238 }
14239
14240 fn should_show_indent_guides(&self) -> Option<bool> {
14241 self.show_indent_guides
14242 }
14243
14244 pub fn toggle_line_numbers(
14245 &mut self,
14246 _: &ToggleLineNumbers,
14247 _: &mut Window,
14248 cx: &mut Context<Self>,
14249 ) {
14250 let mut editor_settings = EditorSettings::get_global(cx).clone();
14251 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14252 EditorSettings::override_global(editor_settings, cx);
14253 }
14254
14255 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14256 self.use_relative_line_numbers
14257 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14258 }
14259
14260 pub fn toggle_relative_line_numbers(
14261 &mut self,
14262 _: &ToggleRelativeLineNumbers,
14263 _: &mut Window,
14264 cx: &mut Context<Self>,
14265 ) {
14266 let is_relative = self.should_use_relative_line_numbers(cx);
14267 self.set_relative_line_number(Some(!is_relative), cx)
14268 }
14269
14270 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14271 self.use_relative_line_numbers = is_relative;
14272 cx.notify();
14273 }
14274
14275 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14276 self.show_gutter = show_gutter;
14277 cx.notify();
14278 }
14279
14280 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14281 self.show_scrollbars = show_scrollbars;
14282 cx.notify();
14283 }
14284
14285 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14286 self.show_line_numbers = Some(show_line_numbers);
14287 cx.notify();
14288 }
14289
14290 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14291 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14292 cx.notify();
14293 }
14294
14295 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14296 self.show_code_actions = Some(show_code_actions);
14297 cx.notify();
14298 }
14299
14300 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14301 self.show_runnables = Some(show_runnables);
14302 cx.notify();
14303 }
14304
14305 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14306 if self.display_map.read(cx).masked != masked {
14307 self.display_map.update(cx, |map, _| map.masked = masked);
14308 }
14309 cx.notify()
14310 }
14311
14312 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14313 self.show_wrap_guides = Some(show_wrap_guides);
14314 cx.notify();
14315 }
14316
14317 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14318 self.show_indent_guides = Some(show_indent_guides);
14319 cx.notify();
14320 }
14321
14322 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14323 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14324 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14325 if let Some(dir) = file.abs_path(cx).parent() {
14326 return Some(dir.to_owned());
14327 }
14328 }
14329
14330 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14331 return Some(project_path.path.to_path_buf());
14332 }
14333 }
14334
14335 None
14336 }
14337
14338 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14339 self.active_excerpt(cx)?
14340 .1
14341 .read(cx)
14342 .file()
14343 .and_then(|f| f.as_local())
14344 }
14345
14346 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14347 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14348 let buffer = buffer.read(cx);
14349 if let Some(project_path) = buffer.project_path(cx) {
14350 let project = self.project.as_ref()?.read(cx);
14351 project.absolute_path(&project_path, cx)
14352 } else {
14353 buffer
14354 .file()
14355 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14356 }
14357 })
14358 }
14359
14360 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14361 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14362 let project_path = buffer.read(cx).project_path(cx)?;
14363 let project = self.project.as_ref()?.read(cx);
14364 let entry = project.entry_for_path(&project_path, cx)?;
14365 let path = entry.path.to_path_buf();
14366 Some(path)
14367 })
14368 }
14369
14370 pub fn reveal_in_finder(
14371 &mut self,
14372 _: &RevealInFileManager,
14373 _window: &mut Window,
14374 cx: &mut Context<Self>,
14375 ) {
14376 if let Some(target) = self.target_file(cx) {
14377 cx.reveal_path(&target.abs_path(cx));
14378 }
14379 }
14380
14381 pub fn copy_path(
14382 &mut self,
14383 _: &zed_actions::workspace::CopyPath,
14384 _window: &mut Window,
14385 cx: &mut Context<Self>,
14386 ) {
14387 if let Some(path) = self.target_file_abs_path(cx) {
14388 if let Some(path) = path.to_str() {
14389 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14390 }
14391 }
14392 }
14393
14394 pub fn copy_relative_path(
14395 &mut self,
14396 _: &zed_actions::workspace::CopyRelativePath,
14397 _window: &mut Window,
14398 cx: &mut Context<Self>,
14399 ) {
14400 if let Some(path) = self.target_file_path(cx) {
14401 if let Some(path) = path.to_str() {
14402 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14403 }
14404 }
14405 }
14406
14407 pub fn copy_file_name_without_extension(
14408 &mut self,
14409 _: &CopyFileNameWithoutExtension,
14410 _: &mut Window,
14411 cx: &mut Context<Self>,
14412 ) {
14413 if let Some(file) = self.target_file(cx) {
14414 if let Some(file_stem) = file.path().file_stem() {
14415 if let Some(name) = file_stem.to_str() {
14416 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14417 }
14418 }
14419 }
14420 }
14421
14422 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14423 if let Some(file) = self.target_file(cx) {
14424 if let Some(file_name) = file.path().file_name() {
14425 if let Some(name) = file_name.to_str() {
14426 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14427 }
14428 }
14429 }
14430 }
14431
14432 pub fn toggle_git_blame(
14433 &mut self,
14434 _: &::git::Blame,
14435 window: &mut Window,
14436 cx: &mut Context<Self>,
14437 ) {
14438 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14439
14440 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14441 self.start_git_blame(true, window, cx);
14442 }
14443
14444 cx.notify();
14445 }
14446
14447 pub fn toggle_git_blame_inline(
14448 &mut self,
14449 _: &ToggleGitBlameInline,
14450 window: &mut Window,
14451 cx: &mut Context<Self>,
14452 ) {
14453 self.toggle_git_blame_inline_internal(true, window, cx);
14454 cx.notify();
14455 }
14456
14457 pub fn git_blame_inline_enabled(&self) -> bool {
14458 self.git_blame_inline_enabled
14459 }
14460
14461 pub fn toggle_selection_menu(
14462 &mut self,
14463 _: &ToggleSelectionMenu,
14464 _: &mut Window,
14465 cx: &mut Context<Self>,
14466 ) {
14467 self.show_selection_menu = self
14468 .show_selection_menu
14469 .map(|show_selections_menu| !show_selections_menu)
14470 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14471
14472 cx.notify();
14473 }
14474
14475 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14476 self.show_selection_menu
14477 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14478 }
14479
14480 fn start_git_blame(
14481 &mut self,
14482 user_triggered: bool,
14483 window: &mut Window,
14484 cx: &mut Context<Self>,
14485 ) {
14486 if let Some(project) = self.project.as_ref() {
14487 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14488 return;
14489 };
14490
14491 if buffer.read(cx).file().is_none() {
14492 return;
14493 }
14494
14495 let focused = self.focus_handle(cx).contains_focused(window, cx);
14496
14497 let project = project.clone();
14498 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14499 self.blame_subscription =
14500 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14501 self.blame = Some(blame);
14502 }
14503 }
14504
14505 fn toggle_git_blame_inline_internal(
14506 &mut self,
14507 user_triggered: bool,
14508 window: &mut Window,
14509 cx: &mut Context<Self>,
14510 ) {
14511 if self.git_blame_inline_enabled {
14512 self.git_blame_inline_enabled = false;
14513 self.show_git_blame_inline = false;
14514 self.show_git_blame_inline_delay_task.take();
14515 } else {
14516 self.git_blame_inline_enabled = true;
14517 self.start_git_blame_inline(user_triggered, window, cx);
14518 }
14519
14520 cx.notify();
14521 }
14522
14523 fn start_git_blame_inline(
14524 &mut self,
14525 user_triggered: bool,
14526 window: &mut Window,
14527 cx: &mut Context<Self>,
14528 ) {
14529 self.start_git_blame(user_triggered, window, cx);
14530
14531 if ProjectSettings::get_global(cx)
14532 .git
14533 .inline_blame_delay()
14534 .is_some()
14535 {
14536 self.start_inline_blame_timer(window, cx);
14537 } else {
14538 self.show_git_blame_inline = true
14539 }
14540 }
14541
14542 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14543 self.blame.as_ref()
14544 }
14545
14546 pub fn show_git_blame_gutter(&self) -> bool {
14547 self.show_git_blame_gutter
14548 }
14549
14550 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14551 self.show_git_blame_gutter && self.has_blame_entries(cx)
14552 }
14553
14554 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14555 self.show_git_blame_inline
14556 && (self.focus_handle.is_focused(window)
14557 || self
14558 .git_blame_inline_tooltip
14559 .as_ref()
14560 .and_then(|t| t.upgrade())
14561 .is_some())
14562 && !self.newest_selection_head_on_empty_line(cx)
14563 && self.has_blame_entries(cx)
14564 }
14565
14566 fn has_blame_entries(&self, cx: &App) -> bool {
14567 self.blame()
14568 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14569 }
14570
14571 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14572 let cursor_anchor = self.selections.newest_anchor().head();
14573
14574 let snapshot = self.buffer.read(cx).snapshot(cx);
14575 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14576
14577 snapshot.line_len(buffer_row) == 0
14578 }
14579
14580 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14581 let buffer_and_selection = maybe!({
14582 let selection = self.selections.newest::<Point>(cx);
14583 let selection_range = selection.range();
14584
14585 let multi_buffer = self.buffer().read(cx);
14586 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14587 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14588
14589 let (buffer, range, _) = if selection.reversed {
14590 buffer_ranges.first()
14591 } else {
14592 buffer_ranges.last()
14593 }?;
14594
14595 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14596 ..text::ToPoint::to_point(&range.end, &buffer).row;
14597 Some((
14598 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14599 selection,
14600 ))
14601 });
14602
14603 let Some((buffer, selection)) = buffer_and_selection else {
14604 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14605 };
14606
14607 let Some(project) = self.project.as_ref() else {
14608 return Task::ready(Err(anyhow!("editor does not have project")));
14609 };
14610
14611 project.update(cx, |project, cx| {
14612 project.get_permalink_to_line(&buffer, selection, cx)
14613 })
14614 }
14615
14616 pub fn copy_permalink_to_line(
14617 &mut self,
14618 _: &CopyPermalinkToLine,
14619 window: &mut Window,
14620 cx: &mut Context<Self>,
14621 ) {
14622 let permalink_task = self.get_permalink_to_line(cx);
14623 let workspace = self.workspace();
14624
14625 cx.spawn_in(window, |_, mut cx| async move {
14626 match permalink_task.await {
14627 Ok(permalink) => {
14628 cx.update(|_, cx| {
14629 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14630 })
14631 .ok();
14632 }
14633 Err(err) => {
14634 let message = format!("Failed to copy permalink: {err}");
14635
14636 Err::<(), anyhow::Error>(err).log_err();
14637
14638 if let Some(workspace) = workspace {
14639 workspace
14640 .update_in(&mut cx, |workspace, _, cx| {
14641 struct CopyPermalinkToLine;
14642
14643 workspace.show_toast(
14644 Toast::new(
14645 NotificationId::unique::<CopyPermalinkToLine>(),
14646 message,
14647 ),
14648 cx,
14649 )
14650 })
14651 .ok();
14652 }
14653 }
14654 }
14655 })
14656 .detach();
14657 }
14658
14659 pub fn copy_file_location(
14660 &mut self,
14661 _: &CopyFileLocation,
14662 _: &mut Window,
14663 cx: &mut Context<Self>,
14664 ) {
14665 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14666 if let Some(file) = self.target_file(cx) {
14667 if let Some(path) = file.path().to_str() {
14668 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14669 }
14670 }
14671 }
14672
14673 pub fn open_permalink_to_line(
14674 &mut self,
14675 _: &OpenPermalinkToLine,
14676 window: &mut Window,
14677 cx: &mut Context<Self>,
14678 ) {
14679 let permalink_task = self.get_permalink_to_line(cx);
14680 let workspace = self.workspace();
14681
14682 cx.spawn_in(window, |_, mut cx| async move {
14683 match permalink_task.await {
14684 Ok(permalink) => {
14685 cx.update(|_, cx| {
14686 cx.open_url(permalink.as_ref());
14687 })
14688 .ok();
14689 }
14690 Err(err) => {
14691 let message = format!("Failed to open permalink: {err}");
14692
14693 Err::<(), anyhow::Error>(err).log_err();
14694
14695 if let Some(workspace) = workspace {
14696 workspace
14697 .update(&mut cx, |workspace, cx| {
14698 struct OpenPermalinkToLine;
14699
14700 workspace.show_toast(
14701 Toast::new(
14702 NotificationId::unique::<OpenPermalinkToLine>(),
14703 message,
14704 ),
14705 cx,
14706 )
14707 })
14708 .ok();
14709 }
14710 }
14711 }
14712 })
14713 .detach();
14714 }
14715
14716 pub fn insert_uuid_v4(
14717 &mut self,
14718 _: &InsertUuidV4,
14719 window: &mut Window,
14720 cx: &mut Context<Self>,
14721 ) {
14722 self.insert_uuid(UuidVersion::V4, window, cx);
14723 }
14724
14725 pub fn insert_uuid_v7(
14726 &mut self,
14727 _: &InsertUuidV7,
14728 window: &mut Window,
14729 cx: &mut Context<Self>,
14730 ) {
14731 self.insert_uuid(UuidVersion::V7, window, cx);
14732 }
14733
14734 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14735 self.transact(window, cx, |this, window, cx| {
14736 let edits = this
14737 .selections
14738 .all::<Point>(cx)
14739 .into_iter()
14740 .map(|selection| {
14741 let uuid = match version {
14742 UuidVersion::V4 => uuid::Uuid::new_v4(),
14743 UuidVersion::V7 => uuid::Uuid::now_v7(),
14744 };
14745
14746 (selection.range(), uuid.to_string())
14747 });
14748 this.edit(edits, cx);
14749 this.refresh_inline_completion(true, false, window, cx);
14750 });
14751 }
14752
14753 pub fn open_selections_in_multibuffer(
14754 &mut self,
14755 _: &OpenSelectionsInMultibuffer,
14756 window: &mut Window,
14757 cx: &mut Context<Self>,
14758 ) {
14759 let multibuffer = self.buffer.read(cx);
14760
14761 let Some(buffer) = multibuffer.as_singleton() else {
14762 return;
14763 };
14764
14765 let Some(workspace) = self.workspace() else {
14766 return;
14767 };
14768
14769 let locations = self
14770 .selections
14771 .disjoint_anchors()
14772 .iter()
14773 .map(|range| Location {
14774 buffer: buffer.clone(),
14775 range: range.start.text_anchor..range.end.text_anchor,
14776 })
14777 .collect::<Vec<_>>();
14778
14779 let title = multibuffer.title(cx).to_string();
14780
14781 cx.spawn_in(window, |_, mut cx| async move {
14782 workspace.update_in(&mut cx, |workspace, window, cx| {
14783 Self::open_locations_in_multibuffer(
14784 workspace,
14785 locations,
14786 format!("Selections for '{title}'"),
14787 false,
14788 MultibufferSelectionMode::All,
14789 window,
14790 cx,
14791 );
14792 })
14793 })
14794 .detach();
14795 }
14796
14797 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14798 /// last highlight added will be used.
14799 ///
14800 /// If the range ends at the beginning of a line, then that line will not be highlighted.
14801 pub fn highlight_rows<T: 'static>(
14802 &mut self,
14803 range: Range<Anchor>,
14804 color: Hsla,
14805 should_autoscroll: bool,
14806 cx: &mut Context<Self>,
14807 ) {
14808 let snapshot = self.buffer().read(cx).snapshot(cx);
14809 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14810 let ix = row_highlights.binary_search_by(|highlight| {
14811 Ordering::Equal
14812 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
14813 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
14814 });
14815
14816 if let Err(mut ix) = ix {
14817 let index = post_inc(&mut self.highlight_order);
14818
14819 // If this range intersects with the preceding highlight, then merge it with
14820 // the preceding highlight. Otherwise insert a new highlight.
14821 let mut merged = false;
14822 if ix > 0 {
14823 let prev_highlight = &mut row_highlights[ix - 1];
14824 if prev_highlight
14825 .range
14826 .end
14827 .cmp(&range.start, &snapshot)
14828 .is_ge()
14829 {
14830 ix -= 1;
14831 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
14832 prev_highlight.range.end = range.end;
14833 }
14834 merged = true;
14835 prev_highlight.index = index;
14836 prev_highlight.color = color;
14837 prev_highlight.should_autoscroll = should_autoscroll;
14838 }
14839 }
14840
14841 if !merged {
14842 row_highlights.insert(
14843 ix,
14844 RowHighlight {
14845 range: range.clone(),
14846 index,
14847 color,
14848 should_autoscroll,
14849 },
14850 );
14851 }
14852
14853 // If any of the following highlights intersect with this one, merge them.
14854 while let Some(next_highlight) = row_highlights.get(ix + 1) {
14855 let highlight = &row_highlights[ix];
14856 if next_highlight
14857 .range
14858 .start
14859 .cmp(&highlight.range.end, &snapshot)
14860 .is_le()
14861 {
14862 if next_highlight
14863 .range
14864 .end
14865 .cmp(&highlight.range.end, &snapshot)
14866 .is_gt()
14867 {
14868 row_highlights[ix].range.end = next_highlight.range.end;
14869 }
14870 row_highlights.remove(ix + 1);
14871 } else {
14872 break;
14873 }
14874 }
14875 }
14876 }
14877
14878 /// Remove any highlighted row ranges of the given type that intersect the
14879 /// given ranges.
14880 pub fn remove_highlighted_rows<T: 'static>(
14881 &mut self,
14882 ranges_to_remove: Vec<Range<Anchor>>,
14883 cx: &mut Context<Self>,
14884 ) {
14885 let snapshot = self.buffer().read(cx).snapshot(cx);
14886 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14887 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
14888 row_highlights.retain(|highlight| {
14889 while let Some(range_to_remove) = ranges_to_remove.peek() {
14890 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
14891 Ordering::Less | Ordering::Equal => {
14892 ranges_to_remove.next();
14893 }
14894 Ordering::Greater => {
14895 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
14896 Ordering::Less | Ordering::Equal => {
14897 return false;
14898 }
14899 Ordering::Greater => break,
14900 }
14901 }
14902 }
14903 }
14904
14905 true
14906 })
14907 }
14908
14909 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
14910 pub fn clear_row_highlights<T: 'static>(&mut self) {
14911 self.highlighted_rows.remove(&TypeId::of::<T>());
14912 }
14913
14914 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14915 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14916 self.highlighted_rows
14917 .get(&TypeId::of::<T>())
14918 .map_or(&[] as &[_], |vec| vec.as_slice())
14919 .iter()
14920 .map(|highlight| (highlight.range.clone(), highlight.color))
14921 }
14922
14923 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14924 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14925 /// Allows to ignore certain kinds of highlights.
14926 pub fn highlighted_display_rows(
14927 &self,
14928 window: &mut Window,
14929 cx: &mut App,
14930 ) -> BTreeMap<DisplayRow, LineHighlight> {
14931 let snapshot = self.snapshot(window, cx);
14932 let mut used_highlight_orders = HashMap::default();
14933 self.highlighted_rows
14934 .iter()
14935 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14936 .fold(
14937 BTreeMap::<DisplayRow, LineHighlight>::new(),
14938 |mut unique_rows, highlight| {
14939 let start = highlight.range.start.to_display_point(&snapshot);
14940 let end = highlight.range.end.to_display_point(&snapshot);
14941 let start_row = start.row().0;
14942 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14943 && end.column() == 0
14944 {
14945 end.row().0.saturating_sub(1)
14946 } else {
14947 end.row().0
14948 };
14949 for row in start_row..=end_row {
14950 let used_index =
14951 used_highlight_orders.entry(row).or_insert(highlight.index);
14952 if highlight.index >= *used_index {
14953 *used_index = highlight.index;
14954 unique_rows.insert(DisplayRow(row), highlight.color.into());
14955 }
14956 }
14957 unique_rows
14958 },
14959 )
14960 }
14961
14962 pub fn highlighted_display_row_for_autoscroll(
14963 &self,
14964 snapshot: &DisplaySnapshot,
14965 ) -> Option<DisplayRow> {
14966 self.highlighted_rows
14967 .values()
14968 .flat_map(|highlighted_rows| highlighted_rows.iter())
14969 .filter_map(|highlight| {
14970 if highlight.should_autoscroll {
14971 Some(highlight.range.start.to_display_point(snapshot).row())
14972 } else {
14973 None
14974 }
14975 })
14976 .min()
14977 }
14978
14979 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14980 self.highlight_background::<SearchWithinRange>(
14981 ranges,
14982 |colors| colors.editor_document_highlight_read_background,
14983 cx,
14984 )
14985 }
14986
14987 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14988 self.breadcrumb_header = Some(new_header);
14989 }
14990
14991 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14992 self.clear_background_highlights::<SearchWithinRange>(cx);
14993 }
14994
14995 pub fn highlight_background<T: 'static>(
14996 &mut self,
14997 ranges: &[Range<Anchor>],
14998 color_fetcher: fn(&ThemeColors) -> Hsla,
14999 cx: &mut Context<Self>,
15000 ) {
15001 self.background_highlights
15002 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15003 self.scrollbar_marker_state.dirty = true;
15004 cx.notify();
15005 }
15006
15007 pub fn clear_background_highlights<T: 'static>(
15008 &mut self,
15009 cx: &mut Context<Self>,
15010 ) -> Option<BackgroundHighlight> {
15011 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15012 if !text_highlights.1.is_empty() {
15013 self.scrollbar_marker_state.dirty = true;
15014 cx.notify();
15015 }
15016 Some(text_highlights)
15017 }
15018
15019 pub fn highlight_gutter<T: 'static>(
15020 &mut self,
15021 ranges: &[Range<Anchor>],
15022 color_fetcher: fn(&App) -> Hsla,
15023 cx: &mut Context<Self>,
15024 ) {
15025 self.gutter_highlights
15026 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15027 cx.notify();
15028 }
15029
15030 pub fn clear_gutter_highlights<T: 'static>(
15031 &mut self,
15032 cx: &mut Context<Self>,
15033 ) -> Option<GutterHighlight> {
15034 cx.notify();
15035 self.gutter_highlights.remove(&TypeId::of::<T>())
15036 }
15037
15038 #[cfg(feature = "test-support")]
15039 pub fn all_text_background_highlights(
15040 &self,
15041 window: &mut Window,
15042 cx: &mut Context<Self>,
15043 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15044 let snapshot = self.snapshot(window, cx);
15045 let buffer = &snapshot.buffer_snapshot;
15046 let start = buffer.anchor_before(0);
15047 let end = buffer.anchor_after(buffer.len());
15048 let theme = cx.theme().colors();
15049 self.background_highlights_in_range(start..end, &snapshot, theme)
15050 }
15051
15052 #[cfg(feature = "test-support")]
15053 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15054 let snapshot = self.buffer().read(cx).snapshot(cx);
15055
15056 let highlights = self
15057 .background_highlights
15058 .get(&TypeId::of::<items::BufferSearchHighlights>());
15059
15060 if let Some((_color, ranges)) = highlights {
15061 ranges
15062 .iter()
15063 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15064 .collect_vec()
15065 } else {
15066 vec![]
15067 }
15068 }
15069
15070 fn document_highlights_for_position<'a>(
15071 &'a self,
15072 position: Anchor,
15073 buffer: &'a MultiBufferSnapshot,
15074 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15075 let read_highlights = self
15076 .background_highlights
15077 .get(&TypeId::of::<DocumentHighlightRead>())
15078 .map(|h| &h.1);
15079 let write_highlights = self
15080 .background_highlights
15081 .get(&TypeId::of::<DocumentHighlightWrite>())
15082 .map(|h| &h.1);
15083 let left_position = position.bias_left(buffer);
15084 let right_position = position.bias_right(buffer);
15085 read_highlights
15086 .into_iter()
15087 .chain(write_highlights)
15088 .flat_map(move |ranges| {
15089 let start_ix = match ranges.binary_search_by(|probe| {
15090 let cmp = probe.end.cmp(&left_position, buffer);
15091 if cmp.is_ge() {
15092 Ordering::Greater
15093 } else {
15094 Ordering::Less
15095 }
15096 }) {
15097 Ok(i) | Err(i) => i,
15098 };
15099
15100 ranges[start_ix..]
15101 .iter()
15102 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15103 })
15104 }
15105
15106 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15107 self.background_highlights
15108 .get(&TypeId::of::<T>())
15109 .map_or(false, |(_, highlights)| !highlights.is_empty())
15110 }
15111
15112 pub fn background_highlights_in_range(
15113 &self,
15114 search_range: Range<Anchor>,
15115 display_snapshot: &DisplaySnapshot,
15116 theme: &ThemeColors,
15117 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15118 let mut results = Vec::new();
15119 for (color_fetcher, ranges) in self.background_highlights.values() {
15120 let color = color_fetcher(theme);
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 for range in &ranges[start_ix..] {
15134 if range
15135 .start
15136 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15137 .is_ge()
15138 {
15139 break;
15140 }
15141
15142 let start = range.start.to_display_point(display_snapshot);
15143 let end = range.end.to_display_point(display_snapshot);
15144 results.push((start..end, color))
15145 }
15146 }
15147 results
15148 }
15149
15150 pub fn background_highlight_row_ranges<T: 'static>(
15151 &self,
15152 search_range: Range<Anchor>,
15153 display_snapshot: &DisplaySnapshot,
15154 count: usize,
15155 ) -> Vec<RangeInclusive<DisplayPoint>> {
15156 let mut results = Vec::new();
15157 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15158 return vec![];
15159 };
15160
15161 let start_ix = match ranges.binary_search_by(|probe| {
15162 let cmp = probe
15163 .end
15164 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15165 if cmp.is_gt() {
15166 Ordering::Greater
15167 } else {
15168 Ordering::Less
15169 }
15170 }) {
15171 Ok(i) | Err(i) => i,
15172 };
15173 let mut push_region = |start: Option<Point>, end: Option<Point>| {
15174 if let (Some(start_display), Some(end_display)) = (start, end) {
15175 results.push(
15176 start_display.to_display_point(display_snapshot)
15177 ..=end_display.to_display_point(display_snapshot),
15178 );
15179 }
15180 };
15181 let mut start_row: Option<Point> = None;
15182 let mut end_row: Option<Point> = None;
15183 if ranges.len() > count {
15184 return Vec::new();
15185 }
15186 for range in &ranges[start_ix..] {
15187 if range
15188 .start
15189 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15190 .is_ge()
15191 {
15192 break;
15193 }
15194 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
15195 if let Some(current_row) = &end_row {
15196 if end.row == current_row.row {
15197 continue;
15198 }
15199 }
15200 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
15201 if start_row.is_none() {
15202 assert_eq!(end_row, None);
15203 start_row = Some(start);
15204 end_row = Some(end);
15205 continue;
15206 }
15207 if let Some(current_end) = end_row.as_mut() {
15208 if start.row > current_end.row + 1 {
15209 push_region(start_row, end_row);
15210 start_row = Some(start);
15211 end_row = Some(end);
15212 } else {
15213 // Merge two hunks.
15214 *current_end = end;
15215 }
15216 } else {
15217 unreachable!();
15218 }
15219 }
15220 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15221 push_region(start_row, end_row);
15222 results
15223 }
15224
15225 pub fn gutter_highlights_in_range(
15226 &self,
15227 search_range: Range<Anchor>,
15228 display_snapshot: &DisplaySnapshot,
15229 cx: &App,
15230 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15231 let mut results = Vec::new();
15232 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15233 let color = color_fetcher(cx);
15234 let start_ix = match ranges.binary_search_by(|probe| {
15235 let cmp = probe
15236 .end
15237 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15238 if cmp.is_gt() {
15239 Ordering::Greater
15240 } else {
15241 Ordering::Less
15242 }
15243 }) {
15244 Ok(i) | Err(i) => i,
15245 };
15246 for range in &ranges[start_ix..] {
15247 if range
15248 .start
15249 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15250 .is_ge()
15251 {
15252 break;
15253 }
15254
15255 let start = range.start.to_display_point(display_snapshot);
15256 let end = range.end.to_display_point(display_snapshot);
15257 results.push((start..end, color))
15258 }
15259 }
15260 results
15261 }
15262
15263 /// Get the text ranges corresponding to the redaction query
15264 pub fn redacted_ranges(
15265 &self,
15266 search_range: Range<Anchor>,
15267 display_snapshot: &DisplaySnapshot,
15268 cx: &App,
15269 ) -> Vec<Range<DisplayPoint>> {
15270 display_snapshot
15271 .buffer_snapshot
15272 .redacted_ranges(search_range, |file| {
15273 if let Some(file) = file {
15274 file.is_private()
15275 && EditorSettings::get(
15276 Some(SettingsLocation {
15277 worktree_id: file.worktree_id(cx),
15278 path: file.path().as_ref(),
15279 }),
15280 cx,
15281 )
15282 .redact_private_values
15283 } else {
15284 false
15285 }
15286 })
15287 .map(|range| {
15288 range.start.to_display_point(display_snapshot)
15289 ..range.end.to_display_point(display_snapshot)
15290 })
15291 .collect()
15292 }
15293
15294 pub fn highlight_text<T: 'static>(
15295 &mut self,
15296 ranges: Vec<Range<Anchor>>,
15297 style: HighlightStyle,
15298 cx: &mut Context<Self>,
15299 ) {
15300 self.display_map.update(cx, |map, _| {
15301 map.highlight_text(TypeId::of::<T>(), ranges, style)
15302 });
15303 cx.notify();
15304 }
15305
15306 pub(crate) fn highlight_inlays<T: 'static>(
15307 &mut self,
15308 highlights: Vec<InlayHighlight>,
15309 style: HighlightStyle,
15310 cx: &mut Context<Self>,
15311 ) {
15312 self.display_map.update(cx, |map, _| {
15313 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15314 });
15315 cx.notify();
15316 }
15317
15318 pub fn text_highlights<'a, T: 'static>(
15319 &'a self,
15320 cx: &'a App,
15321 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15322 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15323 }
15324
15325 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15326 let cleared = self
15327 .display_map
15328 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15329 if cleared {
15330 cx.notify();
15331 }
15332 }
15333
15334 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15335 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15336 && self.focus_handle.is_focused(window)
15337 }
15338
15339 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15340 self.show_cursor_when_unfocused = is_enabled;
15341 cx.notify();
15342 }
15343
15344 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15345 cx.notify();
15346 }
15347
15348 fn on_buffer_event(
15349 &mut self,
15350 multibuffer: &Entity<MultiBuffer>,
15351 event: &multi_buffer::Event,
15352 window: &mut Window,
15353 cx: &mut Context<Self>,
15354 ) {
15355 match event {
15356 multi_buffer::Event::Edited {
15357 singleton_buffer_edited,
15358 edited_buffer: buffer_edited,
15359 } => {
15360 self.scrollbar_marker_state.dirty = true;
15361 self.active_indent_guides_state.dirty = true;
15362 self.refresh_active_diagnostics(cx);
15363 self.refresh_code_actions(window, cx);
15364 if self.has_active_inline_completion() {
15365 self.update_visible_inline_completion(window, cx);
15366 }
15367 if let Some(buffer) = buffer_edited {
15368 let buffer_id = buffer.read(cx).remote_id();
15369 if !self.registered_buffers.contains_key(&buffer_id) {
15370 if let Some(project) = self.project.as_ref() {
15371 project.update(cx, |project, cx| {
15372 self.registered_buffers.insert(
15373 buffer_id,
15374 project.register_buffer_with_language_servers(&buffer, cx),
15375 );
15376 })
15377 }
15378 }
15379 }
15380 cx.emit(EditorEvent::BufferEdited);
15381 cx.emit(SearchEvent::MatchesInvalidated);
15382 if *singleton_buffer_edited {
15383 if let Some(project) = &self.project {
15384 #[allow(clippy::mutable_key_type)]
15385 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15386 multibuffer
15387 .all_buffers()
15388 .into_iter()
15389 .filter_map(|buffer| {
15390 buffer.update(cx, |buffer, cx| {
15391 let language = buffer.language()?;
15392 let should_discard = project.update(cx, |project, cx| {
15393 project.is_local()
15394 && !project.has_language_servers_for(buffer, cx)
15395 });
15396 should_discard.not().then_some(language.clone())
15397 })
15398 })
15399 .collect::<HashSet<_>>()
15400 });
15401 if !languages_affected.is_empty() {
15402 self.refresh_inlay_hints(
15403 InlayHintRefreshReason::BufferEdited(languages_affected),
15404 cx,
15405 );
15406 }
15407 }
15408 }
15409
15410 let Some(project) = &self.project else { return };
15411 let (telemetry, is_via_ssh) = {
15412 let project = project.read(cx);
15413 let telemetry = project.client().telemetry().clone();
15414 let is_via_ssh = project.is_via_ssh();
15415 (telemetry, is_via_ssh)
15416 };
15417 refresh_linked_ranges(self, window, cx);
15418 telemetry.log_edit_event("editor", is_via_ssh);
15419 }
15420 multi_buffer::Event::ExcerptsAdded {
15421 buffer,
15422 predecessor,
15423 excerpts,
15424 } => {
15425 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15426 let buffer_id = buffer.read(cx).remote_id();
15427 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15428 if let Some(project) = &self.project {
15429 get_uncommitted_diff_for_buffer(
15430 project,
15431 [buffer.clone()],
15432 self.buffer.clone(),
15433 cx,
15434 )
15435 .detach();
15436 }
15437 }
15438 cx.emit(EditorEvent::ExcerptsAdded {
15439 buffer: buffer.clone(),
15440 predecessor: *predecessor,
15441 excerpts: excerpts.clone(),
15442 });
15443 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15444 }
15445 multi_buffer::Event::ExcerptsRemoved { ids } => {
15446 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15447 let buffer = self.buffer.read(cx);
15448 self.registered_buffers
15449 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15450 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15451 }
15452 multi_buffer::Event::ExcerptsEdited {
15453 excerpt_ids,
15454 buffer_ids,
15455 } => {
15456 self.display_map.update(cx, |map, cx| {
15457 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15458 });
15459 cx.emit(EditorEvent::ExcerptsEdited {
15460 ids: excerpt_ids.clone(),
15461 })
15462 }
15463 multi_buffer::Event::ExcerptsExpanded { ids } => {
15464 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15465 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15466 }
15467 multi_buffer::Event::Reparsed(buffer_id) => {
15468 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15469
15470 cx.emit(EditorEvent::Reparsed(*buffer_id));
15471 }
15472 multi_buffer::Event::DiffHunksToggled => {
15473 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15474 }
15475 multi_buffer::Event::LanguageChanged(buffer_id) => {
15476 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15477 cx.emit(EditorEvent::Reparsed(*buffer_id));
15478 cx.notify();
15479 }
15480 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15481 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15482 multi_buffer::Event::FileHandleChanged
15483 | multi_buffer::Event::Reloaded
15484 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
15485 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15486 multi_buffer::Event::DiagnosticsUpdated => {
15487 self.refresh_active_diagnostics(cx);
15488 self.refresh_inline_diagnostics(true, window, cx);
15489 self.scrollbar_marker_state.dirty = true;
15490 cx.notify();
15491 }
15492 _ => {}
15493 };
15494 }
15495
15496 fn on_display_map_changed(
15497 &mut self,
15498 _: Entity<DisplayMap>,
15499 _: &mut Window,
15500 cx: &mut Context<Self>,
15501 ) {
15502 cx.notify();
15503 }
15504
15505 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15506 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15507 self.update_edit_prediction_settings(cx);
15508 self.refresh_inline_completion(true, false, window, cx);
15509 self.refresh_inlay_hints(
15510 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15511 self.selections.newest_anchor().head(),
15512 &self.buffer.read(cx).snapshot(cx),
15513 cx,
15514 )),
15515 cx,
15516 );
15517
15518 let old_cursor_shape = self.cursor_shape;
15519
15520 {
15521 let editor_settings = EditorSettings::get_global(cx);
15522 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15523 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15524 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15525 }
15526
15527 if old_cursor_shape != self.cursor_shape {
15528 cx.emit(EditorEvent::CursorShapeChanged);
15529 }
15530
15531 let project_settings = ProjectSettings::get_global(cx);
15532 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15533
15534 if self.mode == EditorMode::Full {
15535 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15536 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15537 if self.show_inline_diagnostics != show_inline_diagnostics {
15538 self.show_inline_diagnostics = show_inline_diagnostics;
15539 self.refresh_inline_diagnostics(false, window, cx);
15540 }
15541
15542 if self.git_blame_inline_enabled != inline_blame_enabled {
15543 self.toggle_git_blame_inline_internal(false, window, cx);
15544 }
15545 }
15546
15547 cx.notify();
15548 }
15549
15550 pub fn set_searchable(&mut self, searchable: bool) {
15551 self.searchable = searchable;
15552 }
15553
15554 pub fn searchable(&self) -> bool {
15555 self.searchable
15556 }
15557
15558 fn open_proposed_changes_editor(
15559 &mut self,
15560 _: &OpenProposedChangesEditor,
15561 window: &mut Window,
15562 cx: &mut Context<Self>,
15563 ) {
15564 let Some(workspace) = self.workspace() else {
15565 cx.propagate();
15566 return;
15567 };
15568
15569 let selections = self.selections.all::<usize>(cx);
15570 let multi_buffer = self.buffer.read(cx);
15571 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15572 let mut new_selections_by_buffer = HashMap::default();
15573 for selection in selections {
15574 for (buffer, range, _) in
15575 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15576 {
15577 let mut range = range.to_point(buffer);
15578 range.start.column = 0;
15579 range.end.column = buffer.line_len(range.end.row);
15580 new_selections_by_buffer
15581 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15582 .or_insert(Vec::new())
15583 .push(range)
15584 }
15585 }
15586
15587 let proposed_changes_buffers = new_selections_by_buffer
15588 .into_iter()
15589 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15590 .collect::<Vec<_>>();
15591 let proposed_changes_editor = cx.new(|cx| {
15592 ProposedChangesEditor::new(
15593 "Proposed changes",
15594 proposed_changes_buffers,
15595 self.project.clone(),
15596 window,
15597 cx,
15598 )
15599 });
15600
15601 window.defer(cx, move |window, cx| {
15602 workspace.update(cx, |workspace, cx| {
15603 workspace.active_pane().update(cx, |pane, cx| {
15604 pane.add_item(
15605 Box::new(proposed_changes_editor),
15606 true,
15607 true,
15608 None,
15609 window,
15610 cx,
15611 );
15612 });
15613 });
15614 });
15615 }
15616
15617 pub fn open_excerpts_in_split(
15618 &mut self,
15619 _: &OpenExcerptsSplit,
15620 window: &mut Window,
15621 cx: &mut Context<Self>,
15622 ) {
15623 self.open_excerpts_common(None, true, window, cx)
15624 }
15625
15626 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15627 self.open_excerpts_common(None, false, window, cx)
15628 }
15629
15630 fn open_excerpts_common(
15631 &mut self,
15632 jump_data: Option<JumpData>,
15633 split: bool,
15634 window: &mut Window,
15635 cx: &mut Context<Self>,
15636 ) {
15637 let Some(workspace) = self.workspace() else {
15638 cx.propagate();
15639 return;
15640 };
15641
15642 if self.buffer.read(cx).is_singleton() {
15643 cx.propagate();
15644 return;
15645 }
15646
15647 let mut new_selections_by_buffer = HashMap::default();
15648 match &jump_data {
15649 Some(JumpData::MultiBufferPoint {
15650 excerpt_id,
15651 position,
15652 anchor,
15653 line_offset_from_top,
15654 }) => {
15655 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15656 if let Some(buffer) = multi_buffer_snapshot
15657 .buffer_id_for_excerpt(*excerpt_id)
15658 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15659 {
15660 let buffer_snapshot = buffer.read(cx).snapshot();
15661 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15662 language::ToPoint::to_point(anchor, &buffer_snapshot)
15663 } else {
15664 buffer_snapshot.clip_point(*position, Bias::Left)
15665 };
15666 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15667 new_selections_by_buffer.insert(
15668 buffer,
15669 (
15670 vec![jump_to_offset..jump_to_offset],
15671 Some(*line_offset_from_top),
15672 ),
15673 );
15674 }
15675 }
15676 Some(JumpData::MultiBufferRow {
15677 row,
15678 line_offset_from_top,
15679 }) => {
15680 let point = MultiBufferPoint::new(row.0, 0);
15681 if let Some((buffer, buffer_point, _)) =
15682 self.buffer.read(cx).point_to_buffer_point(point, cx)
15683 {
15684 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15685 new_selections_by_buffer
15686 .entry(buffer)
15687 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15688 .0
15689 .push(buffer_offset..buffer_offset)
15690 }
15691 }
15692 None => {
15693 let selections = self.selections.all::<usize>(cx);
15694 let multi_buffer = self.buffer.read(cx);
15695 for selection in selections {
15696 for (snapshot, range, _, anchor) in multi_buffer
15697 .snapshot(cx)
15698 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15699 {
15700 if let Some(anchor) = anchor {
15701 // selection is in a deleted hunk
15702 let Some(buffer_id) = anchor.buffer_id else {
15703 continue;
15704 };
15705 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15706 continue;
15707 };
15708 let offset = text::ToOffset::to_offset(
15709 &anchor.text_anchor,
15710 &buffer_handle.read(cx).snapshot(),
15711 );
15712 let range = offset..offset;
15713 new_selections_by_buffer
15714 .entry(buffer_handle)
15715 .or_insert((Vec::new(), None))
15716 .0
15717 .push(range)
15718 } else {
15719 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15720 else {
15721 continue;
15722 };
15723 new_selections_by_buffer
15724 .entry(buffer_handle)
15725 .or_insert((Vec::new(), None))
15726 .0
15727 .push(range)
15728 }
15729 }
15730 }
15731 }
15732 }
15733
15734 if new_selections_by_buffer.is_empty() {
15735 return;
15736 }
15737
15738 // We defer the pane interaction because we ourselves are a workspace item
15739 // and activating a new item causes the pane to call a method on us reentrantly,
15740 // which panics if we're on the stack.
15741 window.defer(cx, move |window, cx| {
15742 workspace.update(cx, |workspace, cx| {
15743 let pane = if split {
15744 workspace.adjacent_pane(window, cx)
15745 } else {
15746 workspace.active_pane().clone()
15747 };
15748
15749 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15750 let editor = buffer
15751 .read(cx)
15752 .file()
15753 .is_none()
15754 .then(|| {
15755 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15756 // so `workspace.open_project_item` will never find them, always opening a new editor.
15757 // Instead, we try to activate the existing editor in the pane first.
15758 let (editor, pane_item_index) =
15759 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15760 let editor = item.downcast::<Editor>()?;
15761 let singleton_buffer =
15762 editor.read(cx).buffer().read(cx).as_singleton()?;
15763 if singleton_buffer == buffer {
15764 Some((editor, i))
15765 } else {
15766 None
15767 }
15768 })?;
15769 pane.update(cx, |pane, cx| {
15770 pane.activate_item(pane_item_index, true, true, window, cx)
15771 });
15772 Some(editor)
15773 })
15774 .flatten()
15775 .unwrap_or_else(|| {
15776 workspace.open_project_item::<Self>(
15777 pane.clone(),
15778 buffer,
15779 true,
15780 true,
15781 window,
15782 cx,
15783 )
15784 });
15785
15786 editor.update(cx, |editor, cx| {
15787 let autoscroll = match scroll_offset {
15788 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15789 None => Autoscroll::newest(),
15790 };
15791 let nav_history = editor.nav_history.take();
15792 editor.change_selections(Some(autoscroll), window, cx, |s| {
15793 s.select_ranges(ranges);
15794 });
15795 editor.nav_history = nav_history;
15796 });
15797 }
15798 })
15799 });
15800 }
15801
15802 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
15803 let snapshot = self.buffer.read(cx).read(cx);
15804 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
15805 Some(
15806 ranges
15807 .iter()
15808 .map(move |range| {
15809 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
15810 })
15811 .collect(),
15812 )
15813 }
15814
15815 fn selection_replacement_ranges(
15816 &self,
15817 range: Range<OffsetUtf16>,
15818 cx: &mut App,
15819 ) -> Vec<Range<OffsetUtf16>> {
15820 let selections = self.selections.all::<OffsetUtf16>(cx);
15821 let newest_selection = selections
15822 .iter()
15823 .max_by_key(|selection| selection.id)
15824 .unwrap();
15825 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
15826 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
15827 let snapshot = self.buffer.read(cx).read(cx);
15828 selections
15829 .into_iter()
15830 .map(|mut selection| {
15831 selection.start.0 =
15832 (selection.start.0 as isize).saturating_add(start_delta) as usize;
15833 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
15834 snapshot.clip_offset_utf16(selection.start, Bias::Left)
15835 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
15836 })
15837 .collect()
15838 }
15839
15840 fn report_editor_event(
15841 &self,
15842 event_type: &'static str,
15843 file_extension: Option<String>,
15844 cx: &App,
15845 ) {
15846 if cfg!(any(test, feature = "test-support")) {
15847 return;
15848 }
15849
15850 let Some(project) = &self.project else { return };
15851
15852 // If None, we are in a file without an extension
15853 let file = self
15854 .buffer
15855 .read(cx)
15856 .as_singleton()
15857 .and_then(|b| b.read(cx).file());
15858 let file_extension = file_extension.or(file
15859 .as_ref()
15860 .and_then(|file| Path::new(file.file_name(cx)).extension())
15861 .and_then(|e| e.to_str())
15862 .map(|a| a.to_string()));
15863
15864 let vim_mode = cx
15865 .global::<SettingsStore>()
15866 .raw_user_settings()
15867 .get("vim_mode")
15868 == Some(&serde_json::Value::Bool(true));
15869
15870 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
15871 let copilot_enabled = edit_predictions_provider
15872 == language::language_settings::EditPredictionProvider::Copilot;
15873 let copilot_enabled_for_language = self
15874 .buffer
15875 .read(cx)
15876 .language_settings(cx)
15877 .show_edit_predictions;
15878
15879 let project = project.read(cx);
15880 telemetry::event!(
15881 event_type,
15882 file_extension,
15883 vim_mode,
15884 copilot_enabled,
15885 copilot_enabled_for_language,
15886 edit_predictions_provider,
15887 is_via_ssh = project.is_via_ssh(),
15888 );
15889 }
15890
15891 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
15892 /// with each line being an array of {text, highlight} objects.
15893 fn copy_highlight_json(
15894 &mut self,
15895 _: &CopyHighlightJson,
15896 window: &mut Window,
15897 cx: &mut Context<Self>,
15898 ) {
15899 #[derive(Serialize)]
15900 struct Chunk<'a> {
15901 text: String,
15902 highlight: Option<&'a str>,
15903 }
15904
15905 let snapshot = self.buffer.read(cx).snapshot(cx);
15906 let range = self
15907 .selected_text_range(false, window, cx)
15908 .and_then(|selection| {
15909 if selection.range.is_empty() {
15910 None
15911 } else {
15912 Some(selection.range)
15913 }
15914 })
15915 .unwrap_or_else(|| 0..snapshot.len());
15916
15917 let chunks = snapshot.chunks(range, true);
15918 let mut lines = Vec::new();
15919 let mut line: VecDeque<Chunk> = VecDeque::new();
15920
15921 let Some(style) = self.style.as_ref() else {
15922 return;
15923 };
15924
15925 for chunk in chunks {
15926 let highlight = chunk
15927 .syntax_highlight_id
15928 .and_then(|id| id.name(&style.syntax));
15929 let mut chunk_lines = chunk.text.split('\n').peekable();
15930 while let Some(text) = chunk_lines.next() {
15931 let mut merged_with_last_token = false;
15932 if let Some(last_token) = line.back_mut() {
15933 if last_token.highlight == highlight {
15934 last_token.text.push_str(text);
15935 merged_with_last_token = true;
15936 }
15937 }
15938
15939 if !merged_with_last_token {
15940 line.push_back(Chunk {
15941 text: text.into(),
15942 highlight,
15943 });
15944 }
15945
15946 if chunk_lines.peek().is_some() {
15947 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15948 line.pop_front();
15949 }
15950 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15951 line.pop_back();
15952 }
15953
15954 lines.push(mem::take(&mut line));
15955 }
15956 }
15957 }
15958
15959 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15960 return;
15961 };
15962 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15963 }
15964
15965 pub fn open_context_menu(
15966 &mut self,
15967 _: &OpenContextMenu,
15968 window: &mut Window,
15969 cx: &mut Context<Self>,
15970 ) {
15971 self.request_autoscroll(Autoscroll::newest(), cx);
15972 let position = self.selections.newest_display(cx).start;
15973 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15974 }
15975
15976 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15977 &self.inlay_hint_cache
15978 }
15979
15980 pub fn replay_insert_event(
15981 &mut self,
15982 text: &str,
15983 relative_utf16_range: Option<Range<isize>>,
15984 window: &mut Window,
15985 cx: &mut Context<Self>,
15986 ) {
15987 if !self.input_enabled {
15988 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15989 return;
15990 }
15991 if let Some(relative_utf16_range) = relative_utf16_range {
15992 let selections = self.selections.all::<OffsetUtf16>(cx);
15993 self.change_selections(None, window, cx, |s| {
15994 let new_ranges = selections.into_iter().map(|range| {
15995 let start = OffsetUtf16(
15996 range
15997 .head()
15998 .0
15999 .saturating_add_signed(relative_utf16_range.start),
16000 );
16001 let end = OffsetUtf16(
16002 range
16003 .head()
16004 .0
16005 .saturating_add_signed(relative_utf16_range.end),
16006 );
16007 start..end
16008 });
16009 s.select_ranges(new_ranges);
16010 });
16011 }
16012
16013 self.handle_input(text, window, cx);
16014 }
16015
16016 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16017 let Some(provider) = self.semantics_provider.as_ref() else {
16018 return false;
16019 };
16020
16021 let mut supports = false;
16022 self.buffer().update(cx, |this, cx| {
16023 this.for_each_buffer(|buffer| {
16024 supports |= provider.supports_inlay_hints(buffer, cx);
16025 });
16026 });
16027
16028 supports
16029 }
16030
16031 pub fn is_focused(&self, window: &Window) -> bool {
16032 self.focus_handle.is_focused(window)
16033 }
16034
16035 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16036 cx.emit(EditorEvent::Focused);
16037
16038 if let Some(descendant) = self
16039 .last_focused_descendant
16040 .take()
16041 .and_then(|descendant| descendant.upgrade())
16042 {
16043 window.focus(&descendant);
16044 } else {
16045 if let Some(blame) = self.blame.as_ref() {
16046 blame.update(cx, GitBlame::focus)
16047 }
16048
16049 self.blink_manager.update(cx, BlinkManager::enable);
16050 self.show_cursor_names(window, cx);
16051 self.buffer.update(cx, |buffer, cx| {
16052 buffer.finalize_last_transaction(cx);
16053 if self.leader_peer_id.is_none() {
16054 buffer.set_active_selections(
16055 &self.selections.disjoint_anchors(),
16056 self.selections.line_mode,
16057 self.cursor_shape,
16058 cx,
16059 );
16060 }
16061 });
16062 }
16063 }
16064
16065 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16066 cx.emit(EditorEvent::FocusedIn)
16067 }
16068
16069 fn handle_focus_out(
16070 &mut self,
16071 event: FocusOutEvent,
16072 _window: &mut Window,
16073 cx: &mut Context<Self>,
16074 ) {
16075 if event.blurred != self.focus_handle {
16076 self.last_focused_descendant = Some(event.blurred);
16077 }
16078 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16079 }
16080
16081 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16082 self.blink_manager.update(cx, BlinkManager::disable);
16083 self.buffer
16084 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16085
16086 if let Some(blame) = self.blame.as_ref() {
16087 blame.update(cx, GitBlame::blur)
16088 }
16089 if !self.hover_state.focused(window, cx) {
16090 hide_hover(self, cx);
16091 }
16092 if !self
16093 .context_menu
16094 .borrow()
16095 .as_ref()
16096 .is_some_and(|context_menu| context_menu.focused(window, cx))
16097 {
16098 self.hide_context_menu(window, cx);
16099 }
16100 self.discard_inline_completion(false, cx);
16101 cx.emit(EditorEvent::Blurred);
16102 cx.notify();
16103 }
16104
16105 pub fn register_action<A: Action>(
16106 &mut self,
16107 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16108 ) -> Subscription {
16109 let id = self.next_editor_action_id.post_inc();
16110 let listener = Arc::new(listener);
16111 self.editor_actions.borrow_mut().insert(
16112 id,
16113 Box::new(move |window, _| {
16114 let listener = listener.clone();
16115 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16116 let action = action.downcast_ref().unwrap();
16117 if phase == DispatchPhase::Bubble {
16118 listener(action, window, cx)
16119 }
16120 })
16121 }),
16122 );
16123
16124 let editor_actions = self.editor_actions.clone();
16125 Subscription::new(move || {
16126 editor_actions.borrow_mut().remove(&id);
16127 })
16128 }
16129
16130 pub fn file_header_size(&self) -> u32 {
16131 FILE_HEADER_HEIGHT
16132 }
16133
16134 pub fn restore(
16135 &mut self,
16136 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16137 window: &mut Window,
16138 cx: &mut Context<Self>,
16139 ) {
16140 let workspace = self.workspace();
16141 let project = self.project.as_ref();
16142 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16143 let mut tasks = Vec::new();
16144 for (buffer_id, changes) in revert_changes {
16145 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16146 buffer.update(cx, |buffer, cx| {
16147 buffer.edit(
16148 changes
16149 .into_iter()
16150 .map(|(range, text)| (range, text.to_string())),
16151 None,
16152 cx,
16153 );
16154 });
16155
16156 if let Some(project) =
16157 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16158 {
16159 project.update(cx, |project, cx| {
16160 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16161 })
16162 }
16163 }
16164 }
16165 tasks
16166 });
16167 cx.spawn_in(window, |_, mut cx| async move {
16168 for (buffer, task) in save_tasks {
16169 let result = task.await;
16170 if result.is_err() {
16171 let Some(path) = buffer
16172 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
16173 .ok()
16174 else {
16175 continue;
16176 };
16177 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
16178 let Some(task) = cx
16179 .update_window_entity(&workspace, |workspace, window, cx| {
16180 workspace
16181 .open_path_preview(path, None, false, false, false, window, cx)
16182 })
16183 .ok()
16184 else {
16185 continue;
16186 };
16187 task.await.log_err();
16188 }
16189 }
16190 }
16191 })
16192 .detach();
16193 self.change_selections(None, window, cx, |selections| selections.refresh());
16194 }
16195
16196 pub fn to_pixel_point(
16197 &self,
16198 source: multi_buffer::Anchor,
16199 editor_snapshot: &EditorSnapshot,
16200 window: &mut Window,
16201 ) -> Option<gpui::Point<Pixels>> {
16202 let source_point = source.to_display_point(editor_snapshot);
16203 self.display_to_pixel_point(source_point, editor_snapshot, window)
16204 }
16205
16206 pub fn display_to_pixel_point(
16207 &self,
16208 source: DisplayPoint,
16209 editor_snapshot: &EditorSnapshot,
16210 window: &mut Window,
16211 ) -> Option<gpui::Point<Pixels>> {
16212 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16213 let text_layout_details = self.text_layout_details(window);
16214 let scroll_top = text_layout_details
16215 .scroll_anchor
16216 .scroll_position(editor_snapshot)
16217 .y;
16218
16219 if source.row().as_f32() < scroll_top.floor() {
16220 return None;
16221 }
16222 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16223 let source_y = line_height * (source.row().as_f32() - scroll_top);
16224 Some(gpui::Point::new(source_x, source_y))
16225 }
16226
16227 pub fn has_visible_completions_menu(&self) -> bool {
16228 !self.edit_prediction_preview_is_active()
16229 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16230 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16231 })
16232 }
16233
16234 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16235 self.addons
16236 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16237 }
16238
16239 pub fn unregister_addon<T: Addon>(&mut self) {
16240 self.addons.remove(&std::any::TypeId::of::<T>());
16241 }
16242
16243 pub fn addon<T: Addon>(&self) -> Option<&T> {
16244 let type_id = std::any::TypeId::of::<T>();
16245 self.addons
16246 .get(&type_id)
16247 .and_then(|item| item.to_any().downcast_ref::<T>())
16248 }
16249
16250 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16251 let text_layout_details = self.text_layout_details(window);
16252 let style = &text_layout_details.editor_style;
16253 let font_id = window.text_system().resolve_font(&style.text.font());
16254 let font_size = style.text.font_size.to_pixels(window.rem_size());
16255 let line_height = style.text.line_height_in_pixels(window.rem_size());
16256 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16257
16258 gpui::Size::new(em_width, line_height)
16259 }
16260
16261 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16262 self.load_diff_task.clone()
16263 }
16264
16265 fn read_selections_from_db(
16266 &mut self,
16267 item_id: u64,
16268 workspace_id: WorkspaceId,
16269 window: &mut Window,
16270 cx: &mut Context<Editor>,
16271 ) {
16272 if !self.is_singleton(cx)
16273 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16274 {
16275 return;
16276 }
16277 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16278 return;
16279 };
16280 if selections.is_empty() {
16281 return;
16282 }
16283
16284 let snapshot = self.buffer.read(cx).snapshot(cx);
16285 self.change_selections(None, window, cx, |s| {
16286 s.select_ranges(selections.into_iter().map(|(start, end)| {
16287 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16288 }));
16289 });
16290 }
16291}
16292
16293fn insert_extra_newline_brackets(
16294 buffer: &MultiBufferSnapshot,
16295 range: Range<usize>,
16296 language: &language::LanguageScope,
16297) -> bool {
16298 let leading_whitespace_len = buffer
16299 .reversed_chars_at(range.start)
16300 .take_while(|c| c.is_whitespace() && *c != '\n')
16301 .map(|c| c.len_utf8())
16302 .sum::<usize>();
16303 let trailing_whitespace_len = buffer
16304 .chars_at(range.end)
16305 .take_while(|c| c.is_whitespace() && *c != '\n')
16306 .map(|c| c.len_utf8())
16307 .sum::<usize>();
16308 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16309
16310 language.brackets().any(|(pair, enabled)| {
16311 let pair_start = pair.start.trim_end();
16312 let pair_end = pair.end.trim_start();
16313
16314 enabled
16315 && pair.newline
16316 && buffer.contains_str_at(range.end, pair_end)
16317 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16318 })
16319}
16320
16321fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16322 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16323 [(buffer, range, _)] => (*buffer, range.clone()),
16324 _ => return false,
16325 };
16326 let pair = {
16327 let mut result: Option<BracketMatch> = None;
16328
16329 for pair in buffer
16330 .all_bracket_ranges(range.clone())
16331 .filter(move |pair| {
16332 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16333 })
16334 {
16335 let len = pair.close_range.end - pair.open_range.start;
16336
16337 if let Some(existing) = &result {
16338 let existing_len = existing.close_range.end - existing.open_range.start;
16339 if len > existing_len {
16340 continue;
16341 }
16342 }
16343
16344 result = Some(pair);
16345 }
16346
16347 result
16348 };
16349 let Some(pair) = pair else {
16350 return false;
16351 };
16352 pair.newline_only
16353 && buffer
16354 .chars_for_range(pair.open_range.end..range.start)
16355 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16356 .all(|c| c.is_whitespace() && c != '\n')
16357}
16358
16359fn get_uncommitted_diff_for_buffer(
16360 project: &Entity<Project>,
16361 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16362 buffer: Entity<MultiBuffer>,
16363 cx: &mut App,
16364) -> Task<()> {
16365 let mut tasks = Vec::new();
16366 project.update(cx, |project, cx| {
16367 for buffer in buffers {
16368 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16369 }
16370 });
16371 cx.spawn(|mut cx| async move {
16372 let diffs = future::join_all(tasks).await;
16373 buffer
16374 .update(&mut cx, |buffer, cx| {
16375 for diff in diffs.into_iter().flatten() {
16376 buffer.add_diff(diff, cx);
16377 }
16378 })
16379 .ok();
16380 })
16381}
16382
16383fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16384 let tab_size = tab_size.get() as usize;
16385 let mut width = offset;
16386
16387 for ch in text.chars() {
16388 width += if ch == '\t' {
16389 tab_size - (width % tab_size)
16390 } else {
16391 1
16392 };
16393 }
16394
16395 width - offset
16396}
16397
16398#[cfg(test)]
16399mod tests {
16400 use super::*;
16401
16402 #[test]
16403 fn test_string_size_with_expanded_tabs() {
16404 let nz = |val| NonZeroU32::new(val).unwrap();
16405 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16406 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16407 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16408 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16409 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16410 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16411 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16412 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16413 }
16414}
16415
16416/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16417struct WordBreakingTokenizer<'a> {
16418 input: &'a str,
16419}
16420
16421impl<'a> WordBreakingTokenizer<'a> {
16422 fn new(input: &'a str) -> Self {
16423 Self { input }
16424 }
16425}
16426
16427fn is_char_ideographic(ch: char) -> bool {
16428 use unicode_script::Script::*;
16429 use unicode_script::UnicodeScript;
16430 matches!(ch.script(), Han | Tangut | Yi)
16431}
16432
16433fn is_grapheme_ideographic(text: &str) -> bool {
16434 text.chars().any(is_char_ideographic)
16435}
16436
16437fn is_grapheme_whitespace(text: &str) -> bool {
16438 text.chars().any(|x| x.is_whitespace())
16439}
16440
16441fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16442 text.chars().next().map_or(false, |ch| {
16443 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16444 })
16445}
16446
16447#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16448struct WordBreakToken<'a> {
16449 token: &'a str,
16450 grapheme_len: usize,
16451 is_whitespace: bool,
16452}
16453
16454impl<'a> Iterator for WordBreakingTokenizer<'a> {
16455 /// Yields a span, the count of graphemes in the token, and whether it was
16456 /// whitespace. Note that it also breaks at word boundaries.
16457 type Item = WordBreakToken<'a>;
16458
16459 fn next(&mut self) -> Option<Self::Item> {
16460 use unicode_segmentation::UnicodeSegmentation;
16461 if self.input.is_empty() {
16462 return None;
16463 }
16464
16465 let mut iter = self.input.graphemes(true).peekable();
16466 let mut offset = 0;
16467 let mut graphemes = 0;
16468 if let Some(first_grapheme) = iter.next() {
16469 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16470 offset += first_grapheme.len();
16471 graphemes += 1;
16472 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16473 if let Some(grapheme) = iter.peek().copied() {
16474 if should_stay_with_preceding_ideograph(grapheme) {
16475 offset += grapheme.len();
16476 graphemes += 1;
16477 }
16478 }
16479 } else {
16480 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16481 let mut next_word_bound = words.peek().copied();
16482 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16483 next_word_bound = words.next();
16484 }
16485 while let Some(grapheme) = iter.peek().copied() {
16486 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16487 break;
16488 };
16489 if is_grapheme_whitespace(grapheme) != is_whitespace {
16490 break;
16491 };
16492 offset += grapheme.len();
16493 graphemes += 1;
16494 iter.next();
16495 }
16496 }
16497 let token = &self.input[..offset];
16498 self.input = &self.input[offset..];
16499 if is_whitespace {
16500 Some(WordBreakToken {
16501 token: " ",
16502 grapheme_len: 1,
16503 is_whitespace: true,
16504 })
16505 } else {
16506 Some(WordBreakToken {
16507 token,
16508 grapheme_len: graphemes,
16509 is_whitespace: false,
16510 })
16511 }
16512 } else {
16513 None
16514 }
16515 }
16516}
16517
16518#[test]
16519fn test_word_breaking_tokenizer() {
16520 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16521 ("", &[]),
16522 (" ", &[(" ", 1, true)]),
16523 ("Ʒ", &[("Ʒ", 1, false)]),
16524 ("Ǽ", &[("Ǽ", 1, false)]),
16525 ("⋑", &[("⋑", 1, false)]),
16526 ("⋑⋑", &[("⋑⋑", 2, false)]),
16527 (
16528 "原理,进而",
16529 &[
16530 ("原", 1, false),
16531 ("理,", 2, false),
16532 ("进", 1, false),
16533 ("而", 1, false),
16534 ],
16535 ),
16536 (
16537 "hello world",
16538 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16539 ),
16540 (
16541 "hello, world",
16542 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16543 ),
16544 (
16545 " hello world",
16546 &[
16547 (" ", 1, true),
16548 ("hello", 5, false),
16549 (" ", 1, true),
16550 ("world", 5, false),
16551 ],
16552 ),
16553 (
16554 "这是什么 \n 钢笔",
16555 &[
16556 ("这", 1, false),
16557 ("是", 1, false),
16558 ("什", 1, false),
16559 ("么", 1, false),
16560 (" ", 1, true),
16561 ("钢", 1, false),
16562 ("笔", 1, false),
16563 ],
16564 ),
16565 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16566 ];
16567
16568 for (input, result) in tests {
16569 assert_eq!(
16570 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16571 result
16572 .iter()
16573 .copied()
16574 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16575 token,
16576 grapheme_len,
16577 is_whitespace,
16578 })
16579 .collect::<Vec<_>>()
16580 );
16581 }
16582}
16583
16584fn wrap_with_prefix(
16585 line_prefix: String,
16586 unwrapped_text: String,
16587 wrap_column: usize,
16588 tab_size: NonZeroU32,
16589) -> String {
16590 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16591 let mut wrapped_text = String::new();
16592 let mut current_line = line_prefix.clone();
16593
16594 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16595 let mut current_line_len = line_prefix_len;
16596 for WordBreakToken {
16597 token,
16598 grapheme_len,
16599 is_whitespace,
16600 } in tokenizer
16601 {
16602 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16603 wrapped_text.push_str(current_line.trim_end());
16604 wrapped_text.push('\n');
16605 current_line.truncate(line_prefix.len());
16606 current_line_len = line_prefix_len;
16607 if !is_whitespace {
16608 current_line.push_str(token);
16609 current_line_len += grapheme_len;
16610 }
16611 } else if !is_whitespace {
16612 current_line.push_str(token);
16613 current_line_len += grapheme_len;
16614 } else if current_line_len != line_prefix_len {
16615 current_line.push(' ');
16616 current_line_len += 1;
16617 }
16618 }
16619
16620 if !current_line.is_empty() {
16621 wrapped_text.push_str(¤t_line);
16622 }
16623 wrapped_text
16624}
16625
16626#[test]
16627fn test_wrap_with_prefix() {
16628 assert_eq!(
16629 wrap_with_prefix(
16630 "# ".to_string(),
16631 "abcdefg".to_string(),
16632 4,
16633 NonZeroU32::new(4).unwrap()
16634 ),
16635 "# abcdefg"
16636 );
16637 assert_eq!(
16638 wrap_with_prefix(
16639 "".to_string(),
16640 "\thello world".to_string(),
16641 8,
16642 NonZeroU32::new(4).unwrap()
16643 ),
16644 "hello\nworld"
16645 );
16646 assert_eq!(
16647 wrap_with_prefix(
16648 "// ".to_string(),
16649 "xx \nyy zz aa bb cc".to_string(),
16650 12,
16651 NonZeroU32::new(4).unwrap()
16652 ),
16653 "// xx yy zz\n// aa bb cc"
16654 );
16655 assert_eq!(
16656 wrap_with_prefix(
16657 String::new(),
16658 "这是什么 \n 钢笔".to_string(),
16659 3,
16660 NonZeroU32::new(4).unwrap()
16661 ),
16662 "这是什\n么 钢\n笔"
16663 );
16664}
16665
16666pub trait CollaborationHub {
16667 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16668 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16669 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16670}
16671
16672impl CollaborationHub for Entity<Project> {
16673 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16674 self.read(cx).collaborators()
16675 }
16676
16677 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16678 self.read(cx).user_store().read(cx).participant_indices()
16679 }
16680
16681 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16682 let this = self.read(cx);
16683 let user_ids = this.collaborators().values().map(|c| c.user_id);
16684 this.user_store().read_with(cx, |user_store, cx| {
16685 user_store.participant_names(user_ids, cx)
16686 })
16687 }
16688}
16689
16690pub trait SemanticsProvider {
16691 fn hover(
16692 &self,
16693 buffer: &Entity<Buffer>,
16694 position: text::Anchor,
16695 cx: &mut App,
16696 ) -> Option<Task<Vec<project::Hover>>>;
16697
16698 fn inlay_hints(
16699 &self,
16700 buffer_handle: Entity<Buffer>,
16701 range: Range<text::Anchor>,
16702 cx: &mut App,
16703 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16704
16705 fn resolve_inlay_hint(
16706 &self,
16707 hint: InlayHint,
16708 buffer_handle: Entity<Buffer>,
16709 server_id: LanguageServerId,
16710 cx: &mut App,
16711 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16712
16713 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16714
16715 fn document_highlights(
16716 &self,
16717 buffer: &Entity<Buffer>,
16718 position: text::Anchor,
16719 cx: &mut App,
16720 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16721
16722 fn definitions(
16723 &self,
16724 buffer: &Entity<Buffer>,
16725 position: text::Anchor,
16726 kind: GotoDefinitionKind,
16727 cx: &mut App,
16728 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16729
16730 fn range_for_rename(
16731 &self,
16732 buffer: &Entity<Buffer>,
16733 position: text::Anchor,
16734 cx: &mut App,
16735 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16736
16737 fn perform_rename(
16738 &self,
16739 buffer: &Entity<Buffer>,
16740 position: text::Anchor,
16741 new_name: String,
16742 cx: &mut App,
16743 ) -> Option<Task<Result<ProjectTransaction>>>;
16744}
16745
16746pub trait CompletionProvider {
16747 fn completions(
16748 &self,
16749 buffer: &Entity<Buffer>,
16750 buffer_position: text::Anchor,
16751 trigger: CompletionContext,
16752 window: &mut Window,
16753 cx: &mut Context<Editor>,
16754 ) -> Task<Result<Vec<Completion>>>;
16755
16756 fn resolve_completions(
16757 &self,
16758 buffer: Entity<Buffer>,
16759 completion_indices: Vec<usize>,
16760 completions: Rc<RefCell<Box<[Completion]>>>,
16761 cx: &mut Context<Editor>,
16762 ) -> Task<Result<bool>>;
16763
16764 fn apply_additional_edits_for_completion(
16765 &self,
16766 _buffer: Entity<Buffer>,
16767 _completions: Rc<RefCell<Box<[Completion]>>>,
16768 _completion_index: usize,
16769 _push_to_history: bool,
16770 _cx: &mut Context<Editor>,
16771 ) -> Task<Result<Option<language::Transaction>>> {
16772 Task::ready(Ok(None))
16773 }
16774
16775 fn is_completion_trigger(
16776 &self,
16777 buffer: &Entity<Buffer>,
16778 position: language::Anchor,
16779 text: &str,
16780 trigger_in_words: bool,
16781 cx: &mut Context<Editor>,
16782 ) -> bool;
16783
16784 fn sort_completions(&self) -> bool {
16785 true
16786 }
16787}
16788
16789pub trait CodeActionProvider {
16790 fn id(&self) -> Arc<str>;
16791
16792 fn code_actions(
16793 &self,
16794 buffer: &Entity<Buffer>,
16795 range: Range<text::Anchor>,
16796 window: &mut Window,
16797 cx: &mut App,
16798 ) -> Task<Result<Vec<CodeAction>>>;
16799
16800 fn apply_code_action(
16801 &self,
16802 buffer_handle: Entity<Buffer>,
16803 action: CodeAction,
16804 excerpt_id: ExcerptId,
16805 push_to_history: bool,
16806 window: &mut Window,
16807 cx: &mut App,
16808 ) -> Task<Result<ProjectTransaction>>;
16809}
16810
16811impl CodeActionProvider for Entity<Project> {
16812 fn id(&self) -> Arc<str> {
16813 "project".into()
16814 }
16815
16816 fn code_actions(
16817 &self,
16818 buffer: &Entity<Buffer>,
16819 range: Range<text::Anchor>,
16820 _window: &mut Window,
16821 cx: &mut App,
16822 ) -> Task<Result<Vec<CodeAction>>> {
16823 self.update(cx, |project, cx| {
16824 project.code_actions(buffer, range, None, cx)
16825 })
16826 }
16827
16828 fn apply_code_action(
16829 &self,
16830 buffer_handle: Entity<Buffer>,
16831 action: CodeAction,
16832 _excerpt_id: ExcerptId,
16833 push_to_history: bool,
16834 _window: &mut Window,
16835 cx: &mut App,
16836 ) -> Task<Result<ProjectTransaction>> {
16837 self.update(cx, |project, cx| {
16838 project.apply_code_action(buffer_handle, action, push_to_history, cx)
16839 })
16840 }
16841}
16842
16843fn snippet_completions(
16844 project: &Project,
16845 buffer: &Entity<Buffer>,
16846 buffer_position: text::Anchor,
16847 cx: &mut App,
16848) -> Task<Result<Vec<Completion>>> {
16849 let language = buffer.read(cx).language_at(buffer_position);
16850 let language_name = language.as_ref().map(|language| language.lsp_id());
16851 let snippet_store = project.snippets().read(cx);
16852 let snippets = snippet_store.snippets_for(language_name, cx);
16853
16854 if snippets.is_empty() {
16855 return Task::ready(Ok(vec![]));
16856 }
16857 let snapshot = buffer.read(cx).text_snapshot();
16858 let chars: String = snapshot
16859 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
16860 .collect();
16861
16862 let scope = language.map(|language| language.default_scope());
16863 let executor = cx.background_executor().clone();
16864
16865 cx.background_spawn(async move {
16866 let classifier = CharClassifier::new(scope).for_completion(true);
16867 let mut last_word = chars
16868 .chars()
16869 .take_while(|c| classifier.is_word(*c))
16870 .collect::<String>();
16871 last_word = last_word.chars().rev().collect();
16872
16873 if last_word.is_empty() {
16874 return Ok(vec![]);
16875 }
16876
16877 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
16878 let to_lsp = |point: &text::Anchor| {
16879 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
16880 point_to_lsp(end)
16881 };
16882 let lsp_end = to_lsp(&buffer_position);
16883
16884 let candidates = snippets
16885 .iter()
16886 .enumerate()
16887 .flat_map(|(ix, snippet)| {
16888 snippet
16889 .prefix
16890 .iter()
16891 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
16892 })
16893 .collect::<Vec<StringMatchCandidate>>();
16894
16895 let mut matches = fuzzy::match_strings(
16896 &candidates,
16897 &last_word,
16898 last_word.chars().any(|c| c.is_uppercase()),
16899 100,
16900 &Default::default(),
16901 executor,
16902 )
16903 .await;
16904
16905 // Remove all candidates where the query's start does not match the start of any word in the candidate
16906 if let Some(query_start) = last_word.chars().next() {
16907 matches.retain(|string_match| {
16908 split_words(&string_match.string).any(|word| {
16909 // Check that the first codepoint of the word as lowercase matches the first
16910 // codepoint of the query as lowercase
16911 word.chars()
16912 .flat_map(|codepoint| codepoint.to_lowercase())
16913 .zip(query_start.to_lowercase())
16914 .all(|(word_cp, query_cp)| word_cp == query_cp)
16915 })
16916 });
16917 }
16918
16919 let matched_strings = matches
16920 .into_iter()
16921 .map(|m| m.string)
16922 .collect::<HashSet<_>>();
16923
16924 let result: Vec<Completion> = snippets
16925 .into_iter()
16926 .filter_map(|snippet| {
16927 let matching_prefix = snippet
16928 .prefix
16929 .iter()
16930 .find(|prefix| matched_strings.contains(*prefix))?;
16931 let start = as_offset - last_word.len();
16932 let start = snapshot.anchor_before(start);
16933 let range = start..buffer_position;
16934 let lsp_start = to_lsp(&start);
16935 let lsp_range = lsp::Range {
16936 start: lsp_start,
16937 end: lsp_end,
16938 };
16939 Some(Completion {
16940 old_range: range,
16941 new_text: snippet.body.clone(),
16942 resolved: false,
16943 label: CodeLabel {
16944 text: matching_prefix.clone(),
16945 runs: vec![],
16946 filter_range: 0..matching_prefix.len(),
16947 },
16948 server_id: LanguageServerId(usize::MAX),
16949 documentation: snippet
16950 .description
16951 .clone()
16952 .map(|description| CompletionDocumentation::SingleLine(description.into())),
16953 lsp_completion: lsp::CompletionItem {
16954 label: snippet.prefix.first().unwrap().clone(),
16955 kind: Some(CompletionItemKind::SNIPPET),
16956 label_details: snippet.description.as_ref().map(|description| {
16957 lsp::CompletionItemLabelDetails {
16958 detail: Some(description.clone()),
16959 description: None,
16960 }
16961 }),
16962 insert_text_format: Some(InsertTextFormat::SNIPPET),
16963 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
16964 lsp::InsertReplaceEdit {
16965 new_text: snippet.body.clone(),
16966 insert: lsp_range,
16967 replace: lsp_range,
16968 },
16969 )),
16970 filter_text: Some(snippet.body.clone()),
16971 sort_text: Some(char::MAX.to_string()),
16972 ..Default::default()
16973 },
16974 confirm: None,
16975 })
16976 })
16977 .collect();
16978
16979 Ok(result)
16980 })
16981}
16982
16983impl CompletionProvider for Entity<Project> {
16984 fn completions(
16985 &self,
16986 buffer: &Entity<Buffer>,
16987 buffer_position: text::Anchor,
16988 options: CompletionContext,
16989 _window: &mut Window,
16990 cx: &mut Context<Editor>,
16991 ) -> Task<Result<Vec<Completion>>> {
16992 self.update(cx, |project, cx| {
16993 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16994 let project_completions = project.completions(buffer, buffer_position, options, cx);
16995 cx.background_spawn(async move {
16996 let mut completions = project_completions.await?;
16997 let snippets_completions = snippets.await?;
16998 completions.extend(snippets_completions);
16999 Ok(completions)
17000 })
17001 })
17002 }
17003
17004 fn resolve_completions(
17005 &self,
17006 buffer: Entity<Buffer>,
17007 completion_indices: Vec<usize>,
17008 completions: Rc<RefCell<Box<[Completion]>>>,
17009 cx: &mut Context<Editor>,
17010 ) -> Task<Result<bool>> {
17011 self.update(cx, |project, cx| {
17012 project.lsp_store().update(cx, |lsp_store, cx| {
17013 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
17014 })
17015 })
17016 }
17017
17018 fn apply_additional_edits_for_completion(
17019 &self,
17020 buffer: Entity<Buffer>,
17021 completions: Rc<RefCell<Box<[Completion]>>>,
17022 completion_index: usize,
17023 push_to_history: bool,
17024 cx: &mut Context<Editor>,
17025 ) -> Task<Result<Option<language::Transaction>>> {
17026 self.update(cx, |project, cx| {
17027 project.lsp_store().update(cx, |lsp_store, cx| {
17028 lsp_store.apply_additional_edits_for_completion(
17029 buffer,
17030 completions,
17031 completion_index,
17032 push_to_history,
17033 cx,
17034 )
17035 })
17036 })
17037 }
17038
17039 fn is_completion_trigger(
17040 &self,
17041 buffer: &Entity<Buffer>,
17042 position: language::Anchor,
17043 text: &str,
17044 trigger_in_words: bool,
17045 cx: &mut Context<Editor>,
17046 ) -> bool {
17047 let mut chars = text.chars();
17048 let char = if let Some(char) = chars.next() {
17049 char
17050 } else {
17051 return false;
17052 };
17053 if chars.next().is_some() {
17054 return false;
17055 }
17056
17057 let buffer = buffer.read(cx);
17058 let snapshot = buffer.snapshot();
17059 if !snapshot.settings_at(position, cx).show_completions_on_input {
17060 return false;
17061 }
17062 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17063 if trigger_in_words && classifier.is_word(char) {
17064 return true;
17065 }
17066
17067 buffer.completion_triggers().contains(text)
17068 }
17069}
17070
17071impl SemanticsProvider for Entity<Project> {
17072 fn hover(
17073 &self,
17074 buffer: &Entity<Buffer>,
17075 position: text::Anchor,
17076 cx: &mut App,
17077 ) -> Option<Task<Vec<project::Hover>>> {
17078 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17079 }
17080
17081 fn document_highlights(
17082 &self,
17083 buffer: &Entity<Buffer>,
17084 position: text::Anchor,
17085 cx: &mut App,
17086 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
17087 Some(self.update(cx, |project, cx| {
17088 project.document_highlights(buffer, position, cx)
17089 }))
17090 }
17091
17092 fn definitions(
17093 &self,
17094 buffer: &Entity<Buffer>,
17095 position: text::Anchor,
17096 kind: GotoDefinitionKind,
17097 cx: &mut App,
17098 ) -> Option<Task<Result<Vec<LocationLink>>>> {
17099 Some(self.update(cx, |project, cx| match kind {
17100 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
17101 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
17102 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
17103 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
17104 }))
17105 }
17106
17107 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
17108 // TODO: make this work for remote projects
17109 self.update(cx, |this, cx| {
17110 buffer.update(cx, |buffer, cx| {
17111 this.any_language_server_supports_inlay_hints(buffer, cx)
17112 })
17113 })
17114 }
17115
17116 fn inlay_hints(
17117 &self,
17118 buffer_handle: Entity<Buffer>,
17119 range: Range<text::Anchor>,
17120 cx: &mut App,
17121 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
17122 Some(self.update(cx, |project, cx| {
17123 project.inlay_hints(buffer_handle, range, cx)
17124 }))
17125 }
17126
17127 fn resolve_inlay_hint(
17128 &self,
17129 hint: InlayHint,
17130 buffer_handle: Entity<Buffer>,
17131 server_id: LanguageServerId,
17132 cx: &mut App,
17133 ) -> Option<Task<anyhow::Result<InlayHint>>> {
17134 Some(self.update(cx, |project, cx| {
17135 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
17136 }))
17137 }
17138
17139 fn range_for_rename(
17140 &self,
17141 buffer: &Entity<Buffer>,
17142 position: text::Anchor,
17143 cx: &mut App,
17144 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
17145 Some(self.update(cx, |project, cx| {
17146 let buffer = buffer.clone();
17147 let task = project.prepare_rename(buffer.clone(), position, cx);
17148 cx.spawn(|_, mut cx| async move {
17149 Ok(match task.await? {
17150 PrepareRenameResponse::Success(range) => Some(range),
17151 PrepareRenameResponse::InvalidPosition => None,
17152 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
17153 // Fallback on using TreeSitter info to determine identifier range
17154 buffer.update(&mut cx, |buffer, _| {
17155 let snapshot = buffer.snapshot();
17156 let (range, kind) = snapshot.surrounding_word(position);
17157 if kind != Some(CharKind::Word) {
17158 return None;
17159 }
17160 Some(
17161 snapshot.anchor_before(range.start)
17162 ..snapshot.anchor_after(range.end),
17163 )
17164 })?
17165 }
17166 })
17167 })
17168 }))
17169 }
17170
17171 fn perform_rename(
17172 &self,
17173 buffer: &Entity<Buffer>,
17174 position: text::Anchor,
17175 new_name: String,
17176 cx: &mut App,
17177 ) -> Option<Task<Result<ProjectTransaction>>> {
17178 Some(self.update(cx, |project, cx| {
17179 project.perform_rename(buffer.clone(), position, new_name, cx)
17180 }))
17181 }
17182}
17183
17184fn inlay_hint_settings(
17185 location: Anchor,
17186 snapshot: &MultiBufferSnapshot,
17187 cx: &mut Context<Editor>,
17188) -> InlayHintSettings {
17189 let file = snapshot.file_at(location);
17190 let language = snapshot.language_at(location).map(|l| l.name());
17191 language_settings(language, file, cx).inlay_hints
17192}
17193
17194fn consume_contiguous_rows(
17195 contiguous_row_selections: &mut Vec<Selection<Point>>,
17196 selection: &Selection<Point>,
17197 display_map: &DisplaySnapshot,
17198 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
17199) -> (MultiBufferRow, MultiBufferRow) {
17200 contiguous_row_selections.push(selection.clone());
17201 let start_row = MultiBufferRow(selection.start.row);
17202 let mut end_row = ending_row(selection, display_map);
17203
17204 while let Some(next_selection) = selections.peek() {
17205 if next_selection.start.row <= end_row.0 {
17206 end_row = ending_row(next_selection, display_map);
17207 contiguous_row_selections.push(selections.next().unwrap().clone());
17208 } else {
17209 break;
17210 }
17211 }
17212 (start_row, end_row)
17213}
17214
17215fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17216 if next_selection.end.column > 0 || next_selection.is_empty() {
17217 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17218 } else {
17219 MultiBufferRow(next_selection.end.row)
17220 }
17221}
17222
17223impl EditorSnapshot {
17224 pub fn remote_selections_in_range<'a>(
17225 &'a self,
17226 range: &'a Range<Anchor>,
17227 collaboration_hub: &dyn CollaborationHub,
17228 cx: &'a App,
17229 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17230 let participant_names = collaboration_hub.user_names(cx);
17231 let participant_indices = collaboration_hub.user_participant_indices(cx);
17232 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17233 let collaborators_by_replica_id = collaborators_by_peer_id
17234 .iter()
17235 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17236 .collect::<HashMap<_, _>>();
17237 self.buffer_snapshot
17238 .selections_in_range(range, false)
17239 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17240 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17241 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17242 let user_name = participant_names.get(&collaborator.user_id).cloned();
17243 Some(RemoteSelection {
17244 replica_id,
17245 selection,
17246 cursor_shape,
17247 line_mode,
17248 participant_index,
17249 peer_id: collaborator.peer_id,
17250 user_name,
17251 })
17252 })
17253 }
17254
17255 pub fn hunks_for_ranges(
17256 &self,
17257 ranges: impl IntoIterator<Item = Range<Point>>,
17258 ) -> Vec<MultiBufferDiffHunk> {
17259 let mut hunks = Vec::new();
17260 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17261 HashMap::default();
17262 for query_range in ranges {
17263 let query_rows =
17264 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17265 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17266 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17267 ) {
17268 // Include deleted hunks that are adjacent to the query range, because
17269 // otherwise they would be missed.
17270 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
17271 if hunk.status().is_deleted() {
17272 intersects_range |= hunk.row_range.start == query_rows.end;
17273 intersects_range |= hunk.row_range.end == query_rows.start;
17274 }
17275 if intersects_range {
17276 if !processed_buffer_rows
17277 .entry(hunk.buffer_id)
17278 .or_default()
17279 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17280 {
17281 continue;
17282 }
17283 hunks.push(hunk);
17284 }
17285 }
17286 }
17287
17288 hunks
17289 }
17290
17291 fn display_diff_hunks_for_rows<'a>(
17292 &'a self,
17293 display_rows: Range<DisplayRow>,
17294 folded_buffers: &'a HashSet<BufferId>,
17295 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17296 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17297 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17298
17299 self.buffer_snapshot
17300 .diff_hunks_in_range(buffer_start..buffer_end)
17301 .filter_map(|hunk| {
17302 if folded_buffers.contains(&hunk.buffer_id) {
17303 return None;
17304 }
17305
17306 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17307 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17308
17309 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17310 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17311
17312 let display_hunk = if hunk_display_start.column() != 0 {
17313 DisplayDiffHunk::Folded {
17314 display_row: hunk_display_start.row(),
17315 }
17316 } else {
17317 let mut end_row = hunk_display_end.row();
17318 if hunk_display_end.column() > 0 {
17319 end_row.0 += 1;
17320 }
17321 let is_created_file = hunk.is_created_file();
17322 DisplayDiffHunk::Unfolded {
17323 status: hunk.status(),
17324 diff_base_byte_range: hunk.diff_base_byte_range,
17325 display_row_range: hunk_display_start.row()..end_row,
17326 multi_buffer_range: Anchor::range_in_buffer(
17327 hunk.excerpt_id,
17328 hunk.buffer_id,
17329 hunk.buffer_range,
17330 ),
17331 is_created_file,
17332 }
17333 };
17334
17335 Some(display_hunk)
17336 })
17337 }
17338
17339 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17340 self.display_snapshot.buffer_snapshot.language_at(position)
17341 }
17342
17343 pub fn is_focused(&self) -> bool {
17344 self.is_focused
17345 }
17346
17347 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17348 self.placeholder_text.as_ref()
17349 }
17350
17351 pub fn scroll_position(&self) -> gpui::Point<f32> {
17352 self.scroll_anchor.scroll_position(&self.display_snapshot)
17353 }
17354
17355 fn gutter_dimensions(
17356 &self,
17357 font_id: FontId,
17358 font_size: Pixels,
17359 max_line_number_width: Pixels,
17360 cx: &App,
17361 ) -> Option<GutterDimensions> {
17362 if !self.show_gutter {
17363 return None;
17364 }
17365
17366 let descent = cx.text_system().descent(font_id, font_size);
17367 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17368 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17369
17370 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17371 matches!(
17372 ProjectSettings::get_global(cx).git.git_gutter,
17373 Some(GitGutterSetting::TrackedFiles)
17374 )
17375 });
17376 let gutter_settings = EditorSettings::get_global(cx).gutter;
17377 let show_line_numbers = self
17378 .show_line_numbers
17379 .unwrap_or(gutter_settings.line_numbers);
17380 let line_gutter_width = if show_line_numbers {
17381 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17382 let min_width_for_number_on_gutter = em_advance * 4.0;
17383 max_line_number_width.max(min_width_for_number_on_gutter)
17384 } else {
17385 0.0.into()
17386 };
17387
17388 let show_code_actions = self
17389 .show_code_actions
17390 .unwrap_or(gutter_settings.code_actions);
17391
17392 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17393
17394 let git_blame_entries_width =
17395 self.git_blame_gutter_max_author_length
17396 .map(|max_author_length| {
17397 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17398
17399 /// The number of characters to dedicate to gaps and margins.
17400 const SPACING_WIDTH: usize = 4;
17401
17402 let max_char_count = max_author_length
17403 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17404 + ::git::SHORT_SHA_LENGTH
17405 + MAX_RELATIVE_TIMESTAMP.len()
17406 + SPACING_WIDTH;
17407
17408 em_advance * max_char_count
17409 });
17410
17411 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17412 left_padding += if show_code_actions || show_runnables {
17413 em_width * 3.0
17414 } else if show_git_gutter && show_line_numbers {
17415 em_width * 2.0
17416 } else if show_git_gutter || show_line_numbers {
17417 em_width
17418 } else {
17419 px(0.)
17420 };
17421
17422 let right_padding = if gutter_settings.folds && show_line_numbers {
17423 em_width * 4.0
17424 } else if gutter_settings.folds {
17425 em_width * 3.0
17426 } else if show_line_numbers {
17427 em_width
17428 } else {
17429 px(0.)
17430 };
17431
17432 Some(GutterDimensions {
17433 left_padding,
17434 right_padding,
17435 width: line_gutter_width + left_padding + right_padding,
17436 margin: -descent,
17437 git_blame_entries_width,
17438 })
17439 }
17440
17441 pub fn render_crease_toggle(
17442 &self,
17443 buffer_row: MultiBufferRow,
17444 row_contains_cursor: bool,
17445 editor: Entity<Editor>,
17446 window: &mut Window,
17447 cx: &mut App,
17448 ) -> Option<AnyElement> {
17449 let folded = self.is_line_folded(buffer_row);
17450 let mut is_foldable = false;
17451
17452 if let Some(crease) = self
17453 .crease_snapshot
17454 .query_row(buffer_row, &self.buffer_snapshot)
17455 {
17456 is_foldable = true;
17457 match crease {
17458 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17459 if let Some(render_toggle) = render_toggle {
17460 let toggle_callback =
17461 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17462 if folded {
17463 editor.update(cx, |editor, cx| {
17464 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17465 });
17466 } else {
17467 editor.update(cx, |editor, cx| {
17468 editor.unfold_at(
17469 &crate::UnfoldAt { buffer_row },
17470 window,
17471 cx,
17472 )
17473 });
17474 }
17475 });
17476 return Some((render_toggle)(
17477 buffer_row,
17478 folded,
17479 toggle_callback,
17480 window,
17481 cx,
17482 ));
17483 }
17484 }
17485 }
17486 }
17487
17488 is_foldable |= self.starts_indent(buffer_row);
17489
17490 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17491 Some(
17492 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17493 .toggle_state(folded)
17494 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17495 if folded {
17496 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17497 } else {
17498 this.fold_at(&FoldAt { buffer_row }, window, cx);
17499 }
17500 }))
17501 .into_any_element(),
17502 )
17503 } else {
17504 None
17505 }
17506 }
17507
17508 pub fn render_crease_trailer(
17509 &self,
17510 buffer_row: MultiBufferRow,
17511 window: &mut Window,
17512 cx: &mut App,
17513 ) -> Option<AnyElement> {
17514 let folded = self.is_line_folded(buffer_row);
17515 if let Crease::Inline { render_trailer, .. } = self
17516 .crease_snapshot
17517 .query_row(buffer_row, &self.buffer_snapshot)?
17518 {
17519 let render_trailer = render_trailer.as_ref()?;
17520 Some(render_trailer(buffer_row, folded, window, cx))
17521 } else {
17522 None
17523 }
17524 }
17525}
17526
17527impl Deref for EditorSnapshot {
17528 type Target = DisplaySnapshot;
17529
17530 fn deref(&self) -> &Self::Target {
17531 &self.display_snapshot
17532 }
17533}
17534
17535#[derive(Clone, Debug, PartialEq, Eq)]
17536pub enum EditorEvent {
17537 InputIgnored {
17538 text: Arc<str>,
17539 },
17540 InputHandled {
17541 utf16_range_to_replace: Option<Range<isize>>,
17542 text: Arc<str>,
17543 },
17544 ExcerptsAdded {
17545 buffer: Entity<Buffer>,
17546 predecessor: ExcerptId,
17547 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17548 },
17549 ExcerptsRemoved {
17550 ids: Vec<ExcerptId>,
17551 },
17552 BufferFoldToggled {
17553 ids: Vec<ExcerptId>,
17554 folded: bool,
17555 },
17556 ExcerptsEdited {
17557 ids: Vec<ExcerptId>,
17558 },
17559 ExcerptsExpanded {
17560 ids: Vec<ExcerptId>,
17561 },
17562 BufferEdited,
17563 Edited {
17564 transaction_id: clock::Lamport,
17565 },
17566 Reparsed(BufferId),
17567 Focused,
17568 FocusedIn,
17569 Blurred,
17570 DirtyChanged,
17571 Saved,
17572 TitleChanged,
17573 DiffBaseChanged,
17574 SelectionsChanged {
17575 local: bool,
17576 },
17577 ScrollPositionChanged {
17578 local: bool,
17579 autoscroll: bool,
17580 },
17581 Closed,
17582 TransactionUndone {
17583 transaction_id: clock::Lamport,
17584 },
17585 TransactionBegun {
17586 transaction_id: clock::Lamport,
17587 },
17588 Reloaded,
17589 CursorShapeChanged,
17590}
17591
17592impl EventEmitter<EditorEvent> for Editor {}
17593
17594impl Focusable for Editor {
17595 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17596 self.focus_handle.clone()
17597 }
17598}
17599
17600impl Render for Editor {
17601 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17602 let settings = ThemeSettings::get_global(cx);
17603
17604 let mut text_style = match self.mode {
17605 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17606 color: cx.theme().colors().editor_foreground,
17607 font_family: settings.ui_font.family.clone(),
17608 font_features: settings.ui_font.features.clone(),
17609 font_fallbacks: settings.ui_font.fallbacks.clone(),
17610 font_size: rems(0.875).into(),
17611 font_weight: settings.ui_font.weight,
17612 line_height: relative(settings.buffer_line_height.value()),
17613 ..Default::default()
17614 },
17615 EditorMode::Full => TextStyle {
17616 color: cx.theme().colors().editor_foreground,
17617 font_family: settings.buffer_font.family.clone(),
17618 font_features: settings.buffer_font.features.clone(),
17619 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17620 font_size: settings.buffer_font_size(cx).into(),
17621 font_weight: settings.buffer_font.weight,
17622 line_height: relative(settings.buffer_line_height.value()),
17623 ..Default::default()
17624 },
17625 };
17626 if let Some(text_style_refinement) = &self.text_style_refinement {
17627 text_style.refine(text_style_refinement)
17628 }
17629
17630 let background = match self.mode {
17631 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17632 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17633 EditorMode::Full => cx.theme().colors().editor_background,
17634 };
17635
17636 EditorElement::new(
17637 &cx.entity(),
17638 EditorStyle {
17639 background,
17640 local_player: cx.theme().players().local(),
17641 text: text_style,
17642 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17643 syntax: cx.theme().syntax().clone(),
17644 status: cx.theme().status().clone(),
17645 inlay_hints_style: make_inlay_hints_style(cx),
17646 inline_completion_styles: make_suggestion_styles(cx),
17647 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17648 },
17649 )
17650 }
17651}
17652
17653impl EntityInputHandler for Editor {
17654 fn text_for_range(
17655 &mut self,
17656 range_utf16: Range<usize>,
17657 adjusted_range: &mut Option<Range<usize>>,
17658 _: &mut Window,
17659 cx: &mut Context<Self>,
17660 ) -> Option<String> {
17661 let snapshot = self.buffer.read(cx).read(cx);
17662 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17663 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17664 if (start.0..end.0) != range_utf16 {
17665 adjusted_range.replace(start.0..end.0);
17666 }
17667 Some(snapshot.text_for_range(start..end).collect())
17668 }
17669
17670 fn selected_text_range(
17671 &mut self,
17672 ignore_disabled_input: bool,
17673 _: &mut Window,
17674 cx: &mut Context<Self>,
17675 ) -> Option<UTF16Selection> {
17676 // Prevent the IME menu from appearing when holding down an alphabetic key
17677 // while input is disabled.
17678 if !ignore_disabled_input && !self.input_enabled {
17679 return None;
17680 }
17681
17682 let selection = self.selections.newest::<OffsetUtf16>(cx);
17683 let range = selection.range();
17684
17685 Some(UTF16Selection {
17686 range: range.start.0..range.end.0,
17687 reversed: selection.reversed,
17688 })
17689 }
17690
17691 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17692 let snapshot = self.buffer.read(cx).read(cx);
17693 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17694 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17695 }
17696
17697 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17698 self.clear_highlights::<InputComposition>(cx);
17699 self.ime_transaction.take();
17700 }
17701
17702 fn replace_text_in_range(
17703 &mut self,
17704 range_utf16: Option<Range<usize>>,
17705 text: &str,
17706 window: &mut Window,
17707 cx: &mut Context<Self>,
17708 ) {
17709 if !self.input_enabled {
17710 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17711 return;
17712 }
17713
17714 self.transact(window, cx, |this, window, cx| {
17715 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17716 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17717 Some(this.selection_replacement_ranges(range_utf16, cx))
17718 } else {
17719 this.marked_text_ranges(cx)
17720 };
17721
17722 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17723 let newest_selection_id = this.selections.newest_anchor().id;
17724 this.selections
17725 .all::<OffsetUtf16>(cx)
17726 .iter()
17727 .zip(ranges_to_replace.iter())
17728 .find_map(|(selection, range)| {
17729 if selection.id == newest_selection_id {
17730 Some(
17731 (range.start.0 as isize - selection.head().0 as isize)
17732 ..(range.end.0 as isize - selection.head().0 as isize),
17733 )
17734 } else {
17735 None
17736 }
17737 })
17738 });
17739
17740 cx.emit(EditorEvent::InputHandled {
17741 utf16_range_to_replace: range_to_replace,
17742 text: text.into(),
17743 });
17744
17745 if let Some(new_selected_ranges) = new_selected_ranges {
17746 this.change_selections(None, window, cx, |selections| {
17747 selections.select_ranges(new_selected_ranges)
17748 });
17749 this.backspace(&Default::default(), window, cx);
17750 }
17751
17752 this.handle_input(text, window, cx);
17753 });
17754
17755 if let Some(transaction) = self.ime_transaction {
17756 self.buffer.update(cx, |buffer, cx| {
17757 buffer.group_until_transaction(transaction, cx);
17758 });
17759 }
17760
17761 self.unmark_text(window, cx);
17762 }
17763
17764 fn replace_and_mark_text_in_range(
17765 &mut self,
17766 range_utf16: Option<Range<usize>>,
17767 text: &str,
17768 new_selected_range_utf16: Option<Range<usize>>,
17769 window: &mut Window,
17770 cx: &mut Context<Self>,
17771 ) {
17772 if !self.input_enabled {
17773 return;
17774 }
17775
17776 let transaction = self.transact(window, cx, |this, window, cx| {
17777 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17778 let snapshot = this.buffer.read(cx).read(cx);
17779 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17780 for marked_range in &mut marked_ranges {
17781 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17782 marked_range.start.0 += relative_range_utf16.start;
17783 marked_range.start =
17784 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
17785 marked_range.end =
17786 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
17787 }
17788 }
17789 Some(marked_ranges)
17790 } else if let Some(range_utf16) = range_utf16 {
17791 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17792 Some(this.selection_replacement_ranges(range_utf16, cx))
17793 } else {
17794 None
17795 };
17796
17797 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
17798 let newest_selection_id = this.selections.newest_anchor().id;
17799 this.selections
17800 .all::<OffsetUtf16>(cx)
17801 .iter()
17802 .zip(ranges_to_replace.iter())
17803 .find_map(|(selection, range)| {
17804 if selection.id == newest_selection_id {
17805 Some(
17806 (range.start.0 as isize - selection.head().0 as isize)
17807 ..(range.end.0 as isize - selection.head().0 as isize),
17808 )
17809 } else {
17810 None
17811 }
17812 })
17813 });
17814
17815 cx.emit(EditorEvent::InputHandled {
17816 utf16_range_to_replace: range_to_replace,
17817 text: text.into(),
17818 });
17819
17820 if let Some(ranges) = ranges_to_replace {
17821 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
17822 }
17823
17824 let marked_ranges = {
17825 let snapshot = this.buffer.read(cx).read(cx);
17826 this.selections
17827 .disjoint_anchors()
17828 .iter()
17829 .map(|selection| {
17830 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
17831 })
17832 .collect::<Vec<_>>()
17833 };
17834
17835 if text.is_empty() {
17836 this.unmark_text(window, cx);
17837 } else {
17838 this.highlight_text::<InputComposition>(
17839 marked_ranges.clone(),
17840 HighlightStyle {
17841 underline: Some(UnderlineStyle {
17842 thickness: px(1.),
17843 color: None,
17844 wavy: false,
17845 }),
17846 ..Default::default()
17847 },
17848 cx,
17849 );
17850 }
17851
17852 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
17853 let use_autoclose = this.use_autoclose;
17854 let use_auto_surround = this.use_auto_surround;
17855 this.set_use_autoclose(false);
17856 this.set_use_auto_surround(false);
17857 this.handle_input(text, window, cx);
17858 this.set_use_autoclose(use_autoclose);
17859 this.set_use_auto_surround(use_auto_surround);
17860
17861 if let Some(new_selected_range) = new_selected_range_utf16 {
17862 let snapshot = this.buffer.read(cx).read(cx);
17863 let new_selected_ranges = marked_ranges
17864 .into_iter()
17865 .map(|marked_range| {
17866 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
17867 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
17868 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
17869 snapshot.clip_offset_utf16(new_start, Bias::Left)
17870 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
17871 })
17872 .collect::<Vec<_>>();
17873
17874 drop(snapshot);
17875 this.change_selections(None, window, cx, |selections| {
17876 selections.select_ranges(new_selected_ranges)
17877 });
17878 }
17879 });
17880
17881 self.ime_transaction = self.ime_transaction.or(transaction);
17882 if let Some(transaction) = self.ime_transaction {
17883 self.buffer.update(cx, |buffer, cx| {
17884 buffer.group_until_transaction(transaction, cx);
17885 });
17886 }
17887
17888 if self.text_highlights::<InputComposition>(cx).is_none() {
17889 self.ime_transaction.take();
17890 }
17891 }
17892
17893 fn bounds_for_range(
17894 &mut self,
17895 range_utf16: Range<usize>,
17896 element_bounds: gpui::Bounds<Pixels>,
17897 window: &mut Window,
17898 cx: &mut Context<Self>,
17899 ) -> Option<gpui::Bounds<Pixels>> {
17900 let text_layout_details = self.text_layout_details(window);
17901 let gpui::Size {
17902 width: em_width,
17903 height: line_height,
17904 } = self.character_size(window);
17905
17906 let snapshot = self.snapshot(window, cx);
17907 let scroll_position = snapshot.scroll_position();
17908 let scroll_left = scroll_position.x * em_width;
17909
17910 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
17911 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
17912 + self.gutter_dimensions.width
17913 + self.gutter_dimensions.margin;
17914 let y = line_height * (start.row().as_f32() - scroll_position.y);
17915
17916 Some(Bounds {
17917 origin: element_bounds.origin + point(x, y),
17918 size: size(em_width, line_height),
17919 })
17920 }
17921
17922 fn character_index_for_point(
17923 &mut self,
17924 point: gpui::Point<Pixels>,
17925 _window: &mut Window,
17926 _cx: &mut Context<Self>,
17927 ) -> Option<usize> {
17928 let position_map = self.last_position_map.as_ref()?;
17929 if !position_map.text_hitbox.contains(&point) {
17930 return None;
17931 }
17932 let display_point = position_map.point_for_position(point).previous_valid;
17933 let anchor = position_map
17934 .snapshot
17935 .display_point_to_anchor(display_point, Bias::Left);
17936 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
17937 Some(utf16_offset.0)
17938 }
17939}
17940
17941trait SelectionExt {
17942 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
17943 fn spanned_rows(
17944 &self,
17945 include_end_if_at_line_start: bool,
17946 map: &DisplaySnapshot,
17947 ) -> Range<MultiBufferRow>;
17948}
17949
17950impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
17951 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
17952 let start = self
17953 .start
17954 .to_point(&map.buffer_snapshot)
17955 .to_display_point(map);
17956 let end = self
17957 .end
17958 .to_point(&map.buffer_snapshot)
17959 .to_display_point(map);
17960 if self.reversed {
17961 end..start
17962 } else {
17963 start..end
17964 }
17965 }
17966
17967 fn spanned_rows(
17968 &self,
17969 include_end_if_at_line_start: bool,
17970 map: &DisplaySnapshot,
17971 ) -> Range<MultiBufferRow> {
17972 let start = self.start.to_point(&map.buffer_snapshot);
17973 let mut end = self.end.to_point(&map.buffer_snapshot);
17974 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
17975 end.row -= 1;
17976 }
17977
17978 let buffer_start = map.prev_line_boundary(start).0;
17979 let buffer_end = map.next_line_boundary(end).0;
17980 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
17981 }
17982}
17983
17984impl<T: InvalidationRegion> InvalidationStack<T> {
17985 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
17986 where
17987 S: Clone + ToOffset,
17988 {
17989 while let Some(region) = self.last() {
17990 let all_selections_inside_invalidation_ranges =
17991 if selections.len() == region.ranges().len() {
17992 selections
17993 .iter()
17994 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
17995 .all(|(selection, invalidation_range)| {
17996 let head = selection.head().to_offset(buffer);
17997 invalidation_range.start <= head && invalidation_range.end >= head
17998 })
17999 } else {
18000 false
18001 };
18002
18003 if all_selections_inside_invalidation_ranges {
18004 break;
18005 } else {
18006 self.pop();
18007 }
18008 }
18009 }
18010}
18011
18012impl<T> Default for InvalidationStack<T> {
18013 fn default() -> Self {
18014 Self(Default::default())
18015 }
18016}
18017
18018impl<T> Deref for InvalidationStack<T> {
18019 type Target = Vec<T>;
18020
18021 fn deref(&self) -> &Self::Target {
18022 &self.0
18023 }
18024}
18025
18026impl<T> DerefMut for InvalidationStack<T> {
18027 fn deref_mut(&mut self) -> &mut Self::Target {
18028 &mut self.0
18029 }
18030}
18031
18032impl InvalidationRegion for SnippetState {
18033 fn ranges(&self) -> &[Range<Anchor>] {
18034 &self.ranges[self.active_index]
18035 }
18036}
18037
18038pub fn diagnostic_block_renderer(
18039 diagnostic: Diagnostic,
18040 max_message_rows: Option<u8>,
18041 allow_closing: bool,
18042) -> RenderBlock {
18043 let (text_without_backticks, code_ranges) =
18044 highlight_diagnostic_message(&diagnostic, max_message_rows);
18045
18046 Arc::new(move |cx: &mut BlockContext| {
18047 let group_id: SharedString = cx.block_id.to_string().into();
18048
18049 let mut text_style = cx.window.text_style().clone();
18050 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18051 let theme_settings = ThemeSettings::get_global(cx);
18052 text_style.font_family = theme_settings.buffer_font.family.clone();
18053 text_style.font_style = theme_settings.buffer_font.style;
18054 text_style.font_features = theme_settings.buffer_font.features.clone();
18055 text_style.font_weight = theme_settings.buffer_font.weight;
18056
18057 let multi_line_diagnostic = diagnostic.message.contains('\n');
18058
18059 let buttons = |diagnostic: &Diagnostic| {
18060 if multi_line_diagnostic {
18061 v_flex()
18062 } else {
18063 h_flex()
18064 }
18065 .when(allow_closing, |div| {
18066 div.children(diagnostic.is_primary.then(|| {
18067 IconButton::new("close-block", IconName::XCircle)
18068 .icon_color(Color::Muted)
18069 .size(ButtonSize::Compact)
18070 .style(ButtonStyle::Transparent)
18071 .visible_on_hover(group_id.clone())
18072 .on_click(move |_click, window, cx| {
18073 window.dispatch_action(Box::new(Cancel), cx)
18074 })
18075 .tooltip(|window, cx| {
18076 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18077 })
18078 }))
18079 })
18080 .child(
18081 IconButton::new("copy-block", IconName::Copy)
18082 .icon_color(Color::Muted)
18083 .size(ButtonSize::Compact)
18084 .style(ButtonStyle::Transparent)
18085 .visible_on_hover(group_id.clone())
18086 .on_click({
18087 let message = diagnostic.message.clone();
18088 move |_click, _, cx| {
18089 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
18090 }
18091 })
18092 .tooltip(Tooltip::text("Copy diagnostic message")),
18093 )
18094 };
18095
18096 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
18097 AvailableSpace::min_size(),
18098 cx.window,
18099 cx.app,
18100 );
18101
18102 h_flex()
18103 .id(cx.block_id)
18104 .group(group_id.clone())
18105 .relative()
18106 .size_full()
18107 .block_mouse_down()
18108 .pl(cx.gutter_dimensions.width)
18109 .w(cx.max_width - cx.gutter_dimensions.full_width())
18110 .child(
18111 div()
18112 .flex()
18113 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
18114 .flex_shrink(),
18115 )
18116 .child(buttons(&diagnostic))
18117 .child(div().flex().flex_shrink_0().child(
18118 StyledText::new(text_without_backticks.clone()).with_default_highlights(
18119 &text_style,
18120 code_ranges.iter().map(|range| {
18121 (
18122 range.clone(),
18123 HighlightStyle {
18124 font_weight: Some(FontWeight::BOLD),
18125 ..Default::default()
18126 },
18127 )
18128 }),
18129 ),
18130 ))
18131 .into_any_element()
18132 })
18133}
18134
18135fn inline_completion_edit_text(
18136 current_snapshot: &BufferSnapshot,
18137 edits: &[(Range<Anchor>, String)],
18138 edit_preview: &EditPreview,
18139 include_deletions: bool,
18140 cx: &App,
18141) -> HighlightedText {
18142 let edits = edits
18143 .iter()
18144 .map(|(anchor, text)| {
18145 (
18146 anchor.start.text_anchor..anchor.end.text_anchor,
18147 text.clone(),
18148 )
18149 })
18150 .collect::<Vec<_>>();
18151
18152 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
18153}
18154
18155pub fn highlight_diagnostic_message(
18156 diagnostic: &Diagnostic,
18157 mut max_message_rows: Option<u8>,
18158) -> (SharedString, Vec<Range<usize>>) {
18159 let mut text_without_backticks = String::new();
18160 let mut code_ranges = Vec::new();
18161
18162 if let Some(source) = &diagnostic.source {
18163 text_without_backticks.push_str(source);
18164 code_ranges.push(0..source.len());
18165 text_without_backticks.push_str(": ");
18166 }
18167
18168 let mut prev_offset = 0;
18169 let mut in_code_block = false;
18170 let has_row_limit = max_message_rows.is_some();
18171 let mut newline_indices = diagnostic
18172 .message
18173 .match_indices('\n')
18174 .filter(|_| has_row_limit)
18175 .map(|(ix, _)| ix)
18176 .fuse()
18177 .peekable();
18178
18179 for (quote_ix, _) in diagnostic
18180 .message
18181 .match_indices('`')
18182 .chain([(diagnostic.message.len(), "")])
18183 {
18184 let mut first_newline_ix = None;
18185 let mut last_newline_ix = None;
18186 while let Some(newline_ix) = newline_indices.peek() {
18187 if *newline_ix < quote_ix {
18188 if first_newline_ix.is_none() {
18189 first_newline_ix = Some(*newline_ix);
18190 }
18191 last_newline_ix = Some(*newline_ix);
18192
18193 if let Some(rows_left) = &mut max_message_rows {
18194 if *rows_left == 0 {
18195 break;
18196 } else {
18197 *rows_left -= 1;
18198 }
18199 }
18200 let _ = newline_indices.next();
18201 } else {
18202 break;
18203 }
18204 }
18205 let prev_len = text_without_backticks.len();
18206 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18207 text_without_backticks.push_str(new_text);
18208 if in_code_block {
18209 code_ranges.push(prev_len..text_without_backticks.len());
18210 }
18211 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18212 in_code_block = !in_code_block;
18213 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18214 text_without_backticks.push_str("...");
18215 break;
18216 }
18217 }
18218
18219 (text_without_backticks.into(), code_ranges)
18220}
18221
18222fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18223 match severity {
18224 DiagnosticSeverity::ERROR => colors.error,
18225 DiagnosticSeverity::WARNING => colors.warning,
18226 DiagnosticSeverity::INFORMATION => colors.info,
18227 DiagnosticSeverity::HINT => colors.info,
18228 _ => colors.ignored,
18229 }
18230}
18231
18232pub fn styled_runs_for_code_label<'a>(
18233 label: &'a CodeLabel,
18234 syntax_theme: &'a theme::SyntaxTheme,
18235) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18236 let fade_out = HighlightStyle {
18237 fade_out: Some(0.35),
18238 ..Default::default()
18239 };
18240
18241 let mut prev_end = label.filter_range.end;
18242 label
18243 .runs
18244 .iter()
18245 .enumerate()
18246 .flat_map(move |(ix, (range, highlight_id))| {
18247 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18248 style
18249 } else {
18250 return Default::default();
18251 };
18252 let mut muted_style = style;
18253 muted_style.highlight(fade_out);
18254
18255 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18256 if range.start >= label.filter_range.end {
18257 if range.start > prev_end {
18258 runs.push((prev_end..range.start, fade_out));
18259 }
18260 runs.push((range.clone(), muted_style));
18261 } else if range.end <= label.filter_range.end {
18262 runs.push((range.clone(), style));
18263 } else {
18264 runs.push((range.start..label.filter_range.end, style));
18265 runs.push((label.filter_range.end..range.end, muted_style));
18266 }
18267 prev_end = cmp::max(prev_end, range.end);
18268
18269 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18270 runs.push((prev_end..label.text.len(), fade_out));
18271 }
18272
18273 runs
18274 })
18275}
18276
18277pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18278 let mut prev_index = 0;
18279 let mut prev_codepoint: Option<char> = None;
18280 text.char_indices()
18281 .chain([(text.len(), '\0')])
18282 .filter_map(move |(index, codepoint)| {
18283 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18284 let is_boundary = index == text.len()
18285 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18286 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18287 if is_boundary {
18288 let chunk = &text[prev_index..index];
18289 prev_index = index;
18290 Some(chunk)
18291 } else {
18292 None
18293 }
18294 })
18295}
18296
18297pub trait RangeToAnchorExt: Sized {
18298 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18299
18300 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18301 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18302 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18303 }
18304}
18305
18306impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18307 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18308 let start_offset = self.start.to_offset(snapshot);
18309 let end_offset = self.end.to_offset(snapshot);
18310 if start_offset == end_offset {
18311 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18312 } else {
18313 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18314 }
18315 }
18316}
18317
18318pub trait RowExt {
18319 fn as_f32(&self) -> f32;
18320
18321 fn next_row(&self) -> Self;
18322
18323 fn previous_row(&self) -> Self;
18324
18325 fn minus(&self, other: Self) -> u32;
18326}
18327
18328impl RowExt for DisplayRow {
18329 fn as_f32(&self) -> f32 {
18330 self.0 as f32
18331 }
18332
18333 fn next_row(&self) -> Self {
18334 Self(self.0 + 1)
18335 }
18336
18337 fn previous_row(&self) -> Self {
18338 Self(self.0.saturating_sub(1))
18339 }
18340
18341 fn minus(&self, other: Self) -> u32 {
18342 self.0 - other.0
18343 }
18344}
18345
18346impl RowExt for MultiBufferRow {
18347 fn as_f32(&self) -> f32 {
18348 self.0 as f32
18349 }
18350
18351 fn next_row(&self) -> Self {
18352 Self(self.0 + 1)
18353 }
18354
18355 fn previous_row(&self) -> Self {
18356 Self(self.0.saturating_sub(1))
18357 }
18358
18359 fn minus(&self, other: Self) -> u32 {
18360 self.0 - other.0
18361 }
18362}
18363
18364trait RowRangeExt {
18365 type Row;
18366
18367 fn len(&self) -> usize;
18368
18369 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18370}
18371
18372impl RowRangeExt for Range<MultiBufferRow> {
18373 type Row = MultiBufferRow;
18374
18375 fn len(&self) -> usize {
18376 (self.end.0 - self.start.0) as usize
18377 }
18378
18379 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18380 (self.start.0..self.end.0).map(MultiBufferRow)
18381 }
18382}
18383
18384impl RowRangeExt for Range<DisplayRow> {
18385 type Row = DisplayRow;
18386
18387 fn len(&self) -> usize {
18388 (self.end.0 - self.start.0) as usize
18389 }
18390
18391 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18392 (self.start.0..self.end.0).map(DisplayRow)
18393 }
18394}
18395
18396/// If select range has more than one line, we
18397/// just point the cursor to range.start.
18398fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18399 if range.start.row == range.end.row {
18400 range
18401 } else {
18402 range.start..range.start
18403 }
18404}
18405pub struct KillRing(ClipboardItem);
18406impl Global for KillRing {}
18407
18408const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18409
18410fn all_edits_insertions_or_deletions(
18411 edits: &Vec<(Range<Anchor>, String)>,
18412 snapshot: &MultiBufferSnapshot,
18413) -> bool {
18414 let mut all_insertions = true;
18415 let mut all_deletions = true;
18416
18417 for (range, new_text) in edits.iter() {
18418 let range_is_empty = range.to_offset(&snapshot).is_empty();
18419 let text_is_empty = new_text.is_empty();
18420
18421 if range_is_empty != text_is_empty {
18422 if range_is_empty {
18423 all_deletions = false;
18424 } else {
18425 all_insertions = false;
18426 }
18427 } else {
18428 return false;
18429 }
18430
18431 if !all_insertions && !all_deletions {
18432 return false;
18433 }
18434 }
18435 all_insertions || all_deletions
18436}
18437
18438#[derive(Debug, Clone, Copy, PartialEq)]
18439pub struct LineHighlight {
18440 pub background: Background,
18441 pub border: Option<gpui::Hsla>,
18442}
18443
18444impl From<Hsla> for LineHighlight {
18445 fn from(hsla: Hsla) -> Self {
18446 Self {
18447 background: hsla.into(),
18448 border: None,
18449 }
18450 }
18451}
18452
18453impl From<Background> for LineHighlight {
18454 fn from(background: Background) -> Self {
18455 Self {
18456 background,
18457 border: None,
18458 }
18459 }
18460}