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, DiffHunkStatus};
56use client::{Collaborator, ParticipantIndex};
57use clock::ReplicaId;
58use collections::{BTreeMap, HashMap, HashSet, VecDeque};
59use convert_case::{Case, Casing};
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
64};
65pub use editor_settings_controls::*;
66use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::{
71 future::{self, Shared},
72 FutureExt,
73};
74use fuzzy::StringMatchCandidate;
75
76use ::git::{status::FileStatus, Restore};
77use code_context_menus::{
78 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
79 CompletionsMenu, ContextMenuOrigin,
80};
81use git::blame::GitBlame;
82use gpui::{
83 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
84 AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
85 ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity, EntityInputHandler,
86 EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
87 HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
88 ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, Task,
89 TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
90 WeakEntity, WeakFocusHandle, Window,
91};
92use highlight_matching_bracket::refresh_matching_bracket_highlights;
93use hover_popover::{hide_hover, HoverState};
94use indent_guides::ActiveIndentGuidesState;
95use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
96pub use inline_completion::Direction;
97use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
98pub use items::MAX_TAB_TITLE_LEN;
99use itertools::Itertools;
100use language::{
101 language_settings::{
102 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
103 },
104 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
105 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
106 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
107 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
108};
109use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
110use linked_editing_ranges::refresh_linked_ranges;
111use mouse_context_menu::MouseContextMenu;
112use persistence::DB;
113pub use proposed_changes_editor::{
114 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
115};
116use smallvec::smallvec;
117use std::iter::Peekable;
118use task::{ResolvedTask, TaskTemplate, TaskVariables};
119
120use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
121pub use lsp::CompletionContext;
122use lsp::{
123 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
124 LanguageServerId, LanguageServerName,
125};
126
127use language::BufferSnapshot;
128use movement::TextLayoutDetails;
129pub use multi_buffer::{
130 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
131 ToOffset, ToPoint,
132};
133use multi_buffer::{
134 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
135 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
136};
137use project::{
138 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
139 project_settings::{GitGutterSetting, ProjectSettings},
140 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
141 PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
142};
143use rand::prelude::*;
144use rpc::{proto::*, ErrorExt};
145use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
146use selections_collection::{
147 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
148};
149use serde::{Deserialize, Serialize};
150use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
151use smallvec::SmallVec;
152use snippet::Snippet;
153use std::{
154 any::TypeId,
155 borrow::Cow,
156 cell::RefCell,
157 cmp::{self, Ordering, Reverse},
158 mem,
159 num::NonZeroU32,
160 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
161 path::{Path, PathBuf},
162 rc::Rc,
163 sync::Arc,
164 time::{Duration, Instant},
165};
166pub use sum_tree::Bias;
167use sum_tree::TreeMap;
168use text::{BufferId, OffsetUtf16, Rope};
169use theme::{
170 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
171 ThemeColors, ThemeSettings,
172};
173use ui::{
174 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
175 Tooltip,
176};
177use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
178use workspace::{
179 item::{ItemHandle, PreviewTabsSettings},
180 ItemId, RestoreOnStartupBehavior,
181};
182use workspace::{
183 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
184 WorkspaceSettings,
185};
186use workspace::{
187 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
188};
189use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
190
191use crate::hover_links::{find_url, find_url_from_range};
192use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
193
194pub const FILE_HEADER_HEIGHT: u32 = 2;
195pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
196pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
197pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
198const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
199const MAX_LINE_LEN: usize = 1024;
200const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
201const MAX_SELECTION_HISTORY_LEN: usize = 1024;
202pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
203#[doc(hidden)]
204pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
205
206pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
207pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
208
209pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
210pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
211
212const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
213 alt: true,
214 shift: true,
215 control: false,
216 platform: false,
217 function: false,
218};
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
221pub enum InlayId {
222 InlineCompletion(usize),
223 Hint(usize),
224}
225
226impl InlayId {
227 fn id(&self) -> usize {
228 match self {
229 Self::InlineCompletion(id) => *id,
230 Self::Hint(id) => *id,
231 }
232 }
233}
234
235enum DocumentHighlightRead {}
236enum DocumentHighlightWrite {}
237enum InputComposition {}
238enum SelectedTextHighlight {}
239
240#[derive(Debug, Copy, Clone, PartialEq, Eq)]
241pub enum Navigated {
242 Yes,
243 No,
244}
245
246impl Navigated {
247 pub fn from_bool(yes: bool) -> Navigated {
248 if yes {
249 Navigated::Yes
250 } else {
251 Navigated::No
252 }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
257enum DisplayDiffHunk {
258 Folded {
259 display_row: DisplayRow,
260 },
261 Unfolded {
262 diff_base_byte_range: Range<usize>,
263 display_row_range: Range<DisplayRow>,
264 multi_buffer_range: Range<Anchor>,
265 status: DiffHunkStatus,
266 },
267}
268
269pub fn init_settings(cx: &mut App) {
270 EditorSettings::register(cx);
271}
272
273pub fn init(cx: &mut App) {
274 init_settings(cx);
275
276 workspace::register_project_item::<Editor>(cx);
277 workspace::FollowableViewRegistry::register::<Editor>(cx);
278 workspace::register_serializable_item::<Editor>(cx);
279
280 cx.observe_new(
281 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
282 workspace.register_action(Editor::new_file);
283 workspace.register_action(Editor::new_file_vertical);
284 workspace.register_action(Editor::new_file_horizontal);
285 workspace.register_action(Editor::cancel_language_server_work);
286 },
287 )
288 .detach();
289
290 cx.on_action(move |_: &workspace::NewFile, 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 Editor::new_file(workspace, &Default::default(), window, cx)
299 },
300 )
301 .detach();
302 }
303 });
304 cx.on_action(move |_: &workspace::NewWindow, cx| {
305 let app_state = workspace::AppState::global(cx);
306 if let Some(app_state) = app_state.upgrade() {
307 workspace::open_new(
308 Default::default(),
309 app_state,
310 cx,
311 |workspace, window, cx| {
312 cx.activate(true);
313 Editor::new_file(workspace, &Default::default(), window, cx)
314 },
315 )
316 .detach();
317 }
318 });
319}
320
321pub struct SearchWithinRange;
322
323trait InvalidationRegion {
324 fn ranges(&self) -> &[Range<Anchor>];
325}
326
327#[derive(Clone, Debug, PartialEq)]
328pub enum SelectPhase {
329 Begin {
330 position: DisplayPoint,
331 add: bool,
332 click_count: usize,
333 },
334 BeginColumnar {
335 position: DisplayPoint,
336 reset: bool,
337 goal_column: u32,
338 },
339 Extend {
340 position: DisplayPoint,
341 click_count: usize,
342 },
343 Update {
344 position: DisplayPoint,
345 goal_column: u32,
346 scroll_delta: gpui::Point<f32>,
347 },
348 End,
349}
350
351#[derive(Clone, Debug)]
352pub enum SelectMode {
353 Character,
354 Word(Range<Anchor>),
355 Line(Range<Anchor>),
356 All,
357}
358
359#[derive(Copy, Clone, PartialEq, Eq, Debug)]
360pub enum EditorMode {
361 SingleLine { auto_width: bool },
362 AutoHeight { max_lines: usize },
363 Full,
364}
365
366#[derive(Copy, Clone, Debug)]
367pub enum SoftWrap {
368 /// Prefer not to wrap at all.
369 ///
370 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
371 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
372 GitDiff,
373 /// Prefer a single line generally, unless an overly long line is encountered.
374 None,
375 /// Soft wrap lines that exceed the editor width.
376 EditorWidth,
377 /// Soft wrap lines at the preferred line length.
378 Column(u32),
379 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
380 Bounded(u32),
381}
382
383#[derive(Clone)]
384pub struct EditorStyle {
385 pub background: Hsla,
386 pub local_player: PlayerColor,
387 pub text: TextStyle,
388 pub scrollbar_width: Pixels,
389 pub syntax: Arc<SyntaxTheme>,
390 pub status: StatusColors,
391 pub inlay_hints_style: HighlightStyle,
392 pub inline_completion_styles: InlineCompletionStyles,
393 pub unnecessary_code_fade: f32,
394}
395
396impl Default for EditorStyle {
397 fn default() -> Self {
398 Self {
399 background: Hsla::default(),
400 local_player: PlayerColor::default(),
401 text: TextStyle::default(),
402 scrollbar_width: Pixels::default(),
403 syntax: Default::default(),
404 // HACK: Status colors don't have a real default.
405 // We should look into removing the status colors from the editor
406 // style and retrieve them directly from the theme.
407 status: StatusColors::dark(),
408 inlay_hints_style: HighlightStyle::default(),
409 inline_completion_styles: InlineCompletionStyles {
410 insertion: HighlightStyle::default(),
411 whitespace: HighlightStyle::default(),
412 },
413 unnecessary_code_fade: Default::default(),
414 }
415 }
416}
417
418pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
419 let show_background = language_settings::language_settings(None, None, cx)
420 .inlay_hints
421 .show_background;
422
423 HighlightStyle {
424 color: Some(cx.theme().status().hint),
425 background_color: show_background.then(|| cx.theme().status().hint_background),
426 ..HighlightStyle::default()
427 }
428}
429
430pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
431 InlineCompletionStyles {
432 insertion: HighlightStyle {
433 color: Some(cx.theme().status().predictive),
434 ..HighlightStyle::default()
435 },
436 whitespace: HighlightStyle {
437 background_color: Some(cx.theme().status().created_background),
438 ..HighlightStyle::default()
439 },
440 }
441}
442
443type CompletionId = usize;
444
445pub(crate) enum EditDisplayMode {
446 TabAccept,
447 DiffPopover,
448 Inline,
449}
450
451enum InlineCompletion {
452 Edit {
453 edits: Vec<(Range<Anchor>, String)>,
454 edit_preview: Option<EditPreview>,
455 display_mode: EditDisplayMode,
456 snapshot: BufferSnapshot,
457 },
458 Move {
459 target: Anchor,
460 snapshot: BufferSnapshot,
461 },
462}
463
464struct InlineCompletionState {
465 inlay_ids: Vec<InlayId>,
466 completion: InlineCompletion,
467 completion_id: Option<SharedString>,
468 invalidation_range: Range<Anchor>,
469}
470
471enum EditPredictionSettings {
472 Disabled,
473 Enabled {
474 show_in_menu: bool,
475 preview_requires_modifier: bool,
476 },
477}
478
479enum InlineCompletionHighlight {}
480
481#[derive(Debug, Clone)]
482struct InlineDiagnostic {
483 message: SharedString,
484 group_id: usize,
485 is_primary: bool,
486 start: Point,
487 severity: DiagnosticSeverity,
488}
489
490pub enum MenuInlineCompletionsPolicy {
491 Never,
492 ByProvider,
493}
494
495pub enum EditPredictionPreview {
496 /// Modifier is not pressed
497 Inactive { released_too_fast: bool },
498 /// Modifier pressed
499 Active {
500 since: Instant,
501 previous_scroll_position: Option<ScrollAnchor>,
502 },
503}
504
505impl EditPredictionPreview {
506 pub fn released_too_fast(&self) -> bool {
507 match self {
508 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
509 EditPredictionPreview::Active { .. } => false,
510 }
511 }
512
513 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
514 if let EditPredictionPreview::Active {
515 previous_scroll_position,
516 ..
517 } = self
518 {
519 *previous_scroll_position = scroll_position;
520 }
521 }
522}
523
524#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
525struct EditorActionId(usize);
526
527impl EditorActionId {
528 pub fn post_inc(&mut self) -> Self {
529 let answer = self.0;
530
531 *self = Self(answer + 1);
532
533 Self(answer)
534 }
535}
536
537// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
538// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
539
540type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
541type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
542
543#[derive(Default)]
544struct ScrollbarMarkerState {
545 scrollbar_size: Size<Pixels>,
546 dirty: bool,
547 markers: Arc<[PaintQuad]>,
548 pending_refresh: Option<Task<Result<()>>>,
549}
550
551impl ScrollbarMarkerState {
552 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
553 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
554 }
555}
556
557#[derive(Clone, Debug)]
558struct RunnableTasks {
559 templates: Vec<(TaskSourceKind, TaskTemplate)>,
560 offset: multi_buffer::Anchor,
561 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
562 column: u32,
563 // Values of all named captures, including those starting with '_'
564 extra_variables: HashMap<String, String>,
565 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
566 context_range: Range<BufferOffset>,
567}
568
569impl RunnableTasks {
570 fn resolve<'a>(
571 &'a self,
572 cx: &'a task::TaskContext,
573 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
574 self.templates.iter().filter_map(|(kind, template)| {
575 template
576 .resolve_task(&kind.to_id_base(), cx)
577 .map(|task| (kind.clone(), task))
578 })
579 }
580}
581
582#[derive(Clone)]
583struct ResolvedTasks {
584 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
585 position: Anchor,
586}
587#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
588struct BufferOffset(usize);
589
590// Addons allow storing per-editor state in other crates (e.g. Vim)
591pub trait Addon: 'static {
592 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
593
594 fn render_buffer_header_controls(
595 &self,
596 _: &ExcerptInfo,
597 _: &Window,
598 _: &App,
599 ) -> Option<AnyElement> {
600 None
601 }
602
603 fn to_any(&self) -> &dyn std::any::Any;
604}
605
606#[derive(Debug, Copy, Clone, PartialEq, Eq)]
607pub enum IsVimMode {
608 Yes,
609 No,
610}
611
612/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
613///
614/// See the [module level documentation](self) for more information.
615pub struct Editor {
616 focus_handle: FocusHandle,
617 last_focused_descendant: Option<WeakFocusHandle>,
618 /// The text buffer being edited
619 buffer: Entity<MultiBuffer>,
620 /// Map of how text in the buffer should be displayed.
621 /// Handles soft wraps, folds, fake inlay text insertions, etc.
622 pub display_map: Entity<DisplayMap>,
623 pub selections: SelectionsCollection,
624 pub scroll_manager: ScrollManager,
625 /// When inline assist editors are linked, they all render cursors because
626 /// typing enters text into each of them, even the ones that aren't focused.
627 pub(crate) show_cursor_when_unfocused: bool,
628 columnar_selection_tail: Option<Anchor>,
629 add_selections_state: Option<AddSelectionsState>,
630 select_next_state: Option<SelectNextState>,
631 select_prev_state: Option<SelectNextState>,
632 selection_history: SelectionHistory,
633 autoclose_regions: Vec<AutocloseRegion>,
634 snippet_stack: InvalidationStack<SnippetState>,
635 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
636 ime_transaction: Option<TransactionId>,
637 active_diagnostics: Option<ActiveDiagnosticGroup>,
638 show_inline_diagnostics: bool,
639 inline_diagnostics_update: Task<()>,
640 inline_diagnostics_enabled: bool,
641 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
642 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
643
644 // TODO: make this a access method
645 pub project: Option<Entity<Project>>,
646 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
647 completion_provider: Option<Box<dyn CompletionProvider>>,
648 collaboration_hub: Option<Box<dyn CollaborationHub>>,
649 blink_manager: Entity<BlinkManager>,
650 show_cursor_names: bool,
651 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
652 pub show_local_selections: bool,
653 mode: EditorMode,
654 show_breadcrumbs: bool,
655 show_gutter: bool,
656 show_scrollbars: bool,
657 show_line_numbers: Option<bool>,
658 use_relative_line_numbers: Option<bool>,
659 show_git_diff_gutter: Option<bool>,
660 show_code_actions: Option<bool>,
661 show_runnables: Option<bool>,
662 show_wrap_guides: Option<bool>,
663 show_indent_guides: Option<bool>,
664 placeholder_text: Option<Arc<str>>,
665 highlight_order: usize,
666 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
667 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
668 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
669 scrollbar_marker_state: ScrollbarMarkerState,
670 active_indent_guides_state: ActiveIndentGuidesState,
671 nav_history: Option<ItemNavHistory>,
672 context_menu: RefCell<Option<CodeContextMenu>>,
673 mouse_context_menu: Option<MouseContextMenu>,
674 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
675 signature_help_state: SignatureHelpState,
676 auto_signature_help: Option<bool>,
677 find_all_references_task_sources: Vec<Anchor>,
678 next_completion_id: CompletionId,
679 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
680 code_actions_task: Option<Task<Result<()>>>,
681 selection_highlight_task: Option<Task<()>>,
682 document_highlights_task: Option<Task<()>>,
683 linked_editing_range_task: Option<Task<Option<()>>>,
684 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
685 pending_rename: Option<RenameState>,
686 searchable: bool,
687 cursor_shape: CursorShape,
688 current_line_highlight: Option<CurrentLineHighlight>,
689 collapse_matches: bool,
690 autoindent_mode: Option<AutoindentMode>,
691 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
692 input_enabled: bool,
693 use_modal_editing: bool,
694 read_only: bool,
695 leader_peer_id: Option<PeerId>,
696 remote_id: Option<ViewId>,
697 hover_state: HoverState,
698 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
699 gutter_hovered: bool,
700 hovered_link_state: Option<HoveredLinkState>,
701 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
702 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
703 active_inline_completion: Option<InlineCompletionState>,
704 /// Used to prevent flickering as the user types while the menu is open
705 stale_inline_completion_in_menu: Option<InlineCompletionState>,
706 edit_prediction_settings: EditPredictionSettings,
707 inline_completions_hidden_for_vim_mode: bool,
708 show_inline_completions_override: Option<bool>,
709 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
710 edit_prediction_preview: EditPredictionPreview,
711 edit_prediction_indent_conflict: bool,
712 edit_prediction_requires_modifier_in_indent_conflict: bool,
713 inlay_hint_cache: InlayHintCache,
714 next_inlay_id: usize,
715 _subscriptions: Vec<Subscription>,
716 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
717 gutter_dimensions: GutterDimensions,
718 style: Option<EditorStyle>,
719 text_style_refinement: Option<TextStyleRefinement>,
720 next_editor_action_id: EditorActionId,
721 editor_actions:
722 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
723 use_autoclose: bool,
724 use_auto_surround: bool,
725 auto_replace_emoji_shortcode: bool,
726 show_git_blame_gutter: bool,
727 show_git_blame_inline: bool,
728 show_git_blame_inline_delay_task: Option<Task<()>>,
729 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
730 git_blame_inline_enabled: bool,
731 serialize_dirty_buffers: bool,
732 show_selection_menu: Option<bool>,
733 blame: Option<Entity<GitBlame>>,
734 blame_subscription: Option<Subscription>,
735 custom_context_menu: Option<
736 Box<
737 dyn 'static
738 + Fn(
739 &mut Self,
740 DisplayPoint,
741 &mut Window,
742 &mut Context<Self>,
743 ) -> Option<Entity<ui::ContextMenu>>,
744 >,
745 >,
746 last_bounds: Option<Bounds<Pixels>>,
747 last_position_map: Option<Rc<PositionMap>>,
748 expect_bounds_change: Option<Bounds<Pixels>>,
749 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
750 tasks_update_task: Option<Task<()>>,
751 in_project_search: bool,
752 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
753 breadcrumb_header: Option<String>,
754 focused_block: Option<FocusedBlock>,
755 next_scroll_position: NextScrollCursorCenterTopBottom,
756 addons: HashMap<TypeId, Box<dyn Addon>>,
757 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
758 load_diff_task: Option<Shared<Task<()>>>,
759 selection_mark_mode: bool,
760 toggle_fold_multiple_buffers: Task<()>,
761 _scroll_cursor_center_top_bottom_task: Task<()>,
762 serialize_selections: Task<()>,
763}
764
765#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
766enum NextScrollCursorCenterTopBottom {
767 #[default]
768 Center,
769 Top,
770 Bottom,
771}
772
773impl NextScrollCursorCenterTopBottom {
774 fn next(&self) -> Self {
775 match self {
776 Self::Center => Self::Top,
777 Self::Top => Self::Bottom,
778 Self::Bottom => Self::Center,
779 }
780 }
781}
782
783#[derive(Clone)]
784pub struct EditorSnapshot {
785 pub mode: EditorMode,
786 show_gutter: bool,
787 show_line_numbers: Option<bool>,
788 show_git_diff_gutter: Option<bool>,
789 show_code_actions: Option<bool>,
790 show_runnables: Option<bool>,
791 git_blame_gutter_max_author_length: Option<usize>,
792 pub display_snapshot: DisplaySnapshot,
793 pub placeholder_text: Option<Arc<str>>,
794 is_focused: bool,
795 scroll_anchor: ScrollAnchor,
796 ongoing_scroll: OngoingScroll,
797 current_line_highlight: CurrentLineHighlight,
798 gutter_hovered: bool,
799}
800
801const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
802
803#[derive(Default, Debug, Clone, Copy)]
804pub struct GutterDimensions {
805 pub left_padding: Pixels,
806 pub right_padding: Pixels,
807 pub width: Pixels,
808 pub margin: Pixels,
809 pub git_blame_entries_width: Option<Pixels>,
810}
811
812impl GutterDimensions {
813 /// The full width of the space taken up by the gutter.
814 pub fn full_width(&self) -> Pixels {
815 self.margin + self.width
816 }
817
818 /// The width of the space reserved for the fold indicators,
819 /// use alongside 'justify_end' and `gutter_width` to
820 /// right align content with the line numbers
821 pub fn fold_area_width(&self) -> Pixels {
822 self.margin + self.right_padding
823 }
824}
825
826#[derive(Debug)]
827pub struct RemoteSelection {
828 pub replica_id: ReplicaId,
829 pub selection: Selection<Anchor>,
830 pub cursor_shape: CursorShape,
831 pub peer_id: PeerId,
832 pub line_mode: bool,
833 pub participant_index: Option<ParticipantIndex>,
834 pub user_name: Option<SharedString>,
835}
836
837#[derive(Clone, Debug)]
838struct SelectionHistoryEntry {
839 selections: Arc<[Selection<Anchor>]>,
840 select_next_state: Option<SelectNextState>,
841 select_prev_state: Option<SelectNextState>,
842 add_selections_state: Option<AddSelectionsState>,
843}
844
845enum SelectionHistoryMode {
846 Normal,
847 Undoing,
848 Redoing,
849}
850
851#[derive(Clone, PartialEq, Eq, Hash)]
852struct HoveredCursor {
853 replica_id: u16,
854 selection_id: usize,
855}
856
857impl Default for SelectionHistoryMode {
858 fn default() -> Self {
859 Self::Normal
860 }
861}
862
863#[derive(Default)]
864struct SelectionHistory {
865 #[allow(clippy::type_complexity)]
866 selections_by_transaction:
867 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
868 mode: SelectionHistoryMode,
869 undo_stack: VecDeque<SelectionHistoryEntry>,
870 redo_stack: VecDeque<SelectionHistoryEntry>,
871}
872
873impl SelectionHistory {
874 fn insert_transaction(
875 &mut self,
876 transaction_id: TransactionId,
877 selections: Arc<[Selection<Anchor>]>,
878 ) {
879 self.selections_by_transaction
880 .insert(transaction_id, (selections, None));
881 }
882
883 #[allow(clippy::type_complexity)]
884 fn transaction(
885 &self,
886 transaction_id: TransactionId,
887 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
888 self.selections_by_transaction.get(&transaction_id)
889 }
890
891 #[allow(clippy::type_complexity)]
892 fn transaction_mut(
893 &mut self,
894 transaction_id: TransactionId,
895 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
896 self.selections_by_transaction.get_mut(&transaction_id)
897 }
898
899 fn push(&mut self, entry: SelectionHistoryEntry) {
900 if !entry.selections.is_empty() {
901 match self.mode {
902 SelectionHistoryMode::Normal => {
903 self.push_undo(entry);
904 self.redo_stack.clear();
905 }
906 SelectionHistoryMode::Undoing => self.push_redo(entry),
907 SelectionHistoryMode::Redoing => self.push_undo(entry),
908 }
909 }
910 }
911
912 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
913 if self
914 .undo_stack
915 .back()
916 .map_or(true, |e| e.selections != entry.selections)
917 {
918 self.undo_stack.push_back(entry);
919 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
920 self.undo_stack.pop_front();
921 }
922 }
923 }
924
925 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
926 if self
927 .redo_stack
928 .back()
929 .map_or(true, |e| e.selections != entry.selections)
930 {
931 self.redo_stack.push_back(entry);
932 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
933 self.redo_stack.pop_front();
934 }
935 }
936 }
937}
938
939struct RowHighlight {
940 index: usize,
941 range: Range<Anchor>,
942 color: Hsla,
943 should_autoscroll: bool,
944}
945
946#[derive(Clone, Debug)]
947struct AddSelectionsState {
948 above: bool,
949 stack: Vec<usize>,
950}
951
952#[derive(Clone)]
953struct SelectNextState {
954 query: AhoCorasick,
955 wordwise: bool,
956 done: bool,
957}
958
959impl std::fmt::Debug for SelectNextState {
960 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961 f.debug_struct(std::any::type_name::<Self>())
962 .field("wordwise", &self.wordwise)
963 .field("done", &self.done)
964 .finish()
965 }
966}
967
968#[derive(Debug)]
969struct AutocloseRegion {
970 selection_id: usize,
971 range: Range<Anchor>,
972 pair: BracketPair,
973}
974
975#[derive(Debug)]
976struct SnippetState {
977 ranges: Vec<Vec<Range<Anchor>>>,
978 active_index: usize,
979 choices: Vec<Option<Vec<String>>>,
980}
981
982#[doc(hidden)]
983pub struct RenameState {
984 pub range: Range<Anchor>,
985 pub old_name: Arc<str>,
986 pub editor: Entity<Editor>,
987 block_id: CustomBlockId,
988}
989
990struct InvalidationStack<T>(Vec<T>);
991
992struct RegisteredInlineCompletionProvider {
993 provider: Arc<dyn InlineCompletionProviderHandle>,
994 _subscription: Subscription,
995}
996
997#[derive(Debug, PartialEq, Eq)]
998struct ActiveDiagnosticGroup {
999 primary_range: Range<Anchor>,
1000 primary_message: String,
1001 group_id: usize,
1002 blocks: HashMap<CustomBlockId, Diagnostic>,
1003 is_valid: bool,
1004}
1005
1006#[derive(Serialize, Deserialize, Clone, Debug)]
1007pub struct ClipboardSelection {
1008 /// The number of bytes in this selection.
1009 pub len: usize,
1010 /// Whether this was a full-line selection.
1011 pub is_entire_line: bool,
1012 /// The column where this selection originally started.
1013 pub start_column: u32,
1014}
1015
1016#[derive(Debug)]
1017pub(crate) struct NavigationData {
1018 cursor_anchor: Anchor,
1019 cursor_position: Point,
1020 scroll_anchor: ScrollAnchor,
1021 scroll_top_row: u32,
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025pub enum GotoDefinitionKind {
1026 Symbol,
1027 Declaration,
1028 Type,
1029 Implementation,
1030}
1031
1032#[derive(Debug, Clone)]
1033enum InlayHintRefreshReason {
1034 ModifiersChanged(bool),
1035 Toggle(bool),
1036 SettingsChange(InlayHintSettings),
1037 NewLinesShown,
1038 BufferEdited(HashSet<Arc<Language>>),
1039 RefreshRequested,
1040 ExcerptsRemoved(Vec<ExcerptId>),
1041}
1042
1043impl InlayHintRefreshReason {
1044 fn description(&self) -> &'static str {
1045 match self {
1046 Self::ModifiersChanged(_) => "modifiers changed",
1047 Self::Toggle(_) => "toggle",
1048 Self::SettingsChange(_) => "settings change",
1049 Self::NewLinesShown => "new lines shown",
1050 Self::BufferEdited(_) => "buffer edited",
1051 Self::RefreshRequested => "refresh requested",
1052 Self::ExcerptsRemoved(_) => "excerpts removed",
1053 }
1054 }
1055}
1056
1057pub enum FormatTarget {
1058 Buffers,
1059 Ranges(Vec<Range<MultiBufferPoint>>),
1060}
1061
1062pub(crate) struct FocusedBlock {
1063 id: BlockId,
1064 focus_handle: WeakFocusHandle,
1065}
1066
1067#[derive(Clone)]
1068enum JumpData {
1069 MultiBufferRow {
1070 row: MultiBufferRow,
1071 line_offset_from_top: u32,
1072 },
1073 MultiBufferPoint {
1074 excerpt_id: ExcerptId,
1075 position: Point,
1076 anchor: text::Anchor,
1077 line_offset_from_top: u32,
1078 },
1079}
1080
1081pub enum MultibufferSelectionMode {
1082 First,
1083 All,
1084}
1085
1086impl Editor {
1087 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1088 let buffer = cx.new(|cx| Buffer::local("", cx));
1089 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1090 Self::new(
1091 EditorMode::SingleLine { auto_width: false },
1092 buffer,
1093 None,
1094 false,
1095 window,
1096 cx,
1097 )
1098 }
1099
1100 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1101 let buffer = cx.new(|cx| Buffer::local("", cx));
1102 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1103 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1104 }
1105
1106 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1107 let buffer = cx.new(|cx| Buffer::local("", cx));
1108 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1109 Self::new(
1110 EditorMode::SingleLine { auto_width: true },
1111 buffer,
1112 None,
1113 false,
1114 window,
1115 cx,
1116 )
1117 }
1118
1119 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1120 let buffer = cx.new(|cx| Buffer::local("", cx));
1121 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1122 Self::new(
1123 EditorMode::AutoHeight { max_lines },
1124 buffer,
1125 None,
1126 false,
1127 window,
1128 cx,
1129 )
1130 }
1131
1132 pub fn for_buffer(
1133 buffer: Entity<Buffer>,
1134 project: Option<Entity<Project>>,
1135 window: &mut Window,
1136 cx: &mut Context<Self>,
1137 ) -> Self {
1138 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1139 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1140 }
1141
1142 pub fn for_multibuffer(
1143 buffer: Entity<MultiBuffer>,
1144 project: Option<Entity<Project>>,
1145 show_excerpt_controls: bool,
1146 window: &mut Window,
1147 cx: &mut Context<Self>,
1148 ) -> Self {
1149 Self::new(
1150 EditorMode::Full,
1151 buffer,
1152 project,
1153 show_excerpt_controls,
1154 window,
1155 cx,
1156 )
1157 }
1158
1159 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1160 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1161 let mut clone = Self::new(
1162 self.mode,
1163 self.buffer.clone(),
1164 self.project.clone(),
1165 show_excerpt_controls,
1166 window,
1167 cx,
1168 );
1169 self.display_map.update(cx, |display_map, cx| {
1170 let snapshot = display_map.snapshot(cx);
1171 clone.display_map.update(cx, |display_map, cx| {
1172 display_map.set_state(&snapshot, cx);
1173 });
1174 });
1175 clone.selections.clone_state(&self.selections);
1176 clone.scroll_manager.clone_state(&self.scroll_manager);
1177 clone.searchable = self.searchable;
1178 clone
1179 }
1180
1181 pub fn new(
1182 mode: EditorMode,
1183 buffer: Entity<MultiBuffer>,
1184 project: Option<Entity<Project>>,
1185 show_excerpt_controls: bool,
1186 window: &mut Window,
1187 cx: &mut Context<Self>,
1188 ) -> Self {
1189 let style = window.text_style();
1190 let font_size = style.font_size.to_pixels(window.rem_size());
1191 let editor = cx.entity().downgrade();
1192 let fold_placeholder = FoldPlaceholder {
1193 constrain_width: true,
1194 render: Arc::new(move |fold_id, fold_range, cx| {
1195 let editor = editor.clone();
1196 div()
1197 .id(fold_id)
1198 .bg(cx.theme().colors().ghost_element_background)
1199 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1200 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1201 .rounded_sm()
1202 .size_full()
1203 .cursor_pointer()
1204 .child("⋯")
1205 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1206 .on_click(move |_, _window, cx| {
1207 editor
1208 .update(cx, |editor, cx| {
1209 editor.unfold_ranges(
1210 &[fold_range.start..fold_range.end],
1211 true,
1212 false,
1213 cx,
1214 );
1215 cx.stop_propagation();
1216 })
1217 .ok();
1218 })
1219 .into_any()
1220 }),
1221 merge_adjacent: true,
1222 ..Default::default()
1223 };
1224 let display_map = cx.new(|cx| {
1225 DisplayMap::new(
1226 buffer.clone(),
1227 style.font(),
1228 font_size,
1229 None,
1230 show_excerpt_controls,
1231 FILE_HEADER_HEIGHT,
1232 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1233 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1234 fold_placeholder,
1235 cx,
1236 )
1237 });
1238
1239 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1240
1241 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1242
1243 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1244 .then(|| language_settings::SoftWrap::None);
1245
1246 let mut project_subscriptions = Vec::new();
1247 if mode == EditorMode::Full {
1248 if let Some(project) = project.as_ref() {
1249 if buffer.read(cx).is_singleton() {
1250 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1251 cx.emit(EditorEvent::TitleChanged);
1252 }));
1253 }
1254 project_subscriptions.push(cx.subscribe_in(
1255 project,
1256 window,
1257 |editor, _, event, window, cx| {
1258 if let project::Event::RefreshInlayHints = event {
1259 editor
1260 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1261 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1262 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1263 let focus_handle = editor.focus_handle(cx);
1264 if focus_handle.is_focused(window) {
1265 let snapshot = buffer.read(cx).snapshot();
1266 for (range, snippet) in snippet_edits {
1267 let editor_range =
1268 language::range_from_lsp(*range).to_offset(&snapshot);
1269 editor
1270 .insert_snippet(
1271 &[editor_range],
1272 snippet.clone(),
1273 window,
1274 cx,
1275 )
1276 .ok();
1277 }
1278 }
1279 }
1280 }
1281 },
1282 ));
1283 if let Some(task_inventory) = project
1284 .read(cx)
1285 .task_store()
1286 .read(cx)
1287 .task_inventory()
1288 .cloned()
1289 {
1290 project_subscriptions.push(cx.observe_in(
1291 &task_inventory,
1292 window,
1293 |editor, _, window, cx| {
1294 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1295 },
1296 ));
1297 }
1298 }
1299 }
1300
1301 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1302
1303 let inlay_hint_settings =
1304 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1305 let focus_handle = cx.focus_handle();
1306 cx.on_focus(&focus_handle, window, Self::handle_focus)
1307 .detach();
1308 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1309 .detach();
1310 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1311 .detach();
1312 cx.on_blur(&focus_handle, window, Self::handle_blur)
1313 .detach();
1314
1315 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1316 Some(false)
1317 } else {
1318 None
1319 };
1320
1321 let mut code_action_providers = Vec::new();
1322 let mut load_uncommitted_diff = None;
1323 if let Some(project) = project.clone() {
1324 load_uncommitted_diff = Some(
1325 get_uncommitted_diff_for_buffer(
1326 &project,
1327 buffer.read(cx).all_buffers(),
1328 buffer.clone(),
1329 cx,
1330 )
1331 .shared(),
1332 );
1333 code_action_providers.push(Rc::new(project) as Rc<_>);
1334 }
1335
1336 let mut this = Self {
1337 focus_handle,
1338 show_cursor_when_unfocused: false,
1339 last_focused_descendant: None,
1340 buffer: buffer.clone(),
1341 display_map: display_map.clone(),
1342 selections,
1343 scroll_manager: ScrollManager::new(cx),
1344 columnar_selection_tail: None,
1345 add_selections_state: None,
1346 select_next_state: None,
1347 select_prev_state: None,
1348 selection_history: Default::default(),
1349 autoclose_regions: Default::default(),
1350 snippet_stack: Default::default(),
1351 select_larger_syntax_node_stack: Vec::new(),
1352 ime_transaction: Default::default(),
1353 active_diagnostics: None,
1354 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1355 inline_diagnostics_update: Task::ready(()),
1356 inline_diagnostics: Vec::new(),
1357 soft_wrap_mode_override,
1358 completion_provider: project.clone().map(|project| Box::new(project) as _),
1359 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1360 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1361 project,
1362 blink_manager: blink_manager.clone(),
1363 show_local_selections: true,
1364 show_scrollbars: true,
1365 mode,
1366 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1367 show_gutter: mode == EditorMode::Full,
1368 show_line_numbers: None,
1369 use_relative_line_numbers: None,
1370 show_git_diff_gutter: None,
1371 show_code_actions: None,
1372 show_runnables: None,
1373 show_wrap_guides: None,
1374 show_indent_guides,
1375 placeholder_text: None,
1376 highlight_order: 0,
1377 highlighted_rows: HashMap::default(),
1378 background_highlights: Default::default(),
1379 gutter_highlights: TreeMap::default(),
1380 scrollbar_marker_state: ScrollbarMarkerState::default(),
1381 active_indent_guides_state: ActiveIndentGuidesState::default(),
1382 nav_history: None,
1383 context_menu: RefCell::new(None),
1384 mouse_context_menu: None,
1385 completion_tasks: Default::default(),
1386 signature_help_state: SignatureHelpState::default(),
1387 auto_signature_help: None,
1388 find_all_references_task_sources: Vec::new(),
1389 next_completion_id: 0,
1390 next_inlay_id: 0,
1391 code_action_providers,
1392 available_code_actions: Default::default(),
1393 code_actions_task: Default::default(),
1394 selection_highlight_task: Default::default(),
1395 document_highlights_task: Default::default(),
1396 linked_editing_range_task: Default::default(),
1397 pending_rename: Default::default(),
1398 searchable: true,
1399 cursor_shape: EditorSettings::get_global(cx)
1400 .cursor_shape
1401 .unwrap_or_default(),
1402 current_line_highlight: None,
1403 autoindent_mode: Some(AutoindentMode::EachLine),
1404 collapse_matches: false,
1405 workspace: None,
1406 input_enabled: true,
1407 use_modal_editing: mode == EditorMode::Full,
1408 read_only: false,
1409 use_autoclose: true,
1410 use_auto_surround: true,
1411 auto_replace_emoji_shortcode: false,
1412 leader_peer_id: None,
1413 remote_id: None,
1414 hover_state: Default::default(),
1415 pending_mouse_down: None,
1416 hovered_link_state: Default::default(),
1417 edit_prediction_provider: None,
1418 active_inline_completion: None,
1419 stale_inline_completion_in_menu: None,
1420 edit_prediction_preview: EditPredictionPreview::Inactive {
1421 released_too_fast: false,
1422 },
1423 inline_diagnostics_enabled: mode == EditorMode::Full,
1424 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1425
1426 gutter_hovered: false,
1427 pixel_position_of_newest_cursor: None,
1428 last_bounds: None,
1429 last_position_map: None,
1430 expect_bounds_change: None,
1431 gutter_dimensions: GutterDimensions::default(),
1432 style: None,
1433 show_cursor_names: false,
1434 hovered_cursors: Default::default(),
1435 next_editor_action_id: EditorActionId::default(),
1436 editor_actions: Rc::default(),
1437 inline_completions_hidden_for_vim_mode: false,
1438 show_inline_completions_override: None,
1439 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1440 edit_prediction_settings: EditPredictionSettings::Disabled,
1441 edit_prediction_indent_conflict: false,
1442 edit_prediction_requires_modifier_in_indent_conflict: true,
1443 custom_context_menu: None,
1444 show_git_blame_gutter: false,
1445 show_git_blame_inline: false,
1446 show_selection_menu: None,
1447 show_git_blame_inline_delay_task: None,
1448 git_blame_inline_tooltip: None,
1449 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1450 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1451 .session
1452 .restore_unsaved_buffers,
1453 blame: None,
1454 blame_subscription: None,
1455 tasks: Default::default(),
1456 _subscriptions: vec![
1457 cx.observe(&buffer, Self::on_buffer_changed),
1458 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1459 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1460 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1461 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1462 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1463 cx.observe_window_activation(window, |editor, window, cx| {
1464 let active = window.is_window_active();
1465 editor.blink_manager.update(cx, |blink_manager, cx| {
1466 if active {
1467 blink_manager.enable(cx);
1468 } else {
1469 blink_manager.disable(cx);
1470 }
1471 });
1472 }),
1473 ],
1474 tasks_update_task: None,
1475 linked_edit_ranges: Default::default(),
1476 in_project_search: false,
1477 previous_search_ranges: None,
1478 breadcrumb_header: None,
1479 focused_block: None,
1480 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1481 addons: HashMap::default(),
1482 registered_buffers: HashMap::default(),
1483 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1484 selection_mark_mode: false,
1485 toggle_fold_multiple_buffers: Task::ready(()),
1486 serialize_selections: Task::ready(()),
1487 text_style_refinement: None,
1488 load_diff_task: load_uncommitted_diff,
1489 };
1490 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1491 this._subscriptions.extend(project_subscriptions);
1492
1493 this.end_selection(window, cx);
1494 this.scroll_manager.show_scrollbar(window, cx);
1495
1496 if mode == EditorMode::Full {
1497 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1498 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1499
1500 if this.git_blame_inline_enabled {
1501 this.git_blame_inline_enabled = true;
1502 this.start_git_blame_inline(false, window, cx);
1503 }
1504
1505 if let Some(buffer) = buffer.read(cx).as_singleton() {
1506 if let Some(project) = this.project.as_ref() {
1507 let handle = project.update(cx, |project, cx| {
1508 project.register_buffer_with_language_servers(&buffer, cx)
1509 });
1510 this.registered_buffers
1511 .insert(buffer.read(cx).remote_id(), handle);
1512 }
1513 }
1514 }
1515
1516 this.report_editor_event("Editor Opened", None, cx);
1517 this
1518 }
1519
1520 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1521 self.mouse_context_menu
1522 .as_ref()
1523 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1524 }
1525
1526 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1527 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1528 }
1529
1530 fn key_context_internal(
1531 &self,
1532 has_active_edit_prediction: bool,
1533 window: &Window,
1534 cx: &App,
1535 ) -> KeyContext {
1536 let mut key_context = KeyContext::new_with_defaults();
1537 key_context.add("Editor");
1538 let mode = match self.mode {
1539 EditorMode::SingleLine { .. } => "single_line",
1540 EditorMode::AutoHeight { .. } => "auto_height",
1541 EditorMode::Full => "full",
1542 };
1543
1544 if EditorSettings::jupyter_enabled(cx) {
1545 key_context.add("jupyter");
1546 }
1547
1548 key_context.set("mode", mode);
1549 if self.pending_rename.is_some() {
1550 key_context.add("renaming");
1551 }
1552
1553 match self.context_menu.borrow().as_ref() {
1554 Some(CodeContextMenu::Completions(_)) => {
1555 key_context.add("menu");
1556 key_context.add("showing_completions");
1557 }
1558 Some(CodeContextMenu::CodeActions(_)) => {
1559 key_context.add("menu");
1560 key_context.add("showing_code_actions")
1561 }
1562 None => {}
1563 }
1564
1565 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1566 if !self.focus_handle(cx).contains_focused(window, cx)
1567 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1568 {
1569 for addon in self.addons.values() {
1570 addon.extend_key_context(&mut key_context, cx)
1571 }
1572 }
1573
1574 if let Some(extension) = self
1575 .buffer
1576 .read(cx)
1577 .as_singleton()
1578 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1579 {
1580 key_context.set("extension", extension.to_string());
1581 }
1582
1583 if has_active_edit_prediction {
1584 if self.edit_prediction_in_conflict() {
1585 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1586 } else {
1587 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1588 key_context.add("copilot_suggestion");
1589 }
1590 }
1591
1592 if self.selection_mark_mode {
1593 key_context.add("selection_mode");
1594 }
1595
1596 key_context
1597 }
1598
1599 pub fn edit_prediction_in_conflict(&self) -> bool {
1600 if !self.show_edit_predictions_in_menu() {
1601 return false;
1602 }
1603
1604 let showing_completions = self
1605 .context_menu
1606 .borrow()
1607 .as_ref()
1608 .map_or(false, |context| {
1609 matches!(context, CodeContextMenu::Completions(_))
1610 });
1611
1612 showing_completions
1613 || self.edit_prediction_requires_modifier()
1614 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1615 // bindings to insert tab characters.
1616 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1617 }
1618
1619 pub fn accept_edit_prediction_keybind(
1620 &self,
1621 window: &Window,
1622 cx: &App,
1623 ) -> AcceptEditPredictionBinding {
1624 let key_context = self.key_context_internal(true, window, cx);
1625 let in_conflict = self.edit_prediction_in_conflict();
1626
1627 AcceptEditPredictionBinding(
1628 window
1629 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1630 .into_iter()
1631 .filter(|binding| {
1632 !in_conflict
1633 || binding
1634 .keystrokes()
1635 .first()
1636 .map_or(false, |keystroke| keystroke.modifiers.modified())
1637 })
1638 .rev()
1639 .min_by_key(|binding| {
1640 binding
1641 .keystrokes()
1642 .first()
1643 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1644 }),
1645 )
1646 }
1647
1648 pub fn new_file(
1649 workspace: &mut Workspace,
1650 _: &workspace::NewFile,
1651 window: &mut Window,
1652 cx: &mut Context<Workspace>,
1653 ) {
1654 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1655 "Failed to create buffer",
1656 window,
1657 cx,
1658 |e, _, _| match e.error_code() {
1659 ErrorCode::RemoteUpgradeRequired => Some(format!(
1660 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1661 e.error_tag("required").unwrap_or("the latest version")
1662 )),
1663 _ => None,
1664 },
1665 );
1666 }
1667
1668 pub fn new_in_workspace(
1669 workspace: &mut Workspace,
1670 window: &mut Window,
1671 cx: &mut Context<Workspace>,
1672 ) -> Task<Result<Entity<Editor>>> {
1673 let project = workspace.project().clone();
1674 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1675
1676 cx.spawn_in(window, |workspace, mut cx| async move {
1677 let buffer = create.await?;
1678 workspace.update_in(&mut cx, |workspace, window, cx| {
1679 let editor =
1680 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1681 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1682 editor
1683 })
1684 })
1685 }
1686
1687 fn new_file_vertical(
1688 workspace: &mut Workspace,
1689 _: &workspace::NewFileSplitVertical,
1690 window: &mut Window,
1691 cx: &mut Context<Workspace>,
1692 ) {
1693 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1694 }
1695
1696 fn new_file_horizontal(
1697 workspace: &mut Workspace,
1698 _: &workspace::NewFileSplitHorizontal,
1699 window: &mut Window,
1700 cx: &mut Context<Workspace>,
1701 ) {
1702 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1703 }
1704
1705 fn new_file_in_direction(
1706 workspace: &mut Workspace,
1707 direction: SplitDirection,
1708 window: &mut Window,
1709 cx: &mut Context<Workspace>,
1710 ) {
1711 let project = workspace.project().clone();
1712 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1713
1714 cx.spawn_in(window, |workspace, mut cx| async move {
1715 let buffer = create.await?;
1716 workspace.update_in(&mut cx, move |workspace, window, cx| {
1717 workspace.split_item(
1718 direction,
1719 Box::new(
1720 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1721 ),
1722 window,
1723 cx,
1724 )
1725 })?;
1726 anyhow::Ok(())
1727 })
1728 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1729 match e.error_code() {
1730 ErrorCode::RemoteUpgradeRequired => Some(format!(
1731 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1732 e.error_tag("required").unwrap_or("the latest version")
1733 )),
1734 _ => None,
1735 }
1736 });
1737 }
1738
1739 pub fn leader_peer_id(&self) -> Option<PeerId> {
1740 self.leader_peer_id
1741 }
1742
1743 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1744 &self.buffer
1745 }
1746
1747 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1748 self.workspace.as_ref()?.0.upgrade()
1749 }
1750
1751 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1752 self.buffer().read(cx).title(cx)
1753 }
1754
1755 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1756 let git_blame_gutter_max_author_length = self
1757 .render_git_blame_gutter(cx)
1758 .then(|| {
1759 if let Some(blame) = self.blame.as_ref() {
1760 let max_author_length =
1761 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1762 Some(max_author_length)
1763 } else {
1764 None
1765 }
1766 })
1767 .flatten();
1768
1769 EditorSnapshot {
1770 mode: self.mode,
1771 show_gutter: self.show_gutter,
1772 show_line_numbers: self.show_line_numbers,
1773 show_git_diff_gutter: self.show_git_diff_gutter,
1774 show_code_actions: self.show_code_actions,
1775 show_runnables: self.show_runnables,
1776 git_blame_gutter_max_author_length,
1777 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1778 scroll_anchor: self.scroll_manager.anchor(),
1779 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1780 placeholder_text: self.placeholder_text.clone(),
1781 is_focused: self.focus_handle.is_focused(window),
1782 current_line_highlight: self
1783 .current_line_highlight
1784 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1785 gutter_hovered: self.gutter_hovered,
1786 }
1787 }
1788
1789 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1790 self.buffer.read(cx).language_at(point, cx)
1791 }
1792
1793 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1794 self.buffer.read(cx).read(cx).file_at(point).cloned()
1795 }
1796
1797 pub fn active_excerpt(
1798 &self,
1799 cx: &App,
1800 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1801 self.buffer
1802 .read(cx)
1803 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1804 }
1805
1806 pub fn mode(&self) -> EditorMode {
1807 self.mode
1808 }
1809
1810 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1811 self.collaboration_hub.as_deref()
1812 }
1813
1814 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1815 self.collaboration_hub = Some(hub);
1816 }
1817
1818 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1819 self.in_project_search = in_project_search;
1820 }
1821
1822 pub fn set_custom_context_menu(
1823 &mut self,
1824 f: impl 'static
1825 + Fn(
1826 &mut Self,
1827 DisplayPoint,
1828 &mut Window,
1829 &mut Context<Self>,
1830 ) -> Option<Entity<ui::ContextMenu>>,
1831 ) {
1832 self.custom_context_menu = Some(Box::new(f))
1833 }
1834
1835 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1836 self.completion_provider = provider;
1837 }
1838
1839 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1840 self.semantics_provider.clone()
1841 }
1842
1843 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1844 self.semantics_provider = provider;
1845 }
1846
1847 pub fn set_edit_prediction_provider<T>(
1848 &mut self,
1849 provider: Option<Entity<T>>,
1850 window: &mut Window,
1851 cx: &mut Context<Self>,
1852 ) where
1853 T: EditPredictionProvider,
1854 {
1855 self.edit_prediction_provider =
1856 provider.map(|provider| RegisteredInlineCompletionProvider {
1857 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1858 if this.focus_handle.is_focused(window) {
1859 this.update_visible_inline_completion(window, cx);
1860 }
1861 }),
1862 provider: Arc::new(provider),
1863 });
1864 self.update_edit_prediction_settings(cx);
1865 self.refresh_inline_completion(false, false, window, cx);
1866 }
1867
1868 pub fn placeholder_text(&self) -> Option<&str> {
1869 self.placeholder_text.as_deref()
1870 }
1871
1872 pub fn set_placeholder_text(
1873 &mut self,
1874 placeholder_text: impl Into<Arc<str>>,
1875 cx: &mut Context<Self>,
1876 ) {
1877 let placeholder_text = Some(placeholder_text.into());
1878 if self.placeholder_text != placeholder_text {
1879 self.placeholder_text = placeholder_text;
1880 cx.notify();
1881 }
1882 }
1883
1884 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1885 self.cursor_shape = cursor_shape;
1886
1887 // Disrupt blink for immediate user feedback that the cursor shape has changed
1888 self.blink_manager.update(cx, BlinkManager::show_cursor);
1889
1890 cx.notify();
1891 }
1892
1893 pub fn set_current_line_highlight(
1894 &mut self,
1895 current_line_highlight: Option<CurrentLineHighlight>,
1896 ) {
1897 self.current_line_highlight = current_line_highlight;
1898 }
1899
1900 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1901 self.collapse_matches = collapse_matches;
1902 }
1903
1904 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1905 let buffers = self.buffer.read(cx).all_buffers();
1906 let Some(project) = self.project.as_ref() else {
1907 return;
1908 };
1909 project.update(cx, |project, cx| {
1910 for buffer in buffers {
1911 self.registered_buffers
1912 .entry(buffer.read(cx).remote_id())
1913 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1914 }
1915 })
1916 }
1917
1918 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1919 if self.collapse_matches {
1920 return range.start..range.start;
1921 }
1922 range.clone()
1923 }
1924
1925 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1926 if self.display_map.read(cx).clip_at_line_ends != clip {
1927 self.display_map
1928 .update(cx, |map, _| map.clip_at_line_ends = clip);
1929 }
1930 }
1931
1932 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1933 self.input_enabled = input_enabled;
1934 }
1935
1936 pub fn set_inline_completions_hidden_for_vim_mode(
1937 &mut self,
1938 hidden: bool,
1939 window: &mut Window,
1940 cx: &mut Context<Self>,
1941 ) {
1942 if hidden != self.inline_completions_hidden_for_vim_mode {
1943 self.inline_completions_hidden_for_vim_mode = hidden;
1944 if hidden {
1945 self.update_visible_inline_completion(window, cx);
1946 } else {
1947 self.refresh_inline_completion(true, false, window, cx);
1948 }
1949 }
1950 }
1951
1952 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1953 self.menu_inline_completions_policy = value;
1954 }
1955
1956 pub fn set_autoindent(&mut self, autoindent: bool) {
1957 if autoindent {
1958 self.autoindent_mode = Some(AutoindentMode::EachLine);
1959 } else {
1960 self.autoindent_mode = None;
1961 }
1962 }
1963
1964 pub fn read_only(&self, cx: &App) -> bool {
1965 self.read_only || self.buffer.read(cx).read_only()
1966 }
1967
1968 pub fn set_read_only(&mut self, read_only: bool) {
1969 self.read_only = read_only;
1970 }
1971
1972 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1973 self.use_autoclose = autoclose;
1974 }
1975
1976 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1977 self.use_auto_surround = auto_surround;
1978 }
1979
1980 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1981 self.auto_replace_emoji_shortcode = auto_replace;
1982 }
1983
1984 pub fn toggle_edit_predictions(
1985 &mut self,
1986 _: &ToggleEditPrediction,
1987 window: &mut Window,
1988 cx: &mut Context<Self>,
1989 ) {
1990 if self.show_inline_completions_override.is_some() {
1991 self.set_show_edit_predictions(None, window, cx);
1992 } else {
1993 let show_edit_predictions = !self.edit_predictions_enabled();
1994 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1995 }
1996 }
1997
1998 pub fn set_show_edit_predictions(
1999 &mut self,
2000 show_edit_predictions: Option<bool>,
2001 window: &mut Window,
2002 cx: &mut Context<Self>,
2003 ) {
2004 self.show_inline_completions_override = show_edit_predictions;
2005 self.update_edit_prediction_settings(cx);
2006
2007 if let Some(false) = show_edit_predictions {
2008 self.discard_inline_completion(false, cx);
2009 } else {
2010 self.refresh_inline_completion(false, true, window, cx);
2011 }
2012 }
2013
2014 fn inline_completions_disabled_in_scope(
2015 &self,
2016 buffer: &Entity<Buffer>,
2017 buffer_position: language::Anchor,
2018 cx: &App,
2019 ) -> bool {
2020 let snapshot = buffer.read(cx).snapshot();
2021 let settings = snapshot.settings_at(buffer_position, cx);
2022
2023 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2024 return false;
2025 };
2026
2027 scope.override_name().map_or(false, |scope_name| {
2028 settings
2029 .edit_predictions_disabled_in
2030 .iter()
2031 .any(|s| s == scope_name)
2032 })
2033 }
2034
2035 pub fn set_use_modal_editing(&mut self, to: bool) {
2036 self.use_modal_editing = to;
2037 }
2038
2039 pub fn use_modal_editing(&self) -> bool {
2040 self.use_modal_editing
2041 }
2042
2043 fn selections_did_change(
2044 &mut self,
2045 local: bool,
2046 old_cursor_position: &Anchor,
2047 show_completions: bool,
2048 window: &mut Window,
2049 cx: &mut Context<Self>,
2050 ) {
2051 window.invalidate_character_coordinates();
2052
2053 // Copy selections to primary selection buffer
2054 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2055 if local {
2056 let selections = self.selections.all::<usize>(cx);
2057 let buffer_handle = self.buffer.read(cx).read(cx);
2058
2059 let mut text = String::new();
2060 for (index, selection) in selections.iter().enumerate() {
2061 let text_for_selection = buffer_handle
2062 .text_for_range(selection.start..selection.end)
2063 .collect::<String>();
2064
2065 text.push_str(&text_for_selection);
2066 if index != selections.len() - 1 {
2067 text.push('\n');
2068 }
2069 }
2070
2071 if !text.is_empty() {
2072 cx.write_to_primary(ClipboardItem::new_string(text));
2073 }
2074 }
2075
2076 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2077 self.buffer.update(cx, |buffer, cx| {
2078 buffer.set_active_selections(
2079 &self.selections.disjoint_anchors(),
2080 self.selections.line_mode,
2081 self.cursor_shape,
2082 cx,
2083 )
2084 });
2085 }
2086 let display_map = self
2087 .display_map
2088 .update(cx, |display_map, cx| display_map.snapshot(cx));
2089 let buffer = &display_map.buffer_snapshot;
2090 self.add_selections_state = None;
2091 self.select_next_state = None;
2092 self.select_prev_state = None;
2093 self.select_larger_syntax_node_stack.clear();
2094 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2095 self.snippet_stack
2096 .invalidate(&self.selections.disjoint_anchors(), buffer);
2097 self.take_rename(false, window, cx);
2098
2099 let new_cursor_position = self.selections.newest_anchor().head();
2100
2101 self.push_to_nav_history(
2102 *old_cursor_position,
2103 Some(new_cursor_position.to_point(buffer)),
2104 cx,
2105 );
2106
2107 if local {
2108 let new_cursor_position = self.selections.newest_anchor().head();
2109 let mut context_menu = self.context_menu.borrow_mut();
2110 let completion_menu = match context_menu.as_ref() {
2111 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2112 _ => {
2113 *context_menu = None;
2114 None
2115 }
2116 };
2117 if let Some(buffer_id) = new_cursor_position.buffer_id {
2118 if !self.registered_buffers.contains_key(&buffer_id) {
2119 if let Some(project) = self.project.as_ref() {
2120 project.update(cx, |project, cx| {
2121 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2122 return;
2123 };
2124 self.registered_buffers.insert(
2125 buffer_id,
2126 project.register_buffer_with_language_servers(&buffer, cx),
2127 );
2128 })
2129 }
2130 }
2131 }
2132
2133 if let Some(completion_menu) = completion_menu {
2134 let cursor_position = new_cursor_position.to_offset(buffer);
2135 let (word_range, kind) =
2136 buffer.surrounding_word(completion_menu.initial_position, true);
2137 if kind == Some(CharKind::Word)
2138 && word_range.to_inclusive().contains(&cursor_position)
2139 {
2140 let mut completion_menu = completion_menu.clone();
2141 drop(context_menu);
2142
2143 let query = Self::completion_query(buffer, cursor_position);
2144 cx.spawn(move |this, mut cx| async move {
2145 completion_menu
2146 .filter(query.as_deref(), cx.background_executor().clone())
2147 .await;
2148
2149 this.update(&mut cx, |this, cx| {
2150 let mut context_menu = this.context_menu.borrow_mut();
2151 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2152 else {
2153 return;
2154 };
2155
2156 if menu.id > completion_menu.id {
2157 return;
2158 }
2159
2160 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2161 drop(context_menu);
2162 cx.notify();
2163 })
2164 })
2165 .detach();
2166
2167 if show_completions {
2168 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2169 }
2170 } else {
2171 drop(context_menu);
2172 self.hide_context_menu(window, cx);
2173 }
2174 } else {
2175 drop(context_menu);
2176 }
2177
2178 hide_hover(self, cx);
2179
2180 if old_cursor_position.to_display_point(&display_map).row()
2181 != new_cursor_position.to_display_point(&display_map).row()
2182 {
2183 self.available_code_actions.take();
2184 }
2185 self.refresh_code_actions(window, cx);
2186 self.refresh_document_highlights(cx);
2187 self.refresh_selected_text_highlights(window, cx);
2188 refresh_matching_bracket_highlights(self, window, cx);
2189 self.update_visible_inline_completion(window, cx);
2190 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2191 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2192 if self.git_blame_inline_enabled {
2193 self.start_inline_blame_timer(window, cx);
2194 }
2195 }
2196
2197 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2198 cx.emit(EditorEvent::SelectionsChanged { local });
2199
2200 let selections = &self.selections.disjoint;
2201 if selections.len() == 1 {
2202 cx.emit(SearchEvent::ActiveMatchChanged)
2203 }
2204 if local
2205 && self.is_singleton(cx)
2206 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2207 {
2208 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2209 let background_executor = cx.background_executor().clone();
2210 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2211 let snapshot = self.buffer().read(cx).snapshot(cx);
2212 let selections = selections.clone();
2213 self.serialize_selections = cx.background_spawn(async move {
2214 background_executor.timer(Duration::from_millis(100)).await;
2215 let selections = selections
2216 .iter()
2217 .map(|selection| {
2218 (
2219 selection.start.to_offset(&snapshot),
2220 selection.end.to_offset(&snapshot),
2221 )
2222 })
2223 .collect();
2224 DB.save_editor_selections(editor_id, workspace_id, selections)
2225 .await
2226 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2227 .log_err();
2228 });
2229 }
2230 }
2231
2232 cx.notify();
2233 }
2234
2235 pub fn change_selections<R>(
2236 &mut self,
2237 autoscroll: Option<Autoscroll>,
2238 window: &mut Window,
2239 cx: &mut Context<Self>,
2240 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2241 ) -> R {
2242 self.change_selections_inner(autoscroll, true, window, cx, change)
2243 }
2244
2245 fn change_selections_inner<R>(
2246 &mut self,
2247 autoscroll: Option<Autoscroll>,
2248 request_completions: bool,
2249 window: &mut Window,
2250 cx: &mut Context<Self>,
2251 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2252 ) -> R {
2253 let old_cursor_position = self.selections.newest_anchor().head();
2254 self.push_to_selection_history();
2255
2256 let (changed, result) = self.selections.change_with(cx, change);
2257
2258 if changed {
2259 if let Some(autoscroll) = autoscroll {
2260 self.request_autoscroll(autoscroll, cx);
2261 }
2262 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2263
2264 if self.should_open_signature_help_automatically(
2265 &old_cursor_position,
2266 self.signature_help_state.backspace_pressed(),
2267 cx,
2268 ) {
2269 self.show_signature_help(&ShowSignatureHelp, window, cx);
2270 }
2271 self.signature_help_state.set_backspace_pressed(false);
2272 }
2273
2274 result
2275 }
2276
2277 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2278 where
2279 I: IntoIterator<Item = (Range<S>, T)>,
2280 S: ToOffset,
2281 T: Into<Arc<str>>,
2282 {
2283 if self.read_only(cx) {
2284 return;
2285 }
2286
2287 self.buffer
2288 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2289 }
2290
2291 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2292 where
2293 I: IntoIterator<Item = (Range<S>, T)>,
2294 S: ToOffset,
2295 T: Into<Arc<str>>,
2296 {
2297 if self.read_only(cx) {
2298 return;
2299 }
2300
2301 self.buffer.update(cx, |buffer, cx| {
2302 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2303 });
2304 }
2305
2306 pub fn edit_with_block_indent<I, S, T>(
2307 &mut self,
2308 edits: I,
2309 original_start_columns: Vec<u32>,
2310 cx: &mut Context<Self>,
2311 ) where
2312 I: IntoIterator<Item = (Range<S>, T)>,
2313 S: ToOffset,
2314 T: Into<Arc<str>>,
2315 {
2316 if self.read_only(cx) {
2317 return;
2318 }
2319
2320 self.buffer.update(cx, |buffer, cx| {
2321 buffer.edit(
2322 edits,
2323 Some(AutoindentMode::Block {
2324 original_start_columns,
2325 }),
2326 cx,
2327 )
2328 });
2329 }
2330
2331 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2332 self.hide_context_menu(window, cx);
2333
2334 match phase {
2335 SelectPhase::Begin {
2336 position,
2337 add,
2338 click_count,
2339 } => self.begin_selection(position, add, click_count, window, cx),
2340 SelectPhase::BeginColumnar {
2341 position,
2342 goal_column,
2343 reset,
2344 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2345 SelectPhase::Extend {
2346 position,
2347 click_count,
2348 } => self.extend_selection(position, click_count, window, cx),
2349 SelectPhase::Update {
2350 position,
2351 goal_column,
2352 scroll_delta,
2353 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2354 SelectPhase::End => self.end_selection(window, cx),
2355 }
2356 }
2357
2358 fn extend_selection(
2359 &mut self,
2360 position: DisplayPoint,
2361 click_count: usize,
2362 window: &mut Window,
2363 cx: &mut Context<Self>,
2364 ) {
2365 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2366 let tail = self.selections.newest::<usize>(cx).tail();
2367 self.begin_selection(position, false, click_count, window, cx);
2368
2369 let position = position.to_offset(&display_map, Bias::Left);
2370 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2371
2372 let mut pending_selection = self
2373 .selections
2374 .pending_anchor()
2375 .expect("extend_selection not called with pending selection");
2376 if position >= tail {
2377 pending_selection.start = tail_anchor;
2378 } else {
2379 pending_selection.end = tail_anchor;
2380 pending_selection.reversed = true;
2381 }
2382
2383 let mut pending_mode = self.selections.pending_mode().unwrap();
2384 match &mut pending_mode {
2385 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2386 _ => {}
2387 }
2388
2389 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2390 s.set_pending(pending_selection, pending_mode)
2391 });
2392 }
2393
2394 fn begin_selection(
2395 &mut self,
2396 position: DisplayPoint,
2397 add: bool,
2398 click_count: usize,
2399 window: &mut Window,
2400 cx: &mut Context<Self>,
2401 ) {
2402 if !self.focus_handle.is_focused(window) {
2403 self.last_focused_descendant = None;
2404 window.focus(&self.focus_handle);
2405 }
2406
2407 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2408 let buffer = &display_map.buffer_snapshot;
2409 let newest_selection = self.selections.newest_anchor().clone();
2410 let position = display_map.clip_point(position, Bias::Left);
2411
2412 let start;
2413 let end;
2414 let mode;
2415 let mut auto_scroll;
2416 match click_count {
2417 1 => {
2418 start = buffer.anchor_before(position.to_point(&display_map));
2419 end = start;
2420 mode = SelectMode::Character;
2421 auto_scroll = true;
2422 }
2423 2 => {
2424 let range = movement::surrounding_word(&display_map, position);
2425 start = buffer.anchor_before(range.start.to_point(&display_map));
2426 end = buffer.anchor_before(range.end.to_point(&display_map));
2427 mode = SelectMode::Word(start..end);
2428 auto_scroll = true;
2429 }
2430 3 => {
2431 let position = display_map
2432 .clip_point(position, Bias::Left)
2433 .to_point(&display_map);
2434 let line_start = display_map.prev_line_boundary(position).0;
2435 let next_line_start = buffer.clip_point(
2436 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2437 Bias::Left,
2438 );
2439 start = buffer.anchor_before(line_start);
2440 end = buffer.anchor_before(next_line_start);
2441 mode = SelectMode::Line(start..end);
2442 auto_scroll = true;
2443 }
2444 _ => {
2445 start = buffer.anchor_before(0);
2446 end = buffer.anchor_before(buffer.len());
2447 mode = SelectMode::All;
2448 auto_scroll = false;
2449 }
2450 }
2451 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2452
2453 let point_to_delete: Option<usize> = {
2454 let selected_points: Vec<Selection<Point>> =
2455 self.selections.disjoint_in_range(start..end, cx);
2456
2457 if !add || click_count > 1 {
2458 None
2459 } else if !selected_points.is_empty() {
2460 Some(selected_points[0].id)
2461 } else {
2462 let clicked_point_already_selected =
2463 self.selections.disjoint.iter().find(|selection| {
2464 selection.start.to_point(buffer) == start.to_point(buffer)
2465 || selection.end.to_point(buffer) == end.to_point(buffer)
2466 });
2467
2468 clicked_point_already_selected.map(|selection| selection.id)
2469 }
2470 };
2471
2472 let selections_count = self.selections.count();
2473
2474 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2475 if let Some(point_to_delete) = point_to_delete {
2476 s.delete(point_to_delete);
2477
2478 if selections_count == 1 {
2479 s.set_pending_anchor_range(start..end, mode);
2480 }
2481 } else {
2482 if !add {
2483 s.clear_disjoint();
2484 } else if click_count > 1 {
2485 s.delete(newest_selection.id)
2486 }
2487
2488 s.set_pending_anchor_range(start..end, mode);
2489 }
2490 });
2491 }
2492
2493 fn begin_columnar_selection(
2494 &mut self,
2495 position: DisplayPoint,
2496 goal_column: u32,
2497 reset: bool,
2498 window: &mut Window,
2499 cx: &mut Context<Self>,
2500 ) {
2501 if !self.focus_handle.is_focused(window) {
2502 self.last_focused_descendant = None;
2503 window.focus(&self.focus_handle);
2504 }
2505
2506 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2507
2508 if reset {
2509 let pointer_position = display_map
2510 .buffer_snapshot
2511 .anchor_before(position.to_point(&display_map));
2512
2513 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2514 s.clear_disjoint();
2515 s.set_pending_anchor_range(
2516 pointer_position..pointer_position,
2517 SelectMode::Character,
2518 );
2519 });
2520 }
2521
2522 let tail = self.selections.newest::<Point>(cx).tail();
2523 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2524
2525 if !reset {
2526 self.select_columns(
2527 tail.to_display_point(&display_map),
2528 position,
2529 goal_column,
2530 &display_map,
2531 window,
2532 cx,
2533 );
2534 }
2535 }
2536
2537 fn update_selection(
2538 &mut self,
2539 position: DisplayPoint,
2540 goal_column: u32,
2541 scroll_delta: gpui::Point<f32>,
2542 window: &mut Window,
2543 cx: &mut Context<Self>,
2544 ) {
2545 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2546
2547 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2548 let tail = tail.to_display_point(&display_map);
2549 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2550 } else if let Some(mut pending) = self.selections.pending_anchor() {
2551 let buffer = self.buffer.read(cx).snapshot(cx);
2552 let head;
2553 let tail;
2554 let mode = self.selections.pending_mode().unwrap();
2555 match &mode {
2556 SelectMode::Character => {
2557 head = position.to_point(&display_map);
2558 tail = pending.tail().to_point(&buffer);
2559 }
2560 SelectMode::Word(original_range) => {
2561 let original_display_range = original_range.start.to_display_point(&display_map)
2562 ..original_range.end.to_display_point(&display_map);
2563 let original_buffer_range = original_display_range.start.to_point(&display_map)
2564 ..original_display_range.end.to_point(&display_map);
2565 if movement::is_inside_word(&display_map, position)
2566 || original_display_range.contains(&position)
2567 {
2568 let word_range = movement::surrounding_word(&display_map, position);
2569 if word_range.start < original_display_range.start {
2570 head = word_range.start.to_point(&display_map);
2571 } else {
2572 head = word_range.end.to_point(&display_map);
2573 }
2574 } else {
2575 head = position.to_point(&display_map);
2576 }
2577
2578 if head <= original_buffer_range.start {
2579 tail = original_buffer_range.end;
2580 } else {
2581 tail = original_buffer_range.start;
2582 }
2583 }
2584 SelectMode::Line(original_range) => {
2585 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2586
2587 let position = display_map
2588 .clip_point(position, Bias::Left)
2589 .to_point(&display_map);
2590 let line_start = display_map.prev_line_boundary(position).0;
2591 let next_line_start = buffer.clip_point(
2592 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2593 Bias::Left,
2594 );
2595
2596 if line_start < original_range.start {
2597 head = line_start
2598 } else {
2599 head = next_line_start
2600 }
2601
2602 if head <= original_range.start {
2603 tail = original_range.end;
2604 } else {
2605 tail = original_range.start;
2606 }
2607 }
2608 SelectMode::All => {
2609 return;
2610 }
2611 };
2612
2613 if head < tail {
2614 pending.start = buffer.anchor_before(head);
2615 pending.end = buffer.anchor_before(tail);
2616 pending.reversed = true;
2617 } else {
2618 pending.start = buffer.anchor_before(tail);
2619 pending.end = buffer.anchor_before(head);
2620 pending.reversed = false;
2621 }
2622
2623 self.change_selections(None, window, cx, |s| {
2624 s.set_pending(pending, mode);
2625 });
2626 } else {
2627 log::error!("update_selection dispatched with no pending selection");
2628 return;
2629 }
2630
2631 self.apply_scroll_delta(scroll_delta, window, cx);
2632 cx.notify();
2633 }
2634
2635 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2636 self.columnar_selection_tail.take();
2637 if self.selections.pending_anchor().is_some() {
2638 let selections = self.selections.all::<usize>(cx);
2639 self.change_selections(None, window, cx, |s| {
2640 s.select(selections);
2641 s.clear_pending();
2642 });
2643 }
2644 }
2645
2646 fn select_columns(
2647 &mut self,
2648 tail: DisplayPoint,
2649 head: DisplayPoint,
2650 goal_column: u32,
2651 display_map: &DisplaySnapshot,
2652 window: &mut Window,
2653 cx: &mut Context<Self>,
2654 ) {
2655 let start_row = cmp::min(tail.row(), head.row());
2656 let end_row = cmp::max(tail.row(), head.row());
2657 let start_column = cmp::min(tail.column(), goal_column);
2658 let end_column = cmp::max(tail.column(), goal_column);
2659 let reversed = start_column < tail.column();
2660
2661 let selection_ranges = (start_row.0..=end_row.0)
2662 .map(DisplayRow)
2663 .filter_map(|row| {
2664 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2665 let start = display_map
2666 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2667 .to_point(display_map);
2668 let end = display_map
2669 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2670 .to_point(display_map);
2671 if reversed {
2672 Some(end..start)
2673 } else {
2674 Some(start..end)
2675 }
2676 } else {
2677 None
2678 }
2679 })
2680 .collect::<Vec<_>>();
2681
2682 self.change_selections(None, window, cx, |s| {
2683 s.select_ranges(selection_ranges);
2684 });
2685 cx.notify();
2686 }
2687
2688 pub fn has_pending_nonempty_selection(&self) -> bool {
2689 let pending_nonempty_selection = match self.selections.pending_anchor() {
2690 Some(Selection { start, end, .. }) => start != end,
2691 None => false,
2692 };
2693
2694 pending_nonempty_selection
2695 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2696 }
2697
2698 pub fn has_pending_selection(&self) -> bool {
2699 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2700 }
2701
2702 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2703 self.selection_mark_mode = false;
2704
2705 if self.clear_expanded_diff_hunks(cx) {
2706 cx.notify();
2707 return;
2708 }
2709 if self.dismiss_menus_and_popups(true, window, cx) {
2710 return;
2711 }
2712
2713 if self.mode == EditorMode::Full
2714 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2715 {
2716 return;
2717 }
2718
2719 cx.propagate();
2720 }
2721
2722 pub fn dismiss_menus_and_popups(
2723 &mut self,
2724 is_user_requested: bool,
2725 window: &mut Window,
2726 cx: &mut Context<Self>,
2727 ) -> bool {
2728 if self.take_rename(false, window, cx).is_some() {
2729 return true;
2730 }
2731
2732 if hide_hover(self, cx) {
2733 return true;
2734 }
2735
2736 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2737 return true;
2738 }
2739
2740 if self.hide_context_menu(window, cx).is_some() {
2741 return true;
2742 }
2743
2744 if self.mouse_context_menu.take().is_some() {
2745 return true;
2746 }
2747
2748 if is_user_requested && self.discard_inline_completion(true, cx) {
2749 return true;
2750 }
2751
2752 if self.snippet_stack.pop().is_some() {
2753 return true;
2754 }
2755
2756 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2757 self.dismiss_diagnostics(cx);
2758 return true;
2759 }
2760
2761 false
2762 }
2763
2764 fn linked_editing_ranges_for(
2765 &self,
2766 selection: Range<text::Anchor>,
2767 cx: &App,
2768 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2769 if self.linked_edit_ranges.is_empty() {
2770 return None;
2771 }
2772 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2773 selection.end.buffer_id.and_then(|end_buffer_id| {
2774 if selection.start.buffer_id != Some(end_buffer_id) {
2775 return None;
2776 }
2777 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2778 let snapshot = buffer.read(cx).snapshot();
2779 self.linked_edit_ranges
2780 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2781 .map(|ranges| (ranges, snapshot, buffer))
2782 })?;
2783 use text::ToOffset as TO;
2784 // find offset from the start of current range to current cursor position
2785 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2786
2787 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2788 let start_difference = start_offset - start_byte_offset;
2789 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2790 let end_difference = end_offset - start_byte_offset;
2791 // Current range has associated linked ranges.
2792 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2793 for range in linked_ranges.iter() {
2794 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2795 let end_offset = start_offset + end_difference;
2796 let start_offset = start_offset + start_difference;
2797 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2798 continue;
2799 }
2800 if self.selections.disjoint_anchor_ranges().any(|s| {
2801 if s.start.buffer_id != selection.start.buffer_id
2802 || s.end.buffer_id != selection.end.buffer_id
2803 {
2804 return false;
2805 }
2806 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2807 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2808 }) {
2809 continue;
2810 }
2811 let start = buffer_snapshot.anchor_after(start_offset);
2812 let end = buffer_snapshot.anchor_after(end_offset);
2813 linked_edits
2814 .entry(buffer.clone())
2815 .or_default()
2816 .push(start..end);
2817 }
2818 Some(linked_edits)
2819 }
2820
2821 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2822 let text: Arc<str> = text.into();
2823
2824 if self.read_only(cx) {
2825 return;
2826 }
2827
2828 let selections = self.selections.all_adjusted(cx);
2829 let mut bracket_inserted = false;
2830 let mut edits = Vec::new();
2831 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2832 let mut new_selections = Vec::with_capacity(selections.len());
2833 let mut new_autoclose_regions = Vec::new();
2834 let snapshot = self.buffer.read(cx).read(cx);
2835
2836 for (selection, autoclose_region) in
2837 self.selections_with_autoclose_regions(selections, &snapshot)
2838 {
2839 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2840 // Determine if the inserted text matches the opening or closing
2841 // bracket of any of this language's bracket pairs.
2842 let mut bracket_pair = None;
2843 let mut is_bracket_pair_start = false;
2844 let mut is_bracket_pair_end = false;
2845 if !text.is_empty() {
2846 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2847 // and they are removing the character that triggered IME popup.
2848 for (pair, enabled) in scope.brackets() {
2849 if !pair.close && !pair.surround {
2850 continue;
2851 }
2852
2853 if enabled && pair.start.ends_with(text.as_ref()) {
2854 let prefix_len = pair.start.len() - text.len();
2855 let preceding_text_matches_prefix = prefix_len == 0
2856 || (selection.start.column >= (prefix_len as u32)
2857 && snapshot.contains_str_at(
2858 Point::new(
2859 selection.start.row,
2860 selection.start.column - (prefix_len as u32),
2861 ),
2862 &pair.start[..prefix_len],
2863 ));
2864 if preceding_text_matches_prefix {
2865 bracket_pair = Some(pair.clone());
2866 is_bracket_pair_start = true;
2867 break;
2868 }
2869 }
2870 if pair.end.as_str() == text.as_ref() {
2871 bracket_pair = Some(pair.clone());
2872 is_bracket_pair_end = true;
2873 break;
2874 }
2875 }
2876 }
2877
2878 if let Some(bracket_pair) = bracket_pair {
2879 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2880 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2881 let auto_surround =
2882 self.use_auto_surround && snapshot_settings.use_auto_surround;
2883 if selection.is_empty() {
2884 if is_bracket_pair_start {
2885 // If the inserted text is a suffix of an opening bracket and the
2886 // selection is preceded by the rest of the opening bracket, then
2887 // insert the closing bracket.
2888 let following_text_allows_autoclose = snapshot
2889 .chars_at(selection.start)
2890 .next()
2891 .map_or(true, |c| scope.should_autoclose_before(c));
2892
2893 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2894 && bracket_pair.start.len() == 1
2895 {
2896 let target = bracket_pair.start.chars().next().unwrap();
2897 let current_line_count = snapshot
2898 .reversed_chars_at(selection.start)
2899 .take_while(|&c| c != '\n')
2900 .filter(|&c| c == target)
2901 .count();
2902 current_line_count % 2 == 1
2903 } else {
2904 false
2905 };
2906
2907 if autoclose
2908 && bracket_pair.close
2909 && following_text_allows_autoclose
2910 && !is_closing_quote
2911 {
2912 let anchor = snapshot.anchor_before(selection.end);
2913 new_selections.push((selection.map(|_| anchor), text.len()));
2914 new_autoclose_regions.push((
2915 anchor,
2916 text.len(),
2917 selection.id,
2918 bracket_pair.clone(),
2919 ));
2920 edits.push((
2921 selection.range(),
2922 format!("{}{}", text, bracket_pair.end).into(),
2923 ));
2924 bracket_inserted = true;
2925 continue;
2926 }
2927 }
2928
2929 if let Some(region) = autoclose_region {
2930 // If the selection is followed by an auto-inserted closing bracket,
2931 // then don't insert that closing bracket again; just move the selection
2932 // past the closing bracket.
2933 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2934 && text.as_ref() == region.pair.end.as_str();
2935 if should_skip {
2936 let anchor = snapshot.anchor_after(selection.end);
2937 new_selections
2938 .push((selection.map(|_| anchor), region.pair.end.len()));
2939 continue;
2940 }
2941 }
2942
2943 let always_treat_brackets_as_autoclosed = snapshot
2944 .settings_at(selection.start, cx)
2945 .always_treat_brackets_as_autoclosed;
2946 if always_treat_brackets_as_autoclosed
2947 && is_bracket_pair_end
2948 && snapshot.contains_str_at(selection.end, text.as_ref())
2949 {
2950 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2951 // and the inserted text is a closing bracket and the selection is followed
2952 // by the closing bracket then move the selection past the closing bracket.
2953 let anchor = snapshot.anchor_after(selection.end);
2954 new_selections.push((selection.map(|_| anchor), text.len()));
2955 continue;
2956 }
2957 }
2958 // If an opening bracket is 1 character long and is typed while
2959 // text is selected, then surround that text with the bracket pair.
2960 else if auto_surround
2961 && bracket_pair.surround
2962 && is_bracket_pair_start
2963 && bracket_pair.start.chars().count() == 1
2964 {
2965 edits.push((selection.start..selection.start, text.clone()));
2966 edits.push((
2967 selection.end..selection.end,
2968 bracket_pair.end.as_str().into(),
2969 ));
2970 bracket_inserted = true;
2971 new_selections.push((
2972 Selection {
2973 id: selection.id,
2974 start: snapshot.anchor_after(selection.start),
2975 end: snapshot.anchor_before(selection.end),
2976 reversed: selection.reversed,
2977 goal: selection.goal,
2978 },
2979 0,
2980 ));
2981 continue;
2982 }
2983 }
2984 }
2985
2986 if self.auto_replace_emoji_shortcode
2987 && selection.is_empty()
2988 && text.as_ref().ends_with(':')
2989 {
2990 if let Some(possible_emoji_short_code) =
2991 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2992 {
2993 if !possible_emoji_short_code.is_empty() {
2994 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2995 let emoji_shortcode_start = Point::new(
2996 selection.start.row,
2997 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2998 );
2999
3000 // Remove shortcode from buffer
3001 edits.push((
3002 emoji_shortcode_start..selection.start,
3003 "".to_string().into(),
3004 ));
3005 new_selections.push((
3006 Selection {
3007 id: selection.id,
3008 start: snapshot.anchor_after(emoji_shortcode_start),
3009 end: snapshot.anchor_before(selection.start),
3010 reversed: selection.reversed,
3011 goal: selection.goal,
3012 },
3013 0,
3014 ));
3015
3016 // Insert emoji
3017 let selection_start_anchor = snapshot.anchor_after(selection.start);
3018 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3019 edits.push((selection.start..selection.end, emoji.to_string().into()));
3020
3021 continue;
3022 }
3023 }
3024 }
3025 }
3026
3027 // If not handling any auto-close operation, then just replace the selected
3028 // text with the given input and move the selection to the end of the
3029 // newly inserted text.
3030 let anchor = snapshot.anchor_after(selection.end);
3031 if !self.linked_edit_ranges.is_empty() {
3032 let start_anchor = snapshot.anchor_before(selection.start);
3033
3034 let is_word_char = text.chars().next().map_or(true, |char| {
3035 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3036 classifier.is_word(char)
3037 });
3038
3039 if is_word_char {
3040 if let Some(ranges) = self
3041 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3042 {
3043 for (buffer, edits) in ranges {
3044 linked_edits
3045 .entry(buffer.clone())
3046 .or_default()
3047 .extend(edits.into_iter().map(|range| (range, text.clone())));
3048 }
3049 }
3050 }
3051 }
3052
3053 new_selections.push((selection.map(|_| anchor), 0));
3054 edits.push((selection.start..selection.end, text.clone()));
3055 }
3056
3057 drop(snapshot);
3058
3059 self.transact(window, cx, |this, window, cx| {
3060 this.buffer.update(cx, |buffer, cx| {
3061 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3062 });
3063 for (buffer, edits) in linked_edits {
3064 buffer.update(cx, |buffer, cx| {
3065 let snapshot = buffer.snapshot();
3066 let edits = edits
3067 .into_iter()
3068 .map(|(range, text)| {
3069 use text::ToPoint as TP;
3070 let end_point = TP::to_point(&range.end, &snapshot);
3071 let start_point = TP::to_point(&range.start, &snapshot);
3072 (start_point..end_point, text)
3073 })
3074 .sorted_by_key(|(range, _)| range.start)
3075 .collect::<Vec<_>>();
3076 buffer.edit(edits, None, cx);
3077 })
3078 }
3079 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3080 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3081 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3082 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3083 .zip(new_selection_deltas)
3084 .map(|(selection, delta)| Selection {
3085 id: selection.id,
3086 start: selection.start + delta,
3087 end: selection.end + delta,
3088 reversed: selection.reversed,
3089 goal: SelectionGoal::None,
3090 })
3091 .collect::<Vec<_>>();
3092
3093 let mut i = 0;
3094 for (position, delta, selection_id, pair) in new_autoclose_regions {
3095 let position = position.to_offset(&map.buffer_snapshot) + delta;
3096 let start = map.buffer_snapshot.anchor_before(position);
3097 let end = map.buffer_snapshot.anchor_after(position);
3098 while let Some(existing_state) = this.autoclose_regions.get(i) {
3099 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3100 Ordering::Less => i += 1,
3101 Ordering::Greater => break,
3102 Ordering::Equal => {
3103 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3104 Ordering::Less => i += 1,
3105 Ordering::Equal => break,
3106 Ordering::Greater => break,
3107 }
3108 }
3109 }
3110 }
3111 this.autoclose_regions.insert(
3112 i,
3113 AutocloseRegion {
3114 selection_id,
3115 range: start..end,
3116 pair,
3117 },
3118 );
3119 }
3120
3121 let had_active_inline_completion = this.has_active_inline_completion();
3122 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3123 s.select(new_selections)
3124 });
3125
3126 if !bracket_inserted {
3127 if let Some(on_type_format_task) =
3128 this.trigger_on_type_formatting(text.to_string(), window, cx)
3129 {
3130 on_type_format_task.detach_and_log_err(cx);
3131 }
3132 }
3133
3134 let editor_settings = EditorSettings::get_global(cx);
3135 if bracket_inserted
3136 && (editor_settings.auto_signature_help
3137 || editor_settings.show_signature_help_after_edits)
3138 {
3139 this.show_signature_help(&ShowSignatureHelp, window, cx);
3140 }
3141
3142 let trigger_in_words =
3143 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3144 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3145 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3146 this.refresh_inline_completion(true, false, window, cx);
3147 });
3148 }
3149
3150 fn find_possible_emoji_shortcode_at_position(
3151 snapshot: &MultiBufferSnapshot,
3152 position: Point,
3153 ) -> Option<String> {
3154 let mut chars = Vec::new();
3155 let mut found_colon = false;
3156 for char in snapshot.reversed_chars_at(position).take(100) {
3157 // Found a possible emoji shortcode in the middle of the buffer
3158 if found_colon {
3159 if char.is_whitespace() {
3160 chars.reverse();
3161 return Some(chars.iter().collect());
3162 }
3163 // If the previous character is not a whitespace, we are in the middle of a word
3164 // and we only want to complete the shortcode if the word is made up of other emojis
3165 let mut containing_word = String::new();
3166 for ch in snapshot
3167 .reversed_chars_at(position)
3168 .skip(chars.len() + 1)
3169 .take(100)
3170 {
3171 if ch.is_whitespace() {
3172 break;
3173 }
3174 containing_word.push(ch);
3175 }
3176 let containing_word = containing_word.chars().rev().collect::<String>();
3177 if util::word_consists_of_emojis(containing_word.as_str()) {
3178 chars.reverse();
3179 return Some(chars.iter().collect());
3180 }
3181 }
3182
3183 if char.is_whitespace() || !char.is_ascii() {
3184 return None;
3185 }
3186 if char == ':' {
3187 found_colon = true;
3188 } else {
3189 chars.push(char);
3190 }
3191 }
3192 // Found a possible emoji shortcode at the beginning of the buffer
3193 chars.reverse();
3194 Some(chars.iter().collect())
3195 }
3196
3197 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3198 self.transact(window, cx, |this, window, cx| {
3199 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3200 let selections = this.selections.all::<usize>(cx);
3201 let multi_buffer = this.buffer.read(cx);
3202 let buffer = multi_buffer.snapshot(cx);
3203 selections
3204 .iter()
3205 .map(|selection| {
3206 let start_point = selection.start.to_point(&buffer);
3207 let mut indent =
3208 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3209 indent.len = cmp::min(indent.len, start_point.column);
3210 let start = selection.start;
3211 let end = selection.end;
3212 let selection_is_empty = start == end;
3213 let language_scope = buffer.language_scope_at(start);
3214 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3215 &language_scope
3216 {
3217 let insert_extra_newline =
3218 insert_extra_newline_brackets(&buffer, start..end, language)
3219 || insert_extra_newline_tree_sitter(&buffer, start..end);
3220
3221 // Comment extension on newline is allowed only for cursor selections
3222 let comment_delimiter = maybe!({
3223 if !selection_is_empty {
3224 return None;
3225 }
3226
3227 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3228 return None;
3229 }
3230
3231 let delimiters = language.line_comment_prefixes();
3232 let max_len_of_delimiter =
3233 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3234 let (snapshot, range) =
3235 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3236
3237 let mut index_of_first_non_whitespace = 0;
3238 let comment_candidate = snapshot
3239 .chars_for_range(range)
3240 .skip_while(|c| {
3241 let should_skip = c.is_whitespace();
3242 if should_skip {
3243 index_of_first_non_whitespace += 1;
3244 }
3245 should_skip
3246 })
3247 .take(max_len_of_delimiter)
3248 .collect::<String>();
3249 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3250 comment_candidate.starts_with(comment_prefix.as_ref())
3251 })?;
3252 let cursor_is_placed_after_comment_marker =
3253 index_of_first_non_whitespace + comment_prefix.len()
3254 <= start_point.column as usize;
3255 if cursor_is_placed_after_comment_marker {
3256 Some(comment_prefix.clone())
3257 } else {
3258 None
3259 }
3260 });
3261 (comment_delimiter, insert_extra_newline)
3262 } else {
3263 (None, false)
3264 };
3265
3266 let capacity_for_delimiter = comment_delimiter
3267 .as_deref()
3268 .map(str::len)
3269 .unwrap_or_default();
3270 let mut new_text =
3271 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3272 new_text.push('\n');
3273 new_text.extend(indent.chars());
3274 if let Some(delimiter) = &comment_delimiter {
3275 new_text.push_str(delimiter);
3276 }
3277 if insert_extra_newline {
3278 new_text = new_text.repeat(2);
3279 }
3280
3281 let anchor = buffer.anchor_after(end);
3282 let new_selection = selection.map(|_| anchor);
3283 (
3284 (start..end, new_text),
3285 (insert_extra_newline, new_selection),
3286 )
3287 })
3288 .unzip()
3289 };
3290
3291 this.edit_with_autoindent(edits, cx);
3292 let buffer = this.buffer.read(cx).snapshot(cx);
3293 let new_selections = selection_fixup_info
3294 .into_iter()
3295 .map(|(extra_newline_inserted, new_selection)| {
3296 let mut cursor = new_selection.end.to_point(&buffer);
3297 if extra_newline_inserted {
3298 cursor.row -= 1;
3299 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3300 }
3301 new_selection.map(|_| cursor)
3302 })
3303 .collect();
3304
3305 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3306 s.select(new_selections)
3307 });
3308 this.refresh_inline_completion(true, false, window, cx);
3309 });
3310 }
3311
3312 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3313 let buffer = self.buffer.read(cx);
3314 let snapshot = buffer.snapshot(cx);
3315
3316 let mut edits = Vec::new();
3317 let mut rows = Vec::new();
3318
3319 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3320 let cursor = selection.head();
3321 let row = cursor.row;
3322
3323 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3324
3325 let newline = "\n".to_string();
3326 edits.push((start_of_line..start_of_line, newline));
3327
3328 rows.push(row + rows_inserted as u32);
3329 }
3330
3331 self.transact(window, cx, |editor, window, cx| {
3332 editor.edit(edits, cx);
3333
3334 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3335 let mut index = 0;
3336 s.move_cursors_with(|map, _, _| {
3337 let row = rows[index];
3338 index += 1;
3339
3340 let point = Point::new(row, 0);
3341 let boundary = map.next_line_boundary(point).1;
3342 let clipped = map.clip_point(boundary, Bias::Left);
3343
3344 (clipped, SelectionGoal::None)
3345 });
3346 });
3347
3348 let mut indent_edits = Vec::new();
3349 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3350 for row in rows {
3351 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3352 for (row, indent) in indents {
3353 if indent.len == 0 {
3354 continue;
3355 }
3356
3357 let text = match indent.kind {
3358 IndentKind::Space => " ".repeat(indent.len as usize),
3359 IndentKind::Tab => "\t".repeat(indent.len as usize),
3360 };
3361 let point = Point::new(row.0, 0);
3362 indent_edits.push((point..point, text));
3363 }
3364 }
3365 editor.edit(indent_edits, cx);
3366 });
3367 }
3368
3369 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3370 let buffer = self.buffer.read(cx);
3371 let snapshot = buffer.snapshot(cx);
3372
3373 let mut edits = Vec::new();
3374 let mut rows = Vec::new();
3375 let mut rows_inserted = 0;
3376
3377 for selection in self.selections.all_adjusted(cx) {
3378 let cursor = selection.head();
3379 let row = cursor.row;
3380
3381 let point = Point::new(row + 1, 0);
3382 let start_of_line = snapshot.clip_point(point, Bias::Left);
3383
3384 let newline = "\n".to_string();
3385 edits.push((start_of_line..start_of_line, newline));
3386
3387 rows_inserted += 1;
3388 rows.push(row + rows_inserted);
3389 }
3390
3391 self.transact(window, cx, |editor, window, cx| {
3392 editor.edit(edits, cx);
3393
3394 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3395 let mut index = 0;
3396 s.move_cursors_with(|map, _, _| {
3397 let row = rows[index];
3398 index += 1;
3399
3400 let point = Point::new(row, 0);
3401 let boundary = map.next_line_boundary(point).1;
3402 let clipped = map.clip_point(boundary, Bias::Left);
3403
3404 (clipped, SelectionGoal::None)
3405 });
3406 });
3407
3408 let mut indent_edits = Vec::new();
3409 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3410 for row in rows {
3411 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3412 for (row, indent) in indents {
3413 if indent.len == 0 {
3414 continue;
3415 }
3416
3417 let text = match indent.kind {
3418 IndentKind::Space => " ".repeat(indent.len as usize),
3419 IndentKind::Tab => "\t".repeat(indent.len as usize),
3420 };
3421 let point = Point::new(row.0, 0);
3422 indent_edits.push((point..point, text));
3423 }
3424 }
3425 editor.edit(indent_edits, cx);
3426 });
3427 }
3428
3429 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3430 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3431 original_start_columns: Vec::new(),
3432 });
3433 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3434 }
3435
3436 fn insert_with_autoindent_mode(
3437 &mut self,
3438 text: &str,
3439 autoindent_mode: Option<AutoindentMode>,
3440 window: &mut Window,
3441 cx: &mut Context<Self>,
3442 ) {
3443 if self.read_only(cx) {
3444 return;
3445 }
3446
3447 let text: Arc<str> = text.into();
3448 self.transact(window, cx, |this, window, cx| {
3449 let old_selections = this.selections.all_adjusted(cx);
3450 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3451 let anchors = {
3452 let snapshot = buffer.read(cx);
3453 old_selections
3454 .iter()
3455 .map(|s| {
3456 let anchor = snapshot.anchor_after(s.head());
3457 s.map(|_| anchor)
3458 })
3459 .collect::<Vec<_>>()
3460 };
3461 buffer.edit(
3462 old_selections
3463 .iter()
3464 .map(|s| (s.start..s.end, text.clone())),
3465 autoindent_mode,
3466 cx,
3467 );
3468 anchors
3469 });
3470
3471 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3472 s.select_anchors(selection_anchors);
3473 });
3474
3475 cx.notify();
3476 });
3477 }
3478
3479 fn trigger_completion_on_input(
3480 &mut self,
3481 text: &str,
3482 trigger_in_words: bool,
3483 window: &mut Window,
3484 cx: &mut Context<Self>,
3485 ) {
3486 if self.is_completion_trigger(text, trigger_in_words, cx) {
3487 self.show_completions(
3488 &ShowCompletions {
3489 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3490 },
3491 window,
3492 cx,
3493 );
3494 } else {
3495 self.hide_context_menu(window, cx);
3496 }
3497 }
3498
3499 fn is_completion_trigger(
3500 &self,
3501 text: &str,
3502 trigger_in_words: bool,
3503 cx: &mut Context<Self>,
3504 ) -> bool {
3505 let position = self.selections.newest_anchor().head();
3506 let multibuffer = self.buffer.read(cx);
3507 let Some(buffer) = position
3508 .buffer_id
3509 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3510 else {
3511 return false;
3512 };
3513
3514 if let Some(completion_provider) = &self.completion_provider {
3515 completion_provider.is_completion_trigger(
3516 &buffer,
3517 position.text_anchor,
3518 text,
3519 trigger_in_words,
3520 cx,
3521 )
3522 } else {
3523 false
3524 }
3525 }
3526
3527 /// If any empty selections is touching the start of its innermost containing autoclose
3528 /// region, expand it to select the brackets.
3529 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3530 let selections = self.selections.all::<usize>(cx);
3531 let buffer = self.buffer.read(cx).read(cx);
3532 let new_selections = self
3533 .selections_with_autoclose_regions(selections, &buffer)
3534 .map(|(mut selection, region)| {
3535 if !selection.is_empty() {
3536 return selection;
3537 }
3538
3539 if let Some(region) = region {
3540 let mut range = region.range.to_offset(&buffer);
3541 if selection.start == range.start && range.start >= region.pair.start.len() {
3542 range.start -= region.pair.start.len();
3543 if buffer.contains_str_at(range.start, ®ion.pair.start)
3544 && buffer.contains_str_at(range.end, ®ion.pair.end)
3545 {
3546 range.end += region.pair.end.len();
3547 selection.start = range.start;
3548 selection.end = range.end;
3549
3550 return selection;
3551 }
3552 }
3553 }
3554
3555 let always_treat_brackets_as_autoclosed = buffer
3556 .settings_at(selection.start, cx)
3557 .always_treat_brackets_as_autoclosed;
3558
3559 if !always_treat_brackets_as_autoclosed {
3560 return selection;
3561 }
3562
3563 if let Some(scope) = buffer.language_scope_at(selection.start) {
3564 for (pair, enabled) in scope.brackets() {
3565 if !enabled || !pair.close {
3566 continue;
3567 }
3568
3569 if buffer.contains_str_at(selection.start, &pair.end) {
3570 let pair_start_len = pair.start.len();
3571 if buffer.contains_str_at(
3572 selection.start.saturating_sub(pair_start_len),
3573 &pair.start,
3574 ) {
3575 selection.start -= pair_start_len;
3576 selection.end += pair.end.len();
3577
3578 return selection;
3579 }
3580 }
3581 }
3582 }
3583
3584 selection
3585 })
3586 .collect();
3587
3588 drop(buffer);
3589 self.change_selections(None, window, cx, |selections| {
3590 selections.select(new_selections)
3591 });
3592 }
3593
3594 /// Iterate the given selections, and for each one, find the smallest surrounding
3595 /// autoclose region. This uses the ordering of the selections and the autoclose
3596 /// regions to avoid repeated comparisons.
3597 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3598 &'a self,
3599 selections: impl IntoIterator<Item = Selection<D>>,
3600 buffer: &'a MultiBufferSnapshot,
3601 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3602 let mut i = 0;
3603 let mut regions = self.autoclose_regions.as_slice();
3604 selections.into_iter().map(move |selection| {
3605 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3606
3607 let mut enclosing = None;
3608 while let Some(pair_state) = regions.get(i) {
3609 if pair_state.range.end.to_offset(buffer) < range.start {
3610 regions = ®ions[i + 1..];
3611 i = 0;
3612 } else if pair_state.range.start.to_offset(buffer) > range.end {
3613 break;
3614 } else {
3615 if pair_state.selection_id == selection.id {
3616 enclosing = Some(pair_state);
3617 }
3618 i += 1;
3619 }
3620 }
3621
3622 (selection, enclosing)
3623 })
3624 }
3625
3626 /// Remove any autoclose regions that no longer contain their selection.
3627 fn invalidate_autoclose_regions(
3628 &mut self,
3629 mut selections: &[Selection<Anchor>],
3630 buffer: &MultiBufferSnapshot,
3631 ) {
3632 self.autoclose_regions.retain(|state| {
3633 let mut i = 0;
3634 while let Some(selection) = selections.get(i) {
3635 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3636 selections = &selections[1..];
3637 continue;
3638 }
3639 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3640 break;
3641 }
3642 if selection.id == state.selection_id {
3643 return true;
3644 } else {
3645 i += 1;
3646 }
3647 }
3648 false
3649 });
3650 }
3651
3652 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3653 let offset = position.to_offset(buffer);
3654 let (word_range, kind) = buffer.surrounding_word(offset, true);
3655 if offset > word_range.start && kind == Some(CharKind::Word) {
3656 Some(
3657 buffer
3658 .text_for_range(word_range.start..offset)
3659 .collect::<String>(),
3660 )
3661 } else {
3662 None
3663 }
3664 }
3665
3666 pub fn toggle_inlay_hints(
3667 &mut self,
3668 _: &ToggleInlayHints,
3669 _: &mut Window,
3670 cx: &mut Context<Self>,
3671 ) {
3672 self.refresh_inlay_hints(
3673 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3674 cx,
3675 );
3676 }
3677
3678 pub fn inlay_hints_enabled(&self) -> bool {
3679 self.inlay_hint_cache.enabled
3680 }
3681
3682 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3683 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3684 return;
3685 }
3686
3687 let reason_description = reason.description();
3688 let ignore_debounce = matches!(
3689 reason,
3690 InlayHintRefreshReason::SettingsChange(_)
3691 | InlayHintRefreshReason::Toggle(_)
3692 | InlayHintRefreshReason::ExcerptsRemoved(_)
3693 );
3694 let (invalidate_cache, required_languages) = match reason {
3695 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3696 match self.inlay_hint_cache.modifiers_override(enabled) {
3697 Some(enabled) => {
3698 if enabled {
3699 (InvalidationStrategy::RefreshRequested, None)
3700 } else {
3701 self.splice_inlays(
3702 &self
3703 .visible_inlay_hints(cx)
3704 .iter()
3705 .map(|inlay| inlay.id)
3706 .collect::<Vec<InlayId>>(),
3707 Vec::new(),
3708 cx,
3709 );
3710 return;
3711 }
3712 }
3713 None => return,
3714 }
3715 }
3716 InlayHintRefreshReason::Toggle(enabled) => {
3717 if self.inlay_hint_cache.toggle(enabled) {
3718 if enabled {
3719 (InvalidationStrategy::RefreshRequested, None)
3720 } else {
3721 self.splice_inlays(
3722 &self
3723 .visible_inlay_hints(cx)
3724 .iter()
3725 .map(|inlay| inlay.id)
3726 .collect::<Vec<InlayId>>(),
3727 Vec::new(),
3728 cx,
3729 );
3730 return;
3731 }
3732 } else {
3733 return;
3734 }
3735 }
3736 InlayHintRefreshReason::SettingsChange(new_settings) => {
3737 match self.inlay_hint_cache.update_settings(
3738 &self.buffer,
3739 new_settings,
3740 self.visible_inlay_hints(cx),
3741 cx,
3742 ) {
3743 ControlFlow::Break(Some(InlaySplice {
3744 to_remove,
3745 to_insert,
3746 })) => {
3747 self.splice_inlays(&to_remove, to_insert, cx);
3748 return;
3749 }
3750 ControlFlow::Break(None) => return,
3751 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3752 }
3753 }
3754 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3755 if let Some(InlaySplice {
3756 to_remove,
3757 to_insert,
3758 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3759 {
3760 self.splice_inlays(&to_remove, to_insert, cx);
3761 }
3762 return;
3763 }
3764 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3765 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3766 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3767 }
3768 InlayHintRefreshReason::RefreshRequested => {
3769 (InvalidationStrategy::RefreshRequested, None)
3770 }
3771 };
3772
3773 if let Some(InlaySplice {
3774 to_remove,
3775 to_insert,
3776 }) = self.inlay_hint_cache.spawn_hint_refresh(
3777 reason_description,
3778 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3779 invalidate_cache,
3780 ignore_debounce,
3781 cx,
3782 ) {
3783 self.splice_inlays(&to_remove, to_insert, cx);
3784 }
3785 }
3786
3787 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3788 self.display_map
3789 .read(cx)
3790 .current_inlays()
3791 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3792 .cloned()
3793 .collect()
3794 }
3795
3796 pub fn excerpts_for_inlay_hints_query(
3797 &self,
3798 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3799 cx: &mut Context<Editor>,
3800 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3801 let Some(project) = self.project.as_ref() else {
3802 return HashMap::default();
3803 };
3804 let project = project.read(cx);
3805 let multi_buffer = self.buffer().read(cx);
3806 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3807 let multi_buffer_visible_start = self
3808 .scroll_manager
3809 .anchor()
3810 .anchor
3811 .to_point(&multi_buffer_snapshot);
3812 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3813 multi_buffer_visible_start
3814 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3815 Bias::Left,
3816 );
3817 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3818 multi_buffer_snapshot
3819 .range_to_buffer_ranges(multi_buffer_visible_range)
3820 .into_iter()
3821 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3822 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3823 let buffer_file = project::File::from_dyn(buffer.file())?;
3824 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3825 let worktree_entry = buffer_worktree
3826 .read(cx)
3827 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3828 if worktree_entry.is_ignored {
3829 return None;
3830 }
3831
3832 let language = buffer.language()?;
3833 if let Some(restrict_to_languages) = restrict_to_languages {
3834 if !restrict_to_languages.contains(language) {
3835 return None;
3836 }
3837 }
3838 Some((
3839 excerpt_id,
3840 (
3841 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3842 buffer.version().clone(),
3843 excerpt_visible_range,
3844 ),
3845 ))
3846 })
3847 .collect()
3848 }
3849
3850 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3851 TextLayoutDetails {
3852 text_system: window.text_system().clone(),
3853 editor_style: self.style.clone().unwrap(),
3854 rem_size: window.rem_size(),
3855 scroll_anchor: self.scroll_manager.anchor(),
3856 visible_rows: self.visible_line_count(),
3857 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3858 }
3859 }
3860
3861 pub fn splice_inlays(
3862 &self,
3863 to_remove: &[InlayId],
3864 to_insert: Vec<Inlay>,
3865 cx: &mut Context<Self>,
3866 ) {
3867 self.display_map.update(cx, |display_map, cx| {
3868 display_map.splice_inlays(to_remove, to_insert, cx)
3869 });
3870 cx.notify();
3871 }
3872
3873 fn trigger_on_type_formatting(
3874 &self,
3875 input: String,
3876 window: &mut Window,
3877 cx: &mut Context<Self>,
3878 ) -> Option<Task<Result<()>>> {
3879 if input.len() != 1 {
3880 return None;
3881 }
3882
3883 let project = self.project.as_ref()?;
3884 let position = self.selections.newest_anchor().head();
3885 let (buffer, buffer_position) = self
3886 .buffer
3887 .read(cx)
3888 .text_anchor_for_position(position, cx)?;
3889
3890 let settings = language_settings::language_settings(
3891 buffer
3892 .read(cx)
3893 .language_at(buffer_position)
3894 .map(|l| l.name()),
3895 buffer.read(cx).file(),
3896 cx,
3897 );
3898 if !settings.use_on_type_format {
3899 return None;
3900 }
3901
3902 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3903 // hence we do LSP request & edit on host side only — add formats to host's history.
3904 let push_to_lsp_host_history = true;
3905 // If this is not the host, append its history with new edits.
3906 let push_to_client_history = project.read(cx).is_via_collab();
3907
3908 let on_type_formatting = project.update(cx, |project, cx| {
3909 project.on_type_format(
3910 buffer.clone(),
3911 buffer_position,
3912 input,
3913 push_to_lsp_host_history,
3914 cx,
3915 )
3916 });
3917 Some(cx.spawn_in(window, |editor, mut cx| async move {
3918 if let Some(transaction) = on_type_formatting.await? {
3919 if push_to_client_history {
3920 buffer
3921 .update(&mut cx, |buffer, _| {
3922 buffer.push_transaction(transaction, Instant::now());
3923 })
3924 .ok();
3925 }
3926 editor.update(&mut cx, |editor, cx| {
3927 editor.refresh_document_highlights(cx);
3928 })?;
3929 }
3930 Ok(())
3931 }))
3932 }
3933
3934 pub fn show_completions(
3935 &mut self,
3936 options: &ShowCompletions,
3937 window: &mut Window,
3938 cx: &mut Context<Self>,
3939 ) {
3940 if self.pending_rename.is_some() {
3941 return;
3942 }
3943
3944 let Some(provider) = self.completion_provider.as_ref() else {
3945 return;
3946 };
3947
3948 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3949 return;
3950 }
3951
3952 let position = self.selections.newest_anchor().head();
3953 if position.diff_base_anchor.is_some() {
3954 return;
3955 }
3956 let (buffer, buffer_position) =
3957 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3958 output
3959 } else {
3960 return;
3961 };
3962 let show_completion_documentation = buffer
3963 .read(cx)
3964 .snapshot()
3965 .settings_at(buffer_position, cx)
3966 .show_completion_documentation;
3967
3968 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3969
3970 let trigger_kind = match &options.trigger {
3971 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3972 CompletionTriggerKind::TRIGGER_CHARACTER
3973 }
3974 _ => CompletionTriggerKind::INVOKED,
3975 };
3976 let completion_context = CompletionContext {
3977 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3978 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3979 Some(String::from(trigger))
3980 } else {
3981 None
3982 }
3983 }),
3984 trigger_kind,
3985 };
3986 let completions =
3987 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3988 let sort_completions = provider.sort_completions();
3989
3990 let id = post_inc(&mut self.next_completion_id);
3991 let task = cx.spawn_in(window, |editor, mut cx| {
3992 async move {
3993 editor.update(&mut cx, |this, _| {
3994 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3995 })?;
3996 let completions = completions.await.log_err();
3997 let menu = if let Some(completions) = completions {
3998 let mut menu = CompletionsMenu::new(
3999 id,
4000 sort_completions,
4001 show_completion_documentation,
4002 position,
4003 buffer.clone(),
4004 completions.into(),
4005 );
4006
4007 menu.filter(query.as_deref(), cx.background_executor().clone())
4008 .await;
4009
4010 menu.visible().then_some(menu)
4011 } else {
4012 None
4013 };
4014
4015 editor.update_in(&mut cx, |editor, window, cx| {
4016 match editor.context_menu.borrow().as_ref() {
4017 None => {}
4018 Some(CodeContextMenu::Completions(prev_menu)) => {
4019 if prev_menu.id > id {
4020 return;
4021 }
4022 }
4023 _ => return,
4024 }
4025
4026 if editor.focus_handle.is_focused(window) && menu.is_some() {
4027 let mut menu = menu.unwrap();
4028 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4029
4030 *editor.context_menu.borrow_mut() =
4031 Some(CodeContextMenu::Completions(menu));
4032
4033 if editor.show_edit_predictions_in_menu() {
4034 editor.update_visible_inline_completion(window, cx);
4035 } else {
4036 editor.discard_inline_completion(false, cx);
4037 }
4038
4039 cx.notify();
4040 } else if editor.completion_tasks.len() <= 1 {
4041 // If there are no more completion tasks and the last menu was
4042 // empty, we should hide it.
4043 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4044 // If it was already hidden and we don't show inline
4045 // completions in the menu, we should also show the
4046 // inline-completion when available.
4047 if was_hidden && editor.show_edit_predictions_in_menu() {
4048 editor.update_visible_inline_completion(window, cx);
4049 }
4050 }
4051 })?;
4052
4053 Ok::<_, anyhow::Error>(())
4054 }
4055 .log_err()
4056 });
4057
4058 self.completion_tasks.push((id, task));
4059 }
4060
4061 pub fn confirm_completion(
4062 &mut self,
4063 action: &ConfirmCompletion,
4064 window: &mut Window,
4065 cx: &mut Context<Self>,
4066 ) -> Option<Task<Result<()>>> {
4067 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4068 }
4069
4070 pub fn compose_completion(
4071 &mut self,
4072 action: &ComposeCompletion,
4073 window: &mut Window,
4074 cx: &mut Context<Self>,
4075 ) -> Option<Task<Result<()>>> {
4076 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4077 }
4078
4079 fn do_completion(
4080 &mut self,
4081 item_ix: Option<usize>,
4082 intent: CompletionIntent,
4083 window: &mut Window,
4084 cx: &mut Context<Editor>,
4085 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4086 use language::ToOffset as _;
4087
4088 let completions_menu =
4089 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4090 menu
4091 } else {
4092 return None;
4093 };
4094
4095 let entries = completions_menu.entries.borrow();
4096 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4097 if self.show_edit_predictions_in_menu() {
4098 self.discard_inline_completion(true, cx);
4099 }
4100 let candidate_id = mat.candidate_id;
4101 drop(entries);
4102
4103 let buffer_handle = completions_menu.buffer;
4104 let completion = completions_menu
4105 .completions
4106 .borrow()
4107 .get(candidate_id)?
4108 .clone();
4109 cx.stop_propagation();
4110
4111 let snippet;
4112 let text;
4113
4114 if completion.is_snippet() {
4115 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4116 text = snippet.as_ref().unwrap().text.clone();
4117 } else {
4118 snippet = None;
4119 text = completion.new_text.clone();
4120 };
4121 let selections = self.selections.all::<usize>(cx);
4122 let buffer = buffer_handle.read(cx);
4123 let old_range = completion.old_range.to_offset(buffer);
4124 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4125
4126 let newest_selection = self.selections.newest_anchor();
4127 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4128 return None;
4129 }
4130
4131 let lookbehind = newest_selection
4132 .start
4133 .text_anchor
4134 .to_offset(buffer)
4135 .saturating_sub(old_range.start);
4136 let lookahead = old_range
4137 .end
4138 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4139 let mut common_prefix_len = old_text
4140 .bytes()
4141 .zip(text.bytes())
4142 .take_while(|(a, b)| a == b)
4143 .count();
4144
4145 let snapshot = self.buffer.read(cx).snapshot(cx);
4146 let mut range_to_replace: Option<Range<isize>> = None;
4147 let mut ranges = Vec::new();
4148 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4149 for selection in &selections {
4150 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4151 let start = selection.start.saturating_sub(lookbehind);
4152 let end = selection.end + lookahead;
4153 if selection.id == newest_selection.id {
4154 range_to_replace = Some(
4155 ((start + common_prefix_len) as isize - selection.start as isize)
4156 ..(end as isize - selection.start as isize),
4157 );
4158 }
4159 ranges.push(start + common_prefix_len..end);
4160 } else {
4161 common_prefix_len = 0;
4162 ranges.clear();
4163 ranges.extend(selections.iter().map(|s| {
4164 if s.id == newest_selection.id {
4165 range_to_replace = Some(
4166 old_range.start.to_offset_utf16(&snapshot).0 as isize
4167 - selection.start as isize
4168 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4169 - selection.start as isize,
4170 );
4171 old_range.clone()
4172 } else {
4173 s.start..s.end
4174 }
4175 }));
4176 break;
4177 }
4178 if !self.linked_edit_ranges.is_empty() {
4179 let start_anchor = snapshot.anchor_before(selection.head());
4180 let end_anchor = snapshot.anchor_after(selection.tail());
4181 if let Some(ranges) = self
4182 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4183 {
4184 for (buffer, edits) in ranges {
4185 linked_edits.entry(buffer.clone()).or_default().extend(
4186 edits
4187 .into_iter()
4188 .map(|range| (range, text[common_prefix_len..].to_owned())),
4189 );
4190 }
4191 }
4192 }
4193 }
4194 let text = &text[common_prefix_len..];
4195
4196 cx.emit(EditorEvent::InputHandled {
4197 utf16_range_to_replace: range_to_replace,
4198 text: text.into(),
4199 });
4200
4201 self.transact(window, cx, |this, window, cx| {
4202 if let Some(mut snippet) = snippet {
4203 snippet.text = text.to_string();
4204 for tabstop in snippet
4205 .tabstops
4206 .iter_mut()
4207 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4208 {
4209 tabstop.start -= common_prefix_len as isize;
4210 tabstop.end -= common_prefix_len as isize;
4211 }
4212
4213 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4214 } else {
4215 this.buffer.update(cx, |buffer, cx| {
4216 buffer.edit(
4217 ranges.iter().map(|range| (range.clone(), text)),
4218 this.autoindent_mode.clone(),
4219 cx,
4220 );
4221 });
4222 }
4223 for (buffer, edits) in linked_edits {
4224 buffer.update(cx, |buffer, cx| {
4225 let snapshot = buffer.snapshot();
4226 let edits = edits
4227 .into_iter()
4228 .map(|(range, text)| {
4229 use text::ToPoint as TP;
4230 let end_point = TP::to_point(&range.end, &snapshot);
4231 let start_point = TP::to_point(&range.start, &snapshot);
4232 (start_point..end_point, text)
4233 })
4234 .sorted_by_key(|(range, _)| range.start)
4235 .collect::<Vec<_>>();
4236 buffer.edit(edits, None, cx);
4237 })
4238 }
4239
4240 this.refresh_inline_completion(true, false, window, cx);
4241 });
4242
4243 let show_new_completions_on_confirm = completion
4244 .confirm
4245 .as_ref()
4246 .map_or(false, |confirm| confirm(intent, window, cx));
4247 if show_new_completions_on_confirm {
4248 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4249 }
4250
4251 let provider = self.completion_provider.as_ref()?;
4252 drop(completion);
4253 let apply_edits = provider.apply_additional_edits_for_completion(
4254 buffer_handle,
4255 completions_menu.completions.clone(),
4256 candidate_id,
4257 true,
4258 cx,
4259 );
4260
4261 let editor_settings = EditorSettings::get_global(cx);
4262 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4263 // After the code completion is finished, users often want to know what signatures are needed.
4264 // so we should automatically call signature_help
4265 self.show_signature_help(&ShowSignatureHelp, window, cx);
4266 }
4267
4268 Some(cx.foreground_executor().spawn(async move {
4269 apply_edits.await?;
4270 Ok(())
4271 }))
4272 }
4273
4274 pub fn toggle_code_actions(
4275 &mut self,
4276 action: &ToggleCodeActions,
4277 window: &mut Window,
4278 cx: &mut Context<Self>,
4279 ) {
4280 let mut context_menu = self.context_menu.borrow_mut();
4281 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4282 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4283 // Toggle if we're selecting the same one
4284 *context_menu = None;
4285 cx.notify();
4286 return;
4287 } else {
4288 // Otherwise, clear it and start a new one
4289 *context_menu = None;
4290 cx.notify();
4291 }
4292 }
4293 drop(context_menu);
4294 let snapshot = self.snapshot(window, cx);
4295 let deployed_from_indicator = action.deployed_from_indicator;
4296 let mut task = self.code_actions_task.take();
4297 let action = action.clone();
4298 cx.spawn_in(window, |editor, mut cx| async move {
4299 while let Some(prev_task) = task {
4300 prev_task.await.log_err();
4301 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4302 }
4303
4304 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4305 if editor.focus_handle.is_focused(window) {
4306 let multibuffer_point = action
4307 .deployed_from_indicator
4308 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4309 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4310 let (buffer, buffer_row) = snapshot
4311 .buffer_snapshot
4312 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4313 .and_then(|(buffer_snapshot, range)| {
4314 editor
4315 .buffer
4316 .read(cx)
4317 .buffer(buffer_snapshot.remote_id())
4318 .map(|buffer| (buffer, range.start.row))
4319 })?;
4320 let (_, code_actions) = editor
4321 .available_code_actions
4322 .clone()
4323 .and_then(|(location, code_actions)| {
4324 let snapshot = location.buffer.read(cx).snapshot();
4325 let point_range = location.range.to_point(&snapshot);
4326 let point_range = point_range.start.row..=point_range.end.row;
4327 if point_range.contains(&buffer_row) {
4328 Some((location, code_actions))
4329 } else {
4330 None
4331 }
4332 })
4333 .unzip();
4334 let buffer_id = buffer.read(cx).remote_id();
4335 let tasks = editor
4336 .tasks
4337 .get(&(buffer_id, buffer_row))
4338 .map(|t| Arc::new(t.to_owned()));
4339 if tasks.is_none() && code_actions.is_none() {
4340 return None;
4341 }
4342
4343 editor.completion_tasks.clear();
4344 editor.discard_inline_completion(false, cx);
4345 let task_context =
4346 tasks
4347 .as_ref()
4348 .zip(editor.project.clone())
4349 .map(|(tasks, project)| {
4350 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4351 });
4352
4353 Some(cx.spawn_in(window, |editor, mut cx| async move {
4354 let task_context = match task_context {
4355 Some(task_context) => task_context.await,
4356 None => None,
4357 };
4358 let resolved_tasks =
4359 tasks.zip(task_context).map(|(tasks, task_context)| {
4360 Rc::new(ResolvedTasks {
4361 templates: tasks.resolve(&task_context).collect(),
4362 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4363 multibuffer_point.row,
4364 tasks.column,
4365 )),
4366 })
4367 });
4368 let spawn_straight_away = resolved_tasks
4369 .as_ref()
4370 .map_or(false, |tasks| tasks.templates.len() == 1)
4371 && code_actions
4372 .as_ref()
4373 .map_or(true, |actions| actions.is_empty());
4374 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4375 *editor.context_menu.borrow_mut() =
4376 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4377 buffer,
4378 actions: CodeActionContents {
4379 tasks: resolved_tasks,
4380 actions: code_actions,
4381 },
4382 selected_item: Default::default(),
4383 scroll_handle: UniformListScrollHandle::default(),
4384 deployed_from_indicator,
4385 }));
4386 if spawn_straight_away {
4387 if let Some(task) = editor.confirm_code_action(
4388 &ConfirmCodeAction { item_ix: Some(0) },
4389 window,
4390 cx,
4391 ) {
4392 cx.notify();
4393 return task;
4394 }
4395 }
4396 cx.notify();
4397 Task::ready(Ok(()))
4398 }) {
4399 task.await
4400 } else {
4401 Ok(())
4402 }
4403 }))
4404 } else {
4405 Some(Task::ready(Ok(())))
4406 }
4407 })?;
4408 if let Some(task) = spawned_test_task {
4409 task.await?;
4410 }
4411
4412 Ok::<_, anyhow::Error>(())
4413 })
4414 .detach_and_log_err(cx);
4415 }
4416
4417 pub fn confirm_code_action(
4418 &mut self,
4419 action: &ConfirmCodeAction,
4420 window: &mut Window,
4421 cx: &mut Context<Self>,
4422 ) -> Option<Task<Result<()>>> {
4423 let actions_menu =
4424 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4425 menu
4426 } else {
4427 return None;
4428 };
4429 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4430 let action = actions_menu.actions.get(action_ix)?;
4431 let title = action.label();
4432 let buffer = actions_menu.buffer;
4433 let workspace = self.workspace()?;
4434
4435 match action {
4436 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4437 workspace.update(cx, |workspace, cx| {
4438 workspace::tasks::schedule_resolved_task(
4439 workspace,
4440 task_source_kind,
4441 resolved_task,
4442 false,
4443 cx,
4444 );
4445
4446 Some(Task::ready(Ok(())))
4447 })
4448 }
4449 CodeActionsItem::CodeAction {
4450 excerpt_id,
4451 action,
4452 provider,
4453 } => {
4454 let apply_code_action =
4455 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4456 let workspace = workspace.downgrade();
4457 Some(cx.spawn_in(window, |editor, cx| async move {
4458 let project_transaction = apply_code_action.await?;
4459 Self::open_project_transaction(
4460 &editor,
4461 workspace,
4462 project_transaction,
4463 title,
4464 cx,
4465 )
4466 .await
4467 }))
4468 }
4469 }
4470 }
4471
4472 pub async fn open_project_transaction(
4473 this: &WeakEntity<Editor>,
4474 workspace: WeakEntity<Workspace>,
4475 transaction: ProjectTransaction,
4476 title: String,
4477 mut cx: AsyncWindowContext,
4478 ) -> Result<()> {
4479 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4480 cx.update(|_, cx| {
4481 entries.sort_unstable_by_key(|(buffer, _)| {
4482 buffer.read(cx).file().map(|f| f.path().clone())
4483 });
4484 })?;
4485
4486 // If the project transaction's edits are all contained within this editor, then
4487 // avoid opening a new editor to display them.
4488
4489 if let Some((buffer, transaction)) = entries.first() {
4490 if entries.len() == 1 {
4491 let excerpt = this.update(&mut cx, |editor, cx| {
4492 editor
4493 .buffer()
4494 .read(cx)
4495 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4496 })?;
4497 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4498 if excerpted_buffer == *buffer {
4499 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4500 let excerpt_range = excerpt_range.to_offset(buffer);
4501 buffer
4502 .edited_ranges_for_transaction::<usize>(transaction)
4503 .all(|range| {
4504 excerpt_range.start <= range.start
4505 && excerpt_range.end >= range.end
4506 })
4507 })?;
4508
4509 if all_edits_within_excerpt {
4510 return Ok(());
4511 }
4512 }
4513 }
4514 }
4515 } else {
4516 return Ok(());
4517 }
4518
4519 let mut ranges_to_highlight = Vec::new();
4520 let excerpt_buffer = cx.new(|cx| {
4521 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4522 for (buffer_handle, transaction) in &entries {
4523 let buffer = buffer_handle.read(cx);
4524 ranges_to_highlight.extend(
4525 multibuffer.push_excerpts_with_context_lines(
4526 buffer_handle.clone(),
4527 buffer
4528 .edited_ranges_for_transaction::<usize>(transaction)
4529 .collect(),
4530 DEFAULT_MULTIBUFFER_CONTEXT,
4531 cx,
4532 ),
4533 );
4534 }
4535 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4536 multibuffer
4537 })?;
4538
4539 workspace.update_in(&mut cx, |workspace, window, cx| {
4540 let project = workspace.project().clone();
4541 let editor = cx
4542 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4543 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4544 editor.update(cx, |editor, cx| {
4545 editor.highlight_background::<Self>(
4546 &ranges_to_highlight,
4547 |theme| theme.editor_highlighted_line_background,
4548 cx,
4549 );
4550 });
4551 })?;
4552
4553 Ok(())
4554 }
4555
4556 pub fn clear_code_action_providers(&mut self) {
4557 self.code_action_providers.clear();
4558 self.available_code_actions.take();
4559 }
4560
4561 pub fn add_code_action_provider(
4562 &mut self,
4563 provider: Rc<dyn CodeActionProvider>,
4564 window: &mut Window,
4565 cx: &mut Context<Self>,
4566 ) {
4567 if self
4568 .code_action_providers
4569 .iter()
4570 .any(|existing_provider| existing_provider.id() == provider.id())
4571 {
4572 return;
4573 }
4574
4575 self.code_action_providers.push(provider);
4576 self.refresh_code_actions(window, cx);
4577 }
4578
4579 pub fn remove_code_action_provider(
4580 &mut self,
4581 id: Arc<str>,
4582 window: &mut Window,
4583 cx: &mut Context<Self>,
4584 ) {
4585 self.code_action_providers
4586 .retain(|provider| provider.id() != id);
4587 self.refresh_code_actions(window, cx);
4588 }
4589
4590 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4591 let buffer = self.buffer.read(cx);
4592 let newest_selection = self.selections.newest_anchor().clone();
4593 if newest_selection.head().diff_base_anchor.is_some() {
4594 return None;
4595 }
4596 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4597 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4598 if start_buffer != end_buffer {
4599 return None;
4600 }
4601
4602 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4603 cx.background_executor()
4604 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4605 .await;
4606
4607 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4608 let providers = this.code_action_providers.clone();
4609 let tasks = this
4610 .code_action_providers
4611 .iter()
4612 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4613 .collect::<Vec<_>>();
4614 (providers, tasks)
4615 })?;
4616
4617 let mut actions = Vec::new();
4618 for (provider, provider_actions) in
4619 providers.into_iter().zip(future::join_all(tasks).await)
4620 {
4621 if let Some(provider_actions) = provider_actions.log_err() {
4622 actions.extend(provider_actions.into_iter().map(|action| {
4623 AvailableCodeAction {
4624 excerpt_id: newest_selection.start.excerpt_id,
4625 action,
4626 provider: provider.clone(),
4627 }
4628 }));
4629 }
4630 }
4631
4632 this.update(&mut cx, |this, cx| {
4633 this.available_code_actions = if actions.is_empty() {
4634 None
4635 } else {
4636 Some((
4637 Location {
4638 buffer: start_buffer,
4639 range: start..end,
4640 },
4641 actions.into(),
4642 ))
4643 };
4644 cx.notify();
4645 })
4646 }));
4647 None
4648 }
4649
4650 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4651 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4652 self.show_git_blame_inline = false;
4653
4654 self.show_git_blame_inline_delay_task =
4655 Some(cx.spawn_in(window, |this, mut cx| async move {
4656 cx.background_executor().timer(delay).await;
4657
4658 this.update(&mut cx, |this, cx| {
4659 this.show_git_blame_inline = true;
4660 cx.notify();
4661 })
4662 .log_err();
4663 }));
4664 }
4665 }
4666
4667 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4668 if self.pending_rename.is_some() {
4669 return None;
4670 }
4671
4672 let provider = self.semantics_provider.clone()?;
4673 let buffer = self.buffer.read(cx);
4674 let newest_selection = self.selections.newest_anchor().clone();
4675 let cursor_position = newest_selection.head();
4676 let (cursor_buffer, cursor_buffer_position) =
4677 buffer.text_anchor_for_position(cursor_position, cx)?;
4678 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4679 if cursor_buffer != tail_buffer {
4680 return None;
4681 }
4682 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4683 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4684 cx.background_executor()
4685 .timer(Duration::from_millis(debounce))
4686 .await;
4687
4688 let highlights = if let Some(highlights) = cx
4689 .update(|cx| {
4690 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4691 })
4692 .ok()
4693 .flatten()
4694 {
4695 highlights.await.log_err()
4696 } else {
4697 None
4698 };
4699
4700 if let Some(highlights) = highlights {
4701 this.update(&mut cx, |this, cx| {
4702 if this.pending_rename.is_some() {
4703 return;
4704 }
4705
4706 let buffer_id = cursor_position.buffer_id;
4707 let buffer = this.buffer.read(cx);
4708 if !buffer
4709 .text_anchor_for_position(cursor_position, cx)
4710 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4711 {
4712 return;
4713 }
4714
4715 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4716 let mut write_ranges = Vec::new();
4717 let mut read_ranges = Vec::new();
4718 for highlight in highlights {
4719 for (excerpt_id, excerpt_range) in
4720 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4721 {
4722 let start = highlight
4723 .range
4724 .start
4725 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4726 let end = highlight
4727 .range
4728 .end
4729 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4730 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4731 continue;
4732 }
4733
4734 let range = Anchor {
4735 buffer_id,
4736 excerpt_id,
4737 text_anchor: start,
4738 diff_base_anchor: None,
4739 }..Anchor {
4740 buffer_id,
4741 excerpt_id,
4742 text_anchor: end,
4743 diff_base_anchor: None,
4744 };
4745 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4746 write_ranges.push(range);
4747 } else {
4748 read_ranges.push(range);
4749 }
4750 }
4751 }
4752
4753 this.highlight_background::<DocumentHighlightRead>(
4754 &read_ranges,
4755 |theme| theme.editor_document_highlight_read_background,
4756 cx,
4757 );
4758 this.highlight_background::<DocumentHighlightWrite>(
4759 &write_ranges,
4760 |theme| theme.editor_document_highlight_write_background,
4761 cx,
4762 );
4763 cx.notify();
4764 })
4765 .log_err();
4766 }
4767 }));
4768 None
4769 }
4770
4771 pub fn refresh_selected_text_highlights(
4772 &mut self,
4773 window: &mut Window,
4774 cx: &mut Context<Editor>,
4775 ) {
4776 self.selection_highlight_task.take();
4777 if !EditorSettings::get_global(cx).selection_highlight {
4778 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4779 return;
4780 }
4781 if self.selections.count() != 1 || self.selections.line_mode {
4782 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4783 return;
4784 }
4785 let selection = self.selections.newest::<Point>(cx);
4786 if selection.is_empty() || selection.start.row != selection.end.row {
4787 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4788 return;
4789 }
4790 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4791 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4792 cx.background_executor()
4793 .timer(Duration::from_millis(debounce))
4794 .await;
4795 let Some(Some(matches_task)) = editor
4796 .update_in(&mut cx, |editor, _, cx| {
4797 if editor.selections.count() != 1 || editor.selections.line_mode {
4798 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4799 return None;
4800 }
4801 let selection = editor.selections.newest::<Point>(cx);
4802 if selection.is_empty() || selection.start.row != selection.end.row {
4803 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4804 return None;
4805 }
4806 let buffer = editor.buffer().read(cx).snapshot(cx);
4807 let query = buffer.text_for_range(selection.range()).collect::<String>();
4808 if query.trim().is_empty() {
4809 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4810 return None;
4811 }
4812 Some(cx.background_spawn(async move {
4813 let mut ranges = Vec::new();
4814 let selection_anchors = selection.range().to_anchors(&buffer);
4815 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4816 for (search_buffer, search_range, excerpt_id) in
4817 buffer.range_to_buffer_ranges(range)
4818 {
4819 ranges.extend(
4820 project::search::SearchQuery::text(
4821 query.clone(),
4822 false,
4823 false,
4824 false,
4825 Default::default(),
4826 Default::default(),
4827 None,
4828 )
4829 .unwrap()
4830 .search(search_buffer, Some(search_range.clone()))
4831 .await
4832 .into_iter()
4833 .filter_map(
4834 |match_range| {
4835 let start = search_buffer.anchor_after(
4836 search_range.start + match_range.start,
4837 );
4838 let end = search_buffer.anchor_before(
4839 search_range.start + match_range.end,
4840 );
4841 let range = Anchor::range_in_buffer(
4842 excerpt_id,
4843 search_buffer.remote_id(),
4844 start..end,
4845 );
4846 (range != selection_anchors).then_some(range)
4847 },
4848 ),
4849 );
4850 }
4851 }
4852 ranges
4853 }))
4854 })
4855 .log_err()
4856 else {
4857 return;
4858 };
4859 let matches = matches_task.await;
4860 editor
4861 .update_in(&mut cx, |editor, _, cx| {
4862 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4863 if !matches.is_empty() {
4864 editor.highlight_background::<SelectedTextHighlight>(
4865 &matches,
4866 |theme| theme.editor_document_highlight_bracket_background,
4867 cx,
4868 )
4869 }
4870 })
4871 .log_err();
4872 }));
4873 }
4874
4875 pub fn refresh_inline_completion(
4876 &mut self,
4877 debounce: bool,
4878 user_requested: bool,
4879 window: &mut Window,
4880 cx: &mut Context<Self>,
4881 ) -> Option<()> {
4882 let provider = self.edit_prediction_provider()?;
4883 let cursor = self.selections.newest_anchor().head();
4884 let (buffer, cursor_buffer_position) =
4885 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4886
4887 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4888 self.discard_inline_completion(false, cx);
4889 return None;
4890 }
4891
4892 if !user_requested
4893 && (!self.should_show_edit_predictions()
4894 || !self.is_focused(window)
4895 || buffer.read(cx).is_empty())
4896 {
4897 self.discard_inline_completion(false, cx);
4898 return None;
4899 }
4900
4901 self.update_visible_inline_completion(window, cx);
4902 provider.refresh(
4903 self.project.clone(),
4904 buffer,
4905 cursor_buffer_position,
4906 debounce,
4907 cx,
4908 );
4909 Some(())
4910 }
4911
4912 fn show_edit_predictions_in_menu(&self) -> bool {
4913 match self.edit_prediction_settings {
4914 EditPredictionSettings::Disabled => false,
4915 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4916 }
4917 }
4918
4919 pub fn edit_predictions_enabled(&self) -> bool {
4920 match self.edit_prediction_settings {
4921 EditPredictionSettings::Disabled => false,
4922 EditPredictionSettings::Enabled { .. } => true,
4923 }
4924 }
4925
4926 fn edit_prediction_requires_modifier(&self) -> bool {
4927 match self.edit_prediction_settings {
4928 EditPredictionSettings::Disabled => false,
4929 EditPredictionSettings::Enabled {
4930 preview_requires_modifier,
4931 ..
4932 } => preview_requires_modifier,
4933 }
4934 }
4935
4936 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
4937 if self.edit_prediction_provider.is_none() {
4938 self.edit_prediction_settings = EditPredictionSettings::Disabled;
4939 } else {
4940 let selection = self.selections.newest_anchor();
4941 let cursor = selection.head();
4942
4943 if let Some((buffer, cursor_buffer_position)) =
4944 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4945 {
4946 self.edit_prediction_settings =
4947 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
4948 }
4949 }
4950 }
4951
4952 fn edit_prediction_settings_at_position(
4953 &self,
4954 buffer: &Entity<Buffer>,
4955 buffer_position: language::Anchor,
4956 cx: &App,
4957 ) -> EditPredictionSettings {
4958 if self.mode != EditorMode::Full
4959 || !self.show_inline_completions_override.unwrap_or(true)
4960 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
4961 {
4962 return EditPredictionSettings::Disabled;
4963 }
4964
4965 let buffer = buffer.read(cx);
4966
4967 let file = buffer.file();
4968
4969 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
4970 return EditPredictionSettings::Disabled;
4971 };
4972
4973 let by_provider = matches!(
4974 self.menu_inline_completions_policy,
4975 MenuInlineCompletionsPolicy::ByProvider
4976 );
4977
4978 let show_in_menu = by_provider
4979 && self
4980 .edit_prediction_provider
4981 .as_ref()
4982 .map_or(false, |provider| {
4983 provider.provider.show_completions_in_menu()
4984 });
4985
4986 let preview_requires_modifier =
4987 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
4988
4989 EditPredictionSettings::Enabled {
4990 show_in_menu,
4991 preview_requires_modifier,
4992 }
4993 }
4994
4995 fn should_show_edit_predictions(&self) -> bool {
4996 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
4997 }
4998
4999 pub fn edit_prediction_preview_is_active(&self) -> bool {
5000 matches!(
5001 self.edit_prediction_preview,
5002 EditPredictionPreview::Active { .. }
5003 )
5004 }
5005
5006 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5007 let cursor = self.selections.newest_anchor().head();
5008 if let Some((buffer, cursor_position)) =
5009 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5010 {
5011 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5012 } else {
5013 false
5014 }
5015 }
5016
5017 fn edit_predictions_enabled_in_buffer(
5018 &self,
5019 buffer: &Entity<Buffer>,
5020 buffer_position: language::Anchor,
5021 cx: &App,
5022 ) -> bool {
5023 maybe!({
5024 let provider = self.edit_prediction_provider()?;
5025 if !provider.is_enabled(&buffer, buffer_position, cx) {
5026 return Some(false);
5027 }
5028 let buffer = buffer.read(cx);
5029 let Some(file) = buffer.file() else {
5030 return Some(true);
5031 };
5032 let settings = all_language_settings(Some(file), cx);
5033 Some(settings.edit_predictions_enabled_for_file(file, cx))
5034 })
5035 .unwrap_or(false)
5036 }
5037
5038 fn cycle_inline_completion(
5039 &mut self,
5040 direction: Direction,
5041 window: &mut Window,
5042 cx: &mut Context<Self>,
5043 ) -> Option<()> {
5044 let provider = self.edit_prediction_provider()?;
5045 let cursor = self.selections.newest_anchor().head();
5046 let (buffer, cursor_buffer_position) =
5047 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5048 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5049 return None;
5050 }
5051
5052 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5053 self.update_visible_inline_completion(window, cx);
5054
5055 Some(())
5056 }
5057
5058 pub fn show_inline_completion(
5059 &mut self,
5060 _: &ShowEditPrediction,
5061 window: &mut Window,
5062 cx: &mut Context<Self>,
5063 ) {
5064 if !self.has_active_inline_completion() {
5065 self.refresh_inline_completion(false, true, window, cx);
5066 return;
5067 }
5068
5069 self.update_visible_inline_completion(window, cx);
5070 }
5071
5072 pub fn display_cursor_names(
5073 &mut self,
5074 _: &DisplayCursorNames,
5075 window: &mut Window,
5076 cx: &mut Context<Self>,
5077 ) {
5078 self.show_cursor_names(window, cx);
5079 }
5080
5081 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5082 self.show_cursor_names = true;
5083 cx.notify();
5084 cx.spawn_in(window, |this, mut cx| async move {
5085 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5086 this.update(&mut cx, |this, cx| {
5087 this.show_cursor_names = false;
5088 cx.notify()
5089 })
5090 .ok()
5091 })
5092 .detach();
5093 }
5094
5095 pub fn next_edit_prediction(
5096 &mut self,
5097 _: &NextEditPrediction,
5098 window: &mut Window,
5099 cx: &mut Context<Self>,
5100 ) {
5101 if self.has_active_inline_completion() {
5102 self.cycle_inline_completion(Direction::Next, window, cx);
5103 } else {
5104 let is_copilot_disabled = self
5105 .refresh_inline_completion(false, true, window, cx)
5106 .is_none();
5107 if is_copilot_disabled {
5108 cx.propagate();
5109 }
5110 }
5111 }
5112
5113 pub fn previous_edit_prediction(
5114 &mut self,
5115 _: &PreviousEditPrediction,
5116 window: &mut Window,
5117 cx: &mut Context<Self>,
5118 ) {
5119 if self.has_active_inline_completion() {
5120 self.cycle_inline_completion(Direction::Prev, window, cx);
5121 } else {
5122 let is_copilot_disabled = self
5123 .refresh_inline_completion(false, true, window, cx)
5124 .is_none();
5125 if is_copilot_disabled {
5126 cx.propagate();
5127 }
5128 }
5129 }
5130
5131 pub fn accept_edit_prediction(
5132 &mut self,
5133 _: &AcceptEditPrediction,
5134 window: &mut Window,
5135 cx: &mut Context<Self>,
5136 ) {
5137 if self.show_edit_predictions_in_menu() {
5138 self.hide_context_menu(window, cx);
5139 }
5140
5141 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5142 return;
5143 };
5144
5145 self.report_inline_completion_event(
5146 active_inline_completion.completion_id.clone(),
5147 true,
5148 cx,
5149 );
5150
5151 match &active_inline_completion.completion {
5152 InlineCompletion::Move { target, .. } => {
5153 let target = *target;
5154
5155 if let Some(position_map) = &self.last_position_map {
5156 if position_map
5157 .visible_row_range
5158 .contains(&target.to_display_point(&position_map.snapshot).row())
5159 || !self.edit_prediction_requires_modifier()
5160 {
5161 self.unfold_ranges(&[target..target], true, false, cx);
5162 // Note that this is also done in vim's handler of the Tab action.
5163 self.change_selections(
5164 Some(Autoscroll::newest()),
5165 window,
5166 cx,
5167 |selections| {
5168 selections.select_anchor_ranges([target..target]);
5169 },
5170 );
5171 self.clear_row_highlights::<EditPredictionPreview>();
5172
5173 self.edit_prediction_preview
5174 .set_previous_scroll_position(None);
5175 } else {
5176 self.edit_prediction_preview
5177 .set_previous_scroll_position(Some(
5178 position_map.snapshot.scroll_anchor,
5179 ));
5180
5181 self.highlight_rows::<EditPredictionPreview>(
5182 target..target,
5183 cx.theme().colors().editor_highlighted_line_background,
5184 true,
5185 cx,
5186 );
5187 self.request_autoscroll(Autoscroll::fit(), cx);
5188 }
5189 }
5190 }
5191 InlineCompletion::Edit { edits, .. } => {
5192 if let Some(provider) = self.edit_prediction_provider() {
5193 provider.accept(cx);
5194 }
5195
5196 let snapshot = self.buffer.read(cx).snapshot(cx);
5197 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5198
5199 self.buffer.update(cx, |buffer, cx| {
5200 buffer.edit(edits.iter().cloned(), None, cx)
5201 });
5202
5203 self.change_selections(None, window, cx, |s| {
5204 s.select_anchor_ranges([last_edit_end..last_edit_end])
5205 });
5206
5207 self.update_visible_inline_completion(window, cx);
5208 if self.active_inline_completion.is_none() {
5209 self.refresh_inline_completion(true, true, window, cx);
5210 }
5211
5212 cx.notify();
5213 }
5214 }
5215
5216 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5217 }
5218
5219 pub fn accept_partial_inline_completion(
5220 &mut self,
5221 _: &AcceptPartialEditPrediction,
5222 window: &mut Window,
5223 cx: &mut Context<Self>,
5224 ) {
5225 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5226 return;
5227 };
5228 if self.selections.count() != 1 {
5229 return;
5230 }
5231
5232 self.report_inline_completion_event(
5233 active_inline_completion.completion_id.clone(),
5234 true,
5235 cx,
5236 );
5237
5238 match &active_inline_completion.completion {
5239 InlineCompletion::Move { target, .. } => {
5240 let target = *target;
5241 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5242 selections.select_anchor_ranges([target..target]);
5243 });
5244 }
5245 InlineCompletion::Edit { edits, .. } => {
5246 // Find an insertion that starts at the cursor position.
5247 let snapshot = self.buffer.read(cx).snapshot(cx);
5248 let cursor_offset = self.selections.newest::<usize>(cx).head();
5249 let insertion = edits.iter().find_map(|(range, text)| {
5250 let range = range.to_offset(&snapshot);
5251 if range.is_empty() && range.start == cursor_offset {
5252 Some(text)
5253 } else {
5254 None
5255 }
5256 });
5257
5258 if let Some(text) = insertion {
5259 let mut partial_completion = text
5260 .chars()
5261 .by_ref()
5262 .take_while(|c| c.is_alphabetic())
5263 .collect::<String>();
5264 if partial_completion.is_empty() {
5265 partial_completion = text
5266 .chars()
5267 .by_ref()
5268 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5269 .collect::<String>();
5270 }
5271
5272 cx.emit(EditorEvent::InputHandled {
5273 utf16_range_to_replace: None,
5274 text: partial_completion.clone().into(),
5275 });
5276
5277 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5278
5279 self.refresh_inline_completion(true, true, window, cx);
5280 cx.notify();
5281 } else {
5282 self.accept_edit_prediction(&Default::default(), window, cx);
5283 }
5284 }
5285 }
5286 }
5287
5288 fn discard_inline_completion(
5289 &mut self,
5290 should_report_inline_completion_event: bool,
5291 cx: &mut Context<Self>,
5292 ) -> bool {
5293 if should_report_inline_completion_event {
5294 let completion_id = self
5295 .active_inline_completion
5296 .as_ref()
5297 .and_then(|active_completion| active_completion.completion_id.clone());
5298
5299 self.report_inline_completion_event(completion_id, false, cx);
5300 }
5301
5302 if let Some(provider) = self.edit_prediction_provider() {
5303 provider.discard(cx);
5304 }
5305
5306 self.take_active_inline_completion(cx)
5307 }
5308
5309 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5310 let Some(provider) = self.edit_prediction_provider() else {
5311 return;
5312 };
5313
5314 let Some((_, buffer, _)) = self
5315 .buffer
5316 .read(cx)
5317 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5318 else {
5319 return;
5320 };
5321
5322 let extension = buffer
5323 .read(cx)
5324 .file()
5325 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5326
5327 let event_type = match accepted {
5328 true => "Edit Prediction Accepted",
5329 false => "Edit Prediction Discarded",
5330 };
5331 telemetry::event!(
5332 event_type,
5333 provider = provider.name(),
5334 prediction_id = id,
5335 suggestion_accepted = accepted,
5336 file_extension = extension,
5337 );
5338 }
5339
5340 pub fn has_active_inline_completion(&self) -> bool {
5341 self.active_inline_completion.is_some()
5342 }
5343
5344 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5345 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5346 return false;
5347 };
5348
5349 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5350 self.clear_highlights::<InlineCompletionHighlight>(cx);
5351 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5352 true
5353 }
5354
5355 /// Returns true when we're displaying the edit prediction popover below the cursor
5356 /// like we are not previewing and the LSP autocomplete menu is visible
5357 /// or we are in `when_holding_modifier` mode.
5358 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5359 if self.edit_prediction_preview_is_active()
5360 || !self.show_edit_predictions_in_menu()
5361 || !self.edit_predictions_enabled()
5362 {
5363 return false;
5364 }
5365
5366 if self.has_visible_completions_menu() {
5367 return true;
5368 }
5369
5370 has_completion && self.edit_prediction_requires_modifier()
5371 }
5372
5373 fn handle_modifiers_changed(
5374 &mut self,
5375 modifiers: Modifiers,
5376 position_map: &PositionMap,
5377 window: &mut Window,
5378 cx: &mut Context<Self>,
5379 ) {
5380 if self.show_edit_predictions_in_menu() {
5381 self.update_edit_prediction_preview(&modifiers, window, cx);
5382 }
5383
5384 self.update_selection_mode(&modifiers, position_map, window, cx);
5385
5386 let mouse_position = window.mouse_position();
5387 if !position_map.text_hitbox.is_hovered(window) {
5388 return;
5389 }
5390
5391 self.update_hovered_link(
5392 position_map.point_for_position(mouse_position),
5393 &position_map.snapshot,
5394 modifiers,
5395 window,
5396 cx,
5397 )
5398 }
5399
5400 fn update_selection_mode(
5401 &mut self,
5402 modifiers: &Modifiers,
5403 position_map: &PositionMap,
5404 window: &mut Window,
5405 cx: &mut Context<Self>,
5406 ) {
5407 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5408 return;
5409 }
5410
5411 let mouse_position = window.mouse_position();
5412 let point_for_position = position_map.point_for_position(mouse_position);
5413 let position = point_for_position.previous_valid;
5414
5415 self.select(
5416 SelectPhase::BeginColumnar {
5417 position,
5418 reset: false,
5419 goal_column: point_for_position.exact_unclipped.column(),
5420 },
5421 window,
5422 cx,
5423 );
5424 }
5425
5426 fn update_edit_prediction_preview(
5427 &mut self,
5428 modifiers: &Modifiers,
5429 window: &mut Window,
5430 cx: &mut Context<Self>,
5431 ) {
5432 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5433 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5434 return;
5435 };
5436
5437 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5438 if matches!(
5439 self.edit_prediction_preview,
5440 EditPredictionPreview::Inactive { .. }
5441 ) {
5442 self.edit_prediction_preview = EditPredictionPreview::Active {
5443 previous_scroll_position: None,
5444 since: Instant::now(),
5445 };
5446
5447 self.update_visible_inline_completion(window, cx);
5448 cx.notify();
5449 }
5450 } else if let EditPredictionPreview::Active {
5451 previous_scroll_position,
5452 since,
5453 } = self.edit_prediction_preview
5454 {
5455 if let (Some(previous_scroll_position), Some(position_map)) =
5456 (previous_scroll_position, self.last_position_map.as_ref())
5457 {
5458 self.set_scroll_position(
5459 previous_scroll_position
5460 .scroll_position(&position_map.snapshot.display_snapshot),
5461 window,
5462 cx,
5463 );
5464 }
5465
5466 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5467 released_too_fast: since.elapsed() < Duration::from_millis(200),
5468 };
5469 self.clear_row_highlights::<EditPredictionPreview>();
5470 self.update_visible_inline_completion(window, cx);
5471 cx.notify();
5472 }
5473 }
5474
5475 fn update_visible_inline_completion(
5476 &mut self,
5477 _window: &mut Window,
5478 cx: &mut Context<Self>,
5479 ) -> Option<()> {
5480 let selection = self.selections.newest_anchor();
5481 let cursor = selection.head();
5482 let multibuffer = self.buffer.read(cx).snapshot(cx);
5483 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5484 let excerpt_id = cursor.excerpt_id;
5485
5486 let show_in_menu = self.show_edit_predictions_in_menu();
5487 let completions_menu_has_precedence = !show_in_menu
5488 && (self.context_menu.borrow().is_some()
5489 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5490
5491 if completions_menu_has_precedence
5492 || !offset_selection.is_empty()
5493 || self
5494 .active_inline_completion
5495 .as_ref()
5496 .map_or(false, |completion| {
5497 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5498 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5499 !invalidation_range.contains(&offset_selection.head())
5500 })
5501 {
5502 self.discard_inline_completion(false, cx);
5503 return None;
5504 }
5505
5506 self.take_active_inline_completion(cx);
5507 let Some(provider) = self.edit_prediction_provider() else {
5508 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5509 return None;
5510 };
5511
5512 let (buffer, cursor_buffer_position) =
5513 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5514
5515 self.edit_prediction_settings =
5516 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5517
5518 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5519
5520 if self.edit_prediction_indent_conflict {
5521 let cursor_point = cursor.to_point(&multibuffer);
5522
5523 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5524
5525 if let Some((_, indent)) = indents.iter().next() {
5526 if indent.len == cursor_point.column {
5527 self.edit_prediction_indent_conflict = false;
5528 }
5529 }
5530 }
5531
5532 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5533 let edits = inline_completion
5534 .edits
5535 .into_iter()
5536 .flat_map(|(range, new_text)| {
5537 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5538 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5539 Some((start..end, new_text))
5540 })
5541 .collect::<Vec<_>>();
5542 if edits.is_empty() {
5543 return None;
5544 }
5545
5546 let first_edit_start = edits.first().unwrap().0.start;
5547 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5548 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5549
5550 let last_edit_end = edits.last().unwrap().0.end;
5551 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5552 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5553
5554 let cursor_row = cursor.to_point(&multibuffer).row;
5555
5556 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5557
5558 let mut inlay_ids = Vec::new();
5559 let invalidation_row_range;
5560 let move_invalidation_row_range = if cursor_row < edit_start_row {
5561 Some(cursor_row..edit_end_row)
5562 } else if cursor_row > edit_end_row {
5563 Some(edit_start_row..cursor_row)
5564 } else {
5565 None
5566 };
5567 let is_move =
5568 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5569 let completion = if is_move {
5570 invalidation_row_range =
5571 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5572 let target = first_edit_start;
5573 InlineCompletion::Move { target, snapshot }
5574 } else {
5575 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5576 && !self.inline_completions_hidden_for_vim_mode;
5577
5578 if show_completions_in_buffer {
5579 if edits
5580 .iter()
5581 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5582 {
5583 let mut inlays = Vec::new();
5584 for (range, new_text) in &edits {
5585 let inlay = Inlay::inline_completion(
5586 post_inc(&mut self.next_inlay_id),
5587 range.start,
5588 new_text.as_str(),
5589 );
5590 inlay_ids.push(inlay.id);
5591 inlays.push(inlay);
5592 }
5593
5594 self.splice_inlays(&[], inlays, cx);
5595 } else {
5596 let background_color = cx.theme().status().deleted_background;
5597 self.highlight_text::<InlineCompletionHighlight>(
5598 edits.iter().map(|(range, _)| range.clone()).collect(),
5599 HighlightStyle {
5600 background_color: Some(background_color),
5601 ..Default::default()
5602 },
5603 cx,
5604 );
5605 }
5606 }
5607
5608 invalidation_row_range = edit_start_row..edit_end_row;
5609
5610 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5611 if provider.show_tab_accept_marker() {
5612 EditDisplayMode::TabAccept
5613 } else {
5614 EditDisplayMode::Inline
5615 }
5616 } else {
5617 EditDisplayMode::DiffPopover
5618 };
5619
5620 InlineCompletion::Edit {
5621 edits,
5622 edit_preview: inline_completion.edit_preview,
5623 display_mode,
5624 snapshot,
5625 }
5626 };
5627
5628 let invalidation_range = multibuffer
5629 .anchor_before(Point::new(invalidation_row_range.start, 0))
5630 ..multibuffer.anchor_after(Point::new(
5631 invalidation_row_range.end,
5632 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5633 ));
5634
5635 self.stale_inline_completion_in_menu = None;
5636 self.active_inline_completion = Some(InlineCompletionState {
5637 inlay_ids,
5638 completion,
5639 completion_id: inline_completion.id,
5640 invalidation_range,
5641 });
5642
5643 cx.notify();
5644
5645 Some(())
5646 }
5647
5648 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5649 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5650 }
5651
5652 fn render_code_actions_indicator(
5653 &self,
5654 _style: &EditorStyle,
5655 row: DisplayRow,
5656 is_active: bool,
5657 cx: &mut Context<Self>,
5658 ) -> Option<IconButton> {
5659 if self.available_code_actions.is_some() {
5660 Some(
5661 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5662 .shape(ui::IconButtonShape::Square)
5663 .icon_size(IconSize::XSmall)
5664 .icon_color(Color::Muted)
5665 .toggle_state(is_active)
5666 .tooltip({
5667 let focus_handle = self.focus_handle.clone();
5668 move |window, cx| {
5669 Tooltip::for_action_in(
5670 "Toggle Code Actions",
5671 &ToggleCodeActions {
5672 deployed_from_indicator: None,
5673 },
5674 &focus_handle,
5675 window,
5676 cx,
5677 )
5678 }
5679 })
5680 .on_click(cx.listener(move |editor, _e, window, cx| {
5681 window.focus(&editor.focus_handle(cx));
5682 editor.toggle_code_actions(
5683 &ToggleCodeActions {
5684 deployed_from_indicator: Some(row),
5685 },
5686 window,
5687 cx,
5688 );
5689 })),
5690 )
5691 } else {
5692 None
5693 }
5694 }
5695
5696 fn clear_tasks(&mut self) {
5697 self.tasks.clear()
5698 }
5699
5700 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5701 if self.tasks.insert(key, value).is_some() {
5702 // This case should hopefully be rare, but just in case...
5703 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5704 }
5705 }
5706
5707 fn build_tasks_context(
5708 project: &Entity<Project>,
5709 buffer: &Entity<Buffer>,
5710 buffer_row: u32,
5711 tasks: &Arc<RunnableTasks>,
5712 cx: &mut Context<Self>,
5713 ) -> Task<Option<task::TaskContext>> {
5714 let position = Point::new(buffer_row, tasks.column);
5715 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5716 let location = Location {
5717 buffer: buffer.clone(),
5718 range: range_start..range_start,
5719 };
5720 // Fill in the environmental variables from the tree-sitter captures
5721 let mut captured_task_variables = TaskVariables::default();
5722 for (capture_name, value) in tasks.extra_variables.clone() {
5723 captured_task_variables.insert(
5724 task::VariableName::Custom(capture_name.into()),
5725 value.clone(),
5726 );
5727 }
5728 project.update(cx, |project, cx| {
5729 project.task_store().update(cx, |task_store, cx| {
5730 task_store.task_context_for_location(captured_task_variables, location, cx)
5731 })
5732 })
5733 }
5734
5735 pub fn spawn_nearest_task(
5736 &mut self,
5737 action: &SpawnNearestTask,
5738 window: &mut Window,
5739 cx: &mut Context<Self>,
5740 ) {
5741 let Some((workspace, _)) = self.workspace.clone() else {
5742 return;
5743 };
5744 let Some(project) = self.project.clone() else {
5745 return;
5746 };
5747
5748 // Try to find a closest, enclosing node using tree-sitter that has a
5749 // task
5750 let Some((buffer, buffer_row, tasks)) = self
5751 .find_enclosing_node_task(cx)
5752 // Or find the task that's closest in row-distance.
5753 .or_else(|| self.find_closest_task(cx))
5754 else {
5755 return;
5756 };
5757
5758 let reveal_strategy = action.reveal;
5759 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5760 cx.spawn_in(window, |_, mut cx| async move {
5761 let context = task_context.await?;
5762 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5763
5764 let resolved = resolved_task.resolved.as_mut()?;
5765 resolved.reveal = reveal_strategy;
5766
5767 workspace
5768 .update(&mut cx, |workspace, cx| {
5769 workspace::tasks::schedule_resolved_task(
5770 workspace,
5771 task_source_kind,
5772 resolved_task,
5773 false,
5774 cx,
5775 );
5776 })
5777 .ok()
5778 })
5779 .detach();
5780 }
5781
5782 fn find_closest_task(
5783 &mut self,
5784 cx: &mut Context<Self>,
5785 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5786 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5787
5788 let ((buffer_id, row), tasks) = self
5789 .tasks
5790 .iter()
5791 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5792
5793 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5794 let tasks = Arc::new(tasks.to_owned());
5795 Some((buffer, *row, tasks))
5796 }
5797
5798 fn find_enclosing_node_task(
5799 &mut self,
5800 cx: &mut Context<Self>,
5801 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5802 let snapshot = self.buffer.read(cx).snapshot(cx);
5803 let offset = self.selections.newest::<usize>(cx).head();
5804 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5805 let buffer_id = excerpt.buffer().remote_id();
5806
5807 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5808 let mut cursor = layer.node().walk();
5809
5810 while cursor.goto_first_child_for_byte(offset).is_some() {
5811 if cursor.node().end_byte() == offset {
5812 cursor.goto_next_sibling();
5813 }
5814 }
5815
5816 // Ascend to the smallest ancestor that contains the range and has a task.
5817 loop {
5818 let node = cursor.node();
5819 let node_range = node.byte_range();
5820 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5821
5822 // Check if this node contains our offset
5823 if node_range.start <= offset && node_range.end >= offset {
5824 // If it contains offset, check for task
5825 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5826 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5827 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5828 }
5829 }
5830
5831 if !cursor.goto_parent() {
5832 break;
5833 }
5834 }
5835 None
5836 }
5837
5838 fn render_run_indicator(
5839 &self,
5840 _style: &EditorStyle,
5841 is_active: bool,
5842 row: DisplayRow,
5843 cx: &mut Context<Self>,
5844 ) -> IconButton {
5845 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5846 .shape(ui::IconButtonShape::Square)
5847 .icon_size(IconSize::XSmall)
5848 .icon_color(Color::Muted)
5849 .toggle_state(is_active)
5850 .on_click(cx.listener(move |editor, _e, window, cx| {
5851 window.focus(&editor.focus_handle(cx));
5852 editor.toggle_code_actions(
5853 &ToggleCodeActions {
5854 deployed_from_indicator: Some(row),
5855 },
5856 window,
5857 cx,
5858 );
5859 }))
5860 }
5861
5862 pub fn context_menu_visible(&self) -> bool {
5863 !self.edit_prediction_preview_is_active()
5864 && self
5865 .context_menu
5866 .borrow()
5867 .as_ref()
5868 .map_or(false, |menu| menu.visible())
5869 }
5870
5871 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5872 self.context_menu
5873 .borrow()
5874 .as_ref()
5875 .map(|menu| menu.origin())
5876 }
5877
5878 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
5879 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
5880
5881 #[allow(clippy::too_many_arguments)]
5882 fn render_edit_prediction_popover(
5883 &mut self,
5884 text_bounds: &Bounds<Pixels>,
5885 content_origin: gpui::Point<Pixels>,
5886 editor_snapshot: &EditorSnapshot,
5887 visible_row_range: Range<DisplayRow>,
5888 scroll_top: f32,
5889 scroll_bottom: f32,
5890 line_layouts: &[LineWithInvisibles],
5891 line_height: Pixels,
5892 scroll_pixel_position: gpui::Point<Pixels>,
5893 newest_selection_head: Option<DisplayPoint>,
5894 editor_width: Pixels,
5895 style: &EditorStyle,
5896 window: &mut Window,
5897 cx: &mut App,
5898 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
5899 let active_inline_completion = self.active_inline_completion.as_ref()?;
5900
5901 if self.edit_prediction_visible_in_cursor_popover(true) {
5902 return None;
5903 }
5904
5905 match &active_inline_completion.completion {
5906 InlineCompletion::Move { target, .. } => {
5907 let target_display_point = target.to_display_point(editor_snapshot);
5908
5909 if self.edit_prediction_requires_modifier() {
5910 if !self.edit_prediction_preview_is_active() {
5911 return None;
5912 }
5913
5914 self.render_edit_prediction_modifier_jump_popover(
5915 text_bounds,
5916 content_origin,
5917 visible_row_range,
5918 line_layouts,
5919 line_height,
5920 scroll_pixel_position,
5921 newest_selection_head,
5922 target_display_point,
5923 window,
5924 cx,
5925 )
5926 } else {
5927 self.render_edit_prediction_eager_jump_popover(
5928 text_bounds,
5929 content_origin,
5930 editor_snapshot,
5931 visible_row_range,
5932 scroll_top,
5933 scroll_bottom,
5934 line_height,
5935 scroll_pixel_position,
5936 target_display_point,
5937 editor_width,
5938 window,
5939 cx,
5940 )
5941 }
5942 }
5943 InlineCompletion::Edit {
5944 display_mode: EditDisplayMode::Inline,
5945 ..
5946 } => None,
5947 InlineCompletion::Edit {
5948 display_mode: EditDisplayMode::TabAccept,
5949 edits,
5950 ..
5951 } => {
5952 let range = &edits.first()?.0;
5953 let target_display_point = range.end.to_display_point(editor_snapshot);
5954
5955 self.render_edit_prediction_end_of_line_popover(
5956 "Accept",
5957 editor_snapshot,
5958 visible_row_range,
5959 target_display_point,
5960 line_height,
5961 scroll_pixel_position,
5962 content_origin,
5963 editor_width,
5964 window,
5965 cx,
5966 )
5967 }
5968 InlineCompletion::Edit {
5969 edits,
5970 edit_preview,
5971 display_mode: EditDisplayMode::DiffPopover,
5972 snapshot,
5973 } => self.render_edit_prediction_diff_popover(
5974 text_bounds,
5975 content_origin,
5976 editor_snapshot,
5977 visible_row_range,
5978 line_layouts,
5979 line_height,
5980 scroll_pixel_position,
5981 newest_selection_head,
5982 editor_width,
5983 style,
5984 edits,
5985 edit_preview,
5986 snapshot,
5987 window,
5988 cx,
5989 ),
5990 }
5991 }
5992
5993 #[allow(clippy::too_many_arguments)]
5994 fn render_edit_prediction_modifier_jump_popover(
5995 &mut self,
5996 text_bounds: &Bounds<Pixels>,
5997 content_origin: gpui::Point<Pixels>,
5998 visible_row_range: Range<DisplayRow>,
5999 line_layouts: &[LineWithInvisibles],
6000 line_height: Pixels,
6001 scroll_pixel_position: gpui::Point<Pixels>,
6002 newest_selection_head: Option<DisplayPoint>,
6003 target_display_point: DisplayPoint,
6004 window: &mut Window,
6005 cx: &mut App,
6006 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6007 let scrolled_content_origin =
6008 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6009
6010 const SCROLL_PADDING_Y: Pixels = px(12.);
6011
6012 if target_display_point.row() < visible_row_range.start {
6013 return self.render_edit_prediction_scroll_popover(
6014 |_| SCROLL_PADDING_Y,
6015 IconName::ArrowUp,
6016 visible_row_range,
6017 line_layouts,
6018 newest_selection_head,
6019 scrolled_content_origin,
6020 window,
6021 cx,
6022 );
6023 } else if target_display_point.row() >= visible_row_range.end {
6024 return self.render_edit_prediction_scroll_popover(
6025 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6026 IconName::ArrowDown,
6027 visible_row_range,
6028 line_layouts,
6029 newest_selection_head,
6030 scrolled_content_origin,
6031 window,
6032 cx,
6033 );
6034 }
6035
6036 const POLE_WIDTH: Pixels = px(2.);
6037
6038 let line_layout =
6039 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6040 let target_column = target_display_point.column() as usize;
6041
6042 let target_x = line_layout.x_for_index(target_column);
6043 let target_y =
6044 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6045
6046 let flag_on_right = target_x < text_bounds.size.width / 2.;
6047
6048 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6049 border_color.l += 0.001;
6050
6051 let mut element = v_flex()
6052 .items_end()
6053 .when(flag_on_right, |el| el.items_start())
6054 .child(if flag_on_right {
6055 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6056 .rounded_bl(px(0.))
6057 .rounded_tl(px(0.))
6058 .border_l_2()
6059 .border_color(border_color)
6060 } else {
6061 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6062 .rounded_br(px(0.))
6063 .rounded_tr(px(0.))
6064 .border_r_2()
6065 .border_color(border_color)
6066 })
6067 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6068 .into_any();
6069
6070 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6071
6072 let mut origin = scrolled_content_origin + point(target_x, target_y)
6073 - point(
6074 if flag_on_right {
6075 POLE_WIDTH
6076 } else {
6077 size.width - POLE_WIDTH
6078 },
6079 size.height - line_height,
6080 );
6081
6082 origin.x = origin.x.max(content_origin.x);
6083
6084 element.prepaint_at(origin, window, cx);
6085
6086 Some((element, origin))
6087 }
6088
6089 #[allow(clippy::too_many_arguments)]
6090 fn render_edit_prediction_scroll_popover(
6091 &mut self,
6092 to_y: impl Fn(Size<Pixels>) -> Pixels,
6093 scroll_icon: IconName,
6094 visible_row_range: Range<DisplayRow>,
6095 line_layouts: &[LineWithInvisibles],
6096 newest_selection_head: Option<DisplayPoint>,
6097 scrolled_content_origin: gpui::Point<Pixels>,
6098 window: &mut Window,
6099 cx: &mut App,
6100 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6101 let mut element = self
6102 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6103 .into_any();
6104
6105 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6106
6107 let cursor = newest_selection_head?;
6108 let cursor_row_layout =
6109 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6110 let cursor_column = cursor.column() as usize;
6111
6112 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6113
6114 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6115
6116 element.prepaint_at(origin, window, cx);
6117 Some((element, origin))
6118 }
6119
6120 #[allow(clippy::too_many_arguments)]
6121 fn render_edit_prediction_eager_jump_popover(
6122 &mut self,
6123 text_bounds: &Bounds<Pixels>,
6124 content_origin: gpui::Point<Pixels>,
6125 editor_snapshot: &EditorSnapshot,
6126 visible_row_range: Range<DisplayRow>,
6127 scroll_top: f32,
6128 scroll_bottom: f32,
6129 line_height: Pixels,
6130 scroll_pixel_position: gpui::Point<Pixels>,
6131 target_display_point: DisplayPoint,
6132 editor_width: Pixels,
6133 window: &mut Window,
6134 cx: &mut App,
6135 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6136 if target_display_point.row().as_f32() < scroll_top {
6137 let mut element = self
6138 .render_edit_prediction_line_popover(
6139 "Jump to Edit",
6140 Some(IconName::ArrowUp),
6141 window,
6142 cx,
6143 )?
6144 .into_any();
6145
6146 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6147 let offset = point(
6148 (text_bounds.size.width - size.width) / 2.,
6149 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6150 );
6151
6152 let origin = text_bounds.origin + offset;
6153 element.prepaint_at(origin, window, cx);
6154 Some((element, origin))
6155 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6156 let mut element = self
6157 .render_edit_prediction_line_popover(
6158 "Jump to Edit",
6159 Some(IconName::ArrowDown),
6160 window,
6161 cx,
6162 )?
6163 .into_any();
6164
6165 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6166 let offset = point(
6167 (text_bounds.size.width - size.width) / 2.,
6168 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6169 );
6170
6171 let origin = text_bounds.origin + offset;
6172 element.prepaint_at(origin, window, cx);
6173 Some((element, origin))
6174 } else {
6175 self.render_edit_prediction_end_of_line_popover(
6176 "Jump to Edit",
6177 editor_snapshot,
6178 visible_row_range,
6179 target_display_point,
6180 line_height,
6181 scroll_pixel_position,
6182 content_origin,
6183 editor_width,
6184 window,
6185 cx,
6186 )
6187 }
6188 }
6189
6190 #[allow(clippy::too_many_arguments)]
6191 fn render_edit_prediction_end_of_line_popover(
6192 self: &mut Editor,
6193 label: &'static str,
6194 editor_snapshot: &EditorSnapshot,
6195 visible_row_range: Range<DisplayRow>,
6196 target_display_point: DisplayPoint,
6197 line_height: Pixels,
6198 scroll_pixel_position: gpui::Point<Pixels>,
6199 content_origin: gpui::Point<Pixels>,
6200 editor_width: Pixels,
6201 window: &mut Window,
6202 cx: &mut App,
6203 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6204 let target_line_end = DisplayPoint::new(
6205 target_display_point.row(),
6206 editor_snapshot.line_len(target_display_point.row()),
6207 );
6208
6209 let mut element = self
6210 .render_edit_prediction_line_popover(label, None, window, cx)?
6211 .into_any();
6212
6213 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6214
6215 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6216
6217 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6218 let mut origin = start_point
6219 + line_origin
6220 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6221 origin.x = origin.x.max(content_origin.x);
6222
6223 let max_x = content_origin.x + editor_width - size.width;
6224
6225 if origin.x > max_x {
6226 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6227
6228 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6229 origin.y += offset;
6230 IconName::ArrowUp
6231 } else {
6232 origin.y -= offset;
6233 IconName::ArrowDown
6234 };
6235
6236 element = self
6237 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6238 .into_any();
6239
6240 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6241
6242 origin.x = content_origin.x + editor_width - size.width - px(2.);
6243 }
6244
6245 element.prepaint_at(origin, window, cx);
6246 Some((element, origin))
6247 }
6248
6249 #[allow(clippy::too_many_arguments)]
6250 fn render_edit_prediction_diff_popover(
6251 self: &Editor,
6252 text_bounds: &Bounds<Pixels>,
6253 content_origin: gpui::Point<Pixels>,
6254 editor_snapshot: &EditorSnapshot,
6255 visible_row_range: Range<DisplayRow>,
6256 line_layouts: &[LineWithInvisibles],
6257 line_height: Pixels,
6258 scroll_pixel_position: gpui::Point<Pixels>,
6259 newest_selection_head: Option<DisplayPoint>,
6260 editor_width: Pixels,
6261 style: &EditorStyle,
6262 edits: &Vec<(Range<Anchor>, String)>,
6263 edit_preview: &Option<language::EditPreview>,
6264 snapshot: &language::BufferSnapshot,
6265 window: &mut Window,
6266 cx: &mut App,
6267 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6268 let edit_start = edits
6269 .first()
6270 .unwrap()
6271 .0
6272 .start
6273 .to_display_point(editor_snapshot);
6274 let edit_end = edits
6275 .last()
6276 .unwrap()
6277 .0
6278 .end
6279 .to_display_point(editor_snapshot);
6280
6281 let is_visible = visible_row_range.contains(&edit_start.row())
6282 || visible_row_range.contains(&edit_end.row());
6283 if !is_visible {
6284 return None;
6285 }
6286
6287 let highlighted_edits =
6288 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6289
6290 let styled_text = highlighted_edits.to_styled_text(&style.text);
6291 let line_count = highlighted_edits.text.lines().count();
6292
6293 const BORDER_WIDTH: Pixels = px(1.);
6294
6295 let mut element = h_flex()
6296 .items_start()
6297 .child(
6298 h_flex()
6299 .bg(cx.theme().colors().editor_background)
6300 .border(BORDER_WIDTH)
6301 .shadow_sm()
6302 .border_color(cx.theme().colors().border)
6303 .rounded_l_lg()
6304 .when(line_count > 1, |el| el.rounded_br_lg())
6305 .pr_1()
6306 .child(styled_text),
6307 )
6308 .child(
6309 h_flex()
6310 .h(line_height + BORDER_WIDTH * px(2.))
6311 .px_1p5()
6312 .gap_1()
6313 // Workaround: For some reason, there's a gap if we don't do this
6314 .ml(-BORDER_WIDTH)
6315 .shadow(smallvec![gpui::BoxShadow {
6316 color: gpui::black().opacity(0.05),
6317 offset: point(px(1.), px(1.)),
6318 blur_radius: px(2.),
6319 spread_radius: px(0.),
6320 }])
6321 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6322 .border(BORDER_WIDTH)
6323 .border_color(cx.theme().colors().border)
6324 .rounded_r_lg()
6325 .children(self.render_edit_prediction_accept_keybind(window, cx)),
6326 )
6327 .into_any();
6328
6329 let longest_row =
6330 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6331 let longest_line_width = if visible_row_range.contains(&longest_row) {
6332 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6333 } else {
6334 layout_line(
6335 longest_row,
6336 editor_snapshot,
6337 style,
6338 editor_width,
6339 |_| false,
6340 window,
6341 cx,
6342 )
6343 .width
6344 };
6345
6346 let viewport_bounds =
6347 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6348 right: -EditorElement::SCROLLBAR_WIDTH,
6349 ..Default::default()
6350 });
6351
6352 let x_after_longest =
6353 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6354 - scroll_pixel_position.x;
6355
6356 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6357
6358 // Fully visible if it can be displayed within the window (allow overlapping other
6359 // panes). However, this is only allowed if the popover starts within text_bounds.
6360 let can_position_to_the_right = x_after_longest < text_bounds.right()
6361 && x_after_longest + element_bounds.width < viewport_bounds.right();
6362
6363 let mut origin = if can_position_to_the_right {
6364 point(
6365 x_after_longest,
6366 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6367 - scroll_pixel_position.y,
6368 )
6369 } else {
6370 let cursor_row = newest_selection_head.map(|head| head.row());
6371 let above_edit = edit_start
6372 .row()
6373 .0
6374 .checked_sub(line_count as u32)
6375 .map(DisplayRow);
6376 let below_edit = Some(edit_end.row() + 1);
6377 let above_cursor =
6378 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6379 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6380
6381 // Place the edit popover adjacent to the edit if there is a location
6382 // available that is onscreen and does not obscure the cursor. Otherwise,
6383 // place it adjacent to the cursor.
6384 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6385 .into_iter()
6386 .flatten()
6387 .find(|&start_row| {
6388 let end_row = start_row + line_count as u32;
6389 visible_row_range.contains(&start_row)
6390 && visible_row_range.contains(&end_row)
6391 && cursor_row.map_or(true, |cursor_row| {
6392 !((start_row..end_row).contains(&cursor_row))
6393 })
6394 })?;
6395
6396 content_origin
6397 + point(
6398 -scroll_pixel_position.x,
6399 row_target.as_f32() * line_height - scroll_pixel_position.y,
6400 )
6401 };
6402
6403 origin.x -= BORDER_WIDTH;
6404
6405 window.defer_draw(element, origin, 1);
6406
6407 // Do not return an element, since it will already be drawn due to defer_draw.
6408 None
6409 }
6410
6411 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6412 px(30.)
6413 }
6414
6415 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6416 if self.read_only(cx) {
6417 cx.theme().players().read_only()
6418 } else {
6419 self.style.as_ref().unwrap().local_player
6420 }
6421 }
6422
6423 fn render_edit_prediction_accept_keybind(&self, window: &mut Window, cx: &App) -> Option<Div> {
6424 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6425 let accept_keystroke = accept_binding.keystroke()?;
6426
6427 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6428
6429 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6430 Color::Accent
6431 } else {
6432 Color::Muted
6433 };
6434
6435 h_flex()
6436 .px_0p5()
6437 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6438 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6439 .text_size(TextSize::XSmall.rems(cx))
6440 .child(h_flex().children(ui::render_modifiers(
6441 &accept_keystroke.modifiers,
6442 PlatformStyle::platform(),
6443 Some(modifiers_color),
6444 Some(IconSize::XSmall.rems().into()),
6445 true,
6446 )))
6447 .when(is_platform_style_mac, |parent| {
6448 parent.child(accept_keystroke.key.clone())
6449 })
6450 .when(!is_platform_style_mac, |parent| {
6451 parent.child(
6452 Key::new(
6453 util::capitalize(&accept_keystroke.key),
6454 Some(Color::Default),
6455 )
6456 .size(Some(IconSize::XSmall.rems().into())),
6457 )
6458 })
6459 .into()
6460 }
6461
6462 fn render_edit_prediction_line_popover(
6463 &self,
6464 label: impl Into<SharedString>,
6465 icon: Option<IconName>,
6466 window: &mut Window,
6467 cx: &App,
6468 ) -> Option<Div> {
6469 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6470
6471 let result = h_flex()
6472 .py_0p5()
6473 .pl_1()
6474 .pr(padding_right)
6475 .gap_1()
6476 .rounded(px(6.))
6477 .border_1()
6478 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6479 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6480 .shadow_sm()
6481 .children(self.render_edit_prediction_accept_keybind(window, cx))
6482 .child(Label::new(label).size(LabelSize::Small))
6483 .when_some(icon, |element, icon| {
6484 element.child(
6485 div()
6486 .mt(px(1.5))
6487 .child(Icon::new(icon).size(IconSize::Small)),
6488 )
6489 });
6490
6491 Some(result)
6492 }
6493
6494 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6495 let accent_color = cx.theme().colors().text_accent;
6496 let editor_bg_color = cx.theme().colors().editor_background;
6497 editor_bg_color.blend(accent_color.opacity(0.1))
6498 }
6499
6500 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6501 let accent_color = cx.theme().colors().text_accent;
6502 let editor_bg_color = cx.theme().colors().editor_background;
6503 editor_bg_color.blend(accent_color.opacity(0.6))
6504 }
6505
6506 #[allow(clippy::too_many_arguments)]
6507 fn render_edit_prediction_cursor_popover(
6508 &self,
6509 min_width: Pixels,
6510 max_width: Pixels,
6511 cursor_point: Point,
6512 style: &EditorStyle,
6513 accept_keystroke: Option<&gpui::Keystroke>,
6514 _window: &Window,
6515 cx: &mut Context<Editor>,
6516 ) -> Option<AnyElement> {
6517 let provider = self.edit_prediction_provider.as_ref()?;
6518
6519 if provider.provider.needs_terms_acceptance(cx) {
6520 return Some(
6521 h_flex()
6522 .min_w(min_width)
6523 .flex_1()
6524 .px_2()
6525 .py_1()
6526 .gap_3()
6527 .elevation_2(cx)
6528 .hover(|style| style.bg(cx.theme().colors().element_hover))
6529 .id("accept-terms")
6530 .cursor_pointer()
6531 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6532 .on_click(cx.listener(|this, _event, window, cx| {
6533 cx.stop_propagation();
6534 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6535 window.dispatch_action(
6536 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6537 cx,
6538 );
6539 }))
6540 .child(
6541 h_flex()
6542 .flex_1()
6543 .gap_2()
6544 .child(Icon::new(IconName::ZedPredict))
6545 .child(Label::new("Accept Terms of Service"))
6546 .child(div().w_full())
6547 .child(
6548 Icon::new(IconName::ArrowUpRight)
6549 .color(Color::Muted)
6550 .size(IconSize::Small),
6551 )
6552 .into_any_element(),
6553 )
6554 .into_any(),
6555 );
6556 }
6557
6558 let is_refreshing = provider.provider.is_refreshing(cx);
6559
6560 fn pending_completion_container() -> Div {
6561 h_flex()
6562 .h_full()
6563 .flex_1()
6564 .gap_2()
6565 .child(Icon::new(IconName::ZedPredict))
6566 }
6567
6568 let completion = match &self.active_inline_completion {
6569 Some(prediction) => {
6570 if !self.has_visible_completions_menu() {
6571 const RADIUS: Pixels = px(6.);
6572 const BORDER_WIDTH: Pixels = px(1.);
6573
6574 return Some(
6575 h_flex()
6576 .elevation_2(cx)
6577 .border(BORDER_WIDTH)
6578 .border_color(cx.theme().colors().border)
6579 .rounded(RADIUS)
6580 .rounded_tl(px(0.))
6581 .overflow_hidden()
6582 .child(div().px_1p5().child(match &prediction.completion {
6583 InlineCompletion::Move { target, snapshot } => {
6584 use text::ToPoint as _;
6585 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6586 {
6587 Icon::new(IconName::ZedPredictDown)
6588 } else {
6589 Icon::new(IconName::ZedPredictUp)
6590 }
6591 }
6592 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6593 }))
6594 .child(
6595 h_flex()
6596 .gap_1()
6597 .py_1()
6598 .px_2()
6599 .rounded_r(RADIUS - BORDER_WIDTH)
6600 .border_l_1()
6601 .border_color(cx.theme().colors().border)
6602 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6603 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6604 el.child(
6605 Label::new("Hold")
6606 .size(LabelSize::Small)
6607 .line_height_style(LineHeightStyle::UiLabel),
6608 )
6609 })
6610 .child(h_flex().children(ui::render_modifiers(
6611 &accept_keystroke?.modifiers,
6612 PlatformStyle::platform(),
6613 Some(Color::Default),
6614 Some(IconSize::XSmall.rems().into()),
6615 false,
6616 ))),
6617 )
6618 .into_any(),
6619 );
6620 }
6621
6622 self.render_edit_prediction_cursor_popover_preview(
6623 prediction,
6624 cursor_point,
6625 style,
6626 cx,
6627 )?
6628 }
6629
6630 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6631 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6632 stale_completion,
6633 cursor_point,
6634 style,
6635 cx,
6636 )?,
6637
6638 None => {
6639 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6640 }
6641 },
6642
6643 None => pending_completion_container().child(Label::new("No Prediction")),
6644 };
6645
6646 let completion = if is_refreshing {
6647 completion
6648 .with_animation(
6649 "loading-completion",
6650 Animation::new(Duration::from_secs(2))
6651 .repeat()
6652 .with_easing(pulsating_between(0.4, 0.8)),
6653 |label, delta| label.opacity(delta),
6654 )
6655 .into_any_element()
6656 } else {
6657 completion.into_any_element()
6658 };
6659
6660 let has_completion = self.active_inline_completion.is_some();
6661
6662 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6663 Some(
6664 h_flex()
6665 .min_w(min_width)
6666 .max_w(max_width)
6667 .flex_1()
6668 .elevation_2(cx)
6669 .border_color(cx.theme().colors().border)
6670 .child(
6671 div()
6672 .flex_1()
6673 .py_1()
6674 .px_2()
6675 .overflow_hidden()
6676 .child(completion),
6677 )
6678 .when_some(accept_keystroke, |el, accept_keystroke| {
6679 if !accept_keystroke.modifiers.modified() {
6680 return el;
6681 }
6682
6683 el.child(
6684 h_flex()
6685 .h_full()
6686 .border_l_1()
6687 .rounded_r_lg()
6688 .border_color(cx.theme().colors().border)
6689 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6690 .gap_1()
6691 .py_1()
6692 .px_2()
6693 .child(
6694 h_flex()
6695 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6696 .when(is_platform_style_mac, |parent| parent.gap_1())
6697 .child(h_flex().children(ui::render_modifiers(
6698 &accept_keystroke.modifiers,
6699 PlatformStyle::platform(),
6700 Some(if !has_completion {
6701 Color::Muted
6702 } else {
6703 Color::Default
6704 }),
6705 None,
6706 false,
6707 ))),
6708 )
6709 .child(Label::new("Preview").into_any_element())
6710 .opacity(if has_completion { 1.0 } else { 0.4 }),
6711 )
6712 })
6713 .into_any(),
6714 )
6715 }
6716
6717 fn render_edit_prediction_cursor_popover_preview(
6718 &self,
6719 completion: &InlineCompletionState,
6720 cursor_point: Point,
6721 style: &EditorStyle,
6722 cx: &mut Context<Editor>,
6723 ) -> Option<Div> {
6724 use text::ToPoint as _;
6725
6726 fn render_relative_row_jump(
6727 prefix: impl Into<String>,
6728 current_row: u32,
6729 target_row: u32,
6730 ) -> Div {
6731 let (row_diff, arrow) = if target_row < current_row {
6732 (current_row - target_row, IconName::ArrowUp)
6733 } else {
6734 (target_row - current_row, IconName::ArrowDown)
6735 };
6736
6737 h_flex()
6738 .child(
6739 Label::new(format!("{}{}", prefix.into(), row_diff))
6740 .color(Color::Muted)
6741 .size(LabelSize::Small),
6742 )
6743 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6744 }
6745
6746 match &completion.completion {
6747 InlineCompletion::Move {
6748 target, snapshot, ..
6749 } => Some(
6750 h_flex()
6751 .px_2()
6752 .gap_2()
6753 .flex_1()
6754 .child(
6755 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6756 Icon::new(IconName::ZedPredictDown)
6757 } else {
6758 Icon::new(IconName::ZedPredictUp)
6759 },
6760 )
6761 .child(Label::new("Jump to Edit")),
6762 ),
6763
6764 InlineCompletion::Edit {
6765 edits,
6766 edit_preview,
6767 snapshot,
6768 display_mode: _,
6769 } => {
6770 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6771
6772 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6773 &snapshot,
6774 &edits,
6775 edit_preview.as_ref()?,
6776 true,
6777 cx,
6778 )
6779 .first_line_preview();
6780
6781 let styled_text = gpui::StyledText::new(highlighted_edits.text)
6782 .with_highlights(&style.text, highlighted_edits.highlights);
6783
6784 let preview = h_flex()
6785 .gap_1()
6786 .min_w_16()
6787 .child(styled_text)
6788 .when(has_more_lines, |parent| parent.child("…"));
6789
6790 let left = if first_edit_row != cursor_point.row {
6791 render_relative_row_jump("", cursor_point.row, first_edit_row)
6792 .into_any_element()
6793 } else {
6794 Icon::new(IconName::ZedPredict).into_any_element()
6795 };
6796
6797 Some(
6798 h_flex()
6799 .h_full()
6800 .flex_1()
6801 .gap_2()
6802 .pr_1()
6803 .overflow_x_hidden()
6804 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6805 .child(left)
6806 .child(preview),
6807 )
6808 }
6809 }
6810 }
6811
6812 fn render_context_menu(
6813 &self,
6814 style: &EditorStyle,
6815 max_height_in_lines: u32,
6816 y_flipped: bool,
6817 window: &mut Window,
6818 cx: &mut Context<Editor>,
6819 ) -> Option<AnyElement> {
6820 let menu = self.context_menu.borrow();
6821 let menu = menu.as_ref()?;
6822 if !menu.visible() {
6823 return None;
6824 };
6825 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
6826 }
6827
6828 fn render_context_menu_aside(
6829 &mut self,
6830 max_size: Size<Pixels>,
6831 window: &mut Window,
6832 cx: &mut Context<Editor>,
6833 ) -> Option<AnyElement> {
6834 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
6835 if menu.visible() {
6836 menu.render_aside(self, max_size, window, cx)
6837 } else {
6838 None
6839 }
6840 })
6841 }
6842
6843 fn hide_context_menu(
6844 &mut self,
6845 window: &mut Window,
6846 cx: &mut Context<Self>,
6847 ) -> Option<CodeContextMenu> {
6848 cx.notify();
6849 self.completion_tasks.clear();
6850 let context_menu = self.context_menu.borrow_mut().take();
6851 self.stale_inline_completion_in_menu.take();
6852 self.update_visible_inline_completion(window, cx);
6853 context_menu
6854 }
6855
6856 fn show_snippet_choices(
6857 &mut self,
6858 choices: &Vec<String>,
6859 selection: Range<Anchor>,
6860 cx: &mut Context<Self>,
6861 ) {
6862 if selection.start.buffer_id.is_none() {
6863 return;
6864 }
6865 let buffer_id = selection.start.buffer_id.unwrap();
6866 let buffer = self.buffer().read(cx).buffer(buffer_id);
6867 let id = post_inc(&mut self.next_completion_id);
6868
6869 if let Some(buffer) = buffer {
6870 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
6871 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6872 ));
6873 }
6874 }
6875
6876 pub fn insert_snippet(
6877 &mut self,
6878 insertion_ranges: &[Range<usize>],
6879 snippet: Snippet,
6880 window: &mut Window,
6881 cx: &mut Context<Self>,
6882 ) -> Result<()> {
6883 struct Tabstop<T> {
6884 is_end_tabstop: bool,
6885 ranges: Vec<Range<T>>,
6886 choices: Option<Vec<String>>,
6887 }
6888
6889 let tabstops = self.buffer.update(cx, |buffer, cx| {
6890 let snippet_text: Arc<str> = snippet.text.clone().into();
6891 buffer.edit(
6892 insertion_ranges
6893 .iter()
6894 .cloned()
6895 .map(|range| (range, snippet_text.clone())),
6896 Some(AutoindentMode::EachLine),
6897 cx,
6898 );
6899
6900 let snapshot = &*buffer.read(cx);
6901 let snippet = &snippet;
6902 snippet
6903 .tabstops
6904 .iter()
6905 .map(|tabstop| {
6906 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6907 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6908 });
6909 let mut tabstop_ranges = tabstop
6910 .ranges
6911 .iter()
6912 .flat_map(|tabstop_range| {
6913 let mut delta = 0_isize;
6914 insertion_ranges.iter().map(move |insertion_range| {
6915 let insertion_start = insertion_range.start as isize + delta;
6916 delta +=
6917 snippet.text.len() as isize - insertion_range.len() as isize;
6918
6919 let start = ((insertion_start + tabstop_range.start) as usize)
6920 .min(snapshot.len());
6921 let end = ((insertion_start + tabstop_range.end) as usize)
6922 .min(snapshot.len());
6923 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6924 })
6925 })
6926 .collect::<Vec<_>>();
6927 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6928
6929 Tabstop {
6930 is_end_tabstop,
6931 ranges: tabstop_ranges,
6932 choices: tabstop.choices.clone(),
6933 }
6934 })
6935 .collect::<Vec<_>>()
6936 });
6937 if let Some(tabstop) = tabstops.first() {
6938 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6939 s.select_ranges(tabstop.ranges.iter().cloned());
6940 });
6941
6942 if let Some(choices) = &tabstop.choices {
6943 if let Some(selection) = tabstop.ranges.first() {
6944 self.show_snippet_choices(choices, selection.clone(), cx)
6945 }
6946 }
6947
6948 // If we're already at the last tabstop and it's at the end of the snippet,
6949 // we're done, we don't need to keep the state around.
6950 if !tabstop.is_end_tabstop {
6951 let choices = tabstops
6952 .iter()
6953 .map(|tabstop| tabstop.choices.clone())
6954 .collect();
6955
6956 let ranges = tabstops
6957 .into_iter()
6958 .map(|tabstop| tabstop.ranges)
6959 .collect::<Vec<_>>();
6960
6961 self.snippet_stack.push(SnippetState {
6962 active_index: 0,
6963 ranges,
6964 choices,
6965 });
6966 }
6967
6968 // Check whether the just-entered snippet ends with an auto-closable bracket.
6969 if self.autoclose_regions.is_empty() {
6970 let snapshot = self.buffer.read(cx).snapshot(cx);
6971 for selection in &mut self.selections.all::<Point>(cx) {
6972 let selection_head = selection.head();
6973 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6974 continue;
6975 };
6976
6977 let mut bracket_pair = None;
6978 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6979 let prev_chars = snapshot
6980 .reversed_chars_at(selection_head)
6981 .collect::<String>();
6982 for (pair, enabled) in scope.brackets() {
6983 if enabled
6984 && pair.close
6985 && prev_chars.starts_with(pair.start.as_str())
6986 && next_chars.starts_with(pair.end.as_str())
6987 {
6988 bracket_pair = Some(pair.clone());
6989 break;
6990 }
6991 }
6992 if let Some(pair) = bracket_pair {
6993 let start = snapshot.anchor_after(selection_head);
6994 let end = snapshot.anchor_after(selection_head);
6995 self.autoclose_regions.push(AutocloseRegion {
6996 selection_id: selection.id,
6997 range: start..end,
6998 pair,
6999 });
7000 }
7001 }
7002 }
7003 }
7004 Ok(())
7005 }
7006
7007 pub fn move_to_next_snippet_tabstop(
7008 &mut self,
7009 window: &mut Window,
7010 cx: &mut Context<Self>,
7011 ) -> bool {
7012 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7013 }
7014
7015 pub fn move_to_prev_snippet_tabstop(
7016 &mut self,
7017 window: &mut Window,
7018 cx: &mut Context<Self>,
7019 ) -> bool {
7020 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7021 }
7022
7023 pub fn move_to_snippet_tabstop(
7024 &mut self,
7025 bias: Bias,
7026 window: &mut Window,
7027 cx: &mut Context<Self>,
7028 ) -> bool {
7029 if let Some(mut snippet) = self.snippet_stack.pop() {
7030 match bias {
7031 Bias::Left => {
7032 if snippet.active_index > 0 {
7033 snippet.active_index -= 1;
7034 } else {
7035 self.snippet_stack.push(snippet);
7036 return false;
7037 }
7038 }
7039 Bias::Right => {
7040 if snippet.active_index + 1 < snippet.ranges.len() {
7041 snippet.active_index += 1;
7042 } else {
7043 self.snippet_stack.push(snippet);
7044 return false;
7045 }
7046 }
7047 }
7048 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7049 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7050 s.select_anchor_ranges(current_ranges.iter().cloned())
7051 });
7052
7053 if let Some(choices) = &snippet.choices[snippet.active_index] {
7054 if let Some(selection) = current_ranges.first() {
7055 self.show_snippet_choices(&choices, selection.clone(), cx);
7056 }
7057 }
7058
7059 // If snippet state is not at the last tabstop, push it back on the stack
7060 if snippet.active_index + 1 < snippet.ranges.len() {
7061 self.snippet_stack.push(snippet);
7062 }
7063 return true;
7064 }
7065 }
7066
7067 false
7068 }
7069
7070 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7071 self.transact(window, cx, |this, window, cx| {
7072 this.select_all(&SelectAll, window, cx);
7073 this.insert("", window, cx);
7074 });
7075 }
7076
7077 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7078 self.transact(window, cx, |this, window, cx| {
7079 this.select_autoclose_pair(window, cx);
7080 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7081 if !this.linked_edit_ranges.is_empty() {
7082 let selections = this.selections.all::<MultiBufferPoint>(cx);
7083 let snapshot = this.buffer.read(cx).snapshot(cx);
7084
7085 for selection in selections.iter() {
7086 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7087 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7088 if selection_start.buffer_id != selection_end.buffer_id {
7089 continue;
7090 }
7091 if let Some(ranges) =
7092 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7093 {
7094 for (buffer, entries) in ranges {
7095 linked_ranges.entry(buffer).or_default().extend(entries);
7096 }
7097 }
7098 }
7099 }
7100
7101 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7102 if !this.selections.line_mode {
7103 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7104 for selection in &mut selections {
7105 if selection.is_empty() {
7106 let old_head = selection.head();
7107 let mut new_head =
7108 movement::left(&display_map, old_head.to_display_point(&display_map))
7109 .to_point(&display_map);
7110 if let Some((buffer, line_buffer_range)) = display_map
7111 .buffer_snapshot
7112 .buffer_line_for_row(MultiBufferRow(old_head.row))
7113 {
7114 let indent_size =
7115 buffer.indent_size_for_line(line_buffer_range.start.row);
7116 let indent_len = match indent_size.kind {
7117 IndentKind::Space => {
7118 buffer.settings_at(line_buffer_range.start, cx).tab_size
7119 }
7120 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7121 };
7122 if old_head.column <= indent_size.len && old_head.column > 0 {
7123 let indent_len = indent_len.get();
7124 new_head = cmp::min(
7125 new_head,
7126 MultiBufferPoint::new(
7127 old_head.row,
7128 ((old_head.column - 1) / indent_len) * indent_len,
7129 ),
7130 );
7131 }
7132 }
7133
7134 selection.set_head(new_head, SelectionGoal::None);
7135 }
7136 }
7137 }
7138
7139 this.signature_help_state.set_backspace_pressed(true);
7140 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7141 s.select(selections)
7142 });
7143 this.insert("", window, cx);
7144 let empty_str: Arc<str> = Arc::from("");
7145 for (buffer, edits) in linked_ranges {
7146 let snapshot = buffer.read(cx).snapshot();
7147 use text::ToPoint as TP;
7148
7149 let edits = edits
7150 .into_iter()
7151 .map(|range| {
7152 let end_point = TP::to_point(&range.end, &snapshot);
7153 let mut start_point = TP::to_point(&range.start, &snapshot);
7154
7155 if end_point == start_point {
7156 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7157 .saturating_sub(1);
7158 start_point =
7159 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7160 };
7161
7162 (start_point..end_point, empty_str.clone())
7163 })
7164 .sorted_by_key(|(range, _)| range.start)
7165 .collect::<Vec<_>>();
7166 buffer.update(cx, |this, cx| {
7167 this.edit(edits, None, cx);
7168 })
7169 }
7170 this.refresh_inline_completion(true, false, window, cx);
7171 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7172 });
7173 }
7174
7175 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7176 self.transact(window, cx, |this, window, cx| {
7177 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7178 let line_mode = s.line_mode;
7179 s.move_with(|map, selection| {
7180 if selection.is_empty() && !line_mode {
7181 let cursor = movement::right(map, selection.head());
7182 selection.end = cursor;
7183 selection.reversed = true;
7184 selection.goal = SelectionGoal::None;
7185 }
7186 })
7187 });
7188 this.insert("", window, cx);
7189 this.refresh_inline_completion(true, false, window, cx);
7190 });
7191 }
7192
7193 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
7194 if self.move_to_prev_snippet_tabstop(window, cx) {
7195 return;
7196 }
7197
7198 self.outdent(&Outdent, window, cx);
7199 }
7200
7201 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7202 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7203 return;
7204 }
7205
7206 let mut selections = self.selections.all_adjusted(cx);
7207 let buffer = self.buffer.read(cx);
7208 let snapshot = buffer.snapshot(cx);
7209 let rows_iter = selections.iter().map(|s| s.head().row);
7210 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7211
7212 let mut edits = Vec::new();
7213 let mut prev_edited_row = 0;
7214 let mut row_delta = 0;
7215 for selection in &mut selections {
7216 if selection.start.row != prev_edited_row {
7217 row_delta = 0;
7218 }
7219 prev_edited_row = selection.end.row;
7220
7221 // If the selection is non-empty, then increase the indentation of the selected lines.
7222 if !selection.is_empty() {
7223 row_delta =
7224 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7225 continue;
7226 }
7227
7228 // If the selection is empty and the cursor is in the leading whitespace before the
7229 // suggested indentation, then auto-indent the line.
7230 let cursor = selection.head();
7231 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7232 if let Some(suggested_indent) =
7233 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7234 {
7235 if cursor.column < suggested_indent.len
7236 && cursor.column <= current_indent.len
7237 && current_indent.len <= suggested_indent.len
7238 {
7239 selection.start = Point::new(cursor.row, suggested_indent.len);
7240 selection.end = selection.start;
7241 if row_delta == 0 {
7242 edits.extend(Buffer::edit_for_indent_size_adjustment(
7243 cursor.row,
7244 current_indent,
7245 suggested_indent,
7246 ));
7247 row_delta = suggested_indent.len - current_indent.len;
7248 }
7249 continue;
7250 }
7251 }
7252
7253 // Otherwise, insert a hard or soft tab.
7254 let settings = buffer.settings_at(cursor, cx);
7255 let tab_size = if settings.hard_tabs {
7256 IndentSize::tab()
7257 } else {
7258 let tab_size = settings.tab_size.get();
7259 let char_column = snapshot
7260 .text_for_range(Point::new(cursor.row, 0)..cursor)
7261 .flat_map(str::chars)
7262 .count()
7263 + row_delta as usize;
7264 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7265 IndentSize::spaces(chars_to_next_tab_stop)
7266 };
7267 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7268 selection.end = selection.start;
7269 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7270 row_delta += tab_size.len;
7271 }
7272
7273 self.transact(window, cx, |this, window, cx| {
7274 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7275 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7276 s.select(selections)
7277 });
7278 this.refresh_inline_completion(true, false, window, cx);
7279 });
7280 }
7281
7282 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7283 if self.read_only(cx) {
7284 return;
7285 }
7286 let mut selections = self.selections.all::<Point>(cx);
7287 let mut prev_edited_row = 0;
7288 let mut row_delta = 0;
7289 let mut edits = Vec::new();
7290 let buffer = self.buffer.read(cx);
7291 let snapshot = buffer.snapshot(cx);
7292 for selection in &mut selections {
7293 if selection.start.row != prev_edited_row {
7294 row_delta = 0;
7295 }
7296 prev_edited_row = selection.end.row;
7297
7298 row_delta =
7299 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7300 }
7301
7302 self.transact(window, cx, |this, window, cx| {
7303 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7304 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7305 s.select(selections)
7306 });
7307 });
7308 }
7309
7310 fn indent_selection(
7311 buffer: &MultiBuffer,
7312 snapshot: &MultiBufferSnapshot,
7313 selection: &mut Selection<Point>,
7314 edits: &mut Vec<(Range<Point>, String)>,
7315 delta_for_start_row: u32,
7316 cx: &App,
7317 ) -> u32 {
7318 let settings = buffer.settings_at(selection.start, cx);
7319 let tab_size = settings.tab_size.get();
7320 let indent_kind = if settings.hard_tabs {
7321 IndentKind::Tab
7322 } else {
7323 IndentKind::Space
7324 };
7325 let mut start_row = selection.start.row;
7326 let mut end_row = selection.end.row + 1;
7327
7328 // If a selection ends at the beginning of a line, don't indent
7329 // that last line.
7330 if selection.end.column == 0 && selection.end.row > selection.start.row {
7331 end_row -= 1;
7332 }
7333
7334 // Avoid re-indenting a row that has already been indented by a
7335 // previous selection, but still update this selection's column
7336 // to reflect that indentation.
7337 if delta_for_start_row > 0 {
7338 start_row += 1;
7339 selection.start.column += delta_for_start_row;
7340 if selection.end.row == selection.start.row {
7341 selection.end.column += delta_for_start_row;
7342 }
7343 }
7344
7345 let mut delta_for_end_row = 0;
7346 let has_multiple_rows = start_row + 1 != end_row;
7347 for row in start_row..end_row {
7348 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7349 let indent_delta = match (current_indent.kind, indent_kind) {
7350 (IndentKind::Space, IndentKind::Space) => {
7351 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7352 IndentSize::spaces(columns_to_next_tab_stop)
7353 }
7354 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7355 (_, IndentKind::Tab) => IndentSize::tab(),
7356 };
7357
7358 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7359 0
7360 } else {
7361 selection.start.column
7362 };
7363 let row_start = Point::new(row, start);
7364 edits.push((
7365 row_start..row_start,
7366 indent_delta.chars().collect::<String>(),
7367 ));
7368
7369 // Update this selection's endpoints to reflect the indentation.
7370 if row == selection.start.row {
7371 selection.start.column += indent_delta.len;
7372 }
7373 if row == selection.end.row {
7374 selection.end.column += indent_delta.len;
7375 delta_for_end_row = indent_delta.len;
7376 }
7377 }
7378
7379 if selection.start.row == selection.end.row {
7380 delta_for_start_row + delta_for_end_row
7381 } else {
7382 delta_for_end_row
7383 }
7384 }
7385
7386 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7387 if self.read_only(cx) {
7388 return;
7389 }
7390 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7391 let selections = self.selections.all::<Point>(cx);
7392 let mut deletion_ranges = Vec::new();
7393 let mut last_outdent = None;
7394 {
7395 let buffer = self.buffer.read(cx);
7396 let snapshot = buffer.snapshot(cx);
7397 for selection in &selections {
7398 let settings = buffer.settings_at(selection.start, cx);
7399 let tab_size = settings.tab_size.get();
7400 let mut rows = selection.spanned_rows(false, &display_map);
7401
7402 // Avoid re-outdenting a row that has already been outdented by a
7403 // previous selection.
7404 if let Some(last_row) = last_outdent {
7405 if last_row == rows.start {
7406 rows.start = rows.start.next_row();
7407 }
7408 }
7409 let has_multiple_rows = rows.len() > 1;
7410 for row in rows.iter_rows() {
7411 let indent_size = snapshot.indent_size_for_line(row);
7412 if indent_size.len > 0 {
7413 let deletion_len = match indent_size.kind {
7414 IndentKind::Space => {
7415 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7416 if columns_to_prev_tab_stop == 0 {
7417 tab_size
7418 } else {
7419 columns_to_prev_tab_stop
7420 }
7421 }
7422 IndentKind::Tab => 1,
7423 };
7424 let start = if has_multiple_rows
7425 || deletion_len > selection.start.column
7426 || indent_size.len < selection.start.column
7427 {
7428 0
7429 } else {
7430 selection.start.column - deletion_len
7431 };
7432 deletion_ranges.push(
7433 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7434 );
7435 last_outdent = Some(row);
7436 }
7437 }
7438 }
7439 }
7440
7441 self.transact(window, cx, |this, window, cx| {
7442 this.buffer.update(cx, |buffer, cx| {
7443 let empty_str: Arc<str> = Arc::default();
7444 buffer.edit(
7445 deletion_ranges
7446 .into_iter()
7447 .map(|range| (range, empty_str.clone())),
7448 None,
7449 cx,
7450 );
7451 });
7452 let selections = this.selections.all::<usize>(cx);
7453 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7454 s.select(selections)
7455 });
7456 });
7457 }
7458
7459 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7460 if self.read_only(cx) {
7461 return;
7462 }
7463 let selections = self
7464 .selections
7465 .all::<usize>(cx)
7466 .into_iter()
7467 .map(|s| s.range());
7468
7469 self.transact(window, cx, |this, window, cx| {
7470 this.buffer.update(cx, |buffer, cx| {
7471 buffer.autoindent_ranges(selections, cx);
7472 });
7473 let selections = this.selections.all::<usize>(cx);
7474 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7475 s.select(selections)
7476 });
7477 });
7478 }
7479
7480 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7481 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7482 let selections = self.selections.all::<Point>(cx);
7483
7484 let mut new_cursors = Vec::new();
7485 let mut edit_ranges = Vec::new();
7486 let mut selections = selections.iter().peekable();
7487 while let Some(selection) = selections.next() {
7488 let mut rows = selection.spanned_rows(false, &display_map);
7489 let goal_display_column = selection.head().to_display_point(&display_map).column();
7490
7491 // Accumulate contiguous regions of rows that we want to delete.
7492 while let Some(next_selection) = selections.peek() {
7493 let next_rows = next_selection.spanned_rows(false, &display_map);
7494 if next_rows.start <= rows.end {
7495 rows.end = next_rows.end;
7496 selections.next().unwrap();
7497 } else {
7498 break;
7499 }
7500 }
7501
7502 let buffer = &display_map.buffer_snapshot;
7503 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7504 let edit_end;
7505 let cursor_buffer_row;
7506 if buffer.max_point().row >= rows.end.0 {
7507 // If there's a line after the range, delete the \n from the end of the row range
7508 // and position the cursor on the next line.
7509 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7510 cursor_buffer_row = rows.end;
7511 } else {
7512 // If there isn't a line after the range, delete the \n from the line before the
7513 // start of the row range and position the cursor there.
7514 edit_start = edit_start.saturating_sub(1);
7515 edit_end = buffer.len();
7516 cursor_buffer_row = rows.start.previous_row();
7517 }
7518
7519 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7520 *cursor.column_mut() =
7521 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7522
7523 new_cursors.push((
7524 selection.id,
7525 buffer.anchor_after(cursor.to_point(&display_map)),
7526 ));
7527 edit_ranges.push(edit_start..edit_end);
7528 }
7529
7530 self.transact(window, cx, |this, window, cx| {
7531 let buffer = this.buffer.update(cx, |buffer, cx| {
7532 let empty_str: Arc<str> = Arc::default();
7533 buffer.edit(
7534 edit_ranges
7535 .into_iter()
7536 .map(|range| (range, empty_str.clone())),
7537 None,
7538 cx,
7539 );
7540 buffer.snapshot(cx)
7541 });
7542 let new_selections = new_cursors
7543 .into_iter()
7544 .map(|(id, cursor)| {
7545 let cursor = cursor.to_point(&buffer);
7546 Selection {
7547 id,
7548 start: cursor,
7549 end: cursor,
7550 reversed: false,
7551 goal: SelectionGoal::None,
7552 }
7553 })
7554 .collect();
7555
7556 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7557 s.select(new_selections);
7558 });
7559 });
7560 }
7561
7562 pub fn join_lines_impl(
7563 &mut self,
7564 insert_whitespace: bool,
7565 window: &mut Window,
7566 cx: &mut Context<Self>,
7567 ) {
7568 if self.read_only(cx) {
7569 return;
7570 }
7571 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7572 for selection in self.selections.all::<Point>(cx) {
7573 let start = MultiBufferRow(selection.start.row);
7574 // Treat single line selections as if they include the next line. Otherwise this action
7575 // would do nothing for single line selections individual cursors.
7576 let end = if selection.start.row == selection.end.row {
7577 MultiBufferRow(selection.start.row + 1)
7578 } else {
7579 MultiBufferRow(selection.end.row)
7580 };
7581
7582 if let Some(last_row_range) = row_ranges.last_mut() {
7583 if start <= last_row_range.end {
7584 last_row_range.end = end;
7585 continue;
7586 }
7587 }
7588 row_ranges.push(start..end);
7589 }
7590
7591 let snapshot = self.buffer.read(cx).snapshot(cx);
7592 let mut cursor_positions = Vec::new();
7593 for row_range in &row_ranges {
7594 let anchor = snapshot.anchor_before(Point::new(
7595 row_range.end.previous_row().0,
7596 snapshot.line_len(row_range.end.previous_row()),
7597 ));
7598 cursor_positions.push(anchor..anchor);
7599 }
7600
7601 self.transact(window, cx, |this, window, cx| {
7602 for row_range in row_ranges.into_iter().rev() {
7603 for row in row_range.iter_rows().rev() {
7604 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7605 let next_line_row = row.next_row();
7606 let indent = snapshot.indent_size_for_line(next_line_row);
7607 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7608
7609 let replace =
7610 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7611 " "
7612 } else {
7613 ""
7614 };
7615
7616 this.buffer.update(cx, |buffer, cx| {
7617 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7618 });
7619 }
7620 }
7621
7622 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7623 s.select_anchor_ranges(cursor_positions)
7624 });
7625 });
7626 }
7627
7628 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7629 self.join_lines_impl(true, window, cx);
7630 }
7631
7632 pub fn sort_lines_case_sensitive(
7633 &mut self,
7634 _: &SortLinesCaseSensitive,
7635 window: &mut Window,
7636 cx: &mut Context<Self>,
7637 ) {
7638 self.manipulate_lines(window, cx, |lines| lines.sort())
7639 }
7640
7641 pub fn sort_lines_case_insensitive(
7642 &mut self,
7643 _: &SortLinesCaseInsensitive,
7644 window: &mut Window,
7645 cx: &mut Context<Self>,
7646 ) {
7647 self.manipulate_lines(window, cx, |lines| {
7648 lines.sort_by_key(|line| line.to_lowercase())
7649 })
7650 }
7651
7652 pub fn unique_lines_case_insensitive(
7653 &mut self,
7654 _: &UniqueLinesCaseInsensitive,
7655 window: &mut Window,
7656 cx: &mut Context<Self>,
7657 ) {
7658 self.manipulate_lines(window, cx, |lines| {
7659 let mut seen = HashSet::default();
7660 lines.retain(|line| seen.insert(line.to_lowercase()));
7661 })
7662 }
7663
7664 pub fn unique_lines_case_sensitive(
7665 &mut self,
7666 _: &UniqueLinesCaseSensitive,
7667 window: &mut Window,
7668 cx: &mut Context<Self>,
7669 ) {
7670 self.manipulate_lines(window, cx, |lines| {
7671 let mut seen = HashSet::default();
7672 lines.retain(|line| seen.insert(*line));
7673 })
7674 }
7675
7676 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7677 let Some(project) = self.project.clone() else {
7678 return;
7679 };
7680 self.reload(project, window, cx)
7681 .detach_and_notify_err(window, cx);
7682 }
7683
7684 pub fn restore_file(
7685 &mut self,
7686 _: &::git::RestoreFile,
7687 window: &mut Window,
7688 cx: &mut Context<Self>,
7689 ) {
7690 let mut buffer_ids = HashSet::default();
7691 let snapshot = self.buffer().read(cx).snapshot(cx);
7692 for selection in self.selections.all::<usize>(cx) {
7693 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7694 }
7695
7696 let buffer = self.buffer().read(cx);
7697 let ranges = buffer_ids
7698 .into_iter()
7699 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7700 .collect::<Vec<_>>();
7701
7702 self.restore_hunks_in_ranges(ranges, window, cx);
7703 }
7704
7705 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7706 let selections = self
7707 .selections
7708 .all(cx)
7709 .into_iter()
7710 .map(|s| s.range())
7711 .collect();
7712 self.restore_hunks_in_ranges(selections, window, cx);
7713 }
7714
7715 fn restore_hunks_in_ranges(
7716 &mut self,
7717 ranges: Vec<Range<Point>>,
7718 window: &mut Window,
7719 cx: &mut Context<Editor>,
7720 ) {
7721 let mut revert_changes = HashMap::default();
7722 let snapshot = self.buffer.read(cx).snapshot(cx);
7723 let Some(project) = &self.project else {
7724 return;
7725 };
7726
7727 let chunk_by = self
7728 .snapshot(window, cx)
7729 .hunks_for_ranges(ranges.into_iter())
7730 .into_iter()
7731 .chunk_by(|hunk| hunk.buffer_id);
7732 for (buffer_id, hunks) in &chunk_by {
7733 let hunks = hunks.collect::<Vec<_>>();
7734 for hunk in &hunks {
7735 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7736 }
7737 Self::do_stage_or_unstage(
7738 project,
7739 false,
7740 buffer_id,
7741 hunks.into_iter(),
7742 &snapshot,
7743 window,
7744 cx,
7745 );
7746 }
7747 drop(chunk_by);
7748 if !revert_changes.is_empty() {
7749 self.transact(window, cx, |editor, window, cx| {
7750 editor.restore(revert_changes, window, cx);
7751 });
7752 }
7753 }
7754
7755 pub fn open_active_item_in_terminal(
7756 &mut self,
7757 _: &OpenInTerminal,
7758 window: &mut Window,
7759 cx: &mut Context<Self>,
7760 ) {
7761 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7762 let project_path = buffer.read(cx).project_path(cx)?;
7763 let project = self.project.as_ref()?.read(cx);
7764 let entry = project.entry_for_path(&project_path, cx)?;
7765 let parent = match &entry.canonical_path {
7766 Some(canonical_path) => canonical_path.to_path_buf(),
7767 None => project.absolute_path(&project_path, cx)?,
7768 }
7769 .parent()?
7770 .to_path_buf();
7771 Some(parent)
7772 }) {
7773 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7774 }
7775 }
7776
7777 pub fn prepare_restore_change(
7778 &self,
7779 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7780 hunk: &MultiBufferDiffHunk,
7781 cx: &mut App,
7782 ) -> Option<()> {
7783 let buffer = self.buffer.read(cx);
7784 let diff = buffer.diff_for(hunk.buffer_id)?;
7785 let buffer = buffer.buffer(hunk.buffer_id)?;
7786 let buffer = buffer.read(cx);
7787 let original_text = diff
7788 .read(cx)
7789 .base_text()
7790 .as_ref()?
7791 .as_rope()
7792 .slice(hunk.diff_base_byte_range.clone());
7793 let buffer_snapshot = buffer.snapshot();
7794 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
7795 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
7796 probe
7797 .0
7798 .start
7799 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
7800 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
7801 }) {
7802 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
7803 Some(())
7804 } else {
7805 None
7806 }
7807 }
7808
7809 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
7810 self.manipulate_lines(window, cx, |lines| lines.reverse())
7811 }
7812
7813 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
7814 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
7815 }
7816
7817 fn manipulate_lines<Fn>(
7818 &mut self,
7819 window: &mut Window,
7820 cx: &mut Context<Self>,
7821 mut callback: Fn,
7822 ) where
7823 Fn: FnMut(&mut Vec<&str>),
7824 {
7825 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7826 let buffer = self.buffer.read(cx).snapshot(cx);
7827
7828 let mut edits = Vec::new();
7829
7830 let selections = self.selections.all::<Point>(cx);
7831 let mut selections = selections.iter().peekable();
7832 let mut contiguous_row_selections = Vec::new();
7833 let mut new_selections = Vec::new();
7834 let mut added_lines = 0;
7835 let mut removed_lines = 0;
7836
7837 while let Some(selection) = selections.next() {
7838 let (start_row, end_row) = consume_contiguous_rows(
7839 &mut contiguous_row_selections,
7840 selection,
7841 &display_map,
7842 &mut selections,
7843 );
7844
7845 let start_point = Point::new(start_row.0, 0);
7846 let end_point = Point::new(
7847 end_row.previous_row().0,
7848 buffer.line_len(end_row.previous_row()),
7849 );
7850 let text = buffer
7851 .text_for_range(start_point..end_point)
7852 .collect::<String>();
7853
7854 let mut lines = text.split('\n').collect_vec();
7855
7856 let lines_before = lines.len();
7857 callback(&mut lines);
7858 let lines_after = lines.len();
7859
7860 edits.push((start_point..end_point, lines.join("\n")));
7861
7862 // Selections must change based on added and removed line count
7863 let start_row =
7864 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
7865 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
7866 new_selections.push(Selection {
7867 id: selection.id,
7868 start: start_row,
7869 end: end_row,
7870 goal: SelectionGoal::None,
7871 reversed: selection.reversed,
7872 });
7873
7874 if lines_after > lines_before {
7875 added_lines += lines_after - lines_before;
7876 } else if lines_before > lines_after {
7877 removed_lines += lines_before - lines_after;
7878 }
7879 }
7880
7881 self.transact(window, cx, |this, window, cx| {
7882 let buffer = this.buffer.update(cx, |buffer, cx| {
7883 buffer.edit(edits, None, cx);
7884 buffer.snapshot(cx)
7885 });
7886
7887 // Recalculate offsets on newly edited buffer
7888 let new_selections = new_selections
7889 .iter()
7890 .map(|s| {
7891 let start_point = Point::new(s.start.0, 0);
7892 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
7893 Selection {
7894 id: s.id,
7895 start: buffer.point_to_offset(start_point),
7896 end: buffer.point_to_offset(end_point),
7897 goal: s.goal,
7898 reversed: s.reversed,
7899 }
7900 })
7901 .collect();
7902
7903 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7904 s.select(new_selections);
7905 });
7906
7907 this.request_autoscroll(Autoscroll::fit(), cx);
7908 });
7909 }
7910
7911 pub fn convert_to_upper_case(
7912 &mut self,
7913 _: &ConvertToUpperCase,
7914 window: &mut Window,
7915 cx: &mut Context<Self>,
7916 ) {
7917 self.manipulate_text(window, cx, |text| text.to_uppercase())
7918 }
7919
7920 pub fn convert_to_lower_case(
7921 &mut self,
7922 _: &ConvertToLowerCase,
7923 window: &mut Window,
7924 cx: &mut Context<Self>,
7925 ) {
7926 self.manipulate_text(window, cx, |text| text.to_lowercase())
7927 }
7928
7929 pub fn convert_to_title_case(
7930 &mut self,
7931 _: &ConvertToTitleCase,
7932 window: &mut Window,
7933 cx: &mut Context<Self>,
7934 ) {
7935 self.manipulate_text(window, cx, |text| {
7936 text.split('\n')
7937 .map(|line| line.to_case(Case::Title))
7938 .join("\n")
7939 })
7940 }
7941
7942 pub fn convert_to_snake_case(
7943 &mut self,
7944 _: &ConvertToSnakeCase,
7945 window: &mut Window,
7946 cx: &mut Context<Self>,
7947 ) {
7948 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7949 }
7950
7951 pub fn convert_to_kebab_case(
7952 &mut self,
7953 _: &ConvertToKebabCase,
7954 window: &mut Window,
7955 cx: &mut Context<Self>,
7956 ) {
7957 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7958 }
7959
7960 pub fn convert_to_upper_camel_case(
7961 &mut self,
7962 _: &ConvertToUpperCamelCase,
7963 window: &mut Window,
7964 cx: &mut Context<Self>,
7965 ) {
7966 self.manipulate_text(window, cx, |text| {
7967 text.split('\n')
7968 .map(|line| line.to_case(Case::UpperCamel))
7969 .join("\n")
7970 })
7971 }
7972
7973 pub fn convert_to_lower_camel_case(
7974 &mut self,
7975 _: &ConvertToLowerCamelCase,
7976 window: &mut Window,
7977 cx: &mut Context<Self>,
7978 ) {
7979 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7980 }
7981
7982 pub fn convert_to_opposite_case(
7983 &mut self,
7984 _: &ConvertToOppositeCase,
7985 window: &mut Window,
7986 cx: &mut Context<Self>,
7987 ) {
7988 self.manipulate_text(window, cx, |text| {
7989 text.chars()
7990 .fold(String::with_capacity(text.len()), |mut t, c| {
7991 if c.is_uppercase() {
7992 t.extend(c.to_lowercase());
7993 } else {
7994 t.extend(c.to_uppercase());
7995 }
7996 t
7997 })
7998 })
7999 }
8000
8001 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8002 where
8003 Fn: FnMut(&str) -> String,
8004 {
8005 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8006 let buffer = self.buffer.read(cx).snapshot(cx);
8007
8008 let mut new_selections = Vec::new();
8009 let mut edits = Vec::new();
8010 let mut selection_adjustment = 0i32;
8011
8012 for selection in self.selections.all::<usize>(cx) {
8013 let selection_is_empty = selection.is_empty();
8014
8015 let (start, end) = if selection_is_empty {
8016 let word_range = movement::surrounding_word(
8017 &display_map,
8018 selection.start.to_display_point(&display_map),
8019 );
8020 let start = word_range.start.to_offset(&display_map, Bias::Left);
8021 let end = word_range.end.to_offset(&display_map, Bias::Left);
8022 (start, end)
8023 } else {
8024 (selection.start, selection.end)
8025 };
8026
8027 let text = buffer.text_for_range(start..end).collect::<String>();
8028 let old_length = text.len() as i32;
8029 let text = callback(&text);
8030
8031 new_selections.push(Selection {
8032 start: (start as i32 - selection_adjustment) as usize,
8033 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8034 goal: SelectionGoal::None,
8035 ..selection
8036 });
8037
8038 selection_adjustment += old_length - text.len() as i32;
8039
8040 edits.push((start..end, text));
8041 }
8042
8043 self.transact(window, cx, |this, window, cx| {
8044 this.buffer.update(cx, |buffer, cx| {
8045 buffer.edit(edits, None, cx);
8046 });
8047
8048 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8049 s.select(new_selections);
8050 });
8051
8052 this.request_autoscroll(Autoscroll::fit(), cx);
8053 });
8054 }
8055
8056 pub fn duplicate(
8057 &mut self,
8058 upwards: bool,
8059 whole_lines: bool,
8060 window: &mut Window,
8061 cx: &mut Context<Self>,
8062 ) {
8063 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8064 let buffer = &display_map.buffer_snapshot;
8065 let selections = self.selections.all::<Point>(cx);
8066
8067 let mut edits = Vec::new();
8068 let mut selections_iter = selections.iter().peekable();
8069 while let Some(selection) = selections_iter.next() {
8070 let mut rows = selection.spanned_rows(false, &display_map);
8071 // duplicate line-wise
8072 if whole_lines || selection.start == selection.end {
8073 // Avoid duplicating the same lines twice.
8074 while let Some(next_selection) = selections_iter.peek() {
8075 let next_rows = next_selection.spanned_rows(false, &display_map);
8076 if next_rows.start < rows.end {
8077 rows.end = next_rows.end;
8078 selections_iter.next().unwrap();
8079 } else {
8080 break;
8081 }
8082 }
8083
8084 // Copy the text from the selected row region and splice it either at the start
8085 // or end of the region.
8086 let start = Point::new(rows.start.0, 0);
8087 let end = Point::new(
8088 rows.end.previous_row().0,
8089 buffer.line_len(rows.end.previous_row()),
8090 );
8091 let text = buffer
8092 .text_for_range(start..end)
8093 .chain(Some("\n"))
8094 .collect::<String>();
8095 let insert_location = if upwards {
8096 Point::new(rows.end.0, 0)
8097 } else {
8098 start
8099 };
8100 edits.push((insert_location..insert_location, text));
8101 } else {
8102 // duplicate character-wise
8103 let start = selection.start;
8104 let end = selection.end;
8105 let text = buffer.text_for_range(start..end).collect::<String>();
8106 edits.push((selection.end..selection.end, text));
8107 }
8108 }
8109
8110 self.transact(window, cx, |this, _, cx| {
8111 this.buffer.update(cx, |buffer, cx| {
8112 buffer.edit(edits, None, cx);
8113 });
8114
8115 this.request_autoscroll(Autoscroll::fit(), cx);
8116 });
8117 }
8118
8119 pub fn duplicate_line_up(
8120 &mut self,
8121 _: &DuplicateLineUp,
8122 window: &mut Window,
8123 cx: &mut Context<Self>,
8124 ) {
8125 self.duplicate(true, true, window, cx);
8126 }
8127
8128 pub fn duplicate_line_down(
8129 &mut self,
8130 _: &DuplicateLineDown,
8131 window: &mut Window,
8132 cx: &mut Context<Self>,
8133 ) {
8134 self.duplicate(false, true, window, cx);
8135 }
8136
8137 pub fn duplicate_selection(
8138 &mut self,
8139 _: &DuplicateSelection,
8140 window: &mut Window,
8141 cx: &mut Context<Self>,
8142 ) {
8143 self.duplicate(false, false, window, cx);
8144 }
8145
8146 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8147 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8148 let buffer = self.buffer.read(cx).snapshot(cx);
8149
8150 let mut edits = Vec::new();
8151 let mut unfold_ranges = Vec::new();
8152 let mut refold_creases = Vec::new();
8153
8154 let selections = self.selections.all::<Point>(cx);
8155 let mut selections = selections.iter().peekable();
8156 let mut contiguous_row_selections = Vec::new();
8157 let mut new_selections = Vec::new();
8158
8159 while let Some(selection) = selections.next() {
8160 // Find all the selections that span a contiguous row range
8161 let (start_row, end_row) = consume_contiguous_rows(
8162 &mut contiguous_row_selections,
8163 selection,
8164 &display_map,
8165 &mut selections,
8166 );
8167
8168 // Move the text spanned by the row range to be before the line preceding the row range
8169 if start_row.0 > 0 {
8170 let range_to_move = Point::new(
8171 start_row.previous_row().0,
8172 buffer.line_len(start_row.previous_row()),
8173 )
8174 ..Point::new(
8175 end_row.previous_row().0,
8176 buffer.line_len(end_row.previous_row()),
8177 );
8178 let insertion_point = display_map
8179 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8180 .0;
8181
8182 // Don't move lines across excerpts
8183 if buffer
8184 .excerpt_containing(insertion_point..range_to_move.end)
8185 .is_some()
8186 {
8187 let text = buffer
8188 .text_for_range(range_to_move.clone())
8189 .flat_map(|s| s.chars())
8190 .skip(1)
8191 .chain(['\n'])
8192 .collect::<String>();
8193
8194 edits.push((
8195 buffer.anchor_after(range_to_move.start)
8196 ..buffer.anchor_before(range_to_move.end),
8197 String::new(),
8198 ));
8199 let insertion_anchor = buffer.anchor_after(insertion_point);
8200 edits.push((insertion_anchor..insertion_anchor, text));
8201
8202 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8203
8204 // Move selections up
8205 new_selections.extend(contiguous_row_selections.drain(..).map(
8206 |mut selection| {
8207 selection.start.row -= row_delta;
8208 selection.end.row -= row_delta;
8209 selection
8210 },
8211 ));
8212
8213 // Move folds up
8214 unfold_ranges.push(range_to_move.clone());
8215 for fold in display_map.folds_in_range(
8216 buffer.anchor_before(range_to_move.start)
8217 ..buffer.anchor_after(range_to_move.end),
8218 ) {
8219 let mut start = fold.range.start.to_point(&buffer);
8220 let mut end = fold.range.end.to_point(&buffer);
8221 start.row -= row_delta;
8222 end.row -= row_delta;
8223 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8224 }
8225 }
8226 }
8227
8228 // If we didn't move line(s), preserve the existing selections
8229 new_selections.append(&mut contiguous_row_selections);
8230 }
8231
8232 self.transact(window, cx, |this, window, cx| {
8233 this.unfold_ranges(&unfold_ranges, true, true, cx);
8234 this.buffer.update(cx, |buffer, cx| {
8235 for (range, text) in edits {
8236 buffer.edit([(range, text)], None, cx);
8237 }
8238 });
8239 this.fold_creases(refold_creases, true, window, cx);
8240 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8241 s.select(new_selections);
8242 })
8243 });
8244 }
8245
8246 pub fn move_line_down(
8247 &mut self,
8248 _: &MoveLineDown,
8249 window: &mut Window,
8250 cx: &mut Context<Self>,
8251 ) {
8252 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8253 let buffer = self.buffer.read(cx).snapshot(cx);
8254
8255 let mut edits = Vec::new();
8256 let mut unfold_ranges = Vec::new();
8257 let mut refold_creases = Vec::new();
8258
8259 let selections = self.selections.all::<Point>(cx);
8260 let mut selections = selections.iter().peekable();
8261 let mut contiguous_row_selections = Vec::new();
8262 let mut new_selections = Vec::new();
8263
8264 while let Some(selection) = selections.next() {
8265 // Find all the selections that span a contiguous row range
8266 let (start_row, end_row) = consume_contiguous_rows(
8267 &mut contiguous_row_selections,
8268 selection,
8269 &display_map,
8270 &mut selections,
8271 );
8272
8273 // Move the text spanned by the row range to be after the last line of the row range
8274 if end_row.0 <= buffer.max_point().row {
8275 let range_to_move =
8276 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8277 let insertion_point = display_map
8278 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8279 .0;
8280
8281 // Don't move lines across excerpt boundaries
8282 if buffer
8283 .excerpt_containing(range_to_move.start..insertion_point)
8284 .is_some()
8285 {
8286 let mut text = String::from("\n");
8287 text.extend(buffer.text_for_range(range_to_move.clone()));
8288 text.pop(); // Drop trailing newline
8289 edits.push((
8290 buffer.anchor_after(range_to_move.start)
8291 ..buffer.anchor_before(range_to_move.end),
8292 String::new(),
8293 ));
8294 let insertion_anchor = buffer.anchor_after(insertion_point);
8295 edits.push((insertion_anchor..insertion_anchor, text));
8296
8297 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8298
8299 // Move selections down
8300 new_selections.extend(contiguous_row_selections.drain(..).map(
8301 |mut selection| {
8302 selection.start.row += row_delta;
8303 selection.end.row += row_delta;
8304 selection
8305 },
8306 ));
8307
8308 // Move folds down
8309 unfold_ranges.push(range_to_move.clone());
8310 for fold in display_map.folds_in_range(
8311 buffer.anchor_before(range_to_move.start)
8312 ..buffer.anchor_after(range_to_move.end),
8313 ) {
8314 let mut start = fold.range.start.to_point(&buffer);
8315 let mut end = fold.range.end.to_point(&buffer);
8316 start.row += row_delta;
8317 end.row += row_delta;
8318 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8319 }
8320 }
8321 }
8322
8323 // If we didn't move line(s), preserve the existing selections
8324 new_selections.append(&mut contiguous_row_selections);
8325 }
8326
8327 self.transact(window, cx, |this, window, cx| {
8328 this.unfold_ranges(&unfold_ranges, true, true, cx);
8329 this.buffer.update(cx, |buffer, cx| {
8330 for (range, text) in edits {
8331 buffer.edit([(range, text)], None, cx);
8332 }
8333 });
8334 this.fold_creases(refold_creases, true, window, cx);
8335 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8336 s.select(new_selections)
8337 });
8338 });
8339 }
8340
8341 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8342 let text_layout_details = &self.text_layout_details(window);
8343 self.transact(window, cx, |this, window, cx| {
8344 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8345 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8346 let line_mode = s.line_mode;
8347 s.move_with(|display_map, selection| {
8348 if !selection.is_empty() || line_mode {
8349 return;
8350 }
8351
8352 let mut head = selection.head();
8353 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8354 if head.column() == display_map.line_len(head.row()) {
8355 transpose_offset = display_map
8356 .buffer_snapshot
8357 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8358 }
8359
8360 if transpose_offset == 0 {
8361 return;
8362 }
8363
8364 *head.column_mut() += 1;
8365 head = display_map.clip_point(head, Bias::Right);
8366 let goal = SelectionGoal::HorizontalPosition(
8367 display_map
8368 .x_for_display_point(head, text_layout_details)
8369 .into(),
8370 );
8371 selection.collapse_to(head, goal);
8372
8373 let transpose_start = display_map
8374 .buffer_snapshot
8375 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8376 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8377 let transpose_end = display_map
8378 .buffer_snapshot
8379 .clip_offset(transpose_offset + 1, Bias::Right);
8380 if let Some(ch) =
8381 display_map.buffer_snapshot.chars_at(transpose_start).next()
8382 {
8383 edits.push((transpose_start..transpose_offset, String::new()));
8384 edits.push((transpose_end..transpose_end, ch.to_string()));
8385 }
8386 }
8387 });
8388 edits
8389 });
8390 this.buffer
8391 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8392 let selections = this.selections.all::<usize>(cx);
8393 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8394 s.select(selections);
8395 });
8396 });
8397 }
8398
8399 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8400 self.rewrap_impl(IsVimMode::No, cx)
8401 }
8402
8403 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
8404 let buffer = self.buffer.read(cx).snapshot(cx);
8405 let selections = self.selections.all::<Point>(cx);
8406 let mut selections = selections.iter().peekable();
8407
8408 let mut edits = Vec::new();
8409 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8410
8411 while let Some(selection) = selections.next() {
8412 let mut start_row = selection.start.row;
8413 let mut end_row = selection.end.row;
8414
8415 // Skip selections that overlap with a range that has already been rewrapped.
8416 let selection_range = start_row..end_row;
8417 if rewrapped_row_ranges
8418 .iter()
8419 .any(|range| range.overlaps(&selection_range))
8420 {
8421 continue;
8422 }
8423
8424 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
8425
8426 // Since not all lines in the selection may be at the same indent
8427 // level, choose the indent size that is the most common between all
8428 // of the lines.
8429 //
8430 // If there is a tie, we use the deepest indent.
8431 let (indent_size, indent_end) = {
8432 let mut indent_size_occurrences = HashMap::default();
8433 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8434
8435 for row in start_row..=end_row {
8436 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8437 rows_by_indent_size.entry(indent).or_default().push(row);
8438 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8439 }
8440
8441 let indent_size = indent_size_occurrences
8442 .into_iter()
8443 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8444 .map(|(indent, _)| indent)
8445 .unwrap_or_default();
8446 let row = rows_by_indent_size[&indent_size][0];
8447 let indent_end = Point::new(row, indent_size.len);
8448
8449 (indent_size, indent_end)
8450 };
8451
8452 let mut line_prefix = indent_size.chars().collect::<String>();
8453
8454 let mut inside_comment = false;
8455 if let Some(comment_prefix) =
8456 buffer
8457 .language_scope_at(selection.head())
8458 .and_then(|language| {
8459 language
8460 .line_comment_prefixes()
8461 .iter()
8462 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8463 .cloned()
8464 })
8465 {
8466 line_prefix.push_str(&comment_prefix);
8467 inside_comment = true;
8468 }
8469
8470 let language_settings = buffer.settings_at(selection.head(), cx);
8471 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8472 RewrapBehavior::InComments => inside_comment,
8473 RewrapBehavior::InSelections => !selection.is_empty(),
8474 RewrapBehavior::Anywhere => true,
8475 };
8476
8477 let should_rewrap = is_vim_mode == IsVimMode::Yes || allow_rewrap_based_on_language;
8478 if !should_rewrap {
8479 continue;
8480 }
8481
8482 if selection.is_empty() {
8483 'expand_upwards: while start_row > 0 {
8484 let prev_row = start_row - 1;
8485 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8486 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8487 {
8488 start_row = prev_row;
8489 } else {
8490 break 'expand_upwards;
8491 }
8492 }
8493
8494 'expand_downwards: while end_row < buffer.max_point().row {
8495 let next_row = end_row + 1;
8496 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8497 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8498 {
8499 end_row = next_row;
8500 } else {
8501 break 'expand_downwards;
8502 }
8503 }
8504 }
8505
8506 let start = Point::new(start_row, 0);
8507 let start_offset = start.to_offset(&buffer);
8508 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8509 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8510 let Some(lines_without_prefixes) = selection_text
8511 .lines()
8512 .map(|line| {
8513 line.strip_prefix(&line_prefix)
8514 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8515 .ok_or_else(|| {
8516 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8517 })
8518 })
8519 .collect::<Result<Vec<_>, _>>()
8520 .log_err()
8521 else {
8522 continue;
8523 };
8524
8525 let wrap_column = buffer
8526 .settings_at(Point::new(start_row, 0), cx)
8527 .preferred_line_length as usize;
8528 let wrapped_text = wrap_with_prefix(
8529 line_prefix,
8530 lines_without_prefixes.join(" "),
8531 wrap_column,
8532 tab_size,
8533 );
8534
8535 // TODO: should always use char-based diff while still supporting cursor behavior that
8536 // matches vim.
8537 let mut diff_options = DiffOptions::default();
8538 if is_vim_mode == IsVimMode::Yes {
8539 diff_options.max_word_diff_len = 0;
8540 diff_options.max_word_diff_line_count = 0;
8541 } else {
8542 diff_options.max_word_diff_len = usize::MAX;
8543 diff_options.max_word_diff_line_count = usize::MAX;
8544 }
8545
8546 for (old_range, new_text) in
8547 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8548 {
8549 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8550 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8551 edits.push((edit_start..edit_end, new_text));
8552 }
8553
8554 rewrapped_row_ranges.push(start_row..=end_row);
8555 }
8556
8557 self.buffer
8558 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8559 }
8560
8561 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8562 let mut text = String::new();
8563 let buffer = self.buffer.read(cx).snapshot(cx);
8564 let mut selections = self.selections.all::<Point>(cx);
8565 let mut clipboard_selections = Vec::with_capacity(selections.len());
8566 {
8567 let max_point = buffer.max_point();
8568 let mut is_first = true;
8569 for selection in &mut selections {
8570 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8571 if is_entire_line {
8572 selection.start = Point::new(selection.start.row, 0);
8573 if !selection.is_empty() && selection.end.column == 0 {
8574 selection.end = cmp::min(max_point, selection.end);
8575 } else {
8576 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8577 }
8578 selection.goal = SelectionGoal::None;
8579 }
8580 if is_first {
8581 is_first = false;
8582 } else {
8583 text += "\n";
8584 }
8585 let mut len = 0;
8586 for chunk in buffer.text_for_range(selection.start..selection.end) {
8587 text.push_str(chunk);
8588 len += chunk.len();
8589 }
8590 clipboard_selections.push(ClipboardSelection {
8591 len,
8592 is_entire_line,
8593 start_column: selection.start.column,
8594 });
8595 }
8596 }
8597
8598 self.transact(window, cx, |this, window, cx| {
8599 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8600 s.select(selections);
8601 });
8602 this.insert("", window, cx);
8603 });
8604 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8605 }
8606
8607 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8608 let item = self.cut_common(window, cx);
8609 cx.write_to_clipboard(item);
8610 }
8611
8612 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8613 self.change_selections(None, window, cx, |s| {
8614 s.move_with(|snapshot, sel| {
8615 if sel.is_empty() {
8616 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8617 }
8618 });
8619 });
8620 let item = self.cut_common(window, cx);
8621 cx.set_global(KillRing(item))
8622 }
8623
8624 pub fn kill_ring_yank(
8625 &mut self,
8626 _: &KillRingYank,
8627 window: &mut Window,
8628 cx: &mut Context<Self>,
8629 ) {
8630 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8631 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8632 (kill_ring.text().to_string(), kill_ring.metadata_json())
8633 } else {
8634 return;
8635 }
8636 } else {
8637 return;
8638 };
8639 self.do_paste(&text, metadata, false, window, cx);
8640 }
8641
8642 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8643 let selections = self.selections.all::<Point>(cx);
8644 let buffer = self.buffer.read(cx).read(cx);
8645 let mut text = String::new();
8646
8647 let mut clipboard_selections = Vec::with_capacity(selections.len());
8648 {
8649 let max_point = buffer.max_point();
8650 let mut is_first = true;
8651 for selection in selections.iter() {
8652 let mut start = selection.start;
8653 let mut end = selection.end;
8654 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8655 if is_entire_line {
8656 start = Point::new(start.row, 0);
8657 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8658 }
8659 if is_first {
8660 is_first = false;
8661 } else {
8662 text += "\n";
8663 }
8664 let mut len = 0;
8665 for chunk in buffer.text_for_range(start..end) {
8666 text.push_str(chunk);
8667 len += chunk.len();
8668 }
8669 clipboard_selections.push(ClipboardSelection {
8670 len,
8671 is_entire_line,
8672 start_column: start.column,
8673 });
8674 }
8675 }
8676
8677 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8678 text,
8679 clipboard_selections,
8680 ));
8681 }
8682
8683 pub fn do_paste(
8684 &mut self,
8685 text: &String,
8686 clipboard_selections: Option<Vec<ClipboardSelection>>,
8687 handle_entire_lines: bool,
8688 window: &mut Window,
8689 cx: &mut Context<Self>,
8690 ) {
8691 if self.read_only(cx) {
8692 return;
8693 }
8694
8695 let clipboard_text = Cow::Borrowed(text);
8696
8697 self.transact(window, cx, |this, window, cx| {
8698 if let Some(mut clipboard_selections) = clipboard_selections {
8699 let old_selections = this.selections.all::<usize>(cx);
8700 let all_selections_were_entire_line =
8701 clipboard_selections.iter().all(|s| s.is_entire_line);
8702 let first_selection_start_column =
8703 clipboard_selections.first().map(|s| s.start_column);
8704 if clipboard_selections.len() != old_selections.len() {
8705 clipboard_selections.drain(..);
8706 }
8707 let cursor_offset = this.selections.last::<usize>(cx).head();
8708 let mut auto_indent_on_paste = true;
8709
8710 this.buffer.update(cx, |buffer, cx| {
8711 let snapshot = buffer.read(cx);
8712 auto_indent_on_paste =
8713 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
8714
8715 let mut start_offset = 0;
8716 let mut edits = Vec::new();
8717 let mut original_start_columns = Vec::new();
8718 for (ix, selection) in old_selections.iter().enumerate() {
8719 let to_insert;
8720 let entire_line;
8721 let original_start_column;
8722 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8723 let end_offset = start_offset + clipboard_selection.len;
8724 to_insert = &clipboard_text[start_offset..end_offset];
8725 entire_line = clipboard_selection.is_entire_line;
8726 start_offset = end_offset + 1;
8727 original_start_column = Some(clipboard_selection.start_column);
8728 } else {
8729 to_insert = clipboard_text.as_str();
8730 entire_line = all_selections_were_entire_line;
8731 original_start_column = first_selection_start_column
8732 }
8733
8734 // If the corresponding selection was empty when this slice of the
8735 // clipboard text was written, then the entire line containing the
8736 // selection was copied. If this selection is also currently empty,
8737 // then paste the line before the current line of the buffer.
8738 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8739 let column = selection.start.to_point(&snapshot).column as usize;
8740 let line_start = selection.start - column;
8741 line_start..line_start
8742 } else {
8743 selection.range()
8744 };
8745
8746 edits.push((range, to_insert));
8747 original_start_columns.extend(original_start_column);
8748 }
8749 drop(snapshot);
8750
8751 buffer.edit(
8752 edits,
8753 if auto_indent_on_paste {
8754 Some(AutoindentMode::Block {
8755 original_start_columns,
8756 })
8757 } else {
8758 None
8759 },
8760 cx,
8761 );
8762 });
8763
8764 let selections = this.selections.all::<usize>(cx);
8765 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8766 s.select(selections)
8767 });
8768 } else {
8769 this.insert(&clipboard_text, window, cx);
8770 }
8771 });
8772 }
8773
8774 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8775 if let Some(item) = cx.read_from_clipboard() {
8776 let entries = item.entries();
8777
8778 match entries.first() {
8779 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8780 // of all the pasted entries.
8781 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8782 .do_paste(
8783 clipboard_string.text(),
8784 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
8785 true,
8786 window,
8787 cx,
8788 ),
8789 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
8790 }
8791 }
8792 }
8793
8794 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
8795 if self.read_only(cx) {
8796 return;
8797 }
8798
8799 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
8800 if let Some((selections, _)) =
8801 self.selection_history.transaction(transaction_id).cloned()
8802 {
8803 self.change_selections(None, window, cx, |s| {
8804 s.select_anchors(selections.to_vec());
8805 });
8806 } else {
8807 log::error!(
8808 "No entry in selection_history found for undo. \
8809 This may correspond to a bug where undo does not update the selection. \
8810 If this is occurring, please add details to \
8811 https://github.com/zed-industries/zed/issues/22692"
8812 );
8813 }
8814 self.request_autoscroll(Autoscroll::fit(), cx);
8815 self.unmark_text(window, cx);
8816 self.refresh_inline_completion(true, false, window, cx);
8817 cx.emit(EditorEvent::Edited { transaction_id });
8818 cx.emit(EditorEvent::TransactionUndone { transaction_id });
8819 }
8820 }
8821
8822 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
8823 if self.read_only(cx) {
8824 return;
8825 }
8826
8827 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
8828 if let Some((_, Some(selections))) =
8829 self.selection_history.transaction(transaction_id).cloned()
8830 {
8831 self.change_selections(None, window, cx, |s| {
8832 s.select_anchors(selections.to_vec());
8833 });
8834 } else {
8835 log::error!(
8836 "No entry in selection_history found for redo. \
8837 This may correspond to a bug where undo does not update the selection. \
8838 If this is occurring, please add details to \
8839 https://github.com/zed-industries/zed/issues/22692"
8840 );
8841 }
8842 self.request_autoscroll(Autoscroll::fit(), cx);
8843 self.unmark_text(window, cx);
8844 self.refresh_inline_completion(true, false, window, cx);
8845 cx.emit(EditorEvent::Edited { transaction_id });
8846 }
8847 }
8848
8849 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
8850 self.buffer
8851 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
8852 }
8853
8854 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
8855 self.buffer
8856 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
8857 }
8858
8859 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
8860 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8861 let line_mode = s.line_mode;
8862 s.move_with(|map, selection| {
8863 let cursor = if selection.is_empty() && !line_mode {
8864 movement::left(map, selection.start)
8865 } else {
8866 selection.start
8867 };
8868 selection.collapse_to(cursor, SelectionGoal::None);
8869 });
8870 })
8871 }
8872
8873 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
8874 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8875 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
8876 })
8877 }
8878
8879 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
8880 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8881 let line_mode = s.line_mode;
8882 s.move_with(|map, selection| {
8883 let cursor = if selection.is_empty() && !line_mode {
8884 movement::right(map, selection.end)
8885 } else {
8886 selection.end
8887 };
8888 selection.collapse_to(cursor, SelectionGoal::None)
8889 });
8890 })
8891 }
8892
8893 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8894 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8895 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8896 })
8897 }
8898
8899 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8900 if self.take_rename(true, window, cx).is_some() {
8901 return;
8902 }
8903
8904 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8905 cx.propagate();
8906 return;
8907 }
8908
8909 let text_layout_details = &self.text_layout_details(window);
8910 let selection_count = self.selections.count();
8911 let first_selection = self.selections.first_anchor();
8912
8913 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8914 let line_mode = s.line_mode;
8915 s.move_with(|map, selection| {
8916 if !selection.is_empty() && !line_mode {
8917 selection.goal = SelectionGoal::None;
8918 }
8919 let (cursor, goal) = movement::up(
8920 map,
8921 selection.start,
8922 selection.goal,
8923 false,
8924 text_layout_details,
8925 );
8926 selection.collapse_to(cursor, goal);
8927 });
8928 });
8929
8930 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8931 {
8932 cx.propagate();
8933 }
8934 }
8935
8936 pub fn move_up_by_lines(
8937 &mut self,
8938 action: &MoveUpByLines,
8939 window: &mut Window,
8940 cx: &mut Context<Self>,
8941 ) {
8942 if self.take_rename(true, window, cx).is_some() {
8943 return;
8944 }
8945
8946 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8947 cx.propagate();
8948 return;
8949 }
8950
8951 let text_layout_details = &self.text_layout_details(window);
8952
8953 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8954 let line_mode = s.line_mode;
8955 s.move_with(|map, selection| {
8956 if !selection.is_empty() && !line_mode {
8957 selection.goal = SelectionGoal::None;
8958 }
8959 let (cursor, goal) = movement::up_by_rows(
8960 map,
8961 selection.start,
8962 action.lines,
8963 selection.goal,
8964 false,
8965 text_layout_details,
8966 );
8967 selection.collapse_to(cursor, goal);
8968 });
8969 })
8970 }
8971
8972 pub fn move_down_by_lines(
8973 &mut self,
8974 action: &MoveDownByLines,
8975 window: &mut Window,
8976 cx: &mut Context<Self>,
8977 ) {
8978 if self.take_rename(true, window, cx).is_some() {
8979 return;
8980 }
8981
8982 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8983 cx.propagate();
8984 return;
8985 }
8986
8987 let text_layout_details = &self.text_layout_details(window);
8988
8989 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8990 let line_mode = s.line_mode;
8991 s.move_with(|map, selection| {
8992 if !selection.is_empty() && !line_mode {
8993 selection.goal = SelectionGoal::None;
8994 }
8995 let (cursor, goal) = movement::down_by_rows(
8996 map,
8997 selection.start,
8998 action.lines,
8999 selection.goal,
9000 false,
9001 text_layout_details,
9002 );
9003 selection.collapse_to(cursor, goal);
9004 });
9005 })
9006 }
9007
9008 pub fn select_down_by_lines(
9009 &mut self,
9010 action: &SelectDownByLines,
9011 window: &mut Window,
9012 cx: &mut Context<Self>,
9013 ) {
9014 let text_layout_details = &self.text_layout_details(window);
9015 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9016 s.move_heads_with(|map, head, goal| {
9017 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9018 })
9019 })
9020 }
9021
9022 pub fn select_up_by_lines(
9023 &mut self,
9024 action: &SelectUpByLines,
9025 window: &mut Window,
9026 cx: &mut Context<Self>,
9027 ) {
9028 let text_layout_details = &self.text_layout_details(window);
9029 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9030 s.move_heads_with(|map, head, goal| {
9031 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9032 })
9033 })
9034 }
9035
9036 pub fn select_page_up(
9037 &mut self,
9038 _: &SelectPageUp,
9039 window: &mut Window,
9040 cx: &mut Context<Self>,
9041 ) {
9042 let Some(row_count) = self.visible_row_count() else {
9043 return;
9044 };
9045
9046 let text_layout_details = &self.text_layout_details(window);
9047
9048 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9049 s.move_heads_with(|map, head, goal| {
9050 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9051 })
9052 })
9053 }
9054
9055 pub fn move_page_up(
9056 &mut self,
9057 action: &MovePageUp,
9058 window: &mut Window,
9059 cx: &mut Context<Self>,
9060 ) {
9061 if self.take_rename(true, window, cx).is_some() {
9062 return;
9063 }
9064
9065 if self
9066 .context_menu
9067 .borrow_mut()
9068 .as_mut()
9069 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9070 .unwrap_or(false)
9071 {
9072 return;
9073 }
9074
9075 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9076 cx.propagate();
9077 return;
9078 }
9079
9080 let Some(row_count) = self.visible_row_count() else {
9081 return;
9082 };
9083
9084 let autoscroll = if action.center_cursor {
9085 Autoscroll::center()
9086 } else {
9087 Autoscroll::fit()
9088 };
9089
9090 let text_layout_details = &self.text_layout_details(window);
9091
9092 self.change_selections(Some(autoscroll), window, cx, |s| {
9093 let line_mode = s.line_mode;
9094 s.move_with(|map, selection| {
9095 if !selection.is_empty() && !line_mode {
9096 selection.goal = SelectionGoal::None;
9097 }
9098 let (cursor, goal) = movement::up_by_rows(
9099 map,
9100 selection.end,
9101 row_count,
9102 selection.goal,
9103 false,
9104 text_layout_details,
9105 );
9106 selection.collapse_to(cursor, goal);
9107 });
9108 });
9109 }
9110
9111 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9112 let text_layout_details = &self.text_layout_details(window);
9113 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9114 s.move_heads_with(|map, head, goal| {
9115 movement::up(map, head, goal, false, text_layout_details)
9116 })
9117 })
9118 }
9119
9120 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9121 self.take_rename(true, window, cx);
9122
9123 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9124 cx.propagate();
9125 return;
9126 }
9127
9128 let text_layout_details = &self.text_layout_details(window);
9129 let selection_count = self.selections.count();
9130 let first_selection = self.selections.first_anchor();
9131
9132 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9133 let line_mode = s.line_mode;
9134 s.move_with(|map, selection| {
9135 if !selection.is_empty() && !line_mode {
9136 selection.goal = SelectionGoal::None;
9137 }
9138 let (cursor, goal) = movement::down(
9139 map,
9140 selection.end,
9141 selection.goal,
9142 false,
9143 text_layout_details,
9144 );
9145 selection.collapse_to(cursor, goal);
9146 });
9147 });
9148
9149 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9150 {
9151 cx.propagate();
9152 }
9153 }
9154
9155 pub fn select_page_down(
9156 &mut self,
9157 _: &SelectPageDown,
9158 window: &mut Window,
9159 cx: &mut Context<Self>,
9160 ) {
9161 let Some(row_count) = self.visible_row_count() else {
9162 return;
9163 };
9164
9165 let text_layout_details = &self.text_layout_details(window);
9166
9167 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9168 s.move_heads_with(|map, head, goal| {
9169 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9170 })
9171 })
9172 }
9173
9174 pub fn move_page_down(
9175 &mut self,
9176 action: &MovePageDown,
9177 window: &mut Window,
9178 cx: &mut Context<Self>,
9179 ) {
9180 if self.take_rename(true, window, cx).is_some() {
9181 return;
9182 }
9183
9184 if self
9185 .context_menu
9186 .borrow_mut()
9187 .as_mut()
9188 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9189 .unwrap_or(false)
9190 {
9191 return;
9192 }
9193
9194 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9195 cx.propagate();
9196 return;
9197 }
9198
9199 let Some(row_count) = self.visible_row_count() else {
9200 return;
9201 };
9202
9203 let autoscroll = if action.center_cursor {
9204 Autoscroll::center()
9205 } else {
9206 Autoscroll::fit()
9207 };
9208
9209 let text_layout_details = &self.text_layout_details(window);
9210 self.change_selections(Some(autoscroll), window, cx, |s| {
9211 let line_mode = s.line_mode;
9212 s.move_with(|map, selection| {
9213 if !selection.is_empty() && !line_mode {
9214 selection.goal = SelectionGoal::None;
9215 }
9216 let (cursor, goal) = movement::down_by_rows(
9217 map,
9218 selection.end,
9219 row_count,
9220 selection.goal,
9221 false,
9222 text_layout_details,
9223 );
9224 selection.collapse_to(cursor, goal);
9225 });
9226 });
9227 }
9228
9229 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9230 let text_layout_details = &self.text_layout_details(window);
9231 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9232 s.move_heads_with(|map, head, goal| {
9233 movement::down(map, head, goal, false, text_layout_details)
9234 })
9235 });
9236 }
9237
9238 pub fn context_menu_first(
9239 &mut self,
9240 _: &ContextMenuFirst,
9241 _window: &mut Window,
9242 cx: &mut Context<Self>,
9243 ) {
9244 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9245 context_menu.select_first(self.completion_provider.as_deref(), cx);
9246 }
9247 }
9248
9249 pub fn context_menu_prev(
9250 &mut self,
9251 _: &ContextMenuPrev,
9252 _window: &mut Window,
9253 cx: &mut Context<Self>,
9254 ) {
9255 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9256 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9257 }
9258 }
9259
9260 pub fn context_menu_next(
9261 &mut self,
9262 _: &ContextMenuNext,
9263 _window: &mut Window,
9264 cx: &mut Context<Self>,
9265 ) {
9266 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9267 context_menu.select_next(self.completion_provider.as_deref(), cx);
9268 }
9269 }
9270
9271 pub fn context_menu_last(
9272 &mut self,
9273 _: &ContextMenuLast,
9274 _window: &mut Window,
9275 cx: &mut Context<Self>,
9276 ) {
9277 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9278 context_menu.select_last(self.completion_provider.as_deref(), cx);
9279 }
9280 }
9281
9282 pub fn move_to_previous_word_start(
9283 &mut self,
9284 _: &MoveToPreviousWordStart,
9285 window: &mut Window,
9286 cx: &mut Context<Self>,
9287 ) {
9288 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9289 s.move_cursors_with(|map, head, _| {
9290 (
9291 movement::previous_word_start(map, head),
9292 SelectionGoal::None,
9293 )
9294 });
9295 })
9296 }
9297
9298 pub fn move_to_previous_subword_start(
9299 &mut self,
9300 _: &MoveToPreviousSubwordStart,
9301 window: &mut Window,
9302 cx: &mut Context<Self>,
9303 ) {
9304 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9305 s.move_cursors_with(|map, head, _| {
9306 (
9307 movement::previous_subword_start(map, head),
9308 SelectionGoal::None,
9309 )
9310 });
9311 })
9312 }
9313
9314 pub fn select_to_previous_word_start(
9315 &mut self,
9316 _: &SelectToPreviousWordStart,
9317 window: &mut Window,
9318 cx: &mut Context<Self>,
9319 ) {
9320 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9321 s.move_heads_with(|map, head, _| {
9322 (
9323 movement::previous_word_start(map, head),
9324 SelectionGoal::None,
9325 )
9326 });
9327 })
9328 }
9329
9330 pub fn select_to_previous_subword_start(
9331 &mut self,
9332 _: &SelectToPreviousSubwordStart,
9333 window: &mut Window,
9334 cx: &mut Context<Self>,
9335 ) {
9336 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9337 s.move_heads_with(|map, head, _| {
9338 (
9339 movement::previous_subword_start(map, head),
9340 SelectionGoal::None,
9341 )
9342 });
9343 })
9344 }
9345
9346 pub fn delete_to_previous_word_start(
9347 &mut self,
9348 action: &DeleteToPreviousWordStart,
9349 window: &mut Window,
9350 cx: &mut Context<Self>,
9351 ) {
9352 self.transact(window, cx, |this, window, cx| {
9353 this.select_autoclose_pair(window, cx);
9354 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9355 let line_mode = s.line_mode;
9356 s.move_with(|map, selection| {
9357 if selection.is_empty() && !line_mode {
9358 let cursor = if action.ignore_newlines {
9359 movement::previous_word_start(map, selection.head())
9360 } else {
9361 movement::previous_word_start_or_newline(map, selection.head())
9362 };
9363 selection.set_head(cursor, SelectionGoal::None);
9364 }
9365 });
9366 });
9367 this.insert("", window, cx);
9368 });
9369 }
9370
9371 pub fn delete_to_previous_subword_start(
9372 &mut self,
9373 _: &DeleteToPreviousSubwordStart,
9374 window: &mut Window,
9375 cx: &mut Context<Self>,
9376 ) {
9377 self.transact(window, cx, |this, window, cx| {
9378 this.select_autoclose_pair(window, cx);
9379 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9380 let line_mode = s.line_mode;
9381 s.move_with(|map, selection| {
9382 if selection.is_empty() && !line_mode {
9383 let cursor = movement::previous_subword_start(map, selection.head());
9384 selection.set_head(cursor, SelectionGoal::None);
9385 }
9386 });
9387 });
9388 this.insert("", window, cx);
9389 });
9390 }
9391
9392 pub fn move_to_next_word_end(
9393 &mut self,
9394 _: &MoveToNextWordEnd,
9395 window: &mut Window,
9396 cx: &mut Context<Self>,
9397 ) {
9398 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9399 s.move_cursors_with(|map, head, _| {
9400 (movement::next_word_end(map, head), SelectionGoal::None)
9401 });
9402 })
9403 }
9404
9405 pub fn move_to_next_subword_end(
9406 &mut self,
9407 _: &MoveToNextSubwordEnd,
9408 window: &mut Window,
9409 cx: &mut Context<Self>,
9410 ) {
9411 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9412 s.move_cursors_with(|map, head, _| {
9413 (movement::next_subword_end(map, head), SelectionGoal::None)
9414 });
9415 })
9416 }
9417
9418 pub fn select_to_next_word_end(
9419 &mut self,
9420 _: &SelectToNextWordEnd,
9421 window: &mut Window,
9422 cx: &mut Context<Self>,
9423 ) {
9424 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9425 s.move_heads_with(|map, head, _| {
9426 (movement::next_word_end(map, head), SelectionGoal::None)
9427 });
9428 })
9429 }
9430
9431 pub fn select_to_next_subword_end(
9432 &mut self,
9433 _: &SelectToNextSubwordEnd,
9434 window: &mut Window,
9435 cx: &mut Context<Self>,
9436 ) {
9437 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9438 s.move_heads_with(|map, head, _| {
9439 (movement::next_subword_end(map, head), SelectionGoal::None)
9440 });
9441 })
9442 }
9443
9444 pub fn delete_to_next_word_end(
9445 &mut self,
9446 action: &DeleteToNextWordEnd,
9447 window: &mut Window,
9448 cx: &mut Context<Self>,
9449 ) {
9450 self.transact(window, cx, |this, window, cx| {
9451 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9452 let line_mode = s.line_mode;
9453 s.move_with(|map, selection| {
9454 if selection.is_empty() && !line_mode {
9455 let cursor = if action.ignore_newlines {
9456 movement::next_word_end(map, selection.head())
9457 } else {
9458 movement::next_word_end_or_newline(map, selection.head())
9459 };
9460 selection.set_head(cursor, SelectionGoal::None);
9461 }
9462 });
9463 });
9464 this.insert("", window, cx);
9465 });
9466 }
9467
9468 pub fn delete_to_next_subword_end(
9469 &mut self,
9470 _: &DeleteToNextSubwordEnd,
9471 window: &mut Window,
9472 cx: &mut Context<Self>,
9473 ) {
9474 self.transact(window, cx, |this, window, cx| {
9475 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9476 s.move_with(|map, selection| {
9477 if selection.is_empty() {
9478 let cursor = movement::next_subword_end(map, selection.head());
9479 selection.set_head(cursor, SelectionGoal::None);
9480 }
9481 });
9482 });
9483 this.insert("", window, cx);
9484 });
9485 }
9486
9487 pub fn move_to_beginning_of_line(
9488 &mut self,
9489 action: &MoveToBeginningOfLine,
9490 window: &mut Window,
9491 cx: &mut Context<Self>,
9492 ) {
9493 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9494 s.move_cursors_with(|map, head, _| {
9495 (
9496 movement::indented_line_beginning(
9497 map,
9498 head,
9499 action.stop_at_soft_wraps,
9500 action.stop_at_indent,
9501 ),
9502 SelectionGoal::None,
9503 )
9504 });
9505 })
9506 }
9507
9508 pub fn select_to_beginning_of_line(
9509 &mut self,
9510 action: &SelectToBeginningOfLine,
9511 window: &mut Window,
9512 cx: &mut Context<Self>,
9513 ) {
9514 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9515 s.move_heads_with(|map, head, _| {
9516 (
9517 movement::indented_line_beginning(
9518 map,
9519 head,
9520 action.stop_at_soft_wraps,
9521 action.stop_at_indent,
9522 ),
9523 SelectionGoal::None,
9524 )
9525 });
9526 });
9527 }
9528
9529 pub fn delete_to_beginning_of_line(
9530 &mut self,
9531 _: &DeleteToBeginningOfLine,
9532 window: &mut Window,
9533 cx: &mut Context<Self>,
9534 ) {
9535 self.transact(window, cx, |this, window, cx| {
9536 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9537 s.move_with(|_, selection| {
9538 selection.reversed = true;
9539 });
9540 });
9541
9542 this.select_to_beginning_of_line(
9543 &SelectToBeginningOfLine {
9544 stop_at_soft_wraps: false,
9545 stop_at_indent: false,
9546 },
9547 window,
9548 cx,
9549 );
9550 this.backspace(&Backspace, window, cx);
9551 });
9552 }
9553
9554 pub fn move_to_end_of_line(
9555 &mut self,
9556 action: &MoveToEndOfLine,
9557 window: &mut Window,
9558 cx: &mut Context<Self>,
9559 ) {
9560 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9561 s.move_cursors_with(|map, head, _| {
9562 (
9563 movement::line_end(map, head, action.stop_at_soft_wraps),
9564 SelectionGoal::None,
9565 )
9566 });
9567 })
9568 }
9569
9570 pub fn select_to_end_of_line(
9571 &mut self,
9572 action: &SelectToEndOfLine,
9573 window: &mut Window,
9574 cx: &mut Context<Self>,
9575 ) {
9576 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9577 s.move_heads_with(|map, head, _| {
9578 (
9579 movement::line_end(map, head, action.stop_at_soft_wraps),
9580 SelectionGoal::None,
9581 )
9582 });
9583 })
9584 }
9585
9586 pub fn delete_to_end_of_line(
9587 &mut self,
9588 _: &DeleteToEndOfLine,
9589 window: &mut Window,
9590 cx: &mut Context<Self>,
9591 ) {
9592 self.transact(window, cx, |this, window, cx| {
9593 this.select_to_end_of_line(
9594 &SelectToEndOfLine {
9595 stop_at_soft_wraps: false,
9596 },
9597 window,
9598 cx,
9599 );
9600 this.delete(&Delete, window, cx);
9601 });
9602 }
9603
9604 pub fn cut_to_end_of_line(
9605 &mut self,
9606 _: &CutToEndOfLine,
9607 window: &mut Window,
9608 cx: &mut Context<Self>,
9609 ) {
9610 self.transact(window, cx, |this, window, cx| {
9611 this.select_to_end_of_line(
9612 &SelectToEndOfLine {
9613 stop_at_soft_wraps: false,
9614 },
9615 window,
9616 cx,
9617 );
9618 this.cut(&Cut, window, cx);
9619 });
9620 }
9621
9622 pub fn move_to_start_of_paragraph(
9623 &mut self,
9624 _: &MoveToStartOfParagraph,
9625 window: &mut Window,
9626 cx: &mut Context<Self>,
9627 ) {
9628 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9629 cx.propagate();
9630 return;
9631 }
9632
9633 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9634 s.move_with(|map, selection| {
9635 selection.collapse_to(
9636 movement::start_of_paragraph(map, selection.head(), 1),
9637 SelectionGoal::None,
9638 )
9639 });
9640 })
9641 }
9642
9643 pub fn move_to_end_of_paragraph(
9644 &mut self,
9645 _: &MoveToEndOfParagraph,
9646 window: &mut Window,
9647 cx: &mut Context<Self>,
9648 ) {
9649 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9650 cx.propagate();
9651 return;
9652 }
9653
9654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9655 s.move_with(|map, selection| {
9656 selection.collapse_to(
9657 movement::end_of_paragraph(map, selection.head(), 1),
9658 SelectionGoal::None,
9659 )
9660 });
9661 })
9662 }
9663
9664 pub fn select_to_start_of_paragraph(
9665 &mut self,
9666 _: &SelectToStartOfParagraph,
9667 window: &mut Window,
9668 cx: &mut Context<Self>,
9669 ) {
9670 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9671 cx.propagate();
9672 return;
9673 }
9674
9675 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9676 s.move_heads_with(|map, head, _| {
9677 (
9678 movement::start_of_paragraph(map, head, 1),
9679 SelectionGoal::None,
9680 )
9681 });
9682 })
9683 }
9684
9685 pub fn select_to_end_of_paragraph(
9686 &mut self,
9687 _: &SelectToEndOfParagraph,
9688 window: &mut Window,
9689 cx: &mut Context<Self>,
9690 ) {
9691 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9692 cx.propagate();
9693 return;
9694 }
9695
9696 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9697 s.move_heads_with(|map, head, _| {
9698 (
9699 movement::end_of_paragraph(map, head, 1),
9700 SelectionGoal::None,
9701 )
9702 });
9703 })
9704 }
9705
9706 pub fn move_to_start_of_excerpt(
9707 &mut self,
9708 _: &MoveToStartOfExcerpt,
9709 window: &mut Window,
9710 cx: &mut Context<Self>,
9711 ) {
9712 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9713 cx.propagate();
9714 return;
9715 }
9716
9717 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9718 s.move_with(|map, selection| {
9719 selection.collapse_to(
9720 movement::start_of_excerpt(
9721 map,
9722 selection.head(),
9723 workspace::searchable::Direction::Prev,
9724 ),
9725 SelectionGoal::None,
9726 )
9727 });
9728 })
9729 }
9730
9731 pub fn move_to_end_of_excerpt(
9732 &mut self,
9733 _: &MoveToEndOfExcerpt,
9734 window: &mut Window,
9735 cx: &mut Context<Self>,
9736 ) {
9737 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9738 cx.propagate();
9739 return;
9740 }
9741
9742 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9743 s.move_with(|map, selection| {
9744 selection.collapse_to(
9745 movement::end_of_excerpt(
9746 map,
9747 selection.head(),
9748 workspace::searchable::Direction::Next,
9749 ),
9750 SelectionGoal::None,
9751 )
9752 });
9753 })
9754 }
9755
9756 pub fn select_to_start_of_excerpt(
9757 &mut self,
9758 _: &SelectToStartOfExcerpt,
9759 window: &mut Window,
9760 cx: &mut Context<Self>,
9761 ) {
9762 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9763 cx.propagate();
9764 return;
9765 }
9766
9767 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9768 s.move_heads_with(|map, head, _| {
9769 (
9770 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
9771 SelectionGoal::None,
9772 )
9773 });
9774 })
9775 }
9776
9777 pub fn select_to_end_of_excerpt(
9778 &mut self,
9779 _: &SelectToEndOfExcerpt,
9780 window: &mut Window,
9781 cx: &mut Context<Self>,
9782 ) {
9783 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9784 cx.propagate();
9785 return;
9786 }
9787
9788 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9789 s.move_heads_with(|map, head, _| {
9790 (
9791 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
9792 SelectionGoal::None,
9793 )
9794 });
9795 })
9796 }
9797
9798 pub fn move_to_beginning(
9799 &mut self,
9800 _: &MoveToBeginning,
9801 window: &mut Window,
9802 cx: &mut Context<Self>,
9803 ) {
9804 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9805 cx.propagate();
9806 return;
9807 }
9808
9809 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9810 s.select_ranges(vec![0..0]);
9811 });
9812 }
9813
9814 pub fn select_to_beginning(
9815 &mut self,
9816 _: &SelectToBeginning,
9817 window: &mut Window,
9818 cx: &mut Context<Self>,
9819 ) {
9820 let mut selection = self.selections.last::<Point>(cx);
9821 selection.set_head(Point::zero(), SelectionGoal::None);
9822
9823 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9824 s.select(vec![selection]);
9825 });
9826 }
9827
9828 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
9829 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9830 cx.propagate();
9831 return;
9832 }
9833
9834 let cursor = self.buffer.read(cx).read(cx).len();
9835 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9836 s.select_ranges(vec![cursor..cursor])
9837 });
9838 }
9839
9840 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
9841 self.nav_history = nav_history;
9842 }
9843
9844 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
9845 self.nav_history.as_ref()
9846 }
9847
9848 fn push_to_nav_history(
9849 &mut self,
9850 cursor_anchor: Anchor,
9851 new_position: Option<Point>,
9852 cx: &mut Context<Self>,
9853 ) {
9854 if let Some(nav_history) = self.nav_history.as_mut() {
9855 let buffer = self.buffer.read(cx).read(cx);
9856 let cursor_position = cursor_anchor.to_point(&buffer);
9857 let scroll_state = self.scroll_manager.anchor();
9858 let scroll_top_row = scroll_state.top_row(&buffer);
9859 drop(buffer);
9860
9861 if let Some(new_position) = new_position {
9862 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
9863 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
9864 return;
9865 }
9866 }
9867
9868 nav_history.push(
9869 Some(NavigationData {
9870 cursor_anchor,
9871 cursor_position,
9872 scroll_anchor: scroll_state,
9873 scroll_top_row,
9874 }),
9875 cx,
9876 );
9877 }
9878 }
9879
9880 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
9881 let buffer = self.buffer.read(cx).snapshot(cx);
9882 let mut selection = self.selections.first::<usize>(cx);
9883 selection.set_head(buffer.len(), SelectionGoal::None);
9884 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9885 s.select(vec![selection]);
9886 });
9887 }
9888
9889 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
9890 let end = self.buffer.read(cx).read(cx).len();
9891 self.change_selections(None, window, cx, |s| {
9892 s.select_ranges(vec![0..end]);
9893 });
9894 }
9895
9896 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
9897 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9898 let mut selections = self.selections.all::<Point>(cx);
9899 let max_point = display_map.buffer_snapshot.max_point();
9900 for selection in &mut selections {
9901 let rows = selection.spanned_rows(true, &display_map);
9902 selection.start = Point::new(rows.start.0, 0);
9903 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
9904 selection.reversed = false;
9905 }
9906 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9907 s.select(selections);
9908 });
9909 }
9910
9911 pub fn split_selection_into_lines(
9912 &mut self,
9913 _: &SplitSelectionIntoLines,
9914 window: &mut Window,
9915 cx: &mut Context<Self>,
9916 ) {
9917 let selections = self
9918 .selections
9919 .all::<Point>(cx)
9920 .into_iter()
9921 .map(|selection| selection.start..selection.end)
9922 .collect::<Vec<_>>();
9923 self.unfold_ranges(&selections, true, true, cx);
9924
9925 let mut new_selection_ranges = Vec::new();
9926 {
9927 let buffer = self.buffer.read(cx).read(cx);
9928 for selection in selections {
9929 for row in selection.start.row..selection.end.row {
9930 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
9931 new_selection_ranges.push(cursor..cursor);
9932 }
9933
9934 let is_multiline_selection = selection.start.row != selection.end.row;
9935 // Don't insert last one if it's a multi-line selection ending at the start of a line,
9936 // so this action feels more ergonomic when paired with other selection operations
9937 let should_skip_last = is_multiline_selection && selection.end.column == 0;
9938 if !should_skip_last {
9939 new_selection_ranges.push(selection.end..selection.end);
9940 }
9941 }
9942 }
9943 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9944 s.select_ranges(new_selection_ranges);
9945 });
9946 }
9947
9948 pub fn add_selection_above(
9949 &mut self,
9950 _: &AddSelectionAbove,
9951 window: &mut Window,
9952 cx: &mut Context<Self>,
9953 ) {
9954 self.add_selection(true, window, cx);
9955 }
9956
9957 pub fn add_selection_below(
9958 &mut self,
9959 _: &AddSelectionBelow,
9960 window: &mut Window,
9961 cx: &mut Context<Self>,
9962 ) {
9963 self.add_selection(false, window, cx);
9964 }
9965
9966 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
9967 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9968 let mut selections = self.selections.all::<Point>(cx);
9969 let text_layout_details = self.text_layout_details(window);
9970 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
9971 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
9972 let range = oldest_selection.display_range(&display_map).sorted();
9973
9974 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
9975 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
9976 let positions = start_x.min(end_x)..start_x.max(end_x);
9977
9978 selections.clear();
9979 let mut stack = Vec::new();
9980 for row in range.start.row().0..=range.end.row().0 {
9981 if let Some(selection) = self.selections.build_columnar_selection(
9982 &display_map,
9983 DisplayRow(row),
9984 &positions,
9985 oldest_selection.reversed,
9986 &text_layout_details,
9987 ) {
9988 stack.push(selection.id);
9989 selections.push(selection);
9990 }
9991 }
9992
9993 if above {
9994 stack.reverse();
9995 }
9996
9997 AddSelectionsState { above, stack }
9998 });
9999
10000 let last_added_selection = *state.stack.last().unwrap();
10001 let mut new_selections = Vec::new();
10002 if above == state.above {
10003 let end_row = if above {
10004 DisplayRow(0)
10005 } else {
10006 display_map.max_point().row()
10007 };
10008
10009 'outer: for selection in selections {
10010 if selection.id == last_added_selection {
10011 let range = selection.display_range(&display_map).sorted();
10012 debug_assert_eq!(range.start.row(), range.end.row());
10013 let mut row = range.start.row();
10014 let positions =
10015 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10016 px(start)..px(end)
10017 } else {
10018 let start_x =
10019 display_map.x_for_display_point(range.start, &text_layout_details);
10020 let end_x =
10021 display_map.x_for_display_point(range.end, &text_layout_details);
10022 start_x.min(end_x)..start_x.max(end_x)
10023 };
10024
10025 while row != end_row {
10026 if above {
10027 row.0 -= 1;
10028 } else {
10029 row.0 += 1;
10030 }
10031
10032 if let Some(new_selection) = self.selections.build_columnar_selection(
10033 &display_map,
10034 row,
10035 &positions,
10036 selection.reversed,
10037 &text_layout_details,
10038 ) {
10039 state.stack.push(new_selection.id);
10040 if above {
10041 new_selections.push(new_selection);
10042 new_selections.push(selection);
10043 } else {
10044 new_selections.push(selection);
10045 new_selections.push(new_selection);
10046 }
10047
10048 continue 'outer;
10049 }
10050 }
10051 }
10052
10053 new_selections.push(selection);
10054 }
10055 } else {
10056 new_selections = selections;
10057 new_selections.retain(|s| s.id != last_added_selection);
10058 state.stack.pop();
10059 }
10060
10061 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10062 s.select(new_selections);
10063 });
10064 if state.stack.len() > 1 {
10065 self.add_selections_state = Some(state);
10066 }
10067 }
10068
10069 pub fn select_next_match_internal(
10070 &mut self,
10071 display_map: &DisplaySnapshot,
10072 replace_newest: bool,
10073 autoscroll: Option<Autoscroll>,
10074 window: &mut Window,
10075 cx: &mut Context<Self>,
10076 ) -> Result<()> {
10077 fn select_next_match_ranges(
10078 this: &mut Editor,
10079 range: Range<usize>,
10080 replace_newest: bool,
10081 auto_scroll: Option<Autoscroll>,
10082 window: &mut Window,
10083 cx: &mut Context<Editor>,
10084 ) {
10085 this.unfold_ranges(&[range.clone()], false, true, cx);
10086 this.change_selections(auto_scroll, window, cx, |s| {
10087 if replace_newest {
10088 s.delete(s.newest_anchor().id);
10089 }
10090 s.insert_range(range.clone());
10091 });
10092 }
10093
10094 let buffer = &display_map.buffer_snapshot;
10095 let mut selections = self.selections.all::<usize>(cx);
10096 if let Some(mut select_next_state) = self.select_next_state.take() {
10097 let query = &select_next_state.query;
10098 if !select_next_state.done {
10099 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10100 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10101 let mut next_selected_range = None;
10102
10103 let bytes_after_last_selection =
10104 buffer.bytes_in_range(last_selection.end..buffer.len());
10105 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10106 let query_matches = query
10107 .stream_find_iter(bytes_after_last_selection)
10108 .map(|result| (last_selection.end, result))
10109 .chain(
10110 query
10111 .stream_find_iter(bytes_before_first_selection)
10112 .map(|result| (0, result)),
10113 );
10114
10115 for (start_offset, query_match) in query_matches {
10116 let query_match = query_match.unwrap(); // can only fail due to I/O
10117 let offset_range =
10118 start_offset + query_match.start()..start_offset + query_match.end();
10119 let display_range = offset_range.start.to_display_point(display_map)
10120 ..offset_range.end.to_display_point(display_map);
10121
10122 if !select_next_state.wordwise
10123 || (!movement::is_inside_word(display_map, display_range.start)
10124 && !movement::is_inside_word(display_map, display_range.end))
10125 {
10126 // TODO: This is n^2, because we might check all the selections
10127 if !selections
10128 .iter()
10129 .any(|selection| selection.range().overlaps(&offset_range))
10130 {
10131 next_selected_range = Some(offset_range);
10132 break;
10133 }
10134 }
10135 }
10136
10137 if let Some(next_selected_range) = next_selected_range {
10138 select_next_match_ranges(
10139 self,
10140 next_selected_range,
10141 replace_newest,
10142 autoscroll,
10143 window,
10144 cx,
10145 );
10146 } else {
10147 select_next_state.done = true;
10148 }
10149 }
10150
10151 self.select_next_state = Some(select_next_state);
10152 } else {
10153 let mut only_carets = true;
10154 let mut same_text_selected = true;
10155 let mut selected_text = None;
10156
10157 let mut selections_iter = selections.iter().peekable();
10158 while let Some(selection) = selections_iter.next() {
10159 if selection.start != selection.end {
10160 only_carets = false;
10161 }
10162
10163 if same_text_selected {
10164 if selected_text.is_none() {
10165 selected_text =
10166 Some(buffer.text_for_range(selection.range()).collect::<String>());
10167 }
10168
10169 if let Some(next_selection) = selections_iter.peek() {
10170 if next_selection.range().len() == selection.range().len() {
10171 let next_selected_text = buffer
10172 .text_for_range(next_selection.range())
10173 .collect::<String>();
10174 if Some(next_selected_text) != selected_text {
10175 same_text_selected = false;
10176 selected_text = None;
10177 }
10178 } else {
10179 same_text_selected = false;
10180 selected_text = None;
10181 }
10182 }
10183 }
10184 }
10185
10186 if only_carets {
10187 for selection in &mut selections {
10188 let word_range = movement::surrounding_word(
10189 display_map,
10190 selection.start.to_display_point(display_map),
10191 );
10192 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10193 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10194 selection.goal = SelectionGoal::None;
10195 selection.reversed = false;
10196 select_next_match_ranges(
10197 self,
10198 selection.start..selection.end,
10199 replace_newest,
10200 autoscroll,
10201 window,
10202 cx,
10203 );
10204 }
10205
10206 if selections.len() == 1 {
10207 let selection = selections
10208 .last()
10209 .expect("ensured that there's only one selection");
10210 let query = buffer
10211 .text_for_range(selection.start..selection.end)
10212 .collect::<String>();
10213 let is_empty = query.is_empty();
10214 let select_state = SelectNextState {
10215 query: AhoCorasick::new(&[query])?,
10216 wordwise: true,
10217 done: is_empty,
10218 };
10219 self.select_next_state = Some(select_state);
10220 } else {
10221 self.select_next_state = None;
10222 }
10223 } else if let Some(selected_text) = selected_text {
10224 self.select_next_state = Some(SelectNextState {
10225 query: AhoCorasick::new(&[selected_text])?,
10226 wordwise: false,
10227 done: false,
10228 });
10229 self.select_next_match_internal(
10230 display_map,
10231 replace_newest,
10232 autoscroll,
10233 window,
10234 cx,
10235 )?;
10236 }
10237 }
10238 Ok(())
10239 }
10240
10241 pub fn select_all_matches(
10242 &mut self,
10243 _action: &SelectAllMatches,
10244 window: &mut Window,
10245 cx: &mut Context<Self>,
10246 ) -> Result<()> {
10247 self.push_to_selection_history();
10248 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10249
10250 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10251 let Some(select_next_state) = self.select_next_state.as_mut() else {
10252 return Ok(());
10253 };
10254 if select_next_state.done {
10255 return Ok(());
10256 }
10257
10258 let mut new_selections = self.selections.all::<usize>(cx);
10259
10260 let buffer = &display_map.buffer_snapshot;
10261 let query_matches = select_next_state
10262 .query
10263 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10264
10265 for query_match in query_matches {
10266 let query_match = query_match.unwrap(); // can only fail due to I/O
10267 let offset_range = query_match.start()..query_match.end();
10268 let display_range = offset_range.start.to_display_point(&display_map)
10269 ..offset_range.end.to_display_point(&display_map);
10270
10271 if !select_next_state.wordwise
10272 || (!movement::is_inside_word(&display_map, display_range.start)
10273 && !movement::is_inside_word(&display_map, display_range.end))
10274 {
10275 self.selections.change_with(cx, |selections| {
10276 new_selections.push(Selection {
10277 id: selections.new_selection_id(),
10278 start: offset_range.start,
10279 end: offset_range.end,
10280 reversed: false,
10281 goal: SelectionGoal::None,
10282 });
10283 });
10284 }
10285 }
10286
10287 new_selections.sort_by_key(|selection| selection.start);
10288 let mut ix = 0;
10289 while ix + 1 < new_selections.len() {
10290 let current_selection = &new_selections[ix];
10291 let next_selection = &new_selections[ix + 1];
10292 if current_selection.range().overlaps(&next_selection.range()) {
10293 if current_selection.id < next_selection.id {
10294 new_selections.remove(ix + 1);
10295 } else {
10296 new_selections.remove(ix);
10297 }
10298 } else {
10299 ix += 1;
10300 }
10301 }
10302
10303 let reversed = self.selections.oldest::<usize>(cx).reversed;
10304
10305 for selection in new_selections.iter_mut() {
10306 selection.reversed = reversed;
10307 }
10308
10309 select_next_state.done = true;
10310 self.unfold_ranges(
10311 &new_selections
10312 .iter()
10313 .map(|selection| selection.range())
10314 .collect::<Vec<_>>(),
10315 false,
10316 false,
10317 cx,
10318 );
10319 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10320 selections.select(new_selections)
10321 });
10322
10323 Ok(())
10324 }
10325
10326 pub fn select_next(
10327 &mut self,
10328 action: &SelectNext,
10329 window: &mut Window,
10330 cx: &mut Context<Self>,
10331 ) -> Result<()> {
10332 self.push_to_selection_history();
10333 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10334 self.select_next_match_internal(
10335 &display_map,
10336 action.replace_newest,
10337 Some(Autoscroll::newest()),
10338 window,
10339 cx,
10340 )?;
10341 Ok(())
10342 }
10343
10344 pub fn select_previous(
10345 &mut self,
10346 action: &SelectPrevious,
10347 window: &mut Window,
10348 cx: &mut Context<Self>,
10349 ) -> Result<()> {
10350 self.push_to_selection_history();
10351 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10352 let buffer = &display_map.buffer_snapshot;
10353 let mut selections = self.selections.all::<usize>(cx);
10354 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10355 let query = &select_prev_state.query;
10356 if !select_prev_state.done {
10357 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10358 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10359 let mut next_selected_range = None;
10360 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10361 let bytes_before_last_selection =
10362 buffer.reversed_bytes_in_range(0..last_selection.start);
10363 let bytes_after_first_selection =
10364 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10365 let query_matches = query
10366 .stream_find_iter(bytes_before_last_selection)
10367 .map(|result| (last_selection.start, result))
10368 .chain(
10369 query
10370 .stream_find_iter(bytes_after_first_selection)
10371 .map(|result| (buffer.len(), result)),
10372 );
10373 for (end_offset, query_match) in query_matches {
10374 let query_match = query_match.unwrap(); // can only fail due to I/O
10375 let offset_range =
10376 end_offset - query_match.end()..end_offset - query_match.start();
10377 let display_range = offset_range.start.to_display_point(&display_map)
10378 ..offset_range.end.to_display_point(&display_map);
10379
10380 if !select_prev_state.wordwise
10381 || (!movement::is_inside_word(&display_map, display_range.start)
10382 && !movement::is_inside_word(&display_map, display_range.end))
10383 {
10384 next_selected_range = Some(offset_range);
10385 break;
10386 }
10387 }
10388
10389 if let Some(next_selected_range) = next_selected_range {
10390 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10391 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10392 if action.replace_newest {
10393 s.delete(s.newest_anchor().id);
10394 }
10395 s.insert_range(next_selected_range);
10396 });
10397 } else {
10398 select_prev_state.done = true;
10399 }
10400 }
10401
10402 self.select_prev_state = Some(select_prev_state);
10403 } else {
10404 let mut only_carets = true;
10405 let mut same_text_selected = true;
10406 let mut selected_text = None;
10407
10408 let mut selections_iter = selections.iter().peekable();
10409 while let Some(selection) = selections_iter.next() {
10410 if selection.start != selection.end {
10411 only_carets = false;
10412 }
10413
10414 if same_text_selected {
10415 if selected_text.is_none() {
10416 selected_text =
10417 Some(buffer.text_for_range(selection.range()).collect::<String>());
10418 }
10419
10420 if let Some(next_selection) = selections_iter.peek() {
10421 if next_selection.range().len() == selection.range().len() {
10422 let next_selected_text = buffer
10423 .text_for_range(next_selection.range())
10424 .collect::<String>();
10425 if Some(next_selected_text) != selected_text {
10426 same_text_selected = false;
10427 selected_text = None;
10428 }
10429 } else {
10430 same_text_selected = false;
10431 selected_text = None;
10432 }
10433 }
10434 }
10435 }
10436
10437 if only_carets {
10438 for selection in &mut selections {
10439 let word_range = movement::surrounding_word(
10440 &display_map,
10441 selection.start.to_display_point(&display_map),
10442 );
10443 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10444 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10445 selection.goal = SelectionGoal::None;
10446 selection.reversed = false;
10447 }
10448 if selections.len() == 1 {
10449 let selection = selections
10450 .last()
10451 .expect("ensured that there's only one selection");
10452 let query = buffer
10453 .text_for_range(selection.start..selection.end)
10454 .collect::<String>();
10455 let is_empty = query.is_empty();
10456 let select_state = SelectNextState {
10457 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10458 wordwise: true,
10459 done: is_empty,
10460 };
10461 self.select_prev_state = Some(select_state);
10462 } else {
10463 self.select_prev_state = None;
10464 }
10465
10466 self.unfold_ranges(
10467 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10468 false,
10469 true,
10470 cx,
10471 );
10472 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10473 s.select(selections);
10474 });
10475 } else if let Some(selected_text) = selected_text {
10476 self.select_prev_state = Some(SelectNextState {
10477 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10478 wordwise: false,
10479 done: false,
10480 });
10481 self.select_previous(action, window, cx)?;
10482 }
10483 }
10484 Ok(())
10485 }
10486
10487 pub fn toggle_comments(
10488 &mut self,
10489 action: &ToggleComments,
10490 window: &mut Window,
10491 cx: &mut Context<Self>,
10492 ) {
10493 if self.read_only(cx) {
10494 return;
10495 }
10496 let text_layout_details = &self.text_layout_details(window);
10497 self.transact(window, cx, |this, window, cx| {
10498 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10499 let mut edits = Vec::new();
10500 let mut selection_edit_ranges = Vec::new();
10501 let mut last_toggled_row = None;
10502 let snapshot = this.buffer.read(cx).read(cx);
10503 let empty_str: Arc<str> = Arc::default();
10504 let mut suffixes_inserted = Vec::new();
10505 let ignore_indent = action.ignore_indent;
10506
10507 fn comment_prefix_range(
10508 snapshot: &MultiBufferSnapshot,
10509 row: MultiBufferRow,
10510 comment_prefix: &str,
10511 comment_prefix_whitespace: &str,
10512 ignore_indent: bool,
10513 ) -> Range<Point> {
10514 let indent_size = if ignore_indent {
10515 0
10516 } else {
10517 snapshot.indent_size_for_line(row).len
10518 };
10519
10520 let start = Point::new(row.0, indent_size);
10521
10522 let mut line_bytes = snapshot
10523 .bytes_in_range(start..snapshot.max_point())
10524 .flatten()
10525 .copied();
10526
10527 // If this line currently begins with the line comment prefix, then record
10528 // the range containing the prefix.
10529 if line_bytes
10530 .by_ref()
10531 .take(comment_prefix.len())
10532 .eq(comment_prefix.bytes())
10533 {
10534 // Include any whitespace that matches the comment prefix.
10535 let matching_whitespace_len = line_bytes
10536 .zip(comment_prefix_whitespace.bytes())
10537 .take_while(|(a, b)| a == b)
10538 .count() as u32;
10539 let end = Point::new(
10540 start.row,
10541 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10542 );
10543 start..end
10544 } else {
10545 start..start
10546 }
10547 }
10548
10549 fn comment_suffix_range(
10550 snapshot: &MultiBufferSnapshot,
10551 row: MultiBufferRow,
10552 comment_suffix: &str,
10553 comment_suffix_has_leading_space: bool,
10554 ) -> Range<Point> {
10555 let end = Point::new(row.0, snapshot.line_len(row));
10556 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10557
10558 let mut line_end_bytes = snapshot
10559 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10560 .flatten()
10561 .copied();
10562
10563 let leading_space_len = if suffix_start_column > 0
10564 && line_end_bytes.next() == Some(b' ')
10565 && comment_suffix_has_leading_space
10566 {
10567 1
10568 } else {
10569 0
10570 };
10571
10572 // If this line currently begins with the line comment prefix, then record
10573 // the range containing the prefix.
10574 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10575 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10576 start..end
10577 } else {
10578 end..end
10579 }
10580 }
10581
10582 // TODO: Handle selections that cross excerpts
10583 for selection in &mut selections {
10584 let start_column = snapshot
10585 .indent_size_for_line(MultiBufferRow(selection.start.row))
10586 .len;
10587 let language = if let Some(language) =
10588 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10589 {
10590 language
10591 } else {
10592 continue;
10593 };
10594
10595 selection_edit_ranges.clear();
10596
10597 // If multiple selections contain a given row, avoid processing that
10598 // row more than once.
10599 let mut start_row = MultiBufferRow(selection.start.row);
10600 if last_toggled_row == Some(start_row) {
10601 start_row = start_row.next_row();
10602 }
10603 let end_row =
10604 if selection.end.row > selection.start.row && selection.end.column == 0 {
10605 MultiBufferRow(selection.end.row - 1)
10606 } else {
10607 MultiBufferRow(selection.end.row)
10608 };
10609 last_toggled_row = Some(end_row);
10610
10611 if start_row > end_row {
10612 continue;
10613 }
10614
10615 // If the language has line comments, toggle those.
10616 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10617
10618 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10619 if ignore_indent {
10620 full_comment_prefixes = full_comment_prefixes
10621 .into_iter()
10622 .map(|s| Arc::from(s.trim_end()))
10623 .collect();
10624 }
10625
10626 if !full_comment_prefixes.is_empty() {
10627 let first_prefix = full_comment_prefixes
10628 .first()
10629 .expect("prefixes is non-empty");
10630 let prefix_trimmed_lengths = full_comment_prefixes
10631 .iter()
10632 .map(|p| p.trim_end_matches(' ').len())
10633 .collect::<SmallVec<[usize; 4]>>();
10634
10635 let mut all_selection_lines_are_comments = true;
10636
10637 for row in start_row.0..=end_row.0 {
10638 let row = MultiBufferRow(row);
10639 if start_row < end_row && snapshot.is_line_blank(row) {
10640 continue;
10641 }
10642
10643 let prefix_range = full_comment_prefixes
10644 .iter()
10645 .zip(prefix_trimmed_lengths.iter().copied())
10646 .map(|(prefix, trimmed_prefix_len)| {
10647 comment_prefix_range(
10648 snapshot.deref(),
10649 row,
10650 &prefix[..trimmed_prefix_len],
10651 &prefix[trimmed_prefix_len..],
10652 ignore_indent,
10653 )
10654 })
10655 .max_by_key(|range| range.end.column - range.start.column)
10656 .expect("prefixes is non-empty");
10657
10658 if prefix_range.is_empty() {
10659 all_selection_lines_are_comments = false;
10660 }
10661
10662 selection_edit_ranges.push(prefix_range);
10663 }
10664
10665 if all_selection_lines_are_comments {
10666 edits.extend(
10667 selection_edit_ranges
10668 .iter()
10669 .cloned()
10670 .map(|range| (range, empty_str.clone())),
10671 );
10672 } else {
10673 let min_column = selection_edit_ranges
10674 .iter()
10675 .map(|range| range.start.column)
10676 .min()
10677 .unwrap_or(0);
10678 edits.extend(selection_edit_ranges.iter().map(|range| {
10679 let position = Point::new(range.start.row, min_column);
10680 (position..position, first_prefix.clone())
10681 }));
10682 }
10683 } else if let Some((full_comment_prefix, comment_suffix)) =
10684 language.block_comment_delimiters()
10685 {
10686 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10687 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10688 let prefix_range = comment_prefix_range(
10689 snapshot.deref(),
10690 start_row,
10691 comment_prefix,
10692 comment_prefix_whitespace,
10693 ignore_indent,
10694 );
10695 let suffix_range = comment_suffix_range(
10696 snapshot.deref(),
10697 end_row,
10698 comment_suffix.trim_start_matches(' '),
10699 comment_suffix.starts_with(' '),
10700 );
10701
10702 if prefix_range.is_empty() || suffix_range.is_empty() {
10703 edits.push((
10704 prefix_range.start..prefix_range.start,
10705 full_comment_prefix.clone(),
10706 ));
10707 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
10708 suffixes_inserted.push((end_row, comment_suffix.len()));
10709 } else {
10710 edits.push((prefix_range, empty_str.clone()));
10711 edits.push((suffix_range, empty_str.clone()));
10712 }
10713 } else {
10714 continue;
10715 }
10716 }
10717
10718 drop(snapshot);
10719 this.buffer.update(cx, |buffer, cx| {
10720 buffer.edit(edits, None, cx);
10721 });
10722
10723 // Adjust selections so that they end before any comment suffixes that
10724 // were inserted.
10725 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
10726 let mut selections = this.selections.all::<Point>(cx);
10727 let snapshot = this.buffer.read(cx).read(cx);
10728 for selection in &mut selections {
10729 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
10730 match row.cmp(&MultiBufferRow(selection.end.row)) {
10731 Ordering::Less => {
10732 suffixes_inserted.next();
10733 continue;
10734 }
10735 Ordering::Greater => break,
10736 Ordering::Equal => {
10737 if selection.end.column == snapshot.line_len(row) {
10738 if selection.is_empty() {
10739 selection.start.column -= suffix_len as u32;
10740 }
10741 selection.end.column -= suffix_len as u32;
10742 }
10743 break;
10744 }
10745 }
10746 }
10747 }
10748
10749 drop(snapshot);
10750 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10751 s.select(selections)
10752 });
10753
10754 let selections = this.selections.all::<Point>(cx);
10755 let selections_on_single_row = selections.windows(2).all(|selections| {
10756 selections[0].start.row == selections[1].start.row
10757 && selections[0].end.row == selections[1].end.row
10758 && selections[0].start.row == selections[0].end.row
10759 });
10760 let selections_selecting = selections
10761 .iter()
10762 .any(|selection| selection.start != selection.end);
10763 let advance_downwards = action.advance_downwards
10764 && selections_on_single_row
10765 && !selections_selecting
10766 && !matches!(this.mode, EditorMode::SingleLine { .. });
10767
10768 if advance_downwards {
10769 let snapshot = this.buffer.read(cx).snapshot(cx);
10770
10771 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10772 s.move_cursors_with(|display_snapshot, display_point, _| {
10773 let mut point = display_point.to_point(display_snapshot);
10774 point.row += 1;
10775 point = snapshot.clip_point(point, Bias::Left);
10776 let display_point = point.to_display_point(display_snapshot);
10777 let goal = SelectionGoal::HorizontalPosition(
10778 display_snapshot
10779 .x_for_display_point(display_point, text_layout_details)
10780 .into(),
10781 );
10782 (display_point, goal)
10783 })
10784 });
10785 }
10786 });
10787 }
10788
10789 pub fn select_enclosing_symbol(
10790 &mut self,
10791 _: &SelectEnclosingSymbol,
10792 window: &mut Window,
10793 cx: &mut Context<Self>,
10794 ) {
10795 let buffer = self.buffer.read(cx).snapshot(cx);
10796 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10797
10798 fn update_selection(
10799 selection: &Selection<usize>,
10800 buffer_snap: &MultiBufferSnapshot,
10801 ) -> Option<Selection<usize>> {
10802 let cursor = selection.head();
10803 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
10804 for symbol in symbols.iter().rev() {
10805 let start = symbol.range.start.to_offset(buffer_snap);
10806 let end = symbol.range.end.to_offset(buffer_snap);
10807 let new_range = start..end;
10808 if start < selection.start || end > selection.end {
10809 return Some(Selection {
10810 id: selection.id,
10811 start: new_range.start,
10812 end: new_range.end,
10813 goal: SelectionGoal::None,
10814 reversed: selection.reversed,
10815 });
10816 }
10817 }
10818 None
10819 }
10820
10821 let mut selected_larger_symbol = false;
10822 let new_selections = old_selections
10823 .iter()
10824 .map(|selection| match update_selection(selection, &buffer) {
10825 Some(new_selection) => {
10826 if new_selection.range() != selection.range() {
10827 selected_larger_symbol = true;
10828 }
10829 new_selection
10830 }
10831 None => selection.clone(),
10832 })
10833 .collect::<Vec<_>>();
10834
10835 if selected_larger_symbol {
10836 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10837 s.select(new_selections);
10838 });
10839 }
10840 }
10841
10842 pub fn select_larger_syntax_node(
10843 &mut self,
10844 _: &SelectLargerSyntaxNode,
10845 window: &mut Window,
10846 cx: &mut Context<Self>,
10847 ) {
10848 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10849 let buffer = self.buffer.read(cx).snapshot(cx);
10850 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
10851
10852 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10853 let mut selected_larger_node = false;
10854 let new_selections = old_selections
10855 .iter()
10856 .map(|selection| {
10857 let old_range = selection.start..selection.end;
10858 let mut new_range = old_range.clone();
10859 let mut new_node = None;
10860 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
10861 {
10862 new_node = Some(node);
10863 new_range = match containing_range {
10864 MultiOrSingleBufferOffsetRange::Single(_) => break,
10865 MultiOrSingleBufferOffsetRange::Multi(range) => range,
10866 };
10867 if !display_map.intersects_fold(new_range.start)
10868 && !display_map.intersects_fold(new_range.end)
10869 {
10870 break;
10871 }
10872 }
10873
10874 if let Some(node) = new_node {
10875 // Log the ancestor, to support using this action as a way to explore TreeSitter
10876 // nodes. Parent and grandparent are also logged because this operation will not
10877 // visit nodes that have the same range as their parent.
10878 log::info!("Node: {node:?}");
10879 let parent = node.parent();
10880 log::info!("Parent: {parent:?}");
10881 let grandparent = parent.and_then(|x| x.parent());
10882 log::info!("Grandparent: {grandparent:?}");
10883 }
10884
10885 selected_larger_node |= new_range != old_range;
10886 Selection {
10887 id: selection.id,
10888 start: new_range.start,
10889 end: new_range.end,
10890 goal: SelectionGoal::None,
10891 reversed: selection.reversed,
10892 }
10893 })
10894 .collect::<Vec<_>>();
10895
10896 if selected_larger_node {
10897 stack.push(old_selections);
10898 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10899 s.select(new_selections);
10900 });
10901 }
10902 self.select_larger_syntax_node_stack = stack;
10903 }
10904
10905 pub fn select_smaller_syntax_node(
10906 &mut self,
10907 _: &SelectSmallerSyntaxNode,
10908 window: &mut Window,
10909 cx: &mut Context<Self>,
10910 ) {
10911 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
10912 if let Some(selections) = stack.pop() {
10913 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10914 s.select(selections.to_vec());
10915 });
10916 }
10917 self.select_larger_syntax_node_stack = stack;
10918 }
10919
10920 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
10921 if !EditorSettings::get_global(cx).gutter.runnables {
10922 self.clear_tasks();
10923 return Task::ready(());
10924 }
10925 let project = self.project.as_ref().map(Entity::downgrade);
10926 cx.spawn_in(window, |this, mut cx| async move {
10927 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
10928 let Some(project) = project.and_then(|p| p.upgrade()) else {
10929 return;
10930 };
10931 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
10932 this.display_map.update(cx, |map, cx| map.snapshot(cx))
10933 }) else {
10934 return;
10935 };
10936
10937 let hide_runnables = project
10938 .update(&mut cx, |project, cx| {
10939 // Do not display any test indicators in non-dev server remote projects.
10940 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
10941 })
10942 .unwrap_or(true);
10943 if hide_runnables {
10944 return;
10945 }
10946 let new_rows =
10947 cx.background_spawn({
10948 let snapshot = display_snapshot.clone();
10949 async move {
10950 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
10951 }
10952 })
10953 .await;
10954
10955 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
10956 this.update(&mut cx, |this, _| {
10957 this.clear_tasks();
10958 for (key, value) in rows {
10959 this.insert_tasks(key, value);
10960 }
10961 })
10962 .ok();
10963 })
10964 }
10965 fn fetch_runnable_ranges(
10966 snapshot: &DisplaySnapshot,
10967 range: Range<Anchor>,
10968 ) -> Vec<language::RunnableRange> {
10969 snapshot.buffer_snapshot.runnable_ranges(range).collect()
10970 }
10971
10972 fn runnable_rows(
10973 project: Entity<Project>,
10974 snapshot: DisplaySnapshot,
10975 runnable_ranges: Vec<RunnableRange>,
10976 mut cx: AsyncWindowContext,
10977 ) -> Vec<((BufferId, u32), RunnableTasks)> {
10978 runnable_ranges
10979 .into_iter()
10980 .filter_map(|mut runnable| {
10981 let tasks = cx
10982 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
10983 .ok()?;
10984 if tasks.is_empty() {
10985 return None;
10986 }
10987
10988 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
10989
10990 let row = snapshot
10991 .buffer_snapshot
10992 .buffer_line_for_row(MultiBufferRow(point.row))?
10993 .1
10994 .start
10995 .row;
10996
10997 let context_range =
10998 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
10999 Some((
11000 (runnable.buffer_id, row),
11001 RunnableTasks {
11002 templates: tasks,
11003 offset: snapshot
11004 .buffer_snapshot
11005 .anchor_before(runnable.run_range.start),
11006 context_range,
11007 column: point.column,
11008 extra_variables: runnable.extra_captures,
11009 },
11010 ))
11011 })
11012 .collect()
11013 }
11014
11015 fn templates_with_tags(
11016 project: &Entity<Project>,
11017 runnable: &mut Runnable,
11018 cx: &mut App,
11019 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11020 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11021 let (worktree_id, file) = project
11022 .buffer_for_id(runnable.buffer, cx)
11023 .and_then(|buffer| buffer.read(cx).file())
11024 .map(|file| (file.worktree_id(cx), file.clone()))
11025 .unzip();
11026
11027 (
11028 project.task_store().read(cx).task_inventory().cloned(),
11029 worktree_id,
11030 file,
11031 )
11032 });
11033
11034 let tags = mem::take(&mut runnable.tags);
11035 let mut tags: Vec<_> = tags
11036 .into_iter()
11037 .flat_map(|tag| {
11038 let tag = tag.0.clone();
11039 inventory
11040 .as_ref()
11041 .into_iter()
11042 .flat_map(|inventory| {
11043 inventory.read(cx).list_tasks(
11044 file.clone(),
11045 Some(runnable.language.clone()),
11046 worktree_id,
11047 cx,
11048 )
11049 })
11050 .filter(move |(_, template)| {
11051 template.tags.iter().any(|source_tag| source_tag == &tag)
11052 })
11053 })
11054 .sorted_by_key(|(kind, _)| kind.to_owned())
11055 .collect();
11056 if let Some((leading_tag_source, _)) = tags.first() {
11057 // Strongest source wins; if we have worktree tag binding, prefer that to
11058 // global and language bindings;
11059 // if we have a global binding, prefer that to language binding.
11060 let first_mismatch = tags
11061 .iter()
11062 .position(|(tag_source, _)| tag_source != leading_tag_source);
11063 if let Some(index) = first_mismatch {
11064 tags.truncate(index);
11065 }
11066 }
11067
11068 tags
11069 }
11070
11071 pub fn move_to_enclosing_bracket(
11072 &mut self,
11073 _: &MoveToEnclosingBracket,
11074 window: &mut Window,
11075 cx: &mut Context<Self>,
11076 ) {
11077 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11078 s.move_offsets_with(|snapshot, selection| {
11079 let Some(enclosing_bracket_ranges) =
11080 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11081 else {
11082 return;
11083 };
11084
11085 let mut best_length = usize::MAX;
11086 let mut best_inside = false;
11087 let mut best_in_bracket_range = false;
11088 let mut best_destination = None;
11089 for (open, close) in enclosing_bracket_ranges {
11090 let close = close.to_inclusive();
11091 let length = close.end() - open.start;
11092 let inside = selection.start >= open.end && selection.end <= *close.start();
11093 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11094 || close.contains(&selection.head());
11095
11096 // If best is next to a bracket and current isn't, skip
11097 if !in_bracket_range && best_in_bracket_range {
11098 continue;
11099 }
11100
11101 // Prefer smaller lengths unless best is inside and current isn't
11102 if length > best_length && (best_inside || !inside) {
11103 continue;
11104 }
11105
11106 best_length = length;
11107 best_inside = inside;
11108 best_in_bracket_range = in_bracket_range;
11109 best_destination = Some(
11110 if close.contains(&selection.start) && close.contains(&selection.end) {
11111 if inside {
11112 open.end
11113 } else {
11114 open.start
11115 }
11116 } else if inside {
11117 *close.start()
11118 } else {
11119 *close.end()
11120 },
11121 );
11122 }
11123
11124 if let Some(destination) = best_destination {
11125 selection.collapse_to(destination, SelectionGoal::None);
11126 }
11127 })
11128 });
11129 }
11130
11131 pub fn undo_selection(
11132 &mut self,
11133 _: &UndoSelection,
11134 window: &mut Window,
11135 cx: &mut Context<Self>,
11136 ) {
11137 self.end_selection(window, cx);
11138 self.selection_history.mode = SelectionHistoryMode::Undoing;
11139 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
11140 self.change_selections(None, window, cx, |s| {
11141 s.select_anchors(entry.selections.to_vec())
11142 });
11143 self.select_next_state = entry.select_next_state;
11144 self.select_prev_state = entry.select_prev_state;
11145 self.add_selections_state = entry.add_selections_state;
11146 self.request_autoscroll(Autoscroll::newest(), cx);
11147 }
11148 self.selection_history.mode = SelectionHistoryMode::Normal;
11149 }
11150
11151 pub fn redo_selection(
11152 &mut self,
11153 _: &RedoSelection,
11154 window: &mut Window,
11155 cx: &mut Context<Self>,
11156 ) {
11157 self.end_selection(window, cx);
11158 self.selection_history.mode = SelectionHistoryMode::Redoing;
11159 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11160 self.change_selections(None, window, cx, |s| {
11161 s.select_anchors(entry.selections.to_vec())
11162 });
11163 self.select_next_state = entry.select_next_state;
11164 self.select_prev_state = entry.select_prev_state;
11165 self.add_selections_state = entry.add_selections_state;
11166 self.request_autoscroll(Autoscroll::newest(), cx);
11167 }
11168 self.selection_history.mode = SelectionHistoryMode::Normal;
11169 }
11170
11171 pub fn expand_excerpts(
11172 &mut self,
11173 action: &ExpandExcerpts,
11174 _: &mut Window,
11175 cx: &mut Context<Self>,
11176 ) {
11177 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11178 }
11179
11180 pub fn expand_excerpts_down(
11181 &mut self,
11182 action: &ExpandExcerptsDown,
11183 _: &mut Window,
11184 cx: &mut Context<Self>,
11185 ) {
11186 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11187 }
11188
11189 pub fn expand_excerpts_up(
11190 &mut self,
11191 action: &ExpandExcerptsUp,
11192 _: &mut Window,
11193 cx: &mut Context<Self>,
11194 ) {
11195 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11196 }
11197
11198 pub fn expand_excerpts_for_direction(
11199 &mut self,
11200 lines: u32,
11201 direction: ExpandExcerptDirection,
11202
11203 cx: &mut Context<Self>,
11204 ) {
11205 let selections = self.selections.disjoint_anchors();
11206
11207 let lines = if lines == 0 {
11208 EditorSettings::get_global(cx).expand_excerpt_lines
11209 } else {
11210 lines
11211 };
11212
11213 self.buffer.update(cx, |buffer, cx| {
11214 let snapshot = buffer.snapshot(cx);
11215 let mut excerpt_ids = selections
11216 .iter()
11217 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11218 .collect::<Vec<_>>();
11219 excerpt_ids.sort();
11220 excerpt_ids.dedup();
11221 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11222 })
11223 }
11224
11225 pub fn expand_excerpt(
11226 &mut self,
11227 excerpt: ExcerptId,
11228 direction: ExpandExcerptDirection,
11229 cx: &mut Context<Self>,
11230 ) {
11231 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11232 self.buffer.update(cx, |buffer, cx| {
11233 buffer.expand_excerpts([excerpt], lines, direction, cx)
11234 })
11235 }
11236
11237 pub fn go_to_singleton_buffer_point(
11238 &mut self,
11239 point: Point,
11240 window: &mut Window,
11241 cx: &mut Context<Self>,
11242 ) {
11243 self.go_to_singleton_buffer_range(point..point, window, cx);
11244 }
11245
11246 pub fn go_to_singleton_buffer_range(
11247 &mut self,
11248 range: Range<Point>,
11249 window: &mut Window,
11250 cx: &mut Context<Self>,
11251 ) {
11252 let multibuffer = self.buffer().read(cx);
11253 let Some(buffer) = multibuffer.as_singleton() else {
11254 return;
11255 };
11256 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11257 return;
11258 };
11259 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11260 return;
11261 };
11262 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11263 s.select_anchor_ranges([start..end])
11264 });
11265 }
11266
11267 fn go_to_diagnostic(
11268 &mut self,
11269 _: &GoToDiagnostic,
11270 window: &mut Window,
11271 cx: &mut Context<Self>,
11272 ) {
11273 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11274 }
11275
11276 fn go_to_prev_diagnostic(
11277 &mut self,
11278 _: &GoToPrevDiagnostic,
11279 window: &mut Window,
11280 cx: &mut Context<Self>,
11281 ) {
11282 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11283 }
11284
11285 pub fn go_to_diagnostic_impl(
11286 &mut self,
11287 direction: Direction,
11288 window: &mut Window,
11289 cx: &mut Context<Self>,
11290 ) {
11291 let buffer = self.buffer.read(cx).snapshot(cx);
11292 let selection = self.selections.newest::<usize>(cx);
11293
11294 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11295 if direction == Direction::Next {
11296 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11297 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11298 return;
11299 };
11300 self.activate_diagnostics(
11301 buffer_id,
11302 popover.local_diagnostic.diagnostic.group_id,
11303 window,
11304 cx,
11305 );
11306 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11307 let primary_range_start = active_diagnostics.primary_range.start;
11308 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11309 let mut new_selection = s.newest_anchor().clone();
11310 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11311 s.select_anchors(vec![new_selection.clone()]);
11312 });
11313 self.refresh_inline_completion(false, true, window, cx);
11314 }
11315 return;
11316 }
11317 }
11318
11319 let active_group_id = self
11320 .active_diagnostics
11321 .as_ref()
11322 .map(|active_group| active_group.group_id);
11323 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11324 active_diagnostics
11325 .primary_range
11326 .to_offset(&buffer)
11327 .to_inclusive()
11328 });
11329 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11330 if active_primary_range.contains(&selection.head()) {
11331 *active_primary_range.start()
11332 } else {
11333 selection.head()
11334 }
11335 } else {
11336 selection.head()
11337 };
11338
11339 let snapshot = self.snapshot(window, cx);
11340 let primary_diagnostics_before = buffer
11341 .diagnostics_in_range::<usize>(0..search_start)
11342 .filter(|entry| entry.diagnostic.is_primary)
11343 .filter(|entry| entry.range.start != entry.range.end)
11344 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11345 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11346 .collect::<Vec<_>>();
11347 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11348 primary_diagnostics_before
11349 .iter()
11350 .position(|entry| entry.diagnostic.group_id == active_group_id)
11351 });
11352
11353 let primary_diagnostics_after = buffer
11354 .diagnostics_in_range::<usize>(search_start..buffer.len())
11355 .filter(|entry| entry.diagnostic.is_primary)
11356 .filter(|entry| entry.range.start != entry.range.end)
11357 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11358 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11359 .collect::<Vec<_>>();
11360 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11361 primary_diagnostics_after
11362 .iter()
11363 .enumerate()
11364 .rev()
11365 .find_map(|(i, entry)| {
11366 if entry.diagnostic.group_id == active_group_id {
11367 Some(i)
11368 } else {
11369 None
11370 }
11371 })
11372 });
11373
11374 let next_primary_diagnostic = match direction {
11375 Direction::Prev => primary_diagnostics_before
11376 .iter()
11377 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11378 .rev()
11379 .next(),
11380 Direction::Next => primary_diagnostics_after
11381 .iter()
11382 .skip(
11383 last_same_group_diagnostic_after
11384 .map(|index| index + 1)
11385 .unwrap_or(0),
11386 )
11387 .next(),
11388 };
11389
11390 // Cycle around to the start of the buffer, potentially moving back to the start of
11391 // the currently active diagnostic.
11392 let cycle_around = || match direction {
11393 Direction::Prev => primary_diagnostics_after
11394 .iter()
11395 .rev()
11396 .chain(primary_diagnostics_before.iter().rev())
11397 .next(),
11398 Direction::Next => primary_diagnostics_before
11399 .iter()
11400 .chain(primary_diagnostics_after.iter())
11401 .next(),
11402 };
11403
11404 if let Some((primary_range, group_id)) = next_primary_diagnostic
11405 .or_else(cycle_around)
11406 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11407 {
11408 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11409 return;
11410 };
11411 self.activate_diagnostics(buffer_id, group_id, window, cx);
11412 if self.active_diagnostics.is_some() {
11413 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11414 s.select(vec![Selection {
11415 id: selection.id,
11416 start: primary_range.start,
11417 end: primary_range.start,
11418 reversed: false,
11419 goal: SelectionGoal::None,
11420 }]);
11421 });
11422 self.refresh_inline_completion(false, true, window, cx);
11423 }
11424 }
11425 }
11426
11427 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11428 let snapshot = self.snapshot(window, cx);
11429 let selection = self.selections.newest::<Point>(cx);
11430 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
11431 }
11432
11433 fn go_to_hunk_after_position(
11434 &mut self,
11435 snapshot: &EditorSnapshot,
11436 position: Point,
11437 window: &mut Window,
11438 cx: &mut Context<Editor>,
11439 ) -> Option<MultiBufferDiffHunk> {
11440 let mut hunk = snapshot
11441 .buffer_snapshot
11442 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11443 .find(|hunk| hunk.row_range.start.0 > position.row);
11444 if hunk.is_none() {
11445 hunk = snapshot
11446 .buffer_snapshot
11447 .diff_hunks_in_range(Point::zero()..position)
11448 .find(|hunk| hunk.row_range.end.0 < position.row)
11449 }
11450 if let Some(hunk) = &hunk {
11451 let destination = Point::new(hunk.row_range.start.0, 0);
11452 self.unfold_ranges(&[destination..destination], false, false, cx);
11453 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11454 s.select_ranges(vec![destination..destination]);
11455 });
11456 }
11457
11458 hunk
11459 }
11460
11461 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
11462 let snapshot = self.snapshot(window, cx);
11463 let selection = self.selections.newest::<Point>(cx);
11464 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
11465 }
11466
11467 fn go_to_hunk_before_position(
11468 &mut self,
11469 snapshot: &EditorSnapshot,
11470 position: Point,
11471 window: &mut Window,
11472 cx: &mut Context<Editor>,
11473 ) -> Option<MultiBufferDiffHunk> {
11474 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
11475 if hunk.is_none() {
11476 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
11477 }
11478 if let Some(hunk) = &hunk {
11479 let destination = Point::new(hunk.row_range.start.0, 0);
11480 self.unfold_ranges(&[destination..destination], false, false, cx);
11481 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11482 s.select_ranges(vec![destination..destination]);
11483 });
11484 }
11485
11486 hunk
11487 }
11488
11489 pub fn go_to_definition(
11490 &mut self,
11491 _: &GoToDefinition,
11492 window: &mut Window,
11493 cx: &mut Context<Self>,
11494 ) -> Task<Result<Navigated>> {
11495 let definition =
11496 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11497 cx.spawn_in(window, |editor, mut cx| async move {
11498 if definition.await? == Navigated::Yes {
11499 return Ok(Navigated::Yes);
11500 }
11501 match editor.update_in(&mut cx, |editor, window, cx| {
11502 editor.find_all_references(&FindAllReferences, window, cx)
11503 })? {
11504 Some(references) => references.await,
11505 None => Ok(Navigated::No),
11506 }
11507 })
11508 }
11509
11510 pub fn go_to_declaration(
11511 &mut self,
11512 _: &GoToDeclaration,
11513 window: &mut Window,
11514 cx: &mut Context<Self>,
11515 ) -> Task<Result<Navigated>> {
11516 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11517 }
11518
11519 pub fn go_to_declaration_split(
11520 &mut self,
11521 _: &GoToDeclaration,
11522 window: &mut Window,
11523 cx: &mut Context<Self>,
11524 ) -> Task<Result<Navigated>> {
11525 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11526 }
11527
11528 pub fn go_to_implementation(
11529 &mut self,
11530 _: &GoToImplementation,
11531 window: &mut Window,
11532 cx: &mut Context<Self>,
11533 ) -> Task<Result<Navigated>> {
11534 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11535 }
11536
11537 pub fn go_to_implementation_split(
11538 &mut self,
11539 _: &GoToImplementationSplit,
11540 window: &mut Window,
11541 cx: &mut Context<Self>,
11542 ) -> Task<Result<Navigated>> {
11543 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11544 }
11545
11546 pub fn go_to_type_definition(
11547 &mut self,
11548 _: &GoToTypeDefinition,
11549 window: &mut Window,
11550 cx: &mut Context<Self>,
11551 ) -> Task<Result<Navigated>> {
11552 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11553 }
11554
11555 pub fn go_to_definition_split(
11556 &mut self,
11557 _: &GoToDefinitionSplit,
11558 window: &mut Window,
11559 cx: &mut Context<Self>,
11560 ) -> Task<Result<Navigated>> {
11561 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11562 }
11563
11564 pub fn go_to_type_definition_split(
11565 &mut self,
11566 _: &GoToTypeDefinitionSplit,
11567 window: &mut Window,
11568 cx: &mut Context<Self>,
11569 ) -> Task<Result<Navigated>> {
11570 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11571 }
11572
11573 fn go_to_definition_of_kind(
11574 &mut self,
11575 kind: GotoDefinitionKind,
11576 split: bool,
11577 window: &mut Window,
11578 cx: &mut Context<Self>,
11579 ) -> Task<Result<Navigated>> {
11580 let Some(provider) = self.semantics_provider.clone() else {
11581 return Task::ready(Ok(Navigated::No));
11582 };
11583 let head = self.selections.newest::<usize>(cx).head();
11584 let buffer = self.buffer.read(cx);
11585 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11586 text_anchor
11587 } else {
11588 return Task::ready(Ok(Navigated::No));
11589 };
11590
11591 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11592 return Task::ready(Ok(Navigated::No));
11593 };
11594
11595 cx.spawn_in(window, |editor, mut cx| async move {
11596 let definitions = definitions.await?;
11597 let navigated = editor
11598 .update_in(&mut cx, |editor, window, cx| {
11599 editor.navigate_to_hover_links(
11600 Some(kind),
11601 definitions
11602 .into_iter()
11603 .filter(|location| {
11604 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11605 })
11606 .map(HoverLink::Text)
11607 .collect::<Vec<_>>(),
11608 split,
11609 window,
11610 cx,
11611 )
11612 })?
11613 .await?;
11614 anyhow::Ok(navigated)
11615 })
11616 }
11617
11618 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11619 let selection = self.selections.newest_anchor();
11620 let head = selection.head();
11621 let tail = selection.tail();
11622
11623 let Some((buffer, start_position)) =
11624 self.buffer.read(cx).text_anchor_for_position(head, cx)
11625 else {
11626 return;
11627 };
11628
11629 let end_position = if head != tail {
11630 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11631 return;
11632 };
11633 Some(pos)
11634 } else {
11635 None
11636 };
11637
11638 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11639 let url = if let Some(end_pos) = end_position {
11640 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11641 } else {
11642 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11643 };
11644
11645 if let Some(url) = url {
11646 editor.update(&mut cx, |_, cx| {
11647 cx.open_url(&url);
11648 })
11649 } else {
11650 Ok(())
11651 }
11652 });
11653
11654 url_finder.detach();
11655 }
11656
11657 pub fn open_selected_filename(
11658 &mut self,
11659 _: &OpenSelectedFilename,
11660 window: &mut Window,
11661 cx: &mut Context<Self>,
11662 ) {
11663 let Some(workspace) = self.workspace() else {
11664 return;
11665 };
11666
11667 let position = self.selections.newest_anchor().head();
11668
11669 let Some((buffer, buffer_position)) =
11670 self.buffer.read(cx).text_anchor_for_position(position, cx)
11671 else {
11672 return;
11673 };
11674
11675 let project = self.project.clone();
11676
11677 cx.spawn_in(window, |_, mut cx| async move {
11678 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
11679
11680 if let Some((_, path)) = result {
11681 workspace
11682 .update_in(&mut cx, |workspace, window, cx| {
11683 workspace.open_resolved_path(path, window, cx)
11684 })?
11685 .await?;
11686 }
11687 anyhow::Ok(())
11688 })
11689 .detach();
11690 }
11691
11692 pub(crate) fn navigate_to_hover_links(
11693 &mut self,
11694 kind: Option<GotoDefinitionKind>,
11695 mut definitions: Vec<HoverLink>,
11696 split: bool,
11697 window: &mut Window,
11698 cx: &mut Context<Editor>,
11699 ) -> Task<Result<Navigated>> {
11700 // If there is one definition, just open it directly
11701 if definitions.len() == 1 {
11702 let definition = definitions.pop().unwrap();
11703
11704 enum TargetTaskResult {
11705 Location(Option<Location>),
11706 AlreadyNavigated,
11707 }
11708
11709 let target_task = match definition {
11710 HoverLink::Text(link) => {
11711 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
11712 }
11713 HoverLink::InlayHint(lsp_location, server_id) => {
11714 let computation =
11715 self.compute_target_location(lsp_location, server_id, window, cx);
11716 cx.background_spawn(async move {
11717 let location = computation.await?;
11718 Ok(TargetTaskResult::Location(location))
11719 })
11720 }
11721 HoverLink::Url(url) => {
11722 cx.open_url(&url);
11723 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
11724 }
11725 HoverLink::File(path) => {
11726 if let Some(workspace) = self.workspace() {
11727 cx.spawn_in(window, |_, mut cx| async move {
11728 workspace
11729 .update_in(&mut cx, |workspace, window, cx| {
11730 workspace.open_resolved_path(path, window, cx)
11731 })?
11732 .await
11733 .map(|_| TargetTaskResult::AlreadyNavigated)
11734 })
11735 } else {
11736 Task::ready(Ok(TargetTaskResult::Location(None)))
11737 }
11738 }
11739 };
11740 cx.spawn_in(window, |editor, mut cx| async move {
11741 let target = match target_task.await.context("target resolution task")? {
11742 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
11743 TargetTaskResult::Location(None) => return Ok(Navigated::No),
11744 TargetTaskResult::Location(Some(target)) => target,
11745 };
11746
11747 editor.update_in(&mut cx, |editor, window, cx| {
11748 let Some(workspace) = editor.workspace() else {
11749 return Navigated::No;
11750 };
11751 let pane = workspace.read(cx).active_pane().clone();
11752
11753 let range = target.range.to_point(target.buffer.read(cx));
11754 let range = editor.range_for_match(&range);
11755 let range = collapse_multiline_range(range);
11756
11757 if !split
11758 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
11759 {
11760 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
11761 } else {
11762 window.defer(cx, move |window, cx| {
11763 let target_editor: Entity<Self> =
11764 workspace.update(cx, |workspace, cx| {
11765 let pane = if split {
11766 workspace.adjacent_pane(window, cx)
11767 } else {
11768 workspace.active_pane().clone()
11769 };
11770
11771 workspace.open_project_item(
11772 pane,
11773 target.buffer.clone(),
11774 true,
11775 true,
11776 window,
11777 cx,
11778 )
11779 });
11780 target_editor.update(cx, |target_editor, cx| {
11781 // When selecting a definition in a different buffer, disable the nav history
11782 // to avoid creating a history entry at the previous cursor location.
11783 pane.update(cx, |pane, _| pane.disable_history());
11784 target_editor.go_to_singleton_buffer_range(range, window, cx);
11785 pane.update(cx, |pane, _| pane.enable_history());
11786 });
11787 });
11788 }
11789 Navigated::Yes
11790 })
11791 })
11792 } else if !definitions.is_empty() {
11793 cx.spawn_in(window, |editor, mut cx| async move {
11794 let (title, location_tasks, workspace) = editor
11795 .update_in(&mut cx, |editor, window, cx| {
11796 let tab_kind = match kind {
11797 Some(GotoDefinitionKind::Implementation) => "Implementations",
11798 _ => "Definitions",
11799 };
11800 let title = definitions
11801 .iter()
11802 .find_map(|definition| match definition {
11803 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
11804 let buffer = origin.buffer.read(cx);
11805 format!(
11806 "{} for {}",
11807 tab_kind,
11808 buffer
11809 .text_for_range(origin.range.clone())
11810 .collect::<String>()
11811 )
11812 }),
11813 HoverLink::InlayHint(_, _) => None,
11814 HoverLink::Url(_) => None,
11815 HoverLink::File(_) => None,
11816 })
11817 .unwrap_or(tab_kind.to_string());
11818 let location_tasks = definitions
11819 .into_iter()
11820 .map(|definition| match definition {
11821 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
11822 HoverLink::InlayHint(lsp_location, server_id) => editor
11823 .compute_target_location(lsp_location, server_id, window, cx),
11824 HoverLink::Url(_) => Task::ready(Ok(None)),
11825 HoverLink::File(_) => Task::ready(Ok(None)),
11826 })
11827 .collect::<Vec<_>>();
11828 (title, location_tasks, editor.workspace().clone())
11829 })
11830 .context("location tasks preparation")?;
11831
11832 let locations = future::join_all(location_tasks)
11833 .await
11834 .into_iter()
11835 .filter_map(|location| location.transpose())
11836 .collect::<Result<_>>()
11837 .context("location tasks")?;
11838
11839 let Some(workspace) = workspace else {
11840 return Ok(Navigated::No);
11841 };
11842 let opened = workspace
11843 .update_in(&mut cx, |workspace, window, cx| {
11844 Self::open_locations_in_multibuffer(
11845 workspace,
11846 locations,
11847 title,
11848 split,
11849 MultibufferSelectionMode::First,
11850 window,
11851 cx,
11852 )
11853 })
11854 .ok();
11855
11856 anyhow::Ok(Navigated::from_bool(opened.is_some()))
11857 })
11858 } else {
11859 Task::ready(Ok(Navigated::No))
11860 }
11861 }
11862
11863 fn compute_target_location(
11864 &self,
11865 lsp_location: lsp::Location,
11866 server_id: LanguageServerId,
11867 window: &mut Window,
11868 cx: &mut Context<Self>,
11869 ) -> Task<anyhow::Result<Option<Location>>> {
11870 let Some(project) = self.project.clone() else {
11871 return Task::ready(Ok(None));
11872 };
11873
11874 cx.spawn_in(window, move |editor, mut cx| async move {
11875 let location_task = editor.update(&mut cx, |_, cx| {
11876 project.update(cx, |project, cx| {
11877 let language_server_name = project
11878 .language_server_statuses(cx)
11879 .find(|(id, _)| server_id == *id)
11880 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
11881 language_server_name.map(|language_server_name| {
11882 project.open_local_buffer_via_lsp(
11883 lsp_location.uri.clone(),
11884 server_id,
11885 language_server_name,
11886 cx,
11887 )
11888 })
11889 })
11890 })?;
11891 let location = match location_task {
11892 Some(task) => Some({
11893 let target_buffer_handle = task.await.context("open local buffer")?;
11894 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
11895 let target_start = target_buffer
11896 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
11897 let target_end = target_buffer
11898 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
11899 target_buffer.anchor_after(target_start)
11900 ..target_buffer.anchor_before(target_end)
11901 })?;
11902 Location {
11903 buffer: target_buffer_handle,
11904 range,
11905 }
11906 }),
11907 None => None,
11908 };
11909 Ok(location)
11910 })
11911 }
11912
11913 pub fn find_all_references(
11914 &mut self,
11915 _: &FindAllReferences,
11916 window: &mut Window,
11917 cx: &mut Context<Self>,
11918 ) -> Option<Task<Result<Navigated>>> {
11919 let selection = self.selections.newest::<usize>(cx);
11920 let multi_buffer = self.buffer.read(cx);
11921 let head = selection.head();
11922
11923 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11924 let head_anchor = multi_buffer_snapshot.anchor_at(
11925 head,
11926 if head < selection.tail() {
11927 Bias::Right
11928 } else {
11929 Bias::Left
11930 },
11931 );
11932
11933 match self
11934 .find_all_references_task_sources
11935 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
11936 {
11937 Ok(_) => {
11938 log::info!(
11939 "Ignoring repeated FindAllReferences invocation with the position of already running task"
11940 );
11941 return None;
11942 }
11943 Err(i) => {
11944 self.find_all_references_task_sources.insert(i, head_anchor);
11945 }
11946 }
11947
11948 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
11949 let workspace = self.workspace()?;
11950 let project = workspace.read(cx).project().clone();
11951 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
11952 Some(cx.spawn_in(window, |editor, mut cx| async move {
11953 let _cleanup = defer({
11954 let mut cx = cx.clone();
11955 move || {
11956 let _ = editor.update(&mut cx, |editor, _| {
11957 if let Ok(i) =
11958 editor
11959 .find_all_references_task_sources
11960 .binary_search_by(|anchor| {
11961 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
11962 })
11963 {
11964 editor.find_all_references_task_sources.remove(i);
11965 }
11966 });
11967 }
11968 });
11969
11970 let locations = references.await?;
11971 if locations.is_empty() {
11972 return anyhow::Ok(Navigated::No);
11973 }
11974
11975 workspace.update_in(&mut cx, |workspace, window, cx| {
11976 let title = locations
11977 .first()
11978 .as_ref()
11979 .map(|location| {
11980 let buffer = location.buffer.read(cx);
11981 format!(
11982 "References to `{}`",
11983 buffer
11984 .text_for_range(location.range.clone())
11985 .collect::<String>()
11986 )
11987 })
11988 .unwrap();
11989 Self::open_locations_in_multibuffer(
11990 workspace,
11991 locations,
11992 title,
11993 false,
11994 MultibufferSelectionMode::First,
11995 window,
11996 cx,
11997 );
11998 Navigated::Yes
11999 })
12000 }))
12001 }
12002
12003 /// Opens a multibuffer with the given project locations in it
12004 pub fn open_locations_in_multibuffer(
12005 workspace: &mut Workspace,
12006 mut locations: Vec<Location>,
12007 title: String,
12008 split: bool,
12009 multibuffer_selection_mode: MultibufferSelectionMode,
12010 window: &mut Window,
12011 cx: &mut Context<Workspace>,
12012 ) {
12013 // If there are multiple definitions, open them in a multibuffer
12014 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12015 let mut locations = locations.into_iter().peekable();
12016 let mut ranges = Vec::new();
12017 let capability = workspace.project().read(cx).capability();
12018
12019 let excerpt_buffer = cx.new(|cx| {
12020 let mut multibuffer = MultiBuffer::new(capability);
12021 while let Some(location) = locations.next() {
12022 let buffer = location.buffer.read(cx);
12023 let mut ranges_for_buffer = Vec::new();
12024 let range = location.range.to_offset(buffer);
12025 ranges_for_buffer.push(range.clone());
12026
12027 while let Some(next_location) = locations.peek() {
12028 if next_location.buffer == location.buffer {
12029 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12030 locations.next();
12031 } else {
12032 break;
12033 }
12034 }
12035
12036 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12037 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12038 location.buffer.clone(),
12039 ranges_for_buffer,
12040 DEFAULT_MULTIBUFFER_CONTEXT,
12041 cx,
12042 ))
12043 }
12044
12045 multibuffer.with_title(title)
12046 });
12047
12048 let editor = cx.new(|cx| {
12049 Editor::for_multibuffer(
12050 excerpt_buffer,
12051 Some(workspace.project().clone()),
12052 true,
12053 window,
12054 cx,
12055 )
12056 });
12057 editor.update(cx, |editor, cx| {
12058 match multibuffer_selection_mode {
12059 MultibufferSelectionMode::First => {
12060 if let Some(first_range) = ranges.first() {
12061 editor.change_selections(None, window, cx, |selections| {
12062 selections.clear_disjoint();
12063 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12064 });
12065 }
12066 editor.highlight_background::<Self>(
12067 &ranges,
12068 |theme| theme.editor_highlighted_line_background,
12069 cx,
12070 );
12071 }
12072 MultibufferSelectionMode::All => {
12073 editor.change_selections(None, window, cx, |selections| {
12074 selections.clear_disjoint();
12075 selections.select_anchor_ranges(ranges);
12076 });
12077 }
12078 }
12079 editor.register_buffers_with_language_servers(cx);
12080 });
12081
12082 let item = Box::new(editor);
12083 let item_id = item.item_id();
12084
12085 if split {
12086 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12087 } else {
12088 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12089 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12090 pane.close_current_preview_item(window, cx)
12091 } else {
12092 None
12093 }
12094 });
12095 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12096 }
12097 workspace.active_pane().update(cx, |pane, cx| {
12098 pane.set_preview_item_id(Some(item_id), cx);
12099 });
12100 }
12101
12102 pub fn rename(
12103 &mut self,
12104 _: &Rename,
12105 window: &mut Window,
12106 cx: &mut Context<Self>,
12107 ) -> Option<Task<Result<()>>> {
12108 use language::ToOffset as _;
12109
12110 let provider = self.semantics_provider.clone()?;
12111 let selection = self.selections.newest_anchor().clone();
12112 let (cursor_buffer, cursor_buffer_position) = self
12113 .buffer
12114 .read(cx)
12115 .text_anchor_for_position(selection.head(), cx)?;
12116 let (tail_buffer, cursor_buffer_position_end) = self
12117 .buffer
12118 .read(cx)
12119 .text_anchor_for_position(selection.tail(), cx)?;
12120 if tail_buffer != cursor_buffer {
12121 return None;
12122 }
12123
12124 let snapshot = cursor_buffer.read(cx).snapshot();
12125 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12126 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12127 let prepare_rename = provider
12128 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12129 .unwrap_or_else(|| Task::ready(Ok(None)));
12130 drop(snapshot);
12131
12132 Some(cx.spawn_in(window, |this, mut cx| async move {
12133 let rename_range = if let Some(range) = prepare_rename.await? {
12134 Some(range)
12135 } else {
12136 this.update(&mut cx, |this, cx| {
12137 let buffer = this.buffer.read(cx).snapshot(cx);
12138 let mut buffer_highlights = this
12139 .document_highlights_for_position(selection.head(), &buffer)
12140 .filter(|highlight| {
12141 highlight.start.excerpt_id == selection.head().excerpt_id
12142 && highlight.end.excerpt_id == selection.head().excerpt_id
12143 });
12144 buffer_highlights
12145 .next()
12146 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12147 })?
12148 };
12149 if let Some(rename_range) = rename_range {
12150 this.update_in(&mut cx, |this, window, cx| {
12151 let snapshot = cursor_buffer.read(cx).snapshot();
12152 let rename_buffer_range = rename_range.to_offset(&snapshot);
12153 let cursor_offset_in_rename_range =
12154 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12155 let cursor_offset_in_rename_range_end =
12156 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12157
12158 this.take_rename(false, window, cx);
12159 let buffer = this.buffer.read(cx).read(cx);
12160 let cursor_offset = selection.head().to_offset(&buffer);
12161 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12162 let rename_end = rename_start + rename_buffer_range.len();
12163 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12164 let mut old_highlight_id = None;
12165 let old_name: Arc<str> = buffer
12166 .chunks(rename_start..rename_end, true)
12167 .map(|chunk| {
12168 if old_highlight_id.is_none() {
12169 old_highlight_id = chunk.syntax_highlight_id;
12170 }
12171 chunk.text
12172 })
12173 .collect::<String>()
12174 .into();
12175
12176 drop(buffer);
12177
12178 // Position the selection in the rename editor so that it matches the current selection.
12179 this.show_local_selections = false;
12180 let rename_editor = cx.new(|cx| {
12181 let mut editor = Editor::single_line(window, cx);
12182 editor.buffer.update(cx, |buffer, cx| {
12183 buffer.edit([(0..0, old_name.clone())], None, cx)
12184 });
12185 let rename_selection_range = match cursor_offset_in_rename_range
12186 .cmp(&cursor_offset_in_rename_range_end)
12187 {
12188 Ordering::Equal => {
12189 editor.select_all(&SelectAll, window, cx);
12190 return editor;
12191 }
12192 Ordering::Less => {
12193 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12194 }
12195 Ordering::Greater => {
12196 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12197 }
12198 };
12199 if rename_selection_range.end > old_name.len() {
12200 editor.select_all(&SelectAll, window, cx);
12201 } else {
12202 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12203 s.select_ranges([rename_selection_range]);
12204 });
12205 }
12206 editor
12207 });
12208 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12209 if e == &EditorEvent::Focused {
12210 cx.emit(EditorEvent::FocusedIn)
12211 }
12212 })
12213 .detach();
12214
12215 let write_highlights =
12216 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12217 let read_highlights =
12218 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12219 let ranges = write_highlights
12220 .iter()
12221 .flat_map(|(_, ranges)| ranges.iter())
12222 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12223 .cloned()
12224 .collect();
12225
12226 this.highlight_text::<Rename>(
12227 ranges,
12228 HighlightStyle {
12229 fade_out: Some(0.6),
12230 ..Default::default()
12231 },
12232 cx,
12233 );
12234 let rename_focus_handle = rename_editor.focus_handle(cx);
12235 window.focus(&rename_focus_handle);
12236 let block_id = this.insert_blocks(
12237 [BlockProperties {
12238 style: BlockStyle::Flex,
12239 placement: BlockPlacement::Below(range.start),
12240 height: 1,
12241 render: Arc::new({
12242 let rename_editor = rename_editor.clone();
12243 move |cx: &mut BlockContext| {
12244 let mut text_style = cx.editor_style.text.clone();
12245 if let Some(highlight_style) = old_highlight_id
12246 .and_then(|h| h.style(&cx.editor_style.syntax))
12247 {
12248 text_style = text_style.highlight(highlight_style);
12249 }
12250 div()
12251 .block_mouse_down()
12252 .pl(cx.anchor_x)
12253 .child(EditorElement::new(
12254 &rename_editor,
12255 EditorStyle {
12256 background: cx.theme().system().transparent,
12257 local_player: cx.editor_style.local_player,
12258 text: text_style,
12259 scrollbar_width: cx.editor_style.scrollbar_width,
12260 syntax: cx.editor_style.syntax.clone(),
12261 status: cx.editor_style.status.clone(),
12262 inlay_hints_style: HighlightStyle {
12263 font_weight: Some(FontWeight::BOLD),
12264 ..make_inlay_hints_style(cx.app)
12265 },
12266 inline_completion_styles: make_suggestion_styles(
12267 cx.app,
12268 ),
12269 ..EditorStyle::default()
12270 },
12271 ))
12272 .into_any_element()
12273 }
12274 }),
12275 priority: 0,
12276 }],
12277 Some(Autoscroll::fit()),
12278 cx,
12279 )[0];
12280 this.pending_rename = Some(RenameState {
12281 range,
12282 old_name,
12283 editor: rename_editor,
12284 block_id,
12285 });
12286 })?;
12287 }
12288
12289 Ok(())
12290 }))
12291 }
12292
12293 pub fn confirm_rename(
12294 &mut self,
12295 _: &ConfirmRename,
12296 window: &mut Window,
12297 cx: &mut Context<Self>,
12298 ) -> Option<Task<Result<()>>> {
12299 let rename = self.take_rename(false, window, cx)?;
12300 let workspace = self.workspace()?.downgrade();
12301 let (buffer, start) = self
12302 .buffer
12303 .read(cx)
12304 .text_anchor_for_position(rename.range.start, cx)?;
12305 let (end_buffer, _) = self
12306 .buffer
12307 .read(cx)
12308 .text_anchor_for_position(rename.range.end, cx)?;
12309 if buffer != end_buffer {
12310 return None;
12311 }
12312
12313 let old_name = rename.old_name;
12314 let new_name = rename.editor.read(cx).text(cx);
12315
12316 let rename = self.semantics_provider.as_ref()?.perform_rename(
12317 &buffer,
12318 start,
12319 new_name.clone(),
12320 cx,
12321 )?;
12322
12323 Some(cx.spawn_in(window, |editor, mut cx| async move {
12324 let project_transaction = rename.await?;
12325 Self::open_project_transaction(
12326 &editor,
12327 workspace,
12328 project_transaction,
12329 format!("Rename: {} → {}", old_name, new_name),
12330 cx.clone(),
12331 )
12332 .await?;
12333
12334 editor.update(&mut cx, |editor, cx| {
12335 editor.refresh_document_highlights(cx);
12336 })?;
12337 Ok(())
12338 }))
12339 }
12340
12341 fn take_rename(
12342 &mut self,
12343 moving_cursor: bool,
12344 window: &mut Window,
12345 cx: &mut Context<Self>,
12346 ) -> Option<RenameState> {
12347 let rename = self.pending_rename.take()?;
12348 if rename.editor.focus_handle(cx).is_focused(window) {
12349 window.focus(&self.focus_handle);
12350 }
12351
12352 self.remove_blocks(
12353 [rename.block_id].into_iter().collect(),
12354 Some(Autoscroll::fit()),
12355 cx,
12356 );
12357 self.clear_highlights::<Rename>(cx);
12358 self.show_local_selections = true;
12359
12360 if moving_cursor {
12361 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12362 editor.selections.newest::<usize>(cx).head()
12363 });
12364
12365 // Update the selection to match the position of the selection inside
12366 // the rename editor.
12367 let snapshot = self.buffer.read(cx).read(cx);
12368 let rename_range = rename.range.to_offset(&snapshot);
12369 let cursor_in_editor = snapshot
12370 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12371 .min(rename_range.end);
12372 drop(snapshot);
12373
12374 self.change_selections(None, window, cx, |s| {
12375 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12376 });
12377 } else {
12378 self.refresh_document_highlights(cx);
12379 }
12380
12381 Some(rename)
12382 }
12383
12384 pub fn pending_rename(&self) -> Option<&RenameState> {
12385 self.pending_rename.as_ref()
12386 }
12387
12388 fn format(
12389 &mut self,
12390 _: &Format,
12391 window: &mut Window,
12392 cx: &mut Context<Self>,
12393 ) -> Option<Task<Result<()>>> {
12394 let project = match &self.project {
12395 Some(project) => project.clone(),
12396 None => return None,
12397 };
12398
12399 Some(self.perform_format(
12400 project,
12401 FormatTrigger::Manual,
12402 FormatTarget::Buffers,
12403 window,
12404 cx,
12405 ))
12406 }
12407
12408 fn format_selections(
12409 &mut self,
12410 _: &FormatSelections,
12411 window: &mut Window,
12412 cx: &mut Context<Self>,
12413 ) -> Option<Task<Result<()>>> {
12414 let project = match &self.project {
12415 Some(project) => project.clone(),
12416 None => return None,
12417 };
12418
12419 let ranges = self
12420 .selections
12421 .all_adjusted(cx)
12422 .into_iter()
12423 .map(|selection| selection.range())
12424 .collect_vec();
12425
12426 Some(self.perform_format(
12427 project,
12428 FormatTrigger::Manual,
12429 FormatTarget::Ranges(ranges),
12430 window,
12431 cx,
12432 ))
12433 }
12434
12435 fn perform_format(
12436 &mut self,
12437 project: Entity<Project>,
12438 trigger: FormatTrigger,
12439 target: FormatTarget,
12440 window: &mut Window,
12441 cx: &mut Context<Self>,
12442 ) -> Task<Result<()>> {
12443 let buffer = self.buffer.clone();
12444 let (buffers, target) = match target {
12445 FormatTarget::Buffers => {
12446 let mut buffers = buffer.read(cx).all_buffers();
12447 if trigger == FormatTrigger::Save {
12448 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12449 }
12450 (buffers, LspFormatTarget::Buffers)
12451 }
12452 FormatTarget::Ranges(selection_ranges) => {
12453 let multi_buffer = buffer.read(cx);
12454 let snapshot = multi_buffer.read(cx);
12455 let mut buffers = HashSet::default();
12456 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12457 BTreeMap::new();
12458 for selection_range in selection_ranges {
12459 for (buffer, buffer_range, _) in
12460 snapshot.range_to_buffer_ranges(selection_range)
12461 {
12462 let buffer_id = buffer.remote_id();
12463 let start = buffer.anchor_before(buffer_range.start);
12464 let end = buffer.anchor_after(buffer_range.end);
12465 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12466 buffer_id_to_ranges
12467 .entry(buffer_id)
12468 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12469 .or_insert_with(|| vec![start..end]);
12470 }
12471 }
12472 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12473 }
12474 };
12475
12476 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12477 let format = project.update(cx, |project, cx| {
12478 project.format(buffers, target, true, trigger, cx)
12479 });
12480
12481 cx.spawn_in(window, |_, mut cx| async move {
12482 let transaction = futures::select_biased! {
12483 () = timeout => {
12484 log::warn!("timed out waiting for formatting");
12485 None
12486 }
12487 transaction = format.log_err().fuse() => transaction,
12488 };
12489
12490 buffer
12491 .update(&mut cx, |buffer, cx| {
12492 if let Some(transaction) = transaction {
12493 if !buffer.is_singleton() {
12494 buffer.push_transaction(&transaction.0, cx);
12495 }
12496 }
12497
12498 cx.notify();
12499 })
12500 .ok();
12501
12502 Ok(())
12503 })
12504 }
12505
12506 fn restart_language_server(
12507 &mut self,
12508 _: &RestartLanguageServer,
12509 _: &mut Window,
12510 cx: &mut Context<Self>,
12511 ) {
12512 if let Some(project) = self.project.clone() {
12513 self.buffer.update(cx, |multi_buffer, cx| {
12514 project.update(cx, |project, cx| {
12515 project.restart_language_servers_for_buffers(
12516 multi_buffer.all_buffers().into_iter().collect(),
12517 cx,
12518 );
12519 });
12520 })
12521 }
12522 }
12523
12524 fn cancel_language_server_work(
12525 workspace: &mut Workspace,
12526 _: &actions::CancelLanguageServerWork,
12527 _: &mut Window,
12528 cx: &mut Context<Workspace>,
12529 ) {
12530 let project = workspace.project();
12531 let buffers = workspace
12532 .active_item(cx)
12533 .and_then(|item| item.act_as::<Editor>(cx))
12534 .map_or(HashSet::default(), |editor| {
12535 editor.read(cx).buffer.read(cx).all_buffers()
12536 });
12537 project.update(cx, |project, cx| {
12538 project.cancel_language_server_work_for_buffers(buffers, cx);
12539 });
12540 }
12541
12542 fn show_character_palette(
12543 &mut self,
12544 _: &ShowCharacterPalette,
12545 window: &mut Window,
12546 _: &mut Context<Self>,
12547 ) {
12548 window.show_character_palette();
12549 }
12550
12551 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12552 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12553 let buffer = self.buffer.read(cx).snapshot(cx);
12554 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12555 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12556 let is_valid = buffer
12557 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12558 .any(|entry| {
12559 entry.diagnostic.is_primary
12560 && !entry.range.is_empty()
12561 && entry.range.start == primary_range_start
12562 && entry.diagnostic.message == active_diagnostics.primary_message
12563 });
12564
12565 if is_valid != active_diagnostics.is_valid {
12566 active_diagnostics.is_valid = is_valid;
12567 if is_valid {
12568 let mut new_styles = HashMap::default();
12569 for (block_id, diagnostic) in &active_diagnostics.blocks {
12570 new_styles.insert(
12571 *block_id,
12572 diagnostic_block_renderer(diagnostic.clone(), None, true),
12573 );
12574 }
12575 self.display_map.update(cx, |display_map, _cx| {
12576 display_map.replace_blocks(new_styles);
12577 });
12578 } else {
12579 self.dismiss_diagnostics(cx);
12580 }
12581 }
12582 }
12583 }
12584
12585 fn activate_diagnostics(
12586 &mut self,
12587 buffer_id: BufferId,
12588 group_id: usize,
12589 window: &mut Window,
12590 cx: &mut Context<Self>,
12591 ) {
12592 self.dismiss_diagnostics(cx);
12593 let snapshot = self.snapshot(window, cx);
12594 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
12595 let buffer = self.buffer.read(cx).snapshot(cx);
12596
12597 let mut primary_range = None;
12598 let mut primary_message = None;
12599 let diagnostic_group = buffer
12600 .diagnostic_group(buffer_id, group_id)
12601 .filter_map(|entry| {
12602 let start = entry.range.start;
12603 let end = entry.range.end;
12604 if snapshot.is_line_folded(MultiBufferRow(start.row))
12605 && (start.row == end.row
12606 || snapshot.is_line_folded(MultiBufferRow(end.row)))
12607 {
12608 return None;
12609 }
12610 if entry.diagnostic.is_primary {
12611 primary_range = Some(entry.range.clone());
12612 primary_message = Some(entry.diagnostic.message.clone());
12613 }
12614 Some(entry)
12615 })
12616 .collect::<Vec<_>>();
12617 let primary_range = primary_range?;
12618 let primary_message = primary_message?;
12619
12620 let blocks = display_map
12621 .insert_blocks(
12622 diagnostic_group.iter().map(|entry| {
12623 let diagnostic = entry.diagnostic.clone();
12624 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
12625 BlockProperties {
12626 style: BlockStyle::Fixed,
12627 placement: BlockPlacement::Below(
12628 buffer.anchor_after(entry.range.start),
12629 ),
12630 height: message_height,
12631 render: diagnostic_block_renderer(diagnostic, None, true),
12632 priority: 0,
12633 }
12634 }),
12635 cx,
12636 )
12637 .into_iter()
12638 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
12639 .collect();
12640
12641 Some(ActiveDiagnosticGroup {
12642 primary_range: buffer.anchor_before(primary_range.start)
12643 ..buffer.anchor_after(primary_range.end),
12644 primary_message,
12645 group_id,
12646 blocks,
12647 is_valid: true,
12648 })
12649 });
12650 }
12651
12652 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
12653 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
12654 self.display_map.update(cx, |display_map, cx| {
12655 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
12656 });
12657 cx.notify();
12658 }
12659 }
12660
12661 /// Disable inline diagnostics rendering for this editor.
12662 pub fn disable_inline_diagnostics(&mut self) {
12663 self.inline_diagnostics_enabled = false;
12664 self.inline_diagnostics_update = Task::ready(());
12665 self.inline_diagnostics.clear();
12666 }
12667
12668 pub fn inline_diagnostics_enabled(&self) -> bool {
12669 self.inline_diagnostics_enabled
12670 }
12671
12672 pub fn show_inline_diagnostics(&self) -> bool {
12673 self.show_inline_diagnostics
12674 }
12675
12676 pub fn toggle_inline_diagnostics(
12677 &mut self,
12678 _: &ToggleInlineDiagnostics,
12679 window: &mut Window,
12680 cx: &mut Context<'_, Editor>,
12681 ) {
12682 self.show_inline_diagnostics = !self.show_inline_diagnostics;
12683 self.refresh_inline_diagnostics(false, window, cx);
12684 }
12685
12686 fn refresh_inline_diagnostics(
12687 &mut self,
12688 debounce: bool,
12689 window: &mut Window,
12690 cx: &mut Context<Self>,
12691 ) {
12692 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
12693 self.inline_diagnostics_update = Task::ready(());
12694 self.inline_diagnostics.clear();
12695 return;
12696 }
12697
12698 let debounce_ms = ProjectSettings::get_global(cx)
12699 .diagnostics
12700 .inline
12701 .update_debounce_ms;
12702 let debounce = if debounce && debounce_ms > 0 {
12703 Some(Duration::from_millis(debounce_ms))
12704 } else {
12705 None
12706 };
12707 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
12708 if let Some(debounce) = debounce {
12709 cx.background_executor().timer(debounce).await;
12710 }
12711 let Some(snapshot) = editor
12712 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
12713 .ok()
12714 else {
12715 return;
12716 };
12717
12718 let new_inline_diagnostics = cx
12719 .background_spawn(async move {
12720 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
12721 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
12722 let message = diagnostic_entry
12723 .diagnostic
12724 .message
12725 .split_once('\n')
12726 .map(|(line, _)| line)
12727 .map(SharedString::new)
12728 .unwrap_or_else(|| {
12729 SharedString::from(diagnostic_entry.diagnostic.message)
12730 });
12731 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
12732 let (Ok(i) | Err(i)) = inline_diagnostics
12733 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
12734 inline_diagnostics.insert(
12735 i,
12736 (
12737 start_anchor,
12738 InlineDiagnostic {
12739 message,
12740 group_id: diagnostic_entry.diagnostic.group_id,
12741 start: diagnostic_entry.range.start.to_point(&snapshot),
12742 is_primary: diagnostic_entry.diagnostic.is_primary,
12743 severity: diagnostic_entry.diagnostic.severity,
12744 },
12745 ),
12746 );
12747 }
12748 inline_diagnostics
12749 })
12750 .await;
12751
12752 editor
12753 .update(&mut cx, |editor, cx| {
12754 editor.inline_diagnostics = new_inline_diagnostics;
12755 cx.notify();
12756 })
12757 .ok();
12758 });
12759 }
12760
12761 pub fn set_selections_from_remote(
12762 &mut self,
12763 selections: Vec<Selection<Anchor>>,
12764 pending_selection: Option<Selection<Anchor>>,
12765 window: &mut Window,
12766 cx: &mut Context<Self>,
12767 ) {
12768 let old_cursor_position = self.selections.newest_anchor().head();
12769 self.selections.change_with(cx, |s| {
12770 s.select_anchors(selections);
12771 if let Some(pending_selection) = pending_selection {
12772 s.set_pending(pending_selection, SelectMode::Character);
12773 } else {
12774 s.clear_pending();
12775 }
12776 });
12777 self.selections_did_change(false, &old_cursor_position, true, window, cx);
12778 }
12779
12780 fn push_to_selection_history(&mut self) {
12781 self.selection_history.push(SelectionHistoryEntry {
12782 selections: self.selections.disjoint_anchors(),
12783 select_next_state: self.select_next_state.clone(),
12784 select_prev_state: self.select_prev_state.clone(),
12785 add_selections_state: self.add_selections_state.clone(),
12786 });
12787 }
12788
12789 pub fn transact(
12790 &mut self,
12791 window: &mut Window,
12792 cx: &mut Context<Self>,
12793 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
12794 ) -> Option<TransactionId> {
12795 self.start_transaction_at(Instant::now(), window, cx);
12796 update(self, window, cx);
12797 self.end_transaction_at(Instant::now(), cx)
12798 }
12799
12800 pub fn start_transaction_at(
12801 &mut self,
12802 now: Instant,
12803 window: &mut Window,
12804 cx: &mut Context<Self>,
12805 ) {
12806 self.end_selection(window, cx);
12807 if let Some(tx_id) = self
12808 .buffer
12809 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
12810 {
12811 self.selection_history
12812 .insert_transaction(tx_id, self.selections.disjoint_anchors());
12813 cx.emit(EditorEvent::TransactionBegun {
12814 transaction_id: tx_id,
12815 })
12816 }
12817 }
12818
12819 pub fn end_transaction_at(
12820 &mut self,
12821 now: Instant,
12822 cx: &mut Context<Self>,
12823 ) -> Option<TransactionId> {
12824 if let Some(transaction_id) = self
12825 .buffer
12826 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
12827 {
12828 if let Some((_, end_selections)) =
12829 self.selection_history.transaction_mut(transaction_id)
12830 {
12831 *end_selections = Some(self.selections.disjoint_anchors());
12832 } else {
12833 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
12834 }
12835
12836 cx.emit(EditorEvent::Edited { transaction_id });
12837 Some(transaction_id)
12838 } else {
12839 None
12840 }
12841 }
12842
12843 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
12844 if self.selection_mark_mode {
12845 self.change_selections(None, window, cx, |s| {
12846 s.move_with(|_, sel| {
12847 sel.collapse_to(sel.head(), SelectionGoal::None);
12848 });
12849 })
12850 }
12851 self.selection_mark_mode = true;
12852 cx.notify();
12853 }
12854
12855 pub fn swap_selection_ends(
12856 &mut self,
12857 _: &actions::SwapSelectionEnds,
12858 window: &mut Window,
12859 cx: &mut Context<Self>,
12860 ) {
12861 self.change_selections(None, window, cx, |s| {
12862 s.move_with(|_, sel| {
12863 if sel.start != sel.end {
12864 sel.reversed = !sel.reversed
12865 }
12866 });
12867 });
12868 self.request_autoscroll(Autoscroll::newest(), cx);
12869 cx.notify();
12870 }
12871
12872 pub fn toggle_fold(
12873 &mut self,
12874 _: &actions::ToggleFold,
12875 window: &mut Window,
12876 cx: &mut Context<Self>,
12877 ) {
12878 if self.is_singleton(cx) {
12879 let selection = self.selections.newest::<Point>(cx);
12880
12881 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12882 let range = if selection.is_empty() {
12883 let point = selection.head().to_display_point(&display_map);
12884 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12885 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12886 .to_point(&display_map);
12887 start..end
12888 } else {
12889 selection.range()
12890 };
12891 if display_map.folds_in_range(range).next().is_some() {
12892 self.unfold_lines(&Default::default(), window, cx)
12893 } else {
12894 self.fold(&Default::default(), window, cx)
12895 }
12896 } else {
12897 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12898 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12899 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12900 .map(|(snapshot, _, _)| snapshot.remote_id())
12901 .collect();
12902
12903 for buffer_id in buffer_ids {
12904 if self.is_buffer_folded(buffer_id, cx) {
12905 self.unfold_buffer(buffer_id, cx);
12906 } else {
12907 self.fold_buffer(buffer_id, cx);
12908 }
12909 }
12910 }
12911 }
12912
12913 pub fn toggle_fold_recursive(
12914 &mut self,
12915 _: &actions::ToggleFoldRecursive,
12916 window: &mut Window,
12917 cx: &mut Context<Self>,
12918 ) {
12919 let selection = self.selections.newest::<Point>(cx);
12920
12921 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12922 let range = if selection.is_empty() {
12923 let point = selection.head().to_display_point(&display_map);
12924 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
12925 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
12926 .to_point(&display_map);
12927 start..end
12928 } else {
12929 selection.range()
12930 };
12931 if display_map.folds_in_range(range).next().is_some() {
12932 self.unfold_recursive(&Default::default(), window, cx)
12933 } else {
12934 self.fold_recursive(&Default::default(), window, cx)
12935 }
12936 }
12937
12938 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
12939 if self.is_singleton(cx) {
12940 let mut to_fold = Vec::new();
12941 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12942 let selections = self.selections.all_adjusted(cx);
12943
12944 for selection in selections {
12945 let range = selection.range().sorted();
12946 let buffer_start_row = range.start.row;
12947
12948 if range.start.row != range.end.row {
12949 let mut found = false;
12950 let mut row = range.start.row;
12951 while row <= range.end.row {
12952 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
12953 {
12954 found = true;
12955 row = crease.range().end.row + 1;
12956 to_fold.push(crease);
12957 } else {
12958 row += 1
12959 }
12960 }
12961 if found {
12962 continue;
12963 }
12964 }
12965
12966 for row in (0..=range.start.row).rev() {
12967 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
12968 if crease.range().end.row >= buffer_start_row {
12969 to_fold.push(crease);
12970 if row <= range.start.row {
12971 break;
12972 }
12973 }
12974 }
12975 }
12976 }
12977
12978 self.fold_creases(to_fold, true, window, cx);
12979 } else {
12980 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12981
12982 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12983 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12984 .map(|(snapshot, _, _)| snapshot.remote_id())
12985 .collect();
12986 for buffer_id in buffer_ids {
12987 self.fold_buffer(buffer_id, cx);
12988 }
12989 }
12990 }
12991
12992 fn fold_at_level(
12993 &mut self,
12994 fold_at: &FoldAtLevel,
12995 window: &mut Window,
12996 cx: &mut Context<Self>,
12997 ) {
12998 if !self.buffer.read(cx).is_singleton() {
12999 return;
13000 }
13001
13002 let fold_at_level = fold_at.0;
13003 let snapshot = self.buffer.read(cx).snapshot(cx);
13004 let mut to_fold = Vec::new();
13005 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13006
13007 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13008 while start_row < end_row {
13009 match self
13010 .snapshot(window, cx)
13011 .crease_for_buffer_row(MultiBufferRow(start_row))
13012 {
13013 Some(crease) => {
13014 let nested_start_row = crease.range().start.row + 1;
13015 let nested_end_row = crease.range().end.row;
13016
13017 if current_level < fold_at_level {
13018 stack.push((nested_start_row, nested_end_row, current_level + 1));
13019 } else if current_level == fold_at_level {
13020 to_fold.push(crease);
13021 }
13022
13023 start_row = nested_end_row + 1;
13024 }
13025 None => start_row += 1,
13026 }
13027 }
13028 }
13029
13030 self.fold_creases(to_fold, true, window, cx);
13031 }
13032
13033 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13034 if self.buffer.read(cx).is_singleton() {
13035 let mut fold_ranges = Vec::new();
13036 let snapshot = self.buffer.read(cx).snapshot(cx);
13037
13038 for row in 0..snapshot.max_row().0 {
13039 if let Some(foldable_range) = self
13040 .snapshot(window, cx)
13041 .crease_for_buffer_row(MultiBufferRow(row))
13042 {
13043 fold_ranges.push(foldable_range);
13044 }
13045 }
13046
13047 self.fold_creases(fold_ranges, true, window, cx);
13048 } else {
13049 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13050 editor
13051 .update_in(&mut cx, |editor, _, cx| {
13052 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13053 editor.fold_buffer(buffer_id, cx);
13054 }
13055 })
13056 .ok();
13057 });
13058 }
13059 }
13060
13061 pub fn fold_function_bodies(
13062 &mut self,
13063 _: &actions::FoldFunctionBodies,
13064 window: &mut Window,
13065 cx: &mut Context<Self>,
13066 ) {
13067 let snapshot = self.buffer.read(cx).snapshot(cx);
13068
13069 let ranges = snapshot
13070 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13071 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13072 .collect::<Vec<_>>();
13073
13074 let creases = ranges
13075 .into_iter()
13076 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13077 .collect();
13078
13079 self.fold_creases(creases, true, window, cx);
13080 }
13081
13082 pub fn fold_recursive(
13083 &mut self,
13084 _: &actions::FoldRecursive,
13085 window: &mut Window,
13086 cx: &mut Context<Self>,
13087 ) {
13088 let mut to_fold = Vec::new();
13089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13090 let selections = self.selections.all_adjusted(cx);
13091
13092 for selection in selections {
13093 let range = selection.range().sorted();
13094 let buffer_start_row = range.start.row;
13095
13096 if range.start.row != range.end.row {
13097 let mut found = false;
13098 for row in range.start.row..=range.end.row {
13099 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13100 found = true;
13101 to_fold.push(crease);
13102 }
13103 }
13104 if found {
13105 continue;
13106 }
13107 }
13108
13109 for row in (0..=range.start.row).rev() {
13110 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13111 if crease.range().end.row >= buffer_start_row {
13112 to_fold.push(crease);
13113 } else {
13114 break;
13115 }
13116 }
13117 }
13118 }
13119
13120 self.fold_creases(to_fold, true, window, cx);
13121 }
13122
13123 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13124 let buffer_row = fold_at.buffer_row;
13125 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13126
13127 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13128 let autoscroll = self
13129 .selections
13130 .all::<Point>(cx)
13131 .iter()
13132 .any(|selection| crease.range().overlaps(&selection.range()));
13133
13134 self.fold_creases(vec![crease], autoscroll, window, cx);
13135 }
13136 }
13137
13138 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13139 if self.is_singleton(cx) {
13140 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13141 let buffer = &display_map.buffer_snapshot;
13142 let selections = self.selections.all::<Point>(cx);
13143 let ranges = selections
13144 .iter()
13145 .map(|s| {
13146 let range = s.display_range(&display_map).sorted();
13147 let mut start = range.start.to_point(&display_map);
13148 let mut end = range.end.to_point(&display_map);
13149 start.column = 0;
13150 end.column = buffer.line_len(MultiBufferRow(end.row));
13151 start..end
13152 })
13153 .collect::<Vec<_>>();
13154
13155 self.unfold_ranges(&ranges, true, true, cx);
13156 } else {
13157 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13158 let buffer_ids: HashSet<_> = multi_buffer_snapshot
13159 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
13160 .map(|(snapshot, _, _)| snapshot.remote_id())
13161 .collect();
13162 for buffer_id in buffer_ids {
13163 self.unfold_buffer(buffer_id, cx);
13164 }
13165 }
13166 }
13167
13168 pub fn unfold_recursive(
13169 &mut self,
13170 _: &UnfoldRecursive,
13171 _window: &mut Window,
13172 cx: &mut Context<Self>,
13173 ) {
13174 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13175 let selections = self.selections.all::<Point>(cx);
13176 let ranges = selections
13177 .iter()
13178 .map(|s| {
13179 let mut range = s.display_range(&display_map).sorted();
13180 *range.start.column_mut() = 0;
13181 *range.end.column_mut() = display_map.line_len(range.end.row());
13182 let start = range.start.to_point(&display_map);
13183 let end = range.end.to_point(&display_map);
13184 start..end
13185 })
13186 .collect::<Vec<_>>();
13187
13188 self.unfold_ranges(&ranges, true, true, cx);
13189 }
13190
13191 pub fn unfold_at(
13192 &mut self,
13193 unfold_at: &UnfoldAt,
13194 _window: &mut Window,
13195 cx: &mut Context<Self>,
13196 ) {
13197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13198
13199 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13200 ..Point::new(
13201 unfold_at.buffer_row.0,
13202 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13203 );
13204
13205 let autoscroll = self
13206 .selections
13207 .all::<Point>(cx)
13208 .iter()
13209 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13210
13211 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13212 }
13213
13214 pub fn unfold_all(
13215 &mut self,
13216 _: &actions::UnfoldAll,
13217 _window: &mut Window,
13218 cx: &mut Context<Self>,
13219 ) {
13220 if self.buffer.read(cx).is_singleton() {
13221 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13222 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13223 } else {
13224 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13225 editor
13226 .update(&mut cx, |editor, cx| {
13227 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13228 editor.unfold_buffer(buffer_id, cx);
13229 }
13230 })
13231 .ok();
13232 });
13233 }
13234 }
13235
13236 pub fn fold_selected_ranges(
13237 &mut self,
13238 _: &FoldSelectedRanges,
13239 window: &mut Window,
13240 cx: &mut Context<Self>,
13241 ) {
13242 let selections = self.selections.all::<Point>(cx);
13243 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13244 let line_mode = self.selections.line_mode;
13245 let ranges = selections
13246 .into_iter()
13247 .map(|s| {
13248 if line_mode {
13249 let start = Point::new(s.start.row, 0);
13250 let end = Point::new(
13251 s.end.row,
13252 display_map
13253 .buffer_snapshot
13254 .line_len(MultiBufferRow(s.end.row)),
13255 );
13256 Crease::simple(start..end, display_map.fold_placeholder.clone())
13257 } else {
13258 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13259 }
13260 })
13261 .collect::<Vec<_>>();
13262 self.fold_creases(ranges, true, window, cx);
13263 }
13264
13265 pub fn fold_ranges<T: ToOffset + Clone>(
13266 &mut self,
13267 ranges: Vec<Range<T>>,
13268 auto_scroll: bool,
13269 window: &mut Window,
13270 cx: &mut Context<Self>,
13271 ) {
13272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13273 let ranges = ranges
13274 .into_iter()
13275 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13276 .collect::<Vec<_>>();
13277 self.fold_creases(ranges, auto_scroll, window, cx);
13278 }
13279
13280 pub fn fold_creases<T: ToOffset + Clone>(
13281 &mut self,
13282 creases: Vec<Crease<T>>,
13283 auto_scroll: bool,
13284 window: &mut Window,
13285 cx: &mut Context<Self>,
13286 ) {
13287 if creases.is_empty() {
13288 return;
13289 }
13290
13291 let mut buffers_affected = HashSet::default();
13292 let multi_buffer = self.buffer().read(cx);
13293 for crease in &creases {
13294 if let Some((_, buffer, _)) =
13295 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13296 {
13297 buffers_affected.insert(buffer.read(cx).remote_id());
13298 };
13299 }
13300
13301 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13302
13303 if auto_scroll {
13304 self.request_autoscroll(Autoscroll::fit(), cx);
13305 }
13306
13307 cx.notify();
13308
13309 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13310 // Clear diagnostics block when folding a range that contains it.
13311 let snapshot = self.snapshot(window, cx);
13312 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13313 drop(snapshot);
13314 self.active_diagnostics = Some(active_diagnostics);
13315 self.dismiss_diagnostics(cx);
13316 } else {
13317 self.active_diagnostics = Some(active_diagnostics);
13318 }
13319 }
13320
13321 self.scrollbar_marker_state.dirty = true;
13322 }
13323
13324 /// Removes any folds whose ranges intersect any of the given ranges.
13325 pub fn unfold_ranges<T: ToOffset + Clone>(
13326 &mut self,
13327 ranges: &[Range<T>],
13328 inclusive: bool,
13329 auto_scroll: bool,
13330 cx: &mut Context<Self>,
13331 ) {
13332 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13333 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13334 });
13335 }
13336
13337 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13338 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13339 return;
13340 }
13341 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13342 self.display_map.update(cx, |display_map, cx| {
13343 display_map.fold_buffers([buffer_id], cx)
13344 });
13345 cx.emit(EditorEvent::BufferFoldToggled {
13346 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13347 folded: true,
13348 });
13349 cx.notify();
13350 }
13351
13352 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13353 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13354 return;
13355 }
13356 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13357 self.display_map.update(cx, |display_map, cx| {
13358 display_map.unfold_buffers([buffer_id], cx);
13359 });
13360 cx.emit(EditorEvent::BufferFoldToggled {
13361 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13362 folded: false,
13363 });
13364 cx.notify();
13365 }
13366
13367 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13368 self.display_map.read(cx).is_buffer_folded(buffer)
13369 }
13370
13371 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13372 self.display_map.read(cx).folded_buffers()
13373 }
13374
13375 /// Removes any folds with the given ranges.
13376 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13377 &mut self,
13378 ranges: &[Range<T>],
13379 type_id: TypeId,
13380 auto_scroll: bool,
13381 cx: &mut Context<Self>,
13382 ) {
13383 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13384 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13385 });
13386 }
13387
13388 fn remove_folds_with<T: ToOffset + Clone>(
13389 &mut self,
13390 ranges: &[Range<T>],
13391 auto_scroll: bool,
13392 cx: &mut Context<Self>,
13393 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13394 ) {
13395 if ranges.is_empty() {
13396 return;
13397 }
13398
13399 let mut buffers_affected = HashSet::default();
13400 let multi_buffer = self.buffer().read(cx);
13401 for range in ranges {
13402 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13403 buffers_affected.insert(buffer.read(cx).remote_id());
13404 };
13405 }
13406
13407 self.display_map.update(cx, update);
13408
13409 if auto_scroll {
13410 self.request_autoscroll(Autoscroll::fit(), cx);
13411 }
13412
13413 cx.notify();
13414 self.scrollbar_marker_state.dirty = true;
13415 self.active_indent_guides_state.dirty = true;
13416 }
13417
13418 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13419 self.display_map.read(cx).fold_placeholder.clone()
13420 }
13421
13422 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13423 self.buffer.update(cx, |buffer, cx| {
13424 buffer.set_all_diff_hunks_expanded(cx);
13425 });
13426 }
13427
13428 pub fn expand_all_diff_hunks(
13429 &mut self,
13430 _: &ExpandAllDiffHunks,
13431 _window: &mut Window,
13432 cx: &mut Context<Self>,
13433 ) {
13434 self.buffer.update(cx, |buffer, cx| {
13435 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13436 });
13437 }
13438
13439 pub fn toggle_selected_diff_hunks(
13440 &mut self,
13441 _: &ToggleSelectedDiffHunks,
13442 _window: &mut Window,
13443 cx: &mut Context<Self>,
13444 ) {
13445 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13446 self.toggle_diff_hunks_in_ranges(ranges, cx);
13447 }
13448
13449 pub fn diff_hunks_in_ranges<'a>(
13450 &'a self,
13451 ranges: &'a [Range<Anchor>],
13452 buffer: &'a MultiBufferSnapshot,
13453 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13454 ranges.iter().flat_map(move |range| {
13455 let end_excerpt_id = range.end.excerpt_id;
13456 let range = range.to_point(buffer);
13457 let mut peek_end = range.end;
13458 if range.end.row < buffer.max_row().0 {
13459 peek_end = Point::new(range.end.row + 1, 0);
13460 }
13461 buffer
13462 .diff_hunks_in_range(range.start..peek_end)
13463 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13464 })
13465 }
13466
13467 pub fn has_stageable_diff_hunks_in_ranges(
13468 &self,
13469 ranges: &[Range<Anchor>],
13470 snapshot: &MultiBufferSnapshot,
13471 ) -> bool {
13472 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13473 hunks.any(|hunk| hunk.secondary_status != DiffHunkSecondaryStatus::None)
13474 }
13475
13476 pub fn toggle_staged_selected_diff_hunks(
13477 &mut self,
13478 _: &::git::ToggleStaged,
13479 window: &mut Window,
13480 cx: &mut Context<Self>,
13481 ) {
13482 let snapshot = self.buffer.read(cx).snapshot(cx);
13483 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13484 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13485 self.stage_or_unstage_diff_hunks(stage, &ranges, window, cx);
13486 }
13487
13488 pub fn stage_and_next(
13489 &mut self,
13490 _: &::git::StageAndNext,
13491 window: &mut Window,
13492 cx: &mut Context<Self>,
13493 ) {
13494 self.do_stage_or_unstage_and_next(true, window, cx);
13495 }
13496
13497 pub fn unstage_and_next(
13498 &mut self,
13499 _: &::git::UnstageAndNext,
13500 window: &mut Window,
13501 cx: &mut Context<Self>,
13502 ) {
13503 self.do_stage_or_unstage_and_next(false, window, cx);
13504 }
13505
13506 pub fn stage_or_unstage_diff_hunks(
13507 &mut self,
13508 stage: bool,
13509 ranges: &[Range<Anchor>],
13510 window: &mut Window,
13511 cx: &mut Context<Self>,
13512 ) {
13513 let snapshot = self.buffer.read(cx).snapshot(cx);
13514 let Some(project) = &self.project else {
13515 return;
13516 };
13517
13518 let chunk_by = self
13519 .diff_hunks_in_ranges(&ranges, &snapshot)
13520 .chunk_by(|hunk| hunk.buffer_id);
13521 for (buffer_id, hunks) in &chunk_by {
13522 Self::do_stage_or_unstage(project, stage, buffer_id, hunks, &snapshot, window, cx);
13523 }
13524 }
13525
13526 fn do_stage_or_unstage_and_next(
13527 &mut self,
13528 stage: bool,
13529 window: &mut Window,
13530 cx: &mut Context<Self>,
13531 ) {
13532 let mut ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13533 if ranges.iter().any(|range| range.start != range.end) {
13534 self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
13535 return;
13536 }
13537
13538 if !self.buffer().read(cx).is_singleton() {
13539 if let Some((excerpt_id, buffer, range)) = self.active_excerpt(cx) {
13540 if buffer.read(cx).is_empty() {
13541 let buffer = buffer.read(cx);
13542 let Some(file) = buffer.file() else {
13543 return;
13544 };
13545 let project_path = project::ProjectPath {
13546 worktree_id: file.worktree_id(cx),
13547 path: file.path().clone(),
13548 };
13549 let Some(project) = self.project.as_ref() else {
13550 return;
13551 };
13552 let project = project.read(cx);
13553
13554 let Some(repo) = project.git_store().read(cx).active_repository() else {
13555 return;
13556 };
13557
13558 repo.update(cx, |repo, cx| {
13559 let Some(repo_path) = repo.project_path_to_repo_path(&project_path) else {
13560 return;
13561 };
13562 let Some(status) = repo.repository_entry.status_for_path(&repo_path) else {
13563 return;
13564 };
13565 if stage && status.status == FileStatus::Untracked {
13566 repo.stage_entries(vec![repo_path], cx)
13567 .detach_and_log_err(cx);
13568 return;
13569 }
13570 })
13571 }
13572 ranges = vec![multi_buffer::Anchor::range_in_buffer(
13573 excerpt_id,
13574 buffer.read(cx).remote_id(),
13575 range,
13576 )];
13577 self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
13578 let snapshot = self.buffer().read(cx).snapshot(cx);
13579 let mut point = ranges.last().unwrap().end.to_point(&snapshot);
13580 if point.row < snapshot.max_row().0 {
13581 point.row += 1;
13582 point.column = 0;
13583 point = snapshot.clip_point(point, Bias::Right);
13584 self.change_selections(Some(Autoscroll::top_relative(6)), window, cx, |s| {
13585 s.select_ranges([point..point]);
13586 })
13587 }
13588 return;
13589 }
13590 }
13591 self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
13592 self.go_to_next_hunk(&Default::default(), window, cx);
13593 }
13594
13595 fn do_stage_or_unstage(
13596 project: &Entity<Project>,
13597 stage: bool,
13598 buffer_id: BufferId,
13599 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
13600 snapshot: &MultiBufferSnapshot,
13601 window: &mut Window,
13602 cx: &mut App,
13603 ) {
13604 let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
13605 log::debug!("no buffer for id");
13606 return;
13607 };
13608 let buffer_snapshot = buffer.read(cx).snapshot();
13609 let file_exists = buffer_snapshot
13610 .file()
13611 .is_some_and(|file| file.disk_state().exists());
13612 let Some((repo, path)) = project
13613 .read(cx)
13614 .repository_and_path_for_buffer_id(buffer_id, cx)
13615 else {
13616 log::debug!("no git repo for buffer id");
13617 return;
13618 };
13619 let Some(diff) = snapshot.diff_for_buffer_id(buffer_id) else {
13620 log::debug!("no diff for buffer id");
13621 return;
13622 };
13623
13624 let new_index_text = if !stage && diff.is_single_insertion || stage && !file_exists {
13625 log::debug!("removing from index");
13626 None
13627 } else {
13628 diff.new_secondary_text_for_stage_or_unstage(
13629 stage,
13630 hunks.filter_map(|hunk| {
13631 if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None {
13632 return None;
13633 } else if !stage
13634 && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk
13635 {
13636 return None;
13637 }
13638 Some((hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone()))
13639 }),
13640 &buffer_snapshot,
13641 cx,
13642 )
13643 };
13644 if file_exists {
13645 let buffer_store = project.read(cx).buffer_store().clone();
13646 buffer_store
13647 .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
13648 .detach_and_log_err(cx);
13649 }
13650 let recv = repo
13651 .read(cx)
13652 .set_index_text(&path, new_index_text.map(|rope| rope.to_string()));
13653
13654 cx.background_spawn(async move { recv.await? })
13655 .detach_and_notify_err(window, cx);
13656 }
13657
13658 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
13659 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13660 self.buffer
13661 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
13662 }
13663
13664 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
13665 self.buffer.update(cx, |buffer, cx| {
13666 let ranges = vec![Anchor::min()..Anchor::max()];
13667 if !buffer.all_diff_hunks_expanded()
13668 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
13669 {
13670 buffer.collapse_diff_hunks(ranges, cx);
13671 true
13672 } else {
13673 false
13674 }
13675 })
13676 }
13677
13678 fn toggle_diff_hunks_in_ranges(
13679 &mut self,
13680 ranges: Vec<Range<Anchor>>,
13681 cx: &mut Context<'_, Editor>,
13682 ) {
13683 self.buffer.update(cx, |buffer, cx| {
13684 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
13685 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
13686 })
13687 }
13688
13689 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
13690 self.buffer.update(cx, |buffer, cx| {
13691 let snapshot = buffer.snapshot(cx);
13692 let excerpt_id = range.end.excerpt_id;
13693 let point_range = range.to_point(&snapshot);
13694 let expand = !buffer.single_hunk_is_expanded(range, cx);
13695 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
13696 })
13697 }
13698
13699 pub(crate) fn apply_all_diff_hunks(
13700 &mut self,
13701 _: &ApplyAllDiffHunks,
13702 window: &mut Window,
13703 cx: &mut Context<Self>,
13704 ) {
13705 let buffers = self.buffer.read(cx).all_buffers();
13706 for branch_buffer in buffers {
13707 branch_buffer.update(cx, |branch_buffer, cx| {
13708 branch_buffer.merge_into_base(Vec::new(), cx);
13709 });
13710 }
13711
13712 if let Some(project) = self.project.clone() {
13713 self.save(true, project, window, cx).detach_and_log_err(cx);
13714 }
13715 }
13716
13717 pub(crate) fn apply_selected_diff_hunks(
13718 &mut self,
13719 _: &ApplyDiffHunk,
13720 window: &mut Window,
13721 cx: &mut Context<Self>,
13722 ) {
13723 let snapshot = self.snapshot(window, cx);
13724 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
13725 let mut ranges_by_buffer = HashMap::default();
13726 self.transact(window, cx, |editor, _window, cx| {
13727 for hunk in hunks {
13728 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
13729 ranges_by_buffer
13730 .entry(buffer.clone())
13731 .or_insert_with(Vec::new)
13732 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
13733 }
13734 }
13735
13736 for (buffer, ranges) in ranges_by_buffer {
13737 buffer.update(cx, |buffer, cx| {
13738 buffer.merge_into_base(ranges, cx);
13739 });
13740 }
13741 });
13742
13743 if let Some(project) = self.project.clone() {
13744 self.save(true, project, window, cx).detach_and_log_err(cx);
13745 }
13746 }
13747
13748 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
13749 if hovered != self.gutter_hovered {
13750 self.gutter_hovered = hovered;
13751 cx.notify();
13752 }
13753 }
13754
13755 pub fn insert_blocks(
13756 &mut self,
13757 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
13758 autoscroll: Option<Autoscroll>,
13759 cx: &mut Context<Self>,
13760 ) -> Vec<CustomBlockId> {
13761 let blocks = self
13762 .display_map
13763 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
13764 if let Some(autoscroll) = autoscroll {
13765 self.request_autoscroll(autoscroll, cx);
13766 }
13767 cx.notify();
13768 blocks
13769 }
13770
13771 pub fn resize_blocks(
13772 &mut self,
13773 heights: HashMap<CustomBlockId, u32>,
13774 autoscroll: Option<Autoscroll>,
13775 cx: &mut Context<Self>,
13776 ) {
13777 self.display_map
13778 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
13779 if let Some(autoscroll) = autoscroll {
13780 self.request_autoscroll(autoscroll, cx);
13781 }
13782 cx.notify();
13783 }
13784
13785 pub fn replace_blocks(
13786 &mut self,
13787 renderers: HashMap<CustomBlockId, RenderBlock>,
13788 autoscroll: Option<Autoscroll>,
13789 cx: &mut Context<Self>,
13790 ) {
13791 self.display_map
13792 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
13793 if let Some(autoscroll) = autoscroll {
13794 self.request_autoscroll(autoscroll, cx);
13795 }
13796 cx.notify();
13797 }
13798
13799 pub fn remove_blocks(
13800 &mut self,
13801 block_ids: HashSet<CustomBlockId>,
13802 autoscroll: Option<Autoscroll>,
13803 cx: &mut Context<Self>,
13804 ) {
13805 self.display_map.update(cx, |display_map, cx| {
13806 display_map.remove_blocks(block_ids, cx)
13807 });
13808 if let Some(autoscroll) = autoscroll {
13809 self.request_autoscroll(autoscroll, cx);
13810 }
13811 cx.notify();
13812 }
13813
13814 pub fn row_for_block(
13815 &self,
13816 block_id: CustomBlockId,
13817 cx: &mut Context<Self>,
13818 ) -> Option<DisplayRow> {
13819 self.display_map
13820 .update(cx, |map, cx| map.row_for_block(block_id, cx))
13821 }
13822
13823 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
13824 self.focused_block = Some(focused_block);
13825 }
13826
13827 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
13828 self.focused_block.take()
13829 }
13830
13831 pub fn insert_creases(
13832 &mut self,
13833 creases: impl IntoIterator<Item = Crease<Anchor>>,
13834 cx: &mut Context<Self>,
13835 ) -> Vec<CreaseId> {
13836 self.display_map
13837 .update(cx, |map, cx| map.insert_creases(creases, cx))
13838 }
13839
13840 pub fn remove_creases(
13841 &mut self,
13842 ids: impl IntoIterator<Item = CreaseId>,
13843 cx: &mut Context<Self>,
13844 ) {
13845 self.display_map
13846 .update(cx, |map, cx| map.remove_creases(ids, cx));
13847 }
13848
13849 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
13850 self.display_map
13851 .update(cx, |map, cx| map.snapshot(cx))
13852 .longest_row()
13853 }
13854
13855 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
13856 self.display_map
13857 .update(cx, |map, cx| map.snapshot(cx))
13858 .max_point()
13859 }
13860
13861 pub fn text(&self, cx: &App) -> String {
13862 self.buffer.read(cx).read(cx).text()
13863 }
13864
13865 pub fn is_empty(&self, cx: &App) -> bool {
13866 self.buffer.read(cx).read(cx).is_empty()
13867 }
13868
13869 pub fn text_option(&self, cx: &App) -> Option<String> {
13870 let text = self.text(cx);
13871 let text = text.trim();
13872
13873 if text.is_empty() {
13874 return None;
13875 }
13876
13877 Some(text.to_string())
13878 }
13879
13880 pub fn set_text(
13881 &mut self,
13882 text: impl Into<Arc<str>>,
13883 window: &mut Window,
13884 cx: &mut Context<Self>,
13885 ) {
13886 self.transact(window, cx, |this, _, cx| {
13887 this.buffer
13888 .read(cx)
13889 .as_singleton()
13890 .expect("you can only call set_text on editors for singleton buffers")
13891 .update(cx, |buffer, cx| buffer.set_text(text, cx));
13892 });
13893 }
13894
13895 pub fn display_text(&self, cx: &mut App) -> String {
13896 self.display_map
13897 .update(cx, |map, cx| map.snapshot(cx))
13898 .text()
13899 }
13900
13901 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
13902 let mut wrap_guides = smallvec::smallvec![];
13903
13904 if self.show_wrap_guides == Some(false) {
13905 return wrap_guides;
13906 }
13907
13908 let settings = self.buffer.read(cx).settings_at(0, cx);
13909 if settings.show_wrap_guides {
13910 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
13911 wrap_guides.push((soft_wrap as usize, true));
13912 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
13913 wrap_guides.push((soft_wrap as usize, true));
13914 }
13915 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
13916 }
13917
13918 wrap_guides
13919 }
13920
13921 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
13922 let settings = self.buffer.read(cx).settings_at(0, cx);
13923 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
13924 match mode {
13925 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
13926 SoftWrap::None
13927 }
13928 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
13929 language_settings::SoftWrap::PreferredLineLength => {
13930 SoftWrap::Column(settings.preferred_line_length)
13931 }
13932 language_settings::SoftWrap::Bounded => {
13933 SoftWrap::Bounded(settings.preferred_line_length)
13934 }
13935 }
13936 }
13937
13938 pub fn set_soft_wrap_mode(
13939 &mut self,
13940 mode: language_settings::SoftWrap,
13941
13942 cx: &mut Context<Self>,
13943 ) {
13944 self.soft_wrap_mode_override = Some(mode);
13945 cx.notify();
13946 }
13947
13948 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
13949 self.text_style_refinement = Some(style);
13950 }
13951
13952 /// called by the Element so we know what style we were most recently rendered with.
13953 pub(crate) fn set_style(
13954 &mut self,
13955 style: EditorStyle,
13956 window: &mut Window,
13957 cx: &mut Context<Self>,
13958 ) {
13959 let rem_size = window.rem_size();
13960 self.display_map.update(cx, |map, cx| {
13961 map.set_font(
13962 style.text.font(),
13963 style.text.font_size.to_pixels(rem_size),
13964 cx,
13965 )
13966 });
13967 self.style = Some(style);
13968 }
13969
13970 pub fn style(&self) -> Option<&EditorStyle> {
13971 self.style.as_ref()
13972 }
13973
13974 // Called by the element. This method is not designed to be called outside of the editor
13975 // element's layout code because it does not notify when rewrapping is computed synchronously.
13976 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
13977 self.display_map
13978 .update(cx, |map, cx| map.set_wrap_width(width, cx))
13979 }
13980
13981 pub fn set_soft_wrap(&mut self) {
13982 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
13983 }
13984
13985 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
13986 if self.soft_wrap_mode_override.is_some() {
13987 self.soft_wrap_mode_override.take();
13988 } else {
13989 let soft_wrap = match self.soft_wrap_mode(cx) {
13990 SoftWrap::GitDiff => return,
13991 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
13992 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
13993 language_settings::SoftWrap::None
13994 }
13995 };
13996 self.soft_wrap_mode_override = Some(soft_wrap);
13997 }
13998 cx.notify();
13999 }
14000
14001 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14002 let Some(workspace) = self.workspace() else {
14003 return;
14004 };
14005 let fs = workspace.read(cx).app_state().fs.clone();
14006 let current_show = TabBarSettings::get_global(cx).show;
14007 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14008 setting.show = Some(!current_show);
14009 });
14010 }
14011
14012 pub fn toggle_indent_guides(
14013 &mut self,
14014 _: &ToggleIndentGuides,
14015 _: &mut Window,
14016 cx: &mut Context<Self>,
14017 ) {
14018 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14019 self.buffer
14020 .read(cx)
14021 .settings_at(0, cx)
14022 .indent_guides
14023 .enabled
14024 });
14025 self.show_indent_guides = Some(!currently_enabled);
14026 cx.notify();
14027 }
14028
14029 fn should_show_indent_guides(&self) -> Option<bool> {
14030 self.show_indent_guides
14031 }
14032
14033 pub fn toggle_line_numbers(
14034 &mut self,
14035 _: &ToggleLineNumbers,
14036 _: &mut Window,
14037 cx: &mut Context<Self>,
14038 ) {
14039 let mut editor_settings = EditorSettings::get_global(cx).clone();
14040 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14041 EditorSettings::override_global(editor_settings, cx);
14042 }
14043
14044 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14045 self.use_relative_line_numbers
14046 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14047 }
14048
14049 pub fn toggle_relative_line_numbers(
14050 &mut self,
14051 _: &ToggleRelativeLineNumbers,
14052 _: &mut Window,
14053 cx: &mut Context<Self>,
14054 ) {
14055 let is_relative = self.should_use_relative_line_numbers(cx);
14056 self.set_relative_line_number(Some(!is_relative), cx)
14057 }
14058
14059 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14060 self.use_relative_line_numbers = is_relative;
14061 cx.notify();
14062 }
14063
14064 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14065 self.show_gutter = show_gutter;
14066 cx.notify();
14067 }
14068
14069 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14070 self.show_scrollbars = show_scrollbars;
14071 cx.notify();
14072 }
14073
14074 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14075 self.show_line_numbers = Some(show_line_numbers);
14076 cx.notify();
14077 }
14078
14079 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14080 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14081 cx.notify();
14082 }
14083
14084 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14085 self.show_code_actions = Some(show_code_actions);
14086 cx.notify();
14087 }
14088
14089 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14090 self.show_runnables = Some(show_runnables);
14091 cx.notify();
14092 }
14093
14094 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14095 if self.display_map.read(cx).masked != masked {
14096 self.display_map.update(cx, |map, _| map.masked = masked);
14097 }
14098 cx.notify()
14099 }
14100
14101 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14102 self.show_wrap_guides = Some(show_wrap_guides);
14103 cx.notify();
14104 }
14105
14106 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14107 self.show_indent_guides = Some(show_indent_guides);
14108 cx.notify();
14109 }
14110
14111 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14112 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14113 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14114 if let Some(dir) = file.abs_path(cx).parent() {
14115 return Some(dir.to_owned());
14116 }
14117 }
14118
14119 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14120 return Some(project_path.path.to_path_buf());
14121 }
14122 }
14123
14124 None
14125 }
14126
14127 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14128 self.active_excerpt(cx)?
14129 .1
14130 .read(cx)
14131 .file()
14132 .and_then(|f| f.as_local())
14133 }
14134
14135 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14136 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14137 let buffer = buffer.read(cx);
14138 if let Some(project_path) = buffer.project_path(cx) {
14139 let project = self.project.as_ref()?.read(cx);
14140 project.absolute_path(&project_path, cx)
14141 } else {
14142 buffer
14143 .file()
14144 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14145 }
14146 })
14147 }
14148
14149 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14150 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14151 let project_path = buffer.read(cx).project_path(cx)?;
14152 let project = self.project.as_ref()?.read(cx);
14153 let entry = project.entry_for_path(&project_path, cx)?;
14154 let path = entry.path.to_path_buf();
14155 Some(path)
14156 })
14157 }
14158
14159 pub fn reveal_in_finder(
14160 &mut self,
14161 _: &RevealInFileManager,
14162 _window: &mut Window,
14163 cx: &mut Context<Self>,
14164 ) {
14165 if let Some(target) = self.target_file(cx) {
14166 cx.reveal_path(&target.abs_path(cx));
14167 }
14168 }
14169
14170 pub fn copy_path(
14171 &mut self,
14172 _: &zed_actions::workspace::CopyPath,
14173 _window: &mut Window,
14174 cx: &mut Context<Self>,
14175 ) {
14176 if let Some(path) = self.target_file_abs_path(cx) {
14177 if let Some(path) = path.to_str() {
14178 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14179 }
14180 }
14181 }
14182
14183 pub fn copy_relative_path(
14184 &mut self,
14185 _: &zed_actions::workspace::CopyRelativePath,
14186 _window: &mut Window,
14187 cx: &mut Context<Self>,
14188 ) {
14189 if let Some(path) = self.target_file_path(cx) {
14190 if let Some(path) = path.to_str() {
14191 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14192 }
14193 }
14194 }
14195
14196 pub fn copy_file_name_without_extension(
14197 &mut self,
14198 _: &CopyFileNameWithoutExtension,
14199 _: &mut Window,
14200 cx: &mut Context<Self>,
14201 ) {
14202 if let Some(file) = self.target_file(cx) {
14203 if let Some(file_stem) = file.path().file_stem() {
14204 if let Some(name) = file_stem.to_str() {
14205 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14206 }
14207 }
14208 }
14209 }
14210
14211 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14212 if let Some(file) = self.target_file(cx) {
14213 if let Some(file_name) = file.path().file_name() {
14214 if let Some(name) = file_name.to_str() {
14215 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14216 }
14217 }
14218 }
14219 }
14220
14221 pub fn toggle_git_blame(
14222 &mut self,
14223 _: &ToggleGitBlame,
14224 window: &mut Window,
14225 cx: &mut Context<Self>,
14226 ) {
14227 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14228
14229 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14230 self.start_git_blame(true, window, cx);
14231 }
14232
14233 cx.notify();
14234 }
14235
14236 pub fn toggle_git_blame_inline(
14237 &mut self,
14238 _: &ToggleGitBlameInline,
14239 window: &mut Window,
14240 cx: &mut Context<Self>,
14241 ) {
14242 self.toggle_git_blame_inline_internal(true, window, cx);
14243 cx.notify();
14244 }
14245
14246 pub fn git_blame_inline_enabled(&self) -> bool {
14247 self.git_blame_inline_enabled
14248 }
14249
14250 pub fn toggle_selection_menu(
14251 &mut self,
14252 _: &ToggleSelectionMenu,
14253 _: &mut Window,
14254 cx: &mut Context<Self>,
14255 ) {
14256 self.show_selection_menu = self
14257 .show_selection_menu
14258 .map(|show_selections_menu| !show_selections_menu)
14259 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14260
14261 cx.notify();
14262 }
14263
14264 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14265 self.show_selection_menu
14266 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14267 }
14268
14269 fn start_git_blame(
14270 &mut self,
14271 user_triggered: bool,
14272 window: &mut Window,
14273 cx: &mut Context<Self>,
14274 ) {
14275 if let Some(project) = self.project.as_ref() {
14276 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14277 return;
14278 };
14279
14280 if buffer.read(cx).file().is_none() {
14281 return;
14282 }
14283
14284 let focused = self.focus_handle(cx).contains_focused(window, cx);
14285
14286 let project = project.clone();
14287 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14288 self.blame_subscription =
14289 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14290 self.blame = Some(blame);
14291 }
14292 }
14293
14294 fn toggle_git_blame_inline_internal(
14295 &mut self,
14296 user_triggered: bool,
14297 window: &mut Window,
14298 cx: &mut Context<Self>,
14299 ) {
14300 if self.git_blame_inline_enabled {
14301 self.git_blame_inline_enabled = false;
14302 self.show_git_blame_inline = false;
14303 self.show_git_blame_inline_delay_task.take();
14304 } else {
14305 self.git_blame_inline_enabled = true;
14306 self.start_git_blame_inline(user_triggered, window, cx);
14307 }
14308
14309 cx.notify();
14310 }
14311
14312 fn start_git_blame_inline(
14313 &mut self,
14314 user_triggered: bool,
14315 window: &mut Window,
14316 cx: &mut Context<Self>,
14317 ) {
14318 self.start_git_blame(user_triggered, window, cx);
14319
14320 if ProjectSettings::get_global(cx)
14321 .git
14322 .inline_blame_delay()
14323 .is_some()
14324 {
14325 self.start_inline_blame_timer(window, cx);
14326 } else {
14327 self.show_git_blame_inline = true
14328 }
14329 }
14330
14331 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14332 self.blame.as_ref()
14333 }
14334
14335 pub fn show_git_blame_gutter(&self) -> bool {
14336 self.show_git_blame_gutter
14337 }
14338
14339 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14340 self.show_git_blame_gutter && self.has_blame_entries(cx)
14341 }
14342
14343 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14344 self.show_git_blame_inline
14345 && (self.focus_handle.is_focused(window)
14346 || self
14347 .git_blame_inline_tooltip
14348 .as_ref()
14349 .and_then(|t| t.upgrade())
14350 .is_some())
14351 && !self.newest_selection_head_on_empty_line(cx)
14352 && self.has_blame_entries(cx)
14353 }
14354
14355 fn has_blame_entries(&self, cx: &App) -> bool {
14356 self.blame()
14357 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14358 }
14359
14360 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14361 let cursor_anchor = self.selections.newest_anchor().head();
14362
14363 let snapshot = self.buffer.read(cx).snapshot(cx);
14364 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14365
14366 snapshot.line_len(buffer_row) == 0
14367 }
14368
14369 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14370 let buffer_and_selection = maybe!({
14371 let selection = self.selections.newest::<Point>(cx);
14372 let selection_range = selection.range();
14373
14374 let multi_buffer = self.buffer().read(cx);
14375 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14376 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14377
14378 let (buffer, range, _) = if selection.reversed {
14379 buffer_ranges.first()
14380 } else {
14381 buffer_ranges.last()
14382 }?;
14383
14384 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14385 ..text::ToPoint::to_point(&range.end, &buffer).row;
14386 Some((
14387 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14388 selection,
14389 ))
14390 });
14391
14392 let Some((buffer, selection)) = buffer_and_selection else {
14393 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14394 };
14395
14396 let Some(project) = self.project.as_ref() else {
14397 return Task::ready(Err(anyhow!("editor does not have project")));
14398 };
14399
14400 project.update(cx, |project, cx| {
14401 project.get_permalink_to_line(&buffer, selection, cx)
14402 })
14403 }
14404
14405 pub fn copy_permalink_to_line(
14406 &mut self,
14407 _: &CopyPermalinkToLine,
14408 window: &mut Window,
14409 cx: &mut Context<Self>,
14410 ) {
14411 let permalink_task = self.get_permalink_to_line(cx);
14412 let workspace = self.workspace();
14413
14414 cx.spawn_in(window, |_, mut cx| async move {
14415 match permalink_task.await {
14416 Ok(permalink) => {
14417 cx.update(|_, cx| {
14418 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14419 })
14420 .ok();
14421 }
14422 Err(err) => {
14423 let message = format!("Failed to copy permalink: {err}");
14424
14425 Err::<(), anyhow::Error>(err).log_err();
14426
14427 if let Some(workspace) = workspace {
14428 workspace
14429 .update_in(&mut cx, |workspace, _, cx| {
14430 struct CopyPermalinkToLine;
14431
14432 workspace.show_toast(
14433 Toast::new(
14434 NotificationId::unique::<CopyPermalinkToLine>(),
14435 message,
14436 ),
14437 cx,
14438 )
14439 })
14440 .ok();
14441 }
14442 }
14443 }
14444 })
14445 .detach();
14446 }
14447
14448 pub fn copy_file_location(
14449 &mut self,
14450 _: &CopyFileLocation,
14451 _: &mut Window,
14452 cx: &mut Context<Self>,
14453 ) {
14454 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14455 if let Some(file) = self.target_file(cx) {
14456 if let Some(path) = file.path().to_str() {
14457 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14458 }
14459 }
14460 }
14461
14462 pub fn open_permalink_to_line(
14463 &mut self,
14464 _: &OpenPermalinkToLine,
14465 window: &mut Window,
14466 cx: &mut Context<Self>,
14467 ) {
14468 let permalink_task = self.get_permalink_to_line(cx);
14469 let workspace = self.workspace();
14470
14471 cx.spawn_in(window, |_, mut cx| async move {
14472 match permalink_task.await {
14473 Ok(permalink) => {
14474 cx.update(|_, cx| {
14475 cx.open_url(permalink.as_ref());
14476 })
14477 .ok();
14478 }
14479 Err(err) => {
14480 let message = format!("Failed to open permalink: {err}");
14481
14482 Err::<(), anyhow::Error>(err).log_err();
14483
14484 if let Some(workspace) = workspace {
14485 workspace
14486 .update(&mut cx, |workspace, cx| {
14487 struct OpenPermalinkToLine;
14488
14489 workspace.show_toast(
14490 Toast::new(
14491 NotificationId::unique::<OpenPermalinkToLine>(),
14492 message,
14493 ),
14494 cx,
14495 )
14496 })
14497 .ok();
14498 }
14499 }
14500 }
14501 })
14502 .detach();
14503 }
14504
14505 pub fn insert_uuid_v4(
14506 &mut self,
14507 _: &InsertUuidV4,
14508 window: &mut Window,
14509 cx: &mut Context<Self>,
14510 ) {
14511 self.insert_uuid(UuidVersion::V4, window, cx);
14512 }
14513
14514 pub fn insert_uuid_v7(
14515 &mut self,
14516 _: &InsertUuidV7,
14517 window: &mut Window,
14518 cx: &mut Context<Self>,
14519 ) {
14520 self.insert_uuid(UuidVersion::V7, window, cx);
14521 }
14522
14523 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14524 self.transact(window, cx, |this, window, cx| {
14525 let edits = this
14526 .selections
14527 .all::<Point>(cx)
14528 .into_iter()
14529 .map(|selection| {
14530 let uuid = match version {
14531 UuidVersion::V4 => uuid::Uuid::new_v4(),
14532 UuidVersion::V7 => uuid::Uuid::now_v7(),
14533 };
14534
14535 (selection.range(), uuid.to_string())
14536 });
14537 this.edit(edits, cx);
14538 this.refresh_inline_completion(true, false, window, cx);
14539 });
14540 }
14541
14542 pub fn open_selections_in_multibuffer(
14543 &mut self,
14544 _: &OpenSelectionsInMultibuffer,
14545 window: &mut Window,
14546 cx: &mut Context<Self>,
14547 ) {
14548 let multibuffer = self.buffer.read(cx);
14549
14550 let Some(buffer) = multibuffer.as_singleton() else {
14551 return;
14552 };
14553
14554 let Some(workspace) = self.workspace() else {
14555 return;
14556 };
14557
14558 let locations = self
14559 .selections
14560 .disjoint_anchors()
14561 .iter()
14562 .map(|range| Location {
14563 buffer: buffer.clone(),
14564 range: range.start.text_anchor..range.end.text_anchor,
14565 })
14566 .collect::<Vec<_>>();
14567
14568 let title = multibuffer.title(cx).to_string();
14569
14570 cx.spawn_in(window, |_, mut cx| async move {
14571 workspace.update_in(&mut cx, |workspace, window, cx| {
14572 Self::open_locations_in_multibuffer(
14573 workspace,
14574 locations,
14575 format!("Selections for '{title}'"),
14576 false,
14577 MultibufferSelectionMode::All,
14578 window,
14579 cx,
14580 );
14581 })
14582 })
14583 .detach();
14584 }
14585
14586 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14587 /// last highlight added will be used.
14588 ///
14589 /// If the range ends at the beginning of a line, then that line will not be highlighted.
14590 pub fn highlight_rows<T: 'static>(
14591 &mut self,
14592 range: Range<Anchor>,
14593 color: Hsla,
14594 should_autoscroll: bool,
14595 cx: &mut Context<Self>,
14596 ) {
14597 let snapshot = self.buffer().read(cx).snapshot(cx);
14598 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14599 let ix = row_highlights.binary_search_by(|highlight| {
14600 Ordering::Equal
14601 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
14602 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
14603 });
14604
14605 if let Err(mut ix) = ix {
14606 let index = post_inc(&mut self.highlight_order);
14607
14608 // If this range intersects with the preceding highlight, then merge it with
14609 // the preceding highlight. Otherwise insert a new highlight.
14610 let mut merged = false;
14611 if ix > 0 {
14612 let prev_highlight = &mut row_highlights[ix - 1];
14613 if prev_highlight
14614 .range
14615 .end
14616 .cmp(&range.start, &snapshot)
14617 .is_ge()
14618 {
14619 ix -= 1;
14620 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
14621 prev_highlight.range.end = range.end;
14622 }
14623 merged = true;
14624 prev_highlight.index = index;
14625 prev_highlight.color = color;
14626 prev_highlight.should_autoscroll = should_autoscroll;
14627 }
14628 }
14629
14630 if !merged {
14631 row_highlights.insert(
14632 ix,
14633 RowHighlight {
14634 range: range.clone(),
14635 index,
14636 color,
14637 should_autoscroll,
14638 },
14639 );
14640 }
14641
14642 // If any of the following highlights intersect with this one, merge them.
14643 while let Some(next_highlight) = row_highlights.get(ix + 1) {
14644 let highlight = &row_highlights[ix];
14645 if next_highlight
14646 .range
14647 .start
14648 .cmp(&highlight.range.end, &snapshot)
14649 .is_le()
14650 {
14651 if next_highlight
14652 .range
14653 .end
14654 .cmp(&highlight.range.end, &snapshot)
14655 .is_gt()
14656 {
14657 row_highlights[ix].range.end = next_highlight.range.end;
14658 }
14659 row_highlights.remove(ix + 1);
14660 } else {
14661 break;
14662 }
14663 }
14664 }
14665 }
14666
14667 /// Remove any highlighted row ranges of the given type that intersect the
14668 /// given ranges.
14669 pub fn remove_highlighted_rows<T: 'static>(
14670 &mut self,
14671 ranges_to_remove: Vec<Range<Anchor>>,
14672 cx: &mut Context<Self>,
14673 ) {
14674 let snapshot = self.buffer().read(cx).snapshot(cx);
14675 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14676 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
14677 row_highlights.retain(|highlight| {
14678 while let Some(range_to_remove) = ranges_to_remove.peek() {
14679 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
14680 Ordering::Less | Ordering::Equal => {
14681 ranges_to_remove.next();
14682 }
14683 Ordering::Greater => {
14684 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
14685 Ordering::Less | Ordering::Equal => {
14686 return false;
14687 }
14688 Ordering::Greater => break,
14689 }
14690 }
14691 }
14692 }
14693
14694 true
14695 })
14696 }
14697
14698 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
14699 pub fn clear_row_highlights<T: 'static>(&mut self) {
14700 self.highlighted_rows.remove(&TypeId::of::<T>());
14701 }
14702
14703 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
14704 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
14705 self.highlighted_rows
14706 .get(&TypeId::of::<T>())
14707 .map_or(&[] as &[_], |vec| vec.as_slice())
14708 .iter()
14709 .map(|highlight| (highlight.range.clone(), highlight.color))
14710 }
14711
14712 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
14713 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
14714 /// Allows to ignore certain kinds of highlights.
14715 pub fn highlighted_display_rows(
14716 &self,
14717 window: &mut Window,
14718 cx: &mut App,
14719 ) -> BTreeMap<DisplayRow, Background> {
14720 let snapshot = self.snapshot(window, cx);
14721 let mut used_highlight_orders = HashMap::default();
14722 self.highlighted_rows
14723 .iter()
14724 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
14725 .fold(
14726 BTreeMap::<DisplayRow, Background>::new(),
14727 |mut unique_rows, highlight| {
14728 let start = highlight.range.start.to_display_point(&snapshot);
14729 let end = highlight.range.end.to_display_point(&snapshot);
14730 let start_row = start.row().0;
14731 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
14732 && end.column() == 0
14733 {
14734 end.row().0.saturating_sub(1)
14735 } else {
14736 end.row().0
14737 };
14738 for row in start_row..=end_row {
14739 let used_index =
14740 used_highlight_orders.entry(row).or_insert(highlight.index);
14741 if highlight.index >= *used_index {
14742 *used_index = highlight.index;
14743 unique_rows.insert(DisplayRow(row), highlight.color.into());
14744 }
14745 }
14746 unique_rows
14747 },
14748 )
14749 }
14750
14751 pub fn highlighted_display_row_for_autoscroll(
14752 &self,
14753 snapshot: &DisplaySnapshot,
14754 ) -> Option<DisplayRow> {
14755 self.highlighted_rows
14756 .values()
14757 .flat_map(|highlighted_rows| highlighted_rows.iter())
14758 .filter_map(|highlight| {
14759 if highlight.should_autoscroll {
14760 Some(highlight.range.start.to_display_point(snapshot).row())
14761 } else {
14762 None
14763 }
14764 })
14765 .min()
14766 }
14767
14768 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
14769 self.highlight_background::<SearchWithinRange>(
14770 ranges,
14771 |colors| colors.editor_document_highlight_read_background,
14772 cx,
14773 )
14774 }
14775
14776 pub fn set_breadcrumb_header(&mut self, new_header: String) {
14777 self.breadcrumb_header = Some(new_header);
14778 }
14779
14780 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
14781 self.clear_background_highlights::<SearchWithinRange>(cx);
14782 }
14783
14784 pub fn highlight_background<T: 'static>(
14785 &mut self,
14786 ranges: &[Range<Anchor>],
14787 color_fetcher: fn(&ThemeColors) -> Hsla,
14788 cx: &mut Context<Self>,
14789 ) {
14790 self.background_highlights
14791 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14792 self.scrollbar_marker_state.dirty = true;
14793 cx.notify();
14794 }
14795
14796 pub fn clear_background_highlights<T: 'static>(
14797 &mut self,
14798 cx: &mut Context<Self>,
14799 ) -> Option<BackgroundHighlight> {
14800 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
14801 if !text_highlights.1.is_empty() {
14802 self.scrollbar_marker_state.dirty = true;
14803 cx.notify();
14804 }
14805 Some(text_highlights)
14806 }
14807
14808 pub fn highlight_gutter<T: 'static>(
14809 &mut self,
14810 ranges: &[Range<Anchor>],
14811 color_fetcher: fn(&App) -> Hsla,
14812 cx: &mut Context<Self>,
14813 ) {
14814 self.gutter_highlights
14815 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
14816 cx.notify();
14817 }
14818
14819 pub fn clear_gutter_highlights<T: 'static>(
14820 &mut self,
14821 cx: &mut Context<Self>,
14822 ) -> Option<GutterHighlight> {
14823 cx.notify();
14824 self.gutter_highlights.remove(&TypeId::of::<T>())
14825 }
14826
14827 #[cfg(feature = "test-support")]
14828 pub fn all_text_background_highlights(
14829 &self,
14830 window: &mut Window,
14831 cx: &mut Context<Self>,
14832 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14833 let snapshot = self.snapshot(window, cx);
14834 let buffer = &snapshot.buffer_snapshot;
14835 let start = buffer.anchor_before(0);
14836 let end = buffer.anchor_after(buffer.len());
14837 let theme = cx.theme().colors();
14838 self.background_highlights_in_range(start..end, &snapshot, theme)
14839 }
14840
14841 #[cfg(feature = "test-support")]
14842 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
14843 let snapshot = self.buffer().read(cx).snapshot(cx);
14844
14845 let highlights = self
14846 .background_highlights
14847 .get(&TypeId::of::<items::BufferSearchHighlights>());
14848
14849 if let Some((_color, ranges)) = highlights {
14850 ranges
14851 .iter()
14852 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
14853 .collect_vec()
14854 } else {
14855 vec![]
14856 }
14857 }
14858
14859 fn document_highlights_for_position<'a>(
14860 &'a self,
14861 position: Anchor,
14862 buffer: &'a MultiBufferSnapshot,
14863 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
14864 let read_highlights = self
14865 .background_highlights
14866 .get(&TypeId::of::<DocumentHighlightRead>())
14867 .map(|h| &h.1);
14868 let write_highlights = self
14869 .background_highlights
14870 .get(&TypeId::of::<DocumentHighlightWrite>())
14871 .map(|h| &h.1);
14872 let left_position = position.bias_left(buffer);
14873 let right_position = position.bias_right(buffer);
14874 read_highlights
14875 .into_iter()
14876 .chain(write_highlights)
14877 .flat_map(move |ranges| {
14878 let start_ix = match ranges.binary_search_by(|probe| {
14879 let cmp = probe.end.cmp(&left_position, buffer);
14880 if cmp.is_ge() {
14881 Ordering::Greater
14882 } else {
14883 Ordering::Less
14884 }
14885 }) {
14886 Ok(i) | Err(i) => i,
14887 };
14888
14889 ranges[start_ix..]
14890 .iter()
14891 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
14892 })
14893 }
14894
14895 pub fn has_background_highlights<T: 'static>(&self) -> bool {
14896 self.background_highlights
14897 .get(&TypeId::of::<T>())
14898 .map_or(false, |(_, highlights)| !highlights.is_empty())
14899 }
14900
14901 pub fn background_highlights_in_range(
14902 &self,
14903 search_range: Range<Anchor>,
14904 display_snapshot: &DisplaySnapshot,
14905 theme: &ThemeColors,
14906 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
14907 let mut results = Vec::new();
14908 for (color_fetcher, ranges) in self.background_highlights.values() {
14909 let color = color_fetcher(theme);
14910 let start_ix = match ranges.binary_search_by(|probe| {
14911 let cmp = probe
14912 .end
14913 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14914 if cmp.is_gt() {
14915 Ordering::Greater
14916 } else {
14917 Ordering::Less
14918 }
14919 }) {
14920 Ok(i) | Err(i) => i,
14921 };
14922 for range in &ranges[start_ix..] {
14923 if range
14924 .start
14925 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14926 .is_ge()
14927 {
14928 break;
14929 }
14930
14931 let start = range.start.to_display_point(display_snapshot);
14932 let end = range.end.to_display_point(display_snapshot);
14933 results.push((start..end, color))
14934 }
14935 }
14936 results
14937 }
14938
14939 pub fn background_highlight_row_ranges<T: 'static>(
14940 &self,
14941 search_range: Range<Anchor>,
14942 display_snapshot: &DisplaySnapshot,
14943 count: usize,
14944 ) -> Vec<RangeInclusive<DisplayPoint>> {
14945 let mut results = Vec::new();
14946 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
14947 return vec![];
14948 };
14949
14950 let start_ix = match ranges.binary_search_by(|probe| {
14951 let cmp = probe
14952 .end
14953 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
14954 if cmp.is_gt() {
14955 Ordering::Greater
14956 } else {
14957 Ordering::Less
14958 }
14959 }) {
14960 Ok(i) | Err(i) => i,
14961 };
14962 let mut push_region = |start: Option<Point>, end: Option<Point>| {
14963 if let (Some(start_display), Some(end_display)) = (start, end) {
14964 results.push(
14965 start_display.to_display_point(display_snapshot)
14966 ..=end_display.to_display_point(display_snapshot),
14967 );
14968 }
14969 };
14970 let mut start_row: Option<Point> = None;
14971 let mut end_row: Option<Point> = None;
14972 if ranges.len() > count {
14973 return Vec::new();
14974 }
14975 for range in &ranges[start_ix..] {
14976 if range
14977 .start
14978 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
14979 .is_ge()
14980 {
14981 break;
14982 }
14983 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
14984 if let Some(current_row) = &end_row {
14985 if end.row == current_row.row {
14986 continue;
14987 }
14988 }
14989 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
14990 if start_row.is_none() {
14991 assert_eq!(end_row, None);
14992 start_row = Some(start);
14993 end_row = Some(end);
14994 continue;
14995 }
14996 if let Some(current_end) = end_row.as_mut() {
14997 if start.row > current_end.row + 1 {
14998 push_region(start_row, end_row);
14999 start_row = Some(start);
15000 end_row = Some(end);
15001 } else {
15002 // Merge two hunks.
15003 *current_end = end;
15004 }
15005 } else {
15006 unreachable!();
15007 }
15008 }
15009 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15010 push_region(start_row, end_row);
15011 results
15012 }
15013
15014 pub fn gutter_highlights_in_range(
15015 &self,
15016 search_range: Range<Anchor>,
15017 display_snapshot: &DisplaySnapshot,
15018 cx: &App,
15019 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15020 let mut results = Vec::new();
15021 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15022 let color = color_fetcher(cx);
15023 let start_ix = match ranges.binary_search_by(|probe| {
15024 let cmp = probe
15025 .end
15026 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15027 if cmp.is_gt() {
15028 Ordering::Greater
15029 } else {
15030 Ordering::Less
15031 }
15032 }) {
15033 Ok(i) | Err(i) => i,
15034 };
15035 for range in &ranges[start_ix..] {
15036 if range
15037 .start
15038 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15039 .is_ge()
15040 {
15041 break;
15042 }
15043
15044 let start = range.start.to_display_point(display_snapshot);
15045 let end = range.end.to_display_point(display_snapshot);
15046 results.push((start..end, color))
15047 }
15048 }
15049 results
15050 }
15051
15052 /// Get the text ranges corresponding to the redaction query
15053 pub fn redacted_ranges(
15054 &self,
15055 search_range: Range<Anchor>,
15056 display_snapshot: &DisplaySnapshot,
15057 cx: &App,
15058 ) -> Vec<Range<DisplayPoint>> {
15059 display_snapshot
15060 .buffer_snapshot
15061 .redacted_ranges(search_range, |file| {
15062 if let Some(file) = file {
15063 file.is_private()
15064 && EditorSettings::get(
15065 Some(SettingsLocation {
15066 worktree_id: file.worktree_id(cx),
15067 path: file.path().as_ref(),
15068 }),
15069 cx,
15070 )
15071 .redact_private_values
15072 } else {
15073 false
15074 }
15075 })
15076 .map(|range| {
15077 range.start.to_display_point(display_snapshot)
15078 ..range.end.to_display_point(display_snapshot)
15079 })
15080 .collect()
15081 }
15082
15083 pub fn highlight_text<T: 'static>(
15084 &mut self,
15085 ranges: Vec<Range<Anchor>>,
15086 style: HighlightStyle,
15087 cx: &mut Context<Self>,
15088 ) {
15089 self.display_map.update(cx, |map, _| {
15090 map.highlight_text(TypeId::of::<T>(), ranges, style)
15091 });
15092 cx.notify();
15093 }
15094
15095 pub(crate) fn highlight_inlays<T: 'static>(
15096 &mut self,
15097 highlights: Vec<InlayHighlight>,
15098 style: HighlightStyle,
15099 cx: &mut Context<Self>,
15100 ) {
15101 self.display_map.update(cx, |map, _| {
15102 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15103 });
15104 cx.notify();
15105 }
15106
15107 pub fn text_highlights<'a, T: 'static>(
15108 &'a self,
15109 cx: &'a App,
15110 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15111 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15112 }
15113
15114 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15115 let cleared = self
15116 .display_map
15117 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15118 if cleared {
15119 cx.notify();
15120 }
15121 }
15122
15123 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15124 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15125 && self.focus_handle.is_focused(window)
15126 }
15127
15128 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15129 self.show_cursor_when_unfocused = is_enabled;
15130 cx.notify();
15131 }
15132
15133 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15134 cx.notify();
15135 }
15136
15137 fn on_buffer_event(
15138 &mut self,
15139 multibuffer: &Entity<MultiBuffer>,
15140 event: &multi_buffer::Event,
15141 window: &mut Window,
15142 cx: &mut Context<Self>,
15143 ) {
15144 match event {
15145 multi_buffer::Event::Edited {
15146 singleton_buffer_edited,
15147 edited_buffer: buffer_edited,
15148 } => {
15149 self.scrollbar_marker_state.dirty = true;
15150 self.active_indent_guides_state.dirty = true;
15151 self.refresh_active_diagnostics(cx);
15152 self.refresh_code_actions(window, cx);
15153 if self.has_active_inline_completion() {
15154 self.update_visible_inline_completion(window, cx);
15155 }
15156 if let Some(buffer) = buffer_edited {
15157 let buffer_id = buffer.read(cx).remote_id();
15158 if !self.registered_buffers.contains_key(&buffer_id) {
15159 if let Some(project) = self.project.as_ref() {
15160 project.update(cx, |project, cx| {
15161 self.registered_buffers.insert(
15162 buffer_id,
15163 project.register_buffer_with_language_servers(&buffer, cx),
15164 );
15165 })
15166 }
15167 }
15168 }
15169 cx.emit(EditorEvent::BufferEdited);
15170 cx.emit(SearchEvent::MatchesInvalidated);
15171 if *singleton_buffer_edited {
15172 if let Some(project) = &self.project {
15173 #[allow(clippy::mutable_key_type)]
15174 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15175 multibuffer
15176 .all_buffers()
15177 .into_iter()
15178 .filter_map(|buffer| {
15179 buffer.update(cx, |buffer, cx| {
15180 let language = buffer.language()?;
15181 let should_discard = project.update(cx, |project, cx| {
15182 project.is_local()
15183 && !project.has_language_servers_for(buffer, cx)
15184 });
15185 should_discard.not().then_some(language.clone())
15186 })
15187 })
15188 .collect::<HashSet<_>>()
15189 });
15190 if !languages_affected.is_empty() {
15191 self.refresh_inlay_hints(
15192 InlayHintRefreshReason::BufferEdited(languages_affected),
15193 cx,
15194 );
15195 }
15196 }
15197 }
15198
15199 let Some(project) = &self.project else { return };
15200 let (telemetry, is_via_ssh) = {
15201 let project = project.read(cx);
15202 let telemetry = project.client().telemetry().clone();
15203 let is_via_ssh = project.is_via_ssh();
15204 (telemetry, is_via_ssh)
15205 };
15206 refresh_linked_ranges(self, window, cx);
15207 telemetry.log_edit_event("editor", is_via_ssh);
15208 }
15209 multi_buffer::Event::ExcerptsAdded {
15210 buffer,
15211 predecessor,
15212 excerpts,
15213 } => {
15214 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15215 let buffer_id = buffer.read(cx).remote_id();
15216 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15217 if let Some(project) = &self.project {
15218 get_uncommitted_diff_for_buffer(
15219 project,
15220 [buffer.clone()],
15221 self.buffer.clone(),
15222 cx,
15223 )
15224 .detach();
15225 }
15226 }
15227 cx.emit(EditorEvent::ExcerptsAdded {
15228 buffer: buffer.clone(),
15229 predecessor: *predecessor,
15230 excerpts: excerpts.clone(),
15231 });
15232 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15233 }
15234 multi_buffer::Event::ExcerptsRemoved { ids } => {
15235 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15236 let buffer = self.buffer.read(cx);
15237 self.registered_buffers
15238 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15239 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15240 }
15241 multi_buffer::Event::ExcerptsEdited {
15242 excerpt_ids,
15243 buffer_ids,
15244 } => {
15245 self.display_map.update(cx, |map, cx| {
15246 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15247 });
15248 cx.emit(EditorEvent::ExcerptsEdited {
15249 ids: excerpt_ids.clone(),
15250 })
15251 }
15252 multi_buffer::Event::ExcerptsExpanded { ids } => {
15253 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15254 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15255 }
15256 multi_buffer::Event::Reparsed(buffer_id) => {
15257 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15258
15259 cx.emit(EditorEvent::Reparsed(*buffer_id));
15260 }
15261 multi_buffer::Event::DiffHunksToggled => {
15262 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15263 }
15264 multi_buffer::Event::LanguageChanged(buffer_id) => {
15265 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15266 cx.emit(EditorEvent::Reparsed(*buffer_id));
15267 cx.notify();
15268 }
15269 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15270 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15271 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
15272 cx.emit(EditorEvent::TitleChanged)
15273 }
15274 // multi_buffer::Event::DiffBaseChanged => {
15275 // self.scrollbar_marker_state.dirty = true;
15276 // cx.emit(EditorEvent::DiffBaseChanged);
15277 // cx.notify();
15278 // }
15279 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15280 multi_buffer::Event::DiagnosticsUpdated => {
15281 self.refresh_active_diagnostics(cx);
15282 self.refresh_inline_diagnostics(true, window, cx);
15283 self.scrollbar_marker_state.dirty = true;
15284 cx.notify();
15285 }
15286 _ => {}
15287 };
15288 }
15289
15290 fn on_display_map_changed(
15291 &mut self,
15292 _: Entity<DisplayMap>,
15293 _: &mut Window,
15294 cx: &mut Context<Self>,
15295 ) {
15296 cx.notify();
15297 }
15298
15299 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15300 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15301 self.update_edit_prediction_settings(cx);
15302 self.refresh_inline_completion(true, false, window, cx);
15303 self.refresh_inlay_hints(
15304 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15305 self.selections.newest_anchor().head(),
15306 &self.buffer.read(cx).snapshot(cx),
15307 cx,
15308 )),
15309 cx,
15310 );
15311
15312 let old_cursor_shape = self.cursor_shape;
15313
15314 {
15315 let editor_settings = EditorSettings::get_global(cx);
15316 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15317 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15318 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15319 }
15320
15321 if old_cursor_shape != self.cursor_shape {
15322 cx.emit(EditorEvent::CursorShapeChanged);
15323 }
15324
15325 let project_settings = ProjectSettings::get_global(cx);
15326 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15327
15328 if self.mode == EditorMode::Full {
15329 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15330 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15331 if self.show_inline_diagnostics != show_inline_diagnostics {
15332 self.show_inline_diagnostics = show_inline_diagnostics;
15333 self.refresh_inline_diagnostics(false, window, cx);
15334 }
15335
15336 if self.git_blame_inline_enabled != inline_blame_enabled {
15337 self.toggle_git_blame_inline_internal(false, window, cx);
15338 }
15339 }
15340
15341 cx.notify();
15342 }
15343
15344 pub fn set_searchable(&mut self, searchable: bool) {
15345 self.searchable = searchable;
15346 }
15347
15348 pub fn searchable(&self) -> bool {
15349 self.searchable
15350 }
15351
15352 fn open_proposed_changes_editor(
15353 &mut self,
15354 _: &OpenProposedChangesEditor,
15355 window: &mut Window,
15356 cx: &mut Context<Self>,
15357 ) {
15358 let Some(workspace) = self.workspace() else {
15359 cx.propagate();
15360 return;
15361 };
15362
15363 let selections = self.selections.all::<usize>(cx);
15364 let multi_buffer = self.buffer.read(cx);
15365 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15366 let mut new_selections_by_buffer = HashMap::default();
15367 for selection in selections {
15368 for (buffer, range, _) in
15369 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15370 {
15371 let mut range = range.to_point(buffer);
15372 range.start.column = 0;
15373 range.end.column = buffer.line_len(range.end.row);
15374 new_selections_by_buffer
15375 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15376 .or_insert(Vec::new())
15377 .push(range)
15378 }
15379 }
15380
15381 let proposed_changes_buffers = new_selections_by_buffer
15382 .into_iter()
15383 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15384 .collect::<Vec<_>>();
15385 let proposed_changes_editor = cx.new(|cx| {
15386 ProposedChangesEditor::new(
15387 "Proposed changes",
15388 proposed_changes_buffers,
15389 self.project.clone(),
15390 window,
15391 cx,
15392 )
15393 });
15394
15395 window.defer(cx, move |window, cx| {
15396 workspace.update(cx, |workspace, cx| {
15397 workspace.active_pane().update(cx, |pane, cx| {
15398 pane.add_item(
15399 Box::new(proposed_changes_editor),
15400 true,
15401 true,
15402 None,
15403 window,
15404 cx,
15405 );
15406 });
15407 });
15408 });
15409 }
15410
15411 pub fn open_excerpts_in_split(
15412 &mut self,
15413 _: &OpenExcerptsSplit,
15414 window: &mut Window,
15415 cx: &mut Context<Self>,
15416 ) {
15417 self.open_excerpts_common(None, true, window, cx)
15418 }
15419
15420 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15421 self.open_excerpts_common(None, false, window, cx)
15422 }
15423
15424 fn open_excerpts_common(
15425 &mut self,
15426 jump_data: Option<JumpData>,
15427 split: bool,
15428 window: &mut Window,
15429 cx: &mut Context<Self>,
15430 ) {
15431 let Some(workspace) = self.workspace() else {
15432 cx.propagate();
15433 return;
15434 };
15435
15436 if self.buffer.read(cx).is_singleton() {
15437 cx.propagate();
15438 return;
15439 }
15440
15441 let mut new_selections_by_buffer = HashMap::default();
15442 match &jump_data {
15443 Some(JumpData::MultiBufferPoint {
15444 excerpt_id,
15445 position,
15446 anchor,
15447 line_offset_from_top,
15448 }) => {
15449 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15450 if let Some(buffer) = multi_buffer_snapshot
15451 .buffer_id_for_excerpt(*excerpt_id)
15452 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15453 {
15454 let buffer_snapshot = buffer.read(cx).snapshot();
15455 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15456 language::ToPoint::to_point(anchor, &buffer_snapshot)
15457 } else {
15458 buffer_snapshot.clip_point(*position, Bias::Left)
15459 };
15460 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15461 new_selections_by_buffer.insert(
15462 buffer,
15463 (
15464 vec![jump_to_offset..jump_to_offset],
15465 Some(*line_offset_from_top),
15466 ),
15467 );
15468 }
15469 }
15470 Some(JumpData::MultiBufferRow {
15471 row,
15472 line_offset_from_top,
15473 }) => {
15474 let point = MultiBufferPoint::new(row.0, 0);
15475 if let Some((buffer, buffer_point, _)) =
15476 self.buffer.read(cx).point_to_buffer_point(point, cx)
15477 {
15478 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15479 new_selections_by_buffer
15480 .entry(buffer)
15481 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15482 .0
15483 .push(buffer_offset..buffer_offset)
15484 }
15485 }
15486 None => {
15487 let selections = self.selections.all::<usize>(cx);
15488 let multi_buffer = self.buffer.read(cx);
15489 for selection in selections {
15490 for (snapshot, range, _, anchor) in multi_buffer
15491 .snapshot(cx)
15492 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15493 {
15494 if let Some(anchor) = anchor {
15495 // selection is in a deleted hunk
15496 let Some(buffer_id) = anchor.buffer_id else {
15497 continue;
15498 };
15499 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15500 continue;
15501 };
15502 let offset = text::ToOffset::to_offset(
15503 &anchor.text_anchor,
15504 &buffer_handle.read(cx).snapshot(),
15505 );
15506 let range = offset..offset;
15507 new_selections_by_buffer
15508 .entry(buffer_handle)
15509 .or_insert((Vec::new(), None))
15510 .0
15511 .push(range)
15512 } else {
15513 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15514 else {
15515 continue;
15516 };
15517 new_selections_by_buffer
15518 .entry(buffer_handle)
15519 .or_insert((Vec::new(), None))
15520 .0
15521 .push(range)
15522 }
15523 }
15524 }
15525 }
15526 }
15527
15528 if new_selections_by_buffer.is_empty() {
15529 return;
15530 }
15531
15532 // We defer the pane interaction because we ourselves are a workspace item
15533 // and activating a new item causes the pane to call a method on us reentrantly,
15534 // which panics if we're on the stack.
15535 window.defer(cx, move |window, cx| {
15536 workspace.update(cx, |workspace, cx| {
15537 let pane = if split {
15538 workspace.adjacent_pane(window, cx)
15539 } else {
15540 workspace.active_pane().clone()
15541 };
15542
15543 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15544 let editor = buffer
15545 .read(cx)
15546 .file()
15547 .is_none()
15548 .then(|| {
15549 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15550 // so `workspace.open_project_item` will never find them, always opening a new editor.
15551 // Instead, we try to activate the existing editor in the pane first.
15552 let (editor, pane_item_index) =
15553 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15554 let editor = item.downcast::<Editor>()?;
15555 let singleton_buffer =
15556 editor.read(cx).buffer().read(cx).as_singleton()?;
15557 if singleton_buffer == buffer {
15558 Some((editor, i))
15559 } else {
15560 None
15561 }
15562 })?;
15563 pane.update(cx, |pane, cx| {
15564 pane.activate_item(pane_item_index, true, true, window, cx)
15565 });
15566 Some(editor)
15567 })
15568 .flatten()
15569 .unwrap_or_else(|| {
15570 workspace.open_project_item::<Self>(
15571 pane.clone(),
15572 buffer,
15573 true,
15574 true,
15575 window,
15576 cx,
15577 )
15578 });
15579
15580 editor.update(cx, |editor, cx| {
15581 let autoscroll = match scroll_offset {
15582 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15583 None => Autoscroll::newest(),
15584 };
15585 let nav_history = editor.nav_history.take();
15586 editor.change_selections(Some(autoscroll), window, cx, |s| {
15587 s.select_ranges(ranges);
15588 });
15589 editor.nav_history = nav_history;
15590 });
15591 }
15592 })
15593 });
15594 }
15595
15596 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
15597 let snapshot = self.buffer.read(cx).read(cx);
15598 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
15599 Some(
15600 ranges
15601 .iter()
15602 .map(move |range| {
15603 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
15604 })
15605 .collect(),
15606 )
15607 }
15608
15609 fn selection_replacement_ranges(
15610 &self,
15611 range: Range<OffsetUtf16>,
15612 cx: &mut App,
15613 ) -> Vec<Range<OffsetUtf16>> {
15614 let selections = self.selections.all::<OffsetUtf16>(cx);
15615 let newest_selection = selections
15616 .iter()
15617 .max_by_key(|selection| selection.id)
15618 .unwrap();
15619 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
15620 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
15621 let snapshot = self.buffer.read(cx).read(cx);
15622 selections
15623 .into_iter()
15624 .map(|mut selection| {
15625 selection.start.0 =
15626 (selection.start.0 as isize).saturating_add(start_delta) as usize;
15627 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
15628 snapshot.clip_offset_utf16(selection.start, Bias::Left)
15629 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
15630 })
15631 .collect()
15632 }
15633
15634 fn report_editor_event(
15635 &self,
15636 event_type: &'static str,
15637 file_extension: Option<String>,
15638 cx: &App,
15639 ) {
15640 if cfg!(any(test, feature = "test-support")) {
15641 return;
15642 }
15643
15644 let Some(project) = &self.project else { return };
15645
15646 // If None, we are in a file without an extension
15647 let file = self
15648 .buffer
15649 .read(cx)
15650 .as_singleton()
15651 .and_then(|b| b.read(cx).file());
15652 let file_extension = file_extension.or(file
15653 .as_ref()
15654 .and_then(|file| Path::new(file.file_name(cx)).extension())
15655 .and_then(|e| e.to_str())
15656 .map(|a| a.to_string()));
15657
15658 let vim_mode = cx
15659 .global::<SettingsStore>()
15660 .raw_user_settings()
15661 .get("vim_mode")
15662 == Some(&serde_json::Value::Bool(true));
15663
15664 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
15665 let copilot_enabled = edit_predictions_provider
15666 == language::language_settings::EditPredictionProvider::Copilot;
15667 let copilot_enabled_for_language = self
15668 .buffer
15669 .read(cx)
15670 .settings_at(0, cx)
15671 .show_edit_predictions;
15672
15673 let project = project.read(cx);
15674 telemetry::event!(
15675 event_type,
15676 file_extension,
15677 vim_mode,
15678 copilot_enabled,
15679 copilot_enabled_for_language,
15680 edit_predictions_provider,
15681 is_via_ssh = project.is_via_ssh(),
15682 );
15683 }
15684
15685 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
15686 /// with each line being an array of {text, highlight} objects.
15687 fn copy_highlight_json(
15688 &mut self,
15689 _: &CopyHighlightJson,
15690 window: &mut Window,
15691 cx: &mut Context<Self>,
15692 ) {
15693 #[derive(Serialize)]
15694 struct Chunk<'a> {
15695 text: String,
15696 highlight: Option<&'a str>,
15697 }
15698
15699 let snapshot = self.buffer.read(cx).snapshot(cx);
15700 let range = self
15701 .selected_text_range(false, window, cx)
15702 .and_then(|selection| {
15703 if selection.range.is_empty() {
15704 None
15705 } else {
15706 Some(selection.range)
15707 }
15708 })
15709 .unwrap_or_else(|| 0..snapshot.len());
15710
15711 let chunks = snapshot.chunks(range, true);
15712 let mut lines = Vec::new();
15713 let mut line: VecDeque<Chunk> = VecDeque::new();
15714
15715 let Some(style) = self.style.as_ref() else {
15716 return;
15717 };
15718
15719 for chunk in chunks {
15720 let highlight = chunk
15721 .syntax_highlight_id
15722 .and_then(|id| id.name(&style.syntax));
15723 let mut chunk_lines = chunk.text.split('\n').peekable();
15724 while let Some(text) = chunk_lines.next() {
15725 let mut merged_with_last_token = false;
15726 if let Some(last_token) = line.back_mut() {
15727 if last_token.highlight == highlight {
15728 last_token.text.push_str(text);
15729 merged_with_last_token = true;
15730 }
15731 }
15732
15733 if !merged_with_last_token {
15734 line.push_back(Chunk {
15735 text: text.into(),
15736 highlight,
15737 });
15738 }
15739
15740 if chunk_lines.peek().is_some() {
15741 if line.len() > 1 && line.front().unwrap().text.is_empty() {
15742 line.pop_front();
15743 }
15744 if line.len() > 1 && line.back().unwrap().text.is_empty() {
15745 line.pop_back();
15746 }
15747
15748 lines.push(mem::take(&mut line));
15749 }
15750 }
15751 }
15752
15753 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
15754 return;
15755 };
15756 cx.write_to_clipboard(ClipboardItem::new_string(lines));
15757 }
15758
15759 pub fn open_context_menu(
15760 &mut self,
15761 _: &OpenContextMenu,
15762 window: &mut Window,
15763 cx: &mut Context<Self>,
15764 ) {
15765 self.request_autoscroll(Autoscroll::newest(), cx);
15766 let position = self.selections.newest_display(cx).start;
15767 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
15768 }
15769
15770 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
15771 &self.inlay_hint_cache
15772 }
15773
15774 pub fn replay_insert_event(
15775 &mut self,
15776 text: &str,
15777 relative_utf16_range: Option<Range<isize>>,
15778 window: &mut Window,
15779 cx: &mut Context<Self>,
15780 ) {
15781 if !self.input_enabled {
15782 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15783 return;
15784 }
15785 if let Some(relative_utf16_range) = relative_utf16_range {
15786 let selections = self.selections.all::<OffsetUtf16>(cx);
15787 self.change_selections(None, window, cx, |s| {
15788 let new_ranges = selections.into_iter().map(|range| {
15789 let start = OffsetUtf16(
15790 range
15791 .head()
15792 .0
15793 .saturating_add_signed(relative_utf16_range.start),
15794 );
15795 let end = OffsetUtf16(
15796 range
15797 .head()
15798 .0
15799 .saturating_add_signed(relative_utf16_range.end),
15800 );
15801 start..end
15802 });
15803 s.select_ranges(new_ranges);
15804 });
15805 }
15806
15807 self.handle_input(text, window, cx);
15808 }
15809
15810 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
15811 let Some(provider) = self.semantics_provider.as_ref() else {
15812 return false;
15813 };
15814
15815 let mut supports = false;
15816 self.buffer().update(cx, |this, cx| {
15817 this.for_each_buffer(|buffer| {
15818 supports |= provider.supports_inlay_hints(buffer, cx);
15819 });
15820 });
15821
15822 supports
15823 }
15824
15825 pub fn is_focused(&self, window: &Window) -> bool {
15826 self.focus_handle.is_focused(window)
15827 }
15828
15829 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15830 cx.emit(EditorEvent::Focused);
15831
15832 if let Some(descendant) = self
15833 .last_focused_descendant
15834 .take()
15835 .and_then(|descendant| descendant.upgrade())
15836 {
15837 window.focus(&descendant);
15838 } else {
15839 if let Some(blame) = self.blame.as_ref() {
15840 blame.update(cx, GitBlame::focus)
15841 }
15842
15843 self.blink_manager.update(cx, BlinkManager::enable);
15844 self.show_cursor_names(window, cx);
15845 self.buffer.update(cx, |buffer, cx| {
15846 buffer.finalize_last_transaction(cx);
15847 if self.leader_peer_id.is_none() {
15848 buffer.set_active_selections(
15849 &self.selections.disjoint_anchors(),
15850 self.selections.line_mode,
15851 self.cursor_shape,
15852 cx,
15853 );
15854 }
15855 });
15856 }
15857 }
15858
15859 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15860 cx.emit(EditorEvent::FocusedIn)
15861 }
15862
15863 fn handle_focus_out(
15864 &mut self,
15865 event: FocusOutEvent,
15866 _window: &mut Window,
15867 cx: &mut Context<Self>,
15868 ) {
15869 if event.blurred != self.focus_handle {
15870 self.last_focused_descendant = Some(event.blurred);
15871 }
15872 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
15873 }
15874
15875 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15876 self.blink_manager.update(cx, BlinkManager::disable);
15877 self.buffer
15878 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
15879
15880 if let Some(blame) = self.blame.as_ref() {
15881 blame.update(cx, GitBlame::blur)
15882 }
15883 if !self.hover_state.focused(window, cx) {
15884 hide_hover(self, cx);
15885 }
15886 if !self
15887 .context_menu
15888 .borrow()
15889 .as_ref()
15890 .is_some_and(|context_menu| context_menu.focused(window, cx))
15891 {
15892 self.hide_context_menu(window, cx);
15893 }
15894 self.discard_inline_completion(false, cx);
15895 cx.emit(EditorEvent::Blurred);
15896 cx.notify();
15897 }
15898
15899 pub fn register_action<A: Action>(
15900 &mut self,
15901 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
15902 ) -> Subscription {
15903 let id = self.next_editor_action_id.post_inc();
15904 let listener = Arc::new(listener);
15905 self.editor_actions.borrow_mut().insert(
15906 id,
15907 Box::new(move |window, _| {
15908 let listener = listener.clone();
15909 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
15910 let action = action.downcast_ref().unwrap();
15911 if phase == DispatchPhase::Bubble {
15912 listener(action, window, cx)
15913 }
15914 })
15915 }),
15916 );
15917
15918 let editor_actions = self.editor_actions.clone();
15919 Subscription::new(move || {
15920 editor_actions.borrow_mut().remove(&id);
15921 })
15922 }
15923
15924 pub fn file_header_size(&self) -> u32 {
15925 FILE_HEADER_HEIGHT
15926 }
15927
15928 pub fn restore(
15929 &mut self,
15930 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
15931 window: &mut Window,
15932 cx: &mut Context<Self>,
15933 ) {
15934 let workspace = self.workspace();
15935 let project = self.project.as_ref();
15936 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
15937 let mut tasks = Vec::new();
15938 for (buffer_id, changes) in revert_changes {
15939 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15940 buffer.update(cx, |buffer, cx| {
15941 buffer.edit(
15942 changes.into_iter().map(|(range, text)| {
15943 (range, text.to_string().map(Arc::<str>::from))
15944 }),
15945 None,
15946 cx,
15947 );
15948 });
15949
15950 if let Some(project) =
15951 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
15952 {
15953 project.update(cx, |project, cx| {
15954 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
15955 })
15956 }
15957 }
15958 }
15959 tasks
15960 });
15961 cx.spawn_in(window, |_, mut cx| async move {
15962 for (buffer, task) in save_tasks {
15963 let result = task.await;
15964 if result.is_err() {
15965 let Some(path) = buffer
15966 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
15967 .ok()
15968 else {
15969 continue;
15970 };
15971 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
15972 let Some(task) = cx
15973 .update_window_entity(&workspace, |workspace, window, cx| {
15974 workspace
15975 .open_path_preview(path, None, false, false, false, window, cx)
15976 })
15977 .ok()
15978 else {
15979 continue;
15980 };
15981 task.await.log_err();
15982 }
15983 }
15984 }
15985 })
15986 .detach();
15987 self.change_selections(None, window, cx, |selections| selections.refresh());
15988 }
15989
15990 pub fn to_pixel_point(
15991 &self,
15992 source: multi_buffer::Anchor,
15993 editor_snapshot: &EditorSnapshot,
15994 window: &mut Window,
15995 ) -> Option<gpui::Point<Pixels>> {
15996 let source_point = source.to_display_point(editor_snapshot);
15997 self.display_to_pixel_point(source_point, editor_snapshot, window)
15998 }
15999
16000 pub fn display_to_pixel_point(
16001 &self,
16002 source: DisplayPoint,
16003 editor_snapshot: &EditorSnapshot,
16004 window: &mut Window,
16005 ) -> Option<gpui::Point<Pixels>> {
16006 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16007 let text_layout_details = self.text_layout_details(window);
16008 let scroll_top = text_layout_details
16009 .scroll_anchor
16010 .scroll_position(editor_snapshot)
16011 .y;
16012
16013 if source.row().as_f32() < scroll_top.floor() {
16014 return None;
16015 }
16016 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16017 let source_y = line_height * (source.row().as_f32() - scroll_top);
16018 Some(gpui::Point::new(source_x, source_y))
16019 }
16020
16021 pub fn has_visible_completions_menu(&self) -> bool {
16022 !self.edit_prediction_preview_is_active()
16023 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16024 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16025 })
16026 }
16027
16028 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16029 self.addons
16030 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16031 }
16032
16033 pub fn unregister_addon<T: Addon>(&mut self) {
16034 self.addons.remove(&std::any::TypeId::of::<T>());
16035 }
16036
16037 pub fn addon<T: Addon>(&self) -> Option<&T> {
16038 let type_id = std::any::TypeId::of::<T>();
16039 self.addons
16040 .get(&type_id)
16041 .and_then(|item| item.to_any().downcast_ref::<T>())
16042 }
16043
16044 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16045 let text_layout_details = self.text_layout_details(window);
16046 let style = &text_layout_details.editor_style;
16047 let font_id = window.text_system().resolve_font(&style.text.font());
16048 let font_size = style.text.font_size.to_pixels(window.rem_size());
16049 let line_height = style.text.line_height_in_pixels(window.rem_size());
16050 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16051
16052 gpui::Size::new(em_width, line_height)
16053 }
16054
16055 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16056 self.load_diff_task.clone()
16057 }
16058
16059 fn read_selections_from_db(
16060 &mut self,
16061 item_id: u64,
16062 workspace_id: WorkspaceId,
16063 window: &mut Window,
16064 cx: &mut Context<Editor>,
16065 ) {
16066 if !self.is_singleton(cx)
16067 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16068 {
16069 return;
16070 }
16071 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16072 return;
16073 };
16074 if selections.is_empty() {
16075 return;
16076 }
16077
16078 let snapshot = self.buffer.read(cx).snapshot(cx);
16079 self.change_selections(None, window, cx, |s| {
16080 s.select_ranges(selections.into_iter().map(|(start, end)| {
16081 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16082 }));
16083 });
16084 }
16085}
16086
16087fn insert_extra_newline_brackets(
16088 buffer: &MultiBufferSnapshot,
16089 range: Range<usize>,
16090 language: &language::LanguageScope,
16091) -> bool {
16092 let leading_whitespace_len = buffer
16093 .reversed_chars_at(range.start)
16094 .take_while(|c| c.is_whitespace() && *c != '\n')
16095 .map(|c| c.len_utf8())
16096 .sum::<usize>();
16097 let trailing_whitespace_len = buffer
16098 .chars_at(range.end)
16099 .take_while(|c| c.is_whitespace() && *c != '\n')
16100 .map(|c| c.len_utf8())
16101 .sum::<usize>();
16102 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16103
16104 language.brackets().any(|(pair, enabled)| {
16105 let pair_start = pair.start.trim_end();
16106 let pair_end = pair.end.trim_start();
16107
16108 enabled
16109 && pair.newline
16110 && buffer.contains_str_at(range.end, pair_end)
16111 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16112 })
16113}
16114
16115fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16116 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16117 [(buffer, range, _)] => (*buffer, range.clone()),
16118 _ => return false,
16119 };
16120 let pair = {
16121 let mut result: Option<BracketMatch> = None;
16122
16123 for pair in buffer
16124 .all_bracket_ranges(range.clone())
16125 .filter(move |pair| {
16126 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16127 })
16128 {
16129 let len = pair.close_range.end - pair.open_range.start;
16130
16131 if let Some(existing) = &result {
16132 let existing_len = existing.close_range.end - existing.open_range.start;
16133 if len > existing_len {
16134 continue;
16135 }
16136 }
16137
16138 result = Some(pair);
16139 }
16140
16141 result
16142 };
16143 let Some(pair) = pair else {
16144 return false;
16145 };
16146 pair.newline_only
16147 && buffer
16148 .chars_for_range(pair.open_range.end..range.start)
16149 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16150 .all(|c| c.is_whitespace() && c != '\n')
16151}
16152
16153fn get_uncommitted_diff_for_buffer(
16154 project: &Entity<Project>,
16155 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16156 buffer: Entity<MultiBuffer>,
16157 cx: &mut App,
16158) -> Task<()> {
16159 let mut tasks = Vec::new();
16160 project.update(cx, |project, cx| {
16161 for buffer in buffers {
16162 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16163 }
16164 });
16165 cx.spawn(|mut cx| async move {
16166 let diffs = futures::future::join_all(tasks).await;
16167 buffer
16168 .update(&mut cx, |buffer, cx| {
16169 for diff in diffs.into_iter().flatten() {
16170 buffer.add_diff(diff, cx);
16171 }
16172 })
16173 .ok();
16174 })
16175}
16176
16177fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16178 let tab_size = tab_size.get() as usize;
16179 let mut width = offset;
16180
16181 for ch in text.chars() {
16182 width += if ch == '\t' {
16183 tab_size - (width % tab_size)
16184 } else {
16185 1
16186 };
16187 }
16188
16189 width - offset
16190}
16191
16192#[cfg(test)]
16193mod tests {
16194 use super::*;
16195
16196 #[test]
16197 fn test_string_size_with_expanded_tabs() {
16198 let nz = |val| NonZeroU32::new(val).unwrap();
16199 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16200 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16201 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16202 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16203 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16204 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16205 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16206 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16207 }
16208}
16209
16210/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16211struct WordBreakingTokenizer<'a> {
16212 input: &'a str,
16213}
16214
16215impl<'a> WordBreakingTokenizer<'a> {
16216 fn new(input: &'a str) -> Self {
16217 Self { input }
16218 }
16219}
16220
16221fn is_char_ideographic(ch: char) -> bool {
16222 use unicode_script::Script::*;
16223 use unicode_script::UnicodeScript;
16224 matches!(ch.script(), Han | Tangut | Yi)
16225}
16226
16227fn is_grapheme_ideographic(text: &str) -> bool {
16228 text.chars().any(is_char_ideographic)
16229}
16230
16231fn is_grapheme_whitespace(text: &str) -> bool {
16232 text.chars().any(|x| x.is_whitespace())
16233}
16234
16235fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16236 text.chars().next().map_or(false, |ch| {
16237 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16238 })
16239}
16240
16241#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16242struct WordBreakToken<'a> {
16243 token: &'a str,
16244 grapheme_len: usize,
16245 is_whitespace: bool,
16246}
16247
16248impl<'a> Iterator for WordBreakingTokenizer<'a> {
16249 /// Yields a span, the count of graphemes in the token, and whether it was
16250 /// whitespace. Note that it also breaks at word boundaries.
16251 type Item = WordBreakToken<'a>;
16252
16253 fn next(&mut self) -> Option<Self::Item> {
16254 use unicode_segmentation::UnicodeSegmentation;
16255 if self.input.is_empty() {
16256 return None;
16257 }
16258
16259 let mut iter = self.input.graphemes(true).peekable();
16260 let mut offset = 0;
16261 let mut graphemes = 0;
16262 if let Some(first_grapheme) = iter.next() {
16263 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16264 offset += first_grapheme.len();
16265 graphemes += 1;
16266 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16267 if let Some(grapheme) = iter.peek().copied() {
16268 if should_stay_with_preceding_ideograph(grapheme) {
16269 offset += grapheme.len();
16270 graphemes += 1;
16271 }
16272 }
16273 } else {
16274 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16275 let mut next_word_bound = words.peek().copied();
16276 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16277 next_word_bound = words.next();
16278 }
16279 while let Some(grapheme) = iter.peek().copied() {
16280 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16281 break;
16282 };
16283 if is_grapheme_whitespace(grapheme) != is_whitespace {
16284 break;
16285 };
16286 offset += grapheme.len();
16287 graphemes += 1;
16288 iter.next();
16289 }
16290 }
16291 let token = &self.input[..offset];
16292 self.input = &self.input[offset..];
16293 if is_whitespace {
16294 Some(WordBreakToken {
16295 token: " ",
16296 grapheme_len: 1,
16297 is_whitespace: true,
16298 })
16299 } else {
16300 Some(WordBreakToken {
16301 token,
16302 grapheme_len: graphemes,
16303 is_whitespace: false,
16304 })
16305 }
16306 } else {
16307 None
16308 }
16309 }
16310}
16311
16312#[test]
16313fn test_word_breaking_tokenizer() {
16314 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16315 ("", &[]),
16316 (" ", &[(" ", 1, true)]),
16317 ("Ʒ", &[("Ʒ", 1, false)]),
16318 ("Ǽ", &[("Ǽ", 1, false)]),
16319 ("⋑", &[("⋑", 1, false)]),
16320 ("⋑⋑", &[("⋑⋑", 2, false)]),
16321 (
16322 "原理,进而",
16323 &[
16324 ("原", 1, false),
16325 ("理,", 2, false),
16326 ("进", 1, false),
16327 ("而", 1, false),
16328 ],
16329 ),
16330 (
16331 "hello world",
16332 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16333 ),
16334 (
16335 "hello, world",
16336 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16337 ),
16338 (
16339 " hello world",
16340 &[
16341 (" ", 1, true),
16342 ("hello", 5, false),
16343 (" ", 1, true),
16344 ("world", 5, false),
16345 ],
16346 ),
16347 (
16348 "这是什么 \n 钢笔",
16349 &[
16350 ("这", 1, false),
16351 ("是", 1, false),
16352 ("什", 1, false),
16353 ("么", 1, false),
16354 (" ", 1, true),
16355 ("钢", 1, false),
16356 ("笔", 1, false),
16357 ],
16358 ),
16359 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16360 ];
16361
16362 for (input, result) in tests {
16363 assert_eq!(
16364 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16365 result
16366 .iter()
16367 .copied()
16368 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16369 token,
16370 grapheme_len,
16371 is_whitespace,
16372 })
16373 .collect::<Vec<_>>()
16374 );
16375 }
16376}
16377
16378fn wrap_with_prefix(
16379 line_prefix: String,
16380 unwrapped_text: String,
16381 wrap_column: usize,
16382 tab_size: NonZeroU32,
16383) -> String {
16384 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16385 let mut wrapped_text = String::new();
16386 let mut current_line = line_prefix.clone();
16387
16388 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16389 let mut current_line_len = line_prefix_len;
16390 for WordBreakToken {
16391 token,
16392 grapheme_len,
16393 is_whitespace,
16394 } in tokenizer
16395 {
16396 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16397 wrapped_text.push_str(current_line.trim_end());
16398 wrapped_text.push('\n');
16399 current_line.truncate(line_prefix.len());
16400 current_line_len = line_prefix_len;
16401 if !is_whitespace {
16402 current_line.push_str(token);
16403 current_line_len += grapheme_len;
16404 }
16405 } else if !is_whitespace {
16406 current_line.push_str(token);
16407 current_line_len += grapheme_len;
16408 } else if current_line_len != line_prefix_len {
16409 current_line.push(' ');
16410 current_line_len += 1;
16411 }
16412 }
16413
16414 if !current_line.is_empty() {
16415 wrapped_text.push_str(¤t_line);
16416 }
16417 wrapped_text
16418}
16419
16420#[test]
16421fn test_wrap_with_prefix() {
16422 assert_eq!(
16423 wrap_with_prefix(
16424 "# ".to_string(),
16425 "abcdefg".to_string(),
16426 4,
16427 NonZeroU32::new(4).unwrap()
16428 ),
16429 "# abcdefg"
16430 );
16431 assert_eq!(
16432 wrap_with_prefix(
16433 "".to_string(),
16434 "\thello world".to_string(),
16435 8,
16436 NonZeroU32::new(4).unwrap()
16437 ),
16438 "hello\nworld"
16439 );
16440 assert_eq!(
16441 wrap_with_prefix(
16442 "// ".to_string(),
16443 "xx \nyy zz aa bb cc".to_string(),
16444 12,
16445 NonZeroU32::new(4).unwrap()
16446 ),
16447 "// xx yy zz\n// aa bb cc"
16448 );
16449 assert_eq!(
16450 wrap_with_prefix(
16451 String::new(),
16452 "这是什么 \n 钢笔".to_string(),
16453 3,
16454 NonZeroU32::new(4).unwrap()
16455 ),
16456 "这是什\n么 钢\n笔"
16457 );
16458}
16459
16460pub trait CollaborationHub {
16461 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16462 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16463 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16464}
16465
16466impl CollaborationHub for Entity<Project> {
16467 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16468 self.read(cx).collaborators()
16469 }
16470
16471 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16472 self.read(cx).user_store().read(cx).participant_indices()
16473 }
16474
16475 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16476 let this = self.read(cx);
16477 let user_ids = this.collaborators().values().map(|c| c.user_id);
16478 this.user_store().read_with(cx, |user_store, cx| {
16479 user_store.participant_names(user_ids, cx)
16480 })
16481 }
16482}
16483
16484pub trait SemanticsProvider {
16485 fn hover(
16486 &self,
16487 buffer: &Entity<Buffer>,
16488 position: text::Anchor,
16489 cx: &mut App,
16490 ) -> Option<Task<Vec<project::Hover>>>;
16491
16492 fn inlay_hints(
16493 &self,
16494 buffer_handle: Entity<Buffer>,
16495 range: Range<text::Anchor>,
16496 cx: &mut App,
16497 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16498
16499 fn resolve_inlay_hint(
16500 &self,
16501 hint: InlayHint,
16502 buffer_handle: Entity<Buffer>,
16503 server_id: LanguageServerId,
16504 cx: &mut App,
16505 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16506
16507 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16508
16509 fn document_highlights(
16510 &self,
16511 buffer: &Entity<Buffer>,
16512 position: text::Anchor,
16513 cx: &mut App,
16514 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16515
16516 fn definitions(
16517 &self,
16518 buffer: &Entity<Buffer>,
16519 position: text::Anchor,
16520 kind: GotoDefinitionKind,
16521 cx: &mut App,
16522 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16523
16524 fn range_for_rename(
16525 &self,
16526 buffer: &Entity<Buffer>,
16527 position: text::Anchor,
16528 cx: &mut App,
16529 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16530
16531 fn perform_rename(
16532 &self,
16533 buffer: &Entity<Buffer>,
16534 position: text::Anchor,
16535 new_name: String,
16536 cx: &mut App,
16537 ) -> Option<Task<Result<ProjectTransaction>>>;
16538}
16539
16540pub trait CompletionProvider {
16541 fn completions(
16542 &self,
16543 buffer: &Entity<Buffer>,
16544 buffer_position: text::Anchor,
16545 trigger: CompletionContext,
16546 window: &mut Window,
16547 cx: &mut Context<Editor>,
16548 ) -> Task<Result<Vec<Completion>>>;
16549
16550 fn resolve_completions(
16551 &self,
16552 buffer: Entity<Buffer>,
16553 completion_indices: Vec<usize>,
16554 completions: Rc<RefCell<Box<[Completion]>>>,
16555 cx: &mut Context<Editor>,
16556 ) -> Task<Result<bool>>;
16557
16558 fn apply_additional_edits_for_completion(
16559 &self,
16560 _buffer: Entity<Buffer>,
16561 _completions: Rc<RefCell<Box<[Completion]>>>,
16562 _completion_index: usize,
16563 _push_to_history: bool,
16564 _cx: &mut Context<Editor>,
16565 ) -> Task<Result<Option<language::Transaction>>> {
16566 Task::ready(Ok(None))
16567 }
16568
16569 fn is_completion_trigger(
16570 &self,
16571 buffer: &Entity<Buffer>,
16572 position: language::Anchor,
16573 text: &str,
16574 trigger_in_words: bool,
16575 cx: &mut Context<Editor>,
16576 ) -> bool;
16577
16578 fn sort_completions(&self) -> bool {
16579 true
16580 }
16581}
16582
16583pub trait CodeActionProvider {
16584 fn id(&self) -> Arc<str>;
16585
16586 fn code_actions(
16587 &self,
16588 buffer: &Entity<Buffer>,
16589 range: Range<text::Anchor>,
16590 window: &mut Window,
16591 cx: &mut App,
16592 ) -> Task<Result<Vec<CodeAction>>>;
16593
16594 fn apply_code_action(
16595 &self,
16596 buffer_handle: Entity<Buffer>,
16597 action: CodeAction,
16598 excerpt_id: ExcerptId,
16599 push_to_history: bool,
16600 window: &mut Window,
16601 cx: &mut App,
16602 ) -> Task<Result<ProjectTransaction>>;
16603}
16604
16605impl CodeActionProvider for Entity<Project> {
16606 fn id(&self) -> Arc<str> {
16607 "project".into()
16608 }
16609
16610 fn code_actions(
16611 &self,
16612 buffer: &Entity<Buffer>,
16613 range: Range<text::Anchor>,
16614 _window: &mut Window,
16615 cx: &mut App,
16616 ) -> Task<Result<Vec<CodeAction>>> {
16617 self.update(cx, |project, cx| {
16618 project.code_actions(buffer, range, None, cx)
16619 })
16620 }
16621
16622 fn apply_code_action(
16623 &self,
16624 buffer_handle: Entity<Buffer>,
16625 action: CodeAction,
16626 _excerpt_id: ExcerptId,
16627 push_to_history: bool,
16628 _window: &mut Window,
16629 cx: &mut App,
16630 ) -> Task<Result<ProjectTransaction>> {
16631 self.update(cx, |project, cx| {
16632 project.apply_code_action(buffer_handle, action, push_to_history, cx)
16633 })
16634 }
16635}
16636
16637fn snippet_completions(
16638 project: &Project,
16639 buffer: &Entity<Buffer>,
16640 buffer_position: text::Anchor,
16641 cx: &mut App,
16642) -> Task<Result<Vec<Completion>>> {
16643 let language = buffer.read(cx).language_at(buffer_position);
16644 let language_name = language.as_ref().map(|language| language.lsp_id());
16645 let snippet_store = project.snippets().read(cx);
16646 let snippets = snippet_store.snippets_for(language_name, cx);
16647
16648 if snippets.is_empty() {
16649 return Task::ready(Ok(vec![]));
16650 }
16651 let snapshot = buffer.read(cx).text_snapshot();
16652 let chars: String = snapshot
16653 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
16654 .collect();
16655
16656 let scope = language.map(|language| language.default_scope());
16657 let executor = cx.background_executor().clone();
16658
16659 cx.background_spawn(async move {
16660 let classifier = CharClassifier::new(scope).for_completion(true);
16661 let mut last_word = chars
16662 .chars()
16663 .take_while(|c| classifier.is_word(*c))
16664 .collect::<String>();
16665 last_word = last_word.chars().rev().collect();
16666
16667 if last_word.is_empty() {
16668 return Ok(vec![]);
16669 }
16670
16671 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
16672 let to_lsp = |point: &text::Anchor| {
16673 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
16674 point_to_lsp(end)
16675 };
16676 let lsp_end = to_lsp(&buffer_position);
16677
16678 let candidates = snippets
16679 .iter()
16680 .enumerate()
16681 .flat_map(|(ix, snippet)| {
16682 snippet
16683 .prefix
16684 .iter()
16685 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
16686 })
16687 .collect::<Vec<StringMatchCandidate>>();
16688
16689 let mut matches = fuzzy::match_strings(
16690 &candidates,
16691 &last_word,
16692 last_word.chars().any(|c| c.is_uppercase()),
16693 100,
16694 &Default::default(),
16695 executor,
16696 )
16697 .await;
16698
16699 // Remove all candidates where the query's start does not match the start of any word in the candidate
16700 if let Some(query_start) = last_word.chars().next() {
16701 matches.retain(|string_match| {
16702 split_words(&string_match.string).any(|word| {
16703 // Check that the first codepoint of the word as lowercase matches the first
16704 // codepoint of the query as lowercase
16705 word.chars()
16706 .flat_map(|codepoint| codepoint.to_lowercase())
16707 .zip(query_start.to_lowercase())
16708 .all(|(word_cp, query_cp)| word_cp == query_cp)
16709 })
16710 });
16711 }
16712
16713 let matched_strings = matches
16714 .into_iter()
16715 .map(|m| m.string)
16716 .collect::<HashSet<_>>();
16717
16718 let result: Vec<Completion> = snippets
16719 .into_iter()
16720 .filter_map(|snippet| {
16721 let matching_prefix = snippet
16722 .prefix
16723 .iter()
16724 .find(|prefix| matched_strings.contains(*prefix))?;
16725 let start = as_offset - last_word.len();
16726 let start = snapshot.anchor_before(start);
16727 let range = start..buffer_position;
16728 let lsp_start = to_lsp(&start);
16729 let lsp_range = lsp::Range {
16730 start: lsp_start,
16731 end: lsp_end,
16732 };
16733 Some(Completion {
16734 old_range: range,
16735 new_text: snippet.body.clone(),
16736 resolved: false,
16737 label: CodeLabel {
16738 text: matching_prefix.clone(),
16739 runs: vec![],
16740 filter_range: 0..matching_prefix.len(),
16741 },
16742 server_id: LanguageServerId(usize::MAX),
16743 documentation: snippet
16744 .description
16745 .clone()
16746 .map(|description| CompletionDocumentation::SingleLine(description.into())),
16747 lsp_completion: lsp::CompletionItem {
16748 label: snippet.prefix.first().unwrap().clone(),
16749 kind: Some(CompletionItemKind::SNIPPET),
16750 label_details: snippet.description.as_ref().map(|description| {
16751 lsp::CompletionItemLabelDetails {
16752 detail: Some(description.clone()),
16753 description: None,
16754 }
16755 }),
16756 insert_text_format: Some(InsertTextFormat::SNIPPET),
16757 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
16758 lsp::InsertReplaceEdit {
16759 new_text: snippet.body.clone(),
16760 insert: lsp_range,
16761 replace: lsp_range,
16762 },
16763 )),
16764 filter_text: Some(snippet.body.clone()),
16765 sort_text: Some(char::MAX.to_string()),
16766 ..Default::default()
16767 },
16768 confirm: None,
16769 })
16770 })
16771 .collect();
16772
16773 Ok(result)
16774 })
16775}
16776
16777impl CompletionProvider for Entity<Project> {
16778 fn completions(
16779 &self,
16780 buffer: &Entity<Buffer>,
16781 buffer_position: text::Anchor,
16782 options: CompletionContext,
16783 _window: &mut Window,
16784 cx: &mut Context<Editor>,
16785 ) -> Task<Result<Vec<Completion>>> {
16786 self.update(cx, |project, cx| {
16787 let snippets = snippet_completions(project, buffer, buffer_position, cx);
16788 let project_completions = project.completions(buffer, buffer_position, options, cx);
16789 cx.background_spawn(async move {
16790 let mut completions = project_completions.await?;
16791 let snippets_completions = snippets.await?;
16792 completions.extend(snippets_completions);
16793 Ok(completions)
16794 })
16795 })
16796 }
16797
16798 fn resolve_completions(
16799 &self,
16800 buffer: Entity<Buffer>,
16801 completion_indices: Vec<usize>,
16802 completions: Rc<RefCell<Box<[Completion]>>>,
16803 cx: &mut Context<Editor>,
16804 ) -> Task<Result<bool>> {
16805 self.update(cx, |project, cx| {
16806 project.lsp_store().update(cx, |lsp_store, cx| {
16807 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
16808 })
16809 })
16810 }
16811
16812 fn apply_additional_edits_for_completion(
16813 &self,
16814 buffer: Entity<Buffer>,
16815 completions: Rc<RefCell<Box<[Completion]>>>,
16816 completion_index: usize,
16817 push_to_history: bool,
16818 cx: &mut Context<Editor>,
16819 ) -> Task<Result<Option<language::Transaction>>> {
16820 self.update(cx, |project, cx| {
16821 project.lsp_store().update(cx, |lsp_store, cx| {
16822 lsp_store.apply_additional_edits_for_completion(
16823 buffer,
16824 completions,
16825 completion_index,
16826 push_to_history,
16827 cx,
16828 )
16829 })
16830 })
16831 }
16832
16833 fn is_completion_trigger(
16834 &self,
16835 buffer: &Entity<Buffer>,
16836 position: language::Anchor,
16837 text: &str,
16838 trigger_in_words: bool,
16839 cx: &mut Context<Editor>,
16840 ) -> bool {
16841 let mut chars = text.chars();
16842 let char = if let Some(char) = chars.next() {
16843 char
16844 } else {
16845 return false;
16846 };
16847 if chars.next().is_some() {
16848 return false;
16849 }
16850
16851 let buffer = buffer.read(cx);
16852 let snapshot = buffer.snapshot();
16853 if !snapshot.settings_at(position, cx).show_completions_on_input {
16854 return false;
16855 }
16856 let classifier = snapshot.char_classifier_at(position).for_completion(true);
16857 if trigger_in_words && classifier.is_word(char) {
16858 return true;
16859 }
16860
16861 buffer.completion_triggers().contains(text)
16862 }
16863}
16864
16865impl SemanticsProvider for Entity<Project> {
16866 fn hover(
16867 &self,
16868 buffer: &Entity<Buffer>,
16869 position: text::Anchor,
16870 cx: &mut App,
16871 ) -> Option<Task<Vec<project::Hover>>> {
16872 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
16873 }
16874
16875 fn document_highlights(
16876 &self,
16877 buffer: &Entity<Buffer>,
16878 position: text::Anchor,
16879 cx: &mut App,
16880 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
16881 Some(self.update(cx, |project, cx| {
16882 project.document_highlights(buffer, position, cx)
16883 }))
16884 }
16885
16886 fn definitions(
16887 &self,
16888 buffer: &Entity<Buffer>,
16889 position: text::Anchor,
16890 kind: GotoDefinitionKind,
16891 cx: &mut App,
16892 ) -> Option<Task<Result<Vec<LocationLink>>>> {
16893 Some(self.update(cx, |project, cx| match kind {
16894 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
16895 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
16896 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
16897 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
16898 }))
16899 }
16900
16901 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
16902 // TODO: make this work for remote projects
16903 self.update(cx, |this, cx| {
16904 buffer.update(cx, |buffer, cx| {
16905 this.any_language_server_supports_inlay_hints(buffer, cx)
16906 })
16907 })
16908 }
16909
16910 fn inlay_hints(
16911 &self,
16912 buffer_handle: Entity<Buffer>,
16913 range: Range<text::Anchor>,
16914 cx: &mut App,
16915 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
16916 Some(self.update(cx, |project, cx| {
16917 project.inlay_hints(buffer_handle, range, cx)
16918 }))
16919 }
16920
16921 fn resolve_inlay_hint(
16922 &self,
16923 hint: InlayHint,
16924 buffer_handle: Entity<Buffer>,
16925 server_id: LanguageServerId,
16926 cx: &mut App,
16927 ) -> Option<Task<anyhow::Result<InlayHint>>> {
16928 Some(self.update(cx, |project, cx| {
16929 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
16930 }))
16931 }
16932
16933 fn range_for_rename(
16934 &self,
16935 buffer: &Entity<Buffer>,
16936 position: text::Anchor,
16937 cx: &mut App,
16938 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
16939 Some(self.update(cx, |project, cx| {
16940 let buffer = buffer.clone();
16941 let task = project.prepare_rename(buffer.clone(), position, cx);
16942 cx.spawn(|_, mut cx| async move {
16943 Ok(match task.await? {
16944 PrepareRenameResponse::Success(range) => Some(range),
16945 PrepareRenameResponse::InvalidPosition => None,
16946 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
16947 // Fallback on using TreeSitter info to determine identifier range
16948 buffer.update(&mut cx, |buffer, _| {
16949 let snapshot = buffer.snapshot();
16950 let (range, kind) = snapshot.surrounding_word(position);
16951 if kind != Some(CharKind::Word) {
16952 return None;
16953 }
16954 Some(
16955 snapshot.anchor_before(range.start)
16956 ..snapshot.anchor_after(range.end),
16957 )
16958 })?
16959 }
16960 })
16961 })
16962 }))
16963 }
16964
16965 fn perform_rename(
16966 &self,
16967 buffer: &Entity<Buffer>,
16968 position: text::Anchor,
16969 new_name: String,
16970 cx: &mut App,
16971 ) -> Option<Task<Result<ProjectTransaction>>> {
16972 Some(self.update(cx, |project, cx| {
16973 project.perform_rename(buffer.clone(), position, new_name, cx)
16974 }))
16975 }
16976}
16977
16978fn inlay_hint_settings(
16979 location: Anchor,
16980 snapshot: &MultiBufferSnapshot,
16981 cx: &mut Context<Editor>,
16982) -> InlayHintSettings {
16983 let file = snapshot.file_at(location);
16984 let language = snapshot.language_at(location).map(|l| l.name());
16985 language_settings(language, file, cx).inlay_hints
16986}
16987
16988fn consume_contiguous_rows(
16989 contiguous_row_selections: &mut Vec<Selection<Point>>,
16990 selection: &Selection<Point>,
16991 display_map: &DisplaySnapshot,
16992 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
16993) -> (MultiBufferRow, MultiBufferRow) {
16994 contiguous_row_selections.push(selection.clone());
16995 let start_row = MultiBufferRow(selection.start.row);
16996 let mut end_row = ending_row(selection, display_map);
16997
16998 while let Some(next_selection) = selections.peek() {
16999 if next_selection.start.row <= end_row.0 {
17000 end_row = ending_row(next_selection, display_map);
17001 contiguous_row_selections.push(selections.next().unwrap().clone());
17002 } else {
17003 break;
17004 }
17005 }
17006 (start_row, end_row)
17007}
17008
17009fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17010 if next_selection.end.column > 0 || next_selection.is_empty() {
17011 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17012 } else {
17013 MultiBufferRow(next_selection.end.row)
17014 }
17015}
17016
17017impl EditorSnapshot {
17018 pub fn remote_selections_in_range<'a>(
17019 &'a self,
17020 range: &'a Range<Anchor>,
17021 collaboration_hub: &dyn CollaborationHub,
17022 cx: &'a App,
17023 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17024 let participant_names = collaboration_hub.user_names(cx);
17025 let participant_indices = collaboration_hub.user_participant_indices(cx);
17026 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17027 let collaborators_by_replica_id = collaborators_by_peer_id
17028 .iter()
17029 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17030 .collect::<HashMap<_, _>>();
17031 self.buffer_snapshot
17032 .selections_in_range(range, false)
17033 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17034 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17035 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17036 let user_name = participant_names.get(&collaborator.user_id).cloned();
17037 Some(RemoteSelection {
17038 replica_id,
17039 selection,
17040 cursor_shape,
17041 line_mode,
17042 participant_index,
17043 peer_id: collaborator.peer_id,
17044 user_name,
17045 })
17046 })
17047 }
17048
17049 pub fn hunks_for_ranges(
17050 &self,
17051 ranges: impl Iterator<Item = Range<Point>>,
17052 ) -> Vec<MultiBufferDiffHunk> {
17053 let mut hunks = Vec::new();
17054 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17055 HashMap::default();
17056 for query_range in ranges {
17057 let query_rows =
17058 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17059 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17060 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17061 ) {
17062 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
17063 // when the caret is just above or just below the deleted hunk.
17064 let allow_adjacent = hunk.status().is_deleted();
17065 let related_to_selection = if allow_adjacent {
17066 hunk.row_range.overlaps(&query_rows)
17067 || hunk.row_range.start == query_rows.end
17068 || hunk.row_range.end == query_rows.start
17069 } else {
17070 hunk.row_range.overlaps(&query_rows)
17071 };
17072 if related_to_selection {
17073 if !processed_buffer_rows
17074 .entry(hunk.buffer_id)
17075 .or_default()
17076 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17077 {
17078 continue;
17079 }
17080 hunks.push(hunk);
17081 }
17082 }
17083 }
17084
17085 hunks
17086 }
17087
17088 fn display_diff_hunks_for_rows<'a>(
17089 &'a self,
17090 display_rows: Range<DisplayRow>,
17091 folded_buffers: &'a HashSet<BufferId>,
17092 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17093 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17094 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17095
17096 self.buffer_snapshot
17097 .diff_hunks_in_range(buffer_start..buffer_end)
17098 .filter_map(|hunk| {
17099 if folded_buffers.contains(&hunk.buffer_id) {
17100 return None;
17101 }
17102
17103 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17104 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17105
17106 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17107 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17108
17109 let display_hunk = if hunk_display_start.column() != 0 {
17110 DisplayDiffHunk::Folded {
17111 display_row: hunk_display_start.row(),
17112 }
17113 } else {
17114 let mut end_row = hunk_display_end.row();
17115 if hunk_display_end.column() > 0 {
17116 end_row.0 += 1;
17117 }
17118 DisplayDiffHunk::Unfolded {
17119 status: hunk.status(),
17120 diff_base_byte_range: hunk.diff_base_byte_range,
17121 display_row_range: hunk_display_start.row()..end_row,
17122 multi_buffer_range: Anchor::range_in_buffer(
17123 hunk.excerpt_id,
17124 hunk.buffer_id,
17125 hunk.buffer_range,
17126 ),
17127 }
17128 };
17129
17130 Some(display_hunk)
17131 })
17132 }
17133
17134 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17135 self.display_snapshot.buffer_snapshot.language_at(position)
17136 }
17137
17138 pub fn is_focused(&self) -> bool {
17139 self.is_focused
17140 }
17141
17142 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17143 self.placeholder_text.as_ref()
17144 }
17145
17146 pub fn scroll_position(&self) -> gpui::Point<f32> {
17147 self.scroll_anchor.scroll_position(&self.display_snapshot)
17148 }
17149
17150 fn gutter_dimensions(
17151 &self,
17152 font_id: FontId,
17153 font_size: Pixels,
17154 max_line_number_width: Pixels,
17155 cx: &App,
17156 ) -> Option<GutterDimensions> {
17157 if !self.show_gutter {
17158 return None;
17159 }
17160
17161 let descent = cx.text_system().descent(font_id, font_size);
17162 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17163 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17164
17165 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17166 matches!(
17167 ProjectSettings::get_global(cx).git.git_gutter,
17168 Some(GitGutterSetting::TrackedFiles)
17169 )
17170 });
17171 let gutter_settings = EditorSettings::get_global(cx).gutter;
17172 let show_line_numbers = self
17173 .show_line_numbers
17174 .unwrap_or(gutter_settings.line_numbers);
17175 let line_gutter_width = if show_line_numbers {
17176 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17177 let min_width_for_number_on_gutter = em_advance * 4.0;
17178 max_line_number_width.max(min_width_for_number_on_gutter)
17179 } else {
17180 0.0.into()
17181 };
17182
17183 let show_code_actions = self
17184 .show_code_actions
17185 .unwrap_or(gutter_settings.code_actions);
17186
17187 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17188
17189 let git_blame_entries_width =
17190 self.git_blame_gutter_max_author_length
17191 .map(|max_author_length| {
17192 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17193
17194 /// The number of characters to dedicate to gaps and margins.
17195 const SPACING_WIDTH: usize = 4;
17196
17197 let max_char_count = max_author_length
17198 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17199 + ::git::SHORT_SHA_LENGTH
17200 + MAX_RELATIVE_TIMESTAMP.len()
17201 + SPACING_WIDTH;
17202
17203 em_advance * max_char_count
17204 });
17205
17206 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17207 left_padding += if show_code_actions || show_runnables {
17208 em_width * 3.0
17209 } else if show_git_gutter && show_line_numbers {
17210 em_width * 2.0
17211 } else if show_git_gutter || show_line_numbers {
17212 em_width
17213 } else {
17214 px(0.)
17215 };
17216
17217 let right_padding = if gutter_settings.folds && show_line_numbers {
17218 em_width * 4.0
17219 } else if gutter_settings.folds {
17220 em_width * 3.0
17221 } else if show_line_numbers {
17222 em_width
17223 } else {
17224 px(0.)
17225 };
17226
17227 Some(GutterDimensions {
17228 left_padding,
17229 right_padding,
17230 width: line_gutter_width + left_padding + right_padding,
17231 margin: -descent,
17232 git_blame_entries_width,
17233 })
17234 }
17235
17236 pub fn render_crease_toggle(
17237 &self,
17238 buffer_row: MultiBufferRow,
17239 row_contains_cursor: bool,
17240 editor: Entity<Editor>,
17241 window: &mut Window,
17242 cx: &mut App,
17243 ) -> Option<AnyElement> {
17244 let folded = self.is_line_folded(buffer_row);
17245 let mut is_foldable = false;
17246
17247 if let Some(crease) = self
17248 .crease_snapshot
17249 .query_row(buffer_row, &self.buffer_snapshot)
17250 {
17251 is_foldable = true;
17252 match crease {
17253 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17254 if let Some(render_toggle) = render_toggle {
17255 let toggle_callback =
17256 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17257 if folded {
17258 editor.update(cx, |editor, cx| {
17259 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17260 });
17261 } else {
17262 editor.update(cx, |editor, cx| {
17263 editor.unfold_at(
17264 &crate::UnfoldAt { buffer_row },
17265 window,
17266 cx,
17267 )
17268 });
17269 }
17270 });
17271 return Some((render_toggle)(
17272 buffer_row,
17273 folded,
17274 toggle_callback,
17275 window,
17276 cx,
17277 ));
17278 }
17279 }
17280 }
17281 }
17282
17283 is_foldable |= self.starts_indent(buffer_row);
17284
17285 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17286 Some(
17287 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17288 .toggle_state(folded)
17289 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17290 if folded {
17291 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17292 } else {
17293 this.fold_at(&FoldAt { buffer_row }, window, cx);
17294 }
17295 }))
17296 .into_any_element(),
17297 )
17298 } else {
17299 None
17300 }
17301 }
17302
17303 pub fn render_crease_trailer(
17304 &self,
17305 buffer_row: MultiBufferRow,
17306 window: &mut Window,
17307 cx: &mut App,
17308 ) -> Option<AnyElement> {
17309 let folded = self.is_line_folded(buffer_row);
17310 if let Crease::Inline { render_trailer, .. } = self
17311 .crease_snapshot
17312 .query_row(buffer_row, &self.buffer_snapshot)?
17313 {
17314 let render_trailer = render_trailer.as_ref()?;
17315 Some(render_trailer(buffer_row, folded, window, cx))
17316 } else {
17317 None
17318 }
17319 }
17320}
17321
17322impl Deref for EditorSnapshot {
17323 type Target = DisplaySnapshot;
17324
17325 fn deref(&self) -> &Self::Target {
17326 &self.display_snapshot
17327 }
17328}
17329
17330#[derive(Clone, Debug, PartialEq, Eq)]
17331pub enum EditorEvent {
17332 InputIgnored {
17333 text: Arc<str>,
17334 },
17335 InputHandled {
17336 utf16_range_to_replace: Option<Range<isize>>,
17337 text: Arc<str>,
17338 },
17339 ExcerptsAdded {
17340 buffer: Entity<Buffer>,
17341 predecessor: ExcerptId,
17342 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17343 },
17344 ExcerptsRemoved {
17345 ids: Vec<ExcerptId>,
17346 },
17347 BufferFoldToggled {
17348 ids: Vec<ExcerptId>,
17349 folded: bool,
17350 },
17351 ExcerptsEdited {
17352 ids: Vec<ExcerptId>,
17353 },
17354 ExcerptsExpanded {
17355 ids: Vec<ExcerptId>,
17356 },
17357 BufferEdited,
17358 Edited {
17359 transaction_id: clock::Lamport,
17360 },
17361 Reparsed(BufferId),
17362 Focused,
17363 FocusedIn,
17364 Blurred,
17365 DirtyChanged,
17366 Saved,
17367 TitleChanged,
17368 DiffBaseChanged,
17369 SelectionsChanged {
17370 local: bool,
17371 },
17372 ScrollPositionChanged {
17373 local: bool,
17374 autoscroll: bool,
17375 },
17376 Closed,
17377 TransactionUndone {
17378 transaction_id: clock::Lamport,
17379 },
17380 TransactionBegun {
17381 transaction_id: clock::Lamport,
17382 },
17383 Reloaded,
17384 CursorShapeChanged,
17385}
17386
17387impl EventEmitter<EditorEvent> for Editor {}
17388
17389impl Focusable for Editor {
17390 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17391 self.focus_handle.clone()
17392 }
17393}
17394
17395impl Render for Editor {
17396 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17397 let settings = ThemeSettings::get_global(cx);
17398
17399 let mut text_style = match self.mode {
17400 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17401 color: cx.theme().colors().editor_foreground,
17402 font_family: settings.ui_font.family.clone(),
17403 font_features: settings.ui_font.features.clone(),
17404 font_fallbacks: settings.ui_font.fallbacks.clone(),
17405 font_size: rems(0.875).into(),
17406 font_weight: settings.ui_font.weight,
17407 line_height: relative(settings.buffer_line_height.value()),
17408 ..Default::default()
17409 },
17410 EditorMode::Full => TextStyle {
17411 color: cx.theme().colors().editor_foreground,
17412 font_family: settings.buffer_font.family.clone(),
17413 font_features: settings.buffer_font.features.clone(),
17414 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17415 font_size: settings.buffer_font_size(cx).into(),
17416 font_weight: settings.buffer_font.weight,
17417 line_height: relative(settings.buffer_line_height.value()),
17418 ..Default::default()
17419 },
17420 };
17421 if let Some(text_style_refinement) = &self.text_style_refinement {
17422 text_style.refine(text_style_refinement)
17423 }
17424
17425 let background = match self.mode {
17426 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17427 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17428 EditorMode::Full => cx.theme().colors().editor_background,
17429 };
17430
17431 EditorElement::new(
17432 &cx.entity(),
17433 EditorStyle {
17434 background,
17435 local_player: cx.theme().players().local(),
17436 text: text_style,
17437 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17438 syntax: cx.theme().syntax().clone(),
17439 status: cx.theme().status().clone(),
17440 inlay_hints_style: make_inlay_hints_style(cx),
17441 inline_completion_styles: make_suggestion_styles(cx),
17442 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17443 },
17444 )
17445 }
17446}
17447
17448impl EntityInputHandler for Editor {
17449 fn text_for_range(
17450 &mut self,
17451 range_utf16: Range<usize>,
17452 adjusted_range: &mut Option<Range<usize>>,
17453 _: &mut Window,
17454 cx: &mut Context<Self>,
17455 ) -> Option<String> {
17456 let snapshot = self.buffer.read(cx).read(cx);
17457 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17458 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17459 if (start.0..end.0) != range_utf16 {
17460 adjusted_range.replace(start.0..end.0);
17461 }
17462 Some(snapshot.text_for_range(start..end).collect())
17463 }
17464
17465 fn selected_text_range(
17466 &mut self,
17467 ignore_disabled_input: bool,
17468 _: &mut Window,
17469 cx: &mut Context<Self>,
17470 ) -> Option<UTF16Selection> {
17471 // Prevent the IME menu from appearing when holding down an alphabetic key
17472 // while input is disabled.
17473 if !ignore_disabled_input && !self.input_enabled {
17474 return None;
17475 }
17476
17477 let selection = self.selections.newest::<OffsetUtf16>(cx);
17478 let range = selection.range();
17479
17480 Some(UTF16Selection {
17481 range: range.start.0..range.end.0,
17482 reversed: selection.reversed,
17483 })
17484 }
17485
17486 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17487 let snapshot = self.buffer.read(cx).read(cx);
17488 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17489 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17490 }
17491
17492 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17493 self.clear_highlights::<InputComposition>(cx);
17494 self.ime_transaction.take();
17495 }
17496
17497 fn replace_text_in_range(
17498 &mut self,
17499 range_utf16: Option<Range<usize>>,
17500 text: &str,
17501 window: &mut Window,
17502 cx: &mut Context<Self>,
17503 ) {
17504 if !self.input_enabled {
17505 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17506 return;
17507 }
17508
17509 self.transact(window, cx, |this, window, cx| {
17510 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17511 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17512 Some(this.selection_replacement_ranges(range_utf16, cx))
17513 } else {
17514 this.marked_text_ranges(cx)
17515 };
17516
17517 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17518 let newest_selection_id = this.selections.newest_anchor().id;
17519 this.selections
17520 .all::<OffsetUtf16>(cx)
17521 .iter()
17522 .zip(ranges_to_replace.iter())
17523 .find_map(|(selection, range)| {
17524 if selection.id == newest_selection_id {
17525 Some(
17526 (range.start.0 as isize - selection.head().0 as isize)
17527 ..(range.end.0 as isize - selection.head().0 as isize),
17528 )
17529 } else {
17530 None
17531 }
17532 })
17533 });
17534
17535 cx.emit(EditorEvent::InputHandled {
17536 utf16_range_to_replace: range_to_replace,
17537 text: text.into(),
17538 });
17539
17540 if let Some(new_selected_ranges) = new_selected_ranges {
17541 this.change_selections(None, window, cx, |selections| {
17542 selections.select_ranges(new_selected_ranges)
17543 });
17544 this.backspace(&Default::default(), window, cx);
17545 }
17546
17547 this.handle_input(text, window, cx);
17548 });
17549
17550 if let Some(transaction) = self.ime_transaction {
17551 self.buffer.update(cx, |buffer, cx| {
17552 buffer.group_until_transaction(transaction, cx);
17553 });
17554 }
17555
17556 self.unmark_text(window, cx);
17557 }
17558
17559 fn replace_and_mark_text_in_range(
17560 &mut self,
17561 range_utf16: Option<Range<usize>>,
17562 text: &str,
17563 new_selected_range_utf16: Option<Range<usize>>,
17564 window: &mut Window,
17565 cx: &mut Context<Self>,
17566 ) {
17567 if !self.input_enabled {
17568 return;
17569 }
17570
17571 let transaction = self.transact(window, cx, |this, window, cx| {
17572 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17573 let snapshot = this.buffer.read(cx).read(cx);
17574 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17575 for marked_range in &mut marked_ranges {
17576 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17577 marked_range.start.0 += relative_range_utf16.start;
17578 marked_range.start =
17579 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
17580 marked_range.end =
17581 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
17582 }
17583 }
17584 Some(marked_ranges)
17585 } else if let Some(range_utf16) = range_utf16 {
17586 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17587 Some(this.selection_replacement_ranges(range_utf16, cx))
17588 } else {
17589 None
17590 };
17591
17592 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
17593 let newest_selection_id = this.selections.newest_anchor().id;
17594 this.selections
17595 .all::<OffsetUtf16>(cx)
17596 .iter()
17597 .zip(ranges_to_replace.iter())
17598 .find_map(|(selection, range)| {
17599 if selection.id == newest_selection_id {
17600 Some(
17601 (range.start.0 as isize - selection.head().0 as isize)
17602 ..(range.end.0 as isize - selection.head().0 as isize),
17603 )
17604 } else {
17605 None
17606 }
17607 })
17608 });
17609
17610 cx.emit(EditorEvent::InputHandled {
17611 utf16_range_to_replace: range_to_replace,
17612 text: text.into(),
17613 });
17614
17615 if let Some(ranges) = ranges_to_replace {
17616 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
17617 }
17618
17619 let marked_ranges = {
17620 let snapshot = this.buffer.read(cx).read(cx);
17621 this.selections
17622 .disjoint_anchors()
17623 .iter()
17624 .map(|selection| {
17625 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
17626 })
17627 .collect::<Vec<_>>()
17628 };
17629
17630 if text.is_empty() {
17631 this.unmark_text(window, cx);
17632 } else {
17633 this.highlight_text::<InputComposition>(
17634 marked_ranges.clone(),
17635 HighlightStyle {
17636 underline: Some(UnderlineStyle {
17637 thickness: px(1.),
17638 color: None,
17639 wavy: false,
17640 }),
17641 ..Default::default()
17642 },
17643 cx,
17644 );
17645 }
17646
17647 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
17648 let use_autoclose = this.use_autoclose;
17649 let use_auto_surround = this.use_auto_surround;
17650 this.set_use_autoclose(false);
17651 this.set_use_auto_surround(false);
17652 this.handle_input(text, window, cx);
17653 this.set_use_autoclose(use_autoclose);
17654 this.set_use_auto_surround(use_auto_surround);
17655
17656 if let Some(new_selected_range) = new_selected_range_utf16 {
17657 let snapshot = this.buffer.read(cx).read(cx);
17658 let new_selected_ranges = marked_ranges
17659 .into_iter()
17660 .map(|marked_range| {
17661 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
17662 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
17663 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
17664 snapshot.clip_offset_utf16(new_start, Bias::Left)
17665 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
17666 })
17667 .collect::<Vec<_>>();
17668
17669 drop(snapshot);
17670 this.change_selections(None, window, cx, |selections| {
17671 selections.select_ranges(new_selected_ranges)
17672 });
17673 }
17674 });
17675
17676 self.ime_transaction = self.ime_transaction.or(transaction);
17677 if let Some(transaction) = self.ime_transaction {
17678 self.buffer.update(cx, |buffer, cx| {
17679 buffer.group_until_transaction(transaction, cx);
17680 });
17681 }
17682
17683 if self.text_highlights::<InputComposition>(cx).is_none() {
17684 self.ime_transaction.take();
17685 }
17686 }
17687
17688 fn bounds_for_range(
17689 &mut self,
17690 range_utf16: Range<usize>,
17691 element_bounds: gpui::Bounds<Pixels>,
17692 window: &mut Window,
17693 cx: &mut Context<Self>,
17694 ) -> Option<gpui::Bounds<Pixels>> {
17695 let text_layout_details = self.text_layout_details(window);
17696 let gpui::Size {
17697 width: em_width,
17698 height: line_height,
17699 } = self.character_size(window);
17700
17701 let snapshot = self.snapshot(window, cx);
17702 let scroll_position = snapshot.scroll_position();
17703 let scroll_left = scroll_position.x * em_width;
17704
17705 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
17706 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
17707 + self.gutter_dimensions.width
17708 + self.gutter_dimensions.margin;
17709 let y = line_height * (start.row().as_f32() - scroll_position.y);
17710
17711 Some(Bounds {
17712 origin: element_bounds.origin + point(x, y),
17713 size: size(em_width, line_height),
17714 })
17715 }
17716
17717 fn character_index_for_point(
17718 &mut self,
17719 point: gpui::Point<Pixels>,
17720 _window: &mut Window,
17721 _cx: &mut Context<Self>,
17722 ) -> Option<usize> {
17723 let position_map = self.last_position_map.as_ref()?;
17724 if !position_map.text_hitbox.contains(&point) {
17725 return None;
17726 }
17727 let display_point = position_map.point_for_position(point).previous_valid;
17728 let anchor = position_map
17729 .snapshot
17730 .display_point_to_anchor(display_point, Bias::Left);
17731 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
17732 Some(utf16_offset.0)
17733 }
17734}
17735
17736trait SelectionExt {
17737 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
17738 fn spanned_rows(
17739 &self,
17740 include_end_if_at_line_start: bool,
17741 map: &DisplaySnapshot,
17742 ) -> Range<MultiBufferRow>;
17743}
17744
17745impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
17746 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
17747 let start = self
17748 .start
17749 .to_point(&map.buffer_snapshot)
17750 .to_display_point(map);
17751 let end = self
17752 .end
17753 .to_point(&map.buffer_snapshot)
17754 .to_display_point(map);
17755 if self.reversed {
17756 end..start
17757 } else {
17758 start..end
17759 }
17760 }
17761
17762 fn spanned_rows(
17763 &self,
17764 include_end_if_at_line_start: bool,
17765 map: &DisplaySnapshot,
17766 ) -> Range<MultiBufferRow> {
17767 let start = self.start.to_point(&map.buffer_snapshot);
17768 let mut end = self.end.to_point(&map.buffer_snapshot);
17769 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
17770 end.row -= 1;
17771 }
17772
17773 let buffer_start = map.prev_line_boundary(start).0;
17774 let buffer_end = map.next_line_boundary(end).0;
17775 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
17776 }
17777}
17778
17779impl<T: InvalidationRegion> InvalidationStack<T> {
17780 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
17781 where
17782 S: Clone + ToOffset,
17783 {
17784 while let Some(region) = self.last() {
17785 let all_selections_inside_invalidation_ranges =
17786 if selections.len() == region.ranges().len() {
17787 selections
17788 .iter()
17789 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
17790 .all(|(selection, invalidation_range)| {
17791 let head = selection.head().to_offset(buffer);
17792 invalidation_range.start <= head && invalidation_range.end >= head
17793 })
17794 } else {
17795 false
17796 };
17797
17798 if all_selections_inside_invalidation_ranges {
17799 break;
17800 } else {
17801 self.pop();
17802 }
17803 }
17804 }
17805}
17806
17807impl<T> Default for InvalidationStack<T> {
17808 fn default() -> Self {
17809 Self(Default::default())
17810 }
17811}
17812
17813impl<T> Deref for InvalidationStack<T> {
17814 type Target = Vec<T>;
17815
17816 fn deref(&self) -> &Self::Target {
17817 &self.0
17818 }
17819}
17820
17821impl<T> DerefMut for InvalidationStack<T> {
17822 fn deref_mut(&mut self) -> &mut Self::Target {
17823 &mut self.0
17824 }
17825}
17826
17827impl InvalidationRegion for SnippetState {
17828 fn ranges(&self) -> &[Range<Anchor>] {
17829 &self.ranges[self.active_index]
17830 }
17831}
17832
17833pub fn diagnostic_block_renderer(
17834 diagnostic: Diagnostic,
17835 max_message_rows: Option<u8>,
17836 allow_closing: bool,
17837) -> RenderBlock {
17838 let (text_without_backticks, code_ranges) =
17839 highlight_diagnostic_message(&diagnostic, max_message_rows);
17840
17841 Arc::new(move |cx: &mut BlockContext| {
17842 let group_id: SharedString = cx.block_id.to_string().into();
17843
17844 let mut text_style = cx.window.text_style().clone();
17845 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
17846 let theme_settings = ThemeSettings::get_global(cx);
17847 text_style.font_family = theme_settings.buffer_font.family.clone();
17848 text_style.font_style = theme_settings.buffer_font.style;
17849 text_style.font_features = theme_settings.buffer_font.features.clone();
17850 text_style.font_weight = theme_settings.buffer_font.weight;
17851
17852 let multi_line_diagnostic = diagnostic.message.contains('\n');
17853
17854 let buttons = |diagnostic: &Diagnostic| {
17855 if multi_line_diagnostic {
17856 v_flex()
17857 } else {
17858 h_flex()
17859 }
17860 .when(allow_closing, |div| {
17861 div.children(diagnostic.is_primary.then(|| {
17862 IconButton::new("close-block", IconName::XCircle)
17863 .icon_color(Color::Muted)
17864 .size(ButtonSize::Compact)
17865 .style(ButtonStyle::Transparent)
17866 .visible_on_hover(group_id.clone())
17867 .on_click(move |_click, window, cx| {
17868 window.dispatch_action(Box::new(Cancel), cx)
17869 })
17870 .tooltip(|window, cx| {
17871 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
17872 })
17873 }))
17874 })
17875 .child(
17876 IconButton::new("copy-block", IconName::Copy)
17877 .icon_color(Color::Muted)
17878 .size(ButtonSize::Compact)
17879 .style(ButtonStyle::Transparent)
17880 .visible_on_hover(group_id.clone())
17881 .on_click({
17882 let message = diagnostic.message.clone();
17883 move |_click, _, cx| {
17884 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
17885 }
17886 })
17887 .tooltip(Tooltip::text("Copy diagnostic message")),
17888 )
17889 };
17890
17891 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
17892 AvailableSpace::min_size(),
17893 cx.window,
17894 cx.app,
17895 );
17896
17897 h_flex()
17898 .id(cx.block_id)
17899 .group(group_id.clone())
17900 .relative()
17901 .size_full()
17902 .block_mouse_down()
17903 .pl(cx.gutter_dimensions.width)
17904 .w(cx.max_width - cx.gutter_dimensions.full_width())
17905 .child(
17906 div()
17907 .flex()
17908 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
17909 .flex_shrink(),
17910 )
17911 .child(buttons(&diagnostic))
17912 .child(div().flex().flex_shrink_0().child(
17913 StyledText::new(text_without_backticks.clone()).with_highlights(
17914 &text_style,
17915 code_ranges.iter().map(|range| {
17916 (
17917 range.clone(),
17918 HighlightStyle {
17919 font_weight: Some(FontWeight::BOLD),
17920 ..Default::default()
17921 },
17922 )
17923 }),
17924 ),
17925 ))
17926 .into_any_element()
17927 })
17928}
17929
17930fn inline_completion_edit_text(
17931 current_snapshot: &BufferSnapshot,
17932 edits: &[(Range<Anchor>, String)],
17933 edit_preview: &EditPreview,
17934 include_deletions: bool,
17935 cx: &App,
17936) -> HighlightedText {
17937 let edits = edits
17938 .iter()
17939 .map(|(anchor, text)| {
17940 (
17941 anchor.start.text_anchor..anchor.end.text_anchor,
17942 text.clone(),
17943 )
17944 })
17945 .collect::<Vec<_>>();
17946
17947 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
17948}
17949
17950pub fn highlight_diagnostic_message(
17951 diagnostic: &Diagnostic,
17952 mut max_message_rows: Option<u8>,
17953) -> (SharedString, Vec<Range<usize>>) {
17954 let mut text_without_backticks = String::new();
17955 let mut code_ranges = Vec::new();
17956
17957 if let Some(source) = &diagnostic.source {
17958 text_without_backticks.push_str(source);
17959 code_ranges.push(0..source.len());
17960 text_without_backticks.push_str(": ");
17961 }
17962
17963 let mut prev_offset = 0;
17964 let mut in_code_block = false;
17965 let has_row_limit = max_message_rows.is_some();
17966 let mut newline_indices = diagnostic
17967 .message
17968 .match_indices('\n')
17969 .filter(|_| has_row_limit)
17970 .map(|(ix, _)| ix)
17971 .fuse()
17972 .peekable();
17973
17974 for (quote_ix, _) in diagnostic
17975 .message
17976 .match_indices('`')
17977 .chain([(diagnostic.message.len(), "")])
17978 {
17979 let mut first_newline_ix = None;
17980 let mut last_newline_ix = None;
17981 while let Some(newline_ix) = newline_indices.peek() {
17982 if *newline_ix < quote_ix {
17983 if first_newline_ix.is_none() {
17984 first_newline_ix = Some(*newline_ix);
17985 }
17986 last_newline_ix = Some(*newline_ix);
17987
17988 if let Some(rows_left) = &mut max_message_rows {
17989 if *rows_left == 0 {
17990 break;
17991 } else {
17992 *rows_left -= 1;
17993 }
17994 }
17995 let _ = newline_indices.next();
17996 } else {
17997 break;
17998 }
17999 }
18000 let prev_len = text_without_backticks.len();
18001 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18002 text_without_backticks.push_str(new_text);
18003 if in_code_block {
18004 code_ranges.push(prev_len..text_without_backticks.len());
18005 }
18006 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18007 in_code_block = !in_code_block;
18008 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18009 text_without_backticks.push_str("...");
18010 break;
18011 }
18012 }
18013
18014 (text_without_backticks.into(), code_ranges)
18015}
18016
18017fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18018 match severity {
18019 DiagnosticSeverity::ERROR => colors.error,
18020 DiagnosticSeverity::WARNING => colors.warning,
18021 DiagnosticSeverity::INFORMATION => colors.info,
18022 DiagnosticSeverity::HINT => colors.info,
18023 _ => colors.ignored,
18024 }
18025}
18026
18027pub fn styled_runs_for_code_label<'a>(
18028 label: &'a CodeLabel,
18029 syntax_theme: &'a theme::SyntaxTheme,
18030) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18031 let fade_out = HighlightStyle {
18032 fade_out: Some(0.35),
18033 ..Default::default()
18034 };
18035
18036 let mut prev_end = label.filter_range.end;
18037 label
18038 .runs
18039 .iter()
18040 .enumerate()
18041 .flat_map(move |(ix, (range, highlight_id))| {
18042 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18043 style
18044 } else {
18045 return Default::default();
18046 };
18047 let mut muted_style = style;
18048 muted_style.highlight(fade_out);
18049
18050 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18051 if range.start >= label.filter_range.end {
18052 if range.start > prev_end {
18053 runs.push((prev_end..range.start, fade_out));
18054 }
18055 runs.push((range.clone(), muted_style));
18056 } else if range.end <= label.filter_range.end {
18057 runs.push((range.clone(), style));
18058 } else {
18059 runs.push((range.start..label.filter_range.end, style));
18060 runs.push((label.filter_range.end..range.end, muted_style));
18061 }
18062 prev_end = cmp::max(prev_end, range.end);
18063
18064 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18065 runs.push((prev_end..label.text.len(), fade_out));
18066 }
18067
18068 runs
18069 })
18070}
18071
18072pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18073 let mut prev_index = 0;
18074 let mut prev_codepoint: Option<char> = None;
18075 text.char_indices()
18076 .chain([(text.len(), '\0')])
18077 .filter_map(move |(index, codepoint)| {
18078 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18079 let is_boundary = index == text.len()
18080 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18081 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18082 if is_boundary {
18083 let chunk = &text[prev_index..index];
18084 prev_index = index;
18085 Some(chunk)
18086 } else {
18087 None
18088 }
18089 })
18090}
18091
18092pub trait RangeToAnchorExt: Sized {
18093 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18094
18095 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18096 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18097 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18098 }
18099}
18100
18101impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18102 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18103 let start_offset = self.start.to_offset(snapshot);
18104 let end_offset = self.end.to_offset(snapshot);
18105 if start_offset == end_offset {
18106 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18107 } else {
18108 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18109 }
18110 }
18111}
18112
18113pub trait RowExt {
18114 fn as_f32(&self) -> f32;
18115
18116 fn next_row(&self) -> Self;
18117
18118 fn previous_row(&self) -> Self;
18119
18120 fn minus(&self, other: Self) -> u32;
18121}
18122
18123impl RowExt for DisplayRow {
18124 fn as_f32(&self) -> f32 {
18125 self.0 as f32
18126 }
18127
18128 fn next_row(&self) -> Self {
18129 Self(self.0 + 1)
18130 }
18131
18132 fn previous_row(&self) -> Self {
18133 Self(self.0.saturating_sub(1))
18134 }
18135
18136 fn minus(&self, other: Self) -> u32 {
18137 self.0 - other.0
18138 }
18139}
18140
18141impl RowExt for MultiBufferRow {
18142 fn as_f32(&self) -> f32 {
18143 self.0 as f32
18144 }
18145
18146 fn next_row(&self) -> Self {
18147 Self(self.0 + 1)
18148 }
18149
18150 fn previous_row(&self) -> Self {
18151 Self(self.0.saturating_sub(1))
18152 }
18153
18154 fn minus(&self, other: Self) -> u32 {
18155 self.0 - other.0
18156 }
18157}
18158
18159trait RowRangeExt {
18160 type Row;
18161
18162 fn len(&self) -> usize;
18163
18164 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18165}
18166
18167impl RowRangeExt for Range<MultiBufferRow> {
18168 type Row = MultiBufferRow;
18169
18170 fn len(&self) -> usize {
18171 (self.end.0 - self.start.0) as usize
18172 }
18173
18174 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18175 (self.start.0..self.end.0).map(MultiBufferRow)
18176 }
18177}
18178
18179impl RowRangeExt for Range<DisplayRow> {
18180 type Row = DisplayRow;
18181
18182 fn len(&self) -> usize {
18183 (self.end.0 - self.start.0) as usize
18184 }
18185
18186 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18187 (self.start.0..self.end.0).map(DisplayRow)
18188 }
18189}
18190
18191/// If select range has more than one line, we
18192/// just point the cursor to range.start.
18193fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18194 if range.start.row == range.end.row {
18195 range
18196 } else {
18197 range.start..range.start
18198 }
18199}
18200pub struct KillRing(ClipboardItem);
18201impl Global for KillRing {}
18202
18203const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18204
18205fn all_edits_insertions_or_deletions(
18206 edits: &Vec<(Range<Anchor>, String)>,
18207 snapshot: &MultiBufferSnapshot,
18208) -> bool {
18209 let mut all_insertions = true;
18210 let mut all_deletions = true;
18211
18212 for (range, new_text) in edits.iter() {
18213 let range_is_empty = range.to_offset(&snapshot).is_empty();
18214 let text_is_empty = new_text.is_empty();
18215
18216 if range_is_empty != text_is_empty {
18217 if range_is_empty {
18218 all_deletions = false;
18219 } else {
18220 all_insertions = false;
18221 }
18222 } else {
18223 return false;
18224 }
18225
18226 if !all_insertions && !all_deletions {
18227 return false;
18228 }
18229 }
18230 all_insertions || all_deletions
18231}