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::DiffHunkSecondaryStatus;
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::{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::{status::FileStatus, 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, 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, DiskState,
106 EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language,
107 OffsetRangeExt, Point, 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 std::iter::Peekable;
117use task::{ResolvedTask, TaskTemplate, TaskVariables};
118
119use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
120pub use lsp::CompletionContext;
121use lsp::{
122 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
123 LanguageServerId, LanguageServerName,
124};
125
126use language::BufferSnapshot;
127use movement::TextLayoutDetails;
128pub use multi_buffer::{
129 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
130 ToOffset, ToPoint,
131};
132use multi_buffer::{
133 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
134 ToOffsetUtf16,
135};
136use project::{
137 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
138 project_settings::{GitGutterSetting, ProjectSettings},
139 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
140 PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
141};
142use rand::prelude::*;
143use rpc::{proto::*, ErrorExt};
144use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
145use selections_collection::{
146 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
147};
148use serde::{Deserialize, Serialize};
149use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
150use smallvec::SmallVec;
151use snippet::Snippet;
152use std::{
153 any::TypeId,
154 borrow::Cow,
155 cell::RefCell,
156 cmp::{self, Ordering, Reverse},
157 mem,
158 num::NonZeroU32,
159 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
160 path::{Path, PathBuf},
161 rc::Rc,
162 sync::Arc,
163 time::{Duration, Instant},
164};
165pub use sum_tree::Bias;
166use sum_tree::TreeMap;
167use text::{BufferId, OffsetUtf16, Rope};
168use theme::{
169 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
170 ThemeColors, ThemeSettings,
171};
172use ui::{
173 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
174 Tooltip,
175};
176use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
177use workspace::{
178 item::{ItemHandle, PreviewTabsSettings},
179 ItemId, RestoreOnStartupBehavior,
180};
181use workspace::{
182 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
183 WorkspaceSettings,
184};
185use workspace::{
186 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
187};
188use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
189
190use crate::hover_links::{find_url, find_url_from_range};
191use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
192
193pub const FILE_HEADER_HEIGHT: u32 = 2;
194pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
195pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
196pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
197const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
198const MAX_LINE_LEN: usize = 1024;
199const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
200const MAX_SELECTION_HISTORY_LEN: usize = 1024;
201pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
202#[doc(hidden)]
203pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
204
205pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
206pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
207
208pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
209pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
210
211const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
212 alt: true,
213 shift: true,
214 control: false,
215 platform: false,
216 function: false,
217};
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
220pub enum InlayId {
221 InlineCompletion(usize),
222 Hint(usize),
223}
224
225impl InlayId {
226 fn id(&self) -> usize {
227 match self {
228 Self::InlineCompletion(id) => *id,
229 Self::Hint(id) => *id,
230 }
231 }
232}
233
234enum DocumentHighlightRead {}
235enum DocumentHighlightWrite {}
236enum InputComposition {}
237enum SelectedTextHighlight {}
238
239#[derive(Debug, Copy, Clone, PartialEq, Eq)]
240pub enum Navigated {
241 Yes,
242 No,
243}
244
245impl Navigated {
246 pub fn from_bool(yes: bool) -> Navigated {
247 if yes {
248 Navigated::Yes
249 } else {
250 Navigated::No
251 }
252 }
253}
254
255pub fn init_settings(cx: &mut App) {
256 EditorSettings::register(cx);
257}
258
259pub fn init(cx: &mut App) {
260 init_settings(cx);
261
262 workspace::register_project_item::<Editor>(cx);
263 workspace::FollowableViewRegistry::register::<Editor>(cx);
264 workspace::register_serializable_item::<Editor>(cx);
265
266 cx.observe_new(
267 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
268 workspace.register_action(Editor::new_file);
269 workspace.register_action(Editor::new_file_vertical);
270 workspace.register_action(Editor::new_file_horizontal);
271 workspace.register_action(Editor::cancel_language_server_work);
272 },
273 )
274 .detach();
275
276 cx.on_action(move |_: &workspace::NewFile, cx| {
277 let app_state = workspace::AppState::global(cx);
278 if let Some(app_state) = app_state.upgrade() {
279 workspace::open_new(
280 Default::default(),
281 app_state,
282 cx,
283 |workspace, window, cx| {
284 Editor::new_file(workspace, &Default::default(), window, cx)
285 },
286 )
287 .detach();
288 }
289 });
290 cx.on_action(move |_: &workspace::NewWindow, cx| {
291 let app_state = workspace::AppState::global(cx);
292 if let Some(app_state) = app_state.upgrade() {
293 workspace::open_new(
294 Default::default(),
295 app_state,
296 cx,
297 |workspace, window, cx| {
298 cx.activate(true);
299 Editor::new_file(workspace, &Default::default(), window, cx)
300 },
301 )
302 .detach();
303 }
304 });
305}
306
307pub struct SearchWithinRange;
308
309trait InvalidationRegion {
310 fn ranges(&self) -> &[Range<Anchor>];
311}
312
313#[derive(Clone, Debug, PartialEq)]
314pub enum SelectPhase {
315 Begin {
316 position: DisplayPoint,
317 add: bool,
318 click_count: usize,
319 },
320 BeginColumnar {
321 position: DisplayPoint,
322 reset: bool,
323 goal_column: u32,
324 },
325 Extend {
326 position: DisplayPoint,
327 click_count: usize,
328 },
329 Update {
330 position: DisplayPoint,
331 goal_column: u32,
332 scroll_delta: gpui::Point<f32>,
333 },
334 End,
335}
336
337#[derive(Clone, Debug)]
338pub enum SelectMode {
339 Character,
340 Word(Range<Anchor>),
341 Line(Range<Anchor>),
342 All,
343}
344
345#[derive(Copy, Clone, PartialEq, Eq, Debug)]
346pub enum EditorMode {
347 SingleLine { auto_width: bool },
348 AutoHeight { max_lines: usize },
349 Full,
350}
351
352#[derive(Copy, Clone, Debug)]
353pub enum SoftWrap {
354 /// Prefer not to wrap at all.
355 ///
356 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
357 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
358 GitDiff,
359 /// Prefer a single line generally, unless an overly long line is encountered.
360 None,
361 /// Soft wrap lines that exceed the editor width.
362 EditorWidth,
363 /// Soft wrap lines at the preferred line length.
364 Column(u32),
365 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
366 Bounded(u32),
367}
368
369#[derive(Clone)]
370pub struct EditorStyle {
371 pub background: Hsla,
372 pub local_player: PlayerColor,
373 pub text: TextStyle,
374 pub scrollbar_width: Pixels,
375 pub syntax: Arc<SyntaxTheme>,
376 pub status: StatusColors,
377 pub inlay_hints_style: HighlightStyle,
378 pub inline_completion_styles: InlineCompletionStyles,
379 pub unnecessary_code_fade: f32,
380}
381
382impl Default for EditorStyle {
383 fn default() -> Self {
384 Self {
385 background: Hsla::default(),
386 local_player: PlayerColor::default(),
387 text: TextStyle::default(),
388 scrollbar_width: Pixels::default(),
389 syntax: Default::default(),
390 // HACK: Status colors don't have a real default.
391 // We should look into removing the status colors from the editor
392 // style and retrieve them directly from the theme.
393 status: StatusColors::dark(),
394 inlay_hints_style: HighlightStyle::default(),
395 inline_completion_styles: InlineCompletionStyles {
396 insertion: HighlightStyle::default(),
397 whitespace: HighlightStyle::default(),
398 },
399 unnecessary_code_fade: Default::default(),
400 }
401 }
402}
403
404pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
405 let show_background = language_settings::language_settings(None, None, cx)
406 .inlay_hints
407 .show_background;
408
409 HighlightStyle {
410 color: Some(cx.theme().status().hint),
411 background_color: show_background.then(|| cx.theme().status().hint_background),
412 ..HighlightStyle::default()
413 }
414}
415
416pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
417 InlineCompletionStyles {
418 insertion: HighlightStyle {
419 color: Some(cx.theme().status().predictive),
420 ..HighlightStyle::default()
421 },
422 whitespace: HighlightStyle {
423 background_color: Some(cx.theme().status().created_background),
424 ..HighlightStyle::default()
425 },
426 }
427}
428
429type CompletionId = usize;
430
431pub(crate) enum EditDisplayMode {
432 TabAccept,
433 DiffPopover,
434 Inline,
435}
436
437enum InlineCompletion {
438 Edit {
439 edits: Vec<(Range<Anchor>, String)>,
440 edit_preview: Option<EditPreview>,
441 display_mode: EditDisplayMode,
442 snapshot: BufferSnapshot,
443 },
444 Move {
445 target: Anchor,
446 snapshot: BufferSnapshot,
447 },
448}
449
450struct InlineCompletionState {
451 inlay_ids: Vec<InlayId>,
452 completion: InlineCompletion,
453 completion_id: Option<SharedString>,
454 invalidation_range: Range<Anchor>,
455}
456
457enum EditPredictionSettings {
458 Disabled,
459 Enabled {
460 show_in_menu: bool,
461 preview_requires_modifier: bool,
462 },
463}
464
465enum InlineCompletionHighlight {}
466
467#[derive(Debug, Clone)]
468struct InlineDiagnostic {
469 message: SharedString,
470 group_id: usize,
471 is_primary: bool,
472 start: Point,
473 severity: DiagnosticSeverity,
474}
475
476pub enum MenuInlineCompletionsPolicy {
477 Never,
478 ByProvider,
479}
480
481pub enum EditPredictionPreview {
482 /// Modifier is not pressed
483 Inactive,
484 /// Modifier pressed
485 Active {
486 previous_scroll_position: Option<ScrollAnchor>,
487 },
488}
489
490#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
491struct EditorActionId(usize);
492
493impl EditorActionId {
494 pub fn post_inc(&mut self) -> Self {
495 let answer = self.0;
496
497 *self = Self(answer + 1);
498
499 Self(answer)
500 }
501}
502
503// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
504// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
505
506type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
507type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
508
509#[derive(Default)]
510struct ScrollbarMarkerState {
511 scrollbar_size: Size<Pixels>,
512 dirty: bool,
513 markers: Arc<[PaintQuad]>,
514 pending_refresh: Option<Task<Result<()>>>,
515}
516
517impl ScrollbarMarkerState {
518 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
519 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
520 }
521}
522
523#[derive(Clone, Debug)]
524struct RunnableTasks {
525 templates: Vec<(TaskSourceKind, TaskTemplate)>,
526 offset: MultiBufferOffset,
527 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
528 column: u32,
529 // Values of all named captures, including those starting with '_'
530 extra_variables: HashMap<String, String>,
531 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
532 context_range: Range<BufferOffset>,
533}
534
535impl RunnableTasks {
536 fn resolve<'a>(
537 &'a self,
538 cx: &'a task::TaskContext,
539 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
540 self.templates.iter().filter_map(|(kind, template)| {
541 template
542 .resolve_task(&kind.to_id_base(), cx)
543 .map(|task| (kind.clone(), task))
544 })
545 }
546}
547
548#[derive(Clone)]
549struct ResolvedTasks {
550 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
551 position: Anchor,
552}
553#[derive(Copy, Clone, Debug)]
554struct MultiBufferOffset(usize);
555#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
556struct BufferOffset(usize);
557
558// Addons allow storing per-editor state in other crates (e.g. Vim)
559pub trait Addon: 'static {
560 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
561
562 fn render_buffer_header_controls(
563 &self,
564 _: &ExcerptInfo,
565 _: &Window,
566 _: &App,
567 ) -> Option<AnyElement> {
568 None
569 }
570
571 fn to_any(&self) -> &dyn std::any::Any;
572}
573
574#[derive(Debug, Copy, Clone, PartialEq, Eq)]
575pub enum IsVimMode {
576 Yes,
577 No,
578}
579
580/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
581///
582/// See the [module level documentation](self) for more information.
583pub struct Editor {
584 focus_handle: FocusHandle,
585 last_focused_descendant: Option<WeakFocusHandle>,
586 /// The text buffer being edited
587 buffer: Entity<MultiBuffer>,
588 /// Map of how text in the buffer should be displayed.
589 /// Handles soft wraps, folds, fake inlay text insertions, etc.
590 pub display_map: Entity<DisplayMap>,
591 pub selections: SelectionsCollection,
592 pub scroll_manager: ScrollManager,
593 /// When inline assist editors are linked, they all render cursors because
594 /// typing enters text into each of them, even the ones that aren't focused.
595 pub(crate) show_cursor_when_unfocused: bool,
596 columnar_selection_tail: Option<Anchor>,
597 add_selections_state: Option<AddSelectionsState>,
598 select_next_state: Option<SelectNextState>,
599 select_prev_state: Option<SelectNextState>,
600 selection_history: SelectionHistory,
601 autoclose_regions: Vec<AutocloseRegion>,
602 snippet_stack: InvalidationStack<SnippetState>,
603 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
604 ime_transaction: Option<TransactionId>,
605 active_diagnostics: Option<ActiveDiagnosticGroup>,
606 show_inline_diagnostics: bool,
607 inline_diagnostics_update: Task<()>,
608 inline_diagnostics_enabled: bool,
609 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
610 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
611
612 // TODO: make this a access method
613 pub project: Option<Entity<Project>>,
614 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
615 completion_provider: Option<Box<dyn CompletionProvider>>,
616 collaboration_hub: Option<Box<dyn CollaborationHub>>,
617 blink_manager: Entity<BlinkManager>,
618 show_cursor_names: bool,
619 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
620 pub show_local_selections: bool,
621 mode: EditorMode,
622 show_breadcrumbs: bool,
623 show_gutter: bool,
624 show_scrollbars: bool,
625 show_line_numbers: Option<bool>,
626 use_relative_line_numbers: Option<bool>,
627 show_git_diff_gutter: Option<bool>,
628 show_code_actions: Option<bool>,
629 show_runnables: Option<bool>,
630 show_wrap_guides: Option<bool>,
631 show_indent_guides: Option<bool>,
632 placeholder_text: Option<Arc<str>>,
633 highlight_order: usize,
634 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
635 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
636 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
637 scrollbar_marker_state: ScrollbarMarkerState,
638 active_indent_guides_state: ActiveIndentGuidesState,
639 nav_history: Option<ItemNavHistory>,
640 context_menu: RefCell<Option<CodeContextMenu>>,
641 mouse_context_menu: Option<MouseContextMenu>,
642 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
643 signature_help_state: SignatureHelpState,
644 auto_signature_help: Option<bool>,
645 find_all_references_task_sources: Vec<Anchor>,
646 next_completion_id: CompletionId,
647 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
648 code_actions_task: Option<Task<Result<()>>>,
649 selection_highlight_task: Option<Task<()>>,
650 document_highlights_task: Option<Task<()>>,
651 linked_editing_range_task: Option<Task<Option<()>>>,
652 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
653 pending_rename: Option<RenameState>,
654 searchable: bool,
655 cursor_shape: CursorShape,
656 current_line_highlight: Option<CurrentLineHighlight>,
657 collapse_matches: bool,
658 autoindent_mode: Option<AutoindentMode>,
659 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
660 input_enabled: bool,
661 use_modal_editing: bool,
662 read_only: bool,
663 leader_peer_id: Option<PeerId>,
664 remote_id: Option<ViewId>,
665 hover_state: HoverState,
666 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
667 gutter_hovered: bool,
668 hovered_link_state: Option<HoveredLinkState>,
669 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
670 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
671 active_inline_completion: Option<InlineCompletionState>,
672 /// Used to prevent flickering as the user types while the menu is open
673 stale_inline_completion_in_menu: Option<InlineCompletionState>,
674 edit_prediction_settings: EditPredictionSettings,
675 inline_completions_hidden_for_vim_mode: bool,
676 show_inline_completions_override: Option<bool>,
677 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
678 edit_prediction_preview: EditPredictionPreview,
679 edit_prediction_cursor_on_leading_whitespace: bool,
680 edit_prediction_requires_modifier_in_leading_space: bool,
681 inlay_hint_cache: InlayHintCache,
682 next_inlay_id: usize,
683 _subscriptions: Vec<Subscription>,
684 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
685 gutter_dimensions: GutterDimensions,
686 style: Option<EditorStyle>,
687 text_style_refinement: Option<TextStyleRefinement>,
688 next_editor_action_id: EditorActionId,
689 editor_actions:
690 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
691 use_autoclose: bool,
692 use_auto_surround: bool,
693 auto_replace_emoji_shortcode: bool,
694 show_git_blame_gutter: bool,
695 show_git_blame_inline: bool,
696 show_git_blame_inline_delay_task: Option<Task<()>>,
697 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
698 git_blame_inline_enabled: bool,
699 serialize_dirty_buffers: bool,
700 show_selection_menu: Option<bool>,
701 blame: Option<Entity<GitBlame>>,
702 blame_subscription: Option<Subscription>,
703 custom_context_menu: Option<
704 Box<
705 dyn 'static
706 + Fn(
707 &mut Self,
708 DisplayPoint,
709 &mut Window,
710 &mut Context<Self>,
711 ) -> Option<Entity<ui::ContextMenu>>,
712 >,
713 >,
714 last_bounds: Option<Bounds<Pixels>>,
715 last_position_map: Option<Rc<PositionMap>>,
716 expect_bounds_change: Option<Bounds<Pixels>>,
717 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
718 tasks_update_task: Option<Task<()>>,
719 in_project_search: bool,
720 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
721 breadcrumb_header: Option<String>,
722 focused_block: Option<FocusedBlock>,
723 next_scroll_position: NextScrollCursorCenterTopBottom,
724 addons: HashMap<TypeId, Box<dyn Addon>>,
725 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
726 load_diff_task: Option<Shared<Task<()>>>,
727 selection_mark_mode: bool,
728 toggle_fold_multiple_buffers: Task<()>,
729 _scroll_cursor_center_top_bottom_task: Task<()>,
730 serialize_selections: Task<()>,
731}
732
733#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
734enum NextScrollCursorCenterTopBottom {
735 #[default]
736 Center,
737 Top,
738 Bottom,
739}
740
741impl NextScrollCursorCenterTopBottom {
742 fn next(&self) -> Self {
743 match self {
744 Self::Center => Self::Top,
745 Self::Top => Self::Bottom,
746 Self::Bottom => Self::Center,
747 }
748 }
749}
750
751#[derive(Clone)]
752pub struct EditorSnapshot {
753 pub mode: EditorMode,
754 show_gutter: bool,
755 show_line_numbers: Option<bool>,
756 show_git_diff_gutter: Option<bool>,
757 show_code_actions: Option<bool>,
758 show_runnables: Option<bool>,
759 git_blame_gutter_max_author_length: Option<usize>,
760 pub display_snapshot: DisplaySnapshot,
761 pub placeholder_text: Option<Arc<str>>,
762 is_focused: bool,
763 scroll_anchor: ScrollAnchor,
764 ongoing_scroll: OngoingScroll,
765 current_line_highlight: CurrentLineHighlight,
766 gutter_hovered: bool,
767}
768
769const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
770
771#[derive(Default, Debug, Clone, Copy)]
772pub struct GutterDimensions {
773 pub left_padding: Pixels,
774 pub right_padding: Pixels,
775 pub width: Pixels,
776 pub margin: Pixels,
777 pub git_blame_entries_width: Option<Pixels>,
778}
779
780impl GutterDimensions {
781 /// The full width of the space taken up by the gutter.
782 pub fn full_width(&self) -> Pixels {
783 self.margin + self.width
784 }
785
786 /// The width of the space reserved for the fold indicators,
787 /// use alongside 'justify_end' and `gutter_width` to
788 /// right align content with the line numbers
789 pub fn fold_area_width(&self) -> Pixels {
790 self.margin + self.right_padding
791 }
792}
793
794#[derive(Debug)]
795pub struct RemoteSelection {
796 pub replica_id: ReplicaId,
797 pub selection: Selection<Anchor>,
798 pub cursor_shape: CursorShape,
799 pub peer_id: PeerId,
800 pub line_mode: bool,
801 pub participant_index: Option<ParticipantIndex>,
802 pub user_name: Option<SharedString>,
803}
804
805#[derive(Clone, Debug)]
806struct SelectionHistoryEntry {
807 selections: Arc<[Selection<Anchor>]>,
808 select_next_state: Option<SelectNextState>,
809 select_prev_state: Option<SelectNextState>,
810 add_selections_state: Option<AddSelectionsState>,
811}
812
813enum SelectionHistoryMode {
814 Normal,
815 Undoing,
816 Redoing,
817}
818
819#[derive(Clone, PartialEq, Eq, Hash)]
820struct HoveredCursor {
821 replica_id: u16,
822 selection_id: usize,
823}
824
825impl Default for SelectionHistoryMode {
826 fn default() -> Self {
827 Self::Normal
828 }
829}
830
831#[derive(Default)]
832struct SelectionHistory {
833 #[allow(clippy::type_complexity)]
834 selections_by_transaction:
835 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
836 mode: SelectionHistoryMode,
837 undo_stack: VecDeque<SelectionHistoryEntry>,
838 redo_stack: VecDeque<SelectionHistoryEntry>,
839}
840
841impl SelectionHistory {
842 fn insert_transaction(
843 &mut self,
844 transaction_id: TransactionId,
845 selections: Arc<[Selection<Anchor>]>,
846 ) {
847 self.selections_by_transaction
848 .insert(transaction_id, (selections, None));
849 }
850
851 #[allow(clippy::type_complexity)]
852 fn transaction(
853 &self,
854 transaction_id: TransactionId,
855 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
856 self.selections_by_transaction.get(&transaction_id)
857 }
858
859 #[allow(clippy::type_complexity)]
860 fn transaction_mut(
861 &mut self,
862 transaction_id: TransactionId,
863 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
864 self.selections_by_transaction.get_mut(&transaction_id)
865 }
866
867 fn push(&mut self, entry: SelectionHistoryEntry) {
868 if !entry.selections.is_empty() {
869 match self.mode {
870 SelectionHistoryMode::Normal => {
871 self.push_undo(entry);
872 self.redo_stack.clear();
873 }
874 SelectionHistoryMode::Undoing => self.push_redo(entry),
875 SelectionHistoryMode::Redoing => self.push_undo(entry),
876 }
877 }
878 }
879
880 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
881 if self
882 .undo_stack
883 .back()
884 .map_or(true, |e| e.selections != entry.selections)
885 {
886 self.undo_stack.push_back(entry);
887 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
888 self.undo_stack.pop_front();
889 }
890 }
891 }
892
893 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
894 if self
895 .redo_stack
896 .back()
897 .map_or(true, |e| e.selections != entry.selections)
898 {
899 self.redo_stack.push_back(entry);
900 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
901 self.redo_stack.pop_front();
902 }
903 }
904 }
905}
906
907struct RowHighlight {
908 index: usize,
909 range: Range<Anchor>,
910 color: Hsla,
911 should_autoscroll: bool,
912}
913
914#[derive(Clone, Debug)]
915struct AddSelectionsState {
916 above: bool,
917 stack: Vec<usize>,
918}
919
920#[derive(Clone)]
921struct SelectNextState {
922 query: AhoCorasick,
923 wordwise: bool,
924 done: bool,
925}
926
927impl std::fmt::Debug for SelectNextState {
928 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929 f.debug_struct(std::any::type_name::<Self>())
930 .field("wordwise", &self.wordwise)
931 .field("done", &self.done)
932 .finish()
933 }
934}
935
936#[derive(Debug)]
937struct AutocloseRegion {
938 selection_id: usize,
939 range: Range<Anchor>,
940 pair: BracketPair,
941}
942
943#[derive(Debug)]
944struct SnippetState {
945 ranges: Vec<Vec<Range<Anchor>>>,
946 active_index: usize,
947 choices: Vec<Option<Vec<String>>>,
948}
949
950#[doc(hidden)]
951pub struct RenameState {
952 pub range: Range<Anchor>,
953 pub old_name: Arc<str>,
954 pub editor: Entity<Editor>,
955 block_id: CustomBlockId,
956}
957
958struct InvalidationStack<T>(Vec<T>);
959
960struct RegisteredInlineCompletionProvider {
961 provider: Arc<dyn InlineCompletionProviderHandle>,
962 _subscription: Subscription,
963}
964
965#[derive(Debug)]
966struct ActiveDiagnosticGroup {
967 primary_range: Range<Anchor>,
968 primary_message: String,
969 group_id: usize,
970 blocks: HashMap<CustomBlockId, Diagnostic>,
971 is_valid: bool,
972}
973
974#[derive(Serialize, Deserialize, Clone, Debug)]
975pub struct ClipboardSelection {
976 /// The number of bytes in this selection.
977 pub len: usize,
978 /// Whether this was a full-line selection.
979 pub is_entire_line: bool,
980 /// The column where this selection originally started.
981 pub start_column: u32,
982}
983
984#[derive(Debug)]
985pub(crate) struct NavigationData {
986 cursor_anchor: Anchor,
987 cursor_position: Point,
988 scroll_anchor: ScrollAnchor,
989 scroll_top_row: u32,
990}
991
992#[derive(Debug, Clone, Copy, PartialEq, Eq)]
993pub enum GotoDefinitionKind {
994 Symbol,
995 Declaration,
996 Type,
997 Implementation,
998}
999
1000#[derive(Debug, Clone)]
1001enum InlayHintRefreshReason {
1002 Toggle(bool),
1003 SettingsChange(InlayHintSettings),
1004 NewLinesShown,
1005 BufferEdited(HashSet<Arc<Language>>),
1006 RefreshRequested,
1007 ExcerptsRemoved(Vec<ExcerptId>),
1008}
1009
1010impl InlayHintRefreshReason {
1011 fn description(&self) -> &'static str {
1012 match self {
1013 Self::Toggle(_) => "toggle",
1014 Self::SettingsChange(_) => "settings change",
1015 Self::NewLinesShown => "new lines shown",
1016 Self::BufferEdited(_) => "buffer edited",
1017 Self::RefreshRequested => "refresh requested",
1018 Self::ExcerptsRemoved(_) => "excerpts removed",
1019 }
1020 }
1021}
1022
1023pub enum FormatTarget {
1024 Buffers,
1025 Ranges(Vec<Range<MultiBufferPoint>>),
1026}
1027
1028pub(crate) struct FocusedBlock {
1029 id: BlockId,
1030 focus_handle: WeakFocusHandle,
1031}
1032
1033#[derive(Clone)]
1034enum JumpData {
1035 MultiBufferRow {
1036 row: MultiBufferRow,
1037 line_offset_from_top: u32,
1038 },
1039 MultiBufferPoint {
1040 excerpt_id: ExcerptId,
1041 position: Point,
1042 anchor: text::Anchor,
1043 line_offset_from_top: u32,
1044 },
1045}
1046
1047pub enum MultibufferSelectionMode {
1048 First,
1049 All,
1050}
1051
1052impl Editor {
1053 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1054 let buffer = cx.new(|cx| Buffer::local("", cx));
1055 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1056 Self::new(
1057 EditorMode::SingleLine { auto_width: false },
1058 buffer,
1059 None,
1060 false,
1061 window,
1062 cx,
1063 )
1064 }
1065
1066 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1067 let buffer = cx.new(|cx| Buffer::local("", cx));
1068 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1069 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1070 }
1071
1072 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1073 let buffer = cx.new(|cx| Buffer::local("", cx));
1074 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1075 Self::new(
1076 EditorMode::SingleLine { auto_width: true },
1077 buffer,
1078 None,
1079 false,
1080 window,
1081 cx,
1082 )
1083 }
1084
1085 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1086 let buffer = cx.new(|cx| Buffer::local("", cx));
1087 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1088 Self::new(
1089 EditorMode::AutoHeight { max_lines },
1090 buffer,
1091 None,
1092 false,
1093 window,
1094 cx,
1095 )
1096 }
1097
1098 pub fn for_buffer(
1099 buffer: Entity<Buffer>,
1100 project: Option<Entity<Project>>,
1101 window: &mut Window,
1102 cx: &mut Context<Self>,
1103 ) -> Self {
1104 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1105 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1106 }
1107
1108 pub fn for_multibuffer(
1109 buffer: Entity<MultiBuffer>,
1110 project: Option<Entity<Project>>,
1111 show_excerpt_controls: bool,
1112 window: &mut Window,
1113 cx: &mut Context<Self>,
1114 ) -> Self {
1115 Self::new(
1116 EditorMode::Full,
1117 buffer,
1118 project,
1119 show_excerpt_controls,
1120 window,
1121 cx,
1122 )
1123 }
1124
1125 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1126 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1127 let mut clone = Self::new(
1128 self.mode,
1129 self.buffer.clone(),
1130 self.project.clone(),
1131 show_excerpt_controls,
1132 window,
1133 cx,
1134 );
1135 self.display_map.update(cx, |display_map, cx| {
1136 let snapshot = display_map.snapshot(cx);
1137 clone.display_map.update(cx, |display_map, cx| {
1138 display_map.set_state(&snapshot, cx);
1139 });
1140 });
1141 clone.selections.clone_state(&self.selections);
1142 clone.scroll_manager.clone_state(&self.scroll_manager);
1143 clone.searchable = self.searchable;
1144 clone
1145 }
1146
1147 pub fn new(
1148 mode: EditorMode,
1149 buffer: Entity<MultiBuffer>,
1150 project: Option<Entity<Project>>,
1151 show_excerpt_controls: bool,
1152 window: &mut Window,
1153 cx: &mut Context<Self>,
1154 ) -> Self {
1155 let style = window.text_style();
1156 let font_size = style.font_size.to_pixels(window.rem_size());
1157 let editor = cx.entity().downgrade();
1158 let fold_placeholder = FoldPlaceholder {
1159 constrain_width: true,
1160 render: Arc::new(move |fold_id, fold_range, cx| {
1161 let editor = editor.clone();
1162 div()
1163 .id(fold_id)
1164 .bg(cx.theme().colors().ghost_element_background)
1165 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1166 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1167 .rounded_sm()
1168 .size_full()
1169 .cursor_pointer()
1170 .child("⋯")
1171 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1172 .on_click(move |_, _window, cx| {
1173 editor
1174 .update(cx, |editor, cx| {
1175 editor.unfold_ranges(
1176 &[fold_range.start..fold_range.end],
1177 true,
1178 false,
1179 cx,
1180 );
1181 cx.stop_propagation();
1182 })
1183 .ok();
1184 })
1185 .into_any()
1186 }),
1187 merge_adjacent: true,
1188 ..Default::default()
1189 };
1190 let display_map = cx.new(|cx| {
1191 DisplayMap::new(
1192 buffer.clone(),
1193 style.font(),
1194 font_size,
1195 None,
1196 show_excerpt_controls,
1197 FILE_HEADER_HEIGHT,
1198 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1199 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1200 fold_placeholder,
1201 cx,
1202 )
1203 });
1204
1205 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1206
1207 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1208
1209 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1210 .then(|| language_settings::SoftWrap::None);
1211
1212 let mut project_subscriptions = Vec::new();
1213 if mode == EditorMode::Full {
1214 if let Some(project) = project.as_ref() {
1215 if buffer.read(cx).is_singleton() {
1216 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1217 cx.emit(EditorEvent::TitleChanged);
1218 }));
1219 }
1220 project_subscriptions.push(cx.subscribe_in(
1221 project,
1222 window,
1223 |editor, _, event, window, cx| {
1224 if let project::Event::RefreshInlayHints = event {
1225 editor
1226 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1227 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1228 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1229 let focus_handle = editor.focus_handle(cx);
1230 if focus_handle.is_focused(window) {
1231 let snapshot = buffer.read(cx).snapshot();
1232 for (range, snippet) in snippet_edits {
1233 let editor_range =
1234 language::range_from_lsp(*range).to_offset(&snapshot);
1235 editor
1236 .insert_snippet(
1237 &[editor_range],
1238 snippet.clone(),
1239 window,
1240 cx,
1241 )
1242 .ok();
1243 }
1244 }
1245 }
1246 }
1247 },
1248 ));
1249 if let Some(task_inventory) = project
1250 .read(cx)
1251 .task_store()
1252 .read(cx)
1253 .task_inventory()
1254 .cloned()
1255 {
1256 project_subscriptions.push(cx.observe_in(
1257 &task_inventory,
1258 window,
1259 |editor, _, window, cx| {
1260 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1261 },
1262 ));
1263 }
1264 }
1265 }
1266
1267 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1268
1269 let inlay_hint_settings =
1270 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1271 let focus_handle = cx.focus_handle();
1272 cx.on_focus(&focus_handle, window, Self::handle_focus)
1273 .detach();
1274 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1275 .detach();
1276 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1277 .detach();
1278 cx.on_blur(&focus_handle, window, Self::handle_blur)
1279 .detach();
1280
1281 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1282 Some(false)
1283 } else {
1284 None
1285 };
1286
1287 let mut code_action_providers = Vec::new();
1288 let mut load_uncommitted_diff = None;
1289 if let Some(project) = project.clone() {
1290 load_uncommitted_diff = Some(
1291 get_uncommitted_diff_for_buffer(
1292 &project,
1293 buffer.read(cx).all_buffers(),
1294 buffer.clone(),
1295 cx,
1296 )
1297 .shared(),
1298 );
1299 code_action_providers.push(Rc::new(project) as Rc<_>);
1300 }
1301
1302 let mut this = Self {
1303 focus_handle,
1304 show_cursor_when_unfocused: false,
1305 last_focused_descendant: None,
1306 buffer: buffer.clone(),
1307 display_map: display_map.clone(),
1308 selections,
1309 scroll_manager: ScrollManager::new(cx),
1310 columnar_selection_tail: None,
1311 add_selections_state: None,
1312 select_next_state: None,
1313 select_prev_state: None,
1314 selection_history: Default::default(),
1315 autoclose_regions: Default::default(),
1316 snippet_stack: Default::default(),
1317 select_larger_syntax_node_stack: Vec::new(),
1318 ime_transaction: Default::default(),
1319 active_diagnostics: None,
1320 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1321 inline_diagnostics_update: Task::ready(()),
1322 inline_diagnostics: Vec::new(),
1323 soft_wrap_mode_override,
1324 completion_provider: project.clone().map(|project| Box::new(project) as _),
1325 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1326 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1327 project,
1328 blink_manager: blink_manager.clone(),
1329 show_local_selections: true,
1330 show_scrollbars: true,
1331 mode,
1332 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1333 show_gutter: mode == EditorMode::Full,
1334 show_line_numbers: None,
1335 use_relative_line_numbers: None,
1336 show_git_diff_gutter: None,
1337 show_code_actions: None,
1338 show_runnables: None,
1339 show_wrap_guides: None,
1340 show_indent_guides,
1341 placeholder_text: None,
1342 highlight_order: 0,
1343 highlighted_rows: HashMap::default(),
1344 background_highlights: Default::default(),
1345 gutter_highlights: TreeMap::default(),
1346 scrollbar_marker_state: ScrollbarMarkerState::default(),
1347 active_indent_guides_state: ActiveIndentGuidesState::default(),
1348 nav_history: None,
1349 context_menu: RefCell::new(None),
1350 mouse_context_menu: None,
1351 completion_tasks: Default::default(),
1352 signature_help_state: SignatureHelpState::default(),
1353 auto_signature_help: None,
1354 find_all_references_task_sources: Vec::new(),
1355 next_completion_id: 0,
1356 next_inlay_id: 0,
1357 code_action_providers,
1358 available_code_actions: Default::default(),
1359 code_actions_task: Default::default(),
1360 selection_highlight_task: Default::default(),
1361 document_highlights_task: Default::default(),
1362 linked_editing_range_task: Default::default(),
1363 pending_rename: Default::default(),
1364 searchable: true,
1365 cursor_shape: EditorSettings::get_global(cx)
1366 .cursor_shape
1367 .unwrap_or_default(),
1368 current_line_highlight: None,
1369 autoindent_mode: Some(AutoindentMode::EachLine),
1370 collapse_matches: false,
1371 workspace: None,
1372 input_enabled: true,
1373 use_modal_editing: mode == EditorMode::Full,
1374 read_only: false,
1375 use_autoclose: true,
1376 use_auto_surround: true,
1377 auto_replace_emoji_shortcode: false,
1378 leader_peer_id: None,
1379 remote_id: None,
1380 hover_state: Default::default(),
1381 pending_mouse_down: None,
1382 hovered_link_state: Default::default(),
1383 edit_prediction_provider: None,
1384 active_inline_completion: None,
1385 stale_inline_completion_in_menu: None,
1386 edit_prediction_preview: EditPredictionPreview::Inactive,
1387 inline_diagnostics_enabled: mode == EditorMode::Full,
1388 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1389
1390 gutter_hovered: false,
1391 pixel_position_of_newest_cursor: None,
1392 last_bounds: None,
1393 last_position_map: None,
1394 expect_bounds_change: None,
1395 gutter_dimensions: GutterDimensions::default(),
1396 style: None,
1397 show_cursor_names: false,
1398 hovered_cursors: Default::default(),
1399 next_editor_action_id: EditorActionId::default(),
1400 editor_actions: Rc::default(),
1401 inline_completions_hidden_for_vim_mode: false,
1402 show_inline_completions_override: None,
1403 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1404 edit_prediction_settings: EditPredictionSettings::Disabled,
1405 edit_prediction_cursor_on_leading_whitespace: false,
1406 edit_prediction_requires_modifier_in_leading_space: true,
1407 custom_context_menu: None,
1408 show_git_blame_gutter: false,
1409 show_git_blame_inline: false,
1410 show_selection_menu: None,
1411 show_git_blame_inline_delay_task: None,
1412 git_blame_inline_tooltip: None,
1413 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1414 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1415 .session
1416 .restore_unsaved_buffers,
1417 blame: None,
1418 blame_subscription: None,
1419 tasks: Default::default(),
1420 _subscriptions: vec![
1421 cx.observe(&buffer, Self::on_buffer_changed),
1422 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1423 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1424 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1425 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1426 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1427 cx.observe_window_activation(window, |editor, window, cx| {
1428 let active = window.is_window_active();
1429 editor.blink_manager.update(cx, |blink_manager, cx| {
1430 if active {
1431 blink_manager.enable(cx);
1432 } else {
1433 blink_manager.disable(cx);
1434 }
1435 });
1436 }),
1437 ],
1438 tasks_update_task: None,
1439 linked_edit_ranges: Default::default(),
1440 in_project_search: false,
1441 previous_search_ranges: None,
1442 breadcrumb_header: None,
1443 focused_block: None,
1444 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1445 addons: HashMap::default(),
1446 registered_buffers: HashMap::default(),
1447 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1448 selection_mark_mode: false,
1449 toggle_fold_multiple_buffers: Task::ready(()),
1450 serialize_selections: Task::ready(()),
1451 text_style_refinement: None,
1452 load_diff_task: load_uncommitted_diff,
1453 };
1454 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1455 this._subscriptions.extend(project_subscriptions);
1456
1457 this.end_selection(window, cx);
1458 this.scroll_manager.show_scrollbar(window, cx);
1459
1460 if mode == EditorMode::Full {
1461 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1462 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1463
1464 if this.git_blame_inline_enabled {
1465 this.git_blame_inline_enabled = true;
1466 this.start_git_blame_inline(false, window, cx);
1467 }
1468
1469 if let Some(buffer) = buffer.read(cx).as_singleton() {
1470 if let Some(project) = this.project.as_ref() {
1471 let handle = project.update(cx, |project, cx| {
1472 project.register_buffer_with_language_servers(&buffer, cx)
1473 });
1474 this.registered_buffers
1475 .insert(buffer.read(cx).remote_id(), handle);
1476 }
1477 }
1478 }
1479
1480 this.report_editor_event("Editor Opened", None, cx);
1481 this
1482 }
1483
1484 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1485 self.mouse_context_menu
1486 .as_ref()
1487 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1488 }
1489
1490 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1491 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1492 }
1493
1494 fn key_context_internal(
1495 &self,
1496 has_active_edit_prediction: bool,
1497 window: &Window,
1498 cx: &App,
1499 ) -> KeyContext {
1500 let mut key_context = KeyContext::new_with_defaults();
1501 key_context.add("Editor");
1502 let mode = match self.mode {
1503 EditorMode::SingleLine { .. } => "single_line",
1504 EditorMode::AutoHeight { .. } => "auto_height",
1505 EditorMode::Full => "full",
1506 };
1507
1508 if EditorSettings::jupyter_enabled(cx) {
1509 key_context.add("jupyter");
1510 }
1511
1512 key_context.set("mode", mode);
1513 if self.pending_rename.is_some() {
1514 key_context.add("renaming");
1515 }
1516
1517 match self.context_menu.borrow().as_ref() {
1518 Some(CodeContextMenu::Completions(_)) => {
1519 key_context.add("menu");
1520 key_context.add("showing_completions");
1521 }
1522 Some(CodeContextMenu::CodeActions(_)) => {
1523 key_context.add("menu");
1524 key_context.add("showing_code_actions")
1525 }
1526 None => {}
1527 }
1528
1529 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1530 if !self.focus_handle(cx).contains_focused(window, cx)
1531 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1532 {
1533 for addon in self.addons.values() {
1534 addon.extend_key_context(&mut key_context, cx)
1535 }
1536 }
1537
1538 if let Some(extension) = self
1539 .buffer
1540 .read(cx)
1541 .as_singleton()
1542 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1543 {
1544 key_context.set("extension", extension.to_string());
1545 }
1546
1547 if has_active_edit_prediction {
1548 if self.edit_prediction_in_conflict() {
1549 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1550 } else {
1551 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1552 key_context.add("copilot_suggestion");
1553 }
1554 }
1555
1556 if self.selection_mark_mode {
1557 key_context.add("selection_mode");
1558 }
1559
1560 key_context
1561 }
1562
1563 pub fn edit_prediction_in_conflict(&self) -> bool {
1564 if !self.show_edit_predictions_in_menu() {
1565 return false;
1566 }
1567
1568 let showing_completions = self
1569 .context_menu
1570 .borrow()
1571 .as_ref()
1572 .map_or(false, |context| {
1573 matches!(context, CodeContextMenu::Completions(_))
1574 });
1575
1576 showing_completions
1577 || self.edit_prediction_requires_modifier()
1578 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1579 // bindings to insert tab characters.
1580 || (self.edit_prediction_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
1581 }
1582
1583 pub fn accept_edit_prediction_keybind(
1584 &self,
1585 window: &Window,
1586 cx: &App,
1587 ) -> AcceptEditPredictionBinding {
1588 let key_context = self.key_context_internal(true, window, cx);
1589 let in_conflict = self.edit_prediction_in_conflict();
1590
1591 AcceptEditPredictionBinding(
1592 window
1593 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1594 .into_iter()
1595 .filter(|binding| {
1596 !in_conflict
1597 || binding
1598 .keystrokes()
1599 .first()
1600 .map_or(false, |keystroke| keystroke.modifiers.modified())
1601 })
1602 .rev()
1603 .min_by_key(|binding| {
1604 binding
1605 .keystrokes()
1606 .first()
1607 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1608 }),
1609 )
1610 }
1611
1612 pub fn new_file(
1613 workspace: &mut Workspace,
1614 _: &workspace::NewFile,
1615 window: &mut Window,
1616 cx: &mut Context<Workspace>,
1617 ) {
1618 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1619 "Failed to create buffer",
1620 window,
1621 cx,
1622 |e, _, _| match e.error_code() {
1623 ErrorCode::RemoteUpgradeRequired => Some(format!(
1624 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1625 e.error_tag("required").unwrap_or("the latest version")
1626 )),
1627 _ => None,
1628 },
1629 );
1630 }
1631
1632 pub fn new_in_workspace(
1633 workspace: &mut Workspace,
1634 window: &mut Window,
1635 cx: &mut Context<Workspace>,
1636 ) -> Task<Result<Entity<Editor>>> {
1637 let project = workspace.project().clone();
1638 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1639
1640 cx.spawn_in(window, |workspace, mut cx| async move {
1641 let buffer = create.await?;
1642 workspace.update_in(&mut cx, |workspace, window, cx| {
1643 let editor =
1644 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1645 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1646 editor
1647 })
1648 })
1649 }
1650
1651 fn new_file_vertical(
1652 workspace: &mut Workspace,
1653 _: &workspace::NewFileSplitVertical,
1654 window: &mut Window,
1655 cx: &mut Context<Workspace>,
1656 ) {
1657 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1658 }
1659
1660 fn new_file_horizontal(
1661 workspace: &mut Workspace,
1662 _: &workspace::NewFileSplitHorizontal,
1663 window: &mut Window,
1664 cx: &mut Context<Workspace>,
1665 ) {
1666 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1667 }
1668
1669 fn new_file_in_direction(
1670 workspace: &mut Workspace,
1671 direction: SplitDirection,
1672 window: &mut Window,
1673 cx: &mut Context<Workspace>,
1674 ) {
1675 let project = workspace.project().clone();
1676 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1677
1678 cx.spawn_in(window, |workspace, mut cx| async move {
1679 let buffer = create.await?;
1680 workspace.update_in(&mut cx, move |workspace, window, cx| {
1681 workspace.split_item(
1682 direction,
1683 Box::new(
1684 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1685 ),
1686 window,
1687 cx,
1688 )
1689 })?;
1690 anyhow::Ok(())
1691 })
1692 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1693 match e.error_code() {
1694 ErrorCode::RemoteUpgradeRequired => Some(format!(
1695 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1696 e.error_tag("required").unwrap_or("the latest version")
1697 )),
1698 _ => None,
1699 }
1700 });
1701 }
1702
1703 pub fn leader_peer_id(&self) -> Option<PeerId> {
1704 self.leader_peer_id
1705 }
1706
1707 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1708 &self.buffer
1709 }
1710
1711 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1712 self.workspace.as_ref()?.0.upgrade()
1713 }
1714
1715 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1716 self.buffer().read(cx).title(cx)
1717 }
1718
1719 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1720 let git_blame_gutter_max_author_length = self
1721 .render_git_blame_gutter(cx)
1722 .then(|| {
1723 if let Some(blame) = self.blame.as_ref() {
1724 let max_author_length =
1725 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1726 Some(max_author_length)
1727 } else {
1728 None
1729 }
1730 })
1731 .flatten();
1732
1733 EditorSnapshot {
1734 mode: self.mode,
1735 show_gutter: self.show_gutter,
1736 show_line_numbers: self.show_line_numbers,
1737 show_git_diff_gutter: self.show_git_diff_gutter,
1738 show_code_actions: self.show_code_actions,
1739 show_runnables: self.show_runnables,
1740 git_blame_gutter_max_author_length,
1741 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1742 scroll_anchor: self.scroll_manager.anchor(),
1743 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1744 placeholder_text: self.placeholder_text.clone(),
1745 is_focused: self.focus_handle.is_focused(window),
1746 current_line_highlight: self
1747 .current_line_highlight
1748 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1749 gutter_hovered: self.gutter_hovered,
1750 }
1751 }
1752
1753 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1754 self.buffer.read(cx).language_at(point, cx)
1755 }
1756
1757 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1758 self.buffer.read(cx).read(cx).file_at(point).cloned()
1759 }
1760
1761 pub fn active_excerpt(
1762 &self,
1763 cx: &App,
1764 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1765 self.buffer
1766 .read(cx)
1767 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1768 }
1769
1770 pub fn mode(&self) -> EditorMode {
1771 self.mode
1772 }
1773
1774 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1775 self.collaboration_hub.as_deref()
1776 }
1777
1778 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1779 self.collaboration_hub = Some(hub);
1780 }
1781
1782 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1783 self.in_project_search = in_project_search;
1784 }
1785
1786 pub fn set_custom_context_menu(
1787 &mut self,
1788 f: impl 'static
1789 + Fn(
1790 &mut Self,
1791 DisplayPoint,
1792 &mut Window,
1793 &mut Context<Self>,
1794 ) -> Option<Entity<ui::ContextMenu>>,
1795 ) {
1796 self.custom_context_menu = Some(Box::new(f))
1797 }
1798
1799 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1800 self.completion_provider = provider;
1801 }
1802
1803 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1804 self.semantics_provider.clone()
1805 }
1806
1807 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1808 self.semantics_provider = provider;
1809 }
1810
1811 pub fn set_edit_prediction_provider<T>(
1812 &mut self,
1813 provider: Option<Entity<T>>,
1814 window: &mut Window,
1815 cx: &mut Context<Self>,
1816 ) where
1817 T: EditPredictionProvider,
1818 {
1819 self.edit_prediction_provider =
1820 provider.map(|provider| RegisteredInlineCompletionProvider {
1821 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1822 if this.focus_handle.is_focused(window) {
1823 this.update_visible_inline_completion(window, cx);
1824 }
1825 }),
1826 provider: Arc::new(provider),
1827 });
1828 self.refresh_inline_completion(false, false, window, cx);
1829 }
1830
1831 pub fn placeholder_text(&self) -> Option<&str> {
1832 self.placeholder_text.as_deref()
1833 }
1834
1835 pub fn set_placeholder_text(
1836 &mut self,
1837 placeholder_text: impl Into<Arc<str>>,
1838 cx: &mut Context<Self>,
1839 ) {
1840 let placeholder_text = Some(placeholder_text.into());
1841 if self.placeholder_text != placeholder_text {
1842 self.placeholder_text = placeholder_text;
1843 cx.notify();
1844 }
1845 }
1846
1847 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1848 self.cursor_shape = cursor_shape;
1849
1850 // Disrupt blink for immediate user feedback that the cursor shape has changed
1851 self.blink_manager.update(cx, BlinkManager::show_cursor);
1852
1853 cx.notify();
1854 }
1855
1856 pub fn set_current_line_highlight(
1857 &mut self,
1858 current_line_highlight: Option<CurrentLineHighlight>,
1859 ) {
1860 self.current_line_highlight = current_line_highlight;
1861 }
1862
1863 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1864 self.collapse_matches = collapse_matches;
1865 }
1866
1867 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1868 let buffers = self.buffer.read(cx).all_buffers();
1869 let Some(project) = self.project.as_ref() else {
1870 return;
1871 };
1872 project.update(cx, |project, cx| {
1873 for buffer in buffers {
1874 self.registered_buffers
1875 .entry(buffer.read(cx).remote_id())
1876 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1877 }
1878 })
1879 }
1880
1881 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1882 if self.collapse_matches {
1883 return range.start..range.start;
1884 }
1885 range.clone()
1886 }
1887
1888 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1889 if self.display_map.read(cx).clip_at_line_ends != clip {
1890 self.display_map
1891 .update(cx, |map, _| map.clip_at_line_ends = clip);
1892 }
1893 }
1894
1895 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1896 self.input_enabled = input_enabled;
1897 }
1898
1899 pub fn set_inline_completions_hidden_for_vim_mode(
1900 &mut self,
1901 hidden: bool,
1902 window: &mut Window,
1903 cx: &mut Context<Self>,
1904 ) {
1905 if hidden != self.inline_completions_hidden_for_vim_mode {
1906 self.inline_completions_hidden_for_vim_mode = hidden;
1907 if hidden {
1908 self.update_visible_inline_completion(window, cx);
1909 } else {
1910 self.refresh_inline_completion(true, false, window, cx);
1911 }
1912 }
1913 }
1914
1915 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1916 self.menu_inline_completions_policy = value;
1917 }
1918
1919 pub fn set_autoindent(&mut self, autoindent: bool) {
1920 if autoindent {
1921 self.autoindent_mode = Some(AutoindentMode::EachLine);
1922 } else {
1923 self.autoindent_mode = None;
1924 }
1925 }
1926
1927 pub fn read_only(&self, cx: &App) -> bool {
1928 self.read_only || self.buffer.read(cx).read_only()
1929 }
1930
1931 pub fn set_read_only(&mut self, read_only: bool) {
1932 self.read_only = read_only;
1933 }
1934
1935 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1936 self.use_autoclose = autoclose;
1937 }
1938
1939 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1940 self.use_auto_surround = auto_surround;
1941 }
1942
1943 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1944 self.auto_replace_emoji_shortcode = auto_replace;
1945 }
1946
1947 pub fn toggle_inline_completions(
1948 &mut self,
1949 _: &ToggleEditPrediction,
1950 window: &mut Window,
1951 cx: &mut Context<Self>,
1952 ) {
1953 if self.show_inline_completions_override.is_some() {
1954 self.set_show_edit_predictions(None, window, cx);
1955 } else {
1956 let show_edit_predictions = !self.edit_predictions_enabled();
1957 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1958 }
1959 }
1960
1961 pub fn set_show_edit_predictions(
1962 &mut self,
1963 show_edit_predictions: Option<bool>,
1964 window: &mut Window,
1965 cx: &mut Context<Self>,
1966 ) {
1967 self.show_inline_completions_override = show_edit_predictions;
1968
1969 if let Some(false) = show_edit_predictions {
1970 self.discard_inline_completion(false, cx);
1971 } else {
1972 self.refresh_inline_completion(false, true, window, cx);
1973 }
1974 }
1975
1976 fn inline_completions_disabled_in_scope(
1977 &self,
1978 buffer: &Entity<Buffer>,
1979 buffer_position: language::Anchor,
1980 cx: &App,
1981 ) -> bool {
1982 let snapshot = buffer.read(cx).snapshot();
1983 let settings = snapshot.settings_at(buffer_position, cx);
1984
1985 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1986 return false;
1987 };
1988
1989 scope.override_name().map_or(false, |scope_name| {
1990 settings
1991 .edit_predictions_disabled_in
1992 .iter()
1993 .any(|s| s == scope_name)
1994 })
1995 }
1996
1997 pub fn set_use_modal_editing(&mut self, to: bool) {
1998 self.use_modal_editing = to;
1999 }
2000
2001 pub fn use_modal_editing(&self) -> bool {
2002 self.use_modal_editing
2003 }
2004
2005 fn selections_did_change(
2006 &mut self,
2007 local: bool,
2008 old_cursor_position: &Anchor,
2009 show_completions: bool,
2010 window: &mut Window,
2011 cx: &mut Context<Self>,
2012 ) {
2013 window.invalidate_character_coordinates();
2014
2015 // Copy selections to primary selection buffer
2016 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2017 if local {
2018 let selections = self.selections.all::<usize>(cx);
2019 let buffer_handle = self.buffer.read(cx).read(cx);
2020
2021 let mut text = String::new();
2022 for (index, selection) in selections.iter().enumerate() {
2023 let text_for_selection = buffer_handle
2024 .text_for_range(selection.start..selection.end)
2025 .collect::<String>();
2026
2027 text.push_str(&text_for_selection);
2028 if index != selections.len() - 1 {
2029 text.push('\n');
2030 }
2031 }
2032
2033 if !text.is_empty() {
2034 cx.write_to_primary(ClipboardItem::new_string(text));
2035 }
2036 }
2037
2038 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2039 self.buffer.update(cx, |buffer, cx| {
2040 buffer.set_active_selections(
2041 &self.selections.disjoint_anchors(),
2042 self.selections.line_mode,
2043 self.cursor_shape,
2044 cx,
2045 )
2046 });
2047 }
2048 let display_map = self
2049 .display_map
2050 .update(cx, |display_map, cx| display_map.snapshot(cx));
2051 let buffer = &display_map.buffer_snapshot;
2052 self.add_selections_state = None;
2053 self.select_next_state = None;
2054 self.select_prev_state = None;
2055 self.select_larger_syntax_node_stack.clear();
2056 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2057 self.snippet_stack
2058 .invalidate(&self.selections.disjoint_anchors(), buffer);
2059 self.take_rename(false, window, cx);
2060
2061 let new_cursor_position = self.selections.newest_anchor().head();
2062
2063 self.push_to_nav_history(
2064 *old_cursor_position,
2065 Some(new_cursor_position.to_point(buffer)),
2066 cx,
2067 );
2068
2069 if local {
2070 let new_cursor_position = self.selections.newest_anchor().head();
2071 let mut context_menu = self.context_menu.borrow_mut();
2072 let completion_menu = match context_menu.as_ref() {
2073 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2074 _ => {
2075 *context_menu = None;
2076 None
2077 }
2078 };
2079 if let Some(buffer_id) = new_cursor_position.buffer_id {
2080 if !self.registered_buffers.contains_key(&buffer_id) {
2081 if let Some(project) = self.project.as_ref() {
2082 project.update(cx, |project, cx| {
2083 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2084 return;
2085 };
2086 self.registered_buffers.insert(
2087 buffer_id,
2088 project.register_buffer_with_language_servers(&buffer, cx),
2089 );
2090 })
2091 }
2092 }
2093 }
2094
2095 if let Some(completion_menu) = completion_menu {
2096 let cursor_position = new_cursor_position.to_offset(buffer);
2097 let (word_range, kind) =
2098 buffer.surrounding_word(completion_menu.initial_position, true);
2099 if kind == Some(CharKind::Word)
2100 && word_range.to_inclusive().contains(&cursor_position)
2101 {
2102 let mut completion_menu = completion_menu.clone();
2103 drop(context_menu);
2104
2105 let query = Self::completion_query(buffer, cursor_position);
2106 cx.spawn(move |this, mut cx| async move {
2107 completion_menu
2108 .filter(query.as_deref(), cx.background_executor().clone())
2109 .await;
2110
2111 this.update(&mut cx, |this, cx| {
2112 let mut context_menu = this.context_menu.borrow_mut();
2113 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2114 else {
2115 return;
2116 };
2117
2118 if menu.id > completion_menu.id {
2119 return;
2120 }
2121
2122 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2123 drop(context_menu);
2124 cx.notify();
2125 })
2126 })
2127 .detach();
2128
2129 if show_completions {
2130 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2131 }
2132 } else {
2133 drop(context_menu);
2134 self.hide_context_menu(window, cx);
2135 }
2136 } else {
2137 drop(context_menu);
2138 }
2139
2140 hide_hover(self, cx);
2141
2142 if old_cursor_position.to_display_point(&display_map).row()
2143 != new_cursor_position.to_display_point(&display_map).row()
2144 {
2145 self.available_code_actions.take();
2146 }
2147 self.refresh_code_actions(window, cx);
2148 self.refresh_document_highlights(cx);
2149 self.refresh_selected_text_highlights(window, cx);
2150 refresh_matching_bracket_highlights(self, window, cx);
2151 self.update_visible_inline_completion(window, cx);
2152 self.edit_prediction_requires_modifier_in_leading_space = true;
2153 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2154 if self.git_blame_inline_enabled {
2155 self.start_inline_blame_timer(window, cx);
2156 }
2157 }
2158
2159 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2160 cx.emit(EditorEvent::SelectionsChanged { local });
2161
2162 let selections = &self.selections.disjoint;
2163 if selections.len() == 1 {
2164 cx.emit(SearchEvent::ActiveMatchChanged)
2165 }
2166 if local
2167 && self.is_singleton(cx)
2168 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2169 {
2170 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2171 let background_executor = cx.background_executor().clone();
2172 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2173 let snapshot = self.buffer().read(cx).snapshot(cx);
2174 let selections = selections.clone();
2175 self.serialize_selections = cx.background_spawn(async move {
2176 background_executor.timer(Duration::from_millis(100)).await;
2177 let selections = selections
2178 .iter()
2179 .map(|selection| {
2180 (
2181 selection.start.to_offset(&snapshot),
2182 selection.end.to_offset(&snapshot),
2183 )
2184 })
2185 .collect();
2186 DB.save_editor_selections(editor_id, workspace_id, selections)
2187 .await
2188 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2189 .log_err();
2190 });
2191 }
2192 }
2193
2194 cx.notify();
2195 }
2196
2197 pub fn change_selections<R>(
2198 &mut self,
2199 autoscroll: Option<Autoscroll>,
2200 window: &mut Window,
2201 cx: &mut Context<Self>,
2202 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2203 ) -> R {
2204 self.change_selections_inner(autoscroll, true, window, cx, change)
2205 }
2206
2207 fn change_selections_inner<R>(
2208 &mut self,
2209 autoscroll: Option<Autoscroll>,
2210 request_completions: bool,
2211 window: &mut Window,
2212 cx: &mut Context<Self>,
2213 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2214 ) -> R {
2215 let old_cursor_position = self.selections.newest_anchor().head();
2216 self.push_to_selection_history();
2217
2218 let (changed, result) = self.selections.change_with(cx, change);
2219
2220 if changed {
2221 if let Some(autoscroll) = autoscroll {
2222 self.request_autoscroll(autoscroll, cx);
2223 }
2224 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2225
2226 if self.should_open_signature_help_automatically(
2227 &old_cursor_position,
2228 self.signature_help_state.backspace_pressed(),
2229 cx,
2230 ) {
2231 self.show_signature_help(&ShowSignatureHelp, window, cx);
2232 }
2233 self.signature_help_state.set_backspace_pressed(false);
2234 }
2235
2236 result
2237 }
2238
2239 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2240 where
2241 I: IntoIterator<Item = (Range<S>, T)>,
2242 S: ToOffset,
2243 T: Into<Arc<str>>,
2244 {
2245 if self.read_only(cx) {
2246 return;
2247 }
2248
2249 self.buffer
2250 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2251 }
2252
2253 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2254 where
2255 I: IntoIterator<Item = (Range<S>, T)>,
2256 S: ToOffset,
2257 T: Into<Arc<str>>,
2258 {
2259 if self.read_only(cx) {
2260 return;
2261 }
2262
2263 self.buffer.update(cx, |buffer, cx| {
2264 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2265 });
2266 }
2267
2268 pub fn edit_with_block_indent<I, S, T>(
2269 &mut self,
2270 edits: I,
2271 original_start_columns: Vec<u32>,
2272 cx: &mut Context<Self>,
2273 ) where
2274 I: IntoIterator<Item = (Range<S>, T)>,
2275 S: ToOffset,
2276 T: Into<Arc<str>>,
2277 {
2278 if self.read_only(cx) {
2279 return;
2280 }
2281
2282 self.buffer.update(cx, |buffer, cx| {
2283 buffer.edit(
2284 edits,
2285 Some(AutoindentMode::Block {
2286 original_start_columns,
2287 }),
2288 cx,
2289 )
2290 });
2291 }
2292
2293 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2294 self.hide_context_menu(window, cx);
2295
2296 match phase {
2297 SelectPhase::Begin {
2298 position,
2299 add,
2300 click_count,
2301 } => self.begin_selection(position, add, click_count, window, cx),
2302 SelectPhase::BeginColumnar {
2303 position,
2304 goal_column,
2305 reset,
2306 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2307 SelectPhase::Extend {
2308 position,
2309 click_count,
2310 } => self.extend_selection(position, click_count, window, cx),
2311 SelectPhase::Update {
2312 position,
2313 goal_column,
2314 scroll_delta,
2315 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2316 SelectPhase::End => self.end_selection(window, cx),
2317 }
2318 }
2319
2320 fn extend_selection(
2321 &mut self,
2322 position: DisplayPoint,
2323 click_count: usize,
2324 window: &mut Window,
2325 cx: &mut Context<Self>,
2326 ) {
2327 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2328 let tail = self.selections.newest::<usize>(cx).tail();
2329 self.begin_selection(position, false, click_count, window, cx);
2330
2331 let position = position.to_offset(&display_map, Bias::Left);
2332 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2333
2334 let mut pending_selection = self
2335 .selections
2336 .pending_anchor()
2337 .expect("extend_selection not called with pending selection");
2338 if position >= tail {
2339 pending_selection.start = tail_anchor;
2340 } else {
2341 pending_selection.end = tail_anchor;
2342 pending_selection.reversed = true;
2343 }
2344
2345 let mut pending_mode = self.selections.pending_mode().unwrap();
2346 match &mut pending_mode {
2347 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2348 _ => {}
2349 }
2350
2351 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2352 s.set_pending(pending_selection, pending_mode)
2353 });
2354 }
2355
2356 fn begin_selection(
2357 &mut self,
2358 position: DisplayPoint,
2359 add: bool,
2360 click_count: usize,
2361 window: &mut Window,
2362 cx: &mut Context<Self>,
2363 ) {
2364 if !self.focus_handle.is_focused(window) {
2365 self.last_focused_descendant = None;
2366 window.focus(&self.focus_handle);
2367 }
2368
2369 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2370 let buffer = &display_map.buffer_snapshot;
2371 let newest_selection = self.selections.newest_anchor().clone();
2372 let position = display_map.clip_point(position, Bias::Left);
2373
2374 let start;
2375 let end;
2376 let mode;
2377 let mut auto_scroll;
2378 match click_count {
2379 1 => {
2380 start = buffer.anchor_before(position.to_point(&display_map));
2381 end = start;
2382 mode = SelectMode::Character;
2383 auto_scroll = true;
2384 }
2385 2 => {
2386 let range = movement::surrounding_word(&display_map, position);
2387 start = buffer.anchor_before(range.start.to_point(&display_map));
2388 end = buffer.anchor_before(range.end.to_point(&display_map));
2389 mode = SelectMode::Word(start..end);
2390 auto_scroll = true;
2391 }
2392 3 => {
2393 let position = display_map
2394 .clip_point(position, Bias::Left)
2395 .to_point(&display_map);
2396 let line_start = display_map.prev_line_boundary(position).0;
2397 let next_line_start = buffer.clip_point(
2398 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2399 Bias::Left,
2400 );
2401 start = buffer.anchor_before(line_start);
2402 end = buffer.anchor_before(next_line_start);
2403 mode = SelectMode::Line(start..end);
2404 auto_scroll = true;
2405 }
2406 _ => {
2407 start = buffer.anchor_before(0);
2408 end = buffer.anchor_before(buffer.len());
2409 mode = SelectMode::All;
2410 auto_scroll = false;
2411 }
2412 }
2413 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2414
2415 let point_to_delete: Option<usize> = {
2416 let selected_points: Vec<Selection<Point>> =
2417 self.selections.disjoint_in_range(start..end, cx);
2418
2419 if !add || click_count > 1 {
2420 None
2421 } else if !selected_points.is_empty() {
2422 Some(selected_points[0].id)
2423 } else {
2424 let clicked_point_already_selected =
2425 self.selections.disjoint.iter().find(|selection| {
2426 selection.start.to_point(buffer) == start.to_point(buffer)
2427 || selection.end.to_point(buffer) == end.to_point(buffer)
2428 });
2429
2430 clicked_point_already_selected.map(|selection| selection.id)
2431 }
2432 };
2433
2434 let selections_count = self.selections.count();
2435
2436 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2437 if let Some(point_to_delete) = point_to_delete {
2438 s.delete(point_to_delete);
2439
2440 if selections_count == 1 {
2441 s.set_pending_anchor_range(start..end, mode);
2442 }
2443 } else {
2444 if !add {
2445 s.clear_disjoint();
2446 } else if click_count > 1 {
2447 s.delete(newest_selection.id)
2448 }
2449
2450 s.set_pending_anchor_range(start..end, mode);
2451 }
2452 });
2453 }
2454
2455 fn begin_columnar_selection(
2456 &mut self,
2457 position: DisplayPoint,
2458 goal_column: u32,
2459 reset: bool,
2460 window: &mut Window,
2461 cx: &mut Context<Self>,
2462 ) {
2463 if !self.focus_handle.is_focused(window) {
2464 self.last_focused_descendant = None;
2465 window.focus(&self.focus_handle);
2466 }
2467
2468 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2469
2470 if reset {
2471 let pointer_position = display_map
2472 .buffer_snapshot
2473 .anchor_before(position.to_point(&display_map));
2474
2475 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2476 s.clear_disjoint();
2477 s.set_pending_anchor_range(
2478 pointer_position..pointer_position,
2479 SelectMode::Character,
2480 );
2481 });
2482 }
2483
2484 let tail = self.selections.newest::<Point>(cx).tail();
2485 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2486
2487 if !reset {
2488 self.select_columns(
2489 tail.to_display_point(&display_map),
2490 position,
2491 goal_column,
2492 &display_map,
2493 window,
2494 cx,
2495 );
2496 }
2497 }
2498
2499 fn update_selection(
2500 &mut self,
2501 position: DisplayPoint,
2502 goal_column: u32,
2503 scroll_delta: gpui::Point<f32>,
2504 window: &mut Window,
2505 cx: &mut Context<Self>,
2506 ) {
2507 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2508
2509 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2510 let tail = tail.to_display_point(&display_map);
2511 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2512 } else if let Some(mut pending) = self.selections.pending_anchor() {
2513 let buffer = self.buffer.read(cx).snapshot(cx);
2514 let head;
2515 let tail;
2516 let mode = self.selections.pending_mode().unwrap();
2517 match &mode {
2518 SelectMode::Character => {
2519 head = position.to_point(&display_map);
2520 tail = pending.tail().to_point(&buffer);
2521 }
2522 SelectMode::Word(original_range) => {
2523 let original_display_range = original_range.start.to_display_point(&display_map)
2524 ..original_range.end.to_display_point(&display_map);
2525 let original_buffer_range = original_display_range.start.to_point(&display_map)
2526 ..original_display_range.end.to_point(&display_map);
2527 if movement::is_inside_word(&display_map, position)
2528 || original_display_range.contains(&position)
2529 {
2530 let word_range = movement::surrounding_word(&display_map, position);
2531 if word_range.start < original_display_range.start {
2532 head = word_range.start.to_point(&display_map);
2533 } else {
2534 head = word_range.end.to_point(&display_map);
2535 }
2536 } else {
2537 head = position.to_point(&display_map);
2538 }
2539
2540 if head <= original_buffer_range.start {
2541 tail = original_buffer_range.end;
2542 } else {
2543 tail = original_buffer_range.start;
2544 }
2545 }
2546 SelectMode::Line(original_range) => {
2547 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2548
2549 let position = display_map
2550 .clip_point(position, Bias::Left)
2551 .to_point(&display_map);
2552 let line_start = display_map.prev_line_boundary(position).0;
2553 let next_line_start = buffer.clip_point(
2554 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2555 Bias::Left,
2556 );
2557
2558 if line_start < original_range.start {
2559 head = line_start
2560 } else {
2561 head = next_line_start
2562 }
2563
2564 if head <= original_range.start {
2565 tail = original_range.end;
2566 } else {
2567 tail = original_range.start;
2568 }
2569 }
2570 SelectMode::All => {
2571 return;
2572 }
2573 };
2574
2575 if head < tail {
2576 pending.start = buffer.anchor_before(head);
2577 pending.end = buffer.anchor_before(tail);
2578 pending.reversed = true;
2579 } else {
2580 pending.start = buffer.anchor_before(tail);
2581 pending.end = buffer.anchor_before(head);
2582 pending.reversed = false;
2583 }
2584
2585 self.change_selections(None, window, cx, |s| {
2586 s.set_pending(pending, mode);
2587 });
2588 } else {
2589 log::error!("update_selection dispatched with no pending selection");
2590 return;
2591 }
2592
2593 self.apply_scroll_delta(scroll_delta, window, cx);
2594 cx.notify();
2595 }
2596
2597 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2598 self.columnar_selection_tail.take();
2599 if self.selections.pending_anchor().is_some() {
2600 let selections = self.selections.all::<usize>(cx);
2601 self.change_selections(None, window, cx, |s| {
2602 s.select(selections);
2603 s.clear_pending();
2604 });
2605 }
2606 }
2607
2608 fn select_columns(
2609 &mut self,
2610 tail: DisplayPoint,
2611 head: DisplayPoint,
2612 goal_column: u32,
2613 display_map: &DisplaySnapshot,
2614 window: &mut Window,
2615 cx: &mut Context<Self>,
2616 ) {
2617 let start_row = cmp::min(tail.row(), head.row());
2618 let end_row = cmp::max(tail.row(), head.row());
2619 let start_column = cmp::min(tail.column(), goal_column);
2620 let end_column = cmp::max(tail.column(), goal_column);
2621 let reversed = start_column < tail.column();
2622
2623 let selection_ranges = (start_row.0..=end_row.0)
2624 .map(DisplayRow)
2625 .filter_map(|row| {
2626 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2627 let start = display_map
2628 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2629 .to_point(display_map);
2630 let end = display_map
2631 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2632 .to_point(display_map);
2633 if reversed {
2634 Some(end..start)
2635 } else {
2636 Some(start..end)
2637 }
2638 } else {
2639 None
2640 }
2641 })
2642 .collect::<Vec<_>>();
2643
2644 self.change_selections(None, window, cx, |s| {
2645 s.select_ranges(selection_ranges);
2646 });
2647 cx.notify();
2648 }
2649
2650 pub fn has_pending_nonempty_selection(&self) -> bool {
2651 let pending_nonempty_selection = match self.selections.pending_anchor() {
2652 Some(Selection { start, end, .. }) => start != end,
2653 None => false,
2654 };
2655
2656 pending_nonempty_selection
2657 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2658 }
2659
2660 pub fn has_pending_selection(&self) -> bool {
2661 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2662 }
2663
2664 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2665 self.selection_mark_mode = false;
2666
2667 if self.clear_expanded_diff_hunks(cx) {
2668 cx.notify();
2669 return;
2670 }
2671 if self.dismiss_menus_and_popups(true, window, cx) {
2672 return;
2673 }
2674
2675 if self.mode == EditorMode::Full
2676 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2677 {
2678 return;
2679 }
2680
2681 cx.propagate();
2682 }
2683
2684 pub fn dismiss_menus_and_popups(
2685 &mut self,
2686 is_user_requested: bool,
2687 window: &mut Window,
2688 cx: &mut Context<Self>,
2689 ) -> bool {
2690 if self.take_rename(false, window, cx).is_some() {
2691 return true;
2692 }
2693
2694 if hide_hover(self, cx) {
2695 return true;
2696 }
2697
2698 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2699 return true;
2700 }
2701
2702 if self.hide_context_menu(window, cx).is_some() {
2703 return true;
2704 }
2705
2706 if self.mouse_context_menu.take().is_some() {
2707 return true;
2708 }
2709
2710 if is_user_requested && self.discard_inline_completion(true, cx) {
2711 return true;
2712 }
2713
2714 if self.snippet_stack.pop().is_some() {
2715 return true;
2716 }
2717
2718 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2719 self.dismiss_diagnostics(cx);
2720 return true;
2721 }
2722
2723 false
2724 }
2725
2726 fn linked_editing_ranges_for(
2727 &self,
2728 selection: Range<text::Anchor>,
2729 cx: &App,
2730 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2731 if self.linked_edit_ranges.is_empty() {
2732 return None;
2733 }
2734 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2735 selection.end.buffer_id.and_then(|end_buffer_id| {
2736 if selection.start.buffer_id != Some(end_buffer_id) {
2737 return None;
2738 }
2739 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2740 let snapshot = buffer.read(cx).snapshot();
2741 self.linked_edit_ranges
2742 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2743 .map(|ranges| (ranges, snapshot, buffer))
2744 })?;
2745 use text::ToOffset as TO;
2746 // find offset from the start of current range to current cursor position
2747 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2748
2749 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2750 let start_difference = start_offset - start_byte_offset;
2751 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2752 let end_difference = end_offset - start_byte_offset;
2753 // Current range has associated linked ranges.
2754 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2755 for range in linked_ranges.iter() {
2756 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2757 let end_offset = start_offset + end_difference;
2758 let start_offset = start_offset + start_difference;
2759 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2760 continue;
2761 }
2762 if self.selections.disjoint_anchor_ranges().any(|s| {
2763 if s.start.buffer_id != selection.start.buffer_id
2764 || s.end.buffer_id != selection.end.buffer_id
2765 {
2766 return false;
2767 }
2768 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2769 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2770 }) {
2771 continue;
2772 }
2773 let start = buffer_snapshot.anchor_after(start_offset);
2774 let end = buffer_snapshot.anchor_after(end_offset);
2775 linked_edits
2776 .entry(buffer.clone())
2777 .or_default()
2778 .push(start..end);
2779 }
2780 Some(linked_edits)
2781 }
2782
2783 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2784 let text: Arc<str> = text.into();
2785
2786 if self.read_only(cx) {
2787 return;
2788 }
2789
2790 let selections = self.selections.all_adjusted(cx);
2791 let mut bracket_inserted = false;
2792 let mut edits = Vec::new();
2793 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2794 let mut new_selections = Vec::with_capacity(selections.len());
2795 let mut new_autoclose_regions = Vec::new();
2796 let snapshot = self.buffer.read(cx).read(cx);
2797
2798 for (selection, autoclose_region) in
2799 self.selections_with_autoclose_regions(selections, &snapshot)
2800 {
2801 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2802 // Determine if the inserted text matches the opening or closing
2803 // bracket of any of this language's bracket pairs.
2804 let mut bracket_pair = None;
2805 let mut is_bracket_pair_start = false;
2806 let mut is_bracket_pair_end = false;
2807 if !text.is_empty() {
2808 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2809 // and they are removing the character that triggered IME popup.
2810 for (pair, enabled) in scope.brackets() {
2811 if !pair.close && !pair.surround {
2812 continue;
2813 }
2814
2815 if enabled && pair.start.ends_with(text.as_ref()) {
2816 let prefix_len = pair.start.len() - text.len();
2817 let preceding_text_matches_prefix = prefix_len == 0
2818 || (selection.start.column >= (prefix_len as u32)
2819 && snapshot.contains_str_at(
2820 Point::new(
2821 selection.start.row,
2822 selection.start.column - (prefix_len as u32),
2823 ),
2824 &pair.start[..prefix_len],
2825 ));
2826 if preceding_text_matches_prefix {
2827 bracket_pair = Some(pair.clone());
2828 is_bracket_pair_start = true;
2829 break;
2830 }
2831 }
2832 if pair.end.as_str() == text.as_ref() {
2833 bracket_pair = Some(pair.clone());
2834 is_bracket_pair_end = true;
2835 break;
2836 }
2837 }
2838 }
2839
2840 if let Some(bracket_pair) = bracket_pair {
2841 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2842 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2843 let auto_surround =
2844 self.use_auto_surround && snapshot_settings.use_auto_surround;
2845 if selection.is_empty() {
2846 if is_bracket_pair_start {
2847 // If the inserted text is a suffix of an opening bracket and the
2848 // selection is preceded by the rest of the opening bracket, then
2849 // insert the closing bracket.
2850 let following_text_allows_autoclose = snapshot
2851 .chars_at(selection.start)
2852 .next()
2853 .map_or(true, |c| scope.should_autoclose_before(c));
2854
2855 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2856 && bracket_pair.start.len() == 1
2857 {
2858 let target = bracket_pair.start.chars().next().unwrap();
2859 let current_line_count = snapshot
2860 .reversed_chars_at(selection.start)
2861 .take_while(|&c| c != '\n')
2862 .filter(|&c| c == target)
2863 .count();
2864 current_line_count % 2 == 1
2865 } else {
2866 false
2867 };
2868
2869 if autoclose
2870 && bracket_pair.close
2871 && following_text_allows_autoclose
2872 && !is_closing_quote
2873 {
2874 let anchor = snapshot.anchor_before(selection.end);
2875 new_selections.push((selection.map(|_| anchor), text.len()));
2876 new_autoclose_regions.push((
2877 anchor,
2878 text.len(),
2879 selection.id,
2880 bracket_pair.clone(),
2881 ));
2882 edits.push((
2883 selection.range(),
2884 format!("{}{}", text, bracket_pair.end).into(),
2885 ));
2886 bracket_inserted = true;
2887 continue;
2888 }
2889 }
2890
2891 if let Some(region) = autoclose_region {
2892 // If the selection is followed by an auto-inserted closing bracket,
2893 // then don't insert that closing bracket again; just move the selection
2894 // past the closing bracket.
2895 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2896 && text.as_ref() == region.pair.end.as_str();
2897 if should_skip {
2898 let anchor = snapshot.anchor_after(selection.end);
2899 new_selections
2900 .push((selection.map(|_| anchor), region.pair.end.len()));
2901 continue;
2902 }
2903 }
2904
2905 let always_treat_brackets_as_autoclosed = snapshot
2906 .settings_at(selection.start, cx)
2907 .always_treat_brackets_as_autoclosed;
2908 if always_treat_brackets_as_autoclosed
2909 && is_bracket_pair_end
2910 && snapshot.contains_str_at(selection.end, text.as_ref())
2911 {
2912 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2913 // and the inserted text is a closing bracket and the selection is followed
2914 // by the closing bracket then move the selection past the closing bracket.
2915 let anchor = snapshot.anchor_after(selection.end);
2916 new_selections.push((selection.map(|_| anchor), text.len()));
2917 continue;
2918 }
2919 }
2920 // If an opening bracket is 1 character long and is typed while
2921 // text is selected, then surround that text with the bracket pair.
2922 else if auto_surround
2923 && bracket_pair.surround
2924 && is_bracket_pair_start
2925 && bracket_pair.start.chars().count() == 1
2926 {
2927 edits.push((selection.start..selection.start, text.clone()));
2928 edits.push((
2929 selection.end..selection.end,
2930 bracket_pair.end.as_str().into(),
2931 ));
2932 bracket_inserted = true;
2933 new_selections.push((
2934 Selection {
2935 id: selection.id,
2936 start: snapshot.anchor_after(selection.start),
2937 end: snapshot.anchor_before(selection.end),
2938 reversed: selection.reversed,
2939 goal: selection.goal,
2940 },
2941 0,
2942 ));
2943 continue;
2944 }
2945 }
2946 }
2947
2948 if self.auto_replace_emoji_shortcode
2949 && selection.is_empty()
2950 && text.as_ref().ends_with(':')
2951 {
2952 if let Some(possible_emoji_short_code) =
2953 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2954 {
2955 if !possible_emoji_short_code.is_empty() {
2956 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2957 let emoji_shortcode_start = Point::new(
2958 selection.start.row,
2959 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2960 );
2961
2962 // Remove shortcode from buffer
2963 edits.push((
2964 emoji_shortcode_start..selection.start,
2965 "".to_string().into(),
2966 ));
2967 new_selections.push((
2968 Selection {
2969 id: selection.id,
2970 start: snapshot.anchor_after(emoji_shortcode_start),
2971 end: snapshot.anchor_before(selection.start),
2972 reversed: selection.reversed,
2973 goal: selection.goal,
2974 },
2975 0,
2976 ));
2977
2978 // Insert emoji
2979 let selection_start_anchor = snapshot.anchor_after(selection.start);
2980 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2981 edits.push((selection.start..selection.end, emoji.to_string().into()));
2982
2983 continue;
2984 }
2985 }
2986 }
2987 }
2988
2989 // If not handling any auto-close operation, then just replace the selected
2990 // text with the given input and move the selection to the end of the
2991 // newly inserted text.
2992 let anchor = snapshot.anchor_after(selection.end);
2993 if !self.linked_edit_ranges.is_empty() {
2994 let start_anchor = snapshot.anchor_before(selection.start);
2995
2996 let is_word_char = text.chars().next().map_or(true, |char| {
2997 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2998 classifier.is_word(char)
2999 });
3000
3001 if is_word_char {
3002 if let Some(ranges) = self
3003 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3004 {
3005 for (buffer, edits) in ranges {
3006 linked_edits
3007 .entry(buffer.clone())
3008 .or_default()
3009 .extend(edits.into_iter().map(|range| (range, text.clone())));
3010 }
3011 }
3012 }
3013 }
3014
3015 new_selections.push((selection.map(|_| anchor), 0));
3016 edits.push((selection.start..selection.end, text.clone()));
3017 }
3018
3019 drop(snapshot);
3020
3021 self.transact(window, cx, |this, window, cx| {
3022 this.buffer.update(cx, |buffer, cx| {
3023 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3024 });
3025 for (buffer, edits) in linked_edits {
3026 buffer.update(cx, |buffer, cx| {
3027 let snapshot = buffer.snapshot();
3028 let edits = edits
3029 .into_iter()
3030 .map(|(range, text)| {
3031 use text::ToPoint as TP;
3032 let end_point = TP::to_point(&range.end, &snapshot);
3033 let start_point = TP::to_point(&range.start, &snapshot);
3034 (start_point..end_point, text)
3035 })
3036 .sorted_by_key(|(range, _)| range.start)
3037 .collect::<Vec<_>>();
3038 buffer.edit(edits, None, cx);
3039 })
3040 }
3041 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3042 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3043 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3044 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3045 .zip(new_selection_deltas)
3046 .map(|(selection, delta)| Selection {
3047 id: selection.id,
3048 start: selection.start + delta,
3049 end: selection.end + delta,
3050 reversed: selection.reversed,
3051 goal: SelectionGoal::None,
3052 })
3053 .collect::<Vec<_>>();
3054
3055 let mut i = 0;
3056 for (position, delta, selection_id, pair) in new_autoclose_regions {
3057 let position = position.to_offset(&map.buffer_snapshot) + delta;
3058 let start = map.buffer_snapshot.anchor_before(position);
3059 let end = map.buffer_snapshot.anchor_after(position);
3060 while let Some(existing_state) = this.autoclose_regions.get(i) {
3061 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3062 Ordering::Less => i += 1,
3063 Ordering::Greater => break,
3064 Ordering::Equal => {
3065 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3066 Ordering::Less => i += 1,
3067 Ordering::Equal => break,
3068 Ordering::Greater => break,
3069 }
3070 }
3071 }
3072 }
3073 this.autoclose_regions.insert(
3074 i,
3075 AutocloseRegion {
3076 selection_id,
3077 range: start..end,
3078 pair,
3079 },
3080 );
3081 }
3082
3083 let had_active_inline_completion = this.has_active_inline_completion();
3084 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3085 s.select(new_selections)
3086 });
3087
3088 if !bracket_inserted {
3089 if let Some(on_type_format_task) =
3090 this.trigger_on_type_formatting(text.to_string(), window, cx)
3091 {
3092 on_type_format_task.detach_and_log_err(cx);
3093 }
3094 }
3095
3096 let editor_settings = EditorSettings::get_global(cx);
3097 if bracket_inserted
3098 && (editor_settings.auto_signature_help
3099 || editor_settings.show_signature_help_after_edits)
3100 {
3101 this.show_signature_help(&ShowSignatureHelp, window, cx);
3102 }
3103
3104 let trigger_in_words =
3105 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3106 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3107 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3108 this.refresh_inline_completion(true, false, window, cx);
3109 });
3110 }
3111
3112 fn find_possible_emoji_shortcode_at_position(
3113 snapshot: &MultiBufferSnapshot,
3114 position: Point,
3115 ) -> Option<String> {
3116 let mut chars = Vec::new();
3117 let mut found_colon = false;
3118 for char in snapshot.reversed_chars_at(position).take(100) {
3119 // Found a possible emoji shortcode in the middle of the buffer
3120 if found_colon {
3121 if char.is_whitespace() {
3122 chars.reverse();
3123 return Some(chars.iter().collect());
3124 }
3125 // If the previous character is not a whitespace, we are in the middle of a word
3126 // and we only want to complete the shortcode if the word is made up of other emojis
3127 let mut containing_word = String::new();
3128 for ch in snapshot
3129 .reversed_chars_at(position)
3130 .skip(chars.len() + 1)
3131 .take(100)
3132 {
3133 if ch.is_whitespace() {
3134 break;
3135 }
3136 containing_word.push(ch);
3137 }
3138 let containing_word = containing_word.chars().rev().collect::<String>();
3139 if util::word_consists_of_emojis(containing_word.as_str()) {
3140 chars.reverse();
3141 return Some(chars.iter().collect());
3142 }
3143 }
3144
3145 if char.is_whitespace() || !char.is_ascii() {
3146 return None;
3147 }
3148 if char == ':' {
3149 found_colon = true;
3150 } else {
3151 chars.push(char);
3152 }
3153 }
3154 // Found a possible emoji shortcode at the beginning of the buffer
3155 chars.reverse();
3156 Some(chars.iter().collect())
3157 }
3158
3159 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3160 self.transact(window, cx, |this, window, cx| {
3161 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3162 let selections = this.selections.all::<usize>(cx);
3163 let multi_buffer = this.buffer.read(cx);
3164 let buffer = multi_buffer.snapshot(cx);
3165 selections
3166 .iter()
3167 .map(|selection| {
3168 let start_point = selection.start.to_point(&buffer);
3169 let mut indent =
3170 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3171 indent.len = cmp::min(indent.len, start_point.column);
3172 let start = selection.start;
3173 let end = selection.end;
3174 let selection_is_empty = start == end;
3175 let language_scope = buffer.language_scope_at(start);
3176 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3177 &language_scope
3178 {
3179 let insert_extra_newline =
3180 insert_extra_newline_brackets(&buffer, start..end, language)
3181 || insert_extra_newline_tree_sitter(&buffer, start..end);
3182
3183 // Comment extension on newline is allowed only for cursor selections
3184 let comment_delimiter = maybe!({
3185 if !selection_is_empty {
3186 return None;
3187 }
3188
3189 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3190 return None;
3191 }
3192
3193 let delimiters = language.line_comment_prefixes();
3194 let max_len_of_delimiter =
3195 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3196 let (snapshot, range) =
3197 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3198
3199 let mut index_of_first_non_whitespace = 0;
3200 let comment_candidate = snapshot
3201 .chars_for_range(range)
3202 .skip_while(|c| {
3203 let should_skip = c.is_whitespace();
3204 if should_skip {
3205 index_of_first_non_whitespace += 1;
3206 }
3207 should_skip
3208 })
3209 .take(max_len_of_delimiter)
3210 .collect::<String>();
3211 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3212 comment_candidate.starts_with(comment_prefix.as_ref())
3213 })?;
3214 let cursor_is_placed_after_comment_marker =
3215 index_of_first_non_whitespace + comment_prefix.len()
3216 <= start_point.column as usize;
3217 if cursor_is_placed_after_comment_marker {
3218 Some(comment_prefix.clone())
3219 } else {
3220 None
3221 }
3222 });
3223 (comment_delimiter, insert_extra_newline)
3224 } else {
3225 (None, false)
3226 };
3227
3228 let capacity_for_delimiter = comment_delimiter
3229 .as_deref()
3230 .map(str::len)
3231 .unwrap_or_default();
3232 let mut new_text =
3233 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3234 new_text.push('\n');
3235 new_text.extend(indent.chars());
3236 if let Some(delimiter) = &comment_delimiter {
3237 new_text.push_str(delimiter);
3238 }
3239 if insert_extra_newline {
3240 new_text = new_text.repeat(2);
3241 }
3242
3243 let anchor = buffer.anchor_after(end);
3244 let new_selection = selection.map(|_| anchor);
3245 (
3246 (start..end, new_text),
3247 (insert_extra_newline, new_selection),
3248 )
3249 })
3250 .unzip()
3251 };
3252
3253 this.edit_with_autoindent(edits, cx);
3254 let buffer = this.buffer.read(cx).snapshot(cx);
3255 let new_selections = selection_fixup_info
3256 .into_iter()
3257 .map(|(extra_newline_inserted, new_selection)| {
3258 let mut cursor = new_selection.end.to_point(&buffer);
3259 if extra_newline_inserted {
3260 cursor.row -= 1;
3261 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3262 }
3263 new_selection.map(|_| cursor)
3264 })
3265 .collect();
3266
3267 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3268 s.select(new_selections)
3269 });
3270 this.refresh_inline_completion(true, false, window, cx);
3271 });
3272 }
3273
3274 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3275 let buffer = self.buffer.read(cx);
3276 let snapshot = buffer.snapshot(cx);
3277
3278 let mut edits = Vec::new();
3279 let mut rows = Vec::new();
3280
3281 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3282 let cursor = selection.head();
3283 let row = cursor.row;
3284
3285 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3286
3287 let newline = "\n".to_string();
3288 edits.push((start_of_line..start_of_line, newline));
3289
3290 rows.push(row + rows_inserted as u32);
3291 }
3292
3293 self.transact(window, cx, |editor, window, cx| {
3294 editor.edit(edits, cx);
3295
3296 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3297 let mut index = 0;
3298 s.move_cursors_with(|map, _, _| {
3299 let row = rows[index];
3300 index += 1;
3301
3302 let point = Point::new(row, 0);
3303 let boundary = map.next_line_boundary(point).1;
3304 let clipped = map.clip_point(boundary, Bias::Left);
3305
3306 (clipped, SelectionGoal::None)
3307 });
3308 });
3309
3310 let mut indent_edits = Vec::new();
3311 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3312 for row in rows {
3313 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3314 for (row, indent) in indents {
3315 if indent.len == 0 {
3316 continue;
3317 }
3318
3319 let text = match indent.kind {
3320 IndentKind::Space => " ".repeat(indent.len as usize),
3321 IndentKind::Tab => "\t".repeat(indent.len as usize),
3322 };
3323 let point = Point::new(row.0, 0);
3324 indent_edits.push((point..point, text));
3325 }
3326 }
3327 editor.edit(indent_edits, cx);
3328 });
3329 }
3330
3331 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3332 let buffer = self.buffer.read(cx);
3333 let snapshot = buffer.snapshot(cx);
3334
3335 let mut edits = Vec::new();
3336 let mut rows = Vec::new();
3337 let mut rows_inserted = 0;
3338
3339 for selection in self.selections.all_adjusted(cx) {
3340 let cursor = selection.head();
3341 let row = cursor.row;
3342
3343 let point = Point::new(row + 1, 0);
3344 let start_of_line = snapshot.clip_point(point, Bias::Left);
3345
3346 let newline = "\n".to_string();
3347 edits.push((start_of_line..start_of_line, newline));
3348
3349 rows_inserted += 1;
3350 rows.push(row + rows_inserted);
3351 }
3352
3353 self.transact(window, cx, |editor, window, cx| {
3354 editor.edit(edits, cx);
3355
3356 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3357 let mut index = 0;
3358 s.move_cursors_with(|map, _, _| {
3359 let row = rows[index];
3360 index += 1;
3361
3362 let point = Point::new(row, 0);
3363 let boundary = map.next_line_boundary(point).1;
3364 let clipped = map.clip_point(boundary, Bias::Left);
3365
3366 (clipped, SelectionGoal::None)
3367 });
3368 });
3369
3370 let mut indent_edits = Vec::new();
3371 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3372 for row in rows {
3373 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3374 for (row, indent) in indents {
3375 if indent.len == 0 {
3376 continue;
3377 }
3378
3379 let text = match indent.kind {
3380 IndentKind::Space => " ".repeat(indent.len as usize),
3381 IndentKind::Tab => "\t".repeat(indent.len as usize),
3382 };
3383 let point = Point::new(row.0, 0);
3384 indent_edits.push((point..point, text));
3385 }
3386 }
3387 editor.edit(indent_edits, cx);
3388 });
3389 }
3390
3391 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3392 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3393 original_start_columns: Vec::new(),
3394 });
3395 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3396 }
3397
3398 fn insert_with_autoindent_mode(
3399 &mut self,
3400 text: &str,
3401 autoindent_mode: Option<AutoindentMode>,
3402 window: &mut Window,
3403 cx: &mut Context<Self>,
3404 ) {
3405 if self.read_only(cx) {
3406 return;
3407 }
3408
3409 let text: Arc<str> = text.into();
3410 self.transact(window, cx, |this, window, cx| {
3411 let old_selections = this.selections.all_adjusted(cx);
3412 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3413 let anchors = {
3414 let snapshot = buffer.read(cx);
3415 old_selections
3416 .iter()
3417 .map(|s| {
3418 let anchor = snapshot.anchor_after(s.head());
3419 s.map(|_| anchor)
3420 })
3421 .collect::<Vec<_>>()
3422 };
3423 buffer.edit(
3424 old_selections
3425 .iter()
3426 .map(|s| (s.start..s.end, text.clone())),
3427 autoindent_mode,
3428 cx,
3429 );
3430 anchors
3431 });
3432
3433 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3434 s.select_anchors(selection_anchors);
3435 });
3436
3437 cx.notify();
3438 });
3439 }
3440
3441 fn trigger_completion_on_input(
3442 &mut self,
3443 text: &str,
3444 trigger_in_words: bool,
3445 window: &mut Window,
3446 cx: &mut Context<Self>,
3447 ) {
3448 if self.is_completion_trigger(text, trigger_in_words, cx) {
3449 self.show_completions(
3450 &ShowCompletions {
3451 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3452 },
3453 window,
3454 cx,
3455 );
3456 } else {
3457 self.hide_context_menu(window, cx);
3458 }
3459 }
3460
3461 fn is_completion_trigger(
3462 &self,
3463 text: &str,
3464 trigger_in_words: bool,
3465 cx: &mut Context<Self>,
3466 ) -> bool {
3467 let position = self.selections.newest_anchor().head();
3468 let multibuffer = self.buffer.read(cx);
3469 let Some(buffer) = position
3470 .buffer_id
3471 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3472 else {
3473 return false;
3474 };
3475
3476 if let Some(completion_provider) = &self.completion_provider {
3477 completion_provider.is_completion_trigger(
3478 &buffer,
3479 position.text_anchor,
3480 text,
3481 trigger_in_words,
3482 cx,
3483 )
3484 } else {
3485 false
3486 }
3487 }
3488
3489 /// If any empty selections is touching the start of its innermost containing autoclose
3490 /// region, expand it to select the brackets.
3491 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3492 let selections = self.selections.all::<usize>(cx);
3493 let buffer = self.buffer.read(cx).read(cx);
3494 let new_selections = self
3495 .selections_with_autoclose_regions(selections, &buffer)
3496 .map(|(mut selection, region)| {
3497 if !selection.is_empty() {
3498 return selection;
3499 }
3500
3501 if let Some(region) = region {
3502 let mut range = region.range.to_offset(&buffer);
3503 if selection.start == range.start && range.start >= region.pair.start.len() {
3504 range.start -= region.pair.start.len();
3505 if buffer.contains_str_at(range.start, ®ion.pair.start)
3506 && buffer.contains_str_at(range.end, ®ion.pair.end)
3507 {
3508 range.end += region.pair.end.len();
3509 selection.start = range.start;
3510 selection.end = range.end;
3511
3512 return selection;
3513 }
3514 }
3515 }
3516
3517 let always_treat_brackets_as_autoclosed = buffer
3518 .settings_at(selection.start, cx)
3519 .always_treat_brackets_as_autoclosed;
3520
3521 if !always_treat_brackets_as_autoclosed {
3522 return selection;
3523 }
3524
3525 if let Some(scope) = buffer.language_scope_at(selection.start) {
3526 for (pair, enabled) in scope.brackets() {
3527 if !enabled || !pair.close {
3528 continue;
3529 }
3530
3531 if buffer.contains_str_at(selection.start, &pair.end) {
3532 let pair_start_len = pair.start.len();
3533 if buffer.contains_str_at(
3534 selection.start.saturating_sub(pair_start_len),
3535 &pair.start,
3536 ) {
3537 selection.start -= pair_start_len;
3538 selection.end += pair.end.len();
3539
3540 return selection;
3541 }
3542 }
3543 }
3544 }
3545
3546 selection
3547 })
3548 .collect();
3549
3550 drop(buffer);
3551 self.change_selections(None, window, cx, |selections| {
3552 selections.select(new_selections)
3553 });
3554 }
3555
3556 /// Iterate the given selections, and for each one, find the smallest surrounding
3557 /// autoclose region. This uses the ordering of the selections and the autoclose
3558 /// regions to avoid repeated comparisons.
3559 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3560 &'a self,
3561 selections: impl IntoIterator<Item = Selection<D>>,
3562 buffer: &'a MultiBufferSnapshot,
3563 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3564 let mut i = 0;
3565 let mut regions = self.autoclose_regions.as_slice();
3566 selections.into_iter().map(move |selection| {
3567 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3568
3569 let mut enclosing = None;
3570 while let Some(pair_state) = regions.get(i) {
3571 if pair_state.range.end.to_offset(buffer) < range.start {
3572 regions = ®ions[i + 1..];
3573 i = 0;
3574 } else if pair_state.range.start.to_offset(buffer) > range.end {
3575 break;
3576 } else {
3577 if pair_state.selection_id == selection.id {
3578 enclosing = Some(pair_state);
3579 }
3580 i += 1;
3581 }
3582 }
3583
3584 (selection, enclosing)
3585 })
3586 }
3587
3588 /// Remove any autoclose regions that no longer contain their selection.
3589 fn invalidate_autoclose_regions(
3590 &mut self,
3591 mut selections: &[Selection<Anchor>],
3592 buffer: &MultiBufferSnapshot,
3593 ) {
3594 self.autoclose_regions.retain(|state| {
3595 let mut i = 0;
3596 while let Some(selection) = selections.get(i) {
3597 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3598 selections = &selections[1..];
3599 continue;
3600 }
3601 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3602 break;
3603 }
3604 if selection.id == state.selection_id {
3605 return true;
3606 } else {
3607 i += 1;
3608 }
3609 }
3610 false
3611 });
3612 }
3613
3614 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3615 let offset = position.to_offset(buffer);
3616 let (word_range, kind) = buffer.surrounding_word(offset, true);
3617 if offset > word_range.start && kind == Some(CharKind::Word) {
3618 Some(
3619 buffer
3620 .text_for_range(word_range.start..offset)
3621 .collect::<String>(),
3622 )
3623 } else {
3624 None
3625 }
3626 }
3627
3628 pub fn toggle_inlay_hints(
3629 &mut self,
3630 _: &ToggleInlayHints,
3631 _: &mut Window,
3632 cx: &mut Context<Self>,
3633 ) {
3634 self.refresh_inlay_hints(
3635 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3636 cx,
3637 );
3638 }
3639
3640 pub fn inlay_hints_enabled(&self) -> bool {
3641 self.inlay_hint_cache.enabled
3642 }
3643
3644 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3645 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3646 return;
3647 }
3648
3649 let reason_description = reason.description();
3650 let ignore_debounce = matches!(
3651 reason,
3652 InlayHintRefreshReason::SettingsChange(_)
3653 | InlayHintRefreshReason::Toggle(_)
3654 | InlayHintRefreshReason::ExcerptsRemoved(_)
3655 );
3656 let (invalidate_cache, required_languages) = match reason {
3657 InlayHintRefreshReason::Toggle(enabled) => {
3658 self.inlay_hint_cache.enabled = enabled;
3659 if enabled {
3660 (InvalidationStrategy::RefreshRequested, None)
3661 } else {
3662 self.inlay_hint_cache.clear();
3663 self.splice_inlays(
3664 &self
3665 .visible_inlay_hints(cx)
3666 .iter()
3667 .map(|inlay| inlay.id)
3668 .collect::<Vec<InlayId>>(),
3669 Vec::new(),
3670 cx,
3671 );
3672 return;
3673 }
3674 }
3675 InlayHintRefreshReason::SettingsChange(new_settings) => {
3676 match self.inlay_hint_cache.update_settings(
3677 &self.buffer,
3678 new_settings,
3679 self.visible_inlay_hints(cx),
3680 cx,
3681 ) {
3682 ControlFlow::Break(Some(InlaySplice {
3683 to_remove,
3684 to_insert,
3685 })) => {
3686 self.splice_inlays(&to_remove, to_insert, cx);
3687 return;
3688 }
3689 ControlFlow::Break(None) => return,
3690 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3691 }
3692 }
3693 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3694 if let Some(InlaySplice {
3695 to_remove,
3696 to_insert,
3697 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3698 {
3699 self.splice_inlays(&to_remove, to_insert, cx);
3700 }
3701 return;
3702 }
3703 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3704 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3705 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3706 }
3707 InlayHintRefreshReason::RefreshRequested => {
3708 (InvalidationStrategy::RefreshRequested, None)
3709 }
3710 };
3711
3712 if let Some(InlaySplice {
3713 to_remove,
3714 to_insert,
3715 }) = self.inlay_hint_cache.spawn_hint_refresh(
3716 reason_description,
3717 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3718 invalidate_cache,
3719 ignore_debounce,
3720 cx,
3721 ) {
3722 self.splice_inlays(&to_remove, to_insert, cx);
3723 }
3724 }
3725
3726 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3727 self.display_map
3728 .read(cx)
3729 .current_inlays()
3730 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3731 .cloned()
3732 .collect()
3733 }
3734
3735 pub fn excerpts_for_inlay_hints_query(
3736 &self,
3737 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3738 cx: &mut Context<Editor>,
3739 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3740 let Some(project) = self.project.as_ref() else {
3741 return HashMap::default();
3742 };
3743 let project = project.read(cx);
3744 let multi_buffer = self.buffer().read(cx);
3745 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3746 let multi_buffer_visible_start = self
3747 .scroll_manager
3748 .anchor()
3749 .anchor
3750 .to_point(&multi_buffer_snapshot);
3751 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3752 multi_buffer_visible_start
3753 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3754 Bias::Left,
3755 );
3756 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3757 multi_buffer_snapshot
3758 .range_to_buffer_ranges(multi_buffer_visible_range)
3759 .into_iter()
3760 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3761 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3762 let buffer_file = project::File::from_dyn(buffer.file())?;
3763 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3764 let worktree_entry = buffer_worktree
3765 .read(cx)
3766 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3767 if worktree_entry.is_ignored {
3768 return None;
3769 }
3770
3771 let language = buffer.language()?;
3772 if let Some(restrict_to_languages) = restrict_to_languages {
3773 if !restrict_to_languages.contains(language) {
3774 return None;
3775 }
3776 }
3777 Some((
3778 excerpt_id,
3779 (
3780 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3781 buffer.version().clone(),
3782 excerpt_visible_range,
3783 ),
3784 ))
3785 })
3786 .collect()
3787 }
3788
3789 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3790 TextLayoutDetails {
3791 text_system: window.text_system().clone(),
3792 editor_style: self.style.clone().unwrap(),
3793 rem_size: window.rem_size(),
3794 scroll_anchor: self.scroll_manager.anchor(),
3795 visible_rows: self.visible_line_count(),
3796 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3797 }
3798 }
3799
3800 pub fn splice_inlays(
3801 &self,
3802 to_remove: &[InlayId],
3803 to_insert: Vec<Inlay>,
3804 cx: &mut Context<Self>,
3805 ) {
3806 self.display_map.update(cx, |display_map, cx| {
3807 display_map.splice_inlays(to_remove, to_insert, cx)
3808 });
3809 cx.notify();
3810 }
3811
3812 fn trigger_on_type_formatting(
3813 &self,
3814 input: String,
3815 window: &mut Window,
3816 cx: &mut Context<Self>,
3817 ) -> Option<Task<Result<()>>> {
3818 if input.len() != 1 {
3819 return None;
3820 }
3821
3822 let project = self.project.as_ref()?;
3823 let position = self.selections.newest_anchor().head();
3824 let (buffer, buffer_position) = self
3825 .buffer
3826 .read(cx)
3827 .text_anchor_for_position(position, cx)?;
3828
3829 let settings = language_settings::language_settings(
3830 buffer
3831 .read(cx)
3832 .language_at(buffer_position)
3833 .map(|l| l.name()),
3834 buffer.read(cx).file(),
3835 cx,
3836 );
3837 if !settings.use_on_type_format {
3838 return None;
3839 }
3840
3841 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3842 // hence we do LSP request & edit on host side only — add formats to host's history.
3843 let push_to_lsp_host_history = true;
3844 // If this is not the host, append its history with new edits.
3845 let push_to_client_history = project.read(cx).is_via_collab();
3846
3847 let on_type_formatting = project.update(cx, |project, cx| {
3848 project.on_type_format(
3849 buffer.clone(),
3850 buffer_position,
3851 input,
3852 push_to_lsp_host_history,
3853 cx,
3854 )
3855 });
3856 Some(cx.spawn_in(window, |editor, mut cx| async move {
3857 if let Some(transaction) = on_type_formatting.await? {
3858 if push_to_client_history {
3859 buffer
3860 .update(&mut cx, |buffer, _| {
3861 buffer.push_transaction(transaction, Instant::now());
3862 })
3863 .ok();
3864 }
3865 editor.update(&mut cx, |editor, cx| {
3866 editor.refresh_document_highlights(cx);
3867 })?;
3868 }
3869 Ok(())
3870 }))
3871 }
3872
3873 pub fn show_completions(
3874 &mut self,
3875 options: &ShowCompletions,
3876 window: &mut Window,
3877 cx: &mut Context<Self>,
3878 ) {
3879 if self.pending_rename.is_some() {
3880 return;
3881 }
3882
3883 let Some(provider) = self.completion_provider.as_ref() else {
3884 return;
3885 };
3886
3887 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3888 return;
3889 }
3890
3891 let position = self.selections.newest_anchor().head();
3892 if position.diff_base_anchor.is_some() {
3893 return;
3894 }
3895 let (buffer, buffer_position) =
3896 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3897 output
3898 } else {
3899 return;
3900 };
3901 let show_completion_documentation = buffer
3902 .read(cx)
3903 .snapshot()
3904 .settings_at(buffer_position, cx)
3905 .show_completion_documentation;
3906
3907 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3908
3909 let trigger_kind = match &options.trigger {
3910 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3911 CompletionTriggerKind::TRIGGER_CHARACTER
3912 }
3913 _ => CompletionTriggerKind::INVOKED,
3914 };
3915 let completion_context = CompletionContext {
3916 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3917 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3918 Some(String::from(trigger))
3919 } else {
3920 None
3921 }
3922 }),
3923 trigger_kind,
3924 };
3925 let completions =
3926 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3927 let sort_completions = provider.sort_completions();
3928
3929 let id = post_inc(&mut self.next_completion_id);
3930 let task = cx.spawn_in(window, |editor, mut cx| {
3931 async move {
3932 editor.update(&mut cx, |this, _| {
3933 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3934 })?;
3935 let completions = completions.await.log_err();
3936 let menu = if let Some(completions) = completions {
3937 let mut menu = CompletionsMenu::new(
3938 id,
3939 sort_completions,
3940 show_completion_documentation,
3941 position,
3942 buffer.clone(),
3943 completions.into(),
3944 );
3945
3946 menu.filter(query.as_deref(), cx.background_executor().clone())
3947 .await;
3948
3949 menu.visible().then_some(menu)
3950 } else {
3951 None
3952 };
3953
3954 editor.update_in(&mut cx, |editor, window, cx| {
3955 match editor.context_menu.borrow().as_ref() {
3956 None => {}
3957 Some(CodeContextMenu::Completions(prev_menu)) => {
3958 if prev_menu.id > id {
3959 return;
3960 }
3961 }
3962 _ => return,
3963 }
3964
3965 if editor.focus_handle.is_focused(window) && menu.is_some() {
3966 let mut menu = menu.unwrap();
3967 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3968
3969 *editor.context_menu.borrow_mut() =
3970 Some(CodeContextMenu::Completions(menu));
3971
3972 if editor.show_edit_predictions_in_menu() {
3973 editor.update_visible_inline_completion(window, cx);
3974 } else {
3975 editor.discard_inline_completion(false, cx);
3976 }
3977
3978 cx.notify();
3979 } else if editor.completion_tasks.len() <= 1 {
3980 // If there are no more completion tasks and the last menu was
3981 // empty, we should hide it.
3982 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3983 // If it was already hidden and we don't show inline
3984 // completions in the menu, we should also show the
3985 // inline-completion when available.
3986 if was_hidden && editor.show_edit_predictions_in_menu() {
3987 editor.update_visible_inline_completion(window, cx);
3988 }
3989 }
3990 })?;
3991
3992 Ok::<_, anyhow::Error>(())
3993 }
3994 .log_err()
3995 });
3996
3997 self.completion_tasks.push((id, task));
3998 }
3999
4000 pub fn confirm_completion(
4001 &mut self,
4002 action: &ConfirmCompletion,
4003 window: &mut Window,
4004 cx: &mut Context<Self>,
4005 ) -> Option<Task<Result<()>>> {
4006 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4007 }
4008
4009 pub fn compose_completion(
4010 &mut self,
4011 action: &ComposeCompletion,
4012 window: &mut Window,
4013 cx: &mut Context<Self>,
4014 ) -> Option<Task<Result<()>>> {
4015 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4016 }
4017
4018 fn do_completion(
4019 &mut self,
4020 item_ix: Option<usize>,
4021 intent: CompletionIntent,
4022 window: &mut Window,
4023 cx: &mut Context<Editor>,
4024 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4025 use language::ToOffset as _;
4026
4027 let completions_menu =
4028 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4029 menu
4030 } else {
4031 return None;
4032 };
4033
4034 let entries = completions_menu.entries.borrow();
4035 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4036 if self.show_edit_predictions_in_menu() {
4037 self.discard_inline_completion(true, cx);
4038 }
4039 let candidate_id = mat.candidate_id;
4040 drop(entries);
4041
4042 let buffer_handle = completions_menu.buffer;
4043 let completion = completions_menu
4044 .completions
4045 .borrow()
4046 .get(candidate_id)?
4047 .clone();
4048 cx.stop_propagation();
4049
4050 let snippet;
4051 let text;
4052
4053 if completion.is_snippet() {
4054 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4055 text = snippet.as_ref().unwrap().text.clone();
4056 } else {
4057 snippet = None;
4058 text = completion.new_text.clone();
4059 };
4060 let selections = self.selections.all::<usize>(cx);
4061 let buffer = buffer_handle.read(cx);
4062 let old_range = completion.old_range.to_offset(buffer);
4063 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4064
4065 let newest_selection = self.selections.newest_anchor();
4066 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4067 return None;
4068 }
4069
4070 let lookbehind = newest_selection
4071 .start
4072 .text_anchor
4073 .to_offset(buffer)
4074 .saturating_sub(old_range.start);
4075 let lookahead = old_range
4076 .end
4077 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4078 let mut common_prefix_len = old_text
4079 .bytes()
4080 .zip(text.bytes())
4081 .take_while(|(a, b)| a == b)
4082 .count();
4083
4084 let snapshot = self.buffer.read(cx).snapshot(cx);
4085 let mut range_to_replace: Option<Range<isize>> = None;
4086 let mut ranges = Vec::new();
4087 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4088 for selection in &selections {
4089 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4090 let start = selection.start.saturating_sub(lookbehind);
4091 let end = selection.end + lookahead;
4092 if selection.id == newest_selection.id {
4093 range_to_replace = Some(
4094 ((start + common_prefix_len) as isize - selection.start as isize)
4095 ..(end as isize - selection.start as isize),
4096 );
4097 }
4098 ranges.push(start + common_prefix_len..end);
4099 } else {
4100 common_prefix_len = 0;
4101 ranges.clear();
4102 ranges.extend(selections.iter().map(|s| {
4103 if s.id == newest_selection.id {
4104 range_to_replace = Some(
4105 old_range.start.to_offset_utf16(&snapshot).0 as isize
4106 - selection.start as isize
4107 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4108 - selection.start as isize,
4109 );
4110 old_range.clone()
4111 } else {
4112 s.start..s.end
4113 }
4114 }));
4115 break;
4116 }
4117 if !self.linked_edit_ranges.is_empty() {
4118 let start_anchor = snapshot.anchor_before(selection.head());
4119 let end_anchor = snapshot.anchor_after(selection.tail());
4120 if let Some(ranges) = self
4121 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4122 {
4123 for (buffer, edits) in ranges {
4124 linked_edits.entry(buffer.clone()).or_default().extend(
4125 edits
4126 .into_iter()
4127 .map(|range| (range, text[common_prefix_len..].to_owned())),
4128 );
4129 }
4130 }
4131 }
4132 }
4133 let text = &text[common_prefix_len..];
4134
4135 cx.emit(EditorEvent::InputHandled {
4136 utf16_range_to_replace: range_to_replace,
4137 text: text.into(),
4138 });
4139
4140 self.transact(window, cx, |this, window, cx| {
4141 if let Some(mut snippet) = snippet {
4142 snippet.text = text.to_string();
4143 for tabstop in snippet
4144 .tabstops
4145 .iter_mut()
4146 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4147 {
4148 tabstop.start -= common_prefix_len as isize;
4149 tabstop.end -= common_prefix_len as isize;
4150 }
4151
4152 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4153 } else {
4154 this.buffer.update(cx, |buffer, cx| {
4155 buffer.edit(
4156 ranges.iter().map(|range| (range.clone(), text)),
4157 this.autoindent_mode.clone(),
4158 cx,
4159 );
4160 });
4161 }
4162 for (buffer, edits) in linked_edits {
4163 buffer.update(cx, |buffer, cx| {
4164 let snapshot = buffer.snapshot();
4165 let edits = edits
4166 .into_iter()
4167 .map(|(range, text)| {
4168 use text::ToPoint as TP;
4169 let end_point = TP::to_point(&range.end, &snapshot);
4170 let start_point = TP::to_point(&range.start, &snapshot);
4171 (start_point..end_point, text)
4172 })
4173 .sorted_by_key(|(range, _)| range.start)
4174 .collect::<Vec<_>>();
4175 buffer.edit(edits, None, cx);
4176 })
4177 }
4178
4179 this.refresh_inline_completion(true, false, window, cx);
4180 });
4181
4182 let show_new_completions_on_confirm = completion
4183 .confirm
4184 .as_ref()
4185 .map_or(false, |confirm| confirm(intent, window, cx));
4186 if show_new_completions_on_confirm {
4187 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4188 }
4189
4190 let provider = self.completion_provider.as_ref()?;
4191 drop(completion);
4192 let apply_edits = provider.apply_additional_edits_for_completion(
4193 buffer_handle,
4194 completions_menu.completions.clone(),
4195 candidate_id,
4196 true,
4197 cx,
4198 );
4199
4200 let editor_settings = EditorSettings::get_global(cx);
4201 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4202 // After the code completion is finished, users often want to know what signatures are needed.
4203 // so we should automatically call signature_help
4204 self.show_signature_help(&ShowSignatureHelp, window, cx);
4205 }
4206
4207 Some(cx.foreground_executor().spawn(async move {
4208 apply_edits.await?;
4209 Ok(())
4210 }))
4211 }
4212
4213 pub fn toggle_code_actions(
4214 &mut self,
4215 action: &ToggleCodeActions,
4216 window: &mut Window,
4217 cx: &mut Context<Self>,
4218 ) {
4219 let mut context_menu = self.context_menu.borrow_mut();
4220 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4221 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4222 // Toggle if we're selecting the same one
4223 *context_menu = None;
4224 cx.notify();
4225 return;
4226 } else {
4227 // Otherwise, clear it and start a new one
4228 *context_menu = None;
4229 cx.notify();
4230 }
4231 }
4232 drop(context_menu);
4233 let snapshot = self.snapshot(window, cx);
4234 let deployed_from_indicator = action.deployed_from_indicator;
4235 let mut task = self.code_actions_task.take();
4236 let action = action.clone();
4237 cx.spawn_in(window, |editor, mut cx| async move {
4238 while let Some(prev_task) = task {
4239 prev_task.await.log_err();
4240 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4241 }
4242
4243 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4244 if editor.focus_handle.is_focused(window) {
4245 let multibuffer_point = action
4246 .deployed_from_indicator
4247 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4248 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4249 let (buffer, buffer_row) = snapshot
4250 .buffer_snapshot
4251 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4252 .and_then(|(buffer_snapshot, range)| {
4253 editor
4254 .buffer
4255 .read(cx)
4256 .buffer(buffer_snapshot.remote_id())
4257 .map(|buffer| (buffer, range.start.row))
4258 })?;
4259 let (_, code_actions) = editor
4260 .available_code_actions
4261 .clone()
4262 .and_then(|(location, code_actions)| {
4263 let snapshot = location.buffer.read(cx).snapshot();
4264 let point_range = location.range.to_point(&snapshot);
4265 let point_range = point_range.start.row..=point_range.end.row;
4266 if point_range.contains(&buffer_row) {
4267 Some((location, code_actions))
4268 } else {
4269 None
4270 }
4271 })
4272 .unzip();
4273 let buffer_id = buffer.read(cx).remote_id();
4274 let tasks = editor
4275 .tasks
4276 .get(&(buffer_id, buffer_row))
4277 .map(|t| Arc::new(t.to_owned()));
4278 if tasks.is_none() && code_actions.is_none() {
4279 return None;
4280 }
4281
4282 editor.completion_tasks.clear();
4283 editor.discard_inline_completion(false, cx);
4284 let task_context =
4285 tasks
4286 .as_ref()
4287 .zip(editor.project.clone())
4288 .map(|(tasks, project)| {
4289 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4290 });
4291
4292 Some(cx.spawn_in(window, |editor, mut cx| async move {
4293 let task_context = match task_context {
4294 Some(task_context) => task_context.await,
4295 None => None,
4296 };
4297 let resolved_tasks =
4298 tasks.zip(task_context).map(|(tasks, task_context)| {
4299 Rc::new(ResolvedTasks {
4300 templates: tasks.resolve(&task_context).collect(),
4301 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4302 multibuffer_point.row,
4303 tasks.column,
4304 )),
4305 })
4306 });
4307 let spawn_straight_away = resolved_tasks
4308 .as_ref()
4309 .map_or(false, |tasks| tasks.templates.len() == 1)
4310 && code_actions
4311 .as_ref()
4312 .map_or(true, |actions| actions.is_empty());
4313 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4314 *editor.context_menu.borrow_mut() =
4315 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4316 buffer,
4317 actions: CodeActionContents {
4318 tasks: resolved_tasks,
4319 actions: code_actions,
4320 },
4321 selected_item: Default::default(),
4322 scroll_handle: UniformListScrollHandle::default(),
4323 deployed_from_indicator,
4324 }));
4325 if spawn_straight_away {
4326 if let Some(task) = editor.confirm_code_action(
4327 &ConfirmCodeAction { item_ix: Some(0) },
4328 window,
4329 cx,
4330 ) {
4331 cx.notify();
4332 return task;
4333 }
4334 }
4335 cx.notify();
4336 Task::ready(Ok(()))
4337 }) {
4338 task.await
4339 } else {
4340 Ok(())
4341 }
4342 }))
4343 } else {
4344 Some(Task::ready(Ok(())))
4345 }
4346 })?;
4347 if let Some(task) = spawned_test_task {
4348 task.await?;
4349 }
4350
4351 Ok::<_, anyhow::Error>(())
4352 })
4353 .detach_and_log_err(cx);
4354 }
4355
4356 pub fn confirm_code_action(
4357 &mut self,
4358 action: &ConfirmCodeAction,
4359 window: &mut Window,
4360 cx: &mut Context<Self>,
4361 ) -> Option<Task<Result<()>>> {
4362 let actions_menu =
4363 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4364 menu
4365 } else {
4366 return None;
4367 };
4368 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4369 let action = actions_menu.actions.get(action_ix)?;
4370 let title = action.label();
4371 let buffer = actions_menu.buffer;
4372 let workspace = self.workspace()?;
4373
4374 match action {
4375 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4376 workspace.update(cx, |workspace, cx| {
4377 workspace::tasks::schedule_resolved_task(
4378 workspace,
4379 task_source_kind,
4380 resolved_task,
4381 false,
4382 cx,
4383 );
4384
4385 Some(Task::ready(Ok(())))
4386 })
4387 }
4388 CodeActionsItem::CodeAction {
4389 excerpt_id,
4390 action,
4391 provider,
4392 } => {
4393 let apply_code_action =
4394 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4395 let workspace = workspace.downgrade();
4396 Some(cx.spawn_in(window, |editor, cx| async move {
4397 let project_transaction = apply_code_action.await?;
4398 Self::open_project_transaction(
4399 &editor,
4400 workspace,
4401 project_transaction,
4402 title,
4403 cx,
4404 )
4405 .await
4406 }))
4407 }
4408 }
4409 }
4410
4411 pub async fn open_project_transaction(
4412 this: &WeakEntity<Editor>,
4413 workspace: WeakEntity<Workspace>,
4414 transaction: ProjectTransaction,
4415 title: String,
4416 mut cx: AsyncWindowContext,
4417 ) -> Result<()> {
4418 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4419 cx.update(|_, cx| {
4420 entries.sort_unstable_by_key(|(buffer, _)| {
4421 buffer.read(cx).file().map(|f| f.path().clone())
4422 });
4423 })?;
4424
4425 // If the project transaction's edits are all contained within this editor, then
4426 // avoid opening a new editor to display them.
4427
4428 if let Some((buffer, transaction)) = entries.first() {
4429 if entries.len() == 1 {
4430 let excerpt = this.update(&mut cx, |editor, cx| {
4431 editor
4432 .buffer()
4433 .read(cx)
4434 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4435 })?;
4436 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4437 if excerpted_buffer == *buffer {
4438 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4439 let excerpt_range = excerpt_range.to_offset(buffer);
4440 buffer
4441 .edited_ranges_for_transaction::<usize>(transaction)
4442 .all(|range| {
4443 excerpt_range.start <= range.start
4444 && excerpt_range.end >= range.end
4445 })
4446 })?;
4447
4448 if all_edits_within_excerpt {
4449 return Ok(());
4450 }
4451 }
4452 }
4453 }
4454 } else {
4455 return Ok(());
4456 }
4457
4458 let mut ranges_to_highlight = Vec::new();
4459 let excerpt_buffer = cx.new(|cx| {
4460 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4461 for (buffer_handle, transaction) in &entries {
4462 let buffer = buffer_handle.read(cx);
4463 ranges_to_highlight.extend(
4464 multibuffer.push_excerpts_with_context_lines(
4465 buffer_handle.clone(),
4466 buffer
4467 .edited_ranges_for_transaction::<usize>(transaction)
4468 .collect(),
4469 DEFAULT_MULTIBUFFER_CONTEXT,
4470 cx,
4471 ),
4472 );
4473 }
4474 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4475 multibuffer
4476 })?;
4477
4478 workspace.update_in(&mut cx, |workspace, window, cx| {
4479 let project = workspace.project().clone();
4480 let editor = cx
4481 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4482 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4483 editor.update(cx, |editor, cx| {
4484 editor.highlight_background::<Self>(
4485 &ranges_to_highlight,
4486 |theme| theme.editor_highlighted_line_background,
4487 cx,
4488 );
4489 });
4490 })?;
4491
4492 Ok(())
4493 }
4494
4495 pub fn clear_code_action_providers(&mut self) {
4496 self.code_action_providers.clear();
4497 self.available_code_actions.take();
4498 }
4499
4500 pub fn add_code_action_provider(
4501 &mut self,
4502 provider: Rc<dyn CodeActionProvider>,
4503 window: &mut Window,
4504 cx: &mut Context<Self>,
4505 ) {
4506 if self
4507 .code_action_providers
4508 .iter()
4509 .any(|existing_provider| existing_provider.id() == provider.id())
4510 {
4511 return;
4512 }
4513
4514 self.code_action_providers.push(provider);
4515 self.refresh_code_actions(window, cx);
4516 }
4517
4518 pub fn remove_code_action_provider(
4519 &mut self,
4520 id: Arc<str>,
4521 window: &mut Window,
4522 cx: &mut Context<Self>,
4523 ) {
4524 self.code_action_providers
4525 .retain(|provider| provider.id() != id);
4526 self.refresh_code_actions(window, cx);
4527 }
4528
4529 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4530 let buffer = self.buffer.read(cx);
4531 let newest_selection = self.selections.newest_anchor().clone();
4532 if newest_selection.head().diff_base_anchor.is_some() {
4533 return None;
4534 }
4535 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4536 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4537 if start_buffer != end_buffer {
4538 return None;
4539 }
4540
4541 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4542 cx.background_executor()
4543 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4544 .await;
4545
4546 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4547 let providers = this.code_action_providers.clone();
4548 let tasks = this
4549 .code_action_providers
4550 .iter()
4551 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4552 .collect::<Vec<_>>();
4553 (providers, tasks)
4554 })?;
4555
4556 let mut actions = Vec::new();
4557 for (provider, provider_actions) in
4558 providers.into_iter().zip(future::join_all(tasks).await)
4559 {
4560 if let Some(provider_actions) = provider_actions.log_err() {
4561 actions.extend(provider_actions.into_iter().map(|action| {
4562 AvailableCodeAction {
4563 excerpt_id: newest_selection.start.excerpt_id,
4564 action,
4565 provider: provider.clone(),
4566 }
4567 }));
4568 }
4569 }
4570
4571 this.update(&mut cx, |this, cx| {
4572 this.available_code_actions = if actions.is_empty() {
4573 None
4574 } else {
4575 Some((
4576 Location {
4577 buffer: start_buffer,
4578 range: start..end,
4579 },
4580 actions.into(),
4581 ))
4582 };
4583 cx.notify();
4584 })
4585 }));
4586 None
4587 }
4588
4589 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4590 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4591 self.show_git_blame_inline = false;
4592
4593 self.show_git_blame_inline_delay_task =
4594 Some(cx.spawn_in(window, |this, mut cx| async move {
4595 cx.background_executor().timer(delay).await;
4596
4597 this.update(&mut cx, |this, cx| {
4598 this.show_git_blame_inline = true;
4599 cx.notify();
4600 })
4601 .log_err();
4602 }));
4603 }
4604 }
4605
4606 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4607 if self.pending_rename.is_some() {
4608 return None;
4609 }
4610
4611 let provider = self.semantics_provider.clone()?;
4612 let buffer = self.buffer.read(cx);
4613 let newest_selection = self.selections.newest_anchor().clone();
4614 let cursor_position = newest_selection.head();
4615 let (cursor_buffer, cursor_buffer_position) =
4616 buffer.text_anchor_for_position(cursor_position, cx)?;
4617 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4618 if cursor_buffer != tail_buffer {
4619 return None;
4620 }
4621 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4622 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4623 cx.background_executor()
4624 .timer(Duration::from_millis(debounce))
4625 .await;
4626
4627 let highlights = if let Some(highlights) = cx
4628 .update(|cx| {
4629 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4630 })
4631 .ok()
4632 .flatten()
4633 {
4634 highlights.await.log_err()
4635 } else {
4636 None
4637 };
4638
4639 if let Some(highlights) = highlights {
4640 this.update(&mut cx, |this, cx| {
4641 if this.pending_rename.is_some() {
4642 return;
4643 }
4644
4645 let buffer_id = cursor_position.buffer_id;
4646 let buffer = this.buffer.read(cx);
4647 if !buffer
4648 .text_anchor_for_position(cursor_position, cx)
4649 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4650 {
4651 return;
4652 }
4653
4654 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4655 let mut write_ranges = Vec::new();
4656 let mut read_ranges = Vec::new();
4657 for highlight in highlights {
4658 for (excerpt_id, excerpt_range) in
4659 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4660 {
4661 let start = highlight
4662 .range
4663 .start
4664 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4665 let end = highlight
4666 .range
4667 .end
4668 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4669 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4670 continue;
4671 }
4672
4673 let range = Anchor {
4674 buffer_id,
4675 excerpt_id,
4676 text_anchor: start,
4677 diff_base_anchor: None,
4678 }..Anchor {
4679 buffer_id,
4680 excerpt_id,
4681 text_anchor: end,
4682 diff_base_anchor: None,
4683 };
4684 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4685 write_ranges.push(range);
4686 } else {
4687 read_ranges.push(range);
4688 }
4689 }
4690 }
4691
4692 this.highlight_background::<DocumentHighlightRead>(
4693 &read_ranges,
4694 |theme| theme.editor_document_highlight_read_background,
4695 cx,
4696 );
4697 this.highlight_background::<DocumentHighlightWrite>(
4698 &write_ranges,
4699 |theme| theme.editor_document_highlight_write_background,
4700 cx,
4701 );
4702 cx.notify();
4703 })
4704 .log_err();
4705 }
4706 }));
4707 None
4708 }
4709
4710 pub fn refresh_selected_text_highlights(
4711 &mut self,
4712 window: &mut Window,
4713 cx: &mut Context<Editor>,
4714 ) {
4715 self.selection_highlight_task.take();
4716 if !EditorSettings::get_global(cx).selection_highlight {
4717 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4718 return;
4719 }
4720 if self.selections.count() != 1 || self.selections.line_mode {
4721 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4722 return;
4723 }
4724 let selection = self.selections.newest::<Point>(cx);
4725 if selection.is_empty() || selection.start.row != selection.end.row {
4726 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4727 return;
4728 }
4729 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4730 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4731 cx.background_executor()
4732 .timer(Duration::from_millis(debounce))
4733 .await;
4734 let Some(Some(matches_task)) = editor
4735 .update_in(&mut cx, |editor, _, cx| {
4736 if editor.selections.count() != 1 || editor.selections.line_mode {
4737 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4738 return None;
4739 }
4740 let selection = editor.selections.newest::<Point>(cx);
4741 if selection.is_empty() || selection.start.row != selection.end.row {
4742 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4743 return None;
4744 }
4745 let buffer = editor.buffer().read(cx).snapshot(cx);
4746 let query = buffer.text_for_range(selection.range()).collect::<String>();
4747 if query.trim().is_empty() {
4748 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4749 return None;
4750 }
4751 Some(cx.background_spawn(async move {
4752 let mut ranges = Vec::new();
4753 let selection_anchors = selection.range().to_anchors(&buffer);
4754 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4755 for (search_buffer, search_range, excerpt_id) in
4756 buffer.range_to_buffer_ranges(range)
4757 {
4758 ranges.extend(
4759 project::search::SearchQuery::text(
4760 query.clone(),
4761 false,
4762 false,
4763 false,
4764 Default::default(),
4765 Default::default(),
4766 None,
4767 )
4768 .unwrap()
4769 .search(search_buffer, Some(search_range.clone()))
4770 .await
4771 .into_iter()
4772 .filter_map(
4773 |match_range| {
4774 let start = search_buffer.anchor_after(
4775 search_range.start + match_range.start,
4776 );
4777 let end = search_buffer.anchor_before(
4778 search_range.start + match_range.end,
4779 );
4780 let range = Anchor::range_in_buffer(
4781 excerpt_id,
4782 search_buffer.remote_id(),
4783 start..end,
4784 );
4785 (range != selection_anchors).then_some(range)
4786 },
4787 ),
4788 );
4789 }
4790 }
4791 ranges
4792 }))
4793 })
4794 .log_err()
4795 else {
4796 return;
4797 };
4798 let matches = matches_task.await;
4799 editor
4800 .update_in(&mut cx, |editor, _, cx| {
4801 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4802 if !matches.is_empty() {
4803 editor.highlight_background::<SelectedTextHighlight>(
4804 &matches,
4805 |theme| theme.editor_document_highlight_bracket_background,
4806 cx,
4807 )
4808 }
4809 })
4810 .log_err();
4811 }));
4812 }
4813
4814 pub fn refresh_inline_completion(
4815 &mut self,
4816 debounce: bool,
4817 user_requested: bool,
4818 window: &mut Window,
4819 cx: &mut Context<Self>,
4820 ) -> Option<()> {
4821 let provider = self.edit_prediction_provider()?;
4822 let cursor = self.selections.newest_anchor().head();
4823 let (buffer, cursor_buffer_position) =
4824 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4825
4826 if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4827 self.discard_inline_completion(false, cx);
4828 return None;
4829 }
4830
4831 if !user_requested
4832 && (!self.should_show_edit_predictions()
4833 || !self.is_focused(window)
4834 || buffer.read(cx).is_empty())
4835 {
4836 self.discard_inline_completion(false, cx);
4837 return None;
4838 }
4839
4840 self.update_visible_inline_completion(window, cx);
4841 provider.refresh(
4842 self.project.clone(),
4843 buffer,
4844 cursor_buffer_position,
4845 debounce,
4846 cx,
4847 );
4848 Some(())
4849 }
4850
4851 fn show_edit_predictions_in_menu(&self) -> bool {
4852 match self.edit_prediction_settings {
4853 EditPredictionSettings::Disabled => false,
4854 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4855 }
4856 }
4857
4858 pub fn edit_predictions_enabled(&self) -> bool {
4859 match self.edit_prediction_settings {
4860 EditPredictionSettings::Disabled => false,
4861 EditPredictionSettings::Enabled { .. } => true,
4862 }
4863 }
4864
4865 fn edit_prediction_requires_modifier(&self) -> bool {
4866 match self.edit_prediction_settings {
4867 EditPredictionSettings::Disabled => false,
4868 EditPredictionSettings::Enabled {
4869 preview_requires_modifier,
4870 ..
4871 } => preview_requires_modifier,
4872 }
4873 }
4874
4875 fn edit_prediction_settings_at_position(
4876 &self,
4877 buffer: &Entity<Buffer>,
4878 buffer_position: language::Anchor,
4879 cx: &App,
4880 ) -> EditPredictionSettings {
4881 if self.mode != EditorMode::Full
4882 || !self.show_inline_completions_override.unwrap_or(true)
4883 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
4884 {
4885 return EditPredictionSettings::Disabled;
4886 }
4887
4888 let buffer = buffer.read(cx);
4889
4890 let file = buffer.file();
4891
4892 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
4893 return EditPredictionSettings::Disabled;
4894 };
4895
4896 let by_provider = matches!(
4897 self.menu_inline_completions_policy,
4898 MenuInlineCompletionsPolicy::ByProvider
4899 );
4900
4901 let show_in_menu = by_provider
4902 && self
4903 .edit_prediction_provider
4904 .as_ref()
4905 .map_or(false, |provider| {
4906 provider.provider.show_completions_in_menu()
4907 });
4908
4909 let preview_requires_modifier =
4910 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Auto;
4911
4912 EditPredictionSettings::Enabled {
4913 show_in_menu,
4914 preview_requires_modifier,
4915 }
4916 }
4917
4918 fn should_show_edit_predictions(&self) -> bool {
4919 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
4920 }
4921
4922 pub fn edit_prediction_preview_is_active(&self) -> bool {
4923 matches!(
4924 self.edit_prediction_preview,
4925 EditPredictionPreview::Active { .. }
4926 )
4927 }
4928
4929 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
4930 let cursor = self.selections.newest_anchor().head();
4931 if let Some((buffer, cursor_position)) =
4932 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4933 {
4934 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
4935 } else {
4936 false
4937 }
4938 }
4939
4940 fn inline_completions_enabled_in_buffer(
4941 &self,
4942 buffer: &Entity<Buffer>,
4943 buffer_position: language::Anchor,
4944 cx: &App,
4945 ) -> bool {
4946 maybe!({
4947 let provider = self.edit_prediction_provider()?;
4948 if !provider.is_enabled(&buffer, buffer_position, cx) {
4949 return Some(false);
4950 }
4951 let buffer = buffer.read(cx);
4952 let Some(file) = buffer.file() else {
4953 return Some(true);
4954 };
4955 let settings = all_language_settings(Some(file), cx);
4956 Some(settings.inline_completions_enabled_for_path(file.path()))
4957 })
4958 .unwrap_or(false)
4959 }
4960
4961 fn cycle_inline_completion(
4962 &mut self,
4963 direction: Direction,
4964 window: &mut Window,
4965 cx: &mut Context<Self>,
4966 ) -> Option<()> {
4967 let provider = self.edit_prediction_provider()?;
4968 let cursor = self.selections.newest_anchor().head();
4969 let (buffer, cursor_buffer_position) =
4970 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4971 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
4972 return None;
4973 }
4974
4975 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4976 self.update_visible_inline_completion(window, cx);
4977
4978 Some(())
4979 }
4980
4981 pub fn show_inline_completion(
4982 &mut self,
4983 _: &ShowEditPrediction,
4984 window: &mut Window,
4985 cx: &mut Context<Self>,
4986 ) {
4987 if !self.has_active_inline_completion() {
4988 self.refresh_inline_completion(false, true, window, cx);
4989 return;
4990 }
4991
4992 self.update_visible_inline_completion(window, cx);
4993 }
4994
4995 pub fn display_cursor_names(
4996 &mut self,
4997 _: &DisplayCursorNames,
4998 window: &mut Window,
4999 cx: &mut Context<Self>,
5000 ) {
5001 self.show_cursor_names(window, cx);
5002 }
5003
5004 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5005 self.show_cursor_names = true;
5006 cx.notify();
5007 cx.spawn_in(window, |this, mut cx| async move {
5008 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5009 this.update(&mut cx, |this, cx| {
5010 this.show_cursor_names = false;
5011 cx.notify()
5012 })
5013 .ok()
5014 })
5015 .detach();
5016 }
5017
5018 pub fn next_edit_prediction(
5019 &mut self,
5020 _: &NextEditPrediction,
5021 window: &mut Window,
5022 cx: &mut Context<Self>,
5023 ) {
5024 if self.has_active_inline_completion() {
5025 self.cycle_inline_completion(Direction::Next, window, cx);
5026 } else {
5027 let is_copilot_disabled = self
5028 .refresh_inline_completion(false, true, window, cx)
5029 .is_none();
5030 if is_copilot_disabled {
5031 cx.propagate();
5032 }
5033 }
5034 }
5035
5036 pub fn previous_edit_prediction(
5037 &mut self,
5038 _: &PreviousEditPrediction,
5039 window: &mut Window,
5040 cx: &mut Context<Self>,
5041 ) {
5042 if self.has_active_inline_completion() {
5043 self.cycle_inline_completion(Direction::Prev, window, cx);
5044 } else {
5045 let is_copilot_disabled = self
5046 .refresh_inline_completion(false, true, window, cx)
5047 .is_none();
5048 if is_copilot_disabled {
5049 cx.propagate();
5050 }
5051 }
5052 }
5053
5054 pub fn accept_edit_prediction(
5055 &mut self,
5056 _: &AcceptEditPrediction,
5057 window: &mut Window,
5058 cx: &mut Context<Self>,
5059 ) {
5060 if self.show_edit_predictions_in_menu() {
5061 self.hide_context_menu(window, cx);
5062 }
5063
5064 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5065 return;
5066 };
5067
5068 self.report_inline_completion_event(
5069 active_inline_completion.completion_id.clone(),
5070 true,
5071 cx,
5072 );
5073
5074 match &active_inline_completion.completion {
5075 InlineCompletion::Move { target, .. } => {
5076 let target = *target;
5077
5078 if let Some(position_map) = &self.last_position_map {
5079 if position_map
5080 .visible_row_range
5081 .contains(&target.to_display_point(&position_map.snapshot).row())
5082 || !self.edit_prediction_requires_modifier()
5083 {
5084 self.unfold_ranges(&[target..target], true, false, cx);
5085 // Note that this is also done in vim's handler of the Tab action.
5086 self.change_selections(
5087 Some(Autoscroll::newest()),
5088 window,
5089 cx,
5090 |selections| {
5091 selections.select_anchor_ranges([target..target]);
5092 },
5093 );
5094 self.clear_row_highlights::<EditPredictionPreview>();
5095
5096 self.edit_prediction_preview = EditPredictionPreview::Active {
5097 previous_scroll_position: None,
5098 };
5099 } else {
5100 self.edit_prediction_preview = EditPredictionPreview::Active {
5101 previous_scroll_position: Some(position_map.snapshot.scroll_anchor),
5102 };
5103 self.highlight_rows::<EditPredictionPreview>(
5104 target..target,
5105 cx.theme().colors().editor_highlighted_line_background,
5106 true,
5107 cx,
5108 );
5109 self.request_autoscroll(Autoscroll::fit(), cx);
5110 }
5111 }
5112 }
5113 InlineCompletion::Edit { edits, .. } => {
5114 if let Some(provider) = self.edit_prediction_provider() {
5115 provider.accept(cx);
5116 }
5117
5118 let snapshot = self.buffer.read(cx).snapshot(cx);
5119 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5120
5121 self.buffer.update(cx, |buffer, cx| {
5122 buffer.edit(edits.iter().cloned(), None, cx)
5123 });
5124
5125 self.change_selections(None, window, cx, |s| {
5126 s.select_anchor_ranges([last_edit_end..last_edit_end])
5127 });
5128
5129 self.update_visible_inline_completion(window, cx);
5130 if self.active_inline_completion.is_none() {
5131 self.refresh_inline_completion(true, true, window, cx);
5132 }
5133
5134 cx.notify();
5135 }
5136 }
5137
5138 self.edit_prediction_requires_modifier_in_leading_space = false;
5139 }
5140
5141 pub fn accept_partial_inline_completion(
5142 &mut self,
5143 _: &AcceptPartialEditPrediction,
5144 window: &mut Window,
5145 cx: &mut Context<Self>,
5146 ) {
5147 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5148 return;
5149 };
5150 if self.selections.count() != 1 {
5151 return;
5152 }
5153
5154 self.report_inline_completion_event(
5155 active_inline_completion.completion_id.clone(),
5156 true,
5157 cx,
5158 );
5159
5160 match &active_inline_completion.completion {
5161 InlineCompletion::Move { target, .. } => {
5162 let target = *target;
5163 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5164 selections.select_anchor_ranges([target..target]);
5165 });
5166 }
5167 InlineCompletion::Edit { edits, .. } => {
5168 // Find an insertion that starts at the cursor position.
5169 let snapshot = self.buffer.read(cx).snapshot(cx);
5170 let cursor_offset = self.selections.newest::<usize>(cx).head();
5171 let insertion = edits.iter().find_map(|(range, text)| {
5172 let range = range.to_offset(&snapshot);
5173 if range.is_empty() && range.start == cursor_offset {
5174 Some(text)
5175 } else {
5176 None
5177 }
5178 });
5179
5180 if let Some(text) = insertion {
5181 let mut partial_completion = text
5182 .chars()
5183 .by_ref()
5184 .take_while(|c| c.is_alphabetic())
5185 .collect::<String>();
5186 if partial_completion.is_empty() {
5187 partial_completion = text
5188 .chars()
5189 .by_ref()
5190 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5191 .collect::<String>();
5192 }
5193
5194 cx.emit(EditorEvent::InputHandled {
5195 utf16_range_to_replace: None,
5196 text: partial_completion.clone().into(),
5197 });
5198
5199 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5200
5201 self.refresh_inline_completion(true, true, window, cx);
5202 cx.notify();
5203 } else {
5204 self.accept_edit_prediction(&Default::default(), window, cx);
5205 }
5206 }
5207 }
5208 }
5209
5210 fn discard_inline_completion(
5211 &mut self,
5212 should_report_inline_completion_event: bool,
5213 cx: &mut Context<Self>,
5214 ) -> bool {
5215 if should_report_inline_completion_event {
5216 let completion_id = self
5217 .active_inline_completion
5218 .as_ref()
5219 .and_then(|active_completion| active_completion.completion_id.clone());
5220
5221 self.report_inline_completion_event(completion_id, false, cx);
5222 }
5223
5224 if let Some(provider) = self.edit_prediction_provider() {
5225 provider.discard(cx);
5226 }
5227
5228 self.take_active_inline_completion(cx)
5229 }
5230
5231 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5232 let Some(provider) = self.edit_prediction_provider() else {
5233 return;
5234 };
5235
5236 let Some((_, buffer, _)) = self
5237 .buffer
5238 .read(cx)
5239 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5240 else {
5241 return;
5242 };
5243
5244 let extension = buffer
5245 .read(cx)
5246 .file()
5247 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5248
5249 let event_type = match accepted {
5250 true => "Edit Prediction Accepted",
5251 false => "Edit Prediction Discarded",
5252 };
5253 telemetry::event!(
5254 event_type,
5255 provider = provider.name(),
5256 prediction_id = id,
5257 suggestion_accepted = accepted,
5258 file_extension = extension,
5259 );
5260 }
5261
5262 pub fn has_active_inline_completion(&self) -> bool {
5263 self.active_inline_completion.is_some()
5264 }
5265
5266 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5267 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5268 return false;
5269 };
5270
5271 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5272 self.clear_highlights::<InlineCompletionHighlight>(cx);
5273 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5274 true
5275 }
5276
5277 /// Returns true when we're displaying the edit prediction popover below the cursor
5278 /// like we are not previewing and the LSP autocomplete menu is visible
5279 /// or we are in `when_holding_modifier` mode.
5280 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5281 if self.edit_prediction_preview_is_active()
5282 || !self.show_edit_predictions_in_menu()
5283 || !self.edit_predictions_enabled()
5284 {
5285 return false;
5286 }
5287
5288 if self.has_visible_completions_menu() {
5289 return true;
5290 }
5291
5292 has_completion && self.edit_prediction_requires_modifier()
5293 }
5294
5295 fn handle_modifiers_changed(
5296 &mut self,
5297 modifiers: Modifiers,
5298 position_map: &PositionMap,
5299 window: &mut Window,
5300 cx: &mut Context<Self>,
5301 ) {
5302 if self.show_edit_predictions_in_menu() {
5303 self.update_edit_prediction_preview(&modifiers, window, cx);
5304 }
5305
5306 self.update_selection_mode(&modifiers, position_map, window, cx);
5307
5308 let mouse_position = window.mouse_position();
5309 if !position_map.text_hitbox.is_hovered(window) {
5310 return;
5311 }
5312
5313 self.update_hovered_link(
5314 position_map.point_for_position(mouse_position),
5315 &position_map.snapshot,
5316 modifiers,
5317 window,
5318 cx,
5319 )
5320 }
5321
5322 fn update_selection_mode(
5323 &mut self,
5324 modifiers: &Modifiers,
5325 position_map: &PositionMap,
5326 window: &mut Window,
5327 cx: &mut Context<Self>,
5328 ) {
5329 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5330 return;
5331 }
5332
5333 let mouse_position = window.mouse_position();
5334 let point_for_position = position_map.point_for_position(mouse_position);
5335 let position = point_for_position.previous_valid;
5336
5337 self.select(
5338 SelectPhase::BeginColumnar {
5339 position,
5340 reset: false,
5341 goal_column: point_for_position.exact_unclipped.column(),
5342 },
5343 window,
5344 cx,
5345 );
5346 }
5347
5348 fn update_edit_prediction_preview(
5349 &mut self,
5350 modifiers: &Modifiers,
5351 window: &mut Window,
5352 cx: &mut Context<Self>,
5353 ) {
5354 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5355 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5356 return;
5357 };
5358
5359 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5360 if matches!(
5361 self.edit_prediction_preview,
5362 EditPredictionPreview::Inactive
5363 ) {
5364 self.edit_prediction_preview = EditPredictionPreview::Active {
5365 previous_scroll_position: None,
5366 };
5367
5368 self.update_visible_inline_completion(window, cx);
5369 cx.notify();
5370 }
5371 } else if let EditPredictionPreview::Active {
5372 previous_scroll_position,
5373 } = self.edit_prediction_preview
5374 {
5375 if let (Some(previous_scroll_position), Some(position_map)) =
5376 (previous_scroll_position, self.last_position_map.as_ref())
5377 {
5378 self.set_scroll_position(
5379 previous_scroll_position
5380 .scroll_position(&position_map.snapshot.display_snapshot),
5381 window,
5382 cx,
5383 );
5384 }
5385
5386 self.edit_prediction_preview = EditPredictionPreview::Inactive;
5387 self.clear_row_highlights::<EditPredictionPreview>();
5388 self.update_visible_inline_completion(window, cx);
5389 cx.notify();
5390 }
5391 }
5392
5393 fn update_visible_inline_completion(
5394 &mut self,
5395 _window: &mut Window,
5396 cx: &mut Context<Self>,
5397 ) -> Option<()> {
5398 let selection = self.selections.newest_anchor();
5399 let cursor = selection.head();
5400 let multibuffer = self.buffer.read(cx).snapshot(cx);
5401 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5402 let excerpt_id = cursor.excerpt_id;
5403
5404 let show_in_menu = self.show_edit_predictions_in_menu();
5405 let completions_menu_has_precedence = !show_in_menu
5406 && (self.context_menu.borrow().is_some()
5407 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5408
5409 if completions_menu_has_precedence
5410 || !offset_selection.is_empty()
5411 || self
5412 .active_inline_completion
5413 .as_ref()
5414 .map_or(false, |completion| {
5415 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5416 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5417 !invalidation_range.contains(&offset_selection.head())
5418 })
5419 {
5420 self.discard_inline_completion(false, cx);
5421 return None;
5422 }
5423
5424 self.take_active_inline_completion(cx);
5425 let Some(provider) = self.edit_prediction_provider() else {
5426 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5427 return None;
5428 };
5429
5430 let (buffer, cursor_buffer_position) =
5431 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5432
5433 self.edit_prediction_settings =
5434 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5435
5436 self.edit_prediction_cursor_on_leading_whitespace =
5437 multibuffer.is_line_whitespace_upto(cursor);
5438
5439 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5440 let edits = inline_completion
5441 .edits
5442 .into_iter()
5443 .flat_map(|(range, new_text)| {
5444 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5445 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5446 Some((start..end, new_text))
5447 })
5448 .collect::<Vec<_>>();
5449 if edits.is_empty() {
5450 return None;
5451 }
5452
5453 let first_edit_start = edits.first().unwrap().0.start;
5454 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5455 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5456
5457 let last_edit_end = edits.last().unwrap().0.end;
5458 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5459 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5460
5461 let cursor_row = cursor.to_point(&multibuffer).row;
5462
5463 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5464
5465 let mut inlay_ids = Vec::new();
5466 let invalidation_row_range;
5467 let move_invalidation_row_range = if cursor_row < edit_start_row {
5468 Some(cursor_row..edit_end_row)
5469 } else if cursor_row > edit_end_row {
5470 Some(edit_start_row..cursor_row)
5471 } else {
5472 None
5473 };
5474 let is_move =
5475 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5476 let completion = if is_move {
5477 invalidation_row_range =
5478 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5479 let target = first_edit_start;
5480 InlineCompletion::Move { target, snapshot }
5481 } else {
5482 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5483 && !self.inline_completions_hidden_for_vim_mode;
5484
5485 if show_completions_in_buffer {
5486 if edits
5487 .iter()
5488 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5489 {
5490 let mut inlays = Vec::new();
5491 for (range, new_text) in &edits {
5492 let inlay = Inlay::inline_completion(
5493 post_inc(&mut self.next_inlay_id),
5494 range.start,
5495 new_text.as_str(),
5496 );
5497 inlay_ids.push(inlay.id);
5498 inlays.push(inlay);
5499 }
5500
5501 self.splice_inlays(&[], inlays, cx);
5502 } else {
5503 let background_color = cx.theme().status().deleted_background;
5504 self.highlight_text::<InlineCompletionHighlight>(
5505 edits.iter().map(|(range, _)| range.clone()).collect(),
5506 HighlightStyle {
5507 background_color: Some(background_color),
5508 ..Default::default()
5509 },
5510 cx,
5511 );
5512 }
5513 }
5514
5515 invalidation_row_range = edit_start_row..edit_end_row;
5516
5517 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5518 if provider.show_tab_accept_marker() {
5519 EditDisplayMode::TabAccept
5520 } else {
5521 EditDisplayMode::Inline
5522 }
5523 } else {
5524 EditDisplayMode::DiffPopover
5525 };
5526
5527 InlineCompletion::Edit {
5528 edits,
5529 edit_preview: inline_completion.edit_preview,
5530 display_mode,
5531 snapshot,
5532 }
5533 };
5534
5535 let invalidation_range = multibuffer
5536 .anchor_before(Point::new(invalidation_row_range.start, 0))
5537 ..multibuffer.anchor_after(Point::new(
5538 invalidation_row_range.end,
5539 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5540 ));
5541
5542 self.stale_inline_completion_in_menu = None;
5543 self.active_inline_completion = Some(InlineCompletionState {
5544 inlay_ids,
5545 completion,
5546 completion_id: inline_completion.id,
5547 invalidation_range,
5548 });
5549
5550 cx.notify();
5551
5552 Some(())
5553 }
5554
5555 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5556 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5557 }
5558
5559 fn render_code_actions_indicator(
5560 &self,
5561 _style: &EditorStyle,
5562 row: DisplayRow,
5563 is_active: bool,
5564 cx: &mut Context<Self>,
5565 ) -> Option<IconButton> {
5566 if self.available_code_actions.is_some() {
5567 Some(
5568 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5569 .shape(ui::IconButtonShape::Square)
5570 .icon_size(IconSize::XSmall)
5571 .icon_color(Color::Muted)
5572 .toggle_state(is_active)
5573 .tooltip({
5574 let focus_handle = self.focus_handle.clone();
5575 move |window, cx| {
5576 Tooltip::for_action_in(
5577 "Toggle Code Actions",
5578 &ToggleCodeActions {
5579 deployed_from_indicator: None,
5580 },
5581 &focus_handle,
5582 window,
5583 cx,
5584 )
5585 }
5586 })
5587 .on_click(cx.listener(move |editor, _e, window, cx| {
5588 window.focus(&editor.focus_handle(cx));
5589 editor.toggle_code_actions(
5590 &ToggleCodeActions {
5591 deployed_from_indicator: Some(row),
5592 },
5593 window,
5594 cx,
5595 );
5596 })),
5597 )
5598 } else {
5599 None
5600 }
5601 }
5602
5603 fn clear_tasks(&mut self) {
5604 self.tasks.clear()
5605 }
5606
5607 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5608 if self.tasks.insert(key, value).is_some() {
5609 // This case should hopefully be rare, but just in case...
5610 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5611 }
5612 }
5613
5614 fn build_tasks_context(
5615 project: &Entity<Project>,
5616 buffer: &Entity<Buffer>,
5617 buffer_row: u32,
5618 tasks: &Arc<RunnableTasks>,
5619 cx: &mut Context<Self>,
5620 ) -> Task<Option<task::TaskContext>> {
5621 let position = Point::new(buffer_row, tasks.column);
5622 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5623 let location = Location {
5624 buffer: buffer.clone(),
5625 range: range_start..range_start,
5626 };
5627 // Fill in the environmental variables from the tree-sitter captures
5628 let mut captured_task_variables = TaskVariables::default();
5629 for (capture_name, value) in tasks.extra_variables.clone() {
5630 captured_task_variables.insert(
5631 task::VariableName::Custom(capture_name.into()),
5632 value.clone(),
5633 );
5634 }
5635 project.update(cx, |project, cx| {
5636 project.task_store().update(cx, |task_store, cx| {
5637 task_store.task_context_for_location(captured_task_variables, location, cx)
5638 })
5639 })
5640 }
5641
5642 pub fn spawn_nearest_task(
5643 &mut self,
5644 action: &SpawnNearestTask,
5645 window: &mut Window,
5646 cx: &mut Context<Self>,
5647 ) {
5648 let Some((workspace, _)) = self.workspace.clone() else {
5649 return;
5650 };
5651 let Some(project) = self.project.clone() else {
5652 return;
5653 };
5654
5655 // Try to find a closest, enclosing node using tree-sitter that has a
5656 // task
5657 let Some((buffer, buffer_row, tasks)) = self
5658 .find_enclosing_node_task(cx)
5659 // Or find the task that's closest in row-distance.
5660 .or_else(|| self.find_closest_task(cx))
5661 else {
5662 return;
5663 };
5664
5665 let reveal_strategy = action.reveal;
5666 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5667 cx.spawn_in(window, |_, mut cx| async move {
5668 let context = task_context.await?;
5669 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5670
5671 let resolved = resolved_task.resolved.as_mut()?;
5672 resolved.reveal = reveal_strategy;
5673
5674 workspace
5675 .update(&mut cx, |workspace, cx| {
5676 workspace::tasks::schedule_resolved_task(
5677 workspace,
5678 task_source_kind,
5679 resolved_task,
5680 false,
5681 cx,
5682 );
5683 })
5684 .ok()
5685 })
5686 .detach();
5687 }
5688
5689 fn find_closest_task(
5690 &mut self,
5691 cx: &mut Context<Self>,
5692 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5693 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5694
5695 let ((buffer_id, row), tasks) = self
5696 .tasks
5697 .iter()
5698 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5699
5700 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5701 let tasks = Arc::new(tasks.to_owned());
5702 Some((buffer, *row, tasks))
5703 }
5704
5705 fn find_enclosing_node_task(
5706 &mut self,
5707 cx: &mut Context<Self>,
5708 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5709 let snapshot = self.buffer.read(cx).snapshot(cx);
5710 let offset = self.selections.newest::<usize>(cx).head();
5711 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5712 let buffer_id = excerpt.buffer().remote_id();
5713
5714 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5715 let mut cursor = layer.node().walk();
5716
5717 while cursor.goto_first_child_for_byte(offset).is_some() {
5718 if cursor.node().end_byte() == offset {
5719 cursor.goto_next_sibling();
5720 }
5721 }
5722
5723 // Ascend to the smallest ancestor that contains the range and has a task.
5724 loop {
5725 let node = cursor.node();
5726 let node_range = node.byte_range();
5727 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5728
5729 // Check if this node contains our offset
5730 if node_range.start <= offset && node_range.end >= offset {
5731 // If it contains offset, check for task
5732 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5733 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5734 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5735 }
5736 }
5737
5738 if !cursor.goto_parent() {
5739 break;
5740 }
5741 }
5742 None
5743 }
5744
5745 fn render_run_indicator(
5746 &self,
5747 _style: &EditorStyle,
5748 is_active: bool,
5749 row: DisplayRow,
5750 cx: &mut Context<Self>,
5751 ) -> IconButton {
5752 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5753 .shape(ui::IconButtonShape::Square)
5754 .icon_size(IconSize::XSmall)
5755 .icon_color(Color::Muted)
5756 .toggle_state(is_active)
5757 .on_click(cx.listener(move |editor, _e, window, cx| {
5758 window.focus(&editor.focus_handle(cx));
5759 editor.toggle_code_actions(
5760 &ToggleCodeActions {
5761 deployed_from_indicator: Some(row),
5762 },
5763 window,
5764 cx,
5765 );
5766 }))
5767 }
5768
5769 pub fn context_menu_visible(&self) -> bool {
5770 !self.edit_prediction_preview_is_active()
5771 && self
5772 .context_menu
5773 .borrow()
5774 .as_ref()
5775 .map_or(false, |menu| menu.visible())
5776 }
5777
5778 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5779 self.context_menu
5780 .borrow()
5781 .as_ref()
5782 .map(|menu| menu.origin())
5783 }
5784
5785 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5786 px(30.)
5787 }
5788
5789 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5790 if self.read_only(cx) {
5791 cx.theme().players().read_only()
5792 } else {
5793 self.style.as_ref().unwrap().local_player
5794 }
5795 }
5796
5797 fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
5798 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
5799 let accept_keystroke = accept_binding.keystroke()?;
5800
5801 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
5802
5803 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
5804 Color::Accent
5805 } else {
5806 Color::Muted
5807 };
5808
5809 h_flex()
5810 .px_0p5()
5811 .when(is_platform_style_mac, |parent| parent.gap_0p5())
5812 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
5813 .text_size(TextSize::XSmall.rems(cx))
5814 .child(h_flex().children(ui::render_modifiers(
5815 &accept_keystroke.modifiers,
5816 PlatformStyle::platform(),
5817 Some(modifiers_color),
5818 Some(IconSize::XSmall.rems().into()),
5819 true,
5820 )))
5821 .when(is_platform_style_mac, |parent| {
5822 parent.child(accept_keystroke.key.clone())
5823 })
5824 .when(!is_platform_style_mac, |parent| {
5825 parent.child(
5826 Key::new(
5827 util::capitalize(&accept_keystroke.key),
5828 Some(Color::Default),
5829 )
5830 .size(Some(IconSize::XSmall.rems().into())),
5831 )
5832 })
5833 .into()
5834 }
5835
5836 fn render_edit_prediction_line_popover(
5837 &self,
5838 label: impl Into<SharedString>,
5839 icon: Option<IconName>,
5840 window: &mut Window,
5841 cx: &App,
5842 ) -> Option<Div> {
5843 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
5844
5845 let result = h_flex()
5846 .py_0p5()
5847 .pl_1()
5848 .pr(padding_right)
5849 .gap_1()
5850 .rounded(px(6.))
5851 .border_1()
5852 .bg(Self::edit_prediction_line_popover_bg_color(cx))
5853 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
5854 .shadow_sm()
5855 .children(self.render_edit_prediction_accept_keybind(window, cx))
5856 .child(Label::new(label).size(LabelSize::Small))
5857 .when_some(icon, |element, icon| {
5858 element.child(
5859 div()
5860 .mt(px(1.5))
5861 .child(Icon::new(icon).size(IconSize::Small)),
5862 )
5863 });
5864
5865 Some(result)
5866 }
5867
5868 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
5869 let accent_color = cx.theme().colors().text_accent;
5870 let editor_bg_color = cx.theme().colors().editor_background;
5871 editor_bg_color.blend(accent_color.opacity(0.1))
5872 }
5873
5874 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
5875 let accent_color = cx.theme().colors().text_accent;
5876 let editor_bg_color = cx.theme().colors().editor_background;
5877 editor_bg_color.blend(accent_color.opacity(0.6))
5878 }
5879
5880 #[allow(clippy::too_many_arguments)]
5881 fn render_edit_prediction_cursor_popover(
5882 &self,
5883 min_width: Pixels,
5884 max_width: Pixels,
5885 cursor_point: Point,
5886 style: &EditorStyle,
5887 accept_keystroke: Option<&gpui::Keystroke>,
5888 _window: &Window,
5889 cx: &mut Context<Editor>,
5890 ) -> Option<AnyElement> {
5891 let provider = self.edit_prediction_provider.as_ref()?;
5892
5893 if provider.provider.needs_terms_acceptance(cx) {
5894 return Some(
5895 h_flex()
5896 .min_w(min_width)
5897 .flex_1()
5898 .px_2()
5899 .py_1()
5900 .gap_3()
5901 .elevation_2(cx)
5902 .hover(|style| style.bg(cx.theme().colors().element_hover))
5903 .id("accept-terms")
5904 .cursor_pointer()
5905 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5906 .on_click(cx.listener(|this, _event, window, cx| {
5907 cx.stop_propagation();
5908 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5909 window.dispatch_action(
5910 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5911 cx,
5912 );
5913 }))
5914 .child(
5915 h_flex()
5916 .flex_1()
5917 .gap_2()
5918 .child(Icon::new(IconName::ZedPredict))
5919 .child(Label::new("Accept Terms of Service"))
5920 .child(div().w_full())
5921 .child(
5922 Icon::new(IconName::ArrowUpRight)
5923 .color(Color::Muted)
5924 .size(IconSize::Small),
5925 )
5926 .into_any_element(),
5927 )
5928 .into_any(),
5929 );
5930 }
5931
5932 let is_refreshing = provider.provider.is_refreshing(cx);
5933
5934 fn pending_completion_container() -> Div {
5935 h_flex()
5936 .h_full()
5937 .flex_1()
5938 .gap_2()
5939 .child(Icon::new(IconName::ZedPredict))
5940 }
5941
5942 let completion = match &self.active_inline_completion {
5943 Some(completion) => match &completion.completion {
5944 InlineCompletion::Move {
5945 target, snapshot, ..
5946 } if !self.has_visible_completions_menu() => {
5947 use text::ToPoint as _;
5948
5949 return Some(
5950 h_flex()
5951 .px_2()
5952 .py_1()
5953 .gap_2()
5954 .elevation_2(cx)
5955 .border_color(cx.theme().colors().border)
5956 .rounded(px(6.))
5957 .rounded_tl(px(0.))
5958 .child(
5959 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
5960 Icon::new(IconName::ZedPredictDown)
5961 } else {
5962 Icon::new(IconName::ZedPredictUp)
5963 },
5964 )
5965 .child(Label::new("Hold").size(LabelSize::Small))
5966 .child(h_flex().children(ui::render_modifiers(
5967 &accept_keystroke?.modifiers,
5968 PlatformStyle::platform(),
5969 Some(Color::Default),
5970 Some(IconSize::Small.rems().into()),
5971 false,
5972 )))
5973 .into_any(),
5974 );
5975 }
5976 _ => self.render_edit_prediction_cursor_popover_preview(
5977 completion,
5978 cursor_point,
5979 style,
5980 cx,
5981 )?,
5982 },
5983
5984 None if is_refreshing => match &self.stale_inline_completion_in_menu {
5985 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
5986 stale_completion,
5987 cursor_point,
5988 style,
5989 cx,
5990 )?,
5991
5992 None => {
5993 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
5994 }
5995 },
5996
5997 None => pending_completion_container().child(Label::new("No Prediction")),
5998 };
5999
6000 let completion = if is_refreshing {
6001 completion
6002 .with_animation(
6003 "loading-completion",
6004 Animation::new(Duration::from_secs(2))
6005 .repeat()
6006 .with_easing(pulsating_between(0.4, 0.8)),
6007 |label, delta| label.opacity(delta),
6008 )
6009 .into_any_element()
6010 } else {
6011 completion.into_any_element()
6012 };
6013
6014 let has_completion = self.active_inline_completion.is_some();
6015
6016 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6017 Some(
6018 h_flex()
6019 .min_w(min_width)
6020 .max_w(max_width)
6021 .flex_1()
6022 .elevation_2(cx)
6023 .border_color(cx.theme().colors().border)
6024 .child(
6025 div()
6026 .flex_1()
6027 .py_1()
6028 .px_2()
6029 .overflow_hidden()
6030 .child(completion),
6031 )
6032 .when_some(accept_keystroke, |el, accept_keystroke| {
6033 if !accept_keystroke.modifiers.modified() {
6034 return el;
6035 }
6036
6037 el.child(
6038 h_flex()
6039 .h_full()
6040 .border_l_1()
6041 .rounded_r_lg()
6042 .border_color(cx.theme().colors().border)
6043 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6044 .gap_1()
6045 .py_1()
6046 .px_2()
6047 .child(
6048 h_flex()
6049 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6050 .when(is_platform_style_mac, |parent| parent.gap_1())
6051 .child(h_flex().children(ui::render_modifiers(
6052 &accept_keystroke.modifiers,
6053 PlatformStyle::platform(),
6054 Some(if !has_completion {
6055 Color::Muted
6056 } else {
6057 Color::Default
6058 }),
6059 None,
6060 false,
6061 ))),
6062 )
6063 .child(Label::new("Preview").into_any_element())
6064 .opacity(if has_completion { 1.0 } else { 0.4 }),
6065 )
6066 })
6067 .into_any(),
6068 )
6069 }
6070
6071 fn render_edit_prediction_cursor_popover_preview(
6072 &self,
6073 completion: &InlineCompletionState,
6074 cursor_point: Point,
6075 style: &EditorStyle,
6076 cx: &mut Context<Editor>,
6077 ) -> Option<Div> {
6078 use text::ToPoint as _;
6079
6080 fn render_relative_row_jump(
6081 prefix: impl Into<String>,
6082 current_row: u32,
6083 target_row: u32,
6084 ) -> Div {
6085 let (row_diff, arrow) = if target_row < current_row {
6086 (current_row - target_row, IconName::ArrowUp)
6087 } else {
6088 (target_row - current_row, IconName::ArrowDown)
6089 };
6090
6091 h_flex()
6092 .child(
6093 Label::new(format!("{}{}", prefix.into(), row_diff))
6094 .color(Color::Muted)
6095 .size(LabelSize::Small),
6096 )
6097 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6098 }
6099
6100 match &completion.completion {
6101 InlineCompletion::Move {
6102 target, snapshot, ..
6103 } => Some(
6104 h_flex()
6105 .px_2()
6106 .gap_2()
6107 .flex_1()
6108 .child(
6109 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6110 Icon::new(IconName::ZedPredictDown)
6111 } else {
6112 Icon::new(IconName::ZedPredictUp)
6113 },
6114 )
6115 .child(Label::new("Jump to Edit")),
6116 ),
6117
6118 InlineCompletion::Edit {
6119 edits,
6120 edit_preview,
6121 snapshot,
6122 display_mode: _,
6123 } => {
6124 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6125
6126 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6127 &snapshot,
6128 &edits,
6129 edit_preview.as_ref()?,
6130 true,
6131 cx,
6132 )
6133 .first_line_preview();
6134
6135 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6136 .with_highlights(&style.text, highlighted_edits.highlights);
6137
6138 let preview = h_flex()
6139 .gap_1()
6140 .min_w_16()
6141 .child(styled_text)
6142 .when(has_more_lines, |parent| parent.child("…"));
6143
6144 let left = if first_edit_row != cursor_point.row {
6145 render_relative_row_jump("", cursor_point.row, first_edit_row)
6146 .into_any_element()
6147 } else {
6148 Icon::new(IconName::ZedPredict).into_any_element()
6149 };
6150
6151 Some(
6152 h_flex()
6153 .h_full()
6154 .flex_1()
6155 .gap_2()
6156 .pr_1()
6157 .overflow_x_hidden()
6158 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6159 .child(left)
6160 .child(preview),
6161 )
6162 }
6163 }
6164 }
6165
6166 fn render_context_menu(
6167 &self,
6168 style: &EditorStyle,
6169 max_height_in_lines: u32,
6170 y_flipped: bool,
6171 window: &mut Window,
6172 cx: &mut Context<Editor>,
6173 ) -> Option<AnyElement> {
6174 let menu = self.context_menu.borrow();
6175 let menu = menu.as_ref()?;
6176 if !menu.visible() {
6177 return None;
6178 };
6179 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6180 }
6181
6182 fn render_context_menu_aside(
6183 &mut self,
6184 max_size: Size<Pixels>,
6185 window: &mut Window,
6186 cx: &mut Context<Editor>,
6187 ) -> Option<AnyElement> {
6188 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6189 if menu.visible() {
6190 menu.render_aside(self, max_size, window, cx)
6191 } else {
6192 None
6193 }
6194 })
6195 }
6196
6197 fn hide_context_menu(
6198 &mut self,
6199 window: &mut Window,
6200 cx: &mut Context<Self>,
6201 ) -> Option<CodeContextMenu> {
6202 cx.notify();
6203 self.completion_tasks.clear();
6204 let context_menu = self.context_menu.borrow_mut().take();
6205 self.stale_inline_completion_in_menu.take();
6206 self.update_visible_inline_completion(window, cx);
6207 context_menu
6208 }
6209
6210 fn show_snippet_choices(
6211 &mut self,
6212 choices: &Vec<String>,
6213 selection: Range<Anchor>,
6214 cx: &mut Context<Self>,
6215 ) {
6216 if selection.start.buffer_id.is_none() {
6217 return;
6218 }
6219 let buffer_id = selection.start.buffer_id.unwrap();
6220 let buffer = self.buffer().read(cx).buffer(buffer_id);
6221 let id = post_inc(&mut self.next_completion_id);
6222
6223 if let Some(buffer) = buffer {
6224 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6225 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6226 ));
6227 }
6228 }
6229
6230 pub fn insert_snippet(
6231 &mut self,
6232 insertion_ranges: &[Range<usize>],
6233 snippet: Snippet,
6234 window: &mut Window,
6235 cx: &mut Context<Self>,
6236 ) -> Result<()> {
6237 struct Tabstop<T> {
6238 is_end_tabstop: bool,
6239 ranges: Vec<Range<T>>,
6240 choices: Option<Vec<String>>,
6241 }
6242
6243 let tabstops = self.buffer.update(cx, |buffer, cx| {
6244 let snippet_text: Arc<str> = snippet.text.clone().into();
6245 buffer.edit(
6246 insertion_ranges
6247 .iter()
6248 .cloned()
6249 .map(|range| (range, snippet_text.clone())),
6250 Some(AutoindentMode::EachLine),
6251 cx,
6252 );
6253
6254 let snapshot = &*buffer.read(cx);
6255 let snippet = &snippet;
6256 snippet
6257 .tabstops
6258 .iter()
6259 .map(|tabstop| {
6260 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6261 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6262 });
6263 let mut tabstop_ranges = tabstop
6264 .ranges
6265 .iter()
6266 .flat_map(|tabstop_range| {
6267 let mut delta = 0_isize;
6268 insertion_ranges.iter().map(move |insertion_range| {
6269 let insertion_start = insertion_range.start as isize + delta;
6270 delta +=
6271 snippet.text.len() as isize - insertion_range.len() as isize;
6272
6273 let start = ((insertion_start + tabstop_range.start) as usize)
6274 .min(snapshot.len());
6275 let end = ((insertion_start + tabstop_range.end) as usize)
6276 .min(snapshot.len());
6277 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6278 })
6279 })
6280 .collect::<Vec<_>>();
6281 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6282
6283 Tabstop {
6284 is_end_tabstop,
6285 ranges: tabstop_ranges,
6286 choices: tabstop.choices.clone(),
6287 }
6288 })
6289 .collect::<Vec<_>>()
6290 });
6291 if let Some(tabstop) = tabstops.first() {
6292 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6293 s.select_ranges(tabstop.ranges.iter().cloned());
6294 });
6295
6296 if let Some(choices) = &tabstop.choices {
6297 if let Some(selection) = tabstop.ranges.first() {
6298 self.show_snippet_choices(choices, selection.clone(), cx)
6299 }
6300 }
6301
6302 // If we're already at the last tabstop and it's at the end of the snippet,
6303 // we're done, we don't need to keep the state around.
6304 if !tabstop.is_end_tabstop {
6305 let choices = tabstops
6306 .iter()
6307 .map(|tabstop| tabstop.choices.clone())
6308 .collect();
6309
6310 let ranges = tabstops
6311 .into_iter()
6312 .map(|tabstop| tabstop.ranges)
6313 .collect::<Vec<_>>();
6314
6315 self.snippet_stack.push(SnippetState {
6316 active_index: 0,
6317 ranges,
6318 choices,
6319 });
6320 }
6321
6322 // Check whether the just-entered snippet ends with an auto-closable bracket.
6323 if self.autoclose_regions.is_empty() {
6324 let snapshot = self.buffer.read(cx).snapshot(cx);
6325 for selection in &mut self.selections.all::<Point>(cx) {
6326 let selection_head = selection.head();
6327 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6328 continue;
6329 };
6330
6331 let mut bracket_pair = None;
6332 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6333 let prev_chars = snapshot
6334 .reversed_chars_at(selection_head)
6335 .collect::<String>();
6336 for (pair, enabled) in scope.brackets() {
6337 if enabled
6338 && pair.close
6339 && prev_chars.starts_with(pair.start.as_str())
6340 && next_chars.starts_with(pair.end.as_str())
6341 {
6342 bracket_pair = Some(pair.clone());
6343 break;
6344 }
6345 }
6346 if let Some(pair) = bracket_pair {
6347 let start = snapshot.anchor_after(selection_head);
6348 let end = snapshot.anchor_after(selection_head);
6349 self.autoclose_regions.push(AutocloseRegion {
6350 selection_id: selection.id,
6351 range: start..end,
6352 pair,
6353 });
6354 }
6355 }
6356 }
6357 }
6358 Ok(())
6359 }
6360
6361 pub fn move_to_next_snippet_tabstop(
6362 &mut self,
6363 window: &mut Window,
6364 cx: &mut Context<Self>,
6365 ) -> bool {
6366 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6367 }
6368
6369 pub fn move_to_prev_snippet_tabstop(
6370 &mut self,
6371 window: &mut Window,
6372 cx: &mut Context<Self>,
6373 ) -> bool {
6374 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6375 }
6376
6377 pub fn move_to_snippet_tabstop(
6378 &mut self,
6379 bias: Bias,
6380 window: &mut Window,
6381 cx: &mut Context<Self>,
6382 ) -> bool {
6383 if let Some(mut snippet) = self.snippet_stack.pop() {
6384 match bias {
6385 Bias::Left => {
6386 if snippet.active_index > 0 {
6387 snippet.active_index -= 1;
6388 } else {
6389 self.snippet_stack.push(snippet);
6390 return false;
6391 }
6392 }
6393 Bias::Right => {
6394 if snippet.active_index + 1 < snippet.ranges.len() {
6395 snippet.active_index += 1;
6396 } else {
6397 self.snippet_stack.push(snippet);
6398 return false;
6399 }
6400 }
6401 }
6402 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6403 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6404 s.select_anchor_ranges(current_ranges.iter().cloned())
6405 });
6406
6407 if let Some(choices) = &snippet.choices[snippet.active_index] {
6408 if let Some(selection) = current_ranges.first() {
6409 self.show_snippet_choices(&choices, selection.clone(), cx);
6410 }
6411 }
6412
6413 // If snippet state is not at the last tabstop, push it back on the stack
6414 if snippet.active_index + 1 < snippet.ranges.len() {
6415 self.snippet_stack.push(snippet);
6416 }
6417 return true;
6418 }
6419 }
6420
6421 false
6422 }
6423
6424 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6425 self.transact(window, cx, |this, window, cx| {
6426 this.select_all(&SelectAll, window, cx);
6427 this.insert("", window, cx);
6428 });
6429 }
6430
6431 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6432 self.transact(window, cx, |this, window, cx| {
6433 this.select_autoclose_pair(window, cx);
6434 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6435 if !this.linked_edit_ranges.is_empty() {
6436 let selections = this.selections.all::<MultiBufferPoint>(cx);
6437 let snapshot = this.buffer.read(cx).snapshot(cx);
6438
6439 for selection in selections.iter() {
6440 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6441 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6442 if selection_start.buffer_id != selection_end.buffer_id {
6443 continue;
6444 }
6445 if let Some(ranges) =
6446 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6447 {
6448 for (buffer, entries) in ranges {
6449 linked_ranges.entry(buffer).or_default().extend(entries);
6450 }
6451 }
6452 }
6453 }
6454
6455 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6456 if !this.selections.line_mode {
6457 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6458 for selection in &mut selections {
6459 if selection.is_empty() {
6460 let old_head = selection.head();
6461 let mut new_head =
6462 movement::left(&display_map, old_head.to_display_point(&display_map))
6463 .to_point(&display_map);
6464 if let Some((buffer, line_buffer_range)) = display_map
6465 .buffer_snapshot
6466 .buffer_line_for_row(MultiBufferRow(old_head.row))
6467 {
6468 let indent_size =
6469 buffer.indent_size_for_line(line_buffer_range.start.row);
6470 let indent_len = match indent_size.kind {
6471 IndentKind::Space => {
6472 buffer.settings_at(line_buffer_range.start, cx).tab_size
6473 }
6474 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6475 };
6476 if old_head.column <= indent_size.len && old_head.column > 0 {
6477 let indent_len = indent_len.get();
6478 new_head = cmp::min(
6479 new_head,
6480 MultiBufferPoint::new(
6481 old_head.row,
6482 ((old_head.column - 1) / indent_len) * indent_len,
6483 ),
6484 );
6485 }
6486 }
6487
6488 selection.set_head(new_head, SelectionGoal::None);
6489 }
6490 }
6491 }
6492
6493 this.signature_help_state.set_backspace_pressed(true);
6494 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6495 s.select(selections)
6496 });
6497 this.insert("", window, cx);
6498 let empty_str: Arc<str> = Arc::from("");
6499 for (buffer, edits) in linked_ranges {
6500 let snapshot = buffer.read(cx).snapshot();
6501 use text::ToPoint as TP;
6502
6503 let edits = edits
6504 .into_iter()
6505 .map(|range| {
6506 let end_point = TP::to_point(&range.end, &snapshot);
6507 let mut start_point = TP::to_point(&range.start, &snapshot);
6508
6509 if end_point == start_point {
6510 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6511 .saturating_sub(1);
6512 start_point =
6513 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6514 };
6515
6516 (start_point..end_point, empty_str.clone())
6517 })
6518 .sorted_by_key(|(range, _)| range.start)
6519 .collect::<Vec<_>>();
6520 buffer.update(cx, |this, cx| {
6521 this.edit(edits, None, cx);
6522 })
6523 }
6524 this.refresh_inline_completion(true, false, window, cx);
6525 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6526 });
6527 }
6528
6529 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6530 self.transact(window, cx, |this, window, cx| {
6531 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6532 let line_mode = s.line_mode;
6533 s.move_with(|map, selection| {
6534 if selection.is_empty() && !line_mode {
6535 let cursor = movement::right(map, selection.head());
6536 selection.end = cursor;
6537 selection.reversed = true;
6538 selection.goal = SelectionGoal::None;
6539 }
6540 })
6541 });
6542 this.insert("", window, cx);
6543 this.refresh_inline_completion(true, false, window, cx);
6544 });
6545 }
6546
6547 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6548 if self.move_to_prev_snippet_tabstop(window, cx) {
6549 return;
6550 }
6551
6552 self.outdent(&Outdent, window, cx);
6553 }
6554
6555 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6556 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6557 return;
6558 }
6559
6560 let mut selections = self.selections.all_adjusted(cx);
6561 let buffer = self.buffer.read(cx);
6562 let snapshot = buffer.snapshot(cx);
6563 let rows_iter = selections.iter().map(|s| s.head().row);
6564 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6565
6566 let mut edits = Vec::new();
6567 let mut prev_edited_row = 0;
6568 let mut row_delta = 0;
6569 for selection in &mut selections {
6570 if selection.start.row != prev_edited_row {
6571 row_delta = 0;
6572 }
6573 prev_edited_row = selection.end.row;
6574
6575 // If the selection is non-empty, then increase the indentation of the selected lines.
6576 if !selection.is_empty() {
6577 row_delta =
6578 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6579 continue;
6580 }
6581
6582 // If the selection is empty and the cursor is in the leading whitespace before the
6583 // suggested indentation, then auto-indent the line.
6584 let cursor = selection.head();
6585 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6586 if let Some(suggested_indent) =
6587 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6588 {
6589 if cursor.column < suggested_indent.len
6590 && cursor.column <= current_indent.len
6591 && current_indent.len <= suggested_indent.len
6592 {
6593 selection.start = Point::new(cursor.row, suggested_indent.len);
6594 selection.end = selection.start;
6595 if row_delta == 0 {
6596 edits.extend(Buffer::edit_for_indent_size_adjustment(
6597 cursor.row,
6598 current_indent,
6599 suggested_indent,
6600 ));
6601 row_delta = suggested_indent.len - current_indent.len;
6602 }
6603 continue;
6604 }
6605 }
6606
6607 // Otherwise, insert a hard or soft tab.
6608 let settings = buffer.settings_at(cursor, cx);
6609 let tab_size = if settings.hard_tabs {
6610 IndentSize::tab()
6611 } else {
6612 let tab_size = settings.tab_size.get();
6613 let char_column = snapshot
6614 .text_for_range(Point::new(cursor.row, 0)..cursor)
6615 .flat_map(str::chars)
6616 .count()
6617 + row_delta as usize;
6618 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6619 IndentSize::spaces(chars_to_next_tab_stop)
6620 };
6621 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6622 selection.end = selection.start;
6623 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6624 row_delta += tab_size.len;
6625 }
6626
6627 self.transact(window, cx, |this, window, cx| {
6628 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6629 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6630 s.select(selections)
6631 });
6632 this.refresh_inline_completion(true, false, window, cx);
6633 });
6634 }
6635
6636 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6637 if self.read_only(cx) {
6638 return;
6639 }
6640 let mut selections = self.selections.all::<Point>(cx);
6641 let mut prev_edited_row = 0;
6642 let mut row_delta = 0;
6643 let mut edits = Vec::new();
6644 let buffer = self.buffer.read(cx);
6645 let snapshot = buffer.snapshot(cx);
6646 for selection in &mut selections {
6647 if selection.start.row != prev_edited_row {
6648 row_delta = 0;
6649 }
6650 prev_edited_row = selection.end.row;
6651
6652 row_delta =
6653 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6654 }
6655
6656 self.transact(window, cx, |this, window, cx| {
6657 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6658 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6659 s.select(selections)
6660 });
6661 });
6662 }
6663
6664 fn indent_selection(
6665 buffer: &MultiBuffer,
6666 snapshot: &MultiBufferSnapshot,
6667 selection: &mut Selection<Point>,
6668 edits: &mut Vec<(Range<Point>, String)>,
6669 delta_for_start_row: u32,
6670 cx: &App,
6671 ) -> u32 {
6672 let settings = buffer.settings_at(selection.start, cx);
6673 let tab_size = settings.tab_size.get();
6674 let indent_kind = if settings.hard_tabs {
6675 IndentKind::Tab
6676 } else {
6677 IndentKind::Space
6678 };
6679 let mut start_row = selection.start.row;
6680 let mut end_row = selection.end.row + 1;
6681
6682 // If a selection ends at the beginning of a line, don't indent
6683 // that last line.
6684 if selection.end.column == 0 && selection.end.row > selection.start.row {
6685 end_row -= 1;
6686 }
6687
6688 // Avoid re-indenting a row that has already been indented by a
6689 // previous selection, but still update this selection's column
6690 // to reflect that indentation.
6691 if delta_for_start_row > 0 {
6692 start_row += 1;
6693 selection.start.column += delta_for_start_row;
6694 if selection.end.row == selection.start.row {
6695 selection.end.column += delta_for_start_row;
6696 }
6697 }
6698
6699 let mut delta_for_end_row = 0;
6700 let has_multiple_rows = start_row + 1 != end_row;
6701 for row in start_row..end_row {
6702 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6703 let indent_delta = match (current_indent.kind, indent_kind) {
6704 (IndentKind::Space, IndentKind::Space) => {
6705 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6706 IndentSize::spaces(columns_to_next_tab_stop)
6707 }
6708 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6709 (_, IndentKind::Tab) => IndentSize::tab(),
6710 };
6711
6712 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6713 0
6714 } else {
6715 selection.start.column
6716 };
6717 let row_start = Point::new(row, start);
6718 edits.push((
6719 row_start..row_start,
6720 indent_delta.chars().collect::<String>(),
6721 ));
6722
6723 // Update this selection's endpoints to reflect the indentation.
6724 if row == selection.start.row {
6725 selection.start.column += indent_delta.len;
6726 }
6727 if row == selection.end.row {
6728 selection.end.column += indent_delta.len;
6729 delta_for_end_row = indent_delta.len;
6730 }
6731 }
6732
6733 if selection.start.row == selection.end.row {
6734 delta_for_start_row + delta_for_end_row
6735 } else {
6736 delta_for_end_row
6737 }
6738 }
6739
6740 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6741 if self.read_only(cx) {
6742 return;
6743 }
6744 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6745 let selections = self.selections.all::<Point>(cx);
6746 let mut deletion_ranges = Vec::new();
6747 let mut last_outdent = None;
6748 {
6749 let buffer = self.buffer.read(cx);
6750 let snapshot = buffer.snapshot(cx);
6751 for selection in &selections {
6752 let settings = buffer.settings_at(selection.start, cx);
6753 let tab_size = settings.tab_size.get();
6754 let mut rows = selection.spanned_rows(false, &display_map);
6755
6756 // Avoid re-outdenting a row that has already been outdented by a
6757 // previous selection.
6758 if let Some(last_row) = last_outdent {
6759 if last_row == rows.start {
6760 rows.start = rows.start.next_row();
6761 }
6762 }
6763 let has_multiple_rows = rows.len() > 1;
6764 for row in rows.iter_rows() {
6765 let indent_size = snapshot.indent_size_for_line(row);
6766 if indent_size.len > 0 {
6767 let deletion_len = match indent_size.kind {
6768 IndentKind::Space => {
6769 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6770 if columns_to_prev_tab_stop == 0 {
6771 tab_size
6772 } else {
6773 columns_to_prev_tab_stop
6774 }
6775 }
6776 IndentKind::Tab => 1,
6777 };
6778 let start = if has_multiple_rows
6779 || deletion_len > selection.start.column
6780 || indent_size.len < selection.start.column
6781 {
6782 0
6783 } else {
6784 selection.start.column - deletion_len
6785 };
6786 deletion_ranges.push(
6787 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6788 );
6789 last_outdent = Some(row);
6790 }
6791 }
6792 }
6793 }
6794
6795 self.transact(window, cx, |this, window, cx| {
6796 this.buffer.update(cx, |buffer, cx| {
6797 let empty_str: Arc<str> = Arc::default();
6798 buffer.edit(
6799 deletion_ranges
6800 .into_iter()
6801 .map(|range| (range, empty_str.clone())),
6802 None,
6803 cx,
6804 );
6805 });
6806 let selections = this.selections.all::<usize>(cx);
6807 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6808 s.select(selections)
6809 });
6810 });
6811 }
6812
6813 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6814 if self.read_only(cx) {
6815 return;
6816 }
6817 let selections = self
6818 .selections
6819 .all::<usize>(cx)
6820 .into_iter()
6821 .map(|s| s.range());
6822
6823 self.transact(window, cx, |this, window, cx| {
6824 this.buffer.update(cx, |buffer, cx| {
6825 buffer.autoindent_ranges(selections, cx);
6826 });
6827 let selections = this.selections.all::<usize>(cx);
6828 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6829 s.select(selections)
6830 });
6831 });
6832 }
6833
6834 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6835 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6836 let selections = self.selections.all::<Point>(cx);
6837
6838 let mut new_cursors = Vec::new();
6839 let mut edit_ranges = Vec::new();
6840 let mut selections = selections.iter().peekable();
6841 while let Some(selection) = selections.next() {
6842 let mut rows = selection.spanned_rows(false, &display_map);
6843 let goal_display_column = selection.head().to_display_point(&display_map).column();
6844
6845 // Accumulate contiguous regions of rows that we want to delete.
6846 while let Some(next_selection) = selections.peek() {
6847 let next_rows = next_selection.spanned_rows(false, &display_map);
6848 if next_rows.start <= rows.end {
6849 rows.end = next_rows.end;
6850 selections.next().unwrap();
6851 } else {
6852 break;
6853 }
6854 }
6855
6856 let buffer = &display_map.buffer_snapshot;
6857 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6858 let edit_end;
6859 let cursor_buffer_row;
6860 if buffer.max_point().row >= rows.end.0 {
6861 // If there's a line after the range, delete the \n from the end of the row range
6862 // and position the cursor on the next line.
6863 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6864 cursor_buffer_row = rows.end;
6865 } else {
6866 // If there isn't a line after the range, delete the \n from the line before the
6867 // start of the row range and position the cursor there.
6868 edit_start = edit_start.saturating_sub(1);
6869 edit_end = buffer.len();
6870 cursor_buffer_row = rows.start.previous_row();
6871 }
6872
6873 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6874 *cursor.column_mut() =
6875 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6876
6877 new_cursors.push((
6878 selection.id,
6879 buffer.anchor_after(cursor.to_point(&display_map)),
6880 ));
6881 edit_ranges.push(edit_start..edit_end);
6882 }
6883
6884 self.transact(window, cx, |this, window, cx| {
6885 let buffer = this.buffer.update(cx, |buffer, cx| {
6886 let empty_str: Arc<str> = Arc::default();
6887 buffer.edit(
6888 edit_ranges
6889 .into_iter()
6890 .map(|range| (range, empty_str.clone())),
6891 None,
6892 cx,
6893 );
6894 buffer.snapshot(cx)
6895 });
6896 let new_selections = new_cursors
6897 .into_iter()
6898 .map(|(id, cursor)| {
6899 let cursor = cursor.to_point(&buffer);
6900 Selection {
6901 id,
6902 start: cursor,
6903 end: cursor,
6904 reversed: false,
6905 goal: SelectionGoal::None,
6906 }
6907 })
6908 .collect();
6909
6910 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6911 s.select(new_selections);
6912 });
6913 });
6914 }
6915
6916 pub fn join_lines_impl(
6917 &mut self,
6918 insert_whitespace: bool,
6919 window: &mut Window,
6920 cx: &mut Context<Self>,
6921 ) {
6922 if self.read_only(cx) {
6923 return;
6924 }
6925 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6926 for selection in self.selections.all::<Point>(cx) {
6927 let start = MultiBufferRow(selection.start.row);
6928 // Treat single line selections as if they include the next line. Otherwise this action
6929 // would do nothing for single line selections individual cursors.
6930 let end = if selection.start.row == selection.end.row {
6931 MultiBufferRow(selection.start.row + 1)
6932 } else {
6933 MultiBufferRow(selection.end.row)
6934 };
6935
6936 if let Some(last_row_range) = row_ranges.last_mut() {
6937 if start <= last_row_range.end {
6938 last_row_range.end = end;
6939 continue;
6940 }
6941 }
6942 row_ranges.push(start..end);
6943 }
6944
6945 let snapshot = self.buffer.read(cx).snapshot(cx);
6946 let mut cursor_positions = Vec::new();
6947 for row_range in &row_ranges {
6948 let anchor = snapshot.anchor_before(Point::new(
6949 row_range.end.previous_row().0,
6950 snapshot.line_len(row_range.end.previous_row()),
6951 ));
6952 cursor_positions.push(anchor..anchor);
6953 }
6954
6955 self.transact(window, cx, |this, window, cx| {
6956 for row_range in row_ranges.into_iter().rev() {
6957 for row in row_range.iter_rows().rev() {
6958 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6959 let next_line_row = row.next_row();
6960 let indent = snapshot.indent_size_for_line(next_line_row);
6961 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6962
6963 let replace =
6964 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6965 " "
6966 } else {
6967 ""
6968 };
6969
6970 this.buffer.update(cx, |buffer, cx| {
6971 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6972 });
6973 }
6974 }
6975
6976 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6977 s.select_anchor_ranges(cursor_positions)
6978 });
6979 });
6980 }
6981
6982 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6983 self.join_lines_impl(true, window, cx);
6984 }
6985
6986 pub fn sort_lines_case_sensitive(
6987 &mut self,
6988 _: &SortLinesCaseSensitive,
6989 window: &mut Window,
6990 cx: &mut Context<Self>,
6991 ) {
6992 self.manipulate_lines(window, cx, |lines| lines.sort())
6993 }
6994
6995 pub fn sort_lines_case_insensitive(
6996 &mut self,
6997 _: &SortLinesCaseInsensitive,
6998 window: &mut Window,
6999 cx: &mut Context<Self>,
7000 ) {
7001 self.manipulate_lines(window, cx, |lines| {
7002 lines.sort_by_key(|line| line.to_lowercase())
7003 })
7004 }
7005
7006 pub fn unique_lines_case_insensitive(
7007 &mut self,
7008 _: &UniqueLinesCaseInsensitive,
7009 window: &mut Window,
7010 cx: &mut Context<Self>,
7011 ) {
7012 self.manipulate_lines(window, cx, |lines| {
7013 let mut seen = HashSet::default();
7014 lines.retain(|line| seen.insert(line.to_lowercase()));
7015 })
7016 }
7017
7018 pub fn unique_lines_case_sensitive(
7019 &mut self,
7020 _: &UniqueLinesCaseSensitive,
7021 window: &mut Window,
7022 cx: &mut Context<Self>,
7023 ) {
7024 self.manipulate_lines(window, cx, |lines| {
7025 let mut seen = HashSet::default();
7026 lines.retain(|line| seen.insert(*line));
7027 })
7028 }
7029
7030 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7031 let Some(project) = self.project.clone() else {
7032 return;
7033 };
7034 self.reload(project, window, cx)
7035 .detach_and_notify_err(window, cx);
7036 }
7037
7038 pub fn restore_file(
7039 &mut self,
7040 _: &::git::RestoreFile,
7041 window: &mut Window,
7042 cx: &mut Context<Self>,
7043 ) {
7044 let mut buffer_ids = HashSet::default();
7045 let snapshot = self.buffer().read(cx).snapshot(cx);
7046 for selection in self.selections.all::<usize>(cx) {
7047 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7048 }
7049
7050 let buffer = self.buffer().read(cx);
7051 let ranges = buffer_ids
7052 .into_iter()
7053 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7054 .collect::<Vec<_>>();
7055
7056 self.restore_hunks_in_ranges(ranges, window, cx);
7057 }
7058
7059 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7060 let selections = self
7061 .selections
7062 .all(cx)
7063 .into_iter()
7064 .map(|s| s.range())
7065 .collect();
7066 self.restore_hunks_in_ranges(selections, window, cx);
7067 }
7068
7069 fn restore_hunks_in_ranges(
7070 &mut self,
7071 ranges: Vec<Range<Point>>,
7072 window: &mut Window,
7073 cx: &mut Context<Editor>,
7074 ) {
7075 let mut revert_changes = HashMap::default();
7076 let snapshot = self.buffer.read(cx).snapshot(cx);
7077 let Some(project) = &self.project else {
7078 return;
7079 };
7080
7081 let chunk_by = self
7082 .snapshot(window, cx)
7083 .hunks_for_ranges(ranges.into_iter())
7084 .into_iter()
7085 .chunk_by(|hunk| hunk.buffer_id);
7086 for (buffer_id, hunks) in &chunk_by {
7087 let hunks = hunks.collect::<Vec<_>>();
7088 for hunk in &hunks {
7089 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7090 }
7091 Self::do_stage_or_unstage(project, false, buffer_id, hunks.into_iter(), &snapshot, cx);
7092 }
7093 drop(chunk_by);
7094 if !revert_changes.is_empty() {
7095 self.transact(window, cx, |editor, window, cx| {
7096 editor.revert(revert_changes, window, cx);
7097 });
7098 }
7099 }
7100
7101 pub fn open_active_item_in_terminal(
7102 &mut self,
7103 _: &OpenInTerminal,
7104 window: &mut Window,
7105 cx: &mut Context<Self>,
7106 ) {
7107 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7108 let project_path = buffer.read(cx).project_path(cx)?;
7109 let project = self.project.as_ref()?.read(cx);
7110 let entry = project.entry_for_path(&project_path, cx)?;
7111 let parent = match &entry.canonical_path {
7112 Some(canonical_path) => canonical_path.to_path_buf(),
7113 None => project.absolute_path(&project_path, cx)?,
7114 }
7115 .parent()?
7116 .to_path_buf();
7117 Some(parent)
7118 }) {
7119 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7120 }
7121 }
7122
7123 pub fn prepare_restore_change(
7124 &self,
7125 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7126 hunk: &MultiBufferDiffHunk,
7127 cx: &mut App,
7128 ) -> Option<()> {
7129 let buffer = self.buffer.read(cx);
7130 let diff = buffer.diff_for(hunk.buffer_id)?;
7131 let buffer = buffer.buffer(hunk.buffer_id)?;
7132 let buffer = buffer.read(cx);
7133 let original_text = diff
7134 .read(cx)
7135 .base_text()
7136 .as_ref()?
7137 .as_rope()
7138 .slice(hunk.diff_base_byte_range.clone());
7139 let buffer_snapshot = buffer.snapshot();
7140 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7141 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7142 probe
7143 .0
7144 .start
7145 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7146 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7147 }) {
7148 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7149 Some(())
7150 } else {
7151 None
7152 }
7153 }
7154
7155 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7156 self.manipulate_lines(window, cx, |lines| lines.reverse())
7157 }
7158
7159 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7160 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7161 }
7162
7163 fn manipulate_lines<Fn>(
7164 &mut self,
7165 window: &mut Window,
7166 cx: &mut Context<Self>,
7167 mut callback: Fn,
7168 ) where
7169 Fn: FnMut(&mut Vec<&str>),
7170 {
7171 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7172 let buffer = self.buffer.read(cx).snapshot(cx);
7173
7174 let mut edits = Vec::new();
7175
7176 let selections = self.selections.all::<Point>(cx);
7177 let mut selections = selections.iter().peekable();
7178 let mut contiguous_row_selections = Vec::new();
7179 let mut new_selections = Vec::new();
7180 let mut added_lines = 0;
7181 let mut removed_lines = 0;
7182
7183 while let Some(selection) = selections.next() {
7184 let (start_row, end_row) = consume_contiguous_rows(
7185 &mut contiguous_row_selections,
7186 selection,
7187 &display_map,
7188 &mut selections,
7189 );
7190
7191 let start_point = Point::new(start_row.0, 0);
7192 let end_point = Point::new(
7193 end_row.previous_row().0,
7194 buffer.line_len(end_row.previous_row()),
7195 );
7196 let text = buffer
7197 .text_for_range(start_point..end_point)
7198 .collect::<String>();
7199
7200 let mut lines = text.split('\n').collect_vec();
7201
7202 let lines_before = lines.len();
7203 callback(&mut lines);
7204 let lines_after = lines.len();
7205
7206 edits.push((start_point..end_point, lines.join("\n")));
7207
7208 // Selections must change based on added and removed line count
7209 let start_row =
7210 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7211 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7212 new_selections.push(Selection {
7213 id: selection.id,
7214 start: start_row,
7215 end: end_row,
7216 goal: SelectionGoal::None,
7217 reversed: selection.reversed,
7218 });
7219
7220 if lines_after > lines_before {
7221 added_lines += lines_after - lines_before;
7222 } else if lines_before > lines_after {
7223 removed_lines += lines_before - lines_after;
7224 }
7225 }
7226
7227 self.transact(window, cx, |this, window, cx| {
7228 let buffer = this.buffer.update(cx, |buffer, cx| {
7229 buffer.edit(edits, None, cx);
7230 buffer.snapshot(cx)
7231 });
7232
7233 // Recalculate offsets on newly edited buffer
7234 let new_selections = new_selections
7235 .iter()
7236 .map(|s| {
7237 let start_point = Point::new(s.start.0, 0);
7238 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7239 Selection {
7240 id: s.id,
7241 start: buffer.point_to_offset(start_point),
7242 end: buffer.point_to_offset(end_point),
7243 goal: s.goal,
7244 reversed: s.reversed,
7245 }
7246 })
7247 .collect();
7248
7249 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7250 s.select(new_selections);
7251 });
7252
7253 this.request_autoscroll(Autoscroll::fit(), cx);
7254 });
7255 }
7256
7257 pub fn convert_to_upper_case(
7258 &mut self,
7259 _: &ConvertToUpperCase,
7260 window: &mut Window,
7261 cx: &mut Context<Self>,
7262 ) {
7263 self.manipulate_text(window, cx, |text| text.to_uppercase())
7264 }
7265
7266 pub fn convert_to_lower_case(
7267 &mut self,
7268 _: &ConvertToLowerCase,
7269 window: &mut Window,
7270 cx: &mut Context<Self>,
7271 ) {
7272 self.manipulate_text(window, cx, |text| text.to_lowercase())
7273 }
7274
7275 pub fn convert_to_title_case(
7276 &mut self,
7277 _: &ConvertToTitleCase,
7278 window: &mut Window,
7279 cx: &mut Context<Self>,
7280 ) {
7281 self.manipulate_text(window, cx, |text| {
7282 text.split('\n')
7283 .map(|line| line.to_case(Case::Title))
7284 .join("\n")
7285 })
7286 }
7287
7288 pub fn convert_to_snake_case(
7289 &mut self,
7290 _: &ConvertToSnakeCase,
7291 window: &mut Window,
7292 cx: &mut Context<Self>,
7293 ) {
7294 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7295 }
7296
7297 pub fn convert_to_kebab_case(
7298 &mut self,
7299 _: &ConvertToKebabCase,
7300 window: &mut Window,
7301 cx: &mut Context<Self>,
7302 ) {
7303 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7304 }
7305
7306 pub fn convert_to_upper_camel_case(
7307 &mut self,
7308 _: &ConvertToUpperCamelCase,
7309 window: &mut Window,
7310 cx: &mut Context<Self>,
7311 ) {
7312 self.manipulate_text(window, cx, |text| {
7313 text.split('\n')
7314 .map(|line| line.to_case(Case::UpperCamel))
7315 .join("\n")
7316 })
7317 }
7318
7319 pub fn convert_to_lower_camel_case(
7320 &mut self,
7321 _: &ConvertToLowerCamelCase,
7322 window: &mut Window,
7323 cx: &mut Context<Self>,
7324 ) {
7325 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7326 }
7327
7328 pub fn convert_to_opposite_case(
7329 &mut self,
7330 _: &ConvertToOppositeCase,
7331 window: &mut Window,
7332 cx: &mut Context<Self>,
7333 ) {
7334 self.manipulate_text(window, cx, |text| {
7335 text.chars()
7336 .fold(String::with_capacity(text.len()), |mut t, c| {
7337 if c.is_uppercase() {
7338 t.extend(c.to_lowercase());
7339 } else {
7340 t.extend(c.to_uppercase());
7341 }
7342 t
7343 })
7344 })
7345 }
7346
7347 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7348 where
7349 Fn: FnMut(&str) -> String,
7350 {
7351 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7352 let buffer = self.buffer.read(cx).snapshot(cx);
7353
7354 let mut new_selections = Vec::new();
7355 let mut edits = Vec::new();
7356 let mut selection_adjustment = 0i32;
7357
7358 for selection in self.selections.all::<usize>(cx) {
7359 let selection_is_empty = selection.is_empty();
7360
7361 let (start, end) = if selection_is_empty {
7362 let word_range = movement::surrounding_word(
7363 &display_map,
7364 selection.start.to_display_point(&display_map),
7365 );
7366 let start = word_range.start.to_offset(&display_map, Bias::Left);
7367 let end = word_range.end.to_offset(&display_map, Bias::Left);
7368 (start, end)
7369 } else {
7370 (selection.start, selection.end)
7371 };
7372
7373 let text = buffer.text_for_range(start..end).collect::<String>();
7374 let old_length = text.len() as i32;
7375 let text = callback(&text);
7376
7377 new_selections.push(Selection {
7378 start: (start as i32 - selection_adjustment) as usize,
7379 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7380 goal: SelectionGoal::None,
7381 ..selection
7382 });
7383
7384 selection_adjustment += old_length - text.len() as i32;
7385
7386 edits.push((start..end, text));
7387 }
7388
7389 self.transact(window, cx, |this, window, cx| {
7390 this.buffer.update(cx, |buffer, cx| {
7391 buffer.edit(edits, None, cx);
7392 });
7393
7394 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7395 s.select(new_selections);
7396 });
7397
7398 this.request_autoscroll(Autoscroll::fit(), cx);
7399 });
7400 }
7401
7402 pub fn duplicate(
7403 &mut self,
7404 upwards: bool,
7405 whole_lines: bool,
7406 window: &mut Window,
7407 cx: &mut Context<Self>,
7408 ) {
7409 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7410 let buffer = &display_map.buffer_snapshot;
7411 let selections = self.selections.all::<Point>(cx);
7412
7413 let mut edits = Vec::new();
7414 let mut selections_iter = selections.iter().peekable();
7415 while let Some(selection) = selections_iter.next() {
7416 let mut rows = selection.spanned_rows(false, &display_map);
7417 // duplicate line-wise
7418 if whole_lines || selection.start == selection.end {
7419 // Avoid duplicating the same lines twice.
7420 while let Some(next_selection) = selections_iter.peek() {
7421 let next_rows = next_selection.spanned_rows(false, &display_map);
7422 if next_rows.start < rows.end {
7423 rows.end = next_rows.end;
7424 selections_iter.next().unwrap();
7425 } else {
7426 break;
7427 }
7428 }
7429
7430 // Copy the text from the selected row region and splice it either at the start
7431 // or end of the region.
7432 let start = Point::new(rows.start.0, 0);
7433 let end = Point::new(
7434 rows.end.previous_row().0,
7435 buffer.line_len(rows.end.previous_row()),
7436 );
7437 let text = buffer
7438 .text_for_range(start..end)
7439 .chain(Some("\n"))
7440 .collect::<String>();
7441 let insert_location = if upwards {
7442 Point::new(rows.end.0, 0)
7443 } else {
7444 start
7445 };
7446 edits.push((insert_location..insert_location, text));
7447 } else {
7448 // duplicate character-wise
7449 let start = selection.start;
7450 let end = selection.end;
7451 let text = buffer.text_for_range(start..end).collect::<String>();
7452 edits.push((selection.end..selection.end, text));
7453 }
7454 }
7455
7456 self.transact(window, cx, |this, _, cx| {
7457 this.buffer.update(cx, |buffer, cx| {
7458 buffer.edit(edits, None, cx);
7459 });
7460
7461 this.request_autoscroll(Autoscroll::fit(), cx);
7462 });
7463 }
7464
7465 pub fn duplicate_line_up(
7466 &mut self,
7467 _: &DuplicateLineUp,
7468 window: &mut Window,
7469 cx: &mut Context<Self>,
7470 ) {
7471 self.duplicate(true, true, window, cx);
7472 }
7473
7474 pub fn duplicate_line_down(
7475 &mut self,
7476 _: &DuplicateLineDown,
7477 window: &mut Window,
7478 cx: &mut Context<Self>,
7479 ) {
7480 self.duplicate(false, true, window, cx);
7481 }
7482
7483 pub fn duplicate_selection(
7484 &mut self,
7485 _: &DuplicateSelection,
7486 window: &mut Window,
7487 cx: &mut Context<Self>,
7488 ) {
7489 self.duplicate(false, false, window, cx);
7490 }
7491
7492 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7493 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7494 let buffer = self.buffer.read(cx).snapshot(cx);
7495
7496 let mut edits = Vec::new();
7497 let mut unfold_ranges = Vec::new();
7498 let mut refold_creases = Vec::new();
7499
7500 let selections = self.selections.all::<Point>(cx);
7501 let mut selections = selections.iter().peekable();
7502 let mut contiguous_row_selections = Vec::new();
7503 let mut new_selections = Vec::new();
7504
7505 while let Some(selection) = selections.next() {
7506 // Find all the selections that span a contiguous row range
7507 let (start_row, end_row) = consume_contiguous_rows(
7508 &mut contiguous_row_selections,
7509 selection,
7510 &display_map,
7511 &mut selections,
7512 );
7513
7514 // Move the text spanned by the row range to be before the line preceding the row range
7515 if start_row.0 > 0 {
7516 let range_to_move = Point::new(
7517 start_row.previous_row().0,
7518 buffer.line_len(start_row.previous_row()),
7519 )
7520 ..Point::new(
7521 end_row.previous_row().0,
7522 buffer.line_len(end_row.previous_row()),
7523 );
7524 let insertion_point = display_map
7525 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7526 .0;
7527
7528 // Don't move lines across excerpts
7529 if buffer
7530 .excerpt_containing(insertion_point..range_to_move.end)
7531 .is_some()
7532 {
7533 let text = buffer
7534 .text_for_range(range_to_move.clone())
7535 .flat_map(|s| s.chars())
7536 .skip(1)
7537 .chain(['\n'])
7538 .collect::<String>();
7539
7540 edits.push((
7541 buffer.anchor_after(range_to_move.start)
7542 ..buffer.anchor_before(range_to_move.end),
7543 String::new(),
7544 ));
7545 let insertion_anchor = buffer.anchor_after(insertion_point);
7546 edits.push((insertion_anchor..insertion_anchor, text));
7547
7548 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7549
7550 // Move selections up
7551 new_selections.extend(contiguous_row_selections.drain(..).map(
7552 |mut selection| {
7553 selection.start.row -= row_delta;
7554 selection.end.row -= row_delta;
7555 selection
7556 },
7557 ));
7558
7559 // Move folds up
7560 unfold_ranges.push(range_to_move.clone());
7561 for fold in display_map.folds_in_range(
7562 buffer.anchor_before(range_to_move.start)
7563 ..buffer.anchor_after(range_to_move.end),
7564 ) {
7565 let mut start = fold.range.start.to_point(&buffer);
7566 let mut end = fold.range.end.to_point(&buffer);
7567 start.row -= row_delta;
7568 end.row -= row_delta;
7569 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7570 }
7571 }
7572 }
7573
7574 // If we didn't move line(s), preserve the existing selections
7575 new_selections.append(&mut contiguous_row_selections);
7576 }
7577
7578 self.transact(window, cx, |this, window, cx| {
7579 this.unfold_ranges(&unfold_ranges, true, true, cx);
7580 this.buffer.update(cx, |buffer, cx| {
7581 for (range, text) in edits {
7582 buffer.edit([(range, text)], None, cx);
7583 }
7584 });
7585 this.fold_creases(refold_creases, true, window, cx);
7586 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7587 s.select(new_selections);
7588 })
7589 });
7590 }
7591
7592 pub fn move_line_down(
7593 &mut self,
7594 _: &MoveLineDown,
7595 window: &mut Window,
7596 cx: &mut Context<Self>,
7597 ) {
7598 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7599 let buffer = self.buffer.read(cx).snapshot(cx);
7600
7601 let mut edits = Vec::new();
7602 let mut unfold_ranges = Vec::new();
7603 let mut refold_creases = Vec::new();
7604
7605 let selections = self.selections.all::<Point>(cx);
7606 let mut selections = selections.iter().peekable();
7607 let mut contiguous_row_selections = Vec::new();
7608 let mut new_selections = Vec::new();
7609
7610 while let Some(selection) = selections.next() {
7611 // Find all the selections that span a contiguous row range
7612 let (start_row, end_row) = consume_contiguous_rows(
7613 &mut contiguous_row_selections,
7614 selection,
7615 &display_map,
7616 &mut selections,
7617 );
7618
7619 // Move the text spanned by the row range to be after the last line of the row range
7620 if end_row.0 <= buffer.max_point().row {
7621 let range_to_move =
7622 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7623 let insertion_point = display_map
7624 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7625 .0;
7626
7627 // Don't move lines across excerpt boundaries
7628 if buffer
7629 .excerpt_containing(range_to_move.start..insertion_point)
7630 .is_some()
7631 {
7632 let mut text = String::from("\n");
7633 text.extend(buffer.text_for_range(range_to_move.clone()));
7634 text.pop(); // Drop trailing newline
7635 edits.push((
7636 buffer.anchor_after(range_to_move.start)
7637 ..buffer.anchor_before(range_to_move.end),
7638 String::new(),
7639 ));
7640 let insertion_anchor = buffer.anchor_after(insertion_point);
7641 edits.push((insertion_anchor..insertion_anchor, text));
7642
7643 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7644
7645 // Move selections down
7646 new_selections.extend(contiguous_row_selections.drain(..).map(
7647 |mut selection| {
7648 selection.start.row += row_delta;
7649 selection.end.row += row_delta;
7650 selection
7651 },
7652 ));
7653
7654 // Move folds down
7655 unfold_ranges.push(range_to_move.clone());
7656 for fold in display_map.folds_in_range(
7657 buffer.anchor_before(range_to_move.start)
7658 ..buffer.anchor_after(range_to_move.end),
7659 ) {
7660 let mut start = fold.range.start.to_point(&buffer);
7661 let mut end = fold.range.end.to_point(&buffer);
7662 start.row += row_delta;
7663 end.row += row_delta;
7664 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7665 }
7666 }
7667 }
7668
7669 // If we didn't move line(s), preserve the existing selections
7670 new_selections.append(&mut contiguous_row_selections);
7671 }
7672
7673 self.transact(window, cx, |this, window, cx| {
7674 this.unfold_ranges(&unfold_ranges, true, true, cx);
7675 this.buffer.update(cx, |buffer, cx| {
7676 for (range, text) in edits {
7677 buffer.edit([(range, text)], None, cx);
7678 }
7679 });
7680 this.fold_creases(refold_creases, true, window, cx);
7681 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7682 s.select(new_selections)
7683 });
7684 });
7685 }
7686
7687 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7688 let text_layout_details = &self.text_layout_details(window);
7689 self.transact(window, cx, |this, window, cx| {
7690 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7691 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7692 let line_mode = s.line_mode;
7693 s.move_with(|display_map, selection| {
7694 if !selection.is_empty() || line_mode {
7695 return;
7696 }
7697
7698 let mut head = selection.head();
7699 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7700 if head.column() == display_map.line_len(head.row()) {
7701 transpose_offset = display_map
7702 .buffer_snapshot
7703 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7704 }
7705
7706 if transpose_offset == 0 {
7707 return;
7708 }
7709
7710 *head.column_mut() += 1;
7711 head = display_map.clip_point(head, Bias::Right);
7712 let goal = SelectionGoal::HorizontalPosition(
7713 display_map
7714 .x_for_display_point(head, text_layout_details)
7715 .into(),
7716 );
7717 selection.collapse_to(head, goal);
7718
7719 let transpose_start = display_map
7720 .buffer_snapshot
7721 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7722 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7723 let transpose_end = display_map
7724 .buffer_snapshot
7725 .clip_offset(transpose_offset + 1, Bias::Right);
7726 if let Some(ch) =
7727 display_map.buffer_snapshot.chars_at(transpose_start).next()
7728 {
7729 edits.push((transpose_start..transpose_offset, String::new()));
7730 edits.push((transpose_end..transpose_end, ch.to_string()));
7731 }
7732 }
7733 });
7734 edits
7735 });
7736 this.buffer
7737 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7738 let selections = this.selections.all::<usize>(cx);
7739 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7740 s.select(selections);
7741 });
7742 });
7743 }
7744
7745 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7746 self.rewrap_impl(IsVimMode::No, cx)
7747 }
7748
7749 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7750 let buffer = self.buffer.read(cx).snapshot(cx);
7751 let selections = self.selections.all::<Point>(cx);
7752 let mut selections = selections.iter().peekable();
7753
7754 let mut edits = Vec::new();
7755 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7756
7757 while let Some(selection) = selections.next() {
7758 let mut start_row = selection.start.row;
7759 let mut end_row = selection.end.row;
7760
7761 // Skip selections that overlap with a range that has already been rewrapped.
7762 let selection_range = start_row..end_row;
7763 if rewrapped_row_ranges
7764 .iter()
7765 .any(|range| range.overlaps(&selection_range))
7766 {
7767 continue;
7768 }
7769
7770 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7771
7772 // Since not all lines in the selection may be at the same indent
7773 // level, choose the indent size that is the most common between all
7774 // of the lines.
7775 //
7776 // If there is a tie, we use the deepest indent.
7777 let (indent_size, indent_end) = {
7778 let mut indent_size_occurrences = HashMap::default();
7779 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7780
7781 for row in start_row..=end_row {
7782 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7783 rows_by_indent_size.entry(indent).or_default().push(row);
7784 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7785 }
7786
7787 let indent_size = indent_size_occurrences
7788 .into_iter()
7789 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7790 .map(|(indent, _)| indent)
7791 .unwrap_or_default();
7792 let row = rows_by_indent_size[&indent_size][0];
7793 let indent_end = Point::new(row, indent_size.len);
7794
7795 (indent_size, indent_end)
7796 };
7797
7798 let mut line_prefix = indent_size.chars().collect::<String>();
7799
7800 let mut inside_comment = false;
7801 if let Some(comment_prefix) =
7802 buffer
7803 .language_scope_at(selection.head())
7804 .and_then(|language| {
7805 language
7806 .line_comment_prefixes()
7807 .iter()
7808 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7809 .cloned()
7810 })
7811 {
7812 line_prefix.push_str(&comment_prefix);
7813 inside_comment = true;
7814 }
7815
7816 let language_settings = buffer.settings_at(selection.head(), cx);
7817 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
7818 RewrapBehavior::InComments => inside_comment,
7819 RewrapBehavior::InSelections => !selection.is_empty(),
7820 RewrapBehavior::Anywhere => true,
7821 };
7822
7823 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
7824 if !should_rewrap {
7825 continue;
7826 }
7827
7828 if selection.is_empty() {
7829 'expand_upwards: while start_row > 0 {
7830 let prev_row = start_row - 1;
7831 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7832 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7833 {
7834 start_row = prev_row;
7835 } else {
7836 break 'expand_upwards;
7837 }
7838 }
7839
7840 'expand_downwards: while end_row < buffer.max_point().row {
7841 let next_row = end_row + 1;
7842 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7843 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7844 {
7845 end_row = next_row;
7846 } else {
7847 break 'expand_downwards;
7848 }
7849 }
7850 }
7851
7852 let start = Point::new(start_row, 0);
7853 let start_offset = start.to_offset(&buffer);
7854 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7855 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7856 let Some(lines_without_prefixes) = selection_text
7857 .lines()
7858 .map(|line| {
7859 line.strip_prefix(&line_prefix)
7860 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7861 .ok_or_else(|| {
7862 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7863 })
7864 })
7865 .collect::<Result<Vec<_>, _>>()
7866 .log_err()
7867 else {
7868 continue;
7869 };
7870
7871 let wrap_column = buffer
7872 .settings_at(Point::new(start_row, 0), cx)
7873 .preferred_line_length as usize;
7874 let wrapped_text = wrap_with_prefix(
7875 line_prefix,
7876 lines_without_prefixes.join(" "),
7877 wrap_column,
7878 tab_size,
7879 );
7880
7881 // TODO: should always use char-based diff while still supporting cursor behavior that
7882 // matches vim.
7883 let mut diff_options = DiffOptions::default();
7884 if is_vim_mode == IsVimMode::Yes {
7885 diff_options.max_word_diff_len = 0;
7886 diff_options.max_word_diff_line_count = 0;
7887 } else {
7888 diff_options.max_word_diff_len = usize::MAX;
7889 diff_options.max_word_diff_line_count = usize::MAX;
7890 }
7891
7892 for (old_range, new_text) in
7893 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
7894 {
7895 let edit_start = buffer.anchor_after(start_offset + old_range.start);
7896 let edit_end = buffer.anchor_after(start_offset + old_range.end);
7897 edits.push((edit_start..edit_end, new_text));
7898 }
7899
7900 rewrapped_row_ranges.push(start_row..=end_row);
7901 }
7902
7903 self.buffer
7904 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7905 }
7906
7907 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7908 let mut text = String::new();
7909 let buffer = self.buffer.read(cx).snapshot(cx);
7910 let mut selections = self.selections.all::<Point>(cx);
7911 let mut clipboard_selections = Vec::with_capacity(selections.len());
7912 {
7913 let max_point = buffer.max_point();
7914 let mut is_first = true;
7915 for selection in &mut selections {
7916 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7917 if is_entire_line {
7918 selection.start = Point::new(selection.start.row, 0);
7919 if !selection.is_empty() && selection.end.column == 0 {
7920 selection.end = cmp::min(max_point, selection.end);
7921 } else {
7922 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7923 }
7924 selection.goal = SelectionGoal::None;
7925 }
7926 if is_first {
7927 is_first = false;
7928 } else {
7929 text += "\n";
7930 }
7931 let mut len = 0;
7932 for chunk in buffer.text_for_range(selection.start..selection.end) {
7933 text.push_str(chunk);
7934 len += chunk.len();
7935 }
7936 clipboard_selections.push(ClipboardSelection {
7937 len,
7938 is_entire_line,
7939 start_column: selection.start.column,
7940 });
7941 }
7942 }
7943
7944 self.transact(window, cx, |this, window, cx| {
7945 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7946 s.select(selections);
7947 });
7948 this.insert("", window, cx);
7949 });
7950 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7951 }
7952
7953 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7954 let item = self.cut_common(window, cx);
7955 cx.write_to_clipboard(item);
7956 }
7957
7958 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7959 self.change_selections(None, window, cx, |s| {
7960 s.move_with(|snapshot, sel| {
7961 if sel.is_empty() {
7962 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7963 }
7964 });
7965 });
7966 let item = self.cut_common(window, cx);
7967 cx.set_global(KillRing(item))
7968 }
7969
7970 pub fn kill_ring_yank(
7971 &mut self,
7972 _: &KillRingYank,
7973 window: &mut Window,
7974 cx: &mut Context<Self>,
7975 ) {
7976 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7977 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7978 (kill_ring.text().to_string(), kill_ring.metadata_json())
7979 } else {
7980 return;
7981 }
7982 } else {
7983 return;
7984 };
7985 self.do_paste(&text, metadata, false, window, cx);
7986 }
7987
7988 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7989 let selections = self.selections.all::<Point>(cx);
7990 let buffer = self.buffer.read(cx).read(cx);
7991 let mut text = String::new();
7992
7993 let mut clipboard_selections = Vec::with_capacity(selections.len());
7994 {
7995 let max_point = buffer.max_point();
7996 let mut is_first = true;
7997 for selection in selections.iter() {
7998 let mut start = selection.start;
7999 let mut end = selection.end;
8000 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8001 if is_entire_line {
8002 start = Point::new(start.row, 0);
8003 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8004 }
8005 if is_first {
8006 is_first = false;
8007 } else {
8008 text += "\n";
8009 }
8010 let mut len = 0;
8011 for chunk in buffer.text_for_range(start..end) {
8012 text.push_str(chunk);
8013 len += chunk.len();
8014 }
8015 clipboard_selections.push(ClipboardSelection {
8016 len,
8017 is_entire_line,
8018 start_column: start.column,
8019 });
8020 }
8021 }
8022
8023 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8024 text,
8025 clipboard_selections,
8026 ));
8027 }
8028
8029 pub fn do_paste(
8030 &mut self,
8031 text: &String,
8032 clipboard_selections: Option<Vec<ClipboardSelection>>,
8033 handle_entire_lines: bool,
8034 window: &mut Window,
8035 cx: &mut Context<Self>,
8036 ) {
8037 if self.read_only(cx) {
8038 return;
8039 }
8040
8041 let clipboard_text = Cow::Borrowed(text);
8042
8043 self.transact(window, cx, |this, window, cx| {
8044 if let Some(mut clipboard_selections) = clipboard_selections {
8045 let old_selections = this.selections.all::<usize>(cx);
8046 let all_selections_were_entire_line =
8047 clipboard_selections.iter().all(|s| s.is_entire_line);
8048 let first_selection_start_column =
8049 clipboard_selections.first().map(|s| s.start_column);
8050 if clipboard_selections.len() != old_selections.len() {
8051 clipboard_selections.drain(..);
8052 }
8053 let cursor_offset = this.selections.last::<usize>(cx).head();
8054 let mut auto_indent_on_paste = true;
8055
8056 this.buffer.update(cx, |buffer, cx| {
8057 let snapshot = buffer.read(cx);
8058 auto_indent_on_paste =
8059 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8060
8061 let mut start_offset = 0;
8062 let mut edits = Vec::new();
8063 let mut original_start_columns = Vec::new();
8064 for (ix, selection) in old_selections.iter().enumerate() {
8065 let to_insert;
8066 let entire_line;
8067 let original_start_column;
8068 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8069 let end_offset = start_offset + clipboard_selection.len;
8070 to_insert = &clipboard_text[start_offset..end_offset];
8071 entire_line = clipboard_selection.is_entire_line;
8072 start_offset = end_offset + 1;
8073 original_start_column = Some(clipboard_selection.start_column);
8074 } else {
8075 to_insert = clipboard_text.as_str();
8076 entire_line = all_selections_were_entire_line;
8077 original_start_column = first_selection_start_column
8078 }
8079
8080 // If the corresponding selection was empty when this slice of the
8081 // clipboard text was written, then the entire line containing the
8082 // selection was copied. If this selection is also currently empty,
8083 // then paste the line before the current line of the buffer.
8084 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8085 let column = selection.start.to_point(&snapshot).column as usize;
8086 let line_start = selection.start - column;
8087 line_start..line_start
8088 } else {
8089 selection.range()
8090 };
8091
8092 edits.push((range, to_insert));
8093 original_start_columns.extend(original_start_column);
8094 }
8095 drop(snapshot);
8096
8097 buffer.edit(
8098 edits,
8099 if auto_indent_on_paste {
8100 Some(AutoindentMode::Block {
8101 original_start_columns,
8102 })
8103 } else {
8104 None
8105 },
8106 cx,
8107 );
8108 });
8109
8110 let selections = this.selections.all::<usize>(cx);
8111 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8112 s.select(selections)
8113 });
8114 } else {
8115 this.insert(&clipboard_text, window, cx);
8116 }
8117 });
8118 }
8119
8120 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8121 if let Some(item) = cx.read_from_clipboard() {
8122 let entries = item.entries();
8123
8124 match entries.first() {
8125 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8126 // of all the pasted entries.
8127 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8128 .do_paste(
8129 clipboard_string.text(),
8130 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8131 true,
8132 window,
8133 cx,
8134 ),
8135 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8136 }
8137 }
8138 }
8139
8140 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8141 if self.read_only(cx) {
8142 return;
8143 }
8144
8145 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8146 if let Some((selections, _)) =
8147 self.selection_history.transaction(transaction_id).cloned()
8148 {
8149 self.change_selections(None, window, cx, |s| {
8150 s.select_anchors(selections.to_vec());
8151 });
8152 }
8153 self.request_autoscroll(Autoscroll::fit(), cx);
8154 self.unmark_text(window, cx);
8155 self.refresh_inline_completion(true, false, window, cx);
8156 cx.emit(EditorEvent::Edited { transaction_id });
8157 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8158 }
8159 }
8160
8161 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8162 if self.read_only(cx) {
8163 return;
8164 }
8165
8166 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8167 if let Some((_, Some(selections))) =
8168 self.selection_history.transaction(transaction_id).cloned()
8169 {
8170 self.change_selections(None, window, cx, |s| {
8171 s.select_anchors(selections.to_vec());
8172 });
8173 }
8174 self.request_autoscroll(Autoscroll::fit(), cx);
8175 self.unmark_text(window, cx);
8176 self.refresh_inline_completion(true, false, window, cx);
8177 cx.emit(EditorEvent::Edited { transaction_id });
8178 }
8179 }
8180
8181 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8182 self.buffer
8183 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8184 }
8185
8186 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8187 self.buffer
8188 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8189 }
8190
8191 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8192 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8193 let line_mode = s.line_mode;
8194 s.move_with(|map, selection| {
8195 let cursor = if selection.is_empty() && !line_mode {
8196 movement::left(map, selection.start)
8197 } else {
8198 selection.start
8199 };
8200 selection.collapse_to(cursor, SelectionGoal::None);
8201 });
8202 })
8203 }
8204
8205 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8206 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8207 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8208 })
8209 }
8210
8211 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8212 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8213 let line_mode = s.line_mode;
8214 s.move_with(|map, selection| {
8215 let cursor = if selection.is_empty() && !line_mode {
8216 movement::right(map, selection.end)
8217 } else {
8218 selection.end
8219 };
8220 selection.collapse_to(cursor, SelectionGoal::None)
8221 });
8222 })
8223 }
8224
8225 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8226 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8227 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8228 })
8229 }
8230
8231 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8232 if self.take_rename(true, window, cx).is_some() {
8233 return;
8234 }
8235
8236 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8237 cx.propagate();
8238 return;
8239 }
8240
8241 let text_layout_details = &self.text_layout_details(window);
8242 let selection_count = self.selections.count();
8243 let first_selection = self.selections.first_anchor();
8244
8245 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8246 let line_mode = s.line_mode;
8247 s.move_with(|map, selection| {
8248 if !selection.is_empty() && !line_mode {
8249 selection.goal = SelectionGoal::None;
8250 }
8251 let (cursor, goal) = movement::up(
8252 map,
8253 selection.start,
8254 selection.goal,
8255 false,
8256 text_layout_details,
8257 );
8258 selection.collapse_to(cursor, goal);
8259 });
8260 });
8261
8262 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8263 {
8264 cx.propagate();
8265 }
8266 }
8267
8268 pub fn move_up_by_lines(
8269 &mut self,
8270 action: &MoveUpByLines,
8271 window: &mut Window,
8272 cx: &mut Context<Self>,
8273 ) {
8274 if self.take_rename(true, window, cx).is_some() {
8275 return;
8276 }
8277
8278 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8279 cx.propagate();
8280 return;
8281 }
8282
8283 let text_layout_details = &self.text_layout_details(window);
8284
8285 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8286 let line_mode = s.line_mode;
8287 s.move_with(|map, selection| {
8288 if !selection.is_empty() && !line_mode {
8289 selection.goal = SelectionGoal::None;
8290 }
8291 let (cursor, goal) = movement::up_by_rows(
8292 map,
8293 selection.start,
8294 action.lines,
8295 selection.goal,
8296 false,
8297 text_layout_details,
8298 );
8299 selection.collapse_to(cursor, goal);
8300 });
8301 })
8302 }
8303
8304 pub fn move_down_by_lines(
8305 &mut self,
8306 action: &MoveDownByLines,
8307 window: &mut Window,
8308 cx: &mut Context<Self>,
8309 ) {
8310 if self.take_rename(true, window, cx).is_some() {
8311 return;
8312 }
8313
8314 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8315 cx.propagate();
8316 return;
8317 }
8318
8319 let text_layout_details = &self.text_layout_details(window);
8320
8321 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8322 let line_mode = s.line_mode;
8323 s.move_with(|map, selection| {
8324 if !selection.is_empty() && !line_mode {
8325 selection.goal = SelectionGoal::None;
8326 }
8327 let (cursor, goal) = movement::down_by_rows(
8328 map,
8329 selection.start,
8330 action.lines,
8331 selection.goal,
8332 false,
8333 text_layout_details,
8334 );
8335 selection.collapse_to(cursor, goal);
8336 });
8337 })
8338 }
8339
8340 pub fn select_down_by_lines(
8341 &mut self,
8342 action: &SelectDownByLines,
8343 window: &mut Window,
8344 cx: &mut Context<Self>,
8345 ) {
8346 let text_layout_details = &self.text_layout_details(window);
8347 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8348 s.move_heads_with(|map, head, goal| {
8349 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8350 })
8351 })
8352 }
8353
8354 pub fn select_up_by_lines(
8355 &mut self,
8356 action: &SelectUpByLines,
8357 window: &mut Window,
8358 cx: &mut Context<Self>,
8359 ) {
8360 let text_layout_details = &self.text_layout_details(window);
8361 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8362 s.move_heads_with(|map, head, goal| {
8363 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8364 })
8365 })
8366 }
8367
8368 pub fn select_page_up(
8369 &mut self,
8370 _: &SelectPageUp,
8371 window: &mut Window,
8372 cx: &mut Context<Self>,
8373 ) {
8374 let Some(row_count) = self.visible_row_count() else {
8375 return;
8376 };
8377
8378 let text_layout_details = &self.text_layout_details(window);
8379
8380 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8381 s.move_heads_with(|map, head, goal| {
8382 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8383 })
8384 })
8385 }
8386
8387 pub fn move_page_up(
8388 &mut self,
8389 action: &MovePageUp,
8390 window: &mut Window,
8391 cx: &mut Context<Self>,
8392 ) {
8393 if self.take_rename(true, window, cx).is_some() {
8394 return;
8395 }
8396
8397 if self
8398 .context_menu
8399 .borrow_mut()
8400 .as_mut()
8401 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8402 .unwrap_or(false)
8403 {
8404 return;
8405 }
8406
8407 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8408 cx.propagate();
8409 return;
8410 }
8411
8412 let Some(row_count) = self.visible_row_count() else {
8413 return;
8414 };
8415
8416 let autoscroll = if action.center_cursor {
8417 Autoscroll::center()
8418 } else {
8419 Autoscroll::fit()
8420 };
8421
8422 let text_layout_details = &self.text_layout_details(window);
8423
8424 self.change_selections(Some(autoscroll), window, cx, |s| {
8425 let line_mode = s.line_mode;
8426 s.move_with(|map, selection| {
8427 if !selection.is_empty() && !line_mode {
8428 selection.goal = SelectionGoal::None;
8429 }
8430 let (cursor, goal) = movement::up_by_rows(
8431 map,
8432 selection.end,
8433 row_count,
8434 selection.goal,
8435 false,
8436 text_layout_details,
8437 );
8438 selection.collapse_to(cursor, goal);
8439 });
8440 });
8441 }
8442
8443 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8444 let text_layout_details = &self.text_layout_details(window);
8445 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8446 s.move_heads_with(|map, head, goal| {
8447 movement::up(map, head, goal, false, text_layout_details)
8448 })
8449 })
8450 }
8451
8452 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8453 self.take_rename(true, window, cx);
8454
8455 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8456 cx.propagate();
8457 return;
8458 }
8459
8460 let text_layout_details = &self.text_layout_details(window);
8461 let selection_count = self.selections.count();
8462 let first_selection = self.selections.first_anchor();
8463
8464 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8465 let line_mode = s.line_mode;
8466 s.move_with(|map, selection| {
8467 if !selection.is_empty() && !line_mode {
8468 selection.goal = SelectionGoal::None;
8469 }
8470 let (cursor, goal) = movement::down(
8471 map,
8472 selection.end,
8473 selection.goal,
8474 false,
8475 text_layout_details,
8476 );
8477 selection.collapse_to(cursor, goal);
8478 });
8479 });
8480
8481 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8482 {
8483 cx.propagate();
8484 }
8485 }
8486
8487 pub fn select_page_down(
8488 &mut self,
8489 _: &SelectPageDown,
8490 window: &mut Window,
8491 cx: &mut Context<Self>,
8492 ) {
8493 let Some(row_count) = self.visible_row_count() else {
8494 return;
8495 };
8496
8497 let text_layout_details = &self.text_layout_details(window);
8498
8499 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8500 s.move_heads_with(|map, head, goal| {
8501 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8502 })
8503 })
8504 }
8505
8506 pub fn move_page_down(
8507 &mut self,
8508 action: &MovePageDown,
8509 window: &mut Window,
8510 cx: &mut Context<Self>,
8511 ) {
8512 if self.take_rename(true, window, cx).is_some() {
8513 return;
8514 }
8515
8516 if self
8517 .context_menu
8518 .borrow_mut()
8519 .as_mut()
8520 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8521 .unwrap_or(false)
8522 {
8523 return;
8524 }
8525
8526 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8527 cx.propagate();
8528 return;
8529 }
8530
8531 let Some(row_count) = self.visible_row_count() else {
8532 return;
8533 };
8534
8535 let autoscroll = if action.center_cursor {
8536 Autoscroll::center()
8537 } else {
8538 Autoscroll::fit()
8539 };
8540
8541 let text_layout_details = &self.text_layout_details(window);
8542 self.change_selections(Some(autoscroll), window, cx, |s| {
8543 let line_mode = s.line_mode;
8544 s.move_with(|map, selection| {
8545 if !selection.is_empty() && !line_mode {
8546 selection.goal = SelectionGoal::None;
8547 }
8548 let (cursor, goal) = movement::down_by_rows(
8549 map,
8550 selection.end,
8551 row_count,
8552 selection.goal,
8553 false,
8554 text_layout_details,
8555 );
8556 selection.collapse_to(cursor, goal);
8557 });
8558 });
8559 }
8560
8561 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8562 let text_layout_details = &self.text_layout_details(window);
8563 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8564 s.move_heads_with(|map, head, goal| {
8565 movement::down(map, head, goal, false, text_layout_details)
8566 })
8567 });
8568 }
8569
8570 pub fn context_menu_first(
8571 &mut self,
8572 _: &ContextMenuFirst,
8573 _window: &mut Window,
8574 cx: &mut Context<Self>,
8575 ) {
8576 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8577 context_menu.select_first(self.completion_provider.as_deref(), cx);
8578 }
8579 }
8580
8581 pub fn context_menu_prev(
8582 &mut self,
8583 _: &ContextMenuPrev,
8584 _window: &mut Window,
8585 cx: &mut Context<Self>,
8586 ) {
8587 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8588 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8589 }
8590 }
8591
8592 pub fn context_menu_next(
8593 &mut self,
8594 _: &ContextMenuNext,
8595 _window: &mut Window,
8596 cx: &mut Context<Self>,
8597 ) {
8598 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8599 context_menu.select_next(self.completion_provider.as_deref(), cx);
8600 }
8601 }
8602
8603 pub fn context_menu_last(
8604 &mut self,
8605 _: &ContextMenuLast,
8606 _window: &mut Window,
8607 cx: &mut Context<Self>,
8608 ) {
8609 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8610 context_menu.select_last(self.completion_provider.as_deref(), cx);
8611 }
8612 }
8613
8614 pub fn move_to_previous_word_start(
8615 &mut self,
8616 _: &MoveToPreviousWordStart,
8617 window: &mut Window,
8618 cx: &mut Context<Self>,
8619 ) {
8620 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8621 s.move_cursors_with(|map, head, _| {
8622 (
8623 movement::previous_word_start(map, head),
8624 SelectionGoal::None,
8625 )
8626 });
8627 })
8628 }
8629
8630 pub fn move_to_previous_subword_start(
8631 &mut self,
8632 _: &MoveToPreviousSubwordStart,
8633 window: &mut Window,
8634 cx: &mut Context<Self>,
8635 ) {
8636 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8637 s.move_cursors_with(|map, head, _| {
8638 (
8639 movement::previous_subword_start(map, head),
8640 SelectionGoal::None,
8641 )
8642 });
8643 })
8644 }
8645
8646 pub fn select_to_previous_word_start(
8647 &mut self,
8648 _: &SelectToPreviousWordStart,
8649 window: &mut Window,
8650 cx: &mut Context<Self>,
8651 ) {
8652 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8653 s.move_heads_with(|map, head, _| {
8654 (
8655 movement::previous_word_start(map, head),
8656 SelectionGoal::None,
8657 )
8658 });
8659 })
8660 }
8661
8662 pub fn select_to_previous_subword_start(
8663 &mut self,
8664 _: &SelectToPreviousSubwordStart,
8665 window: &mut Window,
8666 cx: &mut Context<Self>,
8667 ) {
8668 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8669 s.move_heads_with(|map, head, _| {
8670 (
8671 movement::previous_subword_start(map, head),
8672 SelectionGoal::None,
8673 )
8674 });
8675 })
8676 }
8677
8678 pub fn delete_to_previous_word_start(
8679 &mut self,
8680 action: &DeleteToPreviousWordStart,
8681 window: &mut Window,
8682 cx: &mut Context<Self>,
8683 ) {
8684 self.transact(window, cx, |this, window, cx| {
8685 this.select_autoclose_pair(window, cx);
8686 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8687 let line_mode = s.line_mode;
8688 s.move_with(|map, selection| {
8689 if selection.is_empty() && !line_mode {
8690 let cursor = if action.ignore_newlines {
8691 movement::previous_word_start(map, selection.head())
8692 } else {
8693 movement::previous_word_start_or_newline(map, selection.head())
8694 };
8695 selection.set_head(cursor, SelectionGoal::None);
8696 }
8697 });
8698 });
8699 this.insert("", window, cx);
8700 });
8701 }
8702
8703 pub fn delete_to_previous_subword_start(
8704 &mut self,
8705 _: &DeleteToPreviousSubwordStart,
8706 window: &mut Window,
8707 cx: &mut Context<Self>,
8708 ) {
8709 self.transact(window, cx, |this, window, cx| {
8710 this.select_autoclose_pair(window, cx);
8711 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8712 let line_mode = s.line_mode;
8713 s.move_with(|map, selection| {
8714 if selection.is_empty() && !line_mode {
8715 let cursor = movement::previous_subword_start(map, selection.head());
8716 selection.set_head(cursor, SelectionGoal::None);
8717 }
8718 });
8719 });
8720 this.insert("", window, cx);
8721 });
8722 }
8723
8724 pub fn move_to_next_word_end(
8725 &mut self,
8726 _: &MoveToNextWordEnd,
8727 window: &mut Window,
8728 cx: &mut Context<Self>,
8729 ) {
8730 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8731 s.move_cursors_with(|map, head, _| {
8732 (movement::next_word_end(map, head), SelectionGoal::None)
8733 });
8734 })
8735 }
8736
8737 pub fn move_to_next_subword_end(
8738 &mut self,
8739 _: &MoveToNextSubwordEnd,
8740 window: &mut Window,
8741 cx: &mut Context<Self>,
8742 ) {
8743 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8744 s.move_cursors_with(|map, head, _| {
8745 (movement::next_subword_end(map, head), SelectionGoal::None)
8746 });
8747 })
8748 }
8749
8750 pub fn select_to_next_word_end(
8751 &mut self,
8752 _: &SelectToNextWordEnd,
8753 window: &mut Window,
8754 cx: &mut Context<Self>,
8755 ) {
8756 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8757 s.move_heads_with(|map, head, _| {
8758 (movement::next_word_end(map, head), SelectionGoal::None)
8759 });
8760 })
8761 }
8762
8763 pub fn select_to_next_subword_end(
8764 &mut self,
8765 _: &SelectToNextSubwordEnd,
8766 window: &mut Window,
8767 cx: &mut Context<Self>,
8768 ) {
8769 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8770 s.move_heads_with(|map, head, _| {
8771 (movement::next_subword_end(map, head), SelectionGoal::None)
8772 });
8773 })
8774 }
8775
8776 pub fn delete_to_next_word_end(
8777 &mut self,
8778 action: &DeleteToNextWordEnd,
8779 window: &mut Window,
8780 cx: &mut Context<Self>,
8781 ) {
8782 self.transact(window, cx, |this, window, cx| {
8783 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8784 let line_mode = s.line_mode;
8785 s.move_with(|map, selection| {
8786 if selection.is_empty() && !line_mode {
8787 let cursor = if action.ignore_newlines {
8788 movement::next_word_end(map, selection.head())
8789 } else {
8790 movement::next_word_end_or_newline(map, selection.head())
8791 };
8792 selection.set_head(cursor, SelectionGoal::None);
8793 }
8794 });
8795 });
8796 this.insert("", window, cx);
8797 });
8798 }
8799
8800 pub fn delete_to_next_subword_end(
8801 &mut self,
8802 _: &DeleteToNextSubwordEnd,
8803 window: &mut Window,
8804 cx: &mut Context<Self>,
8805 ) {
8806 self.transact(window, cx, |this, window, cx| {
8807 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8808 s.move_with(|map, selection| {
8809 if selection.is_empty() {
8810 let cursor = movement::next_subword_end(map, selection.head());
8811 selection.set_head(cursor, SelectionGoal::None);
8812 }
8813 });
8814 });
8815 this.insert("", window, cx);
8816 });
8817 }
8818
8819 pub fn move_to_beginning_of_line(
8820 &mut self,
8821 action: &MoveToBeginningOfLine,
8822 window: &mut Window,
8823 cx: &mut Context<Self>,
8824 ) {
8825 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8826 s.move_cursors_with(|map, head, _| {
8827 (
8828 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8829 SelectionGoal::None,
8830 )
8831 });
8832 })
8833 }
8834
8835 pub fn select_to_beginning_of_line(
8836 &mut self,
8837 action: &SelectToBeginningOfLine,
8838 window: &mut Window,
8839 cx: &mut Context<Self>,
8840 ) {
8841 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8842 s.move_heads_with(|map, head, _| {
8843 (
8844 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8845 SelectionGoal::None,
8846 )
8847 });
8848 });
8849 }
8850
8851 pub fn delete_to_beginning_of_line(
8852 &mut self,
8853 _: &DeleteToBeginningOfLine,
8854 window: &mut Window,
8855 cx: &mut Context<Self>,
8856 ) {
8857 self.transact(window, cx, |this, window, cx| {
8858 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8859 s.move_with(|_, selection| {
8860 selection.reversed = true;
8861 });
8862 });
8863
8864 this.select_to_beginning_of_line(
8865 &SelectToBeginningOfLine {
8866 stop_at_soft_wraps: false,
8867 },
8868 window,
8869 cx,
8870 );
8871 this.backspace(&Backspace, window, cx);
8872 });
8873 }
8874
8875 pub fn move_to_end_of_line(
8876 &mut self,
8877 action: &MoveToEndOfLine,
8878 window: &mut Window,
8879 cx: &mut Context<Self>,
8880 ) {
8881 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8882 s.move_cursors_with(|map, head, _| {
8883 (
8884 movement::line_end(map, head, action.stop_at_soft_wraps),
8885 SelectionGoal::None,
8886 )
8887 });
8888 })
8889 }
8890
8891 pub fn select_to_end_of_line(
8892 &mut self,
8893 action: &SelectToEndOfLine,
8894 window: &mut Window,
8895 cx: &mut Context<Self>,
8896 ) {
8897 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8898 s.move_heads_with(|map, head, _| {
8899 (
8900 movement::line_end(map, head, action.stop_at_soft_wraps),
8901 SelectionGoal::None,
8902 )
8903 });
8904 })
8905 }
8906
8907 pub fn delete_to_end_of_line(
8908 &mut self,
8909 _: &DeleteToEndOfLine,
8910 window: &mut Window,
8911 cx: &mut Context<Self>,
8912 ) {
8913 self.transact(window, cx, |this, window, cx| {
8914 this.select_to_end_of_line(
8915 &SelectToEndOfLine {
8916 stop_at_soft_wraps: false,
8917 },
8918 window,
8919 cx,
8920 );
8921 this.delete(&Delete, window, cx);
8922 });
8923 }
8924
8925 pub fn cut_to_end_of_line(
8926 &mut self,
8927 _: &CutToEndOfLine,
8928 window: &mut Window,
8929 cx: &mut Context<Self>,
8930 ) {
8931 self.transact(window, cx, |this, window, cx| {
8932 this.select_to_end_of_line(
8933 &SelectToEndOfLine {
8934 stop_at_soft_wraps: false,
8935 },
8936 window,
8937 cx,
8938 );
8939 this.cut(&Cut, window, cx);
8940 });
8941 }
8942
8943 pub fn move_to_start_of_paragraph(
8944 &mut self,
8945 _: &MoveToStartOfParagraph,
8946 window: &mut Window,
8947 cx: &mut Context<Self>,
8948 ) {
8949 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8950 cx.propagate();
8951 return;
8952 }
8953
8954 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8955 s.move_with(|map, selection| {
8956 selection.collapse_to(
8957 movement::start_of_paragraph(map, selection.head(), 1),
8958 SelectionGoal::None,
8959 )
8960 });
8961 })
8962 }
8963
8964 pub fn move_to_end_of_paragraph(
8965 &mut self,
8966 _: &MoveToEndOfParagraph,
8967 window: &mut Window,
8968 cx: &mut Context<Self>,
8969 ) {
8970 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8971 cx.propagate();
8972 return;
8973 }
8974
8975 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8976 s.move_with(|map, selection| {
8977 selection.collapse_to(
8978 movement::end_of_paragraph(map, selection.head(), 1),
8979 SelectionGoal::None,
8980 )
8981 });
8982 })
8983 }
8984
8985 pub fn select_to_start_of_paragraph(
8986 &mut self,
8987 _: &SelectToStartOfParagraph,
8988 window: &mut Window,
8989 cx: &mut Context<Self>,
8990 ) {
8991 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8992 cx.propagate();
8993 return;
8994 }
8995
8996 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8997 s.move_heads_with(|map, head, _| {
8998 (
8999 movement::start_of_paragraph(map, head, 1),
9000 SelectionGoal::None,
9001 )
9002 });
9003 })
9004 }
9005
9006 pub fn select_to_end_of_paragraph(
9007 &mut self,
9008 _: &SelectToEndOfParagraph,
9009 window: &mut Window,
9010 cx: &mut Context<Self>,
9011 ) {
9012 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9013 cx.propagate();
9014 return;
9015 }
9016
9017 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9018 s.move_heads_with(|map, head, _| {
9019 (
9020 movement::end_of_paragraph(map, head, 1),
9021 SelectionGoal::None,
9022 )
9023 });
9024 })
9025 }
9026
9027 pub fn move_to_start_of_excerpt(
9028 &mut self,
9029 _: &MoveToStartOfExcerpt,
9030 window: &mut Window,
9031 cx: &mut Context<Self>,
9032 ) {
9033 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9034 cx.propagate();
9035 return;
9036 }
9037
9038 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9039 s.move_with(|map, selection| {
9040 selection.collapse_to(
9041 movement::start_of_excerpt(
9042 map,
9043 selection.head(),
9044 workspace::searchable::Direction::Prev,
9045 ),
9046 SelectionGoal::None,
9047 )
9048 });
9049 })
9050 }
9051
9052 pub fn move_to_end_of_excerpt(
9053 &mut self,
9054 _: &MoveToEndOfExcerpt,
9055 window: &mut Window,
9056 cx: &mut Context<Self>,
9057 ) {
9058 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9059 cx.propagate();
9060 return;
9061 }
9062
9063 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9064 s.move_with(|map, selection| {
9065 selection.collapse_to(
9066 movement::end_of_excerpt(
9067 map,
9068 selection.head(),
9069 workspace::searchable::Direction::Next,
9070 ),
9071 SelectionGoal::None,
9072 )
9073 });
9074 })
9075 }
9076
9077 pub fn select_to_start_of_excerpt(
9078 &mut self,
9079 _: &SelectToStartOfExcerpt,
9080 window: &mut Window,
9081 cx: &mut Context<Self>,
9082 ) {
9083 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9084 cx.propagate();
9085 return;
9086 }
9087
9088 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9089 s.move_heads_with(|map, head, _| {
9090 (
9091 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9092 SelectionGoal::None,
9093 )
9094 });
9095 })
9096 }
9097
9098 pub fn select_to_end_of_excerpt(
9099 &mut self,
9100 _: &SelectToEndOfExcerpt,
9101 window: &mut Window,
9102 cx: &mut Context<Self>,
9103 ) {
9104 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9105 cx.propagate();
9106 return;
9107 }
9108
9109 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9110 s.move_heads_with(|map, head, _| {
9111 (
9112 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9113 SelectionGoal::None,
9114 )
9115 });
9116 })
9117 }
9118
9119 pub fn move_to_beginning(
9120 &mut self,
9121 _: &MoveToBeginning,
9122 window: &mut Window,
9123 cx: &mut Context<Self>,
9124 ) {
9125 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9126 cx.propagate();
9127 return;
9128 }
9129
9130 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9131 s.select_ranges(vec![0..0]);
9132 });
9133 }
9134
9135 pub fn select_to_beginning(
9136 &mut self,
9137 _: &SelectToBeginning,
9138 window: &mut Window,
9139 cx: &mut Context<Self>,
9140 ) {
9141 let mut selection = self.selections.last::<Point>(cx);
9142 selection.set_head(Point::zero(), SelectionGoal::None);
9143
9144 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9145 s.select(vec![selection]);
9146 });
9147 }
9148
9149 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9150 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9151 cx.propagate();
9152 return;
9153 }
9154
9155 let cursor = self.buffer.read(cx).read(cx).len();
9156 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9157 s.select_ranges(vec![cursor..cursor])
9158 });
9159 }
9160
9161 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9162 self.nav_history = nav_history;
9163 }
9164
9165 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9166 self.nav_history.as_ref()
9167 }
9168
9169 fn push_to_nav_history(
9170 &mut self,
9171 cursor_anchor: Anchor,
9172 new_position: Option<Point>,
9173 cx: &mut Context<Self>,
9174 ) {
9175 if let Some(nav_history) = self.nav_history.as_mut() {
9176 let buffer = self.buffer.read(cx).read(cx);
9177 let cursor_position = cursor_anchor.to_point(&buffer);
9178 let scroll_state = self.scroll_manager.anchor();
9179 let scroll_top_row = scroll_state.top_row(&buffer);
9180 drop(buffer);
9181
9182 if let Some(new_position) = new_position {
9183 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9184 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9185 return;
9186 }
9187 }
9188
9189 nav_history.push(
9190 Some(NavigationData {
9191 cursor_anchor,
9192 cursor_position,
9193 scroll_anchor: scroll_state,
9194 scroll_top_row,
9195 }),
9196 cx,
9197 );
9198 }
9199 }
9200
9201 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9202 let buffer = self.buffer.read(cx).snapshot(cx);
9203 let mut selection = self.selections.first::<usize>(cx);
9204 selection.set_head(buffer.len(), SelectionGoal::None);
9205 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9206 s.select(vec![selection]);
9207 });
9208 }
9209
9210 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9211 let end = self.buffer.read(cx).read(cx).len();
9212 self.change_selections(None, window, cx, |s| {
9213 s.select_ranges(vec![0..end]);
9214 });
9215 }
9216
9217 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9218 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9219 let mut selections = self.selections.all::<Point>(cx);
9220 let max_point = display_map.buffer_snapshot.max_point();
9221 for selection in &mut selections {
9222 let rows = selection.spanned_rows(true, &display_map);
9223 selection.start = Point::new(rows.start.0, 0);
9224 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9225 selection.reversed = false;
9226 }
9227 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9228 s.select(selections);
9229 });
9230 }
9231
9232 pub fn split_selection_into_lines(
9233 &mut self,
9234 _: &SplitSelectionIntoLines,
9235 window: &mut Window,
9236 cx: &mut Context<Self>,
9237 ) {
9238 let selections = self
9239 .selections
9240 .all::<Point>(cx)
9241 .into_iter()
9242 .map(|selection| selection.start..selection.end)
9243 .collect::<Vec<_>>();
9244 self.unfold_ranges(&selections, true, true, cx);
9245
9246 let mut new_selection_ranges = Vec::new();
9247 {
9248 let buffer = self.buffer.read(cx).read(cx);
9249 for selection in selections {
9250 for row in selection.start.row..selection.end.row {
9251 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9252 new_selection_ranges.push(cursor..cursor);
9253 }
9254
9255 let is_multiline_selection = selection.start.row != selection.end.row;
9256 // Don't insert last one if it's a multi-line selection ending at the start of a line,
9257 // so this action feels more ergonomic when paired with other selection operations
9258 let should_skip_last = is_multiline_selection && selection.end.column == 0;
9259 if !should_skip_last {
9260 new_selection_ranges.push(selection.end..selection.end);
9261 }
9262 }
9263 }
9264 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9265 s.select_ranges(new_selection_ranges);
9266 });
9267 }
9268
9269 pub fn add_selection_above(
9270 &mut self,
9271 _: &AddSelectionAbove,
9272 window: &mut Window,
9273 cx: &mut Context<Self>,
9274 ) {
9275 self.add_selection(true, window, cx);
9276 }
9277
9278 pub fn add_selection_below(
9279 &mut self,
9280 _: &AddSelectionBelow,
9281 window: &mut Window,
9282 cx: &mut Context<Self>,
9283 ) {
9284 self.add_selection(false, window, cx);
9285 }
9286
9287 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9288 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9289 let mut selections = self.selections.all::<Point>(cx);
9290 let text_layout_details = self.text_layout_details(window);
9291 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9292 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9293 let range = oldest_selection.display_range(&display_map).sorted();
9294
9295 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9296 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9297 let positions = start_x.min(end_x)..start_x.max(end_x);
9298
9299 selections.clear();
9300 let mut stack = Vec::new();
9301 for row in range.start.row().0..=range.end.row().0 {
9302 if let Some(selection) = self.selections.build_columnar_selection(
9303 &display_map,
9304 DisplayRow(row),
9305 &positions,
9306 oldest_selection.reversed,
9307 &text_layout_details,
9308 ) {
9309 stack.push(selection.id);
9310 selections.push(selection);
9311 }
9312 }
9313
9314 if above {
9315 stack.reverse();
9316 }
9317
9318 AddSelectionsState { above, stack }
9319 });
9320
9321 let last_added_selection = *state.stack.last().unwrap();
9322 let mut new_selections = Vec::new();
9323 if above == state.above {
9324 let end_row = if above {
9325 DisplayRow(0)
9326 } else {
9327 display_map.max_point().row()
9328 };
9329
9330 'outer: for selection in selections {
9331 if selection.id == last_added_selection {
9332 let range = selection.display_range(&display_map).sorted();
9333 debug_assert_eq!(range.start.row(), range.end.row());
9334 let mut row = range.start.row();
9335 let positions =
9336 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9337 px(start)..px(end)
9338 } else {
9339 let start_x =
9340 display_map.x_for_display_point(range.start, &text_layout_details);
9341 let end_x =
9342 display_map.x_for_display_point(range.end, &text_layout_details);
9343 start_x.min(end_x)..start_x.max(end_x)
9344 };
9345
9346 while row != end_row {
9347 if above {
9348 row.0 -= 1;
9349 } else {
9350 row.0 += 1;
9351 }
9352
9353 if let Some(new_selection) = self.selections.build_columnar_selection(
9354 &display_map,
9355 row,
9356 &positions,
9357 selection.reversed,
9358 &text_layout_details,
9359 ) {
9360 state.stack.push(new_selection.id);
9361 if above {
9362 new_selections.push(new_selection);
9363 new_selections.push(selection);
9364 } else {
9365 new_selections.push(selection);
9366 new_selections.push(new_selection);
9367 }
9368
9369 continue 'outer;
9370 }
9371 }
9372 }
9373
9374 new_selections.push(selection);
9375 }
9376 } else {
9377 new_selections = selections;
9378 new_selections.retain(|s| s.id != last_added_selection);
9379 state.stack.pop();
9380 }
9381
9382 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9383 s.select(new_selections);
9384 });
9385 if state.stack.len() > 1 {
9386 self.add_selections_state = Some(state);
9387 }
9388 }
9389
9390 pub fn select_next_match_internal(
9391 &mut self,
9392 display_map: &DisplaySnapshot,
9393 replace_newest: bool,
9394 autoscroll: Option<Autoscroll>,
9395 window: &mut Window,
9396 cx: &mut Context<Self>,
9397 ) -> Result<()> {
9398 fn select_next_match_ranges(
9399 this: &mut Editor,
9400 range: Range<usize>,
9401 replace_newest: bool,
9402 auto_scroll: Option<Autoscroll>,
9403 window: &mut Window,
9404 cx: &mut Context<Editor>,
9405 ) {
9406 this.unfold_ranges(&[range.clone()], false, true, cx);
9407 this.change_selections(auto_scroll, window, cx, |s| {
9408 if replace_newest {
9409 s.delete(s.newest_anchor().id);
9410 }
9411 s.insert_range(range.clone());
9412 });
9413 }
9414
9415 let buffer = &display_map.buffer_snapshot;
9416 let mut selections = self.selections.all::<usize>(cx);
9417 if let Some(mut select_next_state) = self.select_next_state.take() {
9418 let query = &select_next_state.query;
9419 if !select_next_state.done {
9420 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9421 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9422 let mut next_selected_range = None;
9423
9424 let bytes_after_last_selection =
9425 buffer.bytes_in_range(last_selection.end..buffer.len());
9426 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9427 let query_matches = query
9428 .stream_find_iter(bytes_after_last_selection)
9429 .map(|result| (last_selection.end, result))
9430 .chain(
9431 query
9432 .stream_find_iter(bytes_before_first_selection)
9433 .map(|result| (0, result)),
9434 );
9435
9436 for (start_offset, query_match) in query_matches {
9437 let query_match = query_match.unwrap(); // can only fail due to I/O
9438 let offset_range =
9439 start_offset + query_match.start()..start_offset + query_match.end();
9440 let display_range = offset_range.start.to_display_point(display_map)
9441 ..offset_range.end.to_display_point(display_map);
9442
9443 if !select_next_state.wordwise
9444 || (!movement::is_inside_word(display_map, display_range.start)
9445 && !movement::is_inside_word(display_map, display_range.end))
9446 {
9447 // TODO: This is n^2, because we might check all the selections
9448 if !selections
9449 .iter()
9450 .any(|selection| selection.range().overlaps(&offset_range))
9451 {
9452 next_selected_range = Some(offset_range);
9453 break;
9454 }
9455 }
9456 }
9457
9458 if let Some(next_selected_range) = next_selected_range {
9459 select_next_match_ranges(
9460 self,
9461 next_selected_range,
9462 replace_newest,
9463 autoscroll,
9464 window,
9465 cx,
9466 );
9467 } else {
9468 select_next_state.done = true;
9469 }
9470 }
9471
9472 self.select_next_state = Some(select_next_state);
9473 } else {
9474 let mut only_carets = true;
9475 let mut same_text_selected = true;
9476 let mut selected_text = None;
9477
9478 let mut selections_iter = selections.iter().peekable();
9479 while let Some(selection) = selections_iter.next() {
9480 if selection.start != selection.end {
9481 only_carets = false;
9482 }
9483
9484 if same_text_selected {
9485 if selected_text.is_none() {
9486 selected_text =
9487 Some(buffer.text_for_range(selection.range()).collect::<String>());
9488 }
9489
9490 if let Some(next_selection) = selections_iter.peek() {
9491 if next_selection.range().len() == selection.range().len() {
9492 let next_selected_text = buffer
9493 .text_for_range(next_selection.range())
9494 .collect::<String>();
9495 if Some(next_selected_text) != selected_text {
9496 same_text_selected = false;
9497 selected_text = None;
9498 }
9499 } else {
9500 same_text_selected = false;
9501 selected_text = None;
9502 }
9503 }
9504 }
9505 }
9506
9507 if only_carets {
9508 for selection in &mut selections {
9509 let word_range = movement::surrounding_word(
9510 display_map,
9511 selection.start.to_display_point(display_map),
9512 );
9513 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9514 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9515 selection.goal = SelectionGoal::None;
9516 selection.reversed = false;
9517 select_next_match_ranges(
9518 self,
9519 selection.start..selection.end,
9520 replace_newest,
9521 autoscroll,
9522 window,
9523 cx,
9524 );
9525 }
9526
9527 if selections.len() == 1 {
9528 let selection = selections
9529 .last()
9530 .expect("ensured that there's only one selection");
9531 let query = buffer
9532 .text_for_range(selection.start..selection.end)
9533 .collect::<String>();
9534 let is_empty = query.is_empty();
9535 let select_state = SelectNextState {
9536 query: AhoCorasick::new(&[query])?,
9537 wordwise: true,
9538 done: is_empty,
9539 };
9540 self.select_next_state = Some(select_state);
9541 } else {
9542 self.select_next_state = None;
9543 }
9544 } else if let Some(selected_text) = selected_text {
9545 self.select_next_state = Some(SelectNextState {
9546 query: AhoCorasick::new(&[selected_text])?,
9547 wordwise: false,
9548 done: false,
9549 });
9550 self.select_next_match_internal(
9551 display_map,
9552 replace_newest,
9553 autoscroll,
9554 window,
9555 cx,
9556 )?;
9557 }
9558 }
9559 Ok(())
9560 }
9561
9562 pub fn select_all_matches(
9563 &mut self,
9564 _action: &SelectAllMatches,
9565 window: &mut Window,
9566 cx: &mut Context<Self>,
9567 ) -> Result<()> {
9568 self.push_to_selection_history();
9569 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9570
9571 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9572 let Some(select_next_state) = self.select_next_state.as_mut() else {
9573 return Ok(());
9574 };
9575 if select_next_state.done {
9576 return Ok(());
9577 }
9578
9579 let mut new_selections = self.selections.all::<usize>(cx);
9580
9581 let buffer = &display_map.buffer_snapshot;
9582 let query_matches = select_next_state
9583 .query
9584 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9585
9586 for query_match in query_matches {
9587 let query_match = query_match.unwrap(); // can only fail due to I/O
9588 let offset_range = query_match.start()..query_match.end();
9589 let display_range = offset_range.start.to_display_point(&display_map)
9590 ..offset_range.end.to_display_point(&display_map);
9591
9592 if !select_next_state.wordwise
9593 || (!movement::is_inside_word(&display_map, display_range.start)
9594 && !movement::is_inside_word(&display_map, display_range.end))
9595 {
9596 self.selections.change_with(cx, |selections| {
9597 new_selections.push(Selection {
9598 id: selections.new_selection_id(),
9599 start: offset_range.start,
9600 end: offset_range.end,
9601 reversed: false,
9602 goal: SelectionGoal::None,
9603 });
9604 });
9605 }
9606 }
9607
9608 new_selections.sort_by_key(|selection| selection.start);
9609 let mut ix = 0;
9610 while ix + 1 < new_selections.len() {
9611 let current_selection = &new_selections[ix];
9612 let next_selection = &new_selections[ix + 1];
9613 if current_selection.range().overlaps(&next_selection.range()) {
9614 if current_selection.id < next_selection.id {
9615 new_selections.remove(ix + 1);
9616 } else {
9617 new_selections.remove(ix);
9618 }
9619 } else {
9620 ix += 1;
9621 }
9622 }
9623
9624 let reversed = self.selections.oldest::<usize>(cx).reversed;
9625
9626 for selection in new_selections.iter_mut() {
9627 selection.reversed = reversed;
9628 }
9629
9630 select_next_state.done = true;
9631 self.unfold_ranges(
9632 &new_selections
9633 .iter()
9634 .map(|selection| selection.range())
9635 .collect::<Vec<_>>(),
9636 false,
9637 false,
9638 cx,
9639 );
9640 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9641 selections.select(new_selections)
9642 });
9643
9644 Ok(())
9645 }
9646
9647 pub fn select_next(
9648 &mut self,
9649 action: &SelectNext,
9650 window: &mut Window,
9651 cx: &mut Context<Self>,
9652 ) -> Result<()> {
9653 self.push_to_selection_history();
9654 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9655 self.select_next_match_internal(
9656 &display_map,
9657 action.replace_newest,
9658 Some(Autoscroll::newest()),
9659 window,
9660 cx,
9661 )?;
9662 Ok(())
9663 }
9664
9665 pub fn select_previous(
9666 &mut self,
9667 action: &SelectPrevious,
9668 window: &mut Window,
9669 cx: &mut Context<Self>,
9670 ) -> Result<()> {
9671 self.push_to_selection_history();
9672 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9673 let buffer = &display_map.buffer_snapshot;
9674 let mut selections = self.selections.all::<usize>(cx);
9675 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9676 let query = &select_prev_state.query;
9677 if !select_prev_state.done {
9678 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9679 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9680 let mut next_selected_range = None;
9681 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9682 let bytes_before_last_selection =
9683 buffer.reversed_bytes_in_range(0..last_selection.start);
9684 let bytes_after_first_selection =
9685 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9686 let query_matches = query
9687 .stream_find_iter(bytes_before_last_selection)
9688 .map(|result| (last_selection.start, result))
9689 .chain(
9690 query
9691 .stream_find_iter(bytes_after_first_selection)
9692 .map(|result| (buffer.len(), result)),
9693 );
9694 for (end_offset, query_match) in query_matches {
9695 let query_match = query_match.unwrap(); // can only fail due to I/O
9696 let offset_range =
9697 end_offset - query_match.end()..end_offset - query_match.start();
9698 let display_range = offset_range.start.to_display_point(&display_map)
9699 ..offset_range.end.to_display_point(&display_map);
9700
9701 if !select_prev_state.wordwise
9702 || (!movement::is_inside_word(&display_map, display_range.start)
9703 && !movement::is_inside_word(&display_map, display_range.end))
9704 {
9705 next_selected_range = Some(offset_range);
9706 break;
9707 }
9708 }
9709
9710 if let Some(next_selected_range) = next_selected_range {
9711 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9712 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9713 if action.replace_newest {
9714 s.delete(s.newest_anchor().id);
9715 }
9716 s.insert_range(next_selected_range);
9717 });
9718 } else {
9719 select_prev_state.done = true;
9720 }
9721 }
9722
9723 self.select_prev_state = Some(select_prev_state);
9724 } else {
9725 let mut only_carets = true;
9726 let mut same_text_selected = true;
9727 let mut selected_text = None;
9728
9729 let mut selections_iter = selections.iter().peekable();
9730 while let Some(selection) = selections_iter.next() {
9731 if selection.start != selection.end {
9732 only_carets = false;
9733 }
9734
9735 if same_text_selected {
9736 if selected_text.is_none() {
9737 selected_text =
9738 Some(buffer.text_for_range(selection.range()).collect::<String>());
9739 }
9740
9741 if let Some(next_selection) = selections_iter.peek() {
9742 if next_selection.range().len() == selection.range().len() {
9743 let next_selected_text = buffer
9744 .text_for_range(next_selection.range())
9745 .collect::<String>();
9746 if Some(next_selected_text) != selected_text {
9747 same_text_selected = false;
9748 selected_text = None;
9749 }
9750 } else {
9751 same_text_selected = false;
9752 selected_text = None;
9753 }
9754 }
9755 }
9756 }
9757
9758 if only_carets {
9759 for selection in &mut selections {
9760 let word_range = movement::surrounding_word(
9761 &display_map,
9762 selection.start.to_display_point(&display_map),
9763 );
9764 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9765 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9766 selection.goal = SelectionGoal::None;
9767 selection.reversed = false;
9768 }
9769 if selections.len() == 1 {
9770 let selection = selections
9771 .last()
9772 .expect("ensured that there's only one selection");
9773 let query = buffer
9774 .text_for_range(selection.start..selection.end)
9775 .collect::<String>();
9776 let is_empty = query.is_empty();
9777 let select_state = SelectNextState {
9778 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9779 wordwise: true,
9780 done: is_empty,
9781 };
9782 self.select_prev_state = Some(select_state);
9783 } else {
9784 self.select_prev_state = None;
9785 }
9786
9787 self.unfold_ranges(
9788 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9789 false,
9790 true,
9791 cx,
9792 );
9793 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9794 s.select(selections);
9795 });
9796 } else if let Some(selected_text) = selected_text {
9797 self.select_prev_state = Some(SelectNextState {
9798 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9799 wordwise: false,
9800 done: false,
9801 });
9802 self.select_previous(action, window, cx)?;
9803 }
9804 }
9805 Ok(())
9806 }
9807
9808 pub fn toggle_comments(
9809 &mut self,
9810 action: &ToggleComments,
9811 window: &mut Window,
9812 cx: &mut Context<Self>,
9813 ) {
9814 if self.read_only(cx) {
9815 return;
9816 }
9817 let text_layout_details = &self.text_layout_details(window);
9818 self.transact(window, cx, |this, window, cx| {
9819 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9820 let mut edits = Vec::new();
9821 let mut selection_edit_ranges = Vec::new();
9822 let mut last_toggled_row = None;
9823 let snapshot = this.buffer.read(cx).read(cx);
9824 let empty_str: Arc<str> = Arc::default();
9825 let mut suffixes_inserted = Vec::new();
9826 let ignore_indent = action.ignore_indent;
9827
9828 fn comment_prefix_range(
9829 snapshot: &MultiBufferSnapshot,
9830 row: MultiBufferRow,
9831 comment_prefix: &str,
9832 comment_prefix_whitespace: &str,
9833 ignore_indent: bool,
9834 ) -> Range<Point> {
9835 let indent_size = if ignore_indent {
9836 0
9837 } else {
9838 snapshot.indent_size_for_line(row).len
9839 };
9840
9841 let start = Point::new(row.0, indent_size);
9842
9843 let mut line_bytes = snapshot
9844 .bytes_in_range(start..snapshot.max_point())
9845 .flatten()
9846 .copied();
9847
9848 // If this line currently begins with the line comment prefix, then record
9849 // the range containing the prefix.
9850 if line_bytes
9851 .by_ref()
9852 .take(comment_prefix.len())
9853 .eq(comment_prefix.bytes())
9854 {
9855 // Include any whitespace that matches the comment prefix.
9856 let matching_whitespace_len = line_bytes
9857 .zip(comment_prefix_whitespace.bytes())
9858 .take_while(|(a, b)| a == b)
9859 .count() as u32;
9860 let end = Point::new(
9861 start.row,
9862 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9863 );
9864 start..end
9865 } else {
9866 start..start
9867 }
9868 }
9869
9870 fn comment_suffix_range(
9871 snapshot: &MultiBufferSnapshot,
9872 row: MultiBufferRow,
9873 comment_suffix: &str,
9874 comment_suffix_has_leading_space: bool,
9875 ) -> Range<Point> {
9876 let end = Point::new(row.0, snapshot.line_len(row));
9877 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9878
9879 let mut line_end_bytes = snapshot
9880 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9881 .flatten()
9882 .copied();
9883
9884 let leading_space_len = if suffix_start_column > 0
9885 && line_end_bytes.next() == Some(b' ')
9886 && comment_suffix_has_leading_space
9887 {
9888 1
9889 } else {
9890 0
9891 };
9892
9893 // If this line currently begins with the line comment prefix, then record
9894 // the range containing the prefix.
9895 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9896 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9897 start..end
9898 } else {
9899 end..end
9900 }
9901 }
9902
9903 // TODO: Handle selections that cross excerpts
9904 for selection in &mut selections {
9905 let start_column = snapshot
9906 .indent_size_for_line(MultiBufferRow(selection.start.row))
9907 .len;
9908 let language = if let Some(language) =
9909 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9910 {
9911 language
9912 } else {
9913 continue;
9914 };
9915
9916 selection_edit_ranges.clear();
9917
9918 // If multiple selections contain a given row, avoid processing that
9919 // row more than once.
9920 let mut start_row = MultiBufferRow(selection.start.row);
9921 if last_toggled_row == Some(start_row) {
9922 start_row = start_row.next_row();
9923 }
9924 let end_row =
9925 if selection.end.row > selection.start.row && selection.end.column == 0 {
9926 MultiBufferRow(selection.end.row - 1)
9927 } else {
9928 MultiBufferRow(selection.end.row)
9929 };
9930 last_toggled_row = Some(end_row);
9931
9932 if start_row > end_row {
9933 continue;
9934 }
9935
9936 // If the language has line comments, toggle those.
9937 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9938
9939 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9940 if ignore_indent {
9941 full_comment_prefixes = full_comment_prefixes
9942 .into_iter()
9943 .map(|s| Arc::from(s.trim_end()))
9944 .collect();
9945 }
9946
9947 if !full_comment_prefixes.is_empty() {
9948 let first_prefix = full_comment_prefixes
9949 .first()
9950 .expect("prefixes is non-empty");
9951 let prefix_trimmed_lengths = full_comment_prefixes
9952 .iter()
9953 .map(|p| p.trim_end_matches(' ').len())
9954 .collect::<SmallVec<[usize; 4]>>();
9955
9956 let mut all_selection_lines_are_comments = true;
9957
9958 for row in start_row.0..=end_row.0 {
9959 let row = MultiBufferRow(row);
9960 if start_row < end_row && snapshot.is_line_blank(row) {
9961 continue;
9962 }
9963
9964 let prefix_range = full_comment_prefixes
9965 .iter()
9966 .zip(prefix_trimmed_lengths.iter().copied())
9967 .map(|(prefix, trimmed_prefix_len)| {
9968 comment_prefix_range(
9969 snapshot.deref(),
9970 row,
9971 &prefix[..trimmed_prefix_len],
9972 &prefix[trimmed_prefix_len..],
9973 ignore_indent,
9974 )
9975 })
9976 .max_by_key(|range| range.end.column - range.start.column)
9977 .expect("prefixes is non-empty");
9978
9979 if prefix_range.is_empty() {
9980 all_selection_lines_are_comments = false;
9981 }
9982
9983 selection_edit_ranges.push(prefix_range);
9984 }
9985
9986 if all_selection_lines_are_comments {
9987 edits.extend(
9988 selection_edit_ranges
9989 .iter()
9990 .cloned()
9991 .map(|range| (range, empty_str.clone())),
9992 );
9993 } else {
9994 let min_column = selection_edit_ranges
9995 .iter()
9996 .map(|range| range.start.column)
9997 .min()
9998 .unwrap_or(0);
9999 edits.extend(selection_edit_ranges.iter().map(|range| {
10000 let position = Point::new(range.start.row, min_column);
10001 (position..position, first_prefix.clone())
10002 }));
10003 }
10004 } else if let Some((full_comment_prefix, comment_suffix)) =
10005 language.block_comment_delimiters()
10006 {
10007 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10008 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10009 let prefix_range = comment_prefix_range(
10010 snapshot.deref(),
10011 start_row,
10012 comment_prefix,
10013 comment_prefix_whitespace,
10014 ignore_indent,
10015 );
10016 let suffix_range = comment_suffix_range(
10017 snapshot.deref(),
10018 end_row,
10019 comment_suffix.trim_start_matches(' '),
10020 comment_suffix.starts_with(' '),
10021 );
10022
10023 if prefix_range.is_empty() || suffix_range.is_empty() {
10024 edits.push((
10025 prefix_range.start..prefix_range.start,
10026 full_comment_prefix.clone(),
10027 ));
10028 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10029 suffixes_inserted.push((end_row, comment_suffix.len()));
10030 } else {
10031 edits.push((prefix_range, empty_str.clone()));
10032 edits.push((suffix_range, empty_str.clone()));
10033 }
10034 } else {
10035 continue;
10036 }
10037 }
10038
10039 drop(snapshot);
10040 this.buffer.update(cx, |buffer, cx| {
10041 buffer.edit(edits, None, cx);
10042 });
10043
10044 // Adjust selections so that they end before any comment suffixes that
10045 // were inserted.
10046 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10047 let mut selections = this.selections.all::<Point>(cx);
10048 let snapshot = this.buffer.read(cx).read(cx);
10049 for selection in &mut selections {
10050 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10051 match row.cmp(&MultiBufferRow(selection.end.row)) {
10052 Ordering::Less => {
10053 suffixes_inserted.next();
10054 continue;
10055 }
10056 Ordering::Greater => break,
10057 Ordering::Equal => {
10058 if selection.end.column == snapshot.line_len(row) {
10059 if selection.is_empty() {
10060 selection.start.column -= suffix_len as u32;
10061 }
10062 selection.end.column -= suffix_len as u32;
10063 }
10064 break;
10065 }
10066 }
10067 }
10068 }
10069
10070 drop(snapshot);
10071 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10072 s.select(selections)
10073 });
10074
10075 let selections = this.selections.all::<Point>(cx);
10076 let selections_on_single_row = selections.windows(2).all(|selections| {
10077 selections[0].start.row == selections[1].start.row
10078 && selections[0].end.row == selections[1].end.row
10079 && selections[0].start.row == selections[0].end.row
10080 });
10081 let selections_selecting = selections
10082 .iter()
10083 .any(|selection| selection.start != selection.end);
10084 let advance_downwards = action.advance_downwards
10085 && selections_on_single_row
10086 && !selections_selecting
10087 && !matches!(this.mode, EditorMode::SingleLine { .. });
10088
10089 if advance_downwards {
10090 let snapshot = this.buffer.read(cx).snapshot(cx);
10091
10092 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10093 s.move_cursors_with(|display_snapshot, display_point, _| {
10094 let mut point = display_point.to_point(display_snapshot);
10095 point.row += 1;
10096 point = snapshot.clip_point(point, Bias::Left);
10097 let display_point = point.to_display_point(display_snapshot);
10098 let goal = SelectionGoal::HorizontalPosition(
10099 display_snapshot
10100 .x_for_display_point(display_point, text_layout_details)
10101 .into(),
10102 );
10103 (display_point, goal)
10104 })
10105 });
10106 }
10107 });
10108 }
10109
10110 pub fn select_enclosing_symbol(
10111 &mut self,
10112 _: &SelectEnclosingSymbol,
10113 window: &mut Window,
10114 cx: &mut Context<Self>,
10115 ) {
10116 let buffer = self.buffer.read(cx).snapshot(cx);
10117 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10118
10119 fn update_selection(
10120 selection: &Selection<usize>,
10121 buffer_snap: &MultiBufferSnapshot,
10122 ) -> Option<Selection<usize>> {
10123 let cursor = selection.head();
10124 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10125 for symbol in symbols.iter().rev() {
10126 let start = symbol.range.start.to_offset(buffer_snap);
10127 let end = symbol.range.end.to_offset(buffer_snap);
10128 let new_range = start..end;
10129 if start < selection.start || end > selection.end {
10130 return Some(Selection {
10131 id: selection.id,
10132 start: new_range.start,
10133 end: new_range.end,
10134 goal: SelectionGoal::None,
10135 reversed: selection.reversed,
10136 });
10137 }
10138 }
10139 None
10140 }
10141
10142 let mut selected_larger_symbol = false;
10143 let new_selections = old_selections
10144 .iter()
10145 .map(|selection| match update_selection(selection, &buffer) {
10146 Some(new_selection) => {
10147 if new_selection.range() != selection.range() {
10148 selected_larger_symbol = true;
10149 }
10150 new_selection
10151 }
10152 None => selection.clone(),
10153 })
10154 .collect::<Vec<_>>();
10155
10156 if selected_larger_symbol {
10157 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10158 s.select(new_selections);
10159 });
10160 }
10161 }
10162
10163 pub fn select_larger_syntax_node(
10164 &mut self,
10165 _: &SelectLargerSyntaxNode,
10166 window: &mut Window,
10167 cx: &mut Context<Self>,
10168 ) {
10169 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10170 let buffer = self.buffer.read(cx).snapshot(cx);
10171 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10172
10173 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10174 let mut selected_larger_node = false;
10175 let new_selections = old_selections
10176 .iter()
10177 .map(|selection| {
10178 let old_range = selection.start..selection.end;
10179 let mut new_range = old_range.clone();
10180 let mut new_node = None;
10181 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10182 {
10183 new_node = Some(node);
10184 new_range = containing_range;
10185 if !display_map.intersects_fold(new_range.start)
10186 && !display_map.intersects_fold(new_range.end)
10187 {
10188 break;
10189 }
10190 }
10191
10192 if let Some(node) = new_node {
10193 // Log the ancestor, to support using this action as a way to explore TreeSitter
10194 // nodes. Parent and grandparent are also logged because this operation will not
10195 // visit nodes that have the same range as their parent.
10196 log::info!("Node: {node:?}");
10197 let parent = node.parent();
10198 log::info!("Parent: {parent:?}");
10199 let grandparent = parent.and_then(|x| x.parent());
10200 log::info!("Grandparent: {grandparent:?}");
10201 }
10202
10203 selected_larger_node |= new_range != old_range;
10204 Selection {
10205 id: selection.id,
10206 start: new_range.start,
10207 end: new_range.end,
10208 goal: SelectionGoal::None,
10209 reversed: selection.reversed,
10210 }
10211 })
10212 .collect::<Vec<_>>();
10213
10214 if selected_larger_node {
10215 stack.push(old_selections);
10216 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10217 s.select(new_selections);
10218 });
10219 }
10220 self.select_larger_syntax_node_stack = stack;
10221 }
10222
10223 pub fn select_smaller_syntax_node(
10224 &mut self,
10225 _: &SelectSmallerSyntaxNode,
10226 window: &mut Window,
10227 cx: &mut Context<Self>,
10228 ) {
10229 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10230 if let Some(selections) = stack.pop() {
10231 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10232 s.select(selections.to_vec());
10233 });
10234 }
10235 self.select_larger_syntax_node_stack = stack;
10236 }
10237
10238 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10239 if !EditorSettings::get_global(cx).gutter.runnables {
10240 self.clear_tasks();
10241 return Task::ready(());
10242 }
10243 let project = self.project.as_ref().map(Entity::downgrade);
10244 cx.spawn_in(window, |this, mut cx| async move {
10245 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10246 let Some(project) = project.and_then(|p| p.upgrade()) else {
10247 return;
10248 };
10249 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10250 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10251 }) else {
10252 return;
10253 };
10254
10255 let hide_runnables = project
10256 .update(&mut cx, |project, cx| {
10257 // Do not display any test indicators in non-dev server remote projects.
10258 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10259 })
10260 .unwrap_or(true);
10261 if hide_runnables {
10262 return;
10263 }
10264 let new_rows =
10265 cx.background_spawn({
10266 let snapshot = display_snapshot.clone();
10267 async move {
10268 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10269 }
10270 })
10271 .await;
10272
10273 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10274 this.update(&mut cx, |this, _| {
10275 this.clear_tasks();
10276 for (key, value) in rows {
10277 this.insert_tasks(key, value);
10278 }
10279 })
10280 .ok();
10281 })
10282 }
10283 fn fetch_runnable_ranges(
10284 snapshot: &DisplaySnapshot,
10285 range: Range<Anchor>,
10286 ) -> Vec<language::RunnableRange> {
10287 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10288 }
10289
10290 fn runnable_rows(
10291 project: Entity<Project>,
10292 snapshot: DisplaySnapshot,
10293 runnable_ranges: Vec<RunnableRange>,
10294 mut cx: AsyncWindowContext,
10295 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10296 runnable_ranges
10297 .into_iter()
10298 .filter_map(|mut runnable| {
10299 let tasks = cx
10300 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10301 .ok()?;
10302 if tasks.is_empty() {
10303 return None;
10304 }
10305
10306 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10307
10308 let row = snapshot
10309 .buffer_snapshot
10310 .buffer_line_for_row(MultiBufferRow(point.row))?
10311 .1
10312 .start
10313 .row;
10314
10315 let context_range =
10316 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10317 Some((
10318 (runnable.buffer_id, row),
10319 RunnableTasks {
10320 templates: tasks,
10321 offset: MultiBufferOffset(runnable.run_range.start),
10322 context_range,
10323 column: point.column,
10324 extra_variables: runnable.extra_captures,
10325 },
10326 ))
10327 })
10328 .collect()
10329 }
10330
10331 fn templates_with_tags(
10332 project: &Entity<Project>,
10333 runnable: &mut Runnable,
10334 cx: &mut App,
10335 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10336 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10337 let (worktree_id, file) = project
10338 .buffer_for_id(runnable.buffer, cx)
10339 .and_then(|buffer| buffer.read(cx).file())
10340 .map(|file| (file.worktree_id(cx), file.clone()))
10341 .unzip();
10342
10343 (
10344 project.task_store().read(cx).task_inventory().cloned(),
10345 worktree_id,
10346 file,
10347 )
10348 });
10349
10350 let tags = mem::take(&mut runnable.tags);
10351 let mut tags: Vec<_> = tags
10352 .into_iter()
10353 .flat_map(|tag| {
10354 let tag = tag.0.clone();
10355 inventory
10356 .as_ref()
10357 .into_iter()
10358 .flat_map(|inventory| {
10359 inventory.read(cx).list_tasks(
10360 file.clone(),
10361 Some(runnable.language.clone()),
10362 worktree_id,
10363 cx,
10364 )
10365 })
10366 .filter(move |(_, template)| {
10367 template.tags.iter().any(|source_tag| source_tag == &tag)
10368 })
10369 })
10370 .sorted_by_key(|(kind, _)| kind.to_owned())
10371 .collect();
10372 if let Some((leading_tag_source, _)) = tags.first() {
10373 // Strongest source wins; if we have worktree tag binding, prefer that to
10374 // global and language bindings;
10375 // if we have a global binding, prefer that to language binding.
10376 let first_mismatch = tags
10377 .iter()
10378 .position(|(tag_source, _)| tag_source != leading_tag_source);
10379 if let Some(index) = first_mismatch {
10380 tags.truncate(index);
10381 }
10382 }
10383
10384 tags
10385 }
10386
10387 pub fn move_to_enclosing_bracket(
10388 &mut self,
10389 _: &MoveToEnclosingBracket,
10390 window: &mut Window,
10391 cx: &mut Context<Self>,
10392 ) {
10393 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10394 s.move_offsets_with(|snapshot, selection| {
10395 let Some(enclosing_bracket_ranges) =
10396 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10397 else {
10398 return;
10399 };
10400
10401 let mut best_length = usize::MAX;
10402 let mut best_inside = false;
10403 let mut best_in_bracket_range = false;
10404 let mut best_destination = None;
10405 for (open, close) in enclosing_bracket_ranges {
10406 let close = close.to_inclusive();
10407 let length = close.end() - open.start;
10408 let inside = selection.start >= open.end && selection.end <= *close.start();
10409 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10410 || close.contains(&selection.head());
10411
10412 // If best is next to a bracket and current isn't, skip
10413 if !in_bracket_range && best_in_bracket_range {
10414 continue;
10415 }
10416
10417 // Prefer smaller lengths unless best is inside and current isn't
10418 if length > best_length && (best_inside || !inside) {
10419 continue;
10420 }
10421
10422 best_length = length;
10423 best_inside = inside;
10424 best_in_bracket_range = in_bracket_range;
10425 best_destination = Some(
10426 if close.contains(&selection.start) && close.contains(&selection.end) {
10427 if inside {
10428 open.end
10429 } else {
10430 open.start
10431 }
10432 } else if inside {
10433 *close.start()
10434 } else {
10435 *close.end()
10436 },
10437 );
10438 }
10439
10440 if let Some(destination) = best_destination {
10441 selection.collapse_to(destination, SelectionGoal::None);
10442 }
10443 })
10444 });
10445 }
10446
10447 pub fn undo_selection(
10448 &mut self,
10449 _: &UndoSelection,
10450 window: &mut Window,
10451 cx: &mut Context<Self>,
10452 ) {
10453 self.end_selection(window, cx);
10454 self.selection_history.mode = SelectionHistoryMode::Undoing;
10455 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10456 self.change_selections(None, window, cx, |s| {
10457 s.select_anchors(entry.selections.to_vec())
10458 });
10459 self.select_next_state = entry.select_next_state;
10460 self.select_prev_state = entry.select_prev_state;
10461 self.add_selections_state = entry.add_selections_state;
10462 self.request_autoscroll(Autoscroll::newest(), cx);
10463 }
10464 self.selection_history.mode = SelectionHistoryMode::Normal;
10465 }
10466
10467 pub fn redo_selection(
10468 &mut self,
10469 _: &RedoSelection,
10470 window: &mut Window,
10471 cx: &mut Context<Self>,
10472 ) {
10473 self.end_selection(window, cx);
10474 self.selection_history.mode = SelectionHistoryMode::Redoing;
10475 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10476 self.change_selections(None, window, cx, |s| {
10477 s.select_anchors(entry.selections.to_vec())
10478 });
10479 self.select_next_state = entry.select_next_state;
10480 self.select_prev_state = entry.select_prev_state;
10481 self.add_selections_state = entry.add_selections_state;
10482 self.request_autoscroll(Autoscroll::newest(), cx);
10483 }
10484 self.selection_history.mode = SelectionHistoryMode::Normal;
10485 }
10486
10487 pub fn expand_excerpts(
10488 &mut self,
10489 action: &ExpandExcerpts,
10490 _: &mut Window,
10491 cx: &mut Context<Self>,
10492 ) {
10493 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10494 }
10495
10496 pub fn expand_excerpts_down(
10497 &mut self,
10498 action: &ExpandExcerptsDown,
10499 _: &mut Window,
10500 cx: &mut Context<Self>,
10501 ) {
10502 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10503 }
10504
10505 pub fn expand_excerpts_up(
10506 &mut self,
10507 action: &ExpandExcerptsUp,
10508 _: &mut Window,
10509 cx: &mut Context<Self>,
10510 ) {
10511 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10512 }
10513
10514 pub fn expand_excerpts_for_direction(
10515 &mut self,
10516 lines: u32,
10517 direction: ExpandExcerptDirection,
10518
10519 cx: &mut Context<Self>,
10520 ) {
10521 let selections = self.selections.disjoint_anchors();
10522
10523 let lines = if lines == 0 {
10524 EditorSettings::get_global(cx).expand_excerpt_lines
10525 } else {
10526 lines
10527 };
10528
10529 self.buffer.update(cx, |buffer, cx| {
10530 let snapshot = buffer.snapshot(cx);
10531 let mut excerpt_ids = selections
10532 .iter()
10533 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10534 .collect::<Vec<_>>();
10535 excerpt_ids.sort();
10536 excerpt_ids.dedup();
10537 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10538 })
10539 }
10540
10541 pub fn expand_excerpt(
10542 &mut self,
10543 excerpt: ExcerptId,
10544 direction: ExpandExcerptDirection,
10545 cx: &mut Context<Self>,
10546 ) {
10547 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10548 self.buffer.update(cx, |buffer, cx| {
10549 buffer.expand_excerpts([excerpt], lines, direction, cx)
10550 })
10551 }
10552
10553 pub fn go_to_singleton_buffer_point(
10554 &mut self,
10555 point: Point,
10556 window: &mut Window,
10557 cx: &mut Context<Self>,
10558 ) {
10559 self.go_to_singleton_buffer_range(point..point, window, cx);
10560 }
10561
10562 pub fn go_to_singleton_buffer_range(
10563 &mut self,
10564 range: Range<Point>,
10565 window: &mut Window,
10566 cx: &mut Context<Self>,
10567 ) {
10568 let multibuffer = self.buffer().read(cx);
10569 let Some(buffer) = multibuffer.as_singleton() else {
10570 return;
10571 };
10572 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10573 return;
10574 };
10575 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10576 return;
10577 };
10578 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10579 s.select_anchor_ranges([start..end])
10580 });
10581 }
10582
10583 fn go_to_diagnostic(
10584 &mut self,
10585 _: &GoToDiagnostic,
10586 window: &mut Window,
10587 cx: &mut Context<Self>,
10588 ) {
10589 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10590 }
10591
10592 fn go_to_prev_diagnostic(
10593 &mut self,
10594 _: &GoToPrevDiagnostic,
10595 window: &mut Window,
10596 cx: &mut Context<Self>,
10597 ) {
10598 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10599 }
10600
10601 pub fn go_to_diagnostic_impl(
10602 &mut self,
10603 direction: Direction,
10604 window: &mut Window,
10605 cx: &mut Context<Self>,
10606 ) {
10607 let buffer = self.buffer.read(cx).snapshot(cx);
10608 let selection = self.selections.newest::<usize>(cx);
10609
10610 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10611 if direction == Direction::Next {
10612 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10613 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10614 return;
10615 };
10616 self.activate_diagnostics(
10617 buffer_id,
10618 popover.local_diagnostic.diagnostic.group_id,
10619 window,
10620 cx,
10621 );
10622 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10623 let primary_range_start = active_diagnostics.primary_range.start;
10624 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10625 let mut new_selection = s.newest_anchor().clone();
10626 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10627 s.select_anchors(vec![new_selection.clone()]);
10628 });
10629 self.refresh_inline_completion(false, true, window, cx);
10630 }
10631 return;
10632 }
10633 }
10634
10635 let active_group_id = self
10636 .active_diagnostics
10637 .as_ref()
10638 .map(|active_group| active_group.group_id);
10639 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10640 active_diagnostics
10641 .primary_range
10642 .to_offset(&buffer)
10643 .to_inclusive()
10644 });
10645 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10646 if active_primary_range.contains(&selection.head()) {
10647 *active_primary_range.start()
10648 } else {
10649 selection.head()
10650 }
10651 } else {
10652 selection.head()
10653 };
10654
10655 let snapshot = self.snapshot(window, cx);
10656 let primary_diagnostics_before = buffer
10657 .diagnostics_in_range::<usize>(0..search_start)
10658 .filter(|entry| entry.diagnostic.is_primary)
10659 .filter(|entry| entry.range.start != entry.range.end)
10660 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10661 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
10662 .collect::<Vec<_>>();
10663 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
10664 primary_diagnostics_before
10665 .iter()
10666 .position(|entry| entry.diagnostic.group_id == active_group_id)
10667 });
10668
10669 let primary_diagnostics_after = buffer
10670 .diagnostics_in_range::<usize>(search_start..buffer.len())
10671 .filter(|entry| entry.diagnostic.is_primary)
10672 .filter(|entry| entry.range.start != entry.range.end)
10673 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10674 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10675 .collect::<Vec<_>>();
10676 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
10677 primary_diagnostics_after
10678 .iter()
10679 .enumerate()
10680 .rev()
10681 .find_map(|(i, entry)| {
10682 if entry.diagnostic.group_id == active_group_id {
10683 Some(i)
10684 } else {
10685 None
10686 }
10687 })
10688 });
10689
10690 let next_primary_diagnostic = match direction {
10691 Direction::Prev => primary_diagnostics_before
10692 .iter()
10693 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
10694 .rev()
10695 .next(),
10696 Direction::Next => primary_diagnostics_after
10697 .iter()
10698 .skip(
10699 last_same_group_diagnostic_after
10700 .map(|index| index + 1)
10701 .unwrap_or(0),
10702 )
10703 .next(),
10704 };
10705
10706 // Cycle around to the start of the buffer, potentially moving back to the start of
10707 // the currently active diagnostic.
10708 let cycle_around = || match direction {
10709 Direction::Prev => primary_diagnostics_after
10710 .iter()
10711 .rev()
10712 .chain(primary_diagnostics_before.iter().rev())
10713 .next(),
10714 Direction::Next => primary_diagnostics_before
10715 .iter()
10716 .chain(primary_diagnostics_after.iter())
10717 .next(),
10718 };
10719
10720 if let Some((primary_range, group_id)) = next_primary_diagnostic
10721 .or_else(cycle_around)
10722 .map(|entry| (&entry.range, entry.diagnostic.group_id))
10723 {
10724 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10725 return;
10726 };
10727 self.activate_diagnostics(buffer_id, group_id, window, cx);
10728 if self.active_diagnostics.is_some() {
10729 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10730 s.select(vec![Selection {
10731 id: selection.id,
10732 start: primary_range.start,
10733 end: primary_range.start,
10734 reversed: false,
10735 goal: SelectionGoal::None,
10736 }]);
10737 });
10738 self.refresh_inline_completion(false, true, window, cx);
10739 }
10740 }
10741 }
10742
10743 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10744 let snapshot = self.snapshot(window, cx);
10745 let selection = self.selections.newest::<Point>(cx);
10746 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10747 }
10748
10749 fn go_to_hunk_after_position(
10750 &mut self,
10751 snapshot: &EditorSnapshot,
10752 position: Point,
10753 window: &mut Window,
10754 cx: &mut Context<Editor>,
10755 ) -> Option<MultiBufferDiffHunk> {
10756 let mut hunk = snapshot
10757 .buffer_snapshot
10758 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10759 .find(|hunk| hunk.row_range.start.0 > position.row);
10760 if hunk.is_none() {
10761 hunk = snapshot
10762 .buffer_snapshot
10763 .diff_hunks_in_range(Point::zero()..position)
10764 .find(|hunk| hunk.row_range.end.0 < position.row)
10765 }
10766 if let Some(hunk) = &hunk {
10767 let destination = Point::new(hunk.row_range.start.0, 0);
10768 self.unfold_ranges(&[destination..destination], false, false, cx);
10769 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10770 s.select_ranges(vec![destination..destination]);
10771 });
10772 }
10773
10774 hunk
10775 }
10776
10777 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10778 let snapshot = self.snapshot(window, cx);
10779 let selection = self.selections.newest::<Point>(cx);
10780 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10781 }
10782
10783 fn go_to_hunk_before_position(
10784 &mut self,
10785 snapshot: &EditorSnapshot,
10786 position: Point,
10787 window: &mut Window,
10788 cx: &mut Context<Editor>,
10789 ) -> Option<MultiBufferDiffHunk> {
10790 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10791 if hunk.is_none() {
10792 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10793 }
10794 if let Some(hunk) = &hunk {
10795 let destination = Point::new(hunk.row_range.start.0, 0);
10796 self.unfold_ranges(&[destination..destination], false, false, cx);
10797 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10798 s.select_ranges(vec![destination..destination]);
10799 });
10800 }
10801
10802 hunk
10803 }
10804
10805 pub fn go_to_definition(
10806 &mut self,
10807 _: &GoToDefinition,
10808 window: &mut Window,
10809 cx: &mut Context<Self>,
10810 ) -> Task<Result<Navigated>> {
10811 let definition =
10812 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10813 cx.spawn_in(window, |editor, mut cx| async move {
10814 if definition.await? == Navigated::Yes {
10815 return Ok(Navigated::Yes);
10816 }
10817 match editor.update_in(&mut cx, |editor, window, cx| {
10818 editor.find_all_references(&FindAllReferences, window, cx)
10819 })? {
10820 Some(references) => references.await,
10821 None => Ok(Navigated::No),
10822 }
10823 })
10824 }
10825
10826 pub fn go_to_declaration(
10827 &mut self,
10828 _: &GoToDeclaration,
10829 window: &mut Window,
10830 cx: &mut Context<Self>,
10831 ) -> Task<Result<Navigated>> {
10832 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10833 }
10834
10835 pub fn go_to_declaration_split(
10836 &mut self,
10837 _: &GoToDeclaration,
10838 window: &mut Window,
10839 cx: &mut Context<Self>,
10840 ) -> Task<Result<Navigated>> {
10841 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10842 }
10843
10844 pub fn go_to_implementation(
10845 &mut self,
10846 _: &GoToImplementation,
10847 window: &mut Window,
10848 cx: &mut Context<Self>,
10849 ) -> Task<Result<Navigated>> {
10850 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10851 }
10852
10853 pub fn go_to_implementation_split(
10854 &mut self,
10855 _: &GoToImplementationSplit,
10856 window: &mut Window,
10857 cx: &mut Context<Self>,
10858 ) -> Task<Result<Navigated>> {
10859 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10860 }
10861
10862 pub fn go_to_type_definition(
10863 &mut self,
10864 _: &GoToTypeDefinition,
10865 window: &mut Window,
10866 cx: &mut Context<Self>,
10867 ) -> Task<Result<Navigated>> {
10868 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10869 }
10870
10871 pub fn go_to_definition_split(
10872 &mut self,
10873 _: &GoToDefinitionSplit,
10874 window: &mut Window,
10875 cx: &mut Context<Self>,
10876 ) -> Task<Result<Navigated>> {
10877 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10878 }
10879
10880 pub fn go_to_type_definition_split(
10881 &mut self,
10882 _: &GoToTypeDefinitionSplit,
10883 window: &mut Window,
10884 cx: &mut Context<Self>,
10885 ) -> Task<Result<Navigated>> {
10886 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10887 }
10888
10889 fn go_to_definition_of_kind(
10890 &mut self,
10891 kind: GotoDefinitionKind,
10892 split: bool,
10893 window: &mut Window,
10894 cx: &mut Context<Self>,
10895 ) -> Task<Result<Navigated>> {
10896 let Some(provider) = self.semantics_provider.clone() else {
10897 return Task::ready(Ok(Navigated::No));
10898 };
10899 let head = self.selections.newest::<usize>(cx).head();
10900 let buffer = self.buffer.read(cx);
10901 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10902 text_anchor
10903 } else {
10904 return Task::ready(Ok(Navigated::No));
10905 };
10906
10907 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10908 return Task::ready(Ok(Navigated::No));
10909 };
10910
10911 cx.spawn_in(window, |editor, mut cx| async move {
10912 let definitions = definitions.await?;
10913 let navigated = editor
10914 .update_in(&mut cx, |editor, window, cx| {
10915 editor.navigate_to_hover_links(
10916 Some(kind),
10917 definitions
10918 .into_iter()
10919 .filter(|location| {
10920 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10921 })
10922 .map(HoverLink::Text)
10923 .collect::<Vec<_>>(),
10924 split,
10925 window,
10926 cx,
10927 )
10928 })?
10929 .await?;
10930 anyhow::Ok(navigated)
10931 })
10932 }
10933
10934 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10935 let selection = self.selections.newest_anchor();
10936 let head = selection.head();
10937 let tail = selection.tail();
10938
10939 let Some((buffer, start_position)) =
10940 self.buffer.read(cx).text_anchor_for_position(head, cx)
10941 else {
10942 return;
10943 };
10944
10945 let end_position = if head != tail {
10946 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10947 return;
10948 };
10949 Some(pos)
10950 } else {
10951 None
10952 };
10953
10954 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10955 let url = if let Some(end_pos) = end_position {
10956 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10957 } else {
10958 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10959 };
10960
10961 if let Some(url) = url {
10962 editor.update(&mut cx, |_, cx| {
10963 cx.open_url(&url);
10964 })
10965 } else {
10966 Ok(())
10967 }
10968 });
10969
10970 url_finder.detach();
10971 }
10972
10973 pub fn open_selected_filename(
10974 &mut self,
10975 _: &OpenSelectedFilename,
10976 window: &mut Window,
10977 cx: &mut Context<Self>,
10978 ) {
10979 let Some(workspace) = self.workspace() else {
10980 return;
10981 };
10982
10983 let position = self.selections.newest_anchor().head();
10984
10985 let Some((buffer, buffer_position)) =
10986 self.buffer.read(cx).text_anchor_for_position(position, cx)
10987 else {
10988 return;
10989 };
10990
10991 let project = self.project.clone();
10992
10993 cx.spawn_in(window, |_, mut cx| async move {
10994 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10995
10996 if let Some((_, path)) = result {
10997 workspace
10998 .update_in(&mut cx, |workspace, window, cx| {
10999 workspace.open_resolved_path(path, window, cx)
11000 })?
11001 .await?;
11002 }
11003 anyhow::Ok(())
11004 })
11005 .detach();
11006 }
11007
11008 pub(crate) fn navigate_to_hover_links(
11009 &mut self,
11010 kind: Option<GotoDefinitionKind>,
11011 mut definitions: Vec<HoverLink>,
11012 split: bool,
11013 window: &mut Window,
11014 cx: &mut Context<Editor>,
11015 ) -> Task<Result<Navigated>> {
11016 // If there is one definition, just open it directly
11017 if definitions.len() == 1 {
11018 let definition = definitions.pop().unwrap();
11019
11020 enum TargetTaskResult {
11021 Location(Option<Location>),
11022 AlreadyNavigated,
11023 }
11024
11025 let target_task = match definition {
11026 HoverLink::Text(link) => {
11027 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11028 }
11029 HoverLink::InlayHint(lsp_location, server_id) => {
11030 let computation =
11031 self.compute_target_location(lsp_location, server_id, window, cx);
11032 cx.background_spawn(async move {
11033 let location = computation.await?;
11034 Ok(TargetTaskResult::Location(location))
11035 })
11036 }
11037 HoverLink::Url(url) => {
11038 cx.open_url(&url);
11039 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11040 }
11041 HoverLink::File(path) => {
11042 if let Some(workspace) = self.workspace() {
11043 cx.spawn_in(window, |_, mut cx| async move {
11044 workspace
11045 .update_in(&mut cx, |workspace, window, cx| {
11046 workspace.open_resolved_path(path, window, cx)
11047 })?
11048 .await
11049 .map(|_| TargetTaskResult::AlreadyNavigated)
11050 })
11051 } else {
11052 Task::ready(Ok(TargetTaskResult::Location(None)))
11053 }
11054 }
11055 };
11056 cx.spawn_in(window, |editor, mut cx| async move {
11057 let target = match target_task.await.context("target resolution task")? {
11058 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11059 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11060 TargetTaskResult::Location(Some(target)) => target,
11061 };
11062
11063 editor.update_in(&mut cx, |editor, window, cx| {
11064 let Some(workspace) = editor.workspace() else {
11065 return Navigated::No;
11066 };
11067 let pane = workspace.read(cx).active_pane().clone();
11068
11069 let range = target.range.to_point(target.buffer.read(cx));
11070 let range = editor.range_for_match(&range);
11071 let range = collapse_multiline_range(range);
11072
11073 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
11074 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11075 } else {
11076 window.defer(cx, move |window, cx| {
11077 let target_editor: Entity<Self> =
11078 workspace.update(cx, |workspace, cx| {
11079 let pane = if split {
11080 workspace.adjacent_pane(window, cx)
11081 } else {
11082 workspace.active_pane().clone()
11083 };
11084
11085 workspace.open_project_item(
11086 pane,
11087 target.buffer.clone(),
11088 true,
11089 true,
11090 window,
11091 cx,
11092 )
11093 });
11094 target_editor.update(cx, |target_editor, cx| {
11095 // When selecting a definition in a different buffer, disable the nav history
11096 // to avoid creating a history entry at the previous cursor location.
11097 pane.update(cx, |pane, _| pane.disable_history());
11098 target_editor.go_to_singleton_buffer_range(range, window, cx);
11099 pane.update(cx, |pane, _| pane.enable_history());
11100 });
11101 });
11102 }
11103 Navigated::Yes
11104 })
11105 })
11106 } else if !definitions.is_empty() {
11107 cx.spawn_in(window, |editor, mut cx| async move {
11108 let (title, location_tasks, workspace) = editor
11109 .update_in(&mut cx, |editor, window, cx| {
11110 let tab_kind = match kind {
11111 Some(GotoDefinitionKind::Implementation) => "Implementations",
11112 _ => "Definitions",
11113 };
11114 let title = definitions
11115 .iter()
11116 .find_map(|definition| match definition {
11117 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11118 let buffer = origin.buffer.read(cx);
11119 format!(
11120 "{} for {}",
11121 tab_kind,
11122 buffer
11123 .text_for_range(origin.range.clone())
11124 .collect::<String>()
11125 )
11126 }),
11127 HoverLink::InlayHint(_, _) => None,
11128 HoverLink::Url(_) => None,
11129 HoverLink::File(_) => None,
11130 })
11131 .unwrap_or(tab_kind.to_string());
11132 let location_tasks = definitions
11133 .into_iter()
11134 .map(|definition| match definition {
11135 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11136 HoverLink::InlayHint(lsp_location, server_id) => editor
11137 .compute_target_location(lsp_location, server_id, window, cx),
11138 HoverLink::Url(_) => Task::ready(Ok(None)),
11139 HoverLink::File(_) => Task::ready(Ok(None)),
11140 })
11141 .collect::<Vec<_>>();
11142 (title, location_tasks, editor.workspace().clone())
11143 })
11144 .context("location tasks preparation")?;
11145
11146 let locations = future::join_all(location_tasks)
11147 .await
11148 .into_iter()
11149 .filter_map(|location| location.transpose())
11150 .collect::<Result<_>>()
11151 .context("location tasks")?;
11152
11153 let Some(workspace) = workspace else {
11154 return Ok(Navigated::No);
11155 };
11156 let opened = workspace
11157 .update_in(&mut cx, |workspace, window, cx| {
11158 Self::open_locations_in_multibuffer(
11159 workspace,
11160 locations,
11161 title,
11162 split,
11163 MultibufferSelectionMode::First,
11164 window,
11165 cx,
11166 )
11167 })
11168 .ok();
11169
11170 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11171 })
11172 } else {
11173 Task::ready(Ok(Navigated::No))
11174 }
11175 }
11176
11177 fn compute_target_location(
11178 &self,
11179 lsp_location: lsp::Location,
11180 server_id: LanguageServerId,
11181 window: &mut Window,
11182 cx: &mut Context<Self>,
11183 ) -> Task<anyhow::Result<Option<Location>>> {
11184 let Some(project) = self.project.clone() else {
11185 return Task::ready(Ok(None));
11186 };
11187
11188 cx.spawn_in(window, move |editor, mut cx| async move {
11189 let location_task = editor.update(&mut cx, |_, cx| {
11190 project.update(cx, |project, cx| {
11191 let language_server_name = project
11192 .language_server_statuses(cx)
11193 .find(|(id, _)| server_id == *id)
11194 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11195 language_server_name.map(|language_server_name| {
11196 project.open_local_buffer_via_lsp(
11197 lsp_location.uri.clone(),
11198 server_id,
11199 language_server_name,
11200 cx,
11201 )
11202 })
11203 })
11204 })?;
11205 let location = match location_task {
11206 Some(task) => Some({
11207 let target_buffer_handle = task.await.context("open local buffer")?;
11208 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11209 let target_start = target_buffer
11210 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11211 let target_end = target_buffer
11212 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11213 target_buffer.anchor_after(target_start)
11214 ..target_buffer.anchor_before(target_end)
11215 })?;
11216 Location {
11217 buffer: target_buffer_handle,
11218 range,
11219 }
11220 }),
11221 None => None,
11222 };
11223 Ok(location)
11224 })
11225 }
11226
11227 pub fn find_all_references(
11228 &mut self,
11229 _: &FindAllReferences,
11230 window: &mut Window,
11231 cx: &mut Context<Self>,
11232 ) -> Option<Task<Result<Navigated>>> {
11233 let selection = self.selections.newest::<usize>(cx);
11234 let multi_buffer = self.buffer.read(cx);
11235 let head = selection.head();
11236
11237 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11238 let head_anchor = multi_buffer_snapshot.anchor_at(
11239 head,
11240 if head < selection.tail() {
11241 Bias::Right
11242 } else {
11243 Bias::Left
11244 },
11245 );
11246
11247 match self
11248 .find_all_references_task_sources
11249 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11250 {
11251 Ok(_) => {
11252 log::info!(
11253 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11254 );
11255 return None;
11256 }
11257 Err(i) => {
11258 self.find_all_references_task_sources.insert(i, head_anchor);
11259 }
11260 }
11261
11262 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11263 let workspace = self.workspace()?;
11264 let project = workspace.read(cx).project().clone();
11265 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11266 Some(cx.spawn_in(window, |editor, mut cx| async move {
11267 let _cleanup = defer({
11268 let mut cx = cx.clone();
11269 move || {
11270 let _ = editor.update(&mut cx, |editor, _| {
11271 if let Ok(i) =
11272 editor
11273 .find_all_references_task_sources
11274 .binary_search_by(|anchor| {
11275 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11276 })
11277 {
11278 editor.find_all_references_task_sources.remove(i);
11279 }
11280 });
11281 }
11282 });
11283
11284 let locations = references.await?;
11285 if locations.is_empty() {
11286 return anyhow::Ok(Navigated::No);
11287 }
11288
11289 workspace.update_in(&mut cx, |workspace, window, cx| {
11290 let title = locations
11291 .first()
11292 .as_ref()
11293 .map(|location| {
11294 let buffer = location.buffer.read(cx);
11295 format!(
11296 "References to `{}`",
11297 buffer
11298 .text_for_range(location.range.clone())
11299 .collect::<String>()
11300 )
11301 })
11302 .unwrap();
11303 Self::open_locations_in_multibuffer(
11304 workspace,
11305 locations,
11306 title,
11307 false,
11308 MultibufferSelectionMode::First,
11309 window,
11310 cx,
11311 );
11312 Navigated::Yes
11313 })
11314 }))
11315 }
11316
11317 /// Opens a multibuffer with the given project locations in it
11318 pub fn open_locations_in_multibuffer(
11319 workspace: &mut Workspace,
11320 mut locations: Vec<Location>,
11321 title: String,
11322 split: bool,
11323 multibuffer_selection_mode: MultibufferSelectionMode,
11324 window: &mut Window,
11325 cx: &mut Context<Workspace>,
11326 ) {
11327 // If there are multiple definitions, open them in a multibuffer
11328 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
11329 let mut locations = locations.into_iter().peekable();
11330 let mut ranges = Vec::new();
11331 let capability = workspace.project().read(cx).capability();
11332
11333 let excerpt_buffer = cx.new(|cx| {
11334 let mut multibuffer = MultiBuffer::new(capability);
11335 while let Some(location) = locations.next() {
11336 let buffer = location.buffer.read(cx);
11337 let mut ranges_for_buffer = Vec::new();
11338 let range = location.range.to_offset(buffer);
11339 ranges_for_buffer.push(range.clone());
11340
11341 while let Some(next_location) = locations.peek() {
11342 if next_location.buffer == location.buffer {
11343 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11344 locations.next();
11345 } else {
11346 break;
11347 }
11348 }
11349
11350 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11351 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11352 location.buffer.clone(),
11353 ranges_for_buffer,
11354 DEFAULT_MULTIBUFFER_CONTEXT,
11355 cx,
11356 ))
11357 }
11358
11359 multibuffer.with_title(title)
11360 });
11361
11362 let editor = cx.new(|cx| {
11363 Editor::for_multibuffer(
11364 excerpt_buffer,
11365 Some(workspace.project().clone()),
11366 true,
11367 window,
11368 cx,
11369 )
11370 });
11371 editor.update(cx, |editor, cx| {
11372 match multibuffer_selection_mode {
11373 MultibufferSelectionMode::First => {
11374 if let Some(first_range) = ranges.first() {
11375 editor.change_selections(None, window, cx, |selections| {
11376 selections.clear_disjoint();
11377 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11378 });
11379 }
11380 editor.highlight_background::<Self>(
11381 &ranges,
11382 |theme| theme.editor_highlighted_line_background,
11383 cx,
11384 );
11385 }
11386 MultibufferSelectionMode::All => {
11387 editor.change_selections(None, window, cx, |selections| {
11388 selections.clear_disjoint();
11389 selections.select_anchor_ranges(ranges);
11390 });
11391 }
11392 }
11393 editor.register_buffers_with_language_servers(cx);
11394 });
11395
11396 let item = Box::new(editor);
11397 let item_id = item.item_id();
11398
11399 if split {
11400 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11401 } else {
11402 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11403 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11404 pane.close_current_preview_item(window, cx)
11405 } else {
11406 None
11407 }
11408 });
11409 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11410 }
11411 workspace.active_pane().update(cx, |pane, cx| {
11412 pane.set_preview_item_id(Some(item_id), cx);
11413 });
11414 }
11415
11416 pub fn rename(
11417 &mut self,
11418 _: &Rename,
11419 window: &mut Window,
11420 cx: &mut Context<Self>,
11421 ) -> Option<Task<Result<()>>> {
11422 use language::ToOffset as _;
11423
11424 let provider = self.semantics_provider.clone()?;
11425 let selection = self.selections.newest_anchor().clone();
11426 let (cursor_buffer, cursor_buffer_position) = self
11427 .buffer
11428 .read(cx)
11429 .text_anchor_for_position(selection.head(), cx)?;
11430 let (tail_buffer, cursor_buffer_position_end) = self
11431 .buffer
11432 .read(cx)
11433 .text_anchor_for_position(selection.tail(), cx)?;
11434 if tail_buffer != cursor_buffer {
11435 return None;
11436 }
11437
11438 let snapshot = cursor_buffer.read(cx).snapshot();
11439 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11440 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11441 let prepare_rename = provider
11442 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11443 .unwrap_or_else(|| Task::ready(Ok(None)));
11444 drop(snapshot);
11445
11446 Some(cx.spawn_in(window, |this, mut cx| async move {
11447 let rename_range = if let Some(range) = prepare_rename.await? {
11448 Some(range)
11449 } else {
11450 this.update(&mut cx, |this, cx| {
11451 let buffer = this.buffer.read(cx).snapshot(cx);
11452 let mut buffer_highlights = this
11453 .document_highlights_for_position(selection.head(), &buffer)
11454 .filter(|highlight| {
11455 highlight.start.excerpt_id == selection.head().excerpt_id
11456 && highlight.end.excerpt_id == selection.head().excerpt_id
11457 });
11458 buffer_highlights
11459 .next()
11460 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11461 })?
11462 };
11463 if let Some(rename_range) = rename_range {
11464 this.update_in(&mut cx, |this, window, cx| {
11465 let snapshot = cursor_buffer.read(cx).snapshot();
11466 let rename_buffer_range = rename_range.to_offset(&snapshot);
11467 let cursor_offset_in_rename_range =
11468 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11469 let cursor_offset_in_rename_range_end =
11470 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11471
11472 this.take_rename(false, window, cx);
11473 let buffer = this.buffer.read(cx).read(cx);
11474 let cursor_offset = selection.head().to_offset(&buffer);
11475 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11476 let rename_end = rename_start + rename_buffer_range.len();
11477 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11478 let mut old_highlight_id = None;
11479 let old_name: Arc<str> = buffer
11480 .chunks(rename_start..rename_end, true)
11481 .map(|chunk| {
11482 if old_highlight_id.is_none() {
11483 old_highlight_id = chunk.syntax_highlight_id;
11484 }
11485 chunk.text
11486 })
11487 .collect::<String>()
11488 .into();
11489
11490 drop(buffer);
11491
11492 // Position the selection in the rename editor so that it matches the current selection.
11493 this.show_local_selections = false;
11494 let rename_editor = cx.new(|cx| {
11495 let mut editor = Editor::single_line(window, cx);
11496 editor.buffer.update(cx, |buffer, cx| {
11497 buffer.edit([(0..0, old_name.clone())], None, cx)
11498 });
11499 let rename_selection_range = match cursor_offset_in_rename_range
11500 .cmp(&cursor_offset_in_rename_range_end)
11501 {
11502 Ordering::Equal => {
11503 editor.select_all(&SelectAll, window, cx);
11504 return editor;
11505 }
11506 Ordering::Less => {
11507 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11508 }
11509 Ordering::Greater => {
11510 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11511 }
11512 };
11513 if rename_selection_range.end > old_name.len() {
11514 editor.select_all(&SelectAll, window, cx);
11515 } else {
11516 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11517 s.select_ranges([rename_selection_range]);
11518 });
11519 }
11520 editor
11521 });
11522 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11523 if e == &EditorEvent::Focused {
11524 cx.emit(EditorEvent::FocusedIn)
11525 }
11526 })
11527 .detach();
11528
11529 let write_highlights =
11530 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11531 let read_highlights =
11532 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11533 let ranges = write_highlights
11534 .iter()
11535 .flat_map(|(_, ranges)| ranges.iter())
11536 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11537 .cloned()
11538 .collect();
11539
11540 this.highlight_text::<Rename>(
11541 ranges,
11542 HighlightStyle {
11543 fade_out: Some(0.6),
11544 ..Default::default()
11545 },
11546 cx,
11547 );
11548 let rename_focus_handle = rename_editor.focus_handle(cx);
11549 window.focus(&rename_focus_handle);
11550 let block_id = this.insert_blocks(
11551 [BlockProperties {
11552 style: BlockStyle::Flex,
11553 placement: BlockPlacement::Below(range.start),
11554 height: 1,
11555 render: Arc::new({
11556 let rename_editor = rename_editor.clone();
11557 move |cx: &mut BlockContext| {
11558 let mut text_style = cx.editor_style.text.clone();
11559 if let Some(highlight_style) = old_highlight_id
11560 .and_then(|h| h.style(&cx.editor_style.syntax))
11561 {
11562 text_style = text_style.highlight(highlight_style);
11563 }
11564 div()
11565 .block_mouse_down()
11566 .pl(cx.anchor_x)
11567 .child(EditorElement::new(
11568 &rename_editor,
11569 EditorStyle {
11570 background: cx.theme().system().transparent,
11571 local_player: cx.editor_style.local_player,
11572 text: text_style,
11573 scrollbar_width: cx.editor_style.scrollbar_width,
11574 syntax: cx.editor_style.syntax.clone(),
11575 status: cx.editor_style.status.clone(),
11576 inlay_hints_style: HighlightStyle {
11577 font_weight: Some(FontWeight::BOLD),
11578 ..make_inlay_hints_style(cx.app)
11579 },
11580 inline_completion_styles: make_suggestion_styles(
11581 cx.app,
11582 ),
11583 ..EditorStyle::default()
11584 },
11585 ))
11586 .into_any_element()
11587 }
11588 }),
11589 priority: 0,
11590 }],
11591 Some(Autoscroll::fit()),
11592 cx,
11593 )[0];
11594 this.pending_rename = Some(RenameState {
11595 range,
11596 old_name,
11597 editor: rename_editor,
11598 block_id,
11599 });
11600 })?;
11601 }
11602
11603 Ok(())
11604 }))
11605 }
11606
11607 pub fn confirm_rename(
11608 &mut self,
11609 _: &ConfirmRename,
11610 window: &mut Window,
11611 cx: &mut Context<Self>,
11612 ) -> Option<Task<Result<()>>> {
11613 let rename = self.take_rename(false, window, cx)?;
11614 let workspace = self.workspace()?.downgrade();
11615 let (buffer, start) = self
11616 .buffer
11617 .read(cx)
11618 .text_anchor_for_position(rename.range.start, cx)?;
11619 let (end_buffer, _) = self
11620 .buffer
11621 .read(cx)
11622 .text_anchor_for_position(rename.range.end, cx)?;
11623 if buffer != end_buffer {
11624 return None;
11625 }
11626
11627 let old_name = rename.old_name;
11628 let new_name = rename.editor.read(cx).text(cx);
11629
11630 let rename = self.semantics_provider.as_ref()?.perform_rename(
11631 &buffer,
11632 start,
11633 new_name.clone(),
11634 cx,
11635 )?;
11636
11637 Some(cx.spawn_in(window, |editor, mut cx| async move {
11638 let project_transaction = rename.await?;
11639 Self::open_project_transaction(
11640 &editor,
11641 workspace,
11642 project_transaction,
11643 format!("Rename: {} → {}", old_name, new_name),
11644 cx.clone(),
11645 )
11646 .await?;
11647
11648 editor.update(&mut cx, |editor, cx| {
11649 editor.refresh_document_highlights(cx);
11650 })?;
11651 Ok(())
11652 }))
11653 }
11654
11655 fn take_rename(
11656 &mut self,
11657 moving_cursor: bool,
11658 window: &mut Window,
11659 cx: &mut Context<Self>,
11660 ) -> Option<RenameState> {
11661 let rename = self.pending_rename.take()?;
11662 if rename.editor.focus_handle(cx).is_focused(window) {
11663 window.focus(&self.focus_handle);
11664 }
11665
11666 self.remove_blocks(
11667 [rename.block_id].into_iter().collect(),
11668 Some(Autoscroll::fit()),
11669 cx,
11670 );
11671 self.clear_highlights::<Rename>(cx);
11672 self.show_local_selections = true;
11673
11674 if moving_cursor {
11675 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11676 editor.selections.newest::<usize>(cx).head()
11677 });
11678
11679 // Update the selection to match the position of the selection inside
11680 // the rename editor.
11681 let snapshot = self.buffer.read(cx).read(cx);
11682 let rename_range = rename.range.to_offset(&snapshot);
11683 let cursor_in_editor = snapshot
11684 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11685 .min(rename_range.end);
11686 drop(snapshot);
11687
11688 self.change_selections(None, window, cx, |s| {
11689 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11690 });
11691 } else {
11692 self.refresh_document_highlights(cx);
11693 }
11694
11695 Some(rename)
11696 }
11697
11698 pub fn pending_rename(&self) -> Option<&RenameState> {
11699 self.pending_rename.as_ref()
11700 }
11701
11702 fn format(
11703 &mut self,
11704 _: &Format,
11705 window: &mut Window,
11706 cx: &mut Context<Self>,
11707 ) -> Option<Task<Result<()>>> {
11708 let project = match &self.project {
11709 Some(project) => project.clone(),
11710 None => return None,
11711 };
11712
11713 Some(self.perform_format(
11714 project,
11715 FormatTrigger::Manual,
11716 FormatTarget::Buffers,
11717 window,
11718 cx,
11719 ))
11720 }
11721
11722 fn format_selections(
11723 &mut self,
11724 _: &FormatSelections,
11725 window: &mut Window,
11726 cx: &mut Context<Self>,
11727 ) -> Option<Task<Result<()>>> {
11728 let project = match &self.project {
11729 Some(project) => project.clone(),
11730 None => return None,
11731 };
11732
11733 let ranges = self
11734 .selections
11735 .all_adjusted(cx)
11736 .into_iter()
11737 .map(|selection| selection.range())
11738 .collect_vec();
11739
11740 Some(self.perform_format(
11741 project,
11742 FormatTrigger::Manual,
11743 FormatTarget::Ranges(ranges),
11744 window,
11745 cx,
11746 ))
11747 }
11748
11749 fn perform_format(
11750 &mut self,
11751 project: Entity<Project>,
11752 trigger: FormatTrigger,
11753 target: FormatTarget,
11754 window: &mut Window,
11755 cx: &mut Context<Self>,
11756 ) -> Task<Result<()>> {
11757 let buffer = self.buffer.clone();
11758 let (buffers, target) = match target {
11759 FormatTarget::Buffers => {
11760 let mut buffers = buffer.read(cx).all_buffers();
11761 if trigger == FormatTrigger::Save {
11762 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11763 }
11764 (buffers, LspFormatTarget::Buffers)
11765 }
11766 FormatTarget::Ranges(selection_ranges) => {
11767 let multi_buffer = buffer.read(cx);
11768 let snapshot = multi_buffer.read(cx);
11769 let mut buffers = HashSet::default();
11770 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11771 BTreeMap::new();
11772 for selection_range in selection_ranges {
11773 for (buffer, buffer_range, _) in
11774 snapshot.range_to_buffer_ranges(selection_range)
11775 {
11776 let buffer_id = buffer.remote_id();
11777 let start = buffer.anchor_before(buffer_range.start);
11778 let end = buffer.anchor_after(buffer_range.end);
11779 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11780 buffer_id_to_ranges
11781 .entry(buffer_id)
11782 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11783 .or_insert_with(|| vec![start..end]);
11784 }
11785 }
11786 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11787 }
11788 };
11789
11790 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11791 let format = project.update(cx, |project, cx| {
11792 project.format(buffers, target, true, trigger, cx)
11793 });
11794
11795 cx.spawn_in(window, |_, mut cx| async move {
11796 let transaction = futures::select_biased! {
11797 () = timeout => {
11798 log::warn!("timed out waiting for formatting");
11799 None
11800 }
11801 transaction = format.log_err().fuse() => transaction,
11802 };
11803
11804 buffer
11805 .update(&mut cx, |buffer, cx| {
11806 if let Some(transaction) = transaction {
11807 if !buffer.is_singleton() {
11808 buffer.push_transaction(&transaction.0, cx);
11809 }
11810 }
11811
11812 cx.notify();
11813 })
11814 .ok();
11815
11816 Ok(())
11817 })
11818 }
11819
11820 fn restart_language_server(
11821 &mut self,
11822 _: &RestartLanguageServer,
11823 _: &mut Window,
11824 cx: &mut Context<Self>,
11825 ) {
11826 if let Some(project) = self.project.clone() {
11827 self.buffer.update(cx, |multi_buffer, cx| {
11828 project.update(cx, |project, cx| {
11829 project.restart_language_servers_for_buffers(
11830 multi_buffer.all_buffers().into_iter().collect(),
11831 cx,
11832 );
11833 });
11834 })
11835 }
11836 }
11837
11838 fn cancel_language_server_work(
11839 workspace: &mut Workspace,
11840 _: &actions::CancelLanguageServerWork,
11841 _: &mut Window,
11842 cx: &mut Context<Workspace>,
11843 ) {
11844 let project = workspace.project();
11845 let buffers = workspace
11846 .active_item(cx)
11847 .and_then(|item| item.act_as::<Editor>(cx))
11848 .map_or(HashSet::default(), |editor| {
11849 editor.read(cx).buffer.read(cx).all_buffers()
11850 });
11851 project.update(cx, |project, cx| {
11852 project.cancel_language_server_work_for_buffers(buffers, cx);
11853 });
11854 }
11855
11856 fn show_character_palette(
11857 &mut self,
11858 _: &ShowCharacterPalette,
11859 window: &mut Window,
11860 _: &mut Context<Self>,
11861 ) {
11862 window.show_character_palette();
11863 }
11864
11865 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11866 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11867 let buffer = self.buffer.read(cx).snapshot(cx);
11868 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11869 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11870 let is_valid = buffer
11871 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11872 .any(|entry| {
11873 entry.diagnostic.is_primary
11874 && !entry.range.is_empty()
11875 && entry.range.start == primary_range_start
11876 && entry.diagnostic.message == active_diagnostics.primary_message
11877 });
11878
11879 if is_valid != active_diagnostics.is_valid {
11880 active_diagnostics.is_valid = is_valid;
11881 let mut new_styles = HashMap::default();
11882 for (block_id, diagnostic) in &active_diagnostics.blocks {
11883 new_styles.insert(
11884 *block_id,
11885 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11886 );
11887 }
11888 self.display_map.update(cx, |display_map, _cx| {
11889 display_map.replace_blocks(new_styles)
11890 });
11891 }
11892 }
11893 }
11894
11895 fn activate_diagnostics(
11896 &mut self,
11897 buffer_id: BufferId,
11898 group_id: usize,
11899 window: &mut Window,
11900 cx: &mut Context<Self>,
11901 ) {
11902 self.dismiss_diagnostics(cx);
11903 let snapshot = self.snapshot(window, cx);
11904 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11905 let buffer = self.buffer.read(cx).snapshot(cx);
11906
11907 let mut primary_range = None;
11908 let mut primary_message = None;
11909 let diagnostic_group = buffer
11910 .diagnostic_group(buffer_id, group_id)
11911 .filter_map(|entry| {
11912 let start = entry.range.start;
11913 let end = entry.range.end;
11914 if snapshot.is_line_folded(MultiBufferRow(start.row))
11915 && (start.row == end.row
11916 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11917 {
11918 return None;
11919 }
11920 if entry.diagnostic.is_primary {
11921 primary_range = Some(entry.range.clone());
11922 primary_message = Some(entry.diagnostic.message.clone());
11923 }
11924 Some(entry)
11925 })
11926 .collect::<Vec<_>>();
11927 let primary_range = primary_range?;
11928 let primary_message = primary_message?;
11929
11930 let blocks = display_map
11931 .insert_blocks(
11932 diagnostic_group.iter().map(|entry| {
11933 let diagnostic = entry.diagnostic.clone();
11934 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11935 BlockProperties {
11936 style: BlockStyle::Fixed,
11937 placement: BlockPlacement::Below(
11938 buffer.anchor_after(entry.range.start),
11939 ),
11940 height: message_height,
11941 render: diagnostic_block_renderer(diagnostic, None, true, true),
11942 priority: 0,
11943 }
11944 }),
11945 cx,
11946 )
11947 .into_iter()
11948 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11949 .collect();
11950
11951 Some(ActiveDiagnosticGroup {
11952 primary_range: buffer.anchor_before(primary_range.start)
11953 ..buffer.anchor_after(primary_range.end),
11954 primary_message,
11955 group_id,
11956 blocks,
11957 is_valid: true,
11958 })
11959 });
11960 }
11961
11962 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11963 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11964 self.display_map.update(cx, |display_map, cx| {
11965 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11966 });
11967 cx.notify();
11968 }
11969 }
11970
11971 /// Disable inline diagnostics rendering for this editor.
11972 pub fn disable_inline_diagnostics(&mut self) {
11973 self.inline_diagnostics_enabled = false;
11974 self.inline_diagnostics_update = Task::ready(());
11975 self.inline_diagnostics.clear();
11976 }
11977
11978 pub fn inline_diagnostics_enabled(&self) -> bool {
11979 self.inline_diagnostics_enabled
11980 }
11981
11982 pub fn show_inline_diagnostics(&self) -> bool {
11983 self.show_inline_diagnostics
11984 }
11985
11986 pub fn toggle_inline_diagnostics(
11987 &mut self,
11988 _: &ToggleInlineDiagnostics,
11989 window: &mut Window,
11990 cx: &mut Context<'_, Editor>,
11991 ) {
11992 self.show_inline_diagnostics = !self.show_inline_diagnostics;
11993 self.refresh_inline_diagnostics(false, window, cx);
11994 }
11995
11996 fn refresh_inline_diagnostics(
11997 &mut self,
11998 debounce: bool,
11999 window: &mut Window,
12000 cx: &mut Context<Self>,
12001 ) {
12002 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12003 self.inline_diagnostics_update = Task::ready(());
12004 self.inline_diagnostics.clear();
12005 return;
12006 }
12007
12008 let debounce_ms = ProjectSettings::get_global(cx)
12009 .diagnostics
12010 .inline
12011 .update_debounce_ms;
12012 let debounce = if debounce && debounce_ms > 0 {
12013 Some(Duration::from_millis(debounce_ms))
12014 } else {
12015 None
12016 };
12017 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12018 if let Some(debounce) = debounce {
12019 cx.background_executor().timer(debounce).await;
12020 }
12021 let Some(snapshot) = editor
12022 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12023 .ok()
12024 else {
12025 return;
12026 };
12027
12028 let new_inline_diagnostics = cx
12029 .background_spawn(async move {
12030 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12031 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12032 let message = diagnostic_entry
12033 .diagnostic
12034 .message
12035 .split_once('\n')
12036 .map(|(line, _)| line)
12037 .map(SharedString::new)
12038 .unwrap_or_else(|| {
12039 SharedString::from(diagnostic_entry.diagnostic.message)
12040 });
12041 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12042 let (Ok(i) | Err(i)) = inline_diagnostics
12043 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12044 inline_diagnostics.insert(
12045 i,
12046 (
12047 start_anchor,
12048 InlineDiagnostic {
12049 message,
12050 group_id: diagnostic_entry.diagnostic.group_id,
12051 start: diagnostic_entry.range.start.to_point(&snapshot),
12052 is_primary: diagnostic_entry.diagnostic.is_primary,
12053 severity: diagnostic_entry.diagnostic.severity,
12054 },
12055 ),
12056 );
12057 }
12058 inline_diagnostics
12059 })
12060 .await;
12061
12062 editor
12063 .update(&mut cx, |editor, cx| {
12064 editor.inline_diagnostics = new_inline_diagnostics;
12065 cx.notify();
12066 })
12067 .ok();
12068 });
12069 }
12070
12071 pub fn set_selections_from_remote(
12072 &mut self,
12073 selections: Vec<Selection<Anchor>>,
12074 pending_selection: Option<Selection<Anchor>>,
12075 window: &mut Window,
12076 cx: &mut Context<Self>,
12077 ) {
12078 let old_cursor_position = self.selections.newest_anchor().head();
12079 self.selections.change_with(cx, |s| {
12080 s.select_anchors(selections);
12081 if let Some(pending_selection) = pending_selection {
12082 s.set_pending(pending_selection, SelectMode::Character);
12083 } else {
12084 s.clear_pending();
12085 }
12086 });
12087 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12088 }
12089
12090 fn push_to_selection_history(&mut self) {
12091 self.selection_history.push(SelectionHistoryEntry {
12092 selections: self.selections.disjoint_anchors(),
12093 select_next_state: self.select_next_state.clone(),
12094 select_prev_state: self.select_prev_state.clone(),
12095 add_selections_state: self.add_selections_state.clone(),
12096 });
12097 }
12098
12099 pub fn transact(
12100 &mut self,
12101 window: &mut Window,
12102 cx: &mut Context<Self>,
12103 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12104 ) -> Option<TransactionId> {
12105 self.start_transaction_at(Instant::now(), window, cx);
12106 update(self, window, cx);
12107 self.end_transaction_at(Instant::now(), cx)
12108 }
12109
12110 pub fn start_transaction_at(
12111 &mut self,
12112 now: Instant,
12113 window: &mut Window,
12114 cx: &mut Context<Self>,
12115 ) {
12116 self.end_selection(window, cx);
12117 if let Some(tx_id) = self
12118 .buffer
12119 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
12120 {
12121 self.selection_history
12122 .insert_transaction(tx_id, self.selections.disjoint_anchors());
12123 cx.emit(EditorEvent::TransactionBegun {
12124 transaction_id: tx_id,
12125 })
12126 }
12127 }
12128
12129 pub fn end_transaction_at(
12130 &mut self,
12131 now: Instant,
12132 cx: &mut Context<Self>,
12133 ) -> Option<TransactionId> {
12134 if let Some(transaction_id) = self
12135 .buffer
12136 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
12137 {
12138 if let Some((_, end_selections)) =
12139 self.selection_history.transaction_mut(transaction_id)
12140 {
12141 *end_selections = Some(self.selections.disjoint_anchors());
12142 } else {
12143 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
12144 }
12145
12146 cx.emit(EditorEvent::Edited { transaction_id });
12147 Some(transaction_id)
12148 } else {
12149 None
12150 }
12151 }
12152
12153 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
12154 if self.selection_mark_mode {
12155 self.change_selections(None, window, cx, |s| {
12156 s.move_with(|_, sel| {
12157 sel.collapse_to(sel.head(), SelectionGoal::None);
12158 });
12159 })
12160 }
12161 self.selection_mark_mode = true;
12162 cx.notify();
12163 }
12164
12165 pub fn swap_selection_ends(
12166 &mut self,
12167 _: &actions::SwapSelectionEnds,
12168 window: &mut Window,
12169 cx: &mut Context<Self>,
12170 ) {
12171 self.change_selections(None, window, cx, |s| {
12172 s.move_with(|_, sel| {
12173 if sel.start != sel.end {
12174 sel.reversed = !sel.reversed
12175 }
12176 });
12177 });
12178 self.request_autoscroll(Autoscroll::newest(), cx);
12179 cx.notify();
12180 }
12181
12182 pub fn toggle_fold(
12183 &mut self,
12184 _: &actions::ToggleFold,
12185 window: &mut Window,
12186 cx: &mut Context<Self>,
12187 ) {
12188 if self.is_singleton(cx) {
12189 let selection = self.selections.newest::<Point>(cx);
12190
12191 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12192 let range = if selection.is_empty() {
12193 let point = selection.head().to_display_point(&display_map);
12194 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12195 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12196 .to_point(&display_map);
12197 start..end
12198 } else {
12199 selection.range()
12200 };
12201 if display_map.folds_in_range(range).next().is_some() {
12202 self.unfold_lines(&Default::default(), window, cx)
12203 } else {
12204 self.fold(&Default::default(), window, cx)
12205 }
12206 } else {
12207 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12208 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12209 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12210 .map(|(snapshot, _, _)| snapshot.remote_id())
12211 .collect();
12212
12213 for buffer_id in buffer_ids {
12214 if self.is_buffer_folded(buffer_id, cx) {
12215 self.unfold_buffer(buffer_id, cx);
12216 } else {
12217 self.fold_buffer(buffer_id, cx);
12218 }
12219 }
12220 }
12221 }
12222
12223 pub fn toggle_fold_recursive(
12224 &mut self,
12225 _: &actions::ToggleFoldRecursive,
12226 window: &mut Window,
12227 cx: &mut Context<Self>,
12228 ) {
12229 let selection = self.selections.newest::<Point>(cx);
12230
12231 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12232 let range = if selection.is_empty() {
12233 let point = selection.head().to_display_point(&display_map);
12234 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12235 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12236 .to_point(&display_map);
12237 start..end
12238 } else {
12239 selection.range()
12240 };
12241 if display_map.folds_in_range(range).next().is_some() {
12242 self.unfold_recursive(&Default::default(), window, cx)
12243 } else {
12244 self.fold_recursive(&Default::default(), window, cx)
12245 }
12246 }
12247
12248 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12249 if self.is_singleton(cx) {
12250 let mut to_fold = Vec::new();
12251 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12252 let selections = self.selections.all_adjusted(cx);
12253
12254 for selection in selections {
12255 let range = selection.range().sorted();
12256 let buffer_start_row = range.start.row;
12257
12258 if range.start.row != range.end.row {
12259 let mut found = false;
12260 let mut row = range.start.row;
12261 while row <= range.end.row {
12262 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12263 {
12264 found = true;
12265 row = crease.range().end.row + 1;
12266 to_fold.push(crease);
12267 } else {
12268 row += 1
12269 }
12270 }
12271 if found {
12272 continue;
12273 }
12274 }
12275
12276 for row in (0..=range.start.row).rev() {
12277 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12278 if crease.range().end.row >= buffer_start_row {
12279 to_fold.push(crease);
12280 if row <= range.start.row {
12281 break;
12282 }
12283 }
12284 }
12285 }
12286 }
12287
12288 self.fold_creases(to_fold, true, window, cx);
12289 } else {
12290 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12291
12292 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12293 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12294 .map(|(snapshot, _, _)| snapshot.remote_id())
12295 .collect();
12296 for buffer_id in buffer_ids {
12297 self.fold_buffer(buffer_id, cx);
12298 }
12299 }
12300 }
12301
12302 fn fold_at_level(
12303 &mut self,
12304 fold_at: &FoldAtLevel,
12305 window: &mut Window,
12306 cx: &mut Context<Self>,
12307 ) {
12308 if !self.buffer.read(cx).is_singleton() {
12309 return;
12310 }
12311
12312 let fold_at_level = fold_at.0;
12313 let snapshot = self.buffer.read(cx).snapshot(cx);
12314 let mut to_fold = Vec::new();
12315 let mut stack = vec![(0, snapshot.max_row().0, 1)];
12316
12317 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
12318 while start_row < end_row {
12319 match self
12320 .snapshot(window, cx)
12321 .crease_for_buffer_row(MultiBufferRow(start_row))
12322 {
12323 Some(crease) => {
12324 let nested_start_row = crease.range().start.row + 1;
12325 let nested_end_row = crease.range().end.row;
12326
12327 if current_level < fold_at_level {
12328 stack.push((nested_start_row, nested_end_row, current_level + 1));
12329 } else if current_level == fold_at_level {
12330 to_fold.push(crease);
12331 }
12332
12333 start_row = nested_end_row + 1;
12334 }
12335 None => start_row += 1,
12336 }
12337 }
12338 }
12339
12340 self.fold_creases(to_fold, true, window, cx);
12341 }
12342
12343 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
12344 if self.buffer.read(cx).is_singleton() {
12345 let mut fold_ranges = Vec::new();
12346 let snapshot = self.buffer.read(cx).snapshot(cx);
12347
12348 for row in 0..snapshot.max_row().0 {
12349 if let Some(foldable_range) = self
12350 .snapshot(window, cx)
12351 .crease_for_buffer_row(MultiBufferRow(row))
12352 {
12353 fold_ranges.push(foldable_range);
12354 }
12355 }
12356
12357 self.fold_creases(fold_ranges, true, window, cx);
12358 } else {
12359 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
12360 editor
12361 .update_in(&mut cx, |editor, _, cx| {
12362 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12363 editor.fold_buffer(buffer_id, cx);
12364 }
12365 })
12366 .ok();
12367 });
12368 }
12369 }
12370
12371 pub fn fold_function_bodies(
12372 &mut self,
12373 _: &actions::FoldFunctionBodies,
12374 window: &mut Window,
12375 cx: &mut Context<Self>,
12376 ) {
12377 let snapshot = self.buffer.read(cx).snapshot(cx);
12378
12379 let ranges = snapshot
12380 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
12381 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
12382 .collect::<Vec<_>>();
12383
12384 let creases = ranges
12385 .into_iter()
12386 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
12387 .collect();
12388
12389 self.fold_creases(creases, true, window, cx);
12390 }
12391
12392 pub fn fold_recursive(
12393 &mut self,
12394 _: &actions::FoldRecursive,
12395 window: &mut Window,
12396 cx: &mut Context<Self>,
12397 ) {
12398 let mut to_fold = Vec::new();
12399 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12400 let selections = self.selections.all_adjusted(cx);
12401
12402 for selection in selections {
12403 let range = selection.range().sorted();
12404 let buffer_start_row = range.start.row;
12405
12406 if range.start.row != range.end.row {
12407 let mut found = false;
12408 for row in range.start.row..=range.end.row {
12409 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12410 found = true;
12411 to_fold.push(crease);
12412 }
12413 }
12414 if found {
12415 continue;
12416 }
12417 }
12418
12419 for row in (0..=range.start.row).rev() {
12420 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12421 if crease.range().end.row >= buffer_start_row {
12422 to_fold.push(crease);
12423 } else {
12424 break;
12425 }
12426 }
12427 }
12428 }
12429
12430 self.fold_creases(to_fold, true, window, cx);
12431 }
12432
12433 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
12434 let buffer_row = fold_at.buffer_row;
12435 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12436
12437 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12438 let autoscroll = self
12439 .selections
12440 .all::<Point>(cx)
12441 .iter()
12442 .any(|selection| crease.range().overlaps(&selection.range()));
12443
12444 self.fold_creases(vec![crease], autoscroll, window, cx);
12445 }
12446 }
12447
12448 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12449 if self.is_singleton(cx) {
12450 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12451 let buffer = &display_map.buffer_snapshot;
12452 let selections = self.selections.all::<Point>(cx);
12453 let ranges = selections
12454 .iter()
12455 .map(|s| {
12456 let range = s.display_range(&display_map).sorted();
12457 let mut start = range.start.to_point(&display_map);
12458 let mut end = range.end.to_point(&display_map);
12459 start.column = 0;
12460 end.column = buffer.line_len(MultiBufferRow(end.row));
12461 start..end
12462 })
12463 .collect::<Vec<_>>();
12464
12465 self.unfold_ranges(&ranges, true, true, cx);
12466 } else {
12467 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12468 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12469 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12470 .map(|(snapshot, _, _)| snapshot.remote_id())
12471 .collect();
12472 for buffer_id in buffer_ids {
12473 self.unfold_buffer(buffer_id, cx);
12474 }
12475 }
12476 }
12477
12478 pub fn unfold_recursive(
12479 &mut self,
12480 _: &UnfoldRecursive,
12481 _window: &mut Window,
12482 cx: &mut Context<Self>,
12483 ) {
12484 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12485 let selections = self.selections.all::<Point>(cx);
12486 let ranges = selections
12487 .iter()
12488 .map(|s| {
12489 let mut range = s.display_range(&display_map).sorted();
12490 *range.start.column_mut() = 0;
12491 *range.end.column_mut() = display_map.line_len(range.end.row());
12492 let start = range.start.to_point(&display_map);
12493 let end = range.end.to_point(&display_map);
12494 start..end
12495 })
12496 .collect::<Vec<_>>();
12497
12498 self.unfold_ranges(&ranges, true, true, cx);
12499 }
12500
12501 pub fn unfold_at(
12502 &mut self,
12503 unfold_at: &UnfoldAt,
12504 _window: &mut Window,
12505 cx: &mut Context<Self>,
12506 ) {
12507 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12508
12509 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12510 ..Point::new(
12511 unfold_at.buffer_row.0,
12512 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12513 );
12514
12515 let autoscroll = self
12516 .selections
12517 .all::<Point>(cx)
12518 .iter()
12519 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12520
12521 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12522 }
12523
12524 pub fn unfold_all(
12525 &mut self,
12526 _: &actions::UnfoldAll,
12527 _window: &mut Window,
12528 cx: &mut Context<Self>,
12529 ) {
12530 if self.buffer.read(cx).is_singleton() {
12531 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12532 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12533 } else {
12534 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12535 editor
12536 .update(&mut cx, |editor, cx| {
12537 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12538 editor.unfold_buffer(buffer_id, cx);
12539 }
12540 })
12541 .ok();
12542 });
12543 }
12544 }
12545
12546 pub fn fold_selected_ranges(
12547 &mut self,
12548 _: &FoldSelectedRanges,
12549 window: &mut Window,
12550 cx: &mut Context<Self>,
12551 ) {
12552 let selections = self.selections.all::<Point>(cx);
12553 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12554 let line_mode = self.selections.line_mode;
12555 let ranges = selections
12556 .into_iter()
12557 .map(|s| {
12558 if line_mode {
12559 let start = Point::new(s.start.row, 0);
12560 let end = Point::new(
12561 s.end.row,
12562 display_map
12563 .buffer_snapshot
12564 .line_len(MultiBufferRow(s.end.row)),
12565 );
12566 Crease::simple(start..end, display_map.fold_placeholder.clone())
12567 } else {
12568 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12569 }
12570 })
12571 .collect::<Vec<_>>();
12572 self.fold_creases(ranges, true, window, cx);
12573 }
12574
12575 pub fn fold_ranges<T: ToOffset + Clone>(
12576 &mut self,
12577 ranges: Vec<Range<T>>,
12578 auto_scroll: bool,
12579 window: &mut Window,
12580 cx: &mut Context<Self>,
12581 ) {
12582 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12583 let ranges = ranges
12584 .into_iter()
12585 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12586 .collect::<Vec<_>>();
12587 self.fold_creases(ranges, auto_scroll, window, cx);
12588 }
12589
12590 pub fn fold_creases<T: ToOffset + Clone>(
12591 &mut self,
12592 creases: Vec<Crease<T>>,
12593 auto_scroll: bool,
12594 window: &mut Window,
12595 cx: &mut Context<Self>,
12596 ) {
12597 if creases.is_empty() {
12598 return;
12599 }
12600
12601 let mut buffers_affected = HashSet::default();
12602 let multi_buffer = self.buffer().read(cx);
12603 for crease in &creases {
12604 if let Some((_, buffer, _)) =
12605 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12606 {
12607 buffers_affected.insert(buffer.read(cx).remote_id());
12608 };
12609 }
12610
12611 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12612
12613 if auto_scroll {
12614 self.request_autoscroll(Autoscroll::fit(), cx);
12615 }
12616
12617 cx.notify();
12618
12619 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12620 // Clear diagnostics block when folding a range that contains it.
12621 let snapshot = self.snapshot(window, cx);
12622 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12623 drop(snapshot);
12624 self.active_diagnostics = Some(active_diagnostics);
12625 self.dismiss_diagnostics(cx);
12626 } else {
12627 self.active_diagnostics = Some(active_diagnostics);
12628 }
12629 }
12630
12631 self.scrollbar_marker_state.dirty = true;
12632 }
12633
12634 /// Removes any folds whose ranges intersect any of the given ranges.
12635 pub fn unfold_ranges<T: ToOffset + Clone>(
12636 &mut self,
12637 ranges: &[Range<T>],
12638 inclusive: bool,
12639 auto_scroll: bool,
12640 cx: &mut Context<Self>,
12641 ) {
12642 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12643 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12644 });
12645 }
12646
12647 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12648 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12649 return;
12650 }
12651 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12652 self.display_map
12653 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12654 cx.emit(EditorEvent::BufferFoldToggled {
12655 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12656 folded: true,
12657 });
12658 cx.notify();
12659 }
12660
12661 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12662 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12663 return;
12664 }
12665 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12666 self.display_map.update(cx, |display_map, cx| {
12667 display_map.unfold_buffer(buffer_id, cx);
12668 });
12669 cx.emit(EditorEvent::BufferFoldToggled {
12670 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12671 folded: false,
12672 });
12673 cx.notify();
12674 }
12675
12676 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12677 self.display_map.read(cx).is_buffer_folded(buffer)
12678 }
12679
12680 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12681 self.display_map.read(cx).folded_buffers()
12682 }
12683
12684 /// Removes any folds with the given ranges.
12685 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12686 &mut self,
12687 ranges: &[Range<T>],
12688 type_id: TypeId,
12689 auto_scroll: bool,
12690 cx: &mut Context<Self>,
12691 ) {
12692 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12693 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12694 });
12695 }
12696
12697 fn remove_folds_with<T: ToOffset + Clone>(
12698 &mut self,
12699 ranges: &[Range<T>],
12700 auto_scroll: bool,
12701 cx: &mut Context<Self>,
12702 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12703 ) {
12704 if ranges.is_empty() {
12705 return;
12706 }
12707
12708 let mut buffers_affected = HashSet::default();
12709 let multi_buffer = self.buffer().read(cx);
12710 for range in ranges {
12711 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12712 buffers_affected.insert(buffer.read(cx).remote_id());
12713 };
12714 }
12715
12716 self.display_map.update(cx, update);
12717
12718 if auto_scroll {
12719 self.request_autoscroll(Autoscroll::fit(), cx);
12720 }
12721
12722 cx.notify();
12723 self.scrollbar_marker_state.dirty = true;
12724 self.active_indent_guides_state.dirty = true;
12725 }
12726
12727 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12728 self.display_map.read(cx).fold_placeholder.clone()
12729 }
12730
12731 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12732 self.buffer.update(cx, |buffer, cx| {
12733 buffer.set_all_diff_hunks_expanded(cx);
12734 });
12735 }
12736
12737 pub fn expand_all_diff_hunks(
12738 &mut self,
12739 _: &ExpandAllDiffHunks,
12740 _window: &mut Window,
12741 cx: &mut Context<Self>,
12742 ) {
12743 self.buffer.update(cx, |buffer, cx| {
12744 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12745 });
12746 }
12747
12748 pub fn toggle_selected_diff_hunks(
12749 &mut self,
12750 _: &ToggleSelectedDiffHunks,
12751 _window: &mut Window,
12752 cx: &mut Context<Self>,
12753 ) {
12754 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12755 self.toggle_diff_hunks_in_ranges(ranges, cx);
12756 }
12757
12758 pub fn diff_hunks_in_ranges<'a>(
12759 &'a self,
12760 ranges: &'a [Range<Anchor>],
12761 buffer: &'a MultiBufferSnapshot,
12762 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
12763 ranges.iter().flat_map(move |range| {
12764 let end_excerpt_id = range.end.excerpt_id;
12765 let range = range.to_point(buffer);
12766 let mut peek_end = range.end;
12767 if range.end.row < buffer.max_row().0 {
12768 peek_end = Point::new(range.end.row + 1, 0);
12769 }
12770 buffer
12771 .diff_hunks_in_range(range.start..peek_end)
12772 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
12773 })
12774 }
12775
12776 pub fn has_stageable_diff_hunks_in_ranges(
12777 &self,
12778 ranges: &[Range<Anchor>],
12779 snapshot: &MultiBufferSnapshot,
12780 ) -> bool {
12781 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
12782 hunks.any(|hunk| hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk)
12783 }
12784
12785 pub fn toggle_staged_selected_diff_hunks(
12786 &mut self,
12787 _: &::git::ToggleStaged,
12788 _window: &mut Window,
12789 cx: &mut Context<Self>,
12790 ) {
12791 let snapshot = self.buffer.read(cx).snapshot(cx);
12792 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12793 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
12794 self.stage_or_unstage_diff_hunks(stage, &ranges, cx);
12795 }
12796
12797 pub fn stage_and_next(
12798 &mut self,
12799 _: &::git::StageAndNext,
12800 window: &mut Window,
12801 cx: &mut Context<Self>,
12802 ) {
12803 self.do_stage_or_unstage_and_next(true, window, cx);
12804 }
12805
12806 pub fn unstage_and_next(
12807 &mut self,
12808 _: &::git::UnstageAndNext,
12809 window: &mut Window,
12810 cx: &mut Context<Self>,
12811 ) {
12812 self.do_stage_or_unstage_and_next(false, window, cx);
12813 }
12814
12815 pub fn stage_or_unstage_diff_hunks(
12816 &mut self,
12817 stage: bool,
12818 ranges: &[Range<Anchor>],
12819 cx: &mut Context<Self>,
12820 ) {
12821 let snapshot = self.buffer.read(cx).snapshot(cx);
12822 let Some(project) = &self.project else {
12823 return;
12824 };
12825
12826 let chunk_by = self
12827 .diff_hunks_in_ranges(&ranges, &snapshot)
12828 .chunk_by(|hunk| hunk.buffer_id);
12829 for (buffer_id, hunks) in &chunk_by {
12830 Self::do_stage_or_unstage(project, stage, buffer_id, hunks, &snapshot, cx);
12831 }
12832 }
12833
12834 fn do_stage_or_unstage_and_next(
12835 &mut self,
12836 stage: bool,
12837 window: &mut Window,
12838 cx: &mut Context<Self>,
12839 ) {
12840 let mut ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
12841 if ranges.iter().any(|range| range.start != range.end) {
12842 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12843 return;
12844 }
12845
12846 if !self.buffer().read(cx).is_singleton() {
12847 if let Some((excerpt_id, buffer, range)) = self.active_excerpt(cx) {
12848 if buffer.read(cx).is_empty() {
12849 let buffer = buffer.read(cx);
12850 let Some(file) = buffer.file() else {
12851 return;
12852 };
12853 let project_path = project::ProjectPath {
12854 worktree_id: file.worktree_id(cx),
12855 path: file.path().clone(),
12856 };
12857 let Some(project) = self.project.as_ref() else {
12858 return;
12859 };
12860 let project = project.read(cx);
12861
12862 let Some(repo) = project.git_store().read(cx).active_repository() else {
12863 return;
12864 };
12865
12866 repo.update(cx, |repo, cx| {
12867 let Some(repo_path) = repo.project_path_to_repo_path(&project_path) else {
12868 return;
12869 };
12870 let Some(status) = repo.repository_entry.status_for_path(&repo_path) else {
12871 return;
12872 };
12873 if stage && status.status == FileStatus::Untracked {
12874 repo.stage_entries(vec![repo_path], cx)
12875 .detach_and_log_err(cx);
12876 return;
12877 }
12878 })
12879 }
12880 ranges = vec![multi_buffer::Anchor::range_in_buffer(
12881 excerpt_id,
12882 buffer.read(cx).remote_id(),
12883 range,
12884 )];
12885 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12886 let snapshot = self.buffer().read(cx).snapshot(cx);
12887 let mut point = ranges.last().unwrap().end.to_point(&snapshot);
12888 if point.row < snapshot.max_row().0 {
12889 point.row += 1;
12890 point.column = 0;
12891 point = snapshot.clip_point(point, Bias::Right);
12892 self.change_selections(Some(Autoscroll::top_relative(6)), window, cx, |s| {
12893 s.select_ranges([point..point]);
12894 })
12895 }
12896 return;
12897 }
12898 }
12899 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12900 self.go_to_next_hunk(&Default::default(), window, cx);
12901 }
12902
12903 fn do_stage_or_unstage(
12904 project: &Entity<Project>,
12905 stage: bool,
12906 buffer_id: BufferId,
12907 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
12908 snapshot: &MultiBufferSnapshot,
12909 cx: &mut Context<Self>,
12910 ) {
12911 let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
12912 log::debug!("no buffer for id");
12913 return;
12914 };
12915 let buffer_snapshot = buffer.read(cx).snapshot();
12916 let Some((repo, path)) = project
12917 .read(cx)
12918 .repository_and_path_for_buffer_id(buffer_id, cx)
12919 else {
12920 log::debug!("no git repo for buffer id");
12921 return;
12922 };
12923 let Some(diff) = snapshot.diff_for_buffer_id(buffer_id) else {
12924 log::debug!("no diff for buffer id");
12925 return;
12926 };
12927 let Some(secondary_diff) = diff.secondary_diff() else {
12928 log::debug!("no secondary diff for buffer id");
12929 return;
12930 };
12931
12932 let edits = diff.secondary_edits_for_stage_or_unstage(
12933 stage,
12934 hunks.filter_map(|hunk| {
12935 if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None {
12936 return None;
12937 } else if !stage
12938 && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk
12939 {
12940 return None;
12941 }
12942 Some((
12943 hunk.diff_base_byte_range.clone(),
12944 hunk.secondary_diff_base_byte_range.clone(),
12945 hunk.buffer_range.clone(),
12946 ))
12947 }),
12948 &buffer_snapshot,
12949 );
12950
12951 let Some(index_base) = secondary_diff
12952 .base_text()
12953 .map(|snapshot| snapshot.text.as_rope().clone())
12954 else {
12955 log::debug!("no index base");
12956 return;
12957 };
12958 let index_buffer = cx.new(|cx| {
12959 Buffer::local_normalized(index_base.clone(), text::LineEnding::default(), cx)
12960 });
12961 let new_index_text = index_buffer.update(cx, |index_buffer, cx| {
12962 index_buffer.edit(edits, None, cx);
12963 index_buffer.snapshot().as_rope().to_string()
12964 });
12965 let new_index_text = if new_index_text.is_empty()
12966 && !stage
12967 && (diff.is_single_insertion
12968 || buffer_snapshot
12969 .file()
12970 .map_or(false, |file| file.disk_state() == DiskState::New))
12971 {
12972 log::debug!("removing from index");
12973 None
12974 } else {
12975 Some(new_index_text)
12976 };
12977 let buffer_store = project.read(cx).buffer_store().clone();
12978 buffer_store
12979 .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
12980 .detach_and_log_err(cx);
12981
12982 cx.background_spawn(
12983 repo.read(cx)
12984 .set_index_text(&path, new_index_text)
12985 .log_err(),
12986 )
12987 .detach();
12988 }
12989
12990 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12991 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12992 self.buffer
12993 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12994 }
12995
12996 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12997 self.buffer.update(cx, |buffer, cx| {
12998 let ranges = vec![Anchor::min()..Anchor::max()];
12999 if !buffer.all_diff_hunks_expanded()
13000 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
13001 {
13002 buffer.collapse_diff_hunks(ranges, cx);
13003 true
13004 } else {
13005 false
13006 }
13007 })
13008 }
13009
13010 fn toggle_diff_hunks_in_ranges(
13011 &mut self,
13012 ranges: Vec<Range<Anchor>>,
13013 cx: &mut Context<'_, Editor>,
13014 ) {
13015 self.buffer.update(cx, |buffer, cx| {
13016 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
13017 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
13018 })
13019 }
13020
13021 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
13022 self.buffer.update(cx, |buffer, cx| {
13023 let snapshot = buffer.snapshot(cx);
13024 let excerpt_id = range.end.excerpt_id;
13025 let point_range = range.to_point(&snapshot);
13026 let expand = !buffer.single_hunk_is_expanded(range, cx);
13027 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
13028 })
13029 }
13030
13031 pub(crate) fn apply_all_diff_hunks(
13032 &mut self,
13033 _: &ApplyAllDiffHunks,
13034 window: &mut Window,
13035 cx: &mut Context<Self>,
13036 ) {
13037 let buffers = self.buffer.read(cx).all_buffers();
13038 for branch_buffer in buffers {
13039 branch_buffer.update(cx, |branch_buffer, cx| {
13040 branch_buffer.merge_into_base(Vec::new(), cx);
13041 });
13042 }
13043
13044 if let Some(project) = self.project.clone() {
13045 self.save(true, project, window, cx).detach_and_log_err(cx);
13046 }
13047 }
13048
13049 pub(crate) fn apply_selected_diff_hunks(
13050 &mut self,
13051 _: &ApplyDiffHunk,
13052 window: &mut Window,
13053 cx: &mut Context<Self>,
13054 ) {
13055 let snapshot = self.snapshot(window, cx);
13056 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
13057 let mut ranges_by_buffer = HashMap::default();
13058 self.transact(window, cx, |editor, _window, cx| {
13059 for hunk in hunks {
13060 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13061 ranges_by_buffer
13062 .entry(buffer.clone())
13063 .or_insert_with(Vec::new)
13064 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13065 }
13066 }
13067
13068 for (buffer, ranges) in ranges_by_buffer {
13069 buffer.update(cx, |buffer, cx| {
13070 buffer.merge_into_base(ranges, cx);
13071 });
13072 }
13073 });
13074
13075 if let Some(project) = self.project.clone() {
13076 self.save(true, project, window, cx).detach_and_log_err(cx);
13077 }
13078 }
13079
13080 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13081 if hovered != self.gutter_hovered {
13082 self.gutter_hovered = hovered;
13083 cx.notify();
13084 }
13085 }
13086
13087 pub fn insert_blocks(
13088 &mut self,
13089 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13090 autoscroll: Option<Autoscroll>,
13091 cx: &mut Context<Self>,
13092 ) -> Vec<CustomBlockId> {
13093 let blocks = self
13094 .display_map
13095 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13096 if let Some(autoscroll) = autoscroll {
13097 self.request_autoscroll(autoscroll, cx);
13098 }
13099 cx.notify();
13100 blocks
13101 }
13102
13103 pub fn resize_blocks(
13104 &mut self,
13105 heights: HashMap<CustomBlockId, u32>,
13106 autoscroll: Option<Autoscroll>,
13107 cx: &mut Context<Self>,
13108 ) {
13109 self.display_map
13110 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13111 if let Some(autoscroll) = autoscroll {
13112 self.request_autoscroll(autoscroll, cx);
13113 }
13114 cx.notify();
13115 }
13116
13117 pub fn replace_blocks(
13118 &mut self,
13119 renderers: HashMap<CustomBlockId, RenderBlock>,
13120 autoscroll: Option<Autoscroll>,
13121 cx: &mut Context<Self>,
13122 ) {
13123 self.display_map
13124 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13125 if let Some(autoscroll) = autoscroll {
13126 self.request_autoscroll(autoscroll, cx);
13127 }
13128 cx.notify();
13129 }
13130
13131 pub fn remove_blocks(
13132 &mut self,
13133 block_ids: HashSet<CustomBlockId>,
13134 autoscroll: Option<Autoscroll>,
13135 cx: &mut Context<Self>,
13136 ) {
13137 self.display_map.update(cx, |display_map, cx| {
13138 display_map.remove_blocks(block_ids, cx)
13139 });
13140 if let Some(autoscroll) = autoscroll {
13141 self.request_autoscroll(autoscroll, cx);
13142 }
13143 cx.notify();
13144 }
13145
13146 pub fn row_for_block(
13147 &self,
13148 block_id: CustomBlockId,
13149 cx: &mut Context<Self>,
13150 ) -> Option<DisplayRow> {
13151 self.display_map
13152 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13153 }
13154
13155 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13156 self.focused_block = Some(focused_block);
13157 }
13158
13159 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13160 self.focused_block.take()
13161 }
13162
13163 pub fn insert_creases(
13164 &mut self,
13165 creases: impl IntoIterator<Item = Crease<Anchor>>,
13166 cx: &mut Context<Self>,
13167 ) -> Vec<CreaseId> {
13168 self.display_map
13169 .update(cx, |map, cx| map.insert_creases(creases, cx))
13170 }
13171
13172 pub fn remove_creases(
13173 &mut self,
13174 ids: impl IntoIterator<Item = CreaseId>,
13175 cx: &mut Context<Self>,
13176 ) {
13177 self.display_map
13178 .update(cx, |map, cx| map.remove_creases(ids, cx));
13179 }
13180
13181 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
13182 self.display_map
13183 .update(cx, |map, cx| map.snapshot(cx))
13184 .longest_row()
13185 }
13186
13187 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
13188 self.display_map
13189 .update(cx, |map, cx| map.snapshot(cx))
13190 .max_point()
13191 }
13192
13193 pub fn text(&self, cx: &App) -> String {
13194 self.buffer.read(cx).read(cx).text()
13195 }
13196
13197 pub fn is_empty(&self, cx: &App) -> bool {
13198 self.buffer.read(cx).read(cx).is_empty()
13199 }
13200
13201 pub fn text_option(&self, cx: &App) -> Option<String> {
13202 let text = self.text(cx);
13203 let text = text.trim();
13204
13205 if text.is_empty() {
13206 return None;
13207 }
13208
13209 Some(text.to_string())
13210 }
13211
13212 pub fn set_text(
13213 &mut self,
13214 text: impl Into<Arc<str>>,
13215 window: &mut Window,
13216 cx: &mut Context<Self>,
13217 ) {
13218 self.transact(window, cx, |this, _, cx| {
13219 this.buffer
13220 .read(cx)
13221 .as_singleton()
13222 .expect("you can only call set_text on editors for singleton buffers")
13223 .update(cx, |buffer, cx| buffer.set_text(text, cx));
13224 });
13225 }
13226
13227 pub fn display_text(&self, cx: &mut App) -> String {
13228 self.display_map
13229 .update(cx, |map, cx| map.snapshot(cx))
13230 .text()
13231 }
13232
13233 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
13234 let mut wrap_guides = smallvec::smallvec![];
13235
13236 if self.show_wrap_guides == Some(false) {
13237 return wrap_guides;
13238 }
13239
13240 let settings = self.buffer.read(cx).settings_at(0, cx);
13241 if settings.show_wrap_guides {
13242 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
13243 wrap_guides.push((soft_wrap as usize, true));
13244 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
13245 wrap_guides.push((soft_wrap as usize, true));
13246 }
13247 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
13248 }
13249
13250 wrap_guides
13251 }
13252
13253 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
13254 let settings = self.buffer.read(cx).settings_at(0, cx);
13255 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
13256 match mode {
13257 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
13258 SoftWrap::None
13259 }
13260 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
13261 language_settings::SoftWrap::PreferredLineLength => {
13262 SoftWrap::Column(settings.preferred_line_length)
13263 }
13264 language_settings::SoftWrap::Bounded => {
13265 SoftWrap::Bounded(settings.preferred_line_length)
13266 }
13267 }
13268 }
13269
13270 pub fn set_soft_wrap_mode(
13271 &mut self,
13272 mode: language_settings::SoftWrap,
13273
13274 cx: &mut Context<Self>,
13275 ) {
13276 self.soft_wrap_mode_override = Some(mode);
13277 cx.notify();
13278 }
13279
13280 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
13281 self.text_style_refinement = Some(style);
13282 }
13283
13284 /// called by the Element so we know what style we were most recently rendered with.
13285 pub(crate) fn set_style(
13286 &mut self,
13287 style: EditorStyle,
13288 window: &mut Window,
13289 cx: &mut Context<Self>,
13290 ) {
13291 let rem_size = window.rem_size();
13292 self.display_map.update(cx, |map, cx| {
13293 map.set_font(
13294 style.text.font(),
13295 style.text.font_size.to_pixels(rem_size),
13296 cx,
13297 )
13298 });
13299 self.style = Some(style);
13300 }
13301
13302 pub fn style(&self) -> Option<&EditorStyle> {
13303 self.style.as_ref()
13304 }
13305
13306 // Called by the element. This method is not designed to be called outside of the editor
13307 // element's layout code because it does not notify when rewrapping is computed synchronously.
13308 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
13309 self.display_map
13310 .update(cx, |map, cx| map.set_wrap_width(width, cx))
13311 }
13312
13313 pub fn set_soft_wrap(&mut self) {
13314 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
13315 }
13316
13317 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
13318 if self.soft_wrap_mode_override.is_some() {
13319 self.soft_wrap_mode_override.take();
13320 } else {
13321 let soft_wrap = match self.soft_wrap_mode(cx) {
13322 SoftWrap::GitDiff => return,
13323 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
13324 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
13325 language_settings::SoftWrap::None
13326 }
13327 };
13328 self.soft_wrap_mode_override = Some(soft_wrap);
13329 }
13330 cx.notify();
13331 }
13332
13333 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
13334 let Some(workspace) = self.workspace() else {
13335 return;
13336 };
13337 let fs = workspace.read(cx).app_state().fs.clone();
13338 let current_show = TabBarSettings::get_global(cx).show;
13339 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
13340 setting.show = Some(!current_show);
13341 });
13342 }
13343
13344 pub fn toggle_indent_guides(
13345 &mut self,
13346 _: &ToggleIndentGuides,
13347 _: &mut Window,
13348 cx: &mut Context<Self>,
13349 ) {
13350 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
13351 self.buffer
13352 .read(cx)
13353 .settings_at(0, cx)
13354 .indent_guides
13355 .enabled
13356 });
13357 self.show_indent_guides = Some(!currently_enabled);
13358 cx.notify();
13359 }
13360
13361 fn should_show_indent_guides(&self) -> Option<bool> {
13362 self.show_indent_guides
13363 }
13364
13365 pub fn toggle_line_numbers(
13366 &mut self,
13367 _: &ToggleLineNumbers,
13368 _: &mut Window,
13369 cx: &mut Context<Self>,
13370 ) {
13371 let mut editor_settings = EditorSettings::get_global(cx).clone();
13372 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
13373 EditorSettings::override_global(editor_settings, cx);
13374 }
13375
13376 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
13377 self.use_relative_line_numbers
13378 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
13379 }
13380
13381 pub fn toggle_relative_line_numbers(
13382 &mut self,
13383 _: &ToggleRelativeLineNumbers,
13384 _: &mut Window,
13385 cx: &mut Context<Self>,
13386 ) {
13387 let is_relative = self.should_use_relative_line_numbers(cx);
13388 self.set_relative_line_number(Some(!is_relative), cx)
13389 }
13390
13391 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
13392 self.use_relative_line_numbers = is_relative;
13393 cx.notify();
13394 }
13395
13396 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
13397 self.show_gutter = show_gutter;
13398 cx.notify();
13399 }
13400
13401 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
13402 self.show_scrollbars = show_scrollbars;
13403 cx.notify();
13404 }
13405
13406 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
13407 self.show_line_numbers = Some(show_line_numbers);
13408 cx.notify();
13409 }
13410
13411 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
13412 self.show_git_diff_gutter = Some(show_git_diff_gutter);
13413 cx.notify();
13414 }
13415
13416 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
13417 self.show_code_actions = Some(show_code_actions);
13418 cx.notify();
13419 }
13420
13421 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
13422 self.show_runnables = Some(show_runnables);
13423 cx.notify();
13424 }
13425
13426 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
13427 if self.display_map.read(cx).masked != masked {
13428 self.display_map.update(cx, |map, _| map.masked = masked);
13429 }
13430 cx.notify()
13431 }
13432
13433 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
13434 self.show_wrap_guides = Some(show_wrap_guides);
13435 cx.notify();
13436 }
13437
13438 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
13439 self.show_indent_guides = Some(show_indent_guides);
13440 cx.notify();
13441 }
13442
13443 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
13444 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
13445 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
13446 if let Some(dir) = file.abs_path(cx).parent() {
13447 return Some(dir.to_owned());
13448 }
13449 }
13450
13451 if let Some(project_path) = buffer.read(cx).project_path(cx) {
13452 return Some(project_path.path.to_path_buf());
13453 }
13454 }
13455
13456 None
13457 }
13458
13459 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
13460 self.active_excerpt(cx)?
13461 .1
13462 .read(cx)
13463 .file()
13464 .and_then(|f| f.as_local())
13465 }
13466
13467 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13468 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13469 let buffer = buffer.read(cx);
13470 if let Some(project_path) = buffer.project_path(cx) {
13471 let project = self.project.as_ref()?.read(cx);
13472 project.absolute_path(&project_path, cx)
13473 } else {
13474 buffer
13475 .file()
13476 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
13477 }
13478 })
13479 }
13480
13481 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13482 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13483 let project_path = buffer.read(cx).project_path(cx)?;
13484 let project = self.project.as_ref()?.read(cx);
13485 let entry = project.entry_for_path(&project_path, cx)?;
13486 let path = entry.path.to_path_buf();
13487 Some(path)
13488 })
13489 }
13490
13491 pub fn reveal_in_finder(
13492 &mut self,
13493 _: &RevealInFileManager,
13494 _window: &mut Window,
13495 cx: &mut Context<Self>,
13496 ) {
13497 if let Some(target) = self.target_file(cx) {
13498 cx.reveal_path(&target.abs_path(cx));
13499 }
13500 }
13501
13502 pub fn copy_path(
13503 &mut self,
13504 _: &zed_actions::workspace::CopyPath,
13505 _window: &mut Window,
13506 cx: &mut Context<Self>,
13507 ) {
13508 if let Some(path) = self.target_file_abs_path(cx) {
13509 if let Some(path) = path.to_str() {
13510 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13511 }
13512 }
13513 }
13514
13515 pub fn copy_relative_path(
13516 &mut self,
13517 _: &zed_actions::workspace::CopyRelativePath,
13518 _window: &mut Window,
13519 cx: &mut Context<Self>,
13520 ) {
13521 if let Some(path) = self.target_file_path(cx) {
13522 if let Some(path) = path.to_str() {
13523 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13524 }
13525 }
13526 }
13527
13528 pub fn copy_file_name_without_extension(
13529 &mut self,
13530 _: &CopyFileNameWithoutExtension,
13531 _: &mut Window,
13532 cx: &mut Context<Self>,
13533 ) {
13534 if let Some(file) = self.target_file(cx) {
13535 if let Some(file_stem) = file.path().file_stem() {
13536 if let Some(name) = file_stem.to_str() {
13537 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13538 }
13539 }
13540 }
13541 }
13542
13543 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
13544 if let Some(file) = self.target_file(cx) {
13545 if let Some(file_name) = file.path().file_name() {
13546 if let Some(name) = file_name.to_str() {
13547 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13548 }
13549 }
13550 }
13551 }
13552
13553 pub fn toggle_git_blame(
13554 &mut self,
13555 _: &ToggleGitBlame,
13556 window: &mut Window,
13557 cx: &mut Context<Self>,
13558 ) {
13559 self.show_git_blame_gutter = !self.show_git_blame_gutter;
13560
13561 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
13562 self.start_git_blame(true, window, cx);
13563 }
13564
13565 cx.notify();
13566 }
13567
13568 pub fn toggle_git_blame_inline(
13569 &mut self,
13570 _: &ToggleGitBlameInline,
13571 window: &mut Window,
13572 cx: &mut Context<Self>,
13573 ) {
13574 self.toggle_git_blame_inline_internal(true, window, cx);
13575 cx.notify();
13576 }
13577
13578 pub fn git_blame_inline_enabled(&self) -> bool {
13579 self.git_blame_inline_enabled
13580 }
13581
13582 pub fn toggle_selection_menu(
13583 &mut self,
13584 _: &ToggleSelectionMenu,
13585 _: &mut Window,
13586 cx: &mut Context<Self>,
13587 ) {
13588 self.show_selection_menu = self
13589 .show_selection_menu
13590 .map(|show_selections_menu| !show_selections_menu)
13591 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
13592
13593 cx.notify();
13594 }
13595
13596 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
13597 self.show_selection_menu
13598 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
13599 }
13600
13601 fn start_git_blame(
13602 &mut self,
13603 user_triggered: bool,
13604 window: &mut Window,
13605 cx: &mut Context<Self>,
13606 ) {
13607 if let Some(project) = self.project.as_ref() {
13608 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
13609 return;
13610 };
13611
13612 if buffer.read(cx).file().is_none() {
13613 return;
13614 }
13615
13616 let focused = self.focus_handle(cx).contains_focused(window, cx);
13617
13618 let project = project.clone();
13619 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
13620 self.blame_subscription =
13621 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
13622 self.blame = Some(blame);
13623 }
13624 }
13625
13626 fn toggle_git_blame_inline_internal(
13627 &mut self,
13628 user_triggered: bool,
13629 window: &mut Window,
13630 cx: &mut Context<Self>,
13631 ) {
13632 if self.git_blame_inline_enabled {
13633 self.git_blame_inline_enabled = false;
13634 self.show_git_blame_inline = false;
13635 self.show_git_blame_inline_delay_task.take();
13636 } else {
13637 self.git_blame_inline_enabled = true;
13638 self.start_git_blame_inline(user_triggered, window, cx);
13639 }
13640
13641 cx.notify();
13642 }
13643
13644 fn start_git_blame_inline(
13645 &mut self,
13646 user_triggered: bool,
13647 window: &mut Window,
13648 cx: &mut Context<Self>,
13649 ) {
13650 self.start_git_blame(user_triggered, window, cx);
13651
13652 if ProjectSettings::get_global(cx)
13653 .git
13654 .inline_blame_delay()
13655 .is_some()
13656 {
13657 self.start_inline_blame_timer(window, cx);
13658 } else {
13659 self.show_git_blame_inline = true
13660 }
13661 }
13662
13663 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
13664 self.blame.as_ref()
13665 }
13666
13667 pub fn show_git_blame_gutter(&self) -> bool {
13668 self.show_git_blame_gutter
13669 }
13670
13671 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
13672 self.show_git_blame_gutter && self.has_blame_entries(cx)
13673 }
13674
13675 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
13676 self.show_git_blame_inline
13677 && (self.focus_handle.is_focused(window)
13678 || self
13679 .git_blame_inline_tooltip
13680 .as_ref()
13681 .and_then(|t| t.upgrade())
13682 .is_some())
13683 && !self.newest_selection_head_on_empty_line(cx)
13684 && self.has_blame_entries(cx)
13685 }
13686
13687 fn has_blame_entries(&self, cx: &App) -> bool {
13688 self.blame()
13689 .map_or(false, |blame| blame.read(cx).has_generated_entries())
13690 }
13691
13692 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
13693 let cursor_anchor = self.selections.newest_anchor().head();
13694
13695 let snapshot = self.buffer.read(cx).snapshot(cx);
13696 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
13697
13698 snapshot.line_len(buffer_row) == 0
13699 }
13700
13701 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
13702 let buffer_and_selection = maybe!({
13703 let selection = self.selections.newest::<Point>(cx);
13704 let selection_range = selection.range();
13705
13706 let multi_buffer = self.buffer().read(cx);
13707 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13708 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13709
13710 let (buffer, range, _) = if selection.reversed {
13711 buffer_ranges.first()
13712 } else {
13713 buffer_ranges.last()
13714 }?;
13715
13716 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13717 ..text::ToPoint::to_point(&range.end, &buffer).row;
13718 Some((
13719 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13720 selection,
13721 ))
13722 });
13723
13724 let Some((buffer, selection)) = buffer_and_selection else {
13725 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13726 };
13727
13728 let Some(project) = self.project.as_ref() else {
13729 return Task::ready(Err(anyhow!("editor does not have project")));
13730 };
13731
13732 project.update(cx, |project, cx| {
13733 project.get_permalink_to_line(&buffer, selection, cx)
13734 })
13735 }
13736
13737 pub fn copy_permalink_to_line(
13738 &mut self,
13739 _: &CopyPermalinkToLine,
13740 window: &mut Window,
13741 cx: &mut Context<Self>,
13742 ) {
13743 let permalink_task = self.get_permalink_to_line(cx);
13744 let workspace = self.workspace();
13745
13746 cx.spawn_in(window, |_, mut cx| async move {
13747 match permalink_task.await {
13748 Ok(permalink) => {
13749 cx.update(|_, cx| {
13750 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13751 })
13752 .ok();
13753 }
13754 Err(err) => {
13755 let message = format!("Failed to copy permalink: {err}");
13756
13757 Err::<(), anyhow::Error>(err).log_err();
13758
13759 if let Some(workspace) = workspace {
13760 workspace
13761 .update_in(&mut cx, |workspace, _, cx| {
13762 struct CopyPermalinkToLine;
13763
13764 workspace.show_toast(
13765 Toast::new(
13766 NotificationId::unique::<CopyPermalinkToLine>(),
13767 message,
13768 ),
13769 cx,
13770 )
13771 })
13772 .ok();
13773 }
13774 }
13775 }
13776 })
13777 .detach();
13778 }
13779
13780 pub fn copy_file_location(
13781 &mut self,
13782 _: &CopyFileLocation,
13783 _: &mut Window,
13784 cx: &mut Context<Self>,
13785 ) {
13786 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13787 if let Some(file) = self.target_file(cx) {
13788 if let Some(path) = file.path().to_str() {
13789 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13790 }
13791 }
13792 }
13793
13794 pub fn open_permalink_to_line(
13795 &mut self,
13796 _: &OpenPermalinkToLine,
13797 window: &mut Window,
13798 cx: &mut Context<Self>,
13799 ) {
13800 let permalink_task = self.get_permalink_to_line(cx);
13801 let workspace = self.workspace();
13802
13803 cx.spawn_in(window, |_, mut cx| async move {
13804 match permalink_task.await {
13805 Ok(permalink) => {
13806 cx.update(|_, cx| {
13807 cx.open_url(permalink.as_ref());
13808 })
13809 .ok();
13810 }
13811 Err(err) => {
13812 let message = format!("Failed to open permalink: {err}");
13813
13814 Err::<(), anyhow::Error>(err).log_err();
13815
13816 if let Some(workspace) = workspace {
13817 workspace
13818 .update(&mut cx, |workspace, cx| {
13819 struct OpenPermalinkToLine;
13820
13821 workspace.show_toast(
13822 Toast::new(
13823 NotificationId::unique::<OpenPermalinkToLine>(),
13824 message,
13825 ),
13826 cx,
13827 )
13828 })
13829 .ok();
13830 }
13831 }
13832 }
13833 })
13834 .detach();
13835 }
13836
13837 pub fn insert_uuid_v4(
13838 &mut self,
13839 _: &InsertUuidV4,
13840 window: &mut Window,
13841 cx: &mut Context<Self>,
13842 ) {
13843 self.insert_uuid(UuidVersion::V4, window, cx);
13844 }
13845
13846 pub fn insert_uuid_v7(
13847 &mut self,
13848 _: &InsertUuidV7,
13849 window: &mut Window,
13850 cx: &mut Context<Self>,
13851 ) {
13852 self.insert_uuid(UuidVersion::V7, window, cx);
13853 }
13854
13855 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13856 self.transact(window, cx, |this, window, cx| {
13857 let edits = this
13858 .selections
13859 .all::<Point>(cx)
13860 .into_iter()
13861 .map(|selection| {
13862 let uuid = match version {
13863 UuidVersion::V4 => uuid::Uuid::new_v4(),
13864 UuidVersion::V7 => uuid::Uuid::now_v7(),
13865 };
13866
13867 (selection.range(), uuid.to_string())
13868 });
13869 this.edit(edits, cx);
13870 this.refresh_inline_completion(true, false, window, cx);
13871 });
13872 }
13873
13874 pub fn open_selections_in_multibuffer(
13875 &mut self,
13876 _: &OpenSelectionsInMultibuffer,
13877 window: &mut Window,
13878 cx: &mut Context<Self>,
13879 ) {
13880 let multibuffer = self.buffer.read(cx);
13881
13882 let Some(buffer) = multibuffer.as_singleton() else {
13883 return;
13884 };
13885
13886 let Some(workspace) = self.workspace() else {
13887 return;
13888 };
13889
13890 let locations = self
13891 .selections
13892 .disjoint_anchors()
13893 .iter()
13894 .map(|range| Location {
13895 buffer: buffer.clone(),
13896 range: range.start.text_anchor..range.end.text_anchor,
13897 })
13898 .collect::<Vec<_>>();
13899
13900 let title = multibuffer.title(cx).to_string();
13901
13902 cx.spawn_in(window, |_, mut cx| async move {
13903 workspace.update_in(&mut cx, |workspace, window, cx| {
13904 Self::open_locations_in_multibuffer(
13905 workspace,
13906 locations,
13907 format!("Selections for '{title}'"),
13908 false,
13909 MultibufferSelectionMode::All,
13910 window,
13911 cx,
13912 );
13913 })
13914 })
13915 .detach();
13916 }
13917
13918 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13919 /// last highlight added will be used.
13920 ///
13921 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13922 pub fn highlight_rows<T: 'static>(
13923 &mut self,
13924 range: Range<Anchor>,
13925 color: Hsla,
13926 should_autoscroll: bool,
13927 cx: &mut Context<Self>,
13928 ) {
13929 let snapshot = self.buffer().read(cx).snapshot(cx);
13930 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13931 let ix = row_highlights.binary_search_by(|highlight| {
13932 Ordering::Equal
13933 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13934 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13935 });
13936
13937 if let Err(mut ix) = ix {
13938 let index = post_inc(&mut self.highlight_order);
13939
13940 // If this range intersects with the preceding highlight, then merge it with
13941 // the preceding highlight. Otherwise insert a new highlight.
13942 let mut merged = false;
13943 if ix > 0 {
13944 let prev_highlight = &mut row_highlights[ix - 1];
13945 if prev_highlight
13946 .range
13947 .end
13948 .cmp(&range.start, &snapshot)
13949 .is_ge()
13950 {
13951 ix -= 1;
13952 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13953 prev_highlight.range.end = range.end;
13954 }
13955 merged = true;
13956 prev_highlight.index = index;
13957 prev_highlight.color = color;
13958 prev_highlight.should_autoscroll = should_autoscroll;
13959 }
13960 }
13961
13962 if !merged {
13963 row_highlights.insert(
13964 ix,
13965 RowHighlight {
13966 range: range.clone(),
13967 index,
13968 color,
13969 should_autoscroll,
13970 },
13971 );
13972 }
13973
13974 // If any of the following highlights intersect with this one, merge them.
13975 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13976 let highlight = &row_highlights[ix];
13977 if next_highlight
13978 .range
13979 .start
13980 .cmp(&highlight.range.end, &snapshot)
13981 .is_le()
13982 {
13983 if next_highlight
13984 .range
13985 .end
13986 .cmp(&highlight.range.end, &snapshot)
13987 .is_gt()
13988 {
13989 row_highlights[ix].range.end = next_highlight.range.end;
13990 }
13991 row_highlights.remove(ix + 1);
13992 } else {
13993 break;
13994 }
13995 }
13996 }
13997 }
13998
13999 /// Remove any highlighted row ranges of the given type that intersect the
14000 /// given ranges.
14001 pub fn remove_highlighted_rows<T: 'static>(
14002 &mut self,
14003 ranges_to_remove: Vec<Range<Anchor>>,
14004 cx: &mut Context<Self>,
14005 ) {
14006 let snapshot = self.buffer().read(cx).snapshot(cx);
14007 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14008 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
14009 row_highlights.retain(|highlight| {
14010 while let Some(range_to_remove) = ranges_to_remove.peek() {
14011 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
14012 Ordering::Less | Ordering::Equal => {
14013 ranges_to_remove.next();
14014 }
14015 Ordering::Greater => {
14016 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
14017 Ordering::Less | Ordering::Equal => {
14018 return false;
14019 }
14020 Ordering::Greater => break,
14021 }
14022 }
14023 }
14024 }
14025
14026 true
14027 })
14028 }
14029
14030 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
14031 pub fn clear_row_highlights<T: 'static>(&mut self) {
14032 self.highlighted_rows.remove(&TypeId::of::<T>());
14033 }
14034
14035 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14036 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14037 self.highlighted_rows
14038 .get(&TypeId::of::<T>())
14039 .map_or(&[] as &[_], |vec| vec.as_slice())
14040 .iter()
14041 .map(|highlight| (highlight.range.clone(), highlight.color))
14042 }
14043
14044 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14045 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14046 /// Allows to ignore certain kinds of highlights.
14047 pub fn highlighted_display_rows(
14048 &self,
14049 window: &mut Window,
14050 cx: &mut App,
14051 ) -> BTreeMap<DisplayRow, Background> {
14052 let snapshot = self.snapshot(window, cx);
14053 let mut used_highlight_orders = HashMap::default();
14054 self.highlighted_rows
14055 .iter()
14056 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14057 .fold(
14058 BTreeMap::<DisplayRow, Background>::new(),
14059 |mut unique_rows, highlight| {
14060 let start = highlight.range.start.to_display_point(&snapshot);
14061 let end = highlight.range.end.to_display_point(&snapshot);
14062 let start_row = start.row().0;
14063 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14064 && end.column() == 0
14065 {
14066 end.row().0.saturating_sub(1)
14067 } else {
14068 end.row().0
14069 };
14070 for row in start_row..=end_row {
14071 let used_index =
14072 used_highlight_orders.entry(row).or_insert(highlight.index);
14073 if highlight.index >= *used_index {
14074 *used_index = highlight.index;
14075 unique_rows.insert(DisplayRow(row), highlight.color.into());
14076 }
14077 }
14078 unique_rows
14079 },
14080 )
14081 }
14082
14083 pub fn highlighted_display_row_for_autoscroll(
14084 &self,
14085 snapshot: &DisplaySnapshot,
14086 ) -> Option<DisplayRow> {
14087 self.highlighted_rows
14088 .values()
14089 .flat_map(|highlighted_rows| highlighted_rows.iter())
14090 .filter_map(|highlight| {
14091 if highlight.should_autoscroll {
14092 Some(highlight.range.start.to_display_point(snapshot).row())
14093 } else {
14094 None
14095 }
14096 })
14097 .min()
14098 }
14099
14100 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14101 self.highlight_background::<SearchWithinRange>(
14102 ranges,
14103 |colors| colors.editor_document_highlight_read_background,
14104 cx,
14105 )
14106 }
14107
14108 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14109 self.breadcrumb_header = Some(new_header);
14110 }
14111
14112 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14113 self.clear_background_highlights::<SearchWithinRange>(cx);
14114 }
14115
14116 pub fn highlight_background<T: 'static>(
14117 &mut self,
14118 ranges: &[Range<Anchor>],
14119 color_fetcher: fn(&ThemeColors) -> Hsla,
14120 cx: &mut Context<Self>,
14121 ) {
14122 self.background_highlights
14123 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14124 self.scrollbar_marker_state.dirty = true;
14125 cx.notify();
14126 }
14127
14128 pub fn clear_background_highlights<T: 'static>(
14129 &mut self,
14130 cx: &mut Context<Self>,
14131 ) -> Option<BackgroundHighlight> {
14132 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14133 if !text_highlights.1.is_empty() {
14134 self.scrollbar_marker_state.dirty = true;
14135 cx.notify();
14136 }
14137 Some(text_highlights)
14138 }
14139
14140 pub fn highlight_gutter<T: 'static>(
14141 &mut self,
14142 ranges: &[Range<Anchor>],
14143 color_fetcher: fn(&App) -> Hsla,
14144 cx: &mut Context<Self>,
14145 ) {
14146 self.gutter_highlights
14147 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14148 cx.notify();
14149 }
14150
14151 pub fn clear_gutter_highlights<T: 'static>(
14152 &mut self,
14153 cx: &mut Context<Self>,
14154 ) -> Option<GutterHighlight> {
14155 cx.notify();
14156 self.gutter_highlights.remove(&TypeId::of::<T>())
14157 }
14158
14159 #[cfg(feature = "test-support")]
14160 pub fn all_text_background_highlights(
14161 &self,
14162 window: &mut Window,
14163 cx: &mut Context<Self>,
14164 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14165 let snapshot = self.snapshot(window, cx);
14166 let buffer = &snapshot.buffer_snapshot;
14167 let start = buffer.anchor_before(0);
14168 let end = buffer.anchor_after(buffer.len());
14169 let theme = cx.theme().colors();
14170 self.background_highlights_in_range(start..end, &snapshot, theme)
14171 }
14172
14173 #[cfg(feature = "test-support")]
14174 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
14175 let snapshot = self.buffer().read(cx).snapshot(cx);
14176
14177 let highlights = self
14178 .background_highlights
14179 .get(&TypeId::of::<items::BufferSearchHighlights>());
14180
14181 if let Some((_color, ranges)) = highlights {
14182 ranges
14183 .iter()
14184 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
14185 .collect_vec()
14186 } else {
14187 vec![]
14188 }
14189 }
14190
14191 fn document_highlights_for_position<'a>(
14192 &'a self,
14193 position: Anchor,
14194 buffer: &'a MultiBufferSnapshot,
14195 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
14196 let read_highlights = self
14197 .background_highlights
14198 .get(&TypeId::of::<DocumentHighlightRead>())
14199 .map(|h| &h.1);
14200 let write_highlights = self
14201 .background_highlights
14202 .get(&TypeId::of::<DocumentHighlightWrite>())
14203 .map(|h| &h.1);
14204 let left_position = position.bias_left(buffer);
14205 let right_position = position.bias_right(buffer);
14206 read_highlights
14207 .into_iter()
14208 .chain(write_highlights)
14209 .flat_map(move |ranges| {
14210 let start_ix = match ranges.binary_search_by(|probe| {
14211 let cmp = probe.end.cmp(&left_position, buffer);
14212 if cmp.is_ge() {
14213 Ordering::Greater
14214 } else {
14215 Ordering::Less
14216 }
14217 }) {
14218 Ok(i) | Err(i) => i,
14219 };
14220
14221 ranges[start_ix..]
14222 .iter()
14223 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
14224 })
14225 }
14226
14227 pub fn has_background_highlights<T: 'static>(&self) -> bool {
14228 self.background_highlights
14229 .get(&TypeId::of::<T>())
14230 .map_or(false, |(_, highlights)| !highlights.is_empty())
14231 }
14232
14233 pub fn background_highlights_in_range(
14234 &self,
14235 search_range: Range<Anchor>,
14236 display_snapshot: &DisplaySnapshot,
14237 theme: &ThemeColors,
14238 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14239 let mut results = Vec::new();
14240 for (color_fetcher, ranges) in self.background_highlights.values() {
14241 let color = color_fetcher(theme);
14242 let start_ix = match ranges.binary_search_by(|probe| {
14243 let cmp = probe
14244 .end
14245 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14246 if cmp.is_gt() {
14247 Ordering::Greater
14248 } else {
14249 Ordering::Less
14250 }
14251 }) {
14252 Ok(i) | Err(i) => i,
14253 };
14254 for range in &ranges[start_ix..] {
14255 if range
14256 .start
14257 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14258 .is_ge()
14259 {
14260 break;
14261 }
14262
14263 let start = range.start.to_display_point(display_snapshot);
14264 let end = range.end.to_display_point(display_snapshot);
14265 results.push((start..end, color))
14266 }
14267 }
14268 results
14269 }
14270
14271 pub fn background_highlight_row_ranges<T: 'static>(
14272 &self,
14273 search_range: Range<Anchor>,
14274 display_snapshot: &DisplaySnapshot,
14275 count: usize,
14276 ) -> Vec<RangeInclusive<DisplayPoint>> {
14277 let mut results = Vec::new();
14278 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
14279 return vec![];
14280 };
14281
14282 let start_ix = match ranges.binary_search_by(|probe| {
14283 let cmp = probe
14284 .end
14285 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14286 if cmp.is_gt() {
14287 Ordering::Greater
14288 } else {
14289 Ordering::Less
14290 }
14291 }) {
14292 Ok(i) | Err(i) => i,
14293 };
14294 let mut push_region = |start: Option<Point>, end: Option<Point>| {
14295 if let (Some(start_display), Some(end_display)) = (start, end) {
14296 results.push(
14297 start_display.to_display_point(display_snapshot)
14298 ..=end_display.to_display_point(display_snapshot),
14299 );
14300 }
14301 };
14302 let mut start_row: Option<Point> = None;
14303 let mut end_row: Option<Point> = None;
14304 if ranges.len() > count {
14305 return Vec::new();
14306 }
14307 for range in &ranges[start_ix..] {
14308 if range
14309 .start
14310 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14311 .is_ge()
14312 {
14313 break;
14314 }
14315 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
14316 if let Some(current_row) = &end_row {
14317 if end.row == current_row.row {
14318 continue;
14319 }
14320 }
14321 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
14322 if start_row.is_none() {
14323 assert_eq!(end_row, None);
14324 start_row = Some(start);
14325 end_row = Some(end);
14326 continue;
14327 }
14328 if let Some(current_end) = end_row.as_mut() {
14329 if start.row > current_end.row + 1 {
14330 push_region(start_row, end_row);
14331 start_row = Some(start);
14332 end_row = Some(end);
14333 } else {
14334 // Merge two hunks.
14335 *current_end = end;
14336 }
14337 } else {
14338 unreachable!();
14339 }
14340 }
14341 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
14342 push_region(start_row, end_row);
14343 results
14344 }
14345
14346 pub fn gutter_highlights_in_range(
14347 &self,
14348 search_range: Range<Anchor>,
14349 display_snapshot: &DisplaySnapshot,
14350 cx: &App,
14351 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14352 let mut results = Vec::new();
14353 for (color_fetcher, ranges) in self.gutter_highlights.values() {
14354 let color = color_fetcher(cx);
14355 let start_ix = match ranges.binary_search_by(|probe| {
14356 let cmp = probe
14357 .end
14358 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14359 if cmp.is_gt() {
14360 Ordering::Greater
14361 } else {
14362 Ordering::Less
14363 }
14364 }) {
14365 Ok(i) | Err(i) => i,
14366 };
14367 for range in &ranges[start_ix..] {
14368 if range
14369 .start
14370 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14371 .is_ge()
14372 {
14373 break;
14374 }
14375
14376 let start = range.start.to_display_point(display_snapshot);
14377 let end = range.end.to_display_point(display_snapshot);
14378 results.push((start..end, color))
14379 }
14380 }
14381 results
14382 }
14383
14384 /// Get the text ranges corresponding to the redaction query
14385 pub fn redacted_ranges(
14386 &self,
14387 search_range: Range<Anchor>,
14388 display_snapshot: &DisplaySnapshot,
14389 cx: &App,
14390 ) -> Vec<Range<DisplayPoint>> {
14391 display_snapshot
14392 .buffer_snapshot
14393 .redacted_ranges(search_range, |file| {
14394 if let Some(file) = file {
14395 file.is_private()
14396 && EditorSettings::get(
14397 Some(SettingsLocation {
14398 worktree_id: file.worktree_id(cx),
14399 path: file.path().as_ref(),
14400 }),
14401 cx,
14402 )
14403 .redact_private_values
14404 } else {
14405 false
14406 }
14407 })
14408 .map(|range| {
14409 range.start.to_display_point(display_snapshot)
14410 ..range.end.to_display_point(display_snapshot)
14411 })
14412 .collect()
14413 }
14414
14415 pub fn highlight_text<T: 'static>(
14416 &mut self,
14417 ranges: Vec<Range<Anchor>>,
14418 style: HighlightStyle,
14419 cx: &mut Context<Self>,
14420 ) {
14421 self.display_map.update(cx, |map, _| {
14422 map.highlight_text(TypeId::of::<T>(), ranges, style)
14423 });
14424 cx.notify();
14425 }
14426
14427 pub(crate) fn highlight_inlays<T: 'static>(
14428 &mut self,
14429 highlights: Vec<InlayHighlight>,
14430 style: HighlightStyle,
14431 cx: &mut Context<Self>,
14432 ) {
14433 self.display_map.update(cx, |map, _| {
14434 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
14435 });
14436 cx.notify();
14437 }
14438
14439 pub fn text_highlights<'a, T: 'static>(
14440 &'a self,
14441 cx: &'a App,
14442 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
14443 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
14444 }
14445
14446 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
14447 let cleared = self
14448 .display_map
14449 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
14450 if cleared {
14451 cx.notify();
14452 }
14453 }
14454
14455 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
14456 (self.read_only(cx) || self.blink_manager.read(cx).visible())
14457 && self.focus_handle.is_focused(window)
14458 }
14459
14460 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
14461 self.show_cursor_when_unfocused = is_enabled;
14462 cx.notify();
14463 }
14464
14465 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
14466 cx.notify();
14467 }
14468
14469 fn on_buffer_event(
14470 &mut self,
14471 multibuffer: &Entity<MultiBuffer>,
14472 event: &multi_buffer::Event,
14473 window: &mut Window,
14474 cx: &mut Context<Self>,
14475 ) {
14476 match event {
14477 multi_buffer::Event::Edited {
14478 singleton_buffer_edited,
14479 edited_buffer: buffer_edited,
14480 } => {
14481 self.scrollbar_marker_state.dirty = true;
14482 self.active_indent_guides_state.dirty = true;
14483 self.refresh_active_diagnostics(cx);
14484 self.refresh_code_actions(window, cx);
14485 if self.has_active_inline_completion() {
14486 self.update_visible_inline_completion(window, cx);
14487 }
14488 if let Some(buffer) = buffer_edited {
14489 let buffer_id = buffer.read(cx).remote_id();
14490 if !self.registered_buffers.contains_key(&buffer_id) {
14491 if let Some(project) = self.project.as_ref() {
14492 project.update(cx, |project, cx| {
14493 self.registered_buffers.insert(
14494 buffer_id,
14495 project.register_buffer_with_language_servers(&buffer, cx),
14496 );
14497 })
14498 }
14499 }
14500 }
14501 cx.emit(EditorEvent::BufferEdited);
14502 cx.emit(SearchEvent::MatchesInvalidated);
14503 if *singleton_buffer_edited {
14504 if let Some(project) = &self.project {
14505 #[allow(clippy::mutable_key_type)]
14506 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
14507 multibuffer
14508 .all_buffers()
14509 .into_iter()
14510 .filter_map(|buffer| {
14511 buffer.update(cx, |buffer, cx| {
14512 let language = buffer.language()?;
14513 let should_discard = project.update(cx, |project, cx| {
14514 project.is_local()
14515 && !project.has_language_servers_for(buffer, cx)
14516 });
14517 should_discard.not().then_some(language.clone())
14518 })
14519 })
14520 .collect::<HashSet<_>>()
14521 });
14522 if !languages_affected.is_empty() {
14523 self.refresh_inlay_hints(
14524 InlayHintRefreshReason::BufferEdited(languages_affected),
14525 cx,
14526 );
14527 }
14528 }
14529 }
14530
14531 let Some(project) = &self.project else { return };
14532 let (telemetry, is_via_ssh) = {
14533 let project = project.read(cx);
14534 let telemetry = project.client().telemetry().clone();
14535 let is_via_ssh = project.is_via_ssh();
14536 (telemetry, is_via_ssh)
14537 };
14538 refresh_linked_ranges(self, window, cx);
14539 telemetry.log_edit_event("editor", is_via_ssh);
14540 }
14541 multi_buffer::Event::ExcerptsAdded {
14542 buffer,
14543 predecessor,
14544 excerpts,
14545 } => {
14546 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14547 let buffer_id = buffer.read(cx).remote_id();
14548 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
14549 if let Some(project) = &self.project {
14550 get_uncommitted_diff_for_buffer(
14551 project,
14552 [buffer.clone()],
14553 self.buffer.clone(),
14554 cx,
14555 )
14556 .detach();
14557 }
14558 }
14559 cx.emit(EditorEvent::ExcerptsAdded {
14560 buffer: buffer.clone(),
14561 predecessor: *predecessor,
14562 excerpts: excerpts.clone(),
14563 });
14564 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14565 }
14566 multi_buffer::Event::ExcerptsRemoved { ids } => {
14567 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
14568 let buffer = self.buffer.read(cx);
14569 self.registered_buffers
14570 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
14571 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
14572 }
14573 multi_buffer::Event::ExcerptsEdited { ids } => {
14574 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
14575 }
14576 multi_buffer::Event::ExcerptsExpanded { ids } => {
14577 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14578 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
14579 }
14580 multi_buffer::Event::Reparsed(buffer_id) => {
14581 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14582
14583 cx.emit(EditorEvent::Reparsed(*buffer_id));
14584 }
14585 multi_buffer::Event::DiffHunksToggled => {
14586 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14587 }
14588 multi_buffer::Event::LanguageChanged(buffer_id) => {
14589 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
14590 cx.emit(EditorEvent::Reparsed(*buffer_id));
14591 cx.notify();
14592 }
14593 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
14594 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
14595 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
14596 cx.emit(EditorEvent::TitleChanged)
14597 }
14598 // multi_buffer::Event::DiffBaseChanged => {
14599 // self.scrollbar_marker_state.dirty = true;
14600 // cx.emit(EditorEvent::DiffBaseChanged);
14601 // cx.notify();
14602 // }
14603 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
14604 multi_buffer::Event::DiagnosticsUpdated => {
14605 self.refresh_active_diagnostics(cx);
14606 self.refresh_inline_diagnostics(true, window, cx);
14607 self.scrollbar_marker_state.dirty = true;
14608 cx.notify();
14609 }
14610 _ => {}
14611 };
14612 }
14613
14614 fn on_display_map_changed(
14615 &mut self,
14616 _: Entity<DisplayMap>,
14617 _: &mut Window,
14618 cx: &mut Context<Self>,
14619 ) {
14620 cx.notify();
14621 }
14622
14623 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14624 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14625 self.refresh_inline_completion(true, false, window, cx);
14626 self.refresh_inlay_hints(
14627 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
14628 self.selections.newest_anchor().head(),
14629 &self.buffer.read(cx).snapshot(cx),
14630 cx,
14631 )),
14632 cx,
14633 );
14634
14635 let old_cursor_shape = self.cursor_shape;
14636
14637 {
14638 let editor_settings = EditorSettings::get_global(cx);
14639 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
14640 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
14641 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
14642 }
14643
14644 if old_cursor_shape != self.cursor_shape {
14645 cx.emit(EditorEvent::CursorShapeChanged);
14646 }
14647
14648 let project_settings = ProjectSettings::get_global(cx);
14649 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
14650
14651 if self.mode == EditorMode::Full {
14652 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
14653 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
14654 if self.show_inline_diagnostics != show_inline_diagnostics {
14655 self.show_inline_diagnostics = show_inline_diagnostics;
14656 self.refresh_inline_diagnostics(false, window, cx);
14657 }
14658
14659 if self.git_blame_inline_enabled != inline_blame_enabled {
14660 self.toggle_git_blame_inline_internal(false, window, cx);
14661 }
14662 }
14663
14664 cx.notify();
14665 }
14666
14667 pub fn set_searchable(&mut self, searchable: bool) {
14668 self.searchable = searchable;
14669 }
14670
14671 pub fn searchable(&self) -> bool {
14672 self.searchable
14673 }
14674
14675 fn open_proposed_changes_editor(
14676 &mut self,
14677 _: &OpenProposedChangesEditor,
14678 window: &mut Window,
14679 cx: &mut Context<Self>,
14680 ) {
14681 let Some(workspace) = self.workspace() else {
14682 cx.propagate();
14683 return;
14684 };
14685
14686 let selections = self.selections.all::<usize>(cx);
14687 let multi_buffer = self.buffer.read(cx);
14688 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14689 let mut new_selections_by_buffer = HashMap::default();
14690 for selection in selections {
14691 for (buffer, range, _) in
14692 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
14693 {
14694 let mut range = range.to_point(buffer);
14695 range.start.column = 0;
14696 range.end.column = buffer.line_len(range.end.row);
14697 new_selections_by_buffer
14698 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
14699 .or_insert(Vec::new())
14700 .push(range)
14701 }
14702 }
14703
14704 let proposed_changes_buffers = new_selections_by_buffer
14705 .into_iter()
14706 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14707 .collect::<Vec<_>>();
14708 let proposed_changes_editor = cx.new(|cx| {
14709 ProposedChangesEditor::new(
14710 "Proposed changes",
14711 proposed_changes_buffers,
14712 self.project.clone(),
14713 window,
14714 cx,
14715 )
14716 });
14717
14718 window.defer(cx, move |window, cx| {
14719 workspace.update(cx, |workspace, cx| {
14720 workspace.active_pane().update(cx, |pane, cx| {
14721 pane.add_item(
14722 Box::new(proposed_changes_editor),
14723 true,
14724 true,
14725 None,
14726 window,
14727 cx,
14728 );
14729 });
14730 });
14731 });
14732 }
14733
14734 pub fn open_excerpts_in_split(
14735 &mut self,
14736 _: &OpenExcerptsSplit,
14737 window: &mut Window,
14738 cx: &mut Context<Self>,
14739 ) {
14740 self.open_excerpts_common(None, true, window, cx)
14741 }
14742
14743 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14744 self.open_excerpts_common(None, false, window, cx)
14745 }
14746
14747 fn open_excerpts_common(
14748 &mut self,
14749 jump_data: Option<JumpData>,
14750 split: bool,
14751 window: &mut Window,
14752 cx: &mut Context<Self>,
14753 ) {
14754 let Some(workspace) = self.workspace() else {
14755 cx.propagate();
14756 return;
14757 };
14758
14759 if self.buffer.read(cx).is_singleton() {
14760 cx.propagate();
14761 return;
14762 }
14763
14764 let mut new_selections_by_buffer = HashMap::default();
14765 match &jump_data {
14766 Some(JumpData::MultiBufferPoint {
14767 excerpt_id,
14768 position,
14769 anchor,
14770 line_offset_from_top,
14771 }) => {
14772 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14773 if let Some(buffer) = multi_buffer_snapshot
14774 .buffer_id_for_excerpt(*excerpt_id)
14775 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14776 {
14777 let buffer_snapshot = buffer.read(cx).snapshot();
14778 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14779 language::ToPoint::to_point(anchor, &buffer_snapshot)
14780 } else {
14781 buffer_snapshot.clip_point(*position, Bias::Left)
14782 };
14783 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14784 new_selections_by_buffer.insert(
14785 buffer,
14786 (
14787 vec![jump_to_offset..jump_to_offset],
14788 Some(*line_offset_from_top),
14789 ),
14790 );
14791 }
14792 }
14793 Some(JumpData::MultiBufferRow {
14794 row,
14795 line_offset_from_top,
14796 }) => {
14797 let point = MultiBufferPoint::new(row.0, 0);
14798 if let Some((buffer, buffer_point, _)) =
14799 self.buffer.read(cx).point_to_buffer_point(point, cx)
14800 {
14801 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14802 new_selections_by_buffer
14803 .entry(buffer)
14804 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14805 .0
14806 .push(buffer_offset..buffer_offset)
14807 }
14808 }
14809 None => {
14810 let selections = self.selections.all::<usize>(cx);
14811 let multi_buffer = self.buffer.read(cx);
14812 for selection in selections {
14813 for (buffer, mut range, _) in multi_buffer
14814 .snapshot(cx)
14815 .range_to_buffer_ranges(selection.range())
14816 {
14817 // When editing branch buffers, jump to the corresponding location
14818 // in their base buffer.
14819 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14820 let buffer = buffer_handle.read(cx);
14821 if let Some(base_buffer) = buffer.base_buffer() {
14822 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14823 buffer_handle = base_buffer;
14824 }
14825
14826 if selection.reversed {
14827 mem::swap(&mut range.start, &mut range.end);
14828 }
14829 new_selections_by_buffer
14830 .entry(buffer_handle)
14831 .or_insert((Vec::new(), None))
14832 .0
14833 .push(range)
14834 }
14835 }
14836 }
14837 }
14838
14839 if new_selections_by_buffer.is_empty() {
14840 return;
14841 }
14842
14843 // We defer the pane interaction because we ourselves are a workspace item
14844 // and activating a new item causes the pane to call a method on us reentrantly,
14845 // which panics if we're on the stack.
14846 window.defer(cx, move |window, cx| {
14847 workspace.update(cx, |workspace, cx| {
14848 let pane = if split {
14849 workspace.adjacent_pane(window, cx)
14850 } else {
14851 workspace.active_pane().clone()
14852 };
14853
14854 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14855 let editor = buffer
14856 .read(cx)
14857 .file()
14858 .is_none()
14859 .then(|| {
14860 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14861 // so `workspace.open_project_item` will never find them, always opening a new editor.
14862 // Instead, we try to activate the existing editor in the pane first.
14863 let (editor, pane_item_index) =
14864 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14865 let editor = item.downcast::<Editor>()?;
14866 let singleton_buffer =
14867 editor.read(cx).buffer().read(cx).as_singleton()?;
14868 if singleton_buffer == buffer {
14869 Some((editor, i))
14870 } else {
14871 None
14872 }
14873 })?;
14874 pane.update(cx, |pane, cx| {
14875 pane.activate_item(pane_item_index, true, true, window, cx)
14876 });
14877 Some(editor)
14878 })
14879 .flatten()
14880 .unwrap_or_else(|| {
14881 workspace.open_project_item::<Self>(
14882 pane.clone(),
14883 buffer,
14884 true,
14885 true,
14886 window,
14887 cx,
14888 )
14889 });
14890
14891 editor.update(cx, |editor, cx| {
14892 let autoscroll = match scroll_offset {
14893 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14894 None => Autoscroll::newest(),
14895 };
14896 let nav_history = editor.nav_history.take();
14897 editor.change_selections(Some(autoscroll), window, cx, |s| {
14898 s.select_ranges(ranges);
14899 });
14900 editor.nav_history = nav_history;
14901 });
14902 }
14903 })
14904 });
14905 }
14906
14907 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14908 let snapshot = self.buffer.read(cx).read(cx);
14909 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14910 Some(
14911 ranges
14912 .iter()
14913 .map(move |range| {
14914 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14915 })
14916 .collect(),
14917 )
14918 }
14919
14920 fn selection_replacement_ranges(
14921 &self,
14922 range: Range<OffsetUtf16>,
14923 cx: &mut App,
14924 ) -> Vec<Range<OffsetUtf16>> {
14925 let selections = self.selections.all::<OffsetUtf16>(cx);
14926 let newest_selection = selections
14927 .iter()
14928 .max_by_key(|selection| selection.id)
14929 .unwrap();
14930 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14931 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14932 let snapshot = self.buffer.read(cx).read(cx);
14933 selections
14934 .into_iter()
14935 .map(|mut selection| {
14936 selection.start.0 =
14937 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14938 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14939 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14940 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14941 })
14942 .collect()
14943 }
14944
14945 fn report_editor_event(
14946 &self,
14947 event_type: &'static str,
14948 file_extension: Option<String>,
14949 cx: &App,
14950 ) {
14951 if cfg!(any(test, feature = "test-support")) {
14952 return;
14953 }
14954
14955 let Some(project) = &self.project else { return };
14956
14957 // If None, we are in a file without an extension
14958 let file = self
14959 .buffer
14960 .read(cx)
14961 .as_singleton()
14962 .and_then(|b| b.read(cx).file());
14963 let file_extension = file_extension.or(file
14964 .as_ref()
14965 .and_then(|file| Path::new(file.file_name(cx)).extension())
14966 .and_then(|e| e.to_str())
14967 .map(|a| a.to_string()));
14968
14969 let vim_mode = cx
14970 .global::<SettingsStore>()
14971 .raw_user_settings()
14972 .get("vim_mode")
14973 == Some(&serde_json::Value::Bool(true));
14974
14975 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14976 let copilot_enabled = edit_predictions_provider
14977 == language::language_settings::EditPredictionProvider::Copilot;
14978 let copilot_enabled_for_language = self
14979 .buffer
14980 .read(cx)
14981 .settings_at(0, cx)
14982 .show_edit_predictions;
14983
14984 let project = project.read(cx);
14985 telemetry::event!(
14986 event_type,
14987 file_extension,
14988 vim_mode,
14989 copilot_enabled,
14990 copilot_enabled_for_language,
14991 edit_predictions_provider,
14992 is_via_ssh = project.is_via_ssh(),
14993 );
14994 }
14995
14996 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14997 /// with each line being an array of {text, highlight} objects.
14998 fn copy_highlight_json(
14999 &mut self,
15000 _: &CopyHighlightJson,
15001 window: &mut Window,
15002 cx: &mut Context<Self>,
15003 ) {
15004 #[derive(Serialize)]
15005 struct Chunk<'a> {
15006 text: String,
15007 highlight: Option<&'a str>,
15008 }
15009
15010 let snapshot = self.buffer.read(cx).snapshot(cx);
15011 let range = self
15012 .selected_text_range(false, window, cx)
15013 .and_then(|selection| {
15014 if selection.range.is_empty() {
15015 None
15016 } else {
15017 Some(selection.range)
15018 }
15019 })
15020 .unwrap_or_else(|| 0..snapshot.len());
15021
15022 let chunks = snapshot.chunks(range, true);
15023 let mut lines = Vec::new();
15024 let mut line: VecDeque<Chunk> = VecDeque::new();
15025
15026 let Some(style) = self.style.as_ref() else {
15027 return;
15028 };
15029
15030 for chunk in chunks {
15031 let highlight = chunk
15032 .syntax_highlight_id
15033 .and_then(|id| id.name(&style.syntax));
15034 let mut chunk_lines = chunk.text.split('\n').peekable();
15035 while let Some(text) = chunk_lines.next() {
15036 let mut merged_with_last_token = false;
15037 if let Some(last_token) = line.back_mut() {
15038 if last_token.highlight == highlight {
15039 last_token.text.push_str(text);
15040 merged_with_last_token = true;
15041 }
15042 }
15043
15044 if !merged_with_last_token {
15045 line.push_back(Chunk {
15046 text: text.into(),
15047 highlight,
15048 });
15049 }
15050
15051 if chunk_lines.peek().is_some() {
15052 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15053 line.pop_front();
15054 }
15055 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15056 line.pop_back();
15057 }
15058
15059 lines.push(mem::take(&mut line));
15060 }
15061 }
15062 }
15063
15064 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15065 return;
15066 };
15067 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15068 }
15069
15070 pub fn open_context_menu(
15071 &mut self,
15072 _: &OpenContextMenu,
15073 window: &mut Window,
15074 cx: &mut Context<Self>,
15075 ) {
15076 self.request_autoscroll(Autoscroll::newest(), cx);
15077 let position = self.selections.newest_display(cx).start;
15078 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15079 }
15080
15081 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15082 &self.inlay_hint_cache
15083 }
15084
15085 pub fn replay_insert_event(
15086 &mut self,
15087 text: &str,
15088 relative_utf16_range: Option<Range<isize>>,
15089 window: &mut Window,
15090 cx: &mut Context<Self>,
15091 ) {
15092 if !self.input_enabled {
15093 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15094 return;
15095 }
15096 if let Some(relative_utf16_range) = relative_utf16_range {
15097 let selections = self.selections.all::<OffsetUtf16>(cx);
15098 self.change_selections(None, window, cx, |s| {
15099 let new_ranges = selections.into_iter().map(|range| {
15100 let start = OffsetUtf16(
15101 range
15102 .head()
15103 .0
15104 .saturating_add_signed(relative_utf16_range.start),
15105 );
15106 let end = OffsetUtf16(
15107 range
15108 .head()
15109 .0
15110 .saturating_add_signed(relative_utf16_range.end),
15111 );
15112 start..end
15113 });
15114 s.select_ranges(new_ranges);
15115 });
15116 }
15117
15118 self.handle_input(text, window, cx);
15119 }
15120
15121 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15122 let Some(provider) = self.semantics_provider.as_ref() else {
15123 return false;
15124 };
15125
15126 let mut supports = false;
15127 self.buffer().update(cx, |this, cx| {
15128 this.for_each_buffer(|buffer| {
15129 supports |= provider.supports_inlay_hints(buffer, cx);
15130 });
15131 });
15132
15133 supports
15134 }
15135
15136 pub fn is_focused(&self, window: &Window) -> bool {
15137 self.focus_handle.is_focused(window)
15138 }
15139
15140 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15141 cx.emit(EditorEvent::Focused);
15142
15143 if let Some(descendant) = self
15144 .last_focused_descendant
15145 .take()
15146 .and_then(|descendant| descendant.upgrade())
15147 {
15148 window.focus(&descendant);
15149 } else {
15150 if let Some(blame) = self.blame.as_ref() {
15151 blame.update(cx, GitBlame::focus)
15152 }
15153
15154 self.blink_manager.update(cx, BlinkManager::enable);
15155 self.show_cursor_names(window, cx);
15156 self.buffer.update(cx, |buffer, cx| {
15157 buffer.finalize_last_transaction(cx);
15158 if self.leader_peer_id.is_none() {
15159 buffer.set_active_selections(
15160 &self.selections.disjoint_anchors(),
15161 self.selections.line_mode,
15162 self.cursor_shape,
15163 cx,
15164 );
15165 }
15166 });
15167 }
15168 }
15169
15170 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15171 cx.emit(EditorEvent::FocusedIn)
15172 }
15173
15174 fn handle_focus_out(
15175 &mut self,
15176 event: FocusOutEvent,
15177 _window: &mut Window,
15178 _cx: &mut Context<Self>,
15179 ) {
15180 if event.blurred != self.focus_handle {
15181 self.last_focused_descendant = Some(event.blurred);
15182 }
15183 }
15184
15185 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15186 self.blink_manager.update(cx, BlinkManager::disable);
15187 self.buffer
15188 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
15189
15190 if let Some(blame) = self.blame.as_ref() {
15191 blame.update(cx, GitBlame::blur)
15192 }
15193 if !self.hover_state.focused(window, cx) {
15194 hide_hover(self, cx);
15195 }
15196 if !self
15197 .context_menu
15198 .borrow()
15199 .as_ref()
15200 .is_some_and(|context_menu| context_menu.focused(window, cx))
15201 {
15202 self.hide_context_menu(window, cx);
15203 }
15204 self.discard_inline_completion(false, cx);
15205 cx.emit(EditorEvent::Blurred);
15206 cx.notify();
15207 }
15208
15209 pub fn register_action<A: Action>(
15210 &mut self,
15211 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
15212 ) -> Subscription {
15213 let id = self.next_editor_action_id.post_inc();
15214 let listener = Arc::new(listener);
15215 self.editor_actions.borrow_mut().insert(
15216 id,
15217 Box::new(move |window, _| {
15218 let listener = listener.clone();
15219 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
15220 let action = action.downcast_ref().unwrap();
15221 if phase == DispatchPhase::Bubble {
15222 listener(action, window, cx)
15223 }
15224 })
15225 }),
15226 );
15227
15228 let editor_actions = self.editor_actions.clone();
15229 Subscription::new(move || {
15230 editor_actions.borrow_mut().remove(&id);
15231 })
15232 }
15233
15234 pub fn file_header_size(&self) -> u32 {
15235 FILE_HEADER_HEIGHT
15236 }
15237
15238 pub fn revert(
15239 &mut self,
15240 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
15241 window: &mut Window,
15242 cx: &mut Context<Self>,
15243 ) {
15244 self.buffer().update(cx, |multi_buffer, cx| {
15245 for (buffer_id, changes) in revert_changes {
15246 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15247 buffer.update(cx, |buffer, cx| {
15248 buffer.edit(
15249 changes.into_iter().map(|(range, text)| {
15250 (range, text.to_string().map(Arc::<str>::from))
15251 }),
15252 None,
15253 cx,
15254 );
15255 });
15256 }
15257 }
15258 });
15259 self.change_selections(None, window, cx, |selections| selections.refresh());
15260 }
15261
15262 pub fn to_pixel_point(
15263 &self,
15264 source: multi_buffer::Anchor,
15265 editor_snapshot: &EditorSnapshot,
15266 window: &mut Window,
15267 ) -> Option<gpui::Point<Pixels>> {
15268 let source_point = source.to_display_point(editor_snapshot);
15269 self.display_to_pixel_point(source_point, editor_snapshot, window)
15270 }
15271
15272 pub fn display_to_pixel_point(
15273 &self,
15274 source: DisplayPoint,
15275 editor_snapshot: &EditorSnapshot,
15276 window: &mut Window,
15277 ) -> Option<gpui::Point<Pixels>> {
15278 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
15279 let text_layout_details = self.text_layout_details(window);
15280 let scroll_top = text_layout_details
15281 .scroll_anchor
15282 .scroll_position(editor_snapshot)
15283 .y;
15284
15285 if source.row().as_f32() < scroll_top.floor() {
15286 return None;
15287 }
15288 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
15289 let source_y = line_height * (source.row().as_f32() - scroll_top);
15290 Some(gpui::Point::new(source_x, source_y))
15291 }
15292
15293 pub fn has_visible_completions_menu(&self) -> bool {
15294 !self.edit_prediction_preview_is_active()
15295 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
15296 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
15297 })
15298 }
15299
15300 pub fn register_addon<T: Addon>(&mut self, instance: T) {
15301 self.addons
15302 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
15303 }
15304
15305 pub fn unregister_addon<T: Addon>(&mut self) {
15306 self.addons.remove(&std::any::TypeId::of::<T>());
15307 }
15308
15309 pub fn addon<T: Addon>(&self) -> Option<&T> {
15310 let type_id = std::any::TypeId::of::<T>();
15311 self.addons
15312 .get(&type_id)
15313 .and_then(|item| item.to_any().downcast_ref::<T>())
15314 }
15315
15316 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
15317 let text_layout_details = self.text_layout_details(window);
15318 let style = &text_layout_details.editor_style;
15319 let font_id = window.text_system().resolve_font(&style.text.font());
15320 let font_size = style.text.font_size.to_pixels(window.rem_size());
15321 let line_height = style.text.line_height_in_pixels(window.rem_size());
15322 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
15323
15324 gpui::Size::new(em_width, line_height)
15325 }
15326
15327 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
15328 self.load_diff_task.clone()
15329 }
15330
15331 fn read_selections_from_db(
15332 &mut self,
15333 item_id: u64,
15334 workspace_id: WorkspaceId,
15335 window: &mut Window,
15336 cx: &mut Context<Editor>,
15337 ) {
15338 if !self.is_singleton(cx)
15339 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
15340 {
15341 return;
15342 }
15343 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
15344 return;
15345 };
15346 if selections.is_empty() {
15347 return;
15348 }
15349
15350 let snapshot = self.buffer.read(cx).snapshot(cx);
15351 self.change_selections(None, window, cx, |s| {
15352 s.select_ranges(selections.into_iter().map(|(start, end)| {
15353 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
15354 }));
15355 });
15356 }
15357}
15358
15359fn insert_extra_newline_brackets(
15360 buffer: &MultiBufferSnapshot,
15361 range: Range<usize>,
15362 language: &language::LanguageScope,
15363) -> bool {
15364 let leading_whitespace_len = buffer
15365 .reversed_chars_at(range.start)
15366 .take_while(|c| c.is_whitespace() && *c != '\n')
15367 .map(|c| c.len_utf8())
15368 .sum::<usize>();
15369 let trailing_whitespace_len = buffer
15370 .chars_at(range.end)
15371 .take_while(|c| c.is_whitespace() && *c != '\n')
15372 .map(|c| c.len_utf8())
15373 .sum::<usize>();
15374 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
15375
15376 language.brackets().any(|(pair, enabled)| {
15377 let pair_start = pair.start.trim_end();
15378 let pair_end = pair.end.trim_start();
15379
15380 enabled
15381 && pair.newline
15382 && buffer.contains_str_at(range.end, pair_end)
15383 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
15384 })
15385}
15386
15387fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
15388 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
15389 [(buffer, range, _)] => (*buffer, range.clone()),
15390 _ => return false,
15391 };
15392 let pair = {
15393 let mut result: Option<BracketMatch> = None;
15394
15395 for pair in buffer
15396 .all_bracket_ranges(range.clone())
15397 .filter(move |pair| {
15398 pair.open_range.start <= range.start && pair.close_range.end >= range.end
15399 })
15400 {
15401 let len = pair.close_range.end - pair.open_range.start;
15402
15403 if let Some(existing) = &result {
15404 let existing_len = existing.close_range.end - existing.open_range.start;
15405 if len > existing_len {
15406 continue;
15407 }
15408 }
15409
15410 result = Some(pair);
15411 }
15412
15413 result
15414 };
15415 let Some(pair) = pair else {
15416 return false;
15417 };
15418 pair.newline_only
15419 && buffer
15420 .chars_for_range(pair.open_range.end..range.start)
15421 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
15422 .all(|c| c.is_whitespace() && c != '\n')
15423}
15424
15425fn get_uncommitted_diff_for_buffer(
15426 project: &Entity<Project>,
15427 buffers: impl IntoIterator<Item = Entity<Buffer>>,
15428 buffer: Entity<MultiBuffer>,
15429 cx: &mut App,
15430) -> Task<()> {
15431 let mut tasks = Vec::new();
15432 project.update(cx, |project, cx| {
15433 for buffer in buffers {
15434 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
15435 }
15436 });
15437 cx.spawn(|mut cx| async move {
15438 let diffs = futures::future::join_all(tasks).await;
15439 buffer
15440 .update(&mut cx, |buffer, cx| {
15441 for diff in diffs.into_iter().flatten() {
15442 buffer.add_diff(diff, cx);
15443 }
15444 })
15445 .ok();
15446 })
15447}
15448
15449fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
15450 let tab_size = tab_size.get() as usize;
15451 let mut width = offset;
15452
15453 for ch in text.chars() {
15454 width += if ch == '\t' {
15455 tab_size - (width % tab_size)
15456 } else {
15457 1
15458 };
15459 }
15460
15461 width - offset
15462}
15463
15464#[cfg(test)]
15465mod tests {
15466 use super::*;
15467
15468 #[test]
15469 fn test_string_size_with_expanded_tabs() {
15470 let nz = |val| NonZeroU32::new(val).unwrap();
15471 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
15472 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
15473 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
15474 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
15475 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
15476 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
15477 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
15478 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
15479 }
15480}
15481
15482/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
15483struct WordBreakingTokenizer<'a> {
15484 input: &'a str,
15485}
15486
15487impl<'a> WordBreakingTokenizer<'a> {
15488 fn new(input: &'a str) -> Self {
15489 Self { input }
15490 }
15491}
15492
15493fn is_char_ideographic(ch: char) -> bool {
15494 use unicode_script::Script::*;
15495 use unicode_script::UnicodeScript;
15496 matches!(ch.script(), Han | Tangut | Yi)
15497}
15498
15499fn is_grapheme_ideographic(text: &str) -> bool {
15500 text.chars().any(is_char_ideographic)
15501}
15502
15503fn is_grapheme_whitespace(text: &str) -> bool {
15504 text.chars().any(|x| x.is_whitespace())
15505}
15506
15507fn should_stay_with_preceding_ideograph(text: &str) -> bool {
15508 text.chars().next().map_or(false, |ch| {
15509 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
15510 })
15511}
15512
15513#[derive(PartialEq, Eq, Debug, Clone, Copy)]
15514struct WordBreakToken<'a> {
15515 token: &'a str,
15516 grapheme_len: usize,
15517 is_whitespace: bool,
15518}
15519
15520impl<'a> Iterator for WordBreakingTokenizer<'a> {
15521 /// Yields a span, the count of graphemes in the token, and whether it was
15522 /// whitespace. Note that it also breaks at word boundaries.
15523 type Item = WordBreakToken<'a>;
15524
15525 fn next(&mut self) -> Option<Self::Item> {
15526 use unicode_segmentation::UnicodeSegmentation;
15527 if self.input.is_empty() {
15528 return None;
15529 }
15530
15531 let mut iter = self.input.graphemes(true).peekable();
15532 let mut offset = 0;
15533 let mut graphemes = 0;
15534 if let Some(first_grapheme) = iter.next() {
15535 let is_whitespace = is_grapheme_whitespace(first_grapheme);
15536 offset += first_grapheme.len();
15537 graphemes += 1;
15538 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
15539 if let Some(grapheme) = iter.peek().copied() {
15540 if should_stay_with_preceding_ideograph(grapheme) {
15541 offset += grapheme.len();
15542 graphemes += 1;
15543 }
15544 }
15545 } else {
15546 let mut words = self.input[offset..].split_word_bound_indices().peekable();
15547 let mut next_word_bound = words.peek().copied();
15548 if next_word_bound.map_or(false, |(i, _)| i == 0) {
15549 next_word_bound = words.next();
15550 }
15551 while let Some(grapheme) = iter.peek().copied() {
15552 if next_word_bound.map_or(false, |(i, _)| i == offset) {
15553 break;
15554 };
15555 if is_grapheme_whitespace(grapheme) != is_whitespace {
15556 break;
15557 };
15558 offset += grapheme.len();
15559 graphemes += 1;
15560 iter.next();
15561 }
15562 }
15563 let token = &self.input[..offset];
15564 self.input = &self.input[offset..];
15565 if is_whitespace {
15566 Some(WordBreakToken {
15567 token: " ",
15568 grapheme_len: 1,
15569 is_whitespace: true,
15570 })
15571 } else {
15572 Some(WordBreakToken {
15573 token,
15574 grapheme_len: graphemes,
15575 is_whitespace: false,
15576 })
15577 }
15578 } else {
15579 None
15580 }
15581 }
15582}
15583
15584#[test]
15585fn test_word_breaking_tokenizer() {
15586 let tests: &[(&str, &[(&str, usize, bool)])] = &[
15587 ("", &[]),
15588 (" ", &[(" ", 1, true)]),
15589 ("Ʒ", &[("Ʒ", 1, false)]),
15590 ("Ǽ", &[("Ǽ", 1, false)]),
15591 ("⋑", &[("⋑", 1, false)]),
15592 ("⋑⋑", &[("⋑⋑", 2, false)]),
15593 (
15594 "原理,进而",
15595 &[
15596 ("原", 1, false),
15597 ("理,", 2, false),
15598 ("进", 1, false),
15599 ("而", 1, false),
15600 ],
15601 ),
15602 (
15603 "hello world",
15604 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
15605 ),
15606 (
15607 "hello, world",
15608 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
15609 ),
15610 (
15611 " hello world",
15612 &[
15613 (" ", 1, true),
15614 ("hello", 5, false),
15615 (" ", 1, true),
15616 ("world", 5, false),
15617 ],
15618 ),
15619 (
15620 "这是什么 \n 钢笔",
15621 &[
15622 ("这", 1, false),
15623 ("是", 1, false),
15624 ("什", 1, false),
15625 ("么", 1, false),
15626 (" ", 1, true),
15627 ("钢", 1, false),
15628 ("笔", 1, false),
15629 ],
15630 ),
15631 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
15632 ];
15633
15634 for (input, result) in tests {
15635 assert_eq!(
15636 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
15637 result
15638 .iter()
15639 .copied()
15640 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
15641 token,
15642 grapheme_len,
15643 is_whitespace,
15644 })
15645 .collect::<Vec<_>>()
15646 );
15647 }
15648}
15649
15650fn wrap_with_prefix(
15651 line_prefix: String,
15652 unwrapped_text: String,
15653 wrap_column: usize,
15654 tab_size: NonZeroU32,
15655) -> String {
15656 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
15657 let mut wrapped_text = String::new();
15658 let mut current_line = line_prefix.clone();
15659
15660 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
15661 let mut current_line_len = line_prefix_len;
15662 for WordBreakToken {
15663 token,
15664 grapheme_len,
15665 is_whitespace,
15666 } in tokenizer
15667 {
15668 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
15669 wrapped_text.push_str(current_line.trim_end());
15670 wrapped_text.push('\n');
15671 current_line.truncate(line_prefix.len());
15672 current_line_len = line_prefix_len;
15673 if !is_whitespace {
15674 current_line.push_str(token);
15675 current_line_len += grapheme_len;
15676 }
15677 } else if !is_whitespace {
15678 current_line.push_str(token);
15679 current_line_len += grapheme_len;
15680 } else if current_line_len != line_prefix_len {
15681 current_line.push(' ');
15682 current_line_len += 1;
15683 }
15684 }
15685
15686 if !current_line.is_empty() {
15687 wrapped_text.push_str(¤t_line);
15688 }
15689 wrapped_text
15690}
15691
15692#[test]
15693fn test_wrap_with_prefix() {
15694 assert_eq!(
15695 wrap_with_prefix(
15696 "# ".to_string(),
15697 "abcdefg".to_string(),
15698 4,
15699 NonZeroU32::new(4).unwrap()
15700 ),
15701 "# abcdefg"
15702 );
15703 assert_eq!(
15704 wrap_with_prefix(
15705 "".to_string(),
15706 "\thello world".to_string(),
15707 8,
15708 NonZeroU32::new(4).unwrap()
15709 ),
15710 "hello\nworld"
15711 );
15712 assert_eq!(
15713 wrap_with_prefix(
15714 "// ".to_string(),
15715 "xx \nyy zz aa bb cc".to_string(),
15716 12,
15717 NonZeroU32::new(4).unwrap()
15718 ),
15719 "// xx yy zz\n// aa bb cc"
15720 );
15721 assert_eq!(
15722 wrap_with_prefix(
15723 String::new(),
15724 "这是什么 \n 钢笔".to_string(),
15725 3,
15726 NonZeroU32::new(4).unwrap()
15727 ),
15728 "这是什\n么 钢\n笔"
15729 );
15730}
15731
15732pub trait CollaborationHub {
15733 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
15734 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
15735 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
15736}
15737
15738impl CollaborationHub for Entity<Project> {
15739 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
15740 self.read(cx).collaborators()
15741 }
15742
15743 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
15744 self.read(cx).user_store().read(cx).participant_indices()
15745 }
15746
15747 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
15748 let this = self.read(cx);
15749 let user_ids = this.collaborators().values().map(|c| c.user_id);
15750 this.user_store().read_with(cx, |user_store, cx| {
15751 user_store.participant_names(user_ids, cx)
15752 })
15753 }
15754}
15755
15756pub trait SemanticsProvider {
15757 fn hover(
15758 &self,
15759 buffer: &Entity<Buffer>,
15760 position: text::Anchor,
15761 cx: &mut App,
15762 ) -> Option<Task<Vec<project::Hover>>>;
15763
15764 fn inlay_hints(
15765 &self,
15766 buffer_handle: Entity<Buffer>,
15767 range: Range<text::Anchor>,
15768 cx: &mut App,
15769 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
15770
15771 fn resolve_inlay_hint(
15772 &self,
15773 hint: InlayHint,
15774 buffer_handle: Entity<Buffer>,
15775 server_id: LanguageServerId,
15776 cx: &mut App,
15777 ) -> Option<Task<anyhow::Result<InlayHint>>>;
15778
15779 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
15780
15781 fn document_highlights(
15782 &self,
15783 buffer: &Entity<Buffer>,
15784 position: text::Anchor,
15785 cx: &mut App,
15786 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
15787
15788 fn definitions(
15789 &self,
15790 buffer: &Entity<Buffer>,
15791 position: text::Anchor,
15792 kind: GotoDefinitionKind,
15793 cx: &mut App,
15794 ) -> Option<Task<Result<Vec<LocationLink>>>>;
15795
15796 fn range_for_rename(
15797 &self,
15798 buffer: &Entity<Buffer>,
15799 position: text::Anchor,
15800 cx: &mut App,
15801 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
15802
15803 fn perform_rename(
15804 &self,
15805 buffer: &Entity<Buffer>,
15806 position: text::Anchor,
15807 new_name: String,
15808 cx: &mut App,
15809 ) -> Option<Task<Result<ProjectTransaction>>>;
15810}
15811
15812pub trait CompletionProvider {
15813 fn completions(
15814 &self,
15815 buffer: &Entity<Buffer>,
15816 buffer_position: text::Anchor,
15817 trigger: CompletionContext,
15818 window: &mut Window,
15819 cx: &mut Context<Editor>,
15820 ) -> Task<Result<Vec<Completion>>>;
15821
15822 fn resolve_completions(
15823 &self,
15824 buffer: Entity<Buffer>,
15825 completion_indices: Vec<usize>,
15826 completions: Rc<RefCell<Box<[Completion]>>>,
15827 cx: &mut Context<Editor>,
15828 ) -> Task<Result<bool>>;
15829
15830 fn apply_additional_edits_for_completion(
15831 &self,
15832 _buffer: Entity<Buffer>,
15833 _completions: Rc<RefCell<Box<[Completion]>>>,
15834 _completion_index: usize,
15835 _push_to_history: bool,
15836 _cx: &mut Context<Editor>,
15837 ) -> Task<Result<Option<language::Transaction>>> {
15838 Task::ready(Ok(None))
15839 }
15840
15841 fn is_completion_trigger(
15842 &self,
15843 buffer: &Entity<Buffer>,
15844 position: language::Anchor,
15845 text: &str,
15846 trigger_in_words: bool,
15847 cx: &mut Context<Editor>,
15848 ) -> bool;
15849
15850 fn sort_completions(&self) -> bool {
15851 true
15852 }
15853}
15854
15855pub trait CodeActionProvider {
15856 fn id(&self) -> Arc<str>;
15857
15858 fn code_actions(
15859 &self,
15860 buffer: &Entity<Buffer>,
15861 range: Range<text::Anchor>,
15862 window: &mut Window,
15863 cx: &mut App,
15864 ) -> Task<Result<Vec<CodeAction>>>;
15865
15866 fn apply_code_action(
15867 &self,
15868 buffer_handle: Entity<Buffer>,
15869 action: CodeAction,
15870 excerpt_id: ExcerptId,
15871 push_to_history: bool,
15872 window: &mut Window,
15873 cx: &mut App,
15874 ) -> Task<Result<ProjectTransaction>>;
15875}
15876
15877impl CodeActionProvider for Entity<Project> {
15878 fn id(&self) -> Arc<str> {
15879 "project".into()
15880 }
15881
15882 fn code_actions(
15883 &self,
15884 buffer: &Entity<Buffer>,
15885 range: Range<text::Anchor>,
15886 _window: &mut Window,
15887 cx: &mut App,
15888 ) -> Task<Result<Vec<CodeAction>>> {
15889 self.update(cx, |project, cx| {
15890 project.code_actions(buffer, range, None, cx)
15891 })
15892 }
15893
15894 fn apply_code_action(
15895 &self,
15896 buffer_handle: Entity<Buffer>,
15897 action: CodeAction,
15898 _excerpt_id: ExcerptId,
15899 push_to_history: bool,
15900 _window: &mut Window,
15901 cx: &mut App,
15902 ) -> Task<Result<ProjectTransaction>> {
15903 self.update(cx, |project, cx| {
15904 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15905 })
15906 }
15907}
15908
15909fn snippet_completions(
15910 project: &Project,
15911 buffer: &Entity<Buffer>,
15912 buffer_position: text::Anchor,
15913 cx: &mut App,
15914) -> Task<Result<Vec<Completion>>> {
15915 let language = buffer.read(cx).language_at(buffer_position);
15916 let language_name = language.as_ref().map(|language| language.lsp_id());
15917 let snippet_store = project.snippets().read(cx);
15918 let snippets = snippet_store.snippets_for(language_name, cx);
15919
15920 if snippets.is_empty() {
15921 return Task::ready(Ok(vec![]));
15922 }
15923 let snapshot = buffer.read(cx).text_snapshot();
15924 let chars: String = snapshot
15925 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15926 .collect();
15927
15928 let scope = language.map(|language| language.default_scope());
15929 let executor = cx.background_executor().clone();
15930
15931 cx.background_spawn(async move {
15932 let classifier = CharClassifier::new(scope).for_completion(true);
15933 let mut last_word = chars
15934 .chars()
15935 .take_while(|c| classifier.is_word(*c))
15936 .collect::<String>();
15937 last_word = last_word.chars().rev().collect();
15938
15939 if last_word.is_empty() {
15940 return Ok(vec![]);
15941 }
15942
15943 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15944 let to_lsp = |point: &text::Anchor| {
15945 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15946 point_to_lsp(end)
15947 };
15948 let lsp_end = to_lsp(&buffer_position);
15949
15950 let candidates = snippets
15951 .iter()
15952 .enumerate()
15953 .flat_map(|(ix, snippet)| {
15954 snippet
15955 .prefix
15956 .iter()
15957 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15958 })
15959 .collect::<Vec<StringMatchCandidate>>();
15960
15961 let mut matches = fuzzy::match_strings(
15962 &candidates,
15963 &last_word,
15964 last_word.chars().any(|c| c.is_uppercase()),
15965 100,
15966 &Default::default(),
15967 executor,
15968 )
15969 .await;
15970
15971 // Remove all candidates where the query's start does not match the start of any word in the candidate
15972 if let Some(query_start) = last_word.chars().next() {
15973 matches.retain(|string_match| {
15974 split_words(&string_match.string).any(|word| {
15975 // Check that the first codepoint of the word as lowercase matches the first
15976 // codepoint of the query as lowercase
15977 word.chars()
15978 .flat_map(|codepoint| codepoint.to_lowercase())
15979 .zip(query_start.to_lowercase())
15980 .all(|(word_cp, query_cp)| word_cp == query_cp)
15981 })
15982 });
15983 }
15984
15985 let matched_strings = matches
15986 .into_iter()
15987 .map(|m| m.string)
15988 .collect::<HashSet<_>>();
15989
15990 let result: Vec<Completion> = snippets
15991 .into_iter()
15992 .filter_map(|snippet| {
15993 let matching_prefix = snippet
15994 .prefix
15995 .iter()
15996 .find(|prefix| matched_strings.contains(*prefix))?;
15997 let start = as_offset - last_word.len();
15998 let start = snapshot.anchor_before(start);
15999 let range = start..buffer_position;
16000 let lsp_start = to_lsp(&start);
16001 let lsp_range = lsp::Range {
16002 start: lsp_start,
16003 end: lsp_end,
16004 };
16005 Some(Completion {
16006 old_range: range,
16007 new_text: snippet.body.clone(),
16008 resolved: false,
16009 label: CodeLabel {
16010 text: matching_prefix.clone(),
16011 runs: vec![],
16012 filter_range: 0..matching_prefix.len(),
16013 },
16014 server_id: LanguageServerId(usize::MAX),
16015 documentation: snippet
16016 .description
16017 .clone()
16018 .map(|description| CompletionDocumentation::SingleLine(description.into())),
16019 lsp_completion: lsp::CompletionItem {
16020 label: snippet.prefix.first().unwrap().clone(),
16021 kind: Some(CompletionItemKind::SNIPPET),
16022 label_details: snippet.description.as_ref().map(|description| {
16023 lsp::CompletionItemLabelDetails {
16024 detail: Some(description.clone()),
16025 description: None,
16026 }
16027 }),
16028 insert_text_format: Some(InsertTextFormat::SNIPPET),
16029 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
16030 lsp::InsertReplaceEdit {
16031 new_text: snippet.body.clone(),
16032 insert: lsp_range,
16033 replace: lsp_range,
16034 },
16035 )),
16036 filter_text: Some(snippet.body.clone()),
16037 sort_text: Some(char::MAX.to_string()),
16038 ..Default::default()
16039 },
16040 confirm: None,
16041 })
16042 })
16043 .collect();
16044
16045 Ok(result)
16046 })
16047}
16048
16049impl CompletionProvider for Entity<Project> {
16050 fn completions(
16051 &self,
16052 buffer: &Entity<Buffer>,
16053 buffer_position: text::Anchor,
16054 options: CompletionContext,
16055 _window: &mut Window,
16056 cx: &mut Context<Editor>,
16057 ) -> Task<Result<Vec<Completion>>> {
16058 self.update(cx, |project, cx| {
16059 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16060 let project_completions = project.completions(buffer, buffer_position, options, cx);
16061 cx.background_spawn(async move {
16062 let mut completions = project_completions.await?;
16063 let snippets_completions = snippets.await?;
16064 completions.extend(snippets_completions);
16065 Ok(completions)
16066 })
16067 })
16068 }
16069
16070 fn resolve_completions(
16071 &self,
16072 buffer: Entity<Buffer>,
16073 completion_indices: Vec<usize>,
16074 completions: Rc<RefCell<Box<[Completion]>>>,
16075 cx: &mut Context<Editor>,
16076 ) -> Task<Result<bool>> {
16077 self.update(cx, |project, cx| {
16078 project.lsp_store().update(cx, |lsp_store, cx| {
16079 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16080 })
16081 })
16082 }
16083
16084 fn apply_additional_edits_for_completion(
16085 &self,
16086 buffer: Entity<Buffer>,
16087 completions: Rc<RefCell<Box<[Completion]>>>,
16088 completion_index: usize,
16089 push_to_history: bool,
16090 cx: &mut Context<Editor>,
16091 ) -> Task<Result<Option<language::Transaction>>> {
16092 self.update(cx, |project, cx| {
16093 project.lsp_store().update(cx, |lsp_store, cx| {
16094 lsp_store.apply_additional_edits_for_completion(
16095 buffer,
16096 completions,
16097 completion_index,
16098 push_to_history,
16099 cx,
16100 )
16101 })
16102 })
16103 }
16104
16105 fn is_completion_trigger(
16106 &self,
16107 buffer: &Entity<Buffer>,
16108 position: language::Anchor,
16109 text: &str,
16110 trigger_in_words: bool,
16111 cx: &mut Context<Editor>,
16112 ) -> bool {
16113 let mut chars = text.chars();
16114 let char = if let Some(char) = chars.next() {
16115 char
16116 } else {
16117 return false;
16118 };
16119 if chars.next().is_some() {
16120 return false;
16121 }
16122
16123 let buffer = buffer.read(cx);
16124 let snapshot = buffer.snapshot();
16125 if !snapshot.settings_at(position, cx).show_completions_on_input {
16126 return false;
16127 }
16128 let classifier = snapshot.char_classifier_at(position).for_completion(true);
16129 if trigger_in_words && classifier.is_word(char) {
16130 return true;
16131 }
16132
16133 buffer.completion_triggers().contains(text)
16134 }
16135}
16136
16137impl SemanticsProvider for Entity<Project> {
16138 fn hover(
16139 &self,
16140 buffer: &Entity<Buffer>,
16141 position: text::Anchor,
16142 cx: &mut App,
16143 ) -> Option<Task<Vec<project::Hover>>> {
16144 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
16145 }
16146
16147 fn document_highlights(
16148 &self,
16149 buffer: &Entity<Buffer>,
16150 position: text::Anchor,
16151 cx: &mut App,
16152 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
16153 Some(self.update(cx, |project, cx| {
16154 project.document_highlights(buffer, position, cx)
16155 }))
16156 }
16157
16158 fn definitions(
16159 &self,
16160 buffer: &Entity<Buffer>,
16161 position: text::Anchor,
16162 kind: GotoDefinitionKind,
16163 cx: &mut App,
16164 ) -> Option<Task<Result<Vec<LocationLink>>>> {
16165 Some(self.update(cx, |project, cx| match kind {
16166 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
16167 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
16168 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
16169 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
16170 }))
16171 }
16172
16173 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
16174 // TODO: make this work for remote projects
16175 self.update(cx, |this, cx| {
16176 buffer.update(cx, |buffer, cx| {
16177 this.any_language_server_supports_inlay_hints(buffer, cx)
16178 })
16179 })
16180 }
16181
16182 fn inlay_hints(
16183 &self,
16184 buffer_handle: Entity<Buffer>,
16185 range: Range<text::Anchor>,
16186 cx: &mut App,
16187 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
16188 Some(self.update(cx, |project, cx| {
16189 project.inlay_hints(buffer_handle, range, cx)
16190 }))
16191 }
16192
16193 fn resolve_inlay_hint(
16194 &self,
16195 hint: InlayHint,
16196 buffer_handle: Entity<Buffer>,
16197 server_id: LanguageServerId,
16198 cx: &mut App,
16199 ) -> Option<Task<anyhow::Result<InlayHint>>> {
16200 Some(self.update(cx, |project, cx| {
16201 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
16202 }))
16203 }
16204
16205 fn range_for_rename(
16206 &self,
16207 buffer: &Entity<Buffer>,
16208 position: text::Anchor,
16209 cx: &mut App,
16210 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
16211 Some(self.update(cx, |project, cx| {
16212 let buffer = buffer.clone();
16213 let task = project.prepare_rename(buffer.clone(), position, cx);
16214 cx.spawn(|_, mut cx| async move {
16215 Ok(match task.await? {
16216 PrepareRenameResponse::Success(range) => Some(range),
16217 PrepareRenameResponse::InvalidPosition => None,
16218 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
16219 // Fallback on using TreeSitter info to determine identifier range
16220 buffer.update(&mut cx, |buffer, _| {
16221 let snapshot = buffer.snapshot();
16222 let (range, kind) = snapshot.surrounding_word(position);
16223 if kind != Some(CharKind::Word) {
16224 return None;
16225 }
16226 Some(
16227 snapshot.anchor_before(range.start)
16228 ..snapshot.anchor_after(range.end),
16229 )
16230 })?
16231 }
16232 })
16233 })
16234 }))
16235 }
16236
16237 fn perform_rename(
16238 &self,
16239 buffer: &Entity<Buffer>,
16240 position: text::Anchor,
16241 new_name: String,
16242 cx: &mut App,
16243 ) -> Option<Task<Result<ProjectTransaction>>> {
16244 Some(self.update(cx, |project, cx| {
16245 project.perform_rename(buffer.clone(), position, new_name, cx)
16246 }))
16247 }
16248}
16249
16250fn inlay_hint_settings(
16251 location: Anchor,
16252 snapshot: &MultiBufferSnapshot,
16253 cx: &mut Context<Editor>,
16254) -> InlayHintSettings {
16255 let file = snapshot.file_at(location);
16256 let language = snapshot.language_at(location).map(|l| l.name());
16257 language_settings(language, file, cx).inlay_hints
16258}
16259
16260fn consume_contiguous_rows(
16261 contiguous_row_selections: &mut Vec<Selection<Point>>,
16262 selection: &Selection<Point>,
16263 display_map: &DisplaySnapshot,
16264 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
16265) -> (MultiBufferRow, MultiBufferRow) {
16266 contiguous_row_selections.push(selection.clone());
16267 let start_row = MultiBufferRow(selection.start.row);
16268 let mut end_row = ending_row(selection, display_map);
16269
16270 while let Some(next_selection) = selections.peek() {
16271 if next_selection.start.row <= end_row.0 {
16272 end_row = ending_row(next_selection, display_map);
16273 contiguous_row_selections.push(selections.next().unwrap().clone());
16274 } else {
16275 break;
16276 }
16277 }
16278 (start_row, end_row)
16279}
16280
16281fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
16282 if next_selection.end.column > 0 || next_selection.is_empty() {
16283 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
16284 } else {
16285 MultiBufferRow(next_selection.end.row)
16286 }
16287}
16288
16289impl EditorSnapshot {
16290 pub fn remote_selections_in_range<'a>(
16291 &'a self,
16292 range: &'a Range<Anchor>,
16293 collaboration_hub: &dyn CollaborationHub,
16294 cx: &'a App,
16295 ) -> impl 'a + Iterator<Item = RemoteSelection> {
16296 let participant_names = collaboration_hub.user_names(cx);
16297 let participant_indices = collaboration_hub.user_participant_indices(cx);
16298 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
16299 let collaborators_by_replica_id = collaborators_by_peer_id
16300 .iter()
16301 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
16302 .collect::<HashMap<_, _>>();
16303 self.buffer_snapshot
16304 .selections_in_range(range, false)
16305 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
16306 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
16307 let participant_index = participant_indices.get(&collaborator.user_id).copied();
16308 let user_name = participant_names.get(&collaborator.user_id).cloned();
16309 Some(RemoteSelection {
16310 replica_id,
16311 selection,
16312 cursor_shape,
16313 line_mode,
16314 participant_index,
16315 peer_id: collaborator.peer_id,
16316 user_name,
16317 })
16318 })
16319 }
16320
16321 pub fn hunks_for_ranges(
16322 &self,
16323 ranges: impl Iterator<Item = Range<Point>>,
16324 ) -> Vec<MultiBufferDiffHunk> {
16325 let mut hunks = Vec::new();
16326 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
16327 HashMap::default();
16328 for query_range in ranges {
16329 let query_rows =
16330 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
16331 for hunk in self.buffer_snapshot.diff_hunks_in_range(
16332 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
16333 ) {
16334 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
16335 // when the caret is just above or just below the deleted hunk.
16336 let allow_adjacent = hunk.status().is_deleted();
16337 let related_to_selection = if allow_adjacent {
16338 hunk.row_range.overlaps(&query_rows)
16339 || hunk.row_range.start == query_rows.end
16340 || hunk.row_range.end == query_rows.start
16341 } else {
16342 hunk.row_range.overlaps(&query_rows)
16343 };
16344 if related_to_selection {
16345 if !processed_buffer_rows
16346 .entry(hunk.buffer_id)
16347 .or_default()
16348 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
16349 {
16350 continue;
16351 }
16352 hunks.push(hunk);
16353 }
16354 }
16355 }
16356
16357 hunks
16358 }
16359
16360 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
16361 self.display_snapshot.buffer_snapshot.language_at(position)
16362 }
16363
16364 pub fn is_focused(&self) -> bool {
16365 self.is_focused
16366 }
16367
16368 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
16369 self.placeholder_text.as_ref()
16370 }
16371
16372 pub fn scroll_position(&self) -> gpui::Point<f32> {
16373 self.scroll_anchor.scroll_position(&self.display_snapshot)
16374 }
16375
16376 fn gutter_dimensions(
16377 &self,
16378 font_id: FontId,
16379 font_size: Pixels,
16380 max_line_number_width: Pixels,
16381 cx: &App,
16382 ) -> Option<GutterDimensions> {
16383 if !self.show_gutter {
16384 return None;
16385 }
16386
16387 let descent = cx.text_system().descent(font_id, font_size);
16388 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
16389 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
16390
16391 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
16392 matches!(
16393 ProjectSettings::get_global(cx).git.git_gutter,
16394 Some(GitGutterSetting::TrackedFiles)
16395 )
16396 });
16397 let gutter_settings = EditorSettings::get_global(cx).gutter;
16398 let show_line_numbers = self
16399 .show_line_numbers
16400 .unwrap_or(gutter_settings.line_numbers);
16401 let line_gutter_width = if show_line_numbers {
16402 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
16403 let min_width_for_number_on_gutter = em_advance * 4.0;
16404 max_line_number_width.max(min_width_for_number_on_gutter)
16405 } else {
16406 0.0.into()
16407 };
16408
16409 let show_code_actions = self
16410 .show_code_actions
16411 .unwrap_or(gutter_settings.code_actions);
16412
16413 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
16414
16415 let git_blame_entries_width =
16416 self.git_blame_gutter_max_author_length
16417 .map(|max_author_length| {
16418 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
16419
16420 /// The number of characters to dedicate to gaps and margins.
16421 const SPACING_WIDTH: usize = 4;
16422
16423 let max_char_count = max_author_length
16424 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
16425 + ::git::SHORT_SHA_LENGTH
16426 + MAX_RELATIVE_TIMESTAMP.len()
16427 + SPACING_WIDTH;
16428
16429 em_advance * max_char_count
16430 });
16431
16432 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
16433 left_padding += if show_code_actions || show_runnables {
16434 em_width * 3.0
16435 } else if show_git_gutter && show_line_numbers {
16436 em_width * 2.0
16437 } else if show_git_gutter || show_line_numbers {
16438 em_width
16439 } else {
16440 px(0.)
16441 };
16442
16443 let right_padding = if gutter_settings.folds && show_line_numbers {
16444 em_width * 4.0
16445 } else if gutter_settings.folds {
16446 em_width * 3.0
16447 } else if show_line_numbers {
16448 em_width
16449 } else {
16450 px(0.)
16451 };
16452
16453 Some(GutterDimensions {
16454 left_padding,
16455 right_padding,
16456 width: line_gutter_width + left_padding + right_padding,
16457 margin: -descent,
16458 git_blame_entries_width,
16459 })
16460 }
16461
16462 pub fn render_crease_toggle(
16463 &self,
16464 buffer_row: MultiBufferRow,
16465 row_contains_cursor: bool,
16466 editor: Entity<Editor>,
16467 window: &mut Window,
16468 cx: &mut App,
16469 ) -> Option<AnyElement> {
16470 let folded = self.is_line_folded(buffer_row);
16471 let mut is_foldable = false;
16472
16473 if let Some(crease) = self
16474 .crease_snapshot
16475 .query_row(buffer_row, &self.buffer_snapshot)
16476 {
16477 is_foldable = true;
16478 match crease {
16479 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
16480 if let Some(render_toggle) = render_toggle {
16481 let toggle_callback =
16482 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
16483 if folded {
16484 editor.update(cx, |editor, cx| {
16485 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
16486 });
16487 } else {
16488 editor.update(cx, |editor, cx| {
16489 editor.unfold_at(
16490 &crate::UnfoldAt { buffer_row },
16491 window,
16492 cx,
16493 )
16494 });
16495 }
16496 });
16497 return Some((render_toggle)(
16498 buffer_row,
16499 folded,
16500 toggle_callback,
16501 window,
16502 cx,
16503 ));
16504 }
16505 }
16506 }
16507 }
16508
16509 is_foldable |= self.starts_indent(buffer_row);
16510
16511 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
16512 Some(
16513 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
16514 .toggle_state(folded)
16515 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
16516 if folded {
16517 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
16518 } else {
16519 this.fold_at(&FoldAt { buffer_row }, window, cx);
16520 }
16521 }))
16522 .into_any_element(),
16523 )
16524 } else {
16525 None
16526 }
16527 }
16528
16529 pub fn render_crease_trailer(
16530 &self,
16531 buffer_row: MultiBufferRow,
16532 window: &mut Window,
16533 cx: &mut App,
16534 ) -> Option<AnyElement> {
16535 let folded = self.is_line_folded(buffer_row);
16536 if let Crease::Inline { render_trailer, .. } = self
16537 .crease_snapshot
16538 .query_row(buffer_row, &self.buffer_snapshot)?
16539 {
16540 let render_trailer = render_trailer.as_ref()?;
16541 Some(render_trailer(buffer_row, folded, window, cx))
16542 } else {
16543 None
16544 }
16545 }
16546}
16547
16548impl Deref for EditorSnapshot {
16549 type Target = DisplaySnapshot;
16550
16551 fn deref(&self) -> &Self::Target {
16552 &self.display_snapshot
16553 }
16554}
16555
16556#[derive(Clone, Debug, PartialEq, Eq)]
16557pub enum EditorEvent {
16558 InputIgnored {
16559 text: Arc<str>,
16560 },
16561 InputHandled {
16562 utf16_range_to_replace: Option<Range<isize>>,
16563 text: Arc<str>,
16564 },
16565 ExcerptsAdded {
16566 buffer: Entity<Buffer>,
16567 predecessor: ExcerptId,
16568 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
16569 },
16570 ExcerptsRemoved {
16571 ids: Vec<ExcerptId>,
16572 },
16573 BufferFoldToggled {
16574 ids: Vec<ExcerptId>,
16575 folded: bool,
16576 },
16577 ExcerptsEdited {
16578 ids: Vec<ExcerptId>,
16579 },
16580 ExcerptsExpanded {
16581 ids: Vec<ExcerptId>,
16582 },
16583 BufferEdited,
16584 Edited {
16585 transaction_id: clock::Lamport,
16586 },
16587 Reparsed(BufferId),
16588 Focused,
16589 FocusedIn,
16590 Blurred,
16591 DirtyChanged,
16592 Saved,
16593 TitleChanged,
16594 DiffBaseChanged,
16595 SelectionsChanged {
16596 local: bool,
16597 },
16598 ScrollPositionChanged {
16599 local: bool,
16600 autoscroll: bool,
16601 },
16602 Closed,
16603 TransactionUndone {
16604 transaction_id: clock::Lamport,
16605 },
16606 TransactionBegun {
16607 transaction_id: clock::Lamport,
16608 },
16609 Reloaded,
16610 CursorShapeChanged,
16611}
16612
16613impl EventEmitter<EditorEvent> for Editor {}
16614
16615impl Focusable for Editor {
16616 fn focus_handle(&self, _cx: &App) -> FocusHandle {
16617 self.focus_handle.clone()
16618 }
16619}
16620
16621impl Render for Editor {
16622 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
16623 let settings = ThemeSettings::get_global(cx);
16624
16625 let mut text_style = match self.mode {
16626 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
16627 color: cx.theme().colors().editor_foreground,
16628 font_family: settings.ui_font.family.clone(),
16629 font_features: settings.ui_font.features.clone(),
16630 font_fallbacks: settings.ui_font.fallbacks.clone(),
16631 font_size: rems(0.875).into(),
16632 font_weight: settings.ui_font.weight,
16633 line_height: relative(settings.buffer_line_height.value()),
16634 ..Default::default()
16635 },
16636 EditorMode::Full => TextStyle {
16637 color: cx.theme().colors().editor_foreground,
16638 font_family: settings.buffer_font.family.clone(),
16639 font_features: settings.buffer_font.features.clone(),
16640 font_fallbacks: settings.buffer_font.fallbacks.clone(),
16641 font_size: settings.buffer_font_size(cx).into(),
16642 font_weight: settings.buffer_font.weight,
16643 line_height: relative(settings.buffer_line_height.value()),
16644 ..Default::default()
16645 },
16646 };
16647 if let Some(text_style_refinement) = &self.text_style_refinement {
16648 text_style.refine(text_style_refinement)
16649 }
16650
16651 let background = match self.mode {
16652 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
16653 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
16654 EditorMode::Full => cx.theme().colors().editor_background,
16655 };
16656
16657 EditorElement::new(
16658 &cx.entity(),
16659 EditorStyle {
16660 background,
16661 local_player: cx.theme().players().local(),
16662 text: text_style,
16663 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
16664 syntax: cx.theme().syntax().clone(),
16665 status: cx.theme().status().clone(),
16666 inlay_hints_style: make_inlay_hints_style(cx),
16667 inline_completion_styles: make_suggestion_styles(cx),
16668 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
16669 },
16670 )
16671 }
16672}
16673
16674impl EntityInputHandler for Editor {
16675 fn text_for_range(
16676 &mut self,
16677 range_utf16: Range<usize>,
16678 adjusted_range: &mut Option<Range<usize>>,
16679 _: &mut Window,
16680 cx: &mut Context<Self>,
16681 ) -> Option<String> {
16682 let snapshot = self.buffer.read(cx).read(cx);
16683 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
16684 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
16685 if (start.0..end.0) != range_utf16 {
16686 adjusted_range.replace(start.0..end.0);
16687 }
16688 Some(snapshot.text_for_range(start..end).collect())
16689 }
16690
16691 fn selected_text_range(
16692 &mut self,
16693 ignore_disabled_input: bool,
16694 _: &mut Window,
16695 cx: &mut Context<Self>,
16696 ) -> Option<UTF16Selection> {
16697 // Prevent the IME menu from appearing when holding down an alphabetic key
16698 // while input is disabled.
16699 if !ignore_disabled_input && !self.input_enabled {
16700 return None;
16701 }
16702
16703 let selection = self.selections.newest::<OffsetUtf16>(cx);
16704 let range = selection.range();
16705
16706 Some(UTF16Selection {
16707 range: range.start.0..range.end.0,
16708 reversed: selection.reversed,
16709 })
16710 }
16711
16712 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
16713 let snapshot = self.buffer.read(cx).read(cx);
16714 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
16715 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
16716 }
16717
16718 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16719 self.clear_highlights::<InputComposition>(cx);
16720 self.ime_transaction.take();
16721 }
16722
16723 fn replace_text_in_range(
16724 &mut self,
16725 range_utf16: Option<Range<usize>>,
16726 text: &str,
16727 window: &mut Window,
16728 cx: &mut Context<Self>,
16729 ) {
16730 if !self.input_enabled {
16731 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16732 return;
16733 }
16734
16735 self.transact(window, cx, |this, window, cx| {
16736 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
16737 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16738 Some(this.selection_replacement_ranges(range_utf16, cx))
16739 } else {
16740 this.marked_text_ranges(cx)
16741 };
16742
16743 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
16744 let newest_selection_id = this.selections.newest_anchor().id;
16745 this.selections
16746 .all::<OffsetUtf16>(cx)
16747 .iter()
16748 .zip(ranges_to_replace.iter())
16749 .find_map(|(selection, range)| {
16750 if selection.id == newest_selection_id {
16751 Some(
16752 (range.start.0 as isize - selection.head().0 as isize)
16753 ..(range.end.0 as isize - selection.head().0 as isize),
16754 )
16755 } else {
16756 None
16757 }
16758 })
16759 });
16760
16761 cx.emit(EditorEvent::InputHandled {
16762 utf16_range_to_replace: range_to_replace,
16763 text: text.into(),
16764 });
16765
16766 if let Some(new_selected_ranges) = new_selected_ranges {
16767 this.change_selections(None, window, cx, |selections| {
16768 selections.select_ranges(new_selected_ranges)
16769 });
16770 this.backspace(&Default::default(), window, cx);
16771 }
16772
16773 this.handle_input(text, window, cx);
16774 });
16775
16776 if let Some(transaction) = self.ime_transaction {
16777 self.buffer.update(cx, |buffer, cx| {
16778 buffer.group_until_transaction(transaction, cx);
16779 });
16780 }
16781
16782 self.unmark_text(window, cx);
16783 }
16784
16785 fn replace_and_mark_text_in_range(
16786 &mut self,
16787 range_utf16: Option<Range<usize>>,
16788 text: &str,
16789 new_selected_range_utf16: Option<Range<usize>>,
16790 window: &mut Window,
16791 cx: &mut Context<Self>,
16792 ) {
16793 if !self.input_enabled {
16794 return;
16795 }
16796
16797 let transaction = self.transact(window, cx, |this, window, cx| {
16798 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
16799 let snapshot = this.buffer.read(cx).read(cx);
16800 if let Some(relative_range_utf16) = range_utf16.as_ref() {
16801 for marked_range in &mut marked_ranges {
16802 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
16803 marked_range.start.0 += relative_range_utf16.start;
16804 marked_range.start =
16805 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
16806 marked_range.end =
16807 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16808 }
16809 }
16810 Some(marked_ranges)
16811 } else if let Some(range_utf16) = range_utf16 {
16812 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16813 Some(this.selection_replacement_ranges(range_utf16, cx))
16814 } else {
16815 None
16816 };
16817
16818 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16819 let newest_selection_id = this.selections.newest_anchor().id;
16820 this.selections
16821 .all::<OffsetUtf16>(cx)
16822 .iter()
16823 .zip(ranges_to_replace.iter())
16824 .find_map(|(selection, range)| {
16825 if selection.id == newest_selection_id {
16826 Some(
16827 (range.start.0 as isize - selection.head().0 as isize)
16828 ..(range.end.0 as isize - selection.head().0 as isize),
16829 )
16830 } else {
16831 None
16832 }
16833 })
16834 });
16835
16836 cx.emit(EditorEvent::InputHandled {
16837 utf16_range_to_replace: range_to_replace,
16838 text: text.into(),
16839 });
16840
16841 if let Some(ranges) = ranges_to_replace {
16842 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16843 }
16844
16845 let marked_ranges = {
16846 let snapshot = this.buffer.read(cx).read(cx);
16847 this.selections
16848 .disjoint_anchors()
16849 .iter()
16850 .map(|selection| {
16851 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16852 })
16853 .collect::<Vec<_>>()
16854 };
16855
16856 if text.is_empty() {
16857 this.unmark_text(window, cx);
16858 } else {
16859 this.highlight_text::<InputComposition>(
16860 marked_ranges.clone(),
16861 HighlightStyle {
16862 underline: Some(UnderlineStyle {
16863 thickness: px(1.),
16864 color: None,
16865 wavy: false,
16866 }),
16867 ..Default::default()
16868 },
16869 cx,
16870 );
16871 }
16872
16873 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16874 let use_autoclose = this.use_autoclose;
16875 let use_auto_surround = this.use_auto_surround;
16876 this.set_use_autoclose(false);
16877 this.set_use_auto_surround(false);
16878 this.handle_input(text, window, cx);
16879 this.set_use_autoclose(use_autoclose);
16880 this.set_use_auto_surround(use_auto_surround);
16881
16882 if let Some(new_selected_range) = new_selected_range_utf16 {
16883 let snapshot = this.buffer.read(cx).read(cx);
16884 let new_selected_ranges = marked_ranges
16885 .into_iter()
16886 .map(|marked_range| {
16887 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16888 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16889 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16890 snapshot.clip_offset_utf16(new_start, Bias::Left)
16891 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16892 })
16893 .collect::<Vec<_>>();
16894
16895 drop(snapshot);
16896 this.change_selections(None, window, cx, |selections| {
16897 selections.select_ranges(new_selected_ranges)
16898 });
16899 }
16900 });
16901
16902 self.ime_transaction = self.ime_transaction.or(transaction);
16903 if let Some(transaction) = self.ime_transaction {
16904 self.buffer.update(cx, |buffer, cx| {
16905 buffer.group_until_transaction(transaction, cx);
16906 });
16907 }
16908
16909 if self.text_highlights::<InputComposition>(cx).is_none() {
16910 self.ime_transaction.take();
16911 }
16912 }
16913
16914 fn bounds_for_range(
16915 &mut self,
16916 range_utf16: Range<usize>,
16917 element_bounds: gpui::Bounds<Pixels>,
16918 window: &mut Window,
16919 cx: &mut Context<Self>,
16920 ) -> Option<gpui::Bounds<Pixels>> {
16921 let text_layout_details = self.text_layout_details(window);
16922 let gpui::Size {
16923 width: em_width,
16924 height: line_height,
16925 } = self.character_size(window);
16926
16927 let snapshot = self.snapshot(window, cx);
16928 let scroll_position = snapshot.scroll_position();
16929 let scroll_left = scroll_position.x * em_width;
16930
16931 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16932 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16933 + self.gutter_dimensions.width
16934 + self.gutter_dimensions.margin;
16935 let y = line_height * (start.row().as_f32() - scroll_position.y);
16936
16937 Some(Bounds {
16938 origin: element_bounds.origin + point(x, y),
16939 size: size(em_width, line_height),
16940 })
16941 }
16942
16943 fn character_index_for_point(
16944 &mut self,
16945 point: gpui::Point<Pixels>,
16946 _window: &mut Window,
16947 _cx: &mut Context<Self>,
16948 ) -> Option<usize> {
16949 let position_map = self.last_position_map.as_ref()?;
16950 if !position_map.text_hitbox.contains(&point) {
16951 return None;
16952 }
16953 let display_point = position_map.point_for_position(point).previous_valid;
16954 let anchor = position_map
16955 .snapshot
16956 .display_point_to_anchor(display_point, Bias::Left);
16957 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16958 Some(utf16_offset.0)
16959 }
16960}
16961
16962trait SelectionExt {
16963 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16964 fn spanned_rows(
16965 &self,
16966 include_end_if_at_line_start: bool,
16967 map: &DisplaySnapshot,
16968 ) -> Range<MultiBufferRow>;
16969}
16970
16971impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16972 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16973 let start = self
16974 .start
16975 .to_point(&map.buffer_snapshot)
16976 .to_display_point(map);
16977 let end = self
16978 .end
16979 .to_point(&map.buffer_snapshot)
16980 .to_display_point(map);
16981 if self.reversed {
16982 end..start
16983 } else {
16984 start..end
16985 }
16986 }
16987
16988 fn spanned_rows(
16989 &self,
16990 include_end_if_at_line_start: bool,
16991 map: &DisplaySnapshot,
16992 ) -> Range<MultiBufferRow> {
16993 let start = self.start.to_point(&map.buffer_snapshot);
16994 let mut end = self.end.to_point(&map.buffer_snapshot);
16995 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16996 end.row -= 1;
16997 }
16998
16999 let buffer_start = map.prev_line_boundary(start).0;
17000 let buffer_end = map.next_line_boundary(end).0;
17001 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
17002 }
17003}
17004
17005impl<T: InvalidationRegion> InvalidationStack<T> {
17006 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
17007 where
17008 S: Clone + ToOffset,
17009 {
17010 while let Some(region) = self.last() {
17011 let all_selections_inside_invalidation_ranges =
17012 if selections.len() == region.ranges().len() {
17013 selections
17014 .iter()
17015 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
17016 .all(|(selection, invalidation_range)| {
17017 let head = selection.head().to_offset(buffer);
17018 invalidation_range.start <= head && invalidation_range.end >= head
17019 })
17020 } else {
17021 false
17022 };
17023
17024 if all_selections_inside_invalidation_ranges {
17025 break;
17026 } else {
17027 self.pop();
17028 }
17029 }
17030 }
17031}
17032
17033impl<T> Default for InvalidationStack<T> {
17034 fn default() -> Self {
17035 Self(Default::default())
17036 }
17037}
17038
17039impl<T> Deref for InvalidationStack<T> {
17040 type Target = Vec<T>;
17041
17042 fn deref(&self) -> &Self::Target {
17043 &self.0
17044 }
17045}
17046
17047impl<T> DerefMut for InvalidationStack<T> {
17048 fn deref_mut(&mut self) -> &mut Self::Target {
17049 &mut self.0
17050 }
17051}
17052
17053impl InvalidationRegion for SnippetState {
17054 fn ranges(&self) -> &[Range<Anchor>] {
17055 &self.ranges[self.active_index]
17056 }
17057}
17058
17059pub fn diagnostic_block_renderer(
17060 diagnostic: Diagnostic,
17061 max_message_rows: Option<u8>,
17062 allow_closing: bool,
17063 _is_valid: bool,
17064) -> RenderBlock {
17065 let (text_without_backticks, code_ranges) =
17066 highlight_diagnostic_message(&diagnostic, max_message_rows);
17067
17068 Arc::new(move |cx: &mut BlockContext| {
17069 let group_id: SharedString = cx.block_id.to_string().into();
17070
17071 let mut text_style = cx.window.text_style().clone();
17072 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
17073 let theme_settings = ThemeSettings::get_global(cx);
17074 text_style.font_family = theme_settings.buffer_font.family.clone();
17075 text_style.font_style = theme_settings.buffer_font.style;
17076 text_style.font_features = theme_settings.buffer_font.features.clone();
17077 text_style.font_weight = theme_settings.buffer_font.weight;
17078
17079 let multi_line_diagnostic = diagnostic.message.contains('\n');
17080
17081 let buttons = |diagnostic: &Diagnostic| {
17082 if multi_line_diagnostic {
17083 v_flex()
17084 } else {
17085 h_flex()
17086 }
17087 .when(allow_closing, |div| {
17088 div.children(diagnostic.is_primary.then(|| {
17089 IconButton::new("close-block", IconName::XCircle)
17090 .icon_color(Color::Muted)
17091 .size(ButtonSize::Compact)
17092 .style(ButtonStyle::Transparent)
17093 .visible_on_hover(group_id.clone())
17094 .on_click(move |_click, window, cx| {
17095 window.dispatch_action(Box::new(Cancel), cx)
17096 })
17097 .tooltip(|window, cx| {
17098 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
17099 })
17100 }))
17101 })
17102 .child(
17103 IconButton::new("copy-block", IconName::Copy)
17104 .icon_color(Color::Muted)
17105 .size(ButtonSize::Compact)
17106 .style(ButtonStyle::Transparent)
17107 .visible_on_hover(group_id.clone())
17108 .on_click({
17109 let message = diagnostic.message.clone();
17110 move |_click, _, cx| {
17111 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
17112 }
17113 })
17114 .tooltip(Tooltip::text("Copy diagnostic message")),
17115 )
17116 };
17117
17118 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
17119 AvailableSpace::min_size(),
17120 cx.window,
17121 cx.app,
17122 );
17123
17124 h_flex()
17125 .id(cx.block_id)
17126 .group(group_id.clone())
17127 .relative()
17128 .size_full()
17129 .block_mouse_down()
17130 .pl(cx.gutter_dimensions.width)
17131 .w(cx.max_width - cx.gutter_dimensions.full_width())
17132 .child(
17133 div()
17134 .flex()
17135 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
17136 .flex_shrink(),
17137 )
17138 .child(buttons(&diagnostic))
17139 .child(div().flex().flex_shrink_0().child(
17140 StyledText::new(text_without_backticks.clone()).with_highlights(
17141 &text_style,
17142 code_ranges.iter().map(|range| {
17143 (
17144 range.clone(),
17145 HighlightStyle {
17146 font_weight: Some(FontWeight::BOLD),
17147 ..Default::default()
17148 },
17149 )
17150 }),
17151 ),
17152 ))
17153 .into_any_element()
17154 })
17155}
17156
17157fn inline_completion_edit_text(
17158 current_snapshot: &BufferSnapshot,
17159 edits: &[(Range<Anchor>, String)],
17160 edit_preview: &EditPreview,
17161 include_deletions: bool,
17162 cx: &App,
17163) -> HighlightedText {
17164 let edits = edits
17165 .iter()
17166 .map(|(anchor, text)| {
17167 (
17168 anchor.start.text_anchor..anchor.end.text_anchor,
17169 text.clone(),
17170 )
17171 })
17172 .collect::<Vec<_>>();
17173
17174 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
17175}
17176
17177pub fn highlight_diagnostic_message(
17178 diagnostic: &Diagnostic,
17179 mut max_message_rows: Option<u8>,
17180) -> (SharedString, Vec<Range<usize>>) {
17181 let mut text_without_backticks = String::new();
17182 let mut code_ranges = Vec::new();
17183
17184 if let Some(source) = &diagnostic.source {
17185 text_without_backticks.push_str(source);
17186 code_ranges.push(0..source.len());
17187 text_without_backticks.push_str(": ");
17188 }
17189
17190 let mut prev_offset = 0;
17191 let mut in_code_block = false;
17192 let has_row_limit = max_message_rows.is_some();
17193 let mut newline_indices = diagnostic
17194 .message
17195 .match_indices('\n')
17196 .filter(|_| has_row_limit)
17197 .map(|(ix, _)| ix)
17198 .fuse()
17199 .peekable();
17200
17201 for (quote_ix, _) in diagnostic
17202 .message
17203 .match_indices('`')
17204 .chain([(diagnostic.message.len(), "")])
17205 {
17206 let mut first_newline_ix = None;
17207 let mut last_newline_ix = None;
17208 while let Some(newline_ix) = newline_indices.peek() {
17209 if *newline_ix < quote_ix {
17210 if first_newline_ix.is_none() {
17211 first_newline_ix = Some(*newline_ix);
17212 }
17213 last_newline_ix = Some(*newline_ix);
17214
17215 if let Some(rows_left) = &mut max_message_rows {
17216 if *rows_left == 0 {
17217 break;
17218 } else {
17219 *rows_left -= 1;
17220 }
17221 }
17222 let _ = newline_indices.next();
17223 } else {
17224 break;
17225 }
17226 }
17227 let prev_len = text_without_backticks.len();
17228 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
17229 text_without_backticks.push_str(new_text);
17230 if in_code_block {
17231 code_ranges.push(prev_len..text_without_backticks.len());
17232 }
17233 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
17234 in_code_block = !in_code_block;
17235 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
17236 text_without_backticks.push_str("...");
17237 break;
17238 }
17239 }
17240
17241 (text_without_backticks.into(), code_ranges)
17242}
17243
17244fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
17245 match severity {
17246 DiagnosticSeverity::ERROR => colors.error,
17247 DiagnosticSeverity::WARNING => colors.warning,
17248 DiagnosticSeverity::INFORMATION => colors.info,
17249 DiagnosticSeverity::HINT => colors.info,
17250 _ => colors.ignored,
17251 }
17252}
17253
17254pub fn styled_runs_for_code_label<'a>(
17255 label: &'a CodeLabel,
17256 syntax_theme: &'a theme::SyntaxTheme,
17257) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
17258 let fade_out = HighlightStyle {
17259 fade_out: Some(0.35),
17260 ..Default::default()
17261 };
17262
17263 let mut prev_end = label.filter_range.end;
17264 label
17265 .runs
17266 .iter()
17267 .enumerate()
17268 .flat_map(move |(ix, (range, highlight_id))| {
17269 let style = if let Some(style) = highlight_id.style(syntax_theme) {
17270 style
17271 } else {
17272 return Default::default();
17273 };
17274 let mut muted_style = style;
17275 muted_style.highlight(fade_out);
17276
17277 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
17278 if range.start >= label.filter_range.end {
17279 if range.start > prev_end {
17280 runs.push((prev_end..range.start, fade_out));
17281 }
17282 runs.push((range.clone(), muted_style));
17283 } else if range.end <= label.filter_range.end {
17284 runs.push((range.clone(), style));
17285 } else {
17286 runs.push((range.start..label.filter_range.end, style));
17287 runs.push((label.filter_range.end..range.end, muted_style));
17288 }
17289 prev_end = cmp::max(prev_end, range.end);
17290
17291 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
17292 runs.push((prev_end..label.text.len(), fade_out));
17293 }
17294
17295 runs
17296 })
17297}
17298
17299pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
17300 let mut prev_index = 0;
17301 let mut prev_codepoint: Option<char> = None;
17302 text.char_indices()
17303 .chain([(text.len(), '\0')])
17304 .filter_map(move |(index, codepoint)| {
17305 let prev_codepoint = prev_codepoint.replace(codepoint)?;
17306 let is_boundary = index == text.len()
17307 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
17308 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
17309 if is_boundary {
17310 let chunk = &text[prev_index..index];
17311 prev_index = index;
17312 Some(chunk)
17313 } else {
17314 None
17315 }
17316 })
17317}
17318
17319pub trait RangeToAnchorExt: Sized {
17320 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
17321
17322 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
17323 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
17324 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
17325 }
17326}
17327
17328impl<T: ToOffset> RangeToAnchorExt for Range<T> {
17329 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
17330 let start_offset = self.start.to_offset(snapshot);
17331 let end_offset = self.end.to_offset(snapshot);
17332 if start_offset == end_offset {
17333 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
17334 } else {
17335 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
17336 }
17337 }
17338}
17339
17340pub trait RowExt {
17341 fn as_f32(&self) -> f32;
17342
17343 fn next_row(&self) -> Self;
17344
17345 fn previous_row(&self) -> Self;
17346
17347 fn minus(&self, other: Self) -> u32;
17348}
17349
17350impl RowExt for DisplayRow {
17351 fn as_f32(&self) -> f32 {
17352 self.0 as f32
17353 }
17354
17355 fn next_row(&self) -> Self {
17356 Self(self.0 + 1)
17357 }
17358
17359 fn previous_row(&self) -> Self {
17360 Self(self.0.saturating_sub(1))
17361 }
17362
17363 fn minus(&self, other: Self) -> u32 {
17364 self.0 - other.0
17365 }
17366}
17367
17368impl RowExt for MultiBufferRow {
17369 fn as_f32(&self) -> f32 {
17370 self.0 as f32
17371 }
17372
17373 fn next_row(&self) -> Self {
17374 Self(self.0 + 1)
17375 }
17376
17377 fn previous_row(&self) -> Self {
17378 Self(self.0.saturating_sub(1))
17379 }
17380
17381 fn minus(&self, other: Self) -> u32 {
17382 self.0 - other.0
17383 }
17384}
17385
17386trait RowRangeExt {
17387 type Row;
17388
17389 fn len(&self) -> usize;
17390
17391 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
17392}
17393
17394impl RowRangeExt for Range<MultiBufferRow> {
17395 type Row = MultiBufferRow;
17396
17397 fn len(&self) -> usize {
17398 (self.end.0 - self.start.0) as usize
17399 }
17400
17401 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
17402 (self.start.0..self.end.0).map(MultiBufferRow)
17403 }
17404}
17405
17406impl RowRangeExt for Range<DisplayRow> {
17407 type Row = DisplayRow;
17408
17409 fn len(&self) -> usize {
17410 (self.end.0 - self.start.0) as usize
17411 }
17412
17413 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
17414 (self.start.0..self.end.0).map(DisplayRow)
17415 }
17416}
17417
17418/// If select range has more than one line, we
17419/// just point the cursor to range.start.
17420fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
17421 if range.start.row == range.end.row {
17422 range
17423 } else {
17424 range.start..range.start
17425 }
17426}
17427pub struct KillRing(ClipboardItem);
17428impl Global for KillRing {}
17429
17430const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
17431
17432fn all_edits_insertions_or_deletions(
17433 edits: &Vec<(Range<Anchor>, String)>,
17434 snapshot: &MultiBufferSnapshot,
17435) -> bool {
17436 let mut all_insertions = true;
17437 let mut all_deletions = true;
17438
17439 for (range, new_text) in edits.iter() {
17440 let range_is_empty = range.to_offset(&snapshot).is_empty();
17441 let text_is_empty = new_text.is_empty();
17442
17443 if range_is_empty != text_is_empty {
17444 if range_is_empty {
17445 all_deletions = false;
17446 } else {
17447 all_insertions = false;
17448 }
17449 } else {
17450 return false;
17451 }
17452
17453 if !all_insertions && !all_deletions {
17454 return false;
17455 }
17456 }
17457 all_insertions || all_deletions
17458}