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}
733
734#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
735enum NextScrollCursorCenterTopBottom {
736 #[default]
737 Center,
738 Top,
739 Bottom,
740}
741
742impl NextScrollCursorCenterTopBottom {
743 fn next(&self) -> Self {
744 match self {
745 Self::Center => Self::Top,
746 Self::Top => Self::Bottom,
747 Self::Bottom => Self::Center,
748 }
749 }
750}
751
752#[derive(Clone)]
753pub struct EditorSnapshot {
754 pub mode: EditorMode,
755 show_gutter: bool,
756 show_line_numbers: Option<bool>,
757 show_git_diff_gutter: Option<bool>,
758 show_code_actions: Option<bool>,
759 show_runnables: Option<bool>,
760 git_blame_gutter_max_author_length: Option<usize>,
761 pub display_snapshot: DisplaySnapshot,
762 pub placeholder_text: Option<Arc<str>>,
763 is_focused: bool,
764 scroll_anchor: ScrollAnchor,
765 ongoing_scroll: OngoingScroll,
766 current_line_highlight: CurrentLineHighlight,
767 gutter_hovered: bool,
768}
769
770const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
771
772#[derive(Default, Debug, Clone, Copy)]
773pub struct GutterDimensions {
774 pub left_padding: Pixels,
775 pub right_padding: Pixels,
776 pub width: Pixels,
777 pub margin: Pixels,
778 pub git_blame_entries_width: Option<Pixels>,
779}
780
781impl GutterDimensions {
782 /// The full width of the space taken up by the gutter.
783 pub fn full_width(&self) -> Pixels {
784 self.margin + self.width
785 }
786
787 /// The width of the space reserved for the fold indicators,
788 /// use alongside 'justify_end' and `gutter_width` to
789 /// right align content with the line numbers
790 pub fn fold_area_width(&self) -> Pixels {
791 self.margin + self.right_padding
792 }
793}
794
795#[derive(Debug)]
796pub struct RemoteSelection {
797 pub replica_id: ReplicaId,
798 pub selection: Selection<Anchor>,
799 pub cursor_shape: CursorShape,
800 pub peer_id: PeerId,
801 pub line_mode: bool,
802 pub participant_index: Option<ParticipantIndex>,
803 pub user_name: Option<SharedString>,
804}
805
806#[derive(Clone, Debug)]
807struct SelectionHistoryEntry {
808 selections: Arc<[Selection<Anchor>]>,
809 select_next_state: Option<SelectNextState>,
810 select_prev_state: Option<SelectNextState>,
811 add_selections_state: Option<AddSelectionsState>,
812}
813
814enum SelectionHistoryMode {
815 Normal,
816 Undoing,
817 Redoing,
818}
819
820#[derive(Clone, PartialEq, Eq, Hash)]
821struct HoveredCursor {
822 replica_id: u16,
823 selection_id: usize,
824}
825
826impl Default for SelectionHistoryMode {
827 fn default() -> Self {
828 Self::Normal
829 }
830}
831
832#[derive(Default)]
833struct SelectionHistory {
834 #[allow(clippy::type_complexity)]
835 selections_by_transaction:
836 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
837 mode: SelectionHistoryMode,
838 undo_stack: VecDeque<SelectionHistoryEntry>,
839 redo_stack: VecDeque<SelectionHistoryEntry>,
840}
841
842impl SelectionHistory {
843 fn insert_transaction(
844 &mut self,
845 transaction_id: TransactionId,
846 selections: Arc<[Selection<Anchor>]>,
847 ) {
848 self.selections_by_transaction
849 .insert(transaction_id, (selections, None));
850 }
851
852 #[allow(clippy::type_complexity)]
853 fn transaction(
854 &self,
855 transaction_id: TransactionId,
856 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
857 self.selections_by_transaction.get(&transaction_id)
858 }
859
860 #[allow(clippy::type_complexity)]
861 fn transaction_mut(
862 &mut self,
863 transaction_id: TransactionId,
864 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
865 self.selections_by_transaction.get_mut(&transaction_id)
866 }
867
868 fn push(&mut self, entry: SelectionHistoryEntry) {
869 if !entry.selections.is_empty() {
870 match self.mode {
871 SelectionHistoryMode::Normal => {
872 self.push_undo(entry);
873 self.redo_stack.clear();
874 }
875 SelectionHistoryMode::Undoing => self.push_redo(entry),
876 SelectionHistoryMode::Redoing => self.push_undo(entry),
877 }
878 }
879 }
880
881 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
882 if self
883 .undo_stack
884 .back()
885 .map_or(true, |e| e.selections != entry.selections)
886 {
887 self.undo_stack.push_back(entry);
888 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
889 self.undo_stack.pop_front();
890 }
891 }
892 }
893
894 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
895 if self
896 .redo_stack
897 .back()
898 .map_or(true, |e| e.selections != entry.selections)
899 {
900 self.redo_stack.push_back(entry);
901 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
902 self.redo_stack.pop_front();
903 }
904 }
905 }
906}
907
908struct RowHighlight {
909 index: usize,
910 range: Range<Anchor>,
911 color: Hsla,
912 should_autoscroll: bool,
913}
914
915#[derive(Clone, Debug)]
916struct AddSelectionsState {
917 above: bool,
918 stack: Vec<usize>,
919}
920
921#[derive(Clone)]
922struct SelectNextState {
923 query: AhoCorasick,
924 wordwise: bool,
925 done: bool,
926}
927
928impl std::fmt::Debug for SelectNextState {
929 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
930 f.debug_struct(std::any::type_name::<Self>())
931 .field("wordwise", &self.wordwise)
932 .field("done", &self.done)
933 .finish()
934 }
935}
936
937#[derive(Debug)]
938struct AutocloseRegion {
939 selection_id: usize,
940 range: Range<Anchor>,
941 pair: BracketPair,
942}
943
944#[derive(Debug)]
945struct SnippetState {
946 ranges: Vec<Vec<Range<Anchor>>>,
947 active_index: usize,
948 choices: Vec<Option<Vec<String>>>,
949}
950
951#[doc(hidden)]
952pub struct RenameState {
953 pub range: Range<Anchor>,
954 pub old_name: Arc<str>,
955 pub editor: Entity<Editor>,
956 block_id: CustomBlockId,
957}
958
959struct InvalidationStack<T>(Vec<T>);
960
961struct RegisteredInlineCompletionProvider {
962 provider: Arc<dyn InlineCompletionProviderHandle>,
963 _subscription: Subscription,
964}
965
966#[derive(Debug)]
967struct ActiveDiagnosticGroup {
968 primary_range: Range<Anchor>,
969 primary_message: String,
970 group_id: usize,
971 blocks: HashMap<CustomBlockId, Diagnostic>,
972 is_valid: bool,
973}
974
975#[derive(Serialize, Deserialize, Clone, Debug)]
976pub struct ClipboardSelection {
977 /// The number of bytes in this selection.
978 pub len: usize,
979 /// Whether this was a full-line selection.
980 pub is_entire_line: bool,
981 /// The column where this selection originally started.
982 pub start_column: u32,
983}
984
985#[derive(Debug)]
986pub(crate) struct NavigationData {
987 cursor_anchor: Anchor,
988 cursor_position: Point,
989 scroll_anchor: ScrollAnchor,
990 scroll_top_row: u32,
991}
992
993#[derive(Debug, Clone, Copy, PartialEq, Eq)]
994pub enum GotoDefinitionKind {
995 Symbol,
996 Declaration,
997 Type,
998 Implementation,
999}
1000
1001#[derive(Debug, Clone)]
1002enum InlayHintRefreshReason {
1003 Toggle(bool),
1004 SettingsChange(InlayHintSettings),
1005 NewLinesShown,
1006 BufferEdited(HashSet<Arc<Language>>),
1007 RefreshRequested,
1008 ExcerptsRemoved(Vec<ExcerptId>),
1009}
1010
1011impl InlayHintRefreshReason {
1012 fn description(&self) -> &'static str {
1013 match self {
1014 Self::Toggle(_) => "toggle",
1015 Self::SettingsChange(_) => "settings change",
1016 Self::NewLinesShown => "new lines shown",
1017 Self::BufferEdited(_) => "buffer edited",
1018 Self::RefreshRequested => "refresh requested",
1019 Self::ExcerptsRemoved(_) => "excerpts removed",
1020 }
1021 }
1022}
1023
1024pub enum FormatTarget {
1025 Buffers,
1026 Ranges(Vec<Range<MultiBufferPoint>>),
1027}
1028
1029pub(crate) struct FocusedBlock {
1030 id: BlockId,
1031 focus_handle: WeakFocusHandle,
1032}
1033
1034#[derive(Clone)]
1035enum JumpData {
1036 MultiBufferRow {
1037 row: MultiBufferRow,
1038 line_offset_from_top: u32,
1039 },
1040 MultiBufferPoint {
1041 excerpt_id: ExcerptId,
1042 position: Point,
1043 anchor: text::Anchor,
1044 line_offset_from_top: u32,
1045 },
1046}
1047
1048pub enum MultibufferSelectionMode {
1049 First,
1050 All,
1051}
1052
1053impl Editor {
1054 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1055 let buffer = cx.new(|cx| Buffer::local("", cx));
1056 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1057 Self::new(
1058 EditorMode::SingleLine { auto_width: false },
1059 buffer,
1060 None,
1061 false,
1062 window,
1063 cx,
1064 )
1065 }
1066
1067 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1068 let buffer = cx.new(|cx| Buffer::local("", cx));
1069 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1070 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1071 }
1072
1073 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1074 let buffer = cx.new(|cx| Buffer::local("", cx));
1075 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1076 Self::new(
1077 EditorMode::SingleLine { auto_width: true },
1078 buffer,
1079 None,
1080 false,
1081 window,
1082 cx,
1083 )
1084 }
1085
1086 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1087 let buffer = cx.new(|cx| Buffer::local("", cx));
1088 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1089 Self::new(
1090 EditorMode::AutoHeight { max_lines },
1091 buffer,
1092 None,
1093 false,
1094 window,
1095 cx,
1096 )
1097 }
1098
1099 pub fn for_buffer(
1100 buffer: Entity<Buffer>,
1101 project: Option<Entity<Project>>,
1102 window: &mut Window,
1103 cx: &mut Context<Self>,
1104 ) -> Self {
1105 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1106 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1107 }
1108
1109 pub fn for_multibuffer(
1110 buffer: Entity<MultiBuffer>,
1111 project: Option<Entity<Project>>,
1112 show_excerpt_controls: bool,
1113 window: &mut Window,
1114 cx: &mut Context<Self>,
1115 ) -> Self {
1116 Self::new(
1117 EditorMode::Full,
1118 buffer,
1119 project,
1120 show_excerpt_controls,
1121 window,
1122 cx,
1123 )
1124 }
1125
1126 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1127 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1128 let mut clone = Self::new(
1129 self.mode,
1130 self.buffer.clone(),
1131 self.project.clone(),
1132 show_excerpt_controls,
1133 window,
1134 cx,
1135 );
1136 self.display_map.update(cx, |display_map, cx| {
1137 let snapshot = display_map.snapshot(cx);
1138 clone.display_map.update(cx, |display_map, cx| {
1139 display_map.set_state(&snapshot, cx);
1140 });
1141 });
1142 clone.selections.clone_state(&self.selections);
1143 clone.scroll_manager.clone_state(&self.scroll_manager);
1144 clone.searchable = self.searchable;
1145 clone
1146 }
1147
1148 pub fn new(
1149 mode: EditorMode,
1150 buffer: Entity<MultiBuffer>,
1151 project: Option<Entity<Project>>,
1152 show_excerpt_controls: bool,
1153 window: &mut Window,
1154 cx: &mut Context<Self>,
1155 ) -> Self {
1156 let style = window.text_style();
1157 let font_size = style.font_size.to_pixels(window.rem_size());
1158 let editor = cx.entity().downgrade();
1159 let fold_placeholder = FoldPlaceholder {
1160 constrain_width: true,
1161 render: Arc::new(move |fold_id, fold_range, cx| {
1162 let editor = editor.clone();
1163 div()
1164 .id(fold_id)
1165 .bg(cx.theme().colors().ghost_element_background)
1166 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1167 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1168 .rounded_sm()
1169 .size_full()
1170 .cursor_pointer()
1171 .child("⋯")
1172 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1173 .on_click(move |_, _window, cx| {
1174 editor
1175 .update(cx, |editor, cx| {
1176 editor.unfold_ranges(
1177 &[fold_range.start..fold_range.end],
1178 true,
1179 false,
1180 cx,
1181 );
1182 cx.stop_propagation();
1183 })
1184 .ok();
1185 })
1186 .into_any()
1187 }),
1188 merge_adjacent: true,
1189 ..Default::default()
1190 };
1191 let display_map = cx.new(|cx| {
1192 DisplayMap::new(
1193 buffer.clone(),
1194 style.font(),
1195 font_size,
1196 None,
1197 show_excerpt_controls,
1198 FILE_HEADER_HEIGHT,
1199 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1200 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1201 fold_placeholder,
1202 cx,
1203 )
1204 });
1205
1206 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1207
1208 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1209
1210 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1211 .then(|| language_settings::SoftWrap::None);
1212
1213 let mut project_subscriptions = Vec::new();
1214 if mode == EditorMode::Full {
1215 if let Some(project) = project.as_ref() {
1216 if buffer.read(cx).is_singleton() {
1217 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1218 cx.emit(EditorEvent::TitleChanged);
1219 }));
1220 }
1221 project_subscriptions.push(cx.subscribe_in(
1222 project,
1223 window,
1224 |editor, _, event, window, cx| {
1225 if let project::Event::RefreshInlayHints = event {
1226 editor
1227 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1228 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1229 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1230 let focus_handle = editor.focus_handle(cx);
1231 if focus_handle.is_focused(window) {
1232 let snapshot = buffer.read(cx).snapshot();
1233 for (range, snippet) in snippet_edits {
1234 let editor_range =
1235 language::range_from_lsp(*range).to_offset(&snapshot);
1236 editor
1237 .insert_snippet(
1238 &[editor_range],
1239 snippet.clone(),
1240 window,
1241 cx,
1242 )
1243 .ok();
1244 }
1245 }
1246 }
1247 }
1248 },
1249 ));
1250 if let Some(task_inventory) = project
1251 .read(cx)
1252 .task_store()
1253 .read(cx)
1254 .task_inventory()
1255 .cloned()
1256 {
1257 project_subscriptions.push(cx.observe_in(
1258 &task_inventory,
1259 window,
1260 |editor, _, window, cx| {
1261 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1262 },
1263 ));
1264 }
1265 }
1266 }
1267
1268 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1269
1270 let inlay_hint_settings =
1271 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1272 let focus_handle = cx.focus_handle();
1273 cx.on_focus(&focus_handle, window, Self::handle_focus)
1274 .detach();
1275 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1276 .detach();
1277 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1278 .detach();
1279 cx.on_blur(&focus_handle, window, Self::handle_blur)
1280 .detach();
1281
1282 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1283 Some(false)
1284 } else {
1285 None
1286 };
1287
1288 let mut code_action_providers = Vec::new();
1289 let mut load_uncommitted_diff = None;
1290 if let Some(project) = project.clone() {
1291 load_uncommitted_diff = Some(
1292 get_uncommitted_diff_for_buffer(
1293 &project,
1294 buffer.read(cx).all_buffers(),
1295 buffer.clone(),
1296 cx,
1297 )
1298 .shared(),
1299 );
1300 code_action_providers.push(Rc::new(project) as Rc<_>);
1301 }
1302
1303 let mut this = Self {
1304 focus_handle,
1305 show_cursor_when_unfocused: false,
1306 last_focused_descendant: None,
1307 buffer: buffer.clone(),
1308 display_map: display_map.clone(),
1309 selections,
1310 scroll_manager: ScrollManager::new(cx),
1311 columnar_selection_tail: None,
1312 add_selections_state: None,
1313 select_next_state: None,
1314 select_prev_state: None,
1315 selection_history: Default::default(),
1316 autoclose_regions: Default::default(),
1317 snippet_stack: Default::default(),
1318 select_larger_syntax_node_stack: Vec::new(),
1319 ime_transaction: Default::default(),
1320 active_diagnostics: None,
1321 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1322 inline_diagnostics_update: Task::ready(()),
1323 inline_diagnostics: Vec::new(),
1324 soft_wrap_mode_override,
1325 completion_provider: project.clone().map(|project| Box::new(project) as _),
1326 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1327 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1328 project,
1329 blink_manager: blink_manager.clone(),
1330 show_local_selections: true,
1331 show_scrollbars: true,
1332 mode,
1333 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1334 show_gutter: mode == EditorMode::Full,
1335 show_line_numbers: None,
1336 use_relative_line_numbers: None,
1337 show_git_diff_gutter: None,
1338 show_code_actions: None,
1339 show_runnables: None,
1340 show_wrap_guides: None,
1341 show_indent_guides,
1342 placeholder_text: None,
1343 highlight_order: 0,
1344 highlighted_rows: HashMap::default(),
1345 background_highlights: Default::default(),
1346 gutter_highlights: TreeMap::default(),
1347 scrollbar_marker_state: ScrollbarMarkerState::default(),
1348 active_indent_guides_state: ActiveIndentGuidesState::default(),
1349 nav_history: None,
1350 context_menu: RefCell::new(None),
1351 mouse_context_menu: None,
1352 completion_tasks: Default::default(),
1353 signature_help_state: SignatureHelpState::default(),
1354 auto_signature_help: None,
1355 find_all_references_task_sources: Vec::new(),
1356 next_completion_id: 0,
1357 next_inlay_id: 0,
1358 code_action_providers,
1359 available_code_actions: Default::default(),
1360 code_actions_task: Default::default(),
1361 selection_highlight_task: Default::default(),
1362 document_highlights_task: Default::default(),
1363 linked_editing_range_task: Default::default(),
1364 pending_rename: Default::default(),
1365 searchable: true,
1366 cursor_shape: EditorSettings::get_global(cx)
1367 .cursor_shape
1368 .unwrap_or_default(),
1369 current_line_highlight: None,
1370 autoindent_mode: Some(AutoindentMode::EachLine),
1371 collapse_matches: false,
1372 workspace: None,
1373 input_enabled: true,
1374 use_modal_editing: mode == EditorMode::Full,
1375 read_only: false,
1376 use_autoclose: true,
1377 use_auto_surround: true,
1378 auto_replace_emoji_shortcode: false,
1379 leader_peer_id: None,
1380 remote_id: None,
1381 hover_state: Default::default(),
1382 pending_mouse_down: None,
1383 hovered_link_state: Default::default(),
1384 edit_prediction_provider: None,
1385 active_inline_completion: None,
1386 stale_inline_completion_in_menu: None,
1387 edit_prediction_preview: EditPredictionPreview::Inactive,
1388 inline_diagnostics_enabled: mode == EditorMode::Full,
1389 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1390
1391 gutter_hovered: false,
1392 pixel_position_of_newest_cursor: None,
1393 last_bounds: None,
1394 last_position_map: None,
1395 expect_bounds_change: None,
1396 gutter_dimensions: GutterDimensions::default(),
1397 style: None,
1398 show_cursor_names: false,
1399 hovered_cursors: Default::default(),
1400 next_editor_action_id: EditorActionId::default(),
1401 editor_actions: Rc::default(),
1402 inline_completions_hidden_for_vim_mode: false,
1403 show_inline_completions_override: None,
1404 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1405 edit_prediction_settings: EditPredictionSettings::Disabled,
1406 edit_prediction_cursor_on_leading_whitespace: false,
1407 edit_prediction_requires_modifier_in_leading_space: true,
1408 custom_context_menu: None,
1409 show_git_blame_gutter: false,
1410 show_git_blame_inline: false,
1411 distinguish_unstaged_diff_hunks: false,
1412 show_selection_menu: None,
1413 show_git_blame_inline_delay_task: None,
1414 git_blame_inline_tooltip: None,
1415 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1416 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1417 .session
1418 .restore_unsaved_buffers,
1419 blame: None,
1420 blame_subscription: None,
1421 tasks: Default::default(),
1422 _subscriptions: vec![
1423 cx.observe(&buffer, Self::on_buffer_changed),
1424 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1425 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1426 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1427 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1428 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1429 cx.observe_window_activation(window, |editor, window, cx| {
1430 let active = window.is_window_active();
1431 editor.blink_manager.update(cx, |blink_manager, cx| {
1432 if active {
1433 blink_manager.enable(cx);
1434 } else {
1435 blink_manager.disable(cx);
1436 }
1437 });
1438 }),
1439 ],
1440 tasks_update_task: None,
1441 linked_edit_ranges: Default::default(),
1442 in_project_search: false,
1443 previous_search_ranges: None,
1444 breadcrumb_header: None,
1445 focused_block: None,
1446 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1447 addons: HashMap::default(),
1448 registered_buffers: HashMap::default(),
1449 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1450 selection_mark_mode: false,
1451 toggle_fold_multiple_buffers: Task::ready(()),
1452 serialize_selections: Task::ready(()),
1453 text_style_refinement: None,
1454 load_diff_task: load_uncommitted_diff,
1455 };
1456 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1457 this._subscriptions.extend(project_subscriptions);
1458
1459 this.end_selection(window, cx);
1460 this.scroll_manager.show_scrollbar(window, cx);
1461
1462 if mode == EditorMode::Full {
1463 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1464 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1465
1466 if this.git_blame_inline_enabled {
1467 this.git_blame_inline_enabled = true;
1468 this.start_git_blame_inline(false, window, cx);
1469 }
1470
1471 if let Some(buffer) = buffer.read(cx).as_singleton() {
1472 if let Some(project) = this.project.as_ref() {
1473 let handle = project.update(cx, |project, cx| {
1474 project.register_buffer_with_language_servers(&buffer, cx)
1475 });
1476 this.registered_buffers
1477 .insert(buffer.read(cx).remote_id(), handle);
1478 }
1479 }
1480 }
1481
1482 this.report_editor_event("Editor Opened", None, cx);
1483 this
1484 }
1485
1486 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1487 self.mouse_context_menu
1488 .as_ref()
1489 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1490 }
1491
1492 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1493 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1494 }
1495
1496 fn key_context_internal(
1497 &self,
1498 has_active_edit_prediction: bool,
1499 window: &Window,
1500 cx: &App,
1501 ) -> KeyContext {
1502 let mut key_context = KeyContext::new_with_defaults();
1503 key_context.add("Editor");
1504 let mode = match self.mode {
1505 EditorMode::SingleLine { .. } => "single_line",
1506 EditorMode::AutoHeight { .. } => "auto_height",
1507 EditorMode::Full => "full",
1508 };
1509
1510 if EditorSettings::jupyter_enabled(cx) {
1511 key_context.add("jupyter");
1512 }
1513
1514 key_context.set("mode", mode);
1515 if self.pending_rename.is_some() {
1516 key_context.add("renaming");
1517 }
1518
1519 match self.context_menu.borrow().as_ref() {
1520 Some(CodeContextMenu::Completions(_)) => {
1521 key_context.add("menu");
1522 key_context.add("showing_completions");
1523 }
1524 Some(CodeContextMenu::CodeActions(_)) => {
1525 key_context.add("menu");
1526 key_context.add("showing_code_actions")
1527 }
1528 None => {}
1529 }
1530
1531 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1532 if !self.focus_handle(cx).contains_focused(window, cx)
1533 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1534 {
1535 for addon in self.addons.values() {
1536 addon.extend_key_context(&mut key_context, cx)
1537 }
1538 }
1539
1540 if let Some(extension) = self
1541 .buffer
1542 .read(cx)
1543 .as_singleton()
1544 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1545 {
1546 key_context.set("extension", extension.to_string());
1547 }
1548
1549 if has_active_edit_prediction {
1550 if self.edit_prediction_in_conflict() {
1551 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1552 } else {
1553 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1554 key_context.add("copilot_suggestion");
1555 }
1556 }
1557
1558 if self.selection_mark_mode {
1559 key_context.add("selection_mode");
1560 }
1561
1562 key_context
1563 }
1564
1565 pub fn edit_prediction_in_conflict(&self) -> bool {
1566 if !self.show_edit_predictions_in_menu() {
1567 return false;
1568 }
1569
1570 let showing_completions = self
1571 .context_menu
1572 .borrow()
1573 .as_ref()
1574 .map_or(false, |context| {
1575 matches!(context, CodeContextMenu::Completions(_))
1576 });
1577
1578 showing_completions
1579 || self.edit_prediction_requires_modifier()
1580 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1581 // bindings to insert tab characters.
1582 || (self.edit_prediction_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
1583 }
1584
1585 pub fn accept_edit_prediction_keybind(
1586 &self,
1587 window: &Window,
1588 cx: &App,
1589 ) -> AcceptEditPredictionBinding {
1590 let key_context = self.key_context_internal(true, window, cx);
1591 let in_conflict = self.edit_prediction_in_conflict();
1592
1593 AcceptEditPredictionBinding(
1594 window
1595 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1596 .into_iter()
1597 .filter(|binding| {
1598 !in_conflict
1599 || binding
1600 .keystrokes()
1601 .first()
1602 .map_or(false, |keystroke| keystroke.modifiers.modified())
1603 })
1604 .rev()
1605 .min_by_key(|binding| {
1606 binding
1607 .keystrokes()
1608 .first()
1609 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1610 }),
1611 )
1612 }
1613
1614 pub fn new_file(
1615 workspace: &mut Workspace,
1616 _: &workspace::NewFile,
1617 window: &mut Window,
1618 cx: &mut Context<Workspace>,
1619 ) {
1620 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1621 "Failed to create buffer",
1622 window,
1623 cx,
1624 |e, _, _| match e.error_code() {
1625 ErrorCode::RemoteUpgradeRequired => Some(format!(
1626 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1627 e.error_tag("required").unwrap_or("the latest version")
1628 )),
1629 _ => None,
1630 },
1631 );
1632 }
1633
1634 pub fn new_in_workspace(
1635 workspace: &mut Workspace,
1636 window: &mut Window,
1637 cx: &mut Context<Workspace>,
1638 ) -> Task<Result<Entity<Editor>>> {
1639 let project = workspace.project().clone();
1640 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1641
1642 cx.spawn_in(window, |workspace, mut cx| async move {
1643 let buffer = create.await?;
1644 workspace.update_in(&mut cx, |workspace, window, cx| {
1645 let editor =
1646 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1647 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1648 editor
1649 })
1650 })
1651 }
1652
1653 fn new_file_vertical(
1654 workspace: &mut Workspace,
1655 _: &workspace::NewFileSplitVertical,
1656 window: &mut Window,
1657 cx: &mut Context<Workspace>,
1658 ) {
1659 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1660 }
1661
1662 fn new_file_horizontal(
1663 workspace: &mut Workspace,
1664 _: &workspace::NewFileSplitHorizontal,
1665 window: &mut Window,
1666 cx: &mut Context<Workspace>,
1667 ) {
1668 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1669 }
1670
1671 fn new_file_in_direction(
1672 workspace: &mut Workspace,
1673 direction: SplitDirection,
1674 window: &mut Window,
1675 cx: &mut Context<Workspace>,
1676 ) {
1677 let project = workspace.project().clone();
1678 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1679
1680 cx.spawn_in(window, |workspace, mut cx| async move {
1681 let buffer = create.await?;
1682 workspace.update_in(&mut cx, move |workspace, window, cx| {
1683 workspace.split_item(
1684 direction,
1685 Box::new(
1686 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1687 ),
1688 window,
1689 cx,
1690 )
1691 })?;
1692 anyhow::Ok(())
1693 })
1694 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1695 match e.error_code() {
1696 ErrorCode::RemoteUpgradeRequired => Some(format!(
1697 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1698 e.error_tag("required").unwrap_or("the latest version")
1699 )),
1700 _ => None,
1701 }
1702 });
1703 }
1704
1705 pub fn leader_peer_id(&self) -> Option<PeerId> {
1706 self.leader_peer_id
1707 }
1708
1709 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1710 &self.buffer
1711 }
1712
1713 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1714 self.workspace.as_ref()?.0.upgrade()
1715 }
1716
1717 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1718 self.buffer().read(cx).title(cx)
1719 }
1720
1721 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1722 let git_blame_gutter_max_author_length = self
1723 .render_git_blame_gutter(cx)
1724 .then(|| {
1725 if let Some(blame) = self.blame.as_ref() {
1726 let max_author_length =
1727 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1728 Some(max_author_length)
1729 } else {
1730 None
1731 }
1732 })
1733 .flatten();
1734
1735 EditorSnapshot {
1736 mode: self.mode,
1737 show_gutter: self.show_gutter,
1738 show_line_numbers: self.show_line_numbers,
1739 show_git_diff_gutter: self.show_git_diff_gutter,
1740 show_code_actions: self.show_code_actions,
1741 show_runnables: self.show_runnables,
1742 git_blame_gutter_max_author_length,
1743 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1744 scroll_anchor: self.scroll_manager.anchor(),
1745 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1746 placeholder_text: self.placeholder_text.clone(),
1747 is_focused: self.focus_handle.is_focused(window),
1748 current_line_highlight: self
1749 .current_line_highlight
1750 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1751 gutter_hovered: self.gutter_hovered,
1752 }
1753 }
1754
1755 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1756 self.buffer.read(cx).language_at(point, cx)
1757 }
1758
1759 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1760 self.buffer.read(cx).read(cx).file_at(point).cloned()
1761 }
1762
1763 pub fn active_excerpt(
1764 &self,
1765 cx: &App,
1766 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1767 self.buffer
1768 .read(cx)
1769 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1770 }
1771
1772 pub fn mode(&self) -> EditorMode {
1773 self.mode
1774 }
1775
1776 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1777 self.collaboration_hub.as_deref()
1778 }
1779
1780 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1781 self.collaboration_hub = Some(hub);
1782 }
1783
1784 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1785 self.in_project_search = in_project_search;
1786 }
1787
1788 pub fn set_custom_context_menu(
1789 &mut self,
1790 f: impl 'static
1791 + Fn(
1792 &mut Self,
1793 DisplayPoint,
1794 &mut Window,
1795 &mut Context<Self>,
1796 ) -> Option<Entity<ui::ContextMenu>>,
1797 ) {
1798 self.custom_context_menu = Some(Box::new(f))
1799 }
1800
1801 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1802 self.completion_provider = provider;
1803 }
1804
1805 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1806 self.semantics_provider.clone()
1807 }
1808
1809 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1810 self.semantics_provider = provider;
1811 }
1812
1813 pub fn set_edit_prediction_provider<T>(
1814 &mut self,
1815 provider: Option<Entity<T>>,
1816 window: &mut Window,
1817 cx: &mut Context<Self>,
1818 ) where
1819 T: EditPredictionProvider,
1820 {
1821 self.edit_prediction_provider =
1822 provider.map(|provider| RegisteredInlineCompletionProvider {
1823 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1824 if this.focus_handle.is_focused(window) {
1825 this.update_visible_inline_completion(window, cx);
1826 }
1827 }),
1828 provider: Arc::new(provider),
1829 });
1830 self.refresh_inline_completion(false, false, window, cx);
1831 }
1832
1833 pub fn placeholder_text(&self) -> Option<&str> {
1834 self.placeholder_text.as_deref()
1835 }
1836
1837 pub fn set_placeholder_text(
1838 &mut self,
1839 placeholder_text: impl Into<Arc<str>>,
1840 cx: &mut Context<Self>,
1841 ) {
1842 let placeholder_text = Some(placeholder_text.into());
1843 if self.placeholder_text != placeholder_text {
1844 self.placeholder_text = placeholder_text;
1845 cx.notify();
1846 }
1847 }
1848
1849 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1850 self.cursor_shape = cursor_shape;
1851
1852 // Disrupt blink for immediate user feedback that the cursor shape has changed
1853 self.blink_manager.update(cx, BlinkManager::show_cursor);
1854
1855 cx.notify();
1856 }
1857
1858 pub fn set_current_line_highlight(
1859 &mut self,
1860 current_line_highlight: Option<CurrentLineHighlight>,
1861 ) {
1862 self.current_line_highlight = current_line_highlight;
1863 }
1864
1865 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1866 self.collapse_matches = collapse_matches;
1867 }
1868
1869 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1870 let buffers = self.buffer.read(cx).all_buffers();
1871 let Some(project) = self.project.as_ref() else {
1872 return;
1873 };
1874 project.update(cx, |project, cx| {
1875 for buffer in buffers {
1876 self.registered_buffers
1877 .entry(buffer.read(cx).remote_id())
1878 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1879 }
1880 })
1881 }
1882
1883 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1884 if self.collapse_matches {
1885 return range.start..range.start;
1886 }
1887 range.clone()
1888 }
1889
1890 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1891 if self.display_map.read(cx).clip_at_line_ends != clip {
1892 self.display_map
1893 .update(cx, |map, _| map.clip_at_line_ends = clip);
1894 }
1895 }
1896
1897 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1898 self.input_enabled = input_enabled;
1899 }
1900
1901 pub fn set_inline_completions_hidden_for_vim_mode(
1902 &mut self,
1903 hidden: bool,
1904 window: &mut Window,
1905 cx: &mut Context<Self>,
1906 ) {
1907 if hidden != self.inline_completions_hidden_for_vim_mode {
1908 self.inline_completions_hidden_for_vim_mode = hidden;
1909 if hidden {
1910 self.update_visible_inline_completion(window, cx);
1911 } else {
1912 self.refresh_inline_completion(true, false, window, cx);
1913 }
1914 }
1915 }
1916
1917 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1918 self.menu_inline_completions_policy = value;
1919 }
1920
1921 pub fn set_autoindent(&mut self, autoindent: bool) {
1922 if autoindent {
1923 self.autoindent_mode = Some(AutoindentMode::EachLine);
1924 } else {
1925 self.autoindent_mode = None;
1926 }
1927 }
1928
1929 pub fn read_only(&self, cx: &App) -> bool {
1930 self.read_only || self.buffer.read(cx).read_only()
1931 }
1932
1933 pub fn set_read_only(&mut self, read_only: bool) {
1934 self.read_only = read_only;
1935 }
1936
1937 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1938 self.use_autoclose = autoclose;
1939 }
1940
1941 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1942 self.use_auto_surround = auto_surround;
1943 }
1944
1945 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1946 self.auto_replace_emoji_shortcode = auto_replace;
1947 }
1948
1949 pub fn toggle_inline_completions(
1950 &mut self,
1951 _: &ToggleEditPrediction,
1952 window: &mut Window,
1953 cx: &mut Context<Self>,
1954 ) {
1955 if self.show_inline_completions_override.is_some() {
1956 self.set_show_edit_predictions(None, window, cx);
1957 } else {
1958 let show_edit_predictions = !self.edit_predictions_enabled();
1959 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1960 }
1961 }
1962
1963 pub fn set_show_edit_predictions(
1964 &mut self,
1965 show_edit_predictions: Option<bool>,
1966 window: &mut Window,
1967 cx: &mut Context<Self>,
1968 ) {
1969 self.show_inline_completions_override = show_edit_predictions;
1970
1971 if let Some(false) = show_edit_predictions {
1972 self.discard_inline_completion(false, cx);
1973 } else {
1974 self.refresh_inline_completion(false, true, window, cx);
1975 }
1976 }
1977
1978 fn inline_completions_disabled_in_scope(
1979 &self,
1980 buffer: &Entity<Buffer>,
1981 buffer_position: language::Anchor,
1982 cx: &App,
1983 ) -> bool {
1984 let snapshot = buffer.read(cx).snapshot();
1985 let settings = snapshot.settings_at(buffer_position, cx);
1986
1987 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1988 return false;
1989 };
1990
1991 scope.override_name().map_or(false, |scope_name| {
1992 settings
1993 .edit_predictions_disabled_in
1994 .iter()
1995 .any(|s| s == scope_name)
1996 })
1997 }
1998
1999 pub fn set_use_modal_editing(&mut self, to: bool) {
2000 self.use_modal_editing = to;
2001 }
2002
2003 pub fn use_modal_editing(&self) -> bool {
2004 self.use_modal_editing
2005 }
2006
2007 fn selections_did_change(
2008 &mut self,
2009 local: bool,
2010 old_cursor_position: &Anchor,
2011 show_completions: bool,
2012 window: &mut Window,
2013 cx: &mut Context<Self>,
2014 ) {
2015 window.invalidate_character_coordinates();
2016
2017 // Copy selections to primary selection buffer
2018 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2019 if local {
2020 let selections = self.selections.all::<usize>(cx);
2021 let buffer_handle = self.buffer.read(cx).read(cx);
2022
2023 let mut text = String::new();
2024 for (index, selection) in selections.iter().enumerate() {
2025 let text_for_selection = buffer_handle
2026 .text_for_range(selection.start..selection.end)
2027 .collect::<String>();
2028
2029 text.push_str(&text_for_selection);
2030 if index != selections.len() - 1 {
2031 text.push('\n');
2032 }
2033 }
2034
2035 if !text.is_empty() {
2036 cx.write_to_primary(ClipboardItem::new_string(text));
2037 }
2038 }
2039
2040 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2041 self.buffer.update(cx, |buffer, cx| {
2042 buffer.set_active_selections(
2043 &self.selections.disjoint_anchors(),
2044 self.selections.line_mode,
2045 self.cursor_shape,
2046 cx,
2047 )
2048 });
2049 }
2050 let display_map = self
2051 .display_map
2052 .update(cx, |display_map, cx| display_map.snapshot(cx));
2053 let buffer = &display_map.buffer_snapshot;
2054 self.add_selections_state = None;
2055 self.select_next_state = None;
2056 self.select_prev_state = None;
2057 self.select_larger_syntax_node_stack.clear();
2058 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2059 self.snippet_stack
2060 .invalidate(&self.selections.disjoint_anchors(), buffer);
2061 self.take_rename(false, window, cx);
2062
2063 let new_cursor_position = self.selections.newest_anchor().head();
2064
2065 self.push_to_nav_history(
2066 *old_cursor_position,
2067 Some(new_cursor_position.to_point(buffer)),
2068 cx,
2069 );
2070
2071 if local {
2072 let new_cursor_position = self.selections.newest_anchor().head();
2073 let mut context_menu = self.context_menu.borrow_mut();
2074 let completion_menu = match context_menu.as_ref() {
2075 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2076 _ => {
2077 *context_menu = None;
2078 None
2079 }
2080 };
2081 if let Some(buffer_id) = new_cursor_position.buffer_id {
2082 if !self.registered_buffers.contains_key(&buffer_id) {
2083 if let Some(project) = self.project.as_ref() {
2084 project.update(cx, |project, cx| {
2085 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2086 return;
2087 };
2088 self.registered_buffers.insert(
2089 buffer_id,
2090 project.register_buffer_with_language_servers(&buffer, cx),
2091 );
2092 })
2093 }
2094 }
2095 }
2096
2097 if let Some(completion_menu) = completion_menu {
2098 let cursor_position = new_cursor_position.to_offset(buffer);
2099 let (word_range, kind) =
2100 buffer.surrounding_word(completion_menu.initial_position, true);
2101 if kind == Some(CharKind::Word)
2102 && word_range.to_inclusive().contains(&cursor_position)
2103 {
2104 let mut completion_menu = completion_menu.clone();
2105 drop(context_menu);
2106
2107 let query = Self::completion_query(buffer, cursor_position);
2108 cx.spawn(move |this, mut cx| async move {
2109 completion_menu
2110 .filter(query.as_deref(), cx.background_executor().clone())
2111 .await;
2112
2113 this.update(&mut cx, |this, cx| {
2114 let mut context_menu = this.context_menu.borrow_mut();
2115 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2116 else {
2117 return;
2118 };
2119
2120 if menu.id > completion_menu.id {
2121 return;
2122 }
2123
2124 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2125 drop(context_menu);
2126 cx.notify();
2127 })
2128 })
2129 .detach();
2130
2131 if show_completions {
2132 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2133 }
2134 } else {
2135 drop(context_menu);
2136 self.hide_context_menu(window, cx);
2137 }
2138 } else {
2139 drop(context_menu);
2140 }
2141
2142 hide_hover(self, cx);
2143
2144 if old_cursor_position.to_display_point(&display_map).row()
2145 != new_cursor_position.to_display_point(&display_map).row()
2146 {
2147 self.available_code_actions.take();
2148 }
2149 self.refresh_code_actions(window, cx);
2150 self.refresh_document_highlights(cx);
2151 self.refresh_selected_text_highlights(window, cx);
2152 refresh_matching_bracket_highlights(self, window, cx);
2153 self.update_visible_inline_completion(window, cx);
2154 self.edit_prediction_requires_modifier_in_leading_space = true;
2155 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2156 if self.git_blame_inline_enabled {
2157 self.start_inline_blame_timer(window, cx);
2158 }
2159 }
2160
2161 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2162 cx.emit(EditorEvent::SelectionsChanged { local });
2163
2164 let selections = &self.selections.disjoint;
2165 if selections.len() == 1 {
2166 cx.emit(SearchEvent::ActiveMatchChanged)
2167 }
2168 if local
2169 && self.is_singleton(cx)
2170 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2171 {
2172 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2173 let background_executor = cx.background_executor().clone();
2174 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2175 let snapshot = self.buffer().read(cx).snapshot(cx);
2176 let selections = selections.clone();
2177 self.serialize_selections = cx.background_spawn(async move {
2178 background_executor.timer(Duration::from_millis(100)).await;
2179 let selections = selections
2180 .iter()
2181 .map(|selection| {
2182 (
2183 selection.start.to_offset(&snapshot),
2184 selection.end.to_offset(&snapshot),
2185 )
2186 })
2187 .collect();
2188 DB.save_editor_selections(editor_id, workspace_id, selections)
2189 .await
2190 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2191 .log_err();
2192 });
2193 }
2194 }
2195
2196 cx.notify();
2197 }
2198
2199 pub fn change_selections<R>(
2200 &mut self,
2201 autoscroll: Option<Autoscroll>,
2202 window: &mut Window,
2203 cx: &mut Context<Self>,
2204 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2205 ) -> R {
2206 self.change_selections_inner(autoscroll, true, window, cx, change)
2207 }
2208
2209 fn change_selections_inner<R>(
2210 &mut self,
2211 autoscroll: Option<Autoscroll>,
2212 request_completions: bool,
2213 window: &mut Window,
2214 cx: &mut Context<Self>,
2215 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2216 ) -> R {
2217 let old_cursor_position = self.selections.newest_anchor().head();
2218 self.push_to_selection_history();
2219
2220 let (changed, result) = self.selections.change_with(cx, change);
2221
2222 if changed {
2223 if let Some(autoscroll) = autoscroll {
2224 self.request_autoscroll(autoscroll, cx);
2225 }
2226 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2227
2228 if self.should_open_signature_help_automatically(
2229 &old_cursor_position,
2230 self.signature_help_state.backspace_pressed(),
2231 cx,
2232 ) {
2233 self.show_signature_help(&ShowSignatureHelp, window, cx);
2234 }
2235 self.signature_help_state.set_backspace_pressed(false);
2236 }
2237
2238 result
2239 }
2240
2241 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2242 where
2243 I: IntoIterator<Item = (Range<S>, T)>,
2244 S: ToOffset,
2245 T: Into<Arc<str>>,
2246 {
2247 if self.read_only(cx) {
2248 return;
2249 }
2250
2251 self.buffer
2252 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2253 }
2254
2255 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2256 where
2257 I: IntoIterator<Item = (Range<S>, T)>,
2258 S: ToOffset,
2259 T: Into<Arc<str>>,
2260 {
2261 if self.read_only(cx) {
2262 return;
2263 }
2264
2265 self.buffer.update(cx, |buffer, cx| {
2266 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2267 });
2268 }
2269
2270 pub fn edit_with_block_indent<I, S, T>(
2271 &mut self,
2272 edits: I,
2273 original_start_columns: Vec<u32>,
2274 cx: &mut Context<Self>,
2275 ) where
2276 I: IntoIterator<Item = (Range<S>, T)>,
2277 S: ToOffset,
2278 T: Into<Arc<str>>,
2279 {
2280 if self.read_only(cx) {
2281 return;
2282 }
2283
2284 self.buffer.update(cx, |buffer, cx| {
2285 buffer.edit(
2286 edits,
2287 Some(AutoindentMode::Block {
2288 original_start_columns,
2289 }),
2290 cx,
2291 )
2292 });
2293 }
2294
2295 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2296 self.hide_context_menu(window, cx);
2297
2298 match phase {
2299 SelectPhase::Begin {
2300 position,
2301 add,
2302 click_count,
2303 } => self.begin_selection(position, add, click_count, window, cx),
2304 SelectPhase::BeginColumnar {
2305 position,
2306 goal_column,
2307 reset,
2308 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2309 SelectPhase::Extend {
2310 position,
2311 click_count,
2312 } => self.extend_selection(position, click_count, window, cx),
2313 SelectPhase::Update {
2314 position,
2315 goal_column,
2316 scroll_delta,
2317 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2318 SelectPhase::End => self.end_selection(window, cx),
2319 }
2320 }
2321
2322 fn extend_selection(
2323 &mut self,
2324 position: DisplayPoint,
2325 click_count: usize,
2326 window: &mut Window,
2327 cx: &mut Context<Self>,
2328 ) {
2329 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2330 let tail = self.selections.newest::<usize>(cx).tail();
2331 self.begin_selection(position, false, click_count, window, cx);
2332
2333 let position = position.to_offset(&display_map, Bias::Left);
2334 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2335
2336 let mut pending_selection = self
2337 .selections
2338 .pending_anchor()
2339 .expect("extend_selection not called with pending selection");
2340 if position >= tail {
2341 pending_selection.start = tail_anchor;
2342 } else {
2343 pending_selection.end = tail_anchor;
2344 pending_selection.reversed = true;
2345 }
2346
2347 let mut pending_mode = self.selections.pending_mode().unwrap();
2348 match &mut pending_mode {
2349 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2350 _ => {}
2351 }
2352
2353 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2354 s.set_pending(pending_selection, pending_mode)
2355 });
2356 }
2357
2358 fn begin_selection(
2359 &mut self,
2360 position: DisplayPoint,
2361 add: bool,
2362 click_count: usize,
2363 window: &mut Window,
2364 cx: &mut Context<Self>,
2365 ) {
2366 if !self.focus_handle.is_focused(window) {
2367 self.last_focused_descendant = None;
2368 window.focus(&self.focus_handle);
2369 }
2370
2371 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2372 let buffer = &display_map.buffer_snapshot;
2373 let newest_selection = self.selections.newest_anchor().clone();
2374 let position = display_map.clip_point(position, Bias::Left);
2375
2376 let start;
2377 let end;
2378 let mode;
2379 let mut auto_scroll;
2380 match click_count {
2381 1 => {
2382 start = buffer.anchor_before(position.to_point(&display_map));
2383 end = start;
2384 mode = SelectMode::Character;
2385 auto_scroll = true;
2386 }
2387 2 => {
2388 let range = movement::surrounding_word(&display_map, position);
2389 start = buffer.anchor_before(range.start.to_point(&display_map));
2390 end = buffer.anchor_before(range.end.to_point(&display_map));
2391 mode = SelectMode::Word(start..end);
2392 auto_scroll = true;
2393 }
2394 3 => {
2395 let position = display_map
2396 .clip_point(position, Bias::Left)
2397 .to_point(&display_map);
2398 let line_start = display_map.prev_line_boundary(position).0;
2399 let next_line_start = buffer.clip_point(
2400 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2401 Bias::Left,
2402 );
2403 start = buffer.anchor_before(line_start);
2404 end = buffer.anchor_before(next_line_start);
2405 mode = SelectMode::Line(start..end);
2406 auto_scroll = true;
2407 }
2408 _ => {
2409 start = buffer.anchor_before(0);
2410 end = buffer.anchor_before(buffer.len());
2411 mode = SelectMode::All;
2412 auto_scroll = false;
2413 }
2414 }
2415 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2416
2417 let point_to_delete: Option<usize> = {
2418 let selected_points: Vec<Selection<Point>> =
2419 self.selections.disjoint_in_range(start..end, cx);
2420
2421 if !add || click_count > 1 {
2422 None
2423 } else if !selected_points.is_empty() {
2424 Some(selected_points[0].id)
2425 } else {
2426 let clicked_point_already_selected =
2427 self.selections.disjoint.iter().find(|selection| {
2428 selection.start.to_point(buffer) == start.to_point(buffer)
2429 || selection.end.to_point(buffer) == end.to_point(buffer)
2430 });
2431
2432 clicked_point_already_selected.map(|selection| selection.id)
2433 }
2434 };
2435
2436 let selections_count = self.selections.count();
2437
2438 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2439 if let Some(point_to_delete) = point_to_delete {
2440 s.delete(point_to_delete);
2441
2442 if selections_count == 1 {
2443 s.set_pending_anchor_range(start..end, mode);
2444 }
2445 } else {
2446 if !add {
2447 s.clear_disjoint();
2448 } else if click_count > 1 {
2449 s.delete(newest_selection.id)
2450 }
2451
2452 s.set_pending_anchor_range(start..end, mode);
2453 }
2454 });
2455 }
2456
2457 fn begin_columnar_selection(
2458 &mut self,
2459 position: DisplayPoint,
2460 goal_column: u32,
2461 reset: bool,
2462 window: &mut Window,
2463 cx: &mut Context<Self>,
2464 ) {
2465 if !self.focus_handle.is_focused(window) {
2466 self.last_focused_descendant = None;
2467 window.focus(&self.focus_handle);
2468 }
2469
2470 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2471
2472 if reset {
2473 let pointer_position = display_map
2474 .buffer_snapshot
2475 .anchor_before(position.to_point(&display_map));
2476
2477 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2478 s.clear_disjoint();
2479 s.set_pending_anchor_range(
2480 pointer_position..pointer_position,
2481 SelectMode::Character,
2482 );
2483 });
2484 }
2485
2486 let tail = self.selections.newest::<Point>(cx).tail();
2487 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2488
2489 if !reset {
2490 self.select_columns(
2491 tail.to_display_point(&display_map),
2492 position,
2493 goal_column,
2494 &display_map,
2495 window,
2496 cx,
2497 );
2498 }
2499 }
2500
2501 fn update_selection(
2502 &mut self,
2503 position: DisplayPoint,
2504 goal_column: u32,
2505 scroll_delta: gpui::Point<f32>,
2506 window: &mut Window,
2507 cx: &mut Context<Self>,
2508 ) {
2509 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2510
2511 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2512 let tail = tail.to_display_point(&display_map);
2513 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2514 } else if let Some(mut pending) = self.selections.pending_anchor() {
2515 let buffer = self.buffer.read(cx).snapshot(cx);
2516 let head;
2517 let tail;
2518 let mode = self.selections.pending_mode().unwrap();
2519 match &mode {
2520 SelectMode::Character => {
2521 head = position.to_point(&display_map);
2522 tail = pending.tail().to_point(&buffer);
2523 }
2524 SelectMode::Word(original_range) => {
2525 let original_display_range = original_range.start.to_display_point(&display_map)
2526 ..original_range.end.to_display_point(&display_map);
2527 let original_buffer_range = original_display_range.start.to_point(&display_map)
2528 ..original_display_range.end.to_point(&display_map);
2529 if movement::is_inside_word(&display_map, position)
2530 || original_display_range.contains(&position)
2531 {
2532 let word_range = movement::surrounding_word(&display_map, position);
2533 if word_range.start < original_display_range.start {
2534 head = word_range.start.to_point(&display_map);
2535 } else {
2536 head = word_range.end.to_point(&display_map);
2537 }
2538 } else {
2539 head = position.to_point(&display_map);
2540 }
2541
2542 if head <= original_buffer_range.start {
2543 tail = original_buffer_range.end;
2544 } else {
2545 tail = original_buffer_range.start;
2546 }
2547 }
2548 SelectMode::Line(original_range) => {
2549 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2550
2551 let position = display_map
2552 .clip_point(position, Bias::Left)
2553 .to_point(&display_map);
2554 let line_start = display_map.prev_line_boundary(position).0;
2555 let next_line_start = buffer.clip_point(
2556 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2557 Bias::Left,
2558 );
2559
2560 if line_start < original_range.start {
2561 head = line_start
2562 } else {
2563 head = next_line_start
2564 }
2565
2566 if head <= original_range.start {
2567 tail = original_range.end;
2568 } else {
2569 tail = original_range.start;
2570 }
2571 }
2572 SelectMode::All => {
2573 return;
2574 }
2575 };
2576
2577 if head < tail {
2578 pending.start = buffer.anchor_before(head);
2579 pending.end = buffer.anchor_before(tail);
2580 pending.reversed = true;
2581 } else {
2582 pending.start = buffer.anchor_before(tail);
2583 pending.end = buffer.anchor_before(head);
2584 pending.reversed = false;
2585 }
2586
2587 self.change_selections(None, window, cx, |s| {
2588 s.set_pending(pending, mode);
2589 });
2590 } else {
2591 log::error!("update_selection dispatched with no pending selection");
2592 return;
2593 }
2594
2595 self.apply_scroll_delta(scroll_delta, window, cx);
2596 cx.notify();
2597 }
2598
2599 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2600 self.columnar_selection_tail.take();
2601 if self.selections.pending_anchor().is_some() {
2602 let selections = self.selections.all::<usize>(cx);
2603 self.change_selections(None, window, cx, |s| {
2604 s.select(selections);
2605 s.clear_pending();
2606 });
2607 }
2608 }
2609
2610 fn select_columns(
2611 &mut self,
2612 tail: DisplayPoint,
2613 head: DisplayPoint,
2614 goal_column: u32,
2615 display_map: &DisplaySnapshot,
2616 window: &mut Window,
2617 cx: &mut Context<Self>,
2618 ) {
2619 let start_row = cmp::min(tail.row(), head.row());
2620 let end_row = cmp::max(tail.row(), head.row());
2621 let start_column = cmp::min(tail.column(), goal_column);
2622 let end_column = cmp::max(tail.column(), goal_column);
2623 let reversed = start_column < tail.column();
2624
2625 let selection_ranges = (start_row.0..=end_row.0)
2626 .map(DisplayRow)
2627 .filter_map(|row| {
2628 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2629 let start = display_map
2630 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2631 .to_point(display_map);
2632 let end = display_map
2633 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2634 .to_point(display_map);
2635 if reversed {
2636 Some(end..start)
2637 } else {
2638 Some(start..end)
2639 }
2640 } else {
2641 None
2642 }
2643 })
2644 .collect::<Vec<_>>();
2645
2646 self.change_selections(None, window, cx, |s| {
2647 s.select_ranges(selection_ranges);
2648 });
2649 cx.notify();
2650 }
2651
2652 pub fn has_pending_nonempty_selection(&self) -> bool {
2653 let pending_nonempty_selection = match self.selections.pending_anchor() {
2654 Some(Selection { start, end, .. }) => start != end,
2655 None => false,
2656 };
2657
2658 pending_nonempty_selection
2659 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2660 }
2661
2662 pub fn has_pending_selection(&self) -> bool {
2663 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2664 }
2665
2666 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2667 self.selection_mark_mode = false;
2668
2669 if self.clear_expanded_diff_hunks(cx) {
2670 cx.notify();
2671 return;
2672 }
2673 if self.dismiss_menus_and_popups(true, window, cx) {
2674 return;
2675 }
2676
2677 if self.mode == EditorMode::Full
2678 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2679 {
2680 return;
2681 }
2682
2683 cx.propagate();
2684 }
2685
2686 pub fn dismiss_menus_and_popups(
2687 &mut self,
2688 is_user_requested: bool,
2689 window: &mut Window,
2690 cx: &mut Context<Self>,
2691 ) -> bool {
2692 if self.take_rename(false, window, cx).is_some() {
2693 return true;
2694 }
2695
2696 if hide_hover(self, cx) {
2697 return true;
2698 }
2699
2700 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2701 return true;
2702 }
2703
2704 if self.hide_context_menu(window, cx).is_some() {
2705 return true;
2706 }
2707
2708 if self.mouse_context_menu.take().is_some() {
2709 return true;
2710 }
2711
2712 if is_user_requested && self.discard_inline_completion(true, cx) {
2713 return true;
2714 }
2715
2716 if self.snippet_stack.pop().is_some() {
2717 return true;
2718 }
2719
2720 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2721 self.dismiss_diagnostics(cx);
2722 return true;
2723 }
2724
2725 false
2726 }
2727
2728 fn linked_editing_ranges_for(
2729 &self,
2730 selection: Range<text::Anchor>,
2731 cx: &App,
2732 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2733 if self.linked_edit_ranges.is_empty() {
2734 return None;
2735 }
2736 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2737 selection.end.buffer_id.and_then(|end_buffer_id| {
2738 if selection.start.buffer_id != Some(end_buffer_id) {
2739 return None;
2740 }
2741 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2742 let snapshot = buffer.read(cx).snapshot();
2743 self.linked_edit_ranges
2744 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2745 .map(|ranges| (ranges, snapshot, buffer))
2746 })?;
2747 use text::ToOffset as TO;
2748 // find offset from the start of current range to current cursor position
2749 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2750
2751 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2752 let start_difference = start_offset - start_byte_offset;
2753 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2754 let end_difference = end_offset - start_byte_offset;
2755 // Current range has associated linked ranges.
2756 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2757 for range in linked_ranges.iter() {
2758 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2759 let end_offset = start_offset + end_difference;
2760 let start_offset = start_offset + start_difference;
2761 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2762 continue;
2763 }
2764 if self.selections.disjoint_anchor_ranges().any(|s| {
2765 if s.start.buffer_id != selection.start.buffer_id
2766 || s.end.buffer_id != selection.end.buffer_id
2767 {
2768 return false;
2769 }
2770 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2771 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2772 }) {
2773 continue;
2774 }
2775 let start = buffer_snapshot.anchor_after(start_offset);
2776 let end = buffer_snapshot.anchor_after(end_offset);
2777 linked_edits
2778 .entry(buffer.clone())
2779 .or_default()
2780 .push(start..end);
2781 }
2782 Some(linked_edits)
2783 }
2784
2785 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2786 let text: Arc<str> = text.into();
2787
2788 if self.read_only(cx) {
2789 return;
2790 }
2791
2792 let selections = self.selections.all_adjusted(cx);
2793 let mut bracket_inserted = false;
2794 let mut edits = Vec::new();
2795 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2796 let mut new_selections = Vec::with_capacity(selections.len());
2797 let mut new_autoclose_regions = Vec::new();
2798 let snapshot = self.buffer.read(cx).read(cx);
2799
2800 for (selection, autoclose_region) in
2801 self.selections_with_autoclose_regions(selections, &snapshot)
2802 {
2803 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2804 // Determine if the inserted text matches the opening or closing
2805 // bracket of any of this language's bracket pairs.
2806 let mut bracket_pair = None;
2807 let mut is_bracket_pair_start = false;
2808 let mut is_bracket_pair_end = false;
2809 if !text.is_empty() {
2810 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2811 // and they are removing the character that triggered IME popup.
2812 for (pair, enabled) in scope.brackets() {
2813 if !pair.close && !pair.surround {
2814 continue;
2815 }
2816
2817 if enabled && pair.start.ends_with(text.as_ref()) {
2818 let prefix_len = pair.start.len() - text.len();
2819 let preceding_text_matches_prefix = prefix_len == 0
2820 || (selection.start.column >= (prefix_len as u32)
2821 && snapshot.contains_str_at(
2822 Point::new(
2823 selection.start.row,
2824 selection.start.column - (prefix_len as u32),
2825 ),
2826 &pair.start[..prefix_len],
2827 ));
2828 if preceding_text_matches_prefix {
2829 bracket_pair = Some(pair.clone());
2830 is_bracket_pair_start = true;
2831 break;
2832 }
2833 }
2834 if pair.end.as_str() == text.as_ref() {
2835 bracket_pair = Some(pair.clone());
2836 is_bracket_pair_end = true;
2837 break;
2838 }
2839 }
2840 }
2841
2842 if let Some(bracket_pair) = bracket_pair {
2843 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2844 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2845 let auto_surround =
2846 self.use_auto_surround && snapshot_settings.use_auto_surround;
2847 if selection.is_empty() {
2848 if is_bracket_pair_start {
2849 // If the inserted text is a suffix of an opening bracket and the
2850 // selection is preceded by the rest of the opening bracket, then
2851 // insert the closing bracket.
2852 let following_text_allows_autoclose = snapshot
2853 .chars_at(selection.start)
2854 .next()
2855 .map_or(true, |c| scope.should_autoclose_before(c));
2856
2857 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2858 && bracket_pair.start.len() == 1
2859 {
2860 let target = bracket_pair.start.chars().next().unwrap();
2861 let current_line_count = snapshot
2862 .reversed_chars_at(selection.start)
2863 .take_while(|&c| c != '\n')
2864 .filter(|&c| c == target)
2865 .count();
2866 current_line_count % 2 == 1
2867 } else {
2868 false
2869 };
2870
2871 if autoclose
2872 && bracket_pair.close
2873 && following_text_allows_autoclose
2874 && !is_closing_quote
2875 {
2876 let anchor = snapshot.anchor_before(selection.end);
2877 new_selections.push((selection.map(|_| anchor), text.len()));
2878 new_autoclose_regions.push((
2879 anchor,
2880 text.len(),
2881 selection.id,
2882 bracket_pair.clone(),
2883 ));
2884 edits.push((
2885 selection.range(),
2886 format!("{}{}", text, bracket_pair.end).into(),
2887 ));
2888 bracket_inserted = true;
2889 continue;
2890 }
2891 }
2892
2893 if let Some(region) = autoclose_region {
2894 // If the selection is followed by an auto-inserted closing bracket,
2895 // then don't insert that closing bracket again; just move the selection
2896 // past the closing bracket.
2897 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2898 && text.as_ref() == region.pair.end.as_str();
2899 if should_skip {
2900 let anchor = snapshot.anchor_after(selection.end);
2901 new_selections
2902 .push((selection.map(|_| anchor), region.pair.end.len()));
2903 continue;
2904 }
2905 }
2906
2907 let always_treat_brackets_as_autoclosed = snapshot
2908 .settings_at(selection.start, cx)
2909 .always_treat_brackets_as_autoclosed;
2910 if always_treat_brackets_as_autoclosed
2911 && is_bracket_pair_end
2912 && snapshot.contains_str_at(selection.end, text.as_ref())
2913 {
2914 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2915 // and the inserted text is a closing bracket and the selection is followed
2916 // by the closing bracket then move the selection past the closing bracket.
2917 let anchor = snapshot.anchor_after(selection.end);
2918 new_selections.push((selection.map(|_| anchor), text.len()));
2919 continue;
2920 }
2921 }
2922 // If an opening bracket is 1 character long and is typed while
2923 // text is selected, then surround that text with the bracket pair.
2924 else if auto_surround
2925 && bracket_pair.surround
2926 && is_bracket_pair_start
2927 && bracket_pair.start.chars().count() == 1
2928 {
2929 edits.push((selection.start..selection.start, text.clone()));
2930 edits.push((
2931 selection.end..selection.end,
2932 bracket_pair.end.as_str().into(),
2933 ));
2934 bracket_inserted = true;
2935 new_selections.push((
2936 Selection {
2937 id: selection.id,
2938 start: snapshot.anchor_after(selection.start),
2939 end: snapshot.anchor_before(selection.end),
2940 reversed: selection.reversed,
2941 goal: selection.goal,
2942 },
2943 0,
2944 ));
2945 continue;
2946 }
2947 }
2948 }
2949
2950 if self.auto_replace_emoji_shortcode
2951 && selection.is_empty()
2952 && text.as_ref().ends_with(':')
2953 {
2954 if let Some(possible_emoji_short_code) =
2955 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2956 {
2957 if !possible_emoji_short_code.is_empty() {
2958 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2959 let emoji_shortcode_start = Point::new(
2960 selection.start.row,
2961 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2962 );
2963
2964 // Remove shortcode from buffer
2965 edits.push((
2966 emoji_shortcode_start..selection.start,
2967 "".to_string().into(),
2968 ));
2969 new_selections.push((
2970 Selection {
2971 id: selection.id,
2972 start: snapshot.anchor_after(emoji_shortcode_start),
2973 end: snapshot.anchor_before(selection.start),
2974 reversed: selection.reversed,
2975 goal: selection.goal,
2976 },
2977 0,
2978 ));
2979
2980 // Insert emoji
2981 let selection_start_anchor = snapshot.anchor_after(selection.start);
2982 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2983 edits.push((selection.start..selection.end, emoji.to_string().into()));
2984
2985 continue;
2986 }
2987 }
2988 }
2989 }
2990
2991 // If not handling any auto-close operation, then just replace the selected
2992 // text with the given input and move the selection to the end of the
2993 // newly inserted text.
2994 let anchor = snapshot.anchor_after(selection.end);
2995 if !self.linked_edit_ranges.is_empty() {
2996 let start_anchor = snapshot.anchor_before(selection.start);
2997
2998 let is_word_char = text.chars().next().map_or(true, |char| {
2999 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3000 classifier.is_word(char)
3001 });
3002
3003 if is_word_char {
3004 if let Some(ranges) = self
3005 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3006 {
3007 for (buffer, edits) in ranges {
3008 linked_edits
3009 .entry(buffer.clone())
3010 .or_default()
3011 .extend(edits.into_iter().map(|range| (range, text.clone())));
3012 }
3013 }
3014 }
3015 }
3016
3017 new_selections.push((selection.map(|_| anchor), 0));
3018 edits.push((selection.start..selection.end, text.clone()));
3019 }
3020
3021 drop(snapshot);
3022
3023 self.transact(window, cx, |this, window, cx| {
3024 this.buffer.update(cx, |buffer, cx| {
3025 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3026 });
3027 for (buffer, edits) in linked_edits {
3028 buffer.update(cx, |buffer, cx| {
3029 let snapshot = buffer.snapshot();
3030 let edits = edits
3031 .into_iter()
3032 .map(|(range, text)| {
3033 use text::ToPoint as TP;
3034 let end_point = TP::to_point(&range.end, &snapshot);
3035 let start_point = TP::to_point(&range.start, &snapshot);
3036 (start_point..end_point, text)
3037 })
3038 .sorted_by_key(|(range, _)| range.start)
3039 .collect::<Vec<_>>();
3040 buffer.edit(edits, None, cx);
3041 })
3042 }
3043 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3044 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3045 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3046 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3047 .zip(new_selection_deltas)
3048 .map(|(selection, delta)| Selection {
3049 id: selection.id,
3050 start: selection.start + delta,
3051 end: selection.end + delta,
3052 reversed: selection.reversed,
3053 goal: SelectionGoal::None,
3054 })
3055 .collect::<Vec<_>>();
3056
3057 let mut i = 0;
3058 for (position, delta, selection_id, pair) in new_autoclose_regions {
3059 let position = position.to_offset(&map.buffer_snapshot) + delta;
3060 let start = map.buffer_snapshot.anchor_before(position);
3061 let end = map.buffer_snapshot.anchor_after(position);
3062 while let Some(existing_state) = this.autoclose_regions.get(i) {
3063 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3064 Ordering::Less => i += 1,
3065 Ordering::Greater => break,
3066 Ordering::Equal => {
3067 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3068 Ordering::Less => i += 1,
3069 Ordering::Equal => break,
3070 Ordering::Greater => break,
3071 }
3072 }
3073 }
3074 }
3075 this.autoclose_regions.insert(
3076 i,
3077 AutocloseRegion {
3078 selection_id,
3079 range: start..end,
3080 pair,
3081 },
3082 );
3083 }
3084
3085 let had_active_inline_completion = this.has_active_inline_completion();
3086 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3087 s.select(new_selections)
3088 });
3089
3090 if !bracket_inserted {
3091 if let Some(on_type_format_task) =
3092 this.trigger_on_type_formatting(text.to_string(), window, cx)
3093 {
3094 on_type_format_task.detach_and_log_err(cx);
3095 }
3096 }
3097
3098 let editor_settings = EditorSettings::get_global(cx);
3099 if bracket_inserted
3100 && (editor_settings.auto_signature_help
3101 || editor_settings.show_signature_help_after_edits)
3102 {
3103 this.show_signature_help(&ShowSignatureHelp, window, cx);
3104 }
3105
3106 let trigger_in_words =
3107 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3108 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3109 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3110 this.refresh_inline_completion(true, false, window, cx);
3111 });
3112 }
3113
3114 fn find_possible_emoji_shortcode_at_position(
3115 snapshot: &MultiBufferSnapshot,
3116 position: Point,
3117 ) -> Option<String> {
3118 let mut chars = Vec::new();
3119 let mut found_colon = false;
3120 for char in snapshot.reversed_chars_at(position).take(100) {
3121 // Found a possible emoji shortcode in the middle of the buffer
3122 if found_colon {
3123 if char.is_whitespace() {
3124 chars.reverse();
3125 return Some(chars.iter().collect());
3126 }
3127 // If the previous character is not a whitespace, we are in the middle of a word
3128 // and we only want to complete the shortcode if the word is made up of other emojis
3129 let mut containing_word = String::new();
3130 for ch in snapshot
3131 .reversed_chars_at(position)
3132 .skip(chars.len() + 1)
3133 .take(100)
3134 {
3135 if ch.is_whitespace() {
3136 break;
3137 }
3138 containing_word.push(ch);
3139 }
3140 let containing_word = containing_word.chars().rev().collect::<String>();
3141 if util::word_consists_of_emojis(containing_word.as_str()) {
3142 chars.reverse();
3143 return Some(chars.iter().collect());
3144 }
3145 }
3146
3147 if char.is_whitespace() || !char.is_ascii() {
3148 return None;
3149 }
3150 if char == ':' {
3151 found_colon = true;
3152 } else {
3153 chars.push(char);
3154 }
3155 }
3156 // Found a possible emoji shortcode at the beginning of the buffer
3157 chars.reverse();
3158 Some(chars.iter().collect())
3159 }
3160
3161 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3162 self.transact(window, cx, |this, window, cx| {
3163 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3164 let selections = this.selections.all::<usize>(cx);
3165 let multi_buffer = this.buffer.read(cx);
3166 let buffer = multi_buffer.snapshot(cx);
3167 selections
3168 .iter()
3169 .map(|selection| {
3170 let start_point = selection.start.to_point(&buffer);
3171 let mut indent =
3172 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3173 indent.len = cmp::min(indent.len, start_point.column);
3174 let start = selection.start;
3175 let end = selection.end;
3176 let selection_is_empty = start == end;
3177 let language_scope = buffer.language_scope_at(start);
3178 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3179 &language_scope
3180 {
3181 let insert_extra_newline =
3182 insert_extra_newline_brackets(&buffer, start..end, language)
3183 || insert_extra_newline_tree_sitter(&buffer, start..end);
3184
3185 // Comment extension on newline is allowed only for cursor selections
3186 let comment_delimiter = maybe!({
3187 if !selection_is_empty {
3188 return None;
3189 }
3190
3191 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3192 return None;
3193 }
3194
3195 let delimiters = language.line_comment_prefixes();
3196 let max_len_of_delimiter =
3197 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3198 let (snapshot, range) =
3199 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3200
3201 let mut index_of_first_non_whitespace = 0;
3202 let comment_candidate = snapshot
3203 .chars_for_range(range)
3204 .skip_while(|c| {
3205 let should_skip = c.is_whitespace();
3206 if should_skip {
3207 index_of_first_non_whitespace += 1;
3208 }
3209 should_skip
3210 })
3211 .take(max_len_of_delimiter)
3212 .collect::<String>();
3213 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3214 comment_candidate.starts_with(comment_prefix.as_ref())
3215 })?;
3216 let cursor_is_placed_after_comment_marker =
3217 index_of_first_non_whitespace + comment_prefix.len()
3218 <= start_point.column as usize;
3219 if cursor_is_placed_after_comment_marker {
3220 Some(comment_prefix.clone())
3221 } else {
3222 None
3223 }
3224 });
3225 (comment_delimiter, insert_extra_newline)
3226 } else {
3227 (None, false)
3228 };
3229
3230 let capacity_for_delimiter = comment_delimiter
3231 .as_deref()
3232 .map(str::len)
3233 .unwrap_or_default();
3234 let mut new_text =
3235 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3236 new_text.push('\n');
3237 new_text.extend(indent.chars());
3238 if let Some(delimiter) = &comment_delimiter {
3239 new_text.push_str(delimiter);
3240 }
3241 if insert_extra_newline {
3242 new_text = new_text.repeat(2);
3243 }
3244
3245 let anchor = buffer.anchor_after(end);
3246 let new_selection = selection.map(|_| anchor);
3247 (
3248 (start..end, new_text),
3249 (insert_extra_newline, new_selection),
3250 )
3251 })
3252 .unzip()
3253 };
3254
3255 this.edit_with_autoindent(edits, cx);
3256 let buffer = this.buffer.read(cx).snapshot(cx);
3257 let new_selections = selection_fixup_info
3258 .into_iter()
3259 .map(|(extra_newline_inserted, new_selection)| {
3260 let mut cursor = new_selection.end.to_point(&buffer);
3261 if extra_newline_inserted {
3262 cursor.row -= 1;
3263 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3264 }
3265 new_selection.map(|_| cursor)
3266 })
3267 .collect();
3268
3269 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3270 s.select(new_selections)
3271 });
3272 this.refresh_inline_completion(true, false, window, cx);
3273 });
3274 }
3275
3276 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3277 let buffer = self.buffer.read(cx);
3278 let snapshot = buffer.snapshot(cx);
3279
3280 let mut edits = Vec::new();
3281 let mut rows = Vec::new();
3282
3283 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3284 let cursor = selection.head();
3285 let row = cursor.row;
3286
3287 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3288
3289 let newline = "\n".to_string();
3290 edits.push((start_of_line..start_of_line, newline));
3291
3292 rows.push(row + rows_inserted as u32);
3293 }
3294
3295 self.transact(window, cx, |editor, window, cx| {
3296 editor.edit(edits, cx);
3297
3298 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3299 let mut index = 0;
3300 s.move_cursors_with(|map, _, _| {
3301 let row = rows[index];
3302 index += 1;
3303
3304 let point = Point::new(row, 0);
3305 let boundary = map.next_line_boundary(point).1;
3306 let clipped = map.clip_point(boundary, Bias::Left);
3307
3308 (clipped, SelectionGoal::None)
3309 });
3310 });
3311
3312 let mut indent_edits = Vec::new();
3313 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3314 for row in rows {
3315 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3316 for (row, indent) in indents {
3317 if indent.len == 0 {
3318 continue;
3319 }
3320
3321 let text = match indent.kind {
3322 IndentKind::Space => " ".repeat(indent.len as usize),
3323 IndentKind::Tab => "\t".repeat(indent.len as usize),
3324 };
3325 let point = Point::new(row.0, 0);
3326 indent_edits.push((point..point, text));
3327 }
3328 }
3329 editor.edit(indent_edits, cx);
3330 });
3331 }
3332
3333 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3334 let buffer = self.buffer.read(cx);
3335 let snapshot = buffer.snapshot(cx);
3336
3337 let mut edits = Vec::new();
3338 let mut rows = Vec::new();
3339 let mut rows_inserted = 0;
3340
3341 for selection in self.selections.all_adjusted(cx) {
3342 let cursor = selection.head();
3343 let row = cursor.row;
3344
3345 let point = Point::new(row + 1, 0);
3346 let start_of_line = snapshot.clip_point(point, Bias::Left);
3347
3348 let newline = "\n".to_string();
3349 edits.push((start_of_line..start_of_line, newline));
3350
3351 rows_inserted += 1;
3352 rows.push(row + rows_inserted);
3353 }
3354
3355 self.transact(window, cx, |editor, window, cx| {
3356 editor.edit(edits, cx);
3357
3358 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3359 let mut index = 0;
3360 s.move_cursors_with(|map, _, _| {
3361 let row = rows[index];
3362 index += 1;
3363
3364 let point = Point::new(row, 0);
3365 let boundary = map.next_line_boundary(point).1;
3366 let clipped = map.clip_point(boundary, Bias::Left);
3367
3368 (clipped, SelectionGoal::None)
3369 });
3370 });
3371
3372 let mut indent_edits = Vec::new();
3373 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3374 for row in rows {
3375 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3376 for (row, indent) in indents {
3377 if indent.len == 0 {
3378 continue;
3379 }
3380
3381 let text = match indent.kind {
3382 IndentKind::Space => " ".repeat(indent.len as usize),
3383 IndentKind::Tab => "\t".repeat(indent.len as usize),
3384 };
3385 let point = Point::new(row.0, 0);
3386 indent_edits.push((point..point, text));
3387 }
3388 }
3389 editor.edit(indent_edits, cx);
3390 });
3391 }
3392
3393 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3394 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3395 original_start_columns: Vec::new(),
3396 });
3397 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3398 }
3399
3400 fn insert_with_autoindent_mode(
3401 &mut self,
3402 text: &str,
3403 autoindent_mode: Option<AutoindentMode>,
3404 window: &mut Window,
3405 cx: &mut Context<Self>,
3406 ) {
3407 if self.read_only(cx) {
3408 return;
3409 }
3410
3411 let text: Arc<str> = text.into();
3412 self.transact(window, cx, |this, window, cx| {
3413 let old_selections = this.selections.all_adjusted(cx);
3414 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3415 let anchors = {
3416 let snapshot = buffer.read(cx);
3417 old_selections
3418 .iter()
3419 .map(|s| {
3420 let anchor = snapshot.anchor_after(s.head());
3421 s.map(|_| anchor)
3422 })
3423 .collect::<Vec<_>>()
3424 };
3425 buffer.edit(
3426 old_selections
3427 .iter()
3428 .map(|s| (s.start..s.end, text.clone())),
3429 autoindent_mode,
3430 cx,
3431 );
3432 anchors
3433 });
3434
3435 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3436 s.select_anchors(selection_anchors);
3437 });
3438
3439 cx.notify();
3440 });
3441 }
3442
3443 fn trigger_completion_on_input(
3444 &mut self,
3445 text: &str,
3446 trigger_in_words: bool,
3447 window: &mut Window,
3448 cx: &mut Context<Self>,
3449 ) {
3450 if self.is_completion_trigger(text, trigger_in_words, cx) {
3451 self.show_completions(
3452 &ShowCompletions {
3453 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3454 },
3455 window,
3456 cx,
3457 );
3458 } else {
3459 self.hide_context_menu(window, cx);
3460 }
3461 }
3462
3463 fn is_completion_trigger(
3464 &self,
3465 text: &str,
3466 trigger_in_words: bool,
3467 cx: &mut Context<Self>,
3468 ) -> bool {
3469 let position = self.selections.newest_anchor().head();
3470 let multibuffer = self.buffer.read(cx);
3471 let Some(buffer) = position
3472 .buffer_id
3473 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3474 else {
3475 return false;
3476 };
3477
3478 if let Some(completion_provider) = &self.completion_provider {
3479 completion_provider.is_completion_trigger(
3480 &buffer,
3481 position.text_anchor,
3482 text,
3483 trigger_in_words,
3484 cx,
3485 )
3486 } else {
3487 false
3488 }
3489 }
3490
3491 /// If any empty selections is touching the start of its innermost containing autoclose
3492 /// region, expand it to select the brackets.
3493 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3494 let selections = self.selections.all::<usize>(cx);
3495 let buffer = self.buffer.read(cx).read(cx);
3496 let new_selections = self
3497 .selections_with_autoclose_regions(selections, &buffer)
3498 .map(|(mut selection, region)| {
3499 if !selection.is_empty() {
3500 return selection;
3501 }
3502
3503 if let Some(region) = region {
3504 let mut range = region.range.to_offset(&buffer);
3505 if selection.start == range.start && range.start >= region.pair.start.len() {
3506 range.start -= region.pair.start.len();
3507 if buffer.contains_str_at(range.start, ®ion.pair.start)
3508 && buffer.contains_str_at(range.end, ®ion.pair.end)
3509 {
3510 range.end += region.pair.end.len();
3511 selection.start = range.start;
3512 selection.end = range.end;
3513
3514 return selection;
3515 }
3516 }
3517 }
3518
3519 let always_treat_brackets_as_autoclosed = buffer
3520 .settings_at(selection.start, cx)
3521 .always_treat_brackets_as_autoclosed;
3522
3523 if !always_treat_brackets_as_autoclosed {
3524 return selection;
3525 }
3526
3527 if let Some(scope) = buffer.language_scope_at(selection.start) {
3528 for (pair, enabled) in scope.brackets() {
3529 if !enabled || !pair.close {
3530 continue;
3531 }
3532
3533 if buffer.contains_str_at(selection.start, &pair.end) {
3534 let pair_start_len = pair.start.len();
3535 if buffer.contains_str_at(
3536 selection.start.saturating_sub(pair_start_len),
3537 &pair.start,
3538 ) {
3539 selection.start -= pair_start_len;
3540 selection.end += pair.end.len();
3541
3542 return selection;
3543 }
3544 }
3545 }
3546 }
3547
3548 selection
3549 })
3550 .collect();
3551
3552 drop(buffer);
3553 self.change_selections(None, window, cx, |selections| {
3554 selections.select(new_selections)
3555 });
3556 }
3557
3558 /// Iterate the given selections, and for each one, find the smallest surrounding
3559 /// autoclose region. This uses the ordering of the selections and the autoclose
3560 /// regions to avoid repeated comparisons.
3561 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3562 &'a self,
3563 selections: impl IntoIterator<Item = Selection<D>>,
3564 buffer: &'a MultiBufferSnapshot,
3565 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3566 let mut i = 0;
3567 let mut regions = self.autoclose_regions.as_slice();
3568 selections.into_iter().map(move |selection| {
3569 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3570
3571 let mut enclosing = None;
3572 while let Some(pair_state) = regions.get(i) {
3573 if pair_state.range.end.to_offset(buffer) < range.start {
3574 regions = ®ions[i + 1..];
3575 i = 0;
3576 } else if pair_state.range.start.to_offset(buffer) > range.end {
3577 break;
3578 } else {
3579 if pair_state.selection_id == selection.id {
3580 enclosing = Some(pair_state);
3581 }
3582 i += 1;
3583 }
3584 }
3585
3586 (selection, enclosing)
3587 })
3588 }
3589
3590 /// Remove any autoclose regions that no longer contain their selection.
3591 fn invalidate_autoclose_regions(
3592 &mut self,
3593 mut selections: &[Selection<Anchor>],
3594 buffer: &MultiBufferSnapshot,
3595 ) {
3596 self.autoclose_regions.retain(|state| {
3597 let mut i = 0;
3598 while let Some(selection) = selections.get(i) {
3599 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3600 selections = &selections[1..];
3601 continue;
3602 }
3603 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3604 break;
3605 }
3606 if selection.id == state.selection_id {
3607 return true;
3608 } else {
3609 i += 1;
3610 }
3611 }
3612 false
3613 });
3614 }
3615
3616 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3617 let offset = position.to_offset(buffer);
3618 let (word_range, kind) = buffer.surrounding_word(offset, true);
3619 if offset > word_range.start && kind == Some(CharKind::Word) {
3620 Some(
3621 buffer
3622 .text_for_range(word_range.start..offset)
3623 .collect::<String>(),
3624 )
3625 } else {
3626 None
3627 }
3628 }
3629
3630 pub fn toggle_inlay_hints(
3631 &mut self,
3632 _: &ToggleInlayHints,
3633 _: &mut Window,
3634 cx: &mut Context<Self>,
3635 ) {
3636 self.refresh_inlay_hints(
3637 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3638 cx,
3639 );
3640 }
3641
3642 pub fn inlay_hints_enabled(&self) -> bool {
3643 self.inlay_hint_cache.enabled
3644 }
3645
3646 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3647 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3648 return;
3649 }
3650
3651 let reason_description = reason.description();
3652 let ignore_debounce = matches!(
3653 reason,
3654 InlayHintRefreshReason::SettingsChange(_)
3655 | InlayHintRefreshReason::Toggle(_)
3656 | InlayHintRefreshReason::ExcerptsRemoved(_)
3657 );
3658 let (invalidate_cache, required_languages) = match reason {
3659 InlayHintRefreshReason::Toggle(enabled) => {
3660 self.inlay_hint_cache.enabled = enabled;
3661 if enabled {
3662 (InvalidationStrategy::RefreshRequested, None)
3663 } else {
3664 self.inlay_hint_cache.clear();
3665 self.splice_inlays(
3666 &self
3667 .visible_inlay_hints(cx)
3668 .iter()
3669 .map(|inlay| inlay.id)
3670 .collect::<Vec<InlayId>>(),
3671 Vec::new(),
3672 cx,
3673 );
3674 return;
3675 }
3676 }
3677 InlayHintRefreshReason::SettingsChange(new_settings) => {
3678 match self.inlay_hint_cache.update_settings(
3679 &self.buffer,
3680 new_settings,
3681 self.visible_inlay_hints(cx),
3682 cx,
3683 ) {
3684 ControlFlow::Break(Some(InlaySplice {
3685 to_remove,
3686 to_insert,
3687 })) => {
3688 self.splice_inlays(&to_remove, to_insert, cx);
3689 return;
3690 }
3691 ControlFlow::Break(None) => return,
3692 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3693 }
3694 }
3695 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3696 if let Some(InlaySplice {
3697 to_remove,
3698 to_insert,
3699 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3700 {
3701 self.splice_inlays(&to_remove, to_insert, cx);
3702 }
3703 return;
3704 }
3705 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3706 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3707 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3708 }
3709 InlayHintRefreshReason::RefreshRequested => {
3710 (InvalidationStrategy::RefreshRequested, None)
3711 }
3712 };
3713
3714 if let Some(InlaySplice {
3715 to_remove,
3716 to_insert,
3717 }) = self.inlay_hint_cache.spawn_hint_refresh(
3718 reason_description,
3719 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3720 invalidate_cache,
3721 ignore_debounce,
3722 cx,
3723 ) {
3724 self.splice_inlays(&to_remove, to_insert, cx);
3725 }
3726 }
3727
3728 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3729 self.display_map
3730 .read(cx)
3731 .current_inlays()
3732 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3733 .cloned()
3734 .collect()
3735 }
3736
3737 pub fn excerpts_for_inlay_hints_query(
3738 &self,
3739 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3740 cx: &mut Context<Editor>,
3741 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3742 let Some(project) = self.project.as_ref() else {
3743 return HashMap::default();
3744 };
3745 let project = project.read(cx);
3746 let multi_buffer = self.buffer().read(cx);
3747 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3748 let multi_buffer_visible_start = self
3749 .scroll_manager
3750 .anchor()
3751 .anchor
3752 .to_point(&multi_buffer_snapshot);
3753 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3754 multi_buffer_visible_start
3755 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3756 Bias::Left,
3757 );
3758 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3759 multi_buffer_snapshot
3760 .range_to_buffer_ranges(multi_buffer_visible_range)
3761 .into_iter()
3762 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3763 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3764 let buffer_file = project::File::from_dyn(buffer.file())?;
3765 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3766 let worktree_entry = buffer_worktree
3767 .read(cx)
3768 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3769 if worktree_entry.is_ignored {
3770 return None;
3771 }
3772
3773 let language = buffer.language()?;
3774 if let Some(restrict_to_languages) = restrict_to_languages {
3775 if !restrict_to_languages.contains(language) {
3776 return None;
3777 }
3778 }
3779 Some((
3780 excerpt_id,
3781 (
3782 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3783 buffer.version().clone(),
3784 excerpt_visible_range,
3785 ),
3786 ))
3787 })
3788 .collect()
3789 }
3790
3791 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3792 TextLayoutDetails {
3793 text_system: window.text_system().clone(),
3794 editor_style: self.style.clone().unwrap(),
3795 rem_size: window.rem_size(),
3796 scroll_anchor: self.scroll_manager.anchor(),
3797 visible_rows: self.visible_line_count(),
3798 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3799 }
3800 }
3801
3802 pub fn splice_inlays(
3803 &self,
3804 to_remove: &[InlayId],
3805 to_insert: Vec<Inlay>,
3806 cx: &mut Context<Self>,
3807 ) {
3808 self.display_map.update(cx, |display_map, cx| {
3809 display_map.splice_inlays(to_remove, to_insert, cx)
3810 });
3811 cx.notify();
3812 }
3813
3814 fn trigger_on_type_formatting(
3815 &self,
3816 input: String,
3817 window: &mut Window,
3818 cx: &mut Context<Self>,
3819 ) -> Option<Task<Result<()>>> {
3820 if input.len() != 1 {
3821 return None;
3822 }
3823
3824 let project = self.project.as_ref()?;
3825 let position = self.selections.newest_anchor().head();
3826 let (buffer, buffer_position) = self
3827 .buffer
3828 .read(cx)
3829 .text_anchor_for_position(position, cx)?;
3830
3831 let settings = language_settings::language_settings(
3832 buffer
3833 .read(cx)
3834 .language_at(buffer_position)
3835 .map(|l| l.name()),
3836 buffer.read(cx).file(),
3837 cx,
3838 );
3839 if !settings.use_on_type_format {
3840 return None;
3841 }
3842
3843 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3844 // hence we do LSP request & edit on host side only — add formats to host's history.
3845 let push_to_lsp_host_history = true;
3846 // If this is not the host, append its history with new edits.
3847 let push_to_client_history = project.read(cx).is_via_collab();
3848
3849 let on_type_formatting = project.update(cx, |project, cx| {
3850 project.on_type_format(
3851 buffer.clone(),
3852 buffer_position,
3853 input,
3854 push_to_lsp_host_history,
3855 cx,
3856 )
3857 });
3858 Some(cx.spawn_in(window, |editor, mut cx| async move {
3859 if let Some(transaction) = on_type_formatting.await? {
3860 if push_to_client_history {
3861 buffer
3862 .update(&mut cx, |buffer, _| {
3863 buffer.push_transaction(transaction, Instant::now());
3864 })
3865 .ok();
3866 }
3867 editor.update(&mut cx, |editor, cx| {
3868 editor.refresh_document_highlights(cx);
3869 })?;
3870 }
3871 Ok(())
3872 }))
3873 }
3874
3875 pub fn show_completions(
3876 &mut self,
3877 options: &ShowCompletions,
3878 window: &mut Window,
3879 cx: &mut Context<Self>,
3880 ) {
3881 if self.pending_rename.is_some() {
3882 return;
3883 }
3884
3885 let Some(provider) = self.completion_provider.as_ref() else {
3886 return;
3887 };
3888
3889 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3890 return;
3891 }
3892
3893 let position = self.selections.newest_anchor().head();
3894 if position.diff_base_anchor.is_some() {
3895 return;
3896 }
3897 let (buffer, buffer_position) =
3898 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3899 output
3900 } else {
3901 return;
3902 };
3903 let show_completion_documentation = buffer
3904 .read(cx)
3905 .snapshot()
3906 .settings_at(buffer_position, cx)
3907 .show_completion_documentation;
3908
3909 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3910
3911 let trigger_kind = match &options.trigger {
3912 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3913 CompletionTriggerKind::TRIGGER_CHARACTER
3914 }
3915 _ => CompletionTriggerKind::INVOKED,
3916 };
3917 let completion_context = CompletionContext {
3918 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3919 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3920 Some(String::from(trigger))
3921 } else {
3922 None
3923 }
3924 }),
3925 trigger_kind,
3926 };
3927 let completions =
3928 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3929 let sort_completions = provider.sort_completions();
3930
3931 let id = post_inc(&mut self.next_completion_id);
3932 let task = cx.spawn_in(window, |editor, mut cx| {
3933 async move {
3934 editor.update(&mut cx, |this, _| {
3935 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3936 })?;
3937 let completions = completions.await.log_err();
3938 let menu = if let Some(completions) = completions {
3939 let mut menu = CompletionsMenu::new(
3940 id,
3941 sort_completions,
3942 show_completion_documentation,
3943 position,
3944 buffer.clone(),
3945 completions.into(),
3946 );
3947
3948 menu.filter(query.as_deref(), cx.background_executor().clone())
3949 .await;
3950
3951 menu.visible().then_some(menu)
3952 } else {
3953 None
3954 };
3955
3956 editor.update_in(&mut cx, |editor, window, cx| {
3957 match editor.context_menu.borrow().as_ref() {
3958 None => {}
3959 Some(CodeContextMenu::Completions(prev_menu)) => {
3960 if prev_menu.id > id {
3961 return;
3962 }
3963 }
3964 _ => return,
3965 }
3966
3967 if editor.focus_handle.is_focused(window) && menu.is_some() {
3968 let mut menu = menu.unwrap();
3969 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3970
3971 *editor.context_menu.borrow_mut() =
3972 Some(CodeContextMenu::Completions(menu));
3973
3974 if editor.show_edit_predictions_in_menu() {
3975 editor.update_visible_inline_completion(window, cx);
3976 } else {
3977 editor.discard_inline_completion(false, cx);
3978 }
3979
3980 cx.notify();
3981 } else if editor.completion_tasks.len() <= 1 {
3982 // If there are no more completion tasks and the last menu was
3983 // empty, we should hide it.
3984 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3985 // If it was already hidden and we don't show inline
3986 // completions in the menu, we should also show the
3987 // inline-completion when available.
3988 if was_hidden && editor.show_edit_predictions_in_menu() {
3989 editor.update_visible_inline_completion(window, cx);
3990 }
3991 }
3992 })?;
3993
3994 Ok::<_, anyhow::Error>(())
3995 }
3996 .log_err()
3997 });
3998
3999 self.completion_tasks.push((id, task));
4000 }
4001
4002 pub fn confirm_completion(
4003 &mut self,
4004 action: &ConfirmCompletion,
4005 window: &mut Window,
4006 cx: &mut Context<Self>,
4007 ) -> Option<Task<Result<()>>> {
4008 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4009 }
4010
4011 pub fn compose_completion(
4012 &mut self,
4013 action: &ComposeCompletion,
4014 window: &mut Window,
4015 cx: &mut Context<Self>,
4016 ) -> Option<Task<Result<()>>> {
4017 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4018 }
4019
4020 fn do_completion(
4021 &mut self,
4022 item_ix: Option<usize>,
4023 intent: CompletionIntent,
4024 window: &mut Window,
4025 cx: &mut Context<Editor>,
4026 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4027 use language::ToOffset as _;
4028
4029 let completions_menu =
4030 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4031 menu
4032 } else {
4033 return None;
4034 };
4035
4036 let entries = completions_menu.entries.borrow();
4037 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4038 if self.show_edit_predictions_in_menu() {
4039 self.discard_inline_completion(true, cx);
4040 }
4041 let candidate_id = mat.candidate_id;
4042 drop(entries);
4043
4044 let buffer_handle = completions_menu.buffer;
4045 let completion = completions_menu
4046 .completions
4047 .borrow()
4048 .get(candidate_id)?
4049 .clone();
4050 cx.stop_propagation();
4051
4052 let snippet;
4053 let text;
4054
4055 if completion.is_snippet() {
4056 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4057 text = snippet.as_ref().unwrap().text.clone();
4058 } else {
4059 snippet = None;
4060 text = completion.new_text.clone();
4061 };
4062 let selections = self.selections.all::<usize>(cx);
4063 let buffer = buffer_handle.read(cx);
4064 let old_range = completion.old_range.to_offset(buffer);
4065 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4066
4067 let newest_selection = self.selections.newest_anchor();
4068 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4069 return None;
4070 }
4071
4072 let lookbehind = newest_selection
4073 .start
4074 .text_anchor
4075 .to_offset(buffer)
4076 .saturating_sub(old_range.start);
4077 let lookahead = old_range
4078 .end
4079 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4080 let mut common_prefix_len = old_text
4081 .bytes()
4082 .zip(text.bytes())
4083 .take_while(|(a, b)| a == b)
4084 .count();
4085
4086 let snapshot = self.buffer.read(cx).snapshot(cx);
4087 let mut range_to_replace: Option<Range<isize>> = None;
4088 let mut ranges = Vec::new();
4089 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4090 for selection in &selections {
4091 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4092 let start = selection.start.saturating_sub(lookbehind);
4093 let end = selection.end + lookahead;
4094 if selection.id == newest_selection.id {
4095 range_to_replace = Some(
4096 ((start + common_prefix_len) as isize - selection.start as isize)
4097 ..(end as isize - selection.start as isize),
4098 );
4099 }
4100 ranges.push(start + common_prefix_len..end);
4101 } else {
4102 common_prefix_len = 0;
4103 ranges.clear();
4104 ranges.extend(selections.iter().map(|s| {
4105 if s.id == newest_selection.id {
4106 range_to_replace = Some(
4107 old_range.start.to_offset_utf16(&snapshot).0 as isize
4108 - selection.start as isize
4109 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4110 - selection.start as isize,
4111 );
4112 old_range.clone()
4113 } else {
4114 s.start..s.end
4115 }
4116 }));
4117 break;
4118 }
4119 if !self.linked_edit_ranges.is_empty() {
4120 let start_anchor = snapshot.anchor_before(selection.head());
4121 let end_anchor = snapshot.anchor_after(selection.tail());
4122 if let Some(ranges) = self
4123 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4124 {
4125 for (buffer, edits) in ranges {
4126 linked_edits.entry(buffer.clone()).or_default().extend(
4127 edits
4128 .into_iter()
4129 .map(|range| (range, text[common_prefix_len..].to_owned())),
4130 );
4131 }
4132 }
4133 }
4134 }
4135 let text = &text[common_prefix_len..];
4136
4137 cx.emit(EditorEvent::InputHandled {
4138 utf16_range_to_replace: range_to_replace,
4139 text: text.into(),
4140 });
4141
4142 self.transact(window, cx, |this, window, cx| {
4143 if let Some(mut snippet) = snippet {
4144 snippet.text = text.to_string();
4145 for tabstop in snippet
4146 .tabstops
4147 .iter_mut()
4148 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4149 {
4150 tabstop.start -= common_prefix_len as isize;
4151 tabstop.end -= common_prefix_len as isize;
4152 }
4153
4154 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4155 } else {
4156 this.buffer.update(cx, |buffer, cx| {
4157 buffer.edit(
4158 ranges.iter().map(|range| (range.clone(), text)),
4159 this.autoindent_mode.clone(),
4160 cx,
4161 );
4162 });
4163 }
4164 for (buffer, edits) in linked_edits {
4165 buffer.update(cx, |buffer, cx| {
4166 let snapshot = buffer.snapshot();
4167 let edits = edits
4168 .into_iter()
4169 .map(|(range, text)| {
4170 use text::ToPoint as TP;
4171 let end_point = TP::to_point(&range.end, &snapshot);
4172 let start_point = TP::to_point(&range.start, &snapshot);
4173 (start_point..end_point, text)
4174 })
4175 .sorted_by_key(|(range, _)| range.start)
4176 .collect::<Vec<_>>();
4177 buffer.edit(edits, None, cx);
4178 })
4179 }
4180
4181 this.refresh_inline_completion(true, false, window, cx);
4182 });
4183
4184 let show_new_completions_on_confirm = completion
4185 .confirm
4186 .as_ref()
4187 .map_or(false, |confirm| confirm(intent, window, cx));
4188 if show_new_completions_on_confirm {
4189 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4190 }
4191
4192 let provider = self.completion_provider.as_ref()?;
4193 drop(completion);
4194 let apply_edits = provider.apply_additional_edits_for_completion(
4195 buffer_handle,
4196 completions_menu.completions.clone(),
4197 candidate_id,
4198 true,
4199 cx,
4200 );
4201
4202 let editor_settings = EditorSettings::get_global(cx);
4203 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4204 // After the code completion is finished, users often want to know what signatures are needed.
4205 // so we should automatically call signature_help
4206 self.show_signature_help(&ShowSignatureHelp, window, cx);
4207 }
4208
4209 Some(cx.foreground_executor().spawn(async move {
4210 apply_edits.await?;
4211 Ok(())
4212 }))
4213 }
4214
4215 pub fn toggle_code_actions(
4216 &mut self,
4217 action: &ToggleCodeActions,
4218 window: &mut Window,
4219 cx: &mut Context<Self>,
4220 ) {
4221 let mut context_menu = self.context_menu.borrow_mut();
4222 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4223 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4224 // Toggle if we're selecting the same one
4225 *context_menu = None;
4226 cx.notify();
4227 return;
4228 } else {
4229 // Otherwise, clear it and start a new one
4230 *context_menu = None;
4231 cx.notify();
4232 }
4233 }
4234 drop(context_menu);
4235 let snapshot = self.snapshot(window, cx);
4236 let deployed_from_indicator = action.deployed_from_indicator;
4237 let mut task = self.code_actions_task.take();
4238 let action = action.clone();
4239 cx.spawn_in(window, |editor, mut cx| async move {
4240 while let Some(prev_task) = task {
4241 prev_task.await.log_err();
4242 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4243 }
4244
4245 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4246 if editor.focus_handle.is_focused(window) {
4247 let multibuffer_point = action
4248 .deployed_from_indicator
4249 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4250 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4251 let (buffer, buffer_row) = snapshot
4252 .buffer_snapshot
4253 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4254 .and_then(|(buffer_snapshot, range)| {
4255 editor
4256 .buffer
4257 .read(cx)
4258 .buffer(buffer_snapshot.remote_id())
4259 .map(|buffer| (buffer, range.start.row))
4260 })?;
4261 let (_, code_actions) = editor
4262 .available_code_actions
4263 .clone()
4264 .and_then(|(location, code_actions)| {
4265 let snapshot = location.buffer.read(cx).snapshot();
4266 let point_range = location.range.to_point(&snapshot);
4267 let point_range = point_range.start.row..=point_range.end.row;
4268 if point_range.contains(&buffer_row) {
4269 Some((location, code_actions))
4270 } else {
4271 None
4272 }
4273 })
4274 .unzip();
4275 let buffer_id = buffer.read(cx).remote_id();
4276 let tasks = editor
4277 .tasks
4278 .get(&(buffer_id, buffer_row))
4279 .map(|t| Arc::new(t.to_owned()));
4280 if tasks.is_none() && code_actions.is_none() {
4281 return None;
4282 }
4283
4284 editor.completion_tasks.clear();
4285 editor.discard_inline_completion(false, cx);
4286 let task_context =
4287 tasks
4288 .as_ref()
4289 .zip(editor.project.clone())
4290 .map(|(tasks, project)| {
4291 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4292 });
4293
4294 Some(cx.spawn_in(window, |editor, mut cx| async move {
4295 let task_context = match task_context {
4296 Some(task_context) => task_context.await,
4297 None => None,
4298 };
4299 let resolved_tasks =
4300 tasks.zip(task_context).map(|(tasks, task_context)| {
4301 Rc::new(ResolvedTasks {
4302 templates: tasks.resolve(&task_context).collect(),
4303 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4304 multibuffer_point.row,
4305 tasks.column,
4306 )),
4307 })
4308 });
4309 let spawn_straight_away = resolved_tasks
4310 .as_ref()
4311 .map_or(false, |tasks| tasks.templates.len() == 1)
4312 && code_actions
4313 .as_ref()
4314 .map_or(true, |actions| actions.is_empty());
4315 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4316 *editor.context_menu.borrow_mut() =
4317 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4318 buffer,
4319 actions: CodeActionContents {
4320 tasks: resolved_tasks,
4321 actions: code_actions,
4322 },
4323 selected_item: Default::default(),
4324 scroll_handle: UniformListScrollHandle::default(),
4325 deployed_from_indicator,
4326 }));
4327 if spawn_straight_away {
4328 if let Some(task) = editor.confirm_code_action(
4329 &ConfirmCodeAction { item_ix: Some(0) },
4330 window,
4331 cx,
4332 ) {
4333 cx.notify();
4334 return task;
4335 }
4336 }
4337 cx.notify();
4338 Task::ready(Ok(()))
4339 }) {
4340 task.await
4341 } else {
4342 Ok(())
4343 }
4344 }))
4345 } else {
4346 Some(Task::ready(Ok(())))
4347 }
4348 })?;
4349 if let Some(task) = spawned_test_task {
4350 task.await?;
4351 }
4352
4353 Ok::<_, anyhow::Error>(())
4354 })
4355 .detach_and_log_err(cx);
4356 }
4357
4358 pub fn confirm_code_action(
4359 &mut self,
4360 action: &ConfirmCodeAction,
4361 window: &mut Window,
4362 cx: &mut Context<Self>,
4363 ) -> Option<Task<Result<()>>> {
4364 let actions_menu =
4365 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4366 menu
4367 } else {
4368 return None;
4369 };
4370 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4371 let action = actions_menu.actions.get(action_ix)?;
4372 let title = action.label();
4373 let buffer = actions_menu.buffer;
4374 let workspace = self.workspace()?;
4375
4376 match action {
4377 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4378 workspace.update(cx, |workspace, cx| {
4379 workspace::tasks::schedule_resolved_task(
4380 workspace,
4381 task_source_kind,
4382 resolved_task,
4383 false,
4384 cx,
4385 );
4386
4387 Some(Task::ready(Ok(())))
4388 })
4389 }
4390 CodeActionsItem::CodeAction {
4391 excerpt_id,
4392 action,
4393 provider,
4394 } => {
4395 let apply_code_action =
4396 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4397 let workspace = workspace.downgrade();
4398 Some(cx.spawn_in(window, |editor, cx| async move {
4399 let project_transaction = apply_code_action.await?;
4400 Self::open_project_transaction(
4401 &editor,
4402 workspace,
4403 project_transaction,
4404 title,
4405 cx,
4406 )
4407 .await
4408 }))
4409 }
4410 }
4411 }
4412
4413 pub async fn open_project_transaction(
4414 this: &WeakEntity<Editor>,
4415 workspace: WeakEntity<Workspace>,
4416 transaction: ProjectTransaction,
4417 title: String,
4418 mut cx: AsyncWindowContext,
4419 ) -> Result<()> {
4420 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4421 cx.update(|_, cx| {
4422 entries.sort_unstable_by_key(|(buffer, _)| {
4423 buffer.read(cx).file().map(|f| f.path().clone())
4424 });
4425 })?;
4426
4427 // If the project transaction's edits are all contained within this editor, then
4428 // avoid opening a new editor to display them.
4429
4430 if let Some((buffer, transaction)) = entries.first() {
4431 if entries.len() == 1 {
4432 let excerpt = this.update(&mut cx, |editor, cx| {
4433 editor
4434 .buffer()
4435 .read(cx)
4436 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4437 })?;
4438 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4439 if excerpted_buffer == *buffer {
4440 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4441 let excerpt_range = excerpt_range.to_offset(buffer);
4442 buffer
4443 .edited_ranges_for_transaction::<usize>(transaction)
4444 .all(|range| {
4445 excerpt_range.start <= range.start
4446 && excerpt_range.end >= range.end
4447 })
4448 })?;
4449
4450 if all_edits_within_excerpt {
4451 return Ok(());
4452 }
4453 }
4454 }
4455 }
4456 } else {
4457 return Ok(());
4458 }
4459
4460 let mut ranges_to_highlight = Vec::new();
4461 let excerpt_buffer = cx.new(|cx| {
4462 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4463 for (buffer_handle, transaction) in &entries {
4464 let buffer = buffer_handle.read(cx);
4465 ranges_to_highlight.extend(
4466 multibuffer.push_excerpts_with_context_lines(
4467 buffer_handle.clone(),
4468 buffer
4469 .edited_ranges_for_transaction::<usize>(transaction)
4470 .collect(),
4471 DEFAULT_MULTIBUFFER_CONTEXT,
4472 cx,
4473 ),
4474 );
4475 }
4476 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4477 multibuffer
4478 })?;
4479
4480 workspace.update_in(&mut cx, |workspace, window, cx| {
4481 let project = workspace.project().clone();
4482 let editor = cx
4483 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4484 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4485 editor.update(cx, |editor, cx| {
4486 editor.highlight_background::<Self>(
4487 &ranges_to_highlight,
4488 |theme| theme.editor_highlighted_line_background,
4489 cx,
4490 );
4491 });
4492 })?;
4493
4494 Ok(())
4495 }
4496
4497 pub fn clear_code_action_providers(&mut self) {
4498 self.code_action_providers.clear();
4499 self.available_code_actions.take();
4500 }
4501
4502 pub fn add_code_action_provider(
4503 &mut self,
4504 provider: Rc<dyn CodeActionProvider>,
4505 window: &mut Window,
4506 cx: &mut Context<Self>,
4507 ) {
4508 if self
4509 .code_action_providers
4510 .iter()
4511 .any(|existing_provider| existing_provider.id() == provider.id())
4512 {
4513 return;
4514 }
4515
4516 self.code_action_providers.push(provider);
4517 self.refresh_code_actions(window, cx);
4518 }
4519
4520 pub fn remove_code_action_provider(
4521 &mut self,
4522 id: Arc<str>,
4523 window: &mut Window,
4524 cx: &mut Context<Self>,
4525 ) {
4526 self.code_action_providers
4527 .retain(|provider| provider.id() != id);
4528 self.refresh_code_actions(window, cx);
4529 }
4530
4531 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4532 let buffer = self.buffer.read(cx);
4533 let newest_selection = self.selections.newest_anchor().clone();
4534 if newest_selection.head().diff_base_anchor.is_some() {
4535 return None;
4536 }
4537 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4538 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4539 if start_buffer != end_buffer {
4540 return None;
4541 }
4542
4543 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4544 cx.background_executor()
4545 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4546 .await;
4547
4548 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4549 let providers = this.code_action_providers.clone();
4550 let tasks = this
4551 .code_action_providers
4552 .iter()
4553 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4554 .collect::<Vec<_>>();
4555 (providers, tasks)
4556 })?;
4557
4558 let mut actions = Vec::new();
4559 for (provider, provider_actions) in
4560 providers.into_iter().zip(future::join_all(tasks).await)
4561 {
4562 if let Some(provider_actions) = provider_actions.log_err() {
4563 actions.extend(provider_actions.into_iter().map(|action| {
4564 AvailableCodeAction {
4565 excerpt_id: newest_selection.start.excerpt_id,
4566 action,
4567 provider: provider.clone(),
4568 }
4569 }));
4570 }
4571 }
4572
4573 this.update(&mut cx, |this, cx| {
4574 this.available_code_actions = if actions.is_empty() {
4575 None
4576 } else {
4577 Some((
4578 Location {
4579 buffer: start_buffer,
4580 range: start..end,
4581 },
4582 actions.into(),
4583 ))
4584 };
4585 cx.notify();
4586 })
4587 }));
4588 None
4589 }
4590
4591 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4592 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4593 self.show_git_blame_inline = false;
4594
4595 self.show_git_blame_inline_delay_task =
4596 Some(cx.spawn_in(window, |this, mut cx| async move {
4597 cx.background_executor().timer(delay).await;
4598
4599 this.update(&mut cx, |this, cx| {
4600 this.show_git_blame_inline = true;
4601 cx.notify();
4602 })
4603 .log_err();
4604 }));
4605 }
4606 }
4607
4608 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4609 if self.pending_rename.is_some() {
4610 return None;
4611 }
4612
4613 let provider = self.semantics_provider.clone()?;
4614 let buffer = self.buffer.read(cx);
4615 let newest_selection = self.selections.newest_anchor().clone();
4616 let cursor_position = newest_selection.head();
4617 let (cursor_buffer, cursor_buffer_position) =
4618 buffer.text_anchor_for_position(cursor_position, cx)?;
4619 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4620 if cursor_buffer != tail_buffer {
4621 return None;
4622 }
4623 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4624 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4625 cx.background_executor()
4626 .timer(Duration::from_millis(debounce))
4627 .await;
4628
4629 let highlights = if let Some(highlights) = cx
4630 .update(|cx| {
4631 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4632 })
4633 .ok()
4634 .flatten()
4635 {
4636 highlights.await.log_err()
4637 } else {
4638 None
4639 };
4640
4641 if let Some(highlights) = highlights {
4642 this.update(&mut cx, |this, cx| {
4643 if this.pending_rename.is_some() {
4644 return;
4645 }
4646
4647 let buffer_id = cursor_position.buffer_id;
4648 let buffer = this.buffer.read(cx);
4649 if !buffer
4650 .text_anchor_for_position(cursor_position, cx)
4651 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4652 {
4653 return;
4654 }
4655
4656 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4657 let mut write_ranges = Vec::new();
4658 let mut read_ranges = Vec::new();
4659 for highlight in highlights {
4660 for (excerpt_id, excerpt_range) in
4661 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4662 {
4663 let start = highlight
4664 .range
4665 .start
4666 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4667 let end = highlight
4668 .range
4669 .end
4670 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4671 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4672 continue;
4673 }
4674
4675 let range = Anchor {
4676 buffer_id,
4677 excerpt_id,
4678 text_anchor: start,
4679 diff_base_anchor: None,
4680 }..Anchor {
4681 buffer_id,
4682 excerpt_id,
4683 text_anchor: end,
4684 diff_base_anchor: None,
4685 };
4686 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4687 write_ranges.push(range);
4688 } else {
4689 read_ranges.push(range);
4690 }
4691 }
4692 }
4693
4694 this.highlight_background::<DocumentHighlightRead>(
4695 &read_ranges,
4696 |theme| theme.editor_document_highlight_read_background,
4697 cx,
4698 );
4699 this.highlight_background::<DocumentHighlightWrite>(
4700 &write_ranges,
4701 |theme| theme.editor_document_highlight_write_background,
4702 cx,
4703 );
4704 cx.notify();
4705 })
4706 .log_err();
4707 }
4708 }));
4709 None
4710 }
4711
4712 pub fn refresh_selected_text_highlights(
4713 &mut self,
4714 window: &mut Window,
4715 cx: &mut Context<Editor>,
4716 ) {
4717 self.selection_highlight_task.take();
4718 if !EditorSettings::get_global(cx).selection_highlight {
4719 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4720 return;
4721 }
4722 if self.selections.count() != 1 || self.selections.line_mode {
4723 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4724 return;
4725 }
4726 let selection = self.selections.newest::<Point>(cx);
4727 if selection.is_empty() || selection.start.row != selection.end.row {
4728 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4729 return;
4730 }
4731 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4732 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4733 cx.background_executor()
4734 .timer(Duration::from_millis(debounce))
4735 .await;
4736 let Some(Some(matches_task)) = editor
4737 .update_in(&mut cx, |editor, _, cx| {
4738 if editor.selections.count() != 1 || editor.selections.line_mode {
4739 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4740 return None;
4741 }
4742 let selection = editor.selections.newest::<Point>(cx);
4743 if selection.is_empty() || selection.start.row != selection.end.row {
4744 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4745 return None;
4746 }
4747 let buffer = editor.buffer().read(cx).snapshot(cx);
4748 let query = buffer.text_for_range(selection.range()).collect::<String>();
4749 if query.trim().is_empty() {
4750 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4751 return None;
4752 }
4753 Some(cx.background_spawn(async move {
4754 let mut ranges = Vec::new();
4755 let selection_anchors = selection.range().to_anchors(&buffer);
4756 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4757 for (search_buffer, search_range, excerpt_id) in
4758 buffer.range_to_buffer_ranges(range)
4759 {
4760 ranges.extend(
4761 project::search::SearchQuery::text(
4762 query.clone(),
4763 false,
4764 false,
4765 false,
4766 Default::default(),
4767 Default::default(),
4768 None,
4769 )
4770 .unwrap()
4771 .search(search_buffer, Some(search_range.clone()))
4772 .await
4773 .into_iter()
4774 .filter_map(
4775 |match_range| {
4776 let start = search_buffer.anchor_after(
4777 search_range.start + match_range.start,
4778 );
4779 let end = search_buffer.anchor_before(
4780 search_range.start + match_range.end,
4781 );
4782 let range = Anchor::range_in_buffer(
4783 excerpt_id,
4784 search_buffer.remote_id(),
4785 start..end,
4786 );
4787 (range != selection_anchors).then_some(range)
4788 },
4789 ),
4790 );
4791 }
4792 }
4793 ranges
4794 }))
4795 })
4796 .log_err()
4797 else {
4798 return;
4799 };
4800 let matches = matches_task.await;
4801 editor
4802 .update_in(&mut cx, |editor, _, cx| {
4803 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4804 if !matches.is_empty() {
4805 editor.highlight_background::<SelectedTextHighlight>(
4806 &matches,
4807 |theme| theme.editor_document_highlight_bracket_background,
4808 cx,
4809 )
4810 }
4811 })
4812 .log_err();
4813 }));
4814 }
4815
4816 pub fn refresh_inline_completion(
4817 &mut self,
4818 debounce: bool,
4819 user_requested: bool,
4820 window: &mut Window,
4821 cx: &mut Context<Self>,
4822 ) -> Option<()> {
4823 let provider = self.edit_prediction_provider()?;
4824 let cursor = self.selections.newest_anchor().head();
4825 let (buffer, cursor_buffer_position) =
4826 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4827
4828 if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4829 self.discard_inline_completion(false, cx);
4830 return None;
4831 }
4832
4833 if !user_requested
4834 && (!self.should_show_edit_predictions()
4835 || !self.is_focused(window)
4836 || buffer.read(cx).is_empty())
4837 {
4838 self.discard_inline_completion(false, cx);
4839 return None;
4840 }
4841
4842 self.update_visible_inline_completion(window, cx);
4843 provider.refresh(
4844 self.project.clone(),
4845 buffer,
4846 cursor_buffer_position,
4847 debounce,
4848 cx,
4849 );
4850 Some(())
4851 }
4852
4853 fn show_edit_predictions_in_menu(&self) -> bool {
4854 match self.edit_prediction_settings {
4855 EditPredictionSettings::Disabled => false,
4856 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4857 }
4858 }
4859
4860 pub fn edit_predictions_enabled(&self) -> bool {
4861 match self.edit_prediction_settings {
4862 EditPredictionSettings::Disabled => false,
4863 EditPredictionSettings::Enabled { .. } => true,
4864 }
4865 }
4866
4867 fn edit_prediction_requires_modifier(&self) -> bool {
4868 match self.edit_prediction_settings {
4869 EditPredictionSettings::Disabled => false,
4870 EditPredictionSettings::Enabled {
4871 preview_requires_modifier,
4872 ..
4873 } => preview_requires_modifier,
4874 }
4875 }
4876
4877 fn edit_prediction_settings_at_position(
4878 &self,
4879 buffer: &Entity<Buffer>,
4880 buffer_position: language::Anchor,
4881 cx: &App,
4882 ) -> EditPredictionSettings {
4883 if self.mode != EditorMode::Full
4884 || !self.show_inline_completions_override.unwrap_or(true)
4885 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
4886 {
4887 return EditPredictionSettings::Disabled;
4888 }
4889
4890 let buffer = buffer.read(cx);
4891
4892 let file = buffer.file();
4893
4894 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
4895 return EditPredictionSettings::Disabled;
4896 };
4897
4898 let by_provider = matches!(
4899 self.menu_inline_completions_policy,
4900 MenuInlineCompletionsPolicy::ByProvider
4901 );
4902
4903 let show_in_menu = by_provider
4904 && self
4905 .edit_prediction_provider
4906 .as_ref()
4907 .map_or(false, |provider| {
4908 provider.provider.show_completions_in_menu()
4909 });
4910
4911 let preview_requires_modifier =
4912 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Auto;
4913
4914 EditPredictionSettings::Enabled {
4915 show_in_menu,
4916 preview_requires_modifier,
4917 }
4918 }
4919
4920 fn should_show_edit_predictions(&self) -> bool {
4921 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
4922 }
4923
4924 pub fn edit_prediction_preview_is_active(&self) -> bool {
4925 matches!(
4926 self.edit_prediction_preview,
4927 EditPredictionPreview::Active { .. }
4928 )
4929 }
4930
4931 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
4932 let cursor = self.selections.newest_anchor().head();
4933 if let Some((buffer, cursor_position)) =
4934 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4935 {
4936 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
4937 } else {
4938 false
4939 }
4940 }
4941
4942 fn inline_completions_enabled_in_buffer(
4943 &self,
4944 buffer: &Entity<Buffer>,
4945 buffer_position: language::Anchor,
4946 cx: &App,
4947 ) -> bool {
4948 maybe!({
4949 let provider = self.edit_prediction_provider()?;
4950 if !provider.is_enabled(&buffer, buffer_position, cx) {
4951 return Some(false);
4952 }
4953 let buffer = buffer.read(cx);
4954 let Some(file) = buffer.file() else {
4955 return Some(true);
4956 };
4957 let settings = all_language_settings(Some(file), cx);
4958 Some(settings.inline_completions_enabled_for_path(file.path()))
4959 })
4960 .unwrap_or(false)
4961 }
4962
4963 fn cycle_inline_completion(
4964 &mut self,
4965 direction: Direction,
4966 window: &mut Window,
4967 cx: &mut Context<Self>,
4968 ) -> Option<()> {
4969 let provider = self.edit_prediction_provider()?;
4970 let cursor = self.selections.newest_anchor().head();
4971 let (buffer, cursor_buffer_position) =
4972 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4973 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
4974 return None;
4975 }
4976
4977 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4978 self.update_visible_inline_completion(window, cx);
4979
4980 Some(())
4981 }
4982
4983 pub fn show_inline_completion(
4984 &mut self,
4985 _: &ShowEditPrediction,
4986 window: &mut Window,
4987 cx: &mut Context<Self>,
4988 ) {
4989 if !self.has_active_inline_completion() {
4990 self.refresh_inline_completion(false, true, window, cx);
4991 return;
4992 }
4993
4994 self.update_visible_inline_completion(window, cx);
4995 }
4996
4997 pub fn display_cursor_names(
4998 &mut self,
4999 _: &DisplayCursorNames,
5000 window: &mut Window,
5001 cx: &mut Context<Self>,
5002 ) {
5003 self.show_cursor_names(window, cx);
5004 }
5005
5006 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5007 self.show_cursor_names = true;
5008 cx.notify();
5009 cx.spawn_in(window, |this, mut cx| async move {
5010 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5011 this.update(&mut cx, |this, cx| {
5012 this.show_cursor_names = false;
5013 cx.notify()
5014 })
5015 .ok()
5016 })
5017 .detach();
5018 }
5019
5020 pub fn next_edit_prediction(
5021 &mut self,
5022 _: &NextEditPrediction,
5023 window: &mut Window,
5024 cx: &mut Context<Self>,
5025 ) {
5026 if self.has_active_inline_completion() {
5027 self.cycle_inline_completion(Direction::Next, window, cx);
5028 } else {
5029 let is_copilot_disabled = self
5030 .refresh_inline_completion(false, true, window, cx)
5031 .is_none();
5032 if is_copilot_disabled {
5033 cx.propagate();
5034 }
5035 }
5036 }
5037
5038 pub fn previous_edit_prediction(
5039 &mut self,
5040 _: &PreviousEditPrediction,
5041 window: &mut Window,
5042 cx: &mut Context<Self>,
5043 ) {
5044 if self.has_active_inline_completion() {
5045 self.cycle_inline_completion(Direction::Prev, window, cx);
5046 } else {
5047 let is_copilot_disabled = self
5048 .refresh_inline_completion(false, true, window, cx)
5049 .is_none();
5050 if is_copilot_disabled {
5051 cx.propagate();
5052 }
5053 }
5054 }
5055
5056 pub fn accept_edit_prediction(
5057 &mut self,
5058 _: &AcceptEditPrediction,
5059 window: &mut Window,
5060 cx: &mut Context<Self>,
5061 ) {
5062 if self.show_edit_predictions_in_menu() {
5063 self.hide_context_menu(window, cx);
5064 }
5065
5066 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5067 return;
5068 };
5069
5070 self.report_inline_completion_event(
5071 active_inline_completion.completion_id.clone(),
5072 true,
5073 cx,
5074 );
5075
5076 match &active_inline_completion.completion {
5077 InlineCompletion::Move { target, .. } => {
5078 let target = *target;
5079
5080 if let Some(position_map) = &self.last_position_map {
5081 if position_map
5082 .visible_row_range
5083 .contains(&target.to_display_point(&position_map.snapshot).row())
5084 || !self.edit_prediction_requires_modifier()
5085 {
5086 self.unfold_ranges(&[target..target], true, false, cx);
5087 // Note that this is also done in vim's handler of the Tab action.
5088 self.change_selections(
5089 Some(Autoscroll::newest()),
5090 window,
5091 cx,
5092 |selections| {
5093 selections.select_anchor_ranges([target..target]);
5094 },
5095 );
5096 self.clear_row_highlights::<EditPredictionPreview>();
5097
5098 self.edit_prediction_preview = EditPredictionPreview::Active {
5099 previous_scroll_position: None,
5100 };
5101 } else {
5102 self.edit_prediction_preview = EditPredictionPreview::Active {
5103 previous_scroll_position: Some(position_map.snapshot.scroll_anchor),
5104 };
5105 self.highlight_rows::<EditPredictionPreview>(
5106 target..target,
5107 cx.theme().colors().editor_highlighted_line_background,
5108 true,
5109 cx,
5110 );
5111 self.request_autoscroll(Autoscroll::fit(), cx);
5112 }
5113 }
5114 }
5115 InlineCompletion::Edit { edits, .. } => {
5116 if let Some(provider) = self.edit_prediction_provider() {
5117 provider.accept(cx);
5118 }
5119
5120 let snapshot = self.buffer.read(cx).snapshot(cx);
5121 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5122
5123 self.buffer.update(cx, |buffer, cx| {
5124 buffer.edit(edits.iter().cloned(), None, cx)
5125 });
5126
5127 self.change_selections(None, window, cx, |s| {
5128 s.select_anchor_ranges([last_edit_end..last_edit_end])
5129 });
5130
5131 self.update_visible_inline_completion(window, cx);
5132 if self.active_inline_completion.is_none() {
5133 self.refresh_inline_completion(true, true, window, cx);
5134 }
5135
5136 cx.notify();
5137 }
5138 }
5139
5140 self.edit_prediction_requires_modifier_in_leading_space = false;
5141 }
5142
5143 pub fn accept_partial_inline_completion(
5144 &mut self,
5145 _: &AcceptPartialEditPrediction,
5146 window: &mut Window,
5147 cx: &mut Context<Self>,
5148 ) {
5149 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5150 return;
5151 };
5152 if self.selections.count() != 1 {
5153 return;
5154 }
5155
5156 self.report_inline_completion_event(
5157 active_inline_completion.completion_id.clone(),
5158 true,
5159 cx,
5160 );
5161
5162 match &active_inline_completion.completion {
5163 InlineCompletion::Move { target, .. } => {
5164 let target = *target;
5165 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5166 selections.select_anchor_ranges([target..target]);
5167 });
5168 }
5169 InlineCompletion::Edit { edits, .. } => {
5170 // Find an insertion that starts at the cursor position.
5171 let snapshot = self.buffer.read(cx).snapshot(cx);
5172 let cursor_offset = self.selections.newest::<usize>(cx).head();
5173 let insertion = edits.iter().find_map(|(range, text)| {
5174 let range = range.to_offset(&snapshot);
5175 if range.is_empty() && range.start == cursor_offset {
5176 Some(text)
5177 } else {
5178 None
5179 }
5180 });
5181
5182 if let Some(text) = insertion {
5183 let mut partial_completion = text
5184 .chars()
5185 .by_ref()
5186 .take_while(|c| c.is_alphabetic())
5187 .collect::<String>();
5188 if partial_completion.is_empty() {
5189 partial_completion = text
5190 .chars()
5191 .by_ref()
5192 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5193 .collect::<String>();
5194 }
5195
5196 cx.emit(EditorEvent::InputHandled {
5197 utf16_range_to_replace: None,
5198 text: partial_completion.clone().into(),
5199 });
5200
5201 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5202
5203 self.refresh_inline_completion(true, true, window, cx);
5204 cx.notify();
5205 } else {
5206 self.accept_edit_prediction(&Default::default(), window, cx);
5207 }
5208 }
5209 }
5210 }
5211
5212 fn discard_inline_completion(
5213 &mut self,
5214 should_report_inline_completion_event: bool,
5215 cx: &mut Context<Self>,
5216 ) -> bool {
5217 if should_report_inline_completion_event {
5218 let completion_id = self
5219 .active_inline_completion
5220 .as_ref()
5221 .and_then(|active_completion| active_completion.completion_id.clone());
5222
5223 self.report_inline_completion_event(completion_id, false, cx);
5224 }
5225
5226 if let Some(provider) = self.edit_prediction_provider() {
5227 provider.discard(cx);
5228 }
5229
5230 self.take_active_inline_completion(cx)
5231 }
5232
5233 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5234 let Some(provider) = self.edit_prediction_provider() else {
5235 return;
5236 };
5237
5238 let Some((_, buffer, _)) = self
5239 .buffer
5240 .read(cx)
5241 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5242 else {
5243 return;
5244 };
5245
5246 let extension = buffer
5247 .read(cx)
5248 .file()
5249 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5250
5251 let event_type = match accepted {
5252 true => "Edit Prediction Accepted",
5253 false => "Edit Prediction Discarded",
5254 };
5255 telemetry::event!(
5256 event_type,
5257 provider = provider.name(),
5258 prediction_id = id,
5259 suggestion_accepted = accepted,
5260 file_extension = extension,
5261 );
5262 }
5263
5264 pub fn has_active_inline_completion(&self) -> bool {
5265 self.active_inline_completion.is_some()
5266 }
5267
5268 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5269 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5270 return false;
5271 };
5272
5273 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5274 self.clear_highlights::<InlineCompletionHighlight>(cx);
5275 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5276 true
5277 }
5278
5279 /// Returns true when we're displaying the edit prediction popover below the cursor
5280 /// like we are not previewing and the LSP autocomplete menu is visible
5281 /// or we are in `when_holding_modifier` mode.
5282 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5283 if self.edit_prediction_preview_is_active()
5284 || !self.show_edit_predictions_in_menu()
5285 || !self.edit_predictions_enabled()
5286 {
5287 return false;
5288 }
5289
5290 if self.has_visible_completions_menu() {
5291 return true;
5292 }
5293
5294 has_completion && self.edit_prediction_requires_modifier()
5295 }
5296
5297 fn handle_modifiers_changed(
5298 &mut self,
5299 modifiers: Modifiers,
5300 position_map: &PositionMap,
5301 window: &mut Window,
5302 cx: &mut Context<Self>,
5303 ) {
5304 if self.show_edit_predictions_in_menu() {
5305 self.update_edit_prediction_preview(&modifiers, window, cx);
5306 }
5307
5308 self.update_selection_mode(&modifiers, position_map, window, cx);
5309
5310 let mouse_position = window.mouse_position();
5311 if !position_map.text_hitbox.is_hovered(window) {
5312 return;
5313 }
5314
5315 self.update_hovered_link(
5316 position_map.point_for_position(mouse_position),
5317 &position_map.snapshot,
5318 modifiers,
5319 window,
5320 cx,
5321 )
5322 }
5323
5324 fn update_selection_mode(
5325 &mut self,
5326 modifiers: &Modifiers,
5327 position_map: &PositionMap,
5328 window: &mut Window,
5329 cx: &mut Context<Self>,
5330 ) {
5331 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5332 return;
5333 }
5334
5335 let mouse_position = window.mouse_position();
5336 let point_for_position = position_map.point_for_position(mouse_position);
5337 let position = point_for_position.previous_valid;
5338
5339 self.select(
5340 SelectPhase::BeginColumnar {
5341 position,
5342 reset: false,
5343 goal_column: point_for_position.exact_unclipped.column(),
5344 },
5345 window,
5346 cx,
5347 );
5348 }
5349
5350 fn update_edit_prediction_preview(
5351 &mut self,
5352 modifiers: &Modifiers,
5353 window: &mut Window,
5354 cx: &mut Context<Self>,
5355 ) {
5356 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5357 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5358 return;
5359 };
5360
5361 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5362 if matches!(
5363 self.edit_prediction_preview,
5364 EditPredictionPreview::Inactive
5365 ) {
5366 self.edit_prediction_preview = EditPredictionPreview::Active {
5367 previous_scroll_position: None,
5368 };
5369
5370 self.update_visible_inline_completion(window, cx);
5371 cx.notify();
5372 }
5373 } else if let EditPredictionPreview::Active {
5374 previous_scroll_position,
5375 } = self.edit_prediction_preview
5376 {
5377 if let (Some(previous_scroll_position), Some(position_map)) =
5378 (previous_scroll_position, self.last_position_map.as_ref())
5379 {
5380 self.set_scroll_position(
5381 previous_scroll_position
5382 .scroll_position(&position_map.snapshot.display_snapshot),
5383 window,
5384 cx,
5385 );
5386 }
5387
5388 self.edit_prediction_preview = EditPredictionPreview::Inactive;
5389 self.clear_row_highlights::<EditPredictionPreview>();
5390 self.update_visible_inline_completion(window, cx);
5391 cx.notify();
5392 }
5393 }
5394
5395 fn update_visible_inline_completion(
5396 &mut self,
5397 _window: &mut Window,
5398 cx: &mut Context<Self>,
5399 ) -> Option<()> {
5400 let selection = self.selections.newest_anchor();
5401 let cursor = selection.head();
5402 let multibuffer = self.buffer.read(cx).snapshot(cx);
5403 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5404 let excerpt_id = cursor.excerpt_id;
5405
5406 let show_in_menu = self.show_edit_predictions_in_menu();
5407 let completions_menu_has_precedence = !show_in_menu
5408 && (self.context_menu.borrow().is_some()
5409 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5410
5411 if completions_menu_has_precedence
5412 || !offset_selection.is_empty()
5413 || self
5414 .active_inline_completion
5415 .as_ref()
5416 .map_or(false, |completion| {
5417 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5418 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5419 !invalidation_range.contains(&offset_selection.head())
5420 })
5421 {
5422 self.discard_inline_completion(false, cx);
5423 return None;
5424 }
5425
5426 self.take_active_inline_completion(cx);
5427 let Some(provider) = self.edit_prediction_provider() else {
5428 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5429 return None;
5430 };
5431
5432 let (buffer, cursor_buffer_position) =
5433 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5434
5435 self.edit_prediction_settings =
5436 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5437
5438 self.edit_prediction_cursor_on_leading_whitespace =
5439 multibuffer.is_line_whitespace_upto(cursor);
5440
5441 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5442 let edits = inline_completion
5443 .edits
5444 .into_iter()
5445 .flat_map(|(range, new_text)| {
5446 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5447 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5448 Some((start..end, new_text))
5449 })
5450 .collect::<Vec<_>>();
5451 if edits.is_empty() {
5452 return None;
5453 }
5454
5455 let first_edit_start = edits.first().unwrap().0.start;
5456 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5457 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5458
5459 let last_edit_end = edits.last().unwrap().0.end;
5460 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5461 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5462
5463 let cursor_row = cursor.to_point(&multibuffer).row;
5464
5465 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5466
5467 let mut inlay_ids = Vec::new();
5468 let invalidation_row_range;
5469 let move_invalidation_row_range = if cursor_row < edit_start_row {
5470 Some(cursor_row..edit_end_row)
5471 } else if cursor_row > edit_end_row {
5472 Some(edit_start_row..cursor_row)
5473 } else {
5474 None
5475 };
5476 let is_move =
5477 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5478 let completion = if is_move {
5479 invalidation_row_range =
5480 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5481 let target = first_edit_start;
5482 InlineCompletion::Move { target, snapshot }
5483 } else {
5484 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5485 && !self.inline_completions_hidden_for_vim_mode;
5486
5487 if show_completions_in_buffer {
5488 if edits
5489 .iter()
5490 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5491 {
5492 let mut inlays = Vec::new();
5493 for (range, new_text) in &edits {
5494 let inlay = Inlay::inline_completion(
5495 post_inc(&mut self.next_inlay_id),
5496 range.start,
5497 new_text.as_str(),
5498 );
5499 inlay_ids.push(inlay.id);
5500 inlays.push(inlay);
5501 }
5502
5503 self.splice_inlays(&[], inlays, cx);
5504 } else {
5505 let background_color = cx.theme().status().deleted_background;
5506 self.highlight_text::<InlineCompletionHighlight>(
5507 edits.iter().map(|(range, _)| range.clone()).collect(),
5508 HighlightStyle {
5509 background_color: Some(background_color),
5510 ..Default::default()
5511 },
5512 cx,
5513 );
5514 }
5515 }
5516
5517 invalidation_row_range = edit_start_row..edit_end_row;
5518
5519 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5520 if provider.show_tab_accept_marker() {
5521 EditDisplayMode::TabAccept
5522 } else {
5523 EditDisplayMode::Inline
5524 }
5525 } else {
5526 EditDisplayMode::DiffPopover
5527 };
5528
5529 InlineCompletion::Edit {
5530 edits,
5531 edit_preview: inline_completion.edit_preview,
5532 display_mode,
5533 snapshot,
5534 }
5535 };
5536
5537 let invalidation_range = multibuffer
5538 .anchor_before(Point::new(invalidation_row_range.start, 0))
5539 ..multibuffer.anchor_after(Point::new(
5540 invalidation_row_range.end,
5541 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5542 ));
5543
5544 self.stale_inline_completion_in_menu = None;
5545 self.active_inline_completion = Some(InlineCompletionState {
5546 inlay_ids,
5547 completion,
5548 completion_id: inline_completion.id,
5549 invalidation_range,
5550 });
5551
5552 cx.notify();
5553
5554 Some(())
5555 }
5556
5557 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5558 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5559 }
5560
5561 fn render_code_actions_indicator(
5562 &self,
5563 _style: &EditorStyle,
5564 row: DisplayRow,
5565 is_active: bool,
5566 cx: &mut Context<Self>,
5567 ) -> Option<IconButton> {
5568 if self.available_code_actions.is_some() {
5569 Some(
5570 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5571 .shape(ui::IconButtonShape::Square)
5572 .icon_size(IconSize::XSmall)
5573 .icon_color(Color::Muted)
5574 .toggle_state(is_active)
5575 .tooltip({
5576 let focus_handle = self.focus_handle.clone();
5577 move |window, cx| {
5578 Tooltip::for_action_in(
5579 "Toggle Code Actions",
5580 &ToggleCodeActions {
5581 deployed_from_indicator: None,
5582 },
5583 &focus_handle,
5584 window,
5585 cx,
5586 )
5587 }
5588 })
5589 .on_click(cx.listener(move |editor, _e, window, cx| {
5590 window.focus(&editor.focus_handle(cx));
5591 editor.toggle_code_actions(
5592 &ToggleCodeActions {
5593 deployed_from_indicator: Some(row),
5594 },
5595 window,
5596 cx,
5597 );
5598 })),
5599 )
5600 } else {
5601 None
5602 }
5603 }
5604
5605 fn clear_tasks(&mut self) {
5606 self.tasks.clear()
5607 }
5608
5609 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5610 if self.tasks.insert(key, value).is_some() {
5611 // This case should hopefully be rare, but just in case...
5612 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5613 }
5614 }
5615
5616 fn build_tasks_context(
5617 project: &Entity<Project>,
5618 buffer: &Entity<Buffer>,
5619 buffer_row: u32,
5620 tasks: &Arc<RunnableTasks>,
5621 cx: &mut Context<Self>,
5622 ) -> Task<Option<task::TaskContext>> {
5623 let position = Point::new(buffer_row, tasks.column);
5624 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5625 let location = Location {
5626 buffer: buffer.clone(),
5627 range: range_start..range_start,
5628 };
5629 // Fill in the environmental variables from the tree-sitter captures
5630 let mut captured_task_variables = TaskVariables::default();
5631 for (capture_name, value) in tasks.extra_variables.clone() {
5632 captured_task_variables.insert(
5633 task::VariableName::Custom(capture_name.into()),
5634 value.clone(),
5635 );
5636 }
5637 project.update(cx, |project, cx| {
5638 project.task_store().update(cx, |task_store, cx| {
5639 task_store.task_context_for_location(captured_task_variables, location, cx)
5640 })
5641 })
5642 }
5643
5644 pub fn spawn_nearest_task(
5645 &mut self,
5646 action: &SpawnNearestTask,
5647 window: &mut Window,
5648 cx: &mut Context<Self>,
5649 ) {
5650 let Some((workspace, _)) = self.workspace.clone() else {
5651 return;
5652 };
5653 let Some(project) = self.project.clone() else {
5654 return;
5655 };
5656
5657 // Try to find a closest, enclosing node using tree-sitter that has a
5658 // task
5659 let Some((buffer, buffer_row, tasks)) = self
5660 .find_enclosing_node_task(cx)
5661 // Or find the task that's closest in row-distance.
5662 .or_else(|| self.find_closest_task(cx))
5663 else {
5664 return;
5665 };
5666
5667 let reveal_strategy = action.reveal;
5668 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5669 cx.spawn_in(window, |_, mut cx| async move {
5670 let context = task_context.await?;
5671 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5672
5673 let resolved = resolved_task.resolved.as_mut()?;
5674 resolved.reveal = reveal_strategy;
5675
5676 workspace
5677 .update(&mut cx, |workspace, cx| {
5678 workspace::tasks::schedule_resolved_task(
5679 workspace,
5680 task_source_kind,
5681 resolved_task,
5682 false,
5683 cx,
5684 );
5685 })
5686 .ok()
5687 })
5688 .detach();
5689 }
5690
5691 fn find_closest_task(
5692 &mut self,
5693 cx: &mut Context<Self>,
5694 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5695 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5696
5697 let ((buffer_id, row), tasks) = self
5698 .tasks
5699 .iter()
5700 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5701
5702 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5703 let tasks = Arc::new(tasks.to_owned());
5704 Some((buffer, *row, tasks))
5705 }
5706
5707 fn find_enclosing_node_task(
5708 &mut self,
5709 cx: &mut Context<Self>,
5710 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5711 let snapshot = self.buffer.read(cx).snapshot(cx);
5712 let offset = self.selections.newest::<usize>(cx).head();
5713 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5714 let buffer_id = excerpt.buffer().remote_id();
5715
5716 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5717 let mut cursor = layer.node().walk();
5718
5719 while cursor.goto_first_child_for_byte(offset).is_some() {
5720 if cursor.node().end_byte() == offset {
5721 cursor.goto_next_sibling();
5722 }
5723 }
5724
5725 // Ascend to the smallest ancestor that contains the range and has a task.
5726 loop {
5727 let node = cursor.node();
5728 let node_range = node.byte_range();
5729 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5730
5731 // Check if this node contains our offset
5732 if node_range.start <= offset && node_range.end >= offset {
5733 // If it contains offset, check for task
5734 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5735 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5736 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5737 }
5738 }
5739
5740 if !cursor.goto_parent() {
5741 break;
5742 }
5743 }
5744 None
5745 }
5746
5747 fn render_run_indicator(
5748 &self,
5749 _style: &EditorStyle,
5750 is_active: bool,
5751 row: DisplayRow,
5752 cx: &mut Context<Self>,
5753 ) -> IconButton {
5754 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5755 .shape(ui::IconButtonShape::Square)
5756 .icon_size(IconSize::XSmall)
5757 .icon_color(Color::Muted)
5758 .toggle_state(is_active)
5759 .on_click(cx.listener(move |editor, _e, window, cx| {
5760 window.focus(&editor.focus_handle(cx));
5761 editor.toggle_code_actions(
5762 &ToggleCodeActions {
5763 deployed_from_indicator: Some(row),
5764 },
5765 window,
5766 cx,
5767 );
5768 }))
5769 }
5770
5771 pub fn context_menu_visible(&self) -> bool {
5772 !self.edit_prediction_preview_is_active()
5773 && self
5774 .context_menu
5775 .borrow()
5776 .as_ref()
5777 .map_or(false, |menu| menu.visible())
5778 }
5779
5780 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5781 self.context_menu
5782 .borrow()
5783 .as_ref()
5784 .map(|menu| menu.origin())
5785 }
5786
5787 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5788 px(30.)
5789 }
5790
5791 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5792 if self.read_only(cx) {
5793 cx.theme().players().read_only()
5794 } else {
5795 self.style.as_ref().unwrap().local_player
5796 }
5797 }
5798
5799 fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
5800 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
5801 let accept_keystroke = accept_binding.keystroke()?;
5802
5803 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
5804
5805 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
5806 Color::Accent
5807 } else {
5808 Color::Muted
5809 };
5810
5811 h_flex()
5812 .px_0p5()
5813 .when(is_platform_style_mac, |parent| parent.gap_0p5())
5814 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
5815 .text_size(TextSize::XSmall.rems(cx))
5816 .child(h_flex().children(ui::render_modifiers(
5817 &accept_keystroke.modifiers,
5818 PlatformStyle::platform(),
5819 Some(modifiers_color),
5820 Some(IconSize::XSmall.rems().into()),
5821 true,
5822 )))
5823 .when(is_platform_style_mac, |parent| {
5824 parent.child(accept_keystroke.key.clone())
5825 })
5826 .when(!is_platform_style_mac, |parent| {
5827 parent.child(
5828 Key::new(
5829 util::capitalize(&accept_keystroke.key),
5830 Some(Color::Default),
5831 )
5832 .size(Some(IconSize::XSmall.rems().into())),
5833 )
5834 })
5835 .into()
5836 }
5837
5838 fn render_edit_prediction_line_popover(
5839 &self,
5840 label: impl Into<SharedString>,
5841 icon: Option<IconName>,
5842 window: &mut Window,
5843 cx: &App,
5844 ) -> Option<Div> {
5845 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
5846
5847 let result = h_flex()
5848 .py_0p5()
5849 .pl_1()
5850 .pr(padding_right)
5851 .gap_1()
5852 .rounded(px(6.))
5853 .border_1()
5854 .bg(Self::edit_prediction_line_popover_bg_color(cx))
5855 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
5856 .shadow_sm()
5857 .children(self.render_edit_prediction_accept_keybind(window, cx))
5858 .child(Label::new(label).size(LabelSize::Small))
5859 .when_some(icon, |element, icon| {
5860 element.child(
5861 div()
5862 .mt(px(1.5))
5863 .child(Icon::new(icon).size(IconSize::Small)),
5864 )
5865 });
5866
5867 Some(result)
5868 }
5869
5870 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
5871 let accent_color = cx.theme().colors().text_accent;
5872 let editor_bg_color = cx.theme().colors().editor_background;
5873 editor_bg_color.blend(accent_color.opacity(0.1))
5874 }
5875
5876 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
5877 let accent_color = cx.theme().colors().text_accent;
5878 let editor_bg_color = cx.theme().colors().editor_background;
5879 editor_bg_color.blend(accent_color.opacity(0.6))
5880 }
5881
5882 #[allow(clippy::too_many_arguments)]
5883 fn render_edit_prediction_cursor_popover(
5884 &self,
5885 min_width: Pixels,
5886 max_width: Pixels,
5887 cursor_point: Point,
5888 style: &EditorStyle,
5889 accept_keystroke: Option<&gpui::Keystroke>,
5890 _window: &Window,
5891 cx: &mut Context<Editor>,
5892 ) -> Option<AnyElement> {
5893 let provider = self.edit_prediction_provider.as_ref()?;
5894
5895 if provider.provider.needs_terms_acceptance(cx) {
5896 return Some(
5897 h_flex()
5898 .min_w(min_width)
5899 .flex_1()
5900 .px_2()
5901 .py_1()
5902 .gap_3()
5903 .elevation_2(cx)
5904 .hover(|style| style.bg(cx.theme().colors().element_hover))
5905 .id("accept-terms")
5906 .cursor_pointer()
5907 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5908 .on_click(cx.listener(|this, _event, window, cx| {
5909 cx.stop_propagation();
5910 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5911 window.dispatch_action(
5912 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5913 cx,
5914 );
5915 }))
5916 .child(
5917 h_flex()
5918 .flex_1()
5919 .gap_2()
5920 .child(Icon::new(IconName::ZedPredict))
5921 .child(Label::new("Accept Terms of Service"))
5922 .child(div().w_full())
5923 .child(
5924 Icon::new(IconName::ArrowUpRight)
5925 .color(Color::Muted)
5926 .size(IconSize::Small),
5927 )
5928 .into_any_element(),
5929 )
5930 .into_any(),
5931 );
5932 }
5933
5934 let is_refreshing = provider.provider.is_refreshing(cx);
5935
5936 fn pending_completion_container() -> Div {
5937 h_flex()
5938 .h_full()
5939 .flex_1()
5940 .gap_2()
5941 .child(Icon::new(IconName::ZedPredict))
5942 }
5943
5944 let completion = match &self.active_inline_completion {
5945 Some(completion) => match &completion.completion {
5946 InlineCompletion::Move {
5947 target, snapshot, ..
5948 } if !self.has_visible_completions_menu() => {
5949 use text::ToPoint as _;
5950
5951 return Some(
5952 h_flex()
5953 .px_2()
5954 .py_1()
5955 .gap_2()
5956 .elevation_2(cx)
5957 .border_color(cx.theme().colors().border)
5958 .rounded(px(6.))
5959 .rounded_tl(px(0.))
5960 .child(
5961 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
5962 Icon::new(IconName::ZedPredictDown)
5963 } else {
5964 Icon::new(IconName::ZedPredictUp)
5965 },
5966 )
5967 .child(Label::new("Hold").size(LabelSize::Small))
5968 .child(h_flex().children(ui::render_modifiers(
5969 &accept_keystroke?.modifiers,
5970 PlatformStyle::platform(),
5971 Some(Color::Default),
5972 Some(IconSize::Small.rems().into()),
5973 false,
5974 )))
5975 .into_any(),
5976 );
5977 }
5978 _ => self.render_edit_prediction_cursor_popover_preview(
5979 completion,
5980 cursor_point,
5981 style,
5982 cx,
5983 )?,
5984 },
5985
5986 None if is_refreshing => match &self.stale_inline_completion_in_menu {
5987 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
5988 stale_completion,
5989 cursor_point,
5990 style,
5991 cx,
5992 )?,
5993
5994 None => {
5995 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
5996 }
5997 },
5998
5999 None => pending_completion_container().child(Label::new("No Prediction")),
6000 };
6001
6002 let completion = if is_refreshing {
6003 completion
6004 .with_animation(
6005 "loading-completion",
6006 Animation::new(Duration::from_secs(2))
6007 .repeat()
6008 .with_easing(pulsating_between(0.4, 0.8)),
6009 |label, delta| label.opacity(delta),
6010 )
6011 .into_any_element()
6012 } else {
6013 completion.into_any_element()
6014 };
6015
6016 let has_completion = self.active_inline_completion.is_some();
6017
6018 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6019 Some(
6020 h_flex()
6021 .min_w(min_width)
6022 .max_w(max_width)
6023 .flex_1()
6024 .elevation_2(cx)
6025 .border_color(cx.theme().colors().border)
6026 .child(
6027 div()
6028 .flex_1()
6029 .py_1()
6030 .px_2()
6031 .overflow_hidden()
6032 .child(completion),
6033 )
6034 .when_some(accept_keystroke, |el, accept_keystroke| {
6035 if !accept_keystroke.modifiers.modified() {
6036 return el;
6037 }
6038
6039 el.child(
6040 h_flex()
6041 .h_full()
6042 .border_l_1()
6043 .rounded_r_lg()
6044 .border_color(cx.theme().colors().border)
6045 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6046 .gap_1()
6047 .py_1()
6048 .px_2()
6049 .child(
6050 h_flex()
6051 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6052 .when(is_platform_style_mac, |parent| parent.gap_1())
6053 .child(h_flex().children(ui::render_modifiers(
6054 &accept_keystroke.modifiers,
6055 PlatformStyle::platform(),
6056 Some(if !has_completion {
6057 Color::Muted
6058 } else {
6059 Color::Default
6060 }),
6061 None,
6062 false,
6063 ))),
6064 )
6065 .child(Label::new("Preview").into_any_element())
6066 .opacity(if has_completion { 1.0 } else { 0.4 }),
6067 )
6068 })
6069 .into_any(),
6070 )
6071 }
6072
6073 fn render_edit_prediction_cursor_popover_preview(
6074 &self,
6075 completion: &InlineCompletionState,
6076 cursor_point: Point,
6077 style: &EditorStyle,
6078 cx: &mut Context<Editor>,
6079 ) -> Option<Div> {
6080 use text::ToPoint as _;
6081
6082 fn render_relative_row_jump(
6083 prefix: impl Into<String>,
6084 current_row: u32,
6085 target_row: u32,
6086 ) -> Div {
6087 let (row_diff, arrow) = if target_row < current_row {
6088 (current_row - target_row, IconName::ArrowUp)
6089 } else {
6090 (target_row - current_row, IconName::ArrowDown)
6091 };
6092
6093 h_flex()
6094 .child(
6095 Label::new(format!("{}{}", prefix.into(), row_diff))
6096 .color(Color::Muted)
6097 .size(LabelSize::Small),
6098 )
6099 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6100 }
6101
6102 match &completion.completion {
6103 InlineCompletion::Move {
6104 target, snapshot, ..
6105 } => Some(
6106 h_flex()
6107 .px_2()
6108 .gap_2()
6109 .flex_1()
6110 .child(
6111 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6112 Icon::new(IconName::ZedPredictDown)
6113 } else {
6114 Icon::new(IconName::ZedPredictUp)
6115 },
6116 )
6117 .child(Label::new("Jump to Edit")),
6118 ),
6119
6120 InlineCompletion::Edit {
6121 edits,
6122 edit_preview,
6123 snapshot,
6124 display_mode: _,
6125 } => {
6126 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6127
6128 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6129 &snapshot,
6130 &edits,
6131 edit_preview.as_ref()?,
6132 true,
6133 cx,
6134 )
6135 .first_line_preview();
6136
6137 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6138 .with_highlights(&style.text, highlighted_edits.highlights);
6139
6140 let preview = h_flex()
6141 .gap_1()
6142 .min_w_16()
6143 .child(styled_text)
6144 .when(has_more_lines, |parent| parent.child("…"));
6145
6146 let left = if first_edit_row != cursor_point.row {
6147 render_relative_row_jump("", cursor_point.row, first_edit_row)
6148 .into_any_element()
6149 } else {
6150 Icon::new(IconName::ZedPredict).into_any_element()
6151 };
6152
6153 Some(
6154 h_flex()
6155 .h_full()
6156 .flex_1()
6157 .gap_2()
6158 .pr_1()
6159 .overflow_x_hidden()
6160 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6161 .child(left)
6162 .child(preview),
6163 )
6164 }
6165 }
6166 }
6167
6168 fn render_context_menu(
6169 &self,
6170 style: &EditorStyle,
6171 max_height_in_lines: u32,
6172 y_flipped: bool,
6173 window: &mut Window,
6174 cx: &mut Context<Editor>,
6175 ) -> Option<AnyElement> {
6176 let menu = self.context_menu.borrow();
6177 let menu = menu.as_ref()?;
6178 if !menu.visible() {
6179 return None;
6180 };
6181 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6182 }
6183
6184 fn render_context_menu_aside(
6185 &mut self,
6186 max_size: Size<Pixels>,
6187 window: &mut Window,
6188 cx: &mut Context<Editor>,
6189 ) -> Option<AnyElement> {
6190 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6191 if menu.visible() {
6192 menu.render_aside(self, max_size, window, cx)
6193 } else {
6194 None
6195 }
6196 })
6197 }
6198
6199 fn hide_context_menu(
6200 &mut self,
6201 window: &mut Window,
6202 cx: &mut Context<Self>,
6203 ) -> Option<CodeContextMenu> {
6204 cx.notify();
6205 self.completion_tasks.clear();
6206 let context_menu = self.context_menu.borrow_mut().take();
6207 self.stale_inline_completion_in_menu.take();
6208 self.update_visible_inline_completion(window, cx);
6209 context_menu
6210 }
6211
6212 fn show_snippet_choices(
6213 &mut self,
6214 choices: &Vec<String>,
6215 selection: Range<Anchor>,
6216 cx: &mut Context<Self>,
6217 ) {
6218 if selection.start.buffer_id.is_none() {
6219 return;
6220 }
6221 let buffer_id = selection.start.buffer_id.unwrap();
6222 let buffer = self.buffer().read(cx).buffer(buffer_id);
6223 let id = post_inc(&mut self.next_completion_id);
6224
6225 if let Some(buffer) = buffer {
6226 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6227 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6228 ));
6229 }
6230 }
6231
6232 pub fn insert_snippet(
6233 &mut self,
6234 insertion_ranges: &[Range<usize>],
6235 snippet: Snippet,
6236 window: &mut Window,
6237 cx: &mut Context<Self>,
6238 ) -> Result<()> {
6239 struct Tabstop<T> {
6240 is_end_tabstop: bool,
6241 ranges: Vec<Range<T>>,
6242 choices: Option<Vec<String>>,
6243 }
6244
6245 let tabstops = self.buffer.update(cx, |buffer, cx| {
6246 let snippet_text: Arc<str> = snippet.text.clone().into();
6247 buffer.edit(
6248 insertion_ranges
6249 .iter()
6250 .cloned()
6251 .map(|range| (range, snippet_text.clone())),
6252 Some(AutoindentMode::EachLine),
6253 cx,
6254 );
6255
6256 let snapshot = &*buffer.read(cx);
6257 let snippet = &snippet;
6258 snippet
6259 .tabstops
6260 .iter()
6261 .map(|tabstop| {
6262 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6263 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6264 });
6265 let mut tabstop_ranges = tabstop
6266 .ranges
6267 .iter()
6268 .flat_map(|tabstop_range| {
6269 let mut delta = 0_isize;
6270 insertion_ranges.iter().map(move |insertion_range| {
6271 let insertion_start = insertion_range.start as isize + delta;
6272 delta +=
6273 snippet.text.len() as isize - insertion_range.len() as isize;
6274
6275 let start = ((insertion_start + tabstop_range.start) as usize)
6276 .min(snapshot.len());
6277 let end = ((insertion_start + tabstop_range.end) as usize)
6278 .min(snapshot.len());
6279 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6280 })
6281 })
6282 .collect::<Vec<_>>();
6283 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6284
6285 Tabstop {
6286 is_end_tabstop,
6287 ranges: tabstop_ranges,
6288 choices: tabstop.choices.clone(),
6289 }
6290 })
6291 .collect::<Vec<_>>()
6292 });
6293 if let Some(tabstop) = tabstops.first() {
6294 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6295 s.select_ranges(tabstop.ranges.iter().cloned());
6296 });
6297
6298 if let Some(choices) = &tabstop.choices {
6299 if let Some(selection) = tabstop.ranges.first() {
6300 self.show_snippet_choices(choices, selection.clone(), cx)
6301 }
6302 }
6303
6304 // If we're already at the last tabstop and it's at the end of the snippet,
6305 // we're done, we don't need to keep the state around.
6306 if !tabstop.is_end_tabstop {
6307 let choices = tabstops
6308 .iter()
6309 .map(|tabstop| tabstop.choices.clone())
6310 .collect();
6311
6312 let ranges = tabstops
6313 .into_iter()
6314 .map(|tabstop| tabstop.ranges)
6315 .collect::<Vec<_>>();
6316
6317 self.snippet_stack.push(SnippetState {
6318 active_index: 0,
6319 ranges,
6320 choices,
6321 });
6322 }
6323
6324 // Check whether the just-entered snippet ends with an auto-closable bracket.
6325 if self.autoclose_regions.is_empty() {
6326 let snapshot = self.buffer.read(cx).snapshot(cx);
6327 for selection in &mut self.selections.all::<Point>(cx) {
6328 let selection_head = selection.head();
6329 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6330 continue;
6331 };
6332
6333 let mut bracket_pair = None;
6334 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6335 let prev_chars = snapshot
6336 .reversed_chars_at(selection_head)
6337 .collect::<String>();
6338 for (pair, enabled) in scope.brackets() {
6339 if enabled
6340 && pair.close
6341 && prev_chars.starts_with(pair.start.as_str())
6342 && next_chars.starts_with(pair.end.as_str())
6343 {
6344 bracket_pair = Some(pair.clone());
6345 break;
6346 }
6347 }
6348 if let Some(pair) = bracket_pair {
6349 let start = snapshot.anchor_after(selection_head);
6350 let end = snapshot.anchor_after(selection_head);
6351 self.autoclose_regions.push(AutocloseRegion {
6352 selection_id: selection.id,
6353 range: start..end,
6354 pair,
6355 });
6356 }
6357 }
6358 }
6359 }
6360 Ok(())
6361 }
6362
6363 pub fn move_to_next_snippet_tabstop(
6364 &mut self,
6365 window: &mut Window,
6366 cx: &mut Context<Self>,
6367 ) -> bool {
6368 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6369 }
6370
6371 pub fn move_to_prev_snippet_tabstop(
6372 &mut self,
6373 window: &mut Window,
6374 cx: &mut Context<Self>,
6375 ) -> bool {
6376 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6377 }
6378
6379 pub fn move_to_snippet_tabstop(
6380 &mut self,
6381 bias: Bias,
6382 window: &mut Window,
6383 cx: &mut Context<Self>,
6384 ) -> bool {
6385 if let Some(mut snippet) = self.snippet_stack.pop() {
6386 match bias {
6387 Bias::Left => {
6388 if snippet.active_index > 0 {
6389 snippet.active_index -= 1;
6390 } else {
6391 self.snippet_stack.push(snippet);
6392 return false;
6393 }
6394 }
6395 Bias::Right => {
6396 if snippet.active_index + 1 < snippet.ranges.len() {
6397 snippet.active_index += 1;
6398 } else {
6399 self.snippet_stack.push(snippet);
6400 return false;
6401 }
6402 }
6403 }
6404 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6405 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6406 s.select_anchor_ranges(current_ranges.iter().cloned())
6407 });
6408
6409 if let Some(choices) = &snippet.choices[snippet.active_index] {
6410 if let Some(selection) = current_ranges.first() {
6411 self.show_snippet_choices(&choices, selection.clone(), cx);
6412 }
6413 }
6414
6415 // If snippet state is not at the last tabstop, push it back on the stack
6416 if snippet.active_index + 1 < snippet.ranges.len() {
6417 self.snippet_stack.push(snippet);
6418 }
6419 return true;
6420 }
6421 }
6422
6423 false
6424 }
6425
6426 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6427 self.transact(window, cx, |this, window, cx| {
6428 this.select_all(&SelectAll, window, cx);
6429 this.insert("", window, cx);
6430 });
6431 }
6432
6433 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6434 self.transact(window, cx, |this, window, cx| {
6435 this.select_autoclose_pair(window, cx);
6436 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6437 if !this.linked_edit_ranges.is_empty() {
6438 let selections = this.selections.all::<MultiBufferPoint>(cx);
6439 let snapshot = this.buffer.read(cx).snapshot(cx);
6440
6441 for selection in selections.iter() {
6442 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6443 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6444 if selection_start.buffer_id != selection_end.buffer_id {
6445 continue;
6446 }
6447 if let Some(ranges) =
6448 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6449 {
6450 for (buffer, entries) in ranges {
6451 linked_ranges.entry(buffer).or_default().extend(entries);
6452 }
6453 }
6454 }
6455 }
6456
6457 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6458 if !this.selections.line_mode {
6459 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6460 for selection in &mut selections {
6461 if selection.is_empty() {
6462 let old_head = selection.head();
6463 let mut new_head =
6464 movement::left(&display_map, old_head.to_display_point(&display_map))
6465 .to_point(&display_map);
6466 if let Some((buffer, line_buffer_range)) = display_map
6467 .buffer_snapshot
6468 .buffer_line_for_row(MultiBufferRow(old_head.row))
6469 {
6470 let indent_size =
6471 buffer.indent_size_for_line(line_buffer_range.start.row);
6472 let indent_len = match indent_size.kind {
6473 IndentKind::Space => {
6474 buffer.settings_at(line_buffer_range.start, cx).tab_size
6475 }
6476 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6477 };
6478 if old_head.column <= indent_size.len && old_head.column > 0 {
6479 let indent_len = indent_len.get();
6480 new_head = cmp::min(
6481 new_head,
6482 MultiBufferPoint::new(
6483 old_head.row,
6484 ((old_head.column - 1) / indent_len) * indent_len,
6485 ),
6486 );
6487 }
6488 }
6489
6490 selection.set_head(new_head, SelectionGoal::None);
6491 }
6492 }
6493 }
6494
6495 this.signature_help_state.set_backspace_pressed(true);
6496 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6497 s.select(selections)
6498 });
6499 this.insert("", window, cx);
6500 let empty_str: Arc<str> = Arc::from("");
6501 for (buffer, edits) in linked_ranges {
6502 let snapshot = buffer.read(cx).snapshot();
6503 use text::ToPoint as TP;
6504
6505 let edits = edits
6506 .into_iter()
6507 .map(|range| {
6508 let end_point = TP::to_point(&range.end, &snapshot);
6509 let mut start_point = TP::to_point(&range.start, &snapshot);
6510
6511 if end_point == start_point {
6512 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6513 .saturating_sub(1);
6514 start_point =
6515 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6516 };
6517
6518 (start_point..end_point, empty_str.clone())
6519 })
6520 .sorted_by_key(|(range, _)| range.start)
6521 .collect::<Vec<_>>();
6522 buffer.update(cx, |this, cx| {
6523 this.edit(edits, None, cx);
6524 })
6525 }
6526 this.refresh_inline_completion(true, false, window, cx);
6527 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6528 });
6529 }
6530
6531 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6532 self.transact(window, cx, |this, window, cx| {
6533 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6534 let line_mode = s.line_mode;
6535 s.move_with(|map, selection| {
6536 if selection.is_empty() && !line_mode {
6537 let cursor = movement::right(map, selection.head());
6538 selection.end = cursor;
6539 selection.reversed = true;
6540 selection.goal = SelectionGoal::None;
6541 }
6542 })
6543 });
6544 this.insert("", window, cx);
6545 this.refresh_inline_completion(true, false, window, cx);
6546 });
6547 }
6548
6549 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6550 if self.move_to_prev_snippet_tabstop(window, cx) {
6551 return;
6552 }
6553
6554 self.outdent(&Outdent, window, cx);
6555 }
6556
6557 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6558 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6559 return;
6560 }
6561
6562 let mut selections = self.selections.all_adjusted(cx);
6563 let buffer = self.buffer.read(cx);
6564 let snapshot = buffer.snapshot(cx);
6565 let rows_iter = selections.iter().map(|s| s.head().row);
6566 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6567
6568 let mut edits = Vec::new();
6569 let mut prev_edited_row = 0;
6570 let mut row_delta = 0;
6571 for selection in &mut selections {
6572 if selection.start.row != prev_edited_row {
6573 row_delta = 0;
6574 }
6575 prev_edited_row = selection.end.row;
6576
6577 // If the selection is non-empty, then increase the indentation of the selected lines.
6578 if !selection.is_empty() {
6579 row_delta =
6580 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6581 continue;
6582 }
6583
6584 // If the selection is empty and the cursor is in the leading whitespace before the
6585 // suggested indentation, then auto-indent the line.
6586 let cursor = selection.head();
6587 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6588 if let Some(suggested_indent) =
6589 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6590 {
6591 if cursor.column < suggested_indent.len
6592 && cursor.column <= current_indent.len
6593 && current_indent.len <= suggested_indent.len
6594 {
6595 selection.start = Point::new(cursor.row, suggested_indent.len);
6596 selection.end = selection.start;
6597 if row_delta == 0 {
6598 edits.extend(Buffer::edit_for_indent_size_adjustment(
6599 cursor.row,
6600 current_indent,
6601 suggested_indent,
6602 ));
6603 row_delta = suggested_indent.len - current_indent.len;
6604 }
6605 continue;
6606 }
6607 }
6608
6609 // Otherwise, insert a hard or soft tab.
6610 let settings = buffer.settings_at(cursor, cx);
6611 let tab_size = if settings.hard_tabs {
6612 IndentSize::tab()
6613 } else {
6614 let tab_size = settings.tab_size.get();
6615 let char_column = snapshot
6616 .text_for_range(Point::new(cursor.row, 0)..cursor)
6617 .flat_map(str::chars)
6618 .count()
6619 + row_delta as usize;
6620 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6621 IndentSize::spaces(chars_to_next_tab_stop)
6622 };
6623 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6624 selection.end = selection.start;
6625 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6626 row_delta += tab_size.len;
6627 }
6628
6629 self.transact(window, cx, |this, window, cx| {
6630 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6631 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6632 s.select(selections)
6633 });
6634 this.refresh_inline_completion(true, false, window, cx);
6635 });
6636 }
6637
6638 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6639 if self.read_only(cx) {
6640 return;
6641 }
6642 let mut selections = self.selections.all::<Point>(cx);
6643 let mut prev_edited_row = 0;
6644 let mut row_delta = 0;
6645 let mut edits = Vec::new();
6646 let buffer = self.buffer.read(cx);
6647 let snapshot = buffer.snapshot(cx);
6648 for selection in &mut selections {
6649 if selection.start.row != prev_edited_row {
6650 row_delta = 0;
6651 }
6652 prev_edited_row = selection.end.row;
6653
6654 row_delta =
6655 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6656 }
6657
6658 self.transact(window, cx, |this, window, cx| {
6659 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6660 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6661 s.select(selections)
6662 });
6663 });
6664 }
6665
6666 fn indent_selection(
6667 buffer: &MultiBuffer,
6668 snapshot: &MultiBufferSnapshot,
6669 selection: &mut Selection<Point>,
6670 edits: &mut Vec<(Range<Point>, String)>,
6671 delta_for_start_row: u32,
6672 cx: &App,
6673 ) -> u32 {
6674 let settings = buffer.settings_at(selection.start, cx);
6675 let tab_size = settings.tab_size.get();
6676 let indent_kind = if settings.hard_tabs {
6677 IndentKind::Tab
6678 } else {
6679 IndentKind::Space
6680 };
6681 let mut start_row = selection.start.row;
6682 let mut end_row = selection.end.row + 1;
6683
6684 // If a selection ends at the beginning of a line, don't indent
6685 // that last line.
6686 if selection.end.column == 0 && selection.end.row > selection.start.row {
6687 end_row -= 1;
6688 }
6689
6690 // Avoid re-indenting a row that has already been indented by a
6691 // previous selection, but still update this selection's column
6692 // to reflect that indentation.
6693 if delta_for_start_row > 0 {
6694 start_row += 1;
6695 selection.start.column += delta_for_start_row;
6696 if selection.end.row == selection.start.row {
6697 selection.end.column += delta_for_start_row;
6698 }
6699 }
6700
6701 let mut delta_for_end_row = 0;
6702 let has_multiple_rows = start_row + 1 != end_row;
6703 for row in start_row..end_row {
6704 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6705 let indent_delta = match (current_indent.kind, indent_kind) {
6706 (IndentKind::Space, IndentKind::Space) => {
6707 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6708 IndentSize::spaces(columns_to_next_tab_stop)
6709 }
6710 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6711 (_, IndentKind::Tab) => IndentSize::tab(),
6712 };
6713
6714 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6715 0
6716 } else {
6717 selection.start.column
6718 };
6719 let row_start = Point::new(row, start);
6720 edits.push((
6721 row_start..row_start,
6722 indent_delta.chars().collect::<String>(),
6723 ));
6724
6725 // Update this selection's endpoints to reflect the indentation.
6726 if row == selection.start.row {
6727 selection.start.column += indent_delta.len;
6728 }
6729 if row == selection.end.row {
6730 selection.end.column += indent_delta.len;
6731 delta_for_end_row = indent_delta.len;
6732 }
6733 }
6734
6735 if selection.start.row == selection.end.row {
6736 delta_for_start_row + delta_for_end_row
6737 } else {
6738 delta_for_end_row
6739 }
6740 }
6741
6742 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6743 if self.read_only(cx) {
6744 return;
6745 }
6746 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6747 let selections = self.selections.all::<Point>(cx);
6748 let mut deletion_ranges = Vec::new();
6749 let mut last_outdent = None;
6750 {
6751 let buffer = self.buffer.read(cx);
6752 let snapshot = buffer.snapshot(cx);
6753 for selection in &selections {
6754 let settings = buffer.settings_at(selection.start, cx);
6755 let tab_size = settings.tab_size.get();
6756 let mut rows = selection.spanned_rows(false, &display_map);
6757
6758 // Avoid re-outdenting a row that has already been outdented by a
6759 // previous selection.
6760 if let Some(last_row) = last_outdent {
6761 if last_row == rows.start {
6762 rows.start = rows.start.next_row();
6763 }
6764 }
6765 let has_multiple_rows = rows.len() > 1;
6766 for row in rows.iter_rows() {
6767 let indent_size = snapshot.indent_size_for_line(row);
6768 if indent_size.len > 0 {
6769 let deletion_len = match indent_size.kind {
6770 IndentKind::Space => {
6771 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6772 if columns_to_prev_tab_stop == 0 {
6773 tab_size
6774 } else {
6775 columns_to_prev_tab_stop
6776 }
6777 }
6778 IndentKind::Tab => 1,
6779 };
6780 let start = if has_multiple_rows
6781 || deletion_len > selection.start.column
6782 || indent_size.len < selection.start.column
6783 {
6784 0
6785 } else {
6786 selection.start.column - deletion_len
6787 };
6788 deletion_ranges.push(
6789 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6790 );
6791 last_outdent = Some(row);
6792 }
6793 }
6794 }
6795 }
6796
6797 self.transact(window, cx, |this, window, cx| {
6798 this.buffer.update(cx, |buffer, cx| {
6799 let empty_str: Arc<str> = Arc::default();
6800 buffer.edit(
6801 deletion_ranges
6802 .into_iter()
6803 .map(|range| (range, empty_str.clone())),
6804 None,
6805 cx,
6806 );
6807 });
6808 let selections = this.selections.all::<usize>(cx);
6809 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6810 s.select(selections)
6811 });
6812 });
6813 }
6814
6815 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6816 if self.read_only(cx) {
6817 return;
6818 }
6819 let selections = self
6820 .selections
6821 .all::<usize>(cx)
6822 .into_iter()
6823 .map(|s| s.range());
6824
6825 self.transact(window, cx, |this, window, cx| {
6826 this.buffer.update(cx, |buffer, cx| {
6827 buffer.autoindent_ranges(selections, cx);
6828 });
6829 let selections = this.selections.all::<usize>(cx);
6830 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6831 s.select(selections)
6832 });
6833 });
6834 }
6835
6836 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6837 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6838 let selections = self.selections.all::<Point>(cx);
6839
6840 let mut new_cursors = Vec::new();
6841 let mut edit_ranges = Vec::new();
6842 let mut selections = selections.iter().peekable();
6843 while let Some(selection) = selections.next() {
6844 let mut rows = selection.spanned_rows(false, &display_map);
6845 let goal_display_column = selection.head().to_display_point(&display_map).column();
6846
6847 // Accumulate contiguous regions of rows that we want to delete.
6848 while let Some(next_selection) = selections.peek() {
6849 let next_rows = next_selection.spanned_rows(false, &display_map);
6850 if next_rows.start <= rows.end {
6851 rows.end = next_rows.end;
6852 selections.next().unwrap();
6853 } else {
6854 break;
6855 }
6856 }
6857
6858 let buffer = &display_map.buffer_snapshot;
6859 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6860 let edit_end;
6861 let cursor_buffer_row;
6862 if buffer.max_point().row >= rows.end.0 {
6863 // If there's a line after the range, delete the \n from the end of the row range
6864 // and position the cursor on the next line.
6865 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6866 cursor_buffer_row = rows.end;
6867 } else {
6868 // If there isn't a line after the range, delete the \n from the line before the
6869 // start of the row range and position the cursor there.
6870 edit_start = edit_start.saturating_sub(1);
6871 edit_end = buffer.len();
6872 cursor_buffer_row = rows.start.previous_row();
6873 }
6874
6875 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6876 *cursor.column_mut() =
6877 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6878
6879 new_cursors.push((
6880 selection.id,
6881 buffer.anchor_after(cursor.to_point(&display_map)),
6882 ));
6883 edit_ranges.push(edit_start..edit_end);
6884 }
6885
6886 self.transact(window, cx, |this, window, cx| {
6887 let buffer = this.buffer.update(cx, |buffer, cx| {
6888 let empty_str: Arc<str> = Arc::default();
6889 buffer.edit(
6890 edit_ranges
6891 .into_iter()
6892 .map(|range| (range, empty_str.clone())),
6893 None,
6894 cx,
6895 );
6896 buffer.snapshot(cx)
6897 });
6898 let new_selections = new_cursors
6899 .into_iter()
6900 .map(|(id, cursor)| {
6901 let cursor = cursor.to_point(&buffer);
6902 Selection {
6903 id,
6904 start: cursor,
6905 end: cursor,
6906 reversed: false,
6907 goal: SelectionGoal::None,
6908 }
6909 })
6910 .collect();
6911
6912 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6913 s.select(new_selections);
6914 });
6915 });
6916 }
6917
6918 pub fn join_lines_impl(
6919 &mut self,
6920 insert_whitespace: bool,
6921 window: &mut Window,
6922 cx: &mut Context<Self>,
6923 ) {
6924 if self.read_only(cx) {
6925 return;
6926 }
6927 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6928 for selection in self.selections.all::<Point>(cx) {
6929 let start = MultiBufferRow(selection.start.row);
6930 // Treat single line selections as if they include the next line. Otherwise this action
6931 // would do nothing for single line selections individual cursors.
6932 let end = if selection.start.row == selection.end.row {
6933 MultiBufferRow(selection.start.row + 1)
6934 } else {
6935 MultiBufferRow(selection.end.row)
6936 };
6937
6938 if let Some(last_row_range) = row_ranges.last_mut() {
6939 if start <= last_row_range.end {
6940 last_row_range.end = end;
6941 continue;
6942 }
6943 }
6944 row_ranges.push(start..end);
6945 }
6946
6947 let snapshot = self.buffer.read(cx).snapshot(cx);
6948 let mut cursor_positions = Vec::new();
6949 for row_range in &row_ranges {
6950 let anchor = snapshot.anchor_before(Point::new(
6951 row_range.end.previous_row().0,
6952 snapshot.line_len(row_range.end.previous_row()),
6953 ));
6954 cursor_positions.push(anchor..anchor);
6955 }
6956
6957 self.transact(window, cx, |this, window, cx| {
6958 for row_range in row_ranges.into_iter().rev() {
6959 for row in row_range.iter_rows().rev() {
6960 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6961 let next_line_row = row.next_row();
6962 let indent = snapshot.indent_size_for_line(next_line_row);
6963 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6964
6965 let replace =
6966 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6967 " "
6968 } else {
6969 ""
6970 };
6971
6972 this.buffer.update(cx, |buffer, cx| {
6973 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6974 });
6975 }
6976 }
6977
6978 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6979 s.select_anchor_ranges(cursor_positions)
6980 });
6981 });
6982 }
6983
6984 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6985 self.join_lines_impl(true, window, cx);
6986 }
6987
6988 pub fn sort_lines_case_sensitive(
6989 &mut self,
6990 _: &SortLinesCaseSensitive,
6991 window: &mut Window,
6992 cx: &mut Context<Self>,
6993 ) {
6994 self.manipulate_lines(window, cx, |lines| lines.sort())
6995 }
6996
6997 pub fn sort_lines_case_insensitive(
6998 &mut self,
6999 _: &SortLinesCaseInsensitive,
7000 window: &mut Window,
7001 cx: &mut Context<Self>,
7002 ) {
7003 self.manipulate_lines(window, cx, |lines| {
7004 lines.sort_by_key(|line| line.to_lowercase())
7005 })
7006 }
7007
7008 pub fn unique_lines_case_insensitive(
7009 &mut self,
7010 _: &UniqueLinesCaseInsensitive,
7011 window: &mut Window,
7012 cx: &mut Context<Self>,
7013 ) {
7014 self.manipulate_lines(window, cx, |lines| {
7015 let mut seen = HashSet::default();
7016 lines.retain(|line| seen.insert(line.to_lowercase()));
7017 })
7018 }
7019
7020 pub fn unique_lines_case_sensitive(
7021 &mut self,
7022 _: &UniqueLinesCaseSensitive,
7023 window: &mut Window,
7024 cx: &mut Context<Self>,
7025 ) {
7026 self.manipulate_lines(window, cx, |lines| {
7027 let mut seen = HashSet::default();
7028 lines.retain(|line| seen.insert(*line));
7029 })
7030 }
7031
7032 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7033 let Some(project) = self.project.clone() else {
7034 return;
7035 };
7036 self.reload(project, window, cx)
7037 .detach_and_notify_err(window, cx);
7038 }
7039
7040 pub fn restore_file(
7041 &mut self,
7042 _: &::git::RestoreFile,
7043 window: &mut Window,
7044 cx: &mut Context<Self>,
7045 ) {
7046 let mut buffer_ids = HashSet::default();
7047 let snapshot = self.buffer().read(cx).snapshot(cx);
7048 for selection in self.selections.all::<usize>(cx) {
7049 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7050 }
7051
7052 let buffer = self.buffer().read(cx);
7053 let ranges = buffer_ids
7054 .into_iter()
7055 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7056 .collect::<Vec<_>>();
7057
7058 self.restore_hunks_in_ranges(ranges, window, cx);
7059 }
7060
7061 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7062 let selections = self
7063 .selections
7064 .all(cx)
7065 .into_iter()
7066 .map(|s| s.range())
7067 .collect();
7068 self.restore_hunks_in_ranges(selections, window, cx);
7069 }
7070
7071 fn restore_hunks_in_ranges(
7072 &mut self,
7073 ranges: Vec<Range<Point>>,
7074 window: &mut Window,
7075 cx: &mut Context<Editor>,
7076 ) {
7077 let mut revert_changes = HashMap::default();
7078 let snapshot = self.buffer.read(cx).snapshot(cx);
7079 let Some(project) = &self.project else {
7080 return;
7081 };
7082
7083 let chunk_by = self
7084 .snapshot(window, cx)
7085 .hunks_for_ranges(ranges.into_iter())
7086 .into_iter()
7087 .chunk_by(|hunk| hunk.buffer_id);
7088 for (buffer_id, hunks) in &chunk_by {
7089 let hunks = hunks.collect::<Vec<_>>();
7090 for hunk in &hunks {
7091 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7092 }
7093 Self::do_stage_or_unstage(project, false, buffer_id, hunks.into_iter(), &snapshot, cx);
7094 }
7095 drop(chunk_by);
7096 if !revert_changes.is_empty() {
7097 self.transact(window, cx, |editor, window, cx| {
7098 editor.revert(revert_changes, window, cx);
7099 });
7100 }
7101 }
7102
7103 pub fn open_active_item_in_terminal(
7104 &mut self,
7105 _: &OpenInTerminal,
7106 window: &mut Window,
7107 cx: &mut Context<Self>,
7108 ) {
7109 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7110 let project_path = buffer.read(cx).project_path(cx)?;
7111 let project = self.project.as_ref()?.read(cx);
7112 let entry = project.entry_for_path(&project_path, cx)?;
7113 let parent = match &entry.canonical_path {
7114 Some(canonical_path) => canonical_path.to_path_buf(),
7115 None => project.absolute_path(&project_path, cx)?,
7116 }
7117 .parent()?
7118 .to_path_buf();
7119 Some(parent)
7120 }) {
7121 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7122 }
7123 }
7124
7125 pub fn prepare_restore_change(
7126 &self,
7127 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7128 hunk: &MultiBufferDiffHunk,
7129 cx: &mut App,
7130 ) -> Option<()> {
7131 let buffer = self.buffer.read(cx);
7132 let diff = buffer.diff_for(hunk.buffer_id)?;
7133 let buffer = buffer.buffer(hunk.buffer_id)?;
7134 let buffer = buffer.read(cx);
7135 let original_text = diff
7136 .read(cx)
7137 .base_text()
7138 .as_ref()?
7139 .as_rope()
7140 .slice(hunk.diff_base_byte_range.clone());
7141 let buffer_snapshot = buffer.snapshot();
7142 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7143 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7144 probe
7145 .0
7146 .start
7147 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7148 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7149 }) {
7150 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7151 Some(())
7152 } else {
7153 None
7154 }
7155 }
7156
7157 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7158 self.manipulate_lines(window, cx, |lines| lines.reverse())
7159 }
7160
7161 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7162 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7163 }
7164
7165 fn manipulate_lines<Fn>(
7166 &mut self,
7167 window: &mut Window,
7168 cx: &mut Context<Self>,
7169 mut callback: Fn,
7170 ) where
7171 Fn: FnMut(&mut Vec<&str>),
7172 {
7173 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7174 let buffer = self.buffer.read(cx).snapshot(cx);
7175
7176 let mut edits = Vec::new();
7177
7178 let selections = self.selections.all::<Point>(cx);
7179 let mut selections = selections.iter().peekable();
7180 let mut contiguous_row_selections = Vec::new();
7181 let mut new_selections = Vec::new();
7182 let mut added_lines = 0;
7183 let mut removed_lines = 0;
7184
7185 while let Some(selection) = selections.next() {
7186 let (start_row, end_row) = consume_contiguous_rows(
7187 &mut contiguous_row_selections,
7188 selection,
7189 &display_map,
7190 &mut selections,
7191 );
7192
7193 let start_point = Point::new(start_row.0, 0);
7194 let end_point = Point::new(
7195 end_row.previous_row().0,
7196 buffer.line_len(end_row.previous_row()),
7197 );
7198 let text = buffer
7199 .text_for_range(start_point..end_point)
7200 .collect::<String>();
7201
7202 let mut lines = text.split('\n').collect_vec();
7203
7204 let lines_before = lines.len();
7205 callback(&mut lines);
7206 let lines_after = lines.len();
7207
7208 edits.push((start_point..end_point, lines.join("\n")));
7209
7210 // Selections must change based on added and removed line count
7211 let start_row =
7212 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7213 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7214 new_selections.push(Selection {
7215 id: selection.id,
7216 start: start_row,
7217 end: end_row,
7218 goal: SelectionGoal::None,
7219 reversed: selection.reversed,
7220 });
7221
7222 if lines_after > lines_before {
7223 added_lines += lines_after - lines_before;
7224 } else if lines_before > lines_after {
7225 removed_lines += lines_before - lines_after;
7226 }
7227 }
7228
7229 self.transact(window, cx, |this, window, cx| {
7230 let buffer = this.buffer.update(cx, |buffer, cx| {
7231 buffer.edit(edits, None, cx);
7232 buffer.snapshot(cx)
7233 });
7234
7235 // Recalculate offsets on newly edited buffer
7236 let new_selections = new_selections
7237 .iter()
7238 .map(|s| {
7239 let start_point = Point::new(s.start.0, 0);
7240 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7241 Selection {
7242 id: s.id,
7243 start: buffer.point_to_offset(start_point),
7244 end: buffer.point_to_offset(end_point),
7245 goal: s.goal,
7246 reversed: s.reversed,
7247 }
7248 })
7249 .collect();
7250
7251 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7252 s.select(new_selections);
7253 });
7254
7255 this.request_autoscroll(Autoscroll::fit(), cx);
7256 });
7257 }
7258
7259 pub fn convert_to_upper_case(
7260 &mut self,
7261 _: &ConvertToUpperCase,
7262 window: &mut Window,
7263 cx: &mut Context<Self>,
7264 ) {
7265 self.manipulate_text(window, cx, |text| text.to_uppercase())
7266 }
7267
7268 pub fn convert_to_lower_case(
7269 &mut self,
7270 _: &ConvertToLowerCase,
7271 window: &mut Window,
7272 cx: &mut Context<Self>,
7273 ) {
7274 self.manipulate_text(window, cx, |text| text.to_lowercase())
7275 }
7276
7277 pub fn convert_to_title_case(
7278 &mut self,
7279 _: &ConvertToTitleCase,
7280 window: &mut Window,
7281 cx: &mut Context<Self>,
7282 ) {
7283 self.manipulate_text(window, cx, |text| {
7284 text.split('\n')
7285 .map(|line| line.to_case(Case::Title))
7286 .join("\n")
7287 })
7288 }
7289
7290 pub fn convert_to_snake_case(
7291 &mut self,
7292 _: &ConvertToSnakeCase,
7293 window: &mut Window,
7294 cx: &mut Context<Self>,
7295 ) {
7296 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7297 }
7298
7299 pub fn convert_to_kebab_case(
7300 &mut self,
7301 _: &ConvertToKebabCase,
7302 window: &mut Window,
7303 cx: &mut Context<Self>,
7304 ) {
7305 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7306 }
7307
7308 pub fn convert_to_upper_camel_case(
7309 &mut self,
7310 _: &ConvertToUpperCamelCase,
7311 window: &mut Window,
7312 cx: &mut Context<Self>,
7313 ) {
7314 self.manipulate_text(window, cx, |text| {
7315 text.split('\n')
7316 .map(|line| line.to_case(Case::UpperCamel))
7317 .join("\n")
7318 })
7319 }
7320
7321 pub fn convert_to_lower_camel_case(
7322 &mut self,
7323 _: &ConvertToLowerCamelCase,
7324 window: &mut Window,
7325 cx: &mut Context<Self>,
7326 ) {
7327 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7328 }
7329
7330 pub fn convert_to_opposite_case(
7331 &mut self,
7332 _: &ConvertToOppositeCase,
7333 window: &mut Window,
7334 cx: &mut Context<Self>,
7335 ) {
7336 self.manipulate_text(window, cx, |text| {
7337 text.chars()
7338 .fold(String::with_capacity(text.len()), |mut t, c| {
7339 if c.is_uppercase() {
7340 t.extend(c.to_lowercase());
7341 } else {
7342 t.extend(c.to_uppercase());
7343 }
7344 t
7345 })
7346 })
7347 }
7348
7349 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7350 where
7351 Fn: FnMut(&str) -> String,
7352 {
7353 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7354 let buffer = self.buffer.read(cx).snapshot(cx);
7355
7356 let mut new_selections = Vec::new();
7357 let mut edits = Vec::new();
7358 let mut selection_adjustment = 0i32;
7359
7360 for selection in self.selections.all::<usize>(cx) {
7361 let selection_is_empty = selection.is_empty();
7362
7363 let (start, end) = if selection_is_empty {
7364 let word_range = movement::surrounding_word(
7365 &display_map,
7366 selection.start.to_display_point(&display_map),
7367 );
7368 let start = word_range.start.to_offset(&display_map, Bias::Left);
7369 let end = word_range.end.to_offset(&display_map, Bias::Left);
7370 (start, end)
7371 } else {
7372 (selection.start, selection.end)
7373 };
7374
7375 let text = buffer.text_for_range(start..end).collect::<String>();
7376 let old_length = text.len() as i32;
7377 let text = callback(&text);
7378
7379 new_selections.push(Selection {
7380 start: (start as i32 - selection_adjustment) as usize,
7381 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7382 goal: SelectionGoal::None,
7383 ..selection
7384 });
7385
7386 selection_adjustment += old_length - text.len() as i32;
7387
7388 edits.push((start..end, text));
7389 }
7390
7391 self.transact(window, cx, |this, window, cx| {
7392 this.buffer.update(cx, |buffer, cx| {
7393 buffer.edit(edits, None, cx);
7394 });
7395
7396 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7397 s.select(new_selections);
7398 });
7399
7400 this.request_autoscroll(Autoscroll::fit(), cx);
7401 });
7402 }
7403
7404 pub fn duplicate(
7405 &mut self,
7406 upwards: bool,
7407 whole_lines: bool,
7408 window: &mut Window,
7409 cx: &mut Context<Self>,
7410 ) {
7411 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7412 let buffer = &display_map.buffer_snapshot;
7413 let selections = self.selections.all::<Point>(cx);
7414
7415 let mut edits = Vec::new();
7416 let mut selections_iter = selections.iter().peekable();
7417 while let Some(selection) = selections_iter.next() {
7418 let mut rows = selection.spanned_rows(false, &display_map);
7419 // duplicate line-wise
7420 if whole_lines || selection.start == selection.end {
7421 // Avoid duplicating the same lines twice.
7422 while let Some(next_selection) = selections_iter.peek() {
7423 let next_rows = next_selection.spanned_rows(false, &display_map);
7424 if next_rows.start < rows.end {
7425 rows.end = next_rows.end;
7426 selections_iter.next().unwrap();
7427 } else {
7428 break;
7429 }
7430 }
7431
7432 // Copy the text from the selected row region and splice it either at the start
7433 // or end of the region.
7434 let start = Point::new(rows.start.0, 0);
7435 let end = Point::new(
7436 rows.end.previous_row().0,
7437 buffer.line_len(rows.end.previous_row()),
7438 );
7439 let text = buffer
7440 .text_for_range(start..end)
7441 .chain(Some("\n"))
7442 .collect::<String>();
7443 let insert_location = if upwards {
7444 Point::new(rows.end.0, 0)
7445 } else {
7446 start
7447 };
7448 edits.push((insert_location..insert_location, text));
7449 } else {
7450 // duplicate character-wise
7451 let start = selection.start;
7452 let end = selection.end;
7453 let text = buffer.text_for_range(start..end).collect::<String>();
7454 edits.push((selection.end..selection.end, text));
7455 }
7456 }
7457
7458 self.transact(window, cx, |this, _, cx| {
7459 this.buffer.update(cx, |buffer, cx| {
7460 buffer.edit(edits, None, cx);
7461 });
7462
7463 this.request_autoscroll(Autoscroll::fit(), cx);
7464 });
7465 }
7466
7467 pub fn duplicate_line_up(
7468 &mut self,
7469 _: &DuplicateLineUp,
7470 window: &mut Window,
7471 cx: &mut Context<Self>,
7472 ) {
7473 self.duplicate(true, true, window, cx);
7474 }
7475
7476 pub fn duplicate_line_down(
7477 &mut self,
7478 _: &DuplicateLineDown,
7479 window: &mut Window,
7480 cx: &mut Context<Self>,
7481 ) {
7482 self.duplicate(false, true, window, cx);
7483 }
7484
7485 pub fn duplicate_selection(
7486 &mut self,
7487 _: &DuplicateSelection,
7488 window: &mut Window,
7489 cx: &mut Context<Self>,
7490 ) {
7491 self.duplicate(false, false, window, cx);
7492 }
7493
7494 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7495 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7496 let buffer = self.buffer.read(cx).snapshot(cx);
7497
7498 let mut edits = Vec::new();
7499 let mut unfold_ranges = Vec::new();
7500 let mut refold_creases = Vec::new();
7501
7502 let selections = self.selections.all::<Point>(cx);
7503 let mut selections = selections.iter().peekable();
7504 let mut contiguous_row_selections = Vec::new();
7505 let mut new_selections = Vec::new();
7506
7507 while let Some(selection) = selections.next() {
7508 // Find all the selections that span a contiguous row range
7509 let (start_row, end_row) = consume_contiguous_rows(
7510 &mut contiguous_row_selections,
7511 selection,
7512 &display_map,
7513 &mut selections,
7514 );
7515
7516 // Move the text spanned by the row range to be before the line preceding the row range
7517 if start_row.0 > 0 {
7518 let range_to_move = Point::new(
7519 start_row.previous_row().0,
7520 buffer.line_len(start_row.previous_row()),
7521 )
7522 ..Point::new(
7523 end_row.previous_row().0,
7524 buffer.line_len(end_row.previous_row()),
7525 );
7526 let insertion_point = display_map
7527 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7528 .0;
7529
7530 // Don't move lines across excerpts
7531 if buffer
7532 .excerpt_containing(insertion_point..range_to_move.end)
7533 .is_some()
7534 {
7535 let text = buffer
7536 .text_for_range(range_to_move.clone())
7537 .flat_map(|s| s.chars())
7538 .skip(1)
7539 .chain(['\n'])
7540 .collect::<String>();
7541
7542 edits.push((
7543 buffer.anchor_after(range_to_move.start)
7544 ..buffer.anchor_before(range_to_move.end),
7545 String::new(),
7546 ));
7547 let insertion_anchor = buffer.anchor_after(insertion_point);
7548 edits.push((insertion_anchor..insertion_anchor, text));
7549
7550 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7551
7552 // Move selections up
7553 new_selections.extend(contiguous_row_selections.drain(..).map(
7554 |mut selection| {
7555 selection.start.row -= row_delta;
7556 selection.end.row -= row_delta;
7557 selection
7558 },
7559 ));
7560
7561 // Move folds up
7562 unfold_ranges.push(range_to_move.clone());
7563 for fold in display_map.folds_in_range(
7564 buffer.anchor_before(range_to_move.start)
7565 ..buffer.anchor_after(range_to_move.end),
7566 ) {
7567 let mut start = fold.range.start.to_point(&buffer);
7568 let mut end = fold.range.end.to_point(&buffer);
7569 start.row -= row_delta;
7570 end.row -= row_delta;
7571 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7572 }
7573 }
7574 }
7575
7576 // If we didn't move line(s), preserve the existing selections
7577 new_selections.append(&mut contiguous_row_selections);
7578 }
7579
7580 self.transact(window, cx, |this, window, cx| {
7581 this.unfold_ranges(&unfold_ranges, true, true, cx);
7582 this.buffer.update(cx, |buffer, cx| {
7583 for (range, text) in edits {
7584 buffer.edit([(range, text)], None, cx);
7585 }
7586 });
7587 this.fold_creases(refold_creases, true, window, cx);
7588 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7589 s.select(new_selections);
7590 })
7591 });
7592 }
7593
7594 pub fn move_line_down(
7595 &mut self,
7596 _: &MoveLineDown,
7597 window: &mut Window,
7598 cx: &mut Context<Self>,
7599 ) {
7600 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7601 let buffer = self.buffer.read(cx).snapshot(cx);
7602
7603 let mut edits = Vec::new();
7604 let mut unfold_ranges = Vec::new();
7605 let mut refold_creases = Vec::new();
7606
7607 let selections = self.selections.all::<Point>(cx);
7608 let mut selections = selections.iter().peekable();
7609 let mut contiguous_row_selections = Vec::new();
7610 let mut new_selections = Vec::new();
7611
7612 while let Some(selection) = selections.next() {
7613 // Find all the selections that span a contiguous row range
7614 let (start_row, end_row) = consume_contiguous_rows(
7615 &mut contiguous_row_selections,
7616 selection,
7617 &display_map,
7618 &mut selections,
7619 );
7620
7621 // Move the text spanned by the row range to be after the last line of the row range
7622 if end_row.0 <= buffer.max_point().row {
7623 let range_to_move =
7624 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7625 let insertion_point = display_map
7626 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7627 .0;
7628
7629 // Don't move lines across excerpt boundaries
7630 if buffer
7631 .excerpt_containing(range_to_move.start..insertion_point)
7632 .is_some()
7633 {
7634 let mut text = String::from("\n");
7635 text.extend(buffer.text_for_range(range_to_move.clone()));
7636 text.pop(); // Drop trailing newline
7637 edits.push((
7638 buffer.anchor_after(range_to_move.start)
7639 ..buffer.anchor_before(range_to_move.end),
7640 String::new(),
7641 ));
7642 let insertion_anchor = buffer.anchor_after(insertion_point);
7643 edits.push((insertion_anchor..insertion_anchor, text));
7644
7645 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7646
7647 // Move selections down
7648 new_selections.extend(contiguous_row_selections.drain(..).map(
7649 |mut selection| {
7650 selection.start.row += row_delta;
7651 selection.end.row += row_delta;
7652 selection
7653 },
7654 ));
7655
7656 // Move folds down
7657 unfold_ranges.push(range_to_move.clone());
7658 for fold in display_map.folds_in_range(
7659 buffer.anchor_before(range_to_move.start)
7660 ..buffer.anchor_after(range_to_move.end),
7661 ) {
7662 let mut start = fold.range.start.to_point(&buffer);
7663 let mut end = fold.range.end.to_point(&buffer);
7664 start.row += row_delta;
7665 end.row += row_delta;
7666 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7667 }
7668 }
7669 }
7670
7671 // If we didn't move line(s), preserve the existing selections
7672 new_selections.append(&mut contiguous_row_selections);
7673 }
7674
7675 self.transact(window, cx, |this, window, cx| {
7676 this.unfold_ranges(&unfold_ranges, true, true, cx);
7677 this.buffer.update(cx, |buffer, cx| {
7678 for (range, text) in edits {
7679 buffer.edit([(range, text)], None, cx);
7680 }
7681 });
7682 this.fold_creases(refold_creases, true, window, cx);
7683 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7684 s.select(new_selections)
7685 });
7686 });
7687 }
7688
7689 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7690 let text_layout_details = &self.text_layout_details(window);
7691 self.transact(window, cx, |this, window, cx| {
7692 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7693 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7694 let line_mode = s.line_mode;
7695 s.move_with(|display_map, selection| {
7696 if !selection.is_empty() || line_mode {
7697 return;
7698 }
7699
7700 let mut head = selection.head();
7701 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7702 if head.column() == display_map.line_len(head.row()) {
7703 transpose_offset = display_map
7704 .buffer_snapshot
7705 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7706 }
7707
7708 if transpose_offset == 0 {
7709 return;
7710 }
7711
7712 *head.column_mut() += 1;
7713 head = display_map.clip_point(head, Bias::Right);
7714 let goal = SelectionGoal::HorizontalPosition(
7715 display_map
7716 .x_for_display_point(head, text_layout_details)
7717 .into(),
7718 );
7719 selection.collapse_to(head, goal);
7720
7721 let transpose_start = display_map
7722 .buffer_snapshot
7723 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7724 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7725 let transpose_end = display_map
7726 .buffer_snapshot
7727 .clip_offset(transpose_offset + 1, Bias::Right);
7728 if let Some(ch) =
7729 display_map.buffer_snapshot.chars_at(transpose_start).next()
7730 {
7731 edits.push((transpose_start..transpose_offset, String::new()));
7732 edits.push((transpose_end..transpose_end, ch.to_string()));
7733 }
7734 }
7735 });
7736 edits
7737 });
7738 this.buffer
7739 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7740 let selections = this.selections.all::<usize>(cx);
7741 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7742 s.select(selections);
7743 });
7744 });
7745 }
7746
7747 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7748 self.rewrap_impl(IsVimMode::No, cx)
7749 }
7750
7751 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7752 let buffer = self.buffer.read(cx).snapshot(cx);
7753 let selections = self.selections.all::<Point>(cx);
7754 let mut selections = selections.iter().peekable();
7755
7756 let mut edits = Vec::new();
7757 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7758
7759 while let Some(selection) = selections.next() {
7760 let mut start_row = selection.start.row;
7761 let mut end_row = selection.end.row;
7762
7763 // Skip selections that overlap with a range that has already been rewrapped.
7764 let selection_range = start_row..end_row;
7765 if rewrapped_row_ranges
7766 .iter()
7767 .any(|range| range.overlaps(&selection_range))
7768 {
7769 continue;
7770 }
7771
7772 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7773
7774 // Since not all lines in the selection may be at the same indent
7775 // level, choose the indent size that is the most common between all
7776 // of the lines.
7777 //
7778 // If there is a tie, we use the deepest indent.
7779 let (indent_size, indent_end) = {
7780 let mut indent_size_occurrences = HashMap::default();
7781 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7782
7783 for row in start_row..=end_row {
7784 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7785 rows_by_indent_size.entry(indent).or_default().push(row);
7786 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7787 }
7788
7789 let indent_size = indent_size_occurrences
7790 .into_iter()
7791 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7792 .map(|(indent, _)| indent)
7793 .unwrap_or_default();
7794 let row = rows_by_indent_size[&indent_size][0];
7795 let indent_end = Point::new(row, indent_size.len);
7796
7797 (indent_size, indent_end)
7798 };
7799
7800 let mut line_prefix = indent_size.chars().collect::<String>();
7801
7802 let mut inside_comment = false;
7803 if let Some(comment_prefix) =
7804 buffer
7805 .language_scope_at(selection.head())
7806 .and_then(|language| {
7807 language
7808 .line_comment_prefixes()
7809 .iter()
7810 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7811 .cloned()
7812 })
7813 {
7814 line_prefix.push_str(&comment_prefix);
7815 inside_comment = true;
7816 }
7817
7818 let language_settings = buffer.settings_at(selection.head(), cx);
7819 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
7820 RewrapBehavior::InComments => inside_comment,
7821 RewrapBehavior::InSelections => !selection.is_empty(),
7822 RewrapBehavior::Anywhere => true,
7823 };
7824
7825 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
7826 if !should_rewrap {
7827 continue;
7828 }
7829
7830 if selection.is_empty() {
7831 'expand_upwards: while start_row > 0 {
7832 let prev_row = start_row - 1;
7833 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7834 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7835 {
7836 start_row = prev_row;
7837 } else {
7838 break 'expand_upwards;
7839 }
7840 }
7841
7842 'expand_downwards: while end_row < buffer.max_point().row {
7843 let next_row = end_row + 1;
7844 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7845 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7846 {
7847 end_row = next_row;
7848 } else {
7849 break 'expand_downwards;
7850 }
7851 }
7852 }
7853
7854 let start = Point::new(start_row, 0);
7855 let start_offset = start.to_offset(&buffer);
7856 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7857 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7858 let Some(lines_without_prefixes) = selection_text
7859 .lines()
7860 .map(|line| {
7861 line.strip_prefix(&line_prefix)
7862 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7863 .ok_or_else(|| {
7864 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7865 })
7866 })
7867 .collect::<Result<Vec<_>, _>>()
7868 .log_err()
7869 else {
7870 continue;
7871 };
7872
7873 let wrap_column = buffer
7874 .settings_at(Point::new(start_row, 0), cx)
7875 .preferred_line_length as usize;
7876 let wrapped_text = wrap_with_prefix(
7877 line_prefix,
7878 lines_without_prefixes.join(" "),
7879 wrap_column,
7880 tab_size,
7881 );
7882
7883 // TODO: should always use char-based diff while still supporting cursor behavior that
7884 // matches vim.
7885 let mut diff_options = DiffOptions::default();
7886 if is_vim_mode == IsVimMode::Yes {
7887 diff_options.max_word_diff_len = 0;
7888 diff_options.max_word_diff_line_count = 0;
7889 } else {
7890 diff_options.max_word_diff_len = usize::MAX;
7891 diff_options.max_word_diff_line_count = usize::MAX;
7892 }
7893
7894 for (old_range, new_text) in
7895 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
7896 {
7897 let edit_start = buffer.anchor_after(start_offset + old_range.start);
7898 let edit_end = buffer.anchor_after(start_offset + old_range.end);
7899 edits.push((edit_start..edit_end, new_text));
7900 }
7901
7902 rewrapped_row_ranges.push(start_row..=end_row);
7903 }
7904
7905 self.buffer
7906 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7907 }
7908
7909 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7910 let mut text = String::new();
7911 let buffer = self.buffer.read(cx).snapshot(cx);
7912 let mut selections = self.selections.all::<Point>(cx);
7913 let mut clipboard_selections = Vec::with_capacity(selections.len());
7914 {
7915 let max_point = buffer.max_point();
7916 let mut is_first = true;
7917 for selection in &mut selections {
7918 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7919 if is_entire_line {
7920 selection.start = Point::new(selection.start.row, 0);
7921 if !selection.is_empty() && selection.end.column == 0 {
7922 selection.end = cmp::min(max_point, selection.end);
7923 } else {
7924 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7925 }
7926 selection.goal = SelectionGoal::None;
7927 }
7928 if is_first {
7929 is_first = false;
7930 } else {
7931 text += "\n";
7932 }
7933 let mut len = 0;
7934 for chunk in buffer.text_for_range(selection.start..selection.end) {
7935 text.push_str(chunk);
7936 len += chunk.len();
7937 }
7938 clipboard_selections.push(ClipboardSelection {
7939 len,
7940 is_entire_line,
7941 start_column: selection.start.column,
7942 });
7943 }
7944 }
7945
7946 self.transact(window, cx, |this, window, cx| {
7947 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7948 s.select(selections);
7949 });
7950 this.insert("", window, cx);
7951 });
7952 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7953 }
7954
7955 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7956 let item = self.cut_common(window, cx);
7957 cx.write_to_clipboard(item);
7958 }
7959
7960 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7961 self.change_selections(None, window, cx, |s| {
7962 s.move_with(|snapshot, sel| {
7963 if sel.is_empty() {
7964 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7965 }
7966 });
7967 });
7968 let item = self.cut_common(window, cx);
7969 cx.set_global(KillRing(item))
7970 }
7971
7972 pub fn kill_ring_yank(
7973 &mut self,
7974 _: &KillRingYank,
7975 window: &mut Window,
7976 cx: &mut Context<Self>,
7977 ) {
7978 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7979 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7980 (kill_ring.text().to_string(), kill_ring.metadata_json())
7981 } else {
7982 return;
7983 }
7984 } else {
7985 return;
7986 };
7987 self.do_paste(&text, metadata, false, window, cx);
7988 }
7989
7990 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7991 let selections = self.selections.all::<Point>(cx);
7992 let buffer = self.buffer.read(cx).read(cx);
7993 let mut text = String::new();
7994
7995 let mut clipboard_selections = Vec::with_capacity(selections.len());
7996 {
7997 let max_point = buffer.max_point();
7998 let mut is_first = true;
7999 for selection in selections.iter() {
8000 let mut start = selection.start;
8001 let mut end = selection.end;
8002 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8003 if is_entire_line {
8004 start = Point::new(start.row, 0);
8005 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8006 }
8007 if is_first {
8008 is_first = false;
8009 } else {
8010 text += "\n";
8011 }
8012 let mut len = 0;
8013 for chunk in buffer.text_for_range(start..end) {
8014 text.push_str(chunk);
8015 len += chunk.len();
8016 }
8017 clipboard_selections.push(ClipboardSelection {
8018 len,
8019 is_entire_line,
8020 start_column: start.column,
8021 });
8022 }
8023 }
8024
8025 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8026 text,
8027 clipboard_selections,
8028 ));
8029 }
8030
8031 pub fn do_paste(
8032 &mut self,
8033 text: &String,
8034 clipboard_selections: Option<Vec<ClipboardSelection>>,
8035 handle_entire_lines: bool,
8036 window: &mut Window,
8037 cx: &mut Context<Self>,
8038 ) {
8039 if self.read_only(cx) {
8040 return;
8041 }
8042
8043 let clipboard_text = Cow::Borrowed(text);
8044
8045 self.transact(window, cx, |this, window, cx| {
8046 if let Some(mut clipboard_selections) = clipboard_selections {
8047 let old_selections = this.selections.all::<usize>(cx);
8048 let all_selections_were_entire_line =
8049 clipboard_selections.iter().all(|s| s.is_entire_line);
8050 let first_selection_start_column =
8051 clipboard_selections.first().map(|s| s.start_column);
8052 if clipboard_selections.len() != old_selections.len() {
8053 clipboard_selections.drain(..);
8054 }
8055 let cursor_offset = this.selections.last::<usize>(cx).head();
8056 let mut auto_indent_on_paste = true;
8057
8058 this.buffer.update(cx, |buffer, cx| {
8059 let snapshot = buffer.read(cx);
8060 auto_indent_on_paste =
8061 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8062
8063 let mut start_offset = 0;
8064 let mut edits = Vec::new();
8065 let mut original_start_columns = Vec::new();
8066 for (ix, selection) in old_selections.iter().enumerate() {
8067 let to_insert;
8068 let entire_line;
8069 let original_start_column;
8070 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8071 let end_offset = start_offset + clipboard_selection.len;
8072 to_insert = &clipboard_text[start_offset..end_offset];
8073 entire_line = clipboard_selection.is_entire_line;
8074 start_offset = end_offset + 1;
8075 original_start_column = Some(clipboard_selection.start_column);
8076 } else {
8077 to_insert = clipboard_text.as_str();
8078 entire_line = all_selections_were_entire_line;
8079 original_start_column = first_selection_start_column
8080 }
8081
8082 // If the corresponding selection was empty when this slice of the
8083 // clipboard text was written, then the entire line containing the
8084 // selection was copied. If this selection is also currently empty,
8085 // then paste the line before the current line of the buffer.
8086 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8087 let column = selection.start.to_point(&snapshot).column as usize;
8088 let line_start = selection.start - column;
8089 line_start..line_start
8090 } else {
8091 selection.range()
8092 };
8093
8094 edits.push((range, to_insert));
8095 original_start_columns.extend(original_start_column);
8096 }
8097 drop(snapshot);
8098
8099 buffer.edit(
8100 edits,
8101 if auto_indent_on_paste {
8102 Some(AutoindentMode::Block {
8103 original_start_columns,
8104 })
8105 } else {
8106 None
8107 },
8108 cx,
8109 );
8110 });
8111
8112 let selections = this.selections.all::<usize>(cx);
8113 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8114 s.select(selections)
8115 });
8116 } else {
8117 this.insert(&clipboard_text, window, cx);
8118 }
8119 });
8120 }
8121
8122 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8123 if let Some(item) = cx.read_from_clipboard() {
8124 let entries = item.entries();
8125
8126 match entries.first() {
8127 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8128 // of all the pasted entries.
8129 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8130 .do_paste(
8131 clipboard_string.text(),
8132 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8133 true,
8134 window,
8135 cx,
8136 ),
8137 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8138 }
8139 }
8140 }
8141
8142 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8143 if self.read_only(cx) {
8144 return;
8145 }
8146
8147 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8148 if let Some((selections, _)) =
8149 self.selection_history.transaction(transaction_id).cloned()
8150 {
8151 self.change_selections(None, window, cx, |s| {
8152 s.select_anchors(selections.to_vec());
8153 });
8154 }
8155 self.request_autoscroll(Autoscroll::fit(), cx);
8156 self.unmark_text(window, cx);
8157 self.refresh_inline_completion(true, false, window, cx);
8158 cx.emit(EditorEvent::Edited { transaction_id });
8159 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8160 }
8161 }
8162
8163 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8164 if self.read_only(cx) {
8165 return;
8166 }
8167
8168 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8169 if let Some((_, Some(selections))) =
8170 self.selection_history.transaction(transaction_id).cloned()
8171 {
8172 self.change_selections(None, window, cx, |s| {
8173 s.select_anchors(selections.to_vec());
8174 });
8175 }
8176 self.request_autoscroll(Autoscroll::fit(), cx);
8177 self.unmark_text(window, cx);
8178 self.refresh_inline_completion(true, false, window, cx);
8179 cx.emit(EditorEvent::Edited { transaction_id });
8180 }
8181 }
8182
8183 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8184 self.buffer
8185 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8186 }
8187
8188 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8189 self.buffer
8190 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8191 }
8192
8193 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8194 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8195 let line_mode = s.line_mode;
8196 s.move_with(|map, selection| {
8197 let cursor = if selection.is_empty() && !line_mode {
8198 movement::left(map, selection.start)
8199 } else {
8200 selection.start
8201 };
8202 selection.collapse_to(cursor, SelectionGoal::None);
8203 });
8204 })
8205 }
8206
8207 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8208 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8209 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8210 })
8211 }
8212
8213 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8214 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8215 let line_mode = s.line_mode;
8216 s.move_with(|map, selection| {
8217 let cursor = if selection.is_empty() && !line_mode {
8218 movement::right(map, selection.end)
8219 } else {
8220 selection.end
8221 };
8222 selection.collapse_to(cursor, SelectionGoal::None)
8223 });
8224 })
8225 }
8226
8227 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8228 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8229 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8230 })
8231 }
8232
8233 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8234 if self.take_rename(true, window, cx).is_some() {
8235 return;
8236 }
8237
8238 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8239 cx.propagate();
8240 return;
8241 }
8242
8243 let text_layout_details = &self.text_layout_details(window);
8244 let selection_count = self.selections.count();
8245 let first_selection = self.selections.first_anchor();
8246
8247 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8248 let line_mode = s.line_mode;
8249 s.move_with(|map, selection| {
8250 if !selection.is_empty() && !line_mode {
8251 selection.goal = SelectionGoal::None;
8252 }
8253 let (cursor, goal) = movement::up(
8254 map,
8255 selection.start,
8256 selection.goal,
8257 false,
8258 text_layout_details,
8259 );
8260 selection.collapse_to(cursor, goal);
8261 });
8262 });
8263
8264 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8265 {
8266 cx.propagate();
8267 }
8268 }
8269
8270 pub fn move_up_by_lines(
8271 &mut self,
8272 action: &MoveUpByLines,
8273 window: &mut Window,
8274 cx: &mut Context<Self>,
8275 ) {
8276 if self.take_rename(true, window, cx).is_some() {
8277 return;
8278 }
8279
8280 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8281 cx.propagate();
8282 return;
8283 }
8284
8285 let text_layout_details = &self.text_layout_details(window);
8286
8287 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8288 let line_mode = s.line_mode;
8289 s.move_with(|map, selection| {
8290 if !selection.is_empty() && !line_mode {
8291 selection.goal = SelectionGoal::None;
8292 }
8293 let (cursor, goal) = movement::up_by_rows(
8294 map,
8295 selection.start,
8296 action.lines,
8297 selection.goal,
8298 false,
8299 text_layout_details,
8300 );
8301 selection.collapse_to(cursor, goal);
8302 });
8303 })
8304 }
8305
8306 pub fn move_down_by_lines(
8307 &mut self,
8308 action: &MoveDownByLines,
8309 window: &mut Window,
8310 cx: &mut Context<Self>,
8311 ) {
8312 if self.take_rename(true, window, cx).is_some() {
8313 return;
8314 }
8315
8316 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8317 cx.propagate();
8318 return;
8319 }
8320
8321 let text_layout_details = &self.text_layout_details(window);
8322
8323 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8324 let line_mode = s.line_mode;
8325 s.move_with(|map, selection| {
8326 if !selection.is_empty() && !line_mode {
8327 selection.goal = SelectionGoal::None;
8328 }
8329 let (cursor, goal) = movement::down_by_rows(
8330 map,
8331 selection.start,
8332 action.lines,
8333 selection.goal,
8334 false,
8335 text_layout_details,
8336 );
8337 selection.collapse_to(cursor, goal);
8338 });
8339 })
8340 }
8341
8342 pub fn select_down_by_lines(
8343 &mut self,
8344 action: &SelectDownByLines,
8345 window: &mut Window,
8346 cx: &mut Context<Self>,
8347 ) {
8348 let text_layout_details = &self.text_layout_details(window);
8349 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8350 s.move_heads_with(|map, head, goal| {
8351 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8352 })
8353 })
8354 }
8355
8356 pub fn select_up_by_lines(
8357 &mut self,
8358 action: &SelectUpByLines,
8359 window: &mut Window,
8360 cx: &mut Context<Self>,
8361 ) {
8362 let text_layout_details = &self.text_layout_details(window);
8363 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8364 s.move_heads_with(|map, head, goal| {
8365 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8366 })
8367 })
8368 }
8369
8370 pub fn select_page_up(
8371 &mut self,
8372 _: &SelectPageUp,
8373 window: &mut Window,
8374 cx: &mut Context<Self>,
8375 ) {
8376 let Some(row_count) = self.visible_row_count() else {
8377 return;
8378 };
8379
8380 let text_layout_details = &self.text_layout_details(window);
8381
8382 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8383 s.move_heads_with(|map, head, goal| {
8384 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8385 })
8386 })
8387 }
8388
8389 pub fn move_page_up(
8390 &mut self,
8391 action: &MovePageUp,
8392 window: &mut Window,
8393 cx: &mut Context<Self>,
8394 ) {
8395 if self.take_rename(true, window, cx).is_some() {
8396 return;
8397 }
8398
8399 if self
8400 .context_menu
8401 .borrow_mut()
8402 .as_mut()
8403 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8404 .unwrap_or(false)
8405 {
8406 return;
8407 }
8408
8409 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8410 cx.propagate();
8411 return;
8412 }
8413
8414 let Some(row_count) = self.visible_row_count() else {
8415 return;
8416 };
8417
8418 let autoscroll = if action.center_cursor {
8419 Autoscroll::center()
8420 } else {
8421 Autoscroll::fit()
8422 };
8423
8424 let text_layout_details = &self.text_layout_details(window);
8425
8426 self.change_selections(Some(autoscroll), window, cx, |s| {
8427 let line_mode = s.line_mode;
8428 s.move_with(|map, selection| {
8429 if !selection.is_empty() && !line_mode {
8430 selection.goal = SelectionGoal::None;
8431 }
8432 let (cursor, goal) = movement::up_by_rows(
8433 map,
8434 selection.end,
8435 row_count,
8436 selection.goal,
8437 false,
8438 text_layout_details,
8439 );
8440 selection.collapse_to(cursor, goal);
8441 });
8442 });
8443 }
8444
8445 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8446 let text_layout_details = &self.text_layout_details(window);
8447 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8448 s.move_heads_with(|map, head, goal| {
8449 movement::up(map, head, goal, false, text_layout_details)
8450 })
8451 })
8452 }
8453
8454 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8455 self.take_rename(true, window, cx);
8456
8457 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8458 cx.propagate();
8459 return;
8460 }
8461
8462 let text_layout_details = &self.text_layout_details(window);
8463 let selection_count = self.selections.count();
8464 let first_selection = self.selections.first_anchor();
8465
8466 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8467 let line_mode = s.line_mode;
8468 s.move_with(|map, selection| {
8469 if !selection.is_empty() && !line_mode {
8470 selection.goal = SelectionGoal::None;
8471 }
8472 let (cursor, goal) = movement::down(
8473 map,
8474 selection.end,
8475 selection.goal,
8476 false,
8477 text_layout_details,
8478 );
8479 selection.collapse_to(cursor, goal);
8480 });
8481 });
8482
8483 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8484 {
8485 cx.propagate();
8486 }
8487 }
8488
8489 pub fn select_page_down(
8490 &mut self,
8491 _: &SelectPageDown,
8492 window: &mut Window,
8493 cx: &mut Context<Self>,
8494 ) {
8495 let Some(row_count) = self.visible_row_count() else {
8496 return;
8497 };
8498
8499 let text_layout_details = &self.text_layout_details(window);
8500
8501 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8502 s.move_heads_with(|map, head, goal| {
8503 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8504 })
8505 })
8506 }
8507
8508 pub fn move_page_down(
8509 &mut self,
8510 action: &MovePageDown,
8511 window: &mut Window,
8512 cx: &mut Context<Self>,
8513 ) {
8514 if self.take_rename(true, window, cx).is_some() {
8515 return;
8516 }
8517
8518 if self
8519 .context_menu
8520 .borrow_mut()
8521 .as_mut()
8522 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8523 .unwrap_or(false)
8524 {
8525 return;
8526 }
8527
8528 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8529 cx.propagate();
8530 return;
8531 }
8532
8533 let Some(row_count) = self.visible_row_count() else {
8534 return;
8535 };
8536
8537 let autoscroll = if action.center_cursor {
8538 Autoscroll::center()
8539 } else {
8540 Autoscroll::fit()
8541 };
8542
8543 let text_layout_details = &self.text_layout_details(window);
8544 self.change_selections(Some(autoscroll), window, cx, |s| {
8545 let line_mode = s.line_mode;
8546 s.move_with(|map, selection| {
8547 if !selection.is_empty() && !line_mode {
8548 selection.goal = SelectionGoal::None;
8549 }
8550 let (cursor, goal) = movement::down_by_rows(
8551 map,
8552 selection.end,
8553 row_count,
8554 selection.goal,
8555 false,
8556 text_layout_details,
8557 );
8558 selection.collapse_to(cursor, goal);
8559 });
8560 });
8561 }
8562
8563 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8564 let text_layout_details = &self.text_layout_details(window);
8565 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8566 s.move_heads_with(|map, head, goal| {
8567 movement::down(map, head, goal, false, text_layout_details)
8568 })
8569 });
8570 }
8571
8572 pub fn context_menu_first(
8573 &mut self,
8574 _: &ContextMenuFirst,
8575 _window: &mut Window,
8576 cx: &mut Context<Self>,
8577 ) {
8578 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8579 context_menu.select_first(self.completion_provider.as_deref(), cx);
8580 }
8581 }
8582
8583 pub fn context_menu_prev(
8584 &mut self,
8585 _: &ContextMenuPrev,
8586 _window: &mut Window,
8587 cx: &mut Context<Self>,
8588 ) {
8589 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8590 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8591 }
8592 }
8593
8594 pub fn context_menu_next(
8595 &mut self,
8596 _: &ContextMenuNext,
8597 _window: &mut Window,
8598 cx: &mut Context<Self>,
8599 ) {
8600 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8601 context_menu.select_next(self.completion_provider.as_deref(), cx);
8602 }
8603 }
8604
8605 pub fn context_menu_last(
8606 &mut self,
8607 _: &ContextMenuLast,
8608 _window: &mut Window,
8609 cx: &mut Context<Self>,
8610 ) {
8611 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8612 context_menu.select_last(self.completion_provider.as_deref(), cx);
8613 }
8614 }
8615
8616 pub fn move_to_previous_word_start(
8617 &mut self,
8618 _: &MoveToPreviousWordStart,
8619 window: &mut Window,
8620 cx: &mut Context<Self>,
8621 ) {
8622 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8623 s.move_cursors_with(|map, head, _| {
8624 (
8625 movement::previous_word_start(map, head),
8626 SelectionGoal::None,
8627 )
8628 });
8629 })
8630 }
8631
8632 pub fn move_to_previous_subword_start(
8633 &mut self,
8634 _: &MoveToPreviousSubwordStart,
8635 window: &mut Window,
8636 cx: &mut Context<Self>,
8637 ) {
8638 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8639 s.move_cursors_with(|map, head, _| {
8640 (
8641 movement::previous_subword_start(map, head),
8642 SelectionGoal::None,
8643 )
8644 });
8645 })
8646 }
8647
8648 pub fn select_to_previous_word_start(
8649 &mut self,
8650 _: &SelectToPreviousWordStart,
8651 window: &mut Window,
8652 cx: &mut Context<Self>,
8653 ) {
8654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8655 s.move_heads_with(|map, head, _| {
8656 (
8657 movement::previous_word_start(map, head),
8658 SelectionGoal::None,
8659 )
8660 });
8661 })
8662 }
8663
8664 pub fn select_to_previous_subword_start(
8665 &mut self,
8666 _: &SelectToPreviousSubwordStart,
8667 window: &mut Window,
8668 cx: &mut Context<Self>,
8669 ) {
8670 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8671 s.move_heads_with(|map, head, _| {
8672 (
8673 movement::previous_subword_start(map, head),
8674 SelectionGoal::None,
8675 )
8676 });
8677 })
8678 }
8679
8680 pub fn delete_to_previous_word_start(
8681 &mut self,
8682 action: &DeleteToPreviousWordStart,
8683 window: &mut Window,
8684 cx: &mut Context<Self>,
8685 ) {
8686 self.transact(window, cx, |this, window, cx| {
8687 this.select_autoclose_pair(window, cx);
8688 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8689 let line_mode = s.line_mode;
8690 s.move_with(|map, selection| {
8691 if selection.is_empty() && !line_mode {
8692 let cursor = if action.ignore_newlines {
8693 movement::previous_word_start(map, selection.head())
8694 } else {
8695 movement::previous_word_start_or_newline(map, selection.head())
8696 };
8697 selection.set_head(cursor, SelectionGoal::None);
8698 }
8699 });
8700 });
8701 this.insert("", window, cx);
8702 });
8703 }
8704
8705 pub fn delete_to_previous_subword_start(
8706 &mut self,
8707 _: &DeleteToPreviousSubwordStart,
8708 window: &mut Window,
8709 cx: &mut Context<Self>,
8710 ) {
8711 self.transact(window, cx, |this, window, cx| {
8712 this.select_autoclose_pair(window, cx);
8713 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8714 let line_mode = s.line_mode;
8715 s.move_with(|map, selection| {
8716 if selection.is_empty() && !line_mode {
8717 let cursor = movement::previous_subword_start(map, selection.head());
8718 selection.set_head(cursor, SelectionGoal::None);
8719 }
8720 });
8721 });
8722 this.insert("", window, cx);
8723 });
8724 }
8725
8726 pub fn move_to_next_word_end(
8727 &mut self,
8728 _: &MoveToNextWordEnd,
8729 window: &mut Window,
8730 cx: &mut Context<Self>,
8731 ) {
8732 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8733 s.move_cursors_with(|map, head, _| {
8734 (movement::next_word_end(map, head), SelectionGoal::None)
8735 });
8736 })
8737 }
8738
8739 pub fn move_to_next_subword_end(
8740 &mut self,
8741 _: &MoveToNextSubwordEnd,
8742 window: &mut Window,
8743 cx: &mut Context<Self>,
8744 ) {
8745 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8746 s.move_cursors_with(|map, head, _| {
8747 (movement::next_subword_end(map, head), SelectionGoal::None)
8748 });
8749 })
8750 }
8751
8752 pub fn select_to_next_word_end(
8753 &mut self,
8754 _: &SelectToNextWordEnd,
8755 window: &mut Window,
8756 cx: &mut Context<Self>,
8757 ) {
8758 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8759 s.move_heads_with(|map, head, _| {
8760 (movement::next_word_end(map, head), SelectionGoal::None)
8761 });
8762 })
8763 }
8764
8765 pub fn select_to_next_subword_end(
8766 &mut self,
8767 _: &SelectToNextSubwordEnd,
8768 window: &mut Window,
8769 cx: &mut Context<Self>,
8770 ) {
8771 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8772 s.move_heads_with(|map, head, _| {
8773 (movement::next_subword_end(map, head), SelectionGoal::None)
8774 });
8775 })
8776 }
8777
8778 pub fn delete_to_next_word_end(
8779 &mut self,
8780 action: &DeleteToNextWordEnd,
8781 window: &mut Window,
8782 cx: &mut Context<Self>,
8783 ) {
8784 self.transact(window, cx, |this, window, cx| {
8785 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8786 let line_mode = s.line_mode;
8787 s.move_with(|map, selection| {
8788 if selection.is_empty() && !line_mode {
8789 let cursor = if action.ignore_newlines {
8790 movement::next_word_end(map, selection.head())
8791 } else {
8792 movement::next_word_end_or_newline(map, selection.head())
8793 };
8794 selection.set_head(cursor, SelectionGoal::None);
8795 }
8796 });
8797 });
8798 this.insert("", window, cx);
8799 });
8800 }
8801
8802 pub fn delete_to_next_subword_end(
8803 &mut self,
8804 _: &DeleteToNextSubwordEnd,
8805 window: &mut Window,
8806 cx: &mut Context<Self>,
8807 ) {
8808 self.transact(window, cx, |this, window, cx| {
8809 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8810 s.move_with(|map, selection| {
8811 if selection.is_empty() {
8812 let cursor = movement::next_subword_end(map, selection.head());
8813 selection.set_head(cursor, SelectionGoal::None);
8814 }
8815 });
8816 });
8817 this.insert("", window, cx);
8818 });
8819 }
8820
8821 pub fn move_to_beginning_of_line(
8822 &mut self,
8823 action: &MoveToBeginningOfLine,
8824 window: &mut Window,
8825 cx: &mut Context<Self>,
8826 ) {
8827 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8828 s.move_cursors_with(|map, head, _| {
8829 (
8830 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8831 SelectionGoal::None,
8832 )
8833 });
8834 })
8835 }
8836
8837 pub fn select_to_beginning_of_line(
8838 &mut self,
8839 action: &SelectToBeginningOfLine,
8840 window: &mut Window,
8841 cx: &mut Context<Self>,
8842 ) {
8843 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8844 s.move_heads_with(|map, head, _| {
8845 (
8846 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8847 SelectionGoal::None,
8848 )
8849 });
8850 });
8851 }
8852
8853 pub fn delete_to_beginning_of_line(
8854 &mut self,
8855 _: &DeleteToBeginningOfLine,
8856 window: &mut Window,
8857 cx: &mut Context<Self>,
8858 ) {
8859 self.transact(window, cx, |this, window, cx| {
8860 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8861 s.move_with(|_, selection| {
8862 selection.reversed = true;
8863 });
8864 });
8865
8866 this.select_to_beginning_of_line(
8867 &SelectToBeginningOfLine {
8868 stop_at_soft_wraps: false,
8869 },
8870 window,
8871 cx,
8872 );
8873 this.backspace(&Backspace, window, cx);
8874 });
8875 }
8876
8877 pub fn move_to_end_of_line(
8878 &mut self,
8879 action: &MoveToEndOfLine,
8880 window: &mut Window,
8881 cx: &mut Context<Self>,
8882 ) {
8883 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8884 s.move_cursors_with(|map, head, _| {
8885 (
8886 movement::line_end(map, head, action.stop_at_soft_wraps),
8887 SelectionGoal::None,
8888 )
8889 });
8890 })
8891 }
8892
8893 pub fn select_to_end_of_line(
8894 &mut self,
8895 action: &SelectToEndOfLine,
8896 window: &mut Window,
8897 cx: &mut Context<Self>,
8898 ) {
8899 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8900 s.move_heads_with(|map, head, _| {
8901 (
8902 movement::line_end(map, head, action.stop_at_soft_wraps),
8903 SelectionGoal::None,
8904 )
8905 });
8906 })
8907 }
8908
8909 pub fn delete_to_end_of_line(
8910 &mut self,
8911 _: &DeleteToEndOfLine,
8912 window: &mut Window,
8913 cx: &mut Context<Self>,
8914 ) {
8915 self.transact(window, cx, |this, window, cx| {
8916 this.select_to_end_of_line(
8917 &SelectToEndOfLine {
8918 stop_at_soft_wraps: false,
8919 },
8920 window,
8921 cx,
8922 );
8923 this.delete(&Delete, window, cx);
8924 });
8925 }
8926
8927 pub fn cut_to_end_of_line(
8928 &mut self,
8929 _: &CutToEndOfLine,
8930 window: &mut Window,
8931 cx: &mut Context<Self>,
8932 ) {
8933 self.transact(window, cx, |this, window, cx| {
8934 this.select_to_end_of_line(
8935 &SelectToEndOfLine {
8936 stop_at_soft_wraps: false,
8937 },
8938 window,
8939 cx,
8940 );
8941 this.cut(&Cut, window, cx);
8942 });
8943 }
8944
8945 pub fn move_to_start_of_paragraph(
8946 &mut self,
8947 _: &MoveToStartOfParagraph,
8948 window: &mut Window,
8949 cx: &mut Context<Self>,
8950 ) {
8951 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8952 cx.propagate();
8953 return;
8954 }
8955
8956 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8957 s.move_with(|map, selection| {
8958 selection.collapse_to(
8959 movement::start_of_paragraph(map, selection.head(), 1),
8960 SelectionGoal::None,
8961 )
8962 });
8963 })
8964 }
8965
8966 pub fn move_to_end_of_paragraph(
8967 &mut self,
8968 _: &MoveToEndOfParagraph,
8969 window: &mut Window,
8970 cx: &mut Context<Self>,
8971 ) {
8972 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8973 cx.propagate();
8974 return;
8975 }
8976
8977 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8978 s.move_with(|map, selection| {
8979 selection.collapse_to(
8980 movement::end_of_paragraph(map, selection.head(), 1),
8981 SelectionGoal::None,
8982 )
8983 });
8984 })
8985 }
8986
8987 pub fn select_to_start_of_paragraph(
8988 &mut self,
8989 _: &SelectToStartOfParagraph,
8990 window: &mut Window,
8991 cx: &mut Context<Self>,
8992 ) {
8993 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8994 cx.propagate();
8995 return;
8996 }
8997
8998 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8999 s.move_heads_with(|map, head, _| {
9000 (
9001 movement::start_of_paragraph(map, head, 1),
9002 SelectionGoal::None,
9003 )
9004 });
9005 })
9006 }
9007
9008 pub fn select_to_end_of_paragraph(
9009 &mut self,
9010 _: &SelectToEndOfParagraph,
9011 window: &mut Window,
9012 cx: &mut Context<Self>,
9013 ) {
9014 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9015 cx.propagate();
9016 return;
9017 }
9018
9019 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9020 s.move_heads_with(|map, head, _| {
9021 (
9022 movement::end_of_paragraph(map, head, 1),
9023 SelectionGoal::None,
9024 )
9025 });
9026 })
9027 }
9028
9029 pub fn move_to_start_of_excerpt(
9030 &mut self,
9031 _: &MoveToStartOfExcerpt,
9032 window: &mut Window,
9033 cx: &mut Context<Self>,
9034 ) {
9035 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9036 cx.propagate();
9037 return;
9038 }
9039
9040 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9041 s.move_with(|map, selection| {
9042 selection.collapse_to(
9043 movement::start_of_excerpt(
9044 map,
9045 selection.head(),
9046 workspace::searchable::Direction::Prev,
9047 ),
9048 SelectionGoal::None,
9049 )
9050 });
9051 })
9052 }
9053
9054 pub fn move_to_end_of_excerpt(
9055 &mut self,
9056 _: &MoveToEndOfExcerpt,
9057 window: &mut Window,
9058 cx: &mut Context<Self>,
9059 ) {
9060 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9061 cx.propagate();
9062 return;
9063 }
9064
9065 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9066 s.move_with(|map, selection| {
9067 selection.collapse_to(
9068 movement::end_of_excerpt(
9069 map,
9070 selection.head(),
9071 workspace::searchable::Direction::Next,
9072 ),
9073 SelectionGoal::None,
9074 )
9075 });
9076 })
9077 }
9078
9079 pub fn select_to_start_of_excerpt(
9080 &mut self,
9081 _: &SelectToStartOfExcerpt,
9082 window: &mut Window,
9083 cx: &mut Context<Self>,
9084 ) {
9085 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9086 cx.propagate();
9087 return;
9088 }
9089
9090 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9091 s.move_heads_with(|map, head, _| {
9092 (
9093 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9094 SelectionGoal::None,
9095 )
9096 });
9097 })
9098 }
9099
9100 pub fn select_to_end_of_excerpt(
9101 &mut self,
9102 _: &SelectToEndOfExcerpt,
9103 window: &mut Window,
9104 cx: &mut Context<Self>,
9105 ) {
9106 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9107 cx.propagate();
9108 return;
9109 }
9110
9111 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9112 s.move_heads_with(|map, head, _| {
9113 (
9114 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9115 SelectionGoal::None,
9116 )
9117 });
9118 })
9119 }
9120
9121 pub fn move_to_beginning(
9122 &mut self,
9123 _: &MoveToBeginning,
9124 window: &mut Window,
9125 cx: &mut Context<Self>,
9126 ) {
9127 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9128 cx.propagate();
9129 return;
9130 }
9131
9132 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9133 s.select_ranges(vec![0..0]);
9134 });
9135 }
9136
9137 pub fn select_to_beginning(
9138 &mut self,
9139 _: &SelectToBeginning,
9140 window: &mut Window,
9141 cx: &mut Context<Self>,
9142 ) {
9143 let mut selection = self.selections.last::<Point>(cx);
9144 selection.set_head(Point::zero(), SelectionGoal::None);
9145
9146 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9147 s.select(vec![selection]);
9148 });
9149 }
9150
9151 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9152 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9153 cx.propagate();
9154 return;
9155 }
9156
9157 let cursor = self.buffer.read(cx).read(cx).len();
9158 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9159 s.select_ranges(vec![cursor..cursor])
9160 });
9161 }
9162
9163 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9164 self.nav_history = nav_history;
9165 }
9166
9167 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9168 self.nav_history.as_ref()
9169 }
9170
9171 fn push_to_nav_history(
9172 &mut self,
9173 cursor_anchor: Anchor,
9174 new_position: Option<Point>,
9175 cx: &mut Context<Self>,
9176 ) {
9177 if let Some(nav_history) = self.nav_history.as_mut() {
9178 let buffer = self.buffer.read(cx).read(cx);
9179 let cursor_position = cursor_anchor.to_point(&buffer);
9180 let scroll_state = self.scroll_manager.anchor();
9181 let scroll_top_row = scroll_state.top_row(&buffer);
9182 drop(buffer);
9183
9184 if let Some(new_position) = new_position {
9185 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9186 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9187 return;
9188 }
9189 }
9190
9191 nav_history.push(
9192 Some(NavigationData {
9193 cursor_anchor,
9194 cursor_position,
9195 scroll_anchor: scroll_state,
9196 scroll_top_row,
9197 }),
9198 cx,
9199 );
9200 }
9201 }
9202
9203 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9204 let buffer = self.buffer.read(cx).snapshot(cx);
9205 let mut selection = self.selections.first::<usize>(cx);
9206 selection.set_head(buffer.len(), SelectionGoal::None);
9207 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9208 s.select(vec![selection]);
9209 });
9210 }
9211
9212 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9213 let end = self.buffer.read(cx).read(cx).len();
9214 self.change_selections(None, window, cx, |s| {
9215 s.select_ranges(vec![0..end]);
9216 });
9217 }
9218
9219 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9220 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9221 let mut selections = self.selections.all::<Point>(cx);
9222 let max_point = display_map.buffer_snapshot.max_point();
9223 for selection in &mut selections {
9224 let rows = selection.spanned_rows(true, &display_map);
9225 selection.start = Point::new(rows.start.0, 0);
9226 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9227 selection.reversed = false;
9228 }
9229 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9230 s.select(selections);
9231 });
9232 }
9233
9234 pub fn split_selection_into_lines(
9235 &mut self,
9236 _: &SplitSelectionIntoLines,
9237 window: &mut Window,
9238 cx: &mut Context<Self>,
9239 ) {
9240 let selections = self
9241 .selections
9242 .all::<Point>(cx)
9243 .into_iter()
9244 .map(|selection| selection.start..selection.end)
9245 .collect::<Vec<_>>();
9246 self.unfold_ranges(&selections, true, true, cx);
9247
9248 let mut new_selection_ranges = Vec::new();
9249 {
9250 let buffer = self.buffer.read(cx).read(cx);
9251 for selection in selections {
9252 for row in selection.start.row..selection.end.row {
9253 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9254 new_selection_ranges.push(cursor..cursor);
9255 }
9256
9257 let is_multiline_selection = selection.start.row != selection.end.row;
9258 // Don't insert last one if it's a multi-line selection ending at the start of a line,
9259 // so this action feels more ergonomic when paired with other selection operations
9260 let should_skip_last = is_multiline_selection && selection.end.column == 0;
9261 if !should_skip_last {
9262 new_selection_ranges.push(selection.end..selection.end);
9263 }
9264 }
9265 }
9266 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9267 s.select_ranges(new_selection_ranges);
9268 });
9269 }
9270
9271 pub fn add_selection_above(
9272 &mut self,
9273 _: &AddSelectionAbove,
9274 window: &mut Window,
9275 cx: &mut Context<Self>,
9276 ) {
9277 self.add_selection(true, window, cx);
9278 }
9279
9280 pub fn add_selection_below(
9281 &mut self,
9282 _: &AddSelectionBelow,
9283 window: &mut Window,
9284 cx: &mut Context<Self>,
9285 ) {
9286 self.add_selection(false, window, cx);
9287 }
9288
9289 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9290 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9291 let mut selections = self.selections.all::<Point>(cx);
9292 let text_layout_details = self.text_layout_details(window);
9293 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9294 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9295 let range = oldest_selection.display_range(&display_map).sorted();
9296
9297 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9298 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9299 let positions = start_x.min(end_x)..start_x.max(end_x);
9300
9301 selections.clear();
9302 let mut stack = Vec::new();
9303 for row in range.start.row().0..=range.end.row().0 {
9304 if let Some(selection) = self.selections.build_columnar_selection(
9305 &display_map,
9306 DisplayRow(row),
9307 &positions,
9308 oldest_selection.reversed,
9309 &text_layout_details,
9310 ) {
9311 stack.push(selection.id);
9312 selections.push(selection);
9313 }
9314 }
9315
9316 if above {
9317 stack.reverse();
9318 }
9319
9320 AddSelectionsState { above, stack }
9321 });
9322
9323 let last_added_selection = *state.stack.last().unwrap();
9324 let mut new_selections = Vec::new();
9325 if above == state.above {
9326 let end_row = if above {
9327 DisplayRow(0)
9328 } else {
9329 display_map.max_point().row()
9330 };
9331
9332 'outer: for selection in selections {
9333 if selection.id == last_added_selection {
9334 let range = selection.display_range(&display_map).sorted();
9335 debug_assert_eq!(range.start.row(), range.end.row());
9336 let mut row = range.start.row();
9337 let positions =
9338 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9339 px(start)..px(end)
9340 } else {
9341 let start_x =
9342 display_map.x_for_display_point(range.start, &text_layout_details);
9343 let end_x =
9344 display_map.x_for_display_point(range.end, &text_layout_details);
9345 start_x.min(end_x)..start_x.max(end_x)
9346 };
9347
9348 while row != end_row {
9349 if above {
9350 row.0 -= 1;
9351 } else {
9352 row.0 += 1;
9353 }
9354
9355 if let Some(new_selection) = self.selections.build_columnar_selection(
9356 &display_map,
9357 row,
9358 &positions,
9359 selection.reversed,
9360 &text_layout_details,
9361 ) {
9362 state.stack.push(new_selection.id);
9363 if above {
9364 new_selections.push(new_selection);
9365 new_selections.push(selection);
9366 } else {
9367 new_selections.push(selection);
9368 new_selections.push(new_selection);
9369 }
9370
9371 continue 'outer;
9372 }
9373 }
9374 }
9375
9376 new_selections.push(selection);
9377 }
9378 } else {
9379 new_selections = selections;
9380 new_selections.retain(|s| s.id != last_added_selection);
9381 state.stack.pop();
9382 }
9383
9384 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9385 s.select(new_selections);
9386 });
9387 if state.stack.len() > 1 {
9388 self.add_selections_state = Some(state);
9389 }
9390 }
9391
9392 pub fn select_next_match_internal(
9393 &mut self,
9394 display_map: &DisplaySnapshot,
9395 replace_newest: bool,
9396 autoscroll: Option<Autoscroll>,
9397 window: &mut Window,
9398 cx: &mut Context<Self>,
9399 ) -> Result<()> {
9400 fn select_next_match_ranges(
9401 this: &mut Editor,
9402 range: Range<usize>,
9403 replace_newest: bool,
9404 auto_scroll: Option<Autoscroll>,
9405 window: &mut Window,
9406 cx: &mut Context<Editor>,
9407 ) {
9408 this.unfold_ranges(&[range.clone()], false, true, cx);
9409 this.change_selections(auto_scroll, window, cx, |s| {
9410 if replace_newest {
9411 s.delete(s.newest_anchor().id);
9412 }
9413 s.insert_range(range.clone());
9414 });
9415 }
9416
9417 let buffer = &display_map.buffer_snapshot;
9418 let mut selections = self.selections.all::<usize>(cx);
9419 if let Some(mut select_next_state) = self.select_next_state.take() {
9420 let query = &select_next_state.query;
9421 if !select_next_state.done {
9422 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9423 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9424 let mut next_selected_range = None;
9425
9426 let bytes_after_last_selection =
9427 buffer.bytes_in_range(last_selection.end..buffer.len());
9428 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9429 let query_matches = query
9430 .stream_find_iter(bytes_after_last_selection)
9431 .map(|result| (last_selection.end, result))
9432 .chain(
9433 query
9434 .stream_find_iter(bytes_before_first_selection)
9435 .map(|result| (0, result)),
9436 );
9437
9438 for (start_offset, query_match) in query_matches {
9439 let query_match = query_match.unwrap(); // can only fail due to I/O
9440 let offset_range =
9441 start_offset + query_match.start()..start_offset + query_match.end();
9442 let display_range = offset_range.start.to_display_point(display_map)
9443 ..offset_range.end.to_display_point(display_map);
9444
9445 if !select_next_state.wordwise
9446 || (!movement::is_inside_word(display_map, display_range.start)
9447 && !movement::is_inside_word(display_map, display_range.end))
9448 {
9449 // TODO: This is n^2, because we might check all the selections
9450 if !selections
9451 .iter()
9452 .any(|selection| selection.range().overlaps(&offset_range))
9453 {
9454 next_selected_range = Some(offset_range);
9455 break;
9456 }
9457 }
9458 }
9459
9460 if let Some(next_selected_range) = next_selected_range {
9461 select_next_match_ranges(
9462 self,
9463 next_selected_range,
9464 replace_newest,
9465 autoscroll,
9466 window,
9467 cx,
9468 );
9469 } else {
9470 select_next_state.done = true;
9471 }
9472 }
9473
9474 self.select_next_state = Some(select_next_state);
9475 } else {
9476 let mut only_carets = true;
9477 let mut same_text_selected = true;
9478 let mut selected_text = None;
9479
9480 let mut selections_iter = selections.iter().peekable();
9481 while let Some(selection) = selections_iter.next() {
9482 if selection.start != selection.end {
9483 only_carets = false;
9484 }
9485
9486 if same_text_selected {
9487 if selected_text.is_none() {
9488 selected_text =
9489 Some(buffer.text_for_range(selection.range()).collect::<String>());
9490 }
9491
9492 if let Some(next_selection) = selections_iter.peek() {
9493 if next_selection.range().len() == selection.range().len() {
9494 let next_selected_text = buffer
9495 .text_for_range(next_selection.range())
9496 .collect::<String>();
9497 if Some(next_selected_text) != selected_text {
9498 same_text_selected = false;
9499 selected_text = None;
9500 }
9501 } else {
9502 same_text_selected = false;
9503 selected_text = None;
9504 }
9505 }
9506 }
9507 }
9508
9509 if only_carets {
9510 for selection in &mut selections {
9511 let word_range = movement::surrounding_word(
9512 display_map,
9513 selection.start.to_display_point(display_map),
9514 );
9515 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9516 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9517 selection.goal = SelectionGoal::None;
9518 selection.reversed = false;
9519 select_next_match_ranges(
9520 self,
9521 selection.start..selection.end,
9522 replace_newest,
9523 autoscroll,
9524 window,
9525 cx,
9526 );
9527 }
9528
9529 if selections.len() == 1 {
9530 let selection = selections
9531 .last()
9532 .expect("ensured that there's only one selection");
9533 let query = buffer
9534 .text_for_range(selection.start..selection.end)
9535 .collect::<String>();
9536 let is_empty = query.is_empty();
9537 let select_state = SelectNextState {
9538 query: AhoCorasick::new(&[query])?,
9539 wordwise: true,
9540 done: is_empty,
9541 };
9542 self.select_next_state = Some(select_state);
9543 } else {
9544 self.select_next_state = None;
9545 }
9546 } else if let Some(selected_text) = selected_text {
9547 self.select_next_state = Some(SelectNextState {
9548 query: AhoCorasick::new(&[selected_text])?,
9549 wordwise: false,
9550 done: false,
9551 });
9552 self.select_next_match_internal(
9553 display_map,
9554 replace_newest,
9555 autoscroll,
9556 window,
9557 cx,
9558 )?;
9559 }
9560 }
9561 Ok(())
9562 }
9563
9564 pub fn select_all_matches(
9565 &mut self,
9566 _action: &SelectAllMatches,
9567 window: &mut Window,
9568 cx: &mut Context<Self>,
9569 ) -> Result<()> {
9570 self.push_to_selection_history();
9571 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9572
9573 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9574 let Some(select_next_state) = self.select_next_state.as_mut() else {
9575 return Ok(());
9576 };
9577 if select_next_state.done {
9578 return Ok(());
9579 }
9580
9581 let mut new_selections = self.selections.all::<usize>(cx);
9582
9583 let buffer = &display_map.buffer_snapshot;
9584 let query_matches = select_next_state
9585 .query
9586 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9587
9588 for query_match in query_matches {
9589 let query_match = query_match.unwrap(); // can only fail due to I/O
9590 let offset_range = query_match.start()..query_match.end();
9591 let display_range = offset_range.start.to_display_point(&display_map)
9592 ..offset_range.end.to_display_point(&display_map);
9593
9594 if !select_next_state.wordwise
9595 || (!movement::is_inside_word(&display_map, display_range.start)
9596 && !movement::is_inside_word(&display_map, display_range.end))
9597 {
9598 self.selections.change_with(cx, |selections| {
9599 new_selections.push(Selection {
9600 id: selections.new_selection_id(),
9601 start: offset_range.start,
9602 end: offset_range.end,
9603 reversed: false,
9604 goal: SelectionGoal::None,
9605 });
9606 });
9607 }
9608 }
9609
9610 new_selections.sort_by_key(|selection| selection.start);
9611 let mut ix = 0;
9612 while ix + 1 < new_selections.len() {
9613 let current_selection = &new_selections[ix];
9614 let next_selection = &new_selections[ix + 1];
9615 if current_selection.range().overlaps(&next_selection.range()) {
9616 if current_selection.id < next_selection.id {
9617 new_selections.remove(ix + 1);
9618 } else {
9619 new_selections.remove(ix);
9620 }
9621 } else {
9622 ix += 1;
9623 }
9624 }
9625
9626 let reversed = self.selections.oldest::<usize>(cx).reversed;
9627
9628 for selection in new_selections.iter_mut() {
9629 selection.reversed = reversed;
9630 }
9631
9632 select_next_state.done = true;
9633 self.unfold_ranges(
9634 &new_selections
9635 .iter()
9636 .map(|selection| selection.range())
9637 .collect::<Vec<_>>(),
9638 false,
9639 false,
9640 cx,
9641 );
9642 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9643 selections.select(new_selections)
9644 });
9645
9646 Ok(())
9647 }
9648
9649 pub fn select_next(
9650 &mut self,
9651 action: &SelectNext,
9652 window: &mut Window,
9653 cx: &mut Context<Self>,
9654 ) -> Result<()> {
9655 self.push_to_selection_history();
9656 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9657 self.select_next_match_internal(
9658 &display_map,
9659 action.replace_newest,
9660 Some(Autoscroll::newest()),
9661 window,
9662 cx,
9663 )?;
9664 Ok(())
9665 }
9666
9667 pub fn select_previous(
9668 &mut self,
9669 action: &SelectPrevious,
9670 window: &mut Window,
9671 cx: &mut Context<Self>,
9672 ) -> Result<()> {
9673 self.push_to_selection_history();
9674 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9675 let buffer = &display_map.buffer_snapshot;
9676 let mut selections = self.selections.all::<usize>(cx);
9677 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9678 let query = &select_prev_state.query;
9679 if !select_prev_state.done {
9680 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9681 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9682 let mut next_selected_range = None;
9683 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9684 let bytes_before_last_selection =
9685 buffer.reversed_bytes_in_range(0..last_selection.start);
9686 let bytes_after_first_selection =
9687 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9688 let query_matches = query
9689 .stream_find_iter(bytes_before_last_selection)
9690 .map(|result| (last_selection.start, result))
9691 .chain(
9692 query
9693 .stream_find_iter(bytes_after_first_selection)
9694 .map(|result| (buffer.len(), result)),
9695 );
9696 for (end_offset, query_match) in query_matches {
9697 let query_match = query_match.unwrap(); // can only fail due to I/O
9698 let offset_range =
9699 end_offset - query_match.end()..end_offset - query_match.start();
9700 let display_range = offset_range.start.to_display_point(&display_map)
9701 ..offset_range.end.to_display_point(&display_map);
9702
9703 if !select_prev_state.wordwise
9704 || (!movement::is_inside_word(&display_map, display_range.start)
9705 && !movement::is_inside_word(&display_map, display_range.end))
9706 {
9707 next_selected_range = Some(offset_range);
9708 break;
9709 }
9710 }
9711
9712 if let Some(next_selected_range) = next_selected_range {
9713 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9714 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9715 if action.replace_newest {
9716 s.delete(s.newest_anchor().id);
9717 }
9718 s.insert_range(next_selected_range);
9719 });
9720 } else {
9721 select_prev_state.done = true;
9722 }
9723 }
9724
9725 self.select_prev_state = Some(select_prev_state);
9726 } else {
9727 let mut only_carets = true;
9728 let mut same_text_selected = true;
9729 let mut selected_text = None;
9730
9731 let mut selections_iter = selections.iter().peekable();
9732 while let Some(selection) = selections_iter.next() {
9733 if selection.start != selection.end {
9734 only_carets = false;
9735 }
9736
9737 if same_text_selected {
9738 if selected_text.is_none() {
9739 selected_text =
9740 Some(buffer.text_for_range(selection.range()).collect::<String>());
9741 }
9742
9743 if let Some(next_selection) = selections_iter.peek() {
9744 if next_selection.range().len() == selection.range().len() {
9745 let next_selected_text = buffer
9746 .text_for_range(next_selection.range())
9747 .collect::<String>();
9748 if Some(next_selected_text) != selected_text {
9749 same_text_selected = false;
9750 selected_text = None;
9751 }
9752 } else {
9753 same_text_selected = false;
9754 selected_text = None;
9755 }
9756 }
9757 }
9758 }
9759
9760 if only_carets {
9761 for selection in &mut selections {
9762 let word_range = movement::surrounding_word(
9763 &display_map,
9764 selection.start.to_display_point(&display_map),
9765 );
9766 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9767 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9768 selection.goal = SelectionGoal::None;
9769 selection.reversed = false;
9770 }
9771 if selections.len() == 1 {
9772 let selection = selections
9773 .last()
9774 .expect("ensured that there's only one selection");
9775 let query = buffer
9776 .text_for_range(selection.start..selection.end)
9777 .collect::<String>();
9778 let is_empty = query.is_empty();
9779 let select_state = SelectNextState {
9780 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9781 wordwise: true,
9782 done: is_empty,
9783 };
9784 self.select_prev_state = Some(select_state);
9785 } else {
9786 self.select_prev_state = None;
9787 }
9788
9789 self.unfold_ranges(
9790 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9791 false,
9792 true,
9793 cx,
9794 );
9795 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9796 s.select(selections);
9797 });
9798 } else if let Some(selected_text) = selected_text {
9799 self.select_prev_state = Some(SelectNextState {
9800 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9801 wordwise: false,
9802 done: false,
9803 });
9804 self.select_previous(action, window, cx)?;
9805 }
9806 }
9807 Ok(())
9808 }
9809
9810 pub fn toggle_comments(
9811 &mut self,
9812 action: &ToggleComments,
9813 window: &mut Window,
9814 cx: &mut Context<Self>,
9815 ) {
9816 if self.read_only(cx) {
9817 return;
9818 }
9819 let text_layout_details = &self.text_layout_details(window);
9820 self.transact(window, cx, |this, window, cx| {
9821 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9822 let mut edits = Vec::new();
9823 let mut selection_edit_ranges = Vec::new();
9824 let mut last_toggled_row = None;
9825 let snapshot = this.buffer.read(cx).read(cx);
9826 let empty_str: Arc<str> = Arc::default();
9827 let mut suffixes_inserted = Vec::new();
9828 let ignore_indent = action.ignore_indent;
9829
9830 fn comment_prefix_range(
9831 snapshot: &MultiBufferSnapshot,
9832 row: MultiBufferRow,
9833 comment_prefix: &str,
9834 comment_prefix_whitespace: &str,
9835 ignore_indent: bool,
9836 ) -> Range<Point> {
9837 let indent_size = if ignore_indent {
9838 0
9839 } else {
9840 snapshot.indent_size_for_line(row).len
9841 };
9842
9843 let start = Point::new(row.0, indent_size);
9844
9845 let mut line_bytes = snapshot
9846 .bytes_in_range(start..snapshot.max_point())
9847 .flatten()
9848 .copied();
9849
9850 // If this line currently begins with the line comment prefix, then record
9851 // the range containing the prefix.
9852 if line_bytes
9853 .by_ref()
9854 .take(comment_prefix.len())
9855 .eq(comment_prefix.bytes())
9856 {
9857 // Include any whitespace that matches the comment prefix.
9858 let matching_whitespace_len = line_bytes
9859 .zip(comment_prefix_whitespace.bytes())
9860 .take_while(|(a, b)| a == b)
9861 .count() as u32;
9862 let end = Point::new(
9863 start.row,
9864 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9865 );
9866 start..end
9867 } else {
9868 start..start
9869 }
9870 }
9871
9872 fn comment_suffix_range(
9873 snapshot: &MultiBufferSnapshot,
9874 row: MultiBufferRow,
9875 comment_suffix: &str,
9876 comment_suffix_has_leading_space: bool,
9877 ) -> Range<Point> {
9878 let end = Point::new(row.0, snapshot.line_len(row));
9879 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9880
9881 let mut line_end_bytes = snapshot
9882 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9883 .flatten()
9884 .copied();
9885
9886 let leading_space_len = if suffix_start_column > 0
9887 && line_end_bytes.next() == Some(b' ')
9888 && comment_suffix_has_leading_space
9889 {
9890 1
9891 } else {
9892 0
9893 };
9894
9895 // If this line currently begins with the line comment prefix, then record
9896 // the range containing the prefix.
9897 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9898 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9899 start..end
9900 } else {
9901 end..end
9902 }
9903 }
9904
9905 // TODO: Handle selections that cross excerpts
9906 for selection in &mut selections {
9907 let start_column = snapshot
9908 .indent_size_for_line(MultiBufferRow(selection.start.row))
9909 .len;
9910 let language = if let Some(language) =
9911 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9912 {
9913 language
9914 } else {
9915 continue;
9916 };
9917
9918 selection_edit_ranges.clear();
9919
9920 // If multiple selections contain a given row, avoid processing that
9921 // row more than once.
9922 let mut start_row = MultiBufferRow(selection.start.row);
9923 if last_toggled_row == Some(start_row) {
9924 start_row = start_row.next_row();
9925 }
9926 let end_row =
9927 if selection.end.row > selection.start.row && selection.end.column == 0 {
9928 MultiBufferRow(selection.end.row - 1)
9929 } else {
9930 MultiBufferRow(selection.end.row)
9931 };
9932 last_toggled_row = Some(end_row);
9933
9934 if start_row > end_row {
9935 continue;
9936 }
9937
9938 // If the language has line comments, toggle those.
9939 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9940
9941 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9942 if ignore_indent {
9943 full_comment_prefixes = full_comment_prefixes
9944 .into_iter()
9945 .map(|s| Arc::from(s.trim_end()))
9946 .collect();
9947 }
9948
9949 if !full_comment_prefixes.is_empty() {
9950 let first_prefix = full_comment_prefixes
9951 .first()
9952 .expect("prefixes is non-empty");
9953 let prefix_trimmed_lengths = full_comment_prefixes
9954 .iter()
9955 .map(|p| p.trim_end_matches(' ').len())
9956 .collect::<SmallVec<[usize; 4]>>();
9957
9958 let mut all_selection_lines_are_comments = true;
9959
9960 for row in start_row.0..=end_row.0 {
9961 let row = MultiBufferRow(row);
9962 if start_row < end_row && snapshot.is_line_blank(row) {
9963 continue;
9964 }
9965
9966 let prefix_range = full_comment_prefixes
9967 .iter()
9968 .zip(prefix_trimmed_lengths.iter().copied())
9969 .map(|(prefix, trimmed_prefix_len)| {
9970 comment_prefix_range(
9971 snapshot.deref(),
9972 row,
9973 &prefix[..trimmed_prefix_len],
9974 &prefix[trimmed_prefix_len..],
9975 ignore_indent,
9976 )
9977 })
9978 .max_by_key(|range| range.end.column - range.start.column)
9979 .expect("prefixes is non-empty");
9980
9981 if prefix_range.is_empty() {
9982 all_selection_lines_are_comments = false;
9983 }
9984
9985 selection_edit_ranges.push(prefix_range);
9986 }
9987
9988 if all_selection_lines_are_comments {
9989 edits.extend(
9990 selection_edit_ranges
9991 .iter()
9992 .cloned()
9993 .map(|range| (range, empty_str.clone())),
9994 );
9995 } else {
9996 let min_column = selection_edit_ranges
9997 .iter()
9998 .map(|range| range.start.column)
9999 .min()
10000 .unwrap_or(0);
10001 edits.extend(selection_edit_ranges.iter().map(|range| {
10002 let position = Point::new(range.start.row, min_column);
10003 (position..position, first_prefix.clone())
10004 }));
10005 }
10006 } else if let Some((full_comment_prefix, comment_suffix)) =
10007 language.block_comment_delimiters()
10008 {
10009 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10010 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10011 let prefix_range = comment_prefix_range(
10012 snapshot.deref(),
10013 start_row,
10014 comment_prefix,
10015 comment_prefix_whitespace,
10016 ignore_indent,
10017 );
10018 let suffix_range = comment_suffix_range(
10019 snapshot.deref(),
10020 end_row,
10021 comment_suffix.trim_start_matches(' '),
10022 comment_suffix.starts_with(' '),
10023 );
10024
10025 if prefix_range.is_empty() || suffix_range.is_empty() {
10026 edits.push((
10027 prefix_range.start..prefix_range.start,
10028 full_comment_prefix.clone(),
10029 ));
10030 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10031 suffixes_inserted.push((end_row, comment_suffix.len()));
10032 } else {
10033 edits.push((prefix_range, empty_str.clone()));
10034 edits.push((suffix_range, empty_str.clone()));
10035 }
10036 } else {
10037 continue;
10038 }
10039 }
10040
10041 drop(snapshot);
10042 this.buffer.update(cx, |buffer, cx| {
10043 buffer.edit(edits, None, cx);
10044 });
10045
10046 // Adjust selections so that they end before any comment suffixes that
10047 // were inserted.
10048 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10049 let mut selections = this.selections.all::<Point>(cx);
10050 let snapshot = this.buffer.read(cx).read(cx);
10051 for selection in &mut selections {
10052 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10053 match row.cmp(&MultiBufferRow(selection.end.row)) {
10054 Ordering::Less => {
10055 suffixes_inserted.next();
10056 continue;
10057 }
10058 Ordering::Greater => break,
10059 Ordering::Equal => {
10060 if selection.end.column == snapshot.line_len(row) {
10061 if selection.is_empty() {
10062 selection.start.column -= suffix_len as u32;
10063 }
10064 selection.end.column -= suffix_len as u32;
10065 }
10066 break;
10067 }
10068 }
10069 }
10070 }
10071
10072 drop(snapshot);
10073 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10074 s.select(selections)
10075 });
10076
10077 let selections = this.selections.all::<Point>(cx);
10078 let selections_on_single_row = selections.windows(2).all(|selections| {
10079 selections[0].start.row == selections[1].start.row
10080 && selections[0].end.row == selections[1].end.row
10081 && selections[0].start.row == selections[0].end.row
10082 });
10083 let selections_selecting = selections
10084 .iter()
10085 .any(|selection| selection.start != selection.end);
10086 let advance_downwards = action.advance_downwards
10087 && selections_on_single_row
10088 && !selections_selecting
10089 && !matches!(this.mode, EditorMode::SingleLine { .. });
10090
10091 if advance_downwards {
10092 let snapshot = this.buffer.read(cx).snapshot(cx);
10093
10094 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10095 s.move_cursors_with(|display_snapshot, display_point, _| {
10096 let mut point = display_point.to_point(display_snapshot);
10097 point.row += 1;
10098 point = snapshot.clip_point(point, Bias::Left);
10099 let display_point = point.to_display_point(display_snapshot);
10100 let goal = SelectionGoal::HorizontalPosition(
10101 display_snapshot
10102 .x_for_display_point(display_point, text_layout_details)
10103 .into(),
10104 );
10105 (display_point, goal)
10106 })
10107 });
10108 }
10109 });
10110 }
10111
10112 pub fn select_enclosing_symbol(
10113 &mut self,
10114 _: &SelectEnclosingSymbol,
10115 window: &mut Window,
10116 cx: &mut Context<Self>,
10117 ) {
10118 let buffer = self.buffer.read(cx).snapshot(cx);
10119 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10120
10121 fn update_selection(
10122 selection: &Selection<usize>,
10123 buffer_snap: &MultiBufferSnapshot,
10124 ) -> Option<Selection<usize>> {
10125 let cursor = selection.head();
10126 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10127 for symbol in symbols.iter().rev() {
10128 let start = symbol.range.start.to_offset(buffer_snap);
10129 let end = symbol.range.end.to_offset(buffer_snap);
10130 let new_range = start..end;
10131 if start < selection.start || end > selection.end {
10132 return Some(Selection {
10133 id: selection.id,
10134 start: new_range.start,
10135 end: new_range.end,
10136 goal: SelectionGoal::None,
10137 reversed: selection.reversed,
10138 });
10139 }
10140 }
10141 None
10142 }
10143
10144 let mut selected_larger_symbol = false;
10145 let new_selections = old_selections
10146 .iter()
10147 .map(|selection| match update_selection(selection, &buffer) {
10148 Some(new_selection) => {
10149 if new_selection.range() != selection.range() {
10150 selected_larger_symbol = true;
10151 }
10152 new_selection
10153 }
10154 None => selection.clone(),
10155 })
10156 .collect::<Vec<_>>();
10157
10158 if selected_larger_symbol {
10159 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10160 s.select(new_selections);
10161 });
10162 }
10163 }
10164
10165 pub fn select_larger_syntax_node(
10166 &mut self,
10167 _: &SelectLargerSyntaxNode,
10168 window: &mut Window,
10169 cx: &mut Context<Self>,
10170 ) {
10171 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10172 let buffer = self.buffer.read(cx).snapshot(cx);
10173 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10174
10175 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10176 let mut selected_larger_node = false;
10177 let new_selections = old_selections
10178 .iter()
10179 .map(|selection| {
10180 let old_range = selection.start..selection.end;
10181 let mut new_range = old_range.clone();
10182 let mut new_node = None;
10183 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10184 {
10185 new_node = Some(node);
10186 new_range = containing_range;
10187 if !display_map.intersects_fold(new_range.start)
10188 && !display_map.intersects_fold(new_range.end)
10189 {
10190 break;
10191 }
10192 }
10193
10194 if let Some(node) = new_node {
10195 // Log the ancestor, to support using this action as a way to explore TreeSitter
10196 // nodes. Parent and grandparent are also logged because this operation will not
10197 // visit nodes that have the same range as their parent.
10198 log::info!("Node: {node:?}");
10199 let parent = node.parent();
10200 log::info!("Parent: {parent:?}");
10201 let grandparent = parent.and_then(|x| x.parent());
10202 log::info!("Grandparent: {grandparent:?}");
10203 }
10204
10205 selected_larger_node |= new_range != old_range;
10206 Selection {
10207 id: selection.id,
10208 start: new_range.start,
10209 end: new_range.end,
10210 goal: SelectionGoal::None,
10211 reversed: selection.reversed,
10212 }
10213 })
10214 .collect::<Vec<_>>();
10215
10216 if selected_larger_node {
10217 stack.push(old_selections);
10218 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10219 s.select(new_selections);
10220 });
10221 }
10222 self.select_larger_syntax_node_stack = stack;
10223 }
10224
10225 pub fn select_smaller_syntax_node(
10226 &mut self,
10227 _: &SelectSmallerSyntaxNode,
10228 window: &mut Window,
10229 cx: &mut Context<Self>,
10230 ) {
10231 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10232 if let Some(selections) = stack.pop() {
10233 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10234 s.select(selections.to_vec());
10235 });
10236 }
10237 self.select_larger_syntax_node_stack = stack;
10238 }
10239
10240 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10241 if !EditorSettings::get_global(cx).gutter.runnables {
10242 self.clear_tasks();
10243 return Task::ready(());
10244 }
10245 let project = self.project.as_ref().map(Entity::downgrade);
10246 cx.spawn_in(window, |this, mut cx| async move {
10247 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10248 let Some(project) = project.and_then(|p| p.upgrade()) else {
10249 return;
10250 };
10251 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10252 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10253 }) else {
10254 return;
10255 };
10256
10257 let hide_runnables = project
10258 .update(&mut cx, |project, cx| {
10259 // Do not display any test indicators in non-dev server remote projects.
10260 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10261 })
10262 .unwrap_or(true);
10263 if hide_runnables {
10264 return;
10265 }
10266 let new_rows =
10267 cx.background_spawn({
10268 let snapshot = display_snapshot.clone();
10269 async move {
10270 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10271 }
10272 })
10273 .await;
10274
10275 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10276 this.update(&mut cx, |this, _| {
10277 this.clear_tasks();
10278 for (key, value) in rows {
10279 this.insert_tasks(key, value);
10280 }
10281 })
10282 .ok();
10283 })
10284 }
10285 fn fetch_runnable_ranges(
10286 snapshot: &DisplaySnapshot,
10287 range: Range<Anchor>,
10288 ) -> Vec<language::RunnableRange> {
10289 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10290 }
10291
10292 fn runnable_rows(
10293 project: Entity<Project>,
10294 snapshot: DisplaySnapshot,
10295 runnable_ranges: Vec<RunnableRange>,
10296 mut cx: AsyncWindowContext,
10297 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10298 runnable_ranges
10299 .into_iter()
10300 .filter_map(|mut runnable| {
10301 let tasks = cx
10302 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10303 .ok()?;
10304 if tasks.is_empty() {
10305 return None;
10306 }
10307
10308 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10309
10310 let row = snapshot
10311 .buffer_snapshot
10312 .buffer_line_for_row(MultiBufferRow(point.row))?
10313 .1
10314 .start
10315 .row;
10316
10317 let context_range =
10318 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10319 Some((
10320 (runnable.buffer_id, row),
10321 RunnableTasks {
10322 templates: tasks,
10323 offset: MultiBufferOffset(runnable.run_range.start),
10324 context_range,
10325 column: point.column,
10326 extra_variables: runnable.extra_captures,
10327 },
10328 ))
10329 })
10330 .collect()
10331 }
10332
10333 fn templates_with_tags(
10334 project: &Entity<Project>,
10335 runnable: &mut Runnable,
10336 cx: &mut App,
10337 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10338 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10339 let (worktree_id, file) = project
10340 .buffer_for_id(runnable.buffer, cx)
10341 .and_then(|buffer| buffer.read(cx).file())
10342 .map(|file| (file.worktree_id(cx), file.clone()))
10343 .unzip();
10344
10345 (
10346 project.task_store().read(cx).task_inventory().cloned(),
10347 worktree_id,
10348 file,
10349 )
10350 });
10351
10352 let tags = mem::take(&mut runnable.tags);
10353 let mut tags: Vec<_> = tags
10354 .into_iter()
10355 .flat_map(|tag| {
10356 let tag = tag.0.clone();
10357 inventory
10358 .as_ref()
10359 .into_iter()
10360 .flat_map(|inventory| {
10361 inventory.read(cx).list_tasks(
10362 file.clone(),
10363 Some(runnable.language.clone()),
10364 worktree_id,
10365 cx,
10366 )
10367 })
10368 .filter(move |(_, template)| {
10369 template.tags.iter().any(|source_tag| source_tag == &tag)
10370 })
10371 })
10372 .sorted_by_key(|(kind, _)| kind.to_owned())
10373 .collect();
10374 if let Some((leading_tag_source, _)) = tags.first() {
10375 // Strongest source wins; if we have worktree tag binding, prefer that to
10376 // global and language bindings;
10377 // if we have a global binding, prefer that to language binding.
10378 let first_mismatch = tags
10379 .iter()
10380 .position(|(tag_source, _)| tag_source != leading_tag_source);
10381 if let Some(index) = first_mismatch {
10382 tags.truncate(index);
10383 }
10384 }
10385
10386 tags
10387 }
10388
10389 pub fn move_to_enclosing_bracket(
10390 &mut self,
10391 _: &MoveToEnclosingBracket,
10392 window: &mut Window,
10393 cx: &mut Context<Self>,
10394 ) {
10395 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10396 s.move_offsets_with(|snapshot, selection| {
10397 let Some(enclosing_bracket_ranges) =
10398 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10399 else {
10400 return;
10401 };
10402
10403 let mut best_length = usize::MAX;
10404 let mut best_inside = false;
10405 let mut best_in_bracket_range = false;
10406 let mut best_destination = None;
10407 for (open, close) in enclosing_bracket_ranges {
10408 let close = close.to_inclusive();
10409 let length = close.end() - open.start;
10410 let inside = selection.start >= open.end && selection.end <= *close.start();
10411 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10412 || close.contains(&selection.head());
10413
10414 // If best is next to a bracket and current isn't, skip
10415 if !in_bracket_range && best_in_bracket_range {
10416 continue;
10417 }
10418
10419 // Prefer smaller lengths unless best is inside and current isn't
10420 if length > best_length && (best_inside || !inside) {
10421 continue;
10422 }
10423
10424 best_length = length;
10425 best_inside = inside;
10426 best_in_bracket_range = in_bracket_range;
10427 best_destination = Some(
10428 if close.contains(&selection.start) && close.contains(&selection.end) {
10429 if inside {
10430 open.end
10431 } else {
10432 open.start
10433 }
10434 } else if inside {
10435 *close.start()
10436 } else {
10437 *close.end()
10438 },
10439 );
10440 }
10441
10442 if let Some(destination) = best_destination {
10443 selection.collapse_to(destination, SelectionGoal::None);
10444 }
10445 })
10446 });
10447 }
10448
10449 pub fn undo_selection(
10450 &mut self,
10451 _: &UndoSelection,
10452 window: &mut Window,
10453 cx: &mut Context<Self>,
10454 ) {
10455 self.end_selection(window, cx);
10456 self.selection_history.mode = SelectionHistoryMode::Undoing;
10457 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10458 self.change_selections(None, window, cx, |s| {
10459 s.select_anchors(entry.selections.to_vec())
10460 });
10461 self.select_next_state = entry.select_next_state;
10462 self.select_prev_state = entry.select_prev_state;
10463 self.add_selections_state = entry.add_selections_state;
10464 self.request_autoscroll(Autoscroll::newest(), cx);
10465 }
10466 self.selection_history.mode = SelectionHistoryMode::Normal;
10467 }
10468
10469 pub fn redo_selection(
10470 &mut self,
10471 _: &RedoSelection,
10472 window: &mut Window,
10473 cx: &mut Context<Self>,
10474 ) {
10475 self.end_selection(window, cx);
10476 self.selection_history.mode = SelectionHistoryMode::Redoing;
10477 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10478 self.change_selections(None, window, cx, |s| {
10479 s.select_anchors(entry.selections.to_vec())
10480 });
10481 self.select_next_state = entry.select_next_state;
10482 self.select_prev_state = entry.select_prev_state;
10483 self.add_selections_state = entry.add_selections_state;
10484 self.request_autoscroll(Autoscroll::newest(), cx);
10485 }
10486 self.selection_history.mode = SelectionHistoryMode::Normal;
10487 }
10488
10489 pub fn expand_excerpts(
10490 &mut self,
10491 action: &ExpandExcerpts,
10492 _: &mut Window,
10493 cx: &mut Context<Self>,
10494 ) {
10495 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10496 }
10497
10498 pub fn expand_excerpts_down(
10499 &mut self,
10500 action: &ExpandExcerptsDown,
10501 _: &mut Window,
10502 cx: &mut Context<Self>,
10503 ) {
10504 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10505 }
10506
10507 pub fn expand_excerpts_up(
10508 &mut self,
10509 action: &ExpandExcerptsUp,
10510 _: &mut Window,
10511 cx: &mut Context<Self>,
10512 ) {
10513 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10514 }
10515
10516 pub fn expand_excerpts_for_direction(
10517 &mut self,
10518 lines: u32,
10519 direction: ExpandExcerptDirection,
10520
10521 cx: &mut Context<Self>,
10522 ) {
10523 let selections = self.selections.disjoint_anchors();
10524
10525 let lines = if lines == 0 {
10526 EditorSettings::get_global(cx).expand_excerpt_lines
10527 } else {
10528 lines
10529 };
10530
10531 self.buffer.update(cx, |buffer, cx| {
10532 let snapshot = buffer.snapshot(cx);
10533 let mut excerpt_ids = selections
10534 .iter()
10535 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10536 .collect::<Vec<_>>();
10537 excerpt_ids.sort();
10538 excerpt_ids.dedup();
10539 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10540 })
10541 }
10542
10543 pub fn expand_excerpt(
10544 &mut self,
10545 excerpt: ExcerptId,
10546 direction: ExpandExcerptDirection,
10547 cx: &mut Context<Self>,
10548 ) {
10549 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10550 self.buffer.update(cx, |buffer, cx| {
10551 buffer.expand_excerpts([excerpt], lines, direction, cx)
10552 })
10553 }
10554
10555 pub fn go_to_singleton_buffer_point(
10556 &mut self,
10557 point: Point,
10558 window: &mut Window,
10559 cx: &mut Context<Self>,
10560 ) {
10561 self.go_to_singleton_buffer_range(point..point, window, cx);
10562 }
10563
10564 pub fn go_to_singleton_buffer_range(
10565 &mut self,
10566 range: Range<Point>,
10567 window: &mut Window,
10568 cx: &mut Context<Self>,
10569 ) {
10570 let multibuffer = self.buffer().read(cx);
10571 let Some(buffer) = multibuffer.as_singleton() else {
10572 return;
10573 };
10574 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10575 return;
10576 };
10577 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10578 return;
10579 };
10580 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10581 s.select_anchor_ranges([start..end])
10582 });
10583 }
10584
10585 fn go_to_diagnostic(
10586 &mut self,
10587 _: &GoToDiagnostic,
10588 window: &mut Window,
10589 cx: &mut Context<Self>,
10590 ) {
10591 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10592 }
10593
10594 fn go_to_prev_diagnostic(
10595 &mut self,
10596 _: &GoToPrevDiagnostic,
10597 window: &mut Window,
10598 cx: &mut Context<Self>,
10599 ) {
10600 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10601 }
10602
10603 pub fn go_to_diagnostic_impl(
10604 &mut self,
10605 direction: Direction,
10606 window: &mut Window,
10607 cx: &mut Context<Self>,
10608 ) {
10609 let buffer = self.buffer.read(cx).snapshot(cx);
10610 let selection = self.selections.newest::<usize>(cx);
10611
10612 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10613 if direction == Direction::Next {
10614 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10615 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10616 return;
10617 };
10618 self.activate_diagnostics(
10619 buffer_id,
10620 popover.local_diagnostic.diagnostic.group_id,
10621 window,
10622 cx,
10623 );
10624 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10625 let primary_range_start = active_diagnostics.primary_range.start;
10626 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10627 let mut new_selection = s.newest_anchor().clone();
10628 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10629 s.select_anchors(vec![new_selection.clone()]);
10630 });
10631 self.refresh_inline_completion(false, true, window, cx);
10632 }
10633 return;
10634 }
10635 }
10636
10637 let active_group_id = self
10638 .active_diagnostics
10639 .as_ref()
10640 .map(|active_group| active_group.group_id);
10641 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10642 active_diagnostics
10643 .primary_range
10644 .to_offset(&buffer)
10645 .to_inclusive()
10646 });
10647 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10648 if active_primary_range.contains(&selection.head()) {
10649 *active_primary_range.start()
10650 } else {
10651 selection.head()
10652 }
10653 } else {
10654 selection.head()
10655 };
10656
10657 let snapshot = self.snapshot(window, cx);
10658 let primary_diagnostics_before = buffer
10659 .diagnostics_in_range::<usize>(0..search_start)
10660 .filter(|entry| entry.diagnostic.is_primary)
10661 .filter(|entry| entry.range.start != entry.range.end)
10662 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10663 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
10664 .collect::<Vec<_>>();
10665 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
10666 primary_diagnostics_before
10667 .iter()
10668 .position(|entry| entry.diagnostic.group_id == active_group_id)
10669 });
10670
10671 let primary_diagnostics_after = buffer
10672 .diagnostics_in_range::<usize>(search_start..buffer.len())
10673 .filter(|entry| entry.diagnostic.is_primary)
10674 .filter(|entry| entry.range.start != entry.range.end)
10675 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
10676 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10677 .collect::<Vec<_>>();
10678 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
10679 primary_diagnostics_after
10680 .iter()
10681 .enumerate()
10682 .rev()
10683 .find_map(|(i, entry)| {
10684 if entry.diagnostic.group_id == active_group_id {
10685 Some(i)
10686 } else {
10687 None
10688 }
10689 })
10690 });
10691
10692 let next_primary_diagnostic = match direction {
10693 Direction::Prev => primary_diagnostics_before
10694 .iter()
10695 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
10696 .rev()
10697 .next(),
10698 Direction::Next => primary_diagnostics_after
10699 .iter()
10700 .skip(
10701 last_same_group_diagnostic_after
10702 .map(|index| index + 1)
10703 .unwrap_or(0),
10704 )
10705 .next(),
10706 };
10707
10708 // Cycle around to the start of the buffer, potentially moving back to the start of
10709 // the currently active diagnostic.
10710 let cycle_around = || match direction {
10711 Direction::Prev => primary_diagnostics_after
10712 .iter()
10713 .rev()
10714 .chain(primary_diagnostics_before.iter().rev())
10715 .next(),
10716 Direction::Next => primary_diagnostics_before
10717 .iter()
10718 .chain(primary_diagnostics_after.iter())
10719 .next(),
10720 };
10721
10722 if let Some((primary_range, group_id)) = next_primary_diagnostic
10723 .or_else(cycle_around)
10724 .map(|entry| (&entry.range, entry.diagnostic.group_id))
10725 {
10726 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10727 return;
10728 };
10729 self.activate_diagnostics(buffer_id, group_id, window, cx);
10730 if self.active_diagnostics.is_some() {
10731 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10732 s.select(vec![Selection {
10733 id: selection.id,
10734 start: primary_range.start,
10735 end: primary_range.start,
10736 reversed: false,
10737 goal: SelectionGoal::None,
10738 }]);
10739 });
10740 self.refresh_inline_completion(false, true, window, cx);
10741 }
10742 }
10743 }
10744
10745 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10746 let snapshot = self.snapshot(window, cx);
10747 let selection = self.selections.newest::<Point>(cx);
10748 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10749 }
10750
10751 fn go_to_hunk_after_position(
10752 &mut self,
10753 snapshot: &EditorSnapshot,
10754 position: Point,
10755 window: &mut Window,
10756 cx: &mut Context<Editor>,
10757 ) -> Option<MultiBufferDiffHunk> {
10758 let mut hunk = snapshot
10759 .buffer_snapshot
10760 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10761 .find(|hunk| hunk.row_range.start.0 > position.row);
10762 if hunk.is_none() {
10763 hunk = snapshot
10764 .buffer_snapshot
10765 .diff_hunks_in_range(Point::zero()..position)
10766 .find(|hunk| hunk.row_range.end.0 < position.row)
10767 }
10768 if let Some(hunk) = &hunk {
10769 let destination = Point::new(hunk.row_range.start.0, 0);
10770 self.unfold_ranges(&[destination..destination], false, false, cx);
10771 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10772 s.select_ranges(vec![destination..destination]);
10773 });
10774 }
10775
10776 hunk
10777 }
10778
10779 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10780 let snapshot = self.snapshot(window, cx);
10781 let selection = self.selections.newest::<Point>(cx);
10782 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10783 }
10784
10785 fn go_to_hunk_before_position(
10786 &mut self,
10787 snapshot: &EditorSnapshot,
10788 position: Point,
10789 window: &mut Window,
10790 cx: &mut Context<Editor>,
10791 ) -> Option<MultiBufferDiffHunk> {
10792 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10793 if hunk.is_none() {
10794 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10795 }
10796 if let Some(hunk) = &hunk {
10797 let destination = Point::new(hunk.row_range.start.0, 0);
10798 self.unfold_ranges(&[destination..destination], false, false, cx);
10799 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10800 s.select_ranges(vec![destination..destination]);
10801 });
10802 }
10803
10804 hunk
10805 }
10806
10807 pub fn go_to_definition(
10808 &mut self,
10809 _: &GoToDefinition,
10810 window: &mut Window,
10811 cx: &mut Context<Self>,
10812 ) -> Task<Result<Navigated>> {
10813 let definition =
10814 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10815 cx.spawn_in(window, |editor, mut cx| async move {
10816 if definition.await? == Navigated::Yes {
10817 return Ok(Navigated::Yes);
10818 }
10819 match editor.update_in(&mut cx, |editor, window, cx| {
10820 editor.find_all_references(&FindAllReferences, window, cx)
10821 })? {
10822 Some(references) => references.await,
10823 None => Ok(Navigated::No),
10824 }
10825 })
10826 }
10827
10828 pub fn go_to_declaration(
10829 &mut self,
10830 _: &GoToDeclaration,
10831 window: &mut Window,
10832 cx: &mut Context<Self>,
10833 ) -> Task<Result<Navigated>> {
10834 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10835 }
10836
10837 pub fn go_to_declaration_split(
10838 &mut self,
10839 _: &GoToDeclaration,
10840 window: &mut Window,
10841 cx: &mut Context<Self>,
10842 ) -> Task<Result<Navigated>> {
10843 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10844 }
10845
10846 pub fn go_to_implementation(
10847 &mut self,
10848 _: &GoToImplementation,
10849 window: &mut Window,
10850 cx: &mut Context<Self>,
10851 ) -> Task<Result<Navigated>> {
10852 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10853 }
10854
10855 pub fn go_to_implementation_split(
10856 &mut self,
10857 _: &GoToImplementationSplit,
10858 window: &mut Window,
10859 cx: &mut Context<Self>,
10860 ) -> Task<Result<Navigated>> {
10861 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10862 }
10863
10864 pub fn go_to_type_definition(
10865 &mut self,
10866 _: &GoToTypeDefinition,
10867 window: &mut Window,
10868 cx: &mut Context<Self>,
10869 ) -> Task<Result<Navigated>> {
10870 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10871 }
10872
10873 pub fn go_to_definition_split(
10874 &mut self,
10875 _: &GoToDefinitionSplit,
10876 window: &mut Window,
10877 cx: &mut Context<Self>,
10878 ) -> Task<Result<Navigated>> {
10879 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10880 }
10881
10882 pub fn go_to_type_definition_split(
10883 &mut self,
10884 _: &GoToTypeDefinitionSplit,
10885 window: &mut Window,
10886 cx: &mut Context<Self>,
10887 ) -> Task<Result<Navigated>> {
10888 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10889 }
10890
10891 fn go_to_definition_of_kind(
10892 &mut self,
10893 kind: GotoDefinitionKind,
10894 split: bool,
10895 window: &mut Window,
10896 cx: &mut Context<Self>,
10897 ) -> Task<Result<Navigated>> {
10898 let Some(provider) = self.semantics_provider.clone() else {
10899 return Task::ready(Ok(Navigated::No));
10900 };
10901 let head = self.selections.newest::<usize>(cx).head();
10902 let buffer = self.buffer.read(cx);
10903 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10904 text_anchor
10905 } else {
10906 return Task::ready(Ok(Navigated::No));
10907 };
10908
10909 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10910 return Task::ready(Ok(Navigated::No));
10911 };
10912
10913 cx.spawn_in(window, |editor, mut cx| async move {
10914 let definitions = definitions.await?;
10915 let navigated = editor
10916 .update_in(&mut cx, |editor, window, cx| {
10917 editor.navigate_to_hover_links(
10918 Some(kind),
10919 definitions
10920 .into_iter()
10921 .filter(|location| {
10922 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10923 })
10924 .map(HoverLink::Text)
10925 .collect::<Vec<_>>(),
10926 split,
10927 window,
10928 cx,
10929 )
10930 })?
10931 .await?;
10932 anyhow::Ok(navigated)
10933 })
10934 }
10935
10936 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10937 let selection = self.selections.newest_anchor();
10938 let head = selection.head();
10939 let tail = selection.tail();
10940
10941 let Some((buffer, start_position)) =
10942 self.buffer.read(cx).text_anchor_for_position(head, cx)
10943 else {
10944 return;
10945 };
10946
10947 let end_position = if head != tail {
10948 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10949 return;
10950 };
10951 Some(pos)
10952 } else {
10953 None
10954 };
10955
10956 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10957 let url = if let Some(end_pos) = end_position {
10958 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10959 } else {
10960 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10961 };
10962
10963 if let Some(url) = url {
10964 editor.update(&mut cx, |_, cx| {
10965 cx.open_url(&url);
10966 })
10967 } else {
10968 Ok(())
10969 }
10970 });
10971
10972 url_finder.detach();
10973 }
10974
10975 pub fn open_selected_filename(
10976 &mut self,
10977 _: &OpenSelectedFilename,
10978 window: &mut Window,
10979 cx: &mut Context<Self>,
10980 ) {
10981 let Some(workspace) = self.workspace() else {
10982 return;
10983 };
10984
10985 let position = self.selections.newest_anchor().head();
10986
10987 let Some((buffer, buffer_position)) =
10988 self.buffer.read(cx).text_anchor_for_position(position, cx)
10989 else {
10990 return;
10991 };
10992
10993 let project = self.project.clone();
10994
10995 cx.spawn_in(window, |_, mut cx| async move {
10996 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10997
10998 if let Some((_, path)) = result {
10999 workspace
11000 .update_in(&mut cx, |workspace, window, cx| {
11001 workspace.open_resolved_path(path, window, cx)
11002 })?
11003 .await?;
11004 }
11005 anyhow::Ok(())
11006 })
11007 .detach();
11008 }
11009
11010 pub(crate) fn navigate_to_hover_links(
11011 &mut self,
11012 kind: Option<GotoDefinitionKind>,
11013 mut definitions: Vec<HoverLink>,
11014 split: bool,
11015 window: &mut Window,
11016 cx: &mut Context<Editor>,
11017 ) -> Task<Result<Navigated>> {
11018 // If there is one definition, just open it directly
11019 if definitions.len() == 1 {
11020 let definition = definitions.pop().unwrap();
11021
11022 enum TargetTaskResult {
11023 Location(Option<Location>),
11024 AlreadyNavigated,
11025 }
11026
11027 let target_task = match definition {
11028 HoverLink::Text(link) => {
11029 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11030 }
11031 HoverLink::InlayHint(lsp_location, server_id) => {
11032 let computation =
11033 self.compute_target_location(lsp_location, server_id, window, cx);
11034 cx.background_spawn(async move {
11035 let location = computation.await?;
11036 Ok(TargetTaskResult::Location(location))
11037 })
11038 }
11039 HoverLink::Url(url) => {
11040 cx.open_url(&url);
11041 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11042 }
11043 HoverLink::File(path) => {
11044 if let Some(workspace) = self.workspace() {
11045 cx.spawn_in(window, |_, mut cx| async move {
11046 workspace
11047 .update_in(&mut cx, |workspace, window, cx| {
11048 workspace.open_resolved_path(path, window, cx)
11049 })?
11050 .await
11051 .map(|_| TargetTaskResult::AlreadyNavigated)
11052 })
11053 } else {
11054 Task::ready(Ok(TargetTaskResult::Location(None)))
11055 }
11056 }
11057 };
11058 cx.spawn_in(window, |editor, mut cx| async move {
11059 let target = match target_task.await.context("target resolution task")? {
11060 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11061 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11062 TargetTaskResult::Location(Some(target)) => target,
11063 };
11064
11065 editor.update_in(&mut cx, |editor, window, cx| {
11066 let Some(workspace) = editor.workspace() else {
11067 return Navigated::No;
11068 };
11069 let pane = workspace.read(cx).active_pane().clone();
11070
11071 let range = target.range.to_point(target.buffer.read(cx));
11072 let range = editor.range_for_match(&range);
11073 let range = collapse_multiline_range(range);
11074
11075 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
11076 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11077 } else {
11078 window.defer(cx, move |window, cx| {
11079 let target_editor: Entity<Self> =
11080 workspace.update(cx, |workspace, cx| {
11081 let pane = if split {
11082 workspace.adjacent_pane(window, cx)
11083 } else {
11084 workspace.active_pane().clone()
11085 };
11086
11087 workspace.open_project_item(
11088 pane,
11089 target.buffer.clone(),
11090 true,
11091 true,
11092 window,
11093 cx,
11094 )
11095 });
11096 target_editor.update(cx, |target_editor, cx| {
11097 // When selecting a definition in a different buffer, disable the nav history
11098 // to avoid creating a history entry at the previous cursor location.
11099 pane.update(cx, |pane, _| pane.disable_history());
11100 target_editor.go_to_singleton_buffer_range(range, window, cx);
11101 pane.update(cx, |pane, _| pane.enable_history());
11102 });
11103 });
11104 }
11105 Navigated::Yes
11106 })
11107 })
11108 } else if !definitions.is_empty() {
11109 cx.spawn_in(window, |editor, mut cx| async move {
11110 let (title, location_tasks, workspace) = editor
11111 .update_in(&mut cx, |editor, window, cx| {
11112 let tab_kind = match kind {
11113 Some(GotoDefinitionKind::Implementation) => "Implementations",
11114 _ => "Definitions",
11115 };
11116 let title = definitions
11117 .iter()
11118 .find_map(|definition| match definition {
11119 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11120 let buffer = origin.buffer.read(cx);
11121 format!(
11122 "{} for {}",
11123 tab_kind,
11124 buffer
11125 .text_for_range(origin.range.clone())
11126 .collect::<String>()
11127 )
11128 }),
11129 HoverLink::InlayHint(_, _) => None,
11130 HoverLink::Url(_) => None,
11131 HoverLink::File(_) => None,
11132 })
11133 .unwrap_or(tab_kind.to_string());
11134 let location_tasks = definitions
11135 .into_iter()
11136 .map(|definition| match definition {
11137 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11138 HoverLink::InlayHint(lsp_location, server_id) => editor
11139 .compute_target_location(lsp_location, server_id, window, cx),
11140 HoverLink::Url(_) => Task::ready(Ok(None)),
11141 HoverLink::File(_) => Task::ready(Ok(None)),
11142 })
11143 .collect::<Vec<_>>();
11144 (title, location_tasks, editor.workspace().clone())
11145 })
11146 .context("location tasks preparation")?;
11147
11148 let locations = future::join_all(location_tasks)
11149 .await
11150 .into_iter()
11151 .filter_map(|location| location.transpose())
11152 .collect::<Result<_>>()
11153 .context("location tasks")?;
11154
11155 let Some(workspace) = workspace else {
11156 return Ok(Navigated::No);
11157 };
11158 let opened = workspace
11159 .update_in(&mut cx, |workspace, window, cx| {
11160 Self::open_locations_in_multibuffer(
11161 workspace,
11162 locations,
11163 title,
11164 split,
11165 MultibufferSelectionMode::First,
11166 window,
11167 cx,
11168 )
11169 })
11170 .ok();
11171
11172 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11173 })
11174 } else {
11175 Task::ready(Ok(Navigated::No))
11176 }
11177 }
11178
11179 fn compute_target_location(
11180 &self,
11181 lsp_location: lsp::Location,
11182 server_id: LanguageServerId,
11183 window: &mut Window,
11184 cx: &mut Context<Self>,
11185 ) -> Task<anyhow::Result<Option<Location>>> {
11186 let Some(project) = self.project.clone() else {
11187 return Task::ready(Ok(None));
11188 };
11189
11190 cx.spawn_in(window, move |editor, mut cx| async move {
11191 let location_task = editor.update(&mut cx, |_, cx| {
11192 project.update(cx, |project, cx| {
11193 let language_server_name = project
11194 .language_server_statuses(cx)
11195 .find(|(id, _)| server_id == *id)
11196 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11197 language_server_name.map(|language_server_name| {
11198 project.open_local_buffer_via_lsp(
11199 lsp_location.uri.clone(),
11200 server_id,
11201 language_server_name,
11202 cx,
11203 )
11204 })
11205 })
11206 })?;
11207 let location = match location_task {
11208 Some(task) => Some({
11209 let target_buffer_handle = task.await.context("open local buffer")?;
11210 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11211 let target_start = target_buffer
11212 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11213 let target_end = target_buffer
11214 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11215 target_buffer.anchor_after(target_start)
11216 ..target_buffer.anchor_before(target_end)
11217 })?;
11218 Location {
11219 buffer: target_buffer_handle,
11220 range,
11221 }
11222 }),
11223 None => None,
11224 };
11225 Ok(location)
11226 })
11227 }
11228
11229 pub fn find_all_references(
11230 &mut self,
11231 _: &FindAllReferences,
11232 window: &mut Window,
11233 cx: &mut Context<Self>,
11234 ) -> Option<Task<Result<Navigated>>> {
11235 let selection = self.selections.newest::<usize>(cx);
11236 let multi_buffer = self.buffer.read(cx);
11237 let head = selection.head();
11238
11239 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11240 let head_anchor = multi_buffer_snapshot.anchor_at(
11241 head,
11242 if head < selection.tail() {
11243 Bias::Right
11244 } else {
11245 Bias::Left
11246 },
11247 );
11248
11249 match self
11250 .find_all_references_task_sources
11251 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11252 {
11253 Ok(_) => {
11254 log::info!(
11255 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11256 );
11257 return None;
11258 }
11259 Err(i) => {
11260 self.find_all_references_task_sources.insert(i, head_anchor);
11261 }
11262 }
11263
11264 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11265 let workspace = self.workspace()?;
11266 let project = workspace.read(cx).project().clone();
11267 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11268 Some(cx.spawn_in(window, |editor, mut cx| async move {
11269 let _cleanup = defer({
11270 let mut cx = cx.clone();
11271 move || {
11272 let _ = editor.update(&mut cx, |editor, _| {
11273 if let Ok(i) =
11274 editor
11275 .find_all_references_task_sources
11276 .binary_search_by(|anchor| {
11277 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11278 })
11279 {
11280 editor.find_all_references_task_sources.remove(i);
11281 }
11282 });
11283 }
11284 });
11285
11286 let locations = references.await?;
11287 if locations.is_empty() {
11288 return anyhow::Ok(Navigated::No);
11289 }
11290
11291 workspace.update_in(&mut cx, |workspace, window, cx| {
11292 let title = locations
11293 .first()
11294 .as_ref()
11295 .map(|location| {
11296 let buffer = location.buffer.read(cx);
11297 format!(
11298 "References to `{}`",
11299 buffer
11300 .text_for_range(location.range.clone())
11301 .collect::<String>()
11302 )
11303 })
11304 .unwrap();
11305 Self::open_locations_in_multibuffer(
11306 workspace,
11307 locations,
11308 title,
11309 false,
11310 MultibufferSelectionMode::First,
11311 window,
11312 cx,
11313 );
11314 Navigated::Yes
11315 })
11316 }))
11317 }
11318
11319 /// Opens a multibuffer with the given project locations in it
11320 pub fn open_locations_in_multibuffer(
11321 workspace: &mut Workspace,
11322 mut locations: Vec<Location>,
11323 title: String,
11324 split: bool,
11325 multibuffer_selection_mode: MultibufferSelectionMode,
11326 window: &mut Window,
11327 cx: &mut Context<Workspace>,
11328 ) {
11329 // If there are multiple definitions, open them in a multibuffer
11330 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
11331 let mut locations = locations.into_iter().peekable();
11332 let mut ranges = Vec::new();
11333 let capability = workspace.project().read(cx).capability();
11334
11335 let excerpt_buffer = cx.new(|cx| {
11336 let mut multibuffer = MultiBuffer::new(capability);
11337 while let Some(location) = locations.next() {
11338 let buffer = location.buffer.read(cx);
11339 let mut ranges_for_buffer = Vec::new();
11340 let range = location.range.to_offset(buffer);
11341 ranges_for_buffer.push(range.clone());
11342
11343 while let Some(next_location) = locations.peek() {
11344 if next_location.buffer == location.buffer {
11345 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11346 locations.next();
11347 } else {
11348 break;
11349 }
11350 }
11351
11352 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11353 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11354 location.buffer.clone(),
11355 ranges_for_buffer,
11356 DEFAULT_MULTIBUFFER_CONTEXT,
11357 cx,
11358 ))
11359 }
11360
11361 multibuffer.with_title(title)
11362 });
11363
11364 let editor = cx.new(|cx| {
11365 Editor::for_multibuffer(
11366 excerpt_buffer,
11367 Some(workspace.project().clone()),
11368 true,
11369 window,
11370 cx,
11371 )
11372 });
11373 editor.update(cx, |editor, cx| {
11374 match multibuffer_selection_mode {
11375 MultibufferSelectionMode::First => {
11376 if let Some(first_range) = ranges.first() {
11377 editor.change_selections(None, window, cx, |selections| {
11378 selections.clear_disjoint();
11379 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11380 });
11381 }
11382 editor.highlight_background::<Self>(
11383 &ranges,
11384 |theme| theme.editor_highlighted_line_background,
11385 cx,
11386 );
11387 }
11388 MultibufferSelectionMode::All => {
11389 editor.change_selections(None, window, cx, |selections| {
11390 selections.clear_disjoint();
11391 selections.select_anchor_ranges(ranges);
11392 });
11393 }
11394 }
11395 editor.register_buffers_with_language_servers(cx);
11396 });
11397
11398 let item = Box::new(editor);
11399 let item_id = item.item_id();
11400
11401 if split {
11402 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11403 } else {
11404 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11405 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11406 pane.close_current_preview_item(window, cx)
11407 } else {
11408 None
11409 }
11410 });
11411 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11412 }
11413 workspace.active_pane().update(cx, |pane, cx| {
11414 pane.set_preview_item_id(Some(item_id), cx);
11415 });
11416 }
11417
11418 pub fn rename(
11419 &mut self,
11420 _: &Rename,
11421 window: &mut Window,
11422 cx: &mut Context<Self>,
11423 ) -> Option<Task<Result<()>>> {
11424 use language::ToOffset as _;
11425
11426 let provider = self.semantics_provider.clone()?;
11427 let selection = self.selections.newest_anchor().clone();
11428 let (cursor_buffer, cursor_buffer_position) = self
11429 .buffer
11430 .read(cx)
11431 .text_anchor_for_position(selection.head(), cx)?;
11432 let (tail_buffer, cursor_buffer_position_end) = self
11433 .buffer
11434 .read(cx)
11435 .text_anchor_for_position(selection.tail(), cx)?;
11436 if tail_buffer != cursor_buffer {
11437 return None;
11438 }
11439
11440 let snapshot = cursor_buffer.read(cx).snapshot();
11441 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11442 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11443 let prepare_rename = provider
11444 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11445 .unwrap_or_else(|| Task::ready(Ok(None)));
11446 drop(snapshot);
11447
11448 Some(cx.spawn_in(window, |this, mut cx| async move {
11449 let rename_range = if let Some(range) = prepare_rename.await? {
11450 Some(range)
11451 } else {
11452 this.update(&mut cx, |this, cx| {
11453 let buffer = this.buffer.read(cx).snapshot(cx);
11454 let mut buffer_highlights = this
11455 .document_highlights_for_position(selection.head(), &buffer)
11456 .filter(|highlight| {
11457 highlight.start.excerpt_id == selection.head().excerpt_id
11458 && highlight.end.excerpt_id == selection.head().excerpt_id
11459 });
11460 buffer_highlights
11461 .next()
11462 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11463 })?
11464 };
11465 if let Some(rename_range) = rename_range {
11466 this.update_in(&mut cx, |this, window, cx| {
11467 let snapshot = cursor_buffer.read(cx).snapshot();
11468 let rename_buffer_range = rename_range.to_offset(&snapshot);
11469 let cursor_offset_in_rename_range =
11470 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11471 let cursor_offset_in_rename_range_end =
11472 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11473
11474 this.take_rename(false, window, cx);
11475 let buffer = this.buffer.read(cx).read(cx);
11476 let cursor_offset = selection.head().to_offset(&buffer);
11477 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11478 let rename_end = rename_start + rename_buffer_range.len();
11479 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11480 let mut old_highlight_id = None;
11481 let old_name: Arc<str> = buffer
11482 .chunks(rename_start..rename_end, true)
11483 .map(|chunk| {
11484 if old_highlight_id.is_none() {
11485 old_highlight_id = chunk.syntax_highlight_id;
11486 }
11487 chunk.text
11488 })
11489 .collect::<String>()
11490 .into();
11491
11492 drop(buffer);
11493
11494 // Position the selection in the rename editor so that it matches the current selection.
11495 this.show_local_selections = false;
11496 let rename_editor = cx.new(|cx| {
11497 let mut editor = Editor::single_line(window, cx);
11498 editor.buffer.update(cx, |buffer, cx| {
11499 buffer.edit([(0..0, old_name.clone())], None, cx)
11500 });
11501 let rename_selection_range = match cursor_offset_in_rename_range
11502 .cmp(&cursor_offset_in_rename_range_end)
11503 {
11504 Ordering::Equal => {
11505 editor.select_all(&SelectAll, window, cx);
11506 return editor;
11507 }
11508 Ordering::Less => {
11509 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11510 }
11511 Ordering::Greater => {
11512 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11513 }
11514 };
11515 if rename_selection_range.end > old_name.len() {
11516 editor.select_all(&SelectAll, window, cx);
11517 } else {
11518 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11519 s.select_ranges([rename_selection_range]);
11520 });
11521 }
11522 editor
11523 });
11524 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11525 if e == &EditorEvent::Focused {
11526 cx.emit(EditorEvent::FocusedIn)
11527 }
11528 })
11529 .detach();
11530
11531 let write_highlights =
11532 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11533 let read_highlights =
11534 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11535 let ranges = write_highlights
11536 .iter()
11537 .flat_map(|(_, ranges)| ranges.iter())
11538 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11539 .cloned()
11540 .collect();
11541
11542 this.highlight_text::<Rename>(
11543 ranges,
11544 HighlightStyle {
11545 fade_out: Some(0.6),
11546 ..Default::default()
11547 },
11548 cx,
11549 );
11550 let rename_focus_handle = rename_editor.focus_handle(cx);
11551 window.focus(&rename_focus_handle);
11552 let block_id = this.insert_blocks(
11553 [BlockProperties {
11554 style: BlockStyle::Flex,
11555 placement: BlockPlacement::Below(range.start),
11556 height: 1,
11557 render: Arc::new({
11558 let rename_editor = rename_editor.clone();
11559 move |cx: &mut BlockContext| {
11560 let mut text_style = cx.editor_style.text.clone();
11561 if let Some(highlight_style) = old_highlight_id
11562 .and_then(|h| h.style(&cx.editor_style.syntax))
11563 {
11564 text_style = text_style.highlight(highlight_style);
11565 }
11566 div()
11567 .block_mouse_down()
11568 .pl(cx.anchor_x)
11569 .child(EditorElement::new(
11570 &rename_editor,
11571 EditorStyle {
11572 background: cx.theme().system().transparent,
11573 local_player: cx.editor_style.local_player,
11574 text: text_style,
11575 scrollbar_width: cx.editor_style.scrollbar_width,
11576 syntax: cx.editor_style.syntax.clone(),
11577 status: cx.editor_style.status.clone(),
11578 inlay_hints_style: HighlightStyle {
11579 font_weight: Some(FontWeight::BOLD),
11580 ..make_inlay_hints_style(cx.app)
11581 },
11582 inline_completion_styles: make_suggestion_styles(
11583 cx.app,
11584 ),
11585 ..EditorStyle::default()
11586 },
11587 ))
11588 .into_any_element()
11589 }
11590 }),
11591 priority: 0,
11592 }],
11593 Some(Autoscroll::fit()),
11594 cx,
11595 )[0];
11596 this.pending_rename = Some(RenameState {
11597 range,
11598 old_name,
11599 editor: rename_editor,
11600 block_id,
11601 });
11602 })?;
11603 }
11604
11605 Ok(())
11606 }))
11607 }
11608
11609 pub fn confirm_rename(
11610 &mut self,
11611 _: &ConfirmRename,
11612 window: &mut Window,
11613 cx: &mut Context<Self>,
11614 ) -> Option<Task<Result<()>>> {
11615 let rename = self.take_rename(false, window, cx)?;
11616 let workspace = self.workspace()?.downgrade();
11617 let (buffer, start) = self
11618 .buffer
11619 .read(cx)
11620 .text_anchor_for_position(rename.range.start, cx)?;
11621 let (end_buffer, _) = self
11622 .buffer
11623 .read(cx)
11624 .text_anchor_for_position(rename.range.end, cx)?;
11625 if buffer != end_buffer {
11626 return None;
11627 }
11628
11629 let old_name = rename.old_name;
11630 let new_name = rename.editor.read(cx).text(cx);
11631
11632 let rename = self.semantics_provider.as_ref()?.perform_rename(
11633 &buffer,
11634 start,
11635 new_name.clone(),
11636 cx,
11637 )?;
11638
11639 Some(cx.spawn_in(window, |editor, mut cx| async move {
11640 let project_transaction = rename.await?;
11641 Self::open_project_transaction(
11642 &editor,
11643 workspace,
11644 project_transaction,
11645 format!("Rename: {} → {}", old_name, new_name),
11646 cx.clone(),
11647 )
11648 .await?;
11649
11650 editor.update(&mut cx, |editor, cx| {
11651 editor.refresh_document_highlights(cx);
11652 })?;
11653 Ok(())
11654 }))
11655 }
11656
11657 fn take_rename(
11658 &mut self,
11659 moving_cursor: bool,
11660 window: &mut Window,
11661 cx: &mut Context<Self>,
11662 ) -> Option<RenameState> {
11663 let rename = self.pending_rename.take()?;
11664 if rename.editor.focus_handle(cx).is_focused(window) {
11665 window.focus(&self.focus_handle);
11666 }
11667
11668 self.remove_blocks(
11669 [rename.block_id].into_iter().collect(),
11670 Some(Autoscroll::fit()),
11671 cx,
11672 );
11673 self.clear_highlights::<Rename>(cx);
11674 self.show_local_selections = true;
11675
11676 if moving_cursor {
11677 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11678 editor.selections.newest::<usize>(cx).head()
11679 });
11680
11681 // Update the selection to match the position of the selection inside
11682 // the rename editor.
11683 let snapshot = self.buffer.read(cx).read(cx);
11684 let rename_range = rename.range.to_offset(&snapshot);
11685 let cursor_in_editor = snapshot
11686 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11687 .min(rename_range.end);
11688 drop(snapshot);
11689
11690 self.change_selections(None, window, cx, |s| {
11691 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11692 });
11693 } else {
11694 self.refresh_document_highlights(cx);
11695 }
11696
11697 Some(rename)
11698 }
11699
11700 pub fn pending_rename(&self) -> Option<&RenameState> {
11701 self.pending_rename.as_ref()
11702 }
11703
11704 fn format(
11705 &mut self,
11706 _: &Format,
11707 window: &mut Window,
11708 cx: &mut Context<Self>,
11709 ) -> Option<Task<Result<()>>> {
11710 let project = match &self.project {
11711 Some(project) => project.clone(),
11712 None => return None,
11713 };
11714
11715 Some(self.perform_format(
11716 project,
11717 FormatTrigger::Manual,
11718 FormatTarget::Buffers,
11719 window,
11720 cx,
11721 ))
11722 }
11723
11724 fn format_selections(
11725 &mut self,
11726 _: &FormatSelections,
11727 window: &mut Window,
11728 cx: &mut Context<Self>,
11729 ) -> Option<Task<Result<()>>> {
11730 let project = match &self.project {
11731 Some(project) => project.clone(),
11732 None => return None,
11733 };
11734
11735 let ranges = self
11736 .selections
11737 .all_adjusted(cx)
11738 .into_iter()
11739 .map(|selection| selection.range())
11740 .collect_vec();
11741
11742 Some(self.perform_format(
11743 project,
11744 FormatTrigger::Manual,
11745 FormatTarget::Ranges(ranges),
11746 window,
11747 cx,
11748 ))
11749 }
11750
11751 fn perform_format(
11752 &mut self,
11753 project: Entity<Project>,
11754 trigger: FormatTrigger,
11755 target: FormatTarget,
11756 window: &mut Window,
11757 cx: &mut Context<Self>,
11758 ) -> Task<Result<()>> {
11759 let buffer = self.buffer.clone();
11760 let (buffers, target) = match target {
11761 FormatTarget::Buffers => {
11762 let mut buffers = buffer.read(cx).all_buffers();
11763 if trigger == FormatTrigger::Save {
11764 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11765 }
11766 (buffers, LspFormatTarget::Buffers)
11767 }
11768 FormatTarget::Ranges(selection_ranges) => {
11769 let multi_buffer = buffer.read(cx);
11770 let snapshot = multi_buffer.read(cx);
11771 let mut buffers = HashSet::default();
11772 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11773 BTreeMap::new();
11774 for selection_range in selection_ranges {
11775 for (buffer, buffer_range, _) in
11776 snapshot.range_to_buffer_ranges(selection_range)
11777 {
11778 let buffer_id = buffer.remote_id();
11779 let start = buffer.anchor_before(buffer_range.start);
11780 let end = buffer.anchor_after(buffer_range.end);
11781 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11782 buffer_id_to_ranges
11783 .entry(buffer_id)
11784 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11785 .or_insert_with(|| vec![start..end]);
11786 }
11787 }
11788 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11789 }
11790 };
11791
11792 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11793 let format = project.update(cx, |project, cx| {
11794 project.format(buffers, target, true, trigger, cx)
11795 });
11796
11797 cx.spawn_in(window, |_, mut cx| async move {
11798 let transaction = futures::select_biased! {
11799 () = timeout => {
11800 log::warn!("timed out waiting for formatting");
11801 None
11802 }
11803 transaction = format.log_err().fuse() => transaction,
11804 };
11805
11806 buffer
11807 .update(&mut cx, |buffer, cx| {
11808 if let Some(transaction) = transaction {
11809 if !buffer.is_singleton() {
11810 buffer.push_transaction(&transaction.0, cx);
11811 }
11812 }
11813
11814 cx.notify();
11815 })
11816 .ok();
11817
11818 Ok(())
11819 })
11820 }
11821
11822 fn restart_language_server(
11823 &mut self,
11824 _: &RestartLanguageServer,
11825 _: &mut Window,
11826 cx: &mut Context<Self>,
11827 ) {
11828 if let Some(project) = self.project.clone() {
11829 self.buffer.update(cx, |multi_buffer, cx| {
11830 project.update(cx, |project, cx| {
11831 project.restart_language_servers_for_buffers(
11832 multi_buffer.all_buffers().into_iter().collect(),
11833 cx,
11834 );
11835 });
11836 })
11837 }
11838 }
11839
11840 fn cancel_language_server_work(
11841 workspace: &mut Workspace,
11842 _: &actions::CancelLanguageServerWork,
11843 _: &mut Window,
11844 cx: &mut Context<Workspace>,
11845 ) {
11846 let project = workspace.project();
11847 let buffers = workspace
11848 .active_item(cx)
11849 .and_then(|item| item.act_as::<Editor>(cx))
11850 .map_or(HashSet::default(), |editor| {
11851 editor.read(cx).buffer.read(cx).all_buffers()
11852 });
11853 project.update(cx, |project, cx| {
11854 project.cancel_language_server_work_for_buffers(buffers, cx);
11855 });
11856 }
11857
11858 fn show_character_palette(
11859 &mut self,
11860 _: &ShowCharacterPalette,
11861 window: &mut Window,
11862 _: &mut Context<Self>,
11863 ) {
11864 window.show_character_palette();
11865 }
11866
11867 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11868 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11869 let buffer = self.buffer.read(cx).snapshot(cx);
11870 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11871 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11872 let is_valid = buffer
11873 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11874 .any(|entry| {
11875 entry.diagnostic.is_primary
11876 && !entry.range.is_empty()
11877 && entry.range.start == primary_range_start
11878 && entry.diagnostic.message == active_diagnostics.primary_message
11879 });
11880
11881 if is_valid != active_diagnostics.is_valid {
11882 active_diagnostics.is_valid = is_valid;
11883 let mut new_styles = HashMap::default();
11884 for (block_id, diagnostic) in &active_diagnostics.blocks {
11885 new_styles.insert(
11886 *block_id,
11887 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11888 );
11889 }
11890 self.display_map.update(cx, |display_map, _cx| {
11891 display_map.replace_blocks(new_styles)
11892 });
11893 }
11894 }
11895 }
11896
11897 fn activate_diagnostics(
11898 &mut self,
11899 buffer_id: BufferId,
11900 group_id: usize,
11901 window: &mut Window,
11902 cx: &mut Context<Self>,
11903 ) {
11904 self.dismiss_diagnostics(cx);
11905 let snapshot = self.snapshot(window, cx);
11906 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11907 let buffer = self.buffer.read(cx).snapshot(cx);
11908
11909 let mut primary_range = None;
11910 let mut primary_message = None;
11911 let diagnostic_group = buffer
11912 .diagnostic_group(buffer_id, group_id)
11913 .filter_map(|entry| {
11914 let start = entry.range.start;
11915 let end = entry.range.end;
11916 if snapshot.is_line_folded(MultiBufferRow(start.row))
11917 && (start.row == end.row
11918 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11919 {
11920 return None;
11921 }
11922 if entry.diagnostic.is_primary {
11923 primary_range = Some(entry.range.clone());
11924 primary_message = Some(entry.diagnostic.message.clone());
11925 }
11926 Some(entry)
11927 })
11928 .collect::<Vec<_>>();
11929 let primary_range = primary_range?;
11930 let primary_message = primary_message?;
11931
11932 let blocks = display_map
11933 .insert_blocks(
11934 diagnostic_group.iter().map(|entry| {
11935 let diagnostic = entry.diagnostic.clone();
11936 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11937 BlockProperties {
11938 style: BlockStyle::Fixed,
11939 placement: BlockPlacement::Below(
11940 buffer.anchor_after(entry.range.start),
11941 ),
11942 height: message_height,
11943 render: diagnostic_block_renderer(diagnostic, None, true, true),
11944 priority: 0,
11945 }
11946 }),
11947 cx,
11948 )
11949 .into_iter()
11950 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11951 .collect();
11952
11953 Some(ActiveDiagnosticGroup {
11954 primary_range: buffer.anchor_before(primary_range.start)
11955 ..buffer.anchor_after(primary_range.end),
11956 primary_message,
11957 group_id,
11958 blocks,
11959 is_valid: true,
11960 })
11961 });
11962 }
11963
11964 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11965 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11966 self.display_map.update(cx, |display_map, cx| {
11967 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11968 });
11969 cx.notify();
11970 }
11971 }
11972
11973 /// Disable inline diagnostics rendering for this editor.
11974 pub fn disable_inline_diagnostics(&mut self) {
11975 self.inline_diagnostics_enabled = false;
11976 self.inline_diagnostics_update = Task::ready(());
11977 self.inline_diagnostics.clear();
11978 }
11979
11980 pub fn inline_diagnostics_enabled(&self) -> bool {
11981 self.inline_diagnostics_enabled
11982 }
11983
11984 pub fn show_inline_diagnostics(&self) -> bool {
11985 self.show_inline_diagnostics
11986 }
11987
11988 pub fn toggle_inline_diagnostics(
11989 &mut self,
11990 _: &ToggleInlineDiagnostics,
11991 window: &mut Window,
11992 cx: &mut Context<'_, Editor>,
11993 ) {
11994 self.show_inline_diagnostics = !self.show_inline_diagnostics;
11995 self.refresh_inline_diagnostics(false, window, cx);
11996 }
11997
11998 fn refresh_inline_diagnostics(
11999 &mut self,
12000 debounce: bool,
12001 window: &mut Window,
12002 cx: &mut Context<Self>,
12003 ) {
12004 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12005 self.inline_diagnostics_update = Task::ready(());
12006 self.inline_diagnostics.clear();
12007 return;
12008 }
12009
12010 let debounce_ms = ProjectSettings::get_global(cx)
12011 .diagnostics
12012 .inline
12013 .update_debounce_ms;
12014 let debounce = if debounce && debounce_ms > 0 {
12015 Some(Duration::from_millis(debounce_ms))
12016 } else {
12017 None
12018 };
12019 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12020 if let Some(debounce) = debounce {
12021 cx.background_executor().timer(debounce).await;
12022 }
12023 let Some(snapshot) = editor
12024 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12025 .ok()
12026 else {
12027 return;
12028 };
12029
12030 let new_inline_diagnostics = cx
12031 .background_spawn(async move {
12032 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12033 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12034 let message = diagnostic_entry
12035 .diagnostic
12036 .message
12037 .split_once('\n')
12038 .map(|(line, _)| line)
12039 .map(SharedString::new)
12040 .unwrap_or_else(|| {
12041 SharedString::from(diagnostic_entry.diagnostic.message)
12042 });
12043 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12044 let (Ok(i) | Err(i)) = inline_diagnostics
12045 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12046 inline_diagnostics.insert(
12047 i,
12048 (
12049 start_anchor,
12050 InlineDiagnostic {
12051 message,
12052 group_id: diagnostic_entry.diagnostic.group_id,
12053 start: diagnostic_entry.range.start.to_point(&snapshot),
12054 is_primary: diagnostic_entry.diagnostic.is_primary,
12055 severity: diagnostic_entry.diagnostic.severity,
12056 },
12057 ),
12058 );
12059 }
12060 inline_diagnostics
12061 })
12062 .await;
12063
12064 editor
12065 .update(&mut cx, |editor, cx| {
12066 editor.inline_diagnostics = new_inline_diagnostics;
12067 cx.notify();
12068 })
12069 .ok();
12070 });
12071 }
12072
12073 pub fn set_selections_from_remote(
12074 &mut self,
12075 selections: Vec<Selection<Anchor>>,
12076 pending_selection: Option<Selection<Anchor>>,
12077 window: &mut Window,
12078 cx: &mut Context<Self>,
12079 ) {
12080 let old_cursor_position = self.selections.newest_anchor().head();
12081 self.selections.change_with(cx, |s| {
12082 s.select_anchors(selections);
12083 if let Some(pending_selection) = pending_selection {
12084 s.set_pending(pending_selection, SelectMode::Character);
12085 } else {
12086 s.clear_pending();
12087 }
12088 });
12089 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12090 }
12091
12092 fn push_to_selection_history(&mut self) {
12093 self.selection_history.push(SelectionHistoryEntry {
12094 selections: self.selections.disjoint_anchors(),
12095 select_next_state: self.select_next_state.clone(),
12096 select_prev_state: self.select_prev_state.clone(),
12097 add_selections_state: self.add_selections_state.clone(),
12098 });
12099 }
12100
12101 pub fn transact(
12102 &mut self,
12103 window: &mut Window,
12104 cx: &mut Context<Self>,
12105 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12106 ) -> Option<TransactionId> {
12107 self.start_transaction_at(Instant::now(), window, cx);
12108 update(self, window, cx);
12109 self.end_transaction_at(Instant::now(), cx)
12110 }
12111
12112 pub fn start_transaction_at(
12113 &mut self,
12114 now: Instant,
12115 window: &mut Window,
12116 cx: &mut Context<Self>,
12117 ) {
12118 self.end_selection(window, cx);
12119 if let Some(tx_id) = self
12120 .buffer
12121 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
12122 {
12123 self.selection_history
12124 .insert_transaction(tx_id, self.selections.disjoint_anchors());
12125 cx.emit(EditorEvent::TransactionBegun {
12126 transaction_id: tx_id,
12127 })
12128 }
12129 }
12130
12131 pub fn end_transaction_at(
12132 &mut self,
12133 now: Instant,
12134 cx: &mut Context<Self>,
12135 ) -> Option<TransactionId> {
12136 if let Some(transaction_id) = self
12137 .buffer
12138 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
12139 {
12140 if let Some((_, end_selections)) =
12141 self.selection_history.transaction_mut(transaction_id)
12142 {
12143 *end_selections = Some(self.selections.disjoint_anchors());
12144 } else {
12145 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
12146 }
12147
12148 cx.emit(EditorEvent::Edited { transaction_id });
12149 Some(transaction_id)
12150 } else {
12151 None
12152 }
12153 }
12154
12155 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
12156 if self.selection_mark_mode {
12157 self.change_selections(None, window, cx, |s| {
12158 s.move_with(|_, sel| {
12159 sel.collapse_to(sel.head(), SelectionGoal::None);
12160 });
12161 })
12162 }
12163 self.selection_mark_mode = true;
12164 cx.notify();
12165 }
12166
12167 pub fn swap_selection_ends(
12168 &mut self,
12169 _: &actions::SwapSelectionEnds,
12170 window: &mut Window,
12171 cx: &mut Context<Self>,
12172 ) {
12173 self.change_selections(None, window, cx, |s| {
12174 s.move_with(|_, sel| {
12175 if sel.start != sel.end {
12176 sel.reversed = !sel.reversed
12177 }
12178 });
12179 });
12180 self.request_autoscroll(Autoscroll::newest(), cx);
12181 cx.notify();
12182 }
12183
12184 pub fn toggle_fold(
12185 &mut self,
12186 _: &actions::ToggleFold,
12187 window: &mut Window,
12188 cx: &mut Context<Self>,
12189 ) {
12190 if self.is_singleton(cx) {
12191 let selection = self.selections.newest::<Point>(cx);
12192
12193 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12194 let range = if selection.is_empty() {
12195 let point = selection.head().to_display_point(&display_map);
12196 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12197 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12198 .to_point(&display_map);
12199 start..end
12200 } else {
12201 selection.range()
12202 };
12203 if display_map.folds_in_range(range).next().is_some() {
12204 self.unfold_lines(&Default::default(), window, cx)
12205 } else {
12206 self.fold(&Default::default(), window, cx)
12207 }
12208 } else {
12209 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12210 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12211 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12212 .map(|(snapshot, _, _)| snapshot.remote_id())
12213 .collect();
12214
12215 for buffer_id in buffer_ids {
12216 if self.is_buffer_folded(buffer_id, cx) {
12217 self.unfold_buffer(buffer_id, cx);
12218 } else {
12219 self.fold_buffer(buffer_id, cx);
12220 }
12221 }
12222 }
12223 }
12224
12225 pub fn toggle_fold_recursive(
12226 &mut self,
12227 _: &actions::ToggleFoldRecursive,
12228 window: &mut Window,
12229 cx: &mut Context<Self>,
12230 ) {
12231 let selection = self.selections.newest::<Point>(cx);
12232
12233 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12234 let range = if selection.is_empty() {
12235 let point = selection.head().to_display_point(&display_map);
12236 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12237 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12238 .to_point(&display_map);
12239 start..end
12240 } else {
12241 selection.range()
12242 };
12243 if display_map.folds_in_range(range).next().is_some() {
12244 self.unfold_recursive(&Default::default(), window, cx)
12245 } else {
12246 self.fold_recursive(&Default::default(), window, cx)
12247 }
12248 }
12249
12250 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12251 if self.is_singleton(cx) {
12252 let mut to_fold = Vec::new();
12253 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12254 let selections = self.selections.all_adjusted(cx);
12255
12256 for selection in selections {
12257 let range = selection.range().sorted();
12258 let buffer_start_row = range.start.row;
12259
12260 if range.start.row != range.end.row {
12261 let mut found = false;
12262 let mut row = range.start.row;
12263 while row <= range.end.row {
12264 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12265 {
12266 found = true;
12267 row = crease.range().end.row + 1;
12268 to_fold.push(crease);
12269 } else {
12270 row += 1
12271 }
12272 }
12273 if found {
12274 continue;
12275 }
12276 }
12277
12278 for row in (0..=range.start.row).rev() {
12279 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12280 if crease.range().end.row >= buffer_start_row {
12281 to_fold.push(crease);
12282 if row <= range.start.row {
12283 break;
12284 }
12285 }
12286 }
12287 }
12288 }
12289
12290 self.fold_creases(to_fold, true, window, cx);
12291 } else {
12292 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12293
12294 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12295 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12296 .map(|(snapshot, _, _)| snapshot.remote_id())
12297 .collect();
12298 for buffer_id in buffer_ids {
12299 self.fold_buffer(buffer_id, cx);
12300 }
12301 }
12302 }
12303
12304 fn fold_at_level(
12305 &mut self,
12306 fold_at: &FoldAtLevel,
12307 window: &mut Window,
12308 cx: &mut Context<Self>,
12309 ) {
12310 if !self.buffer.read(cx).is_singleton() {
12311 return;
12312 }
12313
12314 let fold_at_level = fold_at.0;
12315 let snapshot = self.buffer.read(cx).snapshot(cx);
12316 let mut to_fold = Vec::new();
12317 let mut stack = vec![(0, snapshot.max_row().0, 1)];
12318
12319 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
12320 while start_row < end_row {
12321 match self
12322 .snapshot(window, cx)
12323 .crease_for_buffer_row(MultiBufferRow(start_row))
12324 {
12325 Some(crease) => {
12326 let nested_start_row = crease.range().start.row + 1;
12327 let nested_end_row = crease.range().end.row;
12328
12329 if current_level < fold_at_level {
12330 stack.push((nested_start_row, nested_end_row, current_level + 1));
12331 } else if current_level == fold_at_level {
12332 to_fold.push(crease);
12333 }
12334
12335 start_row = nested_end_row + 1;
12336 }
12337 None => start_row += 1,
12338 }
12339 }
12340 }
12341
12342 self.fold_creases(to_fold, true, window, cx);
12343 }
12344
12345 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
12346 if self.buffer.read(cx).is_singleton() {
12347 let mut fold_ranges = Vec::new();
12348 let snapshot = self.buffer.read(cx).snapshot(cx);
12349
12350 for row in 0..snapshot.max_row().0 {
12351 if let Some(foldable_range) = self
12352 .snapshot(window, cx)
12353 .crease_for_buffer_row(MultiBufferRow(row))
12354 {
12355 fold_ranges.push(foldable_range);
12356 }
12357 }
12358
12359 self.fold_creases(fold_ranges, true, window, cx);
12360 } else {
12361 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
12362 editor
12363 .update_in(&mut cx, |editor, _, cx| {
12364 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12365 editor.fold_buffer(buffer_id, cx);
12366 }
12367 })
12368 .ok();
12369 });
12370 }
12371 }
12372
12373 pub fn fold_function_bodies(
12374 &mut self,
12375 _: &actions::FoldFunctionBodies,
12376 window: &mut Window,
12377 cx: &mut Context<Self>,
12378 ) {
12379 let snapshot = self.buffer.read(cx).snapshot(cx);
12380
12381 let ranges = snapshot
12382 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
12383 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
12384 .collect::<Vec<_>>();
12385
12386 let creases = ranges
12387 .into_iter()
12388 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
12389 .collect();
12390
12391 self.fold_creases(creases, true, window, cx);
12392 }
12393
12394 pub fn fold_recursive(
12395 &mut self,
12396 _: &actions::FoldRecursive,
12397 window: &mut Window,
12398 cx: &mut Context<Self>,
12399 ) {
12400 let mut to_fold = Vec::new();
12401 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12402 let selections = self.selections.all_adjusted(cx);
12403
12404 for selection in selections {
12405 let range = selection.range().sorted();
12406 let buffer_start_row = range.start.row;
12407
12408 if range.start.row != range.end.row {
12409 let mut found = false;
12410 for row in range.start.row..=range.end.row {
12411 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12412 found = true;
12413 to_fold.push(crease);
12414 }
12415 }
12416 if found {
12417 continue;
12418 }
12419 }
12420
12421 for row in (0..=range.start.row).rev() {
12422 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12423 if crease.range().end.row >= buffer_start_row {
12424 to_fold.push(crease);
12425 } else {
12426 break;
12427 }
12428 }
12429 }
12430 }
12431
12432 self.fold_creases(to_fold, true, window, cx);
12433 }
12434
12435 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
12436 let buffer_row = fold_at.buffer_row;
12437 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12438
12439 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12440 let autoscroll = self
12441 .selections
12442 .all::<Point>(cx)
12443 .iter()
12444 .any(|selection| crease.range().overlaps(&selection.range()));
12445
12446 self.fold_creases(vec![crease], autoscroll, window, cx);
12447 }
12448 }
12449
12450 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12451 if self.is_singleton(cx) {
12452 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12453 let buffer = &display_map.buffer_snapshot;
12454 let selections = self.selections.all::<Point>(cx);
12455 let ranges = selections
12456 .iter()
12457 .map(|s| {
12458 let range = s.display_range(&display_map).sorted();
12459 let mut start = range.start.to_point(&display_map);
12460 let mut end = range.end.to_point(&display_map);
12461 start.column = 0;
12462 end.column = buffer.line_len(MultiBufferRow(end.row));
12463 start..end
12464 })
12465 .collect::<Vec<_>>();
12466
12467 self.unfold_ranges(&ranges, true, true, cx);
12468 } else {
12469 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12470 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12471 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12472 .map(|(snapshot, _, _)| snapshot.remote_id())
12473 .collect();
12474 for buffer_id in buffer_ids {
12475 self.unfold_buffer(buffer_id, cx);
12476 }
12477 }
12478 }
12479
12480 pub fn unfold_recursive(
12481 &mut self,
12482 _: &UnfoldRecursive,
12483 _window: &mut Window,
12484 cx: &mut Context<Self>,
12485 ) {
12486 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12487 let selections = self.selections.all::<Point>(cx);
12488 let ranges = selections
12489 .iter()
12490 .map(|s| {
12491 let mut range = s.display_range(&display_map).sorted();
12492 *range.start.column_mut() = 0;
12493 *range.end.column_mut() = display_map.line_len(range.end.row());
12494 let start = range.start.to_point(&display_map);
12495 let end = range.end.to_point(&display_map);
12496 start..end
12497 })
12498 .collect::<Vec<_>>();
12499
12500 self.unfold_ranges(&ranges, true, true, cx);
12501 }
12502
12503 pub fn unfold_at(
12504 &mut self,
12505 unfold_at: &UnfoldAt,
12506 _window: &mut Window,
12507 cx: &mut Context<Self>,
12508 ) {
12509 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12510
12511 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12512 ..Point::new(
12513 unfold_at.buffer_row.0,
12514 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12515 );
12516
12517 let autoscroll = self
12518 .selections
12519 .all::<Point>(cx)
12520 .iter()
12521 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12522
12523 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12524 }
12525
12526 pub fn unfold_all(
12527 &mut self,
12528 _: &actions::UnfoldAll,
12529 _window: &mut Window,
12530 cx: &mut Context<Self>,
12531 ) {
12532 if self.buffer.read(cx).is_singleton() {
12533 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12534 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12535 } else {
12536 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12537 editor
12538 .update(&mut cx, |editor, cx| {
12539 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12540 editor.unfold_buffer(buffer_id, cx);
12541 }
12542 })
12543 .ok();
12544 });
12545 }
12546 }
12547
12548 pub fn fold_selected_ranges(
12549 &mut self,
12550 _: &FoldSelectedRanges,
12551 window: &mut Window,
12552 cx: &mut Context<Self>,
12553 ) {
12554 let selections = self.selections.all::<Point>(cx);
12555 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12556 let line_mode = self.selections.line_mode;
12557 let ranges = selections
12558 .into_iter()
12559 .map(|s| {
12560 if line_mode {
12561 let start = Point::new(s.start.row, 0);
12562 let end = Point::new(
12563 s.end.row,
12564 display_map
12565 .buffer_snapshot
12566 .line_len(MultiBufferRow(s.end.row)),
12567 );
12568 Crease::simple(start..end, display_map.fold_placeholder.clone())
12569 } else {
12570 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12571 }
12572 })
12573 .collect::<Vec<_>>();
12574 self.fold_creases(ranges, true, window, cx);
12575 }
12576
12577 pub fn fold_ranges<T: ToOffset + Clone>(
12578 &mut self,
12579 ranges: Vec<Range<T>>,
12580 auto_scroll: bool,
12581 window: &mut Window,
12582 cx: &mut Context<Self>,
12583 ) {
12584 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12585 let ranges = ranges
12586 .into_iter()
12587 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12588 .collect::<Vec<_>>();
12589 self.fold_creases(ranges, auto_scroll, window, cx);
12590 }
12591
12592 pub fn fold_creases<T: ToOffset + Clone>(
12593 &mut self,
12594 creases: Vec<Crease<T>>,
12595 auto_scroll: bool,
12596 window: &mut Window,
12597 cx: &mut Context<Self>,
12598 ) {
12599 if creases.is_empty() {
12600 return;
12601 }
12602
12603 let mut buffers_affected = HashSet::default();
12604 let multi_buffer = self.buffer().read(cx);
12605 for crease in &creases {
12606 if let Some((_, buffer, _)) =
12607 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12608 {
12609 buffers_affected.insert(buffer.read(cx).remote_id());
12610 };
12611 }
12612
12613 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12614
12615 if auto_scroll {
12616 self.request_autoscroll(Autoscroll::fit(), cx);
12617 }
12618
12619 cx.notify();
12620
12621 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12622 // Clear diagnostics block when folding a range that contains it.
12623 let snapshot = self.snapshot(window, cx);
12624 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12625 drop(snapshot);
12626 self.active_diagnostics = Some(active_diagnostics);
12627 self.dismiss_diagnostics(cx);
12628 } else {
12629 self.active_diagnostics = Some(active_diagnostics);
12630 }
12631 }
12632
12633 self.scrollbar_marker_state.dirty = true;
12634 }
12635
12636 /// Removes any folds whose ranges intersect any of the given ranges.
12637 pub fn unfold_ranges<T: ToOffset + Clone>(
12638 &mut self,
12639 ranges: &[Range<T>],
12640 inclusive: bool,
12641 auto_scroll: bool,
12642 cx: &mut Context<Self>,
12643 ) {
12644 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12645 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12646 });
12647 }
12648
12649 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12650 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12651 return;
12652 }
12653 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12654 self.display_map
12655 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12656 cx.emit(EditorEvent::BufferFoldToggled {
12657 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12658 folded: true,
12659 });
12660 cx.notify();
12661 }
12662
12663 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12664 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12665 return;
12666 }
12667 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12668 self.display_map.update(cx, |display_map, cx| {
12669 display_map.unfold_buffer(buffer_id, cx);
12670 });
12671 cx.emit(EditorEvent::BufferFoldToggled {
12672 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12673 folded: false,
12674 });
12675 cx.notify();
12676 }
12677
12678 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12679 self.display_map.read(cx).is_buffer_folded(buffer)
12680 }
12681
12682 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12683 self.display_map.read(cx).folded_buffers()
12684 }
12685
12686 /// Removes any folds with the given ranges.
12687 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12688 &mut self,
12689 ranges: &[Range<T>],
12690 type_id: TypeId,
12691 auto_scroll: bool,
12692 cx: &mut Context<Self>,
12693 ) {
12694 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12695 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12696 });
12697 }
12698
12699 fn remove_folds_with<T: ToOffset + Clone>(
12700 &mut self,
12701 ranges: &[Range<T>],
12702 auto_scroll: bool,
12703 cx: &mut Context<Self>,
12704 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12705 ) {
12706 if ranges.is_empty() {
12707 return;
12708 }
12709
12710 let mut buffers_affected = HashSet::default();
12711 let multi_buffer = self.buffer().read(cx);
12712 for range in ranges {
12713 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12714 buffers_affected.insert(buffer.read(cx).remote_id());
12715 };
12716 }
12717
12718 self.display_map.update(cx, update);
12719
12720 if auto_scroll {
12721 self.request_autoscroll(Autoscroll::fit(), cx);
12722 }
12723
12724 cx.notify();
12725 self.scrollbar_marker_state.dirty = true;
12726 self.active_indent_guides_state.dirty = true;
12727 }
12728
12729 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12730 self.display_map.read(cx).fold_placeholder.clone()
12731 }
12732
12733 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12734 self.buffer.update(cx, |buffer, cx| {
12735 buffer.set_all_diff_hunks_expanded(cx);
12736 });
12737 }
12738
12739 pub fn set_distinguish_unstaged_diff_hunks(&mut self) {
12740 self.distinguish_unstaged_diff_hunks = true;
12741 }
12742
12743 pub fn expand_all_diff_hunks(
12744 &mut self,
12745 _: &ExpandAllDiffHunks,
12746 _window: &mut Window,
12747 cx: &mut Context<Self>,
12748 ) {
12749 self.buffer.update(cx, |buffer, cx| {
12750 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12751 });
12752 }
12753
12754 pub fn toggle_selected_diff_hunks(
12755 &mut self,
12756 _: &ToggleSelectedDiffHunks,
12757 _window: &mut Window,
12758 cx: &mut Context<Self>,
12759 ) {
12760 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12761 self.toggle_diff_hunks_in_ranges(ranges, cx);
12762 }
12763
12764 pub fn diff_hunks_in_ranges<'a>(
12765 &'a self,
12766 ranges: &'a [Range<Anchor>],
12767 buffer: &'a MultiBufferSnapshot,
12768 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
12769 ranges.iter().flat_map(move |range| {
12770 let end_excerpt_id = range.end.excerpt_id;
12771 let range = range.to_point(buffer);
12772 let mut peek_end = range.end;
12773 if range.end.row < buffer.max_row().0 {
12774 peek_end = Point::new(range.end.row + 1, 0);
12775 }
12776 buffer
12777 .diff_hunks_in_range(range.start..peek_end)
12778 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
12779 })
12780 }
12781
12782 pub fn has_stageable_diff_hunks_in_ranges(
12783 &self,
12784 ranges: &[Range<Anchor>],
12785 snapshot: &MultiBufferSnapshot,
12786 ) -> bool {
12787 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
12788 hunks.any(|hunk| hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk)
12789 }
12790
12791 pub fn toggle_staged_selected_diff_hunks(
12792 &mut self,
12793 _: &::git::ToggleStaged,
12794 _window: &mut Window,
12795 cx: &mut Context<Self>,
12796 ) {
12797 let snapshot = self.buffer.read(cx).snapshot(cx);
12798 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12799 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
12800 self.stage_or_unstage_diff_hunks(stage, &ranges, cx);
12801 }
12802
12803 pub fn stage_and_next(
12804 &mut self,
12805 _: &::git::StageAndNext,
12806 window: &mut Window,
12807 cx: &mut Context<Self>,
12808 ) {
12809 self.do_stage_or_unstage_and_next(true, window, cx);
12810 }
12811
12812 pub fn unstage_and_next(
12813 &mut self,
12814 _: &::git::UnstageAndNext,
12815 window: &mut Window,
12816 cx: &mut Context<Self>,
12817 ) {
12818 self.do_stage_or_unstage_and_next(false, window, cx);
12819 }
12820
12821 pub fn stage_or_unstage_diff_hunks(
12822 &mut self,
12823 stage: bool,
12824 ranges: &[Range<Anchor>],
12825 cx: &mut Context<Self>,
12826 ) {
12827 let snapshot = self.buffer.read(cx).snapshot(cx);
12828 let Some(project) = &self.project else {
12829 return;
12830 };
12831
12832 let chunk_by = self
12833 .diff_hunks_in_ranges(&ranges, &snapshot)
12834 .chunk_by(|hunk| hunk.buffer_id);
12835 for (buffer_id, hunks) in &chunk_by {
12836 Self::do_stage_or_unstage(project, stage, buffer_id, hunks, &snapshot, cx);
12837 }
12838 }
12839
12840 fn do_stage_or_unstage_and_next(
12841 &mut self,
12842 stage: bool,
12843 window: &mut Window,
12844 cx: &mut Context<Self>,
12845 ) {
12846 let mut ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
12847 if ranges.iter().any(|range| range.start != range.end) {
12848 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12849 return;
12850 }
12851
12852 if !self.buffer().read(cx).is_singleton() {
12853 if let Some((excerpt_id, buffer, range)) = self.active_excerpt(cx) {
12854 ranges = vec![multi_buffer::Anchor::range_in_buffer(
12855 excerpt_id,
12856 buffer.read(cx).remote_id(),
12857 range,
12858 )];
12859 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12860 let snapshot = self.buffer().read(cx).snapshot(cx);
12861 let mut point = ranges.last().unwrap().end.to_point(&snapshot);
12862 if point.row < snapshot.max_row().0 {
12863 point.row += 1;
12864 point.column = 0;
12865 point = snapshot.clip_point(point, Bias::Right);
12866 self.change_selections(Some(Autoscroll::top_relative(6)), window, cx, |s| {
12867 s.select_ranges([point..point]);
12868 })
12869 }
12870 return;
12871 }
12872 }
12873 self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
12874 self.go_to_next_hunk(&Default::default(), window, cx);
12875 }
12876
12877 fn do_stage_or_unstage(
12878 project: &Entity<Project>,
12879 stage: bool,
12880 buffer_id: BufferId,
12881 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
12882 snapshot: &MultiBufferSnapshot,
12883 cx: &mut Context<Self>,
12884 ) {
12885 let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
12886 log::debug!("no buffer for id");
12887 return;
12888 };
12889 let buffer = buffer.read(cx).snapshot();
12890 let Some((repo, path)) = project
12891 .read(cx)
12892 .repository_and_path_for_buffer_id(buffer_id, cx)
12893 else {
12894 log::debug!("no git repo for buffer id");
12895 return;
12896 };
12897 let Some(diff) = snapshot.diff_for_buffer_id(buffer_id) else {
12898 log::debug!("no diff for buffer id");
12899 return;
12900 };
12901 let Some(secondary_diff) = diff.secondary_diff() else {
12902 log::debug!("no secondary diff for buffer id");
12903 return;
12904 };
12905
12906 let edits = diff.secondary_edits_for_stage_or_unstage(
12907 stage,
12908 hunks.filter_map(|hunk| {
12909 if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None {
12910 return None;
12911 } else if !stage
12912 && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk
12913 {
12914 return None;
12915 }
12916 Some((
12917 hunk.diff_base_byte_range.clone(),
12918 hunk.secondary_diff_base_byte_range.clone(),
12919 hunk.buffer_range.clone(),
12920 ))
12921 }),
12922 &buffer,
12923 );
12924
12925 let Some(index_base) = secondary_diff
12926 .base_text()
12927 .map(|snapshot| snapshot.text.as_rope().clone())
12928 else {
12929 log::debug!("no index base");
12930 return;
12931 };
12932 let index_buffer = cx.new(|cx| {
12933 Buffer::local_normalized(index_base.clone(), text::LineEnding::default(), cx)
12934 });
12935 let new_index_text = index_buffer.update(cx, |index_buffer, cx| {
12936 index_buffer.edit(edits, None, cx);
12937 index_buffer.snapshot().as_rope().to_string()
12938 });
12939 let new_index_text = if new_index_text.is_empty()
12940 && (diff.is_single_insertion
12941 || buffer
12942 .file()
12943 .map_or(false, |file| file.disk_state() == DiskState::New))
12944 {
12945 log::debug!("removing from index");
12946 None
12947 } else {
12948 Some(new_index_text)
12949 };
12950
12951 let _ = repo.read(cx).set_index_text(&path, new_index_text);
12952 }
12953
12954 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12955 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12956 self.buffer
12957 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12958 }
12959
12960 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12961 self.buffer.update(cx, |buffer, cx| {
12962 let ranges = vec![Anchor::min()..Anchor::max()];
12963 if !buffer.all_diff_hunks_expanded()
12964 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12965 {
12966 buffer.collapse_diff_hunks(ranges, cx);
12967 true
12968 } else {
12969 false
12970 }
12971 })
12972 }
12973
12974 fn toggle_diff_hunks_in_ranges(
12975 &mut self,
12976 ranges: Vec<Range<Anchor>>,
12977 cx: &mut Context<'_, Editor>,
12978 ) {
12979 self.buffer.update(cx, |buffer, cx| {
12980 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12981 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
12982 })
12983 }
12984
12985 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
12986 self.buffer.update(cx, |buffer, cx| {
12987 let snapshot = buffer.snapshot(cx);
12988 let excerpt_id = range.end.excerpt_id;
12989 let point_range = range.to_point(&snapshot);
12990 let expand = !buffer.single_hunk_is_expanded(range, cx);
12991 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
12992 })
12993 }
12994
12995 pub(crate) fn apply_all_diff_hunks(
12996 &mut self,
12997 _: &ApplyAllDiffHunks,
12998 window: &mut Window,
12999 cx: &mut Context<Self>,
13000 ) {
13001 let buffers = self.buffer.read(cx).all_buffers();
13002 for branch_buffer in buffers {
13003 branch_buffer.update(cx, |branch_buffer, cx| {
13004 branch_buffer.merge_into_base(Vec::new(), cx);
13005 });
13006 }
13007
13008 if let Some(project) = self.project.clone() {
13009 self.save(true, project, window, cx).detach_and_log_err(cx);
13010 }
13011 }
13012
13013 pub(crate) fn apply_selected_diff_hunks(
13014 &mut self,
13015 _: &ApplyDiffHunk,
13016 window: &mut Window,
13017 cx: &mut Context<Self>,
13018 ) {
13019 let snapshot = self.snapshot(window, cx);
13020 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
13021 let mut ranges_by_buffer = HashMap::default();
13022 self.transact(window, cx, |editor, _window, cx| {
13023 for hunk in hunks {
13024 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13025 ranges_by_buffer
13026 .entry(buffer.clone())
13027 .or_insert_with(Vec::new)
13028 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13029 }
13030 }
13031
13032 for (buffer, ranges) in ranges_by_buffer {
13033 buffer.update(cx, |buffer, cx| {
13034 buffer.merge_into_base(ranges, cx);
13035 });
13036 }
13037 });
13038
13039 if let Some(project) = self.project.clone() {
13040 self.save(true, project, window, cx).detach_and_log_err(cx);
13041 }
13042 }
13043
13044 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13045 if hovered != self.gutter_hovered {
13046 self.gutter_hovered = hovered;
13047 cx.notify();
13048 }
13049 }
13050
13051 pub fn insert_blocks(
13052 &mut self,
13053 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13054 autoscroll: Option<Autoscroll>,
13055 cx: &mut Context<Self>,
13056 ) -> Vec<CustomBlockId> {
13057 let blocks = self
13058 .display_map
13059 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13060 if let Some(autoscroll) = autoscroll {
13061 self.request_autoscroll(autoscroll, cx);
13062 }
13063 cx.notify();
13064 blocks
13065 }
13066
13067 pub fn resize_blocks(
13068 &mut self,
13069 heights: HashMap<CustomBlockId, u32>,
13070 autoscroll: Option<Autoscroll>,
13071 cx: &mut Context<Self>,
13072 ) {
13073 self.display_map
13074 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13075 if let Some(autoscroll) = autoscroll {
13076 self.request_autoscroll(autoscroll, cx);
13077 }
13078 cx.notify();
13079 }
13080
13081 pub fn replace_blocks(
13082 &mut self,
13083 renderers: HashMap<CustomBlockId, RenderBlock>,
13084 autoscroll: Option<Autoscroll>,
13085 cx: &mut Context<Self>,
13086 ) {
13087 self.display_map
13088 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13089 if let Some(autoscroll) = autoscroll {
13090 self.request_autoscroll(autoscroll, cx);
13091 }
13092 cx.notify();
13093 }
13094
13095 pub fn remove_blocks(
13096 &mut self,
13097 block_ids: HashSet<CustomBlockId>,
13098 autoscroll: Option<Autoscroll>,
13099 cx: &mut Context<Self>,
13100 ) {
13101 self.display_map.update(cx, |display_map, cx| {
13102 display_map.remove_blocks(block_ids, cx)
13103 });
13104 if let Some(autoscroll) = autoscroll {
13105 self.request_autoscroll(autoscroll, cx);
13106 }
13107 cx.notify();
13108 }
13109
13110 pub fn row_for_block(
13111 &self,
13112 block_id: CustomBlockId,
13113 cx: &mut Context<Self>,
13114 ) -> Option<DisplayRow> {
13115 self.display_map
13116 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13117 }
13118
13119 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13120 self.focused_block = Some(focused_block);
13121 }
13122
13123 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13124 self.focused_block.take()
13125 }
13126
13127 pub fn insert_creases(
13128 &mut self,
13129 creases: impl IntoIterator<Item = Crease<Anchor>>,
13130 cx: &mut Context<Self>,
13131 ) -> Vec<CreaseId> {
13132 self.display_map
13133 .update(cx, |map, cx| map.insert_creases(creases, cx))
13134 }
13135
13136 pub fn remove_creases(
13137 &mut self,
13138 ids: impl IntoIterator<Item = CreaseId>,
13139 cx: &mut Context<Self>,
13140 ) {
13141 self.display_map
13142 .update(cx, |map, cx| map.remove_creases(ids, cx));
13143 }
13144
13145 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
13146 self.display_map
13147 .update(cx, |map, cx| map.snapshot(cx))
13148 .longest_row()
13149 }
13150
13151 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
13152 self.display_map
13153 .update(cx, |map, cx| map.snapshot(cx))
13154 .max_point()
13155 }
13156
13157 pub fn text(&self, cx: &App) -> String {
13158 self.buffer.read(cx).read(cx).text()
13159 }
13160
13161 pub fn is_empty(&self, cx: &App) -> bool {
13162 self.buffer.read(cx).read(cx).is_empty()
13163 }
13164
13165 pub fn text_option(&self, cx: &App) -> Option<String> {
13166 let text = self.text(cx);
13167 let text = text.trim();
13168
13169 if text.is_empty() {
13170 return None;
13171 }
13172
13173 Some(text.to_string())
13174 }
13175
13176 pub fn set_text(
13177 &mut self,
13178 text: impl Into<Arc<str>>,
13179 window: &mut Window,
13180 cx: &mut Context<Self>,
13181 ) {
13182 self.transact(window, cx, |this, _, cx| {
13183 this.buffer
13184 .read(cx)
13185 .as_singleton()
13186 .expect("you can only call set_text on editors for singleton buffers")
13187 .update(cx, |buffer, cx| buffer.set_text(text, cx));
13188 });
13189 }
13190
13191 pub fn display_text(&self, cx: &mut App) -> String {
13192 self.display_map
13193 .update(cx, |map, cx| map.snapshot(cx))
13194 .text()
13195 }
13196
13197 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
13198 let mut wrap_guides = smallvec::smallvec![];
13199
13200 if self.show_wrap_guides == Some(false) {
13201 return wrap_guides;
13202 }
13203
13204 let settings = self.buffer.read(cx).settings_at(0, cx);
13205 if settings.show_wrap_guides {
13206 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
13207 wrap_guides.push((soft_wrap as usize, true));
13208 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
13209 wrap_guides.push((soft_wrap as usize, true));
13210 }
13211 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
13212 }
13213
13214 wrap_guides
13215 }
13216
13217 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
13218 let settings = self.buffer.read(cx).settings_at(0, cx);
13219 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
13220 match mode {
13221 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
13222 SoftWrap::None
13223 }
13224 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
13225 language_settings::SoftWrap::PreferredLineLength => {
13226 SoftWrap::Column(settings.preferred_line_length)
13227 }
13228 language_settings::SoftWrap::Bounded => {
13229 SoftWrap::Bounded(settings.preferred_line_length)
13230 }
13231 }
13232 }
13233
13234 pub fn set_soft_wrap_mode(
13235 &mut self,
13236 mode: language_settings::SoftWrap,
13237
13238 cx: &mut Context<Self>,
13239 ) {
13240 self.soft_wrap_mode_override = Some(mode);
13241 cx.notify();
13242 }
13243
13244 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
13245 self.text_style_refinement = Some(style);
13246 }
13247
13248 /// called by the Element so we know what style we were most recently rendered with.
13249 pub(crate) fn set_style(
13250 &mut self,
13251 style: EditorStyle,
13252 window: &mut Window,
13253 cx: &mut Context<Self>,
13254 ) {
13255 let rem_size = window.rem_size();
13256 self.display_map.update(cx, |map, cx| {
13257 map.set_font(
13258 style.text.font(),
13259 style.text.font_size.to_pixels(rem_size),
13260 cx,
13261 )
13262 });
13263 self.style = Some(style);
13264 }
13265
13266 pub fn style(&self) -> Option<&EditorStyle> {
13267 self.style.as_ref()
13268 }
13269
13270 // Called by the element. This method is not designed to be called outside of the editor
13271 // element's layout code because it does not notify when rewrapping is computed synchronously.
13272 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
13273 self.display_map
13274 .update(cx, |map, cx| map.set_wrap_width(width, cx))
13275 }
13276
13277 pub fn set_soft_wrap(&mut self) {
13278 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
13279 }
13280
13281 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
13282 if self.soft_wrap_mode_override.is_some() {
13283 self.soft_wrap_mode_override.take();
13284 } else {
13285 let soft_wrap = match self.soft_wrap_mode(cx) {
13286 SoftWrap::GitDiff => return,
13287 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
13288 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
13289 language_settings::SoftWrap::None
13290 }
13291 };
13292 self.soft_wrap_mode_override = Some(soft_wrap);
13293 }
13294 cx.notify();
13295 }
13296
13297 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
13298 let Some(workspace) = self.workspace() else {
13299 return;
13300 };
13301 let fs = workspace.read(cx).app_state().fs.clone();
13302 let current_show = TabBarSettings::get_global(cx).show;
13303 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
13304 setting.show = Some(!current_show);
13305 });
13306 }
13307
13308 pub fn toggle_indent_guides(
13309 &mut self,
13310 _: &ToggleIndentGuides,
13311 _: &mut Window,
13312 cx: &mut Context<Self>,
13313 ) {
13314 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
13315 self.buffer
13316 .read(cx)
13317 .settings_at(0, cx)
13318 .indent_guides
13319 .enabled
13320 });
13321 self.show_indent_guides = Some(!currently_enabled);
13322 cx.notify();
13323 }
13324
13325 fn should_show_indent_guides(&self) -> Option<bool> {
13326 self.show_indent_guides
13327 }
13328
13329 pub fn toggle_line_numbers(
13330 &mut self,
13331 _: &ToggleLineNumbers,
13332 _: &mut Window,
13333 cx: &mut Context<Self>,
13334 ) {
13335 let mut editor_settings = EditorSettings::get_global(cx).clone();
13336 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
13337 EditorSettings::override_global(editor_settings, cx);
13338 }
13339
13340 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
13341 self.use_relative_line_numbers
13342 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
13343 }
13344
13345 pub fn toggle_relative_line_numbers(
13346 &mut self,
13347 _: &ToggleRelativeLineNumbers,
13348 _: &mut Window,
13349 cx: &mut Context<Self>,
13350 ) {
13351 let is_relative = self.should_use_relative_line_numbers(cx);
13352 self.set_relative_line_number(Some(!is_relative), cx)
13353 }
13354
13355 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
13356 self.use_relative_line_numbers = is_relative;
13357 cx.notify();
13358 }
13359
13360 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
13361 self.show_gutter = show_gutter;
13362 cx.notify();
13363 }
13364
13365 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
13366 self.show_scrollbars = show_scrollbars;
13367 cx.notify();
13368 }
13369
13370 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
13371 self.show_line_numbers = Some(show_line_numbers);
13372 cx.notify();
13373 }
13374
13375 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
13376 self.show_git_diff_gutter = Some(show_git_diff_gutter);
13377 cx.notify();
13378 }
13379
13380 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
13381 self.show_code_actions = Some(show_code_actions);
13382 cx.notify();
13383 }
13384
13385 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
13386 self.show_runnables = Some(show_runnables);
13387 cx.notify();
13388 }
13389
13390 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
13391 if self.display_map.read(cx).masked != masked {
13392 self.display_map.update(cx, |map, _| map.masked = masked);
13393 }
13394 cx.notify()
13395 }
13396
13397 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
13398 self.show_wrap_guides = Some(show_wrap_guides);
13399 cx.notify();
13400 }
13401
13402 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
13403 self.show_indent_guides = Some(show_indent_guides);
13404 cx.notify();
13405 }
13406
13407 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
13408 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
13409 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
13410 if let Some(dir) = file.abs_path(cx).parent() {
13411 return Some(dir.to_owned());
13412 }
13413 }
13414
13415 if let Some(project_path) = buffer.read(cx).project_path(cx) {
13416 return Some(project_path.path.to_path_buf());
13417 }
13418 }
13419
13420 None
13421 }
13422
13423 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
13424 self.active_excerpt(cx)?
13425 .1
13426 .read(cx)
13427 .file()
13428 .and_then(|f| f.as_local())
13429 }
13430
13431 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13432 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13433 let buffer = buffer.read(cx);
13434 if let Some(project_path) = buffer.project_path(cx) {
13435 let project = self.project.as_ref()?.read(cx);
13436 project.absolute_path(&project_path, cx)
13437 } else {
13438 buffer
13439 .file()
13440 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
13441 }
13442 })
13443 }
13444
13445 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
13446 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
13447 let project_path = buffer.read(cx).project_path(cx)?;
13448 let project = self.project.as_ref()?.read(cx);
13449 let entry = project.entry_for_path(&project_path, cx)?;
13450 let path = entry.path.to_path_buf();
13451 Some(path)
13452 })
13453 }
13454
13455 pub fn reveal_in_finder(
13456 &mut self,
13457 _: &RevealInFileManager,
13458 _window: &mut Window,
13459 cx: &mut Context<Self>,
13460 ) {
13461 if let Some(target) = self.target_file(cx) {
13462 cx.reveal_path(&target.abs_path(cx));
13463 }
13464 }
13465
13466 pub fn copy_path(
13467 &mut self,
13468 _: &zed_actions::workspace::CopyPath,
13469 _window: &mut Window,
13470 cx: &mut Context<Self>,
13471 ) {
13472 if let Some(path) = self.target_file_abs_path(cx) {
13473 if let Some(path) = path.to_str() {
13474 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13475 }
13476 }
13477 }
13478
13479 pub fn copy_relative_path(
13480 &mut self,
13481 _: &zed_actions::workspace::CopyRelativePath,
13482 _window: &mut Window,
13483 cx: &mut Context<Self>,
13484 ) {
13485 if let Some(path) = self.target_file_path(cx) {
13486 if let Some(path) = path.to_str() {
13487 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
13488 }
13489 }
13490 }
13491
13492 pub fn copy_file_name_without_extension(
13493 &mut self,
13494 _: &CopyFileNameWithoutExtension,
13495 _: &mut Window,
13496 cx: &mut Context<Self>,
13497 ) {
13498 if let Some(file) = self.target_file(cx) {
13499 if let Some(file_stem) = file.path().file_stem() {
13500 if let Some(name) = file_stem.to_str() {
13501 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13502 }
13503 }
13504 }
13505 }
13506
13507 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
13508 if let Some(file) = self.target_file(cx) {
13509 if let Some(file_name) = file.path().file_name() {
13510 if let Some(name) = file_name.to_str() {
13511 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
13512 }
13513 }
13514 }
13515 }
13516
13517 pub fn toggle_git_blame(
13518 &mut self,
13519 _: &ToggleGitBlame,
13520 window: &mut Window,
13521 cx: &mut Context<Self>,
13522 ) {
13523 self.show_git_blame_gutter = !self.show_git_blame_gutter;
13524
13525 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
13526 self.start_git_blame(true, window, cx);
13527 }
13528
13529 cx.notify();
13530 }
13531
13532 pub fn toggle_git_blame_inline(
13533 &mut self,
13534 _: &ToggleGitBlameInline,
13535 window: &mut Window,
13536 cx: &mut Context<Self>,
13537 ) {
13538 self.toggle_git_blame_inline_internal(true, window, cx);
13539 cx.notify();
13540 }
13541
13542 pub fn git_blame_inline_enabled(&self) -> bool {
13543 self.git_blame_inline_enabled
13544 }
13545
13546 pub fn toggle_selection_menu(
13547 &mut self,
13548 _: &ToggleSelectionMenu,
13549 _: &mut Window,
13550 cx: &mut Context<Self>,
13551 ) {
13552 self.show_selection_menu = self
13553 .show_selection_menu
13554 .map(|show_selections_menu| !show_selections_menu)
13555 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
13556
13557 cx.notify();
13558 }
13559
13560 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
13561 self.show_selection_menu
13562 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
13563 }
13564
13565 fn start_git_blame(
13566 &mut self,
13567 user_triggered: bool,
13568 window: &mut Window,
13569 cx: &mut Context<Self>,
13570 ) {
13571 if let Some(project) = self.project.as_ref() {
13572 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
13573 return;
13574 };
13575
13576 if buffer.read(cx).file().is_none() {
13577 return;
13578 }
13579
13580 let focused = self.focus_handle(cx).contains_focused(window, cx);
13581
13582 let project = project.clone();
13583 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
13584 self.blame_subscription =
13585 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
13586 self.blame = Some(blame);
13587 }
13588 }
13589
13590 fn toggle_git_blame_inline_internal(
13591 &mut self,
13592 user_triggered: bool,
13593 window: &mut Window,
13594 cx: &mut Context<Self>,
13595 ) {
13596 if self.git_blame_inline_enabled {
13597 self.git_blame_inline_enabled = false;
13598 self.show_git_blame_inline = false;
13599 self.show_git_blame_inline_delay_task.take();
13600 } else {
13601 self.git_blame_inline_enabled = true;
13602 self.start_git_blame_inline(user_triggered, window, cx);
13603 }
13604
13605 cx.notify();
13606 }
13607
13608 fn start_git_blame_inline(
13609 &mut self,
13610 user_triggered: bool,
13611 window: &mut Window,
13612 cx: &mut Context<Self>,
13613 ) {
13614 self.start_git_blame(user_triggered, window, cx);
13615
13616 if ProjectSettings::get_global(cx)
13617 .git
13618 .inline_blame_delay()
13619 .is_some()
13620 {
13621 self.start_inline_blame_timer(window, cx);
13622 } else {
13623 self.show_git_blame_inline = true
13624 }
13625 }
13626
13627 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
13628 self.blame.as_ref()
13629 }
13630
13631 pub fn show_git_blame_gutter(&self) -> bool {
13632 self.show_git_blame_gutter
13633 }
13634
13635 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
13636 self.show_git_blame_gutter && self.has_blame_entries(cx)
13637 }
13638
13639 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
13640 self.show_git_blame_inline
13641 && (self.focus_handle.is_focused(window)
13642 || self
13643 .git_blame_inline_tooltip
13644 .as_ref()
13645 .and_then(|t| t.upgrade())
13646 .is_some())
13647 && !self.newest_selection_head_on_empty_line(cx)
13648 && self.has_blame_entries(cx)
13649 }
13650
13651 fn has_blame_entries(&self, cx: &App) -> bool {
13652 self.blame()
13653 .map_or(false, |blame| blame.read(cx).has_generated_entries())
13654 }
13655
13656 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
13657 let cursor_anchor = self.selections.newest_anchor().head();
13658
13659 let snapshot = self.buffer.read(cx).snapshot(cx);
13660 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
13661
13662 snapshot.line_len(buffer_row) == 0
13663 }
13664
13665 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
13666 let buffer_and_selection = maybe!({
13667 let selection = self.selections.newest::<Point>(cx);
13668 let selection_range = selection.range();
13669
13670 let multi_buffer = self.buffer().read(cx);
13671 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13672 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13673
13674 let (buffer, range, _) = if selection.reversed {
13675 buffer_ranges.first()
13676 } else {
13677 buffer_ranges.last()
13678 }?;
13679
13680 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13681 ..text::ToPoint::to_point(&range.end, &buffer).row;
13682 Some((
13683 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13684 selection,
13685 ))
13686 });
13687
13688 let Some((buffer, selection)) = buffer_and_selection else {
13689 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13690 };
13691
13692 let Some(project) = self.project.as_ref() else {
13693 return Task::ready(Err(anyhow!("editor does not have project")));
13694 };
13695
13696 project.update(cx, |project, cx| {
13697 project.get_permalink_to_line(&buffer, selection, cx)
13698 })
13699 }
13700
13701 pub fn copy_permalink_to_line(
13702 &mut self,
13703 _: &CopyPermalinkToLine,
13704 window: &mut Window,
13705 cx: &mut Context<Self>,
13706 ) {
13707 let permalink_task = self.get_permalink_to_line(cx);
13708 let workspace = self.workspace();
13709
13710 cx.spawn_in(window, |_, mut cx| async move {
13711 match permalink_task.await {
13712 Ok(permalink) => {
13713 cx.update(|_, cx| {
13714 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13715 })
13716 .ok();
13717 }
13718 Err(err) => {
13719 let message = format!("Failed to copy permalink: {err}");
13720
13721 Err::<(), anyhow::Error>(err).log_err();
13722
13723 if let Some(workspace) = workspace {
13724 workspace
13725 .update_in(&mut cx, |workspace, _, cx| {
13726 struct CopyPermalinkToLine;
13727
13728 workspace.show_toast(
13729 Toast::new(
13730 NotificationId::unique::<CopyPermalinkToLine>(),
13731 message,
13732 ),
13733 cx,
13734 )
13735 })
13736 .ok();
13737 }
13738 }
13739 }
13740 })
13741 .detach();
13742 }
13743
13744 pub fn copy_file_location(
13745 &mut self,
13746 _: &CopyFileLocation,
13747 _: &mut Window,
13748 cx: &mut Context<Self>,
13749 ) {
13750 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13751 if let Some(file) = self.target_file(cx) {
13752 if let Some(path) = file.path().to_str() {
13753 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13754 }
13755 }
13756 }
13757
13758 pub fn open_permalink_to_line(
13759 &mut self,
13760 _: &OpenPermalinkToLine,
13761 window: &mut Window,
13762 cx: &mut Context<Self>,
13763 ) {
13764 let permalink_task = self.get_permalink_to_line(cx);
13765 let workspace = self.workspace();
13766
13767 cx.spawn_in(window, |_, mut cx| async move {
13768 match permalink_task.await {
13769 Ok(permalink) => {
13770 cx.update(|_, cx| {
13771 cx.open_url(permalink.as_ref());
13772 })
13773 .ok();
13774 }
13775 Err(err) => {
13776 let message = format!("Failed to open permalink: {err}");
13777
13778 Err::<(), anyhow::Error>(err).log_err();
13779
13780 if let Some(workspace) = workspace {
13781 workspace
13782 .update(&mut cx, |workspace, cx| {
13783 struct OpenPermalinkToLine;
13784
13785 workspace.show_toast(
13786 Toast::new(
13787 NotificationId::unique::<OpenPermalinkToLine>(),
13788 message,
13789 ),
13790 cx,
13791 )
13792 })
13793 .ok();
13794 }
13795 }
13796 }
13797 })
13798 .detach();
13799 }
13800
13801 pub fn insert_uuid_v4(
13802 &mut self,
13803 _: &InsertUuidV4,
13804 window: &mut Window,
13805 cx: &mut Context<Self>,
13806 ) {
13807 self.insert_uuid(UuidVersion::V4, window, cx);
13808 }
13809
13810 pub fn insert_uuid_v7(
13811 &mut self,
13812 _: &InsertUuidV7,
13813 window: &mut Window,
13814 cx: &mut Context<Self>,
13815 ) {
13816 self.insert_uuid(UuidVersion::V7, window, cx);
13817 }
13818
13819 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13820 self.transact(window, cx, |this, window, cx| {
13821 let edits = this
13822 .selections
13823 .all::<Point>(cx)
13824 .into_iter()
13825 .map(|selection| {
13826 let uuid = match version {
13827 UuidVersion::V4 => uuid::Uuid::new_v4(),
13828 UuidVersion::V7 => uuid::Uuid::now_v7(),
13829 };
13830
13831 (selection.range(), uuid.to_string())
13832 });
13833 this.edit(edits, cx);
13834 this.refresh_inline_completion(true, false, window, cx);
13835 });
13836 }
13837
13838 pub fn open_selections_in_multibuffer(
13839 &mut self,
13840 _: &OpenSelectionsInMultibuffer,
13841 window: &mut Window,
13842 cx: &mut Context<Self>,
13843 ) {
13844 let multibuffer = self.buffer.read(cx);
13845
13846 let Some(buffer) = multibuffer.as_singleton() else {
13847 return;
13848 };
13849
13850 let Some(workspace) = self.workspace() else {
13851 return;
13852 };
13853
13854 let locations = self
13855 .selections
13856 .disjoint_anchors()
13857 .iter()
13858 .map(|range| Location {
13859 buffer: buffer.clone(),
13860 range: range.start.text_anchor..range.end.text_anchor,
13861 })
13862 .collect::<Vec<_>>();
13863
13864 let title = multibuffer.title(cx).to_string();
13865
13866 cx.spawn_in(window, |_, mut cx| async move {
13867 workspace.update_in(&mut cx, |workspace, window, cx| {
13868 Self::open_locations_in_multibuffer(
13869 workspace,
13870 locations,
13871 format!("Selections for '{title}'"),
13872 false,
13873 MultibufferSelectionMode::All,
13874 window,
13875 cx,
13876 );
13877 })
13878 })
13879 .detach();
13880 }
13881
13882 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13883 /// last highlight added will be used.
13884 ///
13885 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13886 pub fn highlight_rows<T: 'static>(
13887 &mut self,
13888 range: Range<Anchor>,
13889 color: Hsla,
13890 should_autoscroll: bool,
13891 cx: &mut Context<Self>,
13892 ) {
13893 let snapshot = self.buffer().read(cx).snapshot(cx);
13894 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13895 let ix = row_highlights.binary_search_by(|highlight| {
13896 Ordering::Equal
13897 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13898 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13899 });
13900
13901 if let Err(mut ix) = ix {
13902 let index = post_inc(&mut self.highlight_order);
13903
13904 // If this range intersects with the preceding highlight, then merge it with
13905 // the preceding highlight. Otherwise insert a new highlight.
13906 let mut merged = false;
13907 if ix > 0 {
13908 let prev_highlight = &mut row_highlights[ix - 1];
13909 if prev_highlight
13910 .range
13911 .end
13912 .cmp(&range.start, &snapshot)
13913 .is_ge()
13914 {
13915 ix -= 1;
13916 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13917 prev_highlight.range.end = range.end;
13918 }
13919 merged = true;
13920 prev_highlight.index = index;
13921 prev_highlight.color = color;
13922 prev_highlight.should_autoscroll = should_autoscroll;
13923 }
13924 }
13925
13926 if !merged {
13927 row_highlights.insert(
13928 ix,
13929 RowHighlight {
13930 range: range.clone(),
13931 index,
13932 color,
13933 should_autoscroll,
13934 },
13935 );
13936 }
13937
13938 // If any of the following highlights intersect with this one, merge them.
13939 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13940 let highlight = &row_highlights[ix];
13941 if next_highlight
13942 .range
13943 .start
13944 .cmp(&highlight.range.end, &snapshot)
13945 .is_le()
13946 {
13947 if next_highlight
13948 .range
13949 .end
13950 .cmp(&highlight.range.end, &snapshot)
13951 .is_gt()
13952 {
13953 row_highlights[ix].range.end = next_highlight.range.end;
13954 }
13955 row_highlights.remove(ix + 1);
13956 } else {
13957 break;
13958 }
13959 }
13960 }
13961 }
13962
13963 /// Remove any highlighted row ranges of the given type that intersect the
13964 /// given ranges.
13965 pub fn remove_highlighted_rows<T: 'static>(
13966 &mut self,
13967 ranges_to_remove: Vec<Range<Anchor>>,
13968 cx: &mut Context<Self>,
13969 ) {
13970 let snapshot = self.buffer().read(cx).snapshot(cx);
13971 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13972 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13973 row_highlights.retain(|highlight| {
13974 while let Some(range_to_remove) = ranges_to_remove.peek() {
13975 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13976 Ordering::Less | Ordering::Equal => {
13977 ranges_to_remove.next();
13978 }
13979 Ordering::Greater => {
13980 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13981 Ordering::Less | Ordering::Equal => {
13982 return false;
13983 }
13984 Ordering::Greater => break,
13985 }
13986 }
13987 }
13988 }
13989
13990 true
13991 })
13992 }
13993
13994 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13995 pub fn clear_row_highlights<T: 'static>(&mut self) {
13996 self.highlighted_rows.remove(&TypeId::of::<T>());
13997 }
13998
13999 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14000 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14001 self.highlighted_rows
14002 .get(&TypeId::of::<T>())
14003 .map_or(&[] as &[_], |vec| vec.as_slice())
14004 .iter()
14005 .map(|highlight| (highlight.range.clone(), highlight.color))
14006 }
14007
14008 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14009 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14010 /// Allows to ignore certain kinds of highlights.
14011 pub fn highlighted_display_rows(
14012 &self,
14013 window: &mut Window,
14014 cx: &mut App,
14015 ) -> BTreeMap<DisplayRow, Background> {
14016 let snapshot = self.snapshot(window, cx);
14017 let mut used_highlight_orders = HashMap::default();
14018 self.highlighted_rows
14019 .iter()
14020 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14021 .fold(
14022 BTreeMap::<DisplayRow, Background>::new(),
14023 |mut unique_rows, highlight| {
14024 let start = highlight.range.start.to_display_point(&snapshot);
14025 let end = highlight.range.end.to_display_point(&snapshot);
14026 let start_row = start.row().0;
14027 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14028 && end.column() == 0
14029 {
14030 end.row().0.saturating_sub(1)
14031 } else {
14032 end.row().0
14033 };
14034 for row in start_row..=end_row {
14035 let used_index =
14036 used_highlight_orders.entry(row).or_insert(highlight.index);
14037 if highlight.index >= *used_index {
14038 *used_index = highlight.index;
14039 unique_rows.insert(DisplayRow(row), highlight.color.into());
14040 }
14041 }
14042 unique_rows
14043 },
14044 )
14045 }
14046
14047 pub fn highlighted_display_row_for_autoscroll(
14048 &self,
14049 snapshot: &DisplaySnapshot,
14050 ) -> Option<DisplayRow> {
14051 self.highlighted_rows
14052 .values()
14053 .flat_map(|highlighted_rows| highlighted_rows.iter())
14054 .filter_map(|highlight| {
14055 if highlight.should_autoscroll {
14056 Some(highlight.range.start.to_display_point(snapshot).row())
14057 } else {
14058 None
14059 }
14060 })
14061 .min()
14062 }
14063
14064 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14065 self.highlight_background::<SearchWithinRange>(
14066 ranges,
14067 |colors| colors.editor_document_highlight_read_background,
14068 cx,
14069 )
14070 }
14071
14072 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14073 self.breadcrumb_header = Some(new_header);
14074 }
14075
14076 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14077 self.clear_background_highlights::<SearchWithinRange>(cx);
14078 }
14079
14080 pub fn highlight_background<T: 'static>(
14081 &mut self,
14082 ranges: &[Range<Anchor>],
14083 color_fetcher: fn(&ThemeColors) -> Hsla,
14084 cx: &mut Context<Self>,
14085 ) {
14086 self.background_highlights
14087 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14088 self.scrollbar_marker_state.dirty = true;
14089 cx.notify();
14090 }
14091
14092 pub fn clear_background_highlights<T: 'static>(
14093 &mut self,
14094 cx: &mut Context<Self>,
14095 ) -> Option<BackgroundHighlight> {
14096 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14097 if !text_highlights.1.is_empty() {
14098 self.scrollbar_marker_state.dirty = true;
14099 cx.notify();
14100 }
14101 Some(text_highlights)
14102 }
14103
14104 pub fn highlight_gutter<T: 'static>(
14105 &mut self,
14106 ranges: &[Range<Anchor>],
14107 color_fetcher: fn(&App) -> Hsla,
14108 cx: &mut Context<Self>,
14109 ) {
14110 self.gutter_highlights
14111 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14112 cx.notify();
14113 }
14114
14115 pub fn clear_gutter_highlights<T: 'static>(
14116 &mut self,
14117 cx: &mut Context<Self>,
14118 ) -> Option<GutterHighlight> {
14119 cx.notify();
14120 self.gutter_highlights.remove(&TypeId::of::<T>())
14121 }
14122
14123 #[cfg(feature = "test-support")]
14124 pub fn all_text_background_highlights(
14125 &self,
14126 window: &mut Window,
14127 cx: &mut Context<Self>,
14128 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14129 let snapshot = self.snapshot(window, cx);
14130 let buffer = &snapshot.buffer_snapshot;
14131 let start = buffer.anchor_before(0);
14132 let end = buffer.anchor_after(buffer.len());
14133 let theme = cx.theme().colors();
14134 self.background_highlights_in_range(start..end, &snapshot, theme)
14135 }
14136
14137 #[cfg(feature = "test-support")]
14138 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
14139 let snapshot = self.buffer().read(cx).snapshot(cx);
14140
14141 let highlights = self
14142 .background_highlights
14143 .get(&TypeId::of::<items::BufferSearchHighlights>());
14144
14145 if let Some((_color, ranges)) = highlights {
14146 ranges
14147 .iter()
14148 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
14149 .collect_vec()
14150 } else {
14151 vec![]
14152 }
14153 }
14154
14155 fn document_highlights_for_position<'a>(
14156 &'a self,
14157 position: Anchor,
14158 buffer: &'a MultiBufferSnapshot,
14159 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
14160 let read_highlights = self
14161 .background_highlights
14162 .get(&TypeId::of::<DocumentHighlightRead>())
14163 .map(|h| &h.1);
14164 let write_highlights = self
14165 .background_highlights
14166 .get(&TypeId::of::<DocumentHighlightWrite>())
14167 .map(|h| &h.1);
14168 let left_position = position.bias_left(buffer);
14169 let right_position = position.bias_right(buffer);
14170 read_highlights
14171 .into_iter()
14172 .chain(write_highlights)
14173 .flat_map(move |ranges| {
14174 let start_ix = match ranges.binary_search_by(|probe| {
14175 let cmp = probe.end.cmp(&left_position, buffer);
14176 if cmp.is_ge() {
14177 Ordering::Greater
14178 } else {
14179 Ordering::Less
14180 }
14181 }) {
14182 Ok(i) | Err(i) => i,
14183 };
14184
14185 ranges[start_ix..]
14186 .iter()
14187 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
14188 })
14189 }
14190
14191 pub fn has_background_highlights<T: 'static>(&self) -> bool {
14192 self.background_highlights
14193 .get(&TypeId::of::<T>())
14194 .map_or(false, |(_, highlights)| !highlights.is_empty())
14195 }
14196
14197 pub fn background_highlights_in_range(
14198 &self,
14199 search_range: Range<Anchor>,
14200 display_snapshot: &DisplaySnapshot,
14201 theme: &ThemeColors,
14202 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14203 let mut results = Vec::new();
14204 for (color_fetcher, ranges) in self.background_highlights.values() {
14205 let color = color_fetcher(theme);
14206 let start_ix = match ranges.binary_search_by(|probe| {
14207 let cmp = probe
14208 .end
14209 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14210 if cmp.is_gt() {
14211 Ordering::Greater
14212 } else {
14213 Ordering::Less
14214 }
14215 }) {
14216 Ok(i) | Err(i) => i,
14217 };
14218 for range in &ranges[start_ix..] {
14219 if range
14220 .start
14221 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14222 .is_ge()
14223 {
14224 break;
14225 }
14226
14227 let start = range.start.to_display_point(display_snapshot);
14228 let end = range.end.to_display_point(display_snapshot);
14229 results.push((start..end, color))
14230 }
14231 }
14232 results
14233 }
14234
14235 pub fn background_highlight_row_ranges<T: 'static>(
14236 &self,
14237 search_range: Range<Anchor>,
14238 display_snapshot: &DisplaySnapshot,
14239 count: usize,
14240 ) -> Vec<RangeInclusive<DisplayPoint>> {
14241 let mut results = Vec::new();
14242 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
14243 return vec![];
14244 };
14245
14246 let start_ix = match ranges.binary_search_by(|probe| {
14247 let cmp = probe
14248 .end
14249 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14250 if cmp.is_gt() {
14251 Ordering::Greater
14252 } else {
14253 Ordering::Less
14254 }
14255 }) {
14256 Ok(i) | Err(i) => i,
14257 };
14258 let mut push_region = |start: Option<Point>, end: Option<Point>| {
14259 if let (Some(start_display), Some(end_display)) = (start, end) {
14260 results.push(
14261 start_display.to_display_point(display_snapshot)
14262 ..=end_display.to_display_point(display_snapshot),
14263 );
14264 }
14265 };
14266 let mut start_row: Option<Point> = None;
14267 let mut end_row: Option<Point> = None;
14268 if ranges.len() > count {
14269 return Vec::new();
14270 }
14271 for range in &ranges[start_ix..] {
14272 if range
14273 .start
14274 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14275 .is_ge()
14276 {
14277 break;
14278 }
14279 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
14280 if let Some(current_row) = &end_row {
14281 if end.row == current_row.row {
14282 continue;
14283 }
14284 }
14285 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
14286 if start_row.is_none() {
14287 assert_eq!(end_row, None);
14288 start_row = Some(start);
14289 end_row = Some(end);
14290 continue;
14291 }
14292 if let Some(current_end) = end_row.as_mut() {
14293 if start.row > current_end.row + 1 {
14294 push_region(start_row, end_row);
14295 start_row = Some(start);
14296 end_row = Some(end);
14297 } else {
14298 // Merge two hunks.
14299 *current_end = end;
14300 }
14301 } else {
14302 unreachable!();
14303 }
14304 }
14305 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
14306 push_region(start_row, end_row);
14307 results
14308 }
14309
14310 pub fn gutter_highlights_in_range(
14311 &self,
14312 search_range: Range<Anchor>,
14313 display_snapshot: &DisplaySnapshot,
14314 cx: &App,
14315 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14316 let mut results = Vec::new();
14317 for (color_fetcher, ranges) in self.gutter_highlights.values() {
14318 let color = color_fetcher(cx);
14319 let start_ix = match ranges.binary_search_by(|probe| {
14320 let cmp = probe
14321 .end
14322 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14323 if cmp.is_gt() {
14324 Ordering::Greater
14325 } else {
14326 Ordering::Less
14327 }
14328 }) {
14329 Ok(i) | Err(i) => i,
14330 };
14331 for range in &ranges[start_ix..] {
14332 if range
14333 .start
14334 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14335 .is_ge()
14336 {
14337 break;
14338 }
14339
14340 let start = range.start.to_display_point(display_snapshot);
14341 let end = range.end.to_display_point(display_snapshot);
14342 results.push((start..end, color))
14343 }
14344 }
14345 results
14346 }
14347
14348 /// Get the text ranges corresponding to the redaction query
14349 pub fn redacted_ranges(
14350 &self,
14351 search_range: Range<Anchor>,
14352 display_snapshot: &DisplaySnapshot,
14353 cx: &App,
14354 ) -> Vec<Range<DisplayPoint>> {
14355 display_snapshot
14356 .buffer_snapshot
14357 .redacted_ranges(search_range, |file| {
14358 if let Some(file) = file {
14359 file.is_private()
14360 && EditorSettings::get(
14361 Some(SettingsLocation {
14362 worktree_id: file.worktree_id(cx),
14363 path: file.path().as_ref(),
14364 }),
14365 cx,
14366 )
14367 .redact_private_values
14368 } else {
14369 false
14370 }
14371 })
14372 .map(|range| {
14373 range.start.to_display_point(display_snapshot)
14374 ..range.end.to_display_point(display_snapshot)
14375 })
14376 .collect()
14377 }
14378
14379 pub fn highlight_text<T: 'static>(
14380 &mut self,
14381 ranges: Vec<Range<Anchor>>,
14382 style: HighlightStyle,
14383 cx: &mut Context<Self>,
14384 ) {
14385 self.display_map.update(cx, |map, _| {
14386 map.highlight_text(TypeId::of::<T>(), ranges, style)
14387 });
14388 cx.notify();
14389 }
14390
14391 pub(crate) fn highlight_inlays<T: 'static>(
14392 &mut self,
14393 highlights: Vec<InlayHighlight>,
14394 style: HighlightStyle,
14395 cx: &mut Context<Self>,
14396 ) {
14397 self.display_map.update(cx, |map, _| {
14398 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
14399 });
14400 cx.notify();
14401 }
14402
14403 pub fn text_highlights<'a, T: 'static>(
14404 &'a self,
14405 cx: &'a App,
14406 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
14407 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
14408 }
14409
14410 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
14411 let cleared = self
14412 .display_map
14413 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
14414 if cleared {
14415 cx.notify();
14416 }
14417 }
14418
14419 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
14420 (self.read_only(cx) || self.blink_manager.read(cx).visible())
14421 && self.focus_handle.is_focused(window)
14422 }
14423
14424 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
14425 self.show_cursor_when_unfocused = is_enabled;
14426 cx.notify();
14427 }
14428
14429 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
14430 cx.notify();
14431 }
14432
14433 fn on_buffer_event(
14434 &mut self,
14435 multibuffer: &Entity<MultiBuffer>,
14436 event: &multi_buffer::Event,
14437 window: &mut Window,
14438 cx: &mut Context<Self>,
14439 ) {
14440 match event {
14441 multi_buffer::Event::Edited {
14442 singleton_buffer_edited,
14443 edited_buffer: buffer_edited,
14444 } => {
14445 self.scrollbar_marker_state.dirty = true;
14446 self.active_indent_guides_state.dirty = true;
14447 self.refresh_active_diagnostics(cx);
14448 self.refresh_code_actions(window, cx);
14449 if self.has_active_inline_completion() {
14450 self.update_visible_inline_completion(window, cx);
14451 }
14452 if let Some(buffer) = buffer_edited {
14453 let buffer_id = buffer.read(cx).remote_id();
14454 if !self.registered_buffers.contains_key(&buffer_id) {
14455 if let Some(project) = self.project.as_ref() {
14456 project.update(cx, |project, cx| {
14457 self.registered_buffers.insert(
14458 buffer_id,
14459 project.register_buffer_with_language_servers(&buffer, cx),
14460 );
14461 })
14462 }
14463 }
14464 }
14465 cx.emit(EditorEvent::BufferEdited);
14466 cx.emit(SearchEvent::MatchesInvalidated);
14467 if *singleton_buffer_edited {
14468 if let Some(project) = &self.project {
14469 #[allow(clippy::mutable_key_type)]
14470 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
14471 multibuffer
14472 .all_buffers()
14473 .into_iter()
14474 .filter_map(|buffer| {
14475 buffer.update(cx, |buffer, cx| {
14476 let language = buffer.language()?;
14477 let should_discard = project.update(cx, |project, cx| {
14478 project.is_local()
14479 && !project.has_language_servers_for(buffer, cx)
14480 });
14481 should_discard.not().then_some(language.clone())
14482 })
14483 })
14484 .collect::<HashSet<_>>()
14485 });
14486 if !languages_affected.is_empty() {
14487 self.refresh_inlay_hints(
14488 InlayHintRefreshReason::BufferEdited(languages_affected),
14489 cx,
14490 );
14491 }
14492 }
14493 }
14494
14495 let Some(project) = &self.project else { return };
14496 let (telemetry, is_via_ssh) = {
14497 let project = project.read(cx);
14498 let telemetry = project.client().telemetry().clone();
14499 let is_via_ssh = project.is_via_ssh();
14500 (telemetry, is_via_ssh)
14501 };
14502 refresh_linked_ranges(self, window, cx);
14503 telemetry.log_edit_event("editor", is_via_ssh);
14504 }
14505 multi_buffer::Event::ExcerptsAdded {
14506 buffer,
14507 predecessor,
14508 excerpts,
14509 } => {
14510 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14511 let buffer_id = buffer.read(cx).remote_id();
14512 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
14513 if let Some(project) = &self.project {
14514 get_uncommitted_diff_for_buffer(
14515 project,
14516 [buffer.clone()],
14517 self.buffer.clone(),
14518 cx,
14519 )
14520 .detach();
14521 }
14522 }
14523 cx.emit(EditorEvent::ExcerptsAdded {
14524 buffer: buffer.clone(),
14525 predecessor: *predecessor,
14526 excerpts: excerpts.clone(),
14527 });
14528 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14529 }
14530 multi_buffer::Event::ExcerptsRemoved { ids } => {
14531 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
14532 let buffer = self.buffer.read(cx);
14533 self.registered_buffers
14534 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
14535 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
14536 }
14537 multi_buffer::Event::ExcerptsEdited { ids } => {
14538 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
14539 }
14540 multi_buffer::Event::ExcerptsExpanded { ids } => {
14541 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
14542 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
14543 }
14544 multi_buffer::Event::Reparsed(buffer_id) => {
14545 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14546
14547 cx.emit(EditorEvent::Reparsed(*buffer_id));
14548 }
14549 multi_buffer::Event::DiffHunksToggled => {
14550 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14551 }
14552 multi_buffer::Event::LanguageChanged(buffer_id) => {
14553 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
14554 cx.emit(EditorEvent::Reparsed(*buffer_id));
14555 cx.notify();
14556 }
14557 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
14558 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
14559 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
14560 cx.emit(EditorEvent::TitleChanged)
14561 }
14562 // multi_buffer::Event::DiffBaseChanged => {
14563 // self.scrollbar_marker_state.dirty = true;
14564 // cx.emit(EditorEvent::DiffBaseChanged);
14565 // cx.notify();
14566 // }
14567 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
14568 multi_buffer::Event::DiagnosticsUpdated => {
14569 self.refresh_active_diagnostics(cx);
14570 self.refresh_inline_diagnostics(true, window, cx);
14571 self.scrollbar_marker_state.dirty = true;
14572 cx.notify();
14573 }
14574 _ => {}
14575 };
14576 }
14577
14578 fn on_display_map_changed(
14579 &mut self,
14580 _: Entity<DisplayMap>,
14581 _: &mut Window,
14582 cx: &mut Context<Self>,
14583 ) {
14584 cx.notify();
14585 }
14586
14587 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14588 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
14589 self.refresh_inline_completion(true, false, window, cx);
14590 self.refresh_inlay_hints(
14591 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
14592 self.selections.newest_anchor().head(),
14593 &self.buffer.read(cx).snapshot(cx),
14594 cx,
14595 )),
14596 cx,
14597 );
14598
14599 let old_cursor_shape = self.cursor_shape;
14600
14601 {
14602 let editor_settings = EditorSettings::get_global(cx);
14603 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
14604 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
14605 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
14606 }
14607
14608 if old_cursor_shape != self.cursor_shape {
14609 cx.emit(EditorEvent::CursorShapeChanged);
14610 }
14611
14612 let project_settings = ProjectSettings::get_global(cx);
14613 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
14614
14615 if self.mode == EditorMode::Full {
14616 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
14617 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
14618 if self.show_inline_diagnostics != show_inline_diagnostics {
14619 self.show_inline_diagnostics = show_inline_diagnostics;
14620 self.refresh_inline_diagnostics(false, window, cx);
14621 }
14622
14623 if self.git_blame_inline_enabled != inline_blame_enabled {
14624 self.toggle_git_blame_inline_internal(false, window, cx);
14625 }
14626 }
14627
14628 cx.notify();
14629 }
14630
14631 pub fn set_searchable(&mut self, searchable: bool) {
14632 self.searchable = searchable;
14633 }
14634
14635 pub fn searchable(&self) -> bool {
14636 self.searchable
14637 }
14638
14639 fn open_proposed_changes_editor(
14640 &mut self,
14641 _: &OpenProposedChangesEditor,
14642 window: &mut Window,
14643 cx: &mut Context<Self>,
14644 ) {
14645 let Some(workspace) = self.workspace() else {
14646 cx.propagate();
14647 return;
14648 };
14649
14650 let selections = self.selections.all::<usize>(cx);
14651 let multi_buffer = self.buffer.read(cx);
14652 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14653 let mut new_selections_by_buffer = HashMap::default();
14654 for selection in selections {
14655 for (buffer, range, _) in
14656 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
14657 {
14658 let mut range = range.to_point(buffer);
14659 range.start.column = 0;
14660 range.end.column = buffer.line_len(range.end.row);
14661 new_selections_by_buffer
14662 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
14663 .or_insert(Vec::new())
14664 .push(range)
14665 }
14666 }
14667
14668 let proposed_changes_buffers = new_selections_by_buffer
14669 .into_iter()
14670 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14671 .collect::<Vec<_>>();
14672 let proposed_changes_editor = cx.new(|cx| {
14673 ProposedChangesEditor::new(
14674 "Proposed changes",
14675 proposed_changes_buffers,
14676 self.project.clone(),
14677 window,
14678 cx,
14679 )
14680 });
14681
14682 window.defer(cx, move |window, cx| {
14683 workspace.update(cx, |workspace, cx| {
14684 workspace.active_pane().update(cx, |pane, cx| {
14685 pane.add_item(
14686 Box::new(proposed_changes_editor),
14687 true,
14688 true,
14689 None,
14690 window,
14691 cx,
14692 );
14693 });
14694 });
14695 });
14696 }
14697
14698 pub fn open_excerpts_in_split(
14699 &mut self,
14700 _: &OpenExcerptsSplit,
14701 window: &mut Window,
14702 cx: &mut Context<Self>,
14703 ) {
14704 self.open_excerpts_common(None, true, window, cx)
14705 }
14706
14707 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14708 self.open_excerpts_common(None, false, window, cx)
14709 }
14710
14711 fn open_excerpts_common(
14712 &mut self,
14713 jump_data: Option<JumpData>,
14714 split: bool,
14715 window: &mut Window,
14716 cx: &mut Context<Self>,
14717 ) {
14718 let Some(workspace) = self.workspace() else {
14719 cx.propagate();
14720 return;
14721 };
14722
14723 if self.buffer.read(cx).is_singleton() {
14724 cx.propagate();
14725 return;
14726 }
14727
14728 let mut new_selections_by_buffer = HashMap::default();
14729 match &jump_data {
14730 Some(JumpData::MultiBufferPoint {
14731 excerpt_id,
14732 position,
14733 anchor,
14734 line_offset_from_top,
14735 }) => {
14736 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14737 if let Some(buffer) = multi_buffer_snapshot
14738 .buffer_id_for_excerpt(*excerpt_id)
14739 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14740 {
14741 let buffer_snapshot = buffer.read(cx).snapshot();
14742 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14743 language::ToPoint::to_point(anchor, &buffer_snapshot)
14744 } else {
14745 buffer_snapshot.clip_point(*position, Bias::Left)
14746 };
14747 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14748 new_selections_by_buffer.insert(
14749 buffer,
14750 (
14751 vec![jump_to_offset..jump_to_offset],
14752 Some(*line_offset_from_top),
14753 ),
14754 );
14755 }
14756 }
14757 Some(JumpData::MultiBufferRow {
14758 row,
14759 line_offset_from_top,
14760 }) => {
14761 let point = MultiBufferPoint::new(row.0, 0);
14762 if let Some((buffer, buffer_point, _)) =
14763 self.buffer.read(cx).point_to_buffer_point(point, cx)
14764 {
14765 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14766 new_selections_by_buffer
14767 .entry(buffer)
14768 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14769 .0
14770 .push(buffer_offset..buffer_offset)
14771 }
14772 }
14773 None => {
14774 let selections = self.selections.all::<usize>(cx);
14775 let multi_buffer = self.buffer.read(cx);
14776 for selection in selections {
14777 for (buffer, mut range, _) in multi_buffer
14778 .snapshot(cx)
14779 .range_to_buffer_ranges(selection.range())
14780 {
14781 // When editing branch buffers, jump to the corresponding location
14782 // in their base buffer.
14783 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14784 let buffer = buffer_handle.read(cx);
14785 if let Some(base_buffer) = buffer.base_buffer() {
14786 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14787 buffer_handle = base_buffer;
14788 }
14789
14790 if selection.reversed {
14791 mem::swap(&mut range.start, &mut range.end);
14792 }
14793 new_selections_by_buffer
14794 .entry(buffer_handle)
14795 .or_insert((Vec::new(), None))
14796 .0
14797 .push(range)
14798 }
14799 }
14800 }
14801 }
14802
14803 if new_selections_by_buffer.is_empty() {
14804 return;
14805 }
14806
14807 // We defer the pane interaction because we ourselves are a workspace item
14808 // and activating a new item causes the pane to call a method on us reentrantly,
14809 // which panics if we're on the stack.
14810 window.defer(cx, move |window, cx| {
14811 workspace.update(cx, |workspace, cx| {
14812 let pane = if split {
14813 workspace.adjacent_pane(window, cx)
14814 } else {
14815 workspace.active_pane().clone()
14816 };
14817
14818 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14819 let editor = buffer
14820 .read(cx)
14821 .file()
14822 .is_none()
14823 .then(|| {
14824 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14825 // so `workspace.open_project_item` will never find them, always opening a new editor.
14826 // Instead, we try to activate the existing editor in the pane first.
14827 let (editor, pane_item_index) =
14828 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14829 let editor = item.downcast::<Editor>()?;
14830 let singleton_buffer =
14831 editor.read(cx).buffer().read(cx).as_singleton()?;
14832 if singleton_buffer == buffer {
14833 Some((editor, i))
14834 } else {
14835 None
14836 }
14837 })?;
14838 pane.update(cx, |pane, cx| {
14839 pane.activate_item(pane_item_index, true, true, window, cx)
14840 });
14841 Some(editor)
14842 })
14843 .flatten()
14844 .unwrap_or_else(|| {
14845 workspace.open_project_item::<Self>(
14846 pane.clone(),
14847 buffer,
14848 true,
14849 true,
14850 window,
14851 cx,
14852 )
14853 });
14854
14855 editor.update(cx, |editor, cx| {
14856 let autoscroll = match scroll_offset {
14857 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14858 None => Autoscroll::newest(),
14859 };
14860 let nav_history = editor.nav_history.take();
14861 editor.change_selections(Some(autoscroll), window, cx, |s| {
14862 s.select_ranges(ranges);
14863 });
14864 editor.nav_history = nav_history;
14865 });
14866 }
14867 })
14868 });
14869 }
14870
14871 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14872 let snapshot = self.buffer.read(cx).read(cx);
14873 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14874 Some(
14875 ranges
14876 .iter()
14877 .map(move |range| {
14878 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14879 })
14880 .collect(),
14881 )
14882 }
14883
14884 fn selection_replacement_ranges(
14885 &self,
14886 range: Range<OffsetUtf16>,
14887 cx: &mut App,
14888 ) -> Vec<Range<OffsetUtf16>> {
14889 let selections = self.selections.all::<OffsetUtf16>(cx);
14890 let newest_selection = selections
14891 .iter()
14892 .max_by_key(|selection| selection.id)
14893 .unwrap();
14894 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14895 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14896 let snapshot = self.buffer.read(cx).read(cx);
14897 selections
14898 .into_iter()
14899 .map(|mut selection| {
14900 selection.start.0 =
14901 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14902 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14903 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14904 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14905 })
14906 .collect()
14907 }
14908
14909 fn report_editor_event(
14910 &self,
14911 event_type: &'static str,
14912 file_extension: Option<String>,
14913 cx: &App,
14914 ) {
14915 if cfg!(any(test, feature = "test-support")) {
14916 return;
14917 }
14918
14919 let Some(project) = &self.project else { return };
14920
14921 // If None, we are in a file without an extension
14922 let file = self
14923 .buffer
14924 .read(cx)
14925 .as_singleton()
14926 .and_then(|b| b.read(cx).file());
14927 let file_extension = file_extension.or(file
14928 .as_ref()
14929 .and_then(|file| Path::new(file.file_name(cx)).extension())
14930 .and_then(|e| e.to_str())
14931 .map(|a| a.to_string()));
14932
14933 let vim_mode = cx
14934 .global::<SettingsStore>()
14935 .raw_user_settings()
14936 .get("vim_mode")
14937 == Some(&serde_json::Value::Bool(true));
14938
14939 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14940 let copilot_enabled = edit_predictions_provider
14941 == language::language_settings::EditPredictionProvider::Copilot;
14942 let copilot_enabled_for_language = self
14943 .buffer
14944 .read(cx)
14945 .settings_at(0, cx)
14946 .show_edit_predictions;
14947
14948 let project = project.read(cx);
14949 telemetry::event!(
14950 event_type,
14951 file_extension,
14952 vim_mode,
14953 copilot_enabled,
14954 copilot_enabled_for_language,
14955 edit_predictions_provider,
14956 is_via_ssh = project.is_via_ssh(),
14957 );
14958 }
14959
14960 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14961 /// with each line being an array of {text, highlight} objects.
14962 fn copy_highlight_json(
14963 &mut self,
14964 _: &CopyHighlightJson,
14965 window: &mut Window,
14966 cx: &mut Context<Self>,
14967 ) {
14968 #[derive(Serialize)]
14969 struct Chunk<'a> {
14970 text: String,
14971 highlight: Option<&'a str>,
14972 }
14973
14974 let snapshot = self.buffer.read(cx).snapshot(cx);
14975 let range = self
14976 .selected_text_range(false, window, cx)
14977 .and_then(|selection| {
14978 if selection.range.is_empty() {
14979 None
14980 } else {
14981 Some(selection.range)
14982 }
14983 })
14984 .unwrap_or_else(|| 0..snapshot.len());
14985
14986 let chunks = snapshot.chunks(range, true);
14987 let mut lines = Vec::new();
14988 let mut line: VecDeque<Chunk> = VecDeque::new();
14989
14990 let Some(style) = self.style.as_ref() else {
14991 return;
14992 };
14993
14994 for chunk in chunks {
14995 let highlight = chunk
14996 .syntax_highlight_id
14997 .and_then(|id| id.name(&style.syntax));
14998 let mut chunk_lines = chunk.text.split('\n').peekable();
14999 while let Some(text) = chunk_lines.next() {
15000 let mut merged_with_last_token = false;
15001 if let Some(last_token) = line.back_mut() {
15002 if last_token.highlight == highlight {
15003 last_token.text.push_str(text);
15004 merged_with_last_token = true;
15005 }
15006 }
15007
15008 if !merged_with_last_token {
15009 line.push_back(Chunk {
15010 text: text.into(),
15011 highlight,
15012 });
15013 }
15014
15015 if chunk_lines.peek().is_some() {
15016 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15017 line.pop_front();
15018 }
15019 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15020 line.pop_back();
15021 }
15022
15023 lines.push(mem::take(&mut line));
15024 }
15025 }
15026 }
15027
15028 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15029 return;
15030 };
15031 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15032 }
15033
15034 pub fn open_context_menu(
15035 &mut self,
15036 _: &OpenContextMenu,
15037 window: &mut Window,
15038 cx: &mut Context<Self>,
15039 ) {
15040 self.request_autoscroll(Autoscroll::newest(), cx);
15041 let position = self.selections.newest_display(cx).start;
15042 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15043 }
15044
15045 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15046 &self.inlay_hint_cache
15047 }
15048
15049 pub fn replay_insert_event(
15050 &mut self,
15051 text: &str,
15052 relative_utf16_range: Option<Range<isize>>,
15053 window: &mut Window,
15054 cx: &mut Context<Self>,
15055 ) {
15056 if !self.input_enabled {
15057 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15058 return;
15059 }
15060 if let Some(relative_utf16_range) = relative_utf16_range {
15061 let selections = self.selections.all::<OffsetUtf16>(cx);
15062 self.change_selections(None, window, cx, |s| {
15063 let new_ranges = selections.into_iter().map(|range| {
15064 let start = OffsetUtf16(
15065 range
15066 .head()
15067 .0
15068 .saturating_add_signed(relative_utf16_range.start),
15069 );
15070 let end = OffsetUtf16(
15071 range
15072 .head()
15073 .0
15074 .saturating_add_signed(relative_utf16_range.end),
15075 );
15076 start..end
15077 });
15078 s.select_ranges(new_ranges);
15079 });
15080 }
15081
15082 self.handle_input(text, window, cx);
15083 }
15084
15085 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15086 let Some(provider) = self.semantics_provider.as_ref() else {
15087 return false;
15088 };
15089
15090 let mut supports = false;
15091 self.buffer().update(cx, |this, cx| {
15092 this.for_each_buffer(|buffer| {
15093 supports |= provider.supports_inlay_hints(buffer, cx);
15094 });
15095 });
15096
15097 supports
15098 }
15099
15100 pub fn is_focused(&self, window: &Window) -> bool {
15101 self.focus_handle.is_focused(window)
15102 }
15103
15104 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15105 cx.emit(EditorEvent::Focused);
15106
15107 if let Some(descendant) = self
15108 .last_focused_descendant
15109 .take()
15110 .and_then(|descendant| descendant.upgrade())
15111 {
15112 window.focus(&descendant);
15113 } else {
15114 if let Some(blame) = self.blame.as_ref() {
15115 blame.update(cx, GitBlame::focus)
15116 }
15117
15118 self.blink_manager.update(cx, BlinkManager::enable);
15119 self.show_cursor_names(window, cx);
15120 self.buffer.update(cx, |buffer, cx| {
15121 buffer.finalize_last_transaction(cx);
15122 if self.leader_peer_id.is_none() {
15123 buffer.set_active_selections(
15124 &self.selections.disjoint_anchors(),
15125 self.selections.line_mode,
15126 self.cursor_shape,
15127 cx,
15128 );
15129 }
15130 });
15131 }
15132 }
15133
15134 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15135 cx.emit(EditorEvent::FocusedIn)
15136 }
15137
15138 fn handle_focus_out(
15139 &mut self,
15140 event: FocusOutEvent,
15141 _window: &mut Window,
15142 _cx: &mut Context<Self>,
15143 ) {
15144 if event.blurred != self.focus_handle {
15145 self.last_focused_descendant = Some(event.blurred);
15146 }
15147 }
15148
15149 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15150 self.blink_manager.update(cx, BlinkManager::disable);
15151 self.buffer
15152 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
15153
15154 if let Some(blame) = self.blame.as_ref() {
15155 blame.update(cx, GitBlame::blur)
15156 }
15157 if !self.hover_state.focused(window, cx) {
15158 hide_hover(self, cx);
15159 }
15160 if !self
15161 .context_menu
15162 .borrow()
15163 .as_ref()
15164 .is_some_and(|context_menu| context_menu.focused(window, cx))
15165 {
15166 self.hide_context_menu(window, cx);
15167 }
15168 self.discard_inline_completion(false, cx);
15169 cx.emit(EditorEvent::Blurred);
15170 cx.notify();
15171 }
15172
15173 pub fn register_action<A: Action>(
15174 &mut self,
15175 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
15176 ) -> Subscription {
15177 let id = self.next_editor_action_id.post_inc();
15178 let listener = Arc::new(listener);
15179 self.editor_actions.borrow_mut().insert(
15180 id,
15181 Box::new(move |window, _| {
15182 let listener = listener.clone();
15183 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
15184 let action = action.downcast_ref().unwrap();
15185 if phase == DispatchPhase::Bubble {
15186 listener(action, window, cx)
15187 }
15188 })
15189 }),
15190 );
15191
15192 let editor_actions = self.editor_actions.clone();
15193 Subscription::new(move || {
15194 editor_actions.borrow_mut().remove(&id);
15195 })
15196 }
15197
15198 pub fn file_header_size(&self) -> u32 {
15199 FILE_HEADER_HEIGHT
15200 }
15201
15202 pub fn revert(
15203 &mut self,
15204 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
15205 window: &mut Window,
15206 cx: &mut Context<Self>,
15207 ) {
15208 self.buffer().update(cx, |multi_buffer, cx| {
15209 for (buffer_id, changes) in revert_changes {
15210 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15211 buffer.update(cx, |buffer, cx| {
15212 buffer.edit(
15213 changes.into_iter().map(|(range, text)| {
15214 (range, text.to_string().map(Arc::<str>::from))
15215 }),
15216 None,
15217 cx,
15218 );
15219 });
15220 }
15221 }
15222 });
15223 self.change_selections(None, window, cx, |selections| selections.refresh());
15224 }
15225
15226 pub fn to_pixel_point(
15227 &self,
15228 source: multi_buffer::Anchor,
15229 editor_snapshot: &EditorSnapshot,
15230 window: &mut Window,
15231 ) -> Option<gpui::Point<Pixels>> {
15232 let source_point = source.to_display_point(editor_snapshot);
15233 self.display_to_pixel_point(source_point, editor_snapshot, window)
15234 }
15235
15236 pub fn display_to_pixel_point(
15237 &self,
15238 source: DisplayPoint,
15239 editor_snapshot: &EditorSnapshot,
15240 window: &mut Window,
15241 ) -> Option<gpui::Point<Pixels>> {
15242 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
15243 let text_layout_details = self.text_layout_details(window);
15244 let scroll_top = text_layout_details
15245 .scroll_anchor
15246 .scroll_position(editor_snapshot)
15247 .y;
15248
15249 if source.row().as_f32() < scroll_top.floor() {
15250 return None;
15251 }
15252 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
15253 let source_y = line_height * (source.row().as_f32() - scroll_top);
15254 Some(gpui::Point::new(source_x, source_y))
15255 }
15256
15257 pub fn has_visible_completions_menu(&self) -> bool {
15258 !self.edit_prediction_preview_is_active()
15259 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
15260 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
15261 })
15262 }
15263
15264 pub fn register_addon<T: Addon>(&mut self, instance: T) {
15265 self.addons
15266 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
15267 }
15268
15269 pub fn unregister_addon<T: Addon>(&mut self) {
15270 self.addons.remove(&std::any::TypeId::of::<T>());
15271 }
15272
15273 pub fn addon<T: Addon>(&self) -> Option<&T> {
15274 let type_id = std::any::TypeId::of::<T>();
15275 self.addons
15276 .get(&type_id)
15277 .and_then(|item| item.to_any().downcast_ref::<T>())
15278 }
15279
15280 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
15281 let text_layout_details = self.text_layout_details(window);
15282 let style = &text_layout_details.editor_style;
15283 let font_id = window.text_system().resolve_font(&style.text.font());
15284 let font_size = style.text.font_size.to_pixels(window.rem_size());
15285 let line_height = style.text.line_height_in_pixels(window.rem_size());
15286 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
15287
15288 gpui::Size::new(em_width, line_height)
15289 }
15290
15291 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
15292 self.load_diff_task.clone()
15293 }
15294
15295 fn read_selections_from_db(
15296 &mut self,
15297 item_id: u64,
15298 workspace_id: WorkspaceId,
15299 window: &mut Window,
15300 cx: &mut Context<Editor>,
15301 ) {
15302 if !self.is_singleton(cx)
15303 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
15304 {
15305 return;
15306 }
15307 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
15308 return;
15309 };
15310 if selections.is_empty() {
15311 return;
15312 }
15313
15314 let snapshot = self.buffer.read(cx).snapshot(cx);
15315 self.change_selections(None, window, cx, |s| {
15316 s.select_ranges(selections.into_iter().map(|(start, end)| {
15317 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
15318 }));
15319 });
15320 }
15321}
15322
15323fn insert_extra_newline_brackets(
15324 buffer: &MultiBufferSnapshot,
15325 range: Range<usize>,
15326 language: &language::LanguageScope,
15327) -> bool {
15328 let leading_whitespace_len = buffer
15329 .reversed_chars_at(range.start)
15330 .take_while(|c| c.is_whitespace() && *c != '\n')
15331 .map(|c| c.len_utf8())
15332 .sum::<usize>();
15333 let trailing_whitespace_len = buffer
15334 .chars_at(range.end)
15335 .take_while(|c| c.is_whitespace() && *c != '\n')
15336 .map(|c| c.len_utf8())
15337 .sum::<usize>();
15338 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
15339
15340 language.brackets().any(|(pair, enabled)| {
15341 let pair_start = pair.start.trim_end();
15342 let pair_end = pair.end.trim_start();
15343
15344 enabled
15345 && pair.newline
15346 && buffer.contains_str_at(range.end, pair_end)
15347 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
15348 })
15349}
15350
15351fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
15352 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
15353 [(buffer, range, _)] => (*buffer, range.clone()),
15354 _ => return false,
15355 };
15356 let pair = {
15357 let mut result: Option<BracketMatch> = None;
15358
15359 for pair in buffer
15360 .all_bracket_ranges(range.clone())
15361 .filter(move |pair| {
15362 pair.open_range.start <= range.start && pair.close_range.end >= range.end
15363 })
15364 {
15365 let len = pair.close_range.end - pair.open_range.start;
15366
15367 if let Some(existing) = &result {
15368 let existing_len = existing.close_range.end - existing.open_range.start;
15369 if len > existing_len {
15370 continue;
15371 }
15372 }
15373
15374 result = Some(pair);
15375 }
15376
15377 result
15378 };
15379 let Some(pair) = pair else {
15380 return false;
15381 };
15382 pair.newline_only
15383 && buffer
15384 .chars_for_range(pair.open_range.end..range.start)
15385 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
15386 .all(|c| c.is_whitespace() && c != '\n')
15387}
15388
15389fn get_uncommitted_diff_for_buffer(
15390 project: &Entity<Project>,
15391 buffers: impl IntoIterator<Item = Entity<Buffer>>,
15392 buffer: Entity<MultiBuffer>,
15393 cx: &mut App,
15394) -> Task<()> {
15395 let mut tasks = Vec::new();
15396 project.update(cx, |project, cx| {
15397 for buffer in buffers {
15398 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
15399 }
15400 });
15401 cx.spawn(|mut cx| async move {
15402 let diffs = futures::future::join_all(tasks).await;
15403 buffer
15404 .update(&mut cx, |buffer, cx| {
15405 for diff in diffs.into_iter().flatten() {
15406 buffer.add_diff(diff, cx);
15407 }
15408 })
15409 .ok();
15410 })
15411}
15412
15413fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
15414 let tab_size = tab_size.get() as usize;
15415 let mut width = offset;
15416
15417 for ch in text.chars() {
15418 width += if ch == '\t' {
15419 tab_size - (width % tab_size)
15420 } else {
15421 1
15422 };
15423 }
15424
15425 width - offset
15426}
15427
15428#[cfg(test)]
15429mod tests {
15430 use super::*;
15431
15432 #[test]
15433 fn test_string_size_with_expanded_tabs() {
15434 let nz = |val| NonZeroU32::new(val).unwrap();
15435 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
15436 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
15437 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
15438 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
15439 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
15440 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
15441 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
15442 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
15443 }
15444}
15445
15446/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
15447struct WordBreakingTokenizer<'a> {
15448 input: &'a str,
15449}
15450
15451impl<'a> WordBreakingTokenizer<'a> {
15452 fn new(input: &'a str) -> Self {
15453 Self { input }
15454 }
15455}
15456
15457fn is_char_ideographic(ch: char) -> bool {
15458 use unicode_script::Script::*;
15459 use unicode_script::UnicodeScript;
15460 matches!(ch.script(), Han | Tangut | Yi)
15461}
15462
15463fn is_grapheme_ideographic(text: &str) -> bool {
15464 text.chars().any(is_char_ideographic)
15465}
15466
15467fn is_grapheme_whitespace(text: &str) -> bool {
15468 text.chars().any(|x| x.is_whitespace())
15469}
15470
15471fn should_stay_with_preceding_ideograph(text: &str) -> bool {
15472 text.chars().next().map_or(false, |ch| {
15473 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
15474 })
15475}
15476
15477#[derive(PartialEq, Eq, Debug, Clone, Copy)]
15478struct WordBreakToken<'a> {
15479 token: &'a str,
15480 grapheme_len: usize,
15481 is_whitespace: bool,
15482}
15483
15484impl<'a> Iterator for WordBreakingTokenizer<'a> {
15485 /// Yields a span, the count of graphemes in the token, and whether it was
15486 /// whitespace. Note that it also breaks at word boundaries.
15487 type Item = WordBreakToken<'a>;
15488
15489 fn next(&mut self) -> Option<Self::Item> {
15490 use unicode_segmentation::UnicodeSegmentation;
15491 if self.input.is_empty() {
15492 return None;
15493 }
15494
15495 let mut iter = self.input.graphemes(true).peekable();
15496 let mut offset = 0;
15497 let mut graphemes = 0;
15498 if let Some(first_grapheme) = iter.next() {
15499 let is_whitespace = is_grapheme_whitespace(first_grapheme);
15500 offset += first_grapheme.len();
15501 graphemes += 1;
15502 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
15503 if let Some(grapheme) = iter.peek().copied() {
15504 if should_stay_with_preceding_ideograph(grapheme) {
15505 offset += grapheme.len();
15506 graphemes += 1;
15507 }
15508 }
15509 } else {
15510 let mut words = self.input[offset..].split_word_bound_indices().peekable();
15511 let mut next_word_bound = words.peek().copied();
15512 if next_word_bound.map_or(false, |(i, _)| i == 0) {
15513 next_word_bound = words.next();
15514 }
15515 while let Some(grapheme) = iter.peek().copied() {
15516 if next_word_bound.map_or(false, |(i, _)| i == offset) {
15517 break;
15518 };
15519 if is_grapheme_whitespace(grapheme) != is_whitespace {
15520 break;
15521 };
15522 offset += grapheme.len();
15523 graphemes += 1;
15524 iter.next();
15525 }
15526 }
15527 let token = &self.input[..offset];
15528 self.input = &self.input[offset..];
15529 if is_whitespace {
15530 Some(WordBreakToken {
15531 token: " ",
15532 grapheme_len: 1,
15533 is_whitespace: true,
15534 })
15535 } else {
15536 Some(WordBreakToken {
15537 token,
15538 grapheme_len: graphemes,
15539 is_whitespace: false,
15540 })
15541 }
15542 } else {
15543 None
15544 }
15545 }
15546}
15547
15548#[test]
15549fn test_word_breaking_tokenizer() {
15550 let tests: &[(&str, &[(&str, usize, bool)])] = &[
15551 ("", &[]),
15552 (" ", &[(" ", 1, true)]),
15553 ("Ʒ", &[("Ʒ", 1, false)]),
15554 ("Ǽ", &[("Ǽ", 1, false)]),
15555 ("⋑", &[("⋑", 1, false)]),
15556 ("⋑⋑", &[("⋑⋑", 2, false)]),
15557 (
15558 "原理,进而",
15559 &[
15560 ("原", 1, false),
15561 ("理,", 2, false),
15562 ("进", 1, false),
15563 ("而", 1, false),
15564 ],
15565 ),
15566 (
15567 "hello world",
15568 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
15569 ),
15570 (
15571 "hello, world",
15572 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
15573 ),
15574 (
15575 " hello world",
15576 &[
15577 (" ", 1, true),
15578 ("hello", 5, false),
15579 (" ", 1, true),
15580 ("world", 5, false),
15581 ],
15582 ),
15583 (
15584 "这是什么 \n 钢笔",
15585 &[
15586 ("这", 1, false),
15587 ("是", 1, false),
15588 ("什", 1, false),
15589 ("么", 1, false),
15590 (" ", 1, true),
15591 ("钢", 1, false),
15592 ("笔", 1, false),
15593 ],
15594 ),
15595 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
15596 ];
15597
15598 for (input, result) in tests {
15599 assert_eq!(
15600 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
15601 result
15602 .iter()
15603 .copied()
15604 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
15605 token,
15606 grapheme_len,
15607 is_whitespace,
15608 })
15609 .collect::<Vec<_>>()
15610 );
15611 }
15612}
15613
15614fn wrap_with_prefix(
15615 line_prefix: String,
15616 unwrapped_text: String,
15617 wrap_column: usize,
15618 tab_size: NonZeroU32,
15619) -> String {
15620 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
15621 let mut wrapped_text = String::new();
15622 let mut current_line = line_prefix.clone();
15623
15624 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
15625 let mut current_line_len = line_prefix_len;
15626 for WordBreakToken {
15627 token,
15628 grapheme_len,
15629 is_whitespace,
15630 } in tokenizer
15631 {
15632 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
15633 wrapped_text.push_str(current_line.trim_end());
15634 wrapped_text.push('\n');
15635 current_line.truncate(line_prefix.len());
15636 current_line_len = line_prefix_len;
15637 if !is_whitespace {
15638 current_line.push_str(token);
15639 current_line_len += grapheme_len;
15640 }
15641 } else if !is_whitespace {
15642 current_line.push_str(token);
15643 current_line_len += grapheme_len;
15644 } else if current_line_len != line_prefix_len {
15645 current_line.push(' ');
15646 current_line_len += 1;
15647 }
15648 }
15649
15650 if !current_line.is_empty() {
15651 wrapped_text.push_str(¤t_line);
15652 }
15653 wrapped_text
15654}
15655
15656#[test]
15657fn test_wrap_with_prefix() {
15658 assert_eq!(
15659 wrap_with_prefix(
15660 "# ".to_string(),
15661 "abcdefg".to_string(),
15662 4,
15663 NonZeroU32::new(4).unwrap()
15664 ),
15665 "# abcdefg"
15666 );
15667 assert_eq!(
15668 wrap_with_prefix(
15669 "".to_string(),
15670 "\thello world".to_string(),
15671 8,
15672 NonZeroU32::new(4).unwrap()
15673 ),
15674 "hello\nworld"
15675 );
15676 assert_eq!(
15677 wrap_with_prefix(
15678 "// ".to_string(),
15679 "xx \nyy zz aa bb cc".to_string(),
15680 12,
15681 NonZeroU32::new(4).unwrap()
15682 ),
15683 "// xx yy zz\n// aa bb cc"
15684 );
15685 assert_eq!(
15686 wrap_with_prefix(
15687 String::new(),
15688 "这是什么 \n 钢笔".to_string(),
15689 3,
15690 NonZeroU32::new(4).unwrap()
15691 ),
15692 "这是什\n么 钢\n笔"
15693 );
15694}
15695
15696pub trait CollaborationHub {
15697 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
15698 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
15699 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
15700}
15701
15702impl CollaborationHub for Entity<Project> {
15703 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
15704 self.read(cx).collaborators()
15705 }
15706
15707 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
15708 self.read(cx).user_store().read(cx).participant_indices()
15709 }
15710
15711 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
15712 let this = self.read(cx);
15713 let user_ids = this.collaborators().values().map(|c| c.user_id);
15714 this.user_store().read_with(cx, |user_store, cx| {
15715 user_store.participant_names(user_ids, cx)
15716 })
15717 }
15718}
15719
15720pub trait SemanticsProvider {
15721 fn hover(
15722 &self,
15723 buffer: &Entity<Buffer>,
15724 position: text::Anchor,
15725 cx: &mut App,
15726 ) -> Option<Task<Vec<project::Hover>>>;
15727
15728 fn inlay_hints(
15729 &self,
15730 buffer_handle: Entity<Buffer>,
15731 range: Range<text::Anchor>,
15732 cx: &mut App,
15733 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
15734
15735 fn resolve_inlay_hint(
15736 &self,
15737 hint: InlayHint,
15738 buffer_handle: Entity<Buffer>,
15739 server_id: LanguageServerId,
15740 cx: &mut App,
15741 ) -> Option<Task<anyhow::Result<InlayHint>>>;
15742
15743 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
15744
15745 fn document_highlights(
15746 &self,
15747 buffer: &Entity<Buffer>,
15748 position: text::Anchor,
15749 cx: &mut App,
15750 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
15751
15752 fn definitions(
15753 &self,
15754 buffer: &Entity<Buffer>,
15755 position: text::Anchor,
15756 kind: GotoDefinitionKind,
15757 cx: &mut App,
15758 ) -> Option<Task<Result<Vec<LocationLink>>>>;
15759
15760 fn range_for_rename(
15761 &self,
15762 buffer: &Entity<Buffer>,
15763 position: text::Anchor,
15764 cx: &mut App,
15765 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
15766
15767 fn perform_rename(
15768 &self,
15769 buffer: &Entity<Buffer>,
15770 position: text::Anchor,
15771 new_name: String,
15772 cx: &mut App,
15773 ) -> Option<Task<Result<ProjectTransaction>>>;
15774}
15775
15776pub trait CompletionProvider {
15777 fn completions(
15778 &self,
15779 buffer: &Entity<Buffer>,
15780 buffer_position: text::Anchor,
15781 trigger: CompletionContext,
15782 window: &mut Window,
15783 cx: &mut Context<Editor>,
15784 ) -> Task<Result<Vec<Completion>>>;
15785
15786 fn resolve_completions(
15787 &self,
15788 buffer: Entity<Buffer>,
15789 completion_indices: Vec<usize>,
15790 completions: Rc<RefCell<Box<[Completion]>>>,
15791 cx: &mut Context<Editor>,
15792 ) -> Task<Result<bool>>;
15793
15794 fn apply_additional_edits_for_completion(
15795 &self,
15796 _buffer: Entity<Buffer>,
15797 _completions: Rc<RefCell<Box<[Completion]>>>,
15798 _completion_index: usize,
15799 _push_to_history: bool,
15800 _cx: &mut Context<Editor>,
15801 ) -> Task<Result<Option<language::Transaction>>> {
15802 Task::ready(Ok(None))
15803 }
15804
15805 fn is_completion_trigger(
15806 &self,
15807 buffer: &Entity<Buffer>,
15808 position: language::Anchor,
15809 text: &str,
15810 trigger_in_words: bool,
15811 cx: &mut Context<Editor>,
15812 ) -> bool;
15813
15814 fn sort_completions(&self) -> bool {
15815 true
15816 }
15817}
15818
15819pub trait CodeActionProvider {
15820 fn id(&self) -> Arc<str>;
15821
15822 fn code_actions(
15823 &self,
15824 buffer: &Entity<Buffer>,
15825 range: Range<text::Anchor>,
15826 window: &mut Window,
15827 cx: &mut App,
15828 ) -> Task<Result<Vec<CodeAction>>>;
15829
15830 fn apply_code_action(
15831 &self,
15832 buffer_handle: Entity<Buffer>,
15833 action: CodeAction,
15834 excerpt_id: ExcerptId,
15835 push_to_history: bool,
15836 window: &mut Window,
15837 cx: &mut App,
15838 ) -> Task<Result<ProjectTransaction>>;
15839}
15840
15841impl CodeActionProvider for Entity<Project> {
15842 fn id(&self) -> Arc<str> {
15843 "project".into()
15844 }
15845
15846 fn code_actions(
15847 &self,
15848 buffer: &Entity<Buffer>,
15849 range: Range<text::Anchor>,
15850 _window: &mut Window,
15851 cx: &mut App,
15852 ) -> Task<Result<Vec<CodeAction>>> {
15853 self.update(cx, |project, cx| {
15854 project.code_actions(buffer, range, None, cx)
15855 })
15856 }
15857
15858 fn apply_code_action(
15859 &self,
15860 buffer_handle: Entity<Buffer>,
15861 action: CodeAction,
15862 _excerpt_id: ExcerptId,
15863 push_to_history: bool,
15864 _window: &mut Window,
15865 cx: &mut App,
15866 ) -> Task<Result<ProjectTransaction>> {
15867 self.update(cx, |project, cx| {
15868 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15869 })
15870 }
15871}
15872
15873fn snippet_completions(
15874 project: &Project,
15875 buffer: &Entity<Buffer>,
15876 buffer_position: text::Anchor,
15877 cx: &mut App,
15878) -> Task<Result<Vec<Completion>>> {
15879 let language = buffer.read(cx).language_at(buffer_position);
15880 let language_name = language.as_ref().map(|language| language.lsp_id());
15881 let snippet_store = project.snippets().read(cx);
15882 let snippets = snippet_store.snippets_for(language_name, cx);
15883
15884 if snippets.is_empty() {
15885 return Task::ready(Ok(vec![]));
15886 }
15887 let snapshot = buffer.read(cx).text_snapshot();
15888 let chars: String = snapshot
15889 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15890 .collect();
15891
15892 let scope = language.map(|language| language.default_scope());
15893 let executor = cx.background_executor().clone();
15894
15895 cx.background_spawn(async move {
15896 let classifier = CharClassifier::new(scope).for_completion(true);
15897 let mut last_word = chars
15898 .chars()
15899 .take_while(|c| classifier.is_word(*c))
15900 .collect::<String>();
15901 last_word = last_word.chars().rev().collect();
15902
15903 if last_word.is_empty() {
15904 return Ok(vec![]);
15905 }
15906
15907 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15908 let to_lsp = |point: &text::Anchor| {
15909 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15910 point_to_lsp(end)
15911 };
15912 let lsp_end = to_lsp(&buffer_position);
15913
15914 let candidates = snippets
15915 .iter()
15916 .enumerate()
15917 .flat_map(|(ix, snippet)| {
15918 snippet
15919 .prefix
15920 .iter()
15921 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15922 })
15923 .collect::<Vec<StringMatchCandidate>>();
15924
15925 let mut matches = fuzzy::match_strings(
15926 &candidates,
15927 &last_word,
15928 last_word.chars().any(|c| c.is_uppercase()),
15929 100,
15930 &Default::default(),
15931 executor,
15932 )
15933 .await;
15934
15935 // Remove all candidates where the query's start does not match the start of any word in the candidate
15936 if let Some(query_start) = last_word.chars().next() {
15937 matches.retain(|string_match| {
15938 split_words(&string_match.string).any(|word| {
15939 // Check that the first codepoint of the word as lowercase matches the first
15940 // codepoint of the query as lowercase
15941 word.chars()
15942 .flat_map(|codepoint| codepoint.to_lowercase())
15943 .zip(query_start.to_lowercase())
15944 .all(|(word_cp, query_cp)| word_cp == query_cp)
15945 })
15946 });
15947 }
15948
15949 let matched_strings = matches
15950 .into_iter()
15951 .map(|m| m.string)
15952 .collect::<HashSet<_>>();
15953
15954 let result: Vec<Completion> = snippets
15955 .into_iter()
15956 .filter_map(|snippet| {
15957 let matching_prefix = snippet
15958 .prefix
15959 .iter()
15960 .find(|prefix| matched_strings.contains(*prefix))?;
15961 let start = as_offset - last_word.len();
15962 let start = snapshot.anchor_before(start);
15963 let range = start..buffer_position;
15964 let lsp_start = to_lsp(&start);
15965 let lsp_range = lsp::Range {
15966 start: lsp_start,
15967 end: lsp_end,
15968 };
15969 Some(Completion {
15970 old_range: range,
15971 new_text: snippet.body.clone(),
15972 resolved: false,
15973 label: CodeLabel {
15974 text: matching_prefix.clone(),
15975 runs: vec![],
15976 filter_range: 0..matching_prefix.len(),
15977 },
15978 server_id: LanguageServerId(usize::MAX),
15979 documentation: snippet
15980 .description
15981 .clone()
15982 .map(|description| CompletionDocumentation::SingleLine(description.into())),
15983 lsp_completion: lsp::CompletionItem {
15984 label: snippet.prefix.first().unwrap().clone(),
15985 kind: Some(CompletionItemKind::SNIPPET),
15986 label_details: snippet.description.as_ref().map(|description| {
15987 lsp::CompletionItemLabelDetails {
15988 detail: Some(description.clone()),
15989 description: None,
15990 }
15991 }),
15992 insert_text_format: Some(InsertTextFormat::SNIPPET),
15993 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15994 lsp::InsertReplaceEdit {
15995 new_text: snippet.body.clone(),
15996 insert: lsp_range,
15997 replace: lsp_range,
15998 },
15999 )),
16000 filter_text: Some(snippet.body.clone()),
16001 sort_text: Some(char::MAX.to_string()),
16002 ..Default::default()
16003 },
16004 confirm: None,
16005 })
16006 })
16007 .collect();
16008
16009 Ok(result)
16010 })
16011}
16012
16013impl CompletionProvider for Entity<Project> {
16014 fn completions(
16015 &self,
16016 buffer: &Entity<Buffer>,
16017 buffer_position: text::Anchor,
16018 options: CompletionContext,
16019 _window: &mut Window,
16020 cx: &mut Context<Editor>,
16021 ) -> Task<Result<Vec<Completion>>> {
16022 self.update(cx, |project, cx| {
16023 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16024 let project_completions = project.completions(buffer, buffer_position, options, cx);
16025 cx.background_spawn(async move {
16026 let mut completions = project_completions.await?;
16027 let snippets_completions = snippets.await?;
16028 completions.extend(snippets_completions);
16029 Ok(completions)
16030 })
16031 })
16032 }
16033
16034 fn resolve_completions(
16035 &self,
16036 buffer: Entity<Buffer>,
16037 completion_indices: Vec<usize>,
16038 completions: Rc<RefCell<Box<[Completion]>>>,
16039 cx: &mut Context<Editor>,
16040 ) -> Task<Result<bool>> {
16041 self.update(cx, |project, cx| {
16042 project.lsp_store().update(cx, |lsp_store, cx| {
16043 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16044 })
16045 })
16046 }
16047
16048 fn apply_additional_edits_for_completion(
16049 &self,
16050 buffer: Entity<Buffer>,
16051 completions: Rc<RefCell<Box<[Completion]>>>,
16052 completion_index: usize,
16053 push_to_history: bool,
16054 cx: &mut Context<Editor>,
16055 ) -> Task<Result<Option<language::Transaction>>> {
16056 self.update(cx, |project, cx| {
16057 project.lsp_store().update(cx, |lsp_store, cx| {
16058 lsp_store.apply_additional_edits_for_completion(
16059 buffer,
16060 completions,
16061 completion_index,
16062 push_to_history,
16063 cx,
16064 )
16065 })
16066 })
16067 }
16068
16069 fn is_completion_trigger(
16070 &self,
16071 buffer: &Entity<Buffer>,
16072 position: language::Anchor,
16073 text: &str,
16074 trigger_in_words: bool,
16075 cx: &mut Context<Editor>,
16076 ) -> bool {
16077 let mut chars = text.chars();
16078 let char = if let Some(char) = chars.next() {
16079 char
16080 } else {
16081 return false;
16082 };
16083 if chars.next().is_some() {
16084 return false;
16085 }
16086
16087 let buffer = buffer.read(cx);
16088 let snapshot = buffer.snapshot();
16089 if !snapshot.settings_at(position, cx).show_completions_on_input {
16090 return false;
16091 }
16092 let classifier = snapshot.char_classifier_at(position).for_completion(true);
16093 if trigger_in_words && classifier.is_word(char) {
16094 return true;
16095 }
16096
16097 buffer.completion_triggers().contains(text)
16098 }
16099}
16100
16101impl SemanticsProvider for Entity<Project> {
16102 fn hover(
16103 &self,
16104 buffer: &Entity<Buffer>,
16105 position: text::Anchor,
16106 cx: &mut App,
16107 ) -> Option<Task<Vec<project::Hover>>> {
16108 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
16109 }
16110
16111 fn document_highlights(
16112 &self,
16113 buffer: &Entity<Buffer>,
16114 position: text::Anchor,
16115 cx: &mut App,
16116 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
16117 Some(self.update(cx, |project, cx| {
16118 project.document_highlights(buffer, position, cx)
16119 }))
16120 }
16121
16122 fn definitions(
16123 &self,
16124 buffer: &Entity<Buffer>,
16125 position: text::Anchor,
16126 kind: GotoDefinitionKind,
16127 cx: &mut App,
16128 ) -> Option<Task<Result<Vec<LocationLink>>>> {
16129 Some(self.update(cx, |project, cx| match kind {
16130 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
16131 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
16132 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
16133 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
16134 }))
16135 }
16136
16137 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
16138 // TODO: make this work for remote projects
16139 self.update(cx, |this, cx| {
16140 buffer.update(cx, |buffer, cx| {
16141 this.any_language_server_supports_inlay_hints(buffer, cx)
16142 })
16143 })
16144 }
16145
16146 fn inlay_hints(
16147 &self,
16148 buffer_handle: Entity<Buffer>,
16149 range: Range<text::Anchor>,
16150 cx: &mut App,
16151 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
16152 Some(self.update(cx, |project, cx| {
16153 project.inlay_hints(buffer_handle, range, cx)
16154 }))
16155 }
16156
16157 fn resolve_inlay_hint(
16158 &self,
16159 hint: InlayHint,
16160 buffer_handle: Entity<Buffer>,
16161 server_id: LanguageServerId,
16162 cx: &mut App,
16163 ) -> Option<Task<anyhow::Result<InlayHint>>> {
16164 Some(self.update(cx, |project, cx| {
16165 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
16166 }))
16167 }
16168
16169 fn range_for_rename(
16170 &self,
16171 buffer: &Entity<Buffer>,
16172 position: text::Anchor,
16173 cx: &mut App,
16174 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
16175 Some(self.update(cx, |project, cx| {
16176 let buffer = buffer.clone();
16177 let task = project.prepare_rename(buffer.clone(), position, cx);
16178 cx.spawn(|_, mut cx| async move {
16179 Ok(match task.await? {
16180 PrepareRenameResponse::Success(range) => Some(range),
16181 PrepareRenameResponse::InvalidPosition => None,
16182 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
16183 // Fallback on using TreeSitter info to determine identifier range
16184 buffer.update(&mut cx, |buffer, _| {
16185 let snapshot = buffer.snapshot();
16186 let (range, kind) = snapshot.surrounding_word(position);
16187 if kind != Some(CharKind::Word) {
16188 return None;
16189 }
16190 Some(
16191 snapshot.anchor_before(range.start)
16192 ..snapshot.anchor_after(range.end),
16193 )
16194 })?
16195 }
16196 })
16197 })
16198 }))
16199 }
16200
16201 fn perform_rename(
16202 &self,
16203 buffer: &Entity<Buffer>,
16204 position: text::Anchor,
16205 new_name: String,
16206 cx: &mut App,
16207 ) -> Option<Task<Result<ProjectTransaction>>> {
16208 Some(self.update(cx, |project, cx| {
16209 project.perform_rename(buffer.clone(), position, new_name, cx)
16210 }))
16211 }
16212}
16213
16214fn inlay_hint_settings(
16215 location: Anchor,
16216 snapshot: &MultiBufferSnapshot,
16217 cx: &mut Context<Editor>,
16218) -> InlayHintSettings {
16219 let file = snapshot.file_at(location);
16220 let language = snapshot.language_at(location).map(|l| l.name());
16221 language_settings(language, file, cx).inlay_hints
16222}
16223
16224fn consume_contiguous_rows(
16225 contiguous_row_selections: &mut Vec<Selection<Point>>,
16226 selection: &Selection<Point>,
16227 display_map: &DisplaySnapshot,
16228 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
16229) -> (MultiBufferRow, MultiBufferRow) {
16230 contiguous_row_selections.push(selection.clone());
16231 let start_row = MultiBufferRow(selection.start.row);
16232 let mut end_row = ending_row(selection, display_map);
16233
16234 while let Some(next_selection) = selections.peek() {
16235 if next_selection.start.row <= end_row.0 {
16236 end_row = ending_row(next_selection, display_map);
16237 contiguous_row_selections.push(selections.next().unwrap().clone());
16238 } else {
16239 break;
16240 }
16241 }
16242 (start_row, end_row)
16243}
16244
16245fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
16246 if next_selection.end.column > 0 || next_selection.is_empty() {
16247 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
16248 } else {
16249 MultiBufferRow(next_selection.end.row)
16250 }
16251}
16252
16253impl EditorSnapshot {
16254 pub fn remote_selections_in_range<'a>(
16255 &'a self,
16256 range: &'a Range<Anchor>,
16257 collaboration_hub: &dyn CollaborationHub,
16258 cx: &'a App,
16259 ) -> impl 'a + Iterator<Item = RemoteSelection> {
16260 let participant_names = collaboration_hub.user_names(cx);
16261 let participant_indices = collaboration_hub.user_participant_indices(cx);
16262 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
16263 let collaborators_by_replica_id = collaborators_by_peer_id
16264 .iter()
16265 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
16266 .collect::<HashMap<_, _>>();
16267 self.buffer_snapshot
16268 .selections_in_range(range, false)
16269 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
16270 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
16271 let participant_index = participant_indices.get(&collaborator.user_id).copied();
16272 let user_name = participant_names.get(&collaborator.user_id).cloned();
16273 Some(RemoteSelection {
16274 replica_id,
16275 selection,
16276 cursor_shape,
16277 line_mode,
16278 participant_index,
16279 peer_id: collaborator.peer_id,
16280 user_name,
16281 })
16282 })
16283 }
16284
16285 pub fn hunks_for_ranges(
16286 &self,
16287 ranges: impl Iterator<Item = Range<Point>>,
16288 ) -> Vec<MultiBufferDiffHunk> {
16289 let mut hunks = Vec::new();
16290 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
16291 HashMap::default();
16292 for query_range in ranges {
16293 let query_rows =
16294 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
16295 for hunk in self.buffer_snapshot.diff_hunks_in_range(
16296 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
16297 ) {
16298 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
16299 // when the caret is just above or just below the deleted hunk.
16300 let allow_adjacent = hunk.status().is_deleted();
16301 let related_to_selection = if allow_adjacent {
16302 hunk.row_range.overlaps(&query_rows)
16303 || hunk.row_range.start == query_rows.end
16304 || hunk.row_range.end == query_rows.start
16305 } else {
16306 hunk.row_range.overlaps(&query_rows)
16307 };
16308 if related_to_selection {
16309 if !processed_buffer_rows
16310 .entry(hunk.buffer_id)
16311 .or_default()
16312 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
16313 {
16314 continue;
16315 }
16316 hunks.push(hunk);
16317 }
16318 }
16319 }
16320
16321 hunks
16322 }
16323
16324 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
16325 self.display_snapshot.buffer_snapshot.language_at(position)
16326 }
16327
16328 pub fn is_focused(&self) -> bool {
16329 self.is_focused
16330 }
16331
16332 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
16333 self.placeholder_text.as_ref()
16334 }
16335
16336 pub fn scroll_position(&self) -> gpui::Point<f32> {
16337 self.scroll_anchor.scroll_position(&self.display_snapshot)
16338 }
16339
16340 fn gutter_dimensions(
16341 &self,
16342 font_id: FontId,
16343 font_size: Pixels,
16344 max_line_number_width: Pixels,
16345 cx: &App,
16346 ) -> Option<GutterDimensions> {
16347 if !self.show_gutter {
16348 return None;
16349 }
16350
16351 let descent = cx.text_system().descent(font_id, font_size);
16352 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
16353 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
16354
16355 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
16356 matches!(
16357 ProjectSettings::get_global(cx).git.git_gutter,
16358 Some(GitGutterSetting::TrackedFiles)
16359 )
16360 });
16361 let gutter_settings = EditorSettings::get_global(cx).gutter;
16362 let show_line_numbers = self
16363 .show_line_numbers
16364 .unwrap_or(gutter_settings.line_numbers);
16365 let line_gutter_width = if show_line_numbers {
16366 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
16367 let min_width_for_number_on_gutter = em_advance * 4.0;
16368 max_line_number_width.max(min_width_for_number_on_gutter)
16369 } else {
16370 0.0.into()
16371 };
16372
16373 let show_code_actions = self
16374 .show_code_actions
16375 .unwrap_or(gutter_settings.code_actions);
16376
16377 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
16378
16379 let git_blame_entries_width =
16380 self.git_blame_gutter_max_author_length
16381 .map(|max_author_length| {
16382 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
16383
16384 /// The number of characters to dedicate to gaps and margins.
16385 const SPACING_WIDTH: usize = 4;
16386
16387 let max_char_count = max_author_length
16388 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
16389 + ::git::SHORT_SHA_LENGTH
16390 + MAX_RELATIVE_TIMESTAMP.len()
16391 + SPACING_WIDTH;
16392
16393 em_advance * max_char_count
16394 });
16395
16396 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
16397 left_padding += if show_code_actions || show_runnables {
16398 em_width * 3.0
16399 } else if show_git_gutter && show_line_numbers {
16400 em_width * 2.0
16401 } else if show_git_gutter || show_line_numbers {
16402 em_width
16403 } else {
16404 px(0.)
16405 };
16406
16407 let right_padding = if gutter_settings.folds && show_line_numbers {
16408 em_width * 4.0
16409 } else if gutter_settings.folds {
16410 em_width * 3.0
16411 } else if show_line_numbers {
16412 em_width
16413 } else {
16414 px(0.)
16415 };
16416
16417 Some(GutterDimensions {
16418 left_padding,
16419 right_padding,
16420 width: line_gutter_width + left_padding + right_padding,
16421 margin: -descent,
16422 git_blame_entries_width,
16423 })
16424 }
16425
16426 pub fn render_crease_toggle(
16427 &self,
16428 buffer_row: MultiBufferRow,
16429 row_contains_cursor: bool,
16430 editor: Entity<Editor>,
16431 window: &mut Window,
16432 cx: &mut App,
16433 ) -> Option<AnyElement> {
16434 let folded = self.is_line_folded(buffer_row);
16435 let mut is_foldable = false;
16436
16437 if let Some(crease) = self
16438 .crease_snapshot
16439 .query_row(buffer_row, &self.buffer_snapshot)
16440 {
16441 is_foldable = true;
16442 match crease {
16443 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
16444 if let Some(render_toggle) = render_toggle {
16445 let toggle_callback =
16446 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
16447 if folded {
16448 editor.update(cx, |editor, cx| {
16449 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
16450 });
16451 } else {
16452 editor.update(cx, |editor, cx| {
16453 editor.unfold_at(
16454 &crate::UnfoldAt { buffer_row },
16455 window,
16456 cx,
16457 )
16458 });
16459 }
16460 });
16461 return Some((render_toggle)(
16462 buffer_row,
16463 folded,
16464 toggle_callback,
16465 window,
16466 cx,
16467 ));
16468 }
16469 }
16470 }
16471 }
16472
16473 is_foldable |= self.starts_indent(buffer_row);
16474
16475 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
16476 Some(
16477 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
16478 .toggle_state(folded)
16479 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
16480 if folded {
16481 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
16482 } else {
16483 this.fold_at(&FoldAt { buffer_row }, window, cx);
16484 }
16485 }))
16486 .into_any_element(),
16487 )
16488 } else {
16489 None
16490 }
16491 }
16492
16493 pub fn render_crease_trailer(
16494 &self,
16495 buffer_row: MultiBufferRow,
16496 window: &mut Window,
16497 cx: &mut App,
16498 ) -> Option<AnyElement> {
16499 let folded = self.is_line_folded(buffer_row);
16500 if let Crease::Inline { render_trailer, .. } = self
16501 .crease_snapshot
16502 .query_row(buffer_row, &self.buffer_snapshot)?
16503 {
16504 let render_trailer = render_trailer.as_ref()?;
16505 Some(render_trailer(buffer_row, folded, window, cx))
16506 } else {
16507 None
16508 }
16509 }
16510}
16511
16512impl Deref for EditorSnapshot {
16513 type Target = DisplaySnapshot;
16514
16515 fn deref(&self) -> &Self::Target {
16516 &self.display_snapshot
16517 }
16518}
16519
16520#[derive(Clone, Debug, PartialEq, Eq)]
16521pub enum EditorEvent {
16522 InputIgnored {
16523 text: Arc<str>,
16524 },
16525 InputHandled {
16526 utf16_range_to_replace: Option<Range<isize>>,
16527 text: Arc<str>,
16528 },
16529 ExcerptsAdded {
16530 buffer: Entity<Buffer>,
16531 predecessor: ExcerptId,
16532 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
16533 },
16534 ExcerptsRemoved {
16535 ids: Vec<ExcerptId>,
16536 },
16537 BufferFoldToggled {
16538 ids: Vec<ExcerptId>,
16539 folded: bool,
16540 },
16541 ExcerptsEdited {
16542 ids: Vec<ExcerptId>,
16543 },
16544 ExcerptsExpanded {
16545 ids: Vec<ExcerptId>,
16546 },
16547 BufferEdited,
16548 Edited {
16549 transaction_id: clock::Lamport,
16550 },
16551 Reparsed(BufferId),
16552 Focused,
16553 FocusedIn,
16554 Blurred,
16555 DirtyChanged,
16556 Saved,
16557 TitleChanged,
16558 DiffBaseChanged,
16559 SelectionsChanged {
16560 local: bool,
16561 },
16562 ScrollPositionChanged {
16563 local: bool,
16564 autoscroll: bool,
16565 },
16566 Closed,
16567 TransactionUndone {
16568 transaction_id: clock::Lamport,
16569 },
16570 TransactionBegun {
16571 transaction_id: clock::Lamport,
16572 },
16573 Reloaded,
16574 CursorShapeChanged,
16575}
16576
16577impl EventEmitter<EditorEvent> for Editor {}
16578
16579impl Focusable for Editor {
16580 fn focus_handle(&self, _cx: &App) -> FocusHandle {
16581 self.focus_handle.clone()
16582 }
16583}
16584
16585impl Render for Editor {
16586 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
16587 let settings = ThemeSettings::get_global(cx);
16588
16589 let mut text_style = match self.mode {
16590 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
16591 color: cx.theme().colors().editor_foreground,
16592 font_family: settings.ui_font.family.clone(),
16593 font_features: settings.ui_font.features.clone(),
16594 font_fallbacks: settings.ui_font.fallbacks.clone(),
16595 font_size: rems(0.875).into(),
16596 font_weight: settings.ui_font.weight,
16597 line_height: relative(settings.buffer_line_height.value()),
16598 ..Default::default()
16599 },
16600 EditorMode::Full => TextStyle {
16601 color: cx.theme().colors().editor_foreground,
16602 font_family: settings.buffer_font.family.clone(),
16603 font_features: settings.buffer_font.features.clone(),
16604 font_fallbacks: settings.buffer_font.fallbacks.clone(),
16605 font_size: settings.buffer_font_size(cx).into(),
16606 font_weight: settings.buffer_font.weight,
16607 line_height: relative(settings.buffer_line_height.value()),
16608 ..Default::default()
16609 },
16610 };
16611 if let Some(text_style_refinement) = &self.text_style_refinement {
16612 text_style.refine(text_style_refinement)
16613 }
16614
16615 let background = match self.mode {
16616 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
16617 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
16618 EditorMode::Full => cx.theme().colors().editor_background,
16619 };
16620
16621 EditorElement::new(
16622 &cx.entity(),
16623 EditorStyle {
16624 background,
16625 local_player: cx.theme().players().local(),
16626 text: text_style,
16627 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
16628 syntax: cx.theme().syntax().clone(),
16629 status: cx.theme().status().clone(),
16630 inlay_hints_style: make_inlay_hints_style(cx),
16631 inline_completion_styles: make_suggestion_styles(cx),
16632 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
16633 },
16634 )
16635 }
16636}
16637
16638impl EntityInputHandler for Editor {
16639 fn text_for_range(
16640 &mut self,
16641 range_utf16: Range<usize>,
16642 adjusted_range: &mut Option<Range<usize>>,
16643 _: &mut Window,
16644 cx: &mut Context<Self>,
16645 ) -> Option<String> {
16646 let snapshot = self.buffer.read(cx).read(cx);
16647 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
16648 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
16649 if (start.0..end.0) != range_utf16 {
16650 adjusted_range.replace(start.0..end.0);
16651 }
16652 Some(snapshot.text_for_range(start..end).collect())
16653 }
16654
16655 fn selected_text_range(
16656 &mut self,
16657 ignore_disabled_input: bool,
16658 _: &mut Window,
16659 cx: &mut Context<Self>,
16660 ) -> Option<UTF16Selection> {
16661 // Prevent the IME menu from appearing when holding down an alphabetic key
16662 // while input is disabled.
16663 if !ignore_disabled_input && !self.input_enabled {
16664 return None;
16665 }
16666
16667 let selection = self.selections.newest::<OffsetUtf16>(cx);
16668 let range = selection.range();
16669
16670 Some(UTF16Selection {
16671 range: range.start.0..range.end.0,
16672 reversed: selection.reversed,
16673 })
16674 }
16675
16676 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
16677 let snapshot = self.buffer.read(cx).read(cx);
16678 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
16679 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
16680 }
16681
16682 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16683 self.clear_highlights::<InputComposition>(cx);
16684 self.ime_transaction.take();
16685 }
16686
16687 fn replace_text_in_range(
16688 &mut self,
16689 range_utf16: Option<Range<usize>>,
16690 text: &str,
16691 window: &mut Window,
16692 cx: &mut Context<Self>,
16693 ) {
16694 if !self.input_enabled {
16695 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16696 return;
16697 }
16698
16699 self.transact(window, cx, |this, window, cx| {
16700 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
16701 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16702 Some(this.selection_replacement_ranges(range_utf16, cx))
16703 } else {
16704 this.marked_text_ranges(cx)
16705 };
16706
16707 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
16708 let newest_selection_id = this.selections.newest_anchor().id;
16709 this.selections
16710 .all::<OffsetUtf16>(cx)
16711 .iter()
16712 .zip(ranges_to_replace.iter())
16713 .find_map(|(selection, range)| {
16714 if selection.id == newest_selection_id {
16715 Some(
16716 (range.start.0 as isize - selection.head().0 as isize)
16717 ..(range.end.0 as isize - selection.head().0 as isize),
16718 )
16719 } else {
16720 None
16721 }
16722 })
16723 });
16724
16725 cx.emit(EditorEvent::InputHandled {
16726 utf16_range_to_replace: range_to_replace,
16727 text: text.into(),
16728 });
16729
16730 if let Some(new_selected_ranges) = new_selected_ranges {
16731 this.change_selections(None, window, cx, |selections| {
16732 selections.select_ranges(new_selected_ranges)
16733 });
16734 this.backspace(&Default::default(), window, cx);
16735 }
16736
16737 this.handle_input(text, window, cx);
16738 });
16739
16740 if let Some(transaction) = self.ime_transaction {
16741 self.buffer.update(cx, |buffer, cx| {
16742 buffer.group_until_transaction(transaction, cx);
16743 });
16744 }
16745
16746 self.unmark_text(window, cx);
16747 }
16748
16749 fn replace_and_mark_text_in_range(
16750 &mut self,
16751 range_utf16: Option<Range<usize>>,
16752 text: &str,
16753 new_selected_range_utf16: Option<Range<usize>>,
16754 window: &mut Window,
16755 cx: &mut Context<Self>,
16756 ) {
16757 if !self.input_enabled {
16758 return;
16759 }
16760
16761 let transaction = self.transact(window, cx, |this, window, cx| {
16762 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
16763 let snapshot = this.buffer.read(cx).read(cx);
16764 if let Some(relative_range_utf16) = range_utf16.as_ref() {
16765 for marked_range in &mut marked_ranges {
16766 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
16767 marked_range.start.0 += relative_range_utf16.start;
16768 marked_range.start =
16769 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
16770 marked_range.end =
16771 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16772 }
16773 }
16774 Some(marked_ranges)
16775 } else if let Some(range_utf16) = range_utf16 {
16776 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16777 Some(this.selection_replacement_ranges(range_utf16, cx))
16778 } else {
16779 None
16780 };
16781
16782 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16783 let newest_selection_id = this.selections.newest_anchor().id;
16784 this.selections
16785 .all::<OffsetUtf16>(cx)
16786 .iter()
16787 .zip(ranges_to_replace.iter())
16788 .find_map(|(selection, range)| {
16789 if selection.id == newest_selection_id {
16790 Some(
16791 (range.start.0 as isize - selection.head().0 as isize)
16792 ..(range.end.0 as isize - selection.head().0 as isize),
16793 )
16794 } else {
16795 None
16796 }
16797 })
16798 });
16799
16800 cx.emit(EditorEvent::InputHandled {
16801 utf16_range_to_replace: range_to_replace,
16802 text: text.into(),
16803 });
16804
16805 if let Some(ranges) = ranges_to_replace {
16806 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16807 }
16808
16809 let marked_ranges = {
16810 let snapshot = this.buffer.read(cx).read(cx);
16811 this.selections
16812 .disjoint_anchors()
16813 .iter()
16814 .map(|selection| {
16815 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16816 })
16817 .collect::<Vec<_>>()
16818 };
16819
16820 if text.is_empty() {
16821 this.unmark_text(window, cx);
16822 } else {
16823 this.highlight_text::<InputComposition>(
16824 marked_ranges.clone(),
16825 HighlightStyle {
16826 underline: Some(UnderlineStyle {
16827 thickness: px(1.),
16828 color: None,
16829 wavy: false,
16830 }),
16831 ..Default::default()
16832 },
16833 cx,
16834 );
16835 }
16836
16837 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16838 let use_autoclose = this.use_autoclose;
16839 let use_auto_surround = this.use_auto_surround;
16840 this.set_use_autoclose(false);
16841 this.set_use_auto_surround(false);
16842 this.handle_input(text, window, cx);
16843 this.set_use_autoclose(use_autoclose);
16844 this.set_use_auto_surround(use_auto_surround);
16845
16846 if let Some(new_selected_range) = new_selected_range_utf16 {
16847 let snapshot = this.buffer.read(cx).read(cx);
16848 let new_selected_ranges = marked_ranges
16849 .into_iter()
16850 .map(|marked_range| {
16851 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16852 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16853 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16854 snapshot.clip_offset_utf16(new_start, Bias::Left)
16855 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16856 })
16857 .collect::<Vec<_>>();
16858
16859 drop(snapshot);
16860 this.change_selections(None, window, cx, |selections| {
16861 selections.select_ranges(new_selected_ranges)
16862 });
16863 }
16864 });
16865
16866 self.ime_transaction = self.ime_transaction.or(transaction);
16867 if let Some(transaction) = self.ime_transaction {
16868 self.buffer.update(cx, |buffer, cx| {
16869 buffer.group_until_transaction(transaction, cx);
16870 });
16871 }
16872
16873 if self.text_highlights::<InputComposition>(cx).is_none() {
16874 self.ime_transaction.take();
16875 }
16876 }
16877
16878 fn bounds_for_range(
16879 &mut self,
16880 range_utf16: Range<usize>,
16881 element_bounds: gpui::Bounds<Pixels>,
16882 window: &mut Window,
16883 cx: &mut Context<Self>,
16884 ) -> Option<gpui::Bounds<Pixels>> {
16885 let text_layout_details = self.text_layout_details(window);
16886 let gpui::Size {
16887 width: em_width,
16888 height: line_height,
16889 } = self.character_size(window);
16890
16891 let snapshot = self.snapshot(window, cx);
16892 let scroll_position = snapshot.scroll_position();
16893 let scroll_left = scroll_position.x * em_width;
16894
16895 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16896 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16897 + self.gutter_dimensions.width
16898 + self.gutter_dimensions.margin;
16899 let y = line_height * (start.row().as_f32() - scroll_position.y);
16900
16901 Some(Bounds {
16902 origin: element_bounds.origin + point(x, y),
16903 size: size(em_width, line_height),
16904 })
16905 }
16906
16907 fn character_index_for_point(
16908 &mut self,
16909 point: gpui::Point<Pixels>,
16910 _window: &mut Window,
16911 _cx: &mut Context<Self>,
16912 ) -> Option<usize> {
16913 let position_map = self.last_position_map.as_ref()?;
16914 if !position_map.text_hitbox.contains(&point) {
16915 return None;
16916 }
16917 let display_point = position_map.point_for_position(point).previous_valid;
16918 let anchor = position_map
16919 .snapshot
16920 .display_point_to_anchor(display_point, Bias::Left);
16921 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16922 Some(utf16_offset.0)
16923 }
16924}
16925
16926trait SelectionExt {
16927 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16928 fn spanned_rows(
16929 &self,
16930 include_end_if_at_line_start: bool,
16931 map: &DisplaySnapshot,
16932 ) -> Range<MultiBufferRow>;
16933}
16934
16935impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16936 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16937 let start = self
16938 .start
16939 .to_point(&map.buffer_snapshot)
16940 .to_display_point(map);
16941 let end = self
16942 .end
16943 .to_point(&map.buffer_snapshot)
16944 .to_display_point(map);
16945 if self.reversed {
16946 end..start
16947 } else {
16948 start..end
16949 }
16950 }
16951
16952 fn spanned_rows(
16953 &self,
16954 include_end_if_at_line_start: bool,
16955 map: &DisplaySnapshot,
16956 ) -> Range<MultiBufferRow> {
16957 let start = self.start.to_point(&map.buffer_snapshot);
16958 let mut end = self.end.to_point(&map.buffer_snapshot);
16959 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16960 end.row -= 1;
16961 }
16962
16963 let buffer_start = map.prev_line_boundary(start).0;
16964 let buffer_end = map.next_line_boundary(end).0;
16965 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
16966 }
16967}
16968
16969impl<T: InvalidationRegion> InvalidationStack<T> {
16970 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
16971 where
16972 S: Clone + ToOffset,
16973 {
16974 while let Some(region) = self.last() {
16975 let all_selections_inside_invalidation_ranges =
16976 if selections.len() == region.ranges().len() {
16977 selections
16978 .iter()
16979 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
16980 .all(|(selection, invalidation_range)| {
16981 let head = selection.head().to_offset(buffer);
16982 invalidation_range.start <= head && invalidation_range.end >= head
16983 })
16984 } else {
16985 false
16986 };
16987
16988 if all_selections_inside_invalidation_ranges {
16989 break;
16990 } else {
16991 self.pop();
16992 }
16993 }
16994 }
16995}
16996
16997impl<T> Default for InvalidationStack<T> {
16998 fn default() -> Self {
16999 Self(Default::default())
17000 }
17001}
17002
17003impl<T> Deref for InvalidationStack<T> {
17004 type Target = Vec<T>;
17005
17006 fn deref(&self) -> &Self::Target {
17007 &self.0
17008 }
17009}
17010
17011impl<T> DerefMut for InvalidationStack<T> {
17012 fn deref_mut(&mut self) -> &mut Self::Target {
17013 &mut self.0
17014 }
17015}
17016
17017impl InvalidationRegion for SnippetState {
17018 fn ranges(&self) -> &[Range<Anchor>] {
17019 &self.ranges[self.active_index]
17020 }
17021}
17022
17023pub fn diagnostic_block_renderer(
17024 diagnostic: Diagnostic,
17025 max_message_rows: Option<u8>,
17026 allow_closing: bool,
17027 _is_valid: bool,
17028) -> RenderBlock {
17029 let (text_without_backticks, code_ranges) =
17030 highlight_diagnostic_message(&diagnostic, max_message_rows);
17031
17032 Arc::new(move |cx: &mut BlockContext| {
17033 let group_id: SharedString = cx.block_id.to_string().into();
17034
17035 let mut text_style = cx.window.text_style().clone();
17036 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
17037 let theme_settings = ThemeSettings::get_global(cx);
17038 text_style.font_family = theme_settings.buffer_font.family.clone();
17039 text_style.font_style = theme_settings.buffer_font.style;
17040 text_style.font_features = theme_settings.buffer_font.features.clone();
17041 text_style.font_weight = theme_settings.buffer_font.weight;
17042
17043 let multi_line_diagnostic = diagnostic.message.contains('\n');
17044
17045 let buttons = |diagnostic: &Diagnostic| {
17046 if multi_line_diagnostic {
17047 v_flex()
17048 } else {
17049 h_flex()
17050 }
17051 .when(allow_closing, |div| {
17052 div.children(diagnostic.is_primary.then(|| {
17053 IconButton::new("close-block", IconName::XCircle)
17054 .icon_color(Color::Muted)
17055 .size(ButtonSize::Compact)
17056 .style(ButtonStyle::Transparent)
17057 .visible_on_hover(group_id.clone())
17058 .on_click(move |_click, window, cx| {
17059 window.dispatch_action(Box::new(Cancel), cx)
17060 })
17061 .tooltip(|window, cx| {
17062 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
17063 })
17064 }))
17065 })
17066 .child(
17067 IconButton::new("copy-block", IconName::Copy)
17068 .icon_color(Color::Muted)
17069 .size(ButtonSize::Compact)
17070 .style(ButtonStyle::Transparent)
17071 .visible_on_hover(group_id.clone())
17072 .on_click({
17073 let message = diagnostic.message.clone();
17074 move |_click, _, cx| {
17075 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
17076 }
17077 })
17078 .tooltip(Tooltip::text("Copy diagnostic message")),
17079 )
17080 };
17081
17082 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
17083 AvailableSpace::min_size(),
17084 cx.window,
17085 cx.app,
17086 );
17087
17088 h_flex()
17089 .id(cx.block_id)
17090 .group(group_id.clone())
17091 .relative()
17092 .size_full()
17093 .block_mouse_down()
17094 .pl(cx.gutter_dimensions.width)
17095 .w(cx.max_width - cx.gutter_dimensions.full_width())
17096 .child(
17097 div()
17098 .flex()
17099 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
17100 .flex_shrink(),
17101 )
17102 .child(buttons(&diagnostic))
17103 .child(div().flex().flex_shrink_0().child(
17104 StyledText::new(text_without_backticks.clone()).with_highlights(
17105 &text_style,
17106 code_ranges.iter().map(|range| {
17107 (
17108 range.clone(),
17109 HighlightStyle {
17110 font_weight: Some(FontWeight::BOLD),
17111 ..Default::default()
17112 },
17113 )
17114 }),
17115 ),
17116 ))
17117 .into_any_element()
17118 })
17119}
17120
17121fn inline_completion_edit_text(
17122 current_snapshot: &BufferSnapshot,
17123 edits: &[(Range<Anchor>, String)],
17124 edit_preview: &EditPreview,
17125 include_deletions: bool,
17126 cx: &App,
17127) -> HighlightedText {
17128 let edits = edits
17129 .iter()
17130 .map(|(anchor, text)| {
17131 (
17132 anchor.start.text_anchor..anchor.end.text_anchor,
17133 text.clone(),
17134 )
17135 })
17136 .collect::<Vec<_>>();
17137
17138 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
17139}
17140
17141pub fn highlight_diagnostic_message(
17142 diagnostic: &Diagnostic,
17143 mut max_message_rows: Option<u8>,
17144) -> (SharedString, Vec<Range<usize>>) {
17145 let mut text_without_backticks = String::new();
17146 let mut code_ranges = Vec::new();
17147
17148 if let Some(source) = &diagnostic.source {
17149 text_without_backticks.push_str(source);
17150 code_ranges.push(0..source.len());
17151 text_without_backticks.push_str(": ");
17152 }
17153
17154 let mut prev_offset = 0;
17155 let mut in_code_block = false;
17156 let has_row_limit = max_message_rows.is_some();
17157 let mut newline_indices = diagnostic
17158 .message
17159 .match_indices('\n')
17160 .filter(|_| has_row_limit)
17161 .map(|(ix, _)| ix)
17162 .fuse()
17163 .peekable();
17164
17165 for (quote_ix, _) in diagnostic
17166 .message
17167 .match_indices('`')
17168 .chain([(diagnostic.message.len(), "")])
17169 {
17170 let mut first_newline_ix = None;
17171 let mut last_newline_ix = None;
17172 while let Some(newline_ix) = newline_indices.peek() {
17173 if *newline_ix < quote_ix {
17174 if first_newline_ix.is_none() {
17175 first_newline_ix = Some(*newline_ix);
17176 }
17177 last_newline_ix = Some(*newline_ix);
17178
17179 if let Some(rows_left) = &mut max_message_rows {
17180 if *rows_left == 0 {
17181 break;
17182 } else {
17183 *rows_left -= 1;
17184 }
17185 }
17186 let _ = newline_indices.next();
17187 } else {
17188 break;
17189 }
17190 }
17191 let prev_len = text_without_backticks.len();
17192 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
17193 text_without_backticks.push_str(new_text);
17194 if in_code_block {
17195 code_ranges.push(prev_len..text_without_backticks.len());
17196 }
17197 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
17198 in_code_block = !in_code_block;
17199 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
17200 text_without_backticks.push_str("...");
17201 break;
17202 }
17203 }
17204
17205 (text_without_backticks.into(), code_ranges)
17206}
17207
17208fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
17209 match severity {
17210 DiagnosticSeverity::ERROR => colors.error,
17211 DiagnosticSeverity::WARNING => colors.warning,
17212 DiagnosticSeverity::INFORMATION => colors.info,
17213 DiagnosticSeverity::HINT => colors.info,
17214 _ => colors.ignored,
17215 }
17216}
17217
17218pub fn styled_runs_for_code_label<'a>(
17219 label: &'a CodeLabel,
17220 syntax_theme: &'a theme::SyntaxTheme,
17221) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
17222 let fade_out = HighlightStyle {
17223 fade_out: Some(0.35),
17224 ..Default::default()
17225 };
17226
17227 let mut prev_end = label.filter_range.end;
17228 label
17229 .runs
17230 .iter()
17231 .enumerate()
17232 .flat_map(move |(ix, (range, highlight_id))| {
17233 let style = if let Some(style) = highlight_id.style(syntax_theme) {
17234 style
17235 } else {
17236 return Default::default();
17237 };
17238 let mut muted_style = style;
17239 muted_style.highlight(fade_out);
17240
17241 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
17242 if range.start >= label.filter_range.end {
17243 if range.start > prev_end {
17244 runs.push((prev_end..range.start, fade_out));
17245 }
17246 runs.push((range.clone(), muted_style));
17247 } else if range.end <= label.filter_range.end {
17248 runs.push((range.clone(), style));
17249 } else {
17250 runs.push((range.start..label.filter_range.end, style));
17251 runs.push((label.filter_range.end..range.end, muted_style));
17252 }
17253 prev_end = cmp::max(prev_end, range.end);
17254
17255 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
17256 runs.push((prev_end..label.text.len(), fade_out));
17257 }
17258
17259 runs
17260 })
17261}
17262
17263pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
17264 let mut prev_index = 0;
17265 let mut prev_codepoint: Option<char> = None;
17266 text.char_indices()
17267 .chain([(text.len(), '\0')])
17268 .filter_map(move |(index, codepoint)| {
17269 let prev_codepoint = prev_codepoint.replace(codepoint)?;
17270 let is_boundary = index == text.len()
17271 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
17272 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
17273 if is_boundary {
17274 let chunk = &text[prev_index..index];
17275 prev_index = index;
17276 Some(chunk)
17277 } else {
17278 None
17279 }
17280 })
17281}
17282
17283pub trait RangeToAnchorExt: Sized {
17284 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
17285
17286 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
17287 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
17288 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
17289 }
17290}
17291
17292impl<T: ToOffset> RangeToAnchorExt for Range<T> {
17293 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
17294 let start_offset = self.start.to_offset(snapshot);
17295 let end_offset = self.end.to_offset(snapshot);
17296 if start_offset == end_offset {
17297 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
17298 } else {
17299 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
17300 }
17301 }
17302}
17303
17304pub trait RowExt {
17305 fn as_f32(&self) -> f32;
17306
17307 fn next_row(&self) -> Self;
17308
17309 fn previous_row(&self) -> Self;
17310
17311 fn minus(&self, other: Self) -> u32;
17312}
17313
17314impl RowExt for DisplayRow {
17315 fn as_f32(&self) -> f32 {
17316 self.0 as f32
17317 }
17318
17319 fn next_row(&self) -> Self {
17320 Self(self.0 + 1)
17321 }
17322
17323 fn previous_row(&self) -> Self {
17324 Self(self.0.saturating_sub(1))
17325 }
17326
17327 fn minus(&self, other: Self) -> u32 {
17328 self.0 - other.0
17329 }
17330}
17331
17332impl RowExt for MultiBufferRow {
17333 fn as_f32(&self) -> f32 {
17334 self.0 as f32
17335 }
17336
17337 fn next_row(&self) -> Self {
17338 Self(self.0 + 1)
17339 }
17340
17341 fn previous_row(&self) -> Self {
17342 Self(self.0.saturating_sub(1))
17343 }
17344
17345 fn minus(&self, other: Self) -> u32 {
17346 self.0 - other.0
17347 }
17348}
17349
17350trait RowRangeExt {
17351 type Row;
17352
17353 fn len(&self) -> usize;
17354
17355 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
17356}
17357
17358impl RowRangeExt for Range<MultiBufferRow> {
17359 type Row = MultiBufferRow;
17360
17361 fn len(&self) -> usize {
17362 (self.end.0 - self.start.0) as usize
17363 }
17364
17365 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
17366 (self.start.0..self.end.0).map(MultiBufferRow)
17367 }
17368}
17369
17370impl RowRangeExt for Range<DisplayRow> {
17371 type Row = DisplayRow;
17372
17373 fn len(&self) -> usize {
17374 (self.end.0 - self.start.0) as usize
17375 }
17376
17377 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
17378 (self.start.0..self.end.0).map(DisplayRow)
17379 }
17380}
17381
17382/// If select range has more than one line, we
17383/// just point the cursor to range.start.
17384fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
17385 if range.start.row == range.end.row {
17386 range
17387 } else {
17388 range.start..range.start
17389 }
17390}
17391pub struct KillRing(ClipboardItem);
17392impl Global for KillRing {}
17393
17394const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
17395
17396fn all_edits_insertions_or_deletions(
17397 edits: &Vec<(Range<Anchor>, String)>,
17398 snapshot: &MultiBufferSnapshot,
17399) -> bool {
17400 let mut all_insertions = true;
17401 let mut all_deletions = true;
17402
17403 for (range, new_text) in edits.iter() {
17404 let range_is_empty = range.to_offset(&snapshot).is_empty();
17405 let text_is_empty = new_text.is_empty();
17406
17407 if range_is_empty != text_is_empty {
17408 if range_is_empty {
17409 all_deletions = false;
17410 } else {
17411 all_insertions = false;
17412 }
17413 } else {
17414 return false;
17415 }
17416
17417 if !all_insertions && !all_deletions {
17418 return false;
17419 }
17420 }
17421 all_insertions || all_deletions
17422}