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::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 distinguish_unstaged_diff_hunks: bool,
699 git_blame_inline_enabled: bool,
700 serialize_dirty_buffers: bool,
701 show_selection_menu: Option<bool>,
702 blame: Option<Entity<GitBlame>>,
703 blame_subscription: Option<Subscription>,
704 custom_context_menu: Option<
705 Box<
706 dyn 'static
707 + Fn(
708 &mut Self,
709 DisplayPoint,
710 &mut Window,
711 &mut Context<Self>,
712 ) -> Option<Entity<ui::ContextMenu>>,
713 >,
714 >,
715 last_bounds: Option<Bounds<Pixels>>,
716 last_position_map: Option<Rc<PositionMap>>,
717 expect_bounds_change: Option<Bounds<Pixels>>,
718 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
719 tasks_update_task: Option<Task<()>>,
720 in_project_search: bool,
721 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
722 breadcrumb_header: Option<String>,
723 focused_block: Option<FocusedBlock>,
724 next_scroll_position: NextScrollCursorCenterTopBottom,
725 addons: HashMap<TypeId, Box<dyn Addon>>,
726 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
727 load_diff_task: Option<Shared<Task<()>>>,
728 selection_mark_mode: bool,
729 toggle_fold_multiple_buffers: Task<()>,
730 _scroll_cursor_center_top_bottom_task: Task<()>,
731 serialize_selections: Task<()>,
732 mouse_cursor_hidden: bool,
733 hide_mouse_while_typing: bool,
734}
735
736#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
737enum NextScrollCursorCenterTopBottom {
738 #[default]
739 Center,
740 Top,
741 Bottom,
742}
743
744impl NextScrollCursorCenterTopBottom {
745 fn next(&self) -> Self {
746 match self {
747 Self::Center => Self::Top,
748 Self::Top => Self::Bottom,
749 Self::Bottom => Self::Center,
750 }
751 }
752}
753
754#[derive(Clone)]
755pub struct EditorSnapshot {
756 pub mode: EditorMode,
757 show_gutter: bool,
758 show_line_numbers: Option<bool>,
759 show_git_diff_gutter: Option<bool>,
760 show_code_actions: Option<bool>,
761 show_runnables: Option<bool>,
762 git_blame_gutter_max_author_length: Option<usize>,
763 pub display_snapshot: DisplaySnapshot,
764 pub placeholder_text: Option<Arc<str>>,
765 is_focused: bool,
766 scroll_anchor: ScrollAnchor,
767 ongoing_scroll: OngoingScroll,
768 current_line_highlight: CurrentLineHighlight,
769 gutter_hovered: bool,
770}
771
772const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
773
774#[derive(Default, Debug, Clone, Copy)]
775pub struct GutterDimensions {
776 pub left_padding: Pixels,
777 pub right_padding: Pixels,
778 pub width: Pixels,
779 pub margin: Pixels,
780 pub git_blame_entries_width: Option<Pixels>,
781}
782
783impl GutterDimensions {
784 /// The full width of the space taken up by the gutter.
785 pub fn full_width(&self) -> Pixels {
786 self.margin + self.width
787 }
788
789 /// The width of the space reserved for the fold indicators,
790 /// use alongside 'justify_end' and `gutter_width` to
791 /// right align content with the line numbers
792 pub fn fold_area_width(&self) -> Pixels {
793 self.margin + self.right_padding
794 }
795}
796
797#[derive(Debug)]
798pub struct RemoteSelection {
799 pub replica_id: ReplicaId,
800 pub selection: Selection<Anchor>,
801 pub cursor_shape: CursorShape,
802 pub peer_id: PeerId,
803 pub line_mode: bool,
804 pub participant_index: Option<ParticipantIndex>,
805 pub user_name: Option<SharedString>,
806}
807
808#[derive(Clone, Debug)]
809struct SelectionHistoryEntry {
810 selections: Arc<[Selection<Anchor>]>,
811 select_next_state: Option<SelectNextState>,
812 select_prev_state: Option<SelectNextState>,
813 add_selections_state: Option<AddSelectionsState>,
814}
815
816enum SelectionHistoryMode {
817 Normal,
818 Undoing,
819 Redoing,
820}
821
822#[derive(Clone, PartialEq, Eq, Hash)]
823struct HoveredCursor {
824 replica_id: u16,
825 selection_id: usize,
826}
827
828impl Default for SelectionHistoryMode {
829 fn default() -> Self {
830 Self::Normal
831 }
832}
833
834#[derive(Default)]
835struct SelectionHistory {
836 #[allow(clippy::type_complexity)]
837 selections_by_transaction:
838 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
839 mode: SelectionHistoryMode,
840 undo_stack: VecDeque<SelectionHistoryEntry>,
841 redo_stack: VecDeque<SelectionHistoryEntry>,
842}
843
844impl SelectionHistory {
845 fn insert_transaction(
846 &mut self,
847 transaction_id: TransactionId,
848 selections: Arc<[Selection<Anchor>]>,
849 ) {
850 self.selections_by_transaction
851 .insert(transaction_id, (selections, None));
852 }
853
854 #[allow(clippy::type_complexity)]
855 fn transaction(
856 &self,
857 transaction_id: TransactionId,
858 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
859 self.selections_by_transaction.get(&transaction_id)
860 }
861
862 #[allow(clippy::type_complexity)]
863 fn transaction_mut(
864 &mut self,
865 transaction_id: TransactionId,
866 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
867 self.selections_by_transaction.get_mut(&transaction_id)
868 }
869
870 fn push(&mut self, entry: SelectionHistoryEntry) {
871 if !entry.selections.is_empty() {
872 match self.mode {
873 SelectionHistoryMode::Normal => {
874 self.push_undo(entry);
875 self.redo_stack.clear();
876 }
877 SelectionHistoryMode::Undoing => self.push_redo(entry),
878 SelectionHistoryMode::Redoing => self.push_undo(entry),
879 }
880 }
881 }
882
883 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
884 if self
885 .undo_stack
886 .back()
887 .map_or(true, |e| e.selections != entry.selections)
888 {
889 self.undo_stack.push_back(entry);
890 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
891 self.undo_stack.pop_front();
892 }
893 }
894 }
895
896 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
897 if self
898 .redo_stack
899 .back()
900 .map_or(true, |e| e.selections != entry.selections)
901 {
902 self.redo_stack.push_back(entry);
903 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
904 self.redo_stack.pop_front();
905 }
906 }
907 }
908}
909
910struct RowHighlight {
911 index: usize,
912 range: Range<Anchor>,
913 color: Hsla,
914 should_autoscroll: bool,
915}
916
917#[derive(Clone, Debug)]
918struct AddSelectionsState {
919 above: bool,
920 stack: Vec<usize>,
921}
922
923#[derive(Clone)]
924struct SelectNextState {
925 query: AhoCorasick,
926 wordwise: bool,
927 done: bool,
928}
929
930impl std::fmt::Debug for SelectNextState {
931 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
932 f.debug_struct(std::any::type_name::<Self>())
933 .field("wordwise", &self.wordwise)
934 .field("done", &self.done)
935 .finish()
936 }
937}
938
939#[derive(Debug)]
940struct AutocloseRegion {
941 selection_id: usize,
942 range: Range<Anchor>,
943 pair: BracketPair,
944}
945
946#[derive(Debug)]
947struct SnippetState {
948 ranges: Vec<Vec<Range<Anchor>>>,
949 active_index: usize,
950 choices: Vec<Option<Vec<String>>>,
951}
952
953#[doc(hidden)]
954pub struct RenameState {
955 pub range: Range<Anchor>,
956 pub old_name: Arc<str>,
957 pub editor: Entity<Editor>,
958 block_id: CustomBlockId,
959}
960
961struct InvalidationStack<T>(Vec<T>);
962
963struct RegisteredInlineCompletionProvider {
964 provider: Arc<dyn InlineCompletionProviderHandle>,
965 _subscription: Subscription,
966}
967
968#[derive(Debug)]
969struct ActiveDiagnosticGroup {
970 primary_range: Range<Anchor>,
971 primary_message: String,
972 group_id: usize,
973 blocks: HashMap<CustomBlockId, Diagnostic>,
974 is_valid: bool,
975}
976
977#[derive(Serialize, Deserialize, Clone, Debug)]
978pub struct ClipboardSelection {
979 pub len: usize,
980 pub is_entire_line: bool,
981 pub first_line_indent: 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 distinguish_unstaged_diff_hunks: false,
1411 show_selection_menu: None,
1412 show_git_blame_inline_delay_task: None,
1413 git_blame_inline_tooltip: None,
1414 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1415 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1416 .session
1417 .restore_unsaved_buffers,
1418 blame: None,
1419 blame_subscription: None,
1420 tasks: Default::default(),
1421 _subscriptions: vec![
1422 cx.observe(&buffer, Self::on_buffer_changed),
1423 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1424 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1425 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1426 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1427 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1428 cx.observe_window_activation(window, |editor, window, cx| {
1429 let active = window.is_window_active();
1430 editor.blink_manager.update(cx, |blink_manager, cx| {
1431 if active {
1432 blink_manager.enable(cx);
1433 } else {
1434 blink_manager.disable(cx);
1435 }
1436 });
1437 }),
1438 ],
1439 tasks_update_task: None,
1440 linked_edit_ranges: Default::default(),
1441 in_project_search: false,
1442 previous_search_ranges: None,
1443 breadcrumb_header: None,
1444 focused_block: None,
1445 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1446 addons: HashMap::default(),
1447 registered_buffers: HashMap::default(),
1448 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1449 selection_mark_mode: false,
1450 toggle_fold_multiple_buffers: Task::ready(()),
1451 serialize_selections: Task::ready(()),
1452 text_style_refinement: None,
1453 load_diff_task: load_uncommitted_diff,
1454 mouse_cursor_hidden: false,
1455 hide_mouse_while_typing: EditorSettings::get_global(cx)
1456 .hide_mouse_while_typing
1457 .unwrap_or(true),
1458 };
1459 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1460 this._subscriptions.extend(project_subscriptions);
1461
1462 this.end_selection(window, cx);
1463 this.scroll_manager.show_scrollbar(window, cx);
1464
1465 if mode == EditorMode::Full {
1466 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1467 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1468
1469 if this.git_blame_inline_enabled {
1470 this.git_blame_inline_enabled = true;
1471 this.start_git_blame_inline(false, window, cx);
1472 }
1473
1474 if let Some(buffer) = buffer.read(cx).as_singleton() {
1475 if let Some(project) = this.project.as_ref() {
1476 let handle = project.update(cx, |project, cx| {
1477 project.register_buffer_with_language_servers(&buffer, cx)
1478 });
1479 this.registered_buffers
1480 .insert(buffer.read(cx).remote_id(), handle);
1481 }
1482 }
1483 }
1484
1485 this.report_editor_event("Editor Opened", None, cx);
1486 this
1487 }
1488
1489 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1490 self.mouse_context_menu
1491 .as_ref()
1492 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1493 }
1494
1495 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1496 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1497 }
1498
1499 fn key_context_internal(
1500 &self,
1501 has_active_edit_prediction: bool,
1502 window: &Window,
1503 cx: &App,
1504 ) -> KeyContext {
1505 let mut key_context = KeyContext::new_with_defaults();
1506 key_context.add("Editor");
1507 let mode = match self.mode {
1508 EditorMode::SingleLine { .. } => "single_line",
1509 EditorMode::AutoHeight { .. } => "auto_height",
1510 EditorMode::Full => "full",
1511 };
1512
1513 if EditorSettings::jupyter_enabled(cx) {
1514 key_context.add("jupyter");
1515 }
1516
1517 key_context.set("mode", mode);
1518 if self.pending_rename.is_some() {
1519 key_context.add("renaming");
1520 }
1521
1522 match self.context_menu.borrow().as_ref() {
1523 Some(CodeContextMenu::Completions(_)) => {
1524 key_context.add("menu");
1525 key_context.add("showing_completions");
1526 }
1527 Some(CodeContextMenu::CodeActions(_)) => {
1528 key_context.add("menu");
1529 key_context.add("showing_code_actions")
1530 }
1531 None => {}
1532 }
1533
1534 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1535 if !self.focus_handle(cx).contains_focused(window, cx)
1536 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1537 {
1538 for addon in self.addons.values() {
1539 addon.extend_key_context(&mut key_context, cx)
1540 }
1541 }
1542
1543 if let Some(extension) = self
1544 .buffer
1545 .read(cx)
1546 .as_singleton()
1547 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1548 {
1549 key_context.set("extension", extension.to_string());
1550 }
1551
1552 if has_active_edit_prediction {
1553 if self.edit_prediction_in_conflict() {
1554 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1555 } else {
1556 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1557 key_context.add("copilot_suggestion");
1558 }
1559 }
1560
1561 if self.selection_mark_mode {
1562 key_context.add("selection_mode");
1563 }
1564
1565 key_context
1566 }
1567
1568 pub fn edit_prediction_in_conflict(&self) -> bool {
1569 if !self.show_edit_predictions_in_menu() {
1570 return false;
1571 }
1572
1573 let showing_completions = self
1574 .context_menu
1575 .borrow()
1576 .as_ref()
1577 .map_or(false, |context| {
1578 matches!(context, CodeContextMenu::Completions(_))
1579 });
1580
1581 showing_completions
1582 || self.edit_prediction_requires_modifier()
1583 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1584 // bindings to insert tab characters.
1585 || (self.edit_prediction_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
1586 }
1587
1588 pub fn accept_edit_prediction_keybind(
1589 &self,
1590 window: &Window,
1591 cx: &App,
1592 ) -> AcceptEditPredictionBinding {
1593 let key_context = self.key_context_internal(true, window, cx);
1594 let in_conflict = self.edit_prediction_in_conflict();
1595
1596 AcceptEditPredictionBinding(
1597 window
1598 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1599 .into_iter()
1600 .filter(|binding| {
1601 !in_conflict
1602 || binding
1603 .keystrokes()
1604 .first()
1605 .map_or(false, |keystroke| keystroke.modifiers.modified())
1606 })
1607 .rev()
1608 .min_by_key(|binding| {
1609 binding
1610 .keystrokes()
1611 .first()
1612 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1613 }),
1614 )
1615 }
1616
1617 pub fn new_file(
1618 workspace: &mut Workspace,
1619 _: &workspace::NewFile,
1620 window: &mut Window,
1621 cx: &mut Context<Workspace>,
1622 ) {
1623 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1624 "Failed to create buffer",
1625 window,
1626 cx,
1627 |e, _, _| match e.error_code() {
1628 ErrorCode::RemoteUpgradeRequired => Some(format!(
1629 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1630 e.error_tag("required").unwrap_or("the latest version")
1631 )),
1632 _ => None,
1633 },
1634 );
1635 }
1636
1637 pub fn new_in_workspace(
1638 workspace: &mut Workspace,
1639 window: &mut Window,
1640 cx: &mut Context<Workspace>,
1641 ) -> Task<Result<Entity<Editor>>> {
1642 let project = workspace.project().clone();
1643 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1644
1645 cx.spawn_in(window, |workspace, mut cx| async move {
1646 let buffer = create.await?;
1647 workspace.update_in(&mut cx, |workspace, window, cx| {
1648 let editor =
1649 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1650 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1651 editor
1652 })
1653 })
1654 }
1655
1656 fn new_file_vertical(
1657 workspace: &mut Workspace,
1658 _: &workspace::NewFileSplitVertical,
1659 window: &mut Window,
1660 cx: &mut Context<Workspace>,
1661 ) {
1662 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1663 }
1664
1665 fn new_file_horizontal(
1666 workspace: &mut Workspace,
1667 _: &workspace::NewFileSplitHorizontal,
1668 window: &mut Window,
1669 cx: &mut Context<Workspace>,
1670 ) {
1671 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1672 }
1673
1674 fn new_file_in_direction(
1675 workspace: &mut Workspace,
1676 direction: SplitDirection,
1677 window: &mut Window,
1678 cx: &mut Context<Workspace>,
1679 ) {
1680 let project = workspace.project().clone();
1681 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1682
1683 cx.spawn_in(window, |workspace, mut cx| async move {
1684 let buffer = create.await?;
1685 workspace.update_in(&mut cx, move |workspace, window, cx| {
1686 workspace.split_item(
1687 direction,
1688 Box::new(
1689 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1690 ),
1691 window,
1692 cx,
1693 )
1694 })?;
1695 anyhow::Ok(())
1696 })
1697 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1698 match e.error_code() {
1699 ErrorCode::RemoteUpgradeRequired => Some(format!(
1700 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1701 e.error_tag("required").unwrap_or("the latest version")
1702 )),
1703 _ => None,
1704 }
1705 });
1706 }
1707
1708 pub fn leader_peer_id(&self) -> Option<PeerId> {
1709 self.leader_peer_id
1710 }
1711
1712 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1713 &self.buffer
1714 }
1715
1716 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1717 self.workspace.as_ref()?.0.upgrade()
1718 }
1719
1720 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1721 self.buffer().read(cx).title(cx)
1722 }
1723
1724 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1725 let git_blame_gutter_max_author_length = self
1726 .render_git_blame_gutter(cx)
1727 .then(|| {
1728 if let Some(blame) = self.blame.as_ref() {
1729 let max_author_length =
1730 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1731 Some(max_author_length)
1732 } else {
1733 None
1734 }
1735 })
1736 .flatten();
1737
1738 EditorSnapshot {
1739 mode: self.mode,
1740 show_gutter: self.show_gutter,
1741 show_line_numbers: self.show_line_numbers,
1742 show_git_diff_gutter: self.show_git_diff_gutter,
1743 show_code_actions: self.show_code_actions,
1744 show_runnables: self.show_runnables,
1745 git_blame_gutter_max_author_length,
1746 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1747 scroll_anchor: self.scroll_manager.anchor(),
1748 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1749 placeholder_text: self.placeholder_text.clone(),
1750 is_focused: self.focus_handle.is_focused(window),
1751 current_line_highlight: self
1752 .current_line_highlight
1753 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1754 gutter_hovered: self.gutter_hovered,
1755 }
1756 }
1757
1758 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1759 self.buffer.read(cx).language_at(point, cx)
1760 }
1761
1762 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1763 self.buffer.read(cx).read(cx).file_at(point).cloned()
1764 }
1765
1766 pub fn active_excerpt(
1767 &self,
1768 cx: &App,
1769 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1770 self.buffer
1771 .read(cx)
1772 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1773 }
1774
1775 pub fn mode(&self) -> EditorMode {
1776 self.mode
1777 }
1778
1779 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1780 self.collaboration_hub.as_deref()
1781 }
1782
1783 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1784 self.collaboration_hub = Some(hub);
1785 }
1786
1787 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1788 self.in_project_search = in_project_search;
1789 }
1790
1791 pub fn set_custom_context_menu(
1792 &mut self,
1793 f: impl 'static
1794 + Fn(
1795 &mut Self,
1796 DisplayPoint,
1797 &mut Window,
1798 &mut Context<Self>,
1799 ) -> Option<Entity<ui::ContextMenu>>,
1800 ) {
1801 self.custom_context_menu = Some(Box::new(f))
1802 }
1803
1804 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1805 self.completion_provider = provider;
1806 }
1807
1808 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1809 self.semantics_provider.clone()
1810 }
1811
1812 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1813 self.semantics_provider = provider;
1814 }
1815
1816 pub fn set_edit_prediction_provider<T>(
1817 &mut self,
1818 provider: Option<Entity<T>>,
1819 window: &mut Window,
1820 cx: &mut Context<Self>,
1821 ) where
1822 T: EditPredictionProvider,
1823 {
1824 self.edit_prediction_provider =
1825 provider.map(|provider| RegisteredInlineCompletionProvider {
1826 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1827 if this.focus_handle.is_focused(window) {
1828 this.update_visible_inline_completion(window, cx);
1829 }
1830 }),
1831 provider: Arc::new(provider),
1832 });
1833 self.refresh_inline_completion(false, false, window, cx);
1834 }
1835
1836 pub fn placeholder_text(&self) -> Option<&str> {
1837 self.placeholder_text.as_deref()
1838 }
1839
1840 pub fn set_placeholder_text(
1841 &mut self,
1842 placeholder_text: impl Into<Arc<str>>,
1843 cx: &mut Context<Self>,
1844 ) {
1845 let placeholder_text = Some(placeholder_text.into());
1846 if self.placeholder_text != placeholder_text {
1847 self.placeholder_text = placeholder_text;
1848 cx.notify();
1849 }
1850 }
1851
1852 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1853 self.cursor_shape = cursor_shape;
1854
1855 // Disrupt blink for immediate user feedback that the cursor shape has changed
1856 self.blink_manager.update(cx, BlinkManager::show_cursor);
1857
1858 cx.notify();
1859 }
1860
1861 pub fn set_current_line_highlight(
1862 &mut self,
1863 current_line_highlight: Option<CurrentLineHighlight>,
1864 ) {
1865 self.current_line_highlight = current_line_highlight;
1866 }
1867
1868 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1869 self.collapse_matches = collapse_matches;
1870 }
1871
1872 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1873 let buffers = self.buffer.read(cx).all_buffers();
1874 let Some(project) = self.project.as_ref() else {
1875 return;
1876 };
1877 project.update(cx, |project, cx| {
1878 for buffer in buffers {
1879 self.registered_buffers
1880 .entry(buffer.read(cx).remote_id())
1881 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1882 }
1883 })
1884 }
1885
1886 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1887 if self.collapse_matches {
1888 return range.start..range.start;
1889 }
1890 range.clone()
1891 }
1892
1893 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1894 if self.display_map.read(cx).clip_at_line_ends != clip {
1895 self.display_map
1896 .update(cx, |map, _| map.clip_at_line_ends = clip);
1897 }
1898 }
1899
1900 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1901 self.input_enabled = input_enabled;
1902 }
1903
1904 pub fn set_inline_completions_hidden_for_vim_mode(
1905 &mut self,
1906 hidden: bool,
1907 window: &mut Window,
1908 cx: &mut Context<Self>,
1909 ) {
1910 if hidden != self.inline_completions_hidden_for_vim_mode {
1911 self.inline_completions_hidden_for_vim_mode = hidden;
1912 if hidden {
1913 self.update_visible_inline_completion(window, cx);
1914 } else {
1915 self.refresh_inline_completion(true, false, window, cx);
1916 }
1917 }
1918 }
1919
1920 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1921 self.menu_inline_completions_policy = value;
1922 }
1923
1924 pub fn set_autoindent(&mut self, autoindent: bool) {
1925 if autoindent {
1926 self.autoindent_mode = Some(AutoindentMode::EachLine);
1927 } else {
1928 self.autoindent_mode = None;
1929 }
1930 }
1931
1932 pub fn read_only(&self, cx: &App) -> bool {
1933 self.read_only || self.buffer.read(cx).read_only()
1934 }
1935
1936 pub fn set_read_only(&mut self, read_only: bool) {
1937 self.read_only = read_only;
1938 }
1939
1940 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1941 self.use_autoclose = autoclose;
1942 }
1943
1944 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1945 self.use_auto_surround = auto_surround;
1946 }
1947
1948 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1949 self.auto_replace_emoji_shortcode = auto_replace;
1950 }
1951
1952 pub fn toggle_inline_completions(
1953 &mut self,
1954 _: &ToggleEditPrediction,
1955 window: &mut Window,
1956 cx: &mut Context<Self>,
1957 ) {
1958 if self.show_inline_completions_override.is_some() {
1959 self.set_show_edit_predictions(None, window, cx);
1960 } else {
1961 let show_edit_predictions = !self.edit_predictions_enabled();
1962 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1963 }
1964 }
1965
1966 pub fn set_show_edit_predictions(
1967 &mut self,
1968 show_edit_predictions: Option<bool>,
1969 window: &mut Window,
1970 cx: &mut Context<Self>,
1971 ) {
1972 self.show_inline_completions_override = show_edit_predictions;
1973
1974 if let Some(false) = show_edit_predictions {
1975 self.discard_inline_completion(false, cx);
1976 } else {
1977 self.refresh_inline_completion(false, true, window, cx);
1978 }
1979 }
1980
1981 fn inline_completions_disabled_in_scope(
1982 &self,
1983 buffer: &Entity<Buffer>,
1984 buffer_position: language::Anchor,
1985 cx: &App,
1986 ) -> bool {
1987 let snapshot = buffer.read(cx).snapshot();
1988 let settings = snapshot.settings_at(buffer_position, cx);
1989
1990 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1991 return false;
1992 };
1993
1994 scope.override_name().map_or(false, |scope_name| {
1995 settings
1996 .edit_predictions_disabled_in
1997 .iter()
1998 .any(|s| s == scope_name)
1999 })
2000 }
2001
2002 pub fn set_use_modal_editing(&mut self, to: bool) {
2003 self.use_modal_editing = to;
2004 }
2005
2006 pub fn use_modal_editing(&self) -> bool {
2007 self.use_modal_editing
2008 }
2009
2010 fn selections_did_change(
2011 &mut self,
2012 local: bool,
2013 old_cursor_position: &Anchor,
2014 show_completions: bool,
2015 window: &mut Window,
2016 cx: &mut Context<Self>,
2017 ) {
2018 window.invalidate_character_coordinates();
2019
2020 // Copy selections to primary selection buffer
2021 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2022 if local {
2023 let selections = self.selections.all::<usize>(cx);
2024 let buffer_handle = self.buffer.read(cx).read(cx);
2025
2026 let mut text = String::new();
2027 for (index, selection) in selections.iter().enumerate() {
2028 let text_for_selection = buffer_handle
2029 .text_for_range(selection.start..selection.end)
2030 .collect::<String>();
2031
2032 text.push_str(&text_for_selection);
2033 if index != selections.len() - 1 {
2034 text.push('\n');
2035 }
2036 }
2037
2038 if !text.is_empty() {
2039 cx.write_to_primary(ClipboardItem::new_string(text));
2040 }
2041 }
2042
2043 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2044 self.buffer.update(cx, |buffer, cx| {
2045 buffer.set_active_selections(
2046 &self.selections.disjoint_anchors(),
2047 self.selections.line_mode,
2048 self.cursor_shape,
2049 cx,
2050 )
2051 });
2052 }
2053 let display_map = self
2054 .display_map
2055 .update(cx, |display_map, cx| display_map.snapshot(cx));
2056 let buffer = &display_map.buffer_snapshot;
2057 self.add_selections_state = None;
2058 self.select_next_state = None;
2059 self.select_prev_state = None;
2060 self.select_larger_syntax_node_stack.clear();
2061 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2062 self.snippet_stack
2063 .invalidate(&self.selections.disjoint_anchors(), buffer);
2064 self.take_rename(false, window, cx);
2065
2066 let new_cursor_position = self.selections.newest_anchor().head();
2067
2068 self.push_to_nav_history(
2069 *old_cursor_position,
2070 Some(new_cursor_position.to_point(buffer)),
2071 cx,
2072 );
2073
2074 if local {
2075 let new_cursor_position = self.selections.newest_anchor().head();
2076 let mut context_menu = self.context_menu.borrow_mut();
2077 let completion_menu = match context_menu.as_ref() {
2078 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2079 _ => {
2080 *context_menu = None;
2081 None
2082 }
2083 };
2084 if let Some(buffer_id) = new_cursor_position.buffer_id {
2085 if !self.registered_buffers.contains_key(&buffer_id) {
2086 if let Some(project) = self.project.as_ref() {
2087 project.update(cx, |project, cx| {
2088 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2089 return;
2090 };
2091 self.registered_buffers.insert(
2092 buffer_id,
2093 project.register_buffer_with_language_servers(&buffer, cx),
2094 );
2095 })
2096 }
2097 }
2098 }
2099
2100 if let Some(completion_menu) = completion_menu {
2101 let cursor_position = new_cursor_position.to_offset(buffer);
2102 let (word_range, kind) =
2103 buffer.surrounding_word(completion_menu.initial_position, true);
2104 if kind == Some(CharKind::Word)
2105 && word_range.to_inclusive().contains(&cursor_position)
2106 {
2107 let mut completion_menu = completion_menu.clone();
2108 drop(context_menu);
2109
2110 let query = Self::completion_query(buffer, cursor_position);
2111 cx.spawn(move |this, mut cx| async move {
2112 completion_menu
2113 .filter(query.as_deref(), cx.background_executor().clone())
2114 .await;
2115
2116 this.update(&mut cx, |this, cx| {
2117 let mut context_menu = this.context_menu.borrow_mut();
2118 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2119 else {
2120 return;
2121 };
2122
2123 if menu.id > completion_menu.id {
2124 return;
2125 }
2126
2127 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2128 drop(context_menu);
2129 cx.notify();
2130 })
2131 })
2132 .detach();
2133
2134 if show_completions {
2135 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2136 }
2137 } else {
2138 drop(context_menu);
2139 self.hide_context_menu(window, cx);
2140 }
2141 } else {
2142 drop(context_menu);
2143 }
2144
2145 hide_hover(self, cx);
2146
2147 if old_cursor_position.to_display_point(&display_map).row()
2148 != new_cursor_position.to_display_point(&display_map).row()
2149 {
2150 self.available_code_actions.take();
2151 }
2152 self.refresh_code_actions(window, cx);
2153 self.refresh_document_highlights(cx);
2154 self.refresh_selected_text_highlights(window, cx);
2155 refresh_matching_bracket_highlights(self, window, cx);
2156 self.update_visible_inline_completion(window, cx);
2157 self.edit_prediction_requires_modifier_in_leading_space = true;
2158 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2159 if self.git_blame_inline_enabled {
2160 self.start_inline_blame_timer(window, cx);
2161 }
2162 }
2163
2164 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2165 cx.emit(EditorEvent::SelectionsChanged { local });
2166
2167 let selections = &self.selections.disjoint;
2168 if selections.len() == 1 {
2169 cx.emit(SearchEvent::ActiveMatchChanged)
2170 }
2171 if local
2172 && self.is_singleton(cx)
2173 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2174 {
2175 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2176 let background_executor = cx.background_executor().clone();
2177 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2178 let snapshot = self.buffer().read(cx).snapshot(cx);
2179 let selections = selections.clone();
2180 self.serialize_selections = cx.background_spawn(async move {
2181 background_executor.timer(Duration::from_millis(100)).await;
2182 let selections = selections
2183 .iter()
2184 .map(|selection| {
2185 (
2186 selection.start.to_offset(&snapshot),
2187 selection.end.to_offset(&snapshot),
2188 )
2189 })
2190 .collect();
2191 DB.save_editor_selections(editor_id, workspace_id, selections)
2192 .await
2193 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2194 .log_err();
2195 });
2196 }
2197 }
2198
2199 cx.notify();
2200 }
2201
2202 pub fn change_selections<R>(
2203 &mut self,
2204 autoscroll: Option<Autoscroll>,
2205 window: &mut Window,
2206 cx: &mut Context<Self>,
2207 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2208 ) -> R {
2209 self.change_selections_inner(autoscroll, true, window, cx, change)
2210 }
2211
2212 fn change_selections_inner<R>(
2213 &mut self,
2214 autoscroll: Option<Autoscroll>,
2215 request_completions: bool,
2216 window: &mut Window,
2217 cx: &mut Context<Self>,
2218 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2219 ) -> R {
2220 let old_cursor_position = self.selections.newest_anchor().head();
2221 self.push_to_selection_history();
2222
2223 let (changed, result) = self.selections.change_with(cx, change);
2224
2225 if changed {
2226 if let Some(autoscroll) = autoscroll {
2227 self.request_autoscroll(autoscroll, cx);
2228 }
2229 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2230
2231 if self.should_open_signature_help_automatically(
2232 &old_cursor_position,
2233 self.signature_help_state.backspace_pressed(),
2234 cx,
2235 ) {
2236 self.show_signature_help(&ShowSignatureHelp, window, cx);
2237 }
2238 self.signature_help_state.set_backspace_pressed(false);
2239 }
2240
2241 result
2242 }
2243
2244 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2245 where
2246 I: IntoIterator<Item = (Range<S>, T)>,
2247 S: ToOffset,
2248 T: Into<Arc<str>>,
2249 {
2250 if self.read_only(cx) {
2251 return;
2252 }
2253
2254 self.buffer
2255 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2256 }
2257
2258 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2259 where
2260 I: IntoIterator<Item = (Range<S>, T)>,
2261 S: ToOffset,
2262 T: Into<Arc<str>>,
2263 {
2264 if self.read_only(cx) {
2265 return;
2266 }
2267
2268 self.buffer.update(cx, |buffer, cx| {
2269 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2270 });
2271 }
2272
2273 pub fn edit_with_block_indent<I, S, T>(
2274 &mut self,
2275 edits: I,
2276 original_indent_columns: Vec<u32>,
2277 cx: &mut Context<Self>,
2278 ) where
2279 I: IntoIterator<Item = (Range<S>, T)>,
2280 S: ToOffset,
2281 T: Into<Arc<str>>,
2282 {
2283 if self.read_only(cx) {
2284 return;
2285 }
2286
2287 self.buffer.update(cx, |buffer, cx| {
2288 buffer.edit(
2289 edits,
2290 Some(AutoindentMode::Block {
2291 original_indent_columns,
2292 }),
2293 cx,
2294 )
2295 });
2296 }
2297
2298 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2299 self.hide_context_menu(window, cx);
2300
2301 match phase {
2302 SelectPhase::Begin {
2303 position,
2304 add,
2305 click_count,
2306 } => self.begin_selection(position, add, click_count, window, cx),
2307 SelectPhase::BeginColumnar {
2308 position,
2309 goal_column,
2310 reset,
2311 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2312 SelectPhase::Extend {
2313 position,
2314 click_count,
2315 } => self.extend_selection(position, click_count, window, cx),
2316 SelectPhase::Update {
2317 position,
2318 goal_column,
2319 scroll_delta,
2320 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2321 SelectPhase::End => self.end_selection(window, cx),
2322 }
2323 }
2324
2325 fn extend_selection(
2326 &mut self,
2327 position: DisplayPoint,
2328 click_count: usize,
2329 window: &mut Window,
2330 cx: &mut Context<Self>,
2331 ) {
2332 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2333 let tail = self.selections.newest::<usize>(cx).tail();
2334 self.begin_selection(position, false, click_count, window, cx);
2335
2336 let position = position.to_offset(&display_map, Bias::Left);
2337 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2338
2339 let mut pending_selection = self
2340 .selections
2341 .pending_anchor()
2342 .expect("extend_selection not called with pending selection");
2343 if position >= tail {
2344 pending_selection.start = tail_anchor;
2345 } else {
2346 pending_selection.end = tail_anchor;
2347 pending_selection.reversed = true;
2348 }
2349
2350 let mut pending_mode = self.selections.pending_mode().unwrap();
2351 match &mut pending_mode {
2352 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2353 _ => {}
2354 }
2355
2356 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2357 s.set_pending(pending_selection, pending_mode)
2358 });
2359 }
2360
2361 fn begin_selection(
2362 &mut self,
2363 position: DisplayPoint,
2364 add: bool,
2365 click_count: usize,
2366 window: &mut Window,
2367 cx: &mut Context<Self>,
2368 ) {
2369 if !self.focus_handle.is_focused(window) {
2370 self.last_focused_descendant = None;
2371 window.focus(&self.focus_handle);
2372 }
2373
2374 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2375 let buffer = &display_map.buffer_snapshot;
2376 let newest_selection = self.selections.newest_anchor().clone();
2377 let position = display_map.clip_point(position, Bias::Left);
2378
2379 let start;
2380 let end;
2381 let mode;
2382 let mut auto_scroll;
2383 match click_count {
2384 1 => {
2385 start = buffer.anchor_before(position.to_point(&display_map));
2386 end = start;
2387 mode = SelectMode::Character;
2388 auto_scroll = true;
2389 }
2390 2 => {
2391 let range = movement::surrounding_word(&display_map, position);
2392 start = buffer.anchor_before(range.start.to_point(&display_map));
2393 end = buffer.anchor_before(range.end.to_point(&display_map));
2394 mode = SelectMode::Word(start..end);
2395 auto_scroll = true;
2396 }
2397 3 => {
2398 let position = display_map
2399 .clip_point(position, Bias::Left)
2400 .to_point(&display_map);
2401 let line_start = display_map.prev_line_boundary(position).0;
2402 let next_line_start = buffer.clip_point(
2403 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2404 Bias::Left,
2405 );
2406 start = buffer.anchor_before(line_start);
2407 end = buffer.anchor_before(next_line_start);
2408 mode = SelectMode::Line(start..end);
2409 auto_scroll = true;
2410 }
2411 _ => {
2412 start = buffer.anchor_before(0);
2413 end = buffer.anchor_before(buffer.len());
2414 mode = SelectMode::All;
2415 auto_scroll = false;
2416 }
2417 }
2418 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2419
2420 let point_to_delete: Option<usize> = {
2421 let selected_points: Vec<Selection<Point>> =
2422 self.selections.disjoint_in_range(start..end, cx);
2423
2424 if !add || click_count > 1 {
2425 None
2426 } else if !selected_points.is_empty() {
2427 Some(selected_points[0].id)
2428 } else {
2429 let clicked_point_already_selected =
2430 self.selections.disjoint.iter().find(|selection| {
2431 selection.start.to_point(buffer) == start.to_point(buffer)
2432 || selection.end.to_point(buffer) == end.to_point(buffer)
2433 });
2434
2435 clicked_point_already_selected.map(|selection| selection.id)
2436 }
2437 };
2438
2439 let selections_count = self.selections.count();
2440
2441 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2442 if let Some(point_to_delete) = point_to_delete {
2443 s.delete(point_to_delete);
2444
2445 if selections_count == 1 {
2446 s.set_pending_anchor_range(start..end, mode);
2447 }
2448 } else {
2449 if !add {
2450 s.clear_disjoint();
2451 } else if click_count > 1 {
2452 s.delete(newest_selection.id)
2453 }
2454
2455 s.set_pending_anchor_range(start..end, mode);
2456 }
2457 });
2458 }
2459
2460 fn begin_columnar_selection(
2461 &mut self,
2462 position: DisplayPoint,
2463 goal_column: u32,
2464 reset: bool,
2465 window: &mut Window,
2466 cx: &mut Context<Self>,
2467 ) {
2468 if !self.focus_handle.is_focused(window) {
2469 self.last_focused_descendant = None;
2470 window.focus(&self.focus_handle);
2471 }
2472
2473 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2474
2475 if reset {
2476 let pointer_position = display_map
2477 .buffer_snapshot
2478 .anchor_before(position.to_point(&display_map));
2479
2480 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2481 s.clear_disjoint();
2482 s.set_pending_anchor_range(
2483 pointer_position..pointer_position,
2484 SelectMode::Character,
2485 );
2486 });
2487 }
2488
2489 let tail = self.selections.newest::<Point>(cx).tail();
2490 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2491
2492 if !reset {
2493 self.select_columns(
2494 tail.to_display_point(&display_map),
2495 position,
2496 goal_column,
2497 &display_map,
2498 window,
2499 cx,
2500 );
2501 }
2502 }
2503
2504 fn update_selection(
2505 &mut self,
2506 position: DisplayPoint,
2507 goal_column: u32,
2508 scroll_delta: gpui::Point<f32>,
2509 window: &mut Window,
2510 cx: &mut Context<Self>,
2511 ) {
2512 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2513
2514 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2515 let tail = tail.to_display_point(&display_map);
2516 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2517 } else if let Some(mut pending) = self.selections.pending_anchor() {
2518 let buffer = self.buffer.read(cx).snapshot(cx);
2519 let head;
2520 let tail;
2521 let mode = self.selections.pending_mode().unwrap();
2522 match &mode {
2523 SelectMode::Character => {
2524 head = position.to_point(&display_map);
2525 tail = pending.tail().to_point(&buffer);
2526 }
2527 SelectMode::Word(original_range) => {
2528 let original_display_range = original_range.start.to_display_point(&display_map)
2529 ..original_range.end.to_display_point(&display_map);
2530 let original_buffer_range = original_display_range.start.to_point(&display_map)
2531 ..original_display_range.end.to_point(&display_map);
2532 if movement::is_inside_word(&display_map, position)
2533 || original_display_range.contains(&position)
2534 {
2535 let word_range = movement::surrounding_word(&display_map, position);
2536 if word_range.start < original_display_range.start {
2537 head = word_range.start.to_point(&display_map);
2538 } else {
2539 head = word_range.end.to_point(&display_map);
2540 }
2541 } else {
2542 head = position.to_point(&display_map);
2543 }
2544
2545 if head <= original_buffer_range.start {
2546 tail = original_buffer_range.end;
2547 } else {
2548 tail = original_buffer_range.start;
2549 }
2550 }
2551 SelectMode::Line(original_range) => {
2552 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2553
2554 let position = display_map
2555 .clip_point(position, Bias::Left)
2556 .to_point(&display_map);
2557 let line_start = display_map.prev_line_boundary(position).0;
2558 let next_line_start = buffer.clip_point(
2559 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2560 Bias::Left,
2561 );
2562
2563 if line_start < original_range.start {
2564 head = line_start
2565 } else {
2566 head = next_line_start
2567 }
2568
2569 if head <= original_range.start {
2570 tail = original_range.end;
2571 } else {
2572 tail = original_range.start;
2573 }
2574 }
2575 SelectMode::All => {
2576 return;
2577 }
2578 };
2579
2580 if head < tail {
2581 pending.start = buffer.anchor_before(head);
2582 pending.end = buffer.anchor_before(tail);
2583 pending.reversed = true;
2584 } else {
2585 pending.start = buffer.anchor_before(tail);
2586 pending.end = buffer.anchor_before(head);
2587 pending.reversed = false;
2588 }
2589
2590 self.change_selections(None, window, cx, |s| {
2591 s.set_pending(pending, mode);
2592 });
2593 } else {
2594 log::error!("update_selection dispatched with no pending selection");
2595 return;
2596 }
2597
2598 self.apply_scroll_delta(scroll_delta, window, cx);
2599 cx.notify();
2600 }
2601
2602 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2603 self.columnar_selection_tail.take();
2604 if self.selections.pending_anchor().is_some() {
2605 let selections = self.selections.all::<usize>(cx);
2606 self.change_selections(None, window, cx, |s| {
2607 s.select(selections);
2608 s.clear_pending();
2609 });
2610 }
2611 }
2612
2613 fn select_columns(
2614 &mut self,
2615 tail: DisplayPoint,
2616 head: DisplayPoint,
2617 goal_column: u32,
2618 display_map: &DisplaySnapshot,
2619 window: &mut Window,
2620 cx: &mut Context<Self>,
2621 ) {
2622 let start_row = cmp::min(tail.row(), head.row());
2623 let end_row = cmp::max(tail.row(), head.row());
2624 let start_column = cmp::min(tail.column(), goal_column);
2625 let end_column = cmp::max(tail.column(), goal_column);
2626 let reversed = start_column < tail.column();
2627
2628 let selection_ranges = (start_row.0..=end_row.0)
2629 .map(DisplayRow)
2630 .filter_map(|row| {
2631 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2632 let start = display_map
2633 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2634 .to_point(display_map);
2635 let end = display_map
2636 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2637 .to_point(display_map);
2638 if reversed {
2639 Some(end..start)
2640 } else {
2641 Some(start..end)
2642 }
2643 } else {
2644 None
2645 }
2646 })
2647 .collect::<Vec<_>>();
2648
2649 self.change_selections(None, window, cx, |s| {
2650 s.select_ranges(selection_ranges);
2651 });
2652 cx.notify();
2653 }
2654
2655 pub fn has_pending_nonempty_selection(&self) -> bool {
2656 let pending_nonempty_selection = match self.selections.pending_anchor() {
2657 Some(Selection { start, end, .. }) => start != end,
2658 None => false,
2659 };
2660
2661 pending_nonempty_selection
2662 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2663 }
2664
2665 pub fn has_pending_selection(&self) -> bool {
2666 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2667 }
2668
2669 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2670 self.selection_mark_mode = false;
2671
2672 if self.clear_expanded_diff_hunks(cx) {
2673 cx.notify();
2674 return;
2675 }
2676 if self.dismiss_menus_and_popups(true, window, cx) {
2677 return;
2678 }
2679
2680 if self.mode == EditorMode::Full
2681 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2682 {
2683 return;
2684 }
2685
2686 cx.propagate();
2687 }
2688
2689 pub fn dismiss_menus_and_popups(
2690 &mut self,
2691 is_user_requested: bool,
2692 window: &mut Window,
2693 cx: &mut Context<Self>,
2694 ) -> bool {
2695 if self.take_rename(false, window, cx).is_some() {
2696 return true;
2697 }
2698
2699 if hide_hover(self, cx) {
2700 return true;
2701 }
2702
2703 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2704 return true;
2705 }
2706
2707 if self.hide_context_menu(window, cx).is_some() {
2708 return true;
2709 }
2710
2711 if self.mouse_context_menu.take().is_some() {
2712 return true;
2713 }
2714
2715 if is_user_requested && self.discard_inline_completion(true, cx) {
2716 return true;
2717 }
2718
2719 if self.snippet_stack.pop().is_some() {
2720 return true;
2721 }
2722
2723 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2724 self.dismiss_diagnostics(cx);
2725 return true;
2726 }
2727
2728 false
2729 }
2730
2731 fn linked_editing_ranges_for(
2732 &self,
2733 selection: Range<text::Anchor>,
2734 cx: &App,
2735 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2736 if self.linked_edit_ranges.is_empty() {
2737 return None;
2738 }
2739 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2740 selection.end.buffer_id.and_then(|end_buffer_id| {
2741 if selection.start.buffer_id != Some(end_buffer_id) {
2742 return None;
2743 }
2744 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2745 let snapshot = buffer.read(cx).snapshot();
2746 self.linked_edit_ranges
2747 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2748 .map(|ranges| (ranges, snapshot, buffer))
2749 })?;
2750 use text::ToOffset as TO;
2751 // find offset from the start of current range to current cursor position
2752 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2753
2754 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2755 let start_difference = start_offset - start_byte_offset;
2756 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2757 let end_difference = end_offset - start_byte_offset;
2758 // Current range has associated linked ranges.
2759 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2760 for range in linked_ranges.iter() {
2761 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2762 let end_offset = start_offset + end_difference;
2763 let start_offset = start_offset + start_difference;
2764 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2765 continue;
2766 }
2767 if self.selections.disjoint_anchor_ranges().any(|s| {
2768 if s.start.buffer_id != selection.start.buffer_id
2769 || s.end.buffer_id != selection.end.buffer_id
2770 {
2771 return false;
2772 }
2773 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2774 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2775 }) {
2776 continue;
2777 }
2778 let start = buffer_snapshot.anchor_after(start_offset);
2779 let end = buffer_snapshot.anchor_after(end_offset);
2780 linked_edits
2781 .entry(buffer.clone())
2782 .or_default()
2783 .push(start..end);
2784 }
2785 Some(linked_edits)
2786 }
2787
2788 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2789 let text: Arc<str> = text.into();
2790
2791 if self.read_only(cx) {
2792 return;
2793 }
2794
2795 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
2796
2797 let selections = self.selections.all_adjusted(cx);
2798 let mut bracket_inserted = false;
2799 let mut edits = Vec::new();
2800 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2801 let mut new_selections = Vec::with_capacity(selections.len());
2802 let mut new_autoclose_regions = Vec::new();
2803 let snapshot = self.buffer.read(cx).read(cx);
2804
2805 for (selection, autoclose_region) in
2806 self.selections_with_autoclose_regions(selections, &snapshot)
2807 {
2808 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2809 // Determine if the inserted text matches the opening or closing
2810 // bracket of any of this language's bracket pairs.
2811 let mut bracket_pair = None;
2812 let mut is_bracket_pair_start = false;
2813 let mut is_bracket_pair_end = false;
2814 if !text.is_empty() {
2815 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2816 // and they are removing the character that triggered IME popup.
2817 for (pair, enabled) in scope.brackets() {
2818 if !pair.close && !pair.surround {
2819 continue;
2820 }
2821
2822 if enabled && pair.start.ends_with(text.as_ref()) {
2823 let prefix_len = pair.start.len() - text.len();
2824 let preceding_text_matches_prefix = prefix_len == 0
2825 || (selection.start.column >= (prefix_len as u32)
2826 && snapshot.contains_str_at(
2827 Point::new(
2828 selection.start.row,
2829 selection.start.column - (prefix_len as u32),
2830 ),
2831 &pair.start[..prefix_len],
2832 ));
2833 if preceding_text_matches_prefix {
2834 bracket_pair = Some(pair.clone());
2835 is_bracket_pair_start = true;
2836 break;
2837 }
2838 }
2839 if pair.end.as_str() == text.as_ref() {
2840 bracket_pair = Some(pair.clone());
2841 is_bracket_pair_end = true;
2842 break;
2843 }
2844 }
2845 }
2846
2847 if let Some(bracket_pair) = bracket_pair {
2848 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2849 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2850 let auto_surround =
2851 self.use_auto_surround && snapshot_settings.use_auto_surround;
2852 if selection.is_empty() {
2853 if is_bracket_pair_start {
2854 // If the inserted text is a suffix of an opening bracket and the
2855 // selection is preceded by the rest of the opening bracket, then
2856 // insert the closing bracket.
2857 let following_text_allows_autoclose = snapshot
2858 .chars_at(selection.start)
2859 .next()
2860 .map_or(true, |c| scope.should_autoclose_before(c));
2861
2862 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2863 && bracket_pair.start.len() == 1
2864 {
2865 let target = bracket_pair.start.chars().next().unwrap();
2866 let current_line_count = snapshot
2867 .reversed_chars_at(selection.start)
2868 .take_while(|&c| c != '\n')
2869 .filter(|&c| c == target)
2870 .count();
2871 current_line_count % 2 == 1
2872 } else {
2873 false
2874 };
2875
2876 if autoclose
2877 && bracket_pair.close
2878 && following_text_allows_autoclose
2879 && !is_closing_quote
2880 {
2881 let anchor = snapshot.anchor_before(selection.end);
2882 new_selections.push((selection.map(|_| anchor), text.len()));
2883 new_autoclose_regions.push((
2884 anchor,
2885 text.len(),
2886 selection.id,
2887 bracket_pair.clone(),
2888 ));
2889 edits.push((
2890 selection.range(),
2891 format!("{}{}", text, bracket_pair.end).into(),
2892 ));
2893 bracket_inserted = true;
2894 continue;
2895 }
2896 }
2897
2898 if let Some(region) = autoclose_region {
2899 // If the selection is followed by an auto-inserted closing bracket,
2900 // then don't insert that closing bracket again; just move the selection
2901 // past the closing bracket.
2902 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2903 && text.as_ref() == region.pair.end.as_str();
2904 if should_skip {
2905 let anchor = snapshot.anchor_after(selection.end);
2906 new_selections
2907 .push((selection.map(|_| anchor), region.pair.end.len()));
2908 continue;
2909 }
2910 }
2911
2912 let always_treat_brackets_as_autoclosed = snapshot
2913 .settings_at(selection.start, cx)
2914 .always_treat_brackets_as_autoclosed;
2915 if always_treat_brackets_as_autoclosed
2916 && is_bracket_pair_end
2917 && snapshot.contains_str_at(selection.end, text.as_ref())
2918 {
2919 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2920 // and the inserted text is a closing bracket and the selection is followed
2921 // by the closing bracket then move the selection past the closing bracket.
2922 let anchor = snapshot.anchor_after(selection.end);
2923 new_selections.push((selection.map(|_| anchor), text.len()));
2924 continue;
2925 }
2926 }
2927 // If an opening bracket is 1 character long and is typed while
2928 // text is selected, then surround that text with the bracket pair.
2929 else if auto_surround
2930 && bracket_pair.surround
2931 && is_bracket_pair_start
2932 && bracket_pair.start.chars().count() == 1
2933 {
2934 edits.push((selection.start..selection.start, text.clone()));
2935 edits.push((
2936 selection.end..selection.end,
2937 bracket_pair.end.as_str().into(),
2938 ));
2939 bracket_inserted = true;
2940 new_selections.push((
2941 Selection {
2942 id: selection.id,
2943 start: snapshot.anchor_after(selection.start),
2944 end: snapshot.anchor_before(selection.end),
2945 reversed: selection.reversed,
2946 goal: selection.goal,
2947 },
2948 0,
2949 ));
2950 continue;
2951 }
2952 }
2953 }
2954
2955 if self.auto_replace_emoji_shortcode
2956 && selection.is_empty()
2957 && text.as_ref().ends_with(':')
2958 {
2959 if let Some(possible_emoji_short_code) =
2960 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2961 {
2962 if !possible_emoji_short_code.is_empty() {
2963 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2964 let emoji_shortcode_start = Point::new(
2965 selection.start.row,
2966 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2967 );
2968
2969 // Remove shortcode from buffer
2970 edits.push((
2971 emoji_shortcode_start..selection.start,
2972 "".to_string().into(),
2973 ));
2974 new_selections.push((
2975 Selection {
2976 id: selection.id,
2977 start: snapshot.anchor_after(emoji_shortcode_start),
2978 end: snapshot.anchor_before(selection.start),
2979 reversed: selection.reversed,
2980 goal: selection.goal,
2981 },
2982 0,
2983 ));
2984
2985 // Insert emoji
2986 let selection_start_anchor = snapshot.anchor_after(selection.start);
2987 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2988 edits.push((selection.start..selection.end, emoji.to_string().into()));
2989
2990 continue;
2991 }
2992 }
2993 }
2994 }
2995
2996 // If not handling any auto-close operation, then just replace the selected
2997 // text with the given input and move the selection to the end of the
2998 // newly inserted text.
2999 let anchor = snapshot.anchor_after(selection.end);
3000 if !self.linked_edit_ranges.is_empty() {
3001 let start_anchor = snapshot.anchor_before(selection.start);
3002
3003 let is_word_char = text.chars().next().map_or(true, |char| {
3004 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3005 classifier.is_word(char)
3006 });
3007
3008 if is_word_char {
3009 if let Some(ranges) = self
3010 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3011 {
3012 for (buffer, edits) in ranges {
3013 linked_edits
3014 .entry(buffer.clone())
3015 .or_default()
3016 .extend(edits.into_iter().map(|range| (range, text.clone())));
3017 }
3018 }
3019 }
3020 }
3021
3022 new_selections.push((selection.map(|_| anchor), 0));
3023 edits.push((selection.start..selection.end, text.clone()));
3024 }
3025
3026 drop(snapshot);
3027
3028 self.transact(window, cx, |this, window, cx| {
3029 this.buffer.update(cx, |buffer, cx| {
3030 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3031 });
3032 for (buffer, edits) in linked_edits {
3033 buffer.update(cx, |buffer, cx| {
3034 let snapshot = buffer.snapshot();
3035 let edits = edits
3036 .into_iter()
3037 .map(|(range, text)| {
3038 use text::ToPoint as TP;
3039 let end_point = TP::to_point(&range.end, &snapshot);
3040 let start_point = TP::to_point(&range.start, &snapshot);
3041 (start_point..end_point, text)
3042 })
3043 .sorted_by_key(|(range, _)| range.start)
3044 .collect::<Vec<_>>();
3045 buffer.edit(edits, None, cx);
3046 })
3047 }
3048 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3049 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3050 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3051 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3052 .zip(new_selection_deltas)
3053 .map(|(selection, delta)| Selection {
3054 id: selection.id,
3055 start: selection.start + delta,
3056 end: selection.end + delta,
3057 reversed: selection.reversed,
3058 goal: SelectionGoal::None,
3059 })
3060 .collect::<Vec<_>>();
3061
3062 let mut i = 0;
3063 for (position, delta, selection_id, pair) in new_autoclose_regions {
3064 let position = position.to_offset(&map.buffer_snapshot) + delta;
3065 let start = map.buffer_snapshot.anchor_before(position);
3066 let end = map.buffer_snapshot.anchor_after(position);
3067 while let Some(existing_state) = this.autoclose_regions.get(i) {
3068 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3069 Ordering::Less => i += 1,
3070 Ordering::Greater => break,
3071 Ordering::Equal => {
3072 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3073 Ordering::Less => i += 1,
3074 Ordering::Equal => break,
3075 Ordering::Greater => break,
3076 }
3077 }
3078 }
3079 }
3080 this.autoclose_regions.insert(
3081 i,
3082 AutocloseRegion {
3083 selection_id,
3084 range: start..end,
3085 pair,
3086 },
3087 );
3088 }
3089
3090 let had_active_inline_completion = this.has_active_inline_completion();
3091 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3092 s.select(new_selections)
3093 });
3094
3095 if !bracket_inserted {
3096 if let Some(on_type_format_task) =
3097 this.trigger_on_type_formatting(text.to_string(), window, cx)
3098 {
3099 on_type_format_task.detach_and_log_err(cx);
3100 }
3101 }
3102
3103 let editor_settings = EditorSettings::get_global(cx);
3104 if bracket_inserted
3105 && (editor_settings.auto_signature_help
3106 || editor_settings.show_signature_help_after_edits)
3107 {
3108 this.show_signature_help(&ShowSignatureHelp, window, cx);
3109 }
3110
3111 let trigger_in_words =
3112 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3113 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3114 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3115 this.refresh_inline_completion(true, false, window, cx);
3116 });
3117 }
3118
3119 fn find_possible_emoji_shortcode_at_position(
3120 snapshot: &MultiBufferSnapshot,
3121 position: Point,
3122 ) -> Option<String> {
3123 let mut chars = Vec::new();
3124 let mut found_colon = false;
3125 for char in snapshot.reversed_chars_at(position).take(100) {
3126 // Found a possible emoji shortcode in the middle of the buffer
3127 if found_colon {
3128 if char.is_whitespace() {
3129 chars.reverse();
3130 return Some(chars.iter().collect());
3131 }
3132 // If the previous character is not a whitespace, we are in the middle of a word
3133 // and we only want to complete the shortcode if the word is made up of other emojis
3134 let mut containing_word = String::new();
3135 for ch in snapshot
3136 .reversed_chars_at(position)
3137 .skip(chars.len() + 1)
3138 .take(100)
3139 {
3140 if ch.is_whitespace() {
3141 break;
3142 }
3143 containing_word.push(ch);
3144 }
3145 let containing_word = containing_word.chars().rev().collect::<String>();
3146 if util::word_consists_of_emojis(containing_word.as_str()) {
3147 chars.reverse();
3148 return Some(chars.iter().collect());
3149 }
3150 }
3151
3152 if char.is_whitespace() || !char.is_ascii() {
3153 return None;
3154 }
3155 if char == ':' {
3156 found_colon = true;
3157 } else {
3158 chars.push(char);
3159 }
3160 }
3161 // Found a possible emoji shortcode at the beginning of the buffer
3162 chars.reverse();
3163 Some(chars.iter().collect())
3164 }
3165
3166 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3167 self.transact(window, cx, |this, window, cx| {
3168 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3169 let selections = this.selections.all::<usize>(cx);
3170 let multi_buffer = this.buffer.read(cx);
3171 let buffer = multi_buffer.snapshot(cx);
3172 selections
3173 .iter()
3174 .map(|selection| {
3175 let start_point = selection.start.to_point(&buffer);
3176 let mut indent =
3177 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3178 indent.len = cmp::min(indent.len, start_point.column);
3179 let start = selection.start;
3180 let end = selection.end;
3181 let selection_is_empty = start == end;
3182 let language_scope = buffer.language_scope_at(start);
3183 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3184 &language_scope
3185 {
3186 let insert_extra_newline =
3187 insert_extra_newline_brackets(&buffer, start..end, language)
3188 || insert_extra_newline_tree_sitter(&buffer, start..end);
3189
3190 // Comment extension on newline is allowed only for cursor selections
3191 let comment_delimiter = maybe!({
3192 if !selection_is_empty {
3193 return None;
3194 }
3195
3196 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3197 return None;
3198 }
3199
3200 let delimiters = language.line_comment_prefixes();
3201 let max_len_of_delimiter =
3202 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3203 let (snapshot, range) =
3204 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3205
3206 let mut index_of_first_non_whitespace = 0;
3207 let comment_candidate = snapshot
3208 .chars_for_range(range)
3209 .skip_while(|c| {
3210 let should_skip = c.is_whitespace();
3211 if should_skip {
3212 index_of_first_non_whitespace += 1;
3213 }
3214 should_skip
3215 })
3216 .take(max_len_of_delimiter)
3217 .collect::<String>();
3218 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3219 comment_candidate.starts_with(comment_prefix.as_ref())
3220 })?;
3221 let cursor_is_placed_after_comment_marker =
3222 index_of_first_non_whitespace + comment_prefix.len()
3223 <= start_point.column as usize;
3224 if cursor_is_placed_after_comment_marker {
3225 Some(comment_prefix.clone())
3226 } else {
3227 None
3228 }
3229 });
3230 (comment_delimiter, insert_extra_newline)
3231 } else {
3232 (None, false)
3233 };
3234
3235 let capacity_for_delimiter = comment_delimiter
3236 .as_deref()
3237 .map(str::len)
3238 .unwrap_or_default();
3239 let mut new_text =
3240 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3241 new_text.push('\n');
3242 new_text.extend(indent.chars());
3243 if let Some(delimiter) = &comment_delimiter {
3244 new_text.push_str(delimiter);
3245 }
3246 if insert_extra_newline {
3247 new_text = new_text.repeat(2);
3248 }
3249
3250 let anchor = buffer.anchor_after(end);
3251 let new_selection = selection.map(|_| anchor);
3252 (
3253 (start..end, new_text),
3254 (insert_extra_newline, new_selection),
3255 )
3256 })
3257 .unzip()
3258 };
3259
3260 this.edit_with_autoindent(edits, cx);
3261 let buffer = this.buffer.read(cx).snapshot(cx);
3262 let new_selections = selection_fixup_info
3263 .into_iter()
3264 .map(|(extra_newline_inserted, new_selection)| {
3265 let mut cursor = new_selection.end.to_point(&buffer);
3266 if extra_newline_inserted {
3267 cursor.row -= 1;
3268 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3269 }
3270 new_selection.map(|_| cursor)
3271 })
3272 .collect();
3273
3274 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3275 s.select(new_selections)
3276 });
3277 this.refresh_inline_completion(true, false, window, cx);
3278 });
3279 }
3280
3281 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3282 let buffer = self.buffer.read(cx);
3283 let snapshot = buffer.snapshot(cx);
3284
3285 let mut edits = Vec::new();
3286 let mut rows = Vec::new();
3287
3288 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3289 let cursor = selection.head();
3290 let row = cursor.row;
3291
3292 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3293
3294 let newline = "\n".to_string();
3295 edits.push((start_of_line..start_of_line, newline));
3296
3297 rows.push(row + rows_inserted as u32);
3298 }
3299
3300 self.transact(window, cx, |editor, window, cx| {
3301 editor.edit(edits, cx);
3302
3303 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3304 let mut index = 0;
3305 s.move_cursors_with(|map, _, _| {
3306 let row = rows[index];
3307 index += 1;
3308
3309 let point = Point::new(row, 0);
3310 let boundary = map.next_line_boundary(point).1;
3311 let clipped = map.clip_point(boundary, Bias::Left);
3312
3313 (clipped, SelectionGoal::None)
3314 });
3315 });
3316
3317 let mut indent_edits = Vec::new();
3318 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3319 for row in rows {
3320 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3321 for (row, indent) in indents {
3322 if indent.len == 0 {
3323 continue;
3324 }
3325
3326 let text = match indent.kind {
3327 IndentKind::Space => " ".repeat(indent.len as usize),
3328 IndentKind::Tab => "\t".repeat(indent.len as usize),
3329 };
3330 let point = Point::new(row.0, 0);
3331 indent_edits.push((point..point, text));
3332 }
3333 }
3334 editor.edit(indent_edits, cx);
3335 });
3336 }
3337
3338 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3339 let buffer = self.buffer.read(cx);
3340 let snapshot = buffer.snapshot(cx);
3341
3342 let mut edits = Vec::new();
3343 let mut rows = Vec::new();
3344 let mut rows_inserted = 0;
3345
3346 for selection in self.selections.all_adjusted(cx) {
3347 let cursor = selection.head();
3348 let row = cursor.row;
3349
3350 let point = Point::new(row + 1, 0);
3351 let start_of_line = snapshot.clip_point(point, Bias::Left);
3352
3353 let newline = "\n".to_string();
3354 edits.push((start_of_line..start_of_line, newline));
3355
3356 rows_inserted += 1;
3357 rows.push(row + rows_inserted);
3358 }
3359
3360 self.transact(window, cx, |editor, window, cx| {
3361 editor.edit(edits, cx);
3362
3363 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3364 let mut index = 0;
3365 s.move_cursors_with(|map, _, _| {
3366 let row = rows[index];
3367 index += 1;
3368
3369 let point = Point::new(row, 0);
3370 let boundary = map.next_line_boundary(point).1;
3371 let clipped = map.clip_point(boundary, Bias::Left);
3372
3373 (clipped, SelectionGoal::None)
3374 });
3375 });
3376
3377 let mut indent_edits = Vec::new();
3378 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3379 for row in rows {
3380 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3381 for (row, indent) in indents {
3382 if indent.len == 0 {
3383 continue;
3384 }
3385
3386 let text = match indent.kind {
3387 IndentKind::Space => " ".repeat(indent.len as usize),
3388 IndentKind::Tab => "\t".repeat(indent.len as usize),
3389 };
3390 let point = Point::new(row.0, 0);
3391 indent_edits.push((point..point, text));
3392 }
3393 }
3394 editor.edit(indent_edits, cx);
3395 });
3396 }
3397
3398 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3399 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3400 original_indent_columns: Vec::new(),
3401 });
3402 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3403 }
3404
3405 fn insert_with_autoindent_mode(
3406 &mut self,
3407 text: &str,
3408 autoindent_mode: Option<AutoindentMode>,
3409 window: &mut Window,
3410 cx: &mut Context<Self>,
3411 ) {
3412 if self.read_only(cx) {
3413 return;
3414 }
3415
3416 let text: Arc<str> = text.into();
3417 self.transact(window, cx, |this, window, cx| {
3418 let old_selections = this.selections.all_adjusted(cx);
3419 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3420 let anchors = {
3421 let snapshot = buffer.read(cx);
3422 old_selections
3423 .iter()
3424 .map(|s| {
3425 let anchor = snapshot.anchor_after(s.head());
3426 s.map(|_| anchor)
3427 })
3428 .collect::<Vec<_>>()
3429 };
3430 buffer.edit(
3431 old_selections
3432 .iter()
3433 .map(|s| (s.start..s.end, text.clone())),
3434 autoindent_mode,
3435 cx,
3436 );
3437 anchors
3438 });
3439
3440 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3441 s.select_anchors(selection_anchors);
3442 });
3443
3444 cx.notify();
3445 });
3446 }
3447
3448 fn trigger_completion_on_input(
3449 &mut self,
3450 text: &str,
3451 trigger_in_words: bool,
3452 window: &mut Window,
3453 cx: &mut Context<Self>,
3454 ) {
3455 if self.is_completion_trigger(text, trigger_in_words, cx) {
3456 self.show_completions(
3457 &ShowCompletions {
3458 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3459 },
3460 window,
3461 cx,
3462 );
3463 } else {
3464 self.hide_context_menu(window, cx);
3465 }
3466 }
3467
3468 fn is_completion_trigger(
3469 &self,
3470 text: &str,
3471 trigger_in_words: bool,
3472 cx: &mut Context<Self>,
3473 ) -> bool {
3474 let position = self.selections.newest_anchor().head();
3475 let multibuffer = self.buffer.read(cx);
3476 let Some(buffer) = position
3477 .buffer_id
3478 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3479 else {
3480 return false;
3481 };
3482
3483 if let Some(completion_provider) = &self.completion_provider {
3484 completion_provider.is_completion_trigger(
3485 &buffer,
3486 position.text_anchor,
3487 text,
3488 trigger_in_words,
3489 cx,
3490 )
3491 } else {
3492 false
3493 }
3494 }
3495
3496 /// If any empty selections is touching the start of its innermost containing autoclose
3497 /// region, expand it to select the brackets.
3498 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3499 let selections = self.selections.all::<usize>(cx);
3500 let buffer = self.buffer.read(cx).read(cx);
3501 let new_selections = self
3502 .selections_with_autoclose_regions(selections, &buffer)
3503 .map(|(mut selection, region)| {
3504 if !selection.is_empty() {
3505 return selection;
3506 }
3507
3508 if let Some(region) = region {
3509 let mut range = region.range.to_offset(&buffer);
3510 if selection.start == range.start && range.start >= region.pair.start.len() {
3511 range.start -= region.pair.start.len();
3512 if buffer.contains_str_at(range.start, ®ion.pair.start)
3513 && buffer.contains_str_at(range.end, ®ion.pair.end)
3514 {
3515 range.end += region.pair.end.len();
3516 selection.start = range.start;
3517 selection.end = range.end;
3518
3519 return selection;
3520 }
3521 }
3522 }
3523
3524 let always_treat_brackets_as_autoclosed = buffer
3525 .settings_at(selection.start, cx)
3526 .always_treat_brackets_as_autoclosed;
3527
3528 if !always_treat_brackets_as_autoclosed {
3529 return selection;
3530 }
3531
3532 if let Some(scope) = buffer.language_scope_at(selection.start) {
3533 for (pair, enabled) in scope.brackets() {
3534 if !enabled || !pair.close {
3535 continue;
3536 }
3537
3538 if buffer.contains_str_at(selection.start, &pair.end) {
3539 let pair_start_len = pair.start.len();
3540 if buffer.contains_str_at(
3541 selection.start.saturating_sub(pair_start_len),
3542 &pair.start,
3543 ) {
3544 selection.start -= pair_start_len;
3545 selection.end += pair.end.len();
3546
3547 return selection;
3548 }
3549 }
3550 }
3551 }
3552
3553 selection
3554 })
3555 .collect();
3556
3557 drop(buffer);
3558 self.change_selections(None, window, cx, |selections| {
3559 selections.select(new_selections)
3560 });
3561 }
3562
3563 /// Iterate the given selections, and for each one, find the smallest surrounding
3564 /// autoclose region. This uses the ordering of the selections and the autoclose
3565 /// regions to avoid repeated comparisons.
3566 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3567 &'a self,
3568 selections: impl IntoIterator<Item = Selection<D>>,
3569 buffer: &'a MultiBufferSnapshot,
3570 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3571 let mut i = 0;
3572 let mut regions = self.autoclose_regions.as_slice();
3573 selections.into_iter().map(move |selection| {
3574 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3575
3576 let mut enclosing = None;
3577 while let Some(pair_state) = regions.get(i) {
3578 if pair_state.range.end.to_offset(buffer) < range.start {
3579 regions = ®ions[i + 1..];
3580 i = 0;
3581 } else if pair_state.range.start.to_offset(buffer) > range.end {
3582 break;
3583 } else {
3584 if pair_state.selection_id == selection.id {
3585 enclosing = Some(pair_state);
3586 }
3587 i += 1;
3588 }
3589 }
3590
3591 (selection, enclosing)
3592 })
3593 }
3594
3595 /// Remove any autoclose regions that no longer contain their selection.
3596 fn invalidate_autoclose_regions(
3597 &mut self,
3598 mut selections: &[Selection<Anchor>],
3599 buffer: &MultiBufferSnapshot,
3600 ) {
3601 self.autoclose_regions.retain(|state| {
3602 let mut i = 0;
3603 while let Some(selection) = selections.get(i) {
3604 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3605 selections = &selections[1..];
3606 continue;
3607 }
3608 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3609 break;
3610 }
3611 if selection.id == state.selection_id {
3612 return true;
3613 } else {
3614 i += 1;
3615 }
3616 }
3617 false
3618 });
3619 }
3620
3621 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3622 let offset = position.to_offset(buffer);
3623 let (word_range, kind) = buffer.surrounding_word(offset, true);
3624 if offset > word_range.start && kind == Some(CharKind::Word) {
3625 Some(
3626 buffer
3627 .text_for_range(word_range.start..offset)
3628 .collect::<String>(),
3629 )
3630 } else {
3631 None
3632 }
3633 }
3634
3635 pub fn toggle_inlay_hints(
3636 &mut self,
3637 _: &ToggleInlayHints,
3638 _: &mut Window,
3639 cx: &mut Context<Self>,
3640 ) {
3641 self.refresh_inlay_hints(
3642 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3643 cx,
3644 );
3645 }
3646
3647 pub fn inlay_hints_enabled(&self) -> bool {
3648 self.inlay_hint_cache.enabled
3649 }
3650
3651 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3652 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3653 return;
3654 }
3655
3656 let reason_description = reason.description();
3657 let ignore_debounce = matches!(
3658 reason,
3659 InlayHintRefreshReason::SettingsChange(_)
3660 | InlayHintRefreshReason::Toggle(_)
3661 | InlayHintRefreshReason::ExcerptsRemoved(_)
3662 );
3663 let (invalidate_cache, required_languages) = match reason {
3664 InlayHintRefreshReason::Toggle(enabled) => {
3665 self.inlay_hint_cache.enabled = enabled;
3666 if enabled {
3667 (InvalidationStrategy::RefreshRequested, None)
3668 } else {
3669 self.inlay_hint_cache.clear();
3670 self.splice_inlays(
3671 &self
3672 .visible_inlay_hints(cx)
3673 .iter()
3674 .map(|inlay| inlay.id)
3675 .collect::<Vec<InlayId>>(),
3676 Vec::new(),
3677 cx,
3678 );
3679 return;
3680 }
3681 }
3682 InlayHintRefreshReason::SettingsChange(new_settings) => {
3683 match self.inlay_hint_cache.update_settings(
3684 &self.buffer,
3685 new_settings,
3686 self.visible_inlay_hints(cx),
3687 cx,
3688 ) {
3689 ControlFlow::Break(Some(InlaySplice {
3690 to_remove,
3691 to_insert,
3692 })) => {
3693 self.splice_inlays(&to_remove, to_insert, cx);
3694 return;
3695 }
3696 ControlFlow::Break(None) => return,
3697 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3698 }
3699 }
3700 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3701 if let Some(InlaySplice {
3702 to_remove,
3703 to_insert,
3704 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3705 {
3706 self.splice_inlays(&to_remove, to_insert, cx);
3707 }
3708 return;
3709 }
3710 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3711 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3712 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3713 }
3714 InlayHintRefreshReason::RefreshRequested => {
3715 (InvalidationStrategy::RefreshRequested, None)
3716 }
3717 };
3718
3719 if let Some(InlaySplice {
3720 to_remove,
3721 to_insert,
3722 }) = self.inlay_hint_cache.spawn_hint_refresh(
3723 reason_description,
3724 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3725 invalidate_cache,
3726 ignore_debounce,
3727 cx,
3728 ) {
3729 self.splice_inlays(&to_remove, to_insert, cx);
3730 }
3731 }
3732
3733 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3734 self.display_map
3735 .read(cx)
3736 .current_inlays()
3737 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3738 .cloned()
3739 .collect()
3740 }
3741
3742 pub fn excerpts_for_inlay_hints_query(
3743 &self,
3744 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3745 cx: &mut Context<Editor>,
3746 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3747 let Some(project) = self.project.as_ref() else {
3748 return HashMap::default();
3749 };
3750 let project = project.read(cx);
3751 let multi_buffer = self.buffer().read(cx);
3752 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3753 let multi_buffer_visible_start = self
3754 .scroll_manager
3755 .anchor()
3756 .anchor
3757 .to_point(&multi_buffer_snapshot);
3758 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3759 multi_buffer_visible_start
3760 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3761 Bias::Left,
3762 );
3763 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3764 multi_buffer_snapshot
3765 .range_to_buffer_ranges(multi_buffer_visible_range)
3766 .into_iter()
3767 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3768 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3769 let buffer_file = project::File::from_dyn(buffer.file())?;
3770 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3771 let worktree_entry = buffer_worktree
3772 .read(cx)
3773 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3774 if worktree_entry.is_ignored {
3775 return None;
3776 }
3777
3778 let language = buffer.language()?;
3779 if let Some(restrict_to_languages) = restrict_to_languages {
3780 if !restrict_to_languages.contains(language) {
3781 return None;
3782 }
3783 }
3784 Some((
3785 excerpt_id,
3786 (
3787 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3788 buffer.version().clone(),
3789 excerpt_visible_range,
3790 ),
3791 ))
3792 })
3793 .collect()
3794 }
3795
3796 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3797 TextLayoutDetails {
3798 text_system: window.text_system().clone(),
3799 editor_style: self.style.clone().unwrap(),
3800 rem_size: window.rem_size(),
3801 scroll_anchor: self.scroll_manager.anchor(),
3802 visible_rows: self.visible_line_count(),
3803 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3804 }
3805 }
3806
3807 pub fn splice_inlays(
3808 &self,
3809 to_remove: &[InlayId],
3810 to_insert: Vec<Inlay>,
3811 cx: &mut Context<Self>,
3812 ) {
3813 self.display_map.update(cx, |display_map, cx| {
3814 display_map.splice_inlays(to_remove, to_insert, cx)
3815 });
3816 cx.notify();
3817 }
3818
3819 fn trigger_on_type_formatting(
3820 &self,
3821 input: String,
3822 window: &mut Window,
3823 cx: &mut Context<Self>,
3824 ) -> Option<Task<Result<()>>> {
3825 if input.len() != 1 {
3826 return None;
3827 }
3828
3829 let project = self.project.as_ref()?;
3830 let position = self.selections.newest_anchor().head();
3831 let (buffer, buffer_position) = self
3832 .buffer
3833 .read(cx)
3834 .text_anchor_for_position(position, cx)?;
3835
3836 let settings = language_settings::language_settings(
3837 buffer
3838 .read(cx)
3839 .language_at(buffer_position)
3840 .map(|l| l.name()),
3841 buffer.read(cx).file(),
3842 cx,
3843 );
3844 if !settings.use_on_type_format {
3845 return None;
3846 }
3847
3848 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3849 // hence we do LSP request & edit on host side only — add formats to host's history.
3850 let push_to_lsp_host_history = true;
3851 // If this is not the host, append its history with new edits.
3852 let push_to_client_history = project.read(cx).is_via_collab();
3853
3854 let on_type_formatting = project.update(cx, |project, cx| {
3855 project.on_type_format(
3856 buffer.clone(),
3857 buffer_position,
3858 input,
3859 push_to_lsp_host_history,
3860 cx,
3861 )
3862 });
3863 Some(cx.spawn_in(window, |editor, mut cx| async move {
3864 if let Some(transaction) = on_type_formatting.await? {
3865 if push_to_client_history {
3866 buffer
3867 .update(&mut cx, |buffer, _| {
3868 buffer.push_transaction(transaction, Instant::now());
3869 })
3870 .ok();
3871 }
3872 editor.update(&mut cx, |editor, cx| {
3873 editor.refresh_document_highlights(cx);
3874 })?;
3875 }
3876 Ok(())
3877 }))
3878 }
3879
3880 pub fn show_completions(
3881 &mut self,
3882 options: &ShowCompletions,
3883 window: &mut Window,
3884 cx: &mut Context<Self>,
3885 ) {
3886 if self.pending_rename.is_some() {
3887 return;
3888 }
3889
3890 let Some(provider) = self.completion_provider.as_ref() else {
3891 return;
3892 };
3893
3894 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3895 return;
3896 }
3897
3898 let position = self.selections.newest_anchor().head();
3899 if position.diff_base_anchor.is_some() {
3900 return;
3901 }
3902 let (buffer, buffer_position) =
3903 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3904 output
3905 } else {
3906 return;
3907 };
3908 let show_completion_documentation = buffer
3909 .read(cx)
3910 .snapshot()
3911 .settings_at(buffer_position, cx)
3912 .show_completion_documentation;
3913
3914 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3915
3916 let trigger_kind = match &options.trigger {
3917 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3918 CompletionTriggerKind::TRIGGER_CHARACTER
3919 }
3920 _ => CompletionTriggerKind::INVOKED,
3921 };
3922 let completion_context = CompletionContext {
3923 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3924 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3925 Some(String::from(trigger))
3926 } else {
3927 None
3928 }
3929 }),
3930 trigger_kind,
3931 };
3932 let completions =
3933 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3934 let sort_completions = provider.sort_completions();
3935
3936 let id = post_inc(&mut self.next_completion_id);
3937 let task = cx.spawn_in(window, |editor, mut cx| {
3938 async move {
3939 editor.update(&mut cx, |this, _| {
3940 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3941 })?;
3942 let completions = completions.await.log_err();
3943 let menu = if let Some(completions) = completions {
3944 let mut menu = CompletionsMenu::new(
3945 id,
3946 sort_completions,
3947 show_completion_documentation,
3948 position,
3949 buffer.clone(),
3950 completions.into(),
3951 );
3952
3953 menu.filter(query.as_deref(), cx.background_executor().clone())
3954 .await;
3955
3956 menu.visible().then_some(menu)
3957 } else {
3958 None
3959 };
3960
3961 editor.update_in(&mut cx, |editor, window, cx| {
3962 match editor.context_menu.borrow().as_ref() {
3963 None => {}
3964 Some(CodeContextMenu::Completions(prev_menu)) => {
3965 if prev_menu.id > id {
3966 return;
3967 }
3968 }
3969 _ => return,
3970 }
3971
3972 if editor.focus_handle.is_focused(window) && menu.is_some() {
3973 let mut menu = menu.unwrap();
3974 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3975
3976 *editor.context_menu.borrow_mut() =
3977 Some(CodeContextMenu::Completions(menu));
3978
3979 if editor.show_edit_predictions_in_menu() {
3980 editor.update_visible_inline_completion(window, cx);
3981 } else {
3982 editor.discard_inline_completion(false, cx);
3983 }
3984
3985 cx.notify();
3986 } else if editor.completion_tasks.len() <= 1 {
3987 // If there are no more completion tasks and the last menu was
3988 // empty, we should hide it.
3989 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3990 // If it was already hidden and we don't show inline
3991 // completions in the menu, we should also show the
3992 // inline-completion when available.
3993 if was_hidden && editor.show_edit_predictions_in_menu() {
3994 editor.update_visible_inline_completion(window, cx);
3995 }
3996 }
3997 })?;
3998
3999 Ok::<_, anyhow::Error>(())
4000 }
4001 .log_err()
4002 });
4003
4004 self.completion_tasks.push((id, task));
4005 }
4006
4007 pub fn confirm_completion(
4008 &mut self,
4009 action: &ConfirmCompletion,
4010 window: &mut Window,
4011 cx: &mut Context<Self>,
4012 ) -> Option<Task<Result<()>>> {
4013 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4014 }
4015
4016 pub fn compose_completion(
4017 &mut self,
4018 action: &ComposeCompletion,
4019 window: &mut Window,
4020 cx: &mut Context<Self>,
4021 ) -> Option<Task<Result<()>>> {
4022 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4023 }
4024
4025 fn do_completion(
4026 &mut self,
4027 item_ix: Option<usize>,
4028 intent: CompletionIntent,
4029 window: &mut Window,
4030 cx: &mut Context<Editor>,
4031 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4032 use language::ToOffset as _;
4033
4034 let completions_menu =
4035 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4036 menu
4037 } else {
4038 return None;
4039 };
4040
4041 let entries = completions_menu.entries.borrow();
4042 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4043 if self.show_edit_predictions_in_menu() {
4044 self.discard_inline_completion(true, cx);
4045 }
4046 let candidate_id = mat.candidate_id;
4047 drop(entries);
4048
4049 let buffer_handle = completions_menu.buffer;
4050 let completion = completions_menu
4051 .completions
4052 .borrow()
4053 .get(candidate_id)?
4054 .clone();
4055 cx.stop_propagation();
4056
4057 let snippet;
4058 let text;
4059
4060 if completion.is_snippet() {
4061 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4062 text = snippet.as_ref().unwrap().text.clone();
4063 } else {
4064 snippet = None;
4065 text = completion.new_text.clone();
4066 };
4067 let selections = self.selections.all::<usize>(cx);
4068 let buffer = buffer_handle.read(cx);
4069 let old_range = completion.old_range.to_offset(buffer);
4070 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4071
4072 let newest_selection = self.selections.newest_anchor();
4073 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4074 return None;
4075 }
4076
4077 let lookbehind = newest_selection
4078 .start
4079 .text_anchor
4080 .to_offset(buffer)
4081 .saturating_sub(old_range.start);
4082 let lookahead = old_range
4083 .end
4084 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4085 let mut common_prefix_len = old_text
4086 .bytes()
4087 .zip(text.bytes())
4088 .take_while(|(a, b)| a == b)
4089 .count();
4090
4091 let snapshot = self.buffer.read(cx).snapshot(cx);
4092 let mut range_to_replace: Option<Range<isize>> = None;
4093 let mut ranges = Vec::new();
4094 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4095 for selection in &selections {
4096 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4097 let start = selection.start.saturating_sub(lookbehind);
4098 let end = selection.end + lookahead;
4099 if selection.id == newest_selection.id {
4100 range_to_replace = Some(
4101 ((start + common_prefix_len) as isize - selection.start as isize)
4102 ..(end as isize - selection.start as isize),
4103 );
4104 }
4105 ranges.push(start + common_prefix_len..end);
4106 } else {
4107 common_prefix_len = 0;
4108 ranges.clear();
4109 ranges.extend(selections.iter().map(|s| {
4110 if s.id == newest_selection.id {
4111 range_to_replace = Some(
4112 old_range.start.to_offset_utf16(&snapshot).0 as isize
4113 - selection.start as isize
4114 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4115 - selection.start as isize,
4116 );
4117 old_range.clone()
4118 } else {
4119 s.start..s.end
4120 }
4121 }));
4122 break;
4123 }
4124 if !self.linked_edit_ranges.is_empty() {
4125 let start_anchor = snapshot.anchor_before(selection.head());
4126 let end_anchor = snapshot.anchor_after(selection.tail());
4127 if let Some(ranges) = self
4128 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4129 {
4130 for (buffer, edits) in ranges {
4131 linked_edits.entry(buffer.clone()).or_default().extend(
4132 edits
4133 .into_iter()
4134 .map(|range| (range, text[common_prefix_len..].to_owned())),
4135 );
4136 }
4137 }
4138 }
4139 }
4140 let text = &text[common_prefix_len..];
4141
4142 cx.emit(EditorEvent::InputHandled {
4143 utf16_range_to_replace: range_to_replace,
4144 text: text.into(),
4145 });
4146
4147 self.transact(window, cx, |this, window, cx| {
4148 if let Some(mut snippet) = snippet {
4149 snippet.text = text.to_string();
4150 for tabstop in snippet
4151 .tabstops
4152 .iter_mut()
4153 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4154 {
4155 tabstop.start -= common_prefix_len as isize;
4156 tabstop.end -= common_prefix_len as isize;
4157 }
4158
4159 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4160 } else {
4161 this.buffer.update(cx, |buffer, cx| {
4162 buffer.edit(
4163 ranges.iter().map(|range| (range.clone(), text)),
4164 this.autoindent_mode.clone(),
4165 cx,
4166 );
4167 });
4168 }
4169 for (buffer, edits) in linked_edits {
4170 buffer.update(cx, |buffer, cx| {
4171 let snapshot = buffer.snapshot();
4172 let edits = edits
4173 .into_iter()
4174 .map(|(range, text)| {
4175 use text::ToPoint as TP;
4176 let end_point = TP::to_point(&range.end, &snapshot);
4177 let start_point = TP::to_point(&range.start, &snapshot);
4178 (start_point..end_point, text)
4179 })
4180 .sorted_by_key(|(range, _)| range.start)
4181 .collect::<Vec<_>>();
4182 buffer.edit(edits, None, cx);
4183 })
4184 }
4185
4186 this.refresh_inline_completion(true, false, window, cx);
4187 });
4188
4189 let show_new_completions_on_confirm = completion
4190 .confirm
4191 .as_ref()
4192 .map_or(false, |confirm| confirm(intent, window, cx));
4193 if show_new_completions_on_confirm {
4194 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4195 }
4196
4197 let provider = self.completion_provider.as_ref()?;
4198 drop(completion);
4199 let apply_edits = provider.apply_additional_edits_for_completion(
4200 buffer_handle,
4201 completions_menu.completions.clone(),
4202 candidate_id,
4203 true,
4204 cx,
4205 );
4206
4207 let editor_settings = EditorSettings::get_global(cx);
4208 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4209 // After the code completion is finished, users often want to know what signatures are needed.
4210 // so we should automatically call signature_help
4211 self.show_signature_help(&ShowSignatureHelp, window, cx);
4212 }
4213
4214 Some(cx.foreground_executor().spawn(async move {
4215 apply_edits.await?;
4216 Ok(())
4217 }))
4218 }
4219
4220 pub fn toggle_code_actions(
4221 &mut self,
4222 action: &ToggleCodeActions,
4223 window: &mut Window,
4224 cx: &mut Context<Self>,
4225 ) {
4226 let mut context_menu = self.context_menu.borrow_mut();
4227 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4228 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4229 // Toggle if we're selecting the same one
4230 *context_menu = None;
4231 cx.notify();
4232 return;
4233 } else {
4234 // Otherwise, clear it and start a new one
4235 *context_menu = None;
4236 cx.notify();
4237 }
4238 }
4239 drop(context_menu);
4240 let snapshot = self.snapshot(window, cx);
4241 let deployed_from_indicator = action.deployed_from_indicator;
4242 let mut task = self.code_actions_task.take();
4243 let action = action.clone();
4244 cx.spawn_in(window, |editor, mut cx| async move {
4245 while let Some(prev_task) = task {
4246 prev_task.await.log_err();
4247 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4248 }
4249
4250 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4251 if editor.focus_handle.is_focused(window) {
4252 let multibuffer_point = action
4253 .deployed_from_indicator
4254 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4255 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4256 let (buffer, buffer_row) = snapshot
4257 .buffer_snapshot
4258 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4259 .and_then(|(buffer_snapshot, range)| {
4260 editor
4261 .buffer
4262 .read(cx)
4263 .buffer(buffer_snapshot.remote_id())
4264 .map(|buffer| (buffer, range.start.row))
4265 })?;
4266 let (_, code_actions) = editor
4267 .available_code_actions
4268 .clone()
4269 .and_then(|(location, code_actions)| {
4270 let snapshot = location.buffer.read(cx).snapshot();
4271 let point_range = location.range.to_point(&snapshot);
4272 let point_range = point_range.start.row..=point_range.end.row;
4273 if point_range.contains(&buffer_row) {
4274 Some((location, code_actions))
4275 } else {
4276 None
4277 }
4278 })
4279 .unzip();
4280 let buffer_id = buffer.read(cx).remote_id();
4281 let tasks = editor
4282 .tasks
4283 .get(&(buffer_id, buffer_row))
4284 .map(|t| Arc::new(t.to_owned()));
4285 if tasks.is_none() && code_actions.is_none() {
4286 return None;
4287 }
4288
4289 editor.completion_tasks.clear();
4290 editor.discard_inline_completion(false, cx);
4291 let task_context =
4292 tasks
4293 .as_ref()
4294 .zip(editor.project.clone())
4295 .map(|(tasks, project)| {
4296 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4297 });
4298
4299 Some(cx.spawn_in(window, |editor, mut cx| async move {
4300 let task_context = match task_context {
4301 Some(task_context) => task_context.await,
4302 None => None,
4303 };
4304 let resolved_tasks =
4305 tasks.zip(task_context).map(|(tasks, task_context)| {
4306 Rc::new(ResolvedTasks {
4307 templates: tasks.resolve(&task_context).collect(),
4308 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4309 multibuffer_point.row,
4310 tasks.column,
4311 )),
4312 })
4313 });
4314 let spawn_straight_away = resolved_tasks
4315 .as_ref()
4316 .map_or(false, |tasks| tasks.templates.len() == 1)
4317 && code_actions
4318 .as_ref()
4319 .map_or(true, |actions| actions.is_empty());
4320 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4321 *editor.context_menu.borrow_mut() =
4322 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4323 buffer,
4324 actions: CodeActionContents {
4325 tasks: resolved_tasks,
4326 actions: code_actions,
4327 },
4328 selected_item: Default::default(),
4329 scroll_handle: UniformListScrollHandle::default(),
4330 deployed_from_indicator,
4331 }));
4332 if spawn_straight_away {
4333 if let Some(task) = editor.confirm_code_action(
4334 &ConfirmCodeAction { item_ix: Some(0) },
4335 window,
4336 cx,
4337 ) {
4338 cx.notify();
4339 return task;
4340 }
4341 }
4342 cx.notify();
4343 Task::ready(Ok(()))
4344 }) {
4345 task.await
4346 } else {
4347 Ok(())
4348 }
4349 }))
4350 } else {
4351 Some(Task::ready(Ok(())))
4352 }
4353 })?;
4354 if let Some(task) = spawned_test_task {
4355 task.await?;
4356 }
4357
4358 Ok::<_, anyhow::Error>(())
4359 })
4360 .detach_and_log_err(cx);
4361 }
4362
4363 pub fn confirm_code_action(
4364 &mut self,
4365 action: &ConfirmCodeAction,
4366 window: &mut Window,
4367 cx: &mut Context<Self>,
4368 ) -> Option<Task<Result<()>>> {
4369 let actions_menu =
4370 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4371 menu
4372 } else {
4373 return None;
4374 };
4375 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4376 let action = actions_menu.actions.get(action_ix)?;
4377 let title = action.label();
4378 let buffer = actions_menu.buffer;
4379 let workspace = self.workspace()?;
4380
4381 match action {
4382 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4383 workspace.update(cx, |workspace, cx| {
4384 workspace::tasks::schedule_resolved_task(
4385 workspace,
4386 task_source_kind,
4387 resolved_task,
4388 false,
4389 cx,
4390 );
4391
4392 Some(Task::ready(Ok(())))
4393 })
4394 }
4395 CodeActionsItem::CodeAction {
4396 excerpt_id,
4397 action,
4398 provider,
4399 } => {
4400 let apply_code_action =
4401 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4402 let workspace = workspace.downgrade();
4403 Some(cx.spawn_in(window, |editor, cx| async move {
4404 let project_transaction = apply_code_action.await?;
4405 Self::open_project_transaction(
4406 &editor,
4407 workspace,
4408 project_transaction,
4409 title,
4410 cx,
4411 )
4412 .await
4413 }))
4414 }
4415 }
4416 }
4417
4418 pub async fn open_project_transaction(
4419 this: &WeakEntity<Editor>,
4420 workspace: WeakEntity<Workspace>,
4421 transaction: ProjectTransaction,
4422 title: String,
4423 mut cx: AsyncWindowContext,
4424 ) -> Result<()> {
4425 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4426 cx.update(|_, cx| {
4427 entries.sort_unstable_by_key(|(buffer, _)| {
4428 buffer.read(cx).file().map(|f| f.path().clone())
4429 });
4430 })?;
4431
4432 // If the project transaction's edits are all contained within this editor, then
4433 // avoid opening a new editor to display them.
4434
4435 if let Some((buffer, transaction)) = entries.first() {
4436 if entries.len() == 1 {
4437 let excerpt = this.update(&mut cx, |editor, cx| {
4438 editor
4439 .buffer()
4440 .read(cx)
4441 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4442 })?;
4443 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4444 if excerpted_buffer == *buffer {
4445 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4446 let excerpt_range = excerpt_range.to_offset(buffer);
4447 buffer
4448 .edited_ranges_for_transaction::<usize>(transaction)
4449 .all(|range| {
4450 excerpt_range.start <= range.start
4451 && excerpt_range.end >= range.end
4452 })
4453 })?;
4454
4455 if all_edits_within_excerpt {
4456 return Ok(());
4457 }
4458 }
4459 }
4460 }
4461 } else {
4462 return Ok(());
4463 }
4464
4465 let mut ranges_to_highlight = Vec::new();
4466 let excerpt_buffer = cx.new(|cx| {
4467 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4468 for (buffer_handle, transaction) in &entries {
4469 let buffer = buffer_handle.read(cx);
4470 ranges_to_highlight.extend(
4471 multibuffer.push_excerpts_with_context_lines(
4472 buffer_handle.clone(),
4473 buffer
4474 .edited_ranges_for_transaction::<usize>(transaction)
4475 .collect(),
4476 DEFAULT_MULTIBUFFER_CONTEXT,
4477 cx,
4478 ),
4479 );
4480 }
4481 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4482 multibuffer
4483 })?;
4484
4485 workspace.update_in(&mut cx, |workspace, window, cx| {
4486 let project = workspace.project().clone();
4487 let editor = cx
4488 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4489 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4490 editor.update(cx, |editor, cx| {
4491 editor.highlight_background::<Self>(
4492 &ranges_to_highlight,
4493 |theme| theme.editor_highlighted_line_background,
4494 cx,
4495 );
4496 });
4497 })?;
4498
4499 Ok(())
4500 }
4501
4502 pub fn clear_code_action_providers(&mut self) {
4503 self.code_action_providers.clear();
4504 self.available_code_actions.take();
4505 }
4506
4507 pub fn add_code_action_provider(
4508 &mut self,
4509 provider: Rc<dyn CodeActionProvider>,
4510 window: &mut Window,
4511 cx: &mut Context<Self>,
4512 ) {
4513 if self
4514 .code_action_providers
4515 .iter()
4516 .any(|existing_provider| existing_provider.id() == provider.id())
4517 {
4518 return;
4519 }
4520
4521 self.code_action_providers.push(provider);
4522 self.refresh_code_actions(window, cx);
4523 }
4524
4525 pub fn remove_code_action_provider(
4526 &mut self,
4527 id: Arc<str>,
4528 window: &mut Window,
4529 cx: &mut Context<Self>,
4530 ) {
4531 self.code_action_providers
4532 .retain(|provider| provider.id() != id);
4533 self.refresh_code_actions(window, cx);
4534 }
4535
4536 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4537 let buffer = self.buffer.read(cx);
4538 let newest_selection = self.selections.newest_anchor().clone();
4539 if newest_selection.head().diff_base_anchor.is_some() {
4540 return None;
4541 }
4542 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4543 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4544 if start_buffer != end_buffer {
4545 return None;
4546 }
4547
4548 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4549 cx.background_executor()
4550 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4551 .await;
4552
4553 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4554 let providers = this.code_action_providers.clone();
4555 let tasks = this
4556 .code_action_providers
4557 .iter()
4558 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4559 .collect::<Vec<_>>();
4560 (providers, tasks)
4561 })?;
4562
4563 let mut actions = Vec::new();
4564 for (provider, provider_actions) in
4565 providers.into_iter().zip(future::join_all(tasks).await)
4566 {
4567 if let Some(provider_actions) = provider_actions.log_err() {
4568 actions.extend(provider_actions.into_iter().map(|action| {
4569 AvailableCodeAction {
4570 excerpt_id: newest_selection.start.excerpt_id,
4571 action,
4572 provider: provider.clone(),
4573 }
4574 }));
4575 }
4576 }
4577
4578 this.update(&mut cx, |this, cx| {
4579 this.available_code_actions = if actions.is_empty() {
4580 None
4581 } else {
4582 Some((
4583 Location {
4584 buffer: start_buffer,
4585 range: start..end,
4586 },
4587 actions.into(),
4588 ))
4589 };
4590 cx.notify();
4591 })
4592 }));
4593 None
4594 }
4595
4596 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4597 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4598 self.show_git_blame_inline = false;
4599
4600 self.show_git_blame_inline_delay_task =
4601 Some(cx.spawn_in(window, |this, mut cx| async move {
4602 cx.background_executor().timer(delay).await;
4603
4604 this.update(&mut cx, |this, cx| {
4605 this.show_git_blame_inline = true;
4606 cx.notify();
4607 })
4608 .log_err();
4609 }));
4610 }
4611 }
4612
4613 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4614 if self.pending_rename.is_some() {
4615 return None;
4616 }
4617
4618 let provider = self.semantics_provider.clone()?;
4619 let buffer = self.buffer.read(cx);
4620 let newest_selection = self.selections.newest_anchor().clone();
4621 let cursor_position = newest_selection.head();
4622 let (cursor_buffer, cursor_buffer_position) =
4623 buffer.text_anchor_for_position(cursor_position, cx)?;
4624 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4625 if cursor_buffer != tail_buffer {
4626 return None;
4627 }
4628 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4629 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4630 cx.background_executor()
4631 .timer(Duration::from_millis(debounce))
4632 .await;
4633
4634 let highlights = if let Some(highlights) = cx
4635 .update(|cx| {
4636 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4637 })
4638 .ok()
4639 .flatten()
4640 {
4641 highlights.await.log_err()
4642 } else {
4643 None
4644 };
4645
4646 if let Some(highlights) = highlights {
4647 this.update(&mut cx, |this, cx| {
4648 if this.pending_rename.is_some() {
4649 return;
4650 }
4651
4652 let buffer_id = cursor_position.buffer_id;
4653 let buffer = this.buffer.read(cx);
4654 if !buffer
4655 .text_anchor_for_position(cursor_position, cx)
4656 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4657 {
4658 return;
4659 }
4660
4661 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4662 let mut write_ranges = Vec::new();
4663 let mut read_ranges = Vec::new();
4664 for highlight in highlights {
4665 for (excerpt_id, excerpt_range) in
4666 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4667 {
4668 let start = highlight
4669 .range
4670 .start
4671 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4672 let end = highlight
4673 .range
4674 .end
4675 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4676 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4677 continue;
4678 }
4679
4680 let range = Anchor {
4681 buffer_id,
4682 excerpt_id,
4683 text_anchor: start,
4684 diff_base_anchor: None,
4685 }..Anchor {
4686 buffer_id,
4687 excerpt_id,
4688 text_anchor: end,
4689 diff_base_anchor: None,
4690 };
4691 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4692 write_ranges.push(range);
4693 } else {
4694 read_ranges.push(range);
4695 }
4696 }
4697 }
4698
4699 this.highlight_background::<DocumentHighlightRead>(
4700 &read_ranges,
4701 |theme| theme.editor_document_highlight_read_background,
4702 cx,
4703 );
4704 this.highlight_background::<DocumentHighlightWrite>(
4705 &write_ranges,
4706 |theme| theme.editor_document_highlight_write_background,
4707 cx,
4708 );
4709 cx.notify();
4710 })
4711 .log_err();
4712 }
4713 }));
4714 None
4715 }
4716
4717 pub fn refresh_selected_text_highlights(
4718 &mut self,
4719 window: &mut Window,
4720 cx: &mut Context<Editor>,
4721 ) {
4722 self.selection_highlight_task.take();
4723 if !EditorSettings::get_global(cx).selection_highlight {
4724 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4725 return;
4726 }
4727 if self.selections.count() != 1 || self.selections.line_mode {
4728 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4729 return;
4730 }
4731 let selection = self.selections.newest::<Point>(cx);
4732 if selection.is_empty() || selection.start.row != selection.end.row {
4733 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4734 return;
4735 }
4736 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4737 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4738 cx.background_executor()
4739 .timer(Duration::from_millis(debounce))
4740 .await;
4741 let Some(Some(matches_task)) = editor
4742 .update_in(&mut cx, |editor, _, cx| {
4743 if editor.selections.count() != 1 || editor.selections.line_mode {
4744 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4745 return None;
4746 }
4747 let selection = editor.selections.newest::<Point>(cx);
4748 if selection.is_empty() || selection.start.row != selection.end.row {
4749 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4750 return None;
4751 }
4752 let buffer = editor.buffer().read(cx).snapshot(cx);
4753 let query = buffer.text_for_range(selection.range()).collect::<String>();
4754 if query.trim().is_empty() {
4755 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4756 return None;
4757 }
4758 Some(cx.background_spawn(async move {
4759 let mut ranges = Vec::new();
4760 let selection_anchors = selection.range().to_anchors(&buffer);
4761 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4762 for (search_buffer, search_range, excerpt_id) in
4763 buffer.range_to_buffer_ranges(range)
4764 {
4765 ranges.extend(
4766 project::search::SearchQuery::text(
4767 query.clone(),
4768 false,
4769 false,
4770 false,
4771 Default::default(),
4772 Default::default(),
4773 None,
4774 )
4775 .unwrap()
4776 .search(search_buffer, Some(search_range.clone()))
4777 .await
4778 .into_iter()
4779 .filter_map(
4780 |match_range| {
4781 let start = search_buffer.anchor_after(
4782 search_range.start + match_range.start,
4783 );
4784 let end = search_buffer.anchor_before(
4785 search_range.start + match_range.end,
4786 );
4787 let range = Anchor::range_in_buffer(
4788 excerpt_id,
4789 search_buffer.remote_id(),
4790 start..end,
4791 );
4792 (range != selection_anchors).then_some(range)
4793 },
4794 ),
4795 );
4796 }
4797 }
4798 ranges
4799 }))
4800 })
4801 .log_err()
4802 else {
4803 return;
4804 };
4805 let matches = matches_task.await;
4806 editor
4807 .update_in(&mut cx, |editor, _, cx| {
4808 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4809 if !matches.is_empty() {
4810 editor.highlight_background::<SelectedTextHighlight>(
4811 &matches,
4812 |theme| theme.editor_document_highlight_bracket_background,
4813 cx,
4814 )
4815 }
4816 })
4817 .log_err();
4818 }));
4819 }
4820
4821 pub fn refresh_inline_completion(
4822 &mut self,
4823 debounce: bool,
4824 user_requested: bool,
4825 window: &mut Window,
4826 cx: &mut Context<Self>,
4827 ) -> Option<()> {
4828 let provider = self.edit_prediction_provider()?;
4829 let cursor = self.selections.newest_anchor().head();
4830 let (buffer, cursor_buffer_position) =
4831 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4832
4833 if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4834 self.discard_inline_completion(false, cx);
4835 return None;
4836 }
4837
4838 if !user_requested
4839 && (!self.should_show_edit_predictions()
4840 || !self.is_focused(window)
4841 || buffer.read(cx).is_empty())
4842 {
4843 self.discard_inline_completion(false, cx);
4844 return None;
4845 }
4846
4847 self.update_visible_inline_completion(window, cx);
4848 provider.refresh(
4849 self.project.clone(),
4850 buffer,
4851 cursor_buffer_position,
4852 debounce,
4853 cx,
4854 );
4855 Some(())
4856 }
4857
4858 fn show_edit_predictions_in_menu(&self) -> bool {
4859 match self.edit_prediction_settings {
4860 EditPredictionSettings::Disabled => false,
4861 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4862 }
4863 }
4864
4865 pub fn edit_predictions_enabled(&self) -> bool {
4866 match self.edit_prediction_settings {
4867 EditPredictionSettings::Disabled => false,
4868 EditPredictionSettings::Enabled { .. } => true,
4869 }
4870 }
4871
4872 fn edit_prediction_requires_modifier(&self) -> bool {
4873 match self.edit_prediction_settings {
4874 EditPredictionSettings::Disabled => false,
4875 EditPredictionSettings::Enabled {
4876 preview_requires_modifier,
4877 ..
4878 } => preview_requires_modifier,
4879 }
4880 }
4881
4882 fn edit_prediction_settings_at_position(
4883 &self,
4884 buffer: &Entity<Buffer>,
4885 buffer_position: language::Anchor,
4886 cx: &App,
4887 ) -> EditPredictionSettings {
4888 if self.mode != EditorMode::Full
4889 || !self.show_inline_completions_override.unwrap_or(true)
4890 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
4891 {
4892 return EditPredictionSettings::Disabled;
4893 }
4894
4895 let buffer = buffer.read(cx);
4896
4897 let file = buffer.file();
4898
4899 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
4900 return EditPredictionSettings::Disabled;
4901 };
4902
4903 let by_provider = matches!(
4904 self.menu_inline_completions_policy,
4905 MenuInlineCompletionsPolicy::ByProvider
4906 );
4907
4908 let show_in_menu = by_provider
4909 && self
4910 .edit_prediction_provider
4911 .as_ref()
4912 .map_or(false, |provider| {
4913 provider.provider.show_completions_in_menu()
4914 });
4915
4916 let preview_requires_modifier =
4917 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Auto;
4918
4919 EditPredictionSettings::Enabled {
4920 show_in_menu,
4921 preview_requires_modifier,
4922 }
4923 }
4924
4925 fn should_show_edit_predictions(&self) -> bool {
4926 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
4927 }
4928
4929 pub fn edit_prediction_preview_is_active(&self) -> bool {
4930 matches!(
4931 self.edit_prediction_preview,
4932 EditPredictionPreview::Active { .. }
4933 )
4934 }
4935
4936 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
4937 let cursor = self.selections.newest_anchor().head();
4938 if let Some((buffer, cursor_position)) =
4939 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4940 {
4941 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
4942 } else {
4943 false
4944 }
4945 }
4946
4947 fn inline_completions_enabled_in_buffer(
4948 &self,
4949 buffer: &Entity<Buffer>,
4950 buffer_position: language::Anchor,
4951 cx: &App,
4952 ) -> bool {
4953 maybe!({
4954 let provider = self.edit_prediction_provider()?;
4955 if !provider.is_enabled(&buffer, buffer_position, cx) {
4956 return Some(false);
4957 }
4958 let buffer = buffer.read(cx);
4959 let Some(file) = buffer.file() else {
4960 return Some(true);
4961 };
4962 let settings = all_language_settings(Some(file), cx);
4963 Some(settings.inline_completions_enabled_for_path(file.path()))
4964 })
4965 .unwrap_or(false)
4966 }
4967
4968 fn cycle_inline_completion(
4969 &mut self,
4970 direction: Direction,
4971 window: &mut Window,
4972 cx: &mut Context<Self>,
4973 ) -> Option<()> {
4974 let provider = self.edit_prediction_provider()?;
4975 let cursor = self.selections.newest_anchor().head();
4976 let (buffer, cursor_buffer_position) =
4977 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4978 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
4979 return None;
4980 }
4981
4982 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4983 self.update_visible_inline_completion(window, cx);
4984
4985 Some(())
4986 }
4987
4988 pub fn show_inline_completion(
4989 &mut self,
4990 _: &ShowEditPrediction,
4991 window: &mut Window,
4992 cx: &mut Context<Self>,
4993 ) {
4994 if !self.has_active_inline_completion() {
4995 self.refresh_inline_completion(false, true, window, cx);
4996 return;
4997 }
4998
4999 self.update_visible_inline_completion(window, cx);
5000 }
5001
5002 pub fn display_cursor_names(
5003 &mut self,
5004 _: &DisplayCursorNames,
5005 window: &mut Window,
5006 cx: &mut Context<Self>,
5007 ) {
5008 self.show_cursor_names(window, cx);
5009 }
5010
5011 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5012 self.show_cursor_names = true;
5013 cx.notify();
5014 cx.spawn_in(window, |this, mut cx| async move {
5015 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5016 this.update(&mut cx, |this, cx| {
5017 this.show_cursor_names = false;
5018 cx.notify()
5019 })
5020 .ok()
5021 })
5022 .detach();
5023 }
5024
5025 pub fn next_edit_prediction(
5026 &mut self,
5027 _: &NextEditPrediction,
5028 window: &mut Window,
5029 cx: &mut Context<Self>,
5030 ) {
5031 if self.has_active_inline_completion() {
5032 self.cycle_inline_completion(Direction::Next, window, cx);
5033 } else {
5034 let is_copilot_disabled = self
5035 .refresh_inline_completion(false, true, window, cx)
5036 .is_none();
5037 if is_copilot_disabled {
5038 cx.propagate();
5039 }
5040 }
5041 }
5042
5043 pub fn previous_edit_prediction(
5044 &mut self,
5045 _: &PreviousEditPrediction,
5046 window: &mut Window,
5047 cx: &mut Context<Self>,
5048 ) {
5049 if self.has_active_inline_completion() {
5050 self.cycle_inline_completion(Direction::Prev, window, cx);
5051 } else {
5052 let is_copilot_disabled = self
5053 .refresh_inline_completion(false, true, window, cx)
5054 .is_none();
5055 if is_copilot_disabled {
5056 cx.propagate();
5057 }
5058 }
5059 }
5060
5061 pub fn accept_edit_prediction(
5062 &mut self,
5063 _: &AcceptEditPrediction,
5064 window: &mut Window,
5065 cx: &mut Context<Self>,
5066 ) {
5067 if self.show_edit_predictions_in_menu() {
5068 self.hide_context_menu(window, cx);
5069 }
5070
5071 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5072 return;
5073 };
5074
5075 self.report_inline_completion_event(
5076 active_inline_completion.completion_id.clone(),
5077 true,
5078 cx,
5079 );
5080
5081 match &active_inline_completion.completion {
5082 InlineCompletion::Move { target, .. } => {
5083 let target = *target;
5084
5085 if let Some(position_map) = &self.last_position_map {
5086 if position_map
5087 .visible_row_range
5088 .contains(&target.to_display_point(&position_map.snapshot).row())
5089 || !self.edit_prediction_requires_modifier()
5090 {
5091 self.unfold_ranges(&[target..target], true, false, cx);
5092 // Note that this is also done in vim's handler of the Tab action.
5093 self.change_selections(
5094 Some(Autoscroll::newest()),
5095 window,
5096 cx,
5097 |selections| {
5098 selections.select_anchor_ranges([target..target]);
5099 },
5100 );
5101 self.clear_row_highlights::<EditPredictionPreview>();
5102
5103 self.edit_prediction_preview = EditPredictionPreview::Active {
5104 previous_scroll_position: None,
5105 };
5106 } else {
5107 self.edit_prediction_preview = EditPredictionPreview::Active {
5108 previous_scroll_position: Some(position_map.snapshot.scroll_anchor),
5109 };
5110 self.highlight_rows::<EditPredictionPreview>(
5111 target..target,
5112 cx.theme().colors().editor_highlighted_line_background,
5113 true,
5114 cx,
5115 );
5116 self.request_autoscroll(Autoscroll::fit(), cx);
5117 }
5118 }
5119 }
5120 InlineCompletion::Edit { edits, .. } => {
5121 if let Some(provider) = self.edit_prediction_provider() {
5122 provider.accept(cx);
5123 }
5124
5125 let snapshot = self.buffer.read(cx).snapshot(cx);
5126 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5127
5128 self.buffer.update(cx, |buffer, cx| {
5129 buffer.edit(edits.iter().cloned(), None, cx)
5130 });
5131
5132 self.change_selections(None, window, cx, |s| {
5133 s.select_anchor_ranges([last_edit_end..last_edit_end])
5134 });
5135
5136 self.update_visible_inline_completion(window, cx);
5137 if self.active_inline_completion.is_none() {
5138 self.refresh_inline_completion(true, true, window, cx);
5139 }
5140
5141 cx.notify();
5142 }
5143 }
5144
5145 self.edit_prediction_requires_modifier_in_leading_space = false;
5146 }
5147
5148 pub fn accept_partial_inline_completion(
5149 &mut self,
5150 _: &AcceptPartialEditPrediction,
5151 window: &mut Window,
5152 cx: &mut Context<Self>,
5153 ) {
5154 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5155 return;
5156 };
5157 if self.selections.count() != 1 {
5158 return;
5159 }
5160
5161 self.report_inline_completion_event(
5162 active_inline_completion.completion_id.clone(),
5163 true,
5164 cx,
5165 );
5166
5167 match &active_inline_completion.completion {
5168 InlineCompletion::Move { target, .. } => {
5169 let target = *target;
5170 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5171 selections.select_anchor_ranges([target..target]);
5172 });
5173 }
5174 InlineCompletion::Edit { edits, .. } => {
5175 // Find an insertion that starts at the cursor position.
5176 let snapshot = self.buffer.read(cx).snapshot(cx);
5177 let cursor_offset = self.selections.newest::<usize>(cx).head();
5178 let insertion = edits.iter().find_map(|(range, text)| {
5179 let range = range.to_offset(&snapshot);
5180 if range.is_empty() && range.start == cursor_offset {
5181 Some(text)
5182 } else {
5183 None
5184 }
5185 });
5186
5187 if let Some(text) = insertion {
5188 let mut partial_completion = text
5189 .chars()
5190 .by_ref()
5191 .take_while(|c| c.is_alphabetic())
5192 .collect::<String>();
5193 if partial_completion.is_empty() {
5194 partial_completion = text
5195 .chars()
5196 .by_ref()
5197 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5198 .collect::<String>();
5199 }
5200
5201 cx.emit(EditorEvent::InputHandled {
5202 utf16_range_to_replace: None,
5203 text: partial_completion.clone().into(),
5204 });
5205
5206 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5207
5208 self.refresh_inline_completion(true, true, window, cx);
5209 cx.notify();
5210 } else {
5211 self.accept_edit_prediction(&Default::default(), window, cx);
5212 }
5213 }
5214 }
5215 }
5216
5217 fn discard_inline_completion(
5218 &mut self,
5219 should_report_inline_completion_event: bool,
5220 cx: &mut Context<Self>,
5221 ) -> bool {
5222 if should_report_inline_completion_event {
5223 let completion_id = self
5224 .active_inline_completion
5225 .as_ref()
5226 .and_then(|active_completion| active_completion.completion_id.clone());
5227
5228 self.report_inline_completion_event(completion_id, false, cx);
5229 }
5230
5231 if let Some(provider) = self.edit_prediction_provider() {
5232 provider.discard(cx);
5233 }
5234
5235 self.take_active_inline_completion(cx)
5236 }
5237
5238 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5239 let Some(provider) = self.edit_prediction_provider() else {
5240 return;
5241 };
5242
5243 let Some((_, buffer, _)) = self
5244 .buffer
5245 .read(cx)
5246 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5247 else {
5248 return;
5249 };
5250
5251 let extension = buffer
5252 .read(cx)
5253 .file()
5254 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5255
5256 let event_type = match accepted {
5257 true => "Edit Prediction Accepted",
5258 false => "Edit Prediction Discarded",
5259 };
5260 telemetry::event!(
5261 event_type,
5262 provider = provider.name(),
5263 prediction_id = id,
5264 suggestion_accepted = accepted,
5265 file_extension = extension,
5266 );
5267 }
5268
5269 pub fn has_active_inline_completion(&self) -> bool {
5270 self.active_inline_completion.is_some()
5271 }
5272
5273 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5274 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5275 return false;
5276 };
5277
5278 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5279 self.clear_highlights::<InlineCompletionHighlight>(cx);
5280 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5281 true
5282 }
5283
5284 /// Returns true when we're displaying the edit prediction popover below the cursor
5285 /// like we are not previewing and the LSP autocomplete menu is visible
5286 /// or we are in `when_holding_modifier` mode.
5287 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5288 if self.edit_prediction_preview_is_active()
5289 || !self.show_edit_predictions_in_menu()
5290 || !self.edit_predictions_enabled()
5291 {
5292 return false;
5293 }
5294
5295 if self.has_visible_completions_menu() {
5296 return true;
5297 }
5298
5299 has_completion && self.edit_prediction_requires_modifier()
5300 }
5301
5302 fn handle_modifiers_changed(
5303 &mut self,
5304 modifiers: Modifiers,
5305 position_map: &PositionMap,
5306 window: &mut Window,
5307 cx: &mut Context<Self>,
5308 ) {
5309 if self.show_edit_predictions_in_menu() {
5310 self.update_edit_prediction_preview(&modifiers, window, cx);
5311 }
5312
5313 self.update_selection_mode(&modifiers, position_map, window, cx);
5314
5315 let mouse_position = window.mouse_position();
5316 if !position_map.text_hitbox.is_hovered(window) {
5317 return;
5318 }
5319
5320 self.update_hovered_link(
5321 position_map.point_for_position(mouse_position),
5322 &position_map.snapshot,
5323 modifiers,
5324 window,
5325 cx,
5326 )
5327 }
5328
5329 fn update_selection_mode(
5330 &mut self,
5331 modifiers: &Modifiers,
5332 position_map: &PositionMap,
5333 window: &mut Window,
5334 cx: &mut Context<Self>,
5335 ) {
5336 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5337 return;
5338 }
5339
5340 let mouse_position = window.mouse_position();
5341 let point_for_position = position_map.point_for_position(mouse_position);
5342 let position = point_for_position.previous_valid;
5343
5344 self.select(
5345 SelectPhase::BeginColumnar {
5346 position,
5347 reset: false,
5348 goal_column: point_for_position.exact_unclipped.column(),
5349 },
5350 window,
5351 cx,
5352 );
5353 }
5354
5355 fn update_edit_prediction_preview(
5356 &mut self,
5357 modifiers: &Modifiers,
5358 window: &mut Window,
5359 cx: &mut Context<Self>,
5360 ) {
5361 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5362 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5363 return;
5364 };
5365
5366 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5367 if matches!(
5368 self.edit_prediction_preview,
5369 EditPredictionPreview::Inactive
5370 ) {
5371 self.edit_prediction_preview = EditPredictionPreview::Active {
5372 previous_scroll_position: None,
5373 };
5374
5375 self.update_visible_inline_completion(window, cx);
5376 cx.notify();
5377 }
5378 } else if let EditPredictionPreview::Active {
5379 previous_scroll_position,
5380 } = self.edit_prediction_preview
5381 {
5382 if let (Some(previous_scroll_position), Some(position_map)) =
5383 (previous_scroll_position, self.last_position_map.as_ref())
5384 {
5385 self.set_scroll_position(
5386 previous_scroll_position
5387 .scroll_position(&position_map.snapshot.display_snapshot),
5388 window,
5389 cx,
5390 );
5391 }
5392
5393 self.edit_prediction_preview = EditPredictionPreview::Inactive;
5394 self.clear_row_highlights::<EditPredictionPreview>();
5395 self.update_visible_inline_completion(window, cx);
5396 cx.notify();
5397 }
5398 }
5399
5400 fn update_visible_inline_completion(
5401 &mut self,
5402 _window: &mut Window,
5403 cx: &mut Context<Self>,
5404 ) -> Option<()> {
5405 let selection = self.selections.newest_anchor();
5406 let cursor = selection.head();
5407 let multibuffer = self.buffer.read(cx).snapshot(cx);
5408 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5409 let excerpt_id = cursor.excerpt_id;
5410
5411 let show_in_menu = self.show_edit_predictions_in_menu();
5412 let completions_menu_has_precedence = !show_in_menu
5413 && (self.context_menu.borrow().is_some()
5414 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5415
5416 if completions_menu_has_precedence
5417 || !offset_selection.is_empty()
5418 || self
5419 .active_inline_completion
5420 .as_ref()
5421 .map_or(false, |completion| {
5422 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5423 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5424 !invalidation_range.contains(&offset_selection.head())
5425 })
5426 {
5427 self.discard_inline_completion(false, cx);
5428 return None;
5429 }
5430
5431 self.take_active_inline_completion(cx);
5432 let Some(provider) = self.edit_prediction_provider() else {
5433 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5434 return None;
5435 };
5436
5437 let (buffer, cursor_buffer_position) =
5438 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5439
5440 self.edit_prediction_settings =
5441 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5442
5443 self.edit_prediction_cursor_on_leading_whitespace =
5444 multibuffer.is_line_whitespace_upto(cursor);
5445
5446 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5447 let edits = inline_completion
5448 .edits
5449 .into_iter()
5450 .flat_map(|(range, new_text)| {
5451 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5452 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5453 Some((start..end, new_text))
5454 })
5455 .collect::<Vec<_>>();
5456 if edits.is_empty() {
5457 return None;
5458 }
5459
5460 let first_edit_start = edits.first().unwrap().0.start;
5461 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5462 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5463
5464 let last_edit_end = edits.last().unwrap().0.end;
5465 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5466 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5467
5468 let cursor_row = cursor.to_point(&multibuffer).row;
5469
5470 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5471
5472 let mut inlay_ids = Vec::new();
5473 let invalidation_row_range;
5474 let move_invalidation_row_range = if cursor_row < edit_start_row {
5475 Some(cursor_row..edit_end_row)
5476 } else if cursor_row > edit_end_row {
5477 Some(edit_start_row..cursor_row)
5478 } else {
5479 None
5480 };
5481 let is_move =
5482 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5483 let completion = if is_move {
5484 invalidation_row_range =
5485 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5486 let target = first_edit_start;
5487 InlineCompletion::Move { target, snapshot }
5488 } else {
5489 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5490 && !self.inline_completions_hidden_for_vim_mode;
5491
5492 if show_completions_in_buffer {
5493 if edits
5494 .iter()
5495 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5496 {
5497 let mut inlays = Vec::new();
5498 for (range, new_text) in &edits {
5499 let inlay = Inlay::inline_completion(
5500 post_inc(&mut self.next_inlay_id),
5501 range.start,
5502 new_text.as_str(),
5503 );
5504 inlay_ids.push(inlay.id);
5505 inlays.push(inlay);
5506 }
5507
5508 self.splice_inlays(&[], inlays, cx);
5509 } else {
5510 let background_color = cx.theme().status().deleted_background;
5511 self.highlight_text::<InlineCompletionHighlight>(
5512 edits.iter().map(|(range, _)| range.clone()).collect(),
5513 HighlightStyle {
5514 background_color: Some(background_color),
5515 ..Default::default()
5516 },
5517 cx,
5518 );
5519 }
5520 }
5521
5522 invalidation_row_range = edit_start_row..edit_end_row;
5523
5524 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5525 if provider.show_tab_accept_marker() {
5526 EditDisplayMode::TabAccept
5527 } else {
5528 EditDisplayMode::Inline
5529 }
5530 } else {
5531 EditDisplayMode::DiffPopover
5532 };
5533
5534 InlineCompletion::Edit {
5535 edits,
5536 edit_preview: inline_completion.edit_preview,
5537 display_mode,
5538 snapshot,
5539 }
5540 };
5541
5542 let invalidation_range = multibuffer
5543 .anchor_before(Point::new(invalidation_row_range.start, 0))
5544 ..multibuffer.anchor_after(Point::new(
5545 invalidation_row_range.end,
5546 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5547 ));
5548
5549 self.stale_inline_completion_in_menu = None;
5550 self.active_inline_completion = Some(InlineCompletionState {
5551 inlay_ids,
5552 completion,
5553 completion_id: inline_completion.id,
5554 invalidation_range,
5555 });
5556
5557 cx.notify();
5558
5559 Some(())
5560 }
5561
5562 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5563 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5564 }
5565
5566 fn render_code_actions_indicator(
5567 &self,
5568 _style: &EditorStyle,
5569 row: DisplayRow,
5570 is_active: bool,
5571 cx: &mut Context<Self>,
5572 ) -> Option<IconButton> {
5573 if self.available_code_actions.is_some() {
5574 Some(
5575 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5576 .shape(ui::IconButtonShape::Square)
5577 .icon_size(IconSize::XSmall)
5578 .icon_color(Color::Muted)
5579 .toggle_state(is_active)
5580 .tooltip({
5581 let focus_handle = self.focus_handle.clone();
5582 move |window, cx| {
5583 Tooltip::for_action_in(
5584 "Toggle Code Actions",
5585 &ToggleCodeActions {
5586 deployed_from_indicator: None,
5587 },
5588 &focus_handle,
5589 window,
5590 cx,
5591 )
5592 }
5593 })
5594 .on_click(cx.listener(move |editor, _e, window, cx| {
5595 window.focus(&editor.focus_handle(cx));
5596 editor.toggle_code_actions(
5597 &ToggleCodeActions {
5598 deployed_from_indicator: Some(row),
5599 },
5600 window,
5601 cx,
5602 );
5603 })),
5604 )
5605 } else {
5606 None
5607 }
5608 }
5609
5610 fn clear_tasks(&mut self) {
5611 self.tasks.clear()
5612 }
5613
5614 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5615 if self.tasks.insert(key, value).is_some() {
5616 // This case should hopefully be rare, but just in case...
5617 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5618 }
5619 }
5620
5621 fn build_tasks_context(
5622 project: &Entity<Project>,
5623 buffer: &Entity<Buffer>,
5624 buffer_row: u32,
5625 tasks: &Arc<RunnableTasks>,
5626 cx: &mut Context<Self>,
5627 ) -> Task<Option<task::TaskContext>> {
5628 let position = Point::new(buffer_row, tasks.column);
5629 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5630 let location = Location {
5631 buffer: buffer.clone(),
5632 range: range_start..range_start,
5633 };
5634 // Fill in the environmental variables from the tree-sitter captures
5635 let mut captured_task_variables = TaskVariables::default();
5636 for (capture_name, value) in tasks.extra_variables.clone() {
5637 captured_task_variables.insert(
5638 task::VariableName::Custom(capture_name.into()),
5639 value.clone(),
5640 );
5641 }
5642 project.update(cx, |project, cx| {
5643 project.task_store().update(cx, |task_store, cx| {
5644 task_store.task_context_for_location(captured_task_variables, location, cx)
5645 })
5646 })
5647 }
5648
5649 pub fn spawn_nearest_task(
5650 &mut self,
5651 action: &SpawnNearestTask,
5652 window: &mut Window,
5653 cx: &mut Context<Self>,
5654 ) {
5655 let Some((workspace, _)) = self.workspace.clone() else {
5656 return;
5657 };
5658 let Some(project) = self.project.clone() else {
5659 return;
5660 };
5661
5662 // Try to find a closest, enclosing node using tree-sitter that has a
5663 // task
5664 let Some((buffer, buffer_row, tasks)) = self
5665 .find_enclosing_node_task(cx)
5666 // Or find the task that's closest in row-distance.
5667 .or_else(|| self.find_closest_task(cx))
5668 else {
5669 return;
5670 };
5671
5672 let reveal_strategy = action.reveal;
5673 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5674 cx.spawn_in(window, |_, mut cx| async move {
5675 let context = task_context.await?;
5676 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5677
5678 let resolved = resolved_task.resolved.as_mut()?;
5679 resolved.reveal = reveal_strategy;
5680
5681 workspace
5682 .update(&mut cx, |workspace, cx| {
5683 workspace::tasks::schedule_resolved_task(
5684 workspace,
5685 task_source_kind,
5686 resolved_task,
5687 false,
5688 cx,
5689 );
5690 })
5691 .ok()
5692 })
5693 .detach();
5694 }
5695
5696 fn find_closest_task(
5697 &mut self,
5698 cx: &mut Context<Self>,
5699 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5700 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5701
5702 let ((buffer_id, row), tasks) = self
5703 .tasks
5704 .iter()
5705 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5706
5707 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5708 let tasks = Arc::new(tasks.to_owned());
5709 Some((buffer, *row, tasks))
5710 }
5711
5712 fn find_enclosing_node_task(
5713 &mut self,
5714 cx: &mut Context<Self>,
5715 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5716 let snapshot = self.buffer.read(cx).snapshot(cx);
5717 let offset = self.selections.newest::<usize>(cx).head();
5718 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5719 let buffer_id = excerpt.buffer().remote_id();
5720
5721 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5722 let mut cursor = layer.node().walk();
5723
5724 while cursor.goto_first_child_for_byte(offset).is_some() {
5725 if cursor.node().end_byte() == offset {
5726 cursor.goto_next_sibling();
5727 }
5728 }
5729
5730 // Ascend to the smallest ancestor that contains the range and has a task.
5731 loop {
5732 let node = cursor.node();
5733 let node_range = node.byte_range();
5734 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5735
5736 // Check if this node contains our offset
5737 if node_range.start <= offset && node_range.end >= offset {
5738 // If it contains offset, check for task
5739 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5740 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5741 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5742 }
5743 }
5744
5745 if !cursor.goto_parent() {
5746 break;
5747 }
5748 }
5749 None
5750 }
5751
5752 fn render_run_indicator(
5753 &self,
5754 _style: &EditorStyle,
5755 is_active: bool,
5756 row: DisplayRow,
5757 cx: &mut Context<Self>,
5758 ) -> IconButton {
5759 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5760 .shape(ui::IconButtonShape::Square)
5761 .icon_size(IconSize::XSmall)
5762 .icon_color(Color::Muted)
5763 .toggle_state(is_active)
5764 .on_click(cx.listener(move |editor, _e, window, cx| {
5765 window.focus(&editor.focus_handle(cx));
5766 editor.toggle_code_actions(
5767 &ToggleCodeActions {
5768 deployed_from_indicator: Some(row),
5769 },
5770 window,
5771 cx,
5772 );
5773 }))
5774 }
5775
5776 pub fn context_menu_visible(&self) -> bool {
5777 !self.edit_prediction_preview_is_active()
5778 && self
5779 .context_menu
5780 .borrow()
5781 .as_ref()
5782 .map_or(false, |menu| menu.visible())
5783 }
5784
5785 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5786 self.context_menu
5787 .borrow()
5788 .as_ref()
5789 .map(|menu| menu.origin())
5790 }
5791
5792 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5793 px(30.)
5794 }
5795
5796 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5797 if self.read_only(cx) {
5798 cx.theme().players().read_only()
5799 } else {
5800 self.style.as_ref().unwrap().local_player
5801 }
5802 }
5803
5804 fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
5805 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
5806 let accept_keystroke = accept_binding.keystroke()?;
5807
5808 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
5809
5810 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
5811 Color::Accent
5812 } else {
5813 Color::Muted
5814 };
5815
5816 h_flex()
5817 .px_0p5()
5818 .when(is_platform_style_mac, |parent| parent.gap_0p5())
5819 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
5820 .text_size(TextSize::XSmall.rems(cx))
5821 .child(h_flex().children(ui::render_modifiers(
5822 &accept_keystroke.modifiers,
5823 PlatformStyle::platform(),
5824 Some(modifiers_color),
5825 Some(IconSize::XSmall.rems().into()),
5826 true,
5827 )))
5828 .when(is_platform_style_mac, |parent| {
5829 parent.child(accept_keystroke.key.clone())
5830 })
5831 .when(!is_platform_style_mac, |parent| {
5832 parent.child(
5833 Key::new(
5834 util::capitalize(&accept_keystroke.key),
5835 Some(Color::Default),
5836 )
5837 .size(Some(IconSize::XSmall.rems().into())),
5838 )
5839 })
5840 .into()
5841 }
5842
5843 fn render_edit_prediction_line_popover(
5844 &self,
5845 label: impl Into<SharedString>,
5846 icon: Option<IconName>,
5847 window: &mut Window,
5848 cx: &App,
5849 ) -> Option<Div> {
5850 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
5851
5852 let result = h_flex()
5853 .py_0p5()
5854 .pl_1()
5855 .pr(padding_right)
5856 .gap_1()
5857 .rounded(px(6.))
5858 .border_1()
5859 .bg(Self::edit_prediction_line_popover_bg_color(cx))
5860 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
5861 .shadow_sm()
5862 .children(self.render_edit_prediction_accept_keybind(window, cx))
5863 .child(Label::new(label).size(LabelSize::Small))
5864 .when_some(icon, |element, icon| {
5865 element.child(
5866 div()
5867 .mt(px(1.5))
5868 .child(Icon::new(icon).size(IconSize::Small)),
5869 )
5870 });
5871
5872 Some(result)
5873 }
5874
5875 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
5876 let accent_color = cx.theme().colors().text_accent;
5877 let editor_bg_color = cx.theme().colors().editor_background;
5878 editor_bg_color.blend(accent_color.opacity(0.1))
5879 }
5880
5881 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
5882 let accent_color = cx.theme().colors().text_accent;
5883 let editor_bg_color = cx.theme().colors().editor_background;
5884 editor_bg_color.blend(accent_color.opacity(0.6))
5885 }
5886
5887 #[allow(clippy::too_many_arguments)]
5888 fn render_edit_prediction_cursor_popover(
5889 &self,
5890 min_width: Pixels,
5891 max_width: Pixels,
5892 cursor_point: Point,
5893 style: &EditorStyle,
5894 accept_keystroke: Option<&gpui::Keystroke>,
5895 _window: &Window,
5896 cx: &mut Context<Editor>,
5897 ) -> Option<AnyElement> {
5898 let provider = self.edit_prediction_provider.as_ref()?;
5899
5900 if provider.provider.needs_terms_acceptance(cx) {
5901 return Some(
5902 h_flex()
5903 .min_w(min_width)
5904 .flex_1()
5905 .px_2()
5906 .py_1()
5907 .gap_3()
5908 .elevation_2(cx)
5909 .hover(|style| style.bg(cx.theme().colors().element_hover))
5910 .id("accept-terms")
5911 .cursor_pointer()
5912 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5913 .on_click(cx.listener(|this, _event, window, cx| {
5914 cx.stop_propagation();
5915 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5916 window.dispatch_action(
5917 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5918 cx,
5919 );
5920 }))
5921 .child(
5922 h_flex()
5923 .flex_1()
5924 .gap_2()
5925 .child(Icon::new(IconName::ZedPredict))
5926 .child(Label::new("Accept Terms of Service"))
5927 .child(div().w_full())
5928 .child(
5929 Icon::new(IconName::ArrowUpRight)
5930 .color(Color::Muted)
5931 .size(IconSize::Small),
5932 )
5933 .into_any_element(),
5934 )
5935 .into_any(),
5936 );
5937 }
5938
5939 let is_refreshing = provider.provider.is_refreshing(cx);
5940
5941 fn pending_completion_container() -> Div {
5942 h_flex()
5943 .h_full()
5944 .flex_1()
5945 .gap_2()
5946 .child(Icon::new(IconName::ZedPredict))
5947 }
5948
5949 let completion = match &self.active_inline_completion {
5950 Some(completion) => match &completion.completion {
5951 InlineCompletion::Move {
5952 target, snapshot, ..
5953 } if !self.has_visible_completions_menu() => {
5954 use text::ToPoint as _;
5955
5956 return Some(
5957 h_flex()
5958 .px_2()
5959 .py_1()
5960 .gap_2()
5961 .elevation_2(cx)
5962 .border_color(cx.theme().colors().border)
5963 .rounded(px(6.))
5964 .rounded_tl(px(0.))
5965 .child(
5966 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
5967 Icon::new(IconName::ZedPredictDown)
5968 } else {
5969 Icon::new(IconName::ZedPredictUp)
5970 },
5971 )
5972 .child(Label::new("Hold").size(LabelSize::Small))
5973 .child(h_flex().children(ui::render_modifiers(
5974 &accept_keystroke?.modifiers,
5975 PlatformStyle::platform(),
5976 Some(Color::Default),
5977 Some(IconSize::Small.rems().into()),
5978 false,
5979 )))
5980 .into_any(),
5981 );
5982 }
5983 _ => self.render_edit_prediction_cursor_popover_preview(
5984 completion,
5985 cursor_point,
5986 style,
5987 cx,
5988 )?,
5989 },
5990
5991 None if is_refreshing => match &self.stale_inline_completion_in_menu {
5992 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
5993 stale_completion,
5994 cursor_point,
5995 style,
5996 cx,
5997 )?,
5998
5999 None => {
6000 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6001 }
6002 },
6003
6004 None => pending_completion_container().child(Label::new("No Prediction")),
6005 };
6006
6007 let completion = if is_refreshing {
6008 completion
6009 .with_animation(
6010 "loading-completion",
6011 Animation::new(Duration::from_secs(2))
6012 .repeat()
6013 .with_easing(pulsating_between(0.4, 0.8)),
6014 |label, delta| label.opacity(delta),
6015 )
6016 .into_any_element()
6017 } else {
6018 completion.into_any_element()
6019 };
6020
6021 let has_completion = self.active_inline_completion.is_some();
6022
6023 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6024 Some(
6025 h_flex()
6026 .min_w(min_width)
6027 .max_w(max_width)
6028 .flex_1()
6029 .elevation_2(cx)
6030 .border_color(cx.theme().colors().border)
6031 .child(
6032 div()
6033 .flex_1()
6034 .py_1()
6035 .px_2()
6036 .overflow_hidden()
6037 .child(completion),
6038 )
6039 .when_some(accept_keystroke, |el, accept_keystroke| {
6040 if !accept_keystroke.modifiers.modified() {
6041 return el;
6042 }
6043
6044 el.child(
6045 h_flex()
6046 .h_full()
6047 .border_l_1()
6048 .rounded_r_lg()
6049 .border_color(cx.theme().colors().border)
6050 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6051 .gap_1()
6052 .py_1()
6053 .px_2()
6054 .child(
6055 h_flex()
6056 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6057 .when(is_platform_style_mac, |parent| parent.gap_1())
6058 .child(h_flex().children(ui::render_modifiers(
6059 &accept_keystroke.modifiers,
6060 PlatformStyle::platform(),
6061 Some(if !has_completion {
6062 Color::Muted
6063 } else {
6064 Color::Default
6065 }),
6066 None,
6067 false,
6068 ))),
6069 )
6070 .child(Label::new("Preview").into_any_element())
6071 .opacity(if has_completion { 1.0 } else { 0.4 }),
6072 )
6073 })
6074 .into_any(),
6075 )
6076 }
6077
6078 fn render_edit_prediction_cursor_popover_preview(
6079 &self,
6080 completion: &InlineCompletionState,
6081 cursor_point: Point,
6082 style: &EditorStyle,
6083 cx: &mut Context<Editor>,
6084 ) -> Option<Div> {
6085 use text::ToPoint as _;
6086
6087 fn render_relative_row_jump(
6088 prefix: impl Into<String>,
6089 current_row: u32,
6090 target_row: u32,
6091 ) -> Div {
6092 let (row_diff, arrow) = if target_row < current_row {
6093 (current_row - target_row, IconName::ArrowUp)
6094 } else {
6095 (target_row - current_row, IconName::ArrowDown)
6096 };
6097
6098 h_flex()
6099 .child(
6100 Label::new(format!("{}{}", prefix.into(), row_diff))
6101 .color(Color::Muted)
6102 .size(LabelSize::Small),
6103 )
6104 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6105 }
6106
6107 match &completion.completion {
6108 InlineCompletion::Move {
6109 target, snapshot, ..
6110 } => Some(
6111 h_flex()
6112 .px_2()
6113 .gap_2()
6114 .flex_1()
6115 .child(
6116 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6117 Icon::new(IconName::ZedPredictDown)
6118 } else {
6119 Icon::new(IconName::ZedPredictUp)
6120 },
6121 )
6122 .child(Label::new("Jump to Edit")),
6123 ),
6124
6125 InlineCompletion::Edit {
6126 edits,
6127 edit_preview,
6128 snapshot,
6129 display_mode: _,
6130 } => {
6131 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6132
6133 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6134 &snapshot,
6135 &edits,
6136 edit_preview.as_ref()?,
6137 true,
6138 cx,
6139 )
6140 .first_line_preview();
6141
6142 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6143 .with_highlights(&style.text, highlighted_edits.highlights);
6144
6145 let preview = h_flex()
6146 .gap_1()
6147 .min_w_16()
6148 .child(styled_text)
6149 .when(has_more_lines, |parent| parent.child("…"));
6150
6151 let left = if first_edit_row != cursor_point.row {
6152 render_relative_row_jump("", cursor_point.row, first_edit_row)
6153 .into_any_element()
6154 } else {
6155 Icon::new(IconName::ZedPredict).into_any_element()
6156 };
6157
6158 Some(
6159 h_flex()
6160 .h_full()
6161 .flex_1()
6162 .gap_2()
6163 .pr_1()
6164 .overflow_x_hidden()
6165 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6166 .child(left)
6167 .child(preview),
6168 )
6169 }
6170 }
6171 }
6172
6173 fn render_context_menu(
6174 &self,
6175 style: &EditorStyle,
6176 max_height_in_lines: u32,
6177 y_flipped: bool,
6178 window: &mut Window,
6179 cx: &mut Context<Editor>,
6180 ) -> Option<AnyElement> {
6181 let menu = self.context_menu.borrow();
6182 let menu = menu.as_ref()?;
6183 if !menu.visible() {
6184 return None;
6185 };
6186 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6187 }
6188
6189 fn render_context_menu_aside(
6190 &mut self,
6191 max_size: Size<Pixels>,
6192 window: &mut Window,
6193 cx: &mut Context<Editor>,
6194 ) -> Option<AnyElement> {
6195 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6196 if menu.visible() {
6197 menu.render_aside(self, max_size, window, cx)
6198 } else {
6199 None
6200 }
6201 })
6202 }
6203
6204 fn hide_context_menu(
6205 &mut self,
6206 window: &mut Window,
6207 cx: &mut Context<Self>,
6208 ) -> Option<CodeContextMenu> {
6209 cx.notify();
6210 self.completion_tasks.clear();
6211 let context_menu = self.context_menu.borrow_mut().take();
6212 self.stale_inline_completion_in_menu.take();
6213 self.update_visible_inline_completion(window, cx);
6214 context_menu
6215 }
6216
6217 fn show_snippet_choices(
6218 &mut self,
6219 choices: &Vec<String>,
6220 selection: Range<Anchor>,
6221 cx: &mut Context<Self>,
6222 ) {
6223 if selection.start.buffer_id.is_none() {
6224 return;
6225 }
6226 let buffer_id = selection.start.buffer_id.unwrap();
6227 let buffer = self.buffer().read(cx).buffer(buffer_id);
6228 let id = post_inc(&mut self.next_completion_id);
6229
6230 if let Some(buffer) = buffer {
6231 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6232 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6233 ));
6234 }
6235 }
6236
6237 pub fn insert_snippet(
6238 &mut self,
6239 insertion_ranges: &[Range<usize>],
6240 snippet: Snippet,
6241 window: &mut Window,
6242 cx: &mut Context<Self>,
6243 ) -> Result<()> {
6244 struct Tabstop<T> {
6245 is_end_tabstop: bool,
6246 ranges: Vec<Range<T>>,
6247 choices: Option<Vec<String>>,
6248 }
6249
6250 let tabstops = self.buffer.update(cx, |buffer, cx| {
6251 let snippet_text: Arc<str> = snippet.text.clone().into();
6252 buffer.edit(
6253 insertion_ranges
6254 .iter()
6255 .cloned()
6256 .map(|range| (range, snippet_text.clone())),
6257 Some(AutoindentMode::EachLine),
6258 cx,
6259 );
6260
6261 let snapshot = &*buffer.read(cx);
6262 let snippet = &snippet;
6263 snippet
6264 .tabstops
6265 .iter()
6266 .map(|tabstop| {
6267 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6268 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6269 });
6270 let mut tabstop_ranges = tabstop
6271 .ranges
6272 .iter()
6273 .flat_map(|tabstop_range| {
6274 let mut delta = 0_isize;
6275 insertion_ranges.iter().map(move |insertion_range| {
6276 let insertion_start = insertion_range.start as isize + delta;
6277 delta +=
6278 snippet.text.len() as isize - insertion_range.len() as isize;
6279
6280 let start = ((insertion_start + tabstop_range.start) as usize)
6281 .min(snapshot.len());
6282 let end = ((insertion_start + tabstop_range.end) as usize)
6283 .min(snapshot.len());
6284 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6285 })
6286 })
6287 .collect::<Vec<_>>();
6288 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6289
6290 Tabstop {
6291 is_end_tabstop,
6292 ranges: tabstop_ranges,
6293 choices: tabstop.choices.clone(),
6294 }
6295 })
6296 .collect::<Vec<_>>()
6297 });
6298 if let Some(tabstop) = tabstops.first() {
6299 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6300 s.select_ranges(tabstop.ranges.iter().cloned());
6301 });
6302
6303 if let Some(choices) = &tabstop.choices {
6304 if let Some(selection) = tabstop.ranges.first() {
6305 self.show_snippet_choices(choices, selection.clone(), cx)
6306 }
6307 }
6308
6309 // If we're already at the last tabstop and it's at the end of the snippet,
6310 // we're done, we don't need to keep the state around.
6311 if !tabstop.is_end_tabstop {
6312 let choices = tabstops
6313 .iter()
6314 .map(|tabstop| tabstop.choices.clone())
6315 .collect();
6316
6317 let ranges = tabstops
6318 .into_iter()
6319 .map(|tabstop| tabstop.ranges)
6320 .collect::<Vec<_>>();
6321
6322 self.snippet_stack.push(SnippetState {
6323 active_index: 0,
6324 ranges,
6325 choices,
6326 });
6327 }
6328
6329 // Check whether the just-entered snippet ends with an auto-closable bracket.
6330 if self.autoclose_regions.is_empty() {
6331 let snapshot = self.buffer.read(cx).snapshot(cx);
6332 for selection in &mut self.selections.all::<Point>(cx) {
6333 let selection_head = selection.head();
6334 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6335 continue;
6336 };
6337
6338 let mut bracket_pair = None;
6339 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6340 let prev_chars = snapshot
6341 .reversed_chars_at(selection_head)
6342 .collect::<String>();
6343 for (pair, enabled) in scope.brackets() {
6344 if enabled
6345 && pair.close
6346 && prev_chars.starts_with(pair.start.as_str())
6347 && next_chars.starts_with(pair.end.as_str())
6348 {
6349 bracket_pair = Some(pair.clone());
6350 break;
6351 }
6352 }
6353 if let Some(pair) = bracket_pair {
6354 let start = snapshot.anchor_after(selection_head);
6355 let end = snapshot.anchor_after(selection_head);
6356 self.autoclose_regions.push(AutocloseRegion {
6357 selection_id: selection.id,
6358 range: start..end,
6359 pair,
6360 });
6361 }
6362 }
6363 }
6364 }
6365 Ok(())
6366 }
6367
6368 pub fn move_to_next_snippet_tabstop(
6369 &mut self,
6370 window: &mut Window,
6371 cx: &mut Context<Self>,
6372 ) -> bool {
6373 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6374 }
6375
6376 pub fn move_to_prev_snippet_tabstop(
6377 &mut self,
6378 window: &mut Window,
6379 cx: &mut Context<Self>,
6380 ) -> bool {
6381 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6382 }
6383
6384 pub fn move_to_snippet_tabstop(
6385 &mut self,
6386 bias: Bias,
6387 window: &mut Window,
6388 cx: &mut Context<Self>,
6389 ) -> bool {
6390 if let Some(mut snippet) = self.snippet_stack.pop() {
6391 match bias {
6392 Bias::Left => {
6393 if snippet.active_index > 0 {
6394 snippet.active_index -= 1;
6395 } else {
6396 self.snippet_stack.push(snippet);
6397 return false;
6398 }
6399 }
6400 Bias::Right => {
6401 if snippet.active_index + 1 < snippet.ranges.len() {
6402 snippet.active_index += 1;
6403 } else {
6404 self.snippet_stack.push(snippet);
6405 return false;
6406 }
6407 }
6408 }
6409 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6410 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6411 s.select_anchor_ranges(current_ranges.iter().cloned())
6412 });
6413
6414 if let Some(choices) = &snippet.choices[snippet.active_index] {
6415 if let Some(selection) = current_ranges.first() {
6416 self.show_snippet_choices(&choices, selection.clone(), cx);
6417 }
6418 }
6419
6420 // If snippet state is not at the last tabstop, push it back on the stack
6421 if snippet.active_index + 1 < snippet.ranges.len() {
6422 self.snippet_stack.push(snippet);
6423 }
6424 return true;
6425 }
6426 }
6427
6428 false
6429 }
6430
6431 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6432 self.transact(window, cx, |this, window, cx| {
6433 this.select_all(&SelectAll, window, cx);
6434 this.insert("", window, cx);
6435 });
6436 }
6437
6438 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6439 self.transact(window, cx, |this, window, cx| {
6440 this.select_autoclose_pair(window, cx);
6441 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6442 if !this.linked_edit_ranges.is_empty() {
6443 let selections = this.selections.all::<MultiBufferPoint>(cx);
6444 let snapshot = this.buffer.read(cx).snapshot(cx);
6445
6446 for selection in selections.iter() {
6447 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6448 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6449 if selection_start.buffer_id != selection_end.buffer_id {
6450 continue;
6451 }
6452 if let Some(ranges) =
6453 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6454 {
6455 for (buffer, entries) in ranges {
6456 linked_ranges.entry(buffer).or_default().extend(entries);
6457 }
6458 }
6459 }
6460 }
6461
6462 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6463 if !this.selections.line_mode {
6464 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6465 for selection in &mut selections {
6466 if selection.is_empty() {
6467 let old_head = selection.head();
6468 let mut new_head =
6469 movement::left(&display_map, old_head.to_display_point(&display_map))
6470 .to_point(&display_map);
6471 if let Some((buffer, line_buffer_range)) = display_map
6472 .buffer_snapshot
6473 .buffer_line_for_row(MultiBufferRow(old_head.row))
6474 {
6475 let indent_size =
6476 buffer.indent_size_for_line(line_buffer_range.start.row);
6477 let indent_len = match indent_size.kind {
6478 IndentKind::Space => {
6479 buffer.settings_at(line_buffer_range.start, cx).tab_size
6480 }
6481 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6482 };
6483 if old_head.column <= indent_size.len && old_head.column > 0 {
6484 let indent_len = indent_len.get();
6485 new_head = cmp::min(
6486 new_head,
6487 MultiBufferPoint::new(
6488 old_head.row,
6489 ((old_head.column - 1) / indent_len) * indent_len,
6490 ),
6491 );
6492 }
6493 }
6494
6495 selection.set_head(new_head, SelectionGoal::None);
6496 }
6497 }
6498 }
6499
6500 this.signature_help_state.set_backspace_pressed(true);
6501 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6502 s.select(selections)
6503 });
6504 this.insert("", window, cx);
6505 let empty_str: Arc<str> = Arc::from("");
6506 for (buffer, edits) in linked_ranges {
6507 let snapshot = buffer.read(cx).snapshot();
6508 use text::ToPoint as TP;
6509
6510 let edits = edits
6511 .into_iter()
6512 .map(|range| {
6513 let end_point = TP::to_point(&range.end, &snapshot);
6514 let mut start_point = TP::to_point(&range.start, &snapshot);
6515
6516 if end_point == start_point {
6517 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6518 .saturating_sub(1);
6519 start_point =
6520 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6521 };
6522
6523 (start_point..end_point, empty_str.clone())
6524 })
6525 .sorted_by_key(|(range, _)| range.start)
6526 .collect::<Vec<_>>();
6527 buffer.update(cx, |this, cx| {
6528 this.edit(edits, None, cx);
6529 })
6530 }
6531 this.refresh_inline_completion(true, false, window, cx);
6532 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6533 });
6534 }
6535
6536 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6537 self.transact(window, cx, |this, window, cx| {
6538 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6539 let line_mode = s.line_mode;
6540 s.move_with(|map, selection| {
6541 if selection.is_empty() && !line_mode {
6542 let cursor = movement::right(map, selection.head());
6543 selection.end = cursor;
6544 selection.reversed = true;
6545 selection.goal = SelectionGoal::None;
6546 }
6547 })
6548 });
6549 this.insert("", window, cx);
6550 this.refresh_inline_completion(true, false, window, cx);
6551 });
6552 }
6553
6554 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6555 if self.move_to_prev_snippet_tabstop(window, cx) {
6556 return;
6557 }
6558
6559 self.outdent(&Outdent, window, cx);
6560 }
6561
6562 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6563 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6564 return;
6565 }
6566
6567 let mut selections = self.selections.all_adjusted(cx);
6568 let buffer = self.buffer.read(cx);
6569 let snapshot = buffer.snapshot(cx);
6570 let rows_iter = selections.iter().map(|s| s.head().row);
6571 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6572
6573 let mut edits = Vec::new();
6574 let mut prev_edited_row = 0;
6575 let mut row_delta = 0;
6576 for selection in &mut selections {
6577 if selection.start.row != prev_edited_row {
6578 row_delta = 0;
6579 }
6580 prev_edited_row = selection.end.row;
6581
6582 // If the selection is non-empty, then increase the indentation of the selected lines.
6583 if !selection.is_empty() {
6584 row_delta =
6585 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6586 continue;
6587 }
6588
6589 // If the selection is empty and the cursor is in the leading whitespace before the
6590 // suggested indentation, then auto-indent the line.
6591 let cursor = selection.head();
6592 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6593 if let Some(suggested_indent) =
6594 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6595 {
6596 if cursor.column < suggested_indent.len
6597 && cursor.column <= current_indent.len
6598 && current_indent.len <= suggested_indent.len
6599 {
6600 selection.start = Point::new(cursor.row, suggested_indent.len);
6601 selection.end = selection.start;
6602 if row_delta == 0 {
6603 edits.extend(Buffer::edit_for_indent_size_adjustment(
6604 cursor.row,
6605 current_indent,
6606 suggested_indent,
6607 ));
6608 row_delta = suggested_indent.len - current_indent.len;
6609 }
6610 continue;
6611 }
6612 }
6613
6614 // Otherwise, insert a hard or soft tab.
6615 let settings = buffer.settings_at(cursor, cx);
6616 let tab_size = if settings.hard_tabs {
6617 IndentSize::tab()
6618 } else {
6619 let tab_size = settings.tab_size.get();
6620 let char_column = snapshot
6621 .text_for_range(Point::new(cursor.row, 0)..cursor)
6622 .flat_map(str::chars)
6623 .count()
6624 + row_delta as usize;
6625 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6626 IndentSize::spaces(chars_to_next_tab_stop)
6627 };
6628 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6629 selection.end = selection.start;
6630 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6631 row_delta += tab_size.len;
6632 }
6633
6634 self.transact(window, cx, |this, window, cx| {
6635 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6636 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6637 s.select(selections)
6638 });
6639 this.refresh_inline_completion(true, false, window, cx);
6640 });
6641 }
6642
6643 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6644 if self.read_only(cx) {
6645 return;
6646 }
6647 let mut selections = self.selections.all::<Point>(cx);
6648 let mut prev_edited_row = 0;
6649 let mut row_delta = 0;
6650 let mut edits = Vec::new();
6651 let buffer = self.buffer.read(cx);
6652 let snapshot = buffer.snapshot(cx);
6653 for selection in &mut selections {
6654 if selection.start.row != prev_edited_row {
6655 row_delta = 0;
6656 }
6657 prev_edited_row = selection.end.row;
6658
6659 row_delta =
6660 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6661 }
6662
6663 self.transact(window, cx, |this, window, cx| {
6664 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6665 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6666 s.select(selections)
6667 });
6668 });
6669 }
6670
6671 fn indent_selection(
6672 buffer: &MultiBuffer,
6673 snapshot: &MultiBufferSnapshot,
6674 selection: &mut Selection<Point>,
6675 edits: &mut Vec<(Range<Point>, String)>,
6676 delta_for_start_row: u32,
6677 cx: &App,
6678 ) -> u32 {
6679 let settings = buffer.settings_at(selection.start, cx);
6680 let tab_size = settings.tab_size.get();
6681 let indent_kind = if settings.hard_tabs {
6682 IndentKind::Tab
6683 } else {
6684 IndentKind::Space
6685 };
6686 let mut start_row = selection.start.row;
6687 let mut end_row = selection.end.row + 1;
6688
6689 // If a selection ends at the beginning of a line, don't indent
6690 // that last line.
6691 if selection.end.column == 0 && selection.end.row > selection.start.row {
6692 end_row -= 1;
6693 }
6694
6695 // Avoid re-indenting a row that has already been indented by a
6696 // previous selection, but still update this selection's column
6697 // to reflect that indentation.
6698 if delta_for_start_row > 0 {
6699 start_row += 1;
6700 selection.start.column += delta_for_start_row;
6701 if selection.end.row == selection.start.row {
6702 selection.end.column += delta_for_start_row;
6703 }
6704 }
6705
6706 let mut delta_for_end_row = 0;
6707 let has_multiple_rows = start_row + 1 != end_row;
6708 for row in start_row..end_row {
6709 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6710 let indent_delta = match (current_indent.kind, indent_kind) {
6711 (IndentKind::Space, IndentKind::Space) => {
6712 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6713 IndentSize::spaces(columns_to_next_tab_stop)
6714 }
6715 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6716 (_, IndentKind::Tab) => IndentSize::tab(),
6717 };
6718
6719 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6720 0
6721 } else {
6722 selection.start.column
6723 };
6724 let row_start = Point::new(row, start);
6725 edits.push((
6726 row_start..row_start,
6727 indent_delta.chars().collect::<String>(),
6728 ));
6729
6730 // Update this selection's endpoints to reflect the indentation.
6731 if row == selection.start.row {
6732 selection.start.column += indent_delta.len;
6733 }
6734 if row == selection.end.row {
6735 selection.end.column += indent_delta.len;
6736 delta_for_end_row = indent_delta.len;
6737 }
6738 }
6739
6740 if selection.start.row == selection.end.row {
6741 delta_for_start_row + delta_for_end_row
6742 } else {
6743 delta_for_end_row
6744 }
6745 }
6746
6747 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6748 if self.read_only(cx) {
6749 return;
6750 }
6751 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6752 let selections = self.selections.all::<Point>(cx);
6753 let mut deletion_ranges = Vec::new();
6754 let mut last_outdent = None;
6755 {
6756 let buffer = self.buffer.read(cx);
6757 let snapshot = buffer.snapshot(cx);
6758 for selection in &selections {
6759 let settings = buffer.settings_at(selection.start, cx);
6760 let tab_size = settings.tab_size.get();
6761 let mut rows = selection.spanned_rows(false, &display_map);
6762
6763 // Avoid re-outdenting a row that has already been outdented by a
6764 // previous selection.
6765 if let Some(last_row) = last_outdent {
6766 if last_row == rows.start {
6767 rows.start = rows.start.next_row();
6768 }
6769 }
6770 let has_multiple_rows = rows.len() > 1;
6771 for row in rows.iter_rows() {
6772 let indent_size = snapshot.indent_size_for_line(row);
6773 if indent_size.len > 0 {
6774 let deletion_len = match indent_size.kind {
6775 IndentKind::Space => {
6776 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6777 if columns_to_prev_tab_stop == 0 {
6778 tab_size
6779 } else {
6780 columns_to_prev_tab_stop
6781 }
6782 }
6783 IndentKind::Tab => 1,
6784 };
6785 let start = if has_multiple_rows
6786 || deletion_len > selection.start.column
6787 || indent_size.len < selection.start.column
6788 {
6789 0
6790 } else {
6791 selection.start.column - deletion_len
6792 };
6793 deletion_ranges.push(
6794 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6795 );
6796 last_outdent = Some(row);
6797 }
6798 }
6799 }
6800 }
6801
6802 self.transact(window, cx, |this, window, cx| {
6803 this.buffer.update(cx, |buffer, cx| {
6804 let empty_str: Arc<str> = Arc::default();
6805 buffer.edit(
6806 deletion_ranges
6807 .into_iter()
6808 .map(|range| (range, empty_str.clone())),
6809 None,
6810 cx,
6811 );
6812 });
6813 let selections = this.selections.all::<usize>(cx);
6814 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6815 s.select(selections)
6816 });
6817 });
6818 }
6819
6820 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6821 if self.read_only(cx) {
6822 return;
6823 }
6824 let selections = self
6825 .selections
6826 .all::<usize>(cx)
6827 .into_iter()
6828 .map(|s| s.range());
6829
6830 self.transact(window, cx, |this, window, cx| {
6831 this.buffer.update(cx, |buffer, cx| {
6832 buffer.autoindent_ranges(selections, cx);
6833 });
6834 let selections = this.selections.all::<usize>(cx);
6835 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6836 s.select(selections)
6837 });
6838 });
6839 }
6840
6841 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6842 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6843 let selections = self.selections.all::<Point>(cx);
6844
6845 let mut new_cursors = Vec::new();
6846 let mut edit_ranges = Vec::new();
6847 let mut selections = selections.iter().peekable();
6848 while let Some(selection) = selections.next() {
6849 let mut rows = selection.spanned_rows(false, &display_map);
6850 let goal_display_column = selection.head().to_display_point(&display_map).column();
6851
6852 // Accumulate contiguous regions of rows that we want to delete.
6853 while let Some(next_selection) = selections.peek() {
6854 let next_rows = next_selection.spanned_rows(false, &display_map);
6855 if next_rows.start <= rows.end {
6856 rows.end = next_rows.end;
6857 selections.next().unwrap();
6858 } else {
6859 break;
6860 }
6861 }
6862
6863 let buffer = &display_map.buffer_snapshot;
6864 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6865 let edit_end;
6866 let cursor_buffer_row;
6867 if buffer.max_point().row >= rows.end.0 {
6868 // If there's a line after the range, delete the \n from the end of the row range
6869 // and position the cursor on the next line.
6870 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6871 cursor_buffer_row = rows.end;
6872 } else {
6873 // If there isn't a line after the range, delete the \n from the line before the
6874 // start of the row range and position the cursor there.
6875 edit_start = edit_start.saturating_sub(1);
6876 edit_end = buffer.len();
6877 cursor_buffer_row = rows.start.previous_row();
6878 }
6879
6880 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6881 *cursor.column_mut() =
6882 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6883
6884 new_cursors.push((
6885 selection.id,
6886 buffer.anchor_after(cursor.to_point(&display_map)),
6887 ));
6888 edit_ranges.push(edit_start..edit_end);
6889 }
6890
6891 self.transact(window, cx, |this, window, cx| {
6892 let buffer = this.buffer.update(cx, |buffer, cx| {
6893 let empty_str: Arc<str> = Arc::default();
6894 buffer.edit(
6895 edit_ranges
6896 .into_iter()
6897 .map(|range| (range, empty_str.clone())),
6898 None,
6899 cx,
6900 );
6901 buffer.snapshot(cx)
6902 });
6903 let new_selections = new_cursors
6904 .into_iter()
6905 .map(|(id, cursor)| {
6906 let cursor = cursor.to_point(&buffer);
6907 Selection {
6908 id,
6909 start: cursor,
6910 end: cursor,
6911 reversed: false,
6912 goal: SelectionGoal::None,
6913 }
6914 })
6915 .collect();
6916
6917 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6918 s.select(new_selections);
6919 });
6920 });
6921 }
6922
6923 pub fn join_lines_impl(
6924 &mut self,
6925 insert_whitespace: bool,
6926 window: &mut Window,
6927 cx: &mut Context<Self>,
6928 ) {
6929 if self.read_only(cx) {
6930 return;
6931 }
6932 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6933 for selection in self.selections.all::<Point>(cx) {
6934 let start = MultiBufferRow(selection.start.row);
6935 // Treat single line selections as if they include the next line. Otherwise this action
6936 // would do nothing for single line selections individual cursors.
6937 let end = if selection.start.row == selection.end.row {
6938 MultiBufferRow(selection.start.row + 1)
6939 } else {
6940 MultiBufferRow(selection.end.row)
6941 };
6942
6943 if let Some(last_row_range) = row_ranges.last_mut() {
6944 if start <= last_row_range.end {
6945 last_row_range.end = end;
6946 continue;
6947 }
6948 }
6949 row_ranges.push(start..end);
6950 }
6951
6952 let snapshot = self.buffer.read(cx).snapshot(cx);
6953 let mut cursor_positions = Vec::new();
6954 for row_range in &row_ranges {
6955 let anchor = snapshot.anchor_before(Point::new(
6956 row_range.end.previous_row().0,
6957 snapshot.line_len(row_range.end.previous_row()),
6958 ));
6959 cursor_positions.push(anchor..anchor);
6960 }
6961
6962 self.transact(window, cx, |this, window, cx| {
6963 for row_range in row_ranges.into_iter().rev() {
6964 for row in row_range.iter_rows().rev() {
6965 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6966 let next_line_row = row.next_row();
6967 let indent = snapshot.indent_size_for_line(next_line_row);
6968 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6969
6970 let replace =
6971 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6972 " "
6973 } else {
6974 ""
6975 };
6976
6977 this.buffer.update(cx, |buffer, cx| {
6978 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6979 });
6980 }
6981 }
6982
6983 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6984 s.select_anchor_ranges(cursor_positions)
6985 });
6986 });
6987 }
6988
6989 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6990 self.join_lines_impl(true, window, cx);
6991 }
6992
6993 pub fn sort_lines_case_sensitive(
6994 &mut self,
6995 _: &SortLinesCaseSensitive,
6996 window: &mut Window,
6997 cx: &mut Context<Self>,
6998 ) {
6999 self.manipulate_lines(window, cx, |lines| lines.sort())
7000 }
7001
7002 pub fn sort_lines_case_insensitive(
7003 &mut self,
7004 _: &SortLinesCaseInsensitive,
7005 window: &mut Window,
7006 cx: &mut Context<Self>,
7007 ) {
7008 self.manipulate_lines(window, cx, |lines| {
7009 lines.sort_by_key(|line| line.to_lowercase())
7010 })
7011 }
7012
7013 pub fn unique_lines_case_insensitive(
7014 &mut self,
7015 _: &UniqueLinesCaseInsensitive,
7016 window: &mut Window,
7017 cx: &mut Context<Self>,
7018 ) {
7019 self.manipulate_lines(window, cx, |lines| {
7020 let mut seen = HashSet::default();
7021 lines.retain(|line| seen.insert(line.to_lowercase()));
7022 })
7023 }
7024
7025 pub fn unique_lines_case_sensitive(
7026 &mut self,
7027 _: &UniqueLinesCaseSensitive,
7028 window: &mut Window,
7029 cx: &mut Context<Self>,
7030 ) {
7031 self.manipulate_lines(window, cx, |lines| {
7032 let mut seen = HashSet::default();
7033 lines.retain(|line| seen.insert(*line));
7034 })
7035 }
7036
7037 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7038 let Some(project) = self.project.clone() else {
7039 return;
7040 };
7041 self.reload(project, window, cx)
7042 .detach_and_notify_err(window, cx);
7043 }
7044
7045 pub fn restore_file(
7046 &mut self,
7047 _: &::git::RestoreFile,
7048 window: &mut Window,
7049 cx: &mut Context<Self>,
7050 ) {
7051 let mut buffer_ids = HashSet::default();
7052 let snapshot = self.buffer().read(cx).snapshot(cx);
7053 for selection in self.selections.all::<usize>(cx) {
7054 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7055 }
7056
7057 let buffer = self.buffer().read(cx);
7058 let ranges = buffer_ids
7059 .into_iter()
7060 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7061 .collect::<Vec<_>>();
7062
7063 self.restore_hunks_in_ranges(ranges, window, cx);
7064 }
7065
7066 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7067 let selections = self
7068 .selections
7069 .all(cx)
7070 .into_iter()
7071 .map(|s| s.range())
7072 .collect();
7073 self.restore_hunks_in_ranges(selections, window, cx);
7074 }
7075
7076 fn restore_hunks_in_ranges(
7077 &mut self,
7078 ranges: Vec<Range<Point>>,
7079 window: &mut Window,
7080 cx: &mut Context<Editor>,
7081 ) {
7082 let mut revert_changes = HashMap::default();
7083 let snapshot = self.buffer.read(cx).snapshot(cx);
7084 let Some(project) = &self.project else {
7085 return;
7086 };
7087
7088 let chunk_by = self
7089 .snapshot(window, cx)
7090 .hunks_for_ranges(ranges.into_iter())
7091 .into_iter()
7092 .chunk_by(|hunk| hunk.buffer_id);
7093 for (buffer_id, hunks) in &chunk_by {
7094 let hunks = hunks.collect::<Vec<_>>();
7095 for hunk in &hunks {
7096 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7097 }
7098 Self::do_stage_or_unstage(project, false, buffer_id, hunks.into_iter(), &snapshot, cx);
7099 }
7100 drop(chunk_by);
7101 if !revert_changes.is_empty() {
7102 self.transact(window, cx, |editor, window, cx| {
7103 editor.revert(revert_changes, window, cx);
7104 });
7105 }
7106 }
7107
7108 pub fn open_active_item_in_terminal(
7109 &mut self,
7110 _: &OpenInTerminal,
7111 window: &mut Window,
7112 cx: &mut Context<Self>,
7113 ) {
7114 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7115 let project_path = buffer.read(cx).project_path(cx)?;
7116 let project = self.project.as_ref()?.read(cx);
7117 let entry = project.entry_for_path(&project_path, cx)?;
7118 let parent = match &entry.canonical_path {
7119 Some(canonical_path) => canonical_path.to_path_buf(),
7120 None => project.absolute_path(&project_path, cx)?,
7121 }
7122 .parent()?
7123 .to_path_buf();
7124 Some(parent)
7125 }) {
7126 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7127 }
7128 }
7129
7130 pub fn prepare_restore_change(
7131 &self,
7132 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7133 hunk: &MultiBufferDiffHunk,
7134 cx: &mut App,
7135 ) -> Option<()> {
7136 let buffer = self.buffer.read(cx);
7137 let diff = buffer.diff_for(hunk.buffer_id)?;
7138 let buffer = buffer.buffer(hunk.buffer_id)?;
7139 let buffer = buffer.read(cx);
7140 let original_text = diff
7141 .read(cx)
7142 .base_text()
7143 .as_ref()?
7144 .as_rope()
7145 .slice(hunk.diff_base_byte_range.clone());
7146 let buffer_snapshot = buffer.snapshot();
7147 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7148 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7149 probe
7150 .0
7151 .start
7152 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7153 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7154 }) {
7155 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7156 Some(())
7157 } else {
7158 None
7159 }
7160 }
7161
7162 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7163 self.manipulate_lines(window, cx, |lines| lines.reverse())
7164 }
7165
7166 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7167 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7168 }
7169
7170 fn manipulate_lines<Fn>(
7171 &mut self,
7172 window: &mut Window,
7173 cx: &mut Context<Self>,
7174 mut callback: Fn,
7175 ) where
7176 Fn: FnMut(&mut Vec<&str>),
7177 {
7178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7179 let buffer = self.buffer.read(cx).snapshot(cx);
7180
7181 let mut edits = Vec::new();
7182
7183 let selections = self.selections.all::<Point>(cx);
7184 let mut selections = selections.iter().peekable();
7185 let mut contiguous_row_selections = Vec::new();
7186 let mut new_selections = Vec::new();
7187 let mut added_lines = 0;
7188 let mut removed_lines = 0;
7189
7190 while let Some(selection) = selections.next() {
7191 let (start_row, end_row) = consume_contiguous_rows(
7192 &mut contiguous_row_selections,
7193 selection,
7194 &display_map,
7195 &mut selections,
7196 );
7197
7198 let start_point = Point::new(start_row.0, 0);
7199 let end_point = Point::new(
7200 end_row.previous_row().0,
7201 buffer.line_len(end_row.previous_row()),
7202 );
7203 let text = buffer
7204 .text_for_range(start_point..end_point)
7205 .collect::<String>();
7206
7207 let mut lines = text.split('\n').collect_vec();
7208
7209 let lines_before = lines.len();
7210 callback(&mut lines);
7211 let lines_after = lines.len();
7212
7213 edits.push((start_point..end_point, lines.join("\n")));
7214
7215 // Selections must change based on added and removed line count
7216 let start_row =
7217 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7218 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7219 new_selections.push(Selection {
7220 id: selection.id,
7221 start: start_row,
7222 end: end_row,
7223 goal: SelectionGoal::None,
7224 reversed: selection.reversed,
7225 });
7226
7227 if lines_after > lines_before {
7228 added_lines += lines_after - lines_before;
7229 } else if lines_before > lines_after {
7230 removed_lines += lines_before - lines_after;
7231 }
7232 }
7233
7234 self.transact(window, cx, |this, window, cx| {
7235 let buffer = this.buffer.update(cx, |buffer, cx| {
7236 buffer.edit(edits, None, cx);
7237 buffer.snapshot(cx)
7238 });
7239
7240 // Recalculate offsets on newly edited buffer
7241 let new_selections = new_selections
7242 .iter()
7243 .map(|s| {
7244 let start_point = Point::new(s.start.0, 0);
7245 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7246 Selection {
7247 id: s.id,
7248 start: buffer.point_to_offset(start_point),
7249 end: buffer.point_to_offset(end_point),
7250 goal: s.goal,
7251 reversed: s.reversed,
7252 }
7253 })
7254 .collect();
7255
7256 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7257 s.select(new_selections);
7258 });
7259
7260 this.request_autoscroll(Autoscroll::fit(), cx);
7261 });
7262 }
7263
7264 pub fn convert_to_upper_case(
7265 &mut self,
7266 _: &ConvertToUpperCase,
7267 window: &mut Window,
7268 cx: &mut Context<Self>,
7269 ) {
7270 self.manipulate_text(window, cx, |text| text.to_uppercase())
7271 }
7272
7273 pub fn convert_to_lower_case(
7274 &mut self,
7275 _: &ConvertToLowerCase,
7276 window: &mut Window,
7277 cx: &mut Context<Self>,
7278 ) {
7279 self.manipulate_text(window, cx, |text| text.to_lowercase())
7280 }
7281
7282 pub fn convert_to_title_case(
7283 &mut self,
7284 _: &ConvertToTitleCase,
7285 window: &mut Window,
7286 cx: &mut Context<Self>,
7287 ) {
7288 self.manipulate_text(window, cx, |text| {
7289 text.split('\n')
7290 .map(|line| line.to_case(Case::Title))
7291 .join("\n")
7292 })
7293 }
7294
7295 pub fn convert_to_snake_case(
7296 &mut self,
7297 _: &ConvertToSnakeCase,
7298 window: &mut Window,
7299 cx: &mut Context<Self>,
7300 ) {
7301 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7302 }
7303
7304 pub fn convert_to_kebab_case(
7305 &mut self,
7306 _: &ConvertToKebabCase,
7307 window: &mut Window,
7308 cx: &mut Context<Self>,
7309 ) {
7310 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7311 }
7312
7313 pub fn convert_to_upper_camel_case(
7314 &mut self,
7315 _: &ConvertToUpperCamelCase,
7316 window: &mut Window,
7317 cx: &mut Context<Self>,
7318 ) {
7319 self.manipulate_text(window, cx, |text| {
7320 text.split('\n')
7321 .map(|line| line.to_case(Case::UpperCamel))
7322 .join("\n")
7323 })
7324 }
7325
7326 pub fn convert_to_lower_camel_case(
7327 &mut self,
7328 _: &ConvertToLowerCamelCase,
7329 window: &mut Window,
7330 cx: &mut Context<Self>,
7331 ) {
7332 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7333 }
7334
7335 pub fn convert_to_opposite_case(
7336 &mut self,
7337 _: &ConvertToOppositeCase,
7338 window: &mut Window,
7339 cx: &mut Context<Self>,
7340 ) {
7341 self.manipulate_text(window, cx, |text| {
7342 text.chars()
7343 .fold(String::with_capacity(text.len()), |mut t, c| {
7344 if c.is_uppercase() {
7345 t.extend(c.to_lowercase());
7346 } else {
7347 t.extend(c.to_uppercase());
7348 }
7349 t
7350 })
7351 })
7352 }
7353
7354 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7355 where
7356 Fn: FnMut(&str) -> String,
7357 {
7358 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7359 let buffer = self.buffer.read(cx).snapshot(cx);
7360
7361 let mut new_selections = Vec::new();
7362 let mut edits = Vec::new();
7363 let mut selection_adjustment = 0i32;
7364
7365 for selection in self.selections.all::<usize>(cx) {
7366 let selection_is_empty = selection.is_empty();
7367
7368 let (start, end) = if selection_is_empty {
7369 let word_range = movement::surrounding_word(
7370 &display_map,
7371 selection.start.to_display_point(&display_map),
7372 );
7373 let start = word_range.start.to_offset(&display_map, Bias::Left);
7374 let end = word_range.end.to_offset(&display_map, Bias::Left);
7375 (start, end)
7376 } else {
7377 (selection.start, selection.end)
7378 };
7379
7380 let text = buffer.text_for_range(start..end).collect::<String>();
7381 let old_length = text.len() as i32;
7382 let text = callback(&text);
7383
7384 new_selections.push(Selection {
7385 start: (start as i32 - selection_adjustment) as usize,
7386 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7387 goal: SelectionGoal::None,
7388 ..selection
7389 });
7390
7391 selection_adjustment += old_length - text.len() as i32;
7392
7393 edits.push((start..end, text));
7394 }
7395
7396 self.transact(window, cx, |this, window, cx| {
7397 this.buffer.update(cx, |buffer, cx| {
7398 buffer.edit(edits, None, cx);
7399 });
7400
7401 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7402 s.select(new_selections);
7403 });
7404
7405 this.request_autoscroll(Autoscroll::fit(), cx);
7406 });
7407 }
7408
7409 pub fn duplicate(
7410 &mut self,
7411 upwards: bool,
7412 whole_lines: bool,
7413 window: &mut Window,
7414 cx: &mut Context<Self>,
7415 ) {
7416 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7417 let buffer = &display_map.buffer_snapshot;
7418 let selections = self.selections.all::<Point>(cx);
7419
7420 let mut edits = Vec::new();
7421 let mut selections_iter = selections.iter().peekable();
7422 while let Some(selection) = selections_iter.next() {
7423 let mut rows = selection.spanned_rows(false, &display_map);
7424 // duplicate line-wise
7425 if whole_lines || selection.start == selection.end {
7426 // Avoid duplicating the same lines twice.
7427 while let Some(next_selection) = selections_iter.peek() {
7428 let next_rows = next_selection.spanned_rows(false, &display_map);
7429 if next_rows.start < rows.end {
7430 rows.end = next_rows.end;
7431 selections_iter.next().unwrap();
7432 } else {
7433 break;
7434 }
7435 }
7436
7437 // Copy the text from the selected row region and splice it either at the start
7438 // or end of the region.
7439 let start = Point::new(rows.start.0, 0);
7440 let end = Point::new(
7441 rows.end.previous_row().0,
7442 buffer.line_len(rows.end.previous_row()),
7443 );
7444 let text = buffer
7445 .text_for_range(start..end)
7446 .chain(Some("\n"))
7447 .collect::<String>();
7448 let insert_location = if upwards {
7449 Point::new(rows.end.0, 0)
7450 } else {
7451 start
7452 };
7453 edits.push((insert_location..insert_location, text));
7454 } else {
7455 // duplicate character-wise
7456 let start = selection.start;
7457 let end = selection.end;
7458 let text = buffer.text_for_range(start..end).collect::<String>();
7459 edits.push((selection.end..selection.end, text));
7460 }
7461 }
7462
7463 self.transact(window, cx, |this, _, cx| {
7464 this.buffer.update(cx, |buffer, cx| {
7465 buffer.edit(edits, None, cx);
7466 });
7467
7468 this.request_autoscroll(Autoscroll::fit(), cx);
7469 });
7470 }
7471
7472 pub fn duplicate_line_up(
7473 &mut self,
7474 _: &DuplicateLineUp,
7475 window: &mut Window,
7476 cx: &mut Context<Self>,
7477 ) {
7478 self.duplicate(true, true, window, cx);
7479 }
7480
7481 pub fn duplicate_line_down(
7482 &mut self,
7483 _: &DuplicateLineDown,
7484 window: &mut Window,
7485 cx: &mut Context<Self>,
7486 ) {
7487 self.duplicate(false, true, window, cx);
7488 }
7489
7490 pub fn duplicate_selection(
7491 &mut self,
7492 _: &DuplicateSelection,
7493 window: &mut Window,
7494 cx: &mut Context<Self>,
7495 ) {
7496 self.duplicate(false, false, window, cx);
7497 }
7498
7499 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7500 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7501 let buffer = self.buffer.read(cx).snapshot(cx);
7502
7503 let mut edits = Vec::new();
7504 let mut unfold_ranges = Vec::new();
7505 let mut refold_creases = Vec::new();
7506
7507 let selections = self.selections.all::<Point>(cx);
7508 let mut selections = selections.iter().peekable();
7509 let mut contiguous_row_selections = Vec::new();
7510 let mut new_selections = Vec::new();
7511
7512 while let Some(selection) = selections.next() {
7513 // Find all the selections that span a contiguous row range
7514 let (start_row, end_row) = consume_contiguous_rows(
7515 &mut contiguous_row_selections,
7516 selection,
7517 &display_map,
7518 &mut selections,
7519 );
7520
7521 // Move the text spanned by the row range to be before the line preceding the row range
7522 if start_row.0 > 0 {
7523 let range_to_move = Point::new(
7524 start_row.previous_row().0,
7525 buffer.line_len(start_row.previous_row()),
7526 )
7527 ..Point::new(
7528 end_row.previous_row().0,
7529 buffer.line_len(end_row.previous_row()),
7530 );
7531 let insertion_point = display_map
7532 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7533 .0;
7534
7535 // Don't move lines across excerpts
7536 if buffer
7537 .excerpt_containing(insertion_point..range_to_move.end)
7538 .is_some()
7539 {
7540 let text = buffer
7541 .text_for_range(range_to_move.clone())
7542 .flat_map(|s| s.chars())
7543 .skip(1)
7544 .chain(['\n'])
7545 .collect::<String>();
7546
7547 edits.push((
7548 buffer.anchor_after(range_to_move.start)
7549 ..buffer.anchor_before(range_to_move.end),
7550 String::new(),
7551 ));
7552 let insertion_anchor = buffer.anchor_after(insertion_point);
7553 edits.push((insertion_anchor..insertion_anchor, text));
7554
7555 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7556
7557 // Move selections up
7558 new_selections.extend(contiguous_row_selections.drain(..).map(
7559 |mut selection| {
7560 selection.start.row -= row_delta;
7561 selection.end.row -= row_delta;
7562 selection
7563 },
7564 ));
7565
7566 // Move folds up
7567 unfold_ranges.push(range_to_move.clone());
7568 for fold in display_map.folds_in_range(
7569 buffer.anchor_before(range_to_move.start)
7570 ..buffer.anchor_after(range_to_move.end),
7571 ) {
7572 let mut start = fold.range.start.to_point(&buffer);
7573 let mut end = fold.range.end.to_point(&buffer);
7574 start.row -= row_delta;
7575 end.row -= row_delta;
7576 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7577 }
7578 }
7579 }
7580
7581 // If we didn't move line(s), preserve the existing selections
7582 new_selections.append(&mut contiguous_row_selections);
7583 }
7584
7585 self.transact(window, cx, |this, window, cx| {
7586 this.unfold_ranges(&unfold_ranges, true, true, cx);
7587 this.buffer.update(cx, |buffer, cx| {
7588 for (range, text) in edits {
7589 buffer.edit([(range, text)], None, cx);
7590 }
7591 });
7592 this.fold_creases(refold_creases, true, window, cx);
7593 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7594 s.select(new_selections);
7595 })
7596 });
7597 }
7598
7599 pub fn move_line_down(
7600 &mut self,
7601 _: &MoveLineDown,
7602 window: &mut Window,
7603 cx: &mut Context<Self>,
7604 ) {
7605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7606 let buffer = self.buffer.read(cx).snapshot(cx);
7607
7608 let mut edits = Vec::new();
7609 let mut unfold_ranges = Vec::new();
7610 let mut refold_creases = Vec::new();
7611
7612 let selections = self.selections.all::<Point>(cx);
7613 let mut selections = selections.iter().peekable();
7614 let mut contiguous_row_selections = Vec::new();
7615 let mut new_selections = Vec::new();
7616
7617 while let Some(selection) = selections.next() {
7618 // Find all the selections that span a contiguous row range
7619 let (start_row, end_row) = consume_contiguous_rows(
7620 &mut contiguous_row_selections,
7621 selection,
7622 &display_map,
7623 &mut selections,
7624 );
7625
7626 // Move the text spanned by the row range to be after the last line of the row range
7627 if end_row.0 <= buffer.max_point().row {
7628 let range_to_move =
7629 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7630 let insertion_point = display_map
7631 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7632 .0;
7633
7634 // Don't move lines across excerpt boundaries
7635 if buffer
7636 .excerpt_containing(range_to_move.start..insertion_point)
7637 .is_some()
7638 {
7639 let mut text = String::from("\n");
7640 text.extend(buffer.text_for_range(range_to_move.clone()));
7641 text.pop(); // Drop trailing newline
7642 edits.push((
7643 buffer.anchor_after(range_to_move.start)
7644 ..buffer.anchor_before(range_to_move.end),
7645 String::new(),
7646 ));
7647 let insertion_anchor = buffer.anchor_after(insertion_point);
7648 edits.push((insertion_anchor..insertion_anchor, text));
7649
7650 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7651
7652 // Move selections down
7653 new_selections.extend(contiguous_row_selections.drain(..).map(
7654 |mut selection| {
7655 selection.start.row += row_delta;
7656 selection.end.row += row_delta;
7657 selection
7658 },
7659 ));
7660
7661 // Move folds down
7662 unfold_ranges.push(range_to_move.clone());
7663 for fold in display_map.folds_in_range(
7664 buffer.anchor_before(range_to_move.start)
7665 ..buffer.anchor_after(range_to_move.end),
7666 ) {
7667 let mut start = fold.range.start.to_point(&buffer);
7668 let mut end = fold.range.end.to_point(&buffer);
7669 start.row += row_delta;
7670 end.row += row_delta;
7671 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7672 }
7673 }
7674 }
7675
7676 // If we didn't move line(s), preserve the existing selections
7677 new_selections.append(&mut contiguous_row_selections);
7678 }
7679
7680 self.transact(window, cx, |this, window, cx| {
7681 this.unfold_ranges(&unfold_ranges, true, true, cx);
7682 this.buffer.update(cx, |buffer, cx| {
7683 for (range, text) in edits {
7684 buffer.edit([(range, text)], None, cx);
7685 }
7686 });
7687 this.fold_creases(refold_creases, true, window, cx);
7688 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7689 s.select(new_selections)
7690 });
7691 });
7692 }
7693
7694 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7695 let text_layout_details = &self.text_layout_details(window);
7696 self.transact(window, cx, |this, window, cx| {
7697 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7698 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7699 let line_mode = s.line_mode;
7700 s.move_with(|display_map, selection| {
7701 if !selection.is_empty() || line_mode {
7702 return;
7703 }
7704
7705 let mut head = selection.head();
7706 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7707 if head.column() == display_map.line_len(head.row()) {
7708 transpose_offset = display_map
7709 .buffer_snapshot
7710 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7711 }
7712
7713 if transpose_offset == 0 {
7714 return;
7715 }
7716
7717 *head.column_mut() += 1;
7718 head = display_map.clip_point(head, Bias::Right);
7719 let goal = SelectionGoal::HorizontalPosition(
7720 display_map
7721 .x_for_display_point(head, text_layout_details)
7722 .into(),
7723 );
7724 selection.collapse_to(head, goal);
7725
7726 let transpose_start = display_map
7727 .buffer_snapshot
7728 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7729 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7730 let transpose_end = display_map
7731 .buffer_snapshot
7732 .clip_offset(transpose_offset + 1, Bias::Right);
7733 if let Some(ch) =
7734 display_map.buffer_snapshot.chars_at(transpose_start).next()
7735 {
7736 edits.push((transpose_start..transpose_offset, String::new()));
7737 edits.push((transpose_end..transpose_end, ch.to_string()));
7738 }
7739 }
7740 });
7741 edits
7742 });
7743 this.buffer
7744 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7745 let selections = this.selections.all::<usize>(cx);
7746 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7747 s.select(selections);
7748 });
7749 });
7750 }
7751
7752 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7753 self.rewrap_impl(IsVimMode::No, cx)
7754 }
7755
7756 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7757 let buffer = self.buffer.read(cx).snapshot(cx);
7758 let selections = self.selections.all::<Point>(cx);
7759 let mut selections = selections.iter().peekable();
7760
7761 let mut edits = Vec::new();
7762 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7763
7764 while let Some(selection) = selections.next() {
7765 let mut start_row = selection.start.row;
7766 let mut end_row = selection.end.row;
7767
7768 // Skip selections that overlap with a range that has already been rewrapped.
7769 let selection_range = start_row..end_row;
7770 if rewrapped_row_ranges
7771 .iter()
7772 .any(|range| range.overlaps(&selection_range))
7773 {
7774 continue;
7775 }
7776
7777 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7778
7779 // Since not all lines in the selection may be at the same indent
7780 // level, choose the indent size that is the most common between all
7781 // of the lines.
7782 //
7783 // If there is a tie, we use the deepest indent.
7784 let (indent_size, indent_end) = {
7785 let mut indent_size_occurrences = HashMap::default();
7786 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7787
7788 for row in start_row..=end_row {
7789 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7790 rows_by_indent_size.entry(indent).or_default().push(row);
7791 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7792 }
7793
7794 let indent_size = indent_size_occurrences
7795 .into_iter()
7796 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7797 .map(|(indent, _)| indent)
7798 .unwrap_or_default();
7799 let row = rows_by_indent_size[&indent_size][0];
7800 let indent_end = Point::new(row, indent_size.len);
7801
7802 (indent_size, indent_end)
7803 };
7804
7805 let mut line_prefix = indent_size.chars().collect::<String>();
7806
7807 let mut inside_comment = false;
7808 if let Some(comment_prefix) =
7809 buffer
7810 .language_scope_at(selection.head())
7811 .and_then(|language| {
7812 language
7813 .line_comment_prefixes()
7814 .iter()
7815 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7816 .cloned()
7817 })
7818 {
7819 line_prefix.push_str(&comment_prefix);
7820 inside_comment = true;
7821 }
7822
7823 let language_settings = buffer.settings_at(selection.head(), cx);
7824 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
7825 RewrapBehavior::InComments => inside_comment,
7826 RewrapBehavior::InSelections => !selection.is_empty(),
7827 RewrapBehavior::Anywhere => true,
7828 };
7829
7830 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
7831 if !should_rewrap {
7832 continue;
7833 }
7834
7835 if selection.is_empty() {
7836 'expand_upwards: while start_row > 0 {
7837 let prev_row = start_row - 1;
7838 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7839 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7840 {
7841 start_row = prev_row;
7842 } else {
7843 break 'expand_upwards;
7844 }
7845 }
7846
7847 'expand_downwards: while end_row < buffer.max_point().row {
7848 let next_row = end_row + 1;
7849 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7850 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7851 {
7852 end_row = next_row;
7853 } else {
7854 break 'expand_downwards;
7855 }
7856 }
7857 }
7858
7859 let start = Point::new(start_row, 0);
7860 let start_offset = start.to_offset(&buffer);
7861 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7862 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7863 let Some(lines_without_prefixes) = selection_text
7864 .lines()
7865 .map(|line| {
7866 line.strip_prefix(&line_prefix)
7867 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7868 .ok_or_else(|| {
7869 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7870 })
7871 })
7872 .collect::<Result<Vec<_>, _>>()
7873 .log_err()
7874 else {
7875 continue;
7876 };
7877
7878 let wrap_column = buffer
7879 .settings_at(Point::new(start_row, 0), cx)
7880 .preferred_line_length as usize;
7881 let wrapped_text = wrap_with_prefix(
7882 line_prefix,
7883 lines_without_prefixes.join(" "),
7884 wrap_column,
7885 tab_size,
7886 );
7887
7888 // TODO: should always use char-based diff while still supporting cursor behavior that
7889 // matches vim.
7890 let mut diff_options = DiffOptions::default();
7891 if is_vim_mode == IsVimMode::Yes {
7892 diff_options.max_word_diff_len = 0;
7893 diff_options.max_word_diff_line_count = 0;
7894 } else {
7895 diff_options.max_word_diff_len = usize::MAX;
7896 diff_options.max_word_diff_line_count = usize::MAX;
7897 }
7898
7899 for (old_range, new_text) in
7900 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
7901 {
7902 let edit_start = buffer.anchor_after(start_offset + old_range.start);
7903 let edit_end = buffer.anchor_after(start_offset + old_range.end);
7904 edits.push((edit_start..edit_end, new_text));
7905 }
7906
7907 rewrapped_row_ranges.push(start_row..=end_row);
7908 }
7909
7910 self.buffer
7911 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7912 }
7913
7914 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7915 let mut text = String::new();
7916 let buffer = self.buffer.read(cx).snapshot(cx);
7917 let mut selections = self.selections.all::<Point>(cx);
7918 let mut clipboard_selections = Vec::with_capacity(selections.len());
7919 {
7920 let max_point = buffer.max_point();
7921 let mut is_first = true;
7922 for selection in &mut selections {
7923 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7924 if is_entire_line {
7925 selection.start = Point::new(selection.start.row, 0);
7926 if !selection.is_empty() && selection.end.column == 0 {
7927 selection.end = cmp::min(max_point, selection.end);
7928 } else {
7929 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7930 }
7931 selection.goal = SelectionGoal::None;
7932 }
7933 if is_first {
7934 is_first = false;
7935 } else {
7936 text += "\n";
7937 }
7938 let mut len = 0;
7939 for chunk in buffer.text_for_range(selection.start..selection.end) {
7940 text.push_str(chunk);
7941 len += chunk.len();
7942 }
7943 clipboard_selections.push(ClipboardSelection {
7944 len,
7945 is_entire_line,
7946 first_line_indent: buffer
7947 .indent_size_for_line(MultiBufferRow(selection.start.row))
7948 .len,
7949 });
7950 }
7951 }
7952
7953 self.transact(window, cx, |this, window, cx| {
7954 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7955 s.select(selections);
7956 });
7957 this.insert("", window, cx);
7958 });
7959 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7960 }
7961
7962 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7963 let item = self.cut_common(window, cx);
7964 cx.write_to_clipboard(item);
7965 }
7966
7967 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7968 self.change_selections(None, window, cx, |s| {
7969 s.move_with(|snapshot, sel| {
7970 if sel.is_empty() {
7971 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7972 }
7973 });
7974 });
7975 let item = self.cut_common(window, cx);
7976 cx.set_global(KillRing(item))
7977 }
7978
7979 pub fn kill_ring_yank(
7980 &mut self,
7981 _: &KillRingYank,
7982 window: &mut Window,
7983 cx: &mut Context<Self>,
7984 ) {
7985 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7986 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7987 (kill_ring.text().to_string(), kill_ring.metadata_json())
7988 } else {
7989 return;
7990 }
7991 } else {
7992 return;
7993 };
7994 self.do_paste(&text, metadata, false, window, cx);
7995 }
7996
7997 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7998 let selections = self.selections.all::<Point>(cx);
7999 let buffer = self.buffer.read(cx).read(cx);
8000 let mut text = String::new();
8001
8002 let mut clipboard_selections = Vec::with_capacity(selections.len());
8003 {
8004 let max_point = buffer.max_point();
8005 let mut is_first = true;
8006 for selection in selections.iter() {
8007 let mut start = selection.start;
8008 let mut end = selection.end;
8009 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8010 if is_entire_line {
8011 start = Point::new(start.row, 0);
8012 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8013 }
8014 if is_first {
8015 is_first = false;
8016 } else {
8017 text += "\n";
8018 }
8019 let mut len = 0;
8020 for chunk in buffer.text_for_range(start..end) {
8021 text.push_str(chunk);
8022 len += chunk.len();
8023 }
8024 clipboard_selections.push(ClipboardSelection {
8025 len,
8026 is_entire_line,
8027 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8028 });
8029 }
8030 }
8031
8032 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8033 text,
8034 clipboard_selections,
8035 ));
8036 }
8037
8038 pub fn do_paste(
8039 &mut self,
8040 text: &String,
8041 clipboard_selections: Option<Vec<ClipboardSelection>>,
8042 handle_entire_lines: bool,
8043 window: &mut Window,
8044 cx: &mut Context<Self>,
8045 ) {
8046 if self.read_only(cx) {
8047 return;
8048 }
8049
8050 let clipboard_text = Cow::Borrowed(text);
8051
8052 self.transact(window, cx, |this, window, cx| {
8053 if let Some(mut clipboard_selections) = clipboard_selections {
8054 let old_selections = this.selections.all::<usize>(cx);
8055 let all_selections_were_entire_line =
8056 clipboard_selections.iter().all(|s| s.is_entire_line);
8057 let first_selection_indent_column =
8058 clipboard_selections.first().map(|s| s.first_line_indent);
8059 if clipboard_selections.len() != old_selections.len() {
8060 clipboard_selections.drain(..);
8061 }
8062 let cursor_offset = this.selections.last::<usize>(cx).head();
8063 let mut auto_indent_on_paste = true;
8064
8065 this.buffer.update(cx, |buffer, cx| {
8066 let snapshot = buffer.read(cx);
8067 auto_indent_on_paste =
8068 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8069
8070 let mut start_offset = 0;
8071 let mut edits = Vec::new();
8072 let mut original_indent_columns = Vec::new();
8073 for (ix, selection) in old_selections.iter().enumerate() {
8074 let to_insert;
8075 let entire_line;
8076 let original_indent_column;
8077 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8078 let end_offset = start_offset + clipboard_selection.len;
8079 to_insert = &clipboard_text[start_offset..end_offset];
8080 entire_line = clipboard_selection.is_entire_line;
8081 start_offset = end_offset + 1;
8082 original_indent_column = Some(clipboard_selection.first_line_indent);
8083 } else {
8084 to_insert = clipboard_text.as_str();
8085 entire_line = all_selections_were_entire_line;
8086 original_indent_column = first_selection_indent_column
8087 }
8088
8089 // If the corresponding selection was empty when this slice of the
8090 // clipboard text was written, then the entire line containing the
8091 // selection was copied. If this selection is also currently empty,
8092 // then paste the line before the current line of the buffer.
8093 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8094 let column = selection.start.to_point(&snapshot).column as usize;
8095 let line_start = selection.start - column;
8096 line_start..line_start
8097 } else {
8098 selection.range()
8099 };
8100
8101 edits.push((range, to_insert));
8102 original_indent_columns.extend(original_indent_column);
8103 }
8104 drop(snapshot);
8105
8106 buffer.edit(
8107 edits,
8108 if auto_indent_on_paste {
8109 Some(AutoindentMode::Block {
8110 original_indent_columns,
8111 })
8112 } else {
8113 None
8114 },
8115 cx,
8116 );
8117 });
8118
8119 let selections = this.selections.all::<usize>(cx);
8120 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8121 s.select(selections)
8122 });
8123 } else {
8124 this.insert(&clipboard_text, window, cx);
8125 }
8126 });
8127 }
8128
8129 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8130 if let Some(item) = cx.read_from_clipboard() {
8131 let entries = item.entries();
8132
8133 match entries.first() {
8134 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8135 // of all the pasted entries.
8136 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8137 .do_paste(
8138 clipboard_string.text(),
8139 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8140 true,
8141 window,
8142 cx,
8143 ),
8144 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8145 }
8146 }
8147 }
8148
8149 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8150 if self.read_only(cx) {
8151 return;
8152 }
8153
8154 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8155 if let Some((selections, _)) =
8156 self.selection_history.transaction(transaction_id).cloned()
8157 {
8158 self.change_selections(None, window, cx, |s| {
8159 s.select_anchors(selections.to_vec());
8160 });
8161 }
8162 self.request_autoscroll(Autoscroll::fit(), cx);
8163 self.unmark_text(window, cx);
8164 self.refresh_inline_completion(true, false, window, cx);
8165 cx.emit(EditorEvent::Edited { transaction_id });
8166 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8167 }
8168 }
8169
8170 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8171 if self.read_only(cx) {
8172 return;
8173 }
8174
8175 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8176 if let Some((_, Some(selections))) =
8177 self.selection_history.transaction(transaction_id).cloned()
8178 {
8179 self.change_selections(None, window, cx, |s| {
8180 s.select_anchors(selections.to_vec());
8181 });
8182 }
8183 self.request_autoscroll(Autoscroll::fit(), cx);
8184 self.unmark_text(window, cx);
8185 self.refresh_inline_completion(true, false, window, cx);
8186 cx.emit(EditorEvent::Edited { transaction_id });
8187 }
8188 }
8189
8190 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8191 self.buffer
8192 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8193 }
8194
8195 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8196 self.buffer
8197 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8198 }
8199
8200 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8201 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8202 let line_mode = s.line_mode;
8203 s.move_with(|map, selection| {
8204 let cursor = if selection.is_empty() && !line_mode {
8205 movement::left(map, selection.start)
8206 } else {
8207 selection.start
8208 };
8209 selection.collapse_to(cursor, SelectionGoal::None);
8210 });
8211 })
8212 }
8213
8214 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8215 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8216 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8217 })
8218 }
8219
8220 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8221 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8222 let line_mode = s.line_mode;
8223 s.move_with(|map, selection| {
8224 let cursor = if selection.is_empty() && !line_mode {
8225 movement::right(map, selection.end)
8226 } else {
8227 selection.end
8228 };
8229 selection.collapse_to(cursor, SelectionGoal::None)
8230 });
8231 })
8232 }
8233
8234 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8235 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8236 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8237 })
8238 }
8239
8240 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8241 if self.take_rename(true, window, cx).is_some() {
8242 return;
8243 }
8244
8245 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8246 cx.propagate();
8247 return;
8248 }
8249
8250 let text_layout_details = &self.text_layout_details(window);
8251 let selection_count = self.selections.count();
8252 let first_selection = self.selections.first_anchor();
8253
8254 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8255 let line_mode = s.line_mode;
8256 s.move_with(|map, selection| {
8257 if !selection.is_empty() && !line_mode {
8258 selection.goal = SelectionGoal::None;
8259 }
8260 let (cursor, goal) = movement::up(
8261 map,
8262 selection.start,
8263 selection.goal,
8264 false,
8265 text_layout_details,
8266 );
8267 selection.collapse_to(cursor, goal);
8268 });
8269 });
8270
8271 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8272 {
8273 cx.propagate();
8274 }
8275 }
8276
8277 pub fn move_up_by_lines(
8278 &mut self,
8279 action: &MoveUpByLines,
8280 window: &mut Window,
8281 cx: &mut Context<Self>,
8282 ) {
8283 if self.take_rename(true, window, cx).is_some() {
8284 return;
8285 }
8286
8287 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8288 cx.propagate();
8289 return;
8290 }
8291
8292 let text_layout_details = &self.text_layout_details(window);
8293
8294 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8295 let line_mode = s.line_mode;
8296 s.move_with(|map, selection| {
8297 if !selection.is_empty() && !line_mode {
8298 selection.goal = SelectionGoal::None;
8299 }
8300 let (cursor, goal) = movement::up_by_rows(
8301 map,
8302 selection.start,
8303 action.lines,
8304 selection.goal,
8305 false,
8306 text_layout_details,
8307 );
8308 selection.collapse_to(cursor, goal);
8309 });
8310 })
8311 }
8312
8313 pub fn move_down_by_lines(
8314 &mut self,
8315 action: &MoveDownByLines,
8316 window: &mut Window,
8317 cx: &mut Context<Self>,
8318 ) {
8319 if self.take_rename(true, window, cx).is_some() {
8320 return;
8321 }
8322
8323 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8324 cx.propagate();
8325 return;
8326 }
8327
8328 let text_layout_details = &self.text_layout_details(window);
8329
8330 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8331 let line_mode = s.line_mode;
8332 s.move_with(|map, selection| {
8333 if !selection.is_empty() && !line_mode {
8334 selection.goal = SelectionGoal::None;
8335 }
8336 let (cursor, goal) = movement::down_by_rows(
8337 map,
8338 selection.start,
8339 action.lines,
8340 selection.goal,
8341 false,
8342 text_layout_details,
8343 );
8344 selection.collapse_to(cursor, goal);
8345 });
8346 })
8347 }
8348
8349 pub fn select_down_by_lines(
8350 &mut self,
8351 action: &SelectDownByLines,
8352 window: &mut Window,
8353 cx: &mut Context<Self>,
8354 ) {
8355 let text_layout_details = &self.text_layout_details(window);
8356 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8357 s.move_heads_with(|map, head, goal| {
8358 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8359 })
8360 })
8361 }
8362
8363 pub fn select_up_by_lines(
8364 &mut self,
8365 action: &SelectUpByLines,
8366 window: &mut Window,
8367 cx: &mut Context<Self>,
8368 ) {
8369 let text_layout_details = &self.text_layout_details(window);
8370 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8371 s.move_heads_with(|map, head, goal| {
8372 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8373 })
8374 })
8375 }
8376
8377 pub fn select_page_up(
8378 &mut self,
8379 _: &SelectPageUp,
8380 window: &mut Window,
8381 cx: &mut Context<Self>,
8382 ) {
8383 let Some(row_count) = self.visible_row_count() else {
8384 return;
8385 };
8386
8387 let text_layout_details = &self.text_layout_details(window);
8388
8389 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8390 s.move_heads_with(|map, head, goal| {
8391 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8392 })
8393 })
8394 }
8395
8396 pub fn move_page_up(
8397 &mut self,
8398 action: &MovePageUp,
8399 window: &mut Window,
8400 cx: &mut Context<Self>,
8401 ) {
8402 if self.take_rename(true, window, cx).is_some() {
8403 return;
8404 }
8405
8406 if self
8407 .context_menu
8408 .borrow_mut()
8409 .as_mut()
8410 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8411 .unwrap_or(false)
8412 {
8413 return;
8414 }
8415
8416 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8417 cx.propagate();
8418 return;
8419 }
8420
8421 let Some(row_count) = self.visible_row_count() else {
8422 return;
8423 };
8424
8425 let autoscroll = if action.center_cursor {
8426 Autoscroll::center()
8427 } else {
8428 Autoscroll::fit()
8429 };
8430
8431 let text_layout_details = &self.text_layout_details(window);
8432
8433 self.change_selections(Some(autoscroll), window, cx, |s| {
8434 let line_mode = s.line_mode;
8435 s.move_with(|map, selection| {
8436 if !selection.is_empty() && !line_mode {
8437 selection.goal = SelectionGoal::None;
8438 }
8439 let (cursor, goal) = movement::up_by_rows(
8440 map,
8441 selection.end,
8442 row_count,
8443 selection.goal,
8444 false,
8445 text_layout_details,
8446 );
8447 selection.collapse_to(cursor, goal);
8448 });
8449 });
8450 }
8451
8452 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8453 let text_layout_details = &self.text_layout_details(window);
8454 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8455 s.move_heads_with(|map, head, goal| {
8456 movement::up(map, head, goal, false, text_layout_details)
8457 })
8458 })
8459 }
8460
8461 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8462 self.take_rename(true, window, cx);
8463
8464 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8465 cx.propagate();
8466 return;
8467 }
8468
8469 let text_layout_details = &self.text_layout_details(window);
8470 let selection_count = self.selections.count();
8471 let first_selection = self.selections.first_anchor();
8472
8473 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8474 let line_mode = s.line_mode;
8475 s.move_with(|map, selection| {
8476 if !selection.is_empty() && !line_mode {
8477 selection.goal = SelectionGoal::None;
8478 }
8479 let (cursor, goal) = movement::down(
8480 map,
8481 selection.end,
8482 selection.goal,
8483 false,
8484 text_layout_details,
8485 );
8486 selection.collapse_to(cursor, goal);
8487 });
8488 });
8489
8490 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8491 {
8492 cx.propagate();
8493 }
8494 }
8495
8496 pub fn select_page_down(
8497 &mut self,
8498 _: &SelectPageDown,
8499 window: &mut Window,
8500 cx: &mut Context<Self>,
8501 ) {
8502 let Some(row_count) = self.visible_row_count() else {
8503 return;
8504 };
8505
8506 let text_layout_details = &self.text_layout_details(window);
8507
8508 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8509 s.move_heads_with(|map, head, goal| {
8510 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8511 })
8512 })
8513 }
8514
8515 pub fn move_page_down(
8516 &mut self,
8517 action: &MovePageDown,
8518 window: &mut Window,
8519 cx: &mut Context<Self>,
8520 ) {
8521 if self.take_rename(true, window, cx).is_some() {
8522 return;
8523 }
8524
8525 if self
8526 .context_menu
8527 .borrow_mut()
8528 .as_mut()
8529 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8530 .unwrap_or(false)
8531 {
8532 return;
8533 }
8534
8535 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8536 cx.propagate();
8537 return;
8538 }
8539
8540 let Some(row_count) = self.visible_row_count() else {
8541 return;
8542 };
8543
8544 let autoscroll = if action.center_cursor {
8545 Autoscroll::center()
8546 } else {
8547 Autoscroll::fit()
8548 };
8549
8550 let text_layout_details = &self.text_layout_details(window);
8551 self.change_selections(Some(autoscroll), window, cx, |s| {
8552 let line_mode = s.line_mode;
8553 s.move_with(|map, selection| {
8554 if !selection.is_empty() && !line_mode {
8555 selection.goal = SelectionGoal::None;
8556 }
8557 let (cursor, goal) = movement::down_by_rows(
8558 map,
8559 selection.end,
8560 row_count,
8561 selection.goal,
8562 false,
8563 text_layout_details,
8564 );
8565 selection.collapse_to(cursor, goal);
8566 });
8567 });
8568 }
8569
8570 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8571 let text_layout_details = &self.text_layout_details(window);
8572 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8573 s.move_heads_with(|map, head, goal| {
8574 movement::down(map, head, goal, false, text_layout_details)
8575 })
8576 });
8577 }
8578
8579 pub fn context_menu_first(
8580 &mut self,
8581 _: &ContextMenuFirst,
8582 _window: &mut Window,
8583 cx: &mut Context<Self>,
8584 ) {
8585 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8586 context_menu.select_first(self.completion_provider.as_deref(), cx);
8587 }
8588 }
8589
8590 pub fn context_menu_prev(
8591 &mut self,
8592 _: &ContextMenuPrev,
8593 _window: &mut Window,
8594 cx: &mut Context<Self>,
8595 ) {
8596 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8597 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8598 }
8599 }
8600
8601 pub fn context_menu_next(
8602 &mut self,
8603 _: &ContextMenuNext,
8604 _window: &mut Window,
8605 cx: &mut Context<Self>,
8606 ) {
8607 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8608 context_menu.select_next(self.completion_provider.as_deref(), cx);
8609 }
8610 }
8611
8612 pub fn context_menu_last(
8613 &mut self,
8614 _: &ContextMenuLast,
8615 _window: &mut Window,
8616 cx: &mut Context<Self>,
8617 ) {
8618 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8619 context_menu.select_last(self.completion_provider.as_deref(), cx);
8620 }
8621 }
8622
8623 pub fn move_to_previous_word_start(
8624 &mut self,
8625 _: &MoveToPreviousWordStart,
8626 window: &mut Window,
8627 cx: &mut Context<Self>,
8628 ) {
8629 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8630 s.move_cursors_with(|map, head, _| {
8631 (
8632 movement::previous_word_start(map, head),
8633 SelectionGoal::None,
8634 )
8635 });
8636 })
8637 }
8638
8639 pub fn move_to_previous_subword_start(
8640 &mut self,
8641 _: &MoveToPreviousSubwordStart,
8642 window: &mut Window,
8643 cx: &mut Context<Self>,
8644 ) {
8645 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8646 s.move_cursors_with(|map, head, _| {
8647 (
8648 movement::previous_subword_start(map, head),
8649 SelectionGoal::None,
8650 )
8651 });
8652 })
8653 }
8654
8655 pub fn select_to_previous_word_start(
8656 &mut self,
8657 _: &SelectToPreviousWordStart,
8658 window: &mut Window,
8659 cx: &mut Context<Self>,
8660 ) {
8661 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8662 s.move_heads_with(|map, head, _| {
8663 (
8664 movement::previous_word_start(map, head),
8665 SelectionGoal::None,
8666 )
8667 });
8668 })
8669 }
8670
8671 pub fn select_to_previous_subword_start(
8672 &mut self,
8673 _: &SelectToPreviousSubwordStart,
8674 window: &mut Window,
8675 cx: &mut Context<Self>,
8676 ) {
8677 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8678 s.move_heads_with(|map, head, _| {
8679 (
8680 movement::previous_subword_start(map, head),
8681 SelectionGoal::None,
8682 )
8683 });
8684 })
8685 }
8686
8687 pub fn delete_to_previous_word_start(
8688 &mut self,
8689 action: &DeleteToPreviousWordStart,
8690 window: &mut Window,
8691 cx: &mut Context<Self>,
8692 ) {
8693 self.transact(window, cx, |this, window, cx| {
8694 this.select_autoclose_pair(window, cx);
8695 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8696 let line_mode = s.line_mode;
8697 s.move_with(|map, selection| {
8698 if selection.is_empty() && !line_mode {
8699 let cursor = if action.ignore_newlines {
8700 movement::previous_word_start(map, selection.head())
8701 } else {
8702 movement::previous_word_start_or_newline(map, selection.head())
8703 };
8704 selection.set_head(cursor, SelectionGoal::None);
8705 }
8706 });
8707 });
8708 this.insert("", window, cx);
8709 });
8710 }
8711
8712 pub fn delete_to_previous_subword_start(
8713 &mut self,
8714 _: &DeleteToPreviousSubwordStart,
8715 window: &mut Window,
8716 cx: &mut Context<Self>,
8717 ) {
8718 self.transact(window, cx, |this, window, cx| {
8719 this.select_autoclose_pair(window, cx);
8720 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8721 let line_mode = s.line_mode;
8722 s.move_with(|map, selection| {
8723 if selection.is_empty() && !line_mode {
8724 let cursor = movement::previous_subword_start(map, selection.head());
8725 selection.set_head(cursor, SelectionGoal::None);
8726 }
8727 });
8728 });
8729 this.insert("", window, cx);
8730 });
8731 }
8732
8733 pub fn move_to_next_word_end(
8734 &mut self,
8735 _: &MoveToNextWordEnd,
8736 window: &mut Window,
8737 cx: &mut Context<Self>,
8738 ) {
8739 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8740 s.move_cursors_with(|map, head, _| {
8741 (movement::next_word_end(map, head), SelectionGoal::None)
8742 });
8743 })
8744 }
8745
8746 pub fn move_to_next_subword_end(
8747 &mut self,
8748 _: &MoveToNextSubwordEnd,
8749 window: &mut Window,
8750 cx: &mut Context<Self>,
8751 ) {
8752 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8753 s.move_cursors_with(|map, head, _| {
8754 (movement::next_subword_end(map, head), SelectionGoal::None)
8755 });
8756 })
8757 }
8758
8759 pub fn select_to_next_word_end(
8760 &mut self,
8761 _: &SelectToNextWordEnd,
8762 window: &mut Window,
8763 cx: &mut Context<Self>,
8764 ) {
8765 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8766 s.move_heads_with(|map, head, _| {
8767 (movement::next_word_end(map, head), SelectionGoal::None)
8768 });
8769 })
8770 }
8771
8772 pub fn select_to_next_subword_end(
8773 &mut self,
8774 _: &SelectToNextSubwordEnd,
8775 window: &mut Window,
8776 cx: &mut Context<Self>,
8777 ) {
8778 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8779 s.move_heads_with(|map, head, _| {
8780 (movement::next_subword_end(map, head), SelectionGoal::None)
8781 });
8782 })
8783 }
8784
8785 pub fn delete_to_next_word_end(
8786 &mut self,
8787 action: &DeleteToNextWordEnd,
8788 window: &mut Window,
8789 cx: &mut Context<Self>,
8790 ) {
8791 self.transact(window, cx, |this, window, cx| {
8792 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8793 let line_mode = s.line_mode;
8794 s.move_with(|map, selection| {
8795 if selection.is_empty() && !line_mode {
8796 let cursor = if action.ignore_newlines {
8797 movement::next_word_end(map, selection.head())
8798 } else {
8799 movement::next_word_end_or_newline(map, selection.head())
8800 };
8801 selection.set_head(cursor, SelectionGoal::None);
8802 }
8803 });
8804 });
8805 this.insert("", window, cx);
8806 });
8807 }
8808
8809 pub fn delete_to_next_subword_end(
8810 &mut self,
8811 _: &DeleteToNextSubwordEnd,
8812 window: &mut Window,
8813 cx: &mut Context<Self>,
8814 ) {
8815 self.transact(window, cx, |this, window, cx| {
8816 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8817 s.move_with(|map, selection| {
8818 if selection.is_empty() {
8819 let cursor = movement::next_subword_end(map, selection.head());
8820 selection.set_head(cursor, SelectionGoal::None);
8821 }
8822 });
8823 });
8824 this.insert("", window, cx);
8825 });
8826 }
8827
8828 pub fn move_to_beginning_of_line(
8829 &mut self,
8830 action: &MoveToBeginningOfLine,
8831 window: &mut Window,
8832 cx: &mut Context<Self>,
8833 ) {
8834 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8835 s.move_cursors_with(|map, head, _| {
8836 (
8837 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8838 SelectionGoal::None,
8839 )
8840 });
8841 })
8842 }
8843
8844 pub fn select_to_beginning_of_line(
8845 &mut self,
8846 action: &SelectToBeginningOfLine,
8847 window: &mut Window,
8848 cx: &mut Context<Self>,
8849 ) {
8850 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8851 s.move_heads_with(|map, head, _| {
8852 (
8853 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8854 SelectionGoal::None,
8855 )
8856 });
8857 });
8858 }
8859
8860 pub fn delete_to_beginning_of_line(
8861 &mut self,
8862 _: &DeleteToBeginningOfLine,
8863 window: &mut Window,
8864 cx: &mut Context<Self>,
8865 ) {
8866 self.transact(window, cx, |this, window, cx| {
8867 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8868 s.move_with(|_, selection| {
8869 selection.reversed = true;
8870 });
8871 });
8872
8873 this.select_to_beginning_of_line(
8874 &SelectToBeginningOfLine {
8875 stop_at_soft_wraps: false,
8876 },
8877 window,
8878 cx,
8879 );
8880 this.backspace(&Backspace, window, cx);
8881 });
8882 }
8883
8884 pub fn move_to_end_of_line(
8885 &mut self,
8886 action: &MoveToEndOfLine,
8887 window: &mut Window,
8888 cx: &mut Context<Self>,
8889 ) {
8890 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8891 s.move_cursors_with(|map, head, _| {
8892 (
8893 movement::line_end(map, head, action.stop_at_soft_wraps),
8894 SelectionGoal::None,
8895 )
8896 });
8897 })
8898 }
8899
8900 pub fn select_to_end_of_line(
8901 &mut self,
8902 action: &SelectToEndOfLine,
8903 window: &mut Window,
8904 cx: &mut Context<Self>,
8905 ) {
8906 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8907 s.move_heads_with(|map, head, _| {
8908 (
8909 movement::line_end(map, head, action.stop_at_soft_wraps),
8910 SelectionGoal::None,
8911 )
8912 });
8913 })
8914 }
8915
8916 pub fn delete_to_end_of_line(
8917 &mut self,
8918 _: &DeleteToEndOfLine,
8919 window: &mut Window,
8920 cx: &mut Context<Self>,
8921 ) {
8922 self.transact(window, cx, |this, window, cx| {
8923 this.select_to_end_of_line(
8924 &SelectToEndOfLine {
8925 stop_at_soft_wraps: false,
8926 },
8927 window,
8928 cx,
8929 );
8930 this.delete(&Delete, window, cx);
8931 });
8932 }
8933
8934 pub fn cut_to_end_of_line(
8935 &mut self,
8936 _: &CutToEndOfLine,
8937 window: &mut Window,
8938 cx: &mut Context<Self>,
8939 ) {
8940 self.transact(window, cx, |this, window, cx| {
8941 this.select_to_end_of_line(
8942 &SelectToEndOfLine {
8943 stop_at_soft_wraps: false,
8944 },
8945 window,
8946 cx,
8947 );
8948 this.cut(&Cut, window, cx);
8949 });
8950 }
8951
8952 pub fn move_to_start_of_paragraph(
8953 &mut self,
8954 _: &MoveToStartOfParagraph,
8955 window: &mut Window,
8956 cx: &mut Context<Self>,
8957 ) {
8958 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8959 cx.propagate();
8960 return;
8961 }
8962
8963 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8964 s.move_with(|map, selection| {
8965 selection.collapse_to(
8966 movement::start_of_paragraph(map, selection.head(), 1),
8967 SelectionGoal::None,
8968 )
8969 });
8970 })
8971 }
8972
8973 pub fn move_to_end_of_paragraph(
8974 &mut self,
8975 _: &MoveToEndOfParagraph,
8976 window: &mut Window,
8977 cx: &mut Context<Self>,
8978 ) {
8979 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8980 cx.propagate();
8981 return;
8982 }
8983
8984 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8985 s.move_with(|map, selection| {
8986 selection.collapse_to(
8987 movement::end_of_paragraph(map, selection.head(), 1),
8988 SelectionGoal::None,
8989 )
8990 });
8991 })
8992 }
8993
8994 pub fn select_to_start_of_paragraph(
8995 &mut self,
8996 _: &SelectToStartOfParagraph,
8997 window: &mut Window,
8998 cx: &mut Context<Self>,
8999 ) {
9000 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9001 cx.propagate();
9002 return;
9003 }
9004
9005 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9006 s.move_heads_with(|map, head, _| {
9007 (
9008 movement::start_of_paragraph(map, head, 1),
9009 SelectionGoal::None,
9010 )
9011 });
9012 })
9013 }
9014
9015 pub fn select_to_end_of_paragraph(
9016 &mut self,
9017 _: &SelectToEndOfParagraph,
9018 window: &mut Window,
9019 cx: &mut Context<Self>,
9020 ) {
9021 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9022 cx.propagate();
9023 return;
9024 }
9025
9026 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9027 s.move_heads_with(|map, head, _| {
9028 (
9029 movement::end_of_paragraph(map, head, 1),
9030 SelectionGoal::None,
9031 )
9032 });
9033 })
9034 }
9035
9036 pub fn move_to_start_of_excerpt(
9037 &mut self,
9038 _: &MoveToStartOfExcerpt,
9039 window: &mut Window,
9040 cx: &mut Context<Self>,
9041 ) {
9042 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9043 cx.propagate();
9044 return;
9045 }
9046
9047 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9048 s.move_with(|map, selection| {
9049 selection.collapse_to(
9050 movement::start_of_excerpt(
9051 map,
9052 selection.head(),
9053 workspace::searchable::Direction::Prev,
9054 ),
9055 SelectionGoal::None,
9056 )
9057 });
9058 })
9059 }
9060
9061 pub fn move_to_end_of_excerpt(
9062 &mut self,
9063 _: &MoveToEndOfExcerpt,
9064 window: &mut Window,
9065 cx: &mut Context<Self>,
9066 ) {
9067 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9068 cx.propagate();
9069 return;
9070 }
9071
9072 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9073 s.move_with(|map, selection| {
9074 selection.collapse_to(
9075 movement::end_of_excerpt(
9076 map,
9077 selection.head(),
9078 workspace::searchable::Direction::Next,
9079 ),
9080 SelectionGoal::None,
9081 )
9082 });
9083 })
9084 }
9085
9086 pub fn select_to_start_of_excerpt(
9087 &mut self,
9088 _: &SelectToStartOfExcerpt,
9089 window: &mut Window,
9090 cx: &mut Context<Self>,
9091 ) {
9092 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9093 cx.propagate();
9094 return;
9095 }
9096
9097 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9098 s.move_heads_with(|map, head, _| {
9099 (
9100 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9101 SelectionGoal::None,
9102 )
9103 });
9104 })
9105 }
9106
9107 pub fn select_to_end_of_excerpt(
9108 &mut self,
9109 _: &SelectToEndOfExcerpt,
9110 window: &mut Window,
9111 cx: &mut Context<Self>,
9112 ) {
9113 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9114 cx.propagate();
9115 return;
9116 }
9117
9118 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9119 s.move_heads_with(|map, head, _| {
9120 (
9121 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9122 SelectionGoal::None,
9123 )
9124 });
9125 })
9126 }
9127
9128 pub fn move_to_beginning(
9129 &mut self,
9130 _: &MoveToBeginning,
9131 window: &mut Window,
9132 cx: &mut Context<Self>,
9133 ) {
9134 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9135 cx.propagate();
9136 return;
9137 }
9138
9139 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9140 s.select_ranges(vec![0..0]);
9141 });
9142 }
9143
9144 pub fn select_to_beginning(
9145 &mut self,
9146 _: &SelectToBeginning,
9147 window: &mut Window,
9148 cx: &mut Context<Self>,
9149 ) {
9150 let mut selection = self.selections.last::<Point>(cx);
9151 selection.set_head(Point::zero(), SelectionGoal::None);
9152
9153 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9154 s.select(vec![selection]);
9155 });
9156 }
9157
9158 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9159 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9160 cx.propagate();
9161 return;
9162 }
9163
9164 let cursor = self.buffer.read(cx).read(cx).len();
9165 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9166 s.select_ranges(vec![cursor..cursor])
9167 });
9168 }
9169
9170 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9171 self.nav_history = nav_history;
9172 }
9173
9174 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9175 self.nav_history.as_ref()
9176 }
9177
9178 fn push_to_nav_history(
9179 &mut self,
9180 cursor_anchor: Anchor,
9181 new_position: Option<Point>,
9182 cx: &mut Context<Self>,
9183 ) {
9184 if let Some(nav_history) = self.nav_history.as_mut() {
9185 let buffer = self.buffer.read(cx).read(cx);
9186 let cursor_position = cursor_anchor.to_point(&buffer);
9187 let scroll_state = self.scroll_manager.anchor();
9188 let scroll_top_row = scroll_state.top_row(&buffer);
9189 drop(buffer);
9190
9191 if let Some(new_position) = new_position {
9192 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9193 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9194 return;
9195 }
9196 }
9197
9198 nav_history.push(
9199 Some(NavigationData {
9200 cursor_anchor,
9201 cursor_position,
9202 scroll_anchor: scroll_state,
9203 scroll_top_row,
9204 }),
9205 cx,
9206 );
9207 }
9208 }
9209
9210 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9211 let buffer = self.buffer.read(cx).snapshot(cx);
9212 let mut selection = self.selections.first::<usize>(cx);
9213 selection.set_head(buffer.len(), SelectionGoal::None);
9214 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9215 s.select(vec![selection]);
9216 });
9217 }
9218
9219 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9220 let end = self.buffer.read(cx).read(cx).len();
9221 self.change_selections(None, window, cx, |s| {
9222 s.select_ranges(vec![0..end]);
9223 });
9224 }
9225
9226 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9227 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9228 let mut selections = self.selections.all::<Point>(cx);
9229 let max_point = display_map.buffer_snapshot.max_point();
9230 for selection in &mut selections {
9231 let rows = selection.spanned_rows(true, &display_map);
9232 selection.start = Point::new(rows.start.0, 0);
9233 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9234 selection.reversed = false;
9235 }
9236 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9237 s.select(selections);
9238 });
9239 }
9240
9241 pub fn split_selection_into_lines(
9242 &mut self,
9243 _: &SplitSelectionIntoLines,
9244 window: &mut Window,
9245 cx: &mut Context<Self>,
9246 ) {
9247 let selections = self
9248 .selections
9249 .all::<Point>(cx)
9250 .into_iter()
9251 .map(|selection| selection.start..selection.end)
9252 .collect::<Vec<_>>();
9253 self.unfold_ranges(&selections, true, true, cx);
9254
9255 let mut new_selection_ranges = Vec::new();
9256 {
9257 let buffer = self.buffer.read(cx).read(cx);
9258 for selection in selections {
9259 for row in selection.start.row..selection.end.row {
9260 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9261 new_selection_ranges.push(cursor..cursor);
9262 }
9263
9264 let is_multiline_selection = selection.start.row != selection.end.row;
9265 // Don't insert last one if it's a multi-line selection ending at the start of a line,
9266 // so this action feels more ergonomic when paired with other selection operations
9267 let should_skip_last = is_multiline_selection && selection.end.column == 0;
9268 if !should_skip_last {
9269 new_selection_ranges.push(selection.end..selection.end);
9270 }
9271 }
9272 }
9273 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9274 s.select_ranges(new_selection_ranges);
9275 });
9276 }
9277
9278 pub fn add_selection_above(
9279 &mut self,
9280 _: &AddSelectionAbove,
9281 window: &mut Window,
9282 cx: &mut Context<Self>,
9283 ) {
9284 self.add_selection(true, window, cx);
9285 }
9286
9287 pub fn add_selection_below(
9288 &mut self,
9289 _: &AddSelectionBelow,
9290 window: &mut Window,
9291 cx: &mut Context<Self>,
9292 ) {
9293 self.add_selection(false, window, cx);
9294 }
9295
9296 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9298 let mut selections = self.selections.all::<Point>(cx);
9299 let text_layout_details = self.text_layout_details(window);
9300 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9301 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9302 let range = oldest_selection.display_range(&display_map).sorted();
9303
9304 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9305 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9306 let positions = start_x.min(end_x)..start_x.max(end_x);
9307
9308 selections.clear();
9309 let mut stack = Vec::new();
9310 for row in range.start.row().0..=range.end.row().0 {
9311 if let Some(selection) = self.selections.build_columnar_selection(
9312 &display_map,
9313 DisplayRow(row),
9314 &positions,
9315 oldest_selection.reversed,
9316 &text_layout_details,
9317 ) {
9318 stack.push(selection.id);
9319 selections.push(selection);
9320 }
9321 }
9322
9323 if above {
9324 stack.reverse();
9325 }
9326
9327 AddSelectionsState { above, stack }
9328 });
9329
9330 let last_added_selection = *state.stack.last().unwrap();
9331 let mut new_selections = Vec::new();
9332 if above == state.above {
9333 let end_row = if above {
9334 DisplayRow(0)
9335 } else {
9336 display_map.max_point().row()
9337 };
9338
9339 'outer: for selection in selections {
9340 if selection.id == last_added_selection {
9341 let range = selection.display_range(&display_map).sorted();
9342 debug_assert_eq!(range.start.row(), range.end.row());
9343 let mut row = range.start.row();
9344 let positions =
9345 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9346 px(start)..px(end)
9347 } else {
9348 let start_x =
9349 display_map.x_for_display_point(range.start, &text_layout_details);
9350 let end_x =
9351 display_map.x_for_display_point(range.end, &text_layout_details);
9352 start_x.min(end_x)..start_x.max(end_x)
9353 };
9354
9355 while row != end_row {
9356 if above {
9357 row.0 -= 1;
9358 } else {
9359 row.0 += 1;
9360 }
9361
9362 if let Some(new_selection) = self.selections.build_columnar_selection(
9363 &display_map,
9364 row,
9365 &positions,
9366 selection.reversed,
9367 &text_layout_details,
9368 ) {
9369 state.stack.push(new_selection.id);
9370 if above {
9371 new_selections.push(new_selection);
9372 new_selections.push(selection);
9373 } else {
9374 new_selections.push(selection);
9375 new_selections.push(new_selection);
9376 }
9377
9378 continue 'outer;
9379 }
9380 }
9381 }
9382
9383 new_selections.push(selection);
9384 }
9385 } else {
9386 new_selections = selections;
9387 new_selections.retain(|s| s.id != last_added_selection);
9388 state.stack.pop();
9389 }
9390
9391 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9392 s.select(new_selections);
9393 });
9394 if state.stack.len() > 1 {
9395 self.add_selections_state = Some(state);
9396 }
9397 }
9398
9399 pub fn select_next_match_internal(
9400 &mut self,
9401 display_map: &DisplaySnapshot,
9402 replace_newest: bool,
9403 autoscroll: Option<Autoscroll>,
9404 window: &mut Window,
9405 cx: &mut Context<Self>,
9406 ) -> Result<()> {
9407 fn select_next_match_ranges(
9408 this: &mut Editor,
9409 range: Range<usize>,
9410 replace_newest: bool,
9411 auto_scroll: Option<Autoscroll>,
9412 window: &mut Window,
9413 cx: &mut Context<Editor>,
9414 ) {
9415 this.unfold_ranges(&[range.clone()], false, true, cx);
9416 this.change_selections(auto_scroll, window, cx, |s| {
9417 if replace_newest {
9418 s.delete(s.newest_anchor().id);
9419 }
9420 s.insert_range(range.clone());
9421 });
9422 }
9423
9424 let buffer = &display_map.buffer_snapshot;
9425 let mut selections = self.selections.all::<usize>(cx);
9426 if let Some(mut select_next_state) = self.select_next_state.take() {
9427 let query = &select_next_state.query;
9428 if !select_next_state.done {
9429 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9430 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9431 let mut next_selected_range = None;
9432
9433 let bytes_after_last_selection =
9434 buffer.bytes_in_range(last_selection.end..buffer.len());
9435 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9436 let query_matches = query
9437 .stream_find_iter(bytes_after_last_selection)
9438 .map(|result| (last_selection.end, result))
9439 .chain(
9440 query
9441 .stream_find_iter(bytes_before_first_selection)
9442 .map(|result| (0, result)),
9443 );
9444
9445 for (start_offset, query_match) in query_matches {
9446 let query_match = query_match.unwrap(); // can only fail due to I/O
9447 let offset_range =
9448 start_offset + query_match.start()..start_offset + query_match.end();
9449 let display_range = offset_range.start.to_display_point(display_map)
9450 ..offset_range.end.to_display_point(display_map);
9451
9452 if !select_next_state.wordwise
9453 || (!movement::is_inside_word(display_map, display_range.start)
9454 && !movement::is_inside_word(display_map, display_range.end))
9455 {
9456 // TODO: This is n^2, because we might check all the selections
9457 if !selections
9458 .iter()
9459 .any(|selection| selection.range().overlaps(&offset_range))
9460 {
9461 next_selected_range = Some(offset_range);
9462 break;
9463 }
9464 }
9465 }
9466
9467 if let Some(next_selected_range) = next_selected_range {
9468 select_next_match_ranges(
9469 self,
9470 next_selected_range,
9471 replace_newest,
9472 autoscroll,
9473 window,
9474 cx,
9475 );
9476 } else {
9477 select_next_state.done = true;
9478 }
9479 }
9480
9481 self.select_next_state = Some(select_next_state);
9482 } else {
9483 let mut only_carets = true;
9484 let mut same_text_selected = true;
9485 let mut selected_text = None;
9486
9487 let mut selections_iter = selections.iter().peekable();
9488 while let Some(selection) = selections_iter.next() {
9489 if selection.start != selection.end {
9490 only_carets = false;
9491 }
9492
9493 if same_text_selected {
9494 if selected_text.is_none() {
9495 selected_text =
9496 Some(buffer.text_for_range(selection.range()).collect::<String>());
9497 }
9498
9499 if let Some(next_selection) = selections_iter.peek() {
9500 if next_selection.range().len() == selection.range().len() {
9501 let next_selected_text = buffer
9502 .text_for_range(next_selection.range())
9503 .collect::<String>();
9504 if Some(next_selected_text) != selected_text {
9505 same_text_selected = false;
9506 selected_text = None;
9507 }
9508 } else {
9509 same_text_selected = false;
9510 selected_text = None;
9511 }
9512 }
9513 }
9514 }
9515
9516 if only_carets {
9517 for selection in &mut selections {
9518 let word_range = movement::surrounding_word(
9519 display_map,
9520 selection.start.to_display_point(display_map),
9521 );
9522 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9523 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9524 selection.goal = SelectionGoal::None;
9525 selection.reversed = false;
9526 select_next_match_ranges(
9527 self,
9528 selection.start..selection.end,
9529 replace_newest,
9530 autoscroll,
9531 window,
9532 cx,
9533 );
9534 }
9535
9536 if selections.len() == 1 {
9537 let selection = selections
9538 .last()
9539 .expect("ensured that there's only one selection");
9540 let query = buffer
9541 .text_for_range(selection.start..selection.end)
9542 .collect::<String>();
9543 let is_empty = query.is_empty();
9544 let select_state = SelectNextState {
9545 query: AhoCorasick::new(&[query])?,
9546 wordwise: true,
9547 done: is_empty,
9548 };
9549 self.select_next_state = Some(select_state);
9550 } else {
9551 self.select_next_state = None;
9552 }
9553 } else if let Some(selected_text) = selected_text {
9554 self.select_next_state = Some(SelectNextState {
9555 query: AhoCorasick::new(&[selected_text])?,
9556 wordwise: false,
9557 done: false,
9558 });
9559 self.select_next_match_internal(
9560 display_map,
9561 replace_newest,
9562 autoscroll,
9563 window,
9564 cx,
9565 )?;
9566 }
9567 }
9568 Ok(())
9569 }
9570
9571 pub fn select_all_matches(
9572 &mut self,
9573 _action: &SelectAllMatches,
9574 window: &mut Window,
9575 cx: &mut Context<Self>,
9576 ) -> Result<()> {
9577 self.push_to_selection_history();
9578 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9579
9580 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9581 let Some(select_next_state) = self.select_next_state.as_mut() else {
9582 return Ok(());
9583 };
9584 if select_next_state.done {
9585 return Ok(());
9586 }
9587
9588 let mut new_selections = self.selections.all::<usize>(cx);
9589
9590 let buffer = &display_map.buffer_snapshot;
9591 let query_matches = select_next_state
9592 .query
9593 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9594
9595 for query_match in query_matches {
9596 let query_match = query_match.unwrap(); // can only fail due to I/O
9597 let offset_range = query_match.start()..query_match.end();
9598 let display_range = offset_range.start.to_display_point(&display_map)
9599 ..offset_range.end.to_display_point(&display_map);
9600
9601 if !select_next_state.wordwise
9602 || (!movement::is_inside_word(&display_map, display_range.start)
9603 && !movement::is_inside_word(&display_map, display_range.end))
9604 {
9605 self.selections.change_with(cx, |selections| {
9606 new_selections.push(Selection {
9607 id: selections.new_selection_id(),
9608 start: offset_range.start,
9609 end: offset_range.end,
9610 reversed: false,
9611 goal: SelectionGoal::None,
9612 });
9613 });
9614 }
9615 }
9616
9617 new_selections.sort_by_key(|selection| selection.start);
9618 let mut ix = 0;
9619 while ix + 1 < new_selections.len() {
9620 let current_selection = &new_selections[ix];
9621 let next_selection = &new_selections[ix + 1];
9622 if current_selection.range().overlaps(&next_selection.range()) {
9623 if current_selection.id < next_selection.id {
9624 new_selections.remove(ix + 1);
9625 } else {
9626 new_selections.remove(ix);
9627 }
9628 } else {
9629 ix += 1;
9630 }
9631 }
9632
9633 let reversed = self.selections.oldest::<usize>(cx).reversed;
9634
9635 for selection in new_selections.iter_mut() {
9636 selection.reversed = reversed;
9637 }
9638
9639 select_next_state.done = true;
9640 self.unfold_ranges(
9641 &new_selections
9642 .iter()
9643 .map(|selection| selection.range())
9644 .collect::<Vec<_>>(),
9645 false,
9646 false,
9647 cx,
9648 );
9649 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9650 selections.select(new_selections)
9651 });
9652
9653 Ok(())
9654 }
9655
9656 pub fn select_next(
9657 &mut self,
9658 action: &SelectNext,
9659 window: &mut Window,
9660 cx: &mut Context<Self>,
9661 ) -> Result<()> {
9662 self.push_to_selection_history();
9663 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9664 self.select_next_match_internal(
9665 &display_map,
9666 action.replace_newest,
9667 Some(Autoscroll::newest()),
9668 window,
9669 cx,
9670 )?;
9671 Ok(())
9672 }
9673
9674 pub fn select_previous(
9675 &mut self,
9676 action: &SelectPrevious,
9677 window: &mut Window,
9678 cx: &mut Context<Self>,
9679 ) -> Result<()> {
9680 self.push_to_selection_history();
9681 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9682 let buffer = &display_map.buffer_snapshot;
9683 let mut selections = self.selections.all::<usize>(cx);
9684 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9685 let query = &select_prev_state.query;
9686 if !select_prev_state.done {
9687 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9688 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9689 let mut next_selected_range = None;
9690 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9691 let bytes_before_last_selection =
9692 buffer.reversed_bytes_in_range(0..last_selection.start);
9693 let bytes_after_first_selection =
9694 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9695 let query_matches = query
9696 .stream_find_iter(bytes_before_last_selection)
9697 .map(|result| (last_selection.start, result))
9698 .chain(
9699 query
9700 .stream_find_iter(bytes_after_first_selection)
9701 .map(|result| (buffer.len(), result)),
9702 );
9703 for (end_offset, query_match) in query_matches {
9704 let query_match = query_match.unwrap(); // can only fail due to I/O
9705 let offset_range =
9706 end_offset - query_match.end()..end_offset - query_match.start();
9707 let display_range = offset_range.start.to_display_point(&display_map)
9708 ..offset_range.end.to_display_point(&display_map);
9709
9710 if !select_prev_state.wordwise
9711 || (!movement::is_inside_word(&display_map, display_range.start)
9712 && !movement::is_inside_word(&display_map, display_range.end))
9713 {
9714 next_selected_range = Some(offset_range);
9715 break;
9716 }
9717 }
9718
9719 if let Some(next_selected_range) = next_selected_range {
9720 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9721 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9722 if action.replace_newest {
9723 s.delete(s.newest_anchor().id);
9724 }
9725 s.insert_range(next_selected_range);
9726 });
9727 } else {
9728 select_prev_state.done = true;
9729 }
9730 }
9731
9732 self.select_prev_state = Some(select_prev_state);
9733 } else {
9734 let mut only_carets = true;
9735 let mut same_text_selected = true;
9736 let mut selected_text = None;
9737
9738 let mut selections_iter = selections.iter().peekable();
9739 while let Some(selection) = selections_iter.next() {
9740 if selection.start != selection.end {
9741 only_carets = false;
9742 }
9743
9744 if same_text_selected {
9745 if selected_text.is_none() {
9746 selected_text =
9747 Some(buffer.text_for_range(selection.range()).collect::<String>());
9748 }
9749
9750 if let Some(next_selection) = selections_iter.peek() {
9751 if next_selection.range().len() == selection.range().len() {
9752 let next_selected_text = buffer
9753 .text_for_range(next_selection.range())
9754 .collect::<String>();
9755 if Some(next_selected_text) != selected_text {
9756 same_text_selected = false;
9757 selected_text = None;
9758 }
9759 } else {
9760 same_text_selected = false;
9761 selected_text = None;
9762 }
9763 }
9764 }
9765 }
9766
9767 if only_carets {
9768 for selection in &mut selections {
9769 let word_range = movement::surrounding_word(
9770 &display_map,
9771 selection.start.to_display_point(&display_map),
9772 );
9773 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9774 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9775 selection.goal = SelectionGoal::None;
9776 selection.reversed = false;
9777 }
9778 if selections.len() == 1 {
9779 let selection = selections
9780 .last()
9781 .expect("ensured that there's only one selection");
9782 let query = buffer
9783 .text_for_range(selection.start..selection.end)
9784 .collect::<String>();
9785 let is_empty = query.is_empty();
9786 let select_state = SelectNextState {
9787 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9788 wordwise: true,
9789 done: is_empty,
9790 };
9791 self.select_prev_state = Some(select_state);
9792 } else {
9793 self.select_prev_state = None;
9794 }
9795
9796 self.unfold_ranges(
9797 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9798 false,
9799 true,
9800 cx,
9801 );
9802 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9803 s.select(selections);
9804 });
9805 } else if let Some(selected_text) = selected_text {
9806 self.select_prev_state = Some(SelectNextState {
9807 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9808 wordwise: false,
9809 done: false,
9810 });
9811 self.select_previous(action, window, cx)?;
9812 }
9813 }
9814 Ok(())
9815 }
9816
9817 pub fn toggle_comments(
9818 &mut self,
9819 action: &ToggleComments,
9820 window: &mut Window,
9821 cx: &mut Context<Self>,
9822 ) {
9823 if self.read_only(cx) {
9824 return;
9825 }
9826 let text_layout_details = &self.text_layout_details(window);
9827 self.transact(window, cx, |this, window, cx| {
9828 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9829 let mut edits = Vec::new();
9830 let mut selection_edit_ranges = Vec::new();
9831 let mut last_toggled_row = None;
9832 let snapshot = this.buffer.read(cx).read(cx);
9833 let empty_str: Arc<str> = Arc::default();
9834 let mut suffixes_inserted = Vec::new();
9835 let ignore_indent = action.ignore_indent;
9836
9837 fn comment_prefix_range(
9838 snapshot: &MultiBufferSnapshot,
9839 row: MultiBufferRow,
9840 comment_prefix: &str,
9841 comment_prefix_whitespace: &str,
9842 ignore_indent: bool,
9843 ) -> Range<Point> {
9844 let indent_size = if ignore_indent {
9845 0
9846 } else {
9847 snapshot.indent_size_for_line(row).len
9848 };
9849
9850 let start = Point::new(row.0, indent_size);
9851
9852 let mut line_bytes = snapshot
9853 .bytes_in_range(start..snapshot.max_point())
9854 .flatten()
9855 .copied();
9856
9857 // If this line currently begins with the line comment prefix, then record
9858 // the range containing the prefix.
9859 if line_bytes
9860 .by_ref()
9861 .take(comment_prefix.len())
9862 .eq(comment_prefix.bytes())
9863 {
9864 // Include any whitespace that matches the comment prefix.
9865 let matching_whitespace_len = line_bytes
9866 .zip(comment_prefix_whitespace.bytes())
9867 .take_while(|(a, b)| a == b)
9868 .count() as u32;
9869 let end = Point::new(
9870 start.row,
9871 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9872 );
9873 start..end
9874 } else {
9875 start..start
9876 }
9877 }
9878
9879 fn comment_suffix_range(
9880 snapshot: &MultiBufferSnapshot,
9881 row: MultiBufferRow,
9882 comment_suffix: &str,
9883 comment_suffix_has_leading_space: bool,
9884 ) -> Range<Point> {
9885 let end = Point::new(row.0, snapshot.line_len(row));
9886 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9887
9888 let mut line_end_bytes = snapshot
9889 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9890 .flatten()
9891 .copied();
9892
9893 let leading_space_len = if suffix_start_column > 0
9894 && line_end_bytes.next() == Some(b' ')
9895 && comment_suffix_has_leading_space
9896 {
9897 1
9898 } else {
9899 0
9900 };
9901
9902 // If this line currently begins with the line comment prefix, then record
9903 // the range containing the prefix.
9904 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9905 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9906 start..end
9907 } else {
9908 end..end
9909 }
9910 }
9911
9912 // TODO: Handle selections that cross excerpts
9913 for selection in &mut selections {
9914 let start_column = snapshot
9915 .indent_size_for_line(MultiBufferRow(selection.start.row))
9916 .len;
9917 let language = if let Some(language) =
9918 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9919 {
9920 language
9921 } else {
9922 continue;
9923 };
9924
9925 selection_edit_ranges.clear();
9926
9927 // If multiple selections contain a given row, avoid processing that
9928 // row more than once.
9929 let mut start_row = MultiBufferRow(selection.start.row);
9930 if last_toggled_row == Some(start_row) {
9931 start_row = start_row.next_row();
9932 }
9933 let end_row =
9934 if selection.end.row > selection.start.row && selection.end.column == 0 {
9935 MultiBufferRow(selection.end.row - 1)
9936 } else {
9937 MultiBufferRow(selection.end.row)
9938 };
9939 last_toggled_row = Some(end_row);
9940
9941 if start_row > end_row {
9942 continue;
9943 }
9944
9945 // If the language has line comments, toggle those.
9946 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9947
9948 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9949 if ignore_indent {
9950 full_comment_prefixes = full_comment_prefixes
9951 .into_iter()
9952 .map(|s| Arc::from(s.trim_end()))
9953 .collect();
9954 }
9955
9956 if !full_comment_prefixes.is_empty() {
9957 let first_prefix = full_comment_prefixes
9958 .first()
9959 .expect("prefixes is non-empty");
9960 let prefix_trimmed_lengths = full_comment_prefixes
9961 .iter()
9962 .map(|p| p.trim_end_matches(' ').len())
9963 .collect::<SmallVec<[usize; 4]>>();
9964
9965 let mut all_selection_lines_are_comments = true;
9966
9967 for row in start_row.0..=end_row.0 {
9968 let row = MultiBufferRow(row);
9969 if start_row < end_row && snapshot.is_line_blank(row) {
9970 continue;
9971 }
9972
9973 let prefix_range = full_comment_prefixes
9974 .iter()
9975 .zip(prefix_trimmed_lengths.iter().copied())
9976 .map(|(prefix, trimmed_prefix_len)| {
9977 comment_prefix_range(
9978 snapshot.deref(),
9979 row,
9980 &prefix[..trimmed_prefix_len],
9981 &prefix[trimmed_prefix_len..],
9982 ignore_indent,
9983 )
9984 })
9985 .max_by_key(|range| range.end.column - range.start.column)
9986 .expect("prefixes is non-empty");
9987
9988 if prefix_range.is_empty() {
9989 all_selection_lines_are_comments = false;
9990 }
9991
9992 selection_edit_ranges.push(prefix_range);
9993 }
9994
9995 if all_selection_lines_are_comments {
9996 edits.extend(
9997 selection_edit_ranges
9998 .iter()
9999 .cloned()
10000 .map(|range| (range, empty_str.clone())),
10001 );
10002 } else {
10003 let min_column = selection_edit_ranges
10004 .iter()
10005 .map(|range| range.start.column)
10006 .min()
10007 .unwrap_or(0);
10008 edits.extend(selection_edit_ranges.iter().map(|range| {
10009 let position = Point::new(range.start.row, min_column);
10010 (position..position, first_prefix.clone())
10011 }));
10012 }
10013 } else if let Some((full_comment_prefix, comment_suffix)) =
10014 language.block_comment_delimiters()
10015 {
10016 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10017 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10018 let prefix_range = comment_prefix_range(
10019 snapshot.deref(),
10020 start_row,
10021 comment_prefix,
10022 comment_prefix_whitespace,
10023 ignore_indent,
10024 );
10025 let suffix_range = comment_suffix_range(
10026 snapshot.deref(),
10027 end_row,
10028 comment_suffix.trim_start_matches(' '),
10029 comment_suffix.starts_with(' '),
10030 );
10031
10032 if prefix_range.is_empty() || suffix_range.is_empty() {
10033 edits.push((
10034 prefix_range.start..prefix_range.start,
10035 full_comment_prefix.clone(),
10036 ));
10037 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10038 suffixes_inserted.push((end_row, comment_suffix.len()));
10039 } else {
10040 edits.push((prefix_range, empty_str.clone()));
10041 edits.push((suffix_range, empty_str.clone()));
10042 }
10043 } else {
10044 continue;
10045 }
10046 }
10047
10048 drop(snapshot);
10049 this.buffer.update(cx, |buffer, cx| {
10050 buffer.edit(edits, None, cx);
10051 });
10052
10053 // Adjust selections so that they end before any comment suffixes that
10054 // were inserted.
10055 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10056 let mut selections = this.selections.all::<Point>(cx);
10057 let snapshot = this.buffer.read(cx).read(cx);
10058 for selection in &mut selections {
10059 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10060 match row.cmp(&MultiBufferRow(selection.end.row)) {
10061 Ordering::Less => {
10062 suffixes_inserted.next();
10063 continue;
10064 }
10065 Ordering::Greater => break,
10066 Ordering::Equal => {
10067 if selection.end.column == snapshot.line_len(row) {
10068 if selection.is_empty() {
10069 selection.start.column -= suffix_len as u32;
10070 }
10071 selection.end.column -= suffix_len as u32;
10072 }
10073 break;
10074 }
10075 }
10076 }
10077 }
10078
10079 drop(snapshot);
10080 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10081 s.select(selections)
10082 });
10083
10084 let selections = this.selections.all::<Point>(cx);
10085 let selections_on_single_row = selections.windows(2).all(|selections| {
10086 selections[0].start.row == selections[1].start.row
10087 && selections[0].end.row == selections[1].end.row
10088 && selections[0].start.row == selections[0].end.row
10089 });
10090 let selections_selecting = selections
10091 .iter()
10092 .any(|selection| selection.start != selection.end);
10093 let advance_downwards = action.advance_downwards
10094 && selections_on_single_row
10095 && !selections_selecting
10096 && !matches!(this.mode, EditorMode::SingleLine { .. });
10097
10098 if advance_downwards {
10099 let snapshot = this.buffer.read(cx).snapshot(cx);
10100
10101 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10102 s.move_cursors_with(|display_snapshot, display_point, _| {
10103 let mut point = display_point.to_point(display_snapshot);
10104 point.row += 1;
10105 point = snapshot.clip_point(point, Bias::Left);
10106 let display_point = point.to_display_point(display_snapshot);
10107 let goal = SelectionGoal::HorizontalPosition(
10108 display_snapshot
10109 .x_for_display_point(display_point, text_layout_details)
10110 .into(),
10111 );
10112 (display_point, goal)
10113 })
10114 });
10115 }
10116 });
10117 }
10118
10119 pub fn select_enclosing_symbol(
10120 &mut self,
10121 _: &SelectEnclosingSymbol,
10122 window: &mut Window,
10123 cx: &mut Context<Self>,
10124 ) {
10125 let buffer = self.buffer.read(cx).snapshot(cx);
10126 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10127
10128 fn update_selection(
10129 selection: &Selection<usize>,
10130 buffer_snap: &MultiBufferSnapshot,
10131 ) -> Option<Selection<usize>> {
10132 let cursor = selection.head();
10133 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10134 for symbol in symbols.iter().rev() {
10135 let start = symbol.range.start.to_offset(buffer_snap);
10136 let end = symbol.range.end.to_offset(buffer_snap);
10137 let new_range = start..end;
10138 if start < selection.start || end > selection.end {
10139 return Some(Selection {
10140 id: selection.id,
10141 start: new_range.start,
10142 end: new_range.end,
10143 goal: SelectionGoal::None,
10144 reversed: selection.reversed,
10145 });
10146 }
10147 }
10148 None
10149 }
10150
10151 let mut selected_larger_symbol = false;
10152 let new_selections = old_selections
10153 .iter()
10154 .map(|selection| match update_selection(selection, &buffer) {
10155 Some(new_selection) => {
10156 if new_selection.range() != selection.range() {
10157 selected_larger_symbol = true;
10158 }
10159 new_selection
10160 }
10161 None => selection.clone(),
10162 })
10163 .collect::<Vec<_>>();
10164
10165 if selected_larger_symbol {
10166 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10167 s.select(new_selections);
10168 });
10169 }
10170 }
10171
10172 pub fn select_larger_syntax_node(
10173 &mut self,
10174 _: &SelectLargerSyntaxNode,
10175 window: &mut Window,
10176 cx: &mut Context<Self>,
10177 ) {
10178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10179 let buffer = self.buffer.read(cx).snapshot(cx);
10180 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10181
10182 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10183 let mut selected_larger_node = false;
10184 let new_selections = old_selections
10185 .iter()
10186 .map(|selection| {
10187 let old_range = selection.start..selection.end;
10188 let mut new_range = old_range.clone();
10189 let mut new_node = None;
10190 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10191 {
10192 new_node = Some(node);
10193 new_range = containing_range;
10194 if !display_map.intersects_fold(new_range.start)
10195 && !display_map.intersects_fold(new_range.end)
10196 {
10197 break;
10198 }
10199 }
10200
10201 if let Some(node) = new_node {
10202 // Log the ancestor, to support using this action as a way to explore TreeSitter
10203 // nodes. Parent and grandparent are also logged because this operation will not
10204 // visit nodes that have the same range as their parent.
10205 log::info!("Node: {node:?}");
10206 let parent = node.parent();
10207 log::info!("Parent: {parent:?}");
10208 let grandparent = parent.and_then(|x| x.parent());
10209 log::info!("Grandparent: {grandparent:?}");
10210 }
10211
10212 selected_larger_node |= new_range != old_range;
10213 Selection {
10214 id: selection.id,
10215 start: new_range.start,
10216 end: new_range.end,
10217 goal: SelectionGoal::None,
10218 reversed: selection.reversed,
10219 }
10220 })
10221 .collect::<Vec<_>>();
10222
10223 if selected_larger_node {
10224 stack.push(old_selections);
10225 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10226 s.select(new_selections);
10227 });
10228 }
10229 self.select_larger_syntax_node_stack = stack;
10230 }
10231
10232 pub fn select_smaller_syntax_node(
10233 &mut self,
10234 _: &SelectSmallerSyntaxNode,
10235 window: &mut Window,
10236 cx: &mut Context<Self>,
10237 ) {
10238 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10239 if let Some(selections) = stack.pop() {
10240 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10241 s.select(selections.to_vec());
10242 });
10243 }
10244 self.select_larger_syntax_node_stack = stack;
10245 }
10246
10247 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10248 if !EditorSettings::get_global(cx).gutter.runnables {
10249 self.clear_tasks();
10250 return Task::ready(());
10251 }
10252 let project = self.project.as_ref().map(Entity::downgrade);
10253 cx.spawn_in(window, |this, mut cx| async move {
10254 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10255 let Some(project) = project.and_then(|p| p.upgrade()) else {
10256 return;
10257 };
10258 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10259 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10260 }) else {
10261 return;
10262 };
10263
10264 let hide_runnables = project
10265 .update(&mut cx, |project, cx| {
10266 // Do not display any test indicators in non-dev server remote projects.
10267 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10268 })
10269 .unwrap_or(true);
10270 if hide_runnables {
10271 return;
10272 }
10273 let new_rows =
10274 cx.background_spawn({
10275 let snapshot = display_snapshot.clone();
10276 async move {
10277 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10278 }
10279 })
10280 .await;
10281
10282 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10283 this.update(&mut cx, |this, _| {
10284 this.clear_tasks();
10285 for (key, value) in rows {
10286 this.insert_tasks(key, value);
10287 }
10288 })
10289 .ok();
10290 })
10291 }
10292 fn fetch_runnable_ranges(
10293 snapshot: &DisplaySnapshot,
10294 range: Range<Anchor>,
10295 ) -> Vec<language::RunnableRange> {
10296 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10297 }
10298
10299 fn runnable_rows(
10300 project: Entity<Project>,
10301 snapshot: DisplaySnapshot,
10302 runnable_ranges: Vec<RunnableRange>,
10303 mut cx: AsyncWindowContext,
10304 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10305 runnable_ranges
10306 .into_iter()
10307 .filter_map(|mut runnable| {
10308 let tasks = cx
10309 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10310 .ok()?;
10311 if tasks.is_empty() {
10312 return None;
10313 }
10314
10315 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10316
10317 let row = snapshot
10318 .buffer_snapshot
10319 .buffer_line_for_row(MultiBufferRow(point.row))?
10320 .1
10321 .start
10322 .row;
10323
10324 let context_range =
10325 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10326 Some((
10327 (runnable.buffer_id, row),
10328 RunnableTasks {
10329 templates: tasks,
10330 offset: MultiBufferOffset(runnable.run_range.start),
10331 context_range,
10332 column: point.column,
10333 extra_variables: runnable.extra_captures,
10334 },
10335 ))
10336 })
10337 .collect()
10338 }
10339
10340 fn templates_with_tags(
10341 project: &Entity<Project>,
10342 runnable: &mut Runnable,
10343 cx: &mut App,
10344 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10345 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10346 let (worktree_id, file) = project
10347 .buffer_for_id(runnable.buffer, cx)
10348 .and_then(|buffer| buffer.read(cx).file())
10349 .map(|file| (file.worktree_id(cx), file.clone()))
10350 .unzip();
10351
10352 (
10353 project.task_store().read(cx).task_inventory().cloned(),
10354 worktree_id,
10355 file,
10356 )
10357 });
10358
10359 let tags = mem::take(&mut runnable.tags);
10360 let mut tags: Vec<_> = tags
10361 .into_iter()
10362 .flat_map(|tag| {
10363 let tag = tag.0.clone();
10364 inventory
10365 .as_ref()
10366 .into_iter()
10367 .flat_map(|inventory| {
10368 inventory.read(cx).list_tasks(
10369 file.clone(),
10370 Some(runnable.language.clone()),
10371 worktree_id,
10372 cx,
10373 )
10374 })
10375 .filter(move |(_, template)| {
10376 template.tags.iter().any(|source_tag| source_tag == &tag)
10377 })
10378 })
10379 .sorted_by_key(|(kind, _)| kind.to_owned())
10380 .collect();
10381 if let Some((leading_tag_source, _)) = tags.first() {
10382 // Strongest source wins; if we have worktree tag binding, prefer that to
10383 // global and language bindings;
10384 // if we have a global binding, prefer that to language binding.
10385 let first_mismatch = tags
10386 .iter()
10387 .position(|(tag_source, _)| tag_source != leading_tag_source);
10388 if let Some(index) = first_mismatch {
10389 tags.truncate(index);
10390 }
10391 }
10392
10393 tags
10394 }
10395
10396 pub fn move_to_enclosing_bracket(
10397 &mut self,
10398 _: &MoveToEnclosingBracket,
10399 window: &mut Window,
10400 cx: &mut Context<Self>,
10401 ) {
10402 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10403 s.move_offsets_with(|snapshot, selection| {
10404 let Some(enclosing_bracket_ranges) =
10405 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10406 else {
10407 return;
10408 };
10409
10410 let mut best_length = usize::MAX;
10411 let mut best_inside = false;
10412 let mut best_in_bracket_range = false;
10413 let mut best_destination = None;
10414 for (open, close) in enclosing_bracket_ranges {
10415 let close = close.to_inclusive();
10416 let length = close.end() - open.start;
10417 let inside = selection.start >= open.end && selection.end <= *close.start();
10418 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10419 || close.contains(&selection.head());
10420
10421 // If best is next to a bracket and current isn't, skip
10422 if !in_bracket_range && best_in_bracket_range {
10423 continue;
10424 }
10425
10426 // Prefer smaller lengths unless best is inside and current isn't
10427 if length > best_length && (best_inside || !inside) {
10428 continue;
10429 }
10430
10431 best_length = length;
10432 best_inside = inside;
10433 best_in_bracket_range = in_bracket_range;
10434 best_destination = Some(
10435 if close.contains(&selection.start) && close.contains(&selection.end) {
10436 if inside {
10437 open.end
10438 } else {
10439 open.start
10440 }
10441 } else if inside {
10442 *close.start()
10443 } else {
10444 *close.end()
10445 },
10446 );
10447 }
10448
10449 if let Some(destination) = best_destination {
10450 selection.collapse_to(destination, SelectionGoal::None);
10451 }
10452 })
10453 });
10454 }
10455
10456 pub fn undo_selection(
10457 &mut self,
10458 _: &UndoSelection,
10459 window: &mut Window,
10460 cx: &mut Context<Self>,
10461 ) {
10462 self.end_selection(window, cx);
10463 self.selection_history.mode = SelectionHistoryMode::Undoing;
10464 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10465 self.change_selections(None, window, cx, |s| {
10466 s.select_anchors(entry.selections.to_vec())
10467 });
10468 self.select_next_state = entry.select_next_state;
10469 self.select_prev_state = entry.select_prev_state;
10470 self.add_selections_state = entry.add_selections_state;
10471 self.request_autoscroll(Autoscroll::newest(), cx);
10472 }
10473 self.selection_history.mode = SelectionHistoryMode::Normal;
10474 }
10475
10476 pub fn redo_selection(
10477 &mut self,
10478 _: &RedoSelection,
10479 window: &mut Window,
10480 cx: &mut Context<Self>,
10481 ) {
10482 self.end_selection(window, cx);
10483 self.selection_history.mode = SelectionHistoryMode::Redoing;
10484 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10485 self.change_selections(None, window, cx, |s| {
10486 s.select_anchors(entry.selections.to_vec())
10487 });
10488 self.select_next_state = entry.select_next_state;
10489 self.select_prev_state = entry.select_prev_state;
10490 self.add_selections_state = entry.add_selections_state;
10491 self.request_autoscroll(Autoscroll::newest(), cx);
10492 }
10493 self.selection_history.mode = SelectionHistoryMode::Normal;
10494 }
10495
10496 pub fn expand_excerpts(
10497 &mut self,
10498 action: &ExpandExcerpts,
10499 _: &mut Window,
10500 cx: &mut Context<Self>,
10501 ) {
10502 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10503 }
10504
10505 pub fn expand_excerpts_down(
10506 &mut self,
10507 action: &ExpandExcerptsDown,
10508 _: &mut Window,
10509 cx: &mut Context<Self>,
10510 ) {
10511 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10512 }
10513
10514 pub fn expand_excerpts_up(
10515 &mut self,
10516 action: &ExpandExcerptsUp,
10517 _: &mut Window,
10518 cx: &mut Context<Self>,
10519 ) {
10520 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10521 }
10522
10523 pub fn expand_excerpts_for_direction(
10524 &mut self,
10525 lines: u32,
10526 direction: ExpandExcerptDirection,
10527
10528 cx: &mut Context<Self>,
10529 ) {
10530 let selections = self.selections.disjoint_anchors();
10531
10532 let lines = if lines == 0 {
10533 EditorSettings::get_global(cx).expand_excerpt_lines
10534 } else {
10535 lines
10536 };
10537
10538 self.buffer.update(cx, |buffer, cx| {
10539 let snapshot = buffer.snapshot(cx);
10540 let mut excerpt_ids = selections
10541 .iter()
10542 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10543 .collect::<Vec<_>>();
10544 excerpt_ids.sort();
10545 excerpt_ids.dedup();
10546 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10547 })
10548 }
10549
10550 pub fn expand_excerpt(
10551 &mut self,
10552 excerpt: ExcerptId,
10553 direction: ExpandExcerptDirection,
10554 cx: &mut Context<Self>,
10555 ) {
10556 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10557 self.buffer.update(cx, |buffer, cx| {
10558 buffer.expand_excerpts([excerpt], lines, direction, cx)
10559 })
10560 }
10561
10562 pub fn go_to_singleton_buffer_point(
10563 &mut self,
10564 point: Point,
10565 window: &mut Window,
10566 cx: &mut Context<Self>,
10567 ) {
10568 self.go_to_singleton_buffer_range(point..point, window, cx);
10569 }
10570
10571 pub fn go_to_singleton_buffer_range(
10572 &mut self,
10573 range: Range<Point>,
10574 window: &mut Window,
10575 cx: &mut Context<Self>,
10576 ) {
10577 let multibuffer = self.buffer().read(cx);
10578 let Some(buffer) = multibuffer.as_singleton() else {
10579 return;
10580 };
10581 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10582 return;
10583 };
10584 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10585 return;
10586 };
10587 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10588 s.select_anchor_ranges([start..end])
10589 });
10590 }
10591
10592 fn go_to_diagnostic(
10593 &mut self,
10594 _: &GoToDiagnostic,
10595 window: &mut Window,
10596 cx: &mut Context<Self>,
10597 ) {
10598 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10599 }
10600
10601 fn go_to_prev_diagnostic(
10602 &mut self,
10603 _: &GoToPrevDiagnostic,
10604 window: &mut Window,
10605 cx: &mut Context<Self>,
10606 ) {
10607 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10608 }
10609
10610 pub fn go_to_diagnostic_impl(
10611 &mut self,
10612 direction: Direction,
10613 window: &mut Window,
10614 cx: &mut Context<Self>,
10615 ) {
10616 let buffer = self.buffer.read(cx).snapshot(cx);
10617 let selection = self.selections.newest::<usize>(cx);
10618
10619 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10620 if direction == Direction::Next {
10621 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10622 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10623 return;
10624 };
10625 self.activate_diagnostics(
10626 buffer_id,
10627 popover.local_diagnostic.diagnostic.group_id,
10628 window,
10629 cx,
10630 );
10631 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10632 let primary_range_start = active_diagnostics.primary_range.start;
10633 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10634 let mut new_selection = s.newest_anchor().clone();
10635 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10636 s.select_anchors(vec![new_selection.clone()]);
10637 });
10638 self.refresh_inline_completion(false, true, window, cx);
10639 }
10640 return;
10641 }
10642 }
10643
10644 let active_group_id = self
10645 .active_diagnostics
10646 .as_ref()
10647 .map(|active_group| active_group.group_id);
10648 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10649 active_diagnostics
10650 .primary_range
10651 .to_offset(&buffer)
10652 .to_inclusive()
10653 });
10654 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10655 if active_primary_range.contains(&selection.head()) {
10656 *active_primary_range.start()
10657 } else {
10658 selection.head()
10659 }
10660 } else {
10661 selection.head()
10662 };
10663
10664 let snapshot = self.snapshot(window, cx);
10665 let primary_diagnostics_before = buffer
10666 .diagnostics_in_range::<usize>(0..search_start)
10667 .filter(|entry| entry.diagnostic.is_primary)
10668 .filter(|entry| entry.range.start != entry.range.end)
10669 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10670 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
10671 .collect::<Vec<_>>();
10672 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
10673 primary_diagnostics_before
10674 .iter()
10675 .position(|entry| entry.diagnostic.group_id == active_group_id)
10676 });
10677
10678 let primary_diagnostics_after = buffer
10679 .diagnostics_in_range::<usize>(search_start..buffer.len())
10680 .filter(|entry| entry.diagnostic.is_primary)
10681 .filter(|entry| entry.range.start != entry.range.end)
10682 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10683 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10684 .collect::<Vec<_>>();
10685 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
10686 primary_diagnostics_after
10687 .iter()
10688 .enumerate()
10689 .rev()
10690 .find_map(|(i, entry)| {
10691 if entry.diagnostic.group_id == active_group_id {
10692 Some(i)
10693 } else {
10694 None
10695 }
10696 })
10697 });
10698
10699 let next_primary_diagnostic = match direction {
10700 Direction::Prev => primary_diagnostics_before
10701 .iter()
10702 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
10703 .rev()
10704 .next(),
10705 Direction::Next => primary_diagnostics_after
10706 .iter()
10707 .skip(
10708 last_same_group_diagnostic_after
10709 .map(|index| index + 1)
10710 .unwrap_or(0),
10711 )
10712 .next(),
10713 };
10714
10715 // Cycle around to the start of the buffer, potentially moving back to the start of
10716 // the currently active diagnostic.
10717 let cycle_around = || match direction {
10718 Direction::Prev => primary_diagnostics_after
10719 .iter()
10720 .rev()
10721 .chain(primary_diagnostics_before.iter().rev())
10722 .next(),
10723 Direction::Next => primary_diagnostics_before
10724 .iter()
10725 .chain(primary_diagnostics_after.iter())
10726 .next(),
10727 };
10728
10729 if let Some((primary_range, group_id)) = next_primary_diagnostic
10730 .or_else(cycle_around)
10731 .map(|entry| (&entry.range, entry.diagnostic.group_id))
10732 {
10733 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10734 return;
10735 };
10736 self.activate_diagnostics(buffer_id, group_id, window, cx);
10737 if self.active_diagnostics.is_some() {
10738 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10739 s.select(vec![Selection {
10740 id: selection.id,
10741 start: primary_range.start,
10742 end: primary_range.start,
10743 reversed: false,
10744 goal: SelectionGoal::None,
10745 }]);
10746 });
10747 self.refresh_inline_completion(false, true, window, cx);
10748 }
10749 }
10750 }
10751
10752 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10753 let snapshot = self.snapshot(window, cx);
10754 let selection = self.selections.newest::<Point>(cx);
10755 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10756 }
10757
10758 fn go_to_hunk_after_position(
10759 &mut self,
10760 snapshot: &EditorSnapshot,
10761 position: Point,
10762 window: &mut Window,
10763 cx: &mut Context<Editor>,
10764 ) -> Option<MultiBufferDiffHunk> {
10765 let mut hunk = snapshot
10766 .buffer_snapshot
10767 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10768 .find(|hunk| hunk.row_range.start.0 > position.row);
10769 if hunk.is_none() {
10770 hunk = snapshot
10771 .buffer_snapshot
10772 .diff_hunks_in_range(Point::zero()..position)
10773 .find(|hunk| hunk.row_range.end.0 < position.row)
10774 }
10775 if let Some(hunk) = &hunk {
10776 let destination = Point::new(hunk.row_range.start.0, 0);
10777 self.unfold_ranges(&[destination..destination], false, false, cx);
10778 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10779 s.select_ranges(vec![destination..destination]);
10780 });
10781 }
10782
10783 hunk
10784 }
10785
10786 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10787 let snapshot = self.snapshot(window, cx);
10788 let selection = self.selections.newest::<Point>(cx);
10789 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10790 }
10791
10792 fn go_to_hunk_before_position(
10793 &mut self,
10794 snapshot: &EditorSnapshot,
10795 position: Point,
10796 window: &mut Window,
10797 cx: &mut Context<Editor>,
10798 ) -> Option<MultiBufferDiffHunk> {
10799 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10800 if hunk.is_none() {
10801 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10802 }
10803 if let Some(hunk) = &hunk {
10804 let destination = Point::new(hunk.row_range.start.0, 0);
10805 self.unfold_ranges(&[destination..destination], false, false, cx);
10806 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10807 s.select_ranges(vec![destination..destination]);
10808 });
10809 }
10810
10811 hunk
10812 }
10813
10814 pub fn go_to_definition(
10815 &mut self,
10816 _: &GoToDefinition,
10817 window: &mut Window,
10818 cx: &mut Context<Self>,
10819 ) -> Task<Result<Navigated>> {
10820 let definition =
10821 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10822 cx.spawn_in(window, |editor, mut cx| async move {
10823 if definition.await? == Navigated::Yes {
10824 return Ok(Navigated::Yes);
10825 }
10826 match editor.update_in(&mut cx, |editor, window, cx| {
10827 editor.find_all_references(&FindAllReferences, window, cx)
10828 })? {
10829 Some(references) => references.await,
10830 None => Ok(Navigated::No),
10831 }
10832 })
10833 }
10834
10835 pub fn go_to_declaration(
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, false, window, cx)
10842 }
10843
10844 pub fn go_to_declaration_split(
10845 &mut self,
10846 _: &GoToDeclaration,
10847 window: &mut Window,
10848 cx: &mut Context<Self>,
10849 ) -> Task<Result<Navigated>> {
10850 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10851 }
10852
10853 pub fn go_to_implementation(
10854 &mut self,
10855 _: &GoToImplementation,
10856 window: &mut Window,
10857 cx: &mut Context<Self>,
10858 ) -> Task<Result<Navigated>> {
10859 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10860 }
10861
10862 pub fn go_to_implementation_split(
10863 &mut self,
10864 _: &GoToImplementationSplit,
10865 window: &mut Window,
10866 cx: &mut Context<Self>,
10867 ) -> Task<Result<Navigated>> {
10868 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10869 }
10870
10871 pub fn go_to_type_definition(
10872 &mut self,
10873 _: &GoToTypeDefinition,
10874 window: &mut Window,
10875 cx: &mut Context<Self>,
10876 ) -> Task<Result<Navigated>> {
10877 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10878 }
10879
10880 pub fn go_to_definition_split(
10881 &mut self,
10882 _: &GoToDefinitionSplit,
10883 window: &mut Window,
10884 cx: &mut Context<Self>,
10885 ) -> Task<Result<Navigated>> {
10886 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10887 }
10888
10889 pub fn go_to_type_definition_split(
10890 &mut self,
10891 _: &GoToTypeDefinitionSplit,
10892 window: &mut Window,
10893 cx: &mut Context<Self>,
10894 ) -> Task<Result<Navigated>> {
10895 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10896 }
10897
10898 fn go_to_definition_of_kind(
10899 &mut self,
10900 kind: GotoDefinitionKind,
10901 split: bool,
10902 window: &mut Window,
10903 cx: &mut Context<Self>,
10904 ) -> Task<Result<Navigated>> {
10905 let Some(provider) = self.semantics_provider.clone() else {
10906 return Task::ready(Ok(Navigated::No));
10907 };
10908 let head = self.selections.newest::<usize>(cx).head();
10909 let buffer = self.buffer.read(cx);
10910 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10911 text_anchor
10912 } else {
10913 return Task::ready(Ok(Navigated::No));
10914 };
10915
10916 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10917 return Task::ready(Ok(Navigated::No));
10918 };
10919
10920 cx.spawn_in(window, |editor, mut cx| async move {
10921 let definitions = definitions.await?;
10922 let navigated = editor
10923 .update_in(&mut cx, |editor, window, cx| {
10924 editor.navigate_to_hover_links(
10925 Some(kind),
10926 definitions
10927 .into_iter()
10928 .filter(|location| {
10929 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10930 })
10931 .map(HoverLink::Text)
10932 .collect::<Vec<_>>(),
10933 split,
10934 window,
10935 cx,
10936 )
10937 })?
10938 .await?;
10939 anyhow::Ok(navigated)
10940 })
10941 }
10942
10943 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10944 let selection = self.selections.newest_anchor();
10945 let head = selection.head();
10946 let tail = selection.tail();
10947
10948 let Some((buffer, start_position)) =
10949 self.buffer.read(cx).text_anchor_for_position(head, cx)
10950 else {
10951 return;
10952 };
10953
10954 let end_position = if head != tail {
10955 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10956 return;
10957 };
10958 Some(pos)
10959 } else {
10960 None
10961 };
10962
10963 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10964 let url = if let Some(end_pos) = end_position {
10965 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10966 } else {
10967 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10968 };
10969
10970 if let Some(url) = url {
10971 editor.update(&mut cx, |_, cx| {
10972 cx.open_url(&url);
10973 })
10974 } else {
10975 Ok(())
10976 }
10977 });
10978
10979 url_finder.detach();
10980 }
10981
10982 pub fn open_selected_filename(
10983 &mut self,
10984 _: &OpenSelectedFilename,
10985 window: &mut Window,
10986 cx: &mut Context<Self>,
10987 ) {
10988 let Some(workspace) = self.workspace() else {
10989 return;
10990 };
10991
10992 let position = self.selections.newest_anchor().head();
10993
10994 let Some((buffer, buffer_position)) =
10995 self.buffer.read(cx).text_anchor_for_position(position, cx)
10996 else {
10997 return;
10998 };
10999
11000 let project = self.project.clone();
11001
11002 cx.spawn_in(window, |_, mut cx| async move {
11003 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
11004
11005 if let Some((_, path)) = result {
11006 workspace
11007 .update_in(&mut cx, |workspace, window, cx| {
11008 workspace.open_resolved_path(path, window, cx)
11009 })?
11010 .await?;
11011 }
11012 anyhow::Ok(())
11013 })
11014 .detach();
11015 }
11016
11017 pub(crate) fn navigate_to_hover_links(
11018 &mut self,
11019 kind: Option<GotoDefinitionKind>,
11020 mut definitions: Vec<HoverLink>,
11021 split: bool,
11022 window: &mut Window,
11023 cx: &mut Context<Editor>,
11024 ) -> Task<Result<Navigated>> {
11025 // If there is one definition, just open it directly
11026 if definitions.len() == 1 {
11027 let definition = definitions.pop().unwrap();
11028
11029 enum TargetTaskResult {
11030 Location(Option<Location>),
11031 AlreadyNavigated,
11032 }
11033
11034 let target_task = match definition {
11035 HoverLink::Text(link) => {
11036 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11037 }
11038 HoverLink::InlayHint(lsp_location, server_id) => {
11039 let computation =
11040 self.compute_target_location(lsp_location, server_id, window, cx);
11041 cx.background_spawn(async move {
11042 let location = computation.await?;
11043 Ok(TargetTaskResult::Location(location))
11044 })
11045 }
11046 HoverLink::Url(url) => {
11047 cx.open_url(&url);
11048 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11049 }
11050 HoverLink::File(path) => {
11051 if let Some(workspace) = self.workspace() {
11052 cx.spawn_in(window, |_, mut cx| async move {
11053 workspace
11054 .update_in(&mut cx, |workspace, window, cx| {
11055 workspace.open_resolved_path(path, window, cx)
11056 })?
11057 .await
11058 .map(|_| TargetTaskResult::AlreadyNavigated)
11059 })
11060 } else {
11061 Task::ready(Ok(TargetTaskResult::Location(None)))
11062 }
11063 }
11064 };
11065 cx.spawn_in(window, |editor, mut cx| async move {
11066 let target = match target_task.await.context("target resolution task")? {
11067 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11068 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11069 TargetTaskResult::Location(Some(target)) => target,
11070 };
11071
11072 editor.update_in(&mut cx, |editor, window, cx| {
11073 let Some(workspace) = editor.workspace() else {
11074 return Navigated::No;
11075 };
11076 let pane = workspace.read(cx).active_pane().clone();
11077
11078 let range = target.range.to_point(target.buffer.read(cx));
11079 let range = editor.range_for_match(&range);
11080 let range = collapse_multiline_range(range);
11081
11082 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
11083 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11084 } else {
11085 window.defer(cx, move |window, cx| {
11086 let target_editor: Entity<Self> =
11087 workspace.update(cx, |workspace, cx| {
11088 let pane = if split {
11089 workspace.adjacent_pane(window, cx)
11090 } else {
11091 workspace.active_pane().clone()
11092 };
11093
11094 workspace.open_project_item(
11095 pane,
11096 target.buffer.clone(),
11097 true,
11098 true,
11099 window,
11100 cx,
11101 )
11102 });
11103 target_editor.update(cx, |target_editor, cx| {
11104 // When selecting a definition in a different buffer, disable the nav history
11105 // to avoid creating a history entry at the previous cursor location.
11106 pane.update(cx, |pane, _| pane.disable_history());
11107 target_editor.go_to_singleton_buffer_range(range, window, cx);
11108 pane.update(cx, |pane, _| pane.enable_history());
11109 });
11110 });
11111 }
11112 Navigated::Yes
11113 })
11114 })
11115 } else if !definitions.is_empty() {
11116 cx.spawn_in(window, |editor, mut cx| async move {
11117 let (title, location_tasks, workspace) = editor
11118 .update_in(&mut cx, |editor, window, cx| {
11119 let tab_kind = match kind {
11120 Some(GotoDefinitionKind::Implementation) => "Implementations",
11121 _ => "Definitions",
11122 };
11123 let title = definitions
11124 .iter()
11125 .find_map(|definition| match definition {
11126 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11127 let buffer = origin.buffer.read(cx);
11128 format!(
11129 "{} for {}",
11130 tab_kind,
11131 buffer
11132 .text_for_range(origin.range.clone())
11133 .collect::<String>()
11134 )
11135 }),
11136 HoverLink::InlayHint(_, _) => None,
11137 HoverLink::Url(_) => None,
11138 HoverLink::File(_) => None,
11139 })
11140 .unwrap_or(tab_kind.to_string());
11141 let location_tasks = definitions
11142 .into_iter()
11143 .map(|definition| match definition {
11144 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11145 HoverLink::InlayHint(lsp_location, server_id) => editor
11146 .compute_target_location(lsp_location, server_id, window, cx),
11147 HoverLink::Url(_) => Task::ready(Ok(None)),
11148 HoverLink::File(_) => Task::ready(Ok(None)),
11149 })
11150 .collect::<Vec<_>>();
11151 (title, location_tasks, editor.workspace().clone())
11152 })
11153 .context("location tasks preparation")?;
11154
11155 let locations = future::join_all(location_tasks)
11156 .await
11157 .into_iter()
11158 .filter_map(|location| location.transpose())
11159 .collect::<Result<_>>()
11160 .context("location tasks")?;
11161
11162 let Some(workspace) = workspace else {
11163 return Ok(Navigated::No);
11164 };
11165 let opened = workspace
11166 .update_in(&mut cx, |workspace, window, cx| {
11167 Self::open_locations_in_multibuffer(
11168 workspace,
11169 locations,
11170 title,
11171 split,
11172 MultibufferSelectionMode::First,
11173 window,
11174 cx,
11175 )
11176 })
11177 .ok();
11178
11179 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11180 })
11181 } else {
11182 Task::ready(Ok(Navigated::No))
11183 }
11184 }
11185
11186 fn compute_target_location(
11187 &self,
11188 lsp_location: lsp::Location,
11189 server_id: LanguageServerId,
11190 window: &mut Window,
11191 cx: &mut Context<Self>,
11192 ) -> Task<anyhow::Result<Option<Location>>> {
11193 let Some(project) = self.project.clone() else {
11194 return Task::ready(Ok(None));
11195 };
11196
11197 cx.spawn_in(window, move |editor, mut cx| async move {
11198 let location_task = editor.update(&mut cx, |_, cx| {
11199 project.update(cx, |project, cx| {
11200 let language_server_name = project
11201 .language_server_statuses(cx)
11202 .find(|(id, _)| server_id == *id)
11203 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11204 language_server_name.map(|language_server_name| {
11205 project.open_local_buffer_via_lsp(
11206 lsp_location.uri.clone(),
11207 server_id,
11208 language_server_name,
11209 cx,
11210 )
11211 })
11212 })
11213 })?;
11214 let location = match location_task {
11215 Some(task) => Some({
11216 let target_buffer_handle = task.await.context("open local buffer")?;
11217 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11218 let target_start = target_buffer
11219 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11220 let target_end = target_buffer
11221 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11222 target_buffer.anchor_after(target_start)
11223 ..target_buffer.anchor_before(target_end)
11224 })?;
11225 Location {
11226 buffer: target_buffer_handle,
11227 range,
11228 }
11229 }),
11230 None => None,
11231 };
11232 Ok(location)
11233 })
11234 }
11235
11236 pub fn find_all_references(
11237 &mut self,
11238 _: &FindAllReferences,
11239 window: &mut Window,
11240 cx: &mut Context<Self>,
11241 ) -> Option<Task<Result<Navigated>>> {
11242 let selection = self.selections.newest::<usize>(cx);
11243 let multi_buffer = self.buffer.read(cx);
11244 let head = selection.head();
11245
11246 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11247 let head_anchor = multi_buffer_snapshot.anchor_at(
11248 head,
11249 if head < selection.tail() {
11250 Bias::Right
11251 } else {
11252 Bias::Left
11253 },
11254 );
11255
11256 match self
11257 .find_all_references_task_sources
11258 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11259 {
11260 Ok(_) => {
11261 log::info!(
11262 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11263 );
11264 return None;
11265 }
11266 Err(i) => {
11267 self.find_all_references_task_sources.insert(i, head_anchor);
11268 }
11269 }
11270
11271 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11272 let workspace = self.workspace()?;
11273 let project = workspace.read(cx).project().clone();
11274 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11275 Some(cx.spawn_in(window, |editor, mut cx| async move {
11276 let _cleanup = defer({
11277 let mut cx = cx.clone();
11278 move || {
11279 let _ = editor.update(&mut cx, |editor, _| {
11280 if let Ok(i) =
11281 editor
11282 .find_all_references_task_sources
11283 .binary_search_by(|anchor| {
11284 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11285 })
11286 {
11287 editor.find_all_references_task_sources.remove(i);
11288 }
11289 });
11290 }
11291 });
11292
11293 let locations = references.await?;
11294 if locations.is_empty() {
11295 return anyhow::Ok(Navigated::No);
11296 }
11297
11298 workspace.update_in(&mut cx, |workspace, window, cx| {
11299 let title = locations
11300 .first()
11301 .as_ref()
11302 .map(|location| {
11303 let buffer = location.buffer.read(cx);
11304 format!(
11305 "References to `{}`",
11306 buffer
11307 .text_for_range(location.range.clone())
11308 .collect::<String>()
11309 )
11310 })
11311 .unwrap();
11312 Self::open_locations_in_multibuffer(
11313 workspace,
11314 locations,
11315 title,
11316 false,
11317 MultibufferSelectionMode::First,
11318 window,
11319 cx,
11320 );
11321 Navigated::Yes
11322 })
11323 }))
11324 }
11325
11326 /// Opens a multibuffer with the given project locations in it
11327 pub fn open_locations_in_multibuffer(
11328 workspace: &mut Workspace,
11329 mut locations: Vec<Location>,
11330 title: String,
11331 split: bool,
11332 multibuffer_selection_mode: MultibufferSelectionMode,
11333 window: &mut Window,
11334 cx: &mut Context<Workspace>,
11335 ) {
11336 // If there are multiple definitions, open them in a multibuffer
11337 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
11338 let mut locations = locations.into_iter().peekable();
11339 let mut ranges = Vec::new();
11340 let capability = workspace.project().read(cx).capability();
11341
11342 let excerpt_buffer = cx.new(|cx| {
11343 let mut multibuffer = MultiBuffer::new(capability);
11344 while let Some(location) = locations.next() {
11345 let buffer = location.buffer.read(cx);
11346 let mut ranges_for_buffer = Vec::new();
11347 let range = location.range.to_offset(buffer);
11348 ranges_for_buffer.push(range.clone());
11349
11350 while let Some(next_location) = locations.peek() {
11351 if next_location.buffer == location.buffer {
11352 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11353 locations.next();
11354 } else {
11355 break;
11356 }
11357 }
11358
11359 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11360 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11361 location.buffer.clone(),
11362 ranges_for_buffer,
11363 DEFAULT_MULTIBUFFER_CONTEXT,
11364 cx,
11365 ))
11366 }
11367
11368 multibuffer.with_title(title)
11369 });
11370
11371 let editor = cx.new(|cx| {
11372 Editor::for_multibuffer(
11373 excerpt_buffer,
11374 Some(workspace.project().clone()),
11375 true,
11376 window,
11377 cx,
11378 )
11379 });
11380 editor.update(cx, |editor, cx| {
11381 match multibuffer_selection_mode {
11382 MultibufferSelectionMode::First => {
11383 if let Some(first_range) = ranges.first() {
11384 editor.change_selections(None, window, cx, |selections| {
11385 selections.clear_disjoint();
11386 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11387 });
11388 }
11389 editor.highlight_background::<Self>(
11390 &ranges,
11391 |theme| theme.editor_highlighted_line_background,
11392 cx,
11393 );
11394 }
11395 MultibufferSelectionMode::All => {
11396 editor.change_selections(None, window, cx, |selections| {
11397 selections.clear_disjoint();
11398 selections.select_anchor_ranges(ranges);
11399 });
11400 }
11401 }
11402 editor.register_buffers_with_language_servers(cx);
11403 });
11404
11405 let item = Box::new(editor);
11406 let item_id = item.item_id();
11407
11408 if split {
11409 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11410 } else {
11411 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11412 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11413 pane.close_current_preview_item(window, cx)
11414 } else {
11415 None
11416 }
11417 });
11418 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11419 }
11420 workspace.active_pane().update(cx, |pane, cx| {
11421 pane.set_preview_item_id(Some(item_id), cx);
11422 });
11423 }
11424
11425 pub fn rename(
11426 &mut self,
11427 _: &Rename,
11428 window: &mut Window,
11429 cx: &mut Context<Self>,
11430 ) -> Option<Task<Result<()>>> {
11431 use language::ToOffset as _;
11432
11433 let provider = self.semantics_provider.clone()?;
11434 let selection = self.selections.newest_anchor().clone();
11435 let (cursor_buffer, cursor_buffer_position) = self
11436 .buffer
11437 .read(cx)
11438 .text_anchor_for_position(selection.head(), cx)?;
11439 let (tail_buffer, cursor_buffer_position_end) = self
11440 .buffer
11441 .read(cx)
11442 .text_anchor_for_position(selection.tail(), cx)?;
11443 if tail_buffer != cursor_buffer {
11444 return None;
11445 }
11446
11447 let snapshot = cursor_buffer.read(cx).snapshot();
11448 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11449 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11450 let prepare_rename = provider
11451 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11452 .unwrap_or_else(|| Task::ready(Ok(None)));
11453 drop(snapshot);
11454
11455 Some(cx.spawn_in(window, |this, mut cx| async move {
11456 let rename_range = if let Some(range) = prepare_rename.await? {
11457 Some(range)
11458 } else {
11459 this.update(&mut cx, |this, cx| {
11460 let buffer = this.buffer.read(cx).snapshot(cx);
11461 let mut buffer_highlights = this
11462 .document_highlights_for_position(selection.head(), &buffer)
11463 .filter(|highlight| {
11464 highlight.start.excerpt_id == selection.head().excerpt_id
11465 && highlight.end.excerpt_id == selection.head().excerpt_id
11466 });
11467 buffer_highlights
11468 .next()
11469 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11470 })?
11471 };
11472 if let Some(rename_range) = rename_range {
11473 this.update_in(&mut cx, |this, window, cx| {
11474 let snapshot = cursor_buffer.read(cx).snapshot();
11475 let rename_buffer_range = rename_range.to_offset(&snapshot);
11476 let cursor_offset_in_rename_range =
11477 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11478 let cursor_offset_in_rename_range_end =
11479 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11480
11481 this.take_rename(false, window, cx);
11482 let buffer = this.buffer.read(cx).read(cx);
11483 let cursor_offset = selection.head().to_offset(&buffer);
11484 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11485 let rename_end = rename_start + rename_buffer_range.len();
11486 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11487 let mut old_highlight_id = None;
11488 let old_name: Arc<str> = buffer
11489 .chunks(rename_start..rename_end, true)
11490 .map(|chunk| {
11491 if old_highlight_id.is_none() {
11492 old_highlight_id = chunk.syntax_highlight_id;
11493 }
11494 chunk.text
11495 })
11496 .collect::<String>()
11497 .into();
11498
11499 drop(buffer);
11500
11501 // Position the selection in the rename editor so that it matches the current selection.
11502 this.show_local_selections = false;
11503 let rename_editor = cx.new(|cx| {
11504 let mut editor = Editor::single_line(window, cx);
11505 editor.buffer.update(cx, |buffer, cx| {
11506 buffer.edit([(0..0, old_name.clone())], None, cx)
11507 });
11508 let rename_selection_range = match cursor_offset_in_rename_range
11509 .cmp(&cursor_offset_in_rename_range_end)
11510 {
11511 Ordering::Equal => {
11512 editor.select_all(&SelectAll, window, cx);
11513 return editor;
11514 }
11515 Ordering::Less => {
11516 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11517 }
11518 Ordering::Greater => {
11519 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11520 }
11521 };
11522 if rename_selection_range.end > old_name.len() {
11523 editor.select_all(&SelectAll, window, cx);
11524 } else {
11525 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11526 s.select_ranges([rename_selection_range]);
11527 });
11528 }
11529 editor
11530 });
11531 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11532 if e == &EditorEvent::Focused {
11533 cx.emit(EditorEvent::FocusedIn)
11534 }
11535 })
11536 .detach();
11537
11538 let write_highlights =
11539 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11540 let read_highlights =
11541 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11542 let ranges = write_highlights
11543 .iter()
11544 .flat_map(|(_, ranges)| ranges.iter())
11545 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11546 .cloned()
11547 .collect();
11548
11549 this.highlight_text::<Rename>(
11550 ranges,
11551 HighlightStyle {
11552 fade_out: Some(0.6),
11553 ..Default::default()
11554 },
11555 cx,
11556 );
11557 let rename_focus_handle = rename_editor.focus_handle(cx);
11558 window.focus(&rename_focus_handle);
11559 let block_id = this.insert_blocks(
11560 [BlockProperties {
11561 style: BlockStyle::Flex,
11562 placement: BlockPlacement::Below(range.start),
11563 height: 1,
11564 render: Arc::new({
11565 let rename_editor = rename_editor.clone();
11566 move |cx: &mut BlockContext| {
11567 let mut text_style = cx.editor_style.text.clone();
11568 if let Some(highlight_style) = old_highlight_id
11569 .and_then(|h| h.style(&cx.editor_style.syntax))
11570 {
11571 text_style = text_style.highlight(highlight_style);
11572 }
11573 div()
11574 .block_mouse_down()
11575 .pl(cx.anchor_x)
11576 .child(EditorElement::new(
11577 &rename_editor,
11578 EditorStyle {
11579 background: cx.theme().system().transparent,
11580 local_player: cx.editor_style.local_player,
11581 text: text_style,
11582 scrollbar_width: cx.editor_style.scrollbar_width,
11583 syntax: cx.editor_style.syntax.clone(),
11584 status: cx.editor_style.status.clone(),
11585 inlay_hints_style: HighlightStyle {
11586 font_weight: Some(FontWeight::BOLD),
11587 ..make_inlay_hints_style(cx.app)
11588 },
11589 inline_completion_styles: make_suggestion_styles(
11590 cx.app,
11591 ),
11592 ..EditorStyle::default()
11593 },
11594 ))
11595 .into_any_element()
11596 }
11597 }),
11598 priority: 0,
11599 }],
11600 Some(Autoscroll::fit()),
11601 cx,
11602 )[0];
11603 this.pending_rename = Some(RenameState {
11604 range,
11605 old_name,
11606 editor: rename_editor,
11607 block_id,
11608 });
11609 })?;
11610 }
11611
11612 Ok(())
11613 }))
11614 }
11615
11616 pub fn confirm_rename(
11617 &mut self,
11618 _: &ConfirmRename,
11619 window: &mut Window,
11620 cx: &mut Context<Self>,
11621 ) -> Option<Task<Result<()>>> {
11622 let rename = self.take_rename(false, window, cx)?;
11623 let workspace = self.workspace()?.downgrade();
11624 let (buffer, start) = self
11625 .buffer
11626 .read(cx)
11627 .text_anchor_for_position(rename.range.start, cx)?;
11628 let (end_buffer, _) = self
11629 .buffer
11630 .read(cx)
11631 .text_anchor_for_position(rename.range.end, cx)?;
11632 if buffer != end_buffer {
11633 return None;
11634 }
11635
11636 let old_name = rename.old_name;
11637 let new_name = rename.editor.read(cx).text(cx);
11638
11639 let rename = self.semantics_provider.as_ref()?.perform_rename(
11640 &buffer,
11641 start,
11642 new_name.clone(),
11643 cx,
11644 )?;
11645
11646 Some(cx.spawn_in(window, |editor, mut cx| async move {
11647 let project_transaction = rename.await?;
11648 Self::open_project_transaction(
11649 &editor,
11650 workspace,
11651 project_transaction,
11652 format!("Rename: {} → {}", old_name, new_name),
11653 cx.clone(),
11654 )
11655 .await?;
11656
11657 editor.update(&mut cx, |editor, cx| {
11658 editor.refresh_document_highlights(cx);
11659 })?;
11660 Ok(())
11661 }))
11662 }
11663
11664 fn take_rename(
11665 &mut self,
11666 moving_cursor: bool,
11667 window: &mut Window,
11668 cx: &mut Context<Self>,
11669 ) -> Option<RenameState> {
11670 let rename = self.pending_rename.take()?;
11671 if rename.editor.focus_handle(cx).is_focused(window) {
11672 window.focus(&self.focus_handle);
11673 }
11674
11675 self.remove_blocks(
11676 [rename.block_id].into_iter().collect(),
11677 Some(Autoscroll::fit()),
11678 cx,
11679 );
11680 self.clear_highlights::<Rename>(cx);
11681 self.show_local_selections = true;
11682
11683 if moving_cursor {
11684 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11685 editor.selections.newest::<usize>(cx).head()
11686 });
11687
11688 // Update the selection to match the position of the selection inside
11689 // the rename editor.
11690 let snapshot = self.buffer.read(cx).read(cx);
11691 let rename_range = rename.range.to_offset(&snapshot);
11692 let cursor_in_editor = snapshot
11693 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11694 .min(rename_range.end);
11695 drop(snapshot);
11696
11697 self.change_selections(None, window, cx, |s| {
11698 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11699 });
11700 } else {
11701 self.refresh_document_highlights(cx);
11702 }
11703
11704 Some(rename)
11705 }
11706
11707 pub fn pending_rename(&self) -> Option<&RenameState> {
11708 self.pending_rename.as_ref()
11709 }
11710
11711 fn format(
11712 &mut self,
11713 _: &Format,
11714 window: &mut Window,
11715 cx: &mut Context<Self>,
11716 ) -> Option<Task<Result<()>>> {
11717 let project = match &self.project {
11718 Some(project) => project.clone(),
11719 None => return None,
11720 };
11721
11722 Some(self.perform_format(
11723 project,
11724 FormatTrigger::Manual,
11725 FormatTarget::Buffers,
11726 window,
11727 cx,
11728 ))
11729 }
11730
11731 fn format_selections(
11732 &mut self,
11733 _: &FormatSelections,
11734 window: &mut Window,
11735 cx: &mut Context<Self>,
11736 ) -> Option<Task<Result<()>>> {
11737 let project = match &self.project {
11738 Some(project) => project.clone(),
11739 None => return None,
11740 };
11741
11742 let ranges = self
11743 .selections
11744 .all_adjusted(cx)
11745 .into_iter()
11746 .map(|selection| selection.range())
11747 .collect_vec();
11748
11749 Some(self.perform_format(
11750 project,
11751 FormatTrigger::Manual,
11752 FormatTarget::Ranges(ranges),
11753 window,
11754 cx,
11755 ))
11756 }
11757
11758 fn perform_format(
11759 &mut self,
11760 project: Entity<Project>,
11761 trigger: FormatTrigger,
11762 target: FormatTarget,
11763 window: &mut Window,
11764 cx: &mut Context<Self>,
11765 ) -> Task<Result<()>> {
11766 let buffer = self.buffer.clone();
11767 let (buffers, target) = match target {
11768 FormatTarget::Buffers => {
11769 let mut buffers = buffer.read(cx).all_buffers();
11770 if trigger == FormatTrigger::Save {
11771 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11772 }
11773 (buffers, LspFormatTarget::Buffers)
11774 }
11775 FormatTarget::Ranges(selection_ranges) => {
11776 let multi_buffer = buffer.read(cx);
11777 let snapshot = multi_buffer.read(cx);
11778 let mut buffers = HashSet::default();
11779 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11780 BTreeMap::new();
11781 for selection_range in selection_ranges {
11782 for (buffer, buffer_range, _) in
11783 snapshot.range_to_buffer_ranges(selection_range)
11784 {
11785 let buffer_id = buffer.remote_id();
11786 let start = buffer.anchor_before(buffer_range.start);
11787 let end = buffer.anchor_after(buffer_range.end);
11788 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11789 buffer_id_to_ranges
11790 .entry(buffer_id)
11791 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11792 .or_insert_with(|| vec![start..end]);
11793 }
11794 }
11795 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11796 }
11797 };
11798
11799 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11800 let format = project.update(cx, |project, cx| {
11801 project.format(buffers, target, true, trigger, cx)
11802 });
11803
11804 cx.spawn_in(window, |_, mut cx| async move {
11805 let transaction = futures::select_biased! {
11806 () = timeout => {
11807 log::warn!("timed out waiting for formatting");
11808 None
11809 }
11810 transaction = format.log_err().fuse() => transaction,
11811 };
11812
11813 buffer
11814 .update(&mut cx, |buffer, cx| {
11815 if let Some(transaction) = transaction {
11816 if !buffer.is_singleton() {
11817 buffer.push_transaction(&transaction.0, cx);
11818 }
11819 }
11820
11821 cx.notify();
11822 })
11823 .ok();
11824
11825 Ok(())
11826 })
11827 }
11828
11829 fn restart_language_server(
11830 &mut self,
11831 _: &RestartLanguageServer,
11832 _: &mut Window,
11833 cx: &mut Context<Self>,
11834 ) {
11835 if let Some(project) = self.project.clone() {
11836 self.buffer.update(cx, |multi_buffer, cx| {
11837 project.update(cx, |project, cx| {
11838 project.restart_language_servers_for_buffers(
11839 multi_buffer.all_buffers().into_iter().collect(),
11840 cx,
11841 );
11842 });
11843 })
11844 }
11845 }
11846
11847 fn cancel_language_server_work(
11848 workspace: &mut Workspace,
11849 _: &actions::CancelLanguageServerWork,
11850 _: &mut Window,
11851 cx: &mut Context<Workspace>,
11852 ) {
11853 let project = workspace.project();
11854 let buffers = workspace
11855 .active_item(cx)
11856 .and_then(|item| item.act_as::<Editor>(cx))
11857 .map_or(HashSet::default(), |editor| {
11858 editor.read(cx).buffer.read(cx).all_buffers()
11859 });
11860 project.update(cx, |project, cx| {
11861 project.cancel_language_server_work_for_buffers(buffers, cx);
11862 });
11863 }
11864
11865 fn show_character_palette(
11866 &mut self,
11867 _: &ShowCharacterPalette,
11868 window: &mut Window,
11869 _: &mut Context<Self>,
11870 ) {
11871 window.show_character_palette();
11872 }
11873
11874 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11875 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11876 let buffer = self.buffer.read(cx).snapshot(cx);
11877 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11878 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11879 let is_valid = buffer
11880 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11881 .any(|entry| {
11882 entry.diagnostic.is_primary
11883 && !entry.range.is_empty()
11884 && entry.range.start == primary_range_start
11885 && entry.diagnostic.message == active_diagnostics.primary_message
11886 });
11887
11888 if is_valid != active_diagnostics.is_valid {
11889 active_diagnostics.is_valid = is_valid;
11890 let mut new_styles = HashMap::default();
11891 for (block_id, diagnostic) in &active_diagnostics.blocks {
11892 new_styles.insert(
11893 *block_id,
11894 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11895 );
11896 }
11897 self.display_map.update(cx, |display_map, _cx| {
11898 display_map.replace_blocks(new_styles)
11899 });
11900 }
11901 }
11902 }
11903
11904 fn activate_diagnostics(
11905 &mut self,
11906 buffer_id: BufferId,
11907 group_id: usize,
11908 window: &mut Window,
11909 cx: &mut Context<Self>,
11910 ) {
11911 self.dismiss_diagnostics(cx);
11912 let snapshot = self.snapshot(window, cx);
11913 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11914 let buffer = self.buffer.read(cx).snapshot(cx);
11915
11916 let mut primary_range = None;
11917 let mut primary_message = None;
11918 let diagnostic_group = buffer
11919 .diagnostic_group(buffer_id, group_id)
11920 .filter_map(|entry| {
11921 let start = entry.range.start;
11922 let end = entry.range.end;
11923 if snapshot.is_line_folded(MultiBufferRow(start.row))
11924 && (start.row == end.row
11925 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11926 {
11927 return None;
11928 }
11929 if entry.diagnostic.is_primary {
11930 primary_range = Some(entry.range.clone());
11931 primary_message = Some(entry.diagnostic.message.clone());
11932 }
11933 Some(entry)
11934 })
11935 .collect::<Vec<_>>();
11936 let primary_range = primary_range?;
11937 let primary_message = primary_message?;
11938
11939 let blocks = display_map
11940 .insert_blocks(
11941 diagnostic_group.iter().map(|entry| {
11942 let diagnostic = entry.diagnostic.clone();
11943 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11944 BlockProperties {
11945 style: BlockStyle::Fixed,
11946 placement: BlockPlacement::Below(
11947 buffer.anchor_after(entry.range.start),
11948 ),
11949 height: message_height,
11950 render: diagnostic_block_renderer(diagnostic, None, true, true),
11951 priority: 0,
11952 }
11953 }),
11954 cx,
11955 )
11956 .into_iter()
11957 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11958 .collect();
11959
11960 Some(ActiveDiagnosticGroup {
11961 primary_range: buffer.anchor_before(primary_range.start)
11962 ..buffer.anchor_after(primary_range.end),
11963 primary_message,
11964 group_id,
11965 blocks,
11966 is_valid: true,
11967 })
11968 });
11969 }
11970
11971 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11972 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11973 self.display_map.update(cx, |display_map, cx| {
11974 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11975 });
11976 cx.notify();
11977 }
11978 }
11979
11980 /// Disable inline diagnostics rendering for this editor.
11981 pub fn disable_inline_diagnostics(&mut self) {
11982 self.inline_diagnostics_enabled = false;
11983 self.inline_diagnostics_update = Task::ready(());
11984 self.inline_diagnostics.clear();
11985 }
11986
11987 pub fn inline_diagnostics_enabled(&self) -> bool {
11988 self.inline_diagnostics_enabled
11989 }
11990
11991 pub fn show_inline_diagnostics(&self) -> bool {
11992 self.show_inline_diagnostics
11993 }
11994
11995 pub fn toggle_inline_diagnostics(
11996 &mut self,
11997 _: &ToggleInlineDiagnostics,
11998 window: &mut Window,
11999 cx: &mut Context<'_, Editor>,
12000 ) {
12001 self.show_inline_diagnostics = !self.show_inline_diagnostics;
12002 self.refresh_inline_diagnostics(false, window, cx);
12003 }
12004
12005 fn refresh_inline_diagnostics(
12006 &mut self,
12007 debounce: bool,
12008 window: &mut Window,
12009 cx: &mut Context<Self>,
12010 ) {
12011 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12012 self.inline_diagnostics_update = Task::ready(());
12013 self.inline_diagnostics.clear();
12014 return;
12015 }
12016
12017 let debounce_ms = ProjectSettings::get_global(cx)
12018 .diagnostics
12019 .inline
12020 .update_debounce_ms;
12021 let debounce = if debounce && debounce_ms > 0 {
12022 Some(Duration::from_millis(debounce_ms))
12023 } else {
12024 None
12025 };
12026 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12027 if let Some(debounce) = debounce {
12028 cx.background_executor().timer(debounce).await;
12029 }
12030 let Some(snapshot) = editor
12031 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12032 .ok()
12033 else {
12034 return;
12035 };
12036
12037 let new_inline_diagnostics = cx
12038 .background_spawn(async move {
12039 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12040 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12041 let message = diagnostic_entry
12042 .diagnostic
12043 .message
12044 .split_once('\n')
12045 .map(|(line, _)| line)
12046 .map(SharedString::new)
12047 .unwrap_or_else(|| {
12048 SharedString::from(diagnostic_entry.diagnostic.message)
12049 });
12050 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12051 let (Ok(i) | Err(i)) = inline_diagnostics
12052 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12053 inline_diagnostics.insert(
12054 i,
12055 (
12056 start_anchor,
12057 InlineDiagnostic {
12058 message,
12059 group_id: diagnostic_entry.diagnostic.group_id,
12060 start: diagnostic_entry.range.start.to_point(&snapshot),
12061 is_primary: diagnostic_entry.diagnostic.is_primary,
12062 severity: diagnostic_entry.diagnostic.severity,
12063 },
12064 ),
12065 );
12066 }
12067 inline_diagnostics
12068 })
12069 .await;
12070
12071 editor
12072 .update(&mut cx, |editor, cx| {
12073 editor.inline_diagnostics = new_inline_diagnostics;
12074 cx.notify();
12075 })
12076 .ok();
12077 });
12078 }
12079
12080 pub fn set_selections_from_remote(
12081 &mut self,
12082 selections: Vec<Selection<Anchor>>,
12083 pending_selection: Option<Selection<Anchor>>,
12084 window: &mut Window,
12085 cx: &mut Context<Self>,
12086 ) {
12087 let old_cursor_position = self.selections.newest_anchor().head();
12088 self.selections.change_with(cx, |s| {
12089 s.select_anchors(selections);
12090 if let Some(pending_selection) = pending_selection {
12091 s.set_pending(pending_selection, SelectMode::Character);
12092 } else {
12093 s.clear_pending();
12094 }
12095 });
12096 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12097 }
12098
12099 fn push_to_selection_history(&mut self) {
12100 self.selection_history.push(SelectionHistoryEntry {
12101 selections: self.selections.disjoint_anchors(),
12102 select_next_state: self.select_next_state.clone(),
12103 select_prev_state: self.select_prev_state.clone(),
12104 add_selections_state: self.add_selections_state.clone(),
12105 });
12106 }
12107
12108 pub fn transact(
12109 &mut self,
12110 window: &mut Window,
12111 cx: &mut Context<Self>,
12112 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12113 ) -> Option<TransactionId> {
12114 self.start_transaction_at(Instant::now(), window, cx);
12115 update(self, window, cx);
12116 self.end_transaction_at(Instant::now(), cx)
12117 }
12118
12119 pub fn start_transaction_at(
12120 &mut self,
12121 now: Instant,
12122 window: &mut Window,
12123 cx: &mut Context<Self>,
12124 ) {
12125 self.end_selection(window, cx);
12126 if let Some(tx_id) = self
12127 .buffer
12128 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
12129 {
12130 self.selection_history
12131 .insert_transaction(tx_id, self.selections.disjoint_anchors());
12132 cx.emit(EditorEvent::TransactionBegun {
12133 transaction_id: tx_id,
12134 })
12135 }
12136 }
12137
12138 pub fn end_transaction_at(
12139 &mut self,
12140 now: Instant,
12141 cx: &mut Context<Self>,
12142 ) -> Option<TransactionId> {
12143 if let Some(transaction_id) = self
12144 .buffer
12145 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
12146 {
12147 if let Some((_, end_selections)) =
12148 self.selection_history.transaction_mut(transaction_id)
12149 {
12150 *end_selections = Some(self.selections.disjoint_anchors());
12151 } else {
12152 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
12153 }
12154
12155 cx.emit(EditorEvent::Edited { transaction_id });
12156 Some(transaction_id)
12157 } else {
12158 None
12159 }
12160 }
12161
12162 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
12163 if self.selection_mark_mode {
12164 self.change_selections(None, window, cx, |s| {
12165 s.move_with(|_, sel| {
12166 sel.collapse_to(sel.head(), SelectionGoal::None);
12167 });
12168 })
12169 }
12170 self.selection_mark_mode = true;
12171 cx.notify();
12172 }
12173
12174 pub fn swap_selection_ends(
12175 &mut self,
12176 _: &actions::SwapSelectionEnds,
12177 window: &mut Window,
12178 cx: &mut Context<Self>,
12179 ) {
12180 self.change_selections(None, window, cx, |s| {
12181 s.move_with(|_, sel| {
12182 if sel.start != sel.end {
12183 sel.reversed = !sel.reversed
12184 }
12185 });
12186 });
12187 self.request_autoscroll(Autoscroll::newest(), cx);
12188 cx.notify();
12189 }
12190
12191 pub fn toggle_fold(
12192 &mut self,
12193 _: &actions::ToggleFold,
12194 window: &mut Window,
12195 cx: &mut Context<Self>,
12196 ) {
12197 if self.is_singleton(cx) {
12198 let selection = self.selections.newest::<Point>(cx);
12199
12200 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12201 let range = if selection.is_empty() {
12202 let point = selection.head().to_display_point(&display_map);
12203 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12204 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12205 .to_point(&display_map);
12206 start..end
12207 } else {
12208 selection.range()
12209 };
12210 if display_map.folds_in_range(range).next().is_some() {
12211 self.unfold_lines(&Default::default(), window, cx)
12212 } else {
12213 self.fold(&Default::default(), window, cx)
12214 }
12215 } else {
12216 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12217 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12218 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12219 .map(|(snapshot, _, _)| snapshot.remote_id())
12220 .collect();
12221
12222 for buffer_id in buffer_ids {
12223 if self.is_buffer_folded(buffer_id, cx) {
12224 self.unfold_buffer(buffer_id, cx);
12225 } else {
12226 self.fold_buffer(buffer_id, cx);
12227 }
12228 }
12229 }
12230 }
12231
12232 pub fn toggle_fold_recursive(
12233 &mut self,
12234 _: &actions::ToggleFoldRecursive,
12235 window: &mut Window,
12236 cx: &mut Context<Self>,
12237 ) {
12238 let selection = self.selections.newest::<Point>(cx);
12239
12240 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12241 let range = if selection.is_empty() {
12242 let point = selection.head().to_display_point(&display_map);
12243 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12244 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12245 .to_point(&display_map);
12246 start..end
12247 } else {
12248 selection.range()
12249 };
12250 if display_map.folds_in_range(range).next().is_some() {
12251 self.unfold_recursive(&Default::default(), window, cx)
12252 } else {
12253 self.fold_recursive(&Default::default(), window, cx)
12254 }
12255 }
12256
12257 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12258 if self.is_singleton(cx) {
12259 let mut to_fold = Vec::new();
12260 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12261 let selections = self.selections.all_adjusted(cx);
12262
12263 for selection in selections {
12264 let range = selection.range().sorted();
12265 let buffer_start_row = range.start.row;
12266
12267 if range.start.row != range.end.row {
12268 let mut found = false;
12269 let mut row = range.start.row;
12270 while row <= range.end.row {
12271 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12272 {
12273 found = true;
12274 row = crease.range().end.row + 1;
12275 to_fold.push(crease);
12276 } else {
12277 row += 1
12278 }
12279 }
12280 if found {
12281 continue;
12282 }
12283 }
12284
12285 for row in (0..=range.start.row).rev() {
12286 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12287 if crease.range().end.row >= buffer_start_row {
12288 to_fold.push(crease);
12289 if row <= range.start.row {
12290 break;
12291 }
12292 }
12293 }
12294 }
12295 }
12296
12297 self.fold_creases(to_fold, true, window, cx);
12298 } else {
12299 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12300
12301 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12302 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12303 .map(|(snapshot, _, _)| snapshot.remote_id())
12304 .collect();
12305 for buffer_id in buffer_ids {
12306 self.fold_buffer(buffer_id, cx);
12307 }
12308 }
12309 }
12310
12311 fn fold_at_level(
12312 &mut self,
12313 fold_at: &FoldAtLevel,
12314 window: &mut Window,
12315 cx: &mut Context<Self>,
12316 ) {
12317 if !self.buffer.read(cx).is_singleton() {
12318 return;
12319 }
12320
12321 let fold_at_level = fold_at.0;
12322 let snapshot = self.buffer.read(cx).snapshot(cx);
12323 let mut to_fold = Vec::new();
12324 let mut stack = vec![(0, snapshot.max_row().0, 1)];
12325
12326 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
12327 while start_row < end_row {
12328 match self
12329 .snapshot(window, cx)
12330 .crease_for_buffer_row(MultiBufferRow(start_row))
12331 {
12332 Some(crease) => {
12333 let nested_start_row = crease.range().start.row + 1;
12334 let nested_end_row = crease.range().end.row;
12335
12336 if current_level < fold_at_level {
12337 stack.push((nested_start_row, nested_end_row, current_level + 1));
12338 } else if current_level == fold_at_level {
12339 to_fold.push(crease);
12340 }
12341
12342 start_row = nested_end_row + 1;
12343 }
12344 None => start_row += 1,
12345 }
12346 }
12347 }
12348
12349 self.fold_creases(to_fold, true, window, cx);
12350 }
12351
12352 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
12353 if self.buffer.read(cx).is_singleton() {
12354 let mut fold_ranges = Vec::new();
12355 let snapshot = self.buffer.read(cx).snapshot(cx);
12356
12357 for row in 0..snapshot.max_row().0 {
12358 if let Some(foldable_range) = self
12359 .snapshot(window, cx)
12360 .crease_for_buffer_row(MultiBufferRow(row))
12361 {
12362 fold_ranges.push(foldable_range);
12363 }
12364 }
12365
12366 self.fold_creases(fold_ranges, true, window, cx);
12367 } else {
12368 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
12369 editor
12370 .update_in(&mut cx, |editor, _, cx| {
12371 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12372 editor.fold_buffer(buffer_id, cx);
12373 }
12374 })
12375 .ok();
12376 });
12377 }
12378 }
12379
12380 pub fn fold_function_bodies(
12381 &mut self,
12382 _: &actions::FoldFunctionBodies,
12383 window: &mut Window,
12384 cx: &mut Context<Self>,
12385 ) {
12386 let snapshot = self.buffer.read(cx).snapshot(cx);
12387
12388 let ranges = snapshot
12389 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
12390 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
12391 .collect::<Vec<_>>();
12392
12393 let creases = ranges
12394 .into_iter()
12395 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
12396 .collect();
12397
12398 self.fold_creases(creases, true, window, cx);
12399 }
12400
12401 pub fn fold_recursive(
12402 &mut self,
12403 _: &actions::FoldRecursive,
12404 window: &mut Window,
12405 cx: &mut Context<Self>,
12406 ) {
12407 let mut to_fold = Vec::new();
12408 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12409 let selections = self.selections.all_adjusted(cx);
12410
12411 for selection in selections {
12412 let range = selection.range().sorted();
12413 let buffer_start_row = range.start.row;
12414
12415 if range.start.row != range.end.row {
12416 let mut found = false;
12417 for row in range.start.row..=range.end.row {
12418 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12419 found = true;
12420 to_fold.push(crease);
12421 }
12422 }
12423 if found {
12424 continue;
12425 }
12426 }
12427
12428 for row in (0..=range.start.row).rev() {
12429 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12430 if crease.range().end.row >= buffer_start_row {
12431 to_fold.push(crease);
12432 } else {
12433 break;
12434 }
12435 }
12436 }
12437 }
12438
12439 self.fold_creases(to_fold, true, window, cx);
12440 }
12441
12442 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
12443 let buffer_row = fold_at.buffer_row;
12444 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12445
12446 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12447 let autoscroll = self
12448 .selections
12449 .all::<Point>(cx)
12450 .iter()
12451 .any(|selection| crease.range().overlaps(&selection.range()));
12452
12453 self.fold_creases(vec![crease], autoscroll, window, cx);
12454 }
12455 }
12456
12457 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12458 if self.is_singleton(cx) {
12459 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12460 let buffer = &display_map.buffer_snapshot;
12461 let selections = self.selections.all::<Point>(cx);
12462 let ranges = selections
12463 .iter()
12464 .map(|s| {
12465 let range = s.display_range(&display_map).sorted();
12466 let mut start = range.start.to_point(&display_map);
12467 let mut end = range.end.to_point(&display_map);
12468 start.column = 0;
12469 end.column = buffer.line_len(MultiBufferRow(end.row));
12470 start..end
12471 })
12472 .collect::<Vec<_>>();
12473
12474 self.unfold_ranges(&ranges, true, true, cx);
12475 } else {
12476 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12477 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12478 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12479 .map(|(snapshot, _, _)| snapshot.remote_id())
12480 .collect();
12481 for buffer_id in buffer_ids {
12482 self.unfold_buffer(buffer_id, cx);
12483 }
12484 }
12485 }
12486
12487 pub fn unfold_recursive(
12488 &mut self,
12489 _: &UnfoldRecursive,
12490 _window: &mut Window,
12491 cx: &mut Context<Self>,
12492 ) {
12493 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12494 let selections = self.selections.all::<Point>(cx);
12495 let ranges = selections
12496 .iter()
12497 .map(|s| {
12498 let mut range = s.display_range(&display_map).sorted();
12499 *range.start.column_mut() = 0;
12500 *range.end.column_mut() = display_map.line_len(range.end.row());
12501 let start = range.start.to_point(&display_map);
12502 let end = range.end.to_point(&display_map);
12503 start..end
12504 })
12505 .collect::<Vec<_>>();
12506
12507 self.unfold_ranges(&ranges, true, true, cx);
12508 }
12509
12510 pub fn unfold_at(
12511 &mut self,
12512 unfold_at: &UnfoldAt,
12513 _window: &mut Window,
12514 cx: &mut Context<Self>,
12515 ) {
12516 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12517
12518 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12519 ..Point::new(
12520 unfold_at.buffer_row.0,
12521 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12522 );
12523
12524 let autoscroll = self
12525 .selections
12526 .all::<Point>(cx)
12527 .iter()
12528 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12529
12530 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12531 }
12532
12533 pub fn unfold_all(
12534 &mut self,
12535 _: &actions::UnfoldAll,
12536 _window: &mut Window,
12537 cx: &mut Context<Self>,
12538 ) {
12539 if self.buffer.read(cx).is_singleton() {
12540 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12541 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12542 } else {
12543 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12544 editor
12545 .update(&mut cx, |editor, cx| {
12546 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12547 editor.unfold_buffer(buffer_id, cx);
12548 }
12549 })
12550 .ok();
12551 });
12552 }
12553 }
12554
12555 pub fn fold_selected_ranges(
12556 &mut self,
12557 _: &FoldSelectedRanges,
12558 window: &mut Window,
12559 cx: &mut Context<Self>,
12560 ) {
12561 let selections = self.selections.all::<Point>(cx);
12562 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12563 let line_mode = self.selections.line_mode;
12564 let ranges = selections
12565 .into_iter()
12566 .map(|s| {
12567 if line_mode {
12568 let start = Point::new(s.start.row, 0);
12569 let end = Point::new(
12570 s.end.row,
12571 display_map
12572 .buffer_snapshot
12573 .line_len(MultiBufferRow(s.end.row)),
12574 );
12575 Crease::simple(start..end, display_map.fold_placeholder.clone())
12576 } else {
12577 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12578 }
12579 })
12580 .collect::<Vec<_>>();
12581 self.fold_creases(ranges, true, window, cx);
12582 }
12583
12584 pub fn fold_ranges<T: ToOffset + Clone>(
12585 &mut self,
12586 ranges: Vec<Range<T>>,
12587 auto_scroll: bool,
12588 window: &mut Window,
12589 cx: &mut Context<Self>,
12590 ) {
12591 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12592 let ranges = ranges
12593 .into_iter()
12594 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12595 .collect::<Vec<_>>();
12596 self.fold_creases(ranges, auto_scroll, window, cx);
12597 }
12598
12599 pub fn fold_creases<T: ToOffset + Clone>(
12600 &mut self,
12601 creases: Vec<Crease<T>>,
12602 auto_scroll: bool,
12603 window: &mut Window,
12604 cx: &mut Context<Self>,
12605 ) {
12606 if creases.is_empty() {
12607 return;
12608 }
12609
12610 let mut buffers_affected = HashSet::default();
12611 let multi_buffer = self.buffer().read(cx);
12612 for crease in &creases {
12613 if let Some((_, buffer, _)) =
12614 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12615 {
12616 buffers_affected.insert(buffer.read(cx).remote_id());
12617 };
12618 }
12619
12620 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12621
12622 if auto_scroll {
12623 self.request_autoscroll(Autoscroll::fit(), cx);
12624 }
12625
12626 cx.notify();
12627
12628 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12629 // Clear diagnostics block when folding a range that contains it.
12630 let snapshot = self.snapshot(window, cx);
12631 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12632 drop(snapshot);
12633 self.active_diagnostics = Some(active_diagnostics);
12634 self.dismiss_diagnostics(cx);
12635 } else {
12636 self.active_diagnostics = Some(active_diagnostics);
12637 }
12638 }
12639
12640 self.scrollbar_marker_state.dirty = true;
12641 }
12642
12643 /// Removes any folds whose ranges intersect any of the given ranges.
12644 pub fn unfold_ranges<T: ToOffset + Clone>(
12645 &mut self,
12646 ranges: &[Range<T>],
12647 inclusive: bool,
12648 auto_scroll: bool,
12649 cx: &mut Context<Self>,
12650 ) {
12651 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12652 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12653 });
12654 }
12655
12656 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12657 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12658 return;
12659 }
12660 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12661 self.display_map
12662 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12663 cx.emit(EditorEvent::BufferFoldToggled {
12664 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12665 folded: true,
12666 });
12667 cx.notify();
12668 }
12669
12670 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12671 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12672 return;
12673 }
12674 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12675 self.display_map.update(cx, |display_map, cx| {
12676 display_map.unfold_buffer(buffer_id, cx);
12677 });
12678 cx.emit(EditorEvent::BufferFoldToggled {
12679 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12680 folded: false,
12681 });
12682 cx.notify();
12683 }
12684
12685 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12686 self.display_map.read(cx).is_buffer_folded(buffer)
12687 }
12688
12689 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12690 self.display_map.read(cx).folded_buffers()
12691 }
12692
12693 /// Removes any folds with the given ranges.
12694 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12695 &mut self,
12696 ranges: &[Range<T>],
12697 type_id: TypeId,
12698 auto_scroll: bool,
12699 cx: &mut Context<Self>,
12700 ) {
12701 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12702 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12703 });
12704 }
12705
12706 fn remove_folds_with<T: ToOffset + Clone>(
12707 &mut self,
12708 ranges: &[Range<T>],
12709 auto_scroll: bool,
12710 cx: &mut Context<Self>,
12711 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12712 ) {
12713 if ranges.is_empty() {
12714 return;
12715 }
12716
12717 let mut buffers_affected = HashSet::default();
12718 let multi_buffer = self.buffer().read(cx);
12719 for range in ranges {
12720 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12721 buffers_affected.insert(buffer.read(cx).remote_id());
12722 };
12723 }
12724
12725 self.display_map.update(cx, update);
12726
12727 if auto_scroll {
12728 self.request_autoscroll(Autoscroll::fit(), cx);
12729 }
12730
12731 cx.notify();
12732 self.scrollbar_marker_state.dirty = true;
12733 self.active_indent_guides_state.dirty = true;
12734 }
12735
12736 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12737 self.display_map.read(cx).fold_placeholder.clone()
12738 }
12739
12740 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12741 self.buffer.update(cx, |buffer, cx| {
12742 buffer.set_all_diff_hunks_expanded(cx);
12743 });
12744 }
12745
12746 pub fn set_distinguish_unstaged_diff_hunks(&mut self) {
12747 self.distinguish_unstaged_diff_hunks = true;
12748 }
12749
12750 pub fn expand_all_diff_hunks(
12751 &mut self,
12752 _: &ExpandAllHunkDiffs,
12753 _window: &mut Window,
12754 cx: &mut Context<Self>,
12755 ) {
12756 self.buffer.update(cx, |buffer, cx| {
12757 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12758 });
12759 }
12760
12761 pub fn toggle_selected_diff_hunks(
12762 &mut self,
12763 _: &ToggleSelectedDiffHunks,
12764 _window: &mut Window,
12765 cx: &mut Context<Self>,
12766 ) {
12767 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12768 self.toggle_diff_hunks_in_ranges(ranges, cx);
12769 }
12770
12771 fn diff_hunks_in_ranges<'a>(
12772 &'a self,
12773 ranges: &'a [Range<Anchor>],
12774 buffer: &'a MultiBufferSnapshot,
12775 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
12776 ranges.iter().flat_map(move |range| {
12777 let end_excerpt_id = range.end.excerpt_id;
12778 let range = range.to_point(buffer);
12779 let mut peek_end = range.end;
12780 if range.end.row < buffer.max_row().0 {
12781 peek_end = Point::new(range.end.row + 1, 0);
12782 }
12783 buffer
12784 .diff_hunks_in_range(range.start..peek_end)
12785 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
12786 })
12787 }
12788
12789 pub fn has_stageable_diff_hunks_in_ranges(
12790 &self,
12791 ranges: &[Range<Anchor>],
12792 snapshot: &MultiBufferSnapshot,
12793 ) -> bool {
12794 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
12795 hunks.any(|hunk| hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk)
12796 }
12797
12798 pub fn toggle_staged_selected_diff_hunks(
12799 &mut self,
12800 _: &::git::ToggleStaged,
12801 _window: &mut Window,
12802 cx: &mut Context<Self>,
12803 ) {
12804 let snapshot = self.buffer.read(cx).snapshot(cx);
12805 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12806 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
12807 self.stage_or_unstage_diff_hunks(stage, &ranges, cx);
12808 }
12809
12810 pub fn stage_and_next(
12811 &mut self,
12812 _: &::git::StageAndNext,
12813 window: &mut Window,
12814 cx: &mut Context<Self>,
12815 ) {
12816 let head = self.selections.newest_anchor().head();
12817 self.stage_or_unstage_diff_hunks(true, &[head..head], cx);
12818 self.go_to_next_hunk(&Default::default(), window, cx);
12819 }
12820
12821 pub fn unstage_and_next(
12822 &mut self,
12823 _: &::git::UnstageAndNext,
12824 window: &mut Window,
12825 cx: &mut Context<Self>,
12826 ) {
12827 let head = self.selections.newest_anchor().head();
12828 self.stage_or_unstage_diff_hunks(false, &[head..head], cx);
12829 self.go_to_next_hunk(&Default::default(), window, cx);
12830 }
12831
12832 pub fn stage_or_unstage_diff_hunks(
12833 &mut self,
12834 stage: bool,
12835 ranges: &[Range<Anchor>],
12836 cx: &mut Context<Self>,
12837 ) {
12838 let snapshot = self.buffer.read(cx).snapshot(cx);
12839 let Some(project) = &self.project else {
12840 return;
12841 };
12842
12843 let chunk_by = self
12844 .diff_hunks_in_ranges(&ranges, &snapshot)
12845 .chunk_by(|hunk| hunk.buffer_id);
12846 for (buffer_id, hunks) in &chunk_by {
12847 Self::do_stage_or_unstage(project, stage, buffer_id, hunks, &snapshot, cx);
12848 }
12849 }
12850
12851 fn do_stage_or_unstage(
12852 project: &Entity<Project>,
12853 stage: bool,
12854 buffer_id: BufferId,
12855 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
12856 snapshot: &MultiBufferSnapshot,
12857 cx: &mut Context<Self>,
12858 ) {
12859 let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
12860 log::debug!("no buffer for id");
12861 return;
12862 };
12863 let buffer = buffer.read(cx).snapshot();
12864 let Some((repo, path)) = project
12865 .read(cx)
12866 .repository_and_path_for_buffer_id(buffer_id, cx)
12867 else {
12868 log::debug!("no git repo for buffer id");
12869 return;
12870 };
12871 let Some(diff) = snapshot.diff_for_buffer_id(buffer_id) else {
12872 log::debug!("no diff for buffer id");
12873 return;
12874 };
12875 let Some(secondary_diff) = diff.secondary_diff() else {
12876 log::debug!("no secondary diff for buffer id");
12877 return;
12878 };
12879
12880 let edits = diff.secondary_edits_for_stage_or_unstage(
12881 stage,
12882 hunks.filter_map(|hunk| {
12883 if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None {
12884 return None;
12885 } else if !stage
12886 && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk
12887 {
12888 return None;
12889 }
12890 Some((
12891 hunk.diff_base_byte_range.clone(),
12892 hunk.secondary_diff_base_byte_range.clone(),
12893 hunk.buffer_range.clone(),
12894 ))
12895 }),
12896 &buffer,
12897 );
12898
12899 let Some(index_base) = secondary_diff
12900 .base_text()
12901 .map(|snapshot| snapshot.text.as_rope().clone())
12902 else {
12903 log::debug!("no index base");
12904 return;
12905 };
12906 let index_buffer = cx.new(|cx| {
12907 Buffer::local_normalized(index_base.clone(), text::LineEnding::default(), cx)
12908 });
12909 let new_index_text = index_buffer.update(cx, |index_buffer, cx| {
12910 index_buffer.edit(edits, None, cx);
12911 index_buffer.snapshot().as_rope().to_string()
12912 });
12913 let new_index_text = if new_index_text.is_empty()
12914 && (diff.is_single_insertion
12915 || buffer
12916 .file()
12917 .map_or(false, |file| file.disk_state() == DiskState::New))
12918 {
12919 log::debug!("removing from index");
12920 None
12921 } else {
12922 Some(new_index_text)
12923 };
12924
12925 let _ = repo.read(cx).set_index_text(&path, new_index_text);
12926 }
12927
12928 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12929 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12930 self.buffer
12931 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12932 }
12933
12934 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12935 self.buffer.update(cx, |buffer, cx| {
12936 let ranges = vec![Anchor::min()..Anchor::max()];
12937 if !buffer.all_diff_hunks_expanded()
12938 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12939 {
12940 buffer.collapse_diff_hunks(ranges, cx);
12941 true
12942 } else {
12943 false
12944 }
12945 })
12946 }
12947
12948 fn toggle_diff_hunks_in_ranges(
12949 &mut self,
12950 ranges: Vec<Range<Anchor>>,
12951 cx: &mut Context<'_, Editor>,
12952 ) {
12953 self.buffer.update(cx, |buffer, cx| {
12954 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12955 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
12956 })
12957 }
12958
12959 fn toggle_diff_hunks_in_ranges_narrow(
12960 &mut self,
12961 ranges: Vec<Range<Anchor>>,
12962 cx: &mut Context<'_, Editor>,
12963 ) {
12964 self.buffer.update(cx, |buffer, cx| {
12965 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12966 buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
12967 })
12968 }
12969
12970 pub(crate) fn apply_all_diff_hunks(
12971 &mut self,
12972 _: &ApplyAllDiffHunks,
12973 window: &mut Window,
12974 cx: &mut Context<Self>,
12975 ) {
12976 let buffers = self.buffer.read(cx).all_buffers();
12977 for branch_buffer in buffers {
12978 branch_buffer.update(cx, |branch_buffer, cx| {
12979 branch_buffer.merge_into_base(Vec::new(), cx);
12980 });
12981 }
12982
12983 if let Some(project) = self.project.clone() {
12984 self.save(true, project, window, cx).detach_and_log_err(cx);
12985 }
12986 }
12987
12988 pub(crate) fn apply_selected_diff_hunks(
12989 &mut self,
12990 _: &ApplyDiffHunk,
12991 window: &mut Window,
12992 cx: &mut Context<Self>,
12993 ) {
12994 let snapshot = self.snapshot(window, cx);
12995 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
12996 let mut ranges_by_buffer = HashMap::default();
12997 self.transact(window, cx, |editor, _window, cx| {
12998 for hunk in hunks {
12999 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13000 ranges_by_buffer
13001 .entry(buffer.clone())
13002 .or_insert_with(Vec::new)
13003 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13004 }
13005 }
13006
13007 for (buffer, ranges) in ranges_by_buffer {
13008 buffer.update(cx, |buffer, cx| {
13009 buffer.merge_into_base(ranges, cx);
13010 });
13011 }
13012 });
13013
13014 if let Some(project) = self.project.clone() {
13015 self.save(true, project, window, cx).detach_and_log_err(cx);
13016 }
13017 }
13018
13019 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13020 if hovered != self.gutter_hovered {
13021 self.gutter_hovered = hovered;
13022 cx.notify();
13023 }
13024 }
13025
13026 pub fn insert_blocks(
13027 &mut self,
13028 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13029 autoscroll: Option<Autoscroll>,
13030 cx: &mut Context<Self>,
13031 ) -> Vec<CustomBlockId> {
13032 let blocks = self
13033 .display_map
13034 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13035 if let Some(autoscroll) = autoscroll {
13036 self.request_autoscroll(autoscroll, cx);
13037 }
13038 cx.notify();
13039 blocks
13040 }
13041
13042 pub fn resize_blocks(
13043 &mut self,
13044 heights: HashMap<CustomBlockId, u32>,
13045 autoscroll: Option<Autoscroll>,
13046 cx: &mut Context<Self>,
13047 ) {
13048 self.display_map
13049 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13050 if let Some(autoscroll) = autoscroll {
13051 self.request_autoscroll(autoscroll, cx);
13052 }
13053 cx.notify();
13054 }
13055
13056 pub fn replace_blocks(
13057 &mut self,
13058 renderers: HashMap<CustomBlockId, RenderBlock>,
13059 autoscroll: Option<Autoscroll>,
13060 cx: &mut Context<Self>,
13061 ) {
13062 self.display_map
13063 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13064 if let Some(autoscroll) = autoscroll {
13065 self.request_autoscroll(autoscroll, cx);
13066 }
13067 cx.notify();
13068 }
13069
13070 pub fn remove_blocks(
13071 &mut self,
13072 block_ids: HashSet<CustomBlockId>,
13073 autoscroll: Option<Autoscroll>,
13074 cx: &mut Context<Self>,
13075 ) {
13076 self.display_map.update(cx, |display_map, cx| {
13077 display_map.remove_blocks(block_ids, cx)
13078 });
13079 if let Some(autoscroll) = autoscroll {
13080 self.request_autoscroll(autoscroll, cx);
13081 }
13082 cx.notify();
13083 }
13084
13085 pub fn row_for_block(
13086 &self,
13087 block_id: CustomBlockId,
13088 cx: &mut Context<Self>,
13089 ) -> Option<DisplayRow> {
13090 self.display_map
13091 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13092 }
13093
13094 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13095 self.focused_block = Some(focused_block);
13096 }
13097
13098 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13099 self.focused_block.take()
13100 }
13101
13102 pub fn insert_creases(
13103 &mut self,
13104 creases: impl IntoIterator<Item = Crease<Anchor>>,
13105 cx: &mut Context<Self>,
13106 ) -> Vec<CreaseId> {
13107 self.display_map
13108 .update(cx, |map, cx| map.insert_creases(creases, cx))
13109 }
13110
13111 pub fn remove_creases(
13112 &mut self,
13113 ids: impl IntoIterator<Item = CreaseId>,
13114 cx: &mut Context<Self>,
13115 ) {
13116 self.display_map
13117 .update(cx, |map, cx| map.remove_creases(ids, cx));
13118 }
13119
13120 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
13121 self.display_map
13122 .update(cx, |map, cx| map.snapshot(cx))
13123 .longest_row()
13124 }
13125
13126 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
13127 self.display_map
13128 .update(cx, |map, cx| map.snapshot(cx))
13129 .max_point()
13130 }
13131
13132 pub fn text(&self, cx: &App) -> String {
13133 self.buffer.read(cx).read(cx).text()
13134 }
13135
13136 pub fn is_empty(&self, cx: &App) -> bool {
13137 self.buffer.read(cx).read(cx).is_empty()
13138 }
13139
13140 pub fn text_option(&self, cx: &App) -> Option<String> {
13141 let text = self.text(cx);
13142 let text = text.trim();
13143
13144 if text.is_empty() {
13145 return None;
13146 }
13147
13148 Some(text.to_string())
13149 }
13150
13151 pub fn set_text(
13152 &mut self,
13153 text: impl Into<Arc<str>>,
13154 window: &mut Window,
13155 cx: &mut Context<Self>,
13156 ) {
13157 self.transact(window, cx, |this, _, cx| {
13158 this.buffer
13159 .read(cx)
13160 .as_singleton()
13161 .expect("you can only call set_text on editors for singleton buffers")
13162 .update(cx, |buffer, cx| buffer.set_text(text, cx));
13163 });
13164 }
13165
13166 pub fn display_text(&self, cx: &mut App) -> String {
13167 self.display_map
13168 .update(cx, |map, cx| map.snapshot(cx))
13169 .text()
13170 }
13171
13172 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
13173 let mut wrap_guides = smallvec::smallvec![];
13174
13175 if self.show_wrap_guides == Some(false) {
13176 return wrap_guides;
13177 }
13178
13179 let settings = self.buffer.read(cx).settings_at(0, cx);
13180 if settings.show_wrap_guides {
13181 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
13182 wrap_guides.push((soft_wrap as usize, true));
13183 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
13184 wrap_guides.push((soft_wrap as usize, true));
13185 }
13186 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
13187 }
13188
13189 wrap_guides
13190 }
13191
13192 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
13193 let settings = self.buffer.read(cx).settings_at(0, cx);
13194 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
13195 match mode {
13196 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
13197 SoftWrap::None
13198 }
13199 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
13200 language_settings::SoftWrap::PreferredLineLength => {
13201 SoftWrap::Column(settings.preferred_line_length)
13202 }
13203 language_settings::SoftWrap::Bounded => {
13204 SoftWrap::Bounded(settings.preferred_line_length)
13205 }
13206 }
13207 }
13208
13209 pub fn set_soft_wrap_mode(
13210 &mut self,
13211 mode: language_settings::SoftWrap,
13212
13213 cx: &mut Context<Self>,
13214 ) {
13215 self.soft_wrap_mode_override = Some(mode);
13216 cx.notify();
13217 }
13218
13219 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
13220 self.text_style_refinement = Some(style);
13221 }
13222
13223 /// called by the Element so we know what style we were most recently rendered with.
13224 pub(crate) fn set_style(
13225 &mut self,
13226 style: EditorStyle,
13227 window: &mut Window,
13228 cx: &mut Context<Self>,
13229 ) {
13230 let rem_size = window.rem_size();
13231 self.display_map.update(cx, |map, cx| {
13232 map.set_font(
13233 style.text.font(),
13234 style.text.font_size.to_pixels(rem_size),
13235 cx,
13236 )
13237 });
13238 self.style = Some(style);
13239 }
13240
13241 pub fn style(&self) -> Option<&EditorStyle> {
13242 self.style.as_ref()
13243 }
13244
13245 // Called by the element. This method is not designed to be called outside of the editor
13246 // element's layout code because it does not notify when rewrapping is computed synchronously.
13247 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
13248 self.display_map
13249 .update(cx, |map, cx| map.set_wrap_width(width, cx))
13250 }
13251
13252 pub fn set_soft_wrap(&mut self) {
13253 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
13254 }
13255
13256 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
13257 if self.soft_wrap_mode_override.is_some() {
13258 self.soft_wrap_mode_override.take();
13259 } else {
13260 let soft_wrap = match self.soft_wrap_mode(cx) {
13261 SoftWrap::GitDiff => return,
13262 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
13263 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
13264 language_settings::SoftWrap::None
13265 }
13266 };
13267 self.soft_wrap_mode_override = Some(soft_wrap);
13268 }
13269 cx.notify();
13270 }
13271
13272 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
13273 let Some(workspace) = self.workspace() else {
13274 return;
13275 };
13276 let fs = workspace.read(cx).app_state().fs.clone();
13277 let current_show = TabBarSettings::get_global(cx).show;
13278 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
13279 setting.show = Some(!current_show);
13280 });
13281 }
13282
13283 pub fn toggle_indent_guides(
13284 &mut self,
13285 _: &ToggleIndentGuides,
13286 _: &mut Window,
13287 cx: &mut Context<Self>,
13288 ) {
13289 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
13290 self.buffer
13291 .read(cx)
13292 .settings_at(0, cx)
13293 .indent_guides
13294 .enabled
13295 });
13296 self.show_indent_guides = Some(!currently_enabled);
13297 cx.notify();
13298 }
13299
13300 fn should_show_indent_guides(&self) -> Option<bool> {
13301 self.show_indent_guides
13302 }
13303
13304 pub fn toggle_line_numbers(
13305 &mut self,
13306 _: &ToggleLineNumbers,
13307 _: &mut Window,
13308 cx: &mut Context<Self>,
13309 ) {
13310 let mut editor_settings = EditorSettings::get_global(cx).clone();
13311 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
13312 EditorSettings::override_global(editor_settings, cx);
13313 }
13314
13315 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
13316 self.use_relative_line_numbers
13317 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
13318 }
13319
13320 pub fn toggle_relative_line_numbers(
13321 &mut self,
13322 _: &ToggleRelativeLineNumbers,
13323 _: &mut Window,
13324 cx: &mut Context<Self>,
13325 ) {
13326 let is_relative = self.should_use_relative_line_numbers(cx);
13327 self.set_relative_line_number(Some(!is_relative), cx)
13328 }
13329
13330 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
13331 self.use_relative_line_numbers = is_relative;
13332 cx.notify();
13333 }
13334
13335 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
13336 self.show_gutter = show_gutter;
13337 cx.notify();
13338 }
13339
13340 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
13341 self.show_scrollbars = show_scrollbars;
13342 cx.notify();
13343 }
13344
13345 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
13346 self.show_line_numbers = Some(show_line_numbers);
13347 cx.notify();
13348 }
13349
13350 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
13351 self.show_git_diff_gutter = Some(show_git_diff_gutter);
13352 cx.notify();
13353 }
13354
13355 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
13356 self.show_code_actions = Some(show_code_actions);
13357 cx.notify();
13358 }
13359
13360 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
13361 self.show_runnables = Some(show_runnables);
13362 cx.notify();
13363 }
13364
13365 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
13366 if self.display_map.read(cx).masked != masked {
13367 self.display_map.update(cx, |map, _| map.masked = masked);
13368 }
13369 cx.notify()
13370 }
13371
13372 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
13373 self.show_wrap_guides = Some(show_wrap_guides);
13374 cx.notify();
13375 }
13376
13377 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
13378 self.show_indent_guides = Some(show_indent_guides);
13379 cx.notify();
13380 }
13381
13382 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
13383 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
13384 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
13385 if let Some(dir) = file.abs_path(cx).parent() {
13386 return Some(dir.to_owned());
13387 }
13388 }
13389
13390 if let Some(project_path) = buffer.read(cx).project_path(cx) {
13391 return Some(project_path.path.to_path_buf());
13392 }
13393 }
13394
13395 None
13396 }
13397
13398 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
13399 self.active_excerpt(cx)?
13400 .1
13401 .read(cx)
13402 .file()
13403 .and_then(|f| f.as_local())
13404 }
13405
13406 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13407 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13408 let buffer = buffer.read(cx);
13409 if let Some(project_path) = buffer.project_path(cx) {
13410 let project = self.project.as_ref()?.read(cx);
13411 project.absolute_path(&project_path, cx)
13412 } else {
13413 buffer
13414 .file()
13415 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
13416 }
13417 })
13418 }
13419
13420 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13421 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13422 let project_path = buffer.read(cx).project_path(cx)?;
13423 let project = self.project.as_ref()?.read(cx);
13424 let entry = project.entry_for_path(&project_path, cx)?;
13425 let path = entry.path.to_path_buf();
13426 Some(path)
13427 })
13428 }
13429
13430 pub fn reveal_in_finder(
13431 &mut self,
13432 _: &RevealInFileManager,
13433 _window: &mut Window,
13434 cx: &mut Context<Self>,
13435 ) {
13436 if let Some(target) = self.target_file(cx) {
13437 cx.reveal_path(&target.abs_path(cx));
13438 }
13439 }
13440
13441 pub fn copy_path(
13442 &mut self,
13443 _: &zed_actions::workspace::CopyPath,
13444 _window: &mut Window,
13445 cx: &mut Context<Self>,
13446 ) {
13447 if let Some(path) = self.target_file_abs_path(cx) {
13448 if let Some(path) = path.to_str() {
13449 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13450 }
13451 }
13452 }
13453
13454 pub fn copy_relative_path(
13455 &mut self,
13456 _: &zed_actions::workspace::CopyRelativePath,
13457 _window: &mut Window,
13458 cx: &mut Context<Self>,
13459 ) {
13460 if let Some(path) = self.target_file_path(cx) {
13461 if let Some(path) = path.to_str() {
13462 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13463 }
13464 }
13465 }
13466
13467 pub fn copy_file_name_without_extension(
13468 &mut self,
13469 _: &CopyFileNameWithoutExtension,
13470 _: &mut Window,
13471 cx: &mut Context<Self>,
13472 ) {
13473 if let Some(file) = self.target_file(cx) {
13474 if let Some(file_stem) = file.path().file_stem() {
13475 if let Some(name) = file_stem.to_str() {
13476 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13477 }
13478 }
13479 }
13480 }
13481
13482 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
13483 if let Some(file) = self.target_file(cx) {
13484 if let Some(file_name) = file.path().file_name() {
13485 if let Some(name) = file_name.to_str() {
13486 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13487 }
13488 }
13489 }
13490 }
13491
13492 pub fn toggle_git_blame(
13493 &mut self,
13494 _: &ToggleGitBlame,
13495 window: &mut Window,
13496 cx: &mut Context<Self>,
13497 ) {
13498 self.show_git_blame_gutter = !self.show_git_blame_gutter;
13499
13500 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
13501 self.start_git_blame(true, window, cx);
13502 }
13503
13504 cx.notify();
13505 }
13506
13507 pub fn toggle_git_blame_inline(
13508 &mut self,
13509 _: &ToggleGitBlameInline,
13510 window: &mut Window,
13511 cx: &mut Context<Self>,
13512 ) {
13513 self.toggle_git_blame_inline_internal(true, window, cx);
13514 cx.notify();
13515 }
13516
13517 pub fn git_blame_inline_enabled(&self) -> bool {
13518 self.git_blame_inline_enabled
13519 }
13520
13521 pub fn toggle_selection_menu(
13522 &mut self,
13523 _: &ToggleSelectionMenu,
13524 _: &mut Window,
13525 cx: &mut Context<Self>,
13526 ) {
13527 self.show_selection_menu = self
13528 .show_selection_menu
13529 .map(|show_selections_menu| !show_selections_menu)
13530 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
13531
13532 cx.notify();
13533 }
13534
13535 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
13536 self.show_selection_menu
13537 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
13538 }
13539
13540 fn start_git_blame(
13541 &mut self,
13542 user_triggered: bool,
13543 window: &mut Window,
13544 cx: &mut Context<Self>,
13545 ) {
13546 if let Some(project) = self.project.as_ref() {
13547 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
13548 return;
13549 };
13550
13551 if buffer.read(cx).file().is_none() {
13552 return;
13553 }
13554
13555 let focused = self.focus_handle(cx).contains_focused(window, cx);
13556
13557 let project = project.clone();
13558 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
13559 self.blame_subscription =
13560 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
13561 self.blame = Some(blame);
13562 }
13563 }
13564
13565 fn toggle_git_blame_inline_internal(
13566 &mut self,
13567 user_triggered: bool,
13568 window: &mut Window,
13569 cx: &mut Context<Self>,
13570 ) {
13571 if self.git_blame_inline_enabled {
13572 self.git_blame_inline_enabled = false;
13573 self.show_git_blame_inline = false;
13574 self.show_git_blame_inline_delay_task.take();
13575 } else {
13576 self.git_blame_inline_enabled = true;
13577 self.start_git_blame_inline(user_triggered, window, cx);
13578 }
13579
13580 cx.notify();
13581 }
13582
13583 fn start_git_blame_inline(
13584 &mut self,
13585 user_triggered: bool,
13586 window: &mut Window,
13587 cx: &mut Context<Self>,
13588 ) {
13589 self.start_git_blame(user_triggered, window, cx);
13590
13591 if ProjectSettings::get_global(cx)
13592 .git
13593 .inline_blame_delay()
13594 .is_some()
13595 {
13596 self.start_inline_blame_timer(window, cx);
13597 } else {
13598 self.show_git_blame_inline = true
13599 }
13600 }
13601
13602 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
13603 self.blame.as_ref()
13604 }
13605
13606 pub fn show_git_blame_gutter(&self) -> bool {
13607 self.show_git_blame_gutter
13608 }
13609
13610 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
13611 self.show_git_blame_gutter && self.has_blame_entries(cx)
13612 }
13613
13614 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
13615 self.show_git_blame_inline
13616 && (self.focus_handle.is_focused(window)
13617 || self
13618 .git_blame_inline_tooltip
13619 .as_ref()
13620 .and_then(|t| t.upgrade())
13621 .is_some())
13622 && !self.newest_selection_head_on_empty_line(cx)
13623 && self.has_blame_entries(cx)
13624 }
13625
13626 fn has_blame_entries(&self, cx: &App) -> bool {
13627 self.blame()
13628 .map_or(false, |blame| blame.read(cx).has_generated_entries())
13629 }
13630
13631 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
13632 let cursor_anchor = self.selections.newest_anchor().head();
13633
13634 let snapshot = self.buffer.read(cx).snapshot(cx);
13635 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
13636
13637 snapshot.line_len(buffer_row) == 0
13638 }
13639
13640 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
13641 let buffer_and_selection = maybe!({
13642 let selection = self.selections.newest::<Point>(cx);
13643 let selection_range = selection.range();
13644
13645 let multi_buffer = self.buffer().read(cx);
13646 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13647 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13648
13649 let (buffer, range, _) = if selection.reversed {
13650 buffer_ranges.first()
13651 } else {
13652 buffer_ranges.last()
13653 }?;
13654
13655 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13656 ..text::ToPoint::to_point(&range.end, &buffer).row;
13657 Some((
13658 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13659 selection,
13660 ))
13661 });
13662
13663 let Some((buffer, selection)) = buffer_and_selection else {
13664 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13665 };
13666
13667 let Some(project) = self.project.as_ref() else {
13668 return Task::ready(Err(anyhow!("editor does not have project")));
13669 };
13670
13671 project.update(cx, |project, cx| {
13672 project.get_permalink_to_line(&buffer, selection, cx)
13673 })
13674 }
13675
13676 pub fn copy_permalink_to_line(
13677 &mut self,
13678 _: &CopyPermalinkToLine,
13679 window: &mut Window,
13680 cx: &mut Context<Self>,
13681 ) {
13682 let permalink_task = self.get_permalink_to_line(cx);
13683 let workspace = self.workspace();
13684
13685 cx.spawn_in(window, |_, mut cx| async move {
13686 match permalink_task.await {
13687 Ok(permalink) => {
13688 cx.update(|_, cx| {
13689 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13690 })
13691 .ok();
13692 }
13693 Err(err) => {
13694 let message = format!("Failed to copy permalink: {err}");
13695
13696 Err::<(), anyhow::Error>(err).log_err();
13697
13698 if let Some(workspace) = workspace {
13699 workspace
13700 .update_in(&mut cx, |workspace, _, cx| {
13701 struct CopyPermalinkToLine;
13702
13703 workspace.show_toast(
13704 Toast::new(
13705 NotificationId::unique::<CopyPermalinkToLine>(),
13706 message,
13707 ),
13708 cx,
13709 )
13710 })
13711 .ok();
13712 }
13713 }
13714 }
13715 })
13716 .detach();
13717 }
13718
13719 pub fn copy_file_location(
13720 &mut self,
13721 _: &CopyFileLocation,
13722 _: &mut Window,
13723 cx: &mut Context<Self>,
13724 ) {
13725 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13726 if let Some(file) = self.target_file(cx) {
13727 if let Some(path) = file.path().to_str() {
13728 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13729 }
13730 }
13731 }
13732
13733 pub fn open_permalink_to_line(
13734 &mut self,
13735 _: &OpenPermalinkToLine,
13736 window: &mut Window,
13737 cx: &mut Context<Self>,
13738 ) {
13739 let permalink_task = self.get_permalink_to_line(cx);
13740 let workspace = self.workspace();
13741
13742 cx.spawn_in(window, |_, mut cx| async move {
13743 match permalink_task.await {
13744 Ok(permalink) => {
13745 cx.update(|_, cx| {
13746 cx.open_url(permalink.as_ref());
13747 })
13748 .ok();
13749 }
13750 Err(err) => {
13751 let message = format!("Failed to open permalink: {err}");
13752
13753 Err::<(), anyhow::Error>(err).log_err();
13754
13755 if let Some(workspace) = workspace {
13756 workspace
13757 .update(&mut cx, |workspace, cx| {
13758 struct OpenPermalinkToLine;
13759
13760 workspace.show_toast(
13761 Toast::new(
13762 NotificationId::unique::<OpenPermalinkToLine>(),
13763 message,
13764 ),
13765 cx,
13766 )
13767 })
13768 .ok();
13769 }
13770 }
13771 }
13772 })
13773 .detach();
13774 }
13775
13776 pub fn insert_uuid_v4(
13777 &mut self,
13778 _: &InsertUuidV4,
13779 window: &mut Window,
13780 cx: &mut Context<Self>,
13781 ) {
13782 self.insert_uuid(UuidVersion::V4, window, cx);
13783 }
13784
13785 pub fn insert_uuid_v7(
13786 &mut self,
13787 _: &InsertUuidV7,
13788 window: &mut Window,
13789 cx: &mut Context<Self>,
13790 ) {
13791 self.insert_uuid(UuidVersion::V7, window, cx);
13792 }
13793
13794 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13795 self.transact(window, cx, |this, window, cx| {
13796 let edits = this
13797 .selections
13798 .all::<Point>(cx)
13799 .into_iter()
13800 .map(|selection| {
13801 let uuid = match version {
13802 UuidVersion::V4 => uuid::Uuid::new_v4(),
13803 UuidVersion::V7 => uuid::Uuid::now_v7(),
13804 };
13805
13806 (selection.range(), uuid.to_string())
13807 });
13808 this.edit(edits, cx);
13809 this.refresh_inline_completion(true, false, window, cx);
13810 });
13811 }
13812
13813 pub fn open_selections_in_multibuffer(
13814 &mut self,
13815 _: &OpenSelectionsInMultibuffer,
13816 window: &mut Window,
13817 cx: &mut Context<Self>,
13818 ) {
13819 let multibuffer = self.buffer.read(cx);
13820
13821 let Some(buffer) = multibuffer.as_singleton() else {
13822 return;
13823 };
13824
13825 let Some(workspace) = self.workspace() else {
13826 return;
13827 };
13828
13829 let locations = self
13830 .selections
13831 .disjoint_anchors()
13832 .iter()
13833 .map(|range| Location {
13834 buffer: buffer.clone(),
13835 range: range.start.text_anchor..range.end.text_anchor,
13836 })
13837 .collect::<Vec<_>>();
13838
13839 let title = multibuffer.title(cx).to_string();
13840
13841 cx.spawn_in(window, |_, mut cx| async move {
13842 workspace.update_in(&mut cx, |workspace, window, cx| {
13843 Self::open_locations_in_multibuffer(
13844 workspace,
13845 locations,
13846 format!("Selections for '{title}'"),
13847 false,
13848 MultibufferSelectionMode::All,
13849 window,
13850 cx,
13851 );
13852 })
13853 })
13854 .detach();
13855 }
13856
13857 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13858 /// last highlight added will be used.
13859 ///
13860 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13861 pub fn highlight_rows<T: 'static>(
13862 &mut self,
13863 range: Range<Anchor>,
13864 color: Hsla,
13865 should_autoscroll: bool,
13866 cx: &mut Context<Self>,
13867 ) {
13868 let snapshot = self.buffer().read(cx).snapshot(cx);
13869 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13870 let ix = row_highlights.binary_search_by(|highlight| {
13871 Ordering::Equal
13872 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13873 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13874 });
13875
13876 if let Err(mut ix) = ix {
13877 let index = post_inc(&mut self.highlight_order);
13878
13879 // If this range intersects with the preceding highlight, then merge it with
13880 // the preceding highlight. Otherwise insert a new highlight.
13881 let mut merged = false;
13882 if ix > 0 {
13883 let prev_highlight = &mut row_highlights[ix - 1];
13884 if prev_highlight
13885 .range
13886 .end
13887 .cmp(&range.start, &snapshot)
13888 .is_ge()
13889 {
13890 ix -= 1;
13891 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13892 prev_highlight.range.end = range.end;
13893 }
13894 merged = true;
13895 prev_highlight.index = index;
13896 prev_highlight.color = color;
13897 prev_highlight.should_autoscroll = should_autoscroll;
13898 }
13899 }
13900
13901 if !merged {
13902 row_highlights.insert(
13903 ix,
13904 RowHighlight {
13905 range: range.clone(),
13906 index,
13907 color,
13908 should_autoscroll,
13909 },
13910 );
13911 }
13912
13913 // If any of the following highlights intersect with this one, merge them.
13914 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13915 let highlight = &row_highlights[ix];
13916 if next_highlight
13917 .range
13918 .start
13919 .cmp(&highlight.range.end, &snapshot)
13920 .is_le()
13921 {
13922 if next_highlight
13923 .range
13924 .end
13925 .cmp(&highlight.range.end, &snapshot)
13926 .is_gt()
13927 {
13928 row_highlights[ix].range.end = next_highlight.range.end;
13929 }
13930 row_highlights.remove(ix + 1);
13931 } else {
13932 break;
13933 }
13934 }
13935 }
13936 }
13937
13938 /// Remove any highlighted row ranges of the given type that intersect the
13939 /// given ranges.
13940 pub fn remove_highlighted_rows<T: 'static>(
13941 &mut self,
13942 ranges_to_remove: Vec<Range<Anchor>>,
13943 cx: &mut Context<Self>,
13944 ) {
13945 let snapshot = self.buffer().read(cx).snapshot(cx);
13946 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13947 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13948 row_highlights.retain(|highlight| {
13949 while let Some(range_to_remove) = ranges_to_remove.peek() {
13950 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13951 Ordering::Less | Ordering::Equal => {
13952 ranges_to_remove.next();
13953 }
13954 Ordering::Greater => {
13955 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13956 Ordering::Less | Ordering::Equal => {
13957 return false;
13958 }
13959 Ordering::Greater => break,
13960 }
13961 }
13962 }
13963 }
13964
13965 true
13966 })
13967 }
13968
13969 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13970 pub fn clear_row_highlights<T: 'static>(&mut self) {
13971 self.highlighted_rows.remove(&TypeId::of::<T>());
13972 }
13973
13974 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
13975 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
13976 self.highlighted_rows
13977 .get(&TypeId::of::<T>())
13978 .map_or(&[] as &[_], |vec| vec.as_slice())
13979 .iter()
13980 .map(|highlight| (highlight.range.clone(), highlight.color))
13981 }
13982
13983 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
13984 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
13985 /// Allows to ignore certain kinds of highlights.
13986 pub fn highlighted_display_rows(
13987 &self,
13988 window: &mut Window,
13989 cx: &mut App,
13990 ) -> BTreeMap<DisplayRow, Background> {
13991 let snapshot = self.snapshot(window, cx);
13992 let mut used_highlight_orders = HashMap::default();
13993 self.highlighted_rows
13994 .iter()
13995 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
13996 .fold(
13997 BTreeMap::<DisplayRow, Background>::new(),
13998 |mut unique_rows, highlight| {
13999 let start = highlight.range.start.to_display_point(&snapshot);
14000 let end = highlight.range.end.to_display_point(&snapshot);
14001 let start_row = start.row().0;
14002 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14003 && end.column() == 0
14004 {
14005 end.row().0.saturating_sub(1)
14006 } else {
14007 end.row().0
14008 };
14009 for row in start_row..=end_row {
14010 let used_index =
14011 used_highlight_orders.entry(row).or_insert(highlight.index);
14012 if highlight.index >= *used_index {
14013 *used_index = highlight.index;
14014 unique_rows.insert(DisplayRow(row), highlight.color.into());
14015 }
14016 }
14017 unique_rows
14018 },
14019 )
14020 }
14021
14022 pub fn highlighted_display_row_for_autoscroll(
14023 &self,
14024 snapshot: &DisplaySnapshot,
14025 ) -> Option<DisplayRow> {
14026 self.highlighted_rows
14027 .values()
14028 .flat_map(|highlighted_rows| highlighted_rows.iter())
14029 .filter_map(|highlight| {
14030 if highlight.should_autoscroll {
14031 Some(highlight.range.start.to_display_point(snapshot).row())
14032 } else {
14033 None
14034 }
14035 })
14036 .min()
14037 }
14038
14039 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14040 self.highlight_background::<SearchWithinRange>(
14041 ranges,
14042 |colors| colors.editor_document_highlight_read_background,
14043 cx,
14044 )
14045 }
14046
14047 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14048 self.breadcrumb_header = Some(new_header);
14049 }
14050
14051 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14052 self.clear_background_highlights::<SearchWithinRange>(cx);
14053 }
14054
14055 pub fn highlight_background<T: 'static>(
14056 &mut self,
14057 ranges: &[Range<Anchor>],
14058 color_fetcher: fn(&ThemeColors) -> Hsla,
14059 cx: &mut Context<Self>,
14060 ) {
14061 self.background_highlights
14062 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14063 self.scrollbar_marker_state.dirty = true;
14064 cx.notify();
14065 }
14066
14067 pub fn clear_background_highlights<T: 'static>(
14068 &mut self,
14069 cx: &mut Context<Self>,
14070 ) -> Option<BackgroundHighlight> {
14071 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14072 if !text_highlights.1.is_empty() {
14073 self.scrollbar_marker_state.dirty = true;
14074 cx.notify();
14075 }
14076 Some(text_highlights)
14077 }
14078
14079 pub fn highlight_gutter<T: 'static>(
14080 &mut self,
14081 ranges: &[Range<Anchor>],
14082 color_fetcher: fn(&App) -> Hsla,
14083 cx: &mut Context<Self>,
14084 ) {
14085 self.gutter_highlights
14086 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14087 cx.notify();
14088 }
14089
14090 pub fn clear_gutter_highlights<T: 'static>(
14091 &mut self,
14092 cx: &mut Context<Self>,
14093 ) -> Option<GutterHighlight> {
14094 cx.notify();
14095 self.gutter_highlights.remove(&TypeId::of::<T>())
14096 }
14097
14098 #[cfg(feature = "test-support")]
14099 pub fn all_text_background_highlights(
14100 &self,
14101 window: &mut Window,
14102 cx: &mut Context<Self>,
14103 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14104 let snapshot = self.snapshot(window, cx);
14105 let buffer = &snapshot.buffer_snapshot;
14106 let start = buffer.anchor_before(0);
14107 let end = buffer.anchor_after(buffer.len());
14108 let theme = cx.theme().colors();
14109 self.background_highlights_in_range(start..end, &snapshot, theme)
14110 }
14111
14112 #[cfg(feature = "test-support")]
14113 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
14114 let snapshot = self.buffer().read(cx).snapshot(cx);
14115
14116 let highlights = self
14117 .background_highlights
14118 .get(&TypeId::of::<items::BufferSearchHighlights>());
14119
14120 if let Some((_color, ranges)) = highlights {
14121 ranges
14122 .iter()
14123 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
14124 .collect_vec()
14125 } else {
14126 vec![]
14127 }
14128 }
14129
14130 fn document_highlights_for_position<'a>(
14131 &'a self,
14132 position: Anchor,
14133 buffer: &'a MultiBufferSnapshot,
14134 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
14135 let read_highlights = self
14136 .background_highlights
14137 .get(&TypeId::of::<DocumentHighlightRead>())
14138 .map(|h| &h.1);
14139 let write_highlights = self
14140 .background_highlights
14141 .get(&TypeId::of::<DocumentHighlightWrite>())
14142 .map(|h| &h.1);
14143 let left_position = position.bias_left(buffer);
14144 let right_position = position.bias_right(buffer);
14145 read_highlights
14146 .into_iter()
14147 .chain(write_highlights)
14148 .flat_map(move |ranges| {
14149 let start_ix = match ranges.binary_search_by(|probe| {
14150 let cmp = probe.end.cmp(&left_position, buffer);
14151 if cmp.is_ge() {
14152 Ordering::Greater
14153 } else {
14154 Ordering::Less
14155 }
14156 }) {
14157 Ok(i) | Err(i) => i,
14158 };
14159
14160 ranges[start_ix..]
14161 .iter()
14162 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
14163 })
14164 }
14165
14166 pub fn has_background_highlights<T: 'static>(&self) -> bool {
14167 self.background_highlights
14168 .get(&TypeId::of::<T>())
14169 .map_or(false, |(_, highlights)| !highlights.is_empty())
14170 }
14171
14172 pub fn background_highlights_in_range(
14173 &self,
14174 search_range: Range<Anchor>,
14175 display_snapshot: &DisplaySnapshot,
14176 theme: &ThemeColors,
14177 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14178 let mut results = Vec::new();
14179 for (color_fetcher, ranges) in self.background_highlights.values() {
14180 let color = color_fetcher(theme);
14181 let start_ix = match ranges.binary_search_by(|probe| {
14182 let cmp = probe
14183 .end
14184 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14185 if cmp.is_gt() {
14186 Ordering::Greater
14187 } else {
14188 Ordering::Less
14189 }
14190 }) {
14191 Ok(i) | Err(i) => i,
14192 };
14193 for range in &ranges[start_ix..] {
14194 if range
14195 .start
14196 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14197 .is_ge()
14198 {
14199 break;
14200 }
14201
14202 let start = range.start.to_display_point(display_snapshot);
14203 let end = range.end.to_display_point(display_snapshot);
14204 results.push((start..end, color))
14205 }
14206 }
14207 results
14208 }
14209
14210 pub fn background_highlight_row_ranges<T: 'static>(
14211 &self,
14212 search_range: Range<Anchor>,
14213 display_snapshot: &DisplaySnapshot,
14214 count: usize,
14215 ) -> Vec<RangeInclusive<DisplayPoint>> {
14216 let mut results = Vec::new();
14217 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
14218 return vec![];
14219 };
14220
14221 let start_ix = match ranges.binary_search_by(|probe| {
14222 let cmp = probe
14223 .end
14224 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14225 if cmp.is_gt() {
14226 Ordering::Greater
14227 } else {
14228 Ordering::Less
14229 }
14230 }) {
14231 Ok(i) | Err(i) => i,
14232 };
14233 let mut push_region = |start: Option<Point>, end: Option<Point>| {
14234 if let (Some(start_display), Some(end_display)) = (start, end) {
14235 results.push(
14236 start_display.to_display_point(display_snapshot)
14237 ..=end_display.to_display_point(display_snapshot),
14238 );
14239 }
14240 };
14241 let mut start_row: Option<Point> = None;
14242 let mut end_row: Option<Point> = None;
14243 if ranges.len() > count {
14244 return Vec::new();
14245 }
14246 for range in &ranges[start_ix..] {
14247 if range
14248 .start
14249 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14250 .is_ge()
14251 {
14252 break;
14253 }
14254 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
14255 if let Some(current_row) = &end_row {
14256 if end.row == current_row.row {
14257 continue;
14258 }
14259 }
14260 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
14261 if start_row.is_none() {
14262 assert_eq!(end_row, None);
14263 start_row = Some(start);
14264 end_row = Some(end);
14265 continue;
14266 }
14267 if let Some(current_end) = end_row.as_mut() {
14268 if start.row > current_end.row + 1 {
14269 push_region(start_row, end_row);
14270 start_row = Some(start);
14271 end_row = Some(end);
14272 } else {
14273 // Merge two hunks.
14274 *current_end = end;
14275 }
14276 } else {
14277 unreachable!();
14278 }
14279 }
14280 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
14281 push_region(start_row, end_row);
14282 results
14283 }
14284
14285 pub fn gutter_highlights_in_range(
14286 &self,
14287 search_range: Range<Anchor>,
14288 display_snapshot: &DisplaySnapshot,
14289 cx: &App,
14290 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14291 let mut results = Vec::new();
14292 for (color_fetcher, ranges) in self.gutter_highlights.values() {
14293 let color = color_fetcher(cx);
14294 let start_ix = match ranges.binary_search_by(|probe| {
14295 let cmp = probe
14296 .end
14297 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14298 if cmp.is_gt() {
14299 Ordering::Greater
14300 } else {
14301 Ordering::Less
14302 }
14303 }) {
14304 Ok(i) | Err(i) => i,
14305 };
14306 for range in &ranges[start_ix..] {
14307 if range
14308 .start
14309 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14310 .is_ge()
14311 {
14312 break;
14313 }
14314
14315 let start = range.start.to_display_point(display_snapshot);
14316 let end = range.end.to_display_point(display_snapshot);
14317 results.push((start..end, color))
14318 }
14319 }
14320 results
14321 }
14322
14323 /// Get the text ranges corresponding to the redaction query
14324 pub fn redacted_ranges(
14325 &self,
14326 search_range: Range<Anchor>,
14327 display_snapshot: &DisplaySnapshot,
14328 cx: &App,
14329 ) -> Vec<Range<DisplayPoint>> {
14330 display_snapshot
14331 .buffer_snapshot
14332 .redacted_ranges(search_range, |file| {
14333 if let Some(file) = file {
14334 file.is_private()
14335 && EditorSettings::get(
14336 Some(SettingsLocation {
14337 worktree_id: file.worktree_id(cx),
14338 path: file.path().as_ref(),
14339 }),
14340 cx,
14341 )
14342 .redact_private_values
14343 } else {
14344 false
14345 }
14346 })
14347 .map(|range| {
14348 range.start.to_display_point(display_snapshot)
14349 ..range.end.to_display_point(display_snapshot)
14350 })
14351 .collect()
14352 }
14353
14354 pub fn highlight_text<T: 'static>(
14355 &mut self,
14356 ranges: Vec<Range<Anchor>>,
14357 style: HighlightStyle,
14358 cx: &mut Context<Self>,
14359 ) {
14360 self.display_map.update(cx, |map, _| {
14361 map.highlight_text(TypeId::of::<T>(), ranges, style)
14362 });
14363 cx.notify();
14364 }
14365
14366 pub(crate) fn highlight_inlays<T: 'static>(
14367 &mut self,
14368 highlights: Vec<InlayHighlight>,
14369 style: HighlightStyle,
14370 cx: &mut Context<Self>,
14371 ) {
14372 self.display_map.update(cx, |map, _| {
14373 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
14374 });
14375 cx.notify();
14376 }
14377
14378 pub fn text_highlights<'a, T: 'static>(
14379 &'a self,
14380 cx: &'a App,
14381 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
14382 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
14383 }
14384
14385 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
14386 let cleared = self
14387 .display_map
14388 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
14389 if cleared {
14390 cx.notify();
14391 }
14392 }
14393
14394 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
14395 (self.read_only(cx) || self.blink_manager.read(cx).visible())
14396 && self.focus_handle.is_focused(window)
14397 }
14398
14399 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
14400 self.show_cursor_when_unfocused = is_enabled;
14401 cx.notify();
14402 }
14403
14404 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
14405 cx.notify();
14406 }
14407
14408 fn on_buffer_event(
14409 &mut self,
14410 multibuffer: &Entity<MultiBuffer>,
14411 event: &multi_buffer::Event,
14412 window: &mut Window,
14413 cx: &mut Context<Self>,
14414 ) {
14415 match event {
14416 multi_buffer::Event::Edited {
14417 singleton_buffer_edited,
14418 edited_buffer: buffer_edited,
14419 } => {
14420 self.scrollbar_marker_state.dirty = true;
14421 self.active_indent_guides_state.dirty = true;
14422 self.refresh_active_diagnostics(cx);
14423 self.refresh_code_actions(window, cx);
14424 if self.has_active_inline_completion() {
14425 self.update_visible_inline_completion(window, cx);
14426 }
14427 if let Some(buffer) = buffer_edited {
14428 let buffer_id = buffer.read(cx).remote_id();
14429 if !self.registered_buffers.contains_key(&buffer_id) {
14430 if let Some(project) = self.project.as_ref() {
14431 project.update(cx, |project, cx| {
14432 self.registered_buffers.insert(
14433 buffer_id,
14434 project.register_buffer_with_language_servers(&buffer, cx),
14435 );
14436 })
14437 }
14438 }
14439 }
14440 cx.emit(EditorEvent::BufferEdited);
14441 cx.emit(SearchEvent::MatchesInvalidated);
14442 if *singleton_buffer_edited {
14443 if let Some(project) = &self.project {
14444 #[allow(clippy::mutable_key_type)]
14445 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
14446 multibuffer
14447 .all_buffers()
14448 .into_iter()
14449 .filter_map(|buffer| {
14450 buffer.update(cx, |buffer, cx| {
14451 let language = buffer.language()?;
14452 let should_discard = project.update(cx, |project, cx| {
14453 project.is_local()
14454 && !project.has_language_servers_for(buffer, cx)
14455 });
14456 should_discard.not().then_some(language.clone())
14457 })
14458 })
14459 .collect::<HashSet<_>>()
14460 });
14461 if !languages_affected.is_empty() {
14462 self.refresh_inlay_hints(
14463 InlayHintRefreshReason::BufferEdited(languages_affected),
14464 cx,
14465 );
14466 }
14467 }
14468 }
14469
14470 let Some(project) = &self.project else { return };
14471 let (telemetry, is_via_ssh) = {
14472 let project = project.read(cx);
14473 let telemetry = project.client().telemetry().clone();
14474 let is_via_ssh = project.is_via_ssh();
14475 (telemetry, is_via_ssh)
14476 };
14477 refresh_linked_ranges(self, window, cx);
14478 telemetry.log_edit_event("editor", is_via_ssh);
14479 }
14480 multi_buffer::Event::ExcerptsAdded {
14481 buffer,
14482 predecessor,
14483 excerpts,
14484 } => {
14485 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14486 let buffer_id = buffer.read(cx).remote_id();
14487 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
14488 if let Some(project) = &self.project {
14489 get_uncommitted_diff_for_buffer(
14490 project,
14491 [buffer.clone()],
14492 self.buffer.clone(),
14493 cx,
14494 )
14495 .detach();
14496 }
14497 }
14498 cx.emit(EditorEvent::ExcerptsAdded {
14499 buffer: buffer.clone(),
14500 predecessor: *predecessor,
14501 excerpts: excerpts.clone(),
14502 });
14503 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14504 }
14505 multi_buffer::Event::ExcerptsRemoved { ids } => {
14506 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
14507 let buffer = self.buffer.read(cx);
14508 self.registered_buffers
14509 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
14510 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
14511 }
14512 multi_buffer::Event::ExcerptsEdited { ids } => {
14513 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
14514 }
14515 multi_buffer::Event::ExcerptsExpanded { ids } => {
14516 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14517 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
14518 }
14519 multi_buffer::Event::Reparsed(buffer_id) => {
14520 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14521
14522 cx.emit(EditorEvent::Reparsed(*buffer_id));
14523 }
14524 multi_buffer::Event::DiffHunksToggled => {
14525 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14526 }
14527 multi_buffer::Event::LanguageChanged(buffer_id) => {
14528 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
14529 cx.emit(EditorEvent::Reparsed(*buffer_id));
14530 cx.notify();
14531 }
14532 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
14533 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
14534 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
14535 cx.emit(EditorEvent::TitleChanged)
14536 }
14537 // multi_buffer::Event::DiffBaseChanged => {
14538 // self.scrollbar_marker_state.dirty = true;
14539 // cx.emit(EditorEvent::DiffBaseChanged);
14540 // cx.notify();
14541 // }
14542 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
14543 multi_buffer::Event::DiagnosticsUpdated => {
14544 self.refresh_active_diagnostics(cx);
14545 self.refresh_inline_diagnostics(true, window, cx);
14546 self.scrollbar_marker_state.dirty = true;
14547 cx.notify();
14548 }
14549 _ => {}
14550 };
14551 }
14552
14553 fn on_display_map_changed(
14554 &mut self,
14555 _: Entity<DisplayMap>,
14556 _: &mut Window,
14557 cx: &mut Context<Self>,
14558 ) {
14559 cx.notify();
14560 }
14561
14562 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14563 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14564 self.refresh_inline_completion(true, false, window, cx);
14565 self.refresh_inlay_hints(
14566 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
14567 self.selections.newest_anchor().head(),
14568 &self.buffer.read(cx).snapshot(cx),
14569 cx,
14570 )),
14571 cx,
14572 );
14573
14574 let old_cursor_shape = self.cursor_shape;
14575
14576 {
14577 let editor_settings = EditorSettings::get_global(cx);
14578 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
14579 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
14580 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
14581 self.hide_mouse_while_typing = editor_settings.hide_mouse_while_typing.unwrap_or(true);
14582
14583 if !self.hide_mouse_while_typing {
14584 self.mouse_cursor_hidden = false;
14585 }
14586 }
14587
14588 if old_cursor_shape != self.cursor_shape {
14589 cx.emit(EditorEvent::CursorShapeChanged);
14590 }
14591
14592 let project_settings = ProjectSettings::get_global(cx);
14593 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
14594
14595 if self.mode == EditorMode::Full {
14596 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
14597 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
14598 if self.show_inline_diagnostics != show_inline_diagnostics {
14599 self.show_inline_diagnostics = show_inline_diagnostics;
14600 self.refresh_inline_diagnostics(false, window, cx);
14601 }
14602
14603 if self.git_blame_inline_enabled != inline_blame_enabled {
14604 self.toggle_git_blame_inline_internal(false, window, cx);
14605 }
14606 }
14607
14608 cx.notify();
14609 }
14610
14611 pub fn set_searchable(&mut self, searchable: bool) {
14612 self.searchable = searchable;
14613 }
14614
14615 pub fn searchable(&self) -> bool {
14616 self.searchable
14617 }
14618
14619 fn open_proposed_changes_editor(
14620 &mut self,
14621 _: &OpenProposedChangesEditor,
14622 window: &mut Window,
14623 cx: &mut Context<Self>,
14624 ) {
14625 let Some(workspace) = self.workspace() else {
14626 cx.propagate();
14627 return;
14628 };
14629
14630 let selections = self.selections.all::<usize>(cx);
14631 let multi_buffer = self.buffer.read(cx);
14632 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14633 let mut new_selections_by_buffer = HashMap::default();
14634 for selection in selections {
14635 for (buffer, range, _) in
14636 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
14637 {
14638 let mut range = range.to_point(buffer);
14639 range.start.column = 0;
14640 range.end.column = buffer.line_len(range.end.row);
14641 new_selections_by_buffer
14642 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
14643 .or_insert(Vec::new())
14644 .push(range)
14645 }
14646 }
14647
14648 let proposed_changes_buffers = new_selections_by_buffer
14649 .into_iter()
14650 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14651 .collect::<Vec<_>>();
14652 let proposed_changes_editor = cx.new(|cx| {
14653 ProposedChangesEditor::new(
14654 "Proposed changes",
14655 proposed_changes_buffers,
14656 self.project.clone(),
14657 window,
14658 cx,
14659 )
14660 });
14661
14662 window.defer(cx, move |window, cx| {
14663 workspace.update(cx, |workspace, cx| {
14664 workspace.active_pane().update(cx, |pane, cx| {
14665 pane.add_item(
14666 Box::new(proposed_changes_editor),
14667 true,
14668 true,
14669 None,
14670 window,
14671 cx,
14672 );
14673 });
14674 });
14675 });
14676 }
14677
14678 pub fn open_excerpts_in_split(
14679 &mut self,
14680 _: &OpenExcerptsSplit,
14681 window: &mut Window,
14682 cx: &mut Context<Self>,
14683 ) {
14684 self.open_excerpts_common(None, true, window, cx)
14685 }
14686
14687 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14688 self.open_excerpts_common(None, false, window, cx)
14689 }
14690
14691 fn open_excerpts_common(
14692 &mut self,
14693 jump_data: Option<JumpData>,
14694 split: bool,
14695 window: &mut Window,
14696 cx: &mut Context<Self>,
14697 ) {
14698 let Some(workspace) = self.workspace() else {
14699 cx.propagate();
14700 return;
14701 };
14702
14703 if self.buffer.read(cx).is_singleton() {
14704 cx.propagate();
14705 return;
14706 }
14707
14708 let mut new_selections_by_buffer = HashMap::default();
14709 match &jump_data {
14710 Some(JumpData::MultiBufferPoint {
14711 excerpt_id,
14712 position,
14713 anchor,
14714 line_offset_from_top,
14715 }) => {
14716 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14717 if let Some(buffer) = multi_buffer_snapshot
14718 .buffer_id_for_excerpt(*excerpt_id)
14719 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14720 {
14721 let buffer_snapshot = buffer.read(cx).snapshot();
14722 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14723 language::ToPoint::to_point(anchor, &buffer_snapshot)
14724 } else {
14725 buffer_snapshot.clip_point(*position, Bias::Left)
14726 };
14727 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14728 new_selections_by_buffer.insert(
14729 buffer,
14730 (
14731 vec![jump_to_offset..jump_to_offset],
14732 Some(*line_offset_from_top),
14733 ),
14734 );
14735 }
14736 }
14737 Some(JumpData::MultiBufferRow {
14738 row,
14739 line_offset_from_top,
14740 }) => {
14741 let point = MultiBufferPoint::new(row.0, 0);
14742 if let Some((buffer, buffer_point, _)) =
14743 self.buffer.read(cx).point_to_buffer_point(point, cx)
14744 {
14745 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14746 new_selections_by_buffer
14747 .entry(buffer)
14748 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14749 .0
14750 .push(buffer_offset..buffer_offset)
14751 }
14752 }
14753 None => {
14754 let selections = self.selections.all::<usize>(cx);
14755 let multi_buffer = self.buffer.read(cx);
14756 for selection in selections {
14757 for (buffer, mut range, _) in multi_buffer
14758 .snapshot(cx)
14759 .range_to_buffer_ranges(selection.range())
14760 {
14761 // When editing branch buffers, jump to the corresponding location
14762 // in their base buffer.
14763 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14764 let buffer = buffer_handle.read(cx);
14765 if let Some(base_buffer) = buffer.base_buffer() {
14766 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14767 buffer_handle = base_buffer;
14768 }
14769
14770 if selection.reversed {
14771 mem::swap(&mut range.start, &mut range.end);
14772 }
14773 new_selections_by_buffer
14774 .entry(buffer_handle)
14775 .or_insert((Vec::new(), None))
14776 .0
14777 .push(range)
14778 }
14779 }
14780 }
14781 }
14782
14783 if new_selections_by_buffer.is_empty() {
14784 return;
14785 }
14786
14787 // We defer the pane interaction because we ourselves are a workspace item
14788 // and activating a new item causes the pane to call a method on us reentrantly,
14789 // which panics if we're on the stack.
14790 window.defer(cx, move |window, cx| {
14791 workspace.update(cx, |workspace, cx| {
14792 let pane = if split {
14793 workspace.adjacent_pane(window, cx)
14794 } else {
14795 workspace.active_pane().clone()
14796 };
14797
14798 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14799 let editor = buffer
14800 .read(cx)
14801 .file()
14802 .is_none()
14803 .then(|| {
14804 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14805 // so `workspace.open_project_item` will never find them, always opening a new editor.
14806 // Instead, we try to activate the existing editor in the pane first.
14807 let (editor, pane_item_index) =
14808 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14809 let editor = item.downcast::<Editor>()?;
14810 let singleton_buffer =
14811 editor.read(cx).buffer().read(cx).as_singleton()?;
14812 if singleton_buffer == buffer {
14813 Some((editor, i))
14814 } else {
14815 None
14816 }
14817 })?;
14818 pane.update(cx, |pane, cx| {
14819 pane.activate_item(pane_item_index, true, true, window, cx)
14820 });
14821 Some(editor)
14822 })
14823 .flatten()
14824 .unwrap_or_else(|| {
14825 workspace.open_project_item::<Self>(
14826 pane.clone(),
14827 buffer,
14828 true,
14829 true,
14830 window,
14831 cx,
14832 )
14833 });
14834
14835 editor.update(cx, |editor, cx| {
14836 let autoscroll = match scroll_offset {
14837 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14838 None => Autoscroll::newest(),
14839 };
14840 let nav_history = editor.nav_history.take();
14841 editor.change_selections(Some(autoscroll), window, cx, |s| {
14842 s.select_ranges(ranges);
14843 });
14844 editor.nav_history = nav_history;
14845 });
14846 }
14847 })
14848 });
14849 }
14850
14851 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14852 let snapshot = self.buffer.read(cx).read(cx);
14853 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14854 Some(
14855 ranges
14856 .iter()
14857 .map(move |range| {
14858 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14859 })
14860 .collect(),
14861 )
14862 }
14863
14864 fn selection_replacement_ranges(
14865 &self,
14866 range: Range<OffsetUtf16>,
14867 cx: &mut App,
14868 ) -> Vec<Range<OffsetUtf16>> {
14869 let selections = self.selections.all::<OffsetUtf16>(cx);
14870 let newest_selection = selections
14871 .iter()
14872 .max_by_key(|selection| selection.id)
14873 .unwrap();
14874 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14875 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14876 let snapshot = self.buffer.read(cx).read(cx);
14877 selections
14878 .into_iter()
14879 .map(|mut selection| {
14880 selection.start.0 =
14881 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14882 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14883 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14884 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14885 })
14886 .collect()
14887 }
14888
14889 fn report_editor_event(
14890 &self,
14891 event_type: &'static str,
14892 file_extension: Option<String>,
14893 cx: &App,
14894 ) {
14895 if cfg!(any(test, feature = "test-support")) {
14896 return;
14897 }
14898
14899 let Some(project) = &self.project else { return };
14900
14901 // If None, we are in a file without an extension
14902 let file = self
14903 .buffer
14904 .read(cx)
14905 .as_singleton()
14906 .and_then(|b| b.read(cx).file());
14907 let file_extension = file_extension.or(file
14908 .as_ref()
14909 .and_then(|file| Path::new(file.file_name(cx)).extension())
14910 .and_then(|e| e.to_str())
14911 .map(|a| a.to_string()));
14912
14913 let vim_mode = cx
14914 .global::<SettingsStore>()
14915 .raw_user_settings()
14916 .get("vim_mode")
14917 == Some(&serde_json::Value::Bool(true));
14918
14919 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14920 let copilot_enabled = edit_predictions_provider
14921 == language::language_settings::EditPredictionProvider::Copilot;
14922 let copilot_enabled_for_language = self
14923 .buffer
14924 .read(cx)
14925 .settings_at(0, cx)
14926 .show_edit_predictions;
14927
14928 let project = project.read(cx);
14929 telemetry::event!(
14930 event_type,
14931 file_extension,
14932 vim_mode,
14933 copilot_enabled,
14934 copilot_enabled_for_language,
14935 edit_predictions_provider,
14936 is_via_ssh = project.is_via_ssh(),
14937 );
14938 }
14939
14940 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14941 /// with each line being an array of {text, highlight} objects.
14942 fn copy_highlight_json(
14943 &mut self,
14944 _: &CopyHighlightJson,
14945 window: &mut Window,
14946 cx: &mut Context<Self>,
14947 ) {
14948 #[derive(Serialize)]
14949 struct Chunk<'a> {
14950 text: String,
14951 highlight: Option<&'a str>,
14952 }
14953
14954 let snapshot = self.buffer.read(cx).snapshot(cx);
14955 let range = self
14956 .selected_text_range(false, window, cx)
14957 .and_then(|selection| {
14958 if selection.range.is_empty() {
14959 None
14960 } else {
14961 Some(selection.range)
14962 }
14963 })
14964 .unwrap_or_else(|| 0..snapshot.len());
14965
14966 let chunks = snapshot.chunks(range, true);
14967 let mut lines = Vec::new();
14968 let mut line: VecDeque<Chunk> = VecDeque::new();
14969
14970 let Some(style) = self.style.as_ref() else {
14971 return;
14972 };
14973
14974 for chunk in chunks {
14975 let highlight = chunk
14976 .syntax_highlight_id
14977 .and_then(|id| id.name(&style.syntax));
14978 let mut chunk_lines = chunk.text.split('\n').peekable();
14979 while let Some(text) = chunk_lines.next() {
14980 let mut merged_with_last_token = false;
14981 if let Some(last_token) = line.back_mut() {
14982 if last_token.highlight == highlight {
14983 last_token.text.push_str(text);
14984 merged_with_last_token = true;
14985 }
14986 }
14987
14988 if !merged_with_last_token {
14989 line.push_back(Chunk {
14990 text: text.into(),
14991 highlight,
14992 });
14993 }
14994
14995 if chunk_lines.peek().is_some() {
14996 if line.len() > 1 && line.front().unwrap().text.is_empty() {
14997 line.pop_front();
14998 }
14999 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15000 line.pop_back();
15001 }
15002
15003 lines.push(mem::take(&mut line));
15004 }
15005 }
15006 }
15007
15008 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15009 return;
15010 };
15011 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15012 }
15013
15014 pub fn open_context_menu(
15015 &mut self,
15016 _: &OpenContextMenu,
15017 window: &mut Window,
15018 cx: &mut Context<Self>,
15019 ) {
15020 self.request_autoscroll(Autoscroll::newest(), cx);
15021 let position = self.selections.newest_display(cx).start;
15022 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15023 }
15024
15025 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15026 &self.inlay_hint_cache
15027 }
15028
15029 pub fn replay_insert_event(
15030 &mut self,
15031 text: &str,
15032 relative_utf16_range: Option<Range<isize>>,
15033 window: &mut Window,
15034 cx: &mut Context<Self>,
15035 ) {
15036 if !self.input_enabled {
15037 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15038 return;
15039 }
15040 if let Some(relative_utf16_range) = relative_utf16_range {
15041 let selections = self.selections.all::<OffsetUtf16>(cx);
15042 self.change_selections(None, window, cx, |s| {
15043 let new_ranges = selections.into_iter().map(|range| {
15044 let start = OffsetUtf16(
15045 range
15046 .head()
15047 .0
15048 .saturating_add_signed(relative_utf16_range.start),
15049 );
15050 let end = OffsetUtf16(
15051 range
15052 .head()
15053 .0
15054 .saturating_add_signed(relative_utf16_range.end),
15055 );
15056 start..end
15057 });
15058 s.select_ranges(new_ranges);
15059 });
15060 }
15061
15062 self.handle_input(text, window, cx);
15063 }
15064
15065 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15066 let Some(provider) = self.semantics_provider.as_ref() else {
15067 return false;
15068 };
15069
15070 let mut supports = false;
15071 self.buffer().update(cx, |this, cx| {
15072 this.for_each_buffer(|buffer| {
15073 supports |= provider.supports_inlay_hints(buffer, cx);
15074 });
15075 });
15076
15077 supports
15078 }
15079
15080 pub fn is_focused(&self, window: &Window) -> bool {
15081 self.focus_handle.is_focused(window)
15082 }
15083
15084 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15085 cx.emit(EditorEvent::Focused);
15086
15087 if let Some(descendant) = self
15088 .last_focused_descendant
15089 .take()
15090 .and_then(|descendant| descendant.upgrade())
15091 {
15092 window.focus(&descendant);
15093 } else {
15094 if let Some(blame) = self.blame.as_ref() {
15095 blame.update(cx, GitBlame::focus)
15096 }
15097
15098 self.blink_manager.update(cx, BlinkManager::enable);
15099 self.show_cursor_names(window, cx);
15100 self.buffer.update(cx, |buffer, cx| {
15101 buffer.finalize_last_transaction(cx);
15102 if self.leader_peer_id.is_none() {
15103 buffer.set_active_selections(
15104 &self.selections.disjoint_anchors(),
15105 self.selections.line_mode,
15106 self.cursor_shape,
15107 cx,
15108 );
15109 }
15110 });
15111 }
15112 }
15113
15114 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15115 cx.emit(EditorEvent::FocusedIn)
15116 }
15117
15118 fn handle_focus_out(
15119 &mut self,
15120 event: FocusOutEvent,
15121 _window: &mut Window,
15122 _cx: &mut Context<Self>,
15123 ) {
15124 if event.blurred != self.focus_handle {
15125 self.last_focused_descendant = Some(event.blurred);
15126 }
15127 }
15128
15129 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15130 self.blink_manager.update(cx, BlinkManager::disable);
15131 self.buffer
15132 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
15133
15134 if let Some(blame) = self.blame.as_ref() {
15135 blame.update(cx, GitBlame::blur)
15136 }
15137 if !self.hover_state.focused(window, cx) {
15138 hide_hover(self, cx);
15139 }
15140 if !self
15141 .context_menu
15142 .borrow()
15143 .as_ref()
15144 .is_some_and(|context_menu| context_menu.focused(window, cx))
15145 {
15146 self.hide_context_menu(window, cx);
15147 }
15148 self.discard_inline_completion(false, cx);
15149 cx.emit(EditorEvent::Blurred);
15150 cx.notify();
15151 }
15152
15153 pub fn register_action<A: Action>(
15154 &mut self,
15155 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
15156 ) -> Subscription {
15157 let id = self.next_editor_action_id.post_inc();
15158 let listener = Arc::new(listener);
15159 self.editor_actions.borrow_mut().insert(
15160 id,
15161 Box::new(move |window, _| {
15162 let listener = listener.clone();
15163 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
15164 let action = action.downcast_ref().unwrap();
15165 if phase == DispatchPhase::Bubble {
15166 listener(action, window, cx)
15167 }
15168 })
15169 }),
15170 );
15171
15172 let editor_actions = self.editor_actions.clone();
15173 Subscription::new(move || {
15174 editor_actions.borrow_mut().remove(&id);
15175 })
15176 }
15177
15178 pub fn file_header_size(&self) -> u32 {
15179 FILE_HEADER_HEIGHT
15180 }
15181
15182 pub fn revert(
15183 &mut self,
15184 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
15185 window: &mut Window,
15186 cx: &mut Context<Self>,
15187 ) {
15188 self.buffer().update(cx, |multi_buffer, cx| {
15189 for (buffer_id, changes) in revert_changes {
15190 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15191 buffer.update(cx, |buffer, cx| {
15192 buffer.edit(
15193 changes.into_iter().map(|(range, text)| {
15194 (range, text.to_string().map(Arc::<str>::from))
15195 }),
15196 None,
15197 cx,
15198 );
15199 });
15200 }
15201 }
15202 });
15203 self.change_selections(None, window, cx, |selections| selections.refresh());
15204 }
15205
15206 pub fn to_pixel_point(
15207 &self,
15208 source: multi_buffer::Anchor,
15209 editor_snapshot: &EditorSnapshot,
15210 window: &mut Window,
15211 ) -> Option<gpui::Point<Pixels>> {
15212 let source_point = source.to_display_point(editor_snapshot);
15213 self.display_to_pixel_point(source_point, editor_snapshot, window)
15214 }
15215
15216 pub fn display_to_pixel_point(
15217 &self,
15218 source: DisplayPoint,
15219 editor_snapshot: &EditorSnapshot,
15220 window: &mut Window,
15221 ) -> Option<gpui::Point<Pixels>> {
15222 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
15223 let text_layout_details = self.text_layout_details(window);
15224 let scroll_top = text_layout_details
15225 .scroll_anchor
15226 .scroll_position(editor_snapshot)
15227 .y;
15228
15229 if source.row().as_f32() < scroll_top.floor() {
15230 return None;
15231 }
15232 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
15233 let source_y = line_height * (source.row().as_f32() - scroll_top);
15234 Some(gpui::Point::new(source_x, source_y))
15235 }
15236
15237 pub fn has_visible_completions_menu(&self) -> bool {
15238 !self.edit_prediction_preview_is_active()
15239 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
15240 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
15241 })
15242 }
15243
15244 pub fn register_addon<T: Addon>(&mut self, instance: T) {
15245 self.addons
15246 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
15247 }
15248
15249 pub fn unregister_addon<T: Addon>(&mut self) {
15250 self.addons.remove(&std::any::TypeId::of::<T>());
15251 }
15252
15253 pub fn addon<T: Addon>(&self) -> Option<&T> {
15254 let type_id = std::any::TypeId::of::<T>();
15255 self.addons
15256 .get(&type_id)
15257 .and_then(|item| item.to_any().downcast_ref::<T>())
15258 }
15259
15260 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
15261 let text_layout_details = self.text_layout_details(window);
15262 let style = &text_layout_details.editor_style;
15263 let font_id = window.text_system().resolve_font(&style.text.font());
15264 let font_size = style.text.font_size.to_pixels(window.rem_size());
15265 let line_height = style.text.line_height_in_pixels(window.rem_size());
15266 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
15267
15268 gpui::Size::new(em_width, line_height)
15269 }
15270
15271 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
15272 self.load_diff_task.clone()
15273 }
15274
15275 fn read_selections_from_db(
15276 &mut self,
15277 item_id: u64,
15278 workspace_id: WorkspaceId,
15279 window: &mut Window,
15280 cx: &mut Context<Editor>,
15281 ) {
15282 if !self.is_singleton(cx)
15283 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
15284 {
15285 return;
15286 }
15287 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
15288 return;
15289 };
15290 if selections.is_empty() {
15291 return;
15292 }
15293
15294 let snapshot = self.buffer.read(cx).snapshot(cx);
15295 self.change_selections(None, window, cx, |s| {
15296 s.select_ranges(selections.into_iter().map(|(start, end)| {
15297 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
15298 }));
15299 });
15300 }
15301}
15302
15303fn insert_extra_newline_brackets(
15304 buffer: &MultiBufferSnapshot,
15305 range: Range<usize>,
15306 language: &language::LanguageScope,
15307) -> bool {
15308 let leading_whitespace_len = buffer
15309 .reversed_chars_at(range.start)
15310 .take_while(|c| c.is_whitespace() && *c != '\n')
15311 .map(|c| c.len_utf8())
15312 .sum::<usize>();
15313 let trailing_whitespace_len = buffer
15314 .chars_at(range.end)
15315 .take_while(|c| c.is_whitespace() && *c != '\n')
15316 .map(|c| c.len_utf8())
15317 .sum::<usize>();
15318 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
15319
15320 language.brackets().any(|(pair, enabled)| {
15321 let pair_start = pair.start.trim_end();
15322 let pair_end = pair.end.trim_start();
15323
15324 enabled
15325 && pair.newline
15326 && buffer.contains_str_at(range.end, pair_end)
15327 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
15328 })
15329}
15330
15331fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
15332 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
15333 [(buffer, range, _)] => (*buffer, range.clone()),
15334 _ => return false,
15335 };
15336 let pair = {
15337 let mut result: Option<BracketMatch> = None;
15338
15339 for pair in buffer
15340 .all_bracket_ranges(range.clone())
15341 .filter(move |pair| {
15342 pair.open_range.start <= range.start && pair.close_range.end >= range.end
15343 })
15344 {
15345 let len = pair.close_range.end - pair.open_range.start;
15346
15347 if let Some(existing) = &result {
15348 let existing_len = existing.close_range.end - existing.open_range.start;
15349 if len > existing_len {
15350 continue;
15351 }
15352 }
15353
15354 result = Some(pair);
15355 }
15356
15357 result
15358 };
15359 let Some(pair) = pair else {
15360 return false;
15361 };
15362 pair.newline_only
15363 && buffer
15364 .chars_for_range(pair.open_range.end..range.start)
15365 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
15366 .all(|c| c.is_whitespace() && c != '\n')
15367}
15368
15369fn get_uncommitted_diff_for_buffer(
15370 project: &Entity<Project>,
15371 buffers: impl IntoIterator<Item = Entity<Buffer>>,
15372 buffer: Entity<MultiBuffer>,
15373 cx: &mut App,
15374) -> Task<()> {
15375 let mut tasks = Vec::new();
15376 project.update(cx, |project, cx| {
15377 for buffer in buffers {
15378 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
15379 }
15380 });
15381 cx.spawn(|mut cx| async move {
15382 let diffs = futures::future::join_all(tasks).await;
15383 buffer
15384 .update(&mut cx, |buffer, cx| {
15385 for diff in diffs.into_iter().flatten() {
15386 buffer.add_diff(diff, cx);
15387 }
15388 })
15389 .ok();
15390 })
15391}
15392
15393fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
15394 let tab_size = tab_size.get() as usize;
15395 let mut width = offset;
15396
15397 for ch in text.chars() {
15398 width += if ch == '\t' {
15399 tab_size - (width % tab_size)
15400 } else {
15401 1
15402 };
15403 }
15404
15405 width - offset
15406}
15407
15408#[cfg(test)]
15409mod tests {
15410 use super::*;
15411
15412 #[test]
15413 fn test_string_size_with_expanded_tabs() {
15414 let nz = |val| NonZeroU32::new(val).unwrap();
15415 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
15416 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
15417 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
15418 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
15419 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
15420 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
15421 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
15422 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
15423 }
15424}
15425
15426/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
15427struct WordBreakingTokenizer<'a> {
15428 input: &'a str,
15429}
15430
15431impl<'a> WordBreakingTokenizer<'a> {
15432 fn new(input: &'a str) -> Self {
15433 Self { input }
15434 }
15435}
15436
15437fn is_char_ideographic(ch: char) -> bool {
15438 use unicode_script::Script::*;
15439 use unicode_script::UnicodeScript;
15440 matches!(ch.script(), Han | Tangut | Yi)
15441}
15442
15443fn is_grapheme_ideographic(text: &str) -> bool {
15444 text.chars().any(is_char_ideographic)
15445}
15446
15447fn is_grapheme_whitespace(text: &str) -> bool {
15448 text.chars().any(|x| x.is_whitespace())
15449}
15450
15451fn should_stay_with_preceding_ideograph(text: &str) -> bool {
15452 text.chars().next().map_or(false, |ch| {
15453 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
15454 })
15455}
15456
15457#[derive(PartialEq, Eq, Debug, Clone, Copy)]
15458struct WordBreakToken<'a> {
15459 token: &'a str,
15460 grapheme_len: usize,
15461 is_whitespace: bool,
15462}
15463
15464impl<'a> Iterator for WordBreakingTokenizer<'a> {
15465 /// Yields a span, the count of graphemes in the token, and whether it was
15466 /// whitespace. Note that it also breaks at word boundaries.
15467 type Item = WordBreakToken<'a>;
15468
15469 fn next(&mut self) -> Option<Self::Item> {
15470 use unicode_segmentation::UnicodeSegmentation;
15471 if self.input.is_empty() {
15472 return None;
15473 }
15474
15475 let mut iter = self.input.graphemes(true).peekable();
15476 let mut offset = 0;
15477 let mut graphemes = 0;
15478 if let Some(first_grapheme) = iter.next() {
15479 let is_whitespace = is_grapheme_whitespace(first_grapheme);
15480 offset += first_grapheme.len();
15481 graphemes += 1;
15482 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
15483 if let Some(grapheme) = iter.peek().copied() {
15484 if should_stay_with_preceding_ideograph(grapheme) {
15485 offset += grapheme.len();
15486 graphemes += 1;
15487 }
15488 }
15489 } else {
15490 let mut words = self.input[offset..].split_word_bound_indices().peekable();
15491 let mut next_word_bound = words.peek().copied();
15492 if next_word_bound.map_or(false, |(i, _)| i == 0) {
15493 next_word_bound = words.next();
15494 }
15495 while let Some(grapheme) = iter.peek().copied() {
15496 if next_word_bound.map_or(false, |(i, _)| i == offset) {
15497 break;
15498 };
15499 if is_grapheme_whitespace(grapheme) != is_whitespace {
15500 break;
15501 };
15502 offset += grapheme.len();
15503 graphemes += 1;
15504 iter.next();
15505 }
15506 }
15507 let token = &self.input[..offset];
15508 self.input = &self.input[offset..];
15509 if is_whitespace {
15510 Some(WordBreakToken {
15511 token: " ",
15512 grapheme_len: 1,
15513 is_whitespace: true,
15514 })
15515 } else {
15516 Some(WordBreakToken {
15517 token,
15518 grapheme_len: graphemes,
15519 is_whitespace: false,
15520 })
15521 }
15522 } else {
15523 None
15524 }
15525 }
15526}
15527
15528#[test]
15529fn test_word_breaking_tokenizer() {
15530 let tests: &[(&str, &[(&str, usize, bool)])] = &[
15531 ("", &[]),
15532 (" ", &[(" ", 1, true)]),
15533 ("Ʒ", &[("Ʒ", 1, false)]),
15534 ("Ǽ", &[("Ǽ", 1, false)]),
15535 ("⋑", &[("⋑", 1, false)]),
15536 ("⋑⋑", &[("⋑⋑", 2, false)]),
15537 (
15538 "原理,进而",
15539 &[
15540 ("原", 1, false),
15541 ("理,", 2, false),
15542 ("进", 1, false),
15543 ("而", 1, false),
15544 ],
15545 ),
15546 (
15547 "hello world",
15548 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
15549 ),
15550 (
15551 "hello, world",
15552 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
15553 ),
15554 (
15555 " hello world",
15556 &[
15557 (" ", 1, true),
15558 ("hello", 5, false),
15559 (" ", 1, true),
15560 ("world", 5, false),
15561 ],
15562 ),
15563 (
15564 "这是什么 \n 钢笔",
15565 &[
15566 ("这", 1, false),
15567 ("是", 1, false),
15568 ("什", 1, false),
15569 ("么", 1, false),
15570 (" ", 1, true),
15571 ("钢", 1, false),
15572 ("笔", 1, false),
15573 ],
15574 ),
15575 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
15576 ];
15577
15578 for (input, result) in tests {
15579 assert_eq!(
15580 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
15581 result
15582 .iter()
15583 .copied()
15584 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
15585 token,
15586 grapheme_len,
15587 is_whitespace,
15588 })
15589 .collect::<Vec<_>>()
15590 );
15591 }
15592}
15593
15594fn wrap_with_prefix(
15595 line_prefix: String,
15596 unwrapped_text: String,
15597 wrap_column: usize,
15598 tab_size: NonZeroU32,
15599) -> String {
15600 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
15601 let mut wrapped_text = String::new();
15602 let mut current_line = line_prefix.clone();
15603
15604 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
15605 let mut current_line_len = line_prefix_len;
15606 for WordBreakToken {
15607 token,
15608 grapheme_len,
15609 is_whitespace,
15610 } in tokenizer
15611 {
15612 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
15613 wrapped_text.push_str(current_line.trim_end());
15614 wrapped_text.push('\n');
15615 current_line.truncate(line_prefix.len());
15616 current_line_len = line_prefix_len;
15617 if !is_whitespace {
15618 current_line.push_str(token);
15619 current_line_len += grapheme_len;
15620 }
15621 } else if !is_whitespace {
15622 current_line.push_str(token);
15623 current_line_len += grapheme_len;
15624 } else if current_line_len != line_prefix_len {
15625 current_line.push(' ');
15626 current_line_len += 1;
15627 }
15628 }
15629
15630 if !current_line.is_empty() {
15631 wrapped_text.push_str(¤t_line);
15632 }
15633 wrapped_text
15634}
15635
15636#[test]
15637fn test_wrap_with_prefix() {
15638 assert_eq!(
15639 wrap_with_prefix(
15640 "# ".to_string(),
15641 "abcdefg".to_string(),
15642 4,
15643 NonZeroU32::new(4).unwrap()
15644 ),
15645 "# abcdefg"
15646 );
15647 assert_eq!(
15648 wrap_with_prefix(
15649 "".to_string(),
15650 "\thello world".to_string(),
15651 8,
15652 NonZeroU32::new(4).unwrap()
15653 ),
15654 "hello\nworld"
15655 );
15656 assert_eq!(
15657 wrap_with_prefix(
15658 "// ".to_string(),
15659 "xx \nyy zz aa bb cc".to_string(),
15660 12,
15661 NonZeroU32::new(4).unwrap()
15662 ),
15663 "// xx yy zz\n// aa bb cc"
15664 );
15665 assert_eq!(
15666 wrap_with_prefix(
15667 String::new(),
15668 "这是什么 \n 钢笔".to_string(),
15669 3,
15670 NonZeroU32::new(4).unwrap()
15671 ),
15672 "这是什\n么 钢\n笔"
15673 );
15674}
15675
15676pub trait CollaborationHub {
15677 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
15678 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
15679 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
15680}
15681
15682impl CollaborationHub for Entity<Project> {
15683 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
15684 self.read(cx).collaborators()
15685 }
15686
15687 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
15688 self.read(cx).user_store().read(cx).participant_indices()
15689 }
15690
15691 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
15692 let this = self.read(cx);
15693 let user_ids = this.collaborators().values().map(|c| c.user_id);
15694 this.user_store().read_with(cx, |user_store, cx| {
15695 user_store.participant_names(user_ids, cx)
15696 })
15697 }
15698}
15699
15700pub trait SemanticsProvider {
15701 fn hover(
15702 &self,
15703 buffer: &Entity<Buffer>,
15704 position: text::Anchor,
15705 cx: &mut App,
15706 ) -> Option<Task<Vec<project::Hover>>>;
15707
15708 fn inlay_hints(
15709 &self,
15710 buffer_handle: Entity<Buffer>,
15711 range: Range<text::Anchor>,
15712 cx: &mut App,
15713 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
15714
15715 fn resolve_inlay_hint(
15716 &self,
15717 hint: InlayHint,
15718 buffer_handle: Entity<Buffer>,
15719 server_id: LanguageServerId,
15720 cx: &mut App,
15721 ) -> Option<Task<anyhow::Result<InlayHint>>>;
15722
15723 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
15724
15725 fn document_highlights(
15726 &self,
15727 buffer: &Entity<Buffer>,
15728 position: text::Anchor,
15729 cx: &mut App,
15730 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
15731
15732 fn definitions(
15733 &self,
15734 buffer: &Entity<Buffer>,
15735 position: text::Anchor,
15736 kind: GotoDefinitionKind,
15737 cx: &mut App,
15738 ) -> Option<Task<Result<Vec<LocationLink>>>>;
15739
15740 fn range_for_rename(
15741 &self,
15742 buffer: &Entity<Buffer>,
15743 position: text::Anchor,
15744 cx: &mut App,
15745 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
15746
15747 fn perform_rename(
15748 &self,
15749 buffer: &Entity<Buffer>,
15750 position: text::Anchor,
15751 new_name: String,
15752 cx: &mut App,
15753 ) -> Option<Task<Result<ProjectTransaction>>>;
15754}
15755
15756pub trait CompletionProvider {
15757 fn completions(
15758 &self,
15759 buffer: &Entity<Buffer>,
15760 buffer_position: text::Anchor,
15761 trigger: CompletionContext,
15762 window: &mut Window,
15763 cx: &mut Context<Editor>,
15764 ) -> Task<Result<Vec<Completion>>>;
15765
15766 fn resolve_completions(
15767 &self,
15768 buffer: Entity<Buffer>,
15769 completion_indices: Vec<usize>,
15770 completions: Rc<RefCell<Box<[Completion]>>>,
15771 cx: &mut Context<Editor>,
15772 ) -> Task<Result<bool>>;
15773
15774 fn apply_additional_edits_for_completion(
15775 &self,
15776 _buffer: Entity<Buffer>,
15777 _completions: Rc<RefCell<Box<[Completion]>>>,
15778 _completion_index: usize,
15779 _push_to_history: bool,
15780 _cx: &mut Context<Editor>,
15781 ) -> Task<Result<Option<language::Transaction>>> {
15782 Task::ready(Ok(None))
15783 }
15784
15785 fn is_completion_trigger(
15786 &self,
15787 buffer: &Entity<Buffer>,
15788 position: language::Anchor,
15789 text: &str,
15790 trigger_in_words: bool,
15791 cx: &mut Context<Editor>,
15792 ) -> bool;
15793
15794 fn sort_completions(&self) -> bool {
15795 true
15796 }
15797}
15798
15799pub trait CodeActionProvider {
15800 fn id(&self) -> Arc<str>;
15801
15802 fn code_actions(
15803 &self,
15804 buffer: &Entity<Buffer>,
15805 range: Range<text::Anchor>,
15806 window: &mut Window,
15807 cx: &mut App,
15808 ) -> Task<Result<Vec<CodeAction>>>;
15809
15810 fn apply_code_action(
15811 &self,
15812 buffer_handle: Entity<Buffer>,
15813 action: CodeAction,
15814 excerpt_id: ExcerptId,
15815 push_to_history: bool,
15816 window: &mut Window,
15817 cx: &mut App,
15818 ) -> Task<Result<ProjectTransaction>>;
15819}
15820
15821impl CodeActionProvider for Entity<Project> {
15822 fn id(&self) -> Arc<str> {
15823 "project".into()
15824 }
15825
15826 fn code_actions(
15827 &self,
15828 buffer: &Entity<Buffer>,
15829 range: Range<text::Anchor>,
15830 _window: &mut Window,
15831 cx: &mut App,
15832 ) -> Task<Result<Vec<CodeAction>>> {
15833 self.update(cx, |project, cx| {
15834 project.code_actions(buffer, range, None, cx)
15835 })
15836 }
15837
15838 fn apply_code_action(
15839 &self,
15840 buffer_handle: Entity<Buffer>,
15841 action: CodeAction,
15842 _excerpt_id: ExcerptId,
15843 push_to_history: bool,
15844 _window: &mut Window,
15845 cx: &mut App,
15846 ) -> Task<Result<ProjectTransaction>> {
15847 self.update(cx, |project, cx| {
15848 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15849 })
15850 }
15851}
15852
15853fn snippet_completions(
15854 project: &Project,
15855 buffer: &Entity<Buffer>,
15856 buffer_position: text::Anchor,
15857 cx: &mut App,
15858) -> Task<Result<Vec<Completion>>> {
15859 let language = buffer.read(cx).language_at(buffer_position);
15860 let language_name = language.as_ref().map(|language| language.lsp_id());
15861 let snippet_store = project.snippets().read(cx);
15862 let snippets = snippet_store.snippets_for(language_name, cx);
15863
15864 if snippets.is_empty() {
15865 return Task::ready(Ok(vec![]));
15866 }
15867 let snapshot = buffer.read(cx).text_snapshot();
15868 let chars: String = snapshot
15869 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15870 .collect();
15871
15872 let scope = language.map(|language| language.default_scope());
15873 let executor = cx.background_executor().clone();
15874
15875 cx.background_spawn(async move {
15876 let classifier = CharClassifier::new(scope).for_completion(true);
15877 let mut last_word = chars
15878 .chars()
15879 .take_while(|c| classifier.is_word(*c))
15880 .collect::<String>();
15881 last_word = last_word.chars().rev().collect();
15882
15883 if last_word.is_empty() {
15884 return Ok(vec![]);
15885 }
15886
15887 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15888 let to_lsp = |point: &text::Anchor| {
15889 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15890 point_to_lsp(end)
15891 };
15892 let lsp_end = to_lsp(&buffer_position);
15893
15894 let candidates = snippets
15895 .iter()
15896 .enumerate()
15897 .flat_map(|(ix, snippet)| {
15898 snippet
15899 .prefix
15900 .iter()
15901 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15902 })
15903 .collect::<Vec<StringMatchCandidate>>();
15904
15905 let mut matches = fuzzy::match_strings(
15906 &candidates,
15907 &last_word,
15908 last_word.chars().any(|c| c.is_uppercase()),
15909 100,
15910 &Default::default(),
15911 executor,
15912 )
15913 .await;
15914
15915 // Remove all candidates where the query's start does not match the start of any word in the candidate
15916 if let Some(query_start) = last_word.chars().next() {
15917 matches.retain(|string_match| {
15918 split_words(&string_match.string).any(|word| {
15919 // Check that the first codepoint of the word as lowercase matches the first
15920 // codepoint of the query as lowercase
15921 word.chars()
15922 .flat_map(|codepoint| codepoint.to_lowercase())
15923 .zip(query_start.to_lowercase())
15924 .all(|(word_cp, query_cp)| word_cp == query_cp)
15925 })
15926 });
15927 }
15928
15929 let matched_strings = matches
15930 .into_iter()
15931 .map(|m| m.string)
15932 .collect::<HashSet<_>>();
15933
15934 let result: Vec<Completion> = snippets
15935 .into_iter()
15936 .filter_map(|snippet| {
15937 let matching_prefix = snippet
15938 .prefix
15939 .iter()
15940 .find(|prefix| matched_strings.contains(*prefix))?;
15941 let start = as_offset - last_word.len();
15942 let start = snapshot.anchor_before(start);
15943 let range = start..buffer_position;
15944 let lsp_start = to_lsp(&start);
15945 let lsp_range = lsp::Range {
15946 start: lsp_start,
15947 end: lsp_end,
15948 };
15949 Some(Completion {
15950 old_range: range,
15951 new_text: snippet.body.clone(),
15952 resolved: false,
15953 label: CodeLabel {
15954 text: matching_prefix.clone(),
15955 runs: vec![],
15956 filter_range: 0..matching_prefix.len(),
15957 },
15958 server_id: LanguageServerId(usize::MAX),
15959 documentation: snippet
15960 .description
15961 .clone()
15962 .map(|description| CompletionDocumentation::SingleLine(description.into())),
15963 lsp_completion: lsp::CompletionItem {
15964 label: snippet.prefix.first().unwrap().clone(),
15965 kind: Some(CompletionItemKind::SNIPPET),
15966 label_details: snippet.description.as_ref().map(|description| {
15967 lsp::CompletionItemLabelDetails {
15968 detail: Some(description.clone()),
15969 description: None,
15970 }
15971 }),
15972 insert_text_format: Some(InsertTextFormat::SNIPPET),
15973 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15974 lsp::InsertReplaceEdit {
15975 new_text: snippet.body.clone(),
15976 insert: lsp_range,
15977 replace: lsp_range,
15978 },
15979 )),
15980 filter_text: Some(snippet.body.clone()),
15981 sort_text: Some(char::MAX.to_string()),
15982 ..Default::default()
15983 },
15984 confirm: None,
15985 })
15986 })
15987 .collect();
15988
15989 Ok(result)
15990 })
15991}
15992
15993impl CompletionProvider for Entity<Project> {
15994 fn completions(
15995 &self,
15996 buffer: &Entity<Buffer>,
15997 buffer_position: text::Anchor,
15998 options: CompletionContext,
15999 _window: &mut Window,
16000 cx: &mut Context<Editor>,
16001 ) -> Task<Result<Vec<Completion>>> {
16002 self.update(cx, |project, cx| {
16003 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16004 let project_completions = project.completions(buffer, buffer_position, options, cx);
16005 cx.background_spawn(async move {
16006 let mut completions = project_completions.await?;
16007 let snippets_completions = snippets.await?;
16008 completions.extend(snippets_completions);
16009 Ok(completions)
16010 })
16011 })
16012 }
16013
16014 fn resolve_completions(
16015 &self,
16016 buffer: Entity<Buffer>,
16017 completion_indices: Vec<usize>,
16018 completions: Rc<RefCell<Box<[Completion]>>>,
16019 cx: &mut Context<Editor>,
16020 ) -> Task<Result<bool>> {
16021 self.update(cx, |project, cx| {
16022 project.lsp_store().update(cx, |lsp_store, cx| {
16023 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16024 })
16025 })
16026 }
16027
16028 fn apply_additional_edits_for_completion(
16029 &self,
16030 buffer: Entity<Buffer>,
16031 completions: Rc<RefCell<Box<[Completion]>>>,
16032 completion_index: usize,
16033 push_to_history: bool,
16034 cx: &mut Context<Editor>,
16035 ) -> Task<Result<Option<language::Transaction>>> {
16036 self.update(cx, |project, cx| {
16037 project.lsp_store().update(cx, |lsp_store, cx| {
16038 lsp_store.apply_additional_edits_for_completion(
16039 buffer,
16040 completions,
16041 completion_index,
16042 push_to_history,
16043 cx,
16044 )
16045 })
16046 })
16047 }
16048
16049 fn is_completion_trigger(
16050 &self,
16051 buffer: &Entity<Buffer>,
16052 position: language::Anchor,
16053 text: &str,
16054 trigger_in_words: bool,
16055 cx: &mut Context<Editor>,
16056 ) -> bool {
16057 let mut chars = text.chars();
16058 let char = if let Some(char) = chars.next() {
16059 char
16060 } else {
16061 return false;
16062 };
16063 if chars.next().is_some() {
16064 return false;
16065 }
16066
16067 let buffer = buffer.read(cx);
16068 let snapshot = buffer.snapshot();
16069 if !snapshot.settings_at(position, cx).show_completions_on_input {
16070 return false;
16071 }
16072 let classifier = snapshot.char_classifier_at(position).for_completion(true);
16073 if trigger_in_words && classifier.is_word(char) {
16074 return true;
16075 }
16076
16077 buffer.completion_triggers().contains(text)
16078 }
16079}
16080
16081impl SemanticsProvider for Entity<Project> {
16082 fn hover(
16083 &self,
16084 buffer: &Entity<Buffer>,
16085 position: text::Anchor,
16086 cx: &mut App,
16087 ) -> Option<Task<Vec<project::Hover>>> {
16088 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
16089 }
16090
16091 fn document_highlights(
16092 &self,
16093 buffer: &Entity<Buffer>,
16094 position: text::Anchor,
16095 cx: &mut App,
16096 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
16097 Some(self.update(cx, |project, cx| {
16098 project.document_highlights(buffer, position, cx)
16099 }))
16100 }
16101
16102 fn definitions(
16103 &self,
16104 buffer: &Entity<Buffer>,
16105 position: text::Anchor,
16106 kind: GotoDefinitionKind,
16107 cx: &mut App,
16108 ) -> Option<Task<Result<Vec<LocationLink>>>> {
16109 Some(self.update(cx, |project, cx| match kind {
16110 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
16111 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
16112 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
16113 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
16114 }))
16115 }
16116
16117 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
16118 // TODO: make this work for remote projects
16119 self.update(cx, |this, cx| {
16120 buffer.update(cx, |buffer, cx| {
16121 this.any_language_server_supports_inlay_hints(buffer, cx)
16122 })
16123 })
16124 }
16125
16126 fn inlay_hints(
16127 &self,
16128 buffer_handle: Entity<Buffer>,
16129 range: Range<text::Anchor>,
16130 cx: &mut App,
16131 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
16132 Some(self.update(cx, |project, cx| {
16133 project.inlay_hints(buffer_handle, range, cx)
16134 }))
16135 }
16136
16137 fn resolve_inlay_hint(
16138 &self,
16139 hint: InlayHint,
16140 buffer_handle: Entity<Buffer>,
16141 server_id: LanguageServerId,
16142 cx: &mut App,
16143 ) -> Option<Task<anyhow::Result<InlayHint>>> {
16144 Some(self.update(cx, |project, cx| {
16145 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
16146 }))
16147 }
16148
16149 fn range_for_rename(
16150 &self,
16151 buffer: &Entity<Buffer>,
16152 position: text::Anchor,
16153 cx: &mut App,
16154 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
16155 Some(self.update(cx, |project, cx| {
16156 let buffer = buffer.clone();
16157 let task = project.prepare_rename(buffer.clone(), position, cx);
16158 cx.spawn(|_, mut cx| async move {
16159 Ok(match task.await? {
16160 PrepareRenameResponse::Success(range) => Some(range),
16161 PrepareRenameResponse::InvalidPosition => None,
16162 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
16163 // Fallback on using TreeSitter info to determine identifier range
16164 buffer.update(&mut cx, |buffer, _| {
16165 let snapshot = buffer.snapshot();
16166 let (range, kind) = snapshot.surrounding_word(position);
16167 if kind != Some(CharKind::Word) {
16168 return None;
16169 }
16170 Some(
16171 snapshot.anchor_before(range.start)
16172 ..snapshot.anchor_after(range.end),
16173 )
16174 })?
16175 }
16176 })
16177 })
16178 }))
16179 }
16180
16181 fn perform_rename(
16182 &self,
16183 buffer: &Entity<Buffer>,
16184 position: text::Anchor,
16185 new_name: String,
16186 cx: &mut App,
16187 ) -> Option<Task<Result<ProjectTransaction>>> {
16188 Some(self.update(cx, |project, cx| {
16189 project.perform_rename(buffer.clone(), position, new_name, cx)
16190 }))
16191 }
16192}
16193
16194fn inlay_hint_settings(
16195 location: Anchor,
16196 snapshot: &MultiBufferSnapshot,
16197 cx: &mut Context<Editor>,
16198) -> InlayHintSettings {
16199 let file = snapshot.file_at(location);
16200 let language = snapshot.language_at(location).map(|l| l.name());
16201 language_settings(language, file, cx).inlay_hints
16202}
16203
16204fn consume_contiguous_rows(
16205 contiguous_row_selections: &mut Vec<Selection<Point>>,
16206 selection: &Selection<Point>,
16207 display_map: &DisplaySnapshot,
16208 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
16209) -> (MultiBufferRow, MultiBufferRow) {
16210 contiguous_row_selections.push(selection.clone());
16211 let start_row = MultiBufferRow(selection.start.row);
16212 let mut end_row = ending_row(selection, display_map);
16213
16214 while let Some(next_selection) = selections.peek() {
16215 if next_selection.start.row <= end_row.0 {
16216 end_row = ending_row(next_selection, display_map);
16217 contiguous_row_selections.push(selections.next().unwrap().clone());
16218 } else {
16219 break;
16220 }
16221 }
16222 (start_row, end_row)
16223}
16224
16225fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
16226 if next_selection.end.column > 0 || next_selection.is_empty() {
16227 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
16228 } else {
16229 MultiBufferRow(next_selection.end.row)
16230 }
16231}
16232
16233impl EditorSnapshot {
16234 pub fn remote_selections_in_range<'a>(
16235 &'a self,
16236 range: &'a Range<Anchor>,
16237 collaboration_hub: &dyn CollaborationHub,
16238 cx: &'a App,
16239 ) -> impl 'a + Iterator<Item = RemoteSelection> {
16240 let participant_names = collaboration_hub.user_names(cx);
16241 let participant_indices = collaboration_hub.user_participant_indices(cx);
16242 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
16243 let collaborators_by_replica_id = collaborators_by_peer_id
16244 .iter()
16245 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
16246 .collect::<HashMap<_, _>>();
16247 self.buffer_snapshot
16248 .selections_in_range(range, false)
16249 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
16250 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
16251 let participant_index = participant_indices.get(&collaborator.user_id).copied();
16252 let user_name = participant_names.get(&collaborator.user_id).cloned();
16253 Some(RemoteSelection {
16254 replica_id,
16255 selection,
16256 cursor_shape,
16257 line_mode,
16258 participant_index,
16259 peer_id: collaborator.peer_id,
16260 user_name,
16261 })
16262 })
16263 }
16264
16265 pub fn hunks_for_ranges(
16266 &self,
16267 ranges: impl Iterator<Item = Range<Point>>,
16268 ) -> Vec<MultiBufferDiffHunk> {
16269 let mut hunks = Vec::new();
16270 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
16271 HashMap::default();
16272 for query_range in ranges {
16273 let query_rows =
16274 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
16275 for hunk in self.buffer_snapshot.diff_hunks_in_range(
16276 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
16277 ) {
16278 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
16279 // when the caret is just above or just below the deleted hunk.
16280 let allow_adjacent = hunk.status().is_deleted();
16281 let related_to_selection = if allow_adjacent {
16282 hunk.row_range.overlaps(&query_rows)
16283 || hunk.row_range.start == query_rows.end
16284 || hunk.row_range.end == query_rows.start
16285 } else {
16286 hunk.row_range.overlaps(&query_rows)
16287 };
16288 if related_to_selection {
16289 if !processed_buffer_rows
16290 .entry(hunk.buffer_id)
16291 .or_default()
16292 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
16293 {
16294 continue;
16295 }
16296 hunks.push(hunk);
16297 }
16298 }
16299 }
16300
16301 hunks
16302 }
16303
16304 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
16305 self.display_snapshot.buffer_snapshot.language_at(position)
16306 }
16307
16308 pub fn is_focused(&self) -> bool {
16309 self.is_focused
16310 }
16311
16312 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
16313 self.placeholder_text.as_ref()
16314 }
16315
16316 pub fn scroll_position(&self) -> gpui::Point<f32> {
16317 self.scroll_anchor.scroll_position(&self.display_snapshot)
16318 }
16319
16320 fn gutter_dimensions(
16321 &self,
16322 font_id: FontId,
16323 font_size: Pixels,
16324 max_line_number_width: Pixels,
16325 cx: &App,
16326 ) -> Option<GutterDimensions> {
16327 if !self.show_gutter {
16328 return None;
16329 }
16330
16331 let descent = cx.text_system().descent(font_id, font_size);
16332 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
16333 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
16334
16335 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
16336 matches!(
16337 ProjectSettings::get_global(cx).git.git_gutter,
16338 Some(GitGutterSetting::TrackedFiles)
16339 )
16340 });
16341 let gutter_settings = EditorSettings::get_global(cx).gutter;
16342 let show_line_numbers = self
16343 .show_line_numbers
16344 .unwrap_or(gutter_settings.line_numbers);
16345 let line_gutter_width = if show_line_numbers {
16346 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
16347 let min_width_for_number_on_gutter = em_advance * 4.0;
16348 max_line_number_width.max(min_width_for_number_on_gutter)
16349 } else {
16350 0.0.into()
16351 };
16352
16353 let show_code_actions = self
16354 .show_code_actions
16355 .unwrap_or(gutter_settings.code_actions);
16356
16357 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
16358
16359 let git_blame_entries_width =
16360 self.git_blame_gutter_max_author_length
16361 .map(|max_author_length| {
16362 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
16363
16364 /// The number of characters to dedicate to gaps and margins.
16365 const SPACING_WIDTH: usize = 4;
16366
16367 let max_char_count = max_author_length
16368 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
16369 + ::git::SHORT_SHA_LENGTH
16370 + MAX_RELATIVE_TIMESTAMP.len()
16371 + SPACING_WIDTH;
16372
16373 em_advance * max_char_count
16374 });
16375
16376 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
16377 left_padding += if show_code_actions || show_runnables {
16378 em_width * 3.0
16379 } else if show_git_gutter && show_line_numbers {
16380 em_width * 2.0
16381 } else if show_git_gutter || show_line_numbers {
16382 em_width
16383 } else {
16384 px(0.)
16385 };
16386
16387 let right_padding = if gutter_settings.folds && show_line_numbers {
16388 em_width * 4.0
16389 } else if gutter_settings.folds {
16390 em_width * 3.0
16391 } else if show_line_numbers {
16392 em_width
16393 } else {
16394 px(0.)
16395 };
16396
16397 Some(GutterDimensions {
16398 left_padding,
16399 right_padding,
16400 width: line_gutter_width + left_padding + right_padding,
16401 margin: -descent,
16402 git_blame_entries_width,
16403 })
16404 }
16405
16406 pub fn render_crease_toggle(
16407 &self,
16408 buffer_row: MultiBufferRow,
16409 row_contains_cursor: bool,
16410 editor: Entity<Editor>,
16411 window: &mut Window,
16412 cx: &mut App,
16413 ) -> Option<AnyElement> {
16414 let folded = self.is_line_folded(buffer_row);
16415 let mut is_foldable = false;
16416
16417 if let Some(crease) = self
16418 .crease_snapshot
16419 .query_row(buffer_row, &self.buffer_snapshot)
16420 {
16421 is_foldable = true;
16422 match crease {
16423 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
16424 if let Some(render_toggle) = render_toggle {
16425 let toggle_callback =
16426 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
16427 if folded {
16428 editor.update(cx, |editor, cx| {
16429 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
16430 });
16431 } else {
16432 editor.update(cx, |editor, cx| {
16433 editor.unfold_at(
16434 &crate::UnfoldAt { buffer_row },
16435 window,
16436 cx,
16437 )
16438 });
16439 }
16440 });
16441 return Some((render_toggle)(
16442 buffer_row,
16443 folded,
16444 toggle_callback,
16445 window,
16446 cx,
16447 ));
16448 }
16449 }
16450 }
16451 }
16452
16453 is_foldable |= self.starts_indent(buffer_row);
16454
16455 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
16456 Some(
16457 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
16458 .toggle_state(folded)
16459 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
16460 if folded {
16461 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
16462 } else {
16463 this.fold_at(&FoldAt { buffer_row }, window, cx);
16464 }
16465 }))
16466 .into_any_element(),
16467 )
16468 } else {
16469 None
16470 }
16471 }
16472
16473 pub fn render_crease_trailer(
16474 &self,
16475 buffer_row: MultiBufferRow,
16476 window: &mut Window,
16477 cx: &mut App,
16478 ) -> Option<AnyElement> {
16479 let folded = self.is_line_folded(buffer_row);
16480 if let Crease::Inline { render_trailer, .. } = self
16481 .crease_snapshot
16482 .query_row(buffer_row, &self.buffer_snapshot)?
16483 {
16484 let render_trailer = render_trailer.as_ref()?;
16485 Some(render_trailer(buffer_row, folded, window, cx))
16486 } else {
16487 None
16488 }
16489 }
16490}
16491
16492impl Deref for EditorSnapshot {
16493 type Target = DisplaySnapshot;
16494
16495 fn deref(&self) -> &Self::Target {
16496 &self.display_snapshot
16497 }
16498}
16499
16500#[derive(Clone, Debug, PartialEq, Eq)]
16501pub enum EditorEvent {
16502 InputIgnored {
16503 text: Arc<str>,
16504 },
16505 InputHandled {
16506 utf16_range_to_replace: Option<Range<isize>>,
16507 text: Arc<str>,
16508 },
16509 ExcerptsAdded {
16510 buffer: Entity<Buffer>,
16511 predecessor: ExcerptId,
16512 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
16513 },
16514 ExcerptsRemoved {
16515 ids: Vec<ExcerptId>,
16516 },
16517 BufferFoldToggled {
16518 ids: Vec<ExcerptId>,
16519 folded: bool,
16520 },
16521 ExcerptsEdited {
16522 ids: Vec<ExcerptId>,
16523 },
16524 ExcerptsExpanded {
16525 ids: Vec<ExcerptId>,
16526 },
16527 BufferEdited,
16528 Edited {
16529 transaction_id: clock::Lamport,
16530 },
16531 Reparsed(BufferId),
16532 Focused,
16533 FocusedIn,
16534 Blurred,
16535 DirtyChanged,
16536 Saved,
16537 TitleChanged,
16538 DiffBaseChanged,
16539 SelectionsChanged {
16540 local: bool,
16541 },
16542 ScrollPositionChanged {
16543 local: bool,
16544 autoscroll: bool,
16545 },
16546 Closed,
16547 TransactionUndone {
16548 transaction_id: clock::Lamport,
16549 },
16550 TransactionBegun {
16551 transaction_id: clock::Lamport,
16552 },
16553 Reloaded,
16554 CursorShapeChanged,
16555}
16556
16557impl EventEmitter<EditorEvent> for Editor {}
16558
16559impl Focusable for Editor {
16560 fn focus_handle(&self, _cx: &App) -> FocusHandle {
16561 self.focus_handle.clone()
16562 }
16563}
16564
16565impl Render for Editor {
16566 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
16567 let settings = ThemeSettings::get_global(cx);
16568
16569 let mut text_style = match self.mode {
16570 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
16571 color: cx.theme().colors().editor_foreground,
16572 font_family: settings.ui_font.family.clone(),
16573 font_features: settings.ui_font.features.clone(),
16574 font_fallbacks: settings.ui_font.fallbacks.clone(),
16575 font_size: rems(0.875).into(),
16576 font_weight: settings.ui_font.weight,
16577 line_height: relative(settings.buffer_line_height.value()),
16578 ..Default::default()
16579 },
16580 EditorMode::Full => TextStyle {
16581 color: cx.theme().colors().editor_foreground,
16582 font_family: settings.buffer_font.family.clone(),
16583 font_features: settings.buffer_font.features.clone(),
16584 font_fallbacks: settings.buffer_font.fallbacks.clone(),
16585 font_size: settings.buffer_font_size(cx).into(),
16586 font_weight: settings.buffer_font.weight,
16587 line_height: relative(settings.buffer_line_height.value()),
16588 ..Default::default()
16589 },
16590 };
16591 if let Some(text_style_refinement) = &self.text_style_refinement {
16592 text_style.refine(text_style_refinement)
16593 }
16594
16595 let background = match self.mode {
16596 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
16597 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
16598 EditorMode::Full => cx.theme().colors().editor_background,
16599 };
16600
16601 EditorElement::new(
16602 &cx.entity(),
16603 EditorStyle {
16604 background,
16605 local_player: cx.theme().players().local(),
16606 text: text_style,
16607 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
16608 syntax: cx.theme().syntax().clone(),
16609 status: cx.theme().status().clone(),
16610 inlay_hints_style: make_inlay_hints_style(cx),
16611 inline_completion_styles: make_suggestion_styles(cx),
16612 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
16613 },
16614 )
16615 }
16616}
16617
16618impl EntityInputHandler for Editor {
16619 fn text_for_range(
16620 &mut self,
16621 range_utf16: Range<usize>,
16622 adjusted_range: &mut Option<Range<usize>>,
16623 _: &mut Window,
16624 cx: &mut Context<Self>,
16625 ) -> Option<String> {
16626 let snapshot = self.buffer.read(cx).read(cx);
16627 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
16628 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
16629 if (start.0..end.0) != range_utf16 {
16630 adjusted_range.replace(start.0..end.0);
16631 }
16632 Some(snapshot.text_for_range(start..end).collect())
16633 }
16634
16635 fn selected_text_range(
16636 &mut self,
16637 ignore_disabled_input: bool,
16638 _: &mut Window,
16639 cx: &mut Context<Self>,
16640 ) -> Option<UTF16Selection> {
16641 // Prevent the IME menu from appearing when holding down an alphabetic key
16642 // while input is disabled.
16643 if !ignore_disabled_input && !self.input_enabled {
16644 return None;
16645 }
16646
16647 let selection = self.selections.newest::<OffsetUtf16>(cx);
16648 let range = selection.range();
16649
16650 Some(UTF16Selection {
16651 range: range.start.0..range.end.0,
16652 reversed: selection.reversed,
16653 })
16654 }
16655
16656 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
16657 let snapshot = self.buffer.read(cx).read(cx);
16658 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
16659 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
16660 }
16661
16662 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16663 self.clear_highlights::<InputComposition>(cx);
16664 self.ime_transaction.take();
16665 }
16666
16667 fn replace_text_in_range(
16668 &mut self,
16669 range_utf16: Option<Range<usize>>,
16670 text: &str,
16671 window: &mut Window,
16672 cx: &mut Context<Self>,
16673 ) {
16674 if !self.input_enabled {
16675 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16676 return;
16677 }
16678
16679 self.transact(window, cx, |this, window, cx| {
16680 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
16681 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16682 Some(this.selection_replacement_ranges(range_utf16, cx))
16683 } else {
16684 this.marked_text_ranges(cx)
16685 };
16686
16687 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
16688 let newest_selection_id = this.selections.newest_anchor().id;
16689 this.selections
16690 .all::<OffsetUtf16>(cx)
16691 .iter()
16692 .zip(ranges_to_replace.iter())
16693 .find_map(|(selection, range)| {
16694 if selection.id == newest_selection_id {
16695 Some(
16696 (range.start.0 as isize - selection.head().0 as isize)
16697 ..(range.end.0 as isize - selection.head().0 as isize),
16698 )
16699 } else {
16700 None
16701 }
16702 })
16703 });
16704
16705 cx.emit(EditorEvent::InputHandled {
16706 utf16_range_to_replace: range_to_replace,
16707 text: text.into(),
16708 });
16709
16710 if let Some(new_selected_ranges) = new_selected_ranges {
16711 this.change_selections(None, window, cx, |selections| {
16712 selections.select_ranges(new_selected_ranges)
16713 });
16714 this.backspace(&Default::default(), window, cx);
16715 }
16716
16717 this.handle_input(text, window, cx);
16718 });
16719
16720 if let Some(transaction) = self.ime_transaction {
16721 self.buffer.update(cx, |buffer, cx| {
16722 buffer.group_until_transaction(transaction, cx);
16723 });
16724 }
16725
16726 self.unmark_text(window, cx);
16727 }
16728
16729 fn replace_and_mark_text_in_range(
16730 &mut self,
16731 range_utf16: Option<Range<usize>>,
16732 text: &str,
16733 new_selected_range_utf16: Option<Range<usize>>,
16734 window: &mut Window,
16735 cx: &mut Context<Self>,
16736 ) {
16737 if !self.input_enabled {
16738 return;
16739 }
16740
16741 let transaction = self.transact(window, cx, |this, window, cx| {
16742 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
16743 let snapshot = this.buffer.read(cx).read(cx);
16744 if let Some(relative_range_utf16) = range_utf16.as_ref() {
16745 for marked_range in &mut marked_ranges {
16746 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
16747 marked_range.start.0 += relative_range_utf16.start;
16748 marked_range.start =
16749 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
16750 marked_range.end =
16751 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16752 }
16753 }
16754 Some(marked_ranges)
16755 } else if let Some(range_utf16) = range_utf16 {
16756 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16757 Some(this.selection_replacement_ranges(range_utf16, cx))
16758 } else {
16759 None
16760 };
16761
16762 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16763 let newest_selection_id = this.selections.newest_anchor().id;
16764 this.selections
16765 .all::<OffsetUtf16>(cx)
16766 .iter()
16767 .zip(ranges_to_replace.iter())
16768 .find_map(|(selection, range)| {
16769 if selection.id == newest_selection_id {
16770 Some(
16771 (range.start.0 as isize - selection.head().0 as isize)
16772 ..(range.end.0 as isize - selection.head().0 as isize),
16773 )
16774 } else {
16775 None
16776 }
16777 })
16778 });
16779
16780 cx.emit(EditorEvent::InputHandled {
16781 utf16_range_to_replace: range_to_replace,
16782 text: text.into(),
16783 });
16784
16785 if let Some(ranges) = ranges_to_replace {
16786 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16787 }
16788
16789 let marked_ranges = {
16790 let snapshot = this.buffer.read(cx).read(cx);
16791 this.selections
16792 .disjoint_anchors()
16793 .iter()
16794 .map(|selection| {
16795 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16796 })
16797 .collect::<Vec<_>>()
16798 };
16799
16800 if text.is_empty() {
16801 this.unmark_text(window, cx);
16802 } else {
16803 this.highlight_text::<InputComposition>(
16804 marked_ranges.clone(),
16805 HighlightStyle {
16806 underline: Some(UnderlineStyle {
16807 thickness: px(1.),
16808 color: None,
16809 wavy: false,
16810 }),
16811 ..Default::default()
16812 },
16813 cx,
16814 );
16815 }
16816
16817 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16818 let use_autoclose = this.use_autoclose;
16819 let use_auto_surround = this.use_auto_surround;
16820 this.set_use_autoclose(false);
16821 this.set_use_auto_surround(false);
16822 this.handle_input(text, window, cx);
16823 this.set_use_autoclose(use_autoclose);
16824 this.set_use_auto_surround(use_auto_surround);
16825
16826 if let Some(new_selected_range) = new_selected_range_utf16 {
16827 let snapshot = this.buffer.read(cx).read(cx);
16828 let new_selected_ranges = marked_ranges
16829 .into_iter()
16830 .map(|marked_range| {
16831 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16832 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16833 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16834 snapshot.clip_offset_utf16(new_start, Bias::Left)
16835 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16836 })
16837 .collect::<Vec<_>>();
16838
16839 drop(snapshot);
16840 this.change_selections(None, window, cx, |selections| {
16841 selections.select_ranges(new_selected_ranges)
16842 });
16843 }
16844 });
16845
16846 self.ime_transaction = self.ime_transaction.or(transaction);
16847 if let Some(transaction) = self.ime_transaction {
16848 self.buffer.update(cx, |buffer, cx| {
16849 buffer.group_until_transaction(transaction, cx);
16850 });
16851 }
16852
16853 if self.text_highlights::<InputComposition>(cx).is_none() {
16854 self.ime_transaction.take();
16855 }
16856 }
16857
16858 fn bounds_for_range(
16859 &mut self,
16860 range_utf16: Range<usize>,
16861 element_bounds: gpui::Bounds<Pixels>,
16862 window: &mut Window,
16863 cx: &mut Context<Self>,
16864 ) -> Option<gpui::Bounds<Pixels>> {
16865 let text_layout_details = self.text_layout_details(window);
16866 let gpui::Size {
16867 width: em_width,
16868 height: line_height,
16869 } = self.character_size(window);
16870
16871 let snapshot = self.snapshot(window, cx);
16872 let scroll_position = snapshot.scroll_position();
16873 let scroll_left = scroll_position.x * em_width;
16874
16875 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16876 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16877 + self.gutter_dimensions.width
16878 + self.gutter_dimensions.margin;
16879 let y = line_height * (start.row().as_f32() - scroll_position.y);
16880
16881 Some(Bounds {
16882 origin: element_bounds.origin + point(x, y),
16883 size: size(em_width, line_height),
16884 })
16885 }
16886
16887 fn character_index_for_point(
16888 &mut self,
16889 point: gpui::Point<Pixels>,
16890 _window: &mut Window,
16891 _cx: &mut Context<Self>,
16892 ) -> Option<usize> {
16893 let position_map = self.last_position_map.as_ref()?;
16894 if !position_map.text_hitbox.contains(&point) {
16895 return None;
16896 }
16897 let display_point = position_map.point_for_position(point).previous_valid;
16898 let anchor = position_map
16899 .snapshot
16900 .display_point_to_anchor(display_point, Bias::Left);
16901 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16902 Some(utf16_offset.0)
16903 }
16904}
16905
16906trait SelectionExt {
16907 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16908 fn spanned_rows(
16909 &self,
16910 include_end_if_at_line_start: bool,
16911 map: &DisplaySnapshot,
16912 ) -> Range<MultiBufferRow>;
16913}
16914
16915impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16916 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16917 let start = self
16918 .start
16919 .to_point(&map.buffer_snapshot)
16920 .to_display_point(map);
16921 let end = self
16922 .end
16923 .to_point(&map.buffer_snapshot)
16924 .to_display_point(map);
16925 if self.reversed {
16926 end..start
16927 } else {
16928 start..end
16929 }
16930 }
16931
16932 fn spanned_rows(
16933 &self,
16934 include_end_if_at_line_start: bool,
16935 map: &DisplaySnapshot,
16936 ) -> Range<MultiBufferRow> {
16937 let start = self.start.to_point(&map.buffer_snapshot);
16938 let mut end = self.end.to_point(&map.buffer_snapshot);
16939 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16940 end.row -= 1;
16941 }
16942
16943 let buffer_start = map.prev_line_boundary(start).0;
16944 let buffer_end = map.next_line_boundary(end).0;
16945 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
16946 }
16947}
16948
16949impl<T: InvalidationRegion> InvalidationStack<T> {
16950 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
16951 where
16952 S: Clone + ToOffset,
16953 {
16954 while let Some(region) = self.last() {
16955 let all_selections_inside_invalidation_ranges =
16956 if selections.len() == region.ranges().len() {
16957 selections
16958 .iter()
16959 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
16960 .all(|(selection, invalidation_range)| {
16961 let head = selection.head().to_offset(buffer);
16962 invalidation_range.start <= head && invalidation_range.end >= head
16963 })
16964 } else {
16965 false
16966 };
16967
16968 if all_selections_inside_invalidation_ranges {
16969 break;
16970 } else {
16971 self.pop();
16972 }
16973 }
16974 }
16975}
16976
16977impl<T> Default for InvalidationStack<T> {
16978 fn default() -> Self {
16979 Self(Default::default())
16980 }
16981}
16982
16983impl<T> Deref for InvalidationStack<T> {
16984 type Target = Vec<T>;
16985
16986 fn deref(&self) -> &Self::Target {
16987 &self.0
16988 }
16989}
16990
16991impl<T> DerefMut for InvalidationStack<T> {
16992 fn deref_mut(&mut self) -> &mut Self::Target {
16993 &mut self.0
16994 }
16995}
16996
16997impl InvalidationRegion for SnippetState {
16998 fn ranges(&self) -> &[Range<Anchor>] {
16999 &self.ranges[self.active_index]
17000 }
17001}
17002
17003pub fn diagnostic_block_renderer(
17004 diagnostic: Diagnostic,
17005 max_message_rows: Option<u8>,
17006 allow_closing: bool,
17007 _is_valid: bool,
17008) -> RenderBlock {
17009 let (text_without_backticks, code_ranges) =
17010 highlight_diagnostic_message(&diagnostic, max_message_rows);
17011
17012 Arc::new(move |cx: &mut BlockContext| {
17013 let group_id: SharedString = cx.block_id.to_string().into();
17014
17015 let mut text_style = cx.window.text_style().clone();
17016 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
17017 let theme_settings = ThemeSettings::get_global(cx);
17018 text_style.font_family = theme_settings.buffer_font.family.clone();
17019 text_style.font_style = theme_settings.buffer_font.style;
17020 text_style.font_features = theme_settings.buffer_font.features.clone();
17021 text_style.font_weight = theme_settings.buffer_font.weight;
17022
17023 let multi_line_diagnostic = diagnostic.message.contains('\n');
17024
17025 let buttons = |diagnostic: &Diagnostic| {
17026 if multi_line_diagnostic {
17027 v_flex()
17028 } else {
17029 h_flex()
17030 }
17031 .when(allow_closing, |div| {
17032 div.children(diagnostic.is_primary.then(|| {
17033 IconButton::new("close-block", IconName::XCircle)
17034 .icon_color(Color::Muted)
17035 .size(ButtonSize::Compact)
17036 .style(ButtonStyle::Transparent)
17037 .visible_on_hover(group_id.clone())
17038 .on_click(move |_click, window, cx| {
17039 window.dispatch_action(Box::new(Cancel), cx)
17040 })
17041 .tooltip(|window, cx| {
17042 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
17043 })
17044 }))
17045 })
17046 .child(
17047 IconButton::new("copy-block", IconName::Copy)
17048 .icon_color(Color::Muted)
17049 .size(ButtonSize::Compact)
17050 .style(ButtonStyle::Transparent)
17051 .visible_on_hover(group_id.clone())
17052 .on_click({
17053 let message = diagnostic.message.clone();
17054 move |_click, _, cx| {
17055 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
17056 }
17057 })
17058 .tooltip(Tooltip::text("Copy diagnostic message")),
17059 )
17060 };
17061
17062 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
17063 AvailableSpace::min_size(),
17064 cx.window,
17065 cx.app,
17066 );
17067
17068 h_flex()
17069 .id(cx.block_id)
17070 .group(group_id.clone())
17071 .relative()
17072 .size_full()
17073 .block_mouse_down()
17074 .pl(cx.gutter_dimensions.width)
17075 .w(cx.max_width - cx.gutter_dimensions.full_width())
17076 .child(
17077 div()
17078 .flex()
17079 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
17080 .flex_shrink(),
17081 )
17082 .child(buttons(&diagnostic))
17083 .child(div().flex().flex_shrink_0().child(
17084 StyledText::new(text_without_backticks.clone()).with_highlights(
17085 &text_style,
17086 code_ranges.iter().map(|range| {
17087 (
17088 range.clone(),
17089 HighlightStyle {
17090 font_weight: Some(FontWeight::BOLD),
17091 ..Default::default()
17092 },
17093 )
17094 }),
17095 ),
17096 ))
17097 .into_any_element()
17098 })
17099}
17100
17101fn inline_completion_edit_text(
17102 current_snapshot: &BufferSnapshot,
17103 edits: &[(Range<Anchor>, String)],
17104 edit_preview: &EditPreview,
17105 include_deletions: bool,
17106 cx: &App,
17107) -> HighlightedText {
17108 let edits = edits
17109 .iter()
17110 .map(|(anchor, text)| {
17111 (
17112 anchor.start.text_anchor..anchor.end.text_anchor,
17113 text.clone(),
17114 )
17115 })
17116 .collect::<Vec<_>>();
17117
17118 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
17119}
17120
17121pub fn highlight_diagnostic_message(
17122 diagnostic: &Diagnostic,
17123 mut max_message_rows: Option<u8>,
17124) -> (SharedString, Vec<Range<usize>>) {
17125 let mut text_without_backticks = String::new();
17126 let mut code_ranges = Vec::new();
17127
17128 if let Some(source) = &diagnostic.source {
17129 text_without_backticks.push_str(source);
17130 code_ranges.push(0..source.len());
17131 text_without_backticks.push_str(": ");
17132 }
17133
17134 let mut prev_offset = 0;
17135 let mut in_code_block = false;
17136 let has_row_limit = max_message_rows.is_some();
17137 let mut newline_indices = diagnostic
17138 .message
17139 .match_indices('\n')
17140 .filter(|_| has_row_limit)
17141 .map(|(ix, _)| ix)
17142 .fuse()
17143 .peekable();
17144
17145 for (quote_ix, _) in diagnostic
17146 .message
17147 .match_indices('`')
17148 .chain([(diagnostic.message.len(), "")])
17149 {
17150 let mut first_newline_ix = None;
17151 let mut last_newline_ix = None;
17152 while let Some(newline_ix) = newline_indices.peek() {
17153 if *newline_ix < quote_ix {
17154 if first_newline_ix.is_none() {
17155 first_newline_ix = Some(*newline_ix);
17156 }
17157 last_newline_ix = Some(*newline_ix);
17158
17159 if let Some(rows_left) = &mut max_message_rows {
17160 if *rows_left == 0 {
17161 break;
17162 } else {
17163 *rows_left -= 1;
17164 }
17165 }
17166 let _ = newline_indices.next();
17167 } else {
17168 break;
17169 }
17170 }
17171 let prev_len = text_without_backticks.len();
17172 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
17173 text_without_backticks.push_str(new_text);
17174 if in_code_block {
17175 code_ranges.push(prev_len..text_without_backticks.len());
17176 }
17177 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
17178 in_code_block = !in_code_block;
17179 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
17180 text_without_backticks.push_str("...");
17181 break;
17182 }
17183 }
17184
17185 (text_without_backticks.into(), code_ranges)
17186}
17187
17188fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
17189 match severity {
17190 DiagnosticSeverity::ERROR => colors.error,
17191 DiagnosticSeverity::WARNING => colors.warning,
17192 DiagnosticSeverity::INFORMATION => colors.info,
17193 DiagnosticSeverity::HINT => colors.info,
17194 _ => colors.ignored,
17195 }
17196}
17197
17198pub fn styled_runs_for_code_label<'a>(
17199 label: &'a CodeLabel,
17200 syntax_theme: &'a theme::SyntaxTheme,
17201) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
17202 let fade_out = HighlightStyle {
17203 fade_out: Some(0.35),
17204 ..Default::default()
17205 };
17206
17207 let mut prev_end = label.filter_range.end;
17208 label
17209 .runs
17210 .iter()
17211 .enumerate()
17212 .flat_map(move |(ix, (range, highlight_id))| {
17213 let style = if let Some(style) = highlight_id.style(syntax_theme) {
17214 style
17215 } else {
17216 return Default::default();
17217 };
17218 let mut muted_style = style;
17219 muted_style.highlight(fade_out);
17220
17221 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
17222 if range.start >= label.filter_range.end {
17223 if range.start > prev_end {
17224 runs.push((prev_end..range.start, fade_out));
17225 }
17226 runs.push((range.clone(), muted_style));
17227 } else if range.end <= label.filter_range.end {
17228 runs.push((range.clone(), style));
17229 } else {
17230 runs.push((range.start..label.filter_range.end, style));
17231 runs.push((label.filter_range.end..range.end, muted_style));
17232 }
17233 prev_end = cmp::max(prev_end, range.end);
17234
17235 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
17236 runs.push((prev_end..label.text.len(), fade_out));
17237 }
17238
17239 runs
17240 })
17241}
17242
17243pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
17244 let mut prev_index = 0;
17245 let mut prev_codepoint: Option<char> = None;
17246 text.char_indices()
17247 .chain([(text.len(), '\0')])
17248 .filter_map(move |(index, codepoint)| {
17249 let prev_codepoint = prev_codepoint.replace(codepoint)?;
17250 let is_boundary = index == text.len()
17251 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
17252 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
17253 if is_boundary {
17254 let chunk = &text[prev_index..index];
17255 prev_index = index;
17256 Some(chunk)
17257 } else {
17258 None
17259 }
17260 })
17261}
17262
17263pub trait RangeToAnchorExt: Sized {
17264 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
17265
17266 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
17267 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
17268 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
17269 }
17270}
17271
17272impl<T: ToOffset> RangeToAnchorExt for Range<T> {
17273 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
17274 let start_offset = self.start.to_offset(snapshot);
17275 let end_offset = self.end.to_offset(snapshot);
17276 if start_offset == end_offset {
17277 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
17278 } else {
17279 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
17280 }
17281 }
17282}
17283
17284pub trait RowExt {
17285 fn as_f32(&self) -> f32;
17286
17287 fn next_row(&self) -> Self;
17288
17289 fn previous_row(&self) -> Self;
17290
17291 fn minus(&self, other: Self) -> u32;
17292}
17293
17294impl RowExt for DisplayRow {
17295 fn as_f32(&self) -> f32 {
17296 self.0 as f32
17297 }
17298
17299 fn next_row(&self) -> Self {
17300 Self(self.0 + 1)
17301 }
17302
17303 fn previous_row(&self) -> Self {
17304 Self(self.0.saturating_sub(1))
17305 }
17306
17307 fn minus(&self, other: Self) -> u32 {
17308 self.0 - other.0
17309 }
17310}
17311
17312impl RowExt for MultiBufferRow {
17313 fn as_f32(&self) -> f32 {
17314 self.0 as f32
17315 }
17316
17317 fn next_row(&self) -> Self {
17318 Self(self.0 + 1)
17319 }
17320
17321 fn previous_row(&self) -> Self {
17322 Self(self.0.saturating_sub(1))
17323 }
17324
17325 fn minus(&self, other: Self) -> u32 {
17326 self.0 - other.0
17327 }
17328}
17329
17330trait RowRangeExt {
17331 type Row;
17332
17333 fn len(&self) -> usize;
17334
17335 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
17336}
17337
17338impl RowRangeExt for Range<MultiBufferRow> {
17339 type Row = MultiBufferRow;
17340
17341 fn len(&self) -> usize {
17342 (self.end.0 - self.start.0) as usize
17343 }
17344
17345 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
17346 (self.start.0..self.end.0).map(MultiBufferRow)
17347 }
17348}
17349
17350impl RowRangeExt for Range<DisplayRow> {
17351 type Row = DisplayRow;
17352
17353 fn len(&self) -> usize {
17354 (self.end.0 - self.start.0) as usize
17355 }
17356
17357 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
17358 (self.start.0..self.end.0).map(DisplayRow)
17359 }
17360}
17361
17362/// If select range has more than one line, we
17363/// just point the cursor to range.start.
17364fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
17365 if range.start.row == range.end.row {
17366 range
17367 } else {
17368 range.start..range.start
17369 }
17370}
17371pub struct KillRing(ClipboardItem);
17372impl Global for KillRing {}
17373
17374const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
17375
17376fn all_edits_insertions_or_deletions(
17377 edits: &Vec<(Range<Anchor>, String)>,
17378 snapshot: &MultiBufferSnapshot,
17379) -> bool {
17380 let mut all_insertions = true;
17381 let mut all_deletions = true;
17382
17383 for (range, new_text) in edits.iter() {
17384 let range_is_empty = range.to_offset(&snapshot).is_empty();
17385 let text_is_empty = new_text.is_empty();
17386
17387 if range_is_empty != text_is_empty {
17388 if range_is_empty {
17389 all_deletions = false;
17390 } else {
17391 all_insertions = false;
17392 }
17393 } else {
17394 return false;
17395 }
17396
17397 if !all_insertions && !all_deletions {
17398 return false;
17399 }
17400 }
17401 all_insertions || all_deletions
17402}