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 jsx_tag_auto_close;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51pub(crate) use actions::*;
52pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use buffer_diff::DiffHunkStatus;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{
72 future::{self, Shared},
73 FutureExt,
74};
75use fuzzy::StringMatchCandidate;
76
77use ::git::Restore;
78use code_context_menus::{
79 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
80 CompletionsMenu, ContextMenuOrigin,
81};
82use git::blame::GitBlame;
83use gpui::{
84 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
85 AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
86 ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity, EntityInputHandler,
87 EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
88 HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
89 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
90 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
91 WeakEntity, WeakFocusHandle, Window,
92};
93use highlight_matching_bracket::refresh_matching_bracket_highlights;
94use hover_popover::{hide_hover, HoverState};
95use indent_guides::ActiveIndentGuidesState;
96use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
97pub use inline_completion::Direction;
98use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
99pub use items::MAX_TAB_TITLE_LEN;
100use itertools::Itertools;
101use language::{
102 language_settings::{
103 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
104 WordsCompletionMode,
105 },
106 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
107 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
108 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
109 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
110};
111use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
112use linked_editing_ranges::refresh_linked_ranges;
113use mouse_context_menu::MouseContextMenu;
114use persistence::DB;
115pub use proposed_changes_editor::{
116 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
117};
118use smallvec::smallvec;
119use std::iter::Peekable;
120use task::{ResolvedTask, TaskTemplate, TaskVariables};
121
122use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
123pub use lsp::CompletionContext;
124use lsp::{
125 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
126 InsertTextFormat, LanguageServerId, LanguageServerName,
127};
128
129use language::BufferSnapshot;
130use movement::TextLayoutDetails;
131pub use multi_buffer::{
132 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
133 ToOffset, ToPoint,
134};
135use multi_buffer::{
136 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
137 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
138};
139use project::{
140 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
141 project_settings::{GitGutterSetting, ProjectSettings},
142 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
143 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
144 TaskSourceKind,
145};
146use rand::prelude::*;
147use rpc::{proto::*, ErrorExt};
148use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
149use selections_collection::{
150 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
151};
152use serde::{Deserialize, Serialize};
153use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
154use smallvec::SmallVec;
155use snippet::Snippet;
156use std::{
157 any::TypeId,
158 borrow::Cow,
159 cell::RefCell,
160 cmp::{self, Ordering, Reverse},
161 mem,
162 num::NonZeroU32,
163 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
164 path::{Path, PathBuf},
165 rc::Rc,
166 sync::Arc,
167 time::{Duration, Instant},
168};
169pub use sum_tree::Bias;
170use sum_tree::TreeMap;
171use text::{BufferId, OffsetUtf16, Rope};
172use theme::{
173 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
174 ThemeColors, ThemeSettings,
175};
176use ui::{
177 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
178 Tooltip,
179};
180use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
181use workspace::{
182 item::{ItemHandle, PreviewTabsSettings},
183 ItemId, RestoreOnStartupBehavior,
184};
185use workspace::{
186 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
187 WorkspaceSettings,
188};
189use workspace::{
190 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
191};
192use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
193
194use crate::hover_links::{find_url, find_url_from_range};
195use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
196
197pub const FILE_HEADER_HEIGHT: u32 = 2;
198pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
199pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
200pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
201const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
202const MAX_LINE_LEN: usize = 1024;
203const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
204const MAX_SELECTION_HISTORY_LEN: usize = 1024;
205pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
206#[doc(hidden)]
207pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
208
209pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
210pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
211pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
212
213pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
214pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
215
216const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
217 alt: true,
218 shift: true,
219 control: false,
220 platform: false,
221 function: false,
222};
223
224#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
225pub enum InlayId {
226 InlineCompletion(usize),
227 Hint(usize),
228}
229
230impl InlayId {
231 fn id(&self) -> usize {
232 match self {
233 Self::InlineCompletion(id) => *id,
234 Self::Hint(id) => *id,
235 }
236 }
237}
238
239enum DocumentHighlightRead {}
240enum DocumentHighlightWrite {}
241enum InputComposition {}
242enum SelectedTextHighlight {}
243
244#[derive(Debug, Copy, Clone, PartialEq, Eq)]
245pub enum Navigated {
246 Yes,
247 No,
248}
249
250impl Navigated {
251 pub fn from_bool(yes: bool) -> Navigated {
252 if yes {
253 Navigated::Yes
254 } else {
255 Navigated::No
256 }
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
261enum DisplayDiffHunk {
262 Folded {
263 display_row: DisplayRow,
264 },
265 Unfolded {
266 is_created_file: bool,
267 diff_base_byte_range: Range<usize>,
268 display_row_range: Range<DisplayRow>,
269 multi_buffer_range: Range<Anchor>,
270 status: DiffHunkStatus,
271 },
272}
273
274pub fn init_settings(cx: &mut App) {
275 EditorSettings::register(cx);
276}
277
278pub fn init(cx: &mut App) {
279 init_settings(cx);
280
281 workspace::register_project_item::<Editor>(cx);
282 workspace::FollowableViewRegistry::register::<Editor>(cx);
283 workspace::register_serializable_item::<Editor>(cx);
284
285 cx.observe_new(
286 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
287 workspace.register_action(Editor::new_file);
288 workspace.register_action(Editor::new_file_vertical);
289 workspace.register_action(Editor::new_file_horizontal);
290 workspace.register_action(Editor::cancel_language_server_work);
291 },
292 )
293 .detach();
294
295 cx.on_action(move |_: &workspace::NewFile, cx| {
296 let app_state = workspace::AppState::global(cx);
297 if let Some(app_state) = app_state.upgrade() {
298 workspace::open_new(
299 Default::default(),
300 app_state,
301 cx,
302 |workspace, window, cx| {
303 Editor::new_file(workspace, &Default::default(), window, cx)
304 },
305 )
306 .detach();
307 }
308 });
309 cx.on_action(move |_: &workspace::NewWindow, cx| {
310 let app_state = workspace::AppState::global(cx);
311 if let Some(app_state) = app_state.upgrade() {
312 workspace::open_new(
313 Default::default(),
314 app_state,
315 cx,
316 |workspace, window, cx| {
317 cx.activate(true);
318 Editor::new_file(workspace, &Default::default(), window, cx)
319 },
320 )
321 .detach();
322 }
323 });
324}
325
326pub struct SearchWithinRange;
327
328trait InvalidationRegion {
329 fn ranges(&self) -> &[Range<Anchor>];
330}
331
332#[derive(Clone, Debug, PartialEq)]
333pub enum SelectPhase {
334 Begin {
335 position: DisplayPoint,
336 add: bool,
337 click_count: usize,
338 },
339 BeginColumnar {
340 position: DisplayPoint,
341 reset: bool,
342 goal_column: u32,
343 },
344 Extend {
345 position: DisplayPoint,
346 click_count: usize,
347 },
348 Update {
349 position: DisplayPoint,
350 goal_column: u32,
351 scroll_delta: gpui::Point<f32>,
352 },
353 End,
354}
355
356#[derive(Clone, Debug)]
357pub enum SelectMode {
358 Character,
359 Word(Range<Anchor>),
360 Line(Range<Anchor>),
361 All,
362}
363
364#[derive(Copy, Clone, PartialEq, Eq, Debug)]
365pub enum EditorMode {
366 SingleLine { auto_width: bool },
367 AutoHeight { max_lines: usize },
368 Full,
369}
370
371#[derive(Copy, Clone, Debug)]
372pub enum SoftWrap {
373 /// Prefer not to wrap at all.
374 ///
375 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
376 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
377 GitDiff,
378 /// Prefer a single line generally, unless an overly long line is encountered.
379 None,
380 /// Soft wrap lines that exceed the editor width.
381 EditorWidth,
382 /// Soft wrap lines at the preferred line length.
383 Column(u32),
384 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
385 Bounded(u32),
386}
387
388#[derive(Clone)]
389pub struct EditorStyle {
390 pub background: Hsla,
391 pub local_player: PlayerColor,
392 pub text: TextStyle,
393 pub scrollbar_width: Pixels,
394 pub syntax: Arc<SyntaxTheme>,
395 pub status: StatusColors,
396 pub inlay_hints_style: HighlightStyle,
397 pub inline_completion_styles: InlineCompletionStyles,
398 pub unnecessary_code_fade: f32,
399}
400
401impl Default for EditorStyle {
402 fn default() -> Self {
403 Self {
404 background: Hsla::default(),
405 local_player: PlayerColor::default(),
406 text: TextStyle::default(),
407 scrollbar_width: Pixels::default(),
408 syntax: Default::default(),
409 // HACK: Status colors don't have a real default.
410 // We should look into removing the status colors from the editor
411 // style and retrieve them directly from the theme.
412 status: StatusColors::dark(),
413 inlay_hints_style: HighlightStyle::default(),
414 inline_completion_styles: InlineCompletionStyles {
415 insertion: HighlightStyle::default(),
416 whitespace: HighlightStyle::default(),
417 },
418 unnecessary_code_fade: Default::default(),
419 }
420 }
421}
422
423pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
424 let show_background = language_settings::language_settings(None, None, cx)
425 .inlay_hints
426 .show_background;
427
428 HighlightStyle {
429 color: Some(cx.theme().status().hint),
430 background_color: show_background.then(|| cx.theme().status().hint_background),
431 ..HighlightStyle::default()
432 }
433}
434
435pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
436 InlineCompletionStyles {
437 insertion: HighlightStyle {
438 color: Some(cx.theme().status().predictive),
439 ..HighlightStyle::default()
440 },
441 whitespace: HighlightStyle {
442 background_color: Some(cx.theme().status().created_background),
443 ..HighlightStyle::default()
444 },
445 }
446}
447
448type CompletionId = usize;
449
450pub(crate) enum EditDisplayMode {
451 TabAccept,
452 DiffPopover,
453 Inline,
454}
455
456enum InlineCompletion {
457 Edit {
458 edits: Vec<(Range<Anchor>, String)>,
459 edit_preview: Option<EditPreview>,
460 display_mode: EditDisplayMode,
461 snapshot: BufferSnapshot,
462 },
463 Move {
464 target: Anchor,
465 snapshot: BufferSnapshot,
466 },
467}
468
469struct InlineCompletionState {
470 inlay_ids: Vec<InlayId>,
471 completion: InlineCompletion,
472 completion_id: Option<SharedString>,
473 invalidation_range: Range<Anchor>,
474}
475
476enum EditPredictionSettings {
477 Disabled,
478 Enabled {
479 show_in_menu: bool,
480 preview_requires_modifier: bool,
481 },
482}
483
484enum InlineCompletionHighlight {}
485
486#[derive(Debug, Clone)]
487struct InlineDiagnostic {
488 message: SharedString,
489 group_id: usize,
490 is_primary: bool,
491 start: Point,
492 severity: DiagnosticSeverity,
493}
494
495pub enum MenuInlineCompletionsPolicy {
496 Never,
497 ByProvider,
498}
499
500pub enum EditPredictionPreview {
501 /// Modifier is not pressed
502 Inactive { released_too_fast: bool },
503 /// Modifier pressed
504 Active {
505 since: Instant,
506 previous_scroll_position: Option<ScrollAnchor>,
507 },
508}
509
510impl EditPredictionPreview {
511 pub fn released_too_fast(&self) -> bool {
512 match self {
513 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
514 EditPredictionPreview::Active { .. } => false,
515 }
516 }
517
518 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
519 if let EditPredictionPreview::Active {
520 previous_scroll_position,
521 ..
522 } = self
523 {
524 *previous_scroll_position = scroll_position;
525 }
526 }
527}
528
529#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
530struct EditorActionId(usize);
531
532impl EditorActionId {
533 pub fn post_inc(&mut self) -> Self {
534 let answer = self.0;
535
536 *self = Self(answer + 1);
537
538 Self(answer)
539 }
540}
541
542// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
543// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
544
545type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
546type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
547
548#[derive(Default)]
549struct ScrollbarMarkerState {
550 scrollbar_size: Size<Pixels>,
551 dirty: bool,
552 markers: Arc<[PaintQuad]>,
553 pending_refresh: Option<Task<Result<()>>>,
554}
555
556impl ScrollbarMarkerState {
557 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
558 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
559 }
560}
561
562#[derive(Clone, Debug)]
563struct RunnableTasks {
564 templates: Vec<(TaskSourceKind, TaskTemplate)>,
565 offset: multi_buffer::Anchor,
566 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
567 column: u32,
568 // Values of all named captures, including those starting with '_'
569 extra_variables: HashMap<String, String>,
570 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
571 context_range: Range<BufferOffset>,
572}
573
574impl RunnableTasks {
575 fn resolve<'a>(
576 &'a self,
577 cx: &'a task::TaskContext,
578 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
579 self.templates.iter().filter_map(|(kind, template)| {
580 template
581 .resolve_task(&kind.to_id_base(), cx)
582 .map(|task| (kind.clone(), task))
583 })
584 }
585}
586
587#[derive(Clone)]
588struct ResolvedTasks {
589 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
590 position: Anchor,
591}
592#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
593struct BufferOffset(usize);
594
595// Addons allow storing per-editor state in other crates (e.g. Vim)
596pub trait Addon: 'static {
597 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
598
599 fn render_buffer_header_controls(
600 &self,
601 _: &ExcerptInfo,
602 _: &Window,
603 _: &App,
604 ) -> Option<AnyElement> {
605 None
606 }
607
608 fn to_any(&self) -> &dyn std::any::Any;
609}
610
611/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
612///
613/// See the [module level documentation](self) for more information.
614pub struct Editor {
615 focus_handle: FocusHandle,
616 last_focused_descendant: Option<WeakFocusHandle>,
617 /// The text buffer being edited
618 buffer: Entity<MultiBuffer>,
619 /// Map of how text in the buffer should be displayed.
620 /// Handles soft wraps, folds, fake inlay text insertions, etc.
621 pub display_map: Entity<DisplayMap>,
622 pub selections: SelectionsCollection,
623 pub scroll_manager: ScrollManager,
624 /// When inline assist editors are linked, they all render cursors because
625 /// typing enters text into each of them, even the ones that aren't focused.
626 pub(crate) show_cursor_when_unfocused: bool,
627 columnar_selection_tail: Option<Anchor>,
628 add_selections_state: Option<AddSelectionsState>,
629 select_next_state: Option<SelectNextState>,
630 select_prev_state: Option<SelectNextState>,
631 selection_history: SelectionHistory,
632 autoclose_regions: Vec<AutocloseRegion>,
633 snippet_stack: InvalidationStack<SnippetState>,
634 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
635 ime_transaction: Option<TransactionId>,
636 active_diagnostics: Option<ActiveDiagnosticGroup>,
637 show_inline_diagnostics: bool,
638 inline_diagnostics_update: Task<()>,
639 inline_diagnostics_enabled: bool,
640 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
641 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
642 hard_wrap: Option<usize>,
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 jsx_tag_auto_close_enabled_in_any_buffer: bool,
727 show_git_blame_gutter: bool,
728 show_git_blame_inline: bool,
729 show_git_blame_inline_delay_task: Option<Task<()>>,
730 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
731 git_blame_inline_enabled: bool,
732 serialize_dirty_buffers: bool,
733 show_selection_menu: Option<bool>,
734 blame: Option<Entity<GitBlame>>,
735 blame_subscription: Option<Subscription>,
736 custom_context_menu: Option<
737 Box<
738 dyn 'static
739 + Fn(
740 &mut Self,
741 DisplayPoint,
742 &mut Window,
743 &mut Context<Self>,
744 ) -> Option<Entity<ui::ContextMenu>>,
745 >,
746 >,
747 last_bounds: Option<Bounds<Pixels>>,
748 last_position_map: Option<Rc<PositionMap>>,
749 expect_bounds_change: Option<Bounds<Pixels>>,
750 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
751 tasks_update_task: Option<Task<()>>,
752 in_project_search: bool,
753 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
754 breadcrumb_header: Option<String>,
755 focused_block: Option<FocusedBlock>,
756 next_scroll_position: NextScrollCursorCenterTopBottom,
757 addons: HashMap<TypeId, Box<dyn Addon>>,
758 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
759 load_diff_task: Option<Shared<Task<()>>>,
760 selection_mark_mode: bool,
761 toggle_fold_multiple_buffers: Task<()>,
762 _scroll_cursor_center_top_bottom_task: Task<()>,
763 serialize_selections: Task<()>,
764}
765
766#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
767enum NextScrollCursorCenterTopBottom {
768 #[default]
769 Center,
770 Top,
771 Bottom,
772}
773
774impl NextScrollCursorCenterTopBottom {
775 fn next(&self) -> Self {
776 match self {
777 Self::Center => Self::Top,
778 Self::Top => Self::Bottom,
779 Self::Bottom => Self::Center,
780 }
781 }
782}
783
784#[derive(Clone)]
785pub struct EditorSnapshot {
786 pub mode: EditorMode,
787 show_gutter: bool,
788 show_line_numbers: Option<bool>,
789 show_git_diff_gutter: Option<bool>,
790 show_code_actions: Option<bool>,
791 show_runnables: Option<bool>,
792 git_blame_gutter_max_author_length: Option<usize>,
793 pub display_snapshot: DisplaySnapshot,
794 pub placeholder_text: Option<Arc<str>>,
795 is_focused: bool,
796 scroll_anchor: ScrollAnchor,
797 ongoing_scroll: OngoingScroll,
798 current_line_highlight: CurrentLineHighlight,
799 gutter_hovered: bool,
800}
801
802const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
803
804#[derive(Default, Debug, Clone, Copy)]
805pub struct GutterDimensions {
806 pub left_padding: Pixels,
807 pub right_padding: Pixels,
808 pub width: Pixels,
809 pub margin: Pixels,
810 pub git_blame_entries_width: Option<Pixels>,
811}
812
813impl GutterDimensions {
814 /// The full width of the space taken up by the gutter.
815 pub fn full_width(&self) -> Pixels {
816 self.margin + self.width
817 }
818
819 /// The width of the space reserved for the fold indicators,
820 /// use alongside 'justify_end' and `gutter_width` to
821 /// right align content with the line numbers
822 pub fn fold_area_width(&self) -> Pixels {
823 self.margin + self.right_padding
824 }
825}
826
827#[derive(Debug)]
828pub struct RemoteSelection {
829 pub replica_id: ReplicaId,
830 pub selection: Selection<Anchor>,
831 pub cursor_shape: CursorShape,
832 pub peer_id: PeerId,
833 pub line_mode: bool,
834 pub participant_index: Option<ParticipantIndex>,
835 pub user_name: Option<SharedString>,
836}
837
838#[derive(Clone, Debug)]
839struct SelectionHistoryEntry {
840 selections: Arc<[Selection<Anchor>]>,
841 select_next_state: Option<SelectNextState>,
842 select_prev_state: Option<SelectNextState>,
843 add_selections_state: Option<AddSelectionsState>,
844}
845
846enum SelectionHistoryMode {
847 Normal,
848 Undoing,
849 Redoing,
850}
851
852#[derive(Clone, PartialEq, Eq, Hash)]
853struct HoveredCursor {
854 replica_id: u16,
855 selection_id: usize,
856}
857
858impl Default for SelectionHistoryMode {
859 fn default() -> Self {
860 Self::Normal
861 }
862}
863
864#[derive(Default)]
865struct SelectionHistory {
866 #[allow(clippy::type_complexity)]
867 selections_by_transaction:
868 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
869 mode: SelectionHistoryMode,
870 undo_stack: VecDeque<SelectionHistoryEntry>,
871 redo_stack: VecDeque<SelectionHistoryEntry>,
872}
873
874impl SelectionHistory {
875 fn insert_transaction(
876 &mut self,
877 transaction_id: TransactionId,
878 selections: Arc<[Selection<Anchor>]>,
879 ) {
880 self.selections_by_transaction
881 .insert(transaction_id, (selections, None));
882 }
883
884 #[allow(clippy::type_complexity)]
885 fn transaction(
886 &self,
887 transaction_id: TransactionId,
888 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
889 self.selections_by_transaction.get(&transaction_id)
890 }
891
892 #[allow(clippy::type_complexity)]
893 fn transaction_mut(
894 &mut self,
895 transaction_id: TransactionId,
896 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
897 self.selections_by_transaction.get_mut(&transaction_id)
898 }
899
900 fn push(&mut self, entry: SelectionHistoryEntry) {
901 if !entry.selections.is_empty() {
902 match self.mode {
903 SelectionHistoryMode::Normal => {
904 self.push_undo(entry);
905 self.redo_stack.clear();
906 }
907 SelectionHistoryMode::Undoing => self.push_redo(entry),
908 SelectionHistoryMode::Redoing => self.push_undo(entry),
909 }
910 }
911 }
912
913 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
914 if self
915 .undo_stack
916 .back()
917 .map_or(true, |e| e.selections != entry.selections)
918 {
919 self.undo_stack.push_back(entry);
920 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
921 self.undo_stack.pop_front();
922 }
923 }
924 }
925
926 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
927 if self
928 .redo_stack
929 .back()
930 .map_or(true, |e| e.selections != entry.selections)
931 {
932 self.redo_stack.push_back(entry);
933 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
934 self.redo_stack.pop_front();
935 }
936 }
937 }
938}
939
940struct RowHighlight {
941 index: usize,
942 range: Range<Anchor>,
943 color: Hsla,
944 should_autoscroll: bool,
945}
946
947#[derive(Clone, Debug)]
948struct AddSelectionsState {
949 above: bool,
950 stack: Vec<usize>,
951}
952
953#[derive(Clone)]
954struct SelectNextState {
955 query: AhoCorasick,
956 wordwise: bool,
957 done: bool,
958}
959
960impl std::fmt::Debug for SelectNextState {
961 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
962 f.debug_struct(std::any::type_name::<Self>())
963 .field("wordwise", &self.wordwise)
964 .field("done", &self.done)
965 .finish()
966 }
967}
968
969#[derive(Debug)]
970struct AutocloseRegion {
971 selection_id: usize,
972 range: Range<Anchor>,
973 pair: BracketPair,
974}
975
976#[derive(Debug)]
977struct SnippetState {
978 ranges: Vec<Vec<Range<Anchor>>>,
979 active_index: usize,
980 choices: Vec<Option<Vec<String>>>,
981}
982
983#[doc(hidden)]
984pub struct RenameState {
985 pub range: Range<Anchor>,
986 pub old_name: Arc<str>,
987 pub editor: Entity<Editor>,
988 block_id: CustomBlockId,
989}
990
991struct InvalidationStack<T>(Vec<T>);
992
993struct RegisteredInlineCompletionProvider {
994 provider: Arc<dyn InlineCompletionProviderHandle>,
995 _subscription: Subscription,
996}
997
998#[derive(Debug, PartialEq, Eq)]
999struct ActiveDiagnosticGroup {
1000 primary_range: Range<Anchor>,
1001 primary_message: String,
1002 group_id: usize,
1003 blocks: HashMap<CustomBlockId, Diagnostic>,
1004 is_valid: bool,
1005}
1006
1007#[derive(Serialize, Deserialize, Clone, Debug)]
1008pub struct ClipboardSelection {
1009 /// The number of bytes in this selection.
1010 pub len: usize,
1011 /// Whether this was a full-line selection.
1012 pub is_entire_line: bool,
1013 /// The indentation of the first line when this content was originally copied.
1014 pub first_line_indent: u32,
1015}
1016
1017#[derive(Debug)]
1018pub(crate) struct NavigationData {
1019 cursor_anchor: Anchor,
1020 cursor_position: Point,
1021 scroll_anchor: ScrollAnchor,
1022 scroll_top_row: u32,
1023}
1024
1025#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1026pub enum GotoDefinitionKind {
1027 Symbol,
1028 Declaration,
1029 Type,
1030 Implementation,
1031}
1032
1033#[derive(Debug, Clone)]
1034enum InlayHintRefreshReason {
1035 ModifiersChanged(bool),
1036 Toggle(bool),
1037 SettingsChange(InlayHintSettings),
1038 NewLinesShown,
1039 BufferEdited(HashSet<Arc<Language>>),
1040 RefreshRequested,
1041 ExcerptsRemoved(Vec<ExcerptId>),
1042}
1043
1044impl InlayHintRefreshReason {
1045 fn description(&self) -> &'static str {
1046 match self {
1047 Self::ModifiersChanged(_) => "modifiers changed",
1048 Self::Toggle(_) => "toggle",
1049 Self::SettingsChange(_) => "settings change",
1050 Self::NewLinesShown => "new lines shown",
1051 Self::BufferEdited(_) => "buffer edited",
1052 Self::RefreshRequested => "refresh requested",
1053 Self::ExcerptsRemoved(_) => "excerpts removed",
1054 }
1055 }
1056}
1057
1058pub enum FormatTarget {
1059 Buffers,
1060 Ranges(Vec<Range<MultiBufferPoint>>),
1061}
1062
1063pub(crate) struct FocusedBlock {
1064 id: BlockId,
1065 focus_handle: WeakFocusHandle,
1066}
1067
1068#[derive(Clone)]
1069enum JumpData {
1070 MultiBufferRow {
1071 row: MultiBufferRow,
1072 line_offset_from_top: u32,
1073 },
1074 MultiBufferPoint {
1075 excerpt_id: ExcerptId,
1076 position: Point,
1077 anchor: text::Anchor,
1078 line_offset_from_top: u32,
1079 },
1080}
1081
1082pub enum MultibufferSelectionMode {
1083 First,
1084 All,
1085}
1086
1087impl Editor {
1088 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1089 let buffer = cx.new(|cx| Buffer::local("", cx));
1090 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1091 Self::new(
1092 EditorMode::SingleLine { auto_width: false },
1093 buffer,
1094 None,
1095 false,
1096 window,
1097 cx,
1098 )
1099 }
1100
1101 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1102 let buffer = cx.new(|cx| Buffer::local("", cx));
1103 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1104 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1105 }
1106
1107 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1108 let buffer = cx.new(|cx| Buffer::local("", cx));
1109 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1110 Self::new(
1111 EditorMode::SingleLine { auto_width: true },
1112 buffer,
1113 None,
1114 false,
1115 window,
1116 cx,
1117 )
1118 }
1119
1120 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1121 let buffer = cx.new(|cx| Buffer::local("", cx));
1122 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1123 Self::new(
1124 EditorMode::AutoHeight { max_lines },
1125 buffer,
1126 None,
1127 false,
1128 window,
1129 cx,
1130 )
1131 }
1132
1133 pub fn for_buffer(
1134 buffer: Entity<Buffer>,
1135 project: Option<Entity<Project>>,
1136 window: &mut Window,
1137 cx: &mut Context<Self>,
1138 ) -> Self {
1139 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1140 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1141 }
1142
1143 pub fn for_multibuffer(
1144 buffer: Entity<MultiBuffer>,
1145 project: Option<Entity<Project>>,
1146 show_excerpt_controls: bool,
1147 window: &mut Window,
1148 cx: &mut Context<Self>,
1149 ) -> Self {
1150 Self::new(
1151 EditorMode::Full,
1152 buffer,
1153 project,
1154 show_excerpt_controls,
1155 window,
1156 cx,
1157 )
1158 }
1159
1160 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1161 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1162 let mut clone = Self::new(
1163 self.mode,
1164 self.buffer.clone(),
1165 self.project.clone(),
1166 show_excerpt_controls,
1167 window,
1168 cx,
1169 );
1170 self.display_map.update(cx, |display_map, cx| {
1171 let snapshot = display_map.snapshot(cx);
1172 clone.display_map.update(cx, |display_map, cx| {
1173 display_map.set_state(&snapshot, cx);
1174 });
1175 });
1176 clone.selections.clone_state(&self.selections);
1177 clone.scroll_manager.clone_state(&self.scroll_manager);
1178 clone.searchable = self.searchable;
1179 clone
1180 }
1181
1182 pub fn new(
1183 mode: EditorMode,
1184 buffer: Entity<MultiBuffer>,
1185 project: Option<Entity<Project>>,
1186 show_excerpt_controls: bool,
1187 window: &mut Window,
1188 cx: &mut Context<Self>,
1189 ) -> Self {
1190 let style = window.text_style();
1191 let font_size = style.font_size.to_pixels(window.rem_size());
1192 let editor = cx.entity().downgrade();
1193 let fold_placeholder = FoldPlaceholder {
1194 constrain_width: true,
1195 render: Arc::new(move |fold_id, fold_range, cx| {
1196 let editor = editor.clone();
1197 div()
1198 .id(fold_id)
1199 .bg(cx.theme().colors().ghost_element_background)
1200 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1201 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1202 .rounded_xs()
1203 .size_full()
1204 .cursor_pointer()
1205 .child("⋯")
1206 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1207 .on_click(move |_, _window, cx| {
1208 editor
1209 .update(cx, |editor, cx| {
1210 editor.unfold_ranges(
1211 &[fold_range.start..fold_range.end],
1212 true,
1213 false,
1214 cx,
1215 );
1216 cx.stop_propagation();
1217 })
1218 .ok();
1219 })
1220 .into_any()
1221 }),
1222 merge_adjacent: true,
1223 ..Default::default()
1224 };
1225 let display_map = cx.new(|cx| {
1226 DisplayMap::new(
1227 buffer.clone(),
1228 style.font(),
1229 font_size,
1230 None,
1231 show_excerpt_controls,
1232 FILE_HEADER_HEIGHT,
1233 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1234 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1235 fold_placeholder,
1236 cx,
1237 )
1238 });
1239
1240 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1241
1242 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1243
1244 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1245 .then(|| language_settings::SoftWrap::None);
1246
1247 let mut project_subscriptions = Vec::new();
1248 if mode == EditorMode::Full {
1249 if let Some(project) = project.as_ref() {
1250 project_subscriptions.push(cx.subscribe_in(
1251 project,
1252 window,
1253 |editor, _, event, window, cx| {
1254 if let project::Event::RefreshInlayHints = event {
1255 editor
1256 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1257 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1258 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1259 let focus_handle = editor.focus_handle(cx);
1260 if focus_handle.is_focused(window) {
1261 let snapshot = buffer.read(cx).snapshot();
1262 for (range, snippet) in snippet_edits {
1263 let editor_range =
1264 language::range_from_lsp(*range).to_offset(&snapshot);
1265 editor
1266 .insert_snippet(
1267 &[editor_range],
1268 snippet.clone(),
1269 window,
1270 cx,
1271 )
1272 .ok();
1273 }
1274 }
1275 }
1276 }
1277 },
1278 ));
1279 if let Some(task_inventory) = project
1280 .read(cx)
1281 .task_store()
1282 .read(cx)
1283 .task_inventory()
1284 .cloned()
1285 {
1286 project_subscriptions.push(cx.observe_in(
1287 &task_inventory,
1288 window,
1289 |editor, _, window, cx| {
1290 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1291 },
1292 ));
1293 }
1294 }
1295 }
1296
1297 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1298
1299 let inlay_hint_settings =
1300 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1301 let focus_handle = cx.focus_handle();
1302 cx.on_focus(&focus_handle, window, Self::handle_focus)
1303 .detach();
1304 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1305 .detach();
1306 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1307 .detach();
1308 cx.on_blur(&focus_handle, window, Self::handle_blur)
1309 .detach();
1310
1311 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1312 Some(false)
1313 } else {
1314 None
1315 };
1316
1317 let mut code_action_providers = Vec::new();
1318 let mut load_uncommitted_diff = None;
1319 if let Some(project) = project.clone() {
1320 load_uncommitted_diff = Some(
1321 get_uncommitted_diff_for_buffer(
1322 &project,
1323 buffer.read(cx).all_buffers(),
1324 buffer.clone(),
1325 cx,
1326 )
1327 .shared(),
1328 );
1329 code_action_providers.push(Rc::new(project) as Rc<_>);
1330 }
1331
1332 let mut this = Self {
1333 focus_handle,
1334 show_cursor_when_unfocused: false,
1335 last_focused_descendant: None,
1336 buffer: buffer.clone(),
1337 display_map: display_map.clone(),
1338 selections,
1339 scroll_manager: ScrollManager::new(cx),
1340 columnar_selection_tail: None,
1341 add_selections_state: None,
1342 select_next_state: None,
1343 select_prev_state: None,
1344 selection_history: Default::default(),
1345 autoclose_regions: Default::default(),
1346 snippet_stack: Default::default(),
1347 select_larger_syntax_node_stack: Vec::new(),
1348 ime_transaction: Default::default(),
1349 active_diagnostics: None,
1350 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1351 inline_diagnostics_update: Task::ready(()),
1352 inline_diagnostics: Vec::new(),
1353 soft_wrap_mode_override,
1354 hard_wrap: None,
1355 completion_provider: project.clone().map(|project| Box::new(project) as _),
1356 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1357 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1358 project,
1359 blink_manager: blink_manager.clone(),
1360 show_local_selections: true,
1361 show_scrollbars: true,
1362 mode,
1363 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1364 show_gutter: mode == EditorMode::Full,
1365 show_line_numbers: None,
1366 use_relative_line_numbers: None,
1367 show_git_diff_gutter: None,
1368 show_code_actions: None,
1369 show_runnables: None,
1370 show_wrap_guides: None,
1371 show_indent_guides,
1372 placeholder_text: None,
1373 highlight_order: 0,
1374 highlighted_rows: HashMap::default(),
1375 background_highlights: Default::default(),
1376 gutter_highlights: TreeMap::default(),
1377 scrollbar_marker_state: ScrollbarMarkerState::default(),
1378 active_indent_guides_state: ActiveIndentGuidesState::default(),
1379 nav_history: None,
1380 context_menu: RefCell::new(None),
1381 mouse_context_menu: None,
1382 completion_tasks: Default::default(),
1383 signature_help_state: SignatureHelpState::default(),
1384 auto_signature_help: None,
1385 find_all_references_task_sources: Vec::new(),
1386 next_completion_id: 0,
1387 next_inlay_id: 0,
1388 code_action_providers,
1389 available_code_actions: Default::default(),
1390 code_actions_task: Default::default(),
1391 selection_highlight_task: Default::default(),
1392 document_highlights_task: Default::default(),
1393 linked_editing_range_task: Default::default(),
1394 pending_rename: Default::default(),
1395 searchable: true,
1396 cursor_shape: EditorSettings::get_global(cx)
1397 .cursor_shape
1398 .unwrap_or_default(),
1399 current_line_highlight: None,
1400 autoindent_mode: Some(AutoindentMode::EachLine),
1401 collapse_matches: false,
1402 workspace: None,
1403 input_enabled: true,
1404 use_modal_editing: mode == EditorMode::Full,
1405 read_only: false,
1406 use_autoclose: true,
1407 use_auto_surround: true,
1408 auto_replace_emoji_shortcode: false,
1409 jsx_tag_auto_close_enabled_in_any_buffer: false,
1410 leader_peer_id: None,
1411 remote_id: None,
1412 hover_state: Default::default(),
1413 pending_mouse_down: None,
1414 hovered_link_state: Default::default(),
1415 edit_prediction_provider: None,
1416 active_inline_completion: None,
1417 stale_inline_completion_in_menu: None,
1418 edit_prediction_preview: EditPredictionPreview::Inactive {
1419 released_too_fast: false,
1420 },
1421 inline_diagnostics_enabled: mode == EditorMode::Full,
1422 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1423
1424 gutter_hovered: false,
1425 pixel_position_of_newest_cursor: None,
1426 last_bounds: None,
1427 last_position_map: None,
1428 expect_bounds_change: None,
1429 gutter_dimensions: GutterDimensions::default(),
1430 style: None,
1431 show_cursor_names: false,
1432 hovered_cursors: Default::default(),
1433 next_editor_action_id: EditorActionId::default(),
1434 editor_actions: Rc::default(),
1435 inline_completions_hidden_for_vim_mode: false,
1436 show_inline_completions_override: None,
1437 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1438 edit_prediction_settings: EditPredictionSettings::Disabled,
1439 edit_prediction_indent_conflict: false,
1440 edit_prediction_requires_modifier_in_indent_conflict: true,
1441 custom_context_menu: None,
1442 show_git_blame_gutter: false,
1443 show_git_blame_inline: false,
1444 show_selection_menu: None,
1445 show_git_blame_inline_delay_task: None,
1446 git_blame_inline_tooltip: None,
1447 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1448 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1449 .session
1450 .restore_unsaved_buffers,
1451 blame: None,
1452 blame_subscription: None,
1453 tasks: Default::default(),
1454 _subscriptions: vec![
1455 cx.observe(&buffer, Self::on_buffer_changed),
1456 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1457 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1458 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1459 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1460 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1461 cx.observe_window_activation(window, |editor, window, cx| {
1462 let active = window.is_window_active();
1463 editor.blink_manager.update(cx, |blink_manager, cx| {
1464 if active {
1465 blink_manager.enable(cx);
1466 } else {
1467 blink_manager.disable(cx);
1468 }
1469 });
1470 }),
1471 ],
1472 tasks_update_task: None,
1473 linked_edit_ranges: Default::default(),
1474 in_project_search: false,
1475 previous_search_ranges: None,
1476 breadcrumb_header: None,
1477 focused_block: None,
1478 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1479 addons: HashMap::default(),
1480 registered_buffers: HashMap::default(),
1481 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1482 selection_mark_mode: false,
1483 toggle_fold_multiple_buffers: Task::ready(()),
1484 serialize_selections: Task::ready(()),
1485 text_style_refinement: None,
1486 load_diff_task: load_uncommitted_diff,
1487 };
1488 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1489 this._subscriptions.extend(project_subscriptions);
1490
1491 this.end_selection(window, cx);
1492 this.scroll_manager.show_scrollbar(window, cx);
1493 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1494
1495 if mode == EditorMode::Full {
1496 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1497 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1498
1499 if this.git_blame_inline_enabled {
1500 this.git_blame_inline_enabled = true;
1501 this.start_git_blame_inline(false, window, cx);
1502 }
1503
1504 if let Some(buffer) = buffer.read(cx).as_singleton() {
1505 if let Some(project) = this.project.as_ref() {
1506 let handle = project.update(cx, |project, cx| {
1507 project.register_buffer_with_language_servers(&buffer, cx)
1508 });
1509 this.registered_buffers
1510 .insert(buffer.read(cx).remote_id(), handle);
1511 }
1512 }
1513 }
1514
1515 this.report_editor_event("Editor Opened", None, cx);
1516 this
1517 }
1518
1519 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1520 self.mouse_context_menu
1521 .as_ref()
1522 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1523 }
1524
1525 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1526 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1527 }
1528
1529 fn key_context_internal(
1530 &self,
1531 has_active_edit_prediction: bool,
1532 window: &Window,
1533 cx: &App,
1534 ) -> KeyContext {
1535 let mut key_context = KeyContext::new_with_defaults();
1536 key_context.add("Editor");
1537 let mode = match self.mode {
1538 EditorMode::SingleLine { .. } => "single_line",
1539 EditorMode::AutoHeight { .. } => "auto_height",
1540 EditorMode::Full => "full",
1541 };
1542
1543 if EditorSettings::jupyter_enabled(cx) {
1544 key_context.add("jupyter");
1545 }
1546
1547 key_context.set("mode", mode);
1548 if self.pending_rename.is_some() {
1549 key_context.add("renaming");
1550 }
1551
1552 match self.context_menu.borrow().as_ref() {
1553 Some(CodeContextMenu::Completions(_)) => {
1554 key_context.add("menu");
1555 key_context.add("showing_completions");
1556 }
1557 Some(CodeContextMenu::CodeActions(_)) => {
1558 key_context.add("menu");
1559 key_context.add("showing_code_actions")
1560 }
1561 None => {}
1562 }
1563
1564 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1565 if !self.focus_handle(cx).contains_focused(window, cx)
1566 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1567 {
1568 for addon in self.addons.values() {
1569 addon.extend_key_context(&mut key_context, cx)
1570 }
1571 }
1572
1573 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1574 if let Some(extension) = singleton_buffer
1575 .read(cx)
1576 .file()
1577 .and_then(|file| file.path().extension()?.to_str())
1578 {
1579 key_context.set("extension", extension.to_string());
1580 }
1581 } else {
1582 key_context.add("multibuffer");
1583 }
1584
1585 if has_active_edit_prediction {
1586 if self.edit_prediction_in_conflict() {
1587 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1588 } else {
1589 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1590 key_context.add("copilot_suggestion");
1591 }
1592 }
1593
1594 if self.selection_mark_mode {
1595 key_context.add("selection_mode");
1596 }
1597
1598 key_context
1599 }
1600
1601 pub fn edit_prediction_in_conflict(&self) -> bool {
1602 if !self.show_edit_predictions_in_menu() {
1603 return false;
1604 }
1605
1606 let showing_completions = self
1607 .context_menu
1608 .borrow()
1609 .as_ref()
1610 .map_or(false, |context| {
1611 matches!(context, CodeContextMenu::Completions(_))
1612 });
1613
1614 showing_completions
1615 || self.edit_prediction_requires_modifier()
1616 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1617 // bindings to insert tab characters.
1618 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1619 }
1620
1621 pub fn accept_edit_prediction_keybind(
1622 &self,
1623 window: &Window,
1624 cx: &App,
1625 ) -> AcceptEditPredictionBinding {
1626 let key_context = self.key_context_internal(true, window, cx);
1627 let in_conflict = self.edit_prediction_in_conflict();
1628
1629 AcceptEditPredictionBinding(
1630 window
1631 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1632 .into_iter()
1633 .filter(|binding| {
1634 !in_conflict
1635 || binding
1636 .keystrokes()
1637 .first()
1638 .map_or(false, |keystroke| keystroke.modifiers.modified())
1639 })
1640 .rev()
1641 .min_by_key(|binding| {
1642 binding
1643 .keystrokes()
1644 .first()
1645 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1646 }),
1647 )
1648 }
1649
1650 pub fn new_file(
1651 workspace: &mut Workspace,
1652 _: &workspace::NewFile,
1653 window: &mut Window,
1654 cx: &mut Context<Workspace>,
1655 ) {
1656 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1657 "Failed to create buffer",
1658 window,
1659 cx,
1660 |e, _, _| match e.error_code() {
1661 ErrorCode::RemoteUpgradeRequired => Some(format!(
1662 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1663 e.error_tag("required").unwrap_or("the latest version")
1664 )),
1665 _ => None,
1666 },
1667 );
1668 }
1669
1670 pub fn new_in_workspace(
1671 workspace: &mut Workspace,
1672 window: &mut Window,
1673 cx: &mut Context<Workspace>,
1674 ) -> Task<Result<Entity<Editor>>> {
1675 let project = workspace.project().clone();
1676 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1677
1678 cx.spawn_in(window, |workspace, mut cx| async move {
1679 let buffer = create.await?;
1680 workspace.update_in(&mut cx, |workspace, window, cx| {
1681 let editor =
1682 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1683 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1684 editor
1685 })
1686 })
1687 }
1688
1689 fn new_file_vertical(
1690 workspace: &mut Workspace,
1691 _: &workspace::NewFileSplitVertical,
1692 window: &mut Window,
1693 cx: &mut Context<Workspace>,
1694 ) {
1695 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1696 }
1697
1698 fn new_file_horizontal(
1699 workspace: &mut Workspace,
1700 _: &workspace::NewFileSplitHorizontal,
1701 window: &mut Window,
1702 cx: &mut Context<Workspace>,
1703 ) {
1704 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1705 }
1706
1707 fn new_file_in_direction(
1708 workspace: &mut Workspace,
1709 direction: SplitDirection,
1710 window: &mut Window,
1711 cx: &mut Context<Workspace>,
1712 ) {
1713 let project = workspace.project().clone();
1714 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1715
1716 cx.spawn_in(window, |workspace, mut cx| async move {
1717 let buffer = create.await?;
1718 workspace.update_in(&mut cx, move |workspace, window, cx| {
1719 workspace.split_item(
1720 direction,
1721 Box::new(
1722 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1723 ),
1724 window,
1725 cx,
1726 )
1727 })?;
1728 anyhow::Ok(())
1729 })
1730 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1731 match e.error_code() {
1732 ErrorCode::RemoteUpgradeRequired => Some(format!(
1733 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1734 e.error_tag("required").unwrap_or("the latest version")
1735 )),
1736 _ => None,
1737 }
1738 });
1739 }
1740
1741 pub fn leader_peer_id(&self) -> Option<PeerId> {
1742 self.leader_peer_id
1743 }
1744
1745 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1746 &self.buffer
1747 }
1748
1749 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1750 self.workspace.as_ref()?.0.upgrade()
1751 }
1752
1753 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1754 self.buffer().read(cx).title(cx)
1755 }
1756
1757 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1758 let git_blame_gutter_max_author_length = self
1759 .render_git_blame_gutter(cx)
1760 .then(|| {
1761 if let Some(blame) = self.blame.as_ref() {
1762 let max_author_length =
1763 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1764 Some(max_author_length)
1765 } else {
1766 None
1767 }
1768 })
1769 .flatten();
1770
1771 EditorSnapshot {
1772 mode: self.mode,
1773 show_gutter: self.show_gutter,
1774 show_line_numbers: self.show_line_numbers,
1775 show_git_diff_gutter: self.show_git_diff_gutter,
1776 show_code_actions: self.show_code_actions,
1777 show_runnables: self.show_runnables,
1778 git_blame_gutter_max_author_length,
1779 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1780 scroll_anchor: self.scroll_manager.anchor(),
1781 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1782 placeholder_text: self.placeholder_text.clone(),
1783 is_focused: self.focus_handle.is_focused(window),
1784 current_line_highlight: self
1785 .current_line_highlight
1786 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1787 gutter_hovered: self.gutter_hovered,
1788 }
1789 }
1790
1791 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1792 self.buffer.read(cx).language_at(point, cx)
1793 }
1794
1795 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1796 self.buffer.read(cx).read(cx).file_at(point).cloned()
1797 }
1798
1799 pub fn active_excerpt(
1800 &self,
1801 cx: &App,
1802 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1803 self.buffer
1804 .read(cx)
1805 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1806 }
1807
1808 pub fn mode(&self) -> EditorMode {
1809 self.mode
1810 }
1811
1812 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1813 self.collaboration_hub.as_deref()
1814 }
1815
1816 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1817 self.collaboration_hub = Some(hub);
1818 }
1819
1820 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1821 self.in_project_search = in_project_search;
1822 }
1823
1824 pub fn set_custom_context_menu(
1825 &mut self,
1826 f: impl 'static
1827 + Fn(
1828 &mut Self,
1829 DisplayPoint,
1830 &mut Window,
1831 &mut Context<Self>,
1832 ) -> Option<Entity<ui::ContextMenu>>,
1833 ) {
1834 self.custom_context_menu = Some(Box::new(f))
1835 }
1836
1837 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1838 self.completion_provider = provider;
1839 }
1840
1841 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1842 self.semantics_provider.clone()
1843 }
1844
1845 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1846 self.semantics_provider = provider;
1847 }
1848
1849 pub fn set_edit_prediction_provider<T>(
1850 &mut self,
1851 provider: Option<Entity<T>>,
1852 window: &mut Window,
1853 cx: &mut Context<Self>,
1854 ) where
1855 T: EditPredictionProvider,
1856 {
1857 self.edit_prediction_provider =
1858 provider.map(|provider| RegisteredInlineCompletionProvider {
1859 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1860 if this.focus_handle.is_focused(window) {
1861 this.update_visible_inline_completion(window, cx);
1862 }
1863 }),
1864 provider: Arc::new(provider),
1865 });
1866 self.update_edit_prediction_settings(cx);
1867 self.refresh_inline_completion(false, false, window, cx);
1868 }
1869
1870 pub fn placeholder_text(&self) -> Option<&str> {
1871 self.placeholder_text.as_deref()
1872 }
1873
1874 pub fn set_placeholder_text(
1875 &mut self,
1876 placeholder_text: impl Into<Arc<str>>,
1877 cx: &mut Context<Self>,
1878 ) {
1879 let placeholder_text = Some(placeholder_text.into());
1880 if self.placeholder_text != placeholder_text {
1881 self.placeholder_text = placeholder_text;
1882 cx.notify();
1883 }
1884 }
1885
1886 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1887 self.cursor_shape = cursor_shape;
1888
1889 // Disrupt blink for immediate user feedback that the cursor shape has changed
1890 self.blink_manager.update(cx, BlinkManager::show_cursor);
1891
1892 cx.notify();
1893 }
1894
1895 pub fn set_current_line_highlight(
1896 &mut self,
1897 current_line_highlight: Option<CurrentLineHighlight>,
1898 ) {
1899 self.current_line_highlight = current_line_highlight;
1900 }
1901
1902 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1903 self.collapse_matches = collapse_matches;
1904 }
1905
1906 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1907 let buffers = self.buffer.read(cx).all_buffers();
1908 let Some(project) = self.project.as_ref() else {
1909 return;
1910 };
1911 project.update(cx, |project, cx| {
1912 for buffer in buffers {
1913 self.registered_buffers
1914 .entry(buffer.read(cx).remote_id())
1915 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1916 }
1917 })
1918 }
1919
1920 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1921 if self.collapse_matches {
1922 return range.start..range.start;
1923 }
1924 range.clone()
1925 }
1926
1927 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1928 if self.display_map.read(cx).clip_at_line_ends != clip {
1929 self.display_map
1930 .update(cx, |map, _| map.clip_at_line_ends = clip);
1931 }
1932 }
1933
1934 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1935 self.input_enabled = input_enabled;
1936 }
1937
1938 pub fn set_inline_completions_hidden_for_vim_mode(
1939 &mut self,
1940 hidden: bool,
1941 window: &mut Window,
1942 cx: &mut Context<Self>,
1943 ) {
1944 if hidden != self.inline_completions_hidden_for_vim_mode {
1945 self.inline_completions_hidden_for_vim_mode = hidden;
1946 if hidden {
1947 self.update_visible_inline_completion(window, cx);
1948 } else {
1949 self.refresh_inline_completion(true, false, window, cx);
1950 }
1951 }
1952 }
1953
1954 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1955 self.menu_inline_completions_policy = value;
1956 }
1957
1958 pub fn set_autoindent(&mut self, autoindent: bool) {
1959 if autoindent {
1960 self.autoindent_mode = Some(AutoindentMode::EachLine);
1961 } else {
1962 self.autoindent_mode = None;
1963 }
1964 }
1965
1966 pub fn read_only(&self, cx: &App) -> bool {
1967 self.read_only || self.buffer.read(cx).read_only()
1968 }
1969
1970 pub fn set_read_only(&mut self, read_only: bool) {
1971 self.read_only = read_only;
1972 }
1973
1974 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1975 self.use_autoclose = autoclose;
1976 }
1977
1978 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1979 self.use_auto_surround = auto_surround;
1980 }
1981
1982 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1983 self.auto_replace_emoji_shortcode = auto_replace;
1984 }
1985
1986 pub fn toggle_edit_predictions(
1987 &mut self,
1988 _: &ToggleEditPrediction,
1989 window: &mut Window,
1990 cx: &mut Context<Self>,
1991 ) {
1992 if self.show_inline_completions_override.is_some() {
1993 self.set_show_edit_predictions(None, window, cx);
1994 } else {
1995 let show_edit_predictions = !self.edit_predictions_enabled();
1996 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1997 }
1998 }
1999
2000 pub fn set_show_edit_predictions(
2001 &mut self,
2002 show_edit_predictions: Option<bool>,
2003 window: &mut Window,
2004 cx: &mut Context<Self>,
2005 ) {
2006 self.show_inline_completions_override = show_edit_predictions;
2007 self.update_edit_prediction_settings(cx);
2008
2009 if let Some(false) = show_edit_predictions {
2010 self.discard_inline_completion(false, cx);
2011 } else {
2012 self.refresh_inline_completion(false, true, window, cx);
2013 }
2014 }
2015
2016 fn inline_completions_disabled_in_scope(
2017 &self,
2018 buffer: &Entity<Buffer>,
2019 buffer_position: language::Anchor,
2020 cx: &App,
2021 ) -> bool {
2022 let snapshot = buffer.read(cx).snapshot();
2023 let settings = snapshot.settings_at(buffer_position, cx);
2024
2025 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2026 return false;
2027 };
2028
2029 scope.override_name().map_or(false, |scope_name| {
2030 settings
2031 .edit_predictions_disabled_in
2032 .iter()
2033 .any(|s| s == scope_name)
2034 })
2035 }
2036
2037 pub fn set_use_modal_editing(&mut self, to: bool) {
2038 self.use_modal_editing = to;
2039 }
2040
2041 pub fn use_modal_editing(&self) -> bool {
2042 self.use_modal_editing
2043 }
2044
2045 fn selections_did_change(
2046 &mut self,
2047 local: bool,
2048 old_cursor_position: &Anchor,
2049 show_completions: bool,
2050 window: &mut Window,
2051 cx: &mut Context<Self>,
2052 ) {
2053 window.invalidate_character_coordinates();
2054
2055 // Copy selections to primary selection buffer
2056 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2057 if local {
2058 let selections = self.selections.all::<usize>(cx);
2059 let buffer_handle = self.buffer.read(cx).read(cx);
2060
2061 let mut text = String::new();
2062 for (index, selection) in selections.iter().enumerate() {
2063 let text_for_selection = buffer_handle
2064 .text_for_range(selection.start..selection.end)
2065 .collect::<String>();
2066
2067 text.push_str(&text_for_selection);
2068 if index != selections.len() - 1 {
2069 text.push('\n');
2070 }
2071 }
2072
2073 if !text.is_empty() {
2074 cx.write_to_primary(ClipboardItem::new_string(text));
2075 }
2076 }
2077
2078 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2079 self.buffer.update(cx, |buffer, cx| {
2080 buffer.set_active_selections(
2081 &self.selections.disjoint_anchors(),
2082 self.selections.line_mode,
2083 self.cursor_shape,
2084 cx,
2085 )
2086 });
2087 }
2088 let display_map = self
2089 .display_map
2090 .update(cx, |display_map, cx| display_map.snapshot(cx));
2091 let buffer = &display_map.buffer_snapshot;
2092 self.add_selections_state = None;
2093 self.select_next_state = None;
2094 self.select_prev_state = None;
2095 self.select_larger_syntax_node_stack.clear();
2096 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2097 self.snippet_stack
2098 .invalidate(&self.selections.disjoint_anchors(), buffer);
2099 self.take_rename(false, window, cx);
2100
2101 let new_cursor_position = self.selections.newest_anchor().head();
2102
2103 self.push_to_nav_history(
2104 *old_cursor_position,
2105 Some(new_cursor_position.to_point(buffer)),
2106 cx,
2107 );
2108
2109 if local {
2110 let new_cursor_position = self.selections.newest_anchor().head();
2111 let mut context_menu = self.context_menu.borrow_mut();
2112 let completion_menu = match context_menu.as_ref() {
2113 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2114 _ => {
2115 *context_menu = None;
2116 None
2117 }
2118 };
2119 if let Some(buffer_id) = new_cursor_position.buffer_id {
2120 if !self.registered_buffers.contains_key(&buffer_id) {
2121 if let Some(project) = self.project.as_ref() {
2122 project.update(cx, |project, cx| {
2123 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2124 return;
2125 };
2126 self.registered_buffers.insert(
2127 buffer_id,
2128 project.register_buffer_with_language_servers(&buffer, cx),
2129 );
2130 })
2131 }
2132 }
2133 }
2134
2135 if let Some(completion_menu) = completion_menu {
2136 let cursor_position = new_cursor_position.to_offset(buffer);
2137 let (word_range, kind) =
2138 buffer.surrounding_word(completion_menu.initial_position, true);
2139 if kind == Some(CharKind::Word)
2140 && word_range.to_inclusive().contains(&cursor_position)
2141 {
2142 let mut completion_menu = completion_menu.clone();
2143 drop(context_menu);
2144
2145 let query = Self::completion_query(buffer, cursor_position);
2146 cx.spawn(move |this, mut cx| async move {
2147 completion_menu
2148 .filter(query.as_deref(), cx.background_executor().clone())
2149 .await;
2150
2151 this.update(&mut cx, |this, cx| {
2152 let mut context_menu = this.context_menu.borrow_mut();
2153 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2154 else {
2155 return;
2156 };
2157
2158 if menu.id > completion_menu.id {
2159 return;
2160 }
2161
2162 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2163 drop(context_menu);
2164 cx.notify();
2165 })
2166 })
2167 .detach();
2168
2169 if show_completions {
2170 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2171 }
2172 } else {
2173 drop(context_menu);
2174 self.hide_context_menu(window, cx);
2175 }
2176 } else {
2177 drop(context_menu);
2178 }
2179
2180 hide_hover(self, cx);
2181
2182 if old_cursor_position.to_display_point(&display_map).row()
2183 != new_cursor_position.to_display_point(&display_map).row()
2184 {
2185 self.available_code_actions.take();
2186 }
2187 self.refresh_code_actions(window, cx);
2188 self.refresh_document_highlights(cx);
2189 self.refresh_selected_text_highlights(window, cx);
2190 refresh_matching_bracket_highlights(self, window, cx);
2191 self.update_visible_inline_completion(window, cx);
2192 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2193 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2194 if self.git_blame_inline_enabled {
2195 self.start_inline_blame_timer(window, cx);
2196 }
2197 }
2198
2199 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2200 cx.emit(EditorEvent::SelectionsChanged { local });
2201
2202 let selections = &self.selections.disjoint;
2203 if selections.len() == 1 {
2204 cx.emit(SearchEvent::ActiveMatchChanged)
2205 }
2206 if local
2207 && self.is_singleton(cx)
2208 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2209 {
2210 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2211 let background_executor = cx.background_executor().clone();
2212 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2213 let snapshot = self.buffer().read(cx).snapshot(cx);
2214 let selections = selections.clone();
2215 self.serialize_selections = cx.background_spawn(async move {
2216 background_executor.timer(Duration::from_millis(100)).await;
2217 let selections = selections
2218 .iter()
2219 .map(|selection| {
2220 (
2221 selection.start.to_offset(&snapshot),
2222 selection.end.to_offset(&snapshot),
2223 )
2224 })
2225 .collect();
2226 DB.save_editor_selections(editor_id, workspace_id, selections)
2227 .await
2228 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2229 .log_err();
2230 });
2231 }
2232 }
2233
2234 cx.notify();
2235 }
2236
2237 pub fn sync_selections(
2238 &mut self,
2239 other: Entity<Editor>,
2240 cx: &mut Context<Self>,
2241 ) -> gpui::Subscription {
2242 let other_selections = other.read(cx).selections.disjoint.to_vec();
2243 self.selections.change_with(cx, |selections| {
2244 selections.select_anchors(other_selections);
2245 });
2246
2247 let other_subscription =
2248 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2249 EditorEvent::SelectionsChanged { local: true } => {
2250 let other_selections = other.read(cx).selections.disjoint.to_vec();
2251 if other_selections.is_empty() {
2252 return;
2253 }
2254 this.selections.change_with(cx, |selections| {
2255 selections.select_anchors(other_selections);
2256 });
2257 }
2258 _ => {}
2259 });
2260
2261 let this_subscription =
2262 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2263 EditorEvent::SelectionsChanged { local: true } => {
2264 let these_selections = this.selections.disjoint.to_vec();
2265 if these_selections.is_empty() {
2266 return;
2267 }
2268 other.update(cx, |other_editor, cx| {
2269 other_editor.selections.change_with(cx, |selections| {
2270 selections.select_anchors(these_selections);
2271 })
2272 });
2273 }
2274 _ => {}
2275 });
2276
2277 Subscription::join(other_subscription, this_subscription)
2278 }
2279
2280 pub fn change_selections<R>(
2281 &mut self,
2282 autoscroll: Option<Autoscroll>,
2283 window: &mut Window,
2284 cx: &mut Context<Self>,
2285 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2286 ) -> R {
2287 self.change_selections_inner(autoscroll, true, window, cx, change)
2288 }
2289
2290 fn change_selections_inner<R>(
2291 &mut self,
2292 autoscroll: Option<Autoscroll>,
2293 request_completions: bool,
2294 window: &mut Window,
2295 cx: &mut Context<Self>,
2296 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2297 ) -> R {
2298 let old_cursor_position = self.selections.newest_anchor().head();
2299 self.push_to_selection_history();
2300
2301 let (changed, result) = self.selections.change_with(cx, change);
2302
2303 if changed {
2304 if let Some(autoscroll) = autoscroll {
2305 self.request_autoscroll(autoscroll, cx);
2306 }
2307 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2308
2309 if self.should_open_signature_help_automatically(
2310 &old_cursor_position,
2311 self.signature_help_state.backspace_pressed(),
2312 cx,
2313 ) {
2314 self.show_signature_help(&ShowSignatureHelp, window, cx);
2315 }
2316 self.signature_help_state.set_backspace_pressed(false);
2317 }
2318
2319 result
2320 }
2321
2322 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2323 where
2324 I: IntoIterator<Item = (Range<S>, T)>,
2325 S: ToOffset,
2326 T: Into<Arc<str>>,
2327 {
2328 if self.read_only(cx) {
2329 return;
2330 }
2331
2332 self.buffer
2333 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2334 }
2335
2336 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2337 where
2338 I: IntoIterator<Item = (Range<S>, T)>,
2339 S: ToOffset,
2340 T: Into<Arc<str>>,
2341 {
2342 if self.read_only(cx) {
2343 return;
2344 }
2345
2346 self.buffer.update(cx, |buffer, cx| {
2347 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2348 });
2349 }
2350
2351 pub fn edit_with_block_indent<I, S, T>(
2352 &mut self,
2353 edits: I,
2354 original_indent_columns: Vec<Option<u32>>,
2355 cx: &mut Context<Self>,
2356 ) where
2357 I: IntoIterator<Item = (Range<S>, T)>,
2358 S: ToOffset,
2359 T: Into<Arc<str>>,
2360 {
2361 if self.read_only(cx) {
2362 return;
2363 }
2364
2365 self.buffer.update(cx, |buffer, cx| {
2366 buffer.edit(
2367 edits,
2368 Some(AutoindentMode::Block {
2369 original_indent_columns,
2370 }),
2371 cx,
2372 )
2373 });
2374 }
2375
2376 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2377 self.hide_context_menu(window, cx);
2378
2379 match phase {
2380 SelectPhase::Begin {
2381 position,
2382 add,
2383 click_count,
2384 } => self.begin_selection(position, add, click_count, window, cx),
2385 SelectPhase::BeginColumnar {
2386 position,
2387 goal_column,
2388 reset,
2389 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2390 SelectPhase::Extend {
2391 position,
2392 click_count,
2393 } => self.extend_selection(position, click_count, window, cx),
2394 SelectPhase::Update {
2395 position,
2396 goal_column,
2397 scroll_delta,
2398 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2399 SelectPhase::End => self.end_selection(window, cx),
2400 }
2401 }
2402
2403 fn extend_selection(
2404 &mut self,
2405 position: DisplayPoint,
2406 click_count: usize,
2407 window: &mut Window,
2408 cx: &mut Context<Self>,
2409 ) {
2410 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2411 let tail = self.selections.newest::<usize>(cx).tail();
2412 self.begin_selection(position, false, click_count, window, cx);
2413
2414 let position = position.to_offset(&display_map, Bias::Left);
2415 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2416
2417 let mut pending_selection = self
2418 .selections
2419 .pending_anchor()
2420 .expect("extend_selection not called with pending selection");
2421 if position >= tail {
2422 pending_selection.start = tail_anchor;
2423 } else {
2424 pending_selection.end = tail_anchor;
2425 pending_selection.reversed = true;
2426 }
2427
2428 let mut pending_mode = self.selections.pending_mode().unwrap();
2429 match &mut pending_mode {
2430 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2431 _ => {}
2432 }
2433
2434 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2435 s.set_pending(pending_selection, pending_mode)
2436 });
2437 }
2438
2439 fn begin_selection(
2440 &mut self,
2441 position: DisplayPoint,
2442 add: bool,
2443 click_count: usize,
2444 window: &mut Window,
2445 cx: &mut Context<Self>,
2446 ) {
2447 if !self.focus_handle.is_focused(window) {
2448 self.last_focused_descendant = None;
2449 window.focus(&self.focus_handle);
2450 }
2451
2452 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2453 let buffer = &display_map.buffer_snapshot;
2454 let newest_selection = self.selections.newest_anchor().clone();
2455 let position = display_map.clip_point(position, Bias::Left);
2456
2457 let start;
2458 let end;
2459 let mode;
2460 let mut auto_scroll;
2461 match click_count {
2462 1 => {
2463 start = buffer.anchor_before(position.to_point(&display_map));
2464 end = start;
2465 mode = SelectMode::Character;
2466 auto_scroll = true;
2467 }
2468 2 => {
2469 let range = movement::surrounding_word(&display_map, position);
2470 start = buffer.anchor_before(range.start.to_point(&display_map));
2471 end = buffer.anchor_before(range.end.to_point(&display_map));
2472 mode = SelectMode::Word(start..end);
2473 auto_scroll = true;
2474 }
2475 3 => {
2476 let position = display_map
2477 .clip_point(position, Bias::Left)
2478 .to_point(&display_map);
2479 let line_start = display_map.prev_line_boundary(position).0;
2480 let next_line_start = buffer.clip_point(
2481 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2482 Bias::Left,
2483 );
2484 start = buffer.anchor_before(line_start);
2485 end = buffer.anchor_before(next_line_start);
2486 mode = SelectMode::Line(start..end);
2487 auto_scroll = true;
2488 }
2489 _ => {
2490 start = buffer.anchor_before(0);
2491 end = buffer.anchor_before(buffer.len());
2492 mode = SelectMode::All;
2493 auto_scroll = false;
2494 }
2495 }
2496 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2497
2498 let point_to_delete: Option<usize> = {
2499 let selected_points: Vec<Selection<Point>> =
2500 self.selections.disjoint_in_range(start..end, cx);
2501
2502 if !add || click_count > 1 {
2503 None
2504 } else if !selected_points.is_empty() {
2505 Some(selected_points[0].id)
2506 } else {
2507 let clicked_point_already_selected =
2508 self.selections.disjoint.iter().find(|selection| {
2509 selection.start.to_point(buffer) == start.to_point(buffer)
2510 || selection.end.to_point(buffer) == end.to_point(buffer)
2511 });
2512
2513 clicked_point_already_selected.map(|selection| selection.id)
2514 }
2515 };
2516
2517 let selections_count = self.selections.count();
2518
2519 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2520 if let Some(point_to_delete) = point_to_delete {
2521 s.delete(point_to_delete);
2522
2523 if selections_count == 1 {
2524 s.set_pending_anchor_range(start..end, mode);
2525 }
2526 } else {
2527 if !add {
2528 s.clear_disjoint();
2529 } else if click_count > 1 {
2530 s.delete(newest_selection.id)
2531 }
2532
2533 s.set_pending_anchor_range(start..end, mode);
2534 }
2535 });
2536 }
2537
2538 fn begin_columnar_selection(
2539 &mut self,
2540 position: DisplayPoint,
2541 goal_column: u32,
2542 reset: bool,
2543 window: &mut Window,
2544 cx: &mut Context<Self>,
2545 ) {
2546 if !self.focus_handle.is_focused(window) {
2547 self.last_focused_descendant = None;
2548 window.focus(&self.focus_handle);
2549 }
2550
2551 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2552
2553 if reset {
2554 let pointer_position = display_map
2555 .buffer_snapshot
2556 .anchor_before(position.to_point(&display_map));
2557
2558 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2559 s.clear_disjoint();
2560 s.set_pending_anchor_range(
2561 pointer_position..pointer_position,
2562 SelectMode::Character,
2563 );
2564 });
2565 }
2566
2567 let tail = self.selections.newest::<Point>(cx).tail();
2568 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2569
2570 if !reset {
2571 self.select_columns(
2572 tail.to_display_point(&display_map),
2573 position,
2574 goal_column,
2575 &display_map,
2576 window,
2577 cx,
2578 );
2579 }
2580 }
2581
2582 fn update_selection(
2583 &mut self,
2584 position: DisplayPoint,
2585 goal_column: u32,
2586 scroll_delta: gpui::Point<f32>,
2587 window: &mut Window,
2588 cx: &mut Context<Self>,
2589 ) {
2590 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2591
2592 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2593 let tail = tail.to_display_point(&display_map);
2594 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2595 } else if let Some(mut pending) = self.selections.pending_anchor() {
2596 let buffer = self.buffer.read(cx).snapshot(cx);
2597 let head;
2598 let tail;
2599 let mode = self.selections.pending_mode().unwrap();
2600 match &mode {
2601 SelectMode::Character => {
2602 head = position.to_point(&display_map);
2603 tail = pending.tail().to_point(&buffer);
2604 }
2605 SelectMode::Word(original_range) => {
2606 let original_display_range = original_range.start.to_display_point(&display_map)
2607 ..original_range.end.to_display_point(&display_map);
2608 let original_buffer_range = original_display_range.start.to_point(&display_map)
2609 ..original_display_range.end.to_point(&display_map);
2610 if movement::is_inside_word(&display_map, position)
2611 || original_display_range.contains(&position)
2612 {
2613 let word_range = movement::surrounding_word(&display_map, position);
2614 if word_range.start < original_display_range.start {
2615 head = word_range.start.to_point(&display_map);
2616 } else {
2617 head = word_range.end.to_point(&display_map);
2618 }
2619 } else {
2620 head = position.to_point(&display_map);
2621 }
2622
2623 if head <= original_buffer_range.start {
2624 tail = original_buffer_range.end;
2625 } else {
2626 tail = original_buffer_range.start;
2627 }
2628 }
2629 SelectMode::Line(original_range) => {
2630 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2631
2632 let position = display_map
2633 .clip_point(position, Bias::Left)
2634 .to_point(&display_map);
2635 let line_start = display_map.prev_line_boundary(position).0;
2636 let next_line_start = buffer.clip_point(
2637 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2638 Bias::Left,
2639 );
2640
2641 if line_start < original_range.start {
2642 head = line_start
2643 } else {
2644 head = next_line_start
2645 }
2646
2647 if head <= original_range.start {
2648 tail = original_range.end;
2649 } else {
2650 tail = original_range.start;
2651 }
2652 }
2653 SelectMode::All => {
2654 return;
2655 }
2656 };
2657
2658 if head < tail {
2659 pending.start = buffer.anchor_before(head);
2660 pending.end = buffer.anchor_before(tail);
2661 pending.reversed = true;
2662 } else {
2663 pending.start = buffer.anchor_before(tail);
2664 pending.end = buffer.anchor_before(head);
2665 pending.reversed = false;
2666 }
2667
2668 self.change_selections(None, window, cx, |s| {
2669 s.set_pending(pending, mode);
2670 });
2671 } else {
2672 log::error!("update_selection dispatched with no pending selection");
2673 return;
2674 }
2675
2676 self.apply_scroll_delta(scroll_delta, window, cx);
2677 cx.notify();
2678 }
2679
2680 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2681 self.columnar_selection_tail.take();
2682 if self.selections.pending_anchor().is_some() {
2683 let selections = self.selections.all::<usize>(cx);
2684 self.change_selections(None, window, cx, |s| {
2685 s.select(selections);
2686 s.clear_pending();
2687 });
2688 }
2689 }
2690
2691 fn select_columns(
2692 &mut self,
2693 tail: DisplayPoint,
2694 head: DisplayPoint,
2695 goal_column: u32,
2696 display_map: &DisplaySnapshot,
2697 window: &mut Window,
2698 cx: &mut Context<Self>,
2699 ) {
2700 let start_row = cmp::min(tail.row(), head.row());
2701 let end_row = cmp::max(tail.row(), head.row());
2702 let start_column = cmp::min(tail.column(), goal_column);
2703 let end_column = cmp::max(tail.column(), goal_column);
2704 let reversed = start_column < tail.column();
2705
2706 let selection_ranges = (start_row.0..=end_row.0)
2707 .map(DisplayRow)
2708 .filter_map(|row| {
2709 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2710 let start = display_map
2711 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2712 .to_point(display_map);
2713 let end = display_map
2714 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2715 .to_point(display_map);
2716 if reversed {
2717 Some(end..start)
2718 } else {
2719 Some(start..end)
2720 }
2721 } else {
2722 None
2723 }
2724 })
2725 .collect::<Vec<_>>();
2726
2727 self.change_selections(None, window, cx, |s| {
2728 s.select_ranges(selection_ranges);
2729 });
2730 cx.notify();
2731 }
2732
2733 pub fn has_pending_nonempty_selection(&self) -> bool {
2734 let pending_nonempty_selection = match self.selections.pending_anchor() {
2735 Some(Selection { start, end, .. }) => start != end,
2736 None => false,
2737 };
2738
2739 pending_nonempty_selection
2740 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2741 }
2742
2743 pub fn has_pending_selection(&self) -> bool {
2744 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2745 }
2746
2747 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2748 self.selection_mark_mode = false;
2749
2750 if self.clear_expanded_diff_hunks(cx) {
2751 cx.notify();
2752 return;
2753 }
2754 if self.dismiss_menus_and_popups(true, window, cx) {
2755 return;
2756 }
2757
2758 if self.mode == EditorMode::Full
2759 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2760 {
2761 return;
2762 }
2763
2764 cx.propagate();
2765 }
2766
2767 pub fn dismiss_menus_and_popups(
2768 &mut self,
2769 is_user_requested: bool,
2770 window: &mut Window,
2771 cx: &mut Context<Self>,
2772 ) -> bool {
2773 if self.take_rename(false, window, cx).is_some() {
2774 return true;
2775 }
2776
2777 if hide_hover(self, cx) {
2778 return true;
2779 }
2780
2781 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2782 return true;
2783 }
2784
2785 if self.hide_context_menu(window, cx).is_some() {
2786 return true;
2787 }
2788
2789 if self.mouse_context_menu.take().is_some() {
2790 return true;
2791 }
2792
2793 if is_user_requested && self.discard_inline_completion(true, cx) {
2794 return true;
2795 }
2796
2797 if self.snippet_stack.pop().is_some() {
2798 return true;
2799 }
2800
2801 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2802 self.dismiss_diagnostics(cx);
2803 return true;
2804 }
2805
2806 false
2807 }
2808
2809 fn linked_editing_ranges_for(
2810 &self,
2811 selection: Range<text::Anchor>,
2812 cx: &App,
2813 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2814 if self.linked_edit_ranges.is_empty() {
2815 return None;
2816 }
2817 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2818 selection.end.buffer_id.and_then(|end_buffer_id| {
2819 if selection.start.buffer_id != Some(end_buffer_id) {
2820 return None;
2821 }
2822 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2823 let snapshot = buffer.read(cx).snapshot();
2824 self.linked_edit_ranges
2825 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2826 .map(|ranges| (ranges, snapshot, buffer))
2827 })?;
2828 use text::ToOffset as TO;
2829 // find offset from the start of current range to current cursor position
2830 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2831
2832 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2833 let start_difference = start_offset - start_byte_offset;
2834 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2835 let end_difference = end_offset - start_byte_offset;
2836 // Current range has associated linked ranges.
2837 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2838 for range in linked_ranges.iter() {
2839 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2840 let end_offset = start_offset + end_difference;
2841 let start_offset = start_offset + start_difference;
2842 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2843 continue;
2844 }
2845 if self.selections.disjoint_anchor_ranges().any(|s| {
2846 if s.start.buffer_id != selection.start.buffer_id
2847 || s.end.buffer_id != selection.end.buffer_id
2848 {
2849 return false;
2850 }
2851 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2852 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2853 }) {
2854 continue;
2855 }
2856 let start = buffer_snapshot.anchor_after(start_offset);
2857 let end = buffer_snapshot.anchor_after(end_offset);
2858 linked_edits
2859 .entry(buffer.clone())
2860 .or_default()
2861 .push(start..end);
2862 }
2863 Some(linked_edits)
2864 }
2865
2866 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2867 let text: Arc<str> = text.into();
2868
2869 if self.read_only(cx) {
2870 return;
2871 }
2872
2873 let selections = self.selections.all_adjusted(cx);
2874 let mut bracket_inserted = false;
2875 let mut edits = Vec::new();
2876 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2877 let mut new_selections = Vec::with_capacity(selections.len());
2878 let mut new_autoclose_regions = Vec::new();
2879 let snapshot = self.buffer.read(cx).read(cx);
2880
2881 for (selection, autoclose_region) in
2882 self.selections_with_autoclose_regions(selections, &snapshot)
2883 {
2884 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2885 // Determine if the inserted text matches the opening or closing
2886 // bracket of any of this language's bracket pairs.
2887 let mut bracket_pair = None;
2888 let mut is_bracket_pair_start = false;
2889 let mut is_bracket_pair_end = false;
2890 if !text.is_empty() {
2891 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2892 // and they are removing the character that triggered IME popup.
2893 for (pair, enabled) in scope.brackets() {
2894 if !pair.close && !pair.surround {
2895 continue;
2896 }
2897
2898 if enabled && pair.start.ends_with(text.as_ref()) {
2899 let prefix_len = pair.start.len() - text.len();
2900 let preceding_text_matches_prefix = prefix_len == 0
2901 || (selection.start.column >= (prefix_len as u32)
2902 && snapshot.contains_str_at(
2903 Point::new(
2904 selection.start.row,
2905 selection.start.column - (prefix_len as u32),
2906 ),
2907 &pair.start[..prefix_len],
2908 ));
2909 if preceding_text_matches_prefix {
2910 bracket_pair = Some(pair.clone());
2911 is_bracket_pair_start = true;
2912 break;
2913 }
2914 }
2915 if pair.end.as_str() == text.as_ref() {
2916 bracket_pair = Some(pair.clone());
2917 is_bracket_pair_end = true;
2918 break;
2919 }
2920 }
2921 }
2922
2923 if let Some(bracket_pair) = bracket_pair {
2924 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2925 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2926 let auto_surround =
2927 self.use_auto_surround && snapshot_settings.use_auto_surround;
2928 if selection.is_empty() {
2929 if is_bracket_pair_start {
2930 // If the inserted text is a suffix of an opening bracket and the
2931 // selection is preceded by the rest of the opening bracket, then
2932 // insert the closing bracket.
2933 let following_text_allows_autoclose = snapshot
2934 .chars_at(selection.start)
2935 .next()
2936 .map_or(true, |c| scope.should_autoclose_before(c));
2937
2938 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2939 && bracket_pair.start.len() == 1
2940 {
2941 let target = bracket_pair.start.chars().next().unwrap();
2942 let current_line_count = snapshot
2943 .reversed_chars_at(selection.start)
2944 .take_while(|&c| c != '\n')
2945 .filter(|&c| c == target)
2946 .count();
2947 current_line_count % 2 == 1
2948 } else {
2949 false
2950 };
2951
2952 if autoclose
2953 && bracket_pair.close
2954 && following_text_allows_autoclose
2955 && !is_closing_quote
2956 {
2957 let anchor = snapshot.anchor_before(selection.end);
2958 new_selections.push((selection.map(|_| anchor), text.len()));
2959 new_autoclose_regions.push((
2960 anchor,
2961 text.len(),
2962 selection.id,
2963 bracket_pair.clone(),
2964 ));
2965 edits.push((
2966 selection.range(),
2967 format!("{}{}", text, bracket_pair.end).into(),
2968 ));
2969 bracket_inserted = true;
2970 continue;
2971 }
2972 }
2973
2974 if let Some(region) = autoclose_region {
2975 // If the selection is followed by an auto-inserted closing bracket,
2976 // then don't insert that closing bracket again; just move the selection
2977 // past the closing bracket.
2978 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2979 && text.as_ref() == region.pair.end.as_str();
2980 if should_skip {
2981 let anchor = snapshot.anchor_after(selection.end);
2982 new_selections
2983 .push((selection.map(|_| anchor), region.pair.end.len()));
2984 continue;
2985 }
2986 }
2987
2988 let always_treat_brackets_as_autoclosed = snapshot
2989 .language_settings_at(selection.start, cx)
2990 .always_treat_brackets_as_autoclosed;
2991 if always_treat_brackets_as_autoclosed
2992 && is_bracket_pair_end
2993 && snapshot.contains_str_at(selection.end, text.as_ref())
2994 {
2995 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2996 // and the inserted text is a closing bracket and the selection is followed
2997 // by the closing bracket then move the selection past the closing bracket.
2998 let anchor = snapshot.anchor_after(selection.end);
2999 new_selections.push((selection.map(|_| anchor), text.len()));
3000 continue;
3001 }
3002 }
3003 // If an opening bracket is 1 character long and is typed while
3004 // text is selected, then surround that text with the bracket pair.
3005 else if auto_surround
3006 && bracket_pair.surround
3007 && is_bracket_pair_start
3008 && bracket_pair.start.chars().count() == 1
3009 {
3010 edits.push((selection.start..selection.start, text.clone()));
3011 edits.push((
3012 selection.end..selection.end,
3013 bracket_pair.end.as_str().into(),
3014 ));
3015 bracket_inserted = true;
3016 new_selections.push((
3017 Selection {
3018 id: selection.id,
3019 start: snapshot.anchor_after(selection.start),
3020 end: snapshot.anchor_before(selection.end),
3021 reversed: selection.reversed,
3022 goal: selection.goal,
3023 },
3024 0,
3025 ));
3026 continue;
3027 }
3028 }
3029 }
3030
3031 if self.auto_replace_emoji_shortcode
3032 && selection.is_empty()
3033 && text.as_ref().ends_with(':')
3034 {
3035 if let Some(possible_emoji_short_code) =
3036 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3037 {
3038 if !possible_emoji_short_code.is_empty() {
3039 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3040 let emoji_shortcode_start = Point::new(
3041 selection.start.row,
3042 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3043 );
3044
3045 // Remove shortcode from buffer
3046 edits.push((
3047 emoji_shortcode_start..selection.start,
3048 "".to_string().into(),
3049 ));
3050 new_selections.push((
3051 Selection {
3052 id: selection.id,
3053 start: snapshot.anchor_after(emoji_shortcode_start),
3054 end: snapshot.anchor_before(selection.start),
3055 reversed: selection.reversed,
3056 goal: selection.goal,
3057 },
3058 0,
3059 ));
3060
3061 // Insert emoji
3062 let selection_start_anchor = snapshot.anchor_after(selection.start);
3063 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3064 edits.push((selection.start..selection.end, emoji.to_string().into()));
3065
3066 continue;
3067 }
3068 }
3069 }
3070 }
3071
3072 // If not handling any auto-close operation, then just replace the selected
3073 // text with the given input and move the selection to the end of the
3074 // newly inserted text.
3075 let anchor = snapshot.anchor_after(selection.end);
3076 if !self.linked_edit_ranges.is_empty() {
3077 let start_anchor = snapshot.anchor_before(selection.start);
3078
3079 let is_word_char = text.chars().next().map_or(true, |char| {
3080 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3081 classifier.is_word(char)
3082 });
3083
3084 if is_word_char {
3085 if let Some(ranges) = self
3086 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3087 {
3088 for (buffer, edits) in ranges {
3089 linked_edits
3090 .entry(buffer.clone())
3091 .or_default()
3092 .extend(edits.into_iter().map(|range| (range, text.clone())));
3093 }
3094 }
3095 }
3096 }
3097
3098 new_selections.push((selection.map(|_| anchor), 0));
3099 edits.push((selection.start..selection.end, text.clone()));
3100 }
3101
3102 drop(snapshot);
3103
3104 self.transact(window, cx, |this, window, cx| {
3105 let initial_buffer_versions =
3106 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3107
3108 this.buffer.update(cx, |buffer, cx| {
3109 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3110 });
3111 for (buffer, edits) in linked_edits {
3112 buffer.update(cx, |buffer, cx| {
3113 let snapshot = buffer.snapshot();
3114 let edits = edits
3115 .into_iter()
3116 .map(|(range, text)| {
3117 use text::ToPoint as TP;
3118 let end_point = TP::to_point(&range.end, &snapshot);
3119 let start_point = TP::to_point(&range.start, &snapshot);
3120 (start_point..end_point, text)
3121 })
3122 .sorted_by_key(|(range, _)| range.start)
3123 .collect::<Vec<_>>();
3124 buffer.edit(edits, None, cx);
3125 })
3126 }
3127 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3128 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3129 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3130 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3131 .zip(new_selection_deltas)
3132 .map(|(selection, delta)| Selection {
3133 id: selection.id,
3134 start: selection.start + delta,
3135 end: selection.end + delta,
3136 reversed: selection.reversed,
3137 goal: SelectionGoal::None,
3138 })
3139 .collect::<Vec<_>>();
3140
3141 let mut i = 0;
3142 for (position, delta, selection_id, pair) in new_autoclose_regions {
3143 let position = position.to_offset(&map.buffer_snapshot) + delta;
3144 let start = map.buffer_snapshot.anchor_before(position);
3145 let end = map.buffer_snapshot.anchor_after(position);
3146 while let Some(existing_state) = this.autoclose_regions.get(i) {
3147 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3148 Ordering::Less => i += 1,
3149 Ordering::Greater => break,
3150 Ordering::Equal => {
3151 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3152 Ordering::Less => i += 1,
3153 Ordering::Equal => break,
3154 Ordering::Greater => break,
3155 }
3156 }
3157 }
3158 }
3159 this.autoclose_regions.insert(
3160 i,
3161 AutocloseRegion {
3162 selection_id,
3163 range: start..end,
3164 pair,
3165 },
3166 );
3167 }
3168
3169 let had_active_inline_completion = this.has_active_inline_completion();
3170 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3171 s.select(new_selections)
3172 });
3173
3174 if !bracket_inserted {
3175 if let Some(on_type_format_task) =
3176 this.trigger_on_type_formatting(text.to_string(), window, cx)
3177 {
3178 on_type_format_task.detach_and_log_err(cx);
3179 }
3180 }
3181
3182 let editor_settings = EditorSettings::get_global(cx);
3183 if bracket_inserted
3184 && (editor_settings.auto_signature_help
3185 || editor_settings.show_signature_help_after_edits)
3186 {
3187 this.show_signature_help(&ShowSignatureHelp, window, cx);
3188 }
3189
3190 let trigger_in_words =
3191 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3192 if this.hard_wrap.is_some() {
3193 let latest: Range<Point> = this.selections.newest(cx).range();
3194 if latest.is_empty()
3195 && this
3196 .buffer()
3197 .read(cx)
3198 .snapshot(cx)
3199 .line_len(MultiBufferRow(latest.start.row))
3200 == latest.start.column
3201 {
3202 this.rewrap_impl(true, cx)
3203 }
3204 }
3205 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3206 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3207 this.refresh_inline_completion(true, false, window, cx);
3208 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3209 });
3210 }
3211
3212 fn find_possible_emoji_shortcode_at_position(
3213 snapshot: &MultiBufferSnapshot,
3214 position: Point,
3215 ) -> Option<String> {
3216 let mut chars = Vec::new();
3217 let mut found_colon = false;
3218 for char in snapshot.reversed_chars_at(position).take(100) {
3219 // Found a possible emoji shortcode in the middle of the buffer
3220 if found_colon {
3221 if char.is_whitespace() {
3222 chars.reverse();
3223 return Some(chars.iter().collect());
3224 }
3225 // If the previous character is not a whitespace, we are in the middle of a word
3226 // and we only want to complete the shortcode if the word is made up of other emojis
3227 let mut containing_word = String::new();
3228 for ch in snapshot
3229 .reversed_chars_at(position)
3230 .skip(chars.len() + 1)
3231 .take(100)
3232 {
3233 if ch.is_whitespace() {
3234 break;
3235 }
3236 containing_word.push(ch);
3237 }
3238 let containing_word = containing_word.chars().rev().collect::<String>();
3239 if util::word_consists_of_emojis(containing_word.as_str()) {
3240 chars.reverse();
3241 return Some(chars.iter().collect());
3242 }
3243 }
3244
3245 if char.is_whitespace() || !char.is_ascii() {
3246 return None;
3247 }
3248 if char == ':' {
3249 found_colon = true;
3250 } else {
3251 chars.push(char);
3252 }
3253 }
3254 // Found a possible emoji shortcode at the beginning of the buffer
3255 chars.reverse();
3256 Some(chars.iter().collect())
3257 }
3258
3259 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3260 self.transact(window, cx, |this, window, cx| {
3261 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3262 let selections = this.selections.all::<usize>(cx);
3263 let multi_buffer = this.buffer.read(cx);
3264 let buffer = multi_buffer.snapshot(cx);
3265 selections
3266 .iter()
3267 .map(|selection| {
3268 let start_point = selection.start.to_point(&buffer);
3269 let mut indent =
3270 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3271 indent.len = cmp::min(indent.len, start_point.column);
3272 let start = selection.start;
3273 let end = selection.end;
3274 let selection_is_empty = start == end;
3275 let language_scope = buffer.language_scope_at(start);
3276 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3277 &language_scope
3278 {
3279 let insert_extra_newline =
3280 insert_extra_newline_brackets(&buffer, start..end, language)
3281 || insert_extra_newline_tree_sitter(&buffer, start..end);
3282
3283 // Comment extension on newline is allowed only for cursor selections
3284 let comment_delimiter = maybe!({
3285 if !selection_is_empty {
3286 return None;
3287 }
3288
3289 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3290 return None;
3291 }
3292
3293 let delimiters = language.line_comment_prefixes();
3294 let max_len_of_delimiter =
3295 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3296 let (snapshot, range) =
3297 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3298
3299 let mut index_of_first_non_whitespace = 0;
3300 let comment_candidate = snapshot
3301 .chars_for_range(range)
3302 .skip_while(|c| {
3303 let should_skip = c.is_whitespace();
3304 if should_skip {
3305 index_of_first_non_whitespace += 1;
3306 }
3307 should_skip
3308 })
3309 .take(max_len_of_delimiter)
3310 .collect::<String>();
3311 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3312 comment_candidate.starts_with(comment_prefix.as_ref())
3313 })?;
3314 let cursor_is_placed_after_comment_marker =
3315 index_of_first_non_whitespace + comment_prefix.len()
3316 <= start_point.column as usize;
3317 if cursor_is_placed_after_comment_marker {
3318 Some(comment_prefix.clone())
3319 } else {
3320 None
3321 }
3322 });
3323 (comment_delimiter, insert_extra_newline)
3324 } else {
3325 (None, false)
3326 };
3327
3328 let capacity_for_delimiter = comment_delimiter
3329 .as_deref()
3330 .map(str::len)
3331 .unwrap_or_default();
3332 let mut new_text =
3333 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3334 new_text.push('\n');
3335 new_text.extend(indent.chars());
3336 if let Some(delimiter) = &comment_delimiter {
3337 new_text.push_str(delimiter);
3338 }
3339 if insert_extra_newline {
3340 new_text = new_text.repeat(2);
3341 }
3342
3343 let anchor = buffer.anchor_after(end);
3344 let new_selection = selection.map(|_| anchor);
3345 (
3346 (start..end, new_text),
3347 (insert_extra_newline, new_selection),
3348 )
3349 })
3350 .unzip()
3351 };
3352
3353 this.edit_with_autoindent(edits, cx);
3354 let buffer = this.buffer.read(cx).snapshot(cx);
3355 let new_selections = selection_fixup_info
3356 .into_iter()
3357 .map(|(extra_newline_inserted, new_selection)| {
3358 let mut cursor = new_selection.end.to_point(&buffer);
3359 if extra_newline_inserted {
3360 cursor.row -= 1;
3361 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3362 }
3363 new_selection.map(|_| cursor)
3364 })
3365 .collect();
3366
3367 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3368 s.select(new_selections)
3369 });
3370 this.refresh_inline_completion(true, false, window, cx);
3371 });
3372 }
3373
3374 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3375 let buffer = self.buffer.read(cx);
3376 let snapshot = buffer.snapshot(cx);
3377
3378 let mut edits = Vec::new();
3379 let mut rows = Vec::new();
3380
3381 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3382 let cursor = selection.head();
3383 let row = cursor.row;
3384
3385 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3386
3387 let newline = "\n".to_string();
3388 edits.push((start_of_line..start_of_line, newline));
3389
3390 rows.push(row + rows_inserted as u32);
3391 }
3392
3393 self.transact(window, cx, |editor, window, cx| {
3394 editor.edit(edits, cx);
3395
3396 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3397 let mut index = 0;
3398 s.move_cursors_with(|map, _, _| {
3399 let row = rows[index];
3400 index += 1;
3401
3402 let point = Point::new(row, 0);
3403 let boundary = map.next_line_boundary(point).1;
3404 let clipped = map.clip_point(boundary, Bias::Left);
3405
3406 (clipped, SelectionGoal::None)
3407 });
3408 });
3409
3410 let mut indent_edits = Vec::new();
3411 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3412 for row in rows {
3413 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3414 for (row, indent) in indents {
3415 if indent.len == 0 {
3416 continue;
3417 }
3418
3419 let text = match indent.kind {
3420 IndentKind::Space => " ".repeat(indent.len as usize),
3421 IndentKind::Tab => "\t".repeat(indent.len as usize),
3422 };
3423 let point = Point::new(row.0, 0);
3424 indent_edits.push((point..point, text));
3425 }
3426 }
3427 editor.edit(indent_edits, cx);
3428 });
3429 }
3430
3431 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3432 let buffer = self.buffer.read(cx);
3433 let snapshot = buffer.snapshot(cx);
3434
3435 let mut edits = Vec::new();
3436 let mut rows = Vec::new();
3437 let mut rows_inserted = 0;
3438
3439 for selection in self.selections.all_adjusted(cx) {
3440 let cursor = selection.head();
3441 let row = cursor.row;
3442
3443 let point = Point::new(row + 1, 0);
3444 let start_of_line = snapshot.clip_point(point, Bias::Left);
3445
3446 let newline = "\n".to_string();
3447 edits.push((start_of_line..start_of_line, newline));
3448
3449 rows_inserted += 1;
3450 rows.push(row + rows_inserted);
3451 }
3452
3453 self.transact(window, cx, |editor, window, cx| {
3454 editor.edit(edits, cx);
3455
3456 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3457 let mut index = 0;
3458 s.move_cursors_with(|map, _, _| {
3459 let row = rows[index];
3460 index += 1;
3461
3462 let point = Point::new(row, 0);
3463 let boundary = map.next_line_boundary(point).1;
3464 let clipped = map.clip_point(boundary, Bias::Left);
3465
3466 (clipped, SelectionGoal::None)
3467 });
3468 });
3469
3470 let mut indent_edits = Vec::new();
3471 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3472 for row in rows {
3473 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3474 for (row, indent) in indents {
3475 if indent.len == 0 {
3476 continue;
3477 }
3478
3479 let text = match indent.kind {
3480 IndentKind::Space => " ".repeat(indent.len as usize),
3481 IndentKind::Tab => "\t".repeat(indent.len as usize),
3482 };
3483 let point = Point::new(row.0, 0);
3484 indent_edits.push((point..point, text));
3485 }
3486 }
3487 editor.edit(indent_edits, cx);
3488 });
3489 }
3490
3491 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3492 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3493 original_indent_columns: Vec::new(),
3494 });
3495 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3496 }
3497
3498 fn insert_with_autoindent_mode(
3499 &mut self,
3500 text: &str,
3501 autoindent_mode: Option<AutoindentMode>,
3502 window: &mut Window,
3503 cx: &mut Context<Self>,
3504 ) {
3505 if self.read_only(cx) {
3506 return;
3507 }
3508
3509 let text: Arc<str> = text.into();
3510 self.transact(window, cx, |this, window, cx| {
3511 let old_selections = this.selections.all_adjusted(cx);
3512 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3513 let anchors = {
3514 let snapshot = buffer.read(cx);
3515 old_selections
3516 .iter()
3517 .map(|s| {
3518 let anchor = snapshot.anchor_after(s.head());
3519 s.map(|_| anchor)
3520 })
3521 .collect::<Vec<_>>()
3522 };
3523 buffer.edit(
3524 old_selections
3525 .iter()
3526 .map(|s| (s.start..s.end, text.clone())),
3527 autoindent_mode,
3528 cx,
3529 );
3530 anchors
3531 });
3532
3533 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3534 s.select_anchors(selection_anchors);
3535 });
3536
3537 cx.notify();
3538 });
3539 }
3540
3541 fn trigger_completion_on_input(
3542 &mut self,
3543 text: &str,
3544 trigger_in_words: bool,
3545 window: &mut Window,
3546 cx: &mut Context<Self>,
3547 ) {
3548 if self.is_completion_trigger(text, trigger_in_words, cx) {
3549 self.show_completions(
3550 &ShowCompletions {
3551 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3552 },
3553 window,
3554 cx,
3555 );
3556 } else {
3557 self.hide_context_menu(window, cx);
3558 }
3559 }
3560
3561 fn is_completion_trigger(
3562 &self,
3563 text: &str,
3564 trigger_in_words: bool,
3565 cx: &mut Context<Self>,
3566 ) -> bool {
3567 let position = self.selections.newest_anchor().head();
3568 let multibuffer = self.buffer.read(cx);
3569 let Some(buffer) = position
3570 .buffer_id
3571 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3572 else {
3573 return false;
3574 };
3575
3576 if let Some(completion_provider) = &self.completion_provider {
3577 completion_provider.is_completion_trigger(
3578 &buffer,
3579 position.text_anchor,
3580 text,
3581 trigger_in_words,
3582 cx,
3583 )
3584 } else {
3585 false
3586 }
3587 }
3588
3589 /// If any empty selections is touching the start of its innermost containing autoclose
3590 /// region, expand it to select the brackets.
3591 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3592 let selections = self.selections.all::<usize>(cx);
3593 let buffer = self.buffer.read(cx).read(cx);
3594 let new_selections = self
3595 .selections_with_autoclose_regions(selections, &buffer)
3596 .map(|(mut selection, region)| {
3597 if !selection.is_empty() {
3598 return selection;
3599 }
3600
3601 if let Some(region) = region {
3602 let mut range = region.range.to_offset(&buffer);
3603 if selection.start == range.start && range.start >= region.pair.start.len() {
3604 range.start -= region.pair.start.len();
3605 if buffer.contains_str_at(range.start, ®ion.pair.start)
3606 && buffer.contains_str_at(range.end, ®ion.pair.end)
3607 {
3608 range.end += region.pair.end.len();
3609 selection.start = range.start;
3610 selection.end = range.end;
3611
3612 return selection;
3613 }
3614 }
3615 }
3616
3617 let always_treat_brackets_as_autoclosed = buffer
3618 .language_settings_at(selection.start, cx)
3619 .always_treat_brackets_as_autoclosed;
3620
3621 if !always_treat_brackets_as_autoclosed {
3622 return selection;
3623 }
3624
3625 if let Some(scope) = buffer.language_scope_at(selection.start) {
3626 for (pair, enabled) in scope.brackets() {
3627 if !enabled || !pair.close {
3628 continue;
3629 }
3630
3631 if buffer.contains_str_at(selection.start, &pair.end) {
3632 let pair_start_len = pair.start.len();
3633 if buffer.contains_str_at(
3634 selection.start.saturating_sub(pair_start_len),
3635 &pair.start,
3636 ) {
3637 selection.start -= pair_start_len;
3638 selection.end += pair.end.len();
3639
3640 return selection;
3641 }
3642 }
3643 }
3644 }
3645
3646 selection
3647 })
3648 .collect();
3649
3650 drop(buffer);
3651 self.change_selections(None, window, cx, |selections| {
3652 selections.select(new_selections)
3653 });
3654 }
3655
3656 /// Iterate the given selections, and for each one, find the smallest surrounding
3657 /// autoclose region. This uses the ordering of the selections and the autoclose
3658 /// regions to avoid repeated comparisons.
3659 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3660 &'a self,
3661 selections: impl IntoIterator<Item = Selection<D>>,
3662 buffer: &'a MultiBufferSnapshot,
3663 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3664 let mut i = 0;
3665 let mut regions = self.autoclose_regions.as_slice();
3666 selections.into_iter().map(move |selection| {
3667 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3668
3669 let mut enclosing = None;
3670 while let Some(pair_state) = regions.get(i) {
3671 if pair_state.range.end.to_offset(buffer) < range.start {
3672 regions = ®ions[i + 1..];
3673 i = 0;
3674 } else if pair_state.range.start.to_offset(buffer) > range.end {
3675 break;
3676 } else {
3677 if pair_state.selection_id == selection.id {
3678 enclosing = Some(pair_state);
3679 }
3680 i += 1;
3681 }
3682 }
3683
3684 (selection, enclosing)
3685 })
3686 }
3687
3688 /// Remove any autoclose regions that no longer contain their selection.
3689 fn invalidate_autoclose_regions(
3690 &mut self,
3691 mut selections: &[Selection<Anchor>],
3692 buffer: &MultiBufferSnapshot,
3693 ) {
3694 self.autoclose_regions.retain(|state| {
3695 let mut i = 0;
3696 while let Some(selection) = selections.get(i) {
3697 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3698 selections = &selections[1..];
3699 continue;
3700 }
3701 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3702 break;
3703 }
3704 if selection.id == state.selection_id {
3705 return true;
3706 } else {
3707 i += 1;
3708 }
3709 }
3710 false
3711 });
3712 }
3713
3714 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3715 let offset = position.to_offset(buffer);
3716 let (word_range, kind) = buffer.surrounding_word(offset, true);
3717 if offset > word_range.start && kind == Some(CharKind::Word) {
3718 Some(
3719 buffer
3720 .text_for_range(word_range.start..offset)
3721 .collect::<String>(),
3722 )
3723 } else {
3724 None
3725 }
3726 }
3727
3728 pub fn toggle_inlay_hints(
3729 &mut self,
3730 _: &ToggleInlayHints,
3731 _: &mut Window,
3732 cx: &mut Context<Self>,
3733 ) {
3734 self.refresh_inlay_hints(
3735 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3736 cx,
3737 );
3738 }
3739
3740 pub fn inlay_hints_enabled(&self) -> bool {
3741 self.inlay_hint_cache.enabled
3742 }
3743
3744 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3745 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3746 return;
3747 }
3748
3749 let reason_description = reason.description();
3750 let ignore_debounce = matches!(
3751 reason,
3752 InlayHintRefreshReason::SettingsChange(_)
3753 | InlayHintRefreshReason::Toggle(_)
3754 | InlayHintRefreshReason::ExcerptsRemoved(_)
3755 | InlayHintRefreshReason::ModifiersChanged(_)
3756 );
3757 let (invalidate_cache, required_languages) = match reason {
3758 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3759 match self.inlay_hint_cache.modifiers_override(enabled) {
3760 Some(enabled) => {
3761 if enabled {
3762 (InvalidationStrategy::RefreshRequested, None)
3763 } else {
3764 self.splice_inlays(
3765 &self
3766 .visible_inlay_hints(cx)
3767 .iter()
3768 .map(|inlay| inlay.id)
3769 .collect::<Vec<InlayId>>(),
3770 Vec::new(),
3771 cx,
3772 );
3773 return;
3774 }
3775 }
3776 None => return,
3777 }
3778 }
3779 InlayHintRefreshReason::Toggle(enabled) => {
3780 if self.inlay_hint_cache.toggle(enabled) {
3781 if enabled {
3782 (InvalidationStrategy::RefreshRequested, None)
3783 } else {
3784 self.splice_inlays(
3785 &self
3786 .visible_inlay_hints(cx)
3787 .iter()
3788 .map(|inlay| inlay.id)
3789 .collect::<Vec<InlayId>>(),
3790 Vec::new(),
3791 cx,
3792 );
3793 return;
3794 }
3795 } else {
3796 return;
3797 }
3798 }
3799 InlayHintRefreshReason::SettingsChange(new_settings) => {
3800 match self.inlay_hint_cache.update_settings(
3801 &self.buffer,
3802 new_settings,
3803 self.visible_inlay_hints(cx),
3804 cx,
3805 ) {
3806 ControlFlow::Break(Some(InlaySplice {
3807 to_remove,
3808 to_insert,
3809 })) => {
3810 self.splice_inlays(&to_remove, to_insert, cx);
3811 return;
3812 }
3813 ControlFlow::Break(None) => return,
3814 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3815 }
3816 }
3817 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3818 if let Some(InlaySplice {
3819 to_remove,
3820 to_insert,
3821 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3822 {
3823 self.splice_inlays(&to_remove, to_insert, cx);
3824 }
3825 return;
3826 }
3827 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3828 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3829 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3830 }
3831 InlayHintRefreshReason::RefreshRequested => {
3832 (InvalidationStrategy::RefreshRequested, None)
3833 }
3834 };
3835
3836 if let Some(InlaySplice {
3837 to_remove,
3838 to_insert,
3839 }) = self.inlay_hint_cache.spawn_hint_refresh(
3840 reason_description,
3841 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3842 invalidate_cache,
3843 ignore_debounce,
3844 cx,
3845 ) {
3846 self.splice_inlays(&to_remove, to_insert, cx);
3847 }
3848 }
3849
3850 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3851 self.display_map
3852 .read(cx)
3853 .current_inlays()
3854 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3855 .cloned()
3856 .collect()
3857 }
3858
3859 pub fn excerpts_for_inlay_hints_query(
3860 &self,
3861 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3862 cx: &mut Context<Editor>,
3863 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3864 let Some(project) = self.project.as_ref() else {
3865 return HashMap::default();
3866 };
3867 let project = project.read(cx);
3868 let multi_buffer = self.buffer().read(cx);
3869 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3870 let multi_buffer_visible_start = self
3871 .scroll_manager
3872 .anchor()
3873 .anchor
3874 .to_point(&multi_buffer_snapshot);
3875 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3876 multi_buffer_visible_start
3877 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3878 Bias::Left,
3879 );
3880 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3881 multi_buffer_snapshot
3882 .range_to_buffer_ranges(multi_buffer_visible_range)
3883 .into_iter()
3884 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3885 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3886 let buffer_file = project::File::from_dyn(buffer.file())?;
3887 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3888 let worktree_entry = buffer_worktree
3889 .read(cx)
3890 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3891 if worktree_entry.is_ignored {
3892 return None;
3893 }
3894
3895 let language = buffer.language()?;
3896 if let Some(restrict_to_languages) = restrict_to_languages {
3897 if !restrict_to_languages.contains(language) {
3898 return None;
3899 }
3900 }
3901 Some((
3902 excerpt_id,
3903 (
3904 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3905 buffer.version().clone(),
3906 excerpt_visible_range,
3907 ),
3908 ))
3909 })
3910 .collect()
3911 }
3912
3913 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3914 TextLayoutDetails {
3915 text_system: window.text_system().clone(),
3916 editor_style: self.style.clone().unwrap(),
3917 rem_size: window.rem_size(),
3918 scroll_anchor: self.scroll_manager.anchor(),
3919 visible_rows: self.visible_line_count(),
3920 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3921 }
3922 }
3923
3924 pub fn splice_inlays(
3925 &self,
3926 to_remove: &[InlayId],
3927 to_insert: Vec<Inlay>,
3928 cx: &mut Context<Self>,
3929 ) {
3930 self.display_map.update(cx, |display_map, cx| {
3931 display_map.splice_inlays(to_remove, to_insert, cx)
3932 });
3933 cx.notify();
3934 }
3935
3936 fn trigger_on_type_formatting(
3937 &self,
3938 input: String,
3939 window: &mut Window,
3940 cx: &mut Context<Self>,
3941 ) -> Option<Task<Result<()>>> {
3942 if input.len() != 1 {
3943 return None;
3944 }
3945
3946 let project = self.project.as_ref()?;
3947 let position = self.selections.newest_anchor().head();
3948 let (buffer, buffer_position) = self
3949 .buffer
3950 .read(cx)
3951 .text_anchor_for_position(position, cx)?;
3952
3953 let settings = language_settings::language_settings(
3954 buffer
3955 .read(cx)
3956 .language_at(buffer_position)
3957 .map(|l| l.name()),
3958 buffer.read(cx).file(),
3959 cx,
3960 );
3961 if !settings.use_on_type_format {
3962 return None;
3963 }
3964
3965 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3966 // hence we do LSP request & edit on host side only — add formats to host's history.
3967 let push_to_lsp_host_history = true;
3968 // If this is not the host, append its history with new edits.
3969 let push_to_client_history = project.read(cx).is_via_collab();
3970
3971 let on_type_formatting = project.update(cx, |project, cx| {
3972 project.on_type_format(
3973 buffer.clone(),
3974 buffer_position,
3975 input,
3976 push_to_lsp_host_history,
3977 cx,
3978 )
3979 });
3980 Some(cx.spawn_in(window, |editor, mut cx| async move {
3981 if let Some(transaction) = on_type_formatting.await? {
3982 if push_to_client_history {
3983 buffer
3984 .update(&mut cx, |buffer, _| {
3985 buffer.push_transaction(transaction, Instant::now());
3986 })
3987 .ok();
3988 }
3989 editor.update(&mut cx, |editor, cx| {
3990 editor.refresh_document_highlights(cx);
3991 })?;
3992 }
3993 Ok(())
3994 }))
3995 }
3996
3997 pub fn show_completions(
3998 &mut self,
3999 options: &ShowCompletions,
4000 window: &mut Window,
4001 cx: &mut Context<Self>,
4002 ) {
4003 if self.pending_rename.is_some() {
4004 return;
4005 }
4006
4007 let Some(provider) = self.completion_provider.as_ref() else {
4008 return;
4009 };
4010
4011 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4012 return;
4013 }
4014
4015 let position = self.selections.newest_anchor().head();
4016 if position.diff_base_anchor.is_some() {
4017 return;
4018 }
4019 let (buffer, buffer_position) =
4020 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4021 output
4022 } else {
4023 return;
4024 };
4025 let buffer_snapshot = buffer.read(cx).snapshot();
4026 let show_completion_documentation = buffer_snapshot
4027 .settings_at(buffer_position, cx)
4028 .show_completion_documentation;
4029
4030 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4031
4032 let trigger_kind = match &options.trigger {
4033 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4034 CompletionTriggerKind::TRIGGER_CHARACTER
4035 }
4036 _ => CompletionTriggerKind::INVOKED,
4037 };
4038 let completion_context = CompletionContext {
4039 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4040 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4041 Some(String::from(trigger))
4042 } else {
4043 None
4044 }
4045 }),
4046 trigger_kind,
4047 };
4048 let completions =
4049 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4050 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4051 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4052 let word_to_exclude = buffer_snapshot
4053 .text_for_range(old_range.clone())
4054 .collect::<String>();
4055 (
4056 buffer_snapshot.anchor_before(old_range.start)
4057 ..buffer_snapshot.anchor_after(old_range.end),
4058 Some(word_to_exclude),
4059 )
4060 } else {
4061 (buffer_position..buffer_position, None)
4062 };
4063
4064 let completion_settings = language_settings(
4065 buffer_snapshot
4066 .language_at(buffer_position)
4067 .map(|language| language.name()),
4068 buffer_snapshot.file(),
4069 cx,
4070 )
4071 .completions;
4072
4073 // The document can be large, so stay in reasonable bounds when searching for words,
4074 // otherwise completion pop-up might be slow to appear.
4075 const WORD_LOOKUP_ROWS: u32 = 5_000;
4076 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4077 let min_word_search = buffer_snapshot.clip_point(
4078 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4079 Bias::Left,
4080 );
4081 let max_word_search = buffer_snapshot.clip_point(
4082 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4083 Bias::Right,
4084 );
4085 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4086 ..buffer_snapshot.point_to_offset(max_word_search);
4087 let words = match completion_settings.words {
4088 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4089 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => {
4090 cx.background_spawn(async move {
4091 buffer_snapshot.words_in_range(None, word_search_range)
4092 })
4093 }
4094 };
4095 let sort_completions = provider.sort_completions();
4096
4097 let id = post_inc(&mut self.next_completion_id);
4098 let task = cx.spawn_in(window, |editor, mut cx| {
4099 async move {
4100 editor.update(&mut cx, |this, _| {
4101 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4102 })?;
4103 let mut completions = completions.await.log_err().unwrap_or_default();
4104
4105 match completion_settings.words {
4106 WordsCompletionMode::Enabled => {
4107 let mut words = words.await;
4108 if let Some(word_to_exclude) = &word_to_exclude {
4109 words.remove(word_to_exclude);
4110 }
4111 for lsp_completion in &completions {
4112 words.remove(&lsp_completion.new_text);
4113 }
4114 completions.extend(words.into_iter().map(|(word, word_range)| {
4115 Completion {
4116 old_range: old_range.clone(),
4117 new_text: word.clone(),
4118 label: CodeLabel::plain(word, None),
4119 documentation: None,
4120 source: CompletionSource::BufferWord {
4121 word_range,
4122 resolved: false,
4123 },
4124 confirm: None,
4125 }
4126 }));
4127 }
4128 WordsCompletionMode::Fallback => {
4129 if completions.is_empty() {
4130 completions.extend(
4131 words
4132 .await
4133 .into_iter()
4134 .filter(|(word, _)| word_to_exclude.as_ref() != Some(word))
4135 .map(|(word, word_range)| Completion {
4136 old_range: old_range.clone(),
4137 new_text: word.clone(),
4138 label: CodeLabel::plain(word, None),
4139 documentation: None,
4140 source: CompletionSource::BufferWord {
4141 word_range,
4142 resolved: false,
4143 },
4144 confirm: None,
4145 }),
4146 );
4147 }
4148 }
4149 WordsCompletionMode::Disabled => {}
4150 }
4151
4152 let menu = if completions.is_empty() {
4153 None
4154 } else {
4155 let mut menu = CompletionsMenu::new(
4156 id,
4157 sort_completions,
4158 show_completion_documentation,
4159 position,
4160 buffer.clone(),
4161 completions.into(),
4162 );
4163
4164 menu.filter(query.as_deref(), cx.background_executor().clone())
4165 .await;
4166
4167 menu.visible().then_some(menu)
4168 };
4169
4170 editor.update_in(&mut cx, |editor, window, cx| {
4171 match editor.context_menu.borrow().as_ref() {
4172 None => {}
4173 Some(CodeContextMenu::Completions(prev_menu)) => {
4174 if prev_menu.id > id {
4175 return;
4176 }
4177 }
4178 _ => return,
4179 }
4180
4181 if editor.focus_handle.is_focused(window) && menu.is_some() {
4182 let mut menu = menu.unwrap();
4183 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4184
4185 *editor.context_menu.borrow_mut() =
4186 Some(CodeContextMenu::Completions(menu));
4187
4188 if editor.show_edit_predictions_in_menu() {
4189 editor.update_visible_inline_completion(window, cx);
4190 } else {
4191 editor.discard_inline_completion(false, cx);
4192 }
4193
4194 cx.notify();
4195 } else if editor.completion_tasks.len() <= 1 {
4196 // If there are no more completion tasks and the last menu was
4197 // empty, we should hide it.
4198 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4199 // If it was already hidden and we don't show inline
4200 // completions in the menu, we should also show the
4201 // inline-completion when available.
4202 if was_hidden && editor.show_edit_predictions_in_menu() {
4203 editor.update_visible_inline_completion(window, cx);
4204 }
4205 }
4206 })?;
4207
4208 Ok::<_, anyhow::Error>(())
4209 }
4210 .log_err()
4211 });
4212
4213 self.completion_tasks.push((id, task));
4214 }
4215
4216 pub fn confirm_completion(
4217 &mut self,
4218 action: &ConfirmCompletion,
4219 window: &mut Window,
4220 cx: &mut Context<Self>,
4221 ) -> Option<Task<Result<()>>> {
4222 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4223 }
4224
4225 pub fn compose_completion(
4226 &mut self,
4227 action: &ComposeCompletion,
4228 window: &mut Window,
4229 cx: &mut Context<Self>,
4230 ) -> Option<Task<Result<()>>> {
4231 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4232 }
4233
4234 fn do_completion(
4235 &mut self,
4236 item_ix: Option<usize>,
4237 intent: CompletionIntent,
4238 window: &mut Window,
4239 cx: &mut Context<Editor>,
4240 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4241 use language::ToOffset as _;
4242
4243 let completions_menu =
4244 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4245 menu
4246 } else {
4247 return None;
4248 };
4249
4250 let entries = completions_menu.entries.borrow();
4251 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4252 if self.show_edit_predictions_in_menu() {
4253 self.discard_inline_completion(true, cx);
4254 }
4255 let candidate_id = mat.candidate_id;
4256 drop(entries);
4257
4258 let buffer_handle = completions_menu.buffer;
4259 let completion = completions_menu
4260 .completions
4261 .borrow()
4262 .get(candidate_id)?
4263 .clone();
4264 cx.stop_propagation();
4265
4266 let snippet;
4267 let text;
4268
4269 if completion.is_snippet() {
4270 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4271 text = snippet.as_ref().unwrap().text.clone();
4272 } else {
4273 snippet = None;
4274 text = completion.new_text.clone();
4275 };
4276 let selections = self.selections.all::<usize>(cx);
4277 let buffer = buffer_handle.read(cx);
4278 let old_range = completion.old_range.to_offset(buffer);
4279 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4280
4281 let newest_selection = self.selections.newest_anchor();
4282 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4283 return None;
4284 }
4285
4286 let lookbehind = newest_selection
4287 .start
4288 .text_anchor
4289 .to_offset(buffer)
4290 .saturating_sub(old_range.start);
4291 let lookahead = old_range
4292 .end
4293 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4294 let mut common_prefix_len = old_text
4295 .bytes()
4296 .zip(text.bytes())
4297 .take_while(|(a, b)| a == b)
4298 .count();
4299
4300 let snapshot = self.buffer.read(cx).snapshot(cx);
4301 let mut range_to_replace: Option<Range<isize>> = None;
4302 let mut ranges = Vec::new();
4303 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4304 for selection in &selections {
4305 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4306 let start = selection.start.saturating_sub(lookbehind);
4307 let end = selection.end + lookahead;
4308 if selection.id == newest_selection.id {
4309 range_to_replace = Some(
4310 ((start + common_prefix_len) as isize - selection.start as isize)
4311 ..(end as isize - selection.start as isize),
4312 );
4313 }
4314 ranges.push(start + common_prefix_len..end);
4315 } else {
4316 common_prefix_len = 0;
4317 ranges.clear();
4318 ranges.extend(selections.iter().map(|s| {
4319 if s.id == newest_selection.id {
4320 range_to_replace = Some(
4321 old_range.start.to_offset_utf16(&snapshot).0 as isize
4322 - selection.start as isize
4323 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4324 - selection.start as isize,
4325 );
4326 old_range.clone()
4327 } else {
4328 s.start..s.end
4329 }
4330 }));
4331 break;
4332 }
4333 if !self.linked_edit_ranges.is_empty() {
4334 let start_anchor = snapshot.anchor_before(selection.head());
4335 let end_anchor = snapshot.anchor_after(selection.tail());
4336 if let Some(ranges) = self
4337 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4338 {
4339 for (buffer, edits) in ranges {
4340 linked_edits.entry(buffer.clone()).or_default().extend(
4341 edits
4342 .into_iter()
4343 .map(|range| (range, text[common_prefix_len..].to_owned())),
4344 );
4345 }
4346 }
4347 }
4348 }
4349 let text = &text[common_prefix_len..];
4350
4351 cx.emit(EditorEvent::InputHandled {
4352 utf16_range_to_replace: range_to_replace,
4353 text: text.into(),
4354 });
4355
4356 self.transact(window, cx, |this, window, cx| {
4357 if let Some(mut snippet) = snippet {
4358 snippet.text = text.to_string();
4359 for tabstop in snippet
4360 .tabstops
4361 .iter_mut()
4362 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4363 {
4364 tabstop.start -= common_prefix_len as isize;
4365 tabstop.end -= common_prefix_len as isize;
4366 }
4367
4368 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4369 } else {
4370 this.buffer.update(cx, |buffer, cx| {
4371 buffer.edit(
4372 ranges.iter().map(|range| (range.clone(), text)),
4373 this.autoindent_mode.clone(),
4374 cx,
4375 );
4376 });
4377 }
4378 for (buffer, edits) in linked_edits {
4379 buffer.update(cx, |buffer, cx| {
4380 let snapshot = buffer.snapshot();
4381 let edits = edits
4382 .into_iter()
4383 .map(|(range, text)| {
4384 use text::ToPoint as TP;
4385 let end_point = TP::to_point(&range.end, &snapshot);
4386 let start_point = TP::to_point(&range.start, &snapshot);
4387 (start_point..end_point, text)
4388 })
4389 .sorted_by_key(|(range, _)| range.start)
4390 .collect::<Vec<_>>();
4391 buffer.edit(edits, None, cx);
4392 })
4393 }
4394
4395 this.refresh_inline_completion(true, false, window, cx);
4396 });
4397
4398 let show_new_completions_on_confirm = completion
4399 .confirm
4400 .as_ref()
4401 .map_or(false, |confirm| confirm(intent, window, cx));
4402 if show_new_completions_on_confirm {
4403 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4404 }
4405
4406 let provider = self.completion_provider.as_ref()?;
4407 drop(completion);
4408 let apply_edits = provider.apply_additional_edits_for_completion(
4409 buffer_handle,
4410 completions_menu.completions.clone(),
4411 candidate_id,
4412 true,
4413 cx,
4414 );
4415
4416 let editor_settings = EditorSettings::get_global(cx);
4417 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4418 // After the code completion is finished, users often want to know what signatures are needed.
4419 // so we should automatically call signature_help
4420 self.show_signature_help(&ShowSignatureHelp, window, cx);
4421 }
4422
4423 Some(cx.foreground_executor().spawn(async move {
4424 apply_edits.await?;
4425 Ok(())
4426 }))
4427 }
4428
4429 pub fn toggle_code_actions(
4430 &mut self,
4431 action: &ToggleCodeActions,
4432 window: &mut Window,
4433 cx: &mut Context<Self>,
4434 ) {
4435 let mut context_menu = self.context_menu.borrow_mut();
4436 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4437 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4438 // Toggle if we're selecting the same one
4439 *context_menu = None;
4440 cx.notify();
4441 return;
4442 } else {
4443 // Otherwise, clear it and start a new one
4444 *context_menu = None;
4445 cx.notify();
4446 }
4447 }
4448 drop(context_menu);
4449 let snapshot = self.snapshot(window, cx);
4450 let deployed_from_indicator = action.deployed_from_indicator;
4451 let mut task = self.code_actions_task.take();
4452 let action = action.clone();
4453 cx.spawn_in(window, |editor, mut cx| async move {
4454 while let Some(prev_task) = task {
4455 prev_task.await.log_err();
4456 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4457 }
4458
4459 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4460 if editor.focus_handle.is_focused(window) {
4461 let multibuffer_point = action
4462 .deployed_from_indicator
4463 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4464 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4465 let (buffer, buffer_row) = snapshot
4466 .buffer_snapshot
4467 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4468 .and_then(|(buffer_snapshot, range)| {
4469 editor
4470 .buffer
4471 .read(cx)
4472 .buffer(buffer_snapshot.remote_id())
4473 .map(|buffer| (buffer, range.start.row))
4474 })?;
4475 let (_, code_actions) = editor
4476 .available_code_actions
4477 .clone()
4478 .and_then(|(location, code_actions)| {
4479 let snapshot = location.buffer.read(cx).snapshot();
4480 let point_range = location.range.to_point(&snapshot);
4481 let point_range = point_range.start.row..=point_range.end.row;
4482 if point_range.contains(&buffer_row) {
4483 Some((location, code_actions))
4484 } else {
4485 None
4486 }
4487 })
4488 .unzip();
4489 let buffer_id = buffer.read(cx).remote_id();
4490 let tasks = editor
4491 .tasks
4492 .get(&(buffer_id, buffer_row))
4493 .map(|t| Arc::new(t.to_owned()));
4494 if tasks.is_none() && code_actions.is_none() {
4495 return None;
4496 }
4497
4498 editor.completion_tasks.clear();
4499 editor.discard_inline_completion(false, cx);
4500 let task_context =
4501 tasks
4502 .as_ref()
4503 .zip(editor.project.clone())
4504 .map(|(tasks, project)| {
4505 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4506 });
4507
4508 Some(cx.spawn_in(window, |editor, mut cx| async move {
4509 let task_context = match task_context {
4510 Some(task_context) => task_context.await,
4511 None => None,
4512 };
4513 let resolved_tasks =
4514 tasks.zip(task_context).map(|(tasks, task_context)| {
4515 Rc::new(ResolvedTasks {
4516 templates: tasks.resolve(&task_context).collect(),
4517 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4518 multibuffer_point.row,
4519 tasks.column,
4520 )),
4521 })
4522 });
4523 let spawn_straight_away = resolved_tasks
4524 .as_ref()
4525 .map_or(false, |tasks| tasks.templates.len() == 1)
4526 && code_actions
4527 .as_ref()
4528 .map_or(true, |actions| actions.is_empty());
4529 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4530 *editor.context_menu.borrow_mut() =
4531 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4532 buffer,
4533 actions: CodeActionContents {
4534 tasks: resolved_tasks,
4535 actions: code_actions,
4536 },
4537 selected_item: Default::default(),
4538 scroll_handle: UniformListScrollHandle::default(),
4539 deployed_from_indicator,
4540 }));
4541 if spawn_straight_away {
4542 if let Some(task) = editor.confirm_code_action(
4543 &ConfirmCodeAction { item_ix: Some(0) },
4544 window,
4545 cx,
4546 ) {
4547 cx.notify();
4548 return task;
4549 }
4550 }
4551 cx.notify();
4552 Task::ready(Ok(()))
4553 }) {
4554 task.await
4555 } else {
4556 Ok(())
4557 }
4558 }))
4559 } else {
4560 Some(Task::ready(Ok(())))
4561 }
4562 })?;
4563 if let Some(task) = spawned_test_task {
4564 task.await?;
4565 }
4566
4567 Ok::<_, anyhow::Error>(())
4568 })
4569 .detach_and_log_err(cx);
4570 }
4571
4572 pub fn confirm_code_action(
4573 &mut self,
4574 action: &ConfirmCodeAction,
4575 window: &mut Window,
4576 cx: &mut Context<Self>,
4577 ) -> Option<Task<Result<()>>> {
4578 let actions_menu =
4579 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4580 menu
4581 } else {
4582 return None;
4583 };
4584 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4585 let action = actions_menu.actions.get(action_ix)?;
4586 let title = action.label();
4587 let buffer = actions_menu.buffer;
4588 let workspace = self.workspace()?;
4589
4590 match action {
4591 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4592 workspace.update(cx, |workspace, cx| {
4593 workspace::tasks::schedule_resolved_task(
4594 workspace,
4595 task_source_kind,
4596 resolved_task,
4597 false,
4598 cx,
4599 );
4600
4601 Some(Task::ready(Ok(())))
4602 })
4603 }
4604 CodeActionsItem::CodeAction {
4605 excerpt_id,
4606 action,
4607 provider,
4608 } => {
4609 let apply_code_action =
4610 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4611 let workspace = workspace.downgrade();
4612 Some(cx.spawn_in(window, |editor, cx| async move {
4613 let project_transaction = apply_code_action.await?;
4614 Self::open_project_transaction(
4615 &editor,
4616 workspace,
4617 project_transaction,
4618 title,
4619 cx,
4620 )
4621 .await
4622 }))
4623 }
4624 }
4625 }
4626
4627 pub async fn open_project_transaction(
4628 this: &WeakEntity<Editor>,
4629 workspace: WeakEntity<Workspace>,
4630 transaction: ProjectTransaction,
4631 title: String,
4632 mut cx: AsyncWindowContext,
4633 ) -> Result<()> {
4634 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4635 cx.update(|_, cx| {
4636 entries.sort_unstable_by_key(|(buffer, _)| {
4637 buffer.read(cx).file().map(|f| f.path().clone())
4638 });
4639 })?;
4640
4641 // If the project transaction's edits are all contained within this editor, then
4642 // avoid opening a new editor to display them.
4643
4644 if let Some((buffer, transaction)) = entries.first() {
4645 if entries.len() == 1 {
4646 let excerpt = this.update(&mut cx, |editor, cx| {
4647 editor
4648 .buffer()
4649 .read(cx)
4650 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4651 })?;
4652 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4653 if excerpted_buffer == *buffer {
4654 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4655 let excerpt_range = excerpt_range.to_offset(buffer);
4656 buffer
4657 .edited_ranges_for_transaction::<usize>(transaction)
4658 .all(|range| {
4659 excerpt_range.start <= range.start
4660 && excerpt_range.end >= range.end
4661 })
4662 })?;
4663
4664 if all_edits_within_excerpt {
4665 return Ok(());
4666 }
4667 }
4668 }
4669 }
4670 } else {
4671 return Ok(());
4672 }
4673
4674 let mut ranges_to_highlight = Vec::new();
4675 let excerpt_buffer = cx.new(|cx| {
4676 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4677 for (buffer_handle, transaction) in &entries {
4678 let buffer = buffer_handle.read(cx);
4679 ranges_to_highlight.extend(
4680 multibuffer.push_excerpts_with_context_lines(
4681 buffer_handle.clone(),
4682 buffer
4683 .edited_ranges_for_transaction::<usize>(transaction)
4684 .collect(),
4685 DEFAULT_MULTIBUFFER_CONTEXT,
4686 cx,
4687 ),
4688 );
4689 }
4690 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4691 multibuffer
4692 })?;
4693
4694 workspace.update_in(&mut cx, |workspace, window, cx| {
4695 let project = workspace.project().clone();
4696 let editor = cx
4697 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4698 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4699 editor.update(cx, |editor, cx| {
4700 editor.highlight_background::<Self>(
4701 &ranges_to_highlight,
4702 |theme| theme.editor_highlighted_line_background,
4703 cx,
4704 );
4705 });
4706 })?;
4707
4708 Ok(())
4709 }
4710
4711 pub fn clear_code_action_providers(&mut self) {
4712 self.code_action_providers.clear();
4713 self.available_code_actions.take();
4714 }
4715
4716 pub fn add_code_action_provider(
4717 &mut self,
4718 provider: Rc<dyn CodeActionProvider>,
4719 window: &mut Window,
4720 cx: &mut Context<Self>,
4721 ) {
4722 if self
4723 .code_action_providers
4724 .iter()
4725 .any(|existing_provider| existing_provider.id() == provider.id())
4726 {
4727 return;
4728 }
4729
4730 self.code_action_providers.push(provider);
4731 self.refresh_code_actions(window, cx);
4732 }
4733
4734 pub fn remove_code_action_provider(
4735 &mut self,
4736 id: Arc<str>,
4737 window: &mut Window,
4738 cx: &mut Context<Self>,
4739 ) {
4740 self.code_action_providers
4741 .retain(|provider| provider.id() != id);
4742 self.refresh_code_actions(window, cx);
4743 }
4744
4745 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4746 let buffer = self.buffer.read(cx);
4747 let newest_selection = self.selections.newest_anchor().clone();
4748 if newest_selection.head().diff_base_anchor.is_some() {
4749 return None;
4750 }
4751 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4752 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4753 if start_buffer != end_buffer {
4754 return None;
4755 }
4756
4757 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4758 cx.background_executor()
4759 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4760 .await;
4761
4762 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4763 let providers = this.code_action_providers.clone();
4764 let tasks = this
4765 .code_action_providers
4766 .iter()
4767 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4768 .collect::<Vec<_>>();
4769 (providers, tasks)
4770 })?;
4771
4772 let mut actions = Vec::new();
4773 for (provider, provider_actions) in
4774 providers.into_iter().zip(future::join_all(tasks).await)
4775 {
4776 if let Some(provider_actions) = provider_actions.log_err() {
4777 actions.extend(provider_actions.into_iter().map(|action| {
4778 AvailableCodeAction {
4779 excerpt_id: newest_selection.start.excerpt_id,
4780 action,
4781 provider: provider.clone(),
4782 }
4783 }));
4784 }
4785 }
4786
4787 this.update(&mut cx, |this, cx| {
4788 this.available_code_actions = if actions.is_empty() {
4789 None
4790 } else {
4791 Some((
4792 Location {
4793 buffer: start_buffer,
4794 range: start..end,
4795 },
4796 actions.into(),
4797 ))
4798 };
4799 cx.notify();
4800 })
4801 }));
4802 None
4803 }
4804
4805 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4806 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4807 self.show_git_blame_inline = false;
4808
4809 self.show_git_blame_inline_delay_task =
4810 Some(cx.spawn_in(window, |this, mut cx| async move {
4811 cx.background_executor().timer(delay).await;
4812
4813 this.update(&mut cx, |this, cx| {
4814 this.show_git_blame_inline = true;
4815 cx.notify();
4816 })
4817 .log_err();
4818 }));
4819 }
4820 }
4821
4822 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4823 if self.pending_rename.is_some() {
4824 return None;
4825 }
4826
4827 let provider = self.semantics_provider.clone()?;
4828 let buffer = self.buffer.read(cx);
4829 let newest_selection = self.selections.newest_anchor().clone();
4830 let cursor_position = newest_selection.head();
4831 let (cursor_buffer, cursor_buffer_position) =
4832 buffer.text_anchor_for_position(cursor_position, cx)?;
4833 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4834 if cursor_buffer != tail_buffer {
4835 return None;
4836 }
4837 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4838 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4839 cx.background_executor()
4840 .timer(Duration::from_millis(debounce))
4841 .await;
4842
4843 let highlights = if let Some(highlights) = cx
4844 .update(|cx| {
4845 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4846 })
4847 .ok()
4848 .flatten()
4849 {
4850 highlights.await.log_err()
4851 } else {
4852 None
4853 };
4854
4855 if let Some(highlights) = highlights {
4856 this.update(&mut cx, |this, cx| {
4857 if this.pending_rename.is_some() {
4858 return;
4859 }
4860
4861 let buffer_id = cursor_position.buffer_id;
4862 let buffer = this.buffer.read(cx);
4863 if !buffer
4864 .text_anchor_for_position(cursor_position, cx)
4865 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4866 {
4867 return;
4868 }
4869
4870 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4871 let mut write_ranges = Vec::new();
4872 let mut read_ranges = Vec::new();
4873 for highlight in highlights {
4874 for (excerpt_id, excerpt_range) in
4875 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4876 {
4877 let start = highlight
4878 .range
4879 .start
4880 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4881 let end = highlight
4882 .range
4883 .end
4884 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4885 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4886 continue;
4887 }
4888
4889 let range = Anchor {
4890 buffer_id,
4891 excerpt_id,
4892 text_anchor: start,
4893 diff_base_anchor: None,
4894 }..Anchor {
4895 buffer_id,
4896 excerpt_id,
4897 text_anchor: end,
4898 diff_base_anchor: None,
4899 };
4900 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4901 write_ranges.push(range);
4902 } else {
4903 read_ranges.push(range);
4904 }
4905 }
4906 }
4907
4908 this.highlight_background::<DocumentHighlightRead>(
4909 &read_ranges,
4910 |theme| theme.editor_document_highlight_read_background,
4911 cx,
4912 );
4913 this.highlight_background::<DocumentHighlightWrite>(
4914 &write_ranges,
4915 |theme| theme.editor_document_highlight_write_background,
4916 cx,
4917 );
4918 cx.notify();
4919 })
4920 .log_err();
4921 }
4922 }));
4923 None
4924 }
4925
4926 pub fn refresh_selected_text_highlights(
4927 &mut self,
4928 window: &mut Window,
4929 cx: &mut Context<Editor>,
4930 ) {
4931 self.selection_highlight_task.take();
4932 if !EditorSettings::get_global(cx).selection_highlight {
4933 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4934 return;
4935 }
4936 if self.selections.count() != 1 || self.selections.line_mode {
4937 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4938 return;
4939 }
4940 let selection = self.selections.newest::<Point>(cx);
4941 if selection.is_empty() || selection.start.row != selection.end.row {
4942 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4943 return;
4944 }
4945 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4946 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4947 cx.background_executor()
4948 .timer(Duration::from_millis(debounce))
4949 .await;
4950 let Some(Some(matches_task)) = editor
4951 .update_in(&mut cx, |editor, _, cx| {
4952 if editor.selections.count() != 1 || editor.selections.line_mode {
4953 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4954 return None;
4955 }
4956 let selection = editor.selections.newest::<Point>(cx);
4957 if selection.is_empty() || selection.start.row != selection.end.row {
4958 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4959 return None;
4960 }
4961 let buffer = editor.buffer().read(cx).snapshot(cx);
4962 let query = buffer.text_for_range(selection.range()).collect::<String>();
4963 if query.trim().is_empty() {
4964 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4965 return None;
4966 }
4967 Some(cx.background_spawn(async move {
4968 let mut ranges = Vec::new();
4969 let selection_anchors = selection.range().to_anchors(&buffer);
4970 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4971 for (search_buffer, search_range, excerpt_id) in
4972 buffer.range_to_buffer_ranges(range)
4973 {
4974 ranges.extend(
4975 project::search::SearchQuery::text(
4976 query.clone(),
4977 false,
4978 false,
4979 false,
4980 Default::default(),
4981 Default::default(),
4982 None,
4983 )
4984 .unwrap()
4985 .search(search_buffer, Some(search_range.clone()))
4986 .await
4987 .into_iter()
4988 .filter_map(
4989 |match_range| {
4990 let start = search_buffer.anchor_after(
4991 search_range.start + match_range.start,
4992 );
4993 let end = search_buffer.anchor_before(
4994 search_range.start + match_range.end,
4995 );
4996 let range = Anchor::range_in_buffer(
4997 excerpt_id,
4998 search_buffer.remote_id(),
4999 start..end,
5000 );
5001 (range != selection_anchors).then_some(range)
5002 },
5003 ),
5004 );
5005 }
5006 }
5007 ranges
5008 }))
5009 })
5010 .log_err()
5011 else {
5012 return;
5013 };
5014 let matches = matches_task.await;
5015 editor
5016 .update_in(&mut cx, |editor, _, cx| {
5017 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5018 if !matches.is_empty() {
5019 editor.highlight_background::<SelectedTextHighlight>(
5020 &matches,
5021 |theme| theme.editor_document_highlight_bracket_background,
5022 cx,
5023 )
5024 }
5025 })
5026 .log_err();
5027 }));
5028 }
5029
5030 pub fn refresh_inline_completion(
5031 &mut self,
5032 debounce: bool,
5033 user_requested: bool,
5034 window: &mut Window,
5035 cx: &mut Context<Self>,
5036 ) -> Option<()> {
5037 let provider = self.edit_prediction_provider()?;
5038 let cursor = self.selections.newest_anchor().head();
5039 let (buffer, cursor_buffer_position) =
5040 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5041
5042 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5043 self.discard_inline_completion(false, cx);
5044 return None;
5045 }
5046
5047 if !user_requested
5048 && (!self.should_show_edit_predictions()
5049 || !self.is_focused(window)
5050 || buffer.read(cx).is_empty())
5051 {
5052 self.discard_inline_completion(false, cx);
5053 return None;
5054 }
5055
5056 self.update_visible_inline_completion(window, cx);
5057 provider.refresh(
5058 self.project.clone(),
5059 buffer,
5060 cursor_buffer_position,
5061 debounce,
5062 cx,
5063 );
5064 Some(())
5065 }
5066
5067 fn show_edit_predictions_in_menu(&self) -> bool {
5068 match self.edit_prediction_settings {
5069 EditPredictionSettings::Disabled => false,
5070 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5071 }
5072 }
5073
5074 pub fn edit_predictions_enabled(&self) -> bool {
5075 match self.edit_prediction_settings {
5076 EditPredictionSettings::Disabled => false,
5077 EditPredictionSettings::Enabled { .. } => true,
5078 }
5079 }
5080
5081 fn edit_prediction_requires_modifier(&self) -> bool {
5082 match self.edit_prediction_settings {
5083 EditPredictionSettings::Disabled => false,
5084 EditPredictionSettings::Enabled {
5085 preview_requires_modifier,
5086 ..
5087 } => preview_requires_modifier,
5088 }
5089 }
5090
5091 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5092 if self.edit_prediction_provider.is_none() {
5093 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5094 } else {
5095 let selection = self.selections.newest_anchor();
5096 let cursor = selection.head();
5097
5098 if let Some((buffer, cursor_buffer_position)) =
5099 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5100 {
5101 self.edit_prediction_settings =
5102 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5103 }
5104 }
5105 }
5106
5107 fn edit_prediction_settings_at_position(
5108 &self,
5109 buffer: &Entity<Buffer>,
5110 buffer_position: language::Anchor,
5111 cx: &App,
5112 ) -> EditPredictionSettings {
5113 if self.mode != EditorMode::Full
5114 || !self.show_inline_completions_override.unwrap_or(true)
5115 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5116 {
5117 return EditPredictionSettings::Disabled;
5118 }
5119
5120 let buffer = buffer.read(cx);
5121
5122 let file = buffer.file();
5123
5124 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5125 return EditPredictionSettings::Disabled;
5126 };
5127
5128 let by_provider = matches!(
5129 self.menu_inline_completions_policy,
5130 MenuInlineCompletionsPolicy::ByProvider
5131 );
5132
5133 let show_in_menu = by_provider
5134 && self
5135 .edit_prediction_provider
5136 .as_ref()
5137 .map_or(false, |provider| {
5138 provider.provider.show_completions_in_menu()
5139 });
5140
5141 let preview_requires_modifier =
5142 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5143
5144 EditPredictionSettings::Enabled {
5145 show_in_menu,
5146 preview_requires_modifier,
5147 }
5148 }
5149
5150 fn should_show_edit_predictions(&self) -> bool {
5151 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5152 }
5153
5154 pub fn edit_prediction_preview_is_active(&self) -> bool {
5155 matches!(
5156 self.edit_prediction_preview,
5157 EditPredictionPreview::Active { .. }
5158 )
5159 }
5160
5161 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5162 let cursor = self.selections.newest_anchor().head();
5163 if let Some((buffer, cursor_position)) =
5164 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5165 {
5166 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5167 } else {
5168 false
5169 }
5170 }
5171
5172 fn edit_predictions_enabled_in_buffer(
5173 &self,
5174 buffer: &Entity<Buffer>,
5175 buffer_position: language::Anchor,
5176 cx: &App,
5177 ) -> bool {
5178 maybe!({
5179 let provider = self.edit_prediction_provider()?;
5180 if !provider.is_enabled(&buffer, buffer_position, cx) {
5181 return Some(false);
5182 }
5183 let buffer = buffer.read(cx);
5184 let Some(file) = buffer.file() else {
5185 return Some(true);
5186 };
5187 let settings = all_language_settings(Some(file), cx);
5188 Some(settings.edit_predictions_enabled_for_file(file, cx))
5189 })
5190 .unwrap_or(false)
5191 }
5192
5193 fn cycle_inline_completion(
5194 &mut self,
5195 direction: Direction,
5196 window: &mut Window,
5197 cx: &mut Context<Self>,
5198 ) -> Option<()> {
5199 let provider = self.edit_prediction_provider()?;
5200 let cursor = self.selections.newest_anchor().head();
5201 let (buffer, cursor_buffer_position) =
5202 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5203 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5204 return None;
5205 }
5206
5207 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5208 self.update_visible_inline_completion(window, cx);
5209
5210 Some(())
5211 }
5212
5213 pub fn show_inline_completion(
5214 &mut self,
5215 _: &ShowEditPrediction,
5216 window: &mut Window,
5217 cx: &mut Context<Self>,
5218 ) {
5219 if !self.has_active_inline_completion() {
5220 self.refresh_inline_completion(false, true, window, cx);
5221 return;
5222 }
5223
5224 self.update_visible_inline_completion(window, cx);
5225 }
5226
5227 pub fn display_cursor_names(
5228 &mut self,
5229 _: &DisplayCursorNames,
5230 window: &mut Window,
5231 cx: &mut Context<Self>,
5232 ) {
5233 self.show_cursor_names(window, cx);
5234 }
5235
5236 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5237 self.show_cursor_names = true;
5238 cx.notify();
5239 cx.spawn_in(window, |this, mut cx| async move {
5240 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5241 this.update(&mut cx, |this, cx| {
5242 this.show_cursor_names = false;
5243 cx.notify()
5244 })
5245 .ok()
5246 })
5247 .detach();
5248 }
5249
5250 pub fn next_edit_prediction(
5251 &mut self,
5252 _: &NextEditPrediction,
5253 window: &mut Window,
5254 cx: &mut Context<Self>,
5255 ) {
5256 if self.has_active_inline_completion() {
5257 self.cycle_inline_completion(Direction::Next, window, cx);
5258 } else {
5259 let is_copilot_disabled = self
5260 .refresh_inline_completion(false, true, window, cx)
5261 .is_none();
5262 if is_copilot_disabled {
5263 cx.propagate();
5264 }
5265 }
5266 }
5267
5268 pub fn previous_edit_prediction(
5269 &mut self,
5270 _: &PreviousEditPrediction,
5271 window: &mut Window,
5272 cx: &mut Context<Self>,
5273 ) {
5274 if self.has_active_inline_completion() {
5275 self.cycle_inline_completion(Direction::Prev, window, cx);
5276 } else {
5277 let is_copilot_disabled = self
5278 .refresh_inline_completion(false, true, window, cx)
5279 .is_none();
5280 if is_copilot_disabled {
5281 cx.propagate();
5282 }
5283 }
5284 }
5285
5286 pub fn accept_edit_prediction(
5287 &mut self,
5288 _: &AcceptEditPrediction,
5289 window: &mut Window,
5290 cx: &mut Context<Self>,
5291 ) {
5292 if self.show_edit_predictions_in_menu() {
5293 self.hide_context_menu(window, cx);
5294 }
5295
5296 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5297 return;
5298 };
5299
5300 self.report_inline_completion_event(
5301 active_inline_completion.completion_id.clone(),
5302 true,
5303 cx,
5304 );
5305
5306 match &active_inline_completion.completion {
5307 InlineCompletion::Move { target, .. } => {
5308 let target = *target;
5309
5310 if let Some(position_map) = &self.last_position_map {
5311 if position_map
5312 .visible_row_range
5313 .contains(&target.to_display_point(&position_map.snapshot).row())
5314 || !self.edit_prediction_requires_modifier()
5315 {
5316 self.unfold_ranges(&[target..target], true, false, cx);
5317 // Note that this is also done in vim's handler of the Tab action.
5318 self.change_selections(
5319 Some(Autoscroll::newest()),
5320 window,
5321 cx,
5322 |selections| {
5323 selections.select_anchor_ranges([target..target]);
5324 },
5325 );
5326 self.clear_row_highlights::<EditPredictionPreview>();
5327
5328 self.edit_prediction_preview
5329 .set_previous_scroll_position(None);
5330 } else {
5331 self.edit_prediction_preview
5332 .set_previous_scroll_position(Some(
5333 position_map.snapshot.scroll_anchor,
5334 ));
5335
5336 self.highlight_rows::<EditPredictionPreview>(
5337 target..target,
5338 cx.theme().colors().editor_highlighted_line_background,
5339 true,
5340 cx,
5341 );
5342 self.request_autoscroll(Autoscroll::fit(), cx);
5343 }
5344 }
5345 }
5346 InlineCompletion::Edit { edits, .. } => {
5347 if let Some(provider) = self.edit_prediction_provider() {
5348 provider.accept(cx);
5349 }
5350
5351 let snapshot = self.buffer.read(cx).snapshot(cx);
5352 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5353
5354 self.buffer.update(cx, |buffer, cx| {
5355 buffer.edit(edits.iter().cloned(), None, cx)
5356 });
5357
5358 self.change_selections(None, window, cx, |s| {
5359 s.select_anchor_ranges([last_edit_end..last_edit_end])
5360 });
5361
5362 self.update_visible_inline_completion(window, cx);
5363 if self.active_inline_completion.is_none() {
5364 self.refresh_inline_completion(true, true, window, cx);
5365 }
5366
5367 cx.notify();
5368 }
5369 }
5370
5371 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5372 }
5373
5374 pub fn accept_partial_inline_completion(
5375 &mut self,
5376 _: &AcceptPartialEditPrediction,
5377 window: &mut Window,
5378 cx: &mut Context<Self>,
5379 ) {
5380 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5381 return;
5382 };
5383 if self.selections.count() != 1 {
5384 return;
5385 }
5386
5387 self.report_inline_completion_event(
5388 active_inline_completion.completion_id.clone(),
5389 true,
5390 cx,
5391 );
5392
5393 match &active_inline_completion.completion {
5394 InlineCompletion::Move { target, .. } => {
5395 let target = *target;
5396 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5397 selections.select_anchor_ranges([target..target]);
5398 });
5399 }
5400 InlineCompletion::Edit { edits, .. } => {
5401 // Find an insertion that starts at the cursor position.
5402 let snapshot = self.buffer.read(cx).snapshot(cx);
5403 let cursor_offset = self.selections.newest::<usize>(cx).head();
5404 let insertion = edits.iter().find_map(|(range, text)| {
5405 let range = range.to_offset(&snapshot);
5406 if range.is_empty() && range.start == cursor_offset {
5407 Some(text)
5408 } else {
5409 None
5410 }
5411 });
5412
5413 if let Some(text) = insertion {
5414 let mut partial_completion = text
5415 .chars()
5416 .by_ref()
5417 .take_while(|c| c.is_alphabetic())
5418 .collect::<String>();
5419 if partial_completion.is_empty() {
5420 partial_completion = text
5421 .chars()
5422 .by_ref()
5423 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5424 .collect::<String>();
5425 }
5426
5427 cx.emit(EditorEvent::InputHandled {
5428 utf16_range_to_replace: None,
5429 text: partial_completion.clone().into(),
5430 });
5431
5432 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5433
5434 self.refresh_inline_completion(true, true, window, cx);
5435 cx.notify();
5436 } else {
5437 self.accept_edit_prediction(&Default::default(), window, cx);
5438 }
5439 }
5440 }
5441 }
5442
5443 fn discard_inline_completion(
5444 &mut self,
5445 should_report_inline_completion_event: bool,
5446 cx: &mut Context<Self>,
5447 ) -> bool {
5448 if should_report_inline_completion_event {
5449 let completion_id = self
5450 .active_inline_completion
5451 .as_ref()
5452 .and_then(|active_completion| active_completion.completion_id.clone());
5453
5454 self.report_inline_completion_event(completion_id, false, cx);
5455 }
5456
5457 if let Some(provider) = self.edit_prediction_provider() {
5458 provider.discard(cx);
5459 }
5460
5461 self.take_active_inline_completion(cx)
5462 }
5463
5464 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5465 let Some(provider) = self.edit_prediction_provider() else {
5466 return;
5467 };
5468
5469 let Some((_, buffer, _)) = self
5470 .buffer
5471 .read(cx)
5472 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5473 else {
5474 return;
5475 };
5476
5477 let extension = buffer
5478 .read(cx)
5479 .file()
5480 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5481
5482 let event_type = match accepted {
5483 true => "Edit Prediction Accepted",
5484 false => "Edit Prediction Discarded",
5485 };
5486 telemetry::event!(
5487 event_type,
5488 provider = provider.name(),
5489 prediction_id = id,
5490 suggestion_accepted = accepted,
5491 file_extension = extension,
5492 );
5493 }
5494
5495 pub fn has_active_inline_completion(&self) -> bool {
5496 self.active_inline_completion.is_some()
5497 }
5498
5499 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5500 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5501 return false;
5502 };
5503
5504 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5505 self.clear_highlights::<InlineCompletionHighlight>(cx);
5506 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5507 true
5508 }
5509
5510 /// Returns true when we're displaying the edit prediction popover below the cursor
5511 /// like we are not previewing and the LSP autocomplete menu is visible
5512 /// or we are in `when_holding_modifier` mode.
5513 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5514 if self.edit_prediction_preview_is_active()
5515 || !self.show_edit_predictions_in_menu()
5516 || !self.edit_predictions_enabled()
5517 {
5518 return false;
5519 }
5520
5521 if self.has_visible_completions_menu() {
5522 return true;
5523 }
5524
5525 has_completion && self.edit_prediction_requires_modifier()
5526 }
5527
5528 fn handle_modifiers_changed(
5529 &mut self,
5530 modifiers: Modifiers,
5531 position_map: &PositionMap,
5532 window: &mut Window,
5533 cx: &mut Context<Self>,
5534 ) {
5535 if self.show_edit_predictions_in_menu() {
5536 self.update_edit_prediction_preview(&modifiers, window, cx);
5537 }
5538
5539 self.update_selection_mode(&modifiers, position_map, window, cx);
5540
5541 let mouse_position = window.mouse_position();
5542 if !position_map.text_hitbox.is_hovered(window) {
5543 return;
5544 }
5545
5546 self.update_hovered_link(
5547 position_map.point_for_position(mouse_position),
5548 &position_map.snapshot,
5549 modifiers,
5550 window,
5551 cx,
5552 )
5553 }
5554
5555 fn update_selection_mode(
5556 &mut self,
5557 modifiers: &Modifiers,
5558 position_map: &PositionMap,
5559 window: &mut Window,
5560 cx: &mut Context<Self>,
5561 ) {
5562 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5563 return;
5564 }
5565
5566 let mouse_position = window.mouse_position();
5567 let point_for_position = position_map.point_for_position(mouse_position);
5568 let position = point_for_position.previous_valid;
5569
5570 self.select(
5571 SelectPhase::BeginColumnar {
5572 position,
5573 reset: false,
5574 goal_column: point_for_position.exact_unclipped.column(),
5575 },
5576 window,
5577 cx,
5578 );
5579 }
5580
5581 fn update_edit_prediction_preview(
5582 &mut self,
5583 modifiers: &Modifiers,
5584 window: &mut Window,
5585 cx: &mut Context<Self>,
5586 ) {
5587 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5588 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5589 return;
5590 };
5591
5592 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5593 if matches!(
5594 self.edit_prediction_preview,
5595 EditPredictionPreview::Inactive { .. }
5596 ) {
5597 self.edit_prediction_preview = EditPredictionPreview::Active {
5598 previous_scroll_position: None,
5599 since: Instant::now(),
5600 };
5601
5602 self.update_visible_inline_completion(window, cx);
5603 cx.notify();
5604 }
5605 } else if let EditPredictionPreview::Active {
5606 previous_scroll_position,
5607 since,
5608 } = self.edit_prediction_preview
5609 {
5610 if let (Some(previous_scroll_position), Some(position_map)) =
5611 (previous_scroll_position, self.last_position_map.as_ref())
5612 {
5613 self.set_scroll_position(
5614 previous_scroll_position
5615 .scroll_position(&position_map.snapshot.display_snapshot),
5616 window,
5617 cx,
5618 );
5619 }
5620
5621 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5622 released_too_fast: since.elapsed() < Duration::from_millis(200),
5623 };
5624 self.clear_row_highlights::<EditPredictionPreview>();
5625 self.update_visible_inline_completion(window, cx);
5626 cx.notify();
5627 }
5628 }
5629
5630 fn update_visible_inline_completion(
5631 &mut self,
5632 _window: &mut Window,
5633 cx: &mut Context<Self>,
5634 ) -> Option<()> {
5635 let selection = self.selections.newest_anchor();
5636 let cursor = selection.head();
5637 let multibuffer = self.buffer.read(cx).snapshot(cx);
5638 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5639 let excerpt_id = cursor.excerpt_id;
5640
5641 let show_in_menu = self.show_edit_predictions_in_menu();
5642 let completions_menu_has_precedence = !show_in_menu
5643 && (self.context_menu.borrow().is_some()
5644 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5645
5646 if completions_menu_has_precedence
5647 || !offset_selection.is_empty()
5648 || self
5649 .active_inline_completion
5650 .as_ref()
5651 .map_or(false, |completion| {
5652 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5653 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5654 !invalidation_range.contains(&offset_selection.head())
5655 })
5656 {
5657 self.discard_inline_completion(false, cx);
5658 return None;
5659 }
5660
5661 self.take_active_inline_completion(cx);
5662 let Some(provider) = self.edit_prediction_provider() else {
5663 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5664 return None;
5665 };
5666
5667 let (buffer, cursor_buffer_position) =
5668 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5669
5670 self.edit_prediction_settings =
5671 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5672
5673 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5674
5675 if self.edit_prediction_indent_conflict {
5676 let cursor_point = cursor.to_point(&multibuffer);
5677
5678 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5679
5680 if let Some((_, indent)) = indents.iter().next() {
5681 if indent.len == cursor_point.column {
5682 self.edit_prediction_indent_conflict = false;
5683 }
5684 }
5685 }
5686
5687 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5688 let edits = inline_completion
5689 .edits
5690 .into_iter()
5691 .flat_map(|(range, new_text)| {
5692 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5693 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5694 Some((start..end, new_text))
5695 })
5696 .collect::<Vec<_>>();
5697 if edits.is_empty() {
5698 return None;
5699 }
5700
5701 let first_edit_start = edits.first().unwrap().0.start;
5702 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5703 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5704
5705 let last_edit_end = edits.last().unwrap().0.end;
5706 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5707 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5708
5709 let cursor_row = cursor.to_point(&multibuffer).row;
5710
5711 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5712
5713 let mut inlay_ids = Vec::new();
5714 let invalidation_row_range;
5715 let move_invalidation_row_range = if cursor_row < edit_start_row {
5716 Some(cursor_row..edit_end_row)
5717 } else if cursor_row > edit_end_row {
5718 Some(edit_start_row..cursor_row)
5719 } else {
5720 None
5721 };
5722 let is_move =
5723 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5724 let completion = if is_move {
5725 invalidation_row_range =
5726 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5727 let target = first_edit_start;
5728 InlineCompletion::Move { target, snapshot }
5729 } else {
5730 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5731 && !self.inline_completions_hidden_for_vim_mode;
5732
5733 if show_completions_in_buffer {
5734 if edits
5735 .iter()
5736 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5737 {
5738 let mut inlays = Vec::new();
5739 for (range, new_text) in &edits {
5740 let inlay = Inlay::inline_completion(
5741 post_inc(&mut self.next_inlay_id),
5742 range.start,
5743 new_text.as_str(),
5744 );
5745 inlay_ids.push(inlay.id);
5746 inlays.push(inlay);
5747 }
5748
5749 self.splice_inlays(&[], inlays, cx);
5750 } else {
5751 let background_color = cx.theme().status().deleted_background;
5752 self.highlight_text::<InlineCompletionHighlight>(
5753 edits.iter().map(|(range, _)| range.clone()).collect(),
5754 HighlightStyle {
5755 background_color: Some(background_color),
5756 ..Default::default()
5757 },
5758 cx,
5759 );
5760 }
5761 }
5762
5763 invalidation_row_range = edit_start_row..edit_end_row;
5764
5765 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5766 if provider.show_tab_accept_marker() {
5767 EditDisplayMode::TabAccept
5768 } else {
5769 EditDisplayMode::Inline
5770 }
5771 } else {
5772 EditDisplayMode::DiffPopover
5773 };
5774
5775 InlineCompletion::Edit {
5776 edits,
5777 edit_preview: inline_completion.edit_preview,
5778 display_mode,
5779 snapshot,
5780 }
5781 };
5782
5783 let invalidation_range = multibuffer
5784 .anchor_before(Point::new(invalidation_row_range.start, 0))
5785 ..multibuffer.anchor_after(Point::new(
5786 invalidation_row_range.end,
5787 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5788 ));
5789
5790 self.stale_inline_completion_in_menu = None;
5791 self.active_inline_completion = Some(InlineCompletionState {
5792 inlay_ids,
5793 completion,
5794 completion_id: inline_completion.id,
5795 invalidation_range,
5796 });
5797
5798 cx.notify();
5799
5800 Some(())
5801 }
5802
5803 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5804 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5805 }
5806
5807 fn render_code_actions_indicator(
5808 &self,
5809 _style: &EditorStyle,
5810 row: DisplayRow,
5811 is_active: bool,
5812 cx: &mut Context<Self>,
5813 ) -> Option<IconButton> {
5814 if self.available_code_actions.is_some() {
5815 Some(
5816 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5817 .shape(ui::IconButtonShape::Square)
5818 .icon_size(IconSize::XSmall)
5819 .icon_color(Color::Muted)
5820 .toggle_state(is_active)
5821 .tooltip({
5822 let focus_handle = self.focus_handle.clone();
5823 move |window, cx| {
5824 Tooltip::for_action_in(
5825 "Toggle Code Actions",
5826 &ToggleCodeActions {
5827 deployed_from_indicator: None,
5828 },
5829 &focus_handle,
5830 window,
5831 cx,
5832 )
5833 }
5834 })
5835 .on_click(cx.listener(move |editor, _e, window, cx| {
5836 window.focus(&editor.focus_handle(cx));
5837 editor.toggle_code_actions(
5838 &ToggleCodeActions {
5839 deployed_from_indicator: Some(row),
5840 },
5841 window,
5842 cx,
5843 );
5844 })),
5845 )
5846 } else {
5847 None
5848 }
5849 }
5850
5851 fn clear_tasks(&mut self) {
5852 self.tasks.clear()
5853 }
5854
5855 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5856 if self.tasks.insert(key, value).is_some() {
5857 // This case should hopefully be rare, but just in case...
5858 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5859 }
5860 }
5861
5862 fn build_tasks_context(
5863 project: &Entity<Project>,
5864 buffer: &Entity<Buffer>,
5865 buffer_row: u32,
5866 tasks: &Arc<RunnableTasks>,
5867 cx: &mut Context<Self>,
5868 ) -> Task<Option<task::TaskContext>> {
5869 let position = Point::new(buffer_row, tasks.column);
5870 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5871 let location = Location {
5872 buffer: buffer.clone(),
5873 range: range_start..range_start,
5874 };
5875 // Fill in the environmental variables from the tree-sitter captures
5876 let mut captured_task_variables = TaskVariables::default();
5877 for (capture_name, value) in tasks.extra_variables.clone() {
5878 captured_task_variables.insert(
5879 task::VariableName::Custom(capture_name.into()),
5880 value.clone(),
5881 );
5882 }
5883 project.update(cx, |project, cx| {
5884 project.task_store().update(cx, |task_store, cx| {
5885 task_store.task_context_for_location(captured_task_variables, location, cx)
5886 })
5887 })
5888 }
5889
5890 pub fn spawn_nearest_task(
5891 &mut self,
5892 action: &SpawnNearestTask,
5893 window: &mut Window,
5894 cx: &mut Context<Self>,
5895 ) {
5896 let Some((workspace, _)) = self.workspace.clone() else {
5897 return;
5898 };
5899 let Some(project) = self.project.clone() else {
5900 return;
5901 };
5902
5903 // Try to find a closest, enclosing node using tree-sitter that has a
5904 // task
5905 let Some((buffer, buffer_row, tasks)) = self
5906 .find_enclosing_node_task(cx)
5907 // Or find the task that's closest in row-distance.
5908 .or_else(|| self.find_closest_task(cx))
5909 else {
5910 return;
5911 };
5912
5913 let reveal_strategy = action.reveal;
5914 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5915 cx.spawn_in(window, |_, mut cx| async move {
5916 let context = task_context.await?;
5917 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5918
5919 let resolved = resolved_task.resolved.as_mut()?;
5920 resolved.reveal = reveal_strategy;
5921
5922 workspace
5923 .update(&mut cx, |workspace, cx| {
5924 workspace::tasks::schedule_resolved_task(
5925 workspace,
5926 task_source_kind,
5927 resolved_task,
5928 false,
5929 cx,
5930 );
5931 })
5932 .ok()
5933 })
5934 .detach();
5935 }
5936
5937 fn find_closest_task(
5938 &mut self,
5939 cx: &mut Context<Self>,
5940 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5941 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5942
5943 let ((buffer_id, row), tasks) = self
5944 .tasks
5945 .iter()
5946 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5947
5948 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5949 let tasks = Arc::new(tasks.to_owned());
5950 Some((buffer, *row, tasks))
5951 }
5952
5953 fn find_enclosing_node_task(
5954 &mut self,
5955 cx: &mut Context<Self>,
5956 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5957 let snapshot = self.buffer.read(cx).snapshot(cx);
5958 let offset = self.selections.newest::<usize>(cx).head();
5959 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5960 let buffer_id = excerpt.buffer().remote_id();
5961
5962 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5963 let mut cursor = layer.node().walk();
5964
5965 while cursor.goto_first_child_for_byte(offset).is_some() {
5966 if cursor.node().end_byte() == offset {
5967 cursor.goto_next_sibling();
5968 }
5969 }
5970
5971 // Ascend to the smallest ancestor that contains the range and has a task.
5972 loop {
5973 let node = cursor.node();
5974 let node_range = node.byte_range();
5975 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5976
5977 // Check if this node contains our offset
5978 if node_range.start <= offset && node_range.end >= offset {
5979 // If it contains offset, check for task
5980 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5981 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5982 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5983 }
5984 }
5985
5986 if !cursor.goto_parent() {
5987 break;
5988 }
5989 }
5990 None
5991 }
5992
5993 fn render_run_indicator(
5994 &self,
5995 _style: &EditorStyle,
5996 is_active: bool,
5997 row: DisplayRow,
5998 cx: &mut Context<Self>,
5999 ) -> IconButton {
6000 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6001 .shape(ui::IconButtonShape::Square)
6002 .icon_size(IconSize::XSmall)
6003 .icon_color(Color::Muted)
6004 .toggle_state(is_active)
6005 .on_click(cx.listener(move |editor, _e, window, cx| {
6006 window.focus(&editor.focus_handle(cx));
6007 editor.toggle_code_actions(
6008 &ToggleCodeActions {
6009 deployed_from_indicator: Some(row),
6010 },
6011 window,
6012 cx,
6013 );
6014 }))
6015 }
6016
6017 pub fn context_menu_visible(&self) -> bool {
6018 !self.edit_prediction_preview_is_active()
6019 && self
6020 .context_menu
6021 .borrow()
6022 .as_ref()
6023 .map_or(false, |menu| menu.visible())
6024 }
6025
6026 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6027 self.context_menu
6028 .borrow()
6029 .as_ref()
6030 .map(|menu| menu.origin())
6031 }
6032
6033 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6034 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6035
6036 fn render_edit_prediction_popover(
6037 &mut self,
6038 text_bounds: &Bounds<Pixels>,
6039 content_origin: gpui::Point<Pixels>,
6040 editor_snapshot: &EditorSnapshot,
6041 visible_row_range: Range<DisplayRow>,
6042 scroll_top: f32,
6043 scroll_bottom: f32,
6044 line_layouts: &[LineWithInvisibles],
6045 line_height: Pixels,
6046 scroll_pixel_position: gpui::Point<Pixels>,
6047 newest_selection_head: Option<DisplayPoint>,
6048 editor_width: Pixels,
6049 style: &EditorStyle,
6050 window: &mut Window,
6051 cx: &mut App,
6052 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6053 let active_inline_completion = self.active_inline_completion.as_ref()?;
6054
6055 if self.edit_prediction_visible_in_cursor_popover(true) {
6056 return None;
6057 }
6058
6059 match &active_inline_completion.completion {
6060 InlineCompletion::Move { target, .. } => {
6061 let target_display_point = target.to_display_point(editor_snapshot);
6062
6063 if self.edit_prediction_requires_modifier() {
6064 if !self.edit_prediction_preview_is_active() {
6065 return None;
6066 }
6067
6068 self.render_edit_prediction_modifier_jump_popover(
6069 text_bounds,
6070 content_origin,
6071 visible_row_range,
6072 line_layouts,
6073 line_height,
6074 scroll_pixel_position,
6075 newest_selection_head,
6076 target_display_point,
6077 window,
6078 cx,
6079 )
6080 } else {
6081 self.render_edit_prediction_eager_jump_popover(
6082 text_bounds,
6083 content_origin,
6084 editor_snapshot,
6085 visible_row_range,
6086 scroll_top,
6087 scroll_bottom,
6088 line_height,
6089 scroll_pixel_position,
6090 target_display_point,
6091 editor_width,
6092 window,
6093 cx,
6094 )
6095 }
6096 }
6097 InlineCompletion::Edit {
6098 display_mode: EditDisplayMode::Inline,
6099 ..
6100 } => None,
6101 InlineCompletion::Edit {
6102 display_mode: EditDisplayMode::TabAccept,
6103 edits,
6104 ..
6105 } => {
6106 let range = &edits.first()?.0;
6107 let target_display_point = range.end.to_display_point(editor_snapshot);
6108
6109 self.render_edit_prediction_end_of_line_popover(
6110 "Accept",
6111 editor_snapshot,
6112 visible_row_range,
6113 target_display_point,
6114 line_height,
6115 scroll_pixel_position,
6116 content_origin,
6117 editor_width,
6118 window,
6119 cx,
6120 )
6121 }
6122 InlineCompletion::Edit {
6123 edits,
6124 edit_preview,
6125 display_mode: EditDisplayMode::DiffPopover,
6126 snapshot,
6127 } => self.render_edit_prediction_diff_popover(
6128 text_bounds,
6129 content_origin,
6130 editor_snapshot,
6131 visible_row_range,
6132 line_layouts,
6133 line_height,
6134 scroll_pixel_position,
6135 newest_selection_head,
6136 editor_width,
6137 style,
6138 edits,
6139 edit_preview,
6140 snapshot,
6141 window,
6142 cx,
6143 ),
6144 }
6145 }
6146
6147 fn render_edit_prediction_modifier_jump_popover(
6148 &mut self,
6149 text_bounds: &Bounds<Pixels>,
6150 content_origin: gpui::Point<Pixels>,
6151 visible_row_range: Range<DisplayRow>,
6152 line_layouts: &[LineWithInvisibles],
6153 line_height: Pixels,
6154 scroll_pixel_position: gpui::Point<Pixels>,
6155 newest_selection_head: Option<DisplayPoint>,
6156 target_display_point: DisplayPoint,
6157 window: &mut Window,
6158 cx: &mut App,
6159 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6160 let scrolled_content_origin =
6161 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6162
6163 const SCROLL_PADDING_Y: Pixels = px(12.);
6164
6165 if target_display_point.row() < visible_row_range.start {
6166 return self.render_edit_prediction_scroll_popover(
6167 |_| SCROLL_PADDING_Y,
6168 IconName::ArrowUp,
6169 visible_row_range,
6170 line_layouts,
6171 newest_selection_head,
6172 scrolled_content_origin,
6173 window,
6174 cx,
6175 );
6176 } else if target_display_point.row() >= visible_row_range.end {
6177 return self.render_edit_prediction_scroll_popover(
6178 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6179 IconName::ArrowDown,
6180 visible_row_range,
6181 line_layouts,
6182 newest_selection_head,
6183 scrolled_content_origin,
6184 window,
6185 cx,
6186 );
6187 }
6188
6189 const POLE_WIDTH: Pixels = px(2.);
6190
6191 let line_layout =
6192 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6193 let target_column = target_display_point.column() as usize;
6194
6195 let target_x = line_layout.x_for_index(target_column);
6196 let target_y =
6197 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6198
6199 let flag_on_right = target_x < text_bounds.size.width / 2.;
6200
6201 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6202 border_color.l += 0.001;
6203
6204 let mut element = v_flex()
6205 .items_end()
6206 .when(flag_on_right, |el| el.items_start())
6207 .child(if flag_on_right {
6208 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6209 .rounded_bl(px(0.))
6210 .rounded_tl(px(0.))
6211 .border_l_2()
6212 .border_color(border_color)
6213 } else {
6214 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6215 .rounded_br(px(0.))
6216 .rounded_tr(px(0.))
6217 .border_r_2()
6218 .border_color(border_color)
6219 })
6220 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6221 .into_any();
6222
6223 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6224
6225 let mut origin = scrolled_content_origin + point(target_x, target_y)
6226 - point(
6227 if flag_on_right {
6228 POLE_WIDTH
6229 } else {
6230 size.width - POLE_WIDTH
6231 },
6232 size.height - line_height,
6233 );
6234
6235 origin.x = origin.x.max(content_origin.x);
6236
6237 element.prepaint_at(origin, window, cx);
6238
6239 Some((element, origin))
6240 }
6241
6242 fn render_edit_prediction_scroll_popover(
6243 &mut self,
6244 to_y: impl Fn(Size<Pixels>) -> Pixels,
6245 scroll_icon: IconName,
6246 visible_row_range: Range<DisplayRow>,
6247 line_layouts: &[LineWithInvisibles],
6248 newest_selection_head: Option<DisplayPoint>,
6249 scrolled_content_origin: gpui::Point<Pixels>,
6250 window: &mut Window,
6251 cx: &mut App,
6252 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6253 let mut element = self
6254 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6255 .into_any();
6256
6257 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6258
6259 let cursor = newest_selection_head?;
6260 let cursor_row_layout =
6261 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6262 let cursor_column = cursor.column() as usize;
6263
6264 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6265
6266 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6267
6268 element.prepaint_at(origin, window, cx);
6269 Some((element, origin))
6270 }
6271
6272 fn render_edit_prediction_eager_jump_popover(
6273 &mut self,
6274 text_bounds: &Bounds<Pixels>,
6275 content_origin: gpui::Point<Pixels>,
6276 editor_snapshot: &EditorSnapshot,
6277 visible_row_range: Range<DisplayRow>,
6278 scroll_top: f32,
6279 scroll_bottom: f32,
6280 line_height: Pixels,
6281 scroll_pixel_position: gpui::Point<Pixels>,
6282 target_display_point: DisplayPoint,
6283 editor_width: Pixels,
6284 window: &mut Window,
6285 cx: &mut App,
6286 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6287 if target_display_point.row().as_f32() < scroll_top {
6288 let mut element = self
6289 .render_edit_prediction_line_popover(
6290 "Jump to Edit",
6291 Some(IconName::ArrowUp),
6292 window,
6293 cx,
6294 )?
6295 .into_any();
6296
6297 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6298 let offset = point(
6299 (text_bounds.size.width - size.width) / 2.,
6300 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6301 );
6302
6303 let origin = text_bounds.origin + offset;
6304 element.prepaint_at(origin, window, cx);
6305 Some((element, origin))
6306 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6307 let mut element = self
6308 .render_edit_prediction_line_popover(
6309 "Jump to Edit",
6310 Some(IconName::ArrowDown),
6311 window,
6312 cx,
6313 )?
6314 .into_any();
6315
6316 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6317 let offset = point(
6318 (text_bounds.size.width - size.width) / 2.,
6319 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6320 );
6321
6322 let origin = text_bounds.origin + offset;
6323 element.prepaint_at(origin, window, cx);
6324 Some((element, origin))
6325 } else {
6326 self.render_edit_prediction_end_of_line_popover(
6327 "Jump to Edit",
6328 editor_snapshot,
6329 visible_row_range,
6330 target_display_point,
6331 line_height,
6332 scroll_pixel_position,
6333 content_origin,
6334 editor_width,
6335 window,
6336 cx,
6337 )
6338 }
6339 }
6340
6341 fn render_edit_prediction_end_of_line_popover(
6342 self: &mut Editor,
6343 label: &'static str,
6344 editor_snapshot: &EditorSnapshot,
6345 visible_row_range: Range<DisplayRow>,
6346 target_display_point: DisplayPoint,
6347 line_height: Pixels,
6348 scroll_pixel_position: gpui::Point<Pixels>,
6349 content_origin: gpui::Point<Pixels>,
6350 editor_width: Pixels,
6351 window: &mut Window,
6352 cx: &mut App,
6353 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6354 let target_line_end = DisplayPoint::new(
6355 target_display_point.row(),
6356 editor_snapshot.line_len(target_display_point.row()),
6357 );
6358
6359 let mut element = self
6360 .render_edit_prediction_line_popover(label, None, window, cx)?
6361 .into_any();
6362
6363 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6364
6365 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6366
6367 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6368 let mut origin = start_point
6369 + line_origin
6370 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6371 origin.x = origin.x.max(content_origin.x);
6372
6373 let max_x = content_origin.x + editor_width - size.width;
6374
6375 if origin.x > max_x {
6376 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6377
6378 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6379 origin.y += offset;
6380 IconName::ArrowUp
6381 } else {
6382 origin.y -= offset;
6383 IconName::ArrowDown
6384 };
6385
6386 element = self
6387 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6388 .into_any();
6389
6390 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6391
6392 origin.x = content_origin.x + editor_width - size.width - px(2.);
6393 }
6394
6395 element.prepaint_at(origin, window, cx);
6396 Some((element, origin))
6397 }
6398
6399 fn render_edit_prediction_diff_popover(
6400 self: &Editor,
6401 text_bounds: &Bounds<Pixels>,
6402 content_origin: gpui::Point<Pixels>,
6403 editor_snapshot: &EditorSnapshot,
6404 visible_row_range: Range<DisplayRow>,
6405 line_layouts: &[LineWithInvisibles],
6406 line_height: Pixels,
6407 scroll_pixel_position: gpui::Point<Pixels>,
6408 newest_selection_head: Option<DisplayPoint>,
6409 editor_width: Pixels,
6410 style: &EditorStyle,
6411 edits: &Vec<(Range<Anchor>, String)>,
6412 edit_preview: &Option<language::EditPreview>,
6413 snapshot: &language::BufferSnapshot,
6414 window: &mut Window,
6415 cx: &mut App,
6416 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6417 let edit_start = edits
6418 .first()
6419 .unwrap()
6420 .0
6421 .start
6422 .to_display_point(editor_snapshot);
6423 let edit_end = edits
6424 .last()
6425 .unwrap()
6426 .0
6427 .end
6428 .to_display_point(editor_snapshot);
6429
6430 let is_visible = visible_row_range.contains(&edit_start.row())
6431 || visible_row_range.contains(&edit_end.row());
6432 if !is_visible {
6433 return None;
6434 }
6435
6436 let highlighted_edits =
6437 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6438
6439 let styled_text = highlighted_edits.to_styled_text(&style.text);
6440 let line_count = highlighted_edits.text.lines().count();
6441
6442 const BORDER_WIDTH: Pixels = px(1.);
6443
6444 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6445 let has_keybind = keybind.is_some();
6446
6447 let mut element = h_flex()
6448 .items_start()
6449 .child(
6450 h_flex()
6451 .bg(cx.theme().colors().editor_background)
6452 .border(BORDER_WIDTH)
6453 .shadow_sm()
6454 .border_color(cx.theme().colors().border)
6455 .rounded_l_lg()
6456 .when(line_count > 1, |el| el.rounded_br_lg())
6457 .pr_1()
6458 .child(styled_text),
6459 )
6460 .child(
6461 h_flex()
6462 .h(line_height + BORDER_WIDTH * px(2.))
6463 .px_1p5()
6464 .gap_1()
6465 // Workaround: For some reason, there's a gap if we don't do this
6466 .ml(-BORDER_WIDTH)
6467 .shadow(smallvec![gpui::BoxShadow {
6468 color: gpui::black().opacity(0.05),
6469 offset: point(px(1.), px(1.)),
6470 blur_radius: px(2.),
6471 spread_radius: px(0.),
6472 }])
6473 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6474 .border(BORDER_WIDTH)
6475 .border_color(cx.theme().colors().border)
6476 .rounded_r_lg()
6477 .id("edit_prediction_diff_popover_keybind")
6478 .when(!has_keybind, |el| {
6479 let status_colors = cx.theme().status();
6480
6481 el.bg(status_colors.error_background)
6482 .border_color(status_colors.error.opacity(0.6))
6483 .child(Icon::new(IconName::Info).color(Color::Error))
6484 .cursor_default()
6485 .hoverable_tooltip(move |_window, cx| {
6486 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6487 })
6488 })
6489 .children(keybind),
6490 )
6491 .into_any();
6492
6493 let longest_row =
6494 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6495 let longest_line_width = if visible_row_range.contains(&longest_row) {
6496 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6497 } else {
6498 layout_line(
6499 longest_row,
6500 editor_snapshot,
6501 style,
6502 editor_width,
6503 |_| false,
6504 window,
6505 cx,
6506 )
6507 .width
6508 };
6509
6510 let viewport_bounds =
6511 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6512 right: -EditorElement::SCROLLBAR_WIDTH,
6513 ..Default::default()
6514 });
6515
6516 let x_after_longest =
6517 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6518 - scroll_pixel_position.x;
6519
6520 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6521
6522 // Fully visible if it can be displayed within the window (allow overlapping other
6523 // panes). However, this is only allowed if the popover starts within text_bounds.
6524 let can_position_to_the_right = x_after_longest < text_bounds.right()
6525 && x_after_longest + element_bounds.width < viewport_bounds.right();
6526
6527 let mut origin = if can_position_to_the_right {
6528 point(
6529 x_after_longest,
6530 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6531 - scroll_pixel_position.y,
6532 )
6533 } else {
6534 let cursor_row = newest_selection_head.map(|head| head.row());
6535 let above_edit = edit_start
6536 .row()
6537 .0
6538 .checked_sub(line_count as u32)
6539 .map(DisplayRow);
6540 let below_edit = Some(edit_end.row() + 1);
6541 let above_cursor =
6542 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6543 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6544
6545 // Place the edit popover adjacent to the edit if there is a location
6546 // available that is onscreen and does not obscure the cursor. Otherwise,
6547 // place it adjacent to the cursor.
6548 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6549 .into_iter()
6550 .flatten()
6551 .find(|&start_row| {
6552 let end_row = start_row + line_count as u32;
6553 visible_row_range.contains(&start_row)
6554 && visible_row_range.contains(&end_row)
6555 && cursor_row.map_or(true, |cursor_row| {
6556 !((start_row..end_row).contains(&cursor_row))
6557 })
6558 })?;
6559
6560 content_origin
6561 + point(
6562 -scroll_pixel_position.x,
6563 row_target.as_f32() * line_height - scroll_pixel_position.y,
6564 )
6565 };
6566
6567 origin.x -= BORDER_WIDTH;
6568
6569 window.defer_draw(element, origin, 1);
6570
6571 // Do not return an element, since it will already be drawn due to defer_draw.
6572 None
6573 }
6574
6575 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6576 px(30.)
6577 }
6578
6579 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6580 if self.read_only(cx) {
6581 cx.theme().players().read_only()
6582 } else {
6583 self.style.as_ref().unwrap().local_player
6584 }
6585 }
6586
6587 fn render_edit_prediction_accept_keybind(
6588 &self,
6589 window: &mut Window,
6590 cx: &App,
6591 ) -> Option<AnyElement> {
6592 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6593 let accept_keystroke = accept_binding.keystroke()?;
6594
6595 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6596
6597 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6598 Color::Accent
6599 } else {
6600 Color::Muted
6601 };
6602
6603 h_flex()
6604 .px_0p5()
6605 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6606 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6607 .text_size(TextSize::XSmall.rems(cx))
6608 .child(h_flex().children(ui::render_modifiers(
6609 &accept_keystroke.modifiers,
6610 PlatformStyle::platform(),
6611 Some(modifiers_color),
6612 Some(IconSize::XSmall.rems().into()),
6613 true,
6614 )))
6615 .when(is_platform_style_mac, |parent| {
6616 parent.child(accept_keystroke.key.clone())
6617 })
6618 .when(!is_platform_style_mac, |parent| {
6619 parent.child(
6620 Key::new(
6621 util::capitalize(&accept_keystroke.key),
6622 Some(Color::Default),
6623 )
6624 .size(Some(IconSize::XSmall.rems().into())),
6625 )
6626 })
6627 .into_any()
6628 .into()
6629 }
6630
6631 fn render_edit_prediction_line_popover(
6632 &self,
6633 label: impl Into<SharedString>,
6634 icon: Option<IconName>,
6635 window: &mut Window,
6636 cx: &App,
6637 ) -> Option<Stateful<Div>> {
6638 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6639
6640 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6641 let has_keybind = keybind.is_some();
6642
6643 let result = h_flex()
6644 .id("ep-line-popover")
6645 .py_0p5()
6646 .pl_1()
6647 .pr(padding_right)
6648 .gap_1()
6649 .rounded_md()
6650 .border_1()
6651 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6652 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6653 .shadow_sm()
6654 .when(!has_keybind, |el| {
6655 let status_colors = cx.theme().status();
6656
6657 el.bg(status_colors.error_background)
6658 .border_color(status_colors.error.opacity(0.6))
6659 .pl_2()
6660 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
6661 .cursor_default()
6662 .hoverable_tooltip(move |_window, cx| {
6663 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6664 })
6665 })
6666 .children(keybind)
6667 .child(
6668 Label::new(label)
6669 .size(LabelSize::Small)
6670 .when(!has_keybind, |el| el.color(Color::Error).strikethrough()),
6671 )
6672 .when(!has_keybind, |el| {
6673 el.child(
6674 h_flex().ml_1().child(
6675 Icon::new(IconName::Info)
6676 .size(IconSize::Small)
6677 .color(Color::Error),
6678 ),
6679 )
6680 })
6681 .when_some(icon, |element, icon| {
6682 element.child(
6683 div()
6684 .mt(px(1.5))
6685 .child(Icon::new(icon).size(IconSize::Small)),
6686 )
6687 });
6688
6689 Some(result)
6690 }
6691
6692 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6693 let accent_color = cx.theme().colors().text_accent;
6694 let editor_bg_color = cx.theme().colors().editor_background;
6695 editor_bg_color.blend(accent_color.opacity(0.1))
6696 }
6697
6698 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6699 let accent_color = cx.theme().colors().text_accent;
6700 let editor_bg_color = cx.theme().colors().editor_background;
6701 editor_bg_color.blend(accent_color.opacity(0.6))
6702 }
6703
6704 fn render_edit_prediction_cursor_popover(
6705 &self,
6706 min_width: Pixels,
6707 max_width: Pixels,
6708 cursor_point: Point,
6709 style: &EditorStyle,
6710 accept_keystroke: Option<&gpui::Keystroke>,
6711 _window: &Window,
6712 cx: &mut Context<Editor>,
6713 ) -> Option<AnyElement> {
6714 let provider = self.edit_prediction_provider.as_ref()?;
6715
6716 if provider.provider.needs_terms_acceptance(cx) {
6717 return Some(
6718 h_flex()
6719 .min_w(min_width)
6720 .flex_1()
6721 .px_2()
6722 .py_1()
6723 .gap_3()
6724 .elevation_2(cx)
6725 .hover(|style| style.bg(cx.theme().colors().element_hover))
6726 .id("accept-terms")
6727 .cursor_pointer()
6728 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6729 .on_click(cx.listener(|this, _event, window, cx| {
6730 cx.stop_propagation();
6731 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6732 window.dispatch_action(
6733 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6734 cx,
6735 );
6736 }))
6737 .child(
6738 h_flex()
6739 .flex_1()
6740 .gap_2()
6741 .child(Icon::new(IconName::ZedPredict))
6742 .child(Label::new("Accept Terms of Service"))
6743 .child(div().w_full())
6744 .child(
6745 Icon::new(IconName::ArrowUpRight)
6746 .color(Color::Muted)
6747 .size(IconSize::Small),
6748 )
6749 .into_any_element(),
6750 )
6751 .into_any(),
6752 );
6753 }
6754
6755 let is_refreshing = provider.provider.is_refreshing(cx);
6756
6757 fn pending_completion_container() -> Div {
6758 h_flex()
6759 .h_full()
6760 .flex_1()
6761 .gap_2()
6762 .child(Icon::new(IconName::ZedPredict))
6763 }
6764
6765 let completion = match &self.active_inline_completion {
6766 Some(prediction) => {
6767 if !self.has_visible_completions_menu() {
6768 const RADIUS: Pixels = px(6.);
6769 const BORDER_WIDTH: Pixels = px(1.);
6770
6771 return Some(
6772 h_flex()
6773 .elevation_2(cx)
6774 .border(BORDER_WIDTH)
6775 .border_color(cx.theme().colors().border)
6776 .when(accept_keystroke.is_none(), |el| {
6777 el.border_color(cx.theme().status().error)
6778 })
6779 .rounded(RADIUS)
6780 .rounded_tl(px(0.))
6781 .overflow_hidden()
6782 .child(div().px_1p5().child(match &prediction.completion {
6783 InlineCompletion::Move { target, snapshot } => {
6784 use text::ToPoint as _;
6785 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6786 {
6787 Icon::new(IconName::ZedPredictDown)
6788 } else {
6789 Icon::new(IconName::ZedPredictUp)
6790 }
6791 }
6792 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6793 }))
6794 .child(
6795 h_flex()
6796 .gap_1()
6797 .py_1()
6798 .px_2()
6799 .rounded_r(RADIUS - BORDER_WIDTH)
6800 .border_l_1()
6801 .border_color(cx.theme().colors().border)
6802 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6803 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6804 el.child(
6805 Label::new("Hold")
6806 .size(LabelSize::Small)
6807 .when(accept_keystroke.is_none(), |el| {
6808 el.strikethrough()
6809 })
6810 .line_height_style(LineHeightStyle::UiLabel),
6811 )
6812 })
6813 .id("edit_prediction_cursor_popover_keybind")
6814 .when(accept_keystroke.is_none(), |el| {
6815 let status_colors = cx.theme().status();
6816
6817 el.bg(status_colors.error_background)
6818 .border_color(status_colors.error.opacity(0.6))
6819 .child(Icon::new(IconName::Info).color(Color::Error))
6820 .cursor_default()
6821 .hoverable_tooltip(move |_window, cx| {
6822 cx.new(|_| MissingEditPredictionKeybindingTooltip)
6823 .into()
6824 })
6825 })
6826 .when_some(
6827 accept_keystroke.as_ref(),
6828 |el, accept_keystroke| {
6829 el.child(h_flex().children(ui::render_modifiers(
6830 &accept_keystroke.modifiers,
6831 PlatformStyle::platform(),
6832 Some(Color::Default),
6833 Some(IconSize::XSmall.rems().into()),
6834 false,
6835 )))
6836 },
6837 ),
6838 )
6839 .into_any(),
6840 );
6841 }
6842
6843 self.render_edit_prediction_cursor_popover_preview(
6844 prediction,
6845 cursor_point,
6846 style,
6847 cx,
6848 )?
6849 }
6850
6851 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6852 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6853 stale_completion,
6854 cursor_point,
6855 style,
6856 cx,
6857 )?,
6858
6859 None => {
6860 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6861 }
6862 },
6863
6864 None => pending_completion_container().child(Label::new("No Prediction")),
6865 };
6866
6867 let completion = if is_refreshing {
6868 completion
6869 .with_animation(
6870 "loading-completion",
6871 Animation::new(Duration::from_secs(2))
6872 .repeat()
6873 .with_easing(pulsating_between(0.4, 0.8)),
6874 |label, delta| label.opacity(delta),
6875 )
6876 .into_any_element()
6877 } else {
6878 completion.into_any_element()
6879 };
6880
6881 let has_completion = self.active_inline_completion.is_some();
6882
6883 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6884 Some(
6885 h_flex()
6886 .min_w(min_width)
6887 .max_w(max_width)
6888 .flex_1()
6889 .elevation_2(cx)
6890 .border_color(cx.theme().colors().border)
6891 .child(
6892 div()
6893 .flex_1()
6894 .py_1()
6895 .px_2()
6896 .overflow_hidden()
6897 .child(completion),
6898 )
6899 .when_some(accept_keystroke, |el, accept_keystroke| {
6900 if !accept_keystroke.modifiers.modified() {
6901 return el;
6902 }
6903
6904 el.child(
6905 h_flex()
6906 .h_full()
6907 .border_l_1()
6908 .rounded_r_lg()
6909 .border_color(cx.theme().colors().border)
6910 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6911 .gap_1()
6912 .py_1()
6913 .px_2()
6914 .child(
6915 h_flex()
6916 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6917 .when(is_platform_style_mac, |parent| parent.gap_1())
6918 .child(h_flex().children(ui::render_modifiers(
6919 &accept_keystroke.modifiers,
6920 PlatformStyle::platform(),
6921 Some(if !has_completion {
6922 Color::Muted
6923 } else {
6924 Color::Default
6925 }),
6926 None,
6927 false,
6928 ))),
6929 )
6930 .child(Label::new("Preview").into_any_element())
6931 .opacity(if has_completion { 1.0 } else { 0.4 }),
6932 )
6933 })
6934 .into_any(),
6935 )
6936 }
6937
6938 fn render_edit_prediction_cursor_popover_preview(
6939 &self,
6940 completion: &InlineCompletionState,
6941 cursor_point: Point,
6942 style: &EditorStyle,
6943 cx: &mut Context<Editor>,
6944 ) -> Option<Div> {
6945 use text::ToPoint as _;
6946
6947 fn render_relative_row_jump(
6948 prefix: impl Into<String>,
6949 current_row: u32,
6950 target_row: u32,
6951 ) -> Div {
6952 let (row_diff, arrow) = if target_row < current_row {
6953 (current_row - target_row, IconName::ArrowUp)
6954 } else {
6955 (target_row - current_row, IconName::ArrowDown)
6956 };
6957
6958 h_flex()
6959 .child(
6960 Label::new(format!("{}{}", prefix.into(), row_diff))
6961 .color(Color::Muted)
6962 .size(LabelSize::Small),
6963 )
6964 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6965 }
6966
6967 match &completion.completion {
6968 InlineCompletion::Move {
6969 target, snapshot, ..
6970 } => Some(
6971 h_flex()
6972 .px_2()
6973 .gap_2()
6974 .flex_1()
6975 .child(
6976 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
6977 Icon::new(IconName::ZedPredictDown)
6978 } else {
6979 Icon::new(IconName::ZedPredictUp)
6980 },
6981 )
6982 .child(Label::new("Jump to Edit")),
6983 ),
6984
6985 InlineCompletion::Edit {
6986 edits,
6987 edit_preview,
6988 snapshot,
6989 display_mode: _,
6990 } => {
6991 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
6992
6993 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
6994 &snapshot,
6995 &edits,
6996 edit_preview.as_ref()?,
6997 true,
6998 cx,
6999 )
7000 .first_line_preview();
7001
7002 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7003 .with_default_highlights(&style.text, highlighted_edits.highlights);
7004
7005 let preview = h_flex()
7006 .gap_1()
7007 .min_w_16()
7008 .child(styled_text)
7009 .when(has_more_lines, |parent| parent.child("…"));
7010
7011 let left = if first_edit_row != cursor_point.row {
7012 render_relative_row_jump("", cursor_point.row, first_edit_row)
7013 .into_any_element()
7014 } else {
7015 Icon::new(IconName::ZedPredict).into_any_element()
7016 };
7017
7018 Some(
7019 h_flex()
7020 .h_full()
7021 .flex_1()
7022 .gap_2()
7023 .pr_1()
7024 .overflow_x_hidden()
7025 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7026 .child(left)
7027 .child(preview),
7028 )
7029 }
7030 }
7031 }
7032
7033 fn render_context_menu(
7034 &self,
7035 style: &EditorStyle,
7036 max_height_in_lines: u32,
7037 y_flipped: bool,
7038 window: &mut Window,
7039 cx: &mut Context<Editor>,
7040 ) -> Option<AnyElement> {
7041 let menu = self.context_menu.borrow();
7042 let menu = menu.as_ref()?;
7043 if !menu.visible() {
7044 return None;
7045 };
7046 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7047 }
7048
7049 fn render_context_menu_aside(
7050 &mut self,
7051 max_size: Size<Pixels>,
7052 window: &mut Window,
7053 cx: &mut Context<Editor>,
7054 ) -> Option<AnyElement> {
7055 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7056 if menu.visible() {
7057 menu.render_aside(self, max_size, window, cx)
7058 } else {
7059 None
7060 }
7061 })
7062 }
7063
7064 fn hide_context_menu(
7065 &mut self,
7066 window: &mut Window,
7067 cx: &mut Context<Self>,
7068 ) -> Option<CodeContextMenu> {
7069 cx.notify();
7070 self.completion_tasks.clear();
7071 let context_menu = self.context_menu.borrow_mut().take();
7072 self.stale_inline_completion_in_menu.take();
7073 self.update_visible_inline_completion(window, cx);
7074 context_menu
7075 }
7076
7077 fn show_snippet_choices(
7078 &mut self,
7079 choices: &Vec<String>,
7080 selection: Range<Anchor>,
7081 cx: &mut Context<Self>,
7082 ) {
7083 if selection.start.buffer_id.is_none() {
7084 return;
7085 }
7086 let buffer_id = selection.start.buffer_id.unwrap();
7087 let buffer = self.buffer().read(cx).buffer(buffer_id);
7088 let id = post_inc(&mut self.next_completion_id);
7089
7090 if let Some(buffer) = buffer {
7091 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7092 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7093 ));
7094 }
7095 }
7096
7097 pub fn insert_snippet(
7098 &mut self,
7099 insertion_ranges: &[Range<usize>],
7100 snippet: Snippet,
7101 window: &mut Window,
7102 cx: &mut Context<Self>,
7103 ) -> Result<()> {
7104 struct Tabstop<T> {
7105 is_end_tabstop: bool,
7106 ranges: Vec<Range<T>>,
7107 choices: Option<Vec<String>>,
7108 }
7109
7110 let tabstops = self.buffer.update(cx, |buffer, cx| {
7111 let snippet_text: Arc<str> = snippet.text.clone().into();
7112 buffer.edit(
7113 insertion_ranges
7114 .iter()
7115 .cloned()
7116 .map(|range| (range, snippet_text.clone())),
7117 Some(AutoindentMode::EachLine),
7118 cx,
7119 );
7120
7121 let snapshot = &*buffer.read(cx);
7122 let snippet = &snippet;
7123 snippet
7124 .tabstops
7125 .iter()
7126 .map(|tabstop| {
7127 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7128 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7129 });
7130 let mut tabstop_ranges = tabstop
7131 .ranges
7132 .iter()
7133 .flat_map(|tabstop_range| {
7134 let mut delta = 0_isize;
7135 insertion_ranges.iter().map(move |insertion_range| {
7136 let insertion_start = insertion_range.start as isize + delta;
7137 delta +=
7138 snippet.text.len() as isize - insertion_range.len() as isize;
7139
7140 let start = ((insertion_start + tabstop_range.start) as usize)
7141 .min(snapshot.len());
7142 let end = ((insertion_start + tabstop_range.end) as usize)
7143 .min(snapshot.len());
7144 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7145 })
7146 })
7147 .collect::<Vec<_>>();
7148 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7149
7150 Tabstop {
7151 is_end_tabstop,
7152 ranges: tabstop_ranges,
7153 choices: tabstop.choices.clone(),
7154 }
7155 })
7156 .collect::<Vec<_>>()
7157 });
7158 if let Some(tabstop) = tabstops.first() {
7159 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7160 s.select_ranges(tabstop.ranges.iter().cloned());
7161 });
7162
7163 if let Some(choices) = &tabstop.choices {
7164 if let Some(selection) = tabstop.ranges.first() {
7165 self.show_snippet_choices(choices, selection.clone(), cx)
7166 }
7167 }
7168
7169 // If we're already at the last tabstop and it's at the end of the snippet,
7170 // we're done, we don't need to keep the state around.
7171 if !tabstop.is_end_tabstop {
7172 let choices = tabstops
7173 .iter()
7174 .map(|tabstop| tabstop.choices.clone())
7175 .collect();
7176
7177 let ranges = tabstops
7178 .into_iter()
7179 .map(|tabstop| tabstop.ranges)
7180 .collect::<Vec<_>>();
7181
7182 self.snippet_stack.push(SnippetState {
7183 active_index: 0,
7184 ranges,
7185 choices,
7186 });
7187 }
7188
7189 // Check whether the just-entered snippet ends with an auto-closable bracket.
7190 if self.autoclose_regions.is_empty() {
7191 let snapshot = self.buffer.read(cx).snapshot(cx);
7192 for selection in &mut self.selections.all::<Point>(cx) {
7193 let selection_head = selection.head();
7194 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7195 continue;
7196 };
7197
7198 let mut bracket_pair = None;
7199 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7200 let prev_chars = snapshot
7201 .reversed_chars_at(selection_head)
7202 .collect::<String>();
7203 for (pair, enabled) in scope.brackets() {
7204 if enabled
7205 && pair.close
7206 && prev_chars.starts_with(pair.start.as_str())
7207 && next_chars.starts_with(pair.end.as_str())
7208 {
7209 bracket_pair = Some(pair.clone());
7210 break;
7211 }
7212 }
7213 if let Some(pair) = bracket_pair {
7214 let start = snapshot.anchor_after(selection_head);
7215 let end = snapshot.anchor_after(selection_head);
7216 self.autoclose_regions.push(AutocloseRegion {
7217 selection_id: selection.id,
7218 range: start..end,
7219 pair,
7220 });
7221 }
7222 }
7223 }
7224 }
7225 Ok(())
7226 }
7227
7228 pub fn move_to_next_snippet_tabstop(
7229 &mut self,
7230 window: &mut Window,
7231 cx: &mut Context<Self>,
7232 ) -> bool {
7233 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7234 }
7235
7236 pub fn move_to_prev_snippet_tabstop(
7237 &mut self,
7238 window: &mut Window,
7239 cx: &mut Context<Self>,
7240 ) -> bool {
7241 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7242 }
7243
7244 pub fn move_to_snippet_tabstop(
7245 &mut self,
7246 bias: Bias,
7247 window: &mut Window,
7248 cx: &mut Context<Self>,
7249 ) -> bool {
7250 if let Some(mut snippet) = self.snippet_stack.pop() {
7251 match bias {
7252 Bias::Left => {
7253 if snippet.active_index > 0 {
7254 snippet.active_index -= 1;
7255 } else {
7256 self.snippet_stack.push(snippet);
7257 return false;
7258 }
7259 }
7260 Bias::Right => {
7261 if snippet.active_index + 1 < snippet.ranges.len() {
7262 snippet.active_index += 1;
7263 } else {
7264 self.snippet_stack.push(snippet);
7265 return false;
7266 }
7267 }
7268 }
7269 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7270 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7271 s.select_anchor_ranges(current_ranges.iter().cloned())
7272 });
7273
7274 if let Some(choices) = &snippet.choices[snippet.active_index] {
7275 if let Some(selection) = current_ranges.first() {
7276 self.show_snippet_choices(&choices, selection.clone(), cx);
7277 }
7278 }
7279
7280 // If snippet state is not at the last tabstop, push it back on the stack
7281 if snippet.active_index + 1 < snippet.ranges.len() {
7282 self.snippet_stack.push(snippet);
7283 }
7284 return true;
7285 }
7286 }
7287
7288 false
7289 }
7290
7291 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7292 self.transact(window, cx, |this, window, cx| {
7293 this.select_all(&SelectAll, window, cx);
7294 this.insert("", window, cx);
7295 });
7296 }
7297
7298 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7299 self.transact(window, cx, |this, window, cx| {
7300 this.select_autoclose_pair(window, cx);
7301 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7302 if !this.linked_edit_ranges.is_empty() {
7303 let selections = this.selections.all::<MultiBufferPoint>(cx);
7304 let snapshot = this.buffer.read(cx).snapshot(cx);
7305
7306 for selection in selections.iter() {
7307 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7308 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7309 if selection_start.buffer_id != selection_end.buffer_id {
7310 continue;
7311 }
7312 if let Some(ranges) =
7313 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7314 {
7315 for (buffer, entries) in ranges {
7316 linked_ranges.entry(buffer).or_default().extend(entries);
7317 }
7318 }
7319 }
7320 }
7321
7322 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7323 if !this.selections.line_mode {
7324 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7325 for selection in &mut selections {
7326 if selection.is_empty() {
7327 let old_head = selection.head();
7328 let mut new_head =
7329 movement::left(&display_map, old_head.to_display_point(&display_map))
7330 .to_point(&display_map);
7331 if let Some((buffer, line_buffer_range)) = display_map
7332 .buffer_snapshot
7333 .buffer_line_for_row(MultiBufferRow(old_head.row))
7334 {
7335 let indent_size =
7336 buffer.indent_size_for_line(line_buffer_range.start.row);
7337 let indent_len = match indent_size.kind {
7338 IndentKind::Space => {
7339 buffer.settings_at(line_buffer_range.start, cx).tab_size
7340 }
7341 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7342 };
7343 if old_head.column <= indent_size.len && old_head.column > 0 {
7344 let indent_len = indent_len.get();
7345 new_head = cmp::min(
7346 new_head,
7347 MultiBufferPoint::new(
7348 old_head.row,
7349 ((old_head.column - 1) / indent_len) * indent_len,
7350 ),
7351 );
7352 }
7353 }
7354
7355 selection.set_head(new_head, SelectionGoal::None);
7356 }
7357 }
7358 }
7359
7360 this.signature_help_state.set_backspace_pressed(true);
7361 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7362 s.select(selections)
7363 });
7364 this.insert("", window, cx);
7365 let empty_str: Arc<str> = Arc::from("");
7366 for (buffer, edits) in linked_ranges {
7367 let snapshot = buffer.read(cx).snapshot();
7368 use text::ToPoint as TP;
7369
7370 let edits = edits
7371 .into_iter()
7372 .map(|range| {
7373 let end_point = TP::to_point(&range.end, &snapshot);
7374 let mut start_point = TP::to_point(&range.start, &snapshot);
7375
7376 if end_point == start_point {
7377 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7378 .saturating_sub(1);
7379 start_point =
7380 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7381 };
7382
7383 (start_point..end_point, empty_str.clone())
7384 })
7385 .sorted_by_key(|(range, _)| range.start)
7386 .collect::<Vec<_>>();
7387 buffer.update(cx, |this, cx| {
7388 this.edit(edits, None, cx);
7389 })
7390 }
7391 this.refresh_inline_completion(true, false, window, cx);
7392 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7393 });
7394 }
7395
7396 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7397 self.transact(window, cx, |this, window, cx| {
7398 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7399 let line_mode = s.line_mode;
7400 s.move_with(|map, selection| {
7401 if selection.is_empty() && !line_mode {
7402 let cursor = movement::right(map, selection.head());
7403 selection.end = cursor;
7404 selection.reversed = true;
7405 selection.goal = SelectionGoal::None;
7406 }
7407 })
7408 });
7409 this.insert("", window, cx);
7410 this.refresh_inline_completion(true, false, window, cx);
7411 });
7412 }
7413
7414 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7415 if self.move_to_prev_snippet_tabstop(window, cx) {
7416 return;
7417 }
7418
7419 self.outdent(&Outdent, window, cx);
7420 }
7421
7422 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7423 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7424 return;
7425 }
7426
7427 let mut selections = self.selections.all_adjusted(cx);
7428 let buffer = self.buffer.read(cx);
7429 let snapshot = buffer.snapshot(cx);
7430 let rows_iter = selections.iter().map(|s| s.head().row);
7431 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7432
7433 let mut edits = Vec::new();
7434 let mut prev_edited_row = 0;
7435 let mut row_delta = 0;
7436 for selection in &mut selections {
7437 if selection.start.row != prev_edited_row {
7438 row_delta = 0;
7439 }
7440 prev_edited_row = selection.end.row;
7441
7442 // If the selection is non-empty, then increase the indentation of the selected lines.
7443 if !selection.is_empty() {
7444 row_delta =
7445 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7446 continue;
7447 }
7448
7449 // If the selection is empty and the cursor is in the leading whitespace before the
7450 // suggested indentation, then auto-indent the line.
7451 let cursor = selection.head();
7452 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7453 if let Some(suggested_indent) =
7454 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7455 {
7456 if cursor.column < suggested_indent.len
7457 && cursor.column <= current_indent.len
7458 && current_indent.len <= suggested_indent.len
7459 {
7460 selection.start = Point::new(cursor.row, suggested_indent.len);
7461 selection.end = selection.start;
7462 if row_delta == 0 {
7463 edits.extend(Buffer::edit_for_indent_size_adjustment(
7464 cursor.row,
7465 current_indent,
7466 suggested_indent,
7467 ));
7468 row_delta = suggested_indent.len - current_indent.len;
7469 }
7470 continue;
7471 }
7472 }
7473
7474 // Otherwise, insert a hard or soft tab.
7475 let settings = buffer.language_settings_at(cursor, cx);
7476 let tab_size = if settings.hard_tabs {
7477 IndentSize::tab()
7478 } else {
7479 let tab_size = settings.tab_size.get();
7480 let char_column = snapshot
7481 .text_for_range(Point::new(cursor.row, 0)..cursor)
7482 .flat_map(str::chars)
7483 .count()
7484 + row_delta as usize;
7485 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7486 IndentSize::spaces(chars_to_next_tab_stop)
7487 };
7488 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7489 selection.end = selection.start;
7490 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7491 row_delta += tab_size.len;
7492 }
7493
7494 self.transact(window, cx, |this, window, cx| {
7495 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7496 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7497 s.select(selections)
7498 });
7499 this.refresh_inline_completion(true, false, window, cx);
7500 });
7501 }
7502
7503 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7504 if self.read_only(cx) {
7505 return;
7506 }
7507 let mut selections = self.selections.all::<Point>(cx);
7508 let mut prev_edited_row = 0;
7509 let mut row_delta = 0;
7510 let mut edits = Vec::new();
7511 let buffer = self.buffer.read(cx);
7512 let snapshot = buffer.snapshot(cx);
7513 for selection in &mut selections {
7514 if selection.start.row != prev_edited_row {
7515 row_delta = 0;
7516 }
7517 prev_edited_row = selection.end.row;
7518
7519 row_delta =
7520 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7521 }
7522
7523 self.transact(window, cx, |this, window, cx| {
7524 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7525 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7526 s.select(selections)
7527 });
7528 });
7529 }
7530
7531 fn indent_selection(
7532 buffer: &MultiBuffer,
7533 snapshot: &MultiBufferSnapshot,
7534 selection: &mut Selection<Point>,
7535 edits: &mut Vec<(Range<Point>, String)>,
7536 delta_for_start_row: u32,
7537 cx: &App,
7538 ) -> u32 {
7539 let settings = buffer.language_settings_at(selection.start, cx);
7540 let tab_size = settings.tab_size.get();
7541 let indent_kind = if settings.hard_tabs {
7542 IndentKind::Tab
7543 } else {
7544 IndentKind::Space
7545 };
7546 let mut start_row = selection.start.row;
7547 let mut end_row = selection.end.row + 1;
7548
7549 // If a selection ends at the beginning of a line, don't indent
7550 // that last line.
7551 if selection.end.column == 0 && selection.end.row > selection.start.row {
7552 end_row -= 1;
7553 }
7554
7555 // Avoid re-indenting a row that has already been indented by a
7556 // previous selection, but still update this selection's column
7557 // to reflect that indentation.
7558 if delta_for_start_row > 0 {
7559 start_row += 1;
7560 selection.start.column += delta_for_start_row;
7561 if selection.end.row == selection.start.row {
7562 selection.end.column += delta_for_start_row;
7563 }
7564 }
7565
7566 let mut delta_for_end_row = 0;
7567 let has_multiple_rows = start_row + 1 != end_row;
7568 for row in start_row..end_row {
7569 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7570 let indent_delta = match (current_indent.kind, indent_kind) {
7571 (IndentKind::Space, IndentKind::Space) => {
7572 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7573 IndentSize::spaces(columns_to_next_tab_stop)
7574 }
7575 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7576 (_, IndentKind::Tab) => IndentSize::tab(),
7577 };
7578
7579 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7580 0
7581 } else {
7582 selection.start.column
7583 };
7584 let row_start = Point::new(row, start);
7585 edits.push((
7586 row_start..row_start,
7587 indent_delta.chars().collect::<String>(),
7588 ));
7589
7590 // Update this selection's endpoints to reflect the indentation.
7591 if row == selection.start.row {
7592 selection.start.column += indent_delta.len;
7593 }
7594 if row == selection.end.row {
7595 selection.end.column += indent_delta.len;
7596 delta_for_end_row = indent_delta.len;
7597 }
7598 }
7599
7600 if selection.start.row == selection.end.row {
7601 delta_for_start_row + delta_for_end_row
7602 } else {
7603 delta_for_end_row
7604 }
7605 }
7606
7607 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7608 if self.read_only(cx) {
7609 return;
7610 }
7611 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7612 let selections = self.selections.all::<Point>(cx);
7613 let mut deletion_ranges = Vec::new();
7614 let mut last_outdent = None;
7615 {
7616 let buffer = self.buffer.read(cx);
7617 let snapshot = buffer.snapshot(cx);
7618 for selection in &selections {
7619 let settings = buffer.language_settings_at(selection.start, cx);
7620 let tab_size = settings.tab_size.get();
7621 let mut rows = selection.spanned_rows(false, &display_map);
7622
7623 // Avoid re-outdenting a row that has already been outdented by a
7624 // previous selection.
7625 if let Some(last_row) = last_outdent {
7626 if last_row == rows.start {
7627 rows.start = rows.start.next_row();
7628 }
7629 }
7630 let has_multiple_rows = rows.len() > 1;
7631 for row in rows.iter_rows() {
7632 let indent_size = snapshot.indent_size_for_line(row);
7633 if indent_size.len > 0 {
7634 let deletion_len = match indent_size.kind {
7635 IndentKind::Space => {
7636 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7637 if columns_to_prev_tab_stop == 0 {
7638 tab_size
7639 } else {
7640 columns_to_prev_tab_stop
7641 }
7642 }
7643 IndentKind::Tab => 1,
7644 };
7645 let start = if has_multiple_rows
7646 || deletion_len > selection.start.column
7647 || indent_size.len < selection.start.column
7648 {
7649 0
7650 } else {
7651 selection.start.column - deletion_len
7652 };
7653 deletion_ranges.push(
7654 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7655 );
7656 last_outdent = Some(row);
7657 }
7658 }
7659 }
7660 }
7661
7662 self.transact(window, cx, |this, window, cx| {
7663 this.buffer.update(cx, |buffer, cx| {
7664 let empty_str: Arc<str> = Arc::default();
7665 buffer.edit(
7666 deletion_ranges
7667 .into_iter()
7668 .map(|range| (range, empty_str.clone())),
7669 None,
7670 cx,
7671 );
7672 });
7673 let selections = this.selections.all::<usize>(cx);
7674 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7675 s.select(selections)
7676 });
7677 });
7678 }
7679
7680 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7681 if self.read_only(cx) {
7682 return;
7683 }
7684 let selections = self
7685 .selections
7686 .all::<usize>(cx)
7687 .into_iter()
7688 .map(|s| s.range());
7689
7690 self.transact(window, cx, |this, window, cx| {
7691 this.buffer.update(cx, |buffer, cx| {
7692 buffer.autoindent_ranges(selections, cx);
7693 });
7694 let selections = this.selections.all::<usize>(cx);
7695 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7696 s.select(selections)
7697 });
7698 });
7699 }
7700
7701 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7702 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7703 let selections = self.selections.all::<Point>(cx);
7704
7705 let mut new_cursors = Vec::new();
7706 let mut edit_ranges = Vec::new();
7707 let mut selections = selections.iter().peekable();
7708 while let Some(selection) = selections.next() {
7709 let mut rows = selection.spanned_rows(false, &display_map);
7710 let goal_display_column = selection.head().to_display_point(&display_map).column();
7711
7712 // Accumulate contiguous regions of rows that we want to delete.
7713 while let Some(next_selection) = selections.peek() {
7714 let next_rows = next_selection.spanned_rows(false, &display_map);
7715 if next_rows.start <= rows.end {
7716 rows.end = next_rows.end;
7717 selections.next().unwrap();
7718 } else {
7719 break;
7720 }
7721 }
7722
7723 let buffer = &display_map.buffer_snapshot;
7724 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7725 let edit_end;
7726 let cursor_buffer_row;
7727 if buffer.max_point().row >= rows.end.0 {
7728 // If there's a line after the range, delete the \n from the end of the row range
7729 // and position the cursor on the next line.
7730 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7731 cursor_buffer_row = rows.end;
7732 } else {
7733 // If there isn't a line after the range, delete the \n from the line before the
7734 // start of the row range and position the cursor there.
7735 edit_start = edit_start.saturating_sub(1);
7736 edit_end = buffer.len();
7737 cursor_buffer_row = rows.start.previous_row();
7738 }
7739
7740 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7741 *cursor.column_mut() =
7742 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7743
7744 new_cursors.push((
7745 selection.id,
7746 buffer.anchor_after(cursor.to_point(&display_map)),
7747 ));
7748 edit_ranges.push(edit_start..edit_end);
7749 }
7750
7751 self.transact(window, cx, |this, window, cx| {
7752 let buffer = this.buffer.update(cx, |buffer, cx| {
7753 let empty_str: Arc<str> = Arc::default();
7754 buffer.edit(
7755 edit_ranges
7756 .into_iter()
7757 .map(|range| (range, empty_str.clone())),
7758 None,
7759 cx,
7760 );
7761 buffer.snapshot(cx)
7762 });
7763 let new_selections = new_cursors
7764 .into_iter()
7765 .map(|(id, cursor)| {
7766 let cursor = cursor.to_point(&buffer);
7767 Selection {
7768 id,
7769 start: cursor,
7770 end: cursor,
7771 reversed: false,
7772 goal: SelectionGoal::None,
7773 }
7774 })
7775 .collect();
7776
7777 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7778 s.select(new_selections);
7779 });
7780 });
7781 }
7782
7783 pub fn join_lines_impl(
7784 &mut self,
7785 insert_whitespace: bool,
7786 window: &mut Window,
7787 cx: &mut Context<Self>,
7788 ) {
7789 if self.read_only(cx) {
7790 return;
7791 }
7792 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7793 for selection in self.selections.all::<Point>(cx) {
7794 let start = MultiBufferRow(selection.start.row);
7795 // Treat single line selections as if they include the next line. Otherwise this action
7796 // would do nothing for single line selections individual cursors.
7797 let end = if selection.start.row == selection.end.row {
7798 MultiBufferRow(selection.start.row + 1)
7799 } else {
7800 MultiBufferRow(selection.end.row)
7801 };
7802
7803 if let Some(last_row_range) = row_ranges.last_mut() {
7804 if start <= last_row_range.end {
7805 last_row_range.end = end;
7806 continue;
7807 }
7808 }
7809 row_ranges.push(start..end);
7810 }
7811
7812 let snapshot = self.buffer.read(cx).snapshot(cx);
7813 let mut cursor_positions = Vec::new();
7814 for row_range in &row_ranges {
7815 let anchor = snapshot.anchor_before(Point::new(
7816 row_range.end.previous_row().0,
7817 snapshot.line_len(row_range.end.previous_row()),
7818 ));
7819 cursor_positions.push(anchor..anchor);
7820 }
7821
7822 self.transact(window, cx, |this, window, cx| {
7823 for row_range in row_ranges.into_iter().rev() {
7824 for row in row_range.iter_rows().rev() {
7825 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7826 let next_line_row = row.next_row();
7827 let indent = snapshot.indent_size_for_line(next_line_row);
7828 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7829
7830 let replace =
7831 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7832 " "
7833 } else {
7834 ""
7835 };
7836
7837 this.buffer.update(cx, |buffer, cx| {
7838 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7839 });
7840 }
7841 }
7842
7843 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7844 s.select_anchor_ranges(cursor_positions)
7845 });
7846 });
7847 }
7848
7849 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7850 self.join_lines_impl(true, window, cx);
7851 }
7852
7853 pub fn sort_lines_case_sensitive(
7854 &mut self,
7855 _: &SortLinesCaseSensitive,
7856 window: &mut Window,
7857 cx: &mut Context<Self>,
7858 ) {
7859 self.manipulate_lines(window, cx, |lines| lines.sort())
7860 }
7861
7862 pub fn sort_lines_case_insensitive(
7863 &mut self,
7864 _: &SortLinesCaseInsensitive,
7865 window: &mut Window,
7866 cx: &mut Context<Self>,
7867 ) {
7868 self.manipulate_lines(window, cx, |lines| {
7869 lines.sort_by_key(|line| line.to_lowercase())
7870 })
7871 }
7872
7873 pub fn unique_lines_case_insensitive(
7874 &mut self,
7875 _: &UniqueLinesCaseInsensitive,
7876 window: &mut Window,
7877 cx: &mut Context<Self>,
7878 ) {
7879 self.manipulate_lines(window, cx, |lines| {
7880 let mut seen = HashSet::default();
7881 lines.retain(|line| seen.insert(line.to_lowercase()));
7882 })
7883 }
7884
7885 pub fn unique_lines_case_sensitive(
7886 &mut self,
7887 _: &UniqueLinesCaseSensitive,
7888 window: &mut Window,
7889 cx: &mut Context<Self>,
7890 ) {
7891 self.manipulate_lines(window, cx, |lines| {
7892 let mut seen = HashSet::default();
7893 lines.retain(|line| seen.insert(*line));
7894 })
7895 }
7896
7897 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7898 let Some(project) = self.project.clone() else {
7899 return;
7900 };
7901 self.reload(project, window, cx)
7902 .detach_and_notify_err(window, cx);
7903 }
7904
7905 pub fn restore_file(
7906 &mut self,
7907 _: &::git::RestoreFile,
7908 window: &mut Window,
7909 cx: &mut Context<Self>,
7910 ) {
7911 let mut buffer_ids = HashSet::default();
7912 let snapshot = self.buffer().read(cx).snapshot(cx);
7913 for selection in self.selections.all::<usize>(cx) {
7914 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7915 }
7916
7917 let buffer = self.buffer().read(cx);
7918 let ranges = buffer_ids
7919 .into_iter()
7920 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7921 .collect::<Vec<_>>();
7922
7923 self.restore_hunks_in_ranges(ranges, window, cx);
7924 }
7925
7926 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7927 let selections = self
7928 .selections
7929 .all(cx)
7930 .into_iter()
7931 .map(|s| s.range())
7932 .collect();
7933 self.restore_hunks_in_ranges(selections, window, cx);
7934 }
7935
7936 fn restore_hunks_in_ranges(
7937 &mut self,
7938 ranges: Vec<Range<Point>>,
7939 window: &mut Window,
7940 cx: &mut Context<Editor>,
7941 ) {
7942 let mut revert_changes = HashMap::default();
7943 let chunk_by = self
7944 .snapshot(window, cx)
7945 .hunks_for_ranges(ranges)
7946 .into_iter()
7947 .chunk_by(|hunk| hunk.buffer_id);
7948 for (buffer_id, hunks) in &chunk_by {
7949 let hunks = hunks.collect::<Vec<_>>();
7950 for hunk in &hunks {
7951 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7952 }
7953 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
7954 }
7955 drop(chunk_by);
7956 if !revert_changes.is_empty() {
7957 self.transact(window, cx, |editor, window, cx| {
7958 editor.restore(revert_changes, window, cx);
7959 });
7960 }
7961 }
7962
7963 pub fn open_active_item_in_terminal(
7964 &mut self,
7965 _: &OpenInTerminal,
7966 window: &mut Window,
7967 cx: &mut Context<Self>,
7968 ) {
7969 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7970 let project_path = buffer.read(cx).project_path(cx)?;
7971 let project = self.project.as_ref()?.read(cx);
7972 let entry = project.entry_for_path(&project_path, cx)?;
7973 let parent = match &entry.canonical_path {
7974 Some(canonical_path) => canonical_path.to_path_buf(),
7975 None => project.absolute_path(&project_path, cx)?,
7976 }
7977 .parent()?
7978 .to_path_buf();
7979 Some(parent)
7980 }) {
7981 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
7982 }
7983 }
7984
7985 pub fn prepare_restore_change(
7986 &self,
7987 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
7988 hunk: &MultiBufferDiffHunk,
7989 cx: &mut App,
7990 ) -> Option<()> {
7991 if hunk.is_created_file() {
7992 return None;
7993 }
7994 let buffer = self.buffer.read(cx);
7995 let diff = buffer.diff_for(hunk.buffer_id)?;
7996 let buffer = buffer.buffer(hunk.buffer_id)?;
7997 let buffer = buffer.read(cx);
7998 let original_text = diff
7999 .read(cx)
8000 .base_text()
8001 .as_rope()
8002 .slice(hunk.diff_base_byte_range.clone());
8003 let buffer_snapshot = buffer.snapshot();
8004 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8005 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8006 probe
8007 .0
8008 .start
8009 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8010 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8011 }) {
8012 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8013 Some(())
8014 } else {
8015 None
8016 }
8017 }
8018
8019 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8020 self.manipulate_lines(window, cx, |lines| lines.reverse())
8021 }
8022
8023 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8024 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8025 }
8026
8027 fn manipulate_lines<Fn>(
8028 &mut self,
8029 window: &mut Window,
8030 cx: &mut Context<Self>,
8031 mut callback: Fn,
8032 ) where
8033 Fn: FnMut(&mut Vec<&str>),
8034 {
8035 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8036 let buffer = self.buffer.read(cx).snapshot(cx);
8037
8038 let mut edits = Vec::new();
8039
8040 let selections = self.selections.all::<Point>(cx);
8041 let mut selections = selections.iter().peekable();
8042 let mut contiguous_row_selections = Vec::new();
8043 let mut new_selections = Vec::new();
8044 let mut added_lines = 0;
8045 let mut removed_lines = 0;
8046
8047 while let Some(selection) = selections.next() {
8048 let (start_row, end_row) = consume_contiguous_rows(
8049 &mut contiguous_row_selections,
8050 selection,
8051 &display_map,
8052 &mut selections,
8053 );
8054
8055 let start_point = Point::new(start_row.0, 0);
8056 let end_point = Point::new(
8057 end_row.previous_row().0,
8058 buffer.line_len(end_row.previous_row()),
8059 );
8060 let text = buffer
8061 .text_for_range(start_point..end_point)
8062 .collect::<String>();
8063
8064 let mut lines = text.split('\n').collect_vec();
8065
8066 let lines_before = lines.len();
8067 callback(&mut lines);
8068 let lines_after = lines.len();
8069
8070 edits.push((start_point..end_point, lines.join("\n")));
8071
8072 // Selections must change based on added and removed line count
8073 let start_row =
8074 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8075 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8076 new_selections.push(Selection {
8077 id: selection.id,
8078 start: start_row,
8079 end: end_row,
8080 goal: SelectionGoal::None,
8081 reversed: selection.reversed,
8082 });
8083
8084 if lines_after > lines_before {
8085 added_lines += lines_after - lines_before;
8086 } else if lines_before > lines_after {
8087 removed_lines += lines_before - lines_after;
8088 }
8089 }
8090
8091 self.transact(window, cx, |this, window, cx| {
8092 let buffer = this.buffer.update(cx, |buffer, cx| {
8093 buffer.edit(edits, None, cx);
8094 buffer.snapshot(cx)
8095 });
8096
8097 // Recalculate offsets on newly edited buffer
8098 let new_selections = new_selections
8099 .iter()
8100 .map(|s| {
8101 let start_point = Point::new(s.start.0, 0);
8102 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8103 Selection {
8104 id: s.id,
8105 start: buffer.point_to_offset(start_point),
8106 end: buffer.point_to_offset(end_point),
8107 goal: s.goal,
8108 reversed: s.reversed,
8109 }
8110 })
8111 .collect();
8112
8113 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8114 s.select(new_selections);
8115 });
8116
8117 this.request_autoscroll(Autoscroll::fit(), cx);
8118 });
8119 }
8120
8121 pub fn convert_to_upper_case(
8122 &mut self,
8123 _: &ConvertToUpperCase,
8124 window: &mut Window,
8125 cx: &mut Context<Self>,
8126 ) {
8127 self.manipulate_text(window, cx, |text| text.to_uppercase())
8128 }
8129
8130 pub fn convert_to_lower_case(
8131 &mut self,
8132 _: &ConvertToLowerCase,
8133 window: &mut Window,
8134 cx: &mut Context<Self>,
8135 ) {
8136 self.manipulate_text(window, cx, |text| text.to_lowercase())
8137 }
8138
8139 pub fn convert_to_title_case(
8140 &mut self,
8141 _: &ConvertToTitleCase,
8142 window: &mut Window,
8143 cx: &mut Context<Self>,
8144 ) {
8145 self.manipulate_text(window, cx, |text| {
8146 text.split('\n')
8147 .map(|line| line.to_case(Case::Title))
8148 .join("\n")
8149 })
8150 }
8151
8152 pub fn convert_to_snake_case(
8153 &mut self,
8154 _: &ConvertToSnakeCase,
8155 window: &mut Window,
8156 cx: &mut Context<Self>,
8157 ) {
8158 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8159 }
8160
8161 pub fn convert_to_kebab_case(
8162 &mut self,
8163 _: &ConvertToKebabCase,
8164 window: &mut Window,
8165 cx: &mut Context<Self>,
8166 ) {
8167 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8168 }
8169
8170 pub fn convert_to_upper_camel_case(
8171 &mut self,
8172 _: &ConvertToUpperCamelCase,
8173 window: &mut Window,
8174 cx: &mut Context<Self>,
8175 ) {
8176 self.manipulate_text(window, cx, |text| {
8177 text.split('\n')
8178 .map(|line| line.to_case(Case::UpperCamel))
8179 .join("\n")
8180 })
8181 }
8182
8183 pub fn convert_to_lower_camel_case(
8184 &mut self,
8185 _: &ConvertToLowerCamelCase,
8186 window: &mut Window,
8187 cx: &mut Context<Self>,
8188 ) {
8189 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8190 }
8191
8192 pub fn convert_to_opposite_case(
8193 &mut self,
8194 _: &ConvertToOppositeCase,
8195 window: &mut Window,
8196 cx: &mut Context<Self>,
8197 ) {
8198 self.manipulate_text(window, cx, |text| {
8199 text.chars()
8200 .fold(String::with_capacity(text.len()), |mut t, c| {
8201 if c.is_uppercase() {
8202 t.extend(c.to_lowercase());
8203 } else {
8204 t.extend(c.to_uppercase());
8205 }
8206 t
8207 })
8208 })
8209 }
8210
8211 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8212 where
8213 Fn: FnMut(&str) -> String,
8214 {
8215 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8216 let buffer = self.buffer.read(cx).snapshot(cx);
8217
8218 let mut new_selections = Vec::new();
8219 let mut edits = Vec::new();
8220 let mut selection_adjustment = 0i32;
8221
8222 for selection in self.selections.all::<usize>(cx) {
8223 let selection_is_empty = selection.is_empty();
8224
8225 let (start, end) = if selection_is_empty {
8226 let word_range = movement::surrounding_word(
8227 &display_map,
8228 selection.start.to_display_point(&display_map),
8229 );
8230 let start = word_range.start.to_offset(&display_map, Bias::Left);
8231 let end = word_range.end.to_offset(&display_map, Bias::Left);
8232 (start, end)
8233 } else {
8234 (selection.start, selection.end)
8235 };
8236
8237 let text = buffer.text_for_range(start..end).collect::<String>();
8238 let old_length = text.len() as i32;
8239 let text = callback(&text);
8240
8241 new_selections.push(Selection {
8242 start: (start as i32 - selection_adjustment) as usize,
8243 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8244 goal: SelectionGoal::None,
8245 ..selection
8246 });
8247
8248 selection_adjustment += old_length - text.len() as i32;
8249
8250 edits.push((start..end, text));
8251 }
8252
8253 self.transact(window, cx, |this, window, cx| {
8254 this.buffer.update(cx, |buffer, cx| {
8255 buffer.edit(edits, None, cx);
8256 });
8257
8258 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8259 s.select(new_selections);
8260 });
8261
8262 this.request_autoscroll(Autoscroll::fit(), cx);
8263 });
8264 }
8265
8266 pub fn duplicate(
8267 &mut self,
8268 upwards: bool,
8269 whole_lines: bool,
8270 window: &mut Window,
8271 cx: &mut Context<Self>,
8272 ) {
8273 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8274 let buffer = &display_map.buffer_snapshot;
8275 let selections = self.selections.all::<Point>(cx);
8276
8277 let mut edits = Vec::new();
8278 let mut selections_iter = selections.iter().peekable();
8279 while let Some(selection) = selections_iter.next() {
8280 let mut rows = selection.spanned_rows(false, &display_map);
8281 // duplicate line-wise
8282 if whole_lines || selection.start == selection.end {
8283 // Avoid duplicating the same lines twice.
8284 while let Some(next_selection) = selections_iter.peek() {
8285 let next_rows = next_selection.spanned_rows(false, &display_map);
8286 if next_rows.start < rows.end {
8287 rows.end = next_rows.end;
8288 selections_iter.next().unwrap();
8289 } else {
8290 break;
8291 }
8292 }
8293
8294 // Copy the text from the selected row region and splice it either at the start
8295 // or end of the region.
8296 let start = Point::new(rows.start.0, 0);
8297 let end = Point::new(
8298 rows.end.previous_row().0,
8299 buffer.line_len(rows.end.previous_row()),
8300 );
8301 let text = buffer
8302 .text_for_range(start..end)
8303 .chain(Some("\n"))
8304 .collect::<String>();
8305 let insert_location = if upwards {
8306 Point::new(rows.end.0, 0)
8307 } else {
8308 start
8309 };
8310 edits.push((insert_location..insert_location, text));
8311 } else {
8312 // duplicate character-wise
8313 let start = selection.start;
8314 let end = selection.end;
8315 let text = buffer.text_for_range(start..end).collect::<String>();
8316 edits.push((selection.end..selection.end, text));
8317 }
8318 }
8319
8320 self.transact(window, cx, |this, _, cx| {
8321 this.buffer.update(cx, |buffer, cx| {
8322 buffer.edit(edits, None, cx);
8323 });
8324
8325 this.request_autoscroll(Autoscroll::fit(), cx);
8326 });
8327 }
8328
8329 pub fn duplicate_line_up(
8330 &mut self,
8331 _: &DuplicateLineUp,
8332 window: &mut Window,
8333 cx: &mut Context<Self>,
8334 ) {
8335 self.duplicate(true, true, window, cx);
8336 }
8337
8338 pub fn duplicate_line_down(
8339 &mut self,
8340 _: &DuplicateLineDown,
8341 window: &mut Window,
8342 cx: &mut Context<Self>,
8343 ) {
8344 self.duplicate(false, true, window, cx);
8345 }
8346
8347 pub fn duplicate_selection(
8348 &mut self,
8349 _: &DuplicateSelection,
8350 window: &mut Window,
8351 cx: &mut Context<Self>,
8352 ) {
8353 self.duplicate(false, false, window, cx);
8354 }
8355
8356 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8357 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8358 let buffer = self.buffer.read(cx).snapshot(cx);
8359
8360 let mut edits = Vec::new();
8361 let mut unfold_ranges = Vec::new();
8362 let mut refold_creases = Vec::new();
8363
8364 let selections = self.selections.all::<Point>(cx);
8365 let mut selections = selections.iter().peekable();
8366 let mut contiguous_row_selections = Vec::new();
8367 let mut new_selections = Vec::new();
8368
8369 while let Some(selection) = selections.next() {
8370 // Find all the selections that span a contiguous row range
8371 let (start_row, end_row) = consume_contiguous_rows(
8372 &mut contiguous_row_selections,
8373 selection,
8374 &display_map,
8375 &mut selections,
8376 );
8377
8378 // Move the text spanned by the row range to be before the line preceding the row range
8379 if start_row.0 > 0 {
8380 let range_to_move = Point::new(
8381 start_row.previous_row().0,
8382 buffer.line_len(start_row.previous_row()),
8383 )
8384 ..Point::new(
8385 end_row.previous_row().0,
8386 buffer.line_len(end_row.previous_row()),
8387 );
8388 let insertion_point = display_map
8389 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8390 .0;
8391
8392 // Don't move lines across excerpts
8393 if buffer
8394 .excerpt_containing(insertion_point..range_to_move.end)
8395 .is_some()
8396 {
8397 let text = buffer
8398 .text_for_range(range_to_move.clone())
8399 .flat_map(|s| s.chars())
8400 .skip(1)
8401 .chain(['\n'])
8402 .collect::<String>();
8403
8404 edits.push((
8405 buffer.anchor_after(range_to_move.start)
8406 ..buffer.anchor_before(range_to_move.end),
8407 String::new(),
8408 ));
8409 let insertion_anchor = buffer.anchor_after(insertion_point);
8410 edits.push((insertion_anchor..insertion_anchor, text));
8411
8412 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8413
8414 // Move selections up
8415 new_selections.extend(contiguous_row_selections.drain(..).map(
8416 |mut selection| {
8417 selection.start.row -= row_delta;
8418 selection.end.row -= row_delta;
8419 selection
8420 },
8421 ));
8422
8423 // Move folds up
8424 unfold_ranges.push(range_to_move.clone());
8425 for fold in display_map.folds_in_range(
8426 buffer.anchor_before(range_to_move.start)
8427 ..buffer.anchor_after(range_to_move.end),
8428 ) {
8429 let mut start = fold.range.start.to_point(&buffer);
8430 let mut end = fold.range.end.to_point(&buffer);
8431 start.row -= row_delta;
8432 end.row -= row_delta;
8433 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8434 }
8435 }
8436 }
8437
8438 // If we didn't move line(s), preserve the existing selections
8439 new_selections.append(&mut contiguous_row_selections);
8440 }
8441
8442 self.transact(window, cx, |this, window, cx| {
8443 this.unfold_ranges(&unfold_ranges, true, true, cx);
8444 this.buffer.update(cx, |buffer, cx| {
8445 for (range, text) in edits {
8446 buffer.edit([(range, text)], None, cx);
8447 }
8448 });
8449 this.fold_creases(refold_creases, true, window, cx);
8450 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8451 s.select(new_selections);
8452 })
8453 });
8454 }
8455
8456 pub fn move_line_down(
8457 &mut self,
8458 _: &MoveLineDown,
8459 window: &mut Window,
8460 cx: &mut Context<Self>,
8461 ) {
8462 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8463 let buffer = self.buffer.read(cx).snapshot(cx);
8464
8465 let mut edits = Vec::new();
8466 let mut unfold_ranges = Vec::new();
8467 let mut refold_creases = Vec::new();
8468
8469 let selections = self.selections.all::<Point>(cx);
8470 let mut selections = selections.iter().peekable();
8471 let mut contiguous_row_selections = Vec::new();
8472 let mut new_selections = Vec::new();
8473
8474 while let Some(selection) = selections.next() {
8475 // Find all the selections that span a contiguous row range
8476 let (start_row, end_row) = consume_contiguous_rows(
8477 &mut contiguous_row_selections,
8478 selection,
8479 &display_map,
8480 &mut selections,
8481 );
8482
8483 // Move the text spanned by the row range to be after the last line of the row range
8484 if end_row.0 <= buffer.max_point().row {
8485 let range_to_move =
8486 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8487 let insertion_point = display_map
8488 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8489 .0;
8490
8491 // Don't move lines across excerpt boundaries
8492 if buffer
8493 .excerpt_containing(range_to_move.start..insertion_point)
8494 .is_some()
8495 {
8496 let mut text = String::from("\n");
8497 text.extend(buffer.text_for_range(range_to_move.clone()));
8498 text.pop(); // Drop trailing newline
8499 edits.push((
8500 buffer.anchor_after(range_to_move.start)
8501 ..buffer.anchor_before(range_to_move.end),
8502 String::new(),
8503 ));
8504 let insertion_anchor = buffer.anchor_after(insertion_point);
8505 edits.push((insertion_anchor..insertion_anchor, text));
8506
8507 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8508
8509 // Move selections down
8510 new_selections.extend(contiguous_row_selections.drain(..).map(
8511 |mut selection| {
8512 selection.start.row += row_delta;
8513 selection.end.row += row_delta;
8514 selection
8515 },
8516 ));
8517
8518 // Move folds down
8519 unfold_ranges.push(range_to_move.clone());
8520 for fold in display_map.folds_in_range(
8521 buffer.anchor_before(range_to_move.start)
8522 ..buffer.anchor_after(range_to_move.end),
8523 ) {
8524 let mut start = fold.range.start.to_point(&buffer);
8525 let mut end = fold.range.end.to_point(&buffer);
8526 start.row += row_delta;
8527 end.row += row_delta;
8528 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8529 }
8530 }
8531 }
8532
8533 // If we didn't move line(s), preserve the existing selections
8534 new_selections.append(&mut contiguous_row_selections);
8535 }
8536
8537 self.transact(window, cx, |this, window, cx| {
8538 this.unfold_ranges(&unfold_ranges, true, true, cx);
8539 this.buffer.update(cx, |buffer, cx| {
8540 for (range, text) in edits {
8541 buffer.edit([(range, text)], None, cx);
8542 }
8543 });
8544 this.fold_creases(refold_creases, true, window, cx);
8545 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8546 s.select(new_selections)
8547 });
8548 });
8549 }
8550
8551 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8552 let text_layout_details = &self.text_layout_details(window);
8553 self.transact(window, cx, |this, window, cx| {
8554 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8555 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8556 let line_mode = s.line_mode;
8557 s.move_with(|display_map, selection| {
8558 if !selection.is_empty() || line_mode {
8559 return;
8560 }
8561
8562 let mut head = selection.head();
8563 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8564 if head.column() == display_map.line_len(head.row()) {
8565 transpose_offset = display_map
8566 .buffer_snapshot
8567 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8568 }
8569
8570 if transpose_offset == 0 {
8571 return;
8572 }
8573
8574 *head.column_mut() += 1;
8575 head = display_map.clip_point(head, Bias::Right);
8576 let goal = SelectionGoal::HorizontalPosition(
8577 display_map
8578 .x_for_display_point(head, text_layout_details)
8579 .into(),
8580 );
8581 selection.collapse_to(head, goal);
8582
8583 let transpose_start = display_map
8584 .buffer_snapshot
8585 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8586 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8587 let transpose_end = display_map
8588 .buffer_snapshot
8589 .clip_offset(transpose_offset + 1, Bias::Right);
8590 if let Some(ch) =
8591 display_map.buffer_snapshot.chars_at(transpose_start).next()
8592 {
8593 edits.push((transpose_start..transpose_offset, String::new()));
8594 edits.push((transpose_end..transpose_end, ch.to_string()));
8595 }
8596 }
8597 });
8598 edits
8599 });
8600 this.buffer
8601 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8602 let selections = this.selections.all::<usize>(cx);
8603 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8604 s.select(selections);
8605 });
8606 });
8607 }
8608
8609 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8610 self.rewrap_impl(false, cx)
8611 }
8612
8613 pub fn rewrap_impl(&mut self, override_language_settings: bool, cx: &mut Context<Self>) {
8614 let buffer = self.buffer.read(cx).snapshot(cx);
8615 let selections = self.selections.all::<Point>(cx);
8616 let mut selections = selections.iter().peekable();
8617
8618 let mut edits = Vec::new();
8619 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8620
8621 while let Some(selection) = selections.next() {
8622 let mut start_row = selection.start.row;
8623 let mut end_row = selection.end.row;
8624
8625 // Skip selections that overlap with a range that has already been rewrapped.
8626 let selection_range = start_row..end_row;
8627 if rewrapped_row_ranges
8628 .iter()
8629 .any(|range| range.overlaps(&selection_range))
8630 {
8631 continue;
8632 }
8633
8634 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
8635
8636 // Since not all lines in the selection may be at the same indent
8637 // level, choose the indent size that is the most common between all
8638 // of the lines.
8639 //
8640 // If there is a tie, we use the deepest indent.
8641 let (indent_size, indent_end) = {
8642 let mut indent_size_occurrences = HashMap::default();
8643 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8644
8645 for row in start_row..=end_row {
8646 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8647 rows_by_indent_size.entry(indent).or_default().push(row);
8648 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8649 }
8650
8651 let indent_size = indent_size_occurrences
8652 .into_iter()
8653 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8654 .map(|(indent, _)| indent)
8655 .unwrap_or_default();
8656 let row = rows_by_indent_size[&indent_size][0];
8657 let indent_end = Point::new(row, indent_size.len);
8658
8659 (indent_size, indent_end)
8660 };
8661
8662 let mut line_prefix = indent_size.chars().collect::<String>();
8663
8664 let mut inside_comment = false;
8665 if let Some(comment_prefix) =
8666 buffer
8667 .language_scope_at(selection.head())
8668 .and_then(|language| {
8669 language
8670 .line_comment_prefixes()
8671 .iter()
8672 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8673 .cloned()
8674 })
8675 {
8676 line_prefix.push_str(&comment_prefix);
8677 inside_comment = true;
8678 }
8679
8680 let language_settings = buffer.language_settings_at(selection.head(), cx);
8681 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8682 RewrapBehavior::InComments => inside_comment,
8683 RewrapBehavior::InSelections => !selection.is_empty(),
8684 RewrapBehavior::Anywhere => true,
8685 };
8686
8687 let should_rewrap = override_language_settings
8688 || allow_rewrap_based_on_language
8689 || self.hard_wrap.is_some();
8690 if !should_rewrap {
8691 continue;
8692 }
8693
8694 if selection.is_empty() {
8695 'expand_upwards: while start_row > 0 {
8696 let prev_row = start_row - 1;
8697 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8698 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8699 {
8700 start_row = prev_row;
8701 } else {
8702 break 'expand_upwards;
8703 }
8704 }
8705
8706 'expand_downwards: while end_row < buffer.max_point().row {
8707 let next_row = end_row + 1;
8708 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8709 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8710 {
8711 end_row = next_row;
8712 } else {
8713 break 'expand_downwards;
8714 }
8715 }
8716 }
8717
8718 let start = Point::new(start_row, 0);
8719 let start_offset = start.to_offset(&buffer);
8720 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8721 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8722 let Some(lines_without_prefixes) = selection_text
8723 .lines()
8724 .map(|line| {
8725 line.strip_prefix(&line_prefix)
8726 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8727 .ok_or_else(|| {
8728 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8729 })
8730 })
8731 .collect::<Result<Vec<_>, _>>()
8732 .log_err()
8733 else {
8734 continue;
8735 };
8736
8737 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
8738 buffer
8739 .language_settings_at(Point::new(start_row, 0), cx)
8740 .preferred_line_length as usize
8741 });
8742 let wrapped_text = wrap_with_prefix(
8743 line_prefix,
8744 lines_without_prefixes.join(" "),
8745 wrap_column,
8746 tab_size,
8747 );
8748
8749 // TODO: should always use char-based diff while still supporting cursor behavior that
8750 // matches vim.
8751 let mut diff_options = DiffOptions::default();
8752 if override_language_settings {
8753 diff_options.max_word_diff_len = 0;
8754 diff_options.max_word_diff_line_count = 0;
8755 } else {
8756 diff_options.max_word_diff_len = usize::MAX;
8757 diff_options.max_word_diff_line_count = usize::MAX;
8758 }
8759
8760 for (old_range, new_text) in
8761 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8762 {
8763 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8764 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8765 edits.push((edit_start..edit_end, new_text));
8766 }
8767
8768 rewrapped_row_ranges.push(start_row..=end_row);
8769 }
8770
8771 self.buffer
8772 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8773 }
8774
8775 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8776 let mut text = String::new();
8777 let buffer = self.buffer.read(cx).snapshot(cx);
8778 let mut selections = self.selections.all::<Point>(cx);
8779 let mut clipboard_selections = Vec::with_capacity(selections.len());
8780 {
8781 let max_point = buffer.max_point();
8782 let mut is_first = true;
8783 for selection in &mut selections {
8784 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8785 if is_entire_line {
8786 selection.start = Point::new(selection.start.row, 0);
8787 if !selection.is_empty() && selection.end.column == 0 {
8788 selection.end = cmp::min(max_point, selection.end);
8789 } else {
8790 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8791 }
8792 selection.goal = SelectionGoal::None;
8793 }
8794 if is_first {
8795 is_first = false;
8796 } else {
8797 text += "\n";
8798 }
8799 let mut len = 0;
8800 for chunk in buffer.text_for_range(selection.start..selection.end) {
8801 text.push_str(chunk);
8802 len += chunk.len();
8803 }
8804 clipboard_selections.push(ClipboardSelection {
8805 len,
8806 is_entire_line,
8807 first_line_indent: buffer
8808 .indent_size_for_line(MultiBufferRow(selection.start.row))
8809 .len,
8810 });
8811 }
8812 }
8813
8814 self.transact(window, cx, |this, window, cx| {
8815 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8816 s.select(selections);
8817 });
8818 this.insert("", window, cx);
8819 });
8820 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8821 }
8822
8823 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8824 let item = self.cut_common(window, cx);
8825 cx.write_to_clipboard(item);
8826 }
8827
8828 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8829 self.change_selections(None, window, cx, |s| {
8830 s.move_with(|snapshot, sel| {
8831 if sel.is_empty() {
8832 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8833 }
8834 });
8835 });
8836 let item = self.cut_common(window, cx);
8837 cx.set_global(KillRing(item))
8838 }
8839
8840 pub fn kill_ring_yank(
8841 &mut self,
8842 _: &KillRingYank,
8843 window: &mut Window,
8844 cx: &mut Context<Self>,
8845 ) {
8846 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8847 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8848 (kill_ring.text().to_string(), kill_ring.metadata_json())
8849 } else {
8850 return;
8851 }
8852 } else {
8853 return;
8854 };
8855 self.do_paste(&text, metadata, false, window, cx);
8856 }
8857
8858 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8859 let selections = self.selections.all::<Point>(cx);
8860 let buffer = self.buffer.read(cx).read(cx);
8861 let mut text = String::new();
8862
8863 let mut clipboard_selections = Vec::with_capacity(selections.len());
8864 {
8865 let max_point = buffer.max_point();
8866 let mut is_first = true;
8867 for selection in selections.iter() {
8868 let mut start = selection.start;
8869 let mut end = selection.end;
8870 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8871 if is_entire_line {
8872 start = Point::new(start.row, 0);
8873 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8874 }
8875 if is_first {
8876 is_first = false;
8877 } else {
8878 text += "\n";
8879 }
8880 let mut len = 0;
8881 for chunk in buffer.text_for_range(start..end) {
8882 text.push_str(chunk);
8883 len += chunk.len();
8884 }
8885 clipboard_selections.push(ClipboardSelection {
8886 len,
8887 is_entire_line,
8888 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8889 });
8890 }
8891 }
8892
8893 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8894 text,
8895 clipboard_selections,
8896 ));
8897 }
8898
8899 pub fn do_paste(
8900 &mut self,
8901 text: &String,
8902 clipboard_selections: Option<Vec<ClipboardSelection>>,
8903 handle_entire_lines: bool,
8904 window: &mut Window,
8905 cx: &mut Context<Self>,
8906 ) {
8907 if self.read_only(cx) {
8908 return;
8909 }
8910
8911 let clipboard_text = Cow::Borrowed(text);
8912
8913 self.transact(window, cx, |this, window, cx| {
8914 if let Some(mut clipboard_selections) = clipboard_selections {
8915 let old_selections = this.selections.all::<usize>(cx);
8916 let all_selections_were_entire_line =
8917 clipboard_selections.iter().all(|s| s.is_entire_line);
8918 let first_selection_indent_column =
8919 clipboard_selections.first().map(|s| s.first_line_indent);
8920 if clipboard_selections.len() != old_selections.len() {
8921 clipboard_selections.drain(..);
8922 }
8923 let cursor_offset = this.selections.last::<usize>(cx).head();
8924 let mut auto_indent_on_paste = true;
8925
8926 this.buffer.update(cx, |buffer, cx| {
8927 let snapshot = buffer.read(cx);
8928 auto_indent_on_paste = snapshot
8929 .language_settings_at(cursor_offset, cx)
8930 .auto_indent_on_paste;
8931
8932 let mut start_offset = 0;
8933 let mut edits = Vec::new();
8934 let mut original_indent_columns = Vec::new();
8935 for (ix, selection) in old_selections.iter().enumerate() {
8936 let to_insert;
8937 let entire_line;
8938 let original_indent_column;
8939 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8940 let end_offset = start_offset + clipboard_selection.len;
8941 to_insert = &clipboard_text[start_offset..end_offset];
8942 entire_line = clipboard_selection.is_entire_line;
8943 start_offset = end_offset + 1;
8944 original_indent_column = Some(clipboard_selection.first_line_indent);
8945 } else {
8946 to_insert = clipboard_text.as_str();
8947 entire_line = all_selections_were_entire_line;
8948 original_indent_column = first_selection_indent_column
8949 }
8950
8951 // If the corresponding selection was empty when this slice of the
8952 // clipboard text was written, then the entire line containing the
8953 // selection was copied. If this selection is also currently empty,
8954 // then paste the line before the current line of the buffer.
8955 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8956 let column = selection.start.to_point(&snapshot).column as usize;
8957 let line_start = selection.start - column;
8958 line_start..line_start
8959 } else {
8960 selection.range()
8961 };
8962
8963 edits.push((range, to_insert));
8964 original_indent_columns.push(original_indent_column);
8965 }
8966 drop(snapshot);
8967
8968 buffer.edit(
8969 edits,
8970 if auto_indent_on_paste {
8971 Some(AutoindentMode::Block {
8972 original_indent_columns,
8973 })
8974 } else {
8975 None
8976 },
8977 cx,
8978 );
8979 });
8980
8981 let selections = this.selections.all::<usize>(cx);
8982 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8983 s.select(selections)
8984 });
8985 } else {
8986 this.insert(&clipboard_text, window, cx);
8987 }
8988 });
8989 }
8990
8991 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
8992 if let Some(item) = cx.read_from_clipboard() {
8993 let entries = item.entries();
8994
8995 match entries.first() {
8996 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
8997 // of all the pasted entries.
8998 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
8999 .do_paste(
9000 clipboard_string.text(),
9001 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9002 true,
9003 window,
9004 cx,
9005 ),
9006 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9007 }
9008 }
9009 }
9010
9011 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9012 if self.read_only(cx) {
9013 return;
9014 }
9015
9016 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9017 if let Some((selections, _)) =
9018 self.selection_history.transaction(transaction_id).cloned()
9019 {
9020 self.change_selections(None, window, cx, |s| {
9021 s.select_anchors(selections.to_vec());
9022 });
9023 } else {
9024 log::error!(
9025 "No entry in selection_history found for undo. \
9026 This may correspond to a bug where undo does not update the selection. \
9027 If this is occurring, please add details to \
9028 https://github.com/zed-industries/zed/issues/22692"
9029 );
9030 }
9031 self.request_autoscroll(Autoscroll::fit(), cx);
9032 self.unmark_text(window, cx);
9033 self.refresh_inline_completion(true, false, window, cx);
9034 cx.emit(EditorEvent::Edited { transaction_id });
9035 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9036 }
9037 }
9038
9039 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9040 if self.read_only(cx) {
9041 return;
9042 }
9043
9044 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9045 if let Some((_, Some(selections))) =
9046 self.selection_history.transaction(transaction_id).cloned()
9047 {
9048 self.change_selections(None, window, cx, |s| {
9049 s.select_anchors(selections.to_vec());
9050 });
9051 } else {
9052 log::error!(
9053 "No entry in selection_history found for redo. \
9054 This may correspond to a bug where undo does not update the selection. \
9055 If this is occurring, please add details to \
9056 https://github.com/zed-industries/zed/issues/22692"
9057 );
9058 }
9059 self.request_autoscroll(Autoscroll::fit(), cx);
9060 self.unmark_text(window, cx);
9061 self.refresh_inline_completion(true, false, window, cx);
9062 cx.emit(EditorEvent::Edited { transaction_id });
9063 }
9064 }
9065
9066 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9067 self.buffer
9068 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9069 }
9070
9071 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9072 self.buffer
9073 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9074 }
9075
9076 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9077 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9078 let line_mode = s.line_mode;
9079 s.move_with(|map, selection| {
9080 let cursor = if selection.is_empty() && !line_mode {
9081 movement::left(map, selection.start)
9082 } else {
9083 selection.start
9084 };
9085 selection.collapse_to(cursor, SelectionGoal::None);
9086 });
9087 })
9088 }
9089
9090 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9091 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9092 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9093 })
9094 }
9095
9096 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9097 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9098 let line_mode = s.line_mode;
9099 s.move_with(|map, selection| {
9100 let cursor = if selection.is_empty() && !line_mode {
9101 movement::right(map, selection.end)
9102 } else {
9103 selection.end
9104 };
9105 selection.collapse_to(cursor, SelectionGoal::None)
9106 });
9107 })
9108 }
9109
9110 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9111 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9112 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9113 })
9114 }
9115
9116 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9117 if self.take_rename(true, window, cx).is_some() {
9118 return;
9119 }
9120
9121 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9122 cx.propagate();
9123 return;
9124 }
9125
9126 let text_layout_details = &self.text_layout_details(window);
9127 let selection_count = self.selections.count();
9128 let first_selection = self.selections.first_anchor();
9129
9130 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9131 let line_mode = s.line_mode;
9132 s.move_with(|map, selection| {
9133 if !selection.is_empty() && !line_mode {
9134 selection.goal = SelectionGoal::None;
9135 }
9136 let (cursor, goal) = movement::up(
9137 map,
9138 selection.start,
9139 selection.goal,
9140 false,
9141 text_layout_details,
9142 );
9143 selection.collapse_to(cursor, goal);
9144 });
9145 });
9146
9147 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9148 {
9149 cx.propagate();
9150 }
9151 }
9152
9153 pub fn move_up_by_lines(
9154 &mut self,
9155 action: &MoveUpByLines,
9156 window: &mut Window,
9157 cx: &mut Context<Self>,
9158 ) {
9159 if self.take_rename(true, window, cx).is_some() {
9160 return;
9161 }
9162
9163 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9164 cx.propagate();
9165 return;
9166 }
9167
9168 let text_layout_details = &self.text_layout_details(window);
9169
9170 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9171 let line_mode = s.line_mode;
9172 s.move_with(|map, selection| {
9173 if !selection.is_empty() && !line_mode {
9174 selection.goal = SelectionGoal::None;
9175 }
9176 let (cursor, goal) = movement::up_by_rows(
9177 map,
9178 selection.start,
9179 action.lines,
9180 selection.goal,
9181 false,
9182 text_layout_details,
9183 );
9184 selection.collapse_to(cursor, goal);
9185 });
9186 })
9187 }
9188
9189 pub fn move_down_by_lines(
9190 &mut self,
9191 action: &MoveDownByLines,
9192 window: &mut Window,
9193 cx: &mut Context<Self>,
9194 ) {
9195 if self.take_rename(true, window, cx).is_some() {
9196 return;
9197 }
9198
9199 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9200 cx.propagate();
9201 return;
9202 }
9203
9204 let text_layout_details = &self.text_layout_details(window);
9205
9206 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9207 let line_mode = s.line_mode;
9208 s.move_with(|map, selection| {
9209 if !selection.is_empty() && !line_mode {
9210 selection.goal = SelectionGoal::None;
9211 }
9212 let (cursor, goal) = movement::down_by_rows(
9213 map,
9214 selection.start,
9215 action.lines,
9216 selection.goal,
9217 false,
9218 text_layout_details,
9219 );
9220 selection.collapse_to(cursor, goal);
9221 });
9222 })
9223 }
9224
9225 pub fn select_down_by_lines(
9226 &mut self,
9227 action: &SelectDownByLines,
9228 window: &mut Window,
9229 cx: &mut Context<Self>,
9230 ) {
9231 let text_layout_details = &self.text_layout_details(window);
9232 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9233 s.move_heads_with(|map, head, goal| {
9234 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9235 })
9236 })
9237 }
9238
9239 pub fn select_up_by_lines(
9240 &mut self,
9241 action: &SelectUpByLines,
9242 window: &mut Window,
9243 cx: &mut Context<Self>,
9244 ) {
9245 let text_layout_details = &self.text_layout_details(window);
9246 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9247 s.move_heads_with(|map, head, goal| {
9248 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9249 })
9250 })
9251 }
9252
9253 pub fn select_page_up(
9254 &mut self,
9255 _: &SelectPageUp,
9256 window: &mut Window,
9257 cx: &mut Context<Self>,
9258 ) {
9259 let Some(row_count) = self.visible_row_count() else {
9260 return;
9261 };
9262
9263 let text_layout_details = &self.text_layout_details(window);
9264
9265 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9266 s.move_heads_with(|map, head, goal| {
9267 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9268 })
9269 })
9270 }
9271
9272 pub fn move_page_up(
9273 &mut self,
9274 action: &MovePageUp,
9275 window: &mut Window,
9276 cx: &mut Context<Self>,
9277 ) {
9278 if self.take_rename(true, window, cx).is_some() {
9279 return;
9280 }
9281
9282 if self
9283 .context_menu
9284 .borrow_mut()
9285 .as_mut()
9286 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9287 .unwrap_or(false)
9288 {
9289 return;
9290 }
9291
9292 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9293 cx.propagate();
9294 return;
9295 }
9296
9297 let Some(row_count) = self.visible_row_count() else {
9298 return;
9299 };
9300
9301 let autoscroll = if action.center_cursor {
9302 Autoscroll::center()
9303 } else {
9304 Autoscroll::fit()
9305 };
9306
9307 let text_layout_details = &self.text_layout_details(window);
9308
9309 self.change_selections(Some(autoscroll), window, cx, |s| {
9310 let line_mode = s.line_mode;
9311 s.move_with(|map, selection| {
9312 if !selection.is_empty() && !line_mode {
9313 selection.goal = SelectionGoal::None;
9314 }
9315 let (cursor, goal) = movement::up_by_rows(
9316 map,
9317 selection.end,
9318 row_count,
9319 selection.goal,
9320 false,
9321 text_layout_details,
9322 );
9323 selection.collapse_to(cursor, goal);
9324 });
9325 });
9326 }
9327
9328 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9329 let text_layout_details = &self.text_layout_details(window);
9330 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9331 s.move_heads_with(|map, head, goal| {
9332 movement::up(map, head, goal, false, text_layout_details)
9333 })
9334 })
9335 }
9336
9337 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9338 self.take_rename(true, window, cx);
9339
9340 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9341 cx.propagate();
9342 return;
9343 }
9344
9345 let text_layout_details = &self.text_layout_details(window);
9346 let selection_count = self.selections.count();
9347 let first_selection = self.selections.first_anchor();
9348
9349 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9350 let line_mode = s.line_mode;
9351 s.move_with(|map, selection| {
9352 if !selection.is_empty() && !line_mode {
9353 selection.goal = SelectionGoal::None;
9354 }
9355 let (cursor, goal) = movement::down(
9356 map,
9357 selection.end,
9358 selection.goal,
9359 false,
9360 text_layout_details,
9361 );
9362 selection.collapse_to(cursor, goal);
9363 });
9364 });
9365
9366 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9367 {
9368 cx.propagate();
9369 }
9370 }
9371
9372 pub fn select_page_down(
9373 &mut self,
9374 _: &SelectPageDown,
9375 window: &mut Window,
9376 cx: &mut Context<Self>,
9377 ) {
9378 let Some(row_count) = self.visible_row_count() else {
9379 return;
9380 };
9381
9382 let text_layout_details = &self.text_layout_details(window);
9383
9384 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9385 s.move_heads_with(|map, head, goal| {
9386 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9387 })
9388 })
9389 }
9390
9391 pub fn move_page_down(
9392 &mut self,
9393 action: &MovePageDown,
9394 window: &mut Window,
9395 cx: &mut Context<Self>,
9396 ) {
9397 if self.take_rename(true, window, cx).is_some() {
9398 return;
9399 }
9400
9401 if self
9402 .context_menu
9403 .borrow_mut()
9404 .as_mut()
9405 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9406 .unwrap_or(false)
9407 {
9408 return;
9409 }
9410
9411 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9412 cx.propagate();
9413 return;
9414 }
9415
9416 let Some(row_count) = self.visible_row_count() else {
9417 return;
9418 };
9419
9420 let autoscroll = if action.center_cursor {
9421 Autoscroll::center()
9422 } else {
9423 Autoscroll::fit()
9424 };
9425
9426 let text_layout_details = &self.text_layout_details(window);
9427 self.change_selections(Some(autoscroll), window, cx, |s| {
9428 let line_mode = s.line_mode;
9429 s.move_with(|map, selection| {
9430 if !selection.is_empty() && !line_mode {
9431 selection.goal = SelectionGoal::None;
9432 }
9433 let (cursor, goal) = movement::down_by_rows(
9434 map,
9435 selection.end,
9436 row_count,
9437 selection.goal,
9438 false,
9439 text_layout_details,
9440 );
9441 selection.collapse_to(cursor, goal);
9442 });
9443 });
9444 }
9445
9446 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9447 let text_layout_details = &self.text_layout_details(window);
9448 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9449 s.move_heads_with(|map, head, goal| {
9450 movement::down(map, head, goal, false, text_layout_details)
9451 })
9452 });
9453 }
9454
9455 pub fn context_menu_first(
9456 &mut self,
9457 _: &ContextMenuFirst,
9458 _window: &mut Window,
9459 cx: &mut Context<Self>,
9460 ) {
9461 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9462 context_menu.select_first(self.completion_provider.as_deref(), cx);
9463 }
9464 }
9465
9466 pub fn context_menu_prev(
9467 &mut self,
9468 _: &ContextMenuPrevious,
9469 _window: &mut Window,
9470 cx: &mut Context<Self>,
9471 ) {
9472 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9473 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9474 }
9475 }
9476
9477 pub fn context_menu_next(
9478 &mut self,
9479 _: &ContextMenuNext,
9480 _window: &mut Window,
9481 cx: &mut Context<Self>,
9482 ) {
9483 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9484 context_menu.select_next(self.completion_provider.as_deref(), cx);
9485 }
9486 }
9487
9488 pub fn context_menu_last(
9489 &mut self,
9490 _: &ContextMenuLast,
9491 _window: &mut Window,
9492 cx: &mut Context<Self>,
9493 ) {
9494 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9495 context_menu.select_last(self.completion_provider.as_deref(), cx);
9496 }
9497 }
9498
9499 pub fn move_to_previous_word_start(
9500 &mut self,
9501 _: &MoveToPreviousWordStart,
9502 window: &mut Window,
9503 cx: &mut Context<Self>,
9504 ) {
9505 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9506 s.move_cursors_with(|map, head, _| {
9507 (
9508 movement::previous_word_start(map, head),
9509 SelectionGoal::None,
9510 )
9511 });
9512 })
9513 }
9514
9515 pub fn move_to_previous_subword_start(
9516 &mut self,
9517 _: &MoveToPreviousSubwordStart,
9518 window: &mut Window,
9519 cx: &mut Context<Self>,
9520 ) {
9521 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9522 s.move_cursors_with(|map, head, _| {
9523 (
9524 movement::previous_subword_start(map, head),
9525 SelectionGoal::None,
9526 )
9527 });
9528 })
9529 }
9530
9531 pub fn select_to_previous_word_start(
9532 &mut self,
9533 _: &SelectToPreviousWordStart,
9534 window: &mut Window,
9535 cx: &mut Context<Self>,
9536 ) {
9537 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9538 s.move_heads_with(|map, head, _| {
9539 (
9540 movement::previous_word_start(map, head),
9541 SelectionGoal::None,
9542 )
9543 });
9544 })
9545 }
9546
9547 pub fn select_to_previous_subword_start(
9548 &mut self,
9549 _: &SelectToPreviousSubwordStart,
9550 window: &mut Window,
9551 cx: &mut Context<Self>,
9552 ) {
9553 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9554 s.move_heads_with(|map, head, _| {
9555 (
9556 movement::previous_subword_start(map, head),
9557 SelectionGoal::None,
9558 )
9559 });
9560 })
9561 }
9562
9563 pub fn delete_to_previous_word_start(
9564 &mut self,
9565 action: &DeleteToPreviousWordStart,
9566 window: &mut Window,
9567 cx: &mut Context<Self>,
9568 ) {
9569 self.transact(window, cx, |this, window, cx| {
9570 this.select_autoclose_pair(window, cx);
9571 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9572 let line_mode = s.line_mode;
9573 s.move_with(|map, selection| {
9574 if selection.is_empty() && !line_mode {
9575 let cursor = if action.ignore_newlines {
9576 movement::previous_word_start(map, selection.head())
9577 } else {
9578 movement::previous_word_start_or_newline(map, selection.head())
9579 };
9580 selection.set_head(cursor, SelectionGoal::None);
9581 }
9582 });
9583 });
9584 this.insert("", window, cx);
9585 });
9586 }
9587
9588 pub fn delete_to_previous_subword_start(
9589 &mut self,
9590 _: &DeleteToPreviousSubwordStart,
9591 window: &mut Window,
9592 cx: &mut Context<Self>,
9593 ) {
9594 self.transact(window, cx, |this, window, cx| {
9595 this.select_autoclose_pair(window, cx);
9596 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9597 let line_mode = s.line_mode;
9598 s.move_with(|map, selection| {
9599 if selection.is_empty() && !line_mode {
9600 let cursor = movement::previous_subword_start(map, selection.head());
9601 selection.set_head(cursor, SelectionGoal::None);
9602 }
9603 });
9604 });
9605 this.insert("", window, cx);
9606 });
9607 }
9608
9609 pub fn move_to_next_word_end(
9610 &mut self,
9611 _: &MoveToNextWordEnd,
9612 window: &mut Window,
9613 cx: &mut Context<Self>,
9614 ) {
9615 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9616 s.move_cursors_with(|map, head, _| {
9617 (movement::next_word_end(map, head), SelectionGoal::None)
9618 });
9619 })
9620 }
9621
9622 pub fn move_to_next_subword_end(
9623 &mut self,
9624 _: &MoveToNextSubwordEnd,
9625 window: &mut Window,
9626 cx: &mut Context<Self>,
9627 ) {
9628 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9629 s.move_cursors_with(|map, head, _| {
9630 (movement::next_subword_end(map, head), SelectionGoal::None)
9631 });
9632 })
9633 }
9634
9635 pub fn select_to_next_word_end(
9636 &mut self,
9637 _: &SelectToNextWordEnd,
9638 window: &mut Window,
9639 cx: &mut Context<Self>,
9640 ) {
9641 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9642 s.move_heads_with(|map, head, _| {
9643 (movement::next_word_end(map, head), SelectionGoal::None)
9644 });
9645 })
9646 }
9647
9648 pub fn select_to_next_subword_end(
9649 &mut self,
9650 _: &SelectToNextSubwordEnd,
9651 window: &mut Window,
9652 cx: &mut Context<Self>,
9653 ) {
9654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9655 s.move_heads_with(|map, head, _| {
9656 (movement::next_subword_end(map, head), SelectionGoal::None)
9657 });
9658 })
9659 }
9660
9661 pub fn delete_to_next_word_end(
9662 &mut self,
9663 action: &DeleteToNextWordEnd,
9664 window: &mut Window,
9665 cx: &mut Context<Self>,
9666 ) {
9667 self.transact(window, cx, |this, window, cx| {
9668 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9669 let line_mode = s.line_mode;
9670 s.move_with(|map, selection| {
9671 if selection.is_empty() && !line_mode {
9672 let cursor = if action.ignore_newlines {
9673 movement::next_word_end(map, selection.head())
9674 } else {
9675 movement::next_word_end_or_newline(map, selection.head())
9676 };
9677 selection.set_head(cursor, SelectionGoal::None);
9678 }
9679 });
9680 });
9681 this.insert("", window, cx);
9682 });
9683 }
9684
9685 pub fn delete_to_next_subword_end(
9686 &mut self,
9687 _: &DeleteToNextSubwordEnd,
9688 window: &mut Window,
9689 cx: &mut Context<Self>,
9690 ) {
9691 self.transact(window, cx, |this, window, cx| {
9692 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9693 s.move_with(|map, selection| {
9694 if selection.is_empty() {
9695 let cursor = movement::next_subword_end(map, selection.head());
9696 selection.set_head(cursor, SelectionGoal::None);
9697 }
9698 });
9699 });
9700 this.insert("", window, cx);
9701 });
9702 }
9703
9704 pub fn move_to_beginning_of_line(
9705 &mut self,
9706 action: &MoveToBeginningOfLine,
9707 window: &mut Window,
9708 cx: &mut Context<Self>,
9709 ) {
9710 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9711 s.move_cursors_with(|map, head, _| {
9712 (
9713 movement::indented_line_beginning(
9714 map,
9715 head,
9716 action.stop_at_soft_wraps,
9717 action.stop_at_indent,
9718 ),
9719 SelectionGoal::None,
9720 )
9721 });
9722 })
9723 }
9724
9725 pub fn select_to_beginning_of_line(
9726 &mut self,
9727 action: &SelectToBeginningOfLine,
9728 window: &mut Window,
9729 cx: &mut Context<Self>,
9730 ) {
9731 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9732 s.move_heads_with(|map, head, _| {
9733 (
9734 movement::indented_line_beginning(
9735 map,
9736 head,
9737 action.stop_at_soft_wraps,
9738 action.stop_at_indent,
9739 ),
9740 SelectionGoal::None,
9741 )
9742 });
9743 });
9744 }
9745
9746 pub fn delete_to_beginning_of_line(
9747 &mut self,
9748 action: &DeleteToBeginningOfLine,
9749 window: &mut Window,
9750 cx: &mut Context<Self>,
9751 ) {
9752 self.transact(window, cx, |this, window, cx| {
9753 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9754 s.move_with(|_, selection| {
9755 selection.reversed = true;
9756 });
9757 });
9758
9759 this.select_to_beginning_of_line(
9760 &SelectToBeginningOfLine {
9761 stop_at_soft_wraps: false,
9762 stop_at_indent: action.stop_at_indent,
9763 },
9764 window,
9765 cx,
9766 );
9767 this.backspace(&Backspace, window, cx);
9768 });
9769 }
9770
9771 pub fn move_to_end_of_line(
9772 &mut self,
9773 action: &MoveToEndOfLine,
9774 window: &mut Window,
9775 cx: &mut Context<Self>,
9776 ) {
9777 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9778 s.move_cursors_with(|map, head, _| {
9779 (
9780 movement::line_end(map, head, action.stop_at_soft_wraps),
9781 SelectionGoal::None,
9782 )
9783 });
9784 })
9785 }
9786
9787 pub fn select_to_end_of_line(
9788 &mut self,
9789 action: &SelectToEndOfLine,
9790 window: &mut Window,
9791 cx: &mut Context<Self>,
9792 ) {
9793 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9794 s.move_heads_with(|map, head, _| {
9795 (
9796 movement::line_end(map, head, action.stop_at_soft_wraps),
9797 SelectionGoal::None,
9798 )
9799 });
9800 })
9801 }
9802
9803 pub fn delete_to_end_of_line(
9804 &mut self,
9805 _: &DeleteToEndOfLine,
9806 window: &mut Window,
9807 cx: &mut Context<Self>,
9808 ) {
9809 self.transact(window, cx, |this, window, cx| {
9810 this.select_to_end_of_line(
9811 &SelectToEndOfLine {
9812 stop_at_soft_wraps: false,
9813 },
9814 window,
9815 cx,
9816 );
9817 this.delete(&Delete, window, cx);
9818 });
9819 }
9820
9821 pub fn cut_to_end_of_line(
9822 &mut self,
9823 _: &CutToEndOfLine,
9824 window: &mut Window,
9825 cx: &mut Context<Self>,
9826 ) {
9827 self.transact(window, cx, |this, window, cx| {
9828 this.select_to_end_of_line(
9829 &SelectToEndOfLine {
9830 stop_at_soft_wraps: false,
9831 },
9832 window,
9833 cx,
9834 );
9835 this.cut(&Cut, window, cx);
9836 });
9837 }
9838
9839 pub fn move_to_start_of_paragraph(
9840 &mut self,
9841 _: &MoveToStartOfParagraph,
9842 window: &mut Window,
9843 cx: &mut Context<Self>,
9844 ) {
9845 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9846 cx.propagate();
9847 return;
9848 }
9849
9850 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9851 s.move_with(|map, selection| {
9852 selection.collapse_to(
9853 movement::start_of_paragraph(map, selection.head(), 1),
9854 SelectionGoal::None,
9855 )
9856 });
9857 })
9858 }
9859
9860 pub fn move_to_end_of_paragraph(
9861 &mut self,
9862 _: &MoveToEndOfParagraph,
9863 window: &mut Window,
9864 cx: &mut Context<Self>,
9865 ) {
9866 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9867 cx.propagate();
9868 return;
9869 }
9870
9871 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9872 s.move_with(|map, selection| {
9873 selection.collapse_to(
9874 movement::end_of_paragraph(map, selection.head(), 1),
9875 SelectionGoal::None,
9876 )
9877 });
9878 })
9879 }
9880
9881 pub fn select_to_start_of_paragraph(
9882 &mut self,
9883 _: &SelectToStartOfParagraph,
9884 window: &mut Window,
9885 cx: &mut Context<Self>,
9886 ) {
9887 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9888 cx.propagate();
9889 return;
9890 }
9891
9892 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9893 s.move_heads_with(|map, head, _| {
9894 (
9895 movement::start_of_paragraph(map, head, 1),
9896 SelectionGoal::None,
9897 )
9898 });
9899 })
9900 }
9901
9902 pub fn select_to_end_of_paragraph(
9903 &mut self,
9904 _: &SelectToEndOfParagraph,
9905 window: &mut Window,
9906 cx: &mut Context<Self>,
9907 ) {
9908 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9909 cx.propagate();
9910 return;
9911 }
9912
9913 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9914 s.move_heads_with(|map, head, _| {
9915 (
9916 movement::end_of_paragraph(map, head, 1),
9917 SelectionGoal::None,
9918 )
9919 });
9920 })
9921 }
9922
9923 pub fn move_to_start_of_excerpt(
9924 &mut self,
9925 _: &MoveToStartOfExcerpt,
9926 window: &mut Window,
9927 cx: &mut Context<Self>,
9928 ) {
9929 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9930 cx.propagate();
9931 return;
9932 }
9933
9934 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9935 s.move_with(|map, selection| {
9936 selection.collapse_to(
9937 movement::start_of_excerpt(
9938 map,
9939 selection.head(),
9940 workspace::searchable::Direction::Prev,
9941 ),
9942 SelectionGoal::None,
9943 )
9944 });
9945 })
9946 }
9947
9948 pub fn move_to_start_of_next_excerpt(
9949 &mut self,
9950 _: &MoveToStartOfNextExcerpt,
9951 window: &mut Window,
9952 cx: &mut Context<Self>,
9953 ) {
9954 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9955 cx.propagate();
9956 return;
9957 }
9958
9959 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9960 s.move_with(|map, selection| {
9961 selection.collapse_to(
9962 movement::start_of_excerpt(
9963 map,
9964 selection.head(),
9965 workspace::searchable::Direction::Next,
9966 ),
9967 SelectionGoal::None,
9968 )
9969 });
9970 })
9971 }
9972
9973 pub fn move_to_end_of_excerpt(
9974 &mut self,
9975 _: &MoveToEndOfExcerpt,
9976 window: &mut Window,
9977 cx: &mut Context<Self>,
9978 ) {
9979 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9980 cx.propagate();
9981 return;
9982 }
9983
9984 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9985 s.move_with(|map, selection| {
9986 selection.collapse_to(
9987 movement::end_of_excerpt(
9988 map,
9989 selection.head(),
9990 workspace::searchable::Direction::Next,
9991 ),
9992 SelectionGoal::None,
9993 )
9994 });
9995 })
9996 }
9997
9998 pub fn move_to_end_of_previous_excerpt(
9999 &mut self,
10000 _: &MoveToEndOfPreviousExcerpt,
10001 window: &mut Window,
10002 cx: &mut Context<Self>,
10003 ) {
10004 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10005 cx.propagate();
10006 return;
10007 }
10008
10009 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10010 s.move_with(|map, selection| {
10011 selection.collapse_to(
10012 movement::end_of_excerpt(
10013 map,
10014 selection.head(),
10015 workspace::searchable::Direction::Prev,
10016 ),
10017 SelectionGoal::None,
10018 )
10019 });
10020 })
10021 }
10022
10023 pub fn select_to_start_of_excerpt(
10024 &mut self,
10025 _: &SelectToStartOfExcerpt,
10026 window: &mut Window,
10027 cx: &mut Context<Self>,
10028 ) {
10029 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10030 cx.propagate();
10031 return;
10032 }
10033
10034 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10035 s.move_heads_with(|map, head, _| {
10036 (
10037 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10038 SelectionGoal::None,
10039 )
10040 });
10041 })
10042 }
10043
10044 pub fn select_to_start_of_next_excerpt(
10045 &mut self,
10046 _: &SelectToStartOfNextExcerpt,
10047 window: &mut Window,
10048 cx: &mut Context<Self>,
10049 ) {
10050 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10051 cx.propagate();
10052 return;
10053 }
10054
10055 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10056 s.move_heads_with(|map, head, _| {
10057 (
10058 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10059 SelectionGoal::None,
10060 )
10061 });
10062 })
10063 }
10064
10065 pub fn select_to_end_of_excerpt(
10066 &mut self,
10067 _: &SelectToEndOfExcerpt,
10068 window: &mut Window,
10069 cx: &mut Context<Self>,
10070 ) {
10071 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10072 cx.propagate();
10073 return;
10074 }
10075
10076 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10077 s.move_heads_with(|map, head, _| {
10078 (
10079 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10080 SelectionGoal::None,
10081 )
10082 });
10083 })
10084 }
10085
10086 pub fn select_to_end_of_previous_excerpt(
10087 &mut self,
10088 _: &SelectToEndOfPreviousExcerpt,
10089 window: &mut Window,
10090 cx: &mut Context<Self>,
10091 ) {
10092 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10093 cx.propagate();
10094 return;
10095 }
10096
10097 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10098 s.move_heads_with(|map, head, _| {
10099 (
10100 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10101 SelectionGoal::None,
10102 )
10103 });
10104 })
10105 }
10106
10107 pub fn move_to_beginning(
10108 &mut self,
10109 _: &MoveToBeginning,
10110 window: &mut Window,
10111 cx: &mut Context<Self>,
10112 ) {
10113 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10114 cx.propagate();
10115 return;
10116 }
10117
10118 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10119 s.select_ranges(vec![0..0]);
10120 });
10121 }
10122
10123 pub fn select_to_beginning(
10124 &mut self,
10125 _: &SelectToBeginning,
10126 window: &mut Window,
10127 cx: &mut Context<Self>,
10128 ) {
10129 let mut selection = self.selections.last::<Point>(cx);
10130 selection.set_head(Point::zero(), SelectionGoal::None);
10131
10132 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10133 s.select(vec![selection]);
10134 });
10135 }
10136
10137 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10138 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10139 cx.propagate();
10140 return;
10141 }
10142
10143 let cursor = self.buffer.read(cx).read(cx).len();
10144 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10145 s.select_ranges(vec![cursor..cursor])
10146 });
10147 }
10148
10149 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10150 self.nav_history = nav_history;
10151 }
10152
10153 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10154 self.nav_history.as_ref()
10155 }
10156
10157 fn push_to_nav_history(
10158 &mut self,
10159 cursor_anchor: Anchor,
10160 new_position: Option<Point>,
10161 cx: &mut Context<Self>,
10162 ) {
10163 if let Some(nav_history) = self.nav_history.as_mut() {
10164 let buffer = self.buffer.read(cx).read(cx);
10165 let cursor_position = cursor_anchor.to_point(&buffer);
10166 let scroll_state = self.scroll_manager.anchor();
10167 let scroll_top_row = scroll_state.top_row(&buffer);
10168 drop(buffer);
10169
10170 if let Some(new_position) = new_position {
10171 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10172 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10173 return;
10174 }
10175 }
10176
10177 nav_history.push(
10178 Some(NavigationData {
10179 cursor_anchor,
10180 cursor_position,
10181 scroll_anchor: scroll_state,
10182 scroll_top_row,
10183 }),
10184 cx,
10185 );
10186 }
10187 }
10188
10189 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10190 let buffer = self.buffer.read(cx).snapshot(cx);
10191 let mut selection = self.selections.first::<usize>(cx);
10192 selection.set_head(buffer.len(), SelectionGoal::None);
10193 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10194 s.select(vec![selection]);
10195 });
10196 }
10197
10198 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10199 let end = self.buffer.read(cx).read(cx).len();
10200 self.change_selections(None, window, cx, |s| {
10201 s.select_ranges(vec![0..end]);
10202 });
10203 }
10204
10205 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10206 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10207 let mut selections = self.selections.all::<Point>(cx);
10208 let max_point = display_map.buffer_snapshot.max_point();
10209 for selection in &mut selections {
10210 let rows = selection.spanned_rows(true, &display_map);
10211 selection.start = Point::new(rows.start.0, 0);
10212 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10213 selection.reversed = false;
10214 }
10215 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10216 s.select(selections);
10217 });
10218 }
10219
10220 pub fn split_selection_into_lines(
10221 &mut self,
10222 _: &SplitSelectionIntoLines,
10223 window: &mut Window,
10224 cx: &mut Context<Self>,
10225 ) {
10226 let selections = self
10227 .selections
10228 .all::<Point>(cx)
10229 .into_iter()
10230 .map(|selection| selection.start..selection.end)
10231 .collect::<Vec<_>>();
10232 self.unfold_ranges(&selections, true, true, cx);
10233
10234 let mut new_selection_ranges = Vec::new();
10235 {
10236 let buffer = self.buffer.read(cx).read(cx);
10237 for selection in selections {
10238 for row in selection.start.row..selection.end.row {
10239 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10240 new_selection_ranges.push(cursor..cursor);
10241 }
10242
10243 let is_multiline_selection = selection.start.row != selection.end.row;
10244 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10245 // so this action feels more ergonomic when paired with other selection operations
10246 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10247 if !should_skip_last {
10248 new_selection_ranges.push(selection.end..selection.end);
10249 }
10250 }
10251 }
10252 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10253 s.select_ranges(new_selection_ranges);
10254 });
10255 }
10256
10257 pub fn add_selection_above(
10258 &mut self,
10259 _: &AddSelectionAbove,
10260 window: &mut Window,
10261 cx: &mut Context<Self>,
10262 ) {
10263 self.add_selection(true, window, cx);
10264 }
10265
10266 pub fn add_selection_below(
10267 &mut self,
10268 _: &AddSelectionBelow,
10269 window: &mut Window,
10270 cx: &mut Context<Self>,
10271 ) {
10272 self.add_selection(false, window, cx);
10273 }
10274
10275 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10276 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10277 let mut selections = self.selections.all::<Point>(cx);
10278 let text_layout_details = self.text_layout_details(window);
10279 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10280 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10281 let range = oldest_selection.display_range(&display_map).sorted();
10282
10283 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10284 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10285 let positions = start_x.min(end_x)..start_x.max(end_x);
10286
10287 selections.clear();
10288 let mut stack = Vec::new();
10289 for row in range.start.row().0..=range.end.row().0 {
10290 if let Some(selection) = self.selections.build_columnar_selection(
10291 &display_map,
10292 DisplayRow(row),
10293 &positions,
10294 oldest_selection.reversed,
10295 &text_layout_details,
10296 ) {
10297 stack.push(selection.id);
10298 selections.push(selection);
10299 }
10300 }
10301
10302 if above {
10303 stack.reverse();
10304 }
10305
10306 AddSelectionsState { above, stack }
10307 });
10308
10309 let last_added_selection = *state.stack.last().unwrap();
10310 let mut new_selections = Vec::new();
10311 if above == state.above {
10312 let end_row = if above {
10313 DisplayRow(0)
10314 } else {
10315 display_map.max_point().row()
10316 };
10317
10318 'outer: for selection in selections {
10319 if selection.id == last_added_selection {
10320 let range = selection.display_range(&display_map).sorted();
10321 debug_assert_eq!(range.start.row(), range.end.row());
10322 let mut row = range.start.row();
10323 let positions =
10324 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10325 px(start)..px(end)
10326 } else {
10327 let start_x =
10328 display_map.x_for_display_point(range.start, &text_layout_details);
10329 let end_x =
10330 display_map.x_for_display_point(range.end, &text_layout_details);
10331 start_x.min(end_x)..start_x.max(end_x)
10332 };
10333
10334 while row != end_row {
10335 if above {
10336 row.0 -= 1;
10337 } else {
10338 row.0 += 1;
10339 }
10340
10341 if let Some(new_selection) = self.selections.build_columnar_selection(
10342 &display_map,
10343 row,
10344 &positions,
10345 selection.reversed,
10346 &text_layout_details,
10347 ) {
10348 state.stack.push(new_selection.id);
10349 if above {
10350 new_selections.push(new_selection);
10351 new_selections.push(selection);
10352 } else {
10353 new_selections.push(selection);
10354 new_selections.push(new_selection);
10355 }
10356
10357 continue 'outer;
10358 }
10359 }
10360 }
10361
10362 new_selections.push(selection);
10363 }
10364 } else {
10365 new_selections = selections;
10366 new_selections.retain(|s| s.id != last_added_selection);
10367 state.stack.pop();
10368 }
10369
10370 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10371 s.select(new_selections);
10372 });
10373 if state.stack.len() > 1 {
10374 self.add_selections_state = Some(state);
10375 }
10376 }
10377
10378 pub fn select_next_match_internal(
10379 &mut self,
10380 display_map: &DisplaySnapshot,
10381 replace_newest: bool,
10382 autoscroll: Option<Autoscroll>,
10383 window: &mut Window,
10384 cx: &mut Context<Self>,
10385 ) -> Result<()> {
10386 fn select_next_match_ranges(
10387 this: &mut Editor,
10388 range: Range<usize>,
10389 replace_newest: bool,
10390 auto_scroll: Option<Autoscroll>,
10391 window: &mut Window,
10392 cx: &mut Context<Editor>,
10393 ) {
10394 this.unfold_ranges(&[range.clone()], false, true, cx);
10395 this.change_selections(auto_scroll, window, cx, |s| {
10396 if replace_newest {
10397 s.delete(s.newest_anchor().id);
10398 }
10399 s.insert_range(range.clone());
10400 });
10401 }
10402
10403 let buffer = &display_map.buffer_snapshot;
10404 let mut selections = self.selections.all::<usize>(cx);
10405 if let Some(mut select_next_state) = self.select_next_state.take() {
10406 let query = &select_next_state.query;
10407 if !select_next_state.done {
10408 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10409 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10410 let mut next_selected_range = None;
10411
10412 let bytes_after_last_selection =
10413 buffer.bytes_in_range(last_selection.end..buffer.len());
10414 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10415 let query_matches = query
10416 .stream_find_iter(bytes_after_last_selection)
10417 .map(|result| (last_selection.end, result))
10418 .chain(
10419 query
10420 .stream_find_iter(bytes_before_first_selection)
10421 .map(|result| (0, result)),
10422 );
10423
10424 for (start_offset, query_match) in query_matches {
10425 let query_match = query_match.unwrap(); // can only fail due to I/O
10426 let offset_range =
10427 start_offset + query_match.start()..start_offset + query_match.end();
10428 let display_range = offset_range.start.to_display_point(display_map)
10429 ..offset_range.end.to_display_point(display_map);
10430
10431 if !select_next_state.wordwise
10432 || (!movement::is_inside_word(display_map, display_range.start)
10433 && !movement::is_inside_word(display_map, display_range.end))
10434 {
10435 // TODO: This is n^2, because we might check all the selections
10436 if !selections
10437 .iter()
10438 .any(|selection| selection.range().overlaps(&offset_range))
10439 {
10440 next_selected_range = Some(offset_range);
10441 break;
10442 }
10443 }
10444 }
10445
10446 if let Some(next_selected_range) = next_selected_range {
10447 select_next_match_ranges(
10448 self,
10449 next_selected_range,
10450 replace_newest,
10451 autoscroll,
10452 window,
10453 cx,
10454 );
10455 } else {
10456 select_next_state.done = true;
10457 }
10458 }
10459
10460 self.select_next_state = Some(select_next_state);
10461 } else {
10462 let mut only_carets = true;
10463 let mut same_text_selected = true;
10464 let mut selected_text = None;
10465
10466 let mut selections_iter = selections.iter().peekable();
10467 while let Some(selection) = selections_iter.next() {
10468 if selection.start != selection.end {
10469 only_carets = false;
10470 }
10471
10472 if same_text_selected {
10473 if selected_text.is_none() {
10474 selected_text =
10475 Some(buffer.text_for_range(selection.range()).collect::<String>());
10476 }
10477
10478 if let Some(next_selection) = selections_iter.peek() {
10479 if next_selection.range().len() == selection.range().len() {
10480 let next_selected_text = buffer
10481 .text_for_range(next_selection.range())
10482 .collect::<String>();
10483 if Some(next_selected_text) != selected_text {
10484 same_text_selected = false;
10485 selected_text = None;
10486 }
10487 } else {
10488 same_text_selected = false;
10489 selected_text = None;
10490 }
10491 }
10492 }
10493 }
10494
10495 if only_carets {
10496 for selection in &mut selections {
10497 let word_range = movement::surrounding_word(
10498 display_map,
10499 selection.start.to_display_point(display_map),
10500 );
10501 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10502 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10503 selection.goal = SelectionGoal::None;
10504 selection.reversed = false;
10505 select_next_match_ranges(
10506 self,
10507 selection.start..selection.end,
10508 replace_newest,
10509 autoscroll,
10510 window,
10511 cx,
10512 );
10513 }
10514
10515 if selections.len() == 1 {
10516 let selection = selections
10517 .last()
10518 .expect("ensured that there's only one selection");
10519 let query = buffer
10520 .text_for_range(selection.start..selection.end)
10521 .collect::<String>();
10522 let is_empty = query.is_empty();
10523 let select_state = SelectNextState {
10524 query: AhoCorasick::new(&[query])?,
10525 wordwise: true,
10526 done: is_empty,
10527 };
10528 self.select_next_state = Some(select_state);
10529 } else {
10530 self.select_next_state = None;
10531 }
10532 } else if let Some(selected_text) = selected_text {
10533 self.select_next_state = Some(SelectNextState {
10534 query: AhoCorasick::new(&[selected_text])?,
10535 wordwise: false,
10536 done: false,
10537 });
10538 self.select_next_match_internal(
10539 display_map,
10540 replace_newest,
10541 autoscroll,
10542 window,
10543 cx,
10544 )?;
10545 }
10546 }
10547 Ok(())
10548 }
10549
10550 pub fn select_all_matches(
10551 &mut self,
10552 _action: &SelectAllMatches,
10553 window: &mut Window,
10554 cx: &mut Context<Self>,
10555 ) -> Result<()> {
10556 self.push_to_selection_history();
10557 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10558
10559 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10560 let Some(select_next_state) = self.select_next_state.as_mut() else {
10561 return Ok(());
10562 };
10563 if select_next_state.done {
10564 return Ok(());
10565 }
10566
10567 let mut new_selections = self.selections.all::<usize>(cx);
10568
10569 let buffer = &display_map.buffer_snapshot;
10570 let query_matches = select_next_state
10571 .query
10572 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10573
10574 for query_match in query_matches {
10575 let query_match = query_match.unwrap(); // can only fail due to I/O
10576 let offset_range = query_match.start()..query_match.end();
10577 let display_range = offset_range.start.to_display_point(&display_map)
10578 ..offset_range.end.to_display_point(&display_map);
10579
10580 if !select_next_state.wordwise
10581 || (!movement::is_inside_word(&display_map, display_range.start)
10582 && !movement::is_inside_word(&display_map, display_range.end))
10583 {
10584 self.selections.change_with(cx, |selections| {
10585 new_selections.push(Selection {
10586 id: selections.new_selection_id(),
10587 start: offset_range.start,
10588 end: offset_range.end,
10589 reversed: false,
10590 goal: SelectionGoal::None,
10591 });
10592 });
10593 }
10594 }
10595
10596 new_selections.sort_by_key(|selection| selection.start);
10597 let mut ix = 0;
10598 while ix + 1 < new_selections.len() {
10599 let current_selection = &new_selections[ix];
10600 let next_selection = &new_selections[ix + 1];
10601 if current_selection.range().overlaps(&next_selection.range()) {
10602 if current_selection.id < next_selection.id {
10603 new_selections.remove(ix + 1);
10604 } else {
10605 new_selections.remove(ix);
10606 }
10607 } else {
10608 ix += 1;
10609 }
10610 }
10611
10612 let reversed = self.selections.oldest::<usize>(cx).reversed;
10613
10614 for selection in new_selections.iter_mut() {
10615 selection.reversed = reversed;
10616 }
10617
10618 select_next_state.done = true;
10619 self.unfold_ranges(
10620 &new_selections
10621 .iter()
10622 .map(|selection| selection.range())
10623 .collect::<Vec<_>>(),
10624 false,
10625 false,
10626 cx,
10627 );
10628 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10629 selections.select(new_selections)
10630 });
10631
10632 Ok(())
10633 }
10634
10635 pub fn select_next(
10636 &mut self,
10637 action: &SelectNext,
10638 window: &mut Window,
10639 cx: &mut Context<Self>,
10640 ) -> Result<()> {
10641 self.push_to_selection_history();
10642 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10643 self.select_next_match_internal(
10644 &display_map,
10645 action.replace_newest,
10646 Some(Autoscroll::newest()),
10647 window,
10648 cx,
10649 )?;
10650 Ok(())
10651 }
10652
10653 pub fn select_previous(
10654 &mut self,
10655 action: &SelectPrevious,
10656 window: &mut Window,
10657 cx: &mut Context<Self>,
10658 ) -> Result<()> {
10659 self.push_to_selection_history();
10660 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10661 let buffer = &display_map.buffer_snapshot;
10662 let mut selections = self.selections.all::<usize>(cx);
10663 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10664 let query = &select_prev_state.query;
10665 if !select_prev_state.done {
10666 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10667 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10668 let mut next_selected_range = None;
10669 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10670 let bytes_before_last_selection =
10671 buffer.reversed_bytes_in_range(0..last_selection.start);
10672 let bytes_after_first_selection =
10673 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10674 let query_matches = query
10675 .stream_find_iter(bytes_before_last_selection)
10676 .map(|result| (last_selection.start, result))
10677 .chain(
10678 query
10679 .stream_find_iter(bytes_after_first_selection)
10680 .map(|result| (buffer.len(), result)),
10681 );
10682 for (end_offset, query_match) in query_matches {
10683 let query_match = query_match.unwrap(); // can only fail due to I/O
10684 let offset_range =
10685 end_offset - query_match.end()..end_offset - query_match.start();
10686 let display_range = offset_range.start.to_display_point(&display_map)
10687 ..offset_range.end.to_display_point(&display_map);
10688
10689 if !select_prev_state.wordwise
10690 || (!movement::is_inside_word(&display_map, display_range.start)
10691 && !movement::is_inside_word(&display_map, display_range.end))
10692 {
10693 next_selected_range = Some(offset_range);
10694 break;
10695 }
10696 }
10697
10698 if let Some(next_selected_range) = next_selected_range {
10699 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10700 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10701 if action.replace_newest {
10702 s.delete(s.newest_anchor().id);
10703 }
10704 s.insert_range(next_selected_range);
10705 });
10706 } else {
10707 select_prev_state.done = true;
10708 }
10709 }
10710
10711 self.select_prev_state = Some(select_prev_state);
10712 } else {
10713 let mut only_carets = true;
10714 let mut same_text_selected = true;
10715 let mut selected_text = None;
10716
10717 let mut selections_iter = selections.iter().peekable();
10718 while let Some(selection) = selections_iter.next() {
10719 if selection.start != selection.end {
10720 only_carets = false;
10721 }
10722
10723 if same_text_selected {
10724 if selected_text.is_none() {
10725 selected_text =
10726 Some(buffer.text_for_range(selection.range()).collect::<String>());
10727 }
10728
10729 if let Some(next_selection) = selections_iter.peek() {
10730 if next_selection.range().len() == selection.range().len() {
10731 let next_selected_text = buffer
10732 .text_for_range(next_selection.range())
10733 .collect::<String>();
10734 if Some(next_selected_text) != selected_text {
10735 same_text_selected = false;
10736 selected_text = None;
10737 }
10738 } else {
10739 same_text_selected = false;
10740 selected_text = None;
10741 }
10742 }
10743 }
10744 }
10745
10746 if only_carets {
10747 for selection in &mut selections {
10748 let word_range = movement::surrounding_word(
10749 &display_map,
10750 selection.start.to_display_point(&display_map),
10751 );
10752 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10753 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10754 selection.goal = SelectionGoal::None;
10755 selection.reversed = false;
10756 }
10757 if selections.len() == 1 {
10758 let selection = selections
10759 .last()
10760 .expect("ensured that there's only one selection");
10761 let query = buffer
10762 .text_for_range(selection.start..selection.end)
10763 .collect::<String>();
10764 let is_empty = query.is_empty();
10765 let select_state = SelectNextState {
10766 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10767 wordwise: true,
10768 done: is_empty,
10769 };
10770 self.select_prev_state = Some(select_state);
10771 } else {
10772 self.select_prev_state = None;
10773 }
10774
10775 self.unfold_ranges(
10776 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10777 false,
10778 true,
10779 cx,
10780 );
10781 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10782 s.select(selections);
10783 });
10784 } else if let Some(selected_text) = selected_text {
10785 self.select_prev_state = Some(SelectNextState {
10786 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10787 wordwise: false,
10788 done: false,
10789 });
10790 self.select_previous(action, window, cx)?;
10791 }
10792 }
10793 Ok(())
10794 }
10795
10796 pub fn toggle_comments(
10797 &mut self,
10798 action: &ToggleComments,
10799 window: &mut Window,
10800 cx: &mut Context<Self>,
10801 ) {
10802 if self.read_only(cx) {
10803 return;
10804 }
10805 let text_layout_details = &self.text_layout_details(window);
10806 self.transact(window, cx, |this, window, cx| {
10807 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10808 let mut edits = Vec::new();
10809 let mut selection_edit_ranges = Vec::new();
10810 let mut last_toggled_row = None;
10811 let snapshot = this.buffer.read(cx).read(cx);
10812 let empty_str: Arc<str> = Arc::default();
10813 let mut suffixes_inserted = Vec::new();
10814 let ignore_indent = action.ignore_indent;
10815
10816 fn comment_prefix_range(
10817 snapshot: &MultiBufferSnapshot,
10818 row: MultiBufferRow,
10819 comment_prefix: &str,
10820 comment_prefix_whitespace: &str,
10821 ignore_indent: bool,
10822 ) -> Range<Point> {
10823 let indent_size = if ignore_indent {
10824 0
10825 } else {
10826 snapshot.indent_size_for_line(row).len
10827 };
10828
10829 let start = Point::new(row.0, indent_size);
10830
10831 let mut line_bytes = snapshot
10832 .bytes_in_range(start..snapshot.max_point())
10833 .flatten()
10834 .copied();
10835
10836 // If this line currently begins with the line comment prefix, then record
10837 // the range containing the prefix.
10838 if line_bytes
10839 .by_ref()
10840 .take(comment_prefix.len())
10841 .eq(comment_prefix.bytes())
10842 {
10843 // Include any whitespace that matches the comment prefix.
10844 let matching_whitespace_len = line_bytes
10845 .zip(comment_prefix_whitespace.bytes())
10846 .take_while(|(a, b)| a == b)
10847 .count() as u32;
10848 let end = Point::new(
10849 start.row,
10850 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10851 );
10852 start..end
10853 } else {
10854 start..start
10855 }
10856 }
10857
10858 fn comment_suffix_range(
10859 snapshot: &MultiBufferSnapshot,
10860 row: MultiBufferRow,
10861 comment_suffix: &str,
10862 comment_suffix_has_leading_space: bool,
10863 ) -> Range<Point> {
10864 let end = Point::new(row.0, snapshot.line_len(row));
10865 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10866
10867 let mut line_end_bytes = snapshot
10868 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10869 .flatten()
10870 .copied();
10871
10872 let leading_space_len = if suffix_start_column > 0
10873 && line_end_bytes.next() == Some(b' ')
10874 && comment_suffix_has_leading_space
10875 {
10876 1
10877 } else {
10878 0
10879 };
10880
10881 // If this line currently begins with the line comment prefix, then record
10882 // the range containing the prefix.
10883 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10884 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10885 start..end
10886 } else {
10887 end..end
10888 }
10889 }
10890
10891 // TODO: Handle selections that cross excerpts
10892 for selection in &mut selections {
10893 let start_column = snapshot
10894 .indent_size_for_line(MultiBufferRow(selection.start.row))
10895 .len;
10896 let language = if let Some(language) =
10897 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10898 {
10899 language
10900 } else {
10901 continue;
10902 };
10903
10904 selection_edit_ranges.clear();
10905
10906 // If multiple selections contain a given row, avoid processing that
10907 // row more than once.
10908 let mut start_row = MultiBufferRow(selection.start.row);
10909 if last_toggled_row == Some(start_row) {
10910 start_row = start_row.next_row();
10911 }
10912 let end_row =
10913 if selection.end.row > selection.start.row && selection.end.column == 0 {
10914 MultiBufferRow(selection.end.row - 1)
10915 } else {
10916 MultiBufferRow(selection.end.row)
10917 };
10918 last_toggled_row = Some(end_row);
10919
10920 if start_row > end_row {
10921 continue;
10922 }
10923
10924 // If the language has line comments, toggle those.
10925 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10926
10927 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10928 if ignore_indent {
10929 full_comment_prefixes = full_comment_prefixes
10930 .into_iter()
10931 .map(|s| Arc::from(s.trim_end()))
10932 .collect();
10933 }
10934
10935 if !full_comment_prefixes.is_empty() {
10936 let first_prefix = full_comment_prefixes
10937 .first()
10938 .expect("prefixes is non-empty");
10939 let prefix_trimmed_lengths = full_comment_prefixes
10940 .iter()
10941 .map(|p| p.trim_end_matches(' ').len())
10942 .collect::<SmallVec<[usize; 4]>>();
10943
10944 let mut all_selection_lines_are_comments = true;
10945
10946 for row in start_row.0..=end_row.0 {
10947 let row = MultiBufferRow(row);
10948 if start_row < end_row && snapshot.is_line_blank(row) {
10949 continue;
10950 }
10951
10952 let prefix_range = full_comment_prefixes
10953 .iter()
10954 .zip(prefix_trimmed_lengths.iter().copied())
10955 .map(|(prefix, trimmed_prefix_len)| {
10956 comment_prefix_range(
10957 snapshot.deref(),
10958 row,
10959 &prefix[..trimmed_prefix_len],
10960 &prefix[trimmed_prefix_len..],
10961 ignore_indent,
10962 )
10963 })
10964 .max_by_key(|range| range.end.column - range.start.column)
10965 .expect("prefixes is non-empty");
10966
10967 if prefix_range.is_empty() {
10968 all_selection_lines_are_comments = false;
10969 }
10970
10971 selection_edit_ranges.push(prefix_range);
10972 }
10973
10974 if all_selection_lines_are_comments {
10975 edits.extend(
10976 selection_edit_ranges
10977 .iter()
10978 .cloned()
10979 .map(|range| (range, empty_str.clone())),
10980 );
10981 } else {
10982 let min_column = selection_edit_ranges
10983 .iter()
10984 .map(|range| range.start.column)
10985 .min()
10986 .unwrap_or(0);
10987 edits.extend(selection_edit_ranges.iter().map(|range| {
10988 let position = Point::new(range.start.row, min_column);
10989 (position..position, first_prefix.clone())
10990 }));
10991 }
10992 } else if let Some((full_comment_prefix, comment_suffix)) =
10993 language.block_comment_delimiters()
10994 {
10995 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
10996 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
10997 let prefix_range = comment_prefix_range(
10998 snapshot.deref(),
10999 start_row,
11000 comment_prefix,
11001 comment_prefix_whitespace,
11002 ignore_indent,
11003 );
11004 let suffix_range = comment_suffix_range(
11005 snapshot.deref(),
11006 end_row,
11007 comment_suffix.trim_start_matches(' '),
11008 comment_suffix.starts_with(' '),
11009 );
11010
11011 if prefix_range.is_empty() || suffix_range.is_empty() {
11012 edits.push((
11013 prefix_range.start..prefix_range.start,
11014 full_comment_prefix.clone(),
11015 ));
11016 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11017 suffixes_inserted.push((end_row, comment_suffix.len()));
11018 } else {
11019 edits.push((prefix_range, empty_str.clone()));
11020 edits.push((suffix_range, empty_str.clone()));
11021 }
11022 } else {
11023 continue;
11024 }
11025 }
11026
11027 drop(snapshot);
11028 this.buffer.update(cx, |buffer, cx| {
11029 buffer.edit(edits, None, cx);
11030 });
11031
11032 // Adjust selections so that they end before any comment suffixes that
11033 // were inserted.
11034 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11035 let mut selections = this.selections.all::<Point>(cx);
11036 let snapshot = this.buffer.read(cx).read(cx);
11037 for selection in &mut selections {
11038 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11039 match row.cmp(&MultiBufferRow(selection.end.row)) {
11040 Ordering::Less => {
11041 suffixes_inserted.next();
11042 continue;
11043 }
11044 Ordering::Greater => break,
11045 Ordering::Equal => {
11046 if selection.end.column == snapshot.line_len(row) {
11047 if selection.is_empty() {
11048 selection.start.column -= suffix_len as u32;
11049 }
11050 selection.end.column -= suffix_len as u32;
11051 }
11052 break;
11053 }
11054 }
11055 }
11056 }
11057
11058 drop(snapshot);
11059 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11060 s.select(selections)
11061 });
11062
11063 let selections = this.selections.all::<Point>(cx);
11064 let selections_on_single_row = selections.windows(2).all(|selections| {
11065 selections[0].start.row == selections[1].start.row
11066 && selections[0].end.row == selections[1].end.row
11067 && selections[0].start.row == selections[0].end.row
11068 });
11069 let selections_selecting = selections
11070 .iter()
11071 .any(|selection| selection.start != selection.end);
11072 let advance_downwards = action.advance_downwards
11073 && selections_on_single_row
11074 && !selections_selecting
11075 && !matches!(this.mode, EditorMode::SingleLine { .. });
11076
11077 if advance_downwards {
11078 let snapshot = this.buffer.read(cx).snapshot(cx);
11079
11080 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11081 s.move_cursors_with(|display_snapshot, display_point, _| {
11082 let mut point = display_point.to_point(display_snapshot);
11083 point.row += 1;
11084 point = snapshot.clip_point(point, Bias::Left);
11085 let display_point = point.to_display_point(display_snapshot);
11086 let goal = SelectionGoal::HorizontalPosition(
11087 display_snapshot
11088 .x_for_display_point(display_point, text_layout_details)
11089 .into(),
11090 );
11091 (display_point, goal)
11092 })
11093 });
11094 }
11095 });
11096 }
11097
11098 pub fn select_enclosing_symbol(
11099 &mut self,
11100 _: &SelectEnclosingSymbol,
11101 window: &mut Window,
11102 cx: &mut Context<Self>,
11103 ) {
11104 let buffer = self.buffer.read(cx).snapshot(cx);
11105 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11106
11107 fn update_selection(
11108 selection: &Selection<usize>,
11109 buffer_snap: &MultiBufferSnapshot,
11110 ) -> Option<Selection<usize>> {
11111 let cursor = selection.head();
11112 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11113 for symbol in symbols.iter().rev() {
11114 let start = symbol.range.start.to_offset(buffer_snap);
11115 let end = symbol.range.end.to_offset(buffer_snap);
11116 let new_range = start..end;
11117 if start < selection.start || end > selection.end {
11118 return Some(Selection {
11119 id: selection.id,
11120 start: new_range.start,
11121 end: new_range.end,
11122 goal: SelectionGoal::None,
11123 reversed: selection.reversed,
11124 });
11125 }
11126 }
11127 None
11128 }
11129
11130 let mut selected_larger_symbol = false;
11131 let new_selections = old_selections
11132 .iter()
11133 .map(|selection| match update_selection(selection, &buffer) {
11134 Some(new_selection) => {
11135 if new_selection.range() != selection.range() {
11136 selected_larger_symbol = true;
11137 }
11138 new_selection
11139 }
11140 None => selection.clone(),
11141 })
11142 .collect::<Vec<_>>();
11143
11144 if selected_larger_symbol {
11145 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11146 s.select(new_selections);
11147 });
11148 }
11149 }
11150
11151 pub fn select_larger_syntax_node(
11152 &mut self,
11153 _: &SelectLargerSyntaxNode,
11154 window: &mut Window,
11155 cx: &mut Context<Self>,
11156 ) {
11157 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11158 let buffer = self.buffer.read(cx).snapshot(cx);
11159 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11160
11161 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11162 let mut selected_larger_node = false;
11163 let new_selections = old_selections
11164 .iter()
11165 .map(|selection| {
11166 let old_range = selection.start..selection.end;
11167 let mut new_range = old_range.clone();
11168 let mut new_node = None;
11169 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11170 {
11171 new_node = Some(node);
11172 new_range = match containing_range {
11173 MultiOrSingleBufferOffsetRange::Single(_) => break,
11174 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11175 };
11176 if !display_map.intersects_fold(new_range.start)
11177 && !display_map.intersects_fold(new_range.end)
11178 {
11179 break;
11180 }
11181 }
11182
11183 if let Some(node) = new_node {
11184 // Log the ancestor, to support using this action as a way to explore TreeSitter
11185 // nodes. Parent and grandparent are also logged because this operation will not
11186 // visit nodes that have the same range as their parent.
11187 log::info!("Node: {node:?}");
11188 let parent = node.parent();
11189 log::info!("Parent: {parent:?}");
11190 let grandparent = parent.and_then(|x| x.parent());
11191 log::info!("Grandparent: {grandparent:?}");
11192 }
11193
11194 selected_larger_node |= new_range != old_range;
11195 Selection {
11196 id: selection.id,
11197 start: new_range.start,
11198 end: new_range.end,
11199 goal: SelectionGoal::None,
11200 reversed: selection.reversed,
11201 }
11202 })
11203 .collect::<Vec<_>>();
11204
11205 if selected_larger_node {
11206 stack.push(old_selections);
11207 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11208 s.select(new_selections);
11209 });
11210 }
11211 self.select_larger_syntax_node_stack = stack;
11212 }
11213
11214 pub fn select_smaller_syntax_node(
11215 &mut self,
11216 _: &SelectSmallerSyntaxNode,
11217 window: &mut Window,
11218 cx: &mut Context<Self>,
11219 ) {
11220 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11221 if let Some(selections) = stack.pop() {
11222 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11223 s.select(selections.to_vec());
11224 });
11225 }
11226 self.select_larger_syntax_node_stack = stack;
11227 }
11228
11229 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11230 if !EditorSettings::get_global(cx).gutter.runnables {
11231 self.clear_tasks();
11232 return Task::ready(());
11233 }
11234 let project = self.project.as_ref().map(Entity::downgrade);
11235 cx.spawn_in(window, |this, mut cx| async move {
11236 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11237 let Some(project) = project.and_then(|p| p.upgrade()) else {
11238 return;
11239 };
11240 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11241 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11242 }) else {
11243 return;
11244 };
11245
11246 let hide_runnables = project
11247 .update(&mut cx, |project, cx| {
11248 // Do not display any test indicators in non-dev server remote projects.
11249 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11250 })
11251 .unwrap_or(true);
11252 if hide_runnables {
11253 return;
11254 }
11255 let new_rows =
11256 cx.background_spawn({
11257 let snapshot = display_snapshot.clone();
11258 async move {
11259 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11260 }
11261 })
11262 .await;
11263
11264 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11265 this.update(&mut cx, |this, _| {
11266 this.clear_tasks();
11267 for (key, value) in rows {
11268 this.insert_tasks(key, value);
11269 }
11270 })
11271 .ok();
11272 })
11273 }
11274 fn fetch_runnable_ranges(
11275 snapshot: &DisplaySnapshot,
11276 range: Range<Anchor>,
11277 ) -> Vec<language::RunnableRange> {
11278 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11279 }
11280
11281 fn runnable_rows(
11282 project: Entity<Project>,
11283 snapshot: DisplaySnapshot,
11284 runnable_ranges: Vec<RunnableRange>,
11285 mut cx: AsyncWindowContext,
11286 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11287 runnable_ranges
11288 .into_iter()
11289 .filter_map(|mut runnable| {
11290 let tasks = cx
11291 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11292 .ok()?;
11293 if tasks.is_empty() {
11294 return None;
11295 }
11296
11297 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11298
11299 let row = snapshot
11300 .buffer_snapshot
11301 .buffer_line_for_row(MultiBufferRow(point.row))?
11302 .1
11303 .start
11304 .row;
11305
11306 let context_range =
11307 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11308 Some((
11309 (runnable.buffer_id, row),
11310 RunnableTasks {
11311 templates: tasks,
11312 offset: snapshot
11313 .buffer_snapshot
11314 .anchor_before(runnable.run_range.start),
11315 context_range,
11316 column: point.column,
11317 extra_variables: runnable.extra_captures,
11318 },
11319 ))
11320 })
11321 .collect()
11322 }
11323
11324 fn templates_with_tags(
11325 project: &Entity<Project>,
11326 runnable: &mut Runnable,
11327 cx: &mut App,
11328 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11329 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11330 let (worktree_id, file) = project
11331 .buffer_for_id(runnable.buffer, cx)
11332 .and_then(|buffer| buffer.read(cx).file())
11333 .map(|file| (file.worktree_id(cx), file.clone()))
11334 .unzip();
11335
11336 (
11337 project.task_store().read(cx).task_inventory().cloned(),
11338 worktree_id,
11339 file,
11340 )
11341 });
11342
11343 let tags = mem::take(&mut runnable.tags);
11344 let mut tags: Vec<_> = tags
11345 .into_iter()
11346 .flat_map(|tag| {
11347 let tag = tag.0.clone();
11348 inventory
11349 .as_ref()
11350 .into_iter()
11351 .flat_map(|inventory| {
11352 inventory.read(cx).list_tasks(
11353 file.clone(),
11354 Some(runnable.language.clone()),
11355 worktree_id,
11356 cx,
11357 )
11358 })
11359 .filter(move |(_, template)| {
11360 template.tags.iter().any(|source_tag| source_tag == &tag)
11361 })
11362 })
11363 .sorted_by_key(|(kind, _)| kind.to_owned())
11364 .collect();
11365 if let Some((leading_tag_source, _)) = tags.first() {
11366 // Strongest source wins; if we have worktree tag binding, prefer that to
11367 // global and language bindings;
11368 // if we have a global binding, prefer that to language binding.
11369 let first_mismatch = tags
11370 .iter()
11371 .position(|(tag_source, _)| tag_source != leading_tag_source);
11372 if let Some(index) = first_mismatch {
11373 tags.truncate(index);
11374 }
11375 }
11376
11377 tags
11378 }
11379
11380 pub fn move_to_enclosing_bracket(
11381 &mut self,
11382 _: &MoveToEnclosingBracket,
11383 window: &mut Window,
11384 cx: &mut Context<Self>,
11385 ) {
11386 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11387 s.move_offsets_with(|snapshot, selection| {
11388 let Some(enclosing_bracket_ranges) =
11389 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11390 else {
11391 return;
11392 };
11393
11394 let mut best_length = usize::MAX;
11395 let mut best_inside = false;
11396 let mut best_in_bracket_range = false;
11397 let mut best_destination = None;
11398 for (open, close) in enclosing_bracket_ranges {
11399 let close = close.to_inclusive();
11400 let length = close.end() - open.start;
11401 let inside = selection.start >= open.end && selection.end <= *close.start();
11402 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11403 || close.contains(&selection.head());
11404
11405 // If best is next to a bracket and current isn't, skip
11406 if !in_bracket_range && best_in_bracket_range {
11407 continue;
11408 }
11409
11410 // Prefer smaller lengths unless best is inside and current isn't
11411 if length > best_length && (best_inside || !inside) {
11412 continue;
11413 }
11414
11415 best_length = length;
11416 best_inside = inside;
11417 best_in_bracket_range = in_bracket_range;
11418 best_destination = Some(
11419 if close.contains(&selection.start) && close.contains(&selection.end) {
11420 if inside {
11421 open.end
11422 } else {
11423 open.start
11424 }
11425 } else if inside {
11426 *close.start()
11427 } else {
11428 *close.end()
11429 },
11430 );
11431 }
11432
11433 if let Some(destination) = best_destination {
11434 selection.collapse_to(destination, SelectionGoal::None);
11435 }
11436 })
11437 });
11438 }
11439
11440 pub fn undo_selection(
11441 &mut self,
11442 _: &UndoSelection,
11443 window: &mut Window,
11444 cx: &mut Context<Self>,
11445 ) {
11446 self.end_selection(window, cx);
11447 self.selection_history.mode = SelectionHistoryMode::Undoing;
11448 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
11449 self.change_selections(None, window, cx, |s| {
11450 s.select_anchors(entry.selections.to_vec())
11451 });
11452 self.select_next_state = entry.select_next_state;
11453 self.select_prev_state = entry.select_prev_state;
11454 self.add_selections_state = entry.add_selections_state;
11455 self.request_autoscroll(Autoscroll::newest(), cx);
11456 }
11457 self.selection_history.mode = SelectionHistoryMode::Normal;
11458 }
11459
11460 pub fn redo_selection(
11461 &mut self,
11462 _: &RedoSelection,
11463 window: &mut Window,
11464 cx: &mut Context<Self>,
11465 ) {
11466 self.end_selection(window, cx);
11467 self.selection_history.mode = SelectionHistoryMode::Redoing;
11468 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11469 self.change_selections(None, window, cx, |s| {
11470 s.select_anchors(entry.selections.to_vec())
11471 });
11472 self.select_next_state = entry.select_next_state;
11473 self.select_prev_state = entry.select_prev_state;
11474 self.add_selections_state = entry.add_selections_state;
11475 self.request_autoscroll(Autoscroll::newest(), cx);
11476 }
11477 self.selection_history.mode = SelectionHistoryMode::Normal;
11478 }
11479
11480 pub fn expand_excerpts(
11481 &mut self,
11482 action: &ExpandExcerpts,
11483 _: &mut Window,
11484 cx: &mut Context<Self>,
11485 ) {
11486 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11487 }
11488
11489 pub fn expand_excerpts_down(
11490 &mut self,
11491 action: &ExpandExcerptsDown,
11492 _: &mut Window,
11493 cx: &mut Context<Self>,
11494 ) {
11495 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11496 }
11497
11498 pub fn expand_excerpts_up(
11499 &mut self,
11500 action: &ExpandExcerptsUp,
11501 _: &mut Window,
11502 cx: &mut Context<Self>,
11503 ) {
11504 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11505 }
11506
11507 pub fn expand_excerpts_for_direction(
11508 &mut self,
11509 lines: u32,
11510 direction: ExpandExcerptDirection,
11511
11512 cx: &mut Context<Self>,
11513 ) {
11514 let selections = self.selections.disjoint_anchors();
11515
11516 let lines = if lines == 0 {
11517 EditorSettings::get_global(cx).expand_excerpt_lines
11518 } else {
11519 lines
11520 };
11521
11522 self.buffer.update(cx, |buffer, cx| {
11523 let snapshot = buffer.snapshot(cx);
11524 let mut excerpt_ids = selections
11525 .iter()
11526 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11527 .collect::<Vec<_>>();
11528 excerpt_ids.sort();
11529 excerpt_ids.dedup();
11530 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11531 })
11532 }
11533
11534 pub fn expand_excerpt(
11535 &mut self,
11536 excerpt: ExcerptId,
11537 direction: ExpandExcerptDirection,
11538 cx: &mut Context<Self>,
11539 ) {
11540 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11541 self.buffer.update(cx, |buffer, cx| {
11542 buffer.expand_excerpts([excerpt], lines, direction, cx)
11543 })
11544 }
11545
11546 pub fn go_to_singleton_buffer_point(
11547 &mut self,
11548 point: Point,
11549 window: &mut Window,
11550 cx: &mut Context<Self>,
11551 ) {
11552 self.go_to_singleton_buffer_range(point..point, window, cx);
11553 }
11554
11555 pub fn go_to_singleton_buffer_range(
11556 &mut self,
11557 range: Range<Point>,
11558 window: &mut Window,
11559 cx: &mut Context<Self>,
11560 ) {
11561 let multibuffer = self.buffer().read(cx);
11562 let Some(buffer) = multibuffer.as_singleton() else {
11563 return;
11564 };
11565 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11566 return;
11567 };
11568 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11569 return;
11570 };
11571 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11572 s.select_anchor_ranges([start..end])
11573 });
11574 }
11575
11576 fn go_to_diagnostic(
11577 &mut self,
11578 _: &GoToDiagnostic,
11579 window: &mut Window,
11580 cx: &mut Context<Self>,
11581 ) {
11582 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11583 }
11584
11585 fn go_to_prev_diagnostic(
11586 &mut self,
11587 _: &GoToPreviousDiagnostic,
11588 window: &mut Window,
11589 cx: &mut Context<Self>,
11590 ) {
11591 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11592 }
11593
11594 pub fn go_to_diagnostic_impl(
11595 &mut self,
11596 direction: Direction,
11597 window: &mut Window,
11598 cx: &mut Context<Self>,
11599 ) {
11600 let buffer = self.buffer.read(cx).snapshot(cx);
11601 let selection = self.selections.newest::<usize>(cx);
11602
11603 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11604 if direction == Direction::Next {
11605 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11606 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11607 return;
11608 };
11609 self.activate_diagnostics(
11610 buffer_id,
11611 popover.local_diagnostic.diagnostic.group_id,
11612 window,
11613 cx,
11614 );
11615 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11616 let primary_range_start = active_diagnostics.primary_range.start;
11617 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11618 let mut new_selection = s.newest_anchor().clone();
11619 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11620 s.select_anchors(vec![new_selection.clone()]);
11621 });
11622 self.refresh_inline_completion(false, true, window, cx);
11623 }
11624 return;
11625 }
11626 }
11627
11628 let active_group_id = self
11629 .active_diagnostics
11630 .as_ref()
11631 .map(|active_group| active_group.group_id);
11632 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11633 active_diagnostics
11634 .primary_range
11635 .to_offset(&buffer)
11636 .to_inclusive()
11637 });
11638 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11639 if active_primary_range.contains(&selection.head()) {
11640 *active_primary_range.start()
11641 } else {
11642 selection.head()
11643 }
11644 } else {
11645 selection.head()
11646 };
11647
11648 let snapshot = self.snapshot(window, cx);
11649 let primary_diagnostics_before = buffer
11650 .diagnostics_in_range::<usize>(0..search_start)
11651 .filter(|entry| entry.diagnostic.is_primary)
11652 .filter(|entry| entry.range.start != entry.range.end)
11653 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11654 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11655 .collect::<Vec<_>>();
11656 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11657 primary_diagnostics_before
11658 .iter()
11659 .position(|entry| entry.diagnostic.group_id == active_group_id)
11660 });
11661
11662 let primary_diagnostics_after = buffer
11663 .diagnostics_in_range::<usize>(search_start..buffer.len())
11664 .filter(|entry| entry.diagnostic.is_primary)
11665 .filter(|entry| entry.range.start != entry.range.end)
11666 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11667 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11668 .collect::<Vec<_>>();
11669 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11670 primary_diagnostics_after
11671 .iter()
11672 .enumerate()
11673 .rev()
11674 .find_map(|(i, entry)| {
11675 if entry.diagnostic.group_id == active_group_id {
11676 Some(i)
11677 } else {
11678 None
11679 }
11680 })
11681 });
11682
11683 let next_primary_diagnostic = match direction {
11684 Direction::Prev => primary_diagnostics_before
11685 .iter()
11686 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11687 .rev()
11688 .next(),
11689 Direction::Next => primary_diagnostics_after
11690 .iter()
11691 .skip(
11692 last_same_group_diagnostic_after
11693 .map(|index| index + 1)
11694 .unwrap_or(0),
11695 )
11696 .next(),
11697 };
11698
11699 // Cycle around to the start of the buffer, potentially moving back to the start of
11700 // the currently active diagnostic.
11701 let cycle_around = || match direction {
11702 Direction::Prev => primary_diagnostics_after
11703 .iter()
11704 .rev()
11705 .chain(primary_diagnostics_before.iter().rev())
11706 .next(),
11707 Direction::Next => primary_diagnostics_before
11708 .iter()
11709 .chain(primary_diagnostics_after.iter())
11710 .next(),
11711 };
11712
11713 if let Some((primary_range, group_id)) = next_primary_diagnostic
11714 .or_else(cycle_around)
11715 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11716 {
11717 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11718 return;
11719 };
11720 self.activate_diagnostics(buffer_id, group_id, window, cx);
11721 if self.active_diagnostics.is_some() {
11722 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11723 s.select(vec![Selection {
11724 id: selection.id,
11725 start: primary_range.start,
11726 end: primary_range.start,
11727 reversed: false,
11728 goal: SelectionGoal::None,
11729 }]);
11730 });
11731 self.refresh_inline_completion(false, true, window, cx);
11732 }
11733 }
11734 }
11735
11736 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11737 let snapshot = self.snapshot(window, cx);
11738 let selection = self.selections.newest::<Point>(cx);
11739 self.go_to_hunk_before_or_after_position(
11740 &snapshot,
11741 selection.head(),
11742 Direction::Next,
11743 window,
11744 cx,
11745 );
11746 }
11747
11748 fn go_to_hunk_before_or_after_position(
11749 &mut self,
11750 snapshot: &EditorSnapshot,
11751 position: Point,
11752 direction: Direction,
11753 window: &mut Window,
11754 cx: &mut Context<Editor>,
11755 ) {
11756 let row = if direction == Direction::Next {
11757 self.hunk_after_position(snapshot, position)
11758 .map(|hunk| hunk.row_range.start)
11759 } else {
11760 self.hunk_before_position(snapshot, position)
11761 };
11762
11763 if let Some(row) = row {
11764 let destination = Point::new(row.0, 0);
11765 let autoscroll = Autoscroll::center();
11766
11767 self.unfold_ranges(&[destination..destination], false, false, cx);
11768 self.change_selections(Some(autoscroll), window, cx, |s| {
11769 s.select_ranges([destination..destination]);
11770 });
11771 }
11772 }
11773
11774 fn hunk_after_position(
11775 &mut self,
11776 snapshot: &EditorSnapshot,
11777 position: Point,
11778 ) -> Option<MultiBufferDiffHunk> {
11779 snapshot
11780 .buffer_snapshot
11781 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11782 .find(|hunk| hunk.row_range.start.0 > position.row)
11783 .or_else(|| {
11784 snapshot
11785 .buffer_snapshot
11786 .diff_hunks_in_range(Point::zero()..position)
11787 .find(|hunk| hunk.row_range.end.0 < position.row)
11788 })
11789 }
11790
11791 fn go_to_prev_hunk(
11792 &mut self,
11793 _: &GoToPreviousHunk,
11794 window: &mut Window,
11795 cx: &mut Context<Self>,
11796 ) {
11797 let snapshot = self.snapshot(window, cx);
11798 let selection = self.selections.newest::<Point>(cx);
11799 self.go_to_hunk_before_or_after_position(
11800 &snapshot,
11801 selection.head(),
11802 Direction::Prev,
11803 window,
11804 cx,
11805 );
11806 }
11807
11808 fn hunk_before_position(
11809 &mut self,
11810 snapshot: &EditorSnapshot,
11811 position: Point,
11812 ) -> Option<MultiBufferRow> {
11813 snapshot
11814 .buffer_snapshot
11815 .diff_hunk_before(position)
11816 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
11817 }
11818
11819 pub fn go_to_definition(
11820 &mut self,
11821 _: &GoToDefinition,
11822 window: &mut Window,
11823 cx: &mut Context<Self>,
11824 ) -> Task<Result<Navigated>> {
11825 let definition =
11826 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11827 cx.spawn_in(window, |editor, mut cx| async move {
11828 if definition.await? == Navigated::Yes {
11829 return Ok(Navigated::Yes);
11830 }
11831 match editor.update_in(&mut cx, |editor, window, cx| {
11832 editor.find_all_references(&FindAllReferences, window, cx)
11833 })? {
11834 Some(references) => references.await,
11835 None => Ok(Navigated::No),
11836 }
11837 })
11838 }
11839
11840 pub fn go_to_declaration(
11841 &mut self,
11842 _: &GoToDeclaration,
11843 window: &mut Window,
11844 cx: &mut Context<Self>,
11845 ) -> Task<Result<Navigated>> {
11846 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11847 }
11848
11849 pub fn go_to_declaration_split(
11850 &mut self,
11851 _: &GoToDeclaration,
11852 window: &mut Window,
11853 cx: &mut Context<Self>,
11854 ) -> Task<Result<Navigated>> {
11855 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11856 }
11857
11858 pub fn go_to_implementation(
11859 &mut self,
11860 _: &GoToImplementation,
11861 window: &mut Window,
11862 cx: &mut Context<Self>,
11863 ) -> Task<Result<Navigated>> {
11864 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11865 }
11866
11867 pub fn go_to_implementation_split(
11868 &mut self,
11869 _: &GoToImplementationSplit,
11870 window: &mut Window,
11871 cx: &mut Context<Self>,
11872 ) -> Task<Result<Navigated>> {
11873 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11874 }
11875
11876 pub fn go_to_type_definition(
11877 &mut self,
11878 _: &GoToTypeDefinition,
11879 window: &mut Window,
11880 cx: &mut Context<Self>,
11881 ) -> Task<Result<Navigated>> {
11882 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11883 }
11884
11885 pub fn go_to_definition_split(
11886 &mut self,
11887 _: &GoToDefinitionSplit,
11888 window: &mut Window,
11889 cx: &mut Context<Self>,
11890 ) -> Task<Result<Navigated>> {
11891 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11892 }
11893
11894 pub fn go_to_type_definition_split(
11895 &mut self,
11896 _: &GoToTypeDefinitionSplit,
11897 window: &mut Window,
11898 cx: &mut Context<Self>,
11899 ) -> Task<Result<Navigated>> {
11900 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11901 }
11902
11903 fn go_to_definition_of_kind(
11904 &mut self,
11905 kind: GotoDefinitionKind,
11906 split: bool,
11907 window: &mut Window,
11908 cx: &mut Context<Self>,
11909 ) -> Task<Result<Navigated>> {
11910 let Some(provider) = self.semantics_provider.clone() else {
11911 return Task::ready(Ok(Navigated::No));
11912 };
11913 let head = self.selections.newest::<usize>(cx).head();
11914 let buffer = self.buffer.read(cx);
11915 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11916 text_anchor
11917 } else {
11918 return Task::ready(Ok(Navigated::No));
11919 };
11920
11921 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11922 return Task::ready(Ok(Navigated::No));
11923 };
11924
11925 cx.spawn_in(window, |editor, mut cx| async move {
11926 let definitions = definitions.await?;
11927 let navigated = editor
11928 .update_in(&mut cx, |editor, window, cx| {
11929 editor.navigate_to_hover_links(
11930 Some(kind),
11931 definitions
11932 .into_iter()
11933 .filter(|location| {
11934 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11935 })
11936 .map(HoverLink::Text)
11937 .collect::<Vec<_>>(),
11938 split,
11939 window,
11940 cx,
11941 )
11942 })?
11943 .await?;
11944 anyhow::Ok(navigated)
11945 })
11946 }
11947
11948 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11949 let selection = self.selections.newest_anchor();
11950 let head = selection.head();
11951 let tail = selection.tail();
11952
11953 let Some((buffer, start_position)) =
11954 self.buffer.read(cx).text_anchor_for_position(head, cx)
11955 else {
11956 return;
11957 };
11958
11959 let end_position = if head != tail {
11960 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11961 return;
11962 };
11963 Some(pos)
11964 } else {
11965 None
11966 };
11967
11968 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11969 let url = if let Some(end_pos) = end_position {
11970 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11971 } else {
11972 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11973 };
11974
11975 if let Some(url) = url {
11976 editor.update(&mut cx, |_, cx| {
11977 cx.open_url(&url);
11978 })
11979 } else {
11980 Ok(())
11981 }
11982 });
11983
11984 url_finder.detach();
11985 }
11986
11987 pub fn open_selected_filename(
11988 &mut self,
11989 _: &OpenSelectedFilename,
11990 window: &mut Window,
11991 cx: &mut Context<Self>,
11992 ) {
11993 let Some(workspace) = self.workspace() else {
11994 return;
11995 };
11996
11997 let position = self.selections.newest_anchor().head();
11998
11999 let Some((buffer, buffer_position)) =
12000 self.buffer.read(cx).text_anchor_for_position(position, cx)
12001 else {
12002 return;
12003 };
12004
12005 let project = self.project.clone();
12006
12007 cx.spawn_in(window, |_, mut cx| async move {
12008 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
12009
12010 if let Some((_, path)) = result {
12011 workspace
12012 .update_in(&mut cx, |workspace, window, cx| {
12013 workspace.open_resolved_path(path, window, cx)
12014 })?
12015 .await?;
12016 }
12017 anyhow::Ok(())
12018 })
12019 .detach();
12020 }
12021
12022 pub(crate) fn navigate_to_hover_links(
12023 &mut self,
12024 kind: Option<GotoDefinitionKind>,
12025 mut definitions: Vec<HoverLink>,
12026 split: bool,
12027 window: &mut Window,
12028 cx: &mut Context<Editor>,
12029 ) -> Task<Result<Navigated>> {
12030 // If there is one definition, just open it directly
12031 if definitions.len() == 1 {
12032 let definition = definitions.pop().unwrap();
12033
12034 enum TargetTaskResult {
12035 Location(Option<Location>),
12036 AlreadyNavigated,
12037 }
12038
12039 let target_task = match definition {
12040 HoverLink::Text(link) => {
12041 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12042 }
12043 HoverLink::InlayHint(lsp_location, server_id) => {
12044 let computation =
12045 self.compute_target_location(lsp_location, server_id, window, cx);
12046 cx.background_spawn(async move {
12047 let location = computation.await?;
12048 Ok(TargetTaskResult::Location(location))
12049 })
12050 }
12051 HoverLink::Url(url) => {
12052 cx.open_url(&url);
12053 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12054 }
12055 HoverLink::File(path) => {
12056 if let Some(workspace) = self.workspace() {
12057 cx.spawn_in(window, |_, mut cx| async move {
12058 workspace
12059 .update_in(&mut cx, |workspace, window, cx| {
12060 workspace.open_resolved_path(path, window, cx)
12061 })?
12062 .await
12063 .map(|_| TargetTaskResult::AlreadyNavigated)
12064 })
12065 } else {
12066 Task::ready(Ok(TargetTaskResult::Location(None)))
12067 }
12068 }
12069 };
12070 cx.spawn_in(window, |editor, mut cx| async move {
12071 let target = match target_task.await.context("target resolution task")? {
12072 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12073 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12074 TargetTaskResult::Location(Some(target)) => target,
12075 };
12076
12077 editor.update_in(&mut cx, |editor, window, cx| {
12078 let Some(workspace) = editor.workspace() else {
12079 return Navigated::No;
12080 };
12081 let pane = workspace.read(cx).active_pane().clone();
12082
12083 let range = target.range.to_point(target.buffer.read(cx));
12084 let range = editor.range_for_match(&range);
12085 let range = collapse_multiline_range(range);
12086
12087 if !split
12088 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12089 {
12090 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12091 } else {
12092 window.defer(cx, move |window, cx| {
12093 let target_editor: Entity<Self> =
12094 workspace.update(cx, |workspace, cx| {
12095 let pane = if split {
12096 workspace.adjacent_pane(window, cx)
12097 } else {
12098 workspace.active_pane().clone()
12099 };
12100
12101 workspace.open_project_item(
12102 pane,
12103 target.buffer.clone(),
12104 true,
12105 true,
12106 window,
12107 cx,
12108 )
12109 });
12110 target_editor.update(cx, |target_editor, cx| {
12111 // When selecting a definition in a different buffer, disable the nav history
12112 // to avoid creating a history entry at the previous cursor location.
12113 pane.update(cx, |pane, _| pane.disable_history());
12114 target_editor.go_to_singleton_buffer_range(range, window, cx);
12115 pane.update(cx, |pane, _| pane.enable_history());
12116 });
12117 });
12118 }
12119 Navigated::Yes
12120 })
12121 })
12122 } else if !definitions.is_empty() {
12123 cx.spawn_in(window, |editor, mut cx| async move {
12124 let (title, location_tasks, workspace) = editor
12125 .update_in(&mut cx, |editor, window, cx| {
12126 let tab_kind = match kind {
12127 Some(GotoDefinitionKind::Implementation) => "Implementations",
12128 _ => "Definitions",
12129 };
12130 let title = definitions
12131 .iter()
12132 .find_map(|definition| match definition {
12133 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12134 let buffer = origin.buffer.read(cx);
12135 format!(
12136 "{} for {}",
12137 tab_kind,
12138 buffer
12139 .text_for_range(origin.range.clone())
12140 .collect::<String>()
12141 )
12142 }),
12143 HoverLink::InlayHint(_, _) => None,
12144 HoverLink::Url(_) => None,
12145 HoverLink::File(_) => None,
12146 })
12147 .unwrap_or(tab_kind.to_string());
12148 let location_tasks = definitions
12149 .into_iter()
12150 .map(|definition| match definition {
12151 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12152 HoverLink::InlayHint(lsp_location, server_id) => editor
12153 .compute_target_location(lsp_location, server_id, window, cx),
12154 HoverLink::Url(_) => Task::ready(Ok(None)),
12155 HoverLink::File(_) => Task::ready(Ok(None)),
12156 })
12157 .collect::<Vec<_>>();
12158 (title, location_tasks, editor.workspace().clone())
12159 })
12160 .context("location tasks preparation")?;
12161
12162 let locations = future::join_all(location_tasks)
12163 .await
12164 .into_iter()
12165 .filter_map(|location| location.transpose())
12166 .collect::<Result<_>>()
12167 .context("location tasks")?;
12168
12169 let Some(workspace) = workspace else {
12170 return Ok(Navigated::No);
12171 };
12172 let opened = workspace
12173 .update_in(&mut cx, |workspace, window, cx| {
12174 Self::open_locations_in_multibuffer(
12175 workspace,
12176 locations,
12177 title,
12178 split,
12179 MultibufferSelectionMode::First,
12180 window,
12181 cx,
12182 )
12183 })
12184 .ok();
12185
12186 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12187 })
12188 } else {
12189 Task::ready(Ok(Navigated::No))
12190 }
12191 }
12192
12193 fn compute_target_location(
12194 &self,
12195 lsp_location: lsp::Location,
12196 server_id: LanguageServerId,
12197 window: &mut Window,
12198 cx: &mut Context<Self>,
12199 ) -> Task<anyhow::Result<Option<Location>>> {
12200 let Some(project) = self.project.clone() else {
12201 return Task::ready(Ok(None));
12202 };
12203
12204 cx.spawn_in(window, move |editor, mut cx| async move {
12205 let location_task = editor.update(&mut cx, |_, cx| {
12206 project.update(cx, |project, cx| {
12207 let language_server_name = project
12208 .language_server_statuses(cx)
12209 .find(|(id, _)| server_id == *id)
12210 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12211 language_server_name.map(|language_server_name| {
12212 project.open_local_buffer_via_lsp(
12213 lsp_location.uri.clone(),
12214 server_id,
12215 language_server_name,
12216 cx,
12217 )
12218 })
12219 })
12220 })?;
12221 let location = match location_task {
12222 Some(task) => Some({
12223 let target_buffer_handle = task.await.context("open local buffer")?;
12224 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12225 let target_start = target_buffer
12226 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12227 let target_end = target_buffer
12228 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12229 target_buffer.anchor_after(target_start)
12230 ..target_buffer.anchor_before(target_end)
12231 })?;
12232 Location {
12233 buffer: target_buffer_handle,
12234 range,
12235 }
12236 }),
12237 None => None,
12238 };
12239 Ok(location)
12240 })
12241 }
12242
12243 pub fn find_all_references(
12244 &mut self,
12245 _: &FindAllReferences,
12246 window: &mut Window,
12247 cx: &mut Context<Self>,
12248 ) -> Option<Task<Result<Navigated>>> {
12249 let selection = self.selections.newest::<usize>(cx);
12250 let multi_buffer = self.buffer.read(cx);
12251 let head = selection.head();
12252
12253 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12254 let head_anchor = multi_buffer_snapshot.anchor_at(
12255 head,
12256 if head < selection.tail() {
12257 Bias::Right
12258 } else {
12259 Bias::Left
12260 },
12261 );
12262
12263 match self
12264 .find_all_references_task_sources
12265 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12266 {
12267 Ok(_) => {
12268 log::info!(
12269 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12270 );
12271 return None;
12272 }
12273 Err(i) => {
12274 self.find_all_references_task_sources.insert(i, head_anchor);
12275 }
12276 }
12277
12278 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12279 let workspace = self.workspace()?;
12280 let project = workspace.read(cx).project().clone();
12281 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12282 Some(cx.spawn_in(window, |editor, mut cx| async move {
12283 let _cleanup = defer({
12284 let mut cx = cx.clone();
12285 move || {
12286 let _ = editor.update(&mut cx, |editor, _| {
12287 if let Ok(i) =
12288 editor
12289 .find_all_references_task_sources
12290 .binary_search_by(|anchor| {
12291 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12292 })
12293 {
12294 editor.find_all_references_task_sources.remove(i);
12295 }
12296 });
12297 }
12298 });
12299
12300 let locations = references.await?;
12301 if locations.is_empty() {
12302 return anyhow::Ok(Navigated::No);
12303 }
12304
12305 workspace.update_in(&mut cx, |workspace, window, cx| {
12306 let title = locations
12307 .first()
12308 .as_ref()
12309 .map(|location| {
12310 let buffer = location.buffer.read(cx);
12311 format!(
12312 "References to `{}`",
12313 buffer
12314 .text_for_range(location.range.clone())
12315 .collect::<String>()
12316 )
12317 })
12318 .unwrap();
12319 Self::open_locations_in_multibuffer(
12320 workspace,
12321 locations,
12322 title,
12323 false,
12324 MultibufferSelectionMode::First,
12325 window,
12326 cx,
12327 );
12328 Navigated::Yes
12329 })
12330 }))
12331 }
12332
12333 /// Opens a multibuffer with the given project locations in it
12334 pub fn open_locations_in_multibuffer(
12335 workspace: &mut Workspace,
12336 mut locations: Vec<Location>,
12337 title: String,
12338 split: bool,
12339 multibuffer_selection_mode: MultibufferSelectionMode,
12340 window: &mut Window,
12341 cx: &mut Context<Workspace>,
12342 ) {
12343 // If there are multiple definitions, open them in a multibuffer
12344 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12345 let mut locations = locations.into_iter().peekable();
12346 let mut ranges = Vec::new();
12347 let capability = workspace.project().read(cx).capability();
12348
12349 let excerpt_buffer = cx.new(|cx| {
12350 let mut multibuffer = MultiBuffer::new(capability);
12351 while let Some(location) = locations.next() {
12352 let buffer = location.buffer.read(cx);
12353 let mut ranges_for_buffer = Vec::new();
12354 let range = location.range.to_offset(buffer);
12355 ranges_for_buffer.push(range.clone());
12356
12357 while let Some(next_location) = locations.peek() {
12358 if next_location.buffer == location.buffer {
12359 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12360 locations.next();
12361 } else {
12362 break;
12363 }
12364 }
12365
12366 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12367 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12368 location.buffer.clone(),
12369 ranges_for_buffer,
12370 DEFAULT_MULTIBUFFER_CONTEXT,
12371 cx,
12372 ))
12373 }
12374
12375 multibuffer.with_title(title)
12376 });
12377
12378 let editor = cx.new(|cx| {
12379 Editor::for_multibuffer(
12380 excerpt_buffer,
12381 Some(workspace.project().clone()),
12382 true,
12383 window,
12384 cx,
12385 )
12386 });
12387 editor.update(cx, |editor, cx| {
12388 match multibuffer_selection_mode {
12389 MultibufferSelectionMode::First => {
12390 if let Some(first_range) = ranges.first() {
12391 editor.change_selections(None, window, cx, |selections| {
12392 selections.clear_disjoint();
12393 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12394 });
12395 }
12396 editor.highlight_background::<Self>(
12397 &ranges,
12398 |theme| theme.editor_highlighted_line_background,
12399 cx,
12400 );
12401 }
12402 MultibufferSelectionMode::All => {
12403 editor.change_selections(None, window, cx, |selections| {
12404 selections.clear_disjoint();
12405 selections.select_anchor_ranges(ranges);
12406 });
12407 }
12408 }
12409 editor.register_buffers_with_language_servers(cx);
12410 });
12411
12412 let item = Box::new(editor);
12413 let item_id = item.item_id();
12414
12415 if split {
12416 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12417 } else {
12418 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12419 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12420 pane.close_current_preview_item(window, cx)
12421 } else {
12422 None
12423 }
12424 });
12425 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12426 }
12427 workspace.active_pane().update(cx, |pane, cx| {
12428 pane.set_preview_item_id(Some(item_id), cx);
12429 });
12430 }
12431
12432 pub fn rename(
12433 &mut self,
12434 _: &Rename,
12435 window: &mut Window,
12436 cx: &mut Context<Self>,
12437 ) -> Option<Task<Result<()>>> {
12438 use language::ToOffset as _;
12439
12440 let provider = self.semantics_provider.clone()?;
12441 let selection = self.selections.newest_anchor().clone();
12442 let (cursor_buffer, cursor_buffer_position) = self
12443 .buffer
12444 .read(cx)
12445 .text_anchor_for_position(selection.head(), cx)?;
12446 let (tail_buffer, cursor_buffer_position_end) = self
12447 .buffer
12448 .read(cx)
12449 .text_anchor_for_position(selection.tail(), cx)?;
12450 if tail_buffer != cursor_buffer {
12451 return None;
12452 }
12453
12454 let snapshot = cursor_buffer.read(cx).snapshot();
12455 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12456 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12457 let prepare_rename = provider
12458 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12459 .unwrap_or_else(|| Task::ready(Ok(None)));
12460 drop(snapshot);
12461
12462 Some(cx.spawn_in(window, |this, mut cx| async move {
12463 let rename_range = if let Some(range) = prepare_rename.await? {
12464 Some(range)
12465 } else {
12466 this.update(&mut cx, |this, cx| {
12467 let buffer = this.buffer.read(cx).snapshot(cx);
12468 let mut buffer_highlights = this
12469 .document_highlights_for_position(selection.head(), &buffer)
12470 .filter(|highlight| {
12471 highlight.start.excerpt_id == selection.head().excerpt_id
12472 && highlight.end.excerpt_id == selection.head().excerpt_id
12473 });
12474 buffer_highlights
12475 .next()
12476 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12477 })?
12478 };
12479 if let Some(rename_range) = rename_range {
12480 this.update_in(&mut cx, |this, window, cx| {
12481 let snapshot = cursor_buffer.read(cx).snapshot();
12482 let rename_buffer_range = rename_range.to_offset(&snapshot);
12483 let cursor_offset_in_rename_range =
12484 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12485 let cursor_offset_in_rename_range_end =
12486 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12487
12488 this.take_rename(false, window, cx);
12489 let buffer = this.buffer.read(cx).read(cx);
12490 let cursor_offset = selection.head().to_offset(&buffer);
12491 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12492 let rename_end = rename_start + rename_buffer_range.len();
12493 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12494 let mut old_highlight_id = None;
12495 let old_name: Arc<str> = buffer
12496 .chunks(rename_start..rename_end, true)
12497 .map(|chunk| {
12498 if old_highlight_id.is_none() {
12499 old_highlight_id = chunk.syntax_highlight_id;
12500 }
12501 chunk.text
12502 })
12503 .collect::<String>()
12504 .into();
12505
12506 drop(buffer);
12507
12508 // Position the selection in the rename editor so that it matches the current selection.
12509 this.show_local_selections = false;
12510 let rename_editor = cx.new(|cx| {
12511 let mut editor = Editor::single_line(window, cx);
12512 editor.buffer.update(cx, |buffer, cx| {
12513 buffer.edit([(0..0, old_name.clone())], None, cx)
12514 });
12515 let rename_selection_range = match cursor_offset_in_rename_range
12516 .cmp(&cursor_offset_in_rename_range_end)
12517 {
12518 Ordering::Equal => {
12519 editor.select_all(&SelectAll, window, cx);
12520 return editor;
12521 }
12522 Ordering::Less => {
12523 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12524 }
12525 Ordering::Greater => {
12526 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12527 }
12528 };
12529 if rename_selection_range.end > old_name.len() {
12530 editor.select_all(&SelectAll, window, cx);
12531 } else {
12532 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12533 s.select_ranges([rename_selection_range]);
12534 });
12535 }
12536 editor
12537 });
12538 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12539 if e == &EditorEvent::Focused {
12540 cx.emit(EditorEvent::FocusedIn)
12541 }
12542 })
12543 .detach();
12544
12545 let write_highlights =
12546 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12547 let read_highlights =
12548 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12549 let ranges = write_highlights
12550 .iter()
12551 .flat_map(|(_, ranges)| ranges.iter())
12552 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12553 .cloned()
12554 .collect();
12555
12556 this.highlight_text::<Rename>(
12557 ranges,
12558 HighlightStyle {
12559 fade_out: Some(0.6),
12560 ..Default::default()
12561 },
12562 cx,
12563 );
12564 let rename_focus_handle = rename_editor.focus_handle(cx);
12565 window.focus(&rename_focus_handle);
12566 let block_id = this.insert_blocks(
12567 [BlockProperties {
12568 style: BlockStyle::Flex,
12569 placement: BlockPlacement::Below(range.start),
12570 height: 1,
12571 render: Arc::new({
12572 let rename_editor = rename_editor.clone();
12573 move |cx: &mut BlockContext| {
12574 let mut text_style = cx.editor_style.text.clone();
12575 if let Some(highlight_style) = old_highlight_id
12576 .and_then(|h| h.style(&cx.editor_style.syntax))
12577 {
12578 text_style = text_style.highlight(highlight_style);
12579 }
12580 div()
12581 .block_mouse_down()
12582 .pl(cx.anchor_x)
12583 .child(EditorElement::new(
12584 &rename_editor,
12585 EditorStyle {
12586 background: cx.theme().system().transparent,
12587 local_player: cx.editor_style.local_player,
12588 text: text_style,
12589 scrollbar_width: cx.editor_style.scrollbar_width,
12590 syntax: cx.editor_style.syntax.clone(),
12591 status: cx.editor_style.status.clone(),
12592 inlay_hints_style: HighlightStyle {
12593 font_weight: Some(FontWeight::BOLD),
12594 ..make_inlay_hints_style(cx.app)
12595 },
12596 inline_completion_styles: make_suggestion_styles(
12597 cx.app,
12598 ),
12599 ..EditorStyle::default()
12600 },
12601 ))
12602 .into_any_element()
12603 }
12604 }),
12605 priority: 0,
12606 }],
12607 Some(Autoscroll::fit()),
12608 cx,
12609 )[0];
12610 this.pending_rename = Some(RenameState {
12611 range,
12612 old_name,
12613 editor: rename_editor,
12614 block_id,
12615 });
12616 })?;
12617 }
12618
12619 Ok(())
12620 }))
12621 }
12622
12623 pub fn confirm_rename(
12624 &mut self,
12625 _: &ConfirmRename,
12626 window: &mut Window,
12627 cx: &mut Context<Self>,
12628 ) -> Option<Task<Result<()>>> {
12629 let rename = self.take_rename(false, window, cx)?;
12630 let workspace = self.workspace()?.downgrade();
12631 let (buffer, start) = self
12632 .buffer
12633 .read(cx)
12634 .text_anchor_for_position(rename.range.start, cx)?;
12635 let (end_buffer, _) = self
12636 .buffer
12637 .read(cx)
12638 .text_anchor_for_position(rename.range.end, cx)?;
12639 if buffer != end_buffer {
12640 return None;
12641 }
12642
12643 let old_name = rename.old_name;
12644 let new_name = rename.editor.read(cx).text(cx);
12645
12646 let rename = self.semantics_provider.as_ref()?.perform_rename(
12647 &buffer,
12648 start,
12649 new_name.clone(),
12650 cx,
12651 )?;
12652
12653 Some(cx.spawn_in(window, |editor, mut cx| async move {
12654 let project_transaction = rename.await?;
12655 Self::open_project_transaction(
12656 &editor,
12657 workspace,
12658 project_transaction,
12659 format!("Rename: {} → {}", old_name, new_name),
12660 cx.clone(),
12661 )
12662 .await?;
12663
12664 editor.update(&mut cx, |editor, cx| {
12665 editor.refresh_document_highlights(cx);
12666 })?;
12667 Ok(())
12668 }))
12669 }
12670
12671 fn take_rename(
12672 &mut self,
12673 moving_cursor: bool,
12674 window: &mut Window,
12675 cx: &mut Context<Self>,
12676 ) -> Option<RenameState> {
12677 let rename = self.pending_rename.take()?;
12678 if rename.editor.focus_handle(cx).is_focused(window) {
12679 window.focus(&self.focus_handle);
12680 }
12681
12682 self.remove_blocks(
12683 [rename.block_id].into_iter().collect(),
12684 Some(Autoscroll::fit()),
12685 cx,
12686 );
12687 self.clear_highlights::<Rename>(cx);
12688 self.show_local_selections = true;
12689
12690 if moving_cursor {
12691 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12692 editor.selections.newest::<usize>(cx).head()
12693 });
12694
12695 // Update the selection to match the position of the selection inside
12696 // the rename editor.
12697 let snapshot = self.buffer.read(cx).read(cx);
12698 let rename_range = rename.range.to_offset(&snapshot);
12699 let cursor_in_editor = snapshot
12700 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12701 .min(rename_range.end);
12702 drop(snapshot);
12703
12704 self.change_selections(None, window, cx, |s| {
12705 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12706 });
12707 } else {
12708 self.refresh_document_highlights(cx);
12709 }
12710
12711 Some(rename)
12712 }
12713
12714 pub fn pending_rename(&self) -> Option<&RenameState> {
12715 self.pending_rename.as_ref()
12716 }
12717
12718 fn format(
12719 &mut self,
12720 _: &Format,
12721 window: &mut Window,
12722 cx: &mut Context<Self>,
12723 ) -> Option<Task<Result<()>>> {
12724 let project = match &self.project {
12725 Some(project) => project.clone(),
12726 None => return None,
12727 };
12728
12729 Some(self.perform_format(
12730 project,
12731 FormatTrigger::Manual,
12732 FormatTarget::Buffers,
12733 window,
12734 cx,
12735 ))
12736 }
12737
12738 fn format_selections(
12739 &mut self,
12740 _: &FormatSelections,
12741 window: &mut Window,
12742 cx: &mut Context<Self>,
12743 ) -> Option<Task<Result<()>>> {
12744 let project = match &self.project {
12745 Some(project) => project.clone(),
12746 None => return None,
12747 };
12748
12749 let ranges = self
12750 .selections
12751 .all_adjusted(cx)
12752 .into_iter()
12753 .map(|selection| selection.range())
12754 .collect_vec();
12755
12756 Some(self.perform_format(
12757 project,
12758 FormatTrigger::Manual,
12759 FormatTarget::Ranges(ranges),
12760 window,
12761 cx,
12762 ))
12763 }
12764
12765 fn perform_format(
12766 &mut self,
12767 project: Entity<Project>,
12768 trigger: FormatTrigger,
12769 target: FormatTarget,
12770 window: &mut Window,
12771 cx: &mut Context<Self>,
12772 ) -> Task<Result<()>> {
12773 let buffer = self.buffer.clone();
12774 let (buffers, target) = match target {
12775 FormatTarget::Buffers => {
12776 let mut buffers = buffer.read(cx).all_buffers();
12777 if trigger == FormatTrigger::Save {
12778 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12779 }
12780 (buffers, LspFormatTarget::Buffers)
12781 }
12782 FormatTarget::Ranges(selection_ranges) => {
12783 let multi_buffer = buffer.read(cx);
12784 let snapshot = multi_buffer.read(cx);
12785 let mut buffers = HashSet::default();
12786 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12787 BTreeMap::new();
12788 for selection_range in selection_ranges {
12789 for (buffer, buffer_range, _) in
12790 snapshot.range_to_buffer_ranges(selection_range)
12791 {
12792 let buffer_id = buffer.remote_id();
12793 let start = buffer.anchor_before(buffer_range.start);
12794 let end = buffer.anchor_after(buffer_range.end);
12795 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12796 buffer_id_to_ranges
12797 .entry(buffer_id)
12798 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12799 .or_insert_with(|| vec![start..end]);
12800 }
12801 }
12802 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12803 }
12804 };
12805
12806 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12807 let format = project.update(cx, |project, cx| {
12808 project.format(buffers, target, true, trigger, cx)
12809 });
12810
12811 cx.spawn_in(window, |_, mut cx| async move {
12812 let transaction = futures::select_biased! {
12813 () = timeout => {
12814 log::warn!("timed out waiting for formatting");
12815 None
12816 }
12817 transaction = format.log_err().fuse() => transaction,
12818 };
12819
12820 buffer
12821 .update(&mut cx, |buffer, cx| {
12822 if let Some(transaction) = transaction {
12823 if !buffer.is_singleton() {
12824 buffer.push_transaction(&transaction.0, cx);
12825 }
12826 }
12827 cx.notify();
12828 })
12829 .ok();
12830
12831 Ok(())
12832 })
12833 }
12834
12835 fn organize_imports(
12836 &mut self,
12837 _: &OrganizeImports,
12838 window: &mut Window,
12839 cx: &mut Context<Self>,
12840 ) -> Option<Task<Result<()>>> {
12841 let project = match &self.project {
12842 Some(project) => project.clone(),
12843 None => return None,
12844 };
12845 Some(self.perform_code_action_kind(
12846 project,
12847 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
12848 window,
12849 cx,
12850 ))
12851 }
12852
12853 fn perform_code_action_kind(
12854 &mut self,
12855 project: Entity<Project>,
12856 kind: CodeActionKind,
12857 window: &mut Window,
12858 cx: &mut Context<Self>,
12859 ) -> Task<Result<()>> {
12860 let buffer = self.buffer.clone();
12861 let buffers = buffer.read(cx).all_buffers();
12862 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
12863 let apply_action = project.update(cx, |project, cx| {
12864 project.apply_code_action_kind(buffers, kind, true, cx)
12865 });
12866 cx.spawn_in(window, |_, mut cx| async move {
12867 let transaction = futures::select_biased! {
12868 () = timeout => {
12869 log::warn!("timed out waiting for executing code action");
12870 None
12871 }
12872 transaction = apply_action.log_err().fuse() => transaction,
12873 };
12874 buffer
12875 .update(&mut cx, |buffer, cx| {
12876 // check if we need this
12877 if let Some(transaction) = transaction {
12878 if !buffer.is_singleton() {
12879 buffer.push_transaction(&transaction.0, cx);
12880 }
12881 }
12882 cx.notify();
12883 })
12884 .ok();
12885 Ok(())
12886 })
12887 }
12888
12889 fn restart_language_server(
12890 &mut self,
12891 _: &RestartLanguageServer,
12892 _: &mut Window,
12893 cx: &mut Context<Self>,
12894 ) {
12895 if let Some(project) = self.project.clone() {
12896 self.buffer.update(cx, |multi_buffer, cx| {
12897 project.update(cx, |project, cx| {
12898 project.restart_language_servers_for_buffers(
12899 multi_buffer.all_buffers().into_iter().collect(),
12900 cx,
12901 );
12902 });
12903 })
12904 }
12905 }
12906
12907 fn cancel_language_server_work(
12908 workspace: &mut Workspace,
12909 _: &actions::CancelLanguageServerWork,
12910 _: &mut Window,
12911 cx: &mut Context<Workspace>,
12912 ) {
12913 let project = workspace.project();
12914 let buffers = workspace
12915 .active_item(cx)
12916 .and_then(|item| item.act_as::<Editor>(cx))
12917 .map_or(HashSet::default(), |editor| {
12918 editor.read(cx).buffer.read(cx).all_buffers()
12919 });
12920 project.update(cx, |project, cx| {
12921 project.cancel_language_server_work_for_buffers(buffers, cx);
12922 });
12923 }
12924
12925 fn show_character_palette(
12926 &mut self,
12927 _: &ShowCharacterPalette,
12928 window: &mut Window,
12929 _: &mut Context<Self>,
12930 ) {
12931 window.show_character_palette();
12932 }
12933
12934 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12935 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12936 let buffer = self.buffer.read(cx).snapshot(cx);
12937 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12938 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12939 let is_valid = buffer
12940 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12941 .any(|entry| {
12942 entry.diagnostic.is_primary
12943 && !entry.range.is_empty()
12944 && entry.range.start == primary_range_start
12945 && entry.diagnostic.message == active_diagnostics.primary_message
12946 });
12947
12948 if is_valid != active_diagnostics.is_valid {
12949 active_diagnostics.is_valid = is_valid;
12950 if is_valid {
12951 let mut new_styles = HashMap::default();
12952 for (block_id, diagnostic) in &active_diagnostics.blocks {
12953 new_styles.insert(
12954 *block_id,
12955 diagnostic_block_renderer(diagnostic.clone(), None, true),
12956 );
12957 }
12958 self.display_map.update(cx, |display_map, _cx| {
12959 display_map.replace_blocks(new_styles);
12960 });
12961 } else {
12962 self.dismiss_diagnostics(cx);
12963 }
12964 }
12965 }
12966 }
12967
12968 fn activate_diagnostics(
12969 &mut self,
12970 buffer_id: BufferId,
12971 group_id: usize,
12972 window: &mut Window,
12973 cx: &mut Context<Self>,
12974 ) {
12975 self.dismiss_diagnostics(cx);
12976 let snapshot = self.snapshot(window, cx);
12977 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
12978 let buffer = self.buffer.read(cx).snapshot(cx);
12979
12980 let mut primary_range = None;
12981 let mut primary_message = None;
12982 let diagnostic_group = buffer
12983 .diagnostic_group(buffer_id, group_id)
12984 .filter_map(|entry| {
12985 let start = entry.range.start;
12986 let end = entry.range.end;
12987 if snapshot.is_line_folded(MultiBufferRow(start.row))
12988 && (start.row == end.row
12989 || snapshot.is_line_folded(MultiBufferRow(end.row)))
12990 {
12991 return None;
12992 }
12993 if entry.diagnostic.is_primary {
12994 primary_range = Some(entry.range.clone());
12995 primary_message = Some(entry.diagnostic.message.clone());
12996 }
12997 Some(entry)
12998 })
12999 .collect::<Vec<_>>();
13000 let primary_range = primary_range?;
13001 let primary_message = primary_message?;
13002
13003 let blocks = display_map
13004 .insert_blocks(
13005 diagnostic_group.iter().map(|entry| {
13006 let diagnostic = entry.diagnostic.clone();
13007 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13008 BlockProperties {
13009 style: BlockStyle::Fixed,
13010 placement: BlockPlacement::Below(
13011 buffer.anchor_after(entry.range.start),
13012 ),
13013 height: message_height,
13014 render: diagnostic_block_renderer(diagnostic, None, true),
13015 priority: 0,
13016 }
13017 }),
13018 cx,
13019 )
13020 .into_iter()
13021 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13022 .collect();
13023
13024 Some(ActiveDiagnosticGroup {
13025 primary_range: buffer.anchor_before(primary_range.start)
13026 ..buffer.anchor_after(primary_range.end),
13027 primary_message,
13028 group_id,
13029 blocks,
13030 is_valid: true,
13031 })
13032 });
13033 }
13034
13035 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13036 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13037 self.display_map.update(cx, |display_map, cx| {
13038 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13039 });
13040 cx.notify();
13041 }
13042 }
13043
13044 /// Disable inline diagnostics rendering for this editor.
13045 pub fn disable_inline_diagnostics(&mut self) {
13046 self.inline_diagnostics_enabled = false;
13047 self.inline_diagnostics_update = Task::ready(());
13048 self.inline_diagnostics.clear();
13049 }
13050
13051 pub fn inline_diagnostics_enabled(&self) -> bool {
13052 self.inline_diagnostics_enabled
13053 }
13054
13055 pub fn show_inline_diagnostics(&self) -> bool {
13056 self.show_inline_diagnostics
13057 }
13058
13059 pub fn toggle_inline_diagnostics(
13060 &mut self,
13061 _: &ToggleInlineDiagnostics,
13062 window: &mut Window,
13063 cx: &mut Context<'_, Editor>,
13064 ) {
13065 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13066 self.refresh_inline_diagnostics(false, window, cx);
13067 }
13068
13069 fn refresh_inline_diagnostics(
13070 &mut self,
13071 debounce: bool,
13072 window: &mut Window,
13073 cx: &mut Context<Self>,
13074 ) {
13075 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13076 self.inline_diagnostics_update = Task::ready(());
13077 self.inline_diagnostics.clear();
13078 return;
13079 }
13080
13081 let debounce_ms = ProjectSettings::get_global(cx)
13082 .diagnostics
13083 .inline
13084 .update_debounce_ms;
13085 let debounce = if debounce && debounce_ms > 0 {
13086 Some(Duration::from_millis(debounce_ms))
13087 } else {
13088 None
13089 };
13090 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
13091 if let Some(debounce) = debounce {
13092 cx.background_executor().timer(debounce).await;
13093 }
13094 let Some(snapshot) = editor
13095 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13096 .ok()
13097 else {
13098 return;
13099 };
13100
13101 let new_inline_diagnostics = cx
13102 .background_spawn(async move {
13103 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13104 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13105 let message = diagnostic_entry
13106 .diagnostic
13107 .message
13108 .split_once('\n')
13109 .map(|(line, _)| line)
13110 .map(SharedString::new)
13111 .unwrap_or_else(|| {
13112 SharedString::from(diagnostic_entry.diagnostic.message)
13113 });
13114 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13115 let (Ok(i) | Err(i)) = inline_diagnostics
13116 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13117 inline_diagnostics.insert(
13118 i,
13119 (
13120 start_anchor,
13121 InlineDiagnostic {
13122 message,
13123 group_id: diagnostic_entry.diagnostic.group_id,
13124 start: diagnostic_entry.range.start.to_point(&snapshot),
13125 is_primary: diagnostic_entry.diagnostic.is_primary,
13126 severity: diagnostic_entry.diagnostic.severity,
13127 },
13128 ),
13129 );
13130 }
13131 inline_diagnostics
13132 })
13133 .await;
13134
13135 editor
13136 .update(&mut cx, |editor, cx| {
13137 editor.inline_diagnostics = new_inline_diagnostics;
13138 cx.notify();
13139 })
13140 .ok();
13141 });
13142 }
13143
13144 pub fn set_selections_from_remote(
13145 &mut self,
13146 selections: Vec<Selection<Anchor>>,
13147 pending_selection: Option<Selection<Anchor>>,
13148 window: &mut Window,
13149 cx: &mut Context<Self>,
13150 ) {
13151 let old_cursor_position = self.selections.newest_anchor().head();
13152 self.selections.change_with(cx, |s| {
13153 s.select_anchors(selections);
13154 if let Some(pending_selection) = pending_selection {
13155 s.set_pending(pending_selection, SelectMode::Character);
13156 } else {
13157 s.clear_pending();
13158 }
13159 });
13160 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13161 }
13162
13163 fn push_to_selection_history(&mut self) {
13164 self.selection_history.push(SelectionHistoryEntry {
13165 selections: self.selections.disjoint_anchors(),
13166 select_next_state: self.select_next_state.clone(),
13167 select_prev_state: self.select_prev_state.clone(),
13168 add_selections_state: self.add_selections_state.clone(),
13169 });
13170 }
13171
13172 pub fn transact(
13173 &mut self,
13174 window: &mut Window,
13175 cx: &mut Context<Self>,
13176 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13177 ) -> Option<TransactionId> {
13178 self.start_transaction_at(Instant::now(), window, cx);
13179 update(self, window, cx);
13180 self.end_transaction_at(Instant::now(), cx)
13181 }
13182
13183 pub fn start_transaction_at(
13184 &mut self,
13185 now: Instant,
13186 window: &mut Window,
13187 cx: &mut Context<Self>,
13188 ) {
13189 self.end_selection(window, cx);
13190 if let Some(tx_id) = self
13191 .buffer
13192 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13193 {
13194 self.selection_history
13195 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13196 cx.emit(EditorEvent::TransactionBegun {
13197 transaction_id: tx_id,
13198 })
13199 }
13200 }
13201
13202 pub fn end_transaction_at(
13203 &mut self,
13204 now: Instant,
13205 cx: &mut Context<Self>,
13206 ) -> Option<TransactionId> {
13207 if let Some(transaction_id) = self
13208 .buffer
13209 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13210 {
13211 if let Some((_, end_selections)) =
13212 self.selection_history.transaction_mut(transaction_id)
13213 {
13214 *end_selections = Some(self.selections.disjoint_anchors());
13215 } else {
13216 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13217 }
13218
13219 cx.emit(EditorEvent::Edited { transaction_id });
13220 Some(transaction_id)
13221 } else {
13222 None
13223 }
13224 }
13225
13226 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13227 if self.selection_mark_mode {
13228 self.change_selections(None, window, cx, |s| {
13229 s.move_with(|_, sel| {
13230 sel.collapse_to(sel.head(), SelectionGoal::None);
13231 });
13232 })
13233 }
13234 self.selection_mark_mode = true;
13235 cx.notify();
13236 }
13237
13238 pub fn swap_selection_ends(
13239 &mut self,
13240 _: &actions::SwapSelectionEnds,
13241 window: &mut Window,
13242 cx: &mut Context<Self>,
13243 ) {
13244 self.change_selections(None, window, cx, |s| {
13245 s.move_with(|_, sel| {
13246 if sel.start != sel.end {
13247 sel.reversed = !sel.reversed
13248 }
13249 });
13250 });
13251 self.request_autoscroll(Autoscroll::newest(), cx);
13252 cx.notify();
13253 }
13254
13255 pub fn toggle_fold(
13256 &mut self,
13257 _: &actions::ToggleFold,
13258 window: &mut Window,
13259 cx: &mut Context<Self>,
13260 ) {
13261 if self.is_singleton(cx) {
13262 let selection = self.selections.newest::<Point>(cx);
13263
13264 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13265 let range = if selection.is_empty() {
13266 let point = selection.head().to_display_point(&display_map);
13267 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13268 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13269 .to_point(&display_map);
13270 start..end
13271 } else {
13272 selection.range()
13273 };
13274 if display_map.folds_in_range(range).next().is_some() {
13275 self.unfold_lines(&Default::default(), window, cx)
13276 } else {
13277 self.fold(&Default::default(), window, cx)
13278 }
13279 } else {
13280 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13281 let buffer_ids: HashSet<_> = self
13282 .selections
13283 .disjoint_anchor_ranges()
13284 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13285 .collect();
13286
13287 let should_unfold = buffer_ids
13288 .iter()
13289 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13290
13291 for buffer_id in buffer_ids {
13292 if should_unfold {
13293 self.unfold_buffer(buffer_id, cx);
13294 } else {
13295 self.fold_buffer(buffer_id, cx);
13296 }
13297 }
13298 }
13299 }
13300
13301 pub fn toggle_fold_recursive(
13302 &mut self,
13303 _: &actions::ToggleFoldRecursive,
13304 window: &mut Window,
13305 cx: &mut Context<Self>,
13306 ) {
13307 let selection = self.selections.newest::<Point>(cx);
13308
13309 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13310 let range = if selection.is_empty() {
13311 let point = selection.head().to_display_point(&display_map);
13312 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13313 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13314 .to_point(&display_map);
13315 start..end
13316 } else {
13317 selection.range()
13318 };
13319 if display_map.folds_in_range(range).next().is_some() {
13320 self.unfold_recursive(&Default::default(), window, cx)
13321 } else {
13322 self.fold_recursive(&Default::default(), window, cx)
13323 }
13324 }
13325
13326 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13327 if self.is_singleton(cx) {
13328 let mut to_fold = Vec::new();
13329 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13330 let selections = self.selections.all_adjusted(cx);
13331
13332 for selection in selections {
13333 let range = selection.range().sorted();
13334 let buffer_start_row = range.start.row;
13335
13336 if range.start.row != range.end.row {
13337 let mut found = false;
13338 let mut row = range.start.row;
13339 while row <= range.end.row {
13340 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13341 {
13342 found = true;
13343 row = crease.range().end.row + 1;
13344 to_fold.push(crease);
13345 } else {
13346 row += 1
13347 }
13348 }
13349 if found {
13350 continue;
13351 }
13352 }
13353
13354 for row in (0..=range.start.row).rev() {
13355 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13356 if crease.range().end.row >= buffer_start_row {
13357 to_fold.push(crease);
13358 if row <= range.start.row {
13359 break;
13360 }
13361 }
13362 }
13363 }
13364 }
13365
13366 self.fold_creases(to_fold, true, window, cx);
13367 } else {
13368 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13369 let buffer_ids = self
13370 .selections
13371 .disjoint_anchor_ranges()
13372 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13373 .collect::<HashSet<_>>();
13374 for buffer_id in buffer_ids {
13375 self.fold_buffer(buffer_id, cx);
13376 }
13377 }
13378 }
13379
13380 fn fold_at_level(
13381 &mut self,
13382 fold_at: &FoldAtLevel,
13383 window: &mut Window,
13384 cx: &mut Context<Self>,
13385 ) {
13386 if !self.buffer.read(cx).is_singleton() {
13387 return;
13388 }
13389
13390 let fold_at_level = fold_at.0;
13391 let snapshot = self.buffer.read(cx).snapshot(cx);
13392 let mut to_fold = Vec::new();
13393 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13394
13395 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13396 while start_row < end_row {
13397 match self
13398 .snapshot(window, cx)
13399 .crease_for_buffer_row(MultiBufferRow(start_row))
13400 {
13401 Some(crease) => {
13402 let nested_start_row = crease.range().start.row + 1;
13403 let nested_end_row = crease.range().end.row;
13404
13405 if current_level < fold_at_level {
13406 stack.push((nested_start_row, nested_end_row, current_level + 1));
13407 } else if current_level == fold_at_level {
13408 to_fold.push(crease);
13409 }
13410
13411 start_row = nested_end_row + 1;
13412 }
13413 None => start_row += 1,
13414 }
13415 }
13416 }
13417
13418 self.fold_creases(to_fold, true, window, cx);
13419 }
13420
13421 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13422 if self.buffer.read(cx).is_singleton() {
13423 let mut fold_ranges = Vec::new();
13424 let snapshot = self.buffer.read(cx).snapshot(cx);
13425
13426 for row in 0..snapshot.max_row().0 {
13427 if let Some(foldable_range) = self
13428 .snapshot(window, cx)
13429 .crease_for_buffer_row(MultiBufferRow(row))
13430 {
13431 fold_ranges.push(foldable_range);
13432 }
13433 }
13434
13435 self.fold_creases(fold_ranges, true, window, cx);
13436 } else {
13437 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13438 editor
13439 .update_in(&mut cx, |editor, _, cx| {
13440 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13441 editor.fold_buffer(buffer_id, cx);
13442 }
13443 })
13444 .ok();
13445 });
13446 }
13447 }
13448
13449 pub fn fold_function_bodies(
13450 &mut self,
13451 _: &actions::FoldFunctionBodies,
13452 window: &mut Window,
13453 cx: &mut Context<Self>,
13454 ) {
13455 let snapshot = self.buffer.read(cx).snapshot(cx);
13456
13457 let ranges = snapshot
13458 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13459 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13460 .collect::<Vec<_>>();
13461
13462 let creases = ranges
13463 .into_iter()
13464 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13465 .collect();
13466
13467 self.fold_creases(creases, true, window, cx);
13468 }
13469
13470 pub fn fold_recursive(
13471 &mut self,
13472 _: &actions::FoldRecursive,
13473 window: &mut Window,
13474 cx: &mut Context<Self>,
13475 ) {
13476 let mut to_fold = Vec::new();
13477 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13478 let selections = self.selections.all_adjusted(cx);
13479
13480 for selection in selections {
13481 let range = selection.range().sorted();
13482 let buffer_start_row = range.start.row;
13483
13484 if range.start.row != range.end.row {
13485 let mut found = false;
13486 for row in range.start.row..=range.end.row {
13487 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13488 found = true;
13489 to_fold.push(crease);
13490 }
13491 }
13492 if found {
13493 continue;
13494 }
13495 }
13496
13497 for row in (0..=range.start.row).rev() {
13498 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13499 if crease.range().end.row >= buffer_start_row {
13500 to_fold.push(crease);
13501 } else {
13502 break;
13503 }
13504 }
13505 }
13506 }
13507
13508 self.fold_creases(to_fold, true, window, cx);
13509 }
13510
13511 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13512 let buffer_row = fold_at.buffer_row;
13513 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13514
13515 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13516 let autoscroll = self
13517 .selections
13518 .all::<Point>(cx)
13519 .iter()
13520 .any(|selection| crease.range().overlaps(&selection.range()));
13521
13522 self.fold_creases(vec![crease], autoscroll, window, cx);
13523 }
13524 }
13525
13526 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13527 if self.is_singleton(cx) {
13528 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13529 let buffer = &display_map.buffer_snapshot;
13530 let selections = self.selections.all::<Point>(cx);
13531 let ranges = selections
13532 .iter()
13533 .map(|s| {
13534 let range = s.display_range(&display_map).sorted();
13535 let mut start = range.start.to_point(&display_map);
13536 let mut end = range.end.to_point(&display_map);
13537 start.column = 0;
13538 end.column = buffer.line_len(MultiBufferRow(end.row));
13539 start..end
13540 })
13541 .collect::<Vec<_>>();
13542
13543 self.unfold_ranges(&ranges, true, true, cx);
13544 } else {
13545 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13546 let buffer_ids = self
13547 .selections
13548 .disjoint_anchor_ranges()
13549 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13550 .collect::<HashSet<_>>();
13551 for buffer_id in buffer_ids {
13552 self.unfold_buffer(buffer_id, cx);
13553 }
13554 }
13555 }
13556
13557 pub fn unfold_recursive(
13558 &mut self,
13559 _: &UnfoldRecursive,
13560 _window: &mut Window,
13561 cx: &mut Context<Self>,
13562 ) {
13563 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13564 let selections = self.selections.all::<Point>(cx);
13565 let ranges = selections
13566 .iter()
13567 .map(|s| {
13568 let mut range = s.display_range(&display_map).sorted();
13569 *range.start.column_mut() = 0;
13570 *range.end.column_mut() = display_map.line_len(range.end.row());
13571 let start = range.start.to_point(&display_map);
13572 let end = range.end.to_point(&display_map);
13573 start..end
13574 })
13575 .collect::<Vec<_>>();
13576
13577 self.unfold_ranges(&ranges, true, true, cx);
13578 }
13579
13580 pub fn unfold_at(
13581 &mut self,
13582 unfold_at: &UnfoldAt,
13583 _window: &mut Window,
13584 cx: &mut Context<Self>,
13585 ) {
13586 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13587
13588 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13589 ..Point::new(
13590 unfold_at.buffer_row.0,
13591 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13592 );
13593
13594 let autoscroll = self
13595 .selections
13596 .all::<Point>(cx)
13597 .iter()
13598 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13599
13600 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13601 }
13602
13603 pub fn unfold_all(
13604 &mut self,
13605 _: &actions::UnfoldAll,
13606 _window: &mut Window,
13607 cx: &mut Context<Self>,
13608 ) {
13609 if self.buffer.read(cx).is_singleton() {
13610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13611 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13612 } else {
13613 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13614 editor
13615 .update(&mut cx, |editor, cx| {
13616 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13617 editor.unfold_buffer(buffer_id, cx);
13618 }
13619 })
13620 .ok();
13621 });
13622 }
13623 }
13624
13625 pub fn fold_selected_ranges(
13626 &mut self,
13627 _: &FoldSelectedRanges,
13628 window: &mut Window,
13629 cx: &mut Context<Self>,
13630 ) {
13631 let selections = self.selections.all::<Point>(cx);
13632 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13633 let line_mode = self.selections.line_mode;
13634 let ranges = selections
13635 .into_iter()
13636 .map(|s| {
13637 if line_mode {
13638 let start = Point::new(s.start.row, 0);
13639 let end = Point::new(
13640 s.end.row,
13641 display_map
13642 .buffer_snapshot
13643 .line_len(MultiBufferRow(s.end.row)),
13644 );
13645 Crease::simple(start..end, display_map.fold_placeholder.clone())
13646 } else {
13647 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13648 }
13649 })
13650 .collect::<Vec<_>>();
13651 self.fold_creases(ranges, true, window, cx);
13652 }
13653
13654 pub fn fold_ranges<T: ToOffset + Clone>(
13655 &mut self,
13656 ranges: Vec<Range<T>>,
13657 auto_scroll: bool,
13658 window: &mut Window,
13659 cx: &mut Context<Self>,
13660 ) {
13661 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13662 let ranges = ranges
13663 .into_iter()
13664 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13665 .collect::<Vec<_>>();
13666 self.fold_creases(ranges, auto_scroll, window, cx);
13667 }
13668
13669 pub fn fold_creases<T: ToOffset + Clone>(
13670 &mut self,
13671 creases: Vec<Crease<T>>,
13672 auto_scroll: bool,
13673 window: &mut Window,
13674 cx: &mut Context<Self>,
13675 ) {
13676 if creases.is_empty() {
13677 return;
13678 }
13679
13680 let mut buffers_affected = HashSet::default();
13681 let multi_buffer = self.buffer().read(cx);
13682 for crease in &creases {
13683 if let Some((_, buffer, _)) =
13684 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13685 {
13686 buffers_affected.insert(buffer.read(cx).remote_id());
13687 };
13688 }
13689
13690 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13691
13692 if auto_scroll {
13693 self.request_autoscroll(Autoscroll::fit(), cx);
13694 }
13695
13696 cx.notify();
13697
13698 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13699 // Clear diagnostics block when folding a range that contains it.
13700 let snapshot = self.snapshot(window, cx);
13701 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13702 drop(snapshot);
13703 self.active_diagnostics = Some(active_diagnostics);
13704 self.dismiss_diagnostics(cx);
13705 } else {
13706 self.active_diagnostics = Some(active_diagnostics);
13707 }
13708 }
13709
13710 self.scrollbar_marker_state.dirty = true;
13711 }
13712
13713 /// Removes any folds whose ranges intersect any of the given ranges.
13714 pub fn unfold_ranges<T: ToOffset + Clone>(
13715 &mut self,
13716 ranges: &[Range<T>],
13717 inclusive: bool,
13718 auto_scroll: bool,
13719 cx: &mut Context<Self>,
13720 ) {
13721 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13722 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13723 });
13724 }
13725
13726 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13727 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13728 return;
13729 }
13730 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13731 self.display_map.update(cx, |display_map, cx| {
13732 display_map.fold_buffers([buffer_id], cx)
13733 });
13734 cx.emit(EditorEvent::BufferFoldToggled {
13735 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13736 folded: true,
13737 });
13738 cx.notify();
13739 }
13740
13741 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13742 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13743 return;
13744 }
13745 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13746 self.display_map.update(cx, |display_map, cx| {
13747 display_map.unfold_buffers([buffer_id], cx);
13748 });
13749 cx.emit(EditorEvent::BufferFoldToggled {
13750 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13751 folded: false,
13752 });
13753 cx.notify();
13754 }
13755
13756 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13757 self.display_map.read(cx).is_buffer_folded(buffer)
13758 }
13759
13760 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13761 self.display_map.read(cx).folded_buffers()
13762 }
13763
13764 /// Removes any folds with the given ranges.
13765 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13766 &mut self,
13767 ranges: &[Range<T>],
13768 type_id: TypeId,
13769 auto_scroll: bool,
13770 cx: &mut Context<Self>,
13771 ) {
13772 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13773 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13774 });
13775 }
13776
13777 fn remove_folds_with<T: ToOffset + Clone>(
13778 &mut self,
13779 ranges: &[Range<T>],
13780 auto_scroll: bool,
13781 cx: &mut Context<Self>,
13782 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13783 ) {
13784 if ranges.is_empty() {
13785 return;
13786 }
13787
13788 let mut buffers_affected = HashSet::default();
13789 let multi_buffer = self.buffer().read(cx);
13790 for range in ranges {
13791 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13792 buffers_affected.insert(buffer.read(cx).remote_id());
13793 };
13794 }
13795
13796 self.display_map.update(cx, update);
13797
13798 if auto_scroll {
13799 self.request_autoscroll(Autoscroll::fit(), cx);
13800 }
13801
13802 cx.notify();
13803 self.scrollbar_marker_state.dirty = true;
13804 self.active_indent_guides_state.dirty = true;
13805 }
13806
13807 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13808 self.display_map.read(cx).fold_placeholder.clone()
13809 }
13810
13811 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13812 self.buffer.update(cx, |buffer, cx| {
13813 buffer.set_all_diff_hunks_expanded(cx);
13814 });
13815 }
13816
13817 pub fn expand_all_diff_hunks(
13818 &mut self,
13819 _: &ExpandAllDiffHunks,
13820 _window: &mut Window,
13821 cx: &mut Context<Self>,
13822 ) {
13823 self.buffer.update(cx, |buffer, cx| {
13824 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13825 });
13826 }
13827
13828 pub fn toggle_selected_diff_hunks(
13829 &mut self,
13830 _: &ToggleSelectedDiffHunks,
13831 _window: &mut Window,
13832 cx: &mut Context<Self>,
13833 ) {
13834 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13835 self.toggle_diff_hunks_in_ranges(ranges, cx);
13836 }
13837
13838 pub fn diff_hunks_in_ranges<'a>(
13839 &'a self,
13840 ranges: &'a [Range<Anchor>],
13841 buffer: &'a MultiBufferSnapshot,
13842 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13843 ranges.iter().flat_map(move |range| {
13844 let end_excerpt_id = range.end.excerpt_id;
13845 let range = range.to_point(buffer);
13846 let mut peek_end = range.end;
13847 if range.end.row < buffer.max_row().0 {
13848 peek_end = Point::new(range.end.row + 1, 0);
13849 }
13850 buffer
13851 .diff_hunks_in_range(range.start..peek_end)
13852 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13853 })
13854 }
13855
13856 pub fn has_stageable_diff_hunks_in_ranges(
13857 &self,
13858 ranges: &[Range<Anchor>],
13859 snapshot: &MultiBufferSnapshot,
13860 ) -> bool {
13861 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13862 hunks.any(|hunk| hunk.status().has_secondary_hunk())
13863 }
13864
13865 pub fn toggle_staged_selected_diff_hunks(
13866 &mut self,
13867 _: &::git::ToggleStaged,
13868 _: &mut Window,
13869 cx: &mut Context<Self>,
13870 ) {
13871 let snapshot = self.buffer.read(cx).snapshot(cx);
13872 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13873 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13874 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13875 }
13876
13877 pub fn stage_and_next(
13878 &mut self,
13879 _: &::git::StageAndNext,
13880 window: &mut Window,
13881 cx: &mut Context<Self>,
13882 ) {
13883 self.do_stage_or_unstage_and_next(true, window, cx);
13884 }
13885
13886 pub fn unstage_and_next(
13887 &mut self,
13888 _: &::git::UnstageAndNext,
13889 window: &mut Window,
13890 cx: &mut Context<Self>,
13891 ) {
13892 self.do_stage_or_unstage_and_next(false, window, cx);
13893 }
13894
13895 pub fn stage_or_unstage_diff_hunks(
13896 &mut self,
13897 stage: bool,
13898 ranges: Vec<Range<Anchor>>,
13899 cx: &mut Context<Self>,
13900 ) {
13901 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
13902 cx.spawn(|this, mut cx| async move {
13903 task.await?;
13904 this.update(&mut cx, |this, cx| {
13905 let snapshot = this.buffer.read(cx).snapshot(cx);
13906 let chunk_by = this
13907 .diff_hunks_in_ranges(&ranges, &snapshot)
13908 .chunk_by(|hunk| hunk.buffer_id);
13909 for (buffer_id, hunks) in &chunk_by {
13910 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
13911 }
13912 })
13913 })
13914 .detach_and_log_err(cx);
13915 }
13916
13917 fn save_buffers_for_ranges_if_needed(
13918 &mut self,
13919 ranges: &[Range<Anchor>],
13920 cx: &mut Context<'_, Editor>,
13921 ) -> Task<Result<()>> {
13922 let multibuffer = self.buffer.read(cx);
13923 let snapshot = multibuffer.read(cx);
13924 let buffer_ids: HashSet<_> = ranges
13925 .iter()
13926 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
13927 .collect();
13928 drop(snapshot);
13929
13930 let mut buffers = HashSet::default();
13931 for buffer_id in buffer_ids {
13932 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
13933 let buffer = buffer_entity.read(cx);
13934 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
13935 {
13936 buffers.insert(buffer_entity);
13937 }
13938 }
13939 }
13940
13941 if let Some(project) = &self.project {
13942 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
13943 } else {
13944 Task::ready(Ok(()))
13945 }
13946 }
13947
13948 fn do_stage_or_unstage_and_next(
13949 &mut self,
13950 stage: bool,
13951 window: &mut Window,
13952 cx: &mut Context<Self>,
13953 ) {
13954 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13955
13956 if ranges.iter().any(|range| range.start != range.end) {
13957 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13958 return;
13959 }
13960
13961 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13962 let snapshot = self.snapshot(window, cx);
13963 let position = self.selections.newest::<Point>(cx).head();
13964 let mut row = snapshot
13965 .buffer_snapshot
13966 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
13967 .find(|hunk| hunk.row_range.start.0 > position.row)
13968 .map(|hunk| hunk.row_range.start);
13969
13970 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
13971 // Outside of the project diff editor, wrap around to the beginning.
13972 if !all_diff_hunks_expanded {
13973 row = row.or_else(|| {
13974 snapshot
13975 .buffer_snapshot
13976 .diff_hunks_in_range(Point::zero()..position)
13977 .find(|hunk| hunk.row_range.end.0 < position.row)
13978 .map(|hunk| hunk.row_range.start)
13979 });
13980 }
13981
13982 if let Some(row) = row {
13983 let destination = Point::new(row.0, 0);
13984 let autoscroll = Autoscroll::center();
13985
13986 self.unfold_ranges(&[destination..destination], false, false, cx);
13987 self.change_selections(Some(autoscroll), window, cx, |s| {
13988 s.select_ranges([destination..destination]);
13989 });
13990 } else if all_diff_hunks_expanded {
13991 window.dispatch_action(::git::ExpandCommitEditor.boxed_clone(), cx);
13992 }
13993 }
13994
13995 fn do_stage_or_unstage(
13996 &self,
13997 stage: bool,
13998 buffer_id: BufferId,
13999 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14000 cx: &mut App,
14001 ) -> Option<()> {
14002 let project = self.project.as_ref()?;
14003 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14004 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14005 let buffer_snapshot = buffer.read(cx).snapshot();
14006 let file_exists = buffer_snapshot
14007 .file()
14008 .is_some_and(|file| file.disk_state().exists());
14009 diff.update(cx, |diff, cx| {
14010 diff.stage_or_unstage_hunks(
14011 stage,
14012 &hunks
14013 .map(|hunk| buffer_diff::DiffHunk {
14014 buffer_range: hunk.buffer_range,
14015 diff_base_byte_range: hunk.diff_base_byte_range,
14016 secondary_status: hunk.secondary_status,
14017 range: Point::zero()..Point::zero(), // unused
14018 })
14019 .collect::<Vec<_>>(),
14020 &buffer_snapshot,
14021 file_exists,
14022 cx,
14023 )
14024 });
14025 None
14026 }
14027
14028 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14029 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14030 self.buffer
14031 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14032 }
14033
14034 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14035 self.buffer.update(cx, |buffer, cx| {
14036 let ranges = vec![Anchor::min()..Anchor::max()];
14037 if !buffer.all_diff_hunks_expanded()
14038 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14039 {
14040 buffer.collapse_diff_hunks(ranges, cx);
14041 true
14042 } else {
14043 false
14044 }
14045 })
14046 }
14047
14048 fn toggle_diff_hunks_in_ranges(
14049 &mut self,
14050 ranges: Vec<Range<Anchor>>,
14051 cx: &mut Context<'_, Editor>,
14052 ) {
14053 self.buffer.update(cx, |buffer, cx| {
14054 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14055 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14056 })
14057 }
14058
14059 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14060 self.buffer.update(cx, |buffer, cx| {
14061 let snapshot = buffer.snapshot(cx);
14062 let excerpt_id = range.end.excerpt_id;
14063 let point_range = range.to_point(&snapshot);
14064 let expand = !buffer.single_hunk_is_expanded(range, cx);
14065 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14066 })
14067 }
14068
14069 pub(crate) fn apply_all_diff_hunks(
14070 &mut self,
14071 _: &ApplyAllDiffHunks,
14072 window: &mut Window,
14073 cx: &mut Context<Self>,
14074 ) {
14075 let buffers = self.buffer.read(cx).all_buffers();
14076 for branch_buffer in buffers {
14077 branch_buffer.update(cx, |branch_buffer, cx| {
14078 branch_buffer.merge_into_base(Vec::new(), cx);
14079 });
14080 }
14081
14082 if let Some(project) = self.project.clone() {
14083 self.save(true, project, window, cx).detach_and_log_err(cx);
14084 }
14085 }
14086
14087 pub(crate) fn apply_selected_diff_hunks(
14088 &mut self,
14089 _: &ApplyDiffHunk,
14090 window: &mut Window,
14091 cx: &mut Context<Self>,
14092 ) {
14093 let snapshot = self.snapshot(window, cx);
14094 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14095 let mut ranges_by_buffer = HashMap::default();
14096 self.transact(window, cx, |editor, _window, cx| {
14097 for hunk in hunks {
14098 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14099 ranges_by_buffer
14100 .entry(buffer.clone())
14101 .or_insert_with(Vec::new)
14102 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14103 }
14104 }
14105
14106 for (buffer, ranges) in ranges_by_buffer {
14107 buffer.update(cx, |buffer, cx| {
14108 buffer.merge_into_base(ranges, cx);
14109 });
14110 }
14111 });
14112
14113 if let Some(project) = self.project.clone() {
14114 self.save(true, project, window, cx).detach_and_log_err(cx);
14115 }
14116 }
14117
14118 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14119 if hovered != self.gutter_hovered {
14120 self.gutter_hovered = hovered;
14121 cx.notify();
14122 }
14123 }
14124
14125 pub fn insert_blocks(
14126 &mut self,
14127 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14128 autoscroll: Option<Autoscroll>,
14129 cx: &mut Context<Self>,
14130 ) -> Vec<CustomBlockId> {
14131 let blocks = self
14132 .display_map
14133 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14134 if let Some(autoscroll) = autoscroll {
14135 self.request_autoscroll(autoscroll, cx);
14136 }
14137 cx.notify();
14138 blocks
14139 }
14140
14141 pub fn resize_blocks(
14142 &mut self,
14143 heights: HashMap<CustomBlockId, u32>,
14144 autoscroll: Option<Autoscroll>,
14145 cx: &mut Context<Self>,
14146 ) {
14147 self.display_map
14148 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14149 if let Some(autoscroll) = autoscroll {
14150 self.request_autoscroll(autoscroll, cx);
14151 }
14152 cx.notify();
14153 }
14154
14155 pub fn replace_blocks(
14156 &mut self,
14157 renderers: HashMap<CustomBlockId, RenderBlock>,
14158 autoscroll: Option<Autoscroll>,
14159 cx: &mut Context<Self>,
14160 ) {
14161 self.display_map
14162 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14163 if let Some(autoscroll) = autoscroll {
14164 self.request_autoscroll(autoscroll, cx);
14165 }
14166 cx.notify();
14167 }
14168
14169 pub fn remove_blocks(
14170 &mut self,
14171 block_ids: HashSet<CustomBlockId>,
14172 autoscroll: Option<Autoscroll>,
14173 cx: &mut Context<Self>,
14174 ) {
14175 self.display_map.update(cx, |display_map, cx| {
14176 display_map.remove_blocks(block_ids, cx)
14177 });
14178 if let Some(autoscroll) = autoscroll {
14179 self.request_autoscroll(autoscroll, cx);
14180 }
14181 cx.notify();
14182 }
14183
14184 pub fn row_for_block(
14185 &self,
14186 block_id: CustomBlockId,
14187 cx: &mut Context<Self>,
14188 ) -> Option<DisplayRow> {
14189 self.display_map
14190 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14191 }
14192
14193 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14194 self.focused_block = Some(focused_block);
14195 }
14196
14197 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14198 self.focused_block.take()
14199 }
14200
14201 pub fn insert_creases(
14202 &mut self,
14203 creases: impl IntoIterator<Item = Crease<Anchor>>,
14204 cx: &mut Context<Self>,
14205 ) -> Vec<CreaseId> {
14206 self.display_map
14207 .update(cx, |map, cx| map.insert_creases(creases, cx))
14208 }
14209
14210 pub fn remove_creases(
14211 &mut self,
14212 ids: impl IntoIterator<Item = CreaseId>,
14213 cx: &mut Context<Self>,
14214 ) {
14215 self.display_map
14216 .update(cx, |map, cx| map.remove_creases(ids, cx));
14217 }
14218
14219 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14220 self.display_map
14221 .update(cx, |map, cx| map.snapshot(cx))
14222 .longest_row()
14223 }
14224
14225 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14226 self.display_map
14227 .update(cx, |map, cx| map.snapshot(cx))
14228 .max_point()
14229 }
14230
14231 pub fn text(&self, cx: &App) -> String {
14232 self.buffer.read(cx).read(cx).text()
14233 }
14234
14235 pub fn is_empty(&self, cx: &App) -> bool {
14236 self.buffer.read(cx).read(cx).is_empty()
14237 }
14238
14239 pub fn text_option(&self, cx: &App) -> Option<String> {
14240 let text = self.text(cx);
14241 let text = text.trim();
14242
14243 if text.is_empty() {
14244 return None;
14245 }
14246
14247 Some(text.to_string())
14248 }
14249
14250 pub fn set_text(
14251 &mut self,
14252 text: impl Into<Arc<str>>,
14253 window: &mut Window,
14254 cx: &mut Context<Self>,
14255 ) {
14256 self.transact(window, cx, |this, _, cx| {
14257 this.buffer
14258 .read(cx)
14259 .as_singleton()
14260 .expect("you can only call set_text on editors for singleton buffers")
14261 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14262 });
14263 }
14264
14265 pub fn display_text(&self, cx: &mut App) -> String {
14266 self.display_map
14267 .update(cx, |map, cx| map.snapshot(cx))
14268 .text()
14269 }
14270
14271 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14272 let mut wrap_guides = smallvec::smallvec![];
14273
14274 if self.show_wrap_guides == Some(false) {
14275 return wrap_guides;
14276 }
14277
14278 let settings = self.buffer.read(cx).language_settings(cx);
14279 if settings.show_wrap_guides {
14280 match self.soft_wrap_mode(cx) {
14281 SoftWrap::Column(soft_wrap) => {
14282 wrap_guides.push((soft_wrap as usize, true));
14283 }
14284 SoftWrap::Bounded(soft_wrap) => {
14285 wrap_guides.push((soft_wrap as usize, true));
14286 }
14287 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14288 }
14289 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14290 }
14291
14292 wrap_guides
14293 }
14294
14295 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14296 let settings = self.buffer.read(cx).language_settings(cx);
14297 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14298 match mode {
14299 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14300 SoftWrap::None
14301 }
14302 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14303 language_settings::SoftWrap::PreferredLineLength => {
14304 SoftWrap::Column(settings.preferred_line_length)
14305 }
14306 language_settings::SoftWrap::Bounded => {
14307 SoftWrap::Bounded(settings.preferred_line_length)
14308 }
14309 }
14310 }
14311
14312 pub fn set_soft_wrap_mode(
14313 &mut self,
14314 mode: language_settings::SoftWrap,
14315
14316 cx: &mut Context<Self>,
14317 ) {
14318 self.soft_wrap_mode_override = Some(mode);
14319 cx.notify();
14320 }
14321
14322 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
14323 self.hard_wrap = hard_wrap;
14324 cx.notify();
14325 }
14326
14327 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14328 self.text_style_refinement = Some(style);
14329 }
14330
14331 /// called by the Element so we know what style we were most recently rendered with.
14332 pub(crate) fn set_style(
14333 &mut self,
14334 style: EditorStyle,
14335 window: &mut Window,
14336 cx: &mut Context<Self>,
14337 ) {
14338 let rem_size = window.rem_size();
14339 self.display_map.update(cx, |map, cx| {
14340 map.set_font(
14341 style.text.font(),
14342 style.text.font_size.to_pixels(rem_size),
14343 cx,
14344 )
14345 });
14346 self.style = Some(style);
14347 }
14348
14349 pub fn style(&self) -> Option<&EditorStyle> {
14350 self.style.as_ref()
14351 }
14352
14353 // Called by the element. This method is not designed to be called outside of the editor
14354 // element's layout code because it does not notify when rewrapping is computed synchronously.
14355 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14356 self.display_map
14357 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14358 }
14359
14360 pub fn set_soft_wrap(&mut self) {
14361 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14362 }
14363
14364 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14365 if self.soft_wrap_mode_override.is_some() {
14366 self.soft_wrap_mode_override.take();
14367 } else {
14368 let soft_wrap = match self.soft_wrap_mode(cx) {
14369 SoftWrap::GitDiff => return,
14370 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14371 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14372 language_settings::SoftWrap::None
14373 }
14374 };
14375 self.soft_wrap_mode_override = Some(soft_wrap);
14376 }
14377 cx.notify();
14378 }
14379
14380 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14381 let Some(workspace) = self.workspace() else {
14382 return;
14383 };
14384 let fs = workspace.read(cx).app_state().fs.clone();
14385 let current_show = TabBarSettings::get_global(cx).show;
14386 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14387 setting.show = Some(!current_show);
14388 });
14389 }
14390
14391 pub fn toggle_indent_guides(
14392 &mut self,
14393 _: &ToggleIndentGuides,
14394 _: &mut Window,
14395 cx: &mut Context<Self>,
14396 ) {
14397 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14398 self.buffer
14399 .read(cx)
14400 .language_settings(cx)
14401 .indent_guides
14402 .enabled
14403 });
14404 self.show_indent_guides = Some(!currently_enabled);
14405 cx.notify();
14406 }
14407
14408 fn should_show_indent_guides(&self) -> Option<bool> {
14409 self.show_indent_guides
14410 }
14411
14412 pub fn toggle_line_numbers(
14413 &mut self,
14414 _: &ToggleLineNumbers,
14415 _: &mut Window,
14416 cx: &mut Context<Self>,
14417 ) {
14418 let mut editor_settings = EditorSettings::get_global(cx).clone();
14419 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14420 EditorSettings::override_global(editor_settings, cx);
14421 }
14422
14423 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
14424 if let Some(show_line_numbers) = self.show_line_numbers {
14425 return show_line_numbers;
14426 }
14427 EditorSettings::get_global(cx).gutter.line_numbers
14428 }
14429
14430 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14431 self.use_relative_line_numbers
14432 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14433 }
14434
14435 pub fn toggle_relative_line_numbers(
14436 &mut self,
14437 _: &ToggleRelativeLineNumbers,
14438 _: &mut Window,
14439 cx: &mut Context<Self>,
14440 ) {
14441 let is_relative = self.should_use_relative_line_numbers(cx);
14442 self.set_relative_line_number(Some(!is_relative), cx)
14443 }
14444
14445 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14446 self.use_relative_line_numbers = is_relative;
14447 cx.notify();
14448 }
14449
14450 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14451 self.show_gutter = show_gutter;
14452 cx.notify();
14453 }
14454
14455 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14456 self.show_scrollbars = show_scrollbars;
14457 cx.notify();
14458 }
14459
14460 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14461 self.show_line_numbers = Some(show_line_numbers);
14462 cx.notify();
14463 }
14464
14465 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14466 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14467 cx.notify();
14468 }
14469
14470 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14471 self.show_code_actions = Some(show_code_actions);
14472 cx.notify();
14473 }
14474
14475 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14476 self.show_runnables = Some(show_runnables);
14477 cx.notify();
14478 }
14479
14480 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14481 if self.display_map.read(cx).masked != masked {
14482 self.display_map.update(cx, |map, _| map.masked = masked);
14483 }
14484 cx.notify()
14485 }
14486
14487 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14488 self.show_wrap_guides = Some(show_wrap_guides);
14489 cx.notify();
14490 }
14491
14492 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14493 self.show_indent_guides = Some(show_indent_guides);
14494 cx.notify();
14495 }
14496
14497 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14498 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14499 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14500 if let Some(dir) = file.abs_path(cx).parent() {
14501 return Some(dir.to_owned());
14502 }
14503 }
14504
14505 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14506 return Some(project_path.path.to_path_buf());
14507 }
14508 }
14509
14510 None
14511 }
14512
14513 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14514 self.active_excerpt(cx)?
14515 .1
14516 .read(cx)
14517 .file()
14518 .and_then(|f| f.as_local())
14519 }
14520
14521 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14522 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14523 let buffer = buffer.read(cx);
14524 if let Some(project_path) = buffer.project_path(cx) {
14525 let project = self.project.as_ref()?.read(cx);
14526 project.absolute_path(&project_path, cx)
14527 } else {
14528 buffer
14529 .file()
14530 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14531 }
14532 })
14533 }
14534
14535 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14536 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14537 let project_path = buffer.read(cx).project_path(cx)?;
14538 let project = self.project.as_ref()?.read(cx);
14539 let entry = project.entry_for_path(&project_path, cx)?;
14540 let path = entry.path.to_path_buf();
14541 Some(path)
14542 })
14543 }
14544
14545 pub fn reveal_in_finder(
14546 &mut self,
14547 _: &RevealInFileManager,
14548 _window: &mut Window,
14549 cx: &mut Context<Self>,
14550 ) {
14551 if let Some(target) = self.target_file(cx) {
14552 cx.reveal_path(&target.abs_path(cx));
14553 }
14554 }
14555
14556 pub fn copy_path(
14557 &mut self,
14558 _: &zed_actions::workspace::CopyPath,
14559 _window: &mut Window,
14560 cx: &mut Context<Self>,
14561 ) {
14562 if let Some(path) = self.target_file_abs_path(cx) {
14563 if let Some(path) = path.to_str() {
14564 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14565 }
14566 }
14567 }
14568
14569 pub fn copy_relative_path(
14570 &mut self,
14571 _: &zed_actions::workspace::CopyRelativePath,
14572 _window: &mut Window,
14573 cx: &mut Context<Self>,
14574 ) {
14575 if let Some(path) = self.target_file_path(cx) {
14576 if let Some(path) = path.to_str() {
14577 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14578 }
14579 }
14580 }
14581
14582 pub fn copy_file_name_without_extension(
14583 &mut self,
14584 _: &CopyFileNameWithoutExtension,
14585 _: &mut Window,
14586 cx: &mut Context<Self>,
14587 ) {
14588 if let Some(file) = self.target_file(cx) {
14589 if let Some(file_stem) = file.path().file_stem() {
14590 if let Some(name) = file_stem.to_str() {
14591 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14592 }
14593 }
14594 }
14595 }
14596
14597 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14598 if let Some(file) = self.target_file(cx) {
14599 if let Some(file_name) = file.path().file_name() {
14600 if let Some(name) = file_name.to_str() {
14601 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14602 }
14603 }
14604 }
14605 }
14606
14607 pub fn toggle_git_blame(
14608 &mut self,
14609 _: &::git::Blame,
14610 window: &mut Window,
14611 cx: &mut Context<Self>,
14612 ) {
14613 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14614
14615 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14616 self.start_git_blame(true, window, cx);
14617 }
14618
14619 cx.notify();
14620 }
14621
14622 pub fn toggle_git_blame_inline(
14623 &mut self,
14624 _: &ToggleGitBlameInline,
14625 window: &mut Window,
14626 cx: &mut Context<Self>,
14627 ) {
14628 self.toggle_git_blame_inline_internal(true, window, cx);
14629 cx.notify();
14630 }
14631
14632 pub fn git_blame_inline_enabled(&self) -> bool {
14633 self.git_blame_inline_enabled
14634 }
14635
14636 pub fn toggle_selection_menu(
14637 &mut self,
14638 _: &ToggleSelectionMenu,
14639 _: &mut Window,
14640 cx: &mut Context<Self>,
14641 ) {
14642 self.show_selection_menu = self
14643 .show_selection_menu
14644 .map(|show_selections_menu| !show_selections_menu)
14645 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14646
14647 cx.notify();
14648 }
14649
14650 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14651 self.show_selection_menu
14652 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14653 }
14654
14655 fn start_git_blame(
14656 &mut self,
14657 user_triggered: bool,
14658 window: &mut Window,
14659 cx: &mut Context<Self>,
14660 ) {
14661 if let Some(project) = self.project.as_ref() {
14662 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14663 return;
14664 };
14665
14666 if buffer.read(cx).file().is_none() {
14667 return;
14668 }
14669
14670 let focused = self.focus_handle(cx).contains_focused(window, cx);
14671
14672 let project = project.clone();
14673 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14674 self.blame_subscription =
14675 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14676 self.blame = Some(blame);
14677 }
14678 }
14679
14680 fn toggle_git_blame_inline_internal(
14681 &mut self,
14682 user_triggered: bool,
14683 window: &mut Window,
14684 cx: &mut Context<Self>,
14685 ) {
14686 if self.git_blame_inline_enabled {
14687 self.git_blame_inline_enabled = false;
14688 self.show_git_blame_inline = false;
14689 self.show_git_blame_inline_delay_task.take();
14690 } else {
14691 self.git_blame_inline_enabled = true;
14692 self.start_git_blame_inline(user_triggered, window, cx);
14693 }
14694
14695 cx.notify();
14696 }
14697
14698 fn start_git_blame_inline(
14699 &mut self,
14700 user_triggered: bool,
14701 window: &mut Window,
14702 cx: &mut Context<Self>,
14703 ) {
14704 self.start_git_blame(user_triggered, window, cx);
14705
14706 if ProjectSettings::get_global(cx)
14707 .git
14708 .inline_blame_delay()
14709 .is_some()
14710 {
14711 self.start_inline_blame_timer(window, cx);
14712 } else {
14713 self.show_git_blame_inline = true
14714 }
14715 }
14716
14717 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14718 self.blame.as_ref()
14719 }
14720
14721 pub fn show_git_blame_gutter(&self) -> bool {
14722 self.show_git_blame_gutter
14723 }
14724
14725 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14726 self.show_git_blame_gutter && self.has_blame_entries(cx)
14727 }
14728
14729 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14730 self.show_git_blame_inline
14731 && (self.focus_handle.is_focused(window)
14732 || self
14733 .git_blame_inline_tooltip
14734 .as_ref()
14735 .and_then(|t| t.upgrade())
14736 .is_some())
14737 && !self.newest_selection_head_on_empty_line(cx)
14738 && self.has_blame_entries(cx)
14739 }
14740
14741 fn has_blame_entries(&self, cx: &App) -> bool {
14742 self.blame()
14743 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14744 }
14745
14746 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14747 let cursor_anchor = self.selections.newest_anchor().head();
14748
14749 let snapshot = self.buffer.read(cx).snapshot(cx);
14750 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14751
14752 snapshot.line_len(buffer_row) == 0
14753 }
14754
14755 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14756 let buffer_and_selection = maybe!({
14757 let selection = self.selections.newest::<Point>(cx);
14758 let selection_range = selection.range();
14759
14760 let multi_buffer = self.buffer().read(cx);
14761 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14762 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14763
14764 let (buffer, range, _) = if selection.reversed {
14765 buffer_ranges.first()
14766 } else {
14767 buffer_ranges.last()
14768 }?;
14769
14770 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14771 ..text::ToPoint::to_point(&range.end, &buffer).row;
14772 Some((
14773 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14774 selection,
14775 ))
14776 });
14777
14778 let Some((buffer, selection)) = buffer_and_selection else {
14779 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14780 };
14781
14782 let Some(project) = self.project.as_ref() else {
14783 return Task::ready(Err(anyhow!("editor does not have project")));
14784 };
14785
14786 project.update(cx, |project, cx| {
14787 project.get_permalink_to_line(&buffer, selection, cx)
14788 })
14789 }
14790
14791 pub fn copy_permalink_to_line(
14792 &mut self,
14793 _: &CopyPermalinkToLine,
14794 window: &mut Window,
14795 cx: &mut Context<Self>,
14796 ) {
14797 let permalink_task = self.get_permalink_to_line(cx);
14798 let workspace = self.workspace();
14799
14800 cx.spawn_in(window, |_, mut cx| async move {
14801 match permalink_task.await {
14802 Ok(permalink) => {
14803 cx.update(|_, cx| {
14804 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14805 })
14806 .ok();
14807 }
14808 Err(err) => {
14809 let message = format!("Failed to copy permalink: {err}");
14810
14811 Err::<(), anyhow::Error>(err).log_err();
14812
14813 if let Some(workspace) = workspace {
14814 workspace
14815 .update_in(&mut cx, |workspace, _, cx| {
14816 struct CopyPermalinkToLine;
14817
14818 workspace.show_toast(
14819 Toast::new(
14820 NotificationId::unique::<CopyPermalinkToLine>(),
14821 message,
14822 ),
14823 cx,
14824 )
14825 })
14826 .ok();
14827 }
14828 }
14829 }
14830 })
14831 .detach();
14832 }
14833
14834 pub fn copy_file_location(
14835 &mut self,
14836 _: &CopyFileLocation,
14837 _: &mut Window,
14838 cx: &mut Context<Self>,
14839 ) {
14840 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14841 if let Some(file) = self.target_file(cx) {
14842 if let Some(path) = file.path().to_str() {
14843 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14844 }
14845 }
14846 }
14847
14848 pub fn open_permalink_to_line(
14849 &mut self,
14850 _: &OpenPermalinkToLine,
14851 window: &mut Window,
14852 cx: &mut Context<Self>,
14853 ) {
14854 let permalink_task = self.get_permalink_to_line(cx);
14855 let workspace = self.workspace();
14856
14857 cx.spawn_in(window, |_, mut cx| async move {
14858 match permalink_task.await {
14859 Ok(permalink) => {
14860 cx.update(|_, cx| {
14861 cx.open_url(permalink.as_ref());
14862 })
14863 .ok();
14864 }
14865 Err(err) => {
14866 let message = format!("Failed to open permalink: {err}");
14867
14868 Err::<(), anyhow::Error>(err).log_err();
14869
14870 if let Some(workspace) = workspace {
14871 workspace
14872 .update(&mut cx, |workspace, cx| {
14873 struct OpenPermalinkToLine;
14874
14875 workspace.show_toast(
14876 Toast::new(
14877 NotificationId::unique::<OpenPermalinkToLine>(),
14878 message,
14879 ),
14880 cx,
14881 )
14882 })
14883 .ok();
14884 }
14885 }
14886 }
14887 })
14888 .detach();
14889 }
14890
14891 pub fn insert_uuid_v4(
14892 &mut self,
14893 _: &InsertUuidV4,
14894 window: &mut Window,
14895 cx: &mut Context<Self>,
14896 ) {
14897 self.insert_uuid(UuidVersion::V4, window, cx);
14898 }
14899
14900 pub fn insert_uuid_v7(
14901 &mut self,
14902 _: &InsertUuidV7,
14903 window: &mut Window,
14904 cx: &mut Context<Self>,
14905 ) {
14906 self.insert_uuid(UuidVersion::V7, window, cx);
14907 }
14908
14909 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14910 self.transact(window, cx, |this, window, cx| {
14911 let edits = this
14912 .selections
14913 .all::<Point>(cx)
14914 .into_iter()
14915 .map(|selection| {
14916 let uuid = match version {
14917 UuidVersion::V4 => uuid::Uuid::new_v4(),
14918 UuidVersion::V7 => uuid::Uuid::now_v7(),
14919 };
14920
14921 (selection.range(), uuid.to_string())
14922 });
14923 this.edit(edits, cx);
14924 this.refresh_inline_completion(true, false, window, cx);
14925 });
14926 }
14927
14928 pub fn open_selections_in_multibuffer(
14929 &mut self,
14930 _: &OpenSelectionsInMultibuffer,
14931 window: &mut Window,
14932 cx: &mut Context<Self>,
14933 ) {
14934 let multibuffer = self.buffer.read(cx);
14935
14936 let Some(buffer) = multibuffer.as_singleton() else {
14937 return;
14938 };
14939
14940 let Some(workspace) = self.workspace() else {
14941 return;
14942 };
14943
14944 let locations = self
14945 .selections
14946 .disjoint_anchors()
14947 .iter()
14948 .map(|range| Location {
14949 buffer: buffer.clone(),
14950 range: range.start.text_anchor..range.end.text_anchor,
14951 })
14952 .collect::<Vec<_>>();
14953
14954 let title = multibuffer.title(cx).to_string();
14955
14956 cx.spawn_in(window, |_, mut cx| async move {
14957 workspace.update_in(&mut cx, |workspace, window, cx| {
14958 Self::open_locations_in_multibuffer(
14959 workspace,
14960 locations,
14961 format!("Selections for '{title}'"),
14962 false,
14963 MultibufferSelectionMode::All,
14964 window,
14965 cx,
14966 );
14967 })
14968 })
14969 .detach();
14970 }
14971
14972 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14973 /// last highlight added will be used.
14974 ///
14975 /// If the range ends at the beginning of a line, then that line will not be highlighted.
14976 pub fn highlight_rows<T: 'static>(
14977 &mut self,
14978 range: Range<Anchor>,
14979 color: Hsla,
14980 should_autoscroll: bool,
14981 cx: &mut Context<Self>,
14982 ) {
14983 let snapshot = self.buffer().read(cx).snapshot(cx);
14984 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
14985 let ix = row_highlights.binary_search_by(|highlight| {
14986 Ordering::Equal
14987 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
14988 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
14989 });
14990
14991 if let Err(mut ix) = ix {
14992 let index = post_inc(&mut self.highlight_order);
14993
14994 // If this range intersects with the preceding highlight, then merge it with
14995 // the preceding highlight. Otherwise insert a new highlight.
14996 let mut merged = false;
14997 if ix > 0 {
14998 let prev_highlight = &mut row_highlights[ix - 1];
14999 if prev_highlight
15000 .range
15001 .end
15002 .cmp(&range.start, &snapshot)
15003 .is_ge()
15004 {
15005 ix -= 1;
15006 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15007 prev_highlight.range.end = range.end;
15008 }
15009 merged = true;
15010 prev_highlight.index = index;
15011 prev_highlight.color = color;
15012 prev_highlight.should_autoscroll = should_autoscroll;
15013 }
15014 }
15015
15016 if !merged {
15017 row_highlights.insert(
15018 ix,
15019 RowHighlight {
15020 range: range.clone(),
15021 index,
15022 color,
15023 should_autoscroll,
15024 },
15025 );
15026 }
15027
15028 // If any of the following highlights intersect with this one, merge them.
15029 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15030 let highlight = &row_highlights[ix];
15031 if next_highlight
15032 .range
15033 .start
15034 .cmp(&highlight.range.end, &snapshot)
15035 .is_le()
15036 {
15037 if next_highlight
15038 .range
15039 .end
15040 .cmp(&highlight.range.end, &snapshot)
15041 .is_gt()
15042 {
15043 row_highlights[ix].range.end = next_highlight.range.end;
15044 }
15045 row_highlights.remove(ix + 1);
15046 } else {
15047 break;
15048 }
15049 }
15050 }
15051 }
15052
15053 /// Remove any highlighted row ranges of the given type that intersect the
15054 /// given ranges.
15055 pub fn remove_highlighted_rows<T: 'static>(
15056 &mut self,
15057 ranges_to_remove: Vec<Range<Anchor>>,
15058 cx: &mut Context<Self>,
15059 ) {
15060 let snapshot = self.buffer().read(cx).snapshot(cx);
15061 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15062 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15063 row_highlights.retain(|highlight| {
15064 while let Some(range_to_remove) = ranges_to_remove.peek() {
15065 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15066 Ordering::Less | Ordering::Equal => {
15067 ranges_to_remove.next();
15068 }
15069 Ordering::Greater => {
15070 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15071 Ordering::Less | Ordering::Equal => {
15072 return false;
15073 }
15074 Ordering::Greater => break,
15075 }
15076 }
15077 }
15078 }
15079
15080 true
15081 })
15082 }
15083
15084 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15085 pub fn clear_row_highlights<T: 'static>(&mut self) {
15086 self.highlighted_rows.remove(&TypeId::of::<T>());
15087 }
15088
15089 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15090 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15091 self.highlighted_rows
15092 .get(&TypeId::of::<T>())
15093 .map_or(&[] as &[_], |vec| vec.as_slice())
15094 .iter()
15095 .map(|highlight| (highlight.range.clone(), highlight.color))
15096 }
15097
15098 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15099 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15100 /// Allows to ignore certain kinds of highlights.
15101 pub fn highlighted_display_rows(
15102 &self,
15103 window: &mut Window,
15104 cx: &mut App,
15105 ) -> BTreeMap<DisplayRow, LineHighlight> {
15106 let snapshot = self.snapshot(window, cx);
15107 let mut used_highlight_orders = HashMap::default();
15108 self.highlighted_rows
15109 .iter()
15110 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15111 .fold(
15112 BTreeMap::<DisplayRow, LineHighlight>::new(),
15113 |mut unique_rows, highlight| {
15114 let start = highlight.range.start.to_display_point(&snapshot);
15115 let end = highlight.range.end.to_display_point(&snapshot);
15116 let start_row = start.row().0;
15117 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15118 && end.column() == 0
15119 {
15120 end.row().0.saturating_sub(1)
15121 } else {
15122 end.row().0
15123 };
15124 for row in start_row..=end_row {
15125 let used_index =
15126 used_highlight_orders.entry(row).or_insert(highlight.index);
15127 if highlight.index >= *used_index {
15128 *used_index = highlight.index;
15129 unique_rows.insert(DisplayRow(row), highlight.color.into());
15130 }
15131 }
15132 unique_rows
15133 },
15134 )
15135 }
15136
15137 pub fn highlighted_display_row_for_autoscroll(
15138 &self,
15139 snapshot: &DisplaySnapshot,
15140 ) -> Option<DisplayRow> {
15141 self.highlighted_rows
15142 .values()
15143 .flat_map(|highlighted_rows| highlighted_rows.iter())
15144 .filter_map(|highlight| {
15145 if highlight.should_autoscroll {
15146 Some(highlight.range.start.to_display_point(snapshot).row())
15147 } else {
15148 None
15149 }
15150 })
15151 .min()
15152 }
15153
15154 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
15155 self.highlight_background::<SearchWithinRange>(
15156 ranges,
15157 |colors| colors.editor_document_highlight_read_background,
15158 cx,
15159 )
15160 }
15161
15162 pub fn set_breadcrumb_header(&mut self, new_header: String) {
15163 self.breadcrumb_header = Some(new_header);
15164 }
15165
15166 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
15167 self.clear_background_highlights::<SearchWithinRange>(cx);
15168 }
15169
15170 pub fn highlight_background<T: 'static>(
15171 &mut self,
15172 ranges: &[Range<Anchor>],
15173 color_fetcher: fn(&ThemeColors) -> Hsla,
15174 cx: &mut Context<Self>,
15175 ) {
15176 self.background_highlights
15177 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15178 self.scrollbar_marker_state.dirty = true;
15179 cx.notify();
15180 }
15181
15182 pub fn clear_background_highlights<T: 'static>(
15183 &mut self,
15184 cx: &mut Context<Self>,
15185 ) -> Option<BackgroundHighlight> {
15186 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15187 if !text_highlights.1.is_empty() {
15188 self.scrollbar_marker_state.dirty = true;
15189 cx.notify();
15190 }
15191 Some(text_highlights)
15192 }
15193
15194 pub fn highlight_gutter<T: 'static>(
15195 &mut self,
15196 ranges: &[Range<Anchor>],
15197 color_fetcher: fn(&App) -> Hsla,
15198 cx: &mut Context<Self>,
15199 ) {
15200 self.gutter_highlights
15201 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15202 cx.notify();
15203 }
15204
15205 pub fn clear_gutter_highlights<T: 'static>(
15206 &mut self,
15207 cx: &mut Context<Self>,
15208 ) -> Option<GutterHighlight> {
15209 cx.notify();
15210 self.gutter_highlights.remove(&TypeId::of::<T>())
15211 }
15212
15213 #[cfg(feature = "test-support")]
15214 pub fn all_text_background_highlights(
15215 &self,
15216 window: &mut Window,
15217 cx: &mut Context<Self>,
15218 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15219 let snapshot = self.snapshot(window, cx);
15220 let buffer = &snapshot.buffer_snapshot;
15221 let start = buffer.anchor_before(0);
15222 let end = buffer.anchor_after(buffer.len());
15223 let theme = cx.theme().colors();
15224 self.background_highlights_in_range(start..end, &snapshot, theme)
15225 }
15226
15227 #[cfg(feature = "test-support")]
15228 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15229 let snapshot = self.buffer().read(cx).snapshot(cx);
15230
15231 let highlights = self
15232 .background_highlights
15233 .get(&TypeId::of::<items::BufferSearchHighlights>());
15234
15235 if let Some((_color, ranges)) = highlights {
15236 ranges
15237 .iter()
15238 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15239 .collect_vec()
15240 } else {
15241 vec![]
15242 }
15243 }
15244
15245 fn document_highlights_for_position<'a>(
15246 &'a self,
15247 position: Anchor,
15248 buffer: &'a MultiBufferSnapshot,
15249 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15250 let read_highlights = self
15251 .background_highlights
15252 .get(&TypeId::of::<DocumentHighlightRead>())
15253 .map(|h| &h.1);
15254 let write_highlights = self
15255 .background_highlights
15256 .get(&TypeId::of::<DocumentHighlightWrite>())
15257 .map(|h| &h.1);
15258 let left_position = position.bias_left(buffer);
15259 let right_position = position.bias_right(buffer);
15260 read_highlights
15261 .into_iter()
15262 .chain(write_highlights)
15263 .flat_map(move |ranges| {
15264 let start_ix = match ranges.binary_search_by(|probe| {
15265 let cmp = probe.end.cmp(&left_position, buffer);
15266 if cmp.is_ge() {
15267 Ordering::Greater
15268 } else {
15269 Ordering::Less
15270 }
15271 }) {
15272 Ok(i) | Err(i) => i,
15273 };
15274
15275 ranges[start_ix..]
15276 .iter()
15277 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15278 })
15279 }
15280
15281 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15282 self.background_highlights
15283 .get(&TypeId::of::<T>())
15284 .map_or(false, |(_, highlights)| !highlights.is_empty())
15285 }
15286
15287 pub fn background_highlights_in_range(
15288 &self,
15289 search_range: Range<Anchor>,
15290 display_snapshot: &DisplaySnapshot,
15291 theme: &ThemeColors,
15292 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15293 let mut results = Vec::new();
15294 for (color_fetcher, ranges) in self.background_highlights.values() {
15295 let color = color_fetcher(theme);
15296 let start_ix = match ranges.binary_search_by(|probe| {
15297 let cmp = probe
15298 .end
15299 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15300 if cmp.is_gt() {
15301 Ordering::Greater
15302 } else {
15303 Ordering::Less
15304 }
15305 }) {
15306 Ok(i) | Err(i) => i,
15307 };
15308 for range in &ranges[start_ix..] {
15309 if range
15310 .start
15311 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15312 .is_ge()
15313 {
15314 break;
15315 }
15316
15317 let start = range.start.to_display_point(display_snapshot);
15318 let end = range.end.to_display_point(display_snapshot);
15319 results.push((start..end, color))
15320 }
15321 }
15322 results
15323 }
15324
15325 pub fn background_highlight_row_ranges<T: 'static>(
15326 &self,
15327 search_range: Range<Anchor>,
15328 display_snapshot: &DisplaySnapshot,
15329 count: usize,
15330 ) -> Vec<RangeInclusive<DisplayPoint>> {
15331 let mut results = Vec::new();
15332 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15333 return vec![];
15334 };
15335
15336 let start_ix = match ranges.binary_search_by(|probe| {
15337 let cmp = probe
15338 .end
15339 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15340 if cmp.is_gt() {
15341 Ordering::Greater
15342 } else {
15343 Ordering::Less
15344 }
15345 }) {
15346 Ok(i) | Err(i) => i,
15347 };
15348 let mut push_region = |start: Option<Point>, end: Option<Point>| {
15349 if let (Some(start_display), Some(end_display)) = (start, end) {
15350 results.push(
15351 start_display.to_display_point(display_snapshot)
15352 ..=end_display.to_display_point(display_snapshot),
15353 );
15354 }
15355 };
15356 let mut start_row: Option<Point> = None;
15357 let mut end_row: Option<Point> = None;
15358 if ranges.len() > count {
15359 return Vec::new();
15360 }
15361 for range in &ranges[start_ix..] {
15362 if range
15363 .start
15364 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15365 .is_ge()
15366 {
15367 break;
15368 }
15369 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
15370 if let Some(current_row) = &end_row {
15371 if end.row == current_row.row {
15372 continue;
15373 }
15374 }
15375 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
15376 if start_row.is_none() {
15377 assert_eq!(end_row, None);
15378 start_row = Some(start);
15379 end_row = Some(end);
15380 continue;
15381 }
15382 if let Some(current_end) = end_row.as_mut() {
15383 if start.row > current_end.row + 1 {
15384 push_region(start_row, end_row);
15385 start_row = Some(start);
15386 end_row = Some(end);
15387 } else {
15388 // Merge two hunks.
15389 *current_end = end;
15390 }
15391 } else {
15392 unreachable!();
15393 }
15394 }
15395 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15396 push_region(start_row, end_row);
15397 results
15398 }
15399
15400 pub fn gutter_highlights_in_range(
15401 &self,
15402 search_range: Range<Anchor>,
15403 display_snapshot: &DisplaySnapshot,
15404 cx: &App,
15405 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15406 let mut results = Vec::new();
15407 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15408 let color = color_fetcher(cx);
15409 let start_ix = match ranges.binary_search_by(|probe| {
15410 let cmp = probe
15411 .end
15412 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15413 if cmp.is_gt() {
15414 Ordering::Greater
15415 } else {
15416 Ordering::Less
15417 }
15418 }) {
15419 Ok(i) | Err(i) => i,
15420 };
15421 for range in &ranges[start_ix..] {
15422 if range
15423 .start
15424 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15425 .is_ge()
15426 {
15427 break;
15428 }
15429
15430 let start = range.start.to_display_point(display_snapshot);
15431 let end = range.end.to_display_point(display_snapshot);
15432 results.push((start..end, color))
15433 }
15434 }
15435 results
15436 }
15437
15438 /// Get the text ranges corresponding to the redaction query
15439 pub fn redacted_ranges(
15440 &self,
15441 search_range: Range<Anchor>,
15442 display_snapshot: &DisplaySnapshot,
15443 cx: &App,
15444 ) -> Vec<Range<DisplayPoint>> {
15445 display_snapshot
15446 .buffer_snapshot
15447 .redacted_ranges(search_range, |file| {
15448 if let Some(file) = file {
15449 file.is_private()
15450 && EditorSettings::get(
15451 Some(SettingsLocation {
15452 worktree_id: file.worktree_id(cx),
15453 path: file.path().as_ref(),
15454 }),
15455 cx,
15456 )
15457 .redact_private_values
15458 } else {
15459 false
15460 }
15461 })
15462 .map(|range| {
15463 range.start.to_display_point(display_snapshot)
15464 ..range.end.to_display_point(display_snapshot)
15465 })
15466 .collect()
15467 }
15468
15469 pub fn highlight_text<T: 'static>(
15470 &mut self,
15471 ranges: Vec<Range<Anchor>>,
15472 style: HighlightStyle,
15473 cx: &mut Context<Self>,
15474 ) {
15475 self.display_map.update(cx, |map, _| {
15476 map.highlight_text(TypeId::of::<T>(), ranges, style)
15477 });
15478 cx.notify();
15479 }
15480
15481 pub(crate) fn highlight_inlays<T: 'static>(
15482 &mut self,
15483 highlights: Vec<InlayHighlight>,
15484 style: HighlightStyle,
15485 cx: &mut Context<Self>,
15486 ) {
15487 self.display_map.update(cx, |map, _| {
15488 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15489 });
15490 cx.notify();
15491 }
15492
15493 pub fn text_highlights<'a, T: 'static>(
15494 &'a self,
15495 cx: &'a App,
15496 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15497 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15498 }
15499
15500 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15501 let cleared = self
15502 .display_map
15503 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15504 if cleared {
15505 cx.notify();
15506 }
15507 }
15508
15509 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15510 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15511 && self.focus_handle.is_focused(window)
15512 }
15513
15514 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15515 self.show_cursor_when_unfocused = is_enabled;
15516 cx.notify();
15517 }
15518
15519 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15520 cx.notify();
15521 }
15522
15523 fn on_buffer_event(
15524 &mut self,
15525 multibuffer: &Entity<MultiBuffer>,
15526 event: &multi_buffer::Event,
15527 window: &mut Window,
15528 cx: &mut Context<Self>,
15529 ) {
15530 match event {
15531 multi_buffer::Event::Edited {
15532 singleton_buffer_edited,
15533 edited_buffer: buffer_edited,
15534 } => {
15535 self.scrollbar_marker_state.dirty = true;
15536 self.active_indent_guides_state.dirty = true;
15537 self.refresh_active_diagnostics(cx);
15538 self.refresh_code_actions(window, cx);
15539 if self.has_active_inline_completion() {
15540 self.update_visible_inline_completion(window, cx);
15541 }
15542 if let Some(buffer) = buffer_edited {
15543 let buffer_id = buffer.read(cx).remote_id();
15544 if !self.registered_buffers.contains_key(&buffer_id) {
15545 if let Some(project) = self.project.as_ref() {
15546 project.update(cx, |project, cx| {
15547 self.registered_buffers.insert(
15548 buffer_id,
15549 project.register_buffer_with_language_servers(&buffer, cx),
15550 );
15551 })
15552 }
15553 }
15554 }
15555 cx.emit(EditorEvent::BufferEdited);
15556 cx.emit(SearchEvent::MatchesInvalidated);
15557 if *singleton_buffer_edited {
15558 if let Some(project) = &self.project {
15559 #[allow(clippy::mutable_key_type)]
15560 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15561 multibuffer
15562 .all_buffers()
15563 .into_iter()
15564 .filter_map(|buffer| {
15565 buffer.update(cx, |buffer, cx| {
15566 let language = buffer.language()?;
15567 let should_discard = project.update(cx, |project, cx| {
15568 project.is_local()
15569 && !project.has_language_servers_for(buffer, cx)
15570 });
15571 should_discard.not().then_some(language.clone())
15572 })
15573 })
15574 .collect::<HashSet<_>>()
15575 });
15576 if !languages_affected.is_empty() {
15577 self.refresh_inlay_hints(
15578 InlayHintRefreshReason::BufferEdited(languages_affected),
15579 cx,
15580 );
15581 }
15582 }
15583 }
15584
15585 let Some(project) = &self.project else { return };
15586 let (telemetry, is_via_ssh) = {
15587 let project = project.read(cx);
15588 let telemetry = project.client().telemetry().clone();
15589 let is_via_ssh = project.is_via_ssh();
15590 (telemetry, is_via_ssh)
15591 };
15592 refresh_linked_ranges(self, window, cx);
15593 telemetry.log_edit_event("editor", is_via_ssh);
15594 }
15595 multi_buffer::Event::ExcerptsAdded {
15596 buffer,
15597 predecessor,
15598 excerpts,
15599 } => {
15600 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15601 let buffer_id = buffer.read(cx).remote_id();
15602 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15603 if let Some(project) = &self.project {
15604 get_uncommitted_diff_for_buffer(
15605 project,
15606 [buffer.clone()],
15607 self.buffer.clone(),
15608 cx,
15609 )
15610 .detach();
15611 }
15612 }
15613 cx.emit(EditorEvent::ExcerptsAdded {
15614 buffer: buffer.clone(),
15615 predecessor: *predecessor,
15616 excerpts: excerpts.clone(),
15617 });
15618 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15619 }
15620 multi_buffer::Event::ExcerptsRemoved { ids } => {
15621 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15622 let buffer = self.buffer.read(cx);
15623 self.registered_buffers
15624 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15625 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15626 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15627 }
15628 multi_buffer::Event::ExcerptsEdited {
15629 excerpt_ids,
15630 buffer_ids,
15631 } => {
15632 self.display_map.update(cx, |map, cx| {
15633 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15634 });
15635 cx.emit(EditorEvent::ExcerptsEdited {
15636 ids: excerpt_ids.clone(),
15637 })
15638 }
15639 multi_buffer::Event::ExcerptsExpanded { ids } => {
15640 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15641 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15642 }
15643 multi_buffer::Event::Reparsed(buffer_id) => {
15644 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15645 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15646
15647 cx.emit(EditorEvent::Reparsed(*buffer_id));
15648 }
15649 multi_buffer::Event::DiffHunksToggled => {
15650 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15651 }
15652 multi_buffer::Event::LanguageChanged(buffer_id) => {
15653 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15654 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15655 cx.emit(EditorEvent::Reparsed(*buffer_id));
15656 cx.notify();
15657 }
15658 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15659 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15660 multi_buffer::Event::FileHandleChanged
15661 | multi_buffer::Event::Reloaded
15662 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
15663 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15664 multi_buffer::Event::DiagnosticsUpdated => {
15665 self.refresh_active_diagnostics(cx);
15666 self.refresh_inline_diagnostics(true, window, cx);
15667 self.scrollbar_marker_state.dirty = true;
15668 cx.notify();
15669 }
15670 _ => {}
15671 };
15672 }
15673
15674 fn on_display_map_changed(
15675 &mut self,
15676 _: Entity<DisplayMap>,
15677 _: &mut Window,
15678 cx: &mut Context<Self>,
15679 ) {
15680 cx.notify();
15681 }
15682
15683 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15684 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15685 self.update_edit_prediction_settings(cx);
15686 self.refresh_inline_completion(true, false, window, cx);
15687 self.refresh_inlay_hints(
15688 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15689 self.selections.newest_anchor().head(),
15690 &self.buffer.read(cx).snapshot(cx),
15691 cx,
15692 )),
15693 cx,
15694 );
15695
15696 let old_cursor_shape = self.cursor_shape;
15697
15698 {
15699 let editor_settings = EditorSettings::get_global(cx);
15700 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15701 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15702 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15703 }
15704
15705 if old_cursor_shape != self.cursor_shape {
15706 cx.emit(EditorEvent::CursorShapeChanged);
15707 }
15708
15709 let project_settings = ProjectSettings::get_global(cx);
15710 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15711
15712 if self.mode == EditorMode::Full {
15713 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15714 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15715 if self.show_inline_diagnostics != show_inline_diagnostics {
15716 self.show_inline_diagnostics = show_inline_diagnostics;
15717 self.refresh_inline_diagnostics(false, window, cx);
15718 }
15719
15720 if self.git_blame_inline_enabled != inline_blame_enabled {
15721 self.toggle_git_blame_inline_internal(false, window, cx);
15722 }
15723 }
15724
15725 cx.notify();
15726 }
15727
15728 pub fn set_searchable(&mut self, searchable: bool) {
15729 self.searchable = searchable;
15730 }
15731
15732 pub fn searchable(&self) -> bool {
15733 self.searchable
15734 }
15735
15736 fn open_proposed_changes_editor(
15737 &mut self,
15738 _: &OpenProposedChangesEditor,
15739 window: &mut Window,
15740 cx: &mut Context<Self>,
15741 ) {
15742 let Some(workspace) = self.workspace() else {
15743 cx.propagate();
15744 return;
15745 };
15746
15747 let selections = self.selections.all::<usize>(cx);
15748 let multi_buffer = self.buffer.read(cx);
15749 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15750 let mut new_selections_by_buffer = HashMap::default();
15751 for selection in selections {
15752 for (buffer, range, _) in
15753 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15754 {
15755 let mut range = range.to_point(buffer);
15756 range.start.column = 0;
15757 range.end.column = buffer.line_len(range.end.row);
15758 new_selections_by_buffer
15759 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15760 .or_insert(Vec::new())
15761 .push(range)
15762 }
15763 }
15764
15765 let proposed_changes_buffers = new_selections_by_buffer
15766 .into_iter()
15767 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15768 .collect::<Vec<_>>();
15769 let proposed_changes_editor = cx.new(|cx| {
15770 ProposedChangesEditor::new(
15771 "Proposed changes",
15772 proposed_changes_buffers,
15773 self.project.clone(),
15774 window,
15775 cx,
15776 )
15777 });
15778
15779 window.defer(cx, move |window, cx| {
15780 workspace.update(cx, |workspace, cx| {
15781 workspace.active_pane().update(cx, |pane, cx| {
15782 pane.add_item(
15783 Box::new(proposed_changes_editor),
15784 true,
15785 true,
15786 None,
15787 window,
15788 cx,
15789 );
15790 });
15791 });
15792 });
15793 }
15794
15795 pub fn open_excerpts_in_split(
15796 &mut self,
15797 _: &OpenExcerptsSplit,
15798 window: &mut Window,
15799 cx: &mut Context<Self>,
15800 ) {
15801 self.open_excerpts_common(None, true, window, cx)
15802 }
15803
15804 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15805 self.open_excerpts_common(None, false, window, cx)
15806 }
15807
15808 fn open_excerpts_common(
15809 &mut self,
15810 jump_data: Option<JumpData>,
15811 split: bool,
15812 window: &mut Window,
15813 cx: &mut Context<Self>,
15814 ) {
15815 let Some(workspace) = self.workspace() else {
15816 cx.propagate();
15817 return;
15818 };
15819
15820 if self.buffer.read(cx).is_singleton() {
15821 cx.propagate();
15822 return;
15823 }
15824
15825 let mut new_selections_by_buffer = HashMap::default();
15826 match &jump_data {
15827 Some(JumpData::MultiBufferPoint {
15828 excerpt_id,
15829 position,
15830 anchor,
15831 line_offset_from_top,
15832 }) => {
15833 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15834 if let Some(buffer) = multi_buffer_snapshot
15835 .buffer_id_for_excerpt(*excerpt_id)
15836 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15837 {
15838 let buffer_snapshot = buffer.read(cx).snapshot();
15839 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15840 language::ToPoint::to_point(anchor, &buffer_snapshot)
15841 } else {
15842 buffer_snapshot.clip_point(*position, Bias::Left)
15843 };
15844 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15845 new_selections_by_buffer.insert(
15846 buffer,
15847 (
15848 vec![jump_to_offset..jump_to_offset],
15849 Some(*line_offset_from_top),
15850 ),
15851 );
15852 }
15853 }
15854 Some(JumpData::MultiBufferRow {
15855 row,
15856 line_offset_from_top,
15857 }) => {
15858 let point = MultiBufferPoint::new(row.0, 0);
15859 if let Some((buffer, buffer_point, _)) =
15860 self.buffer.read(cx).point_to_buffer_point(point, cx)
15861 {
15862 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15863 new_selections_by_buffer
15864 .entry(buffer)
15865 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15866 .0
15867 .push(buffer_offset..buffer_offset)
15868 }
15869 }
15870 None => {
15871 let selections = self.selections.all::<usize>(cx);
15872 let multi_buffer = self.buffer.read(cx);
15873 for selection in selections {
15874 for (snapshot, range, _, anchor) in multi_buffer
15875 .snapshot(cx)
15876 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15877 {
15878 if let Some(anchor) = anchor {
15879 // selection is in a deleted hunk
15880 let Some(buffer_id) = anchor.buffer_id else {
15881 continue;
15882 };
15883 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15884 continue;
15885 };
15886 let offset = text::ToOffset::to_offset(
15887 &anchor.text_anchor,
15888 &buffer_handle.read(cx).snapshot(),
15889 );
15890 let range = offset..offset;
15891 new_selections_by_buffer
15892 .entry(buffer_handle)
15893 .or_insert((Vec::new(), None))
15894 .0
15895 .push(range)
15896 } else {
15897 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15898 else {
15899 continue;
15900 };
15901 new_selections_by_buffer
15902 .entry(buffer_handle)
15903 .or_insert((Vec::new(), None))
15904 .0
15905 .push(range)
15906 }
15907 }
15908 }
15909 }
15910 }
15911
15912 if new_selections_by_buffer.is_empty() {
15913 return;
15914 }
15915
15916 // We defer the pane interaction because we ourselves are a workspace item
15917 // and activating a new item causes the pane to call a method on us reentrantly,
15918 // which panics if we're on the stack.
15919 window.defer(cx, move |window, cx| {
15920 workspace.update(cx, |workspace, cx| {
15921 let pane = if split {
15922 workspace.adjacent_pane(window, cx)
15923 } else {
15924 workspace.active_pane().clone()
15925 };
15926
15927 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15928 let editor = buffer
15929 .read(cx)
15930 .file()
15931 .is_none()
15932 .then(|| {
15933 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15934 // so `workspace.open_project_item` will never find them, always opening a new editor.
15935 // Instead, we try to activate the existing editor in the pane first.
15936 let (editor, pane_item_index) =
15937 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15938 let editor = item.downcast::<Editor>()?;
15939 let singleton_buffer =
15940 editor.read(cx).buffer().read(cx).as_singleton()?;
15941 if singleton_buffer == buffer {
15942 Some((editor, i))
15943 } else {
15944 None
15945 }
15946 })?;
15947 pane.update(cx, |pane, cx| {
15948 pane.activate_item(pane_item_index, true, true, window, cx)
15949 });
15950 Some(editor)
15951 })
15952 .flatten()
15953 .unwrap_or_else(|| {
15954 workspace.open_project_item::<Self>(
15955 pane.clone(),
15956 buffer,
15957 true,
15958 true,
15959 window,
15960 cx,
15961 )
15962 });
15963
15964 editor.update(cx, |editor, cx| {
15965 let autoscroll = match scroll_offset {
15966 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15967 None => Autoscroll::newest(),
15968 };
15969 let nav_history = editor.nav_history.take();
15970 editor.change_selections(Some(autoscroll), window, cx, |s| {
15971 s.select_ranges(ranges);
15972 });
15973 editor.nav_history = nav_history;
15974 });
15975 }
15976 })
15977 });
15978 }
15979
15980 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
15981 let snapshot = self.buffer.read(cx).read(cx);
15982 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
15983 Some(
15984 ranges
15985 .iter()
15986 .map(move |range| {
15987 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
15988 })
15989 .collect(),
15990 )
15991 }
15992
15993 fn selection_replacement_ranges(
15994 &self,
15995 range: Range<OffsetUtf16>,
15996 cx: &mut App,
15997 ) -> Vec<Range<OffsetUtf16>> {
15998 let selections = self.selections.all::<OffsetUtf16>(cx);
15999 let newest_selection = selections
16000 .iter()
16001 .max_by_key(|selection| selection.id)
16002 .unwrap();
16003 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16004 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16005 let snapshot = self.buffer.read(cx).read(cx);
16006 selections
16007 .into_iter()
16008 .map(|mut selection| {
16009 selection.start.0 =
16010 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16011 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16012 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16013 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16014 })
16015 .collect()
16016 }
16017
16018 fn report_editor_event(
16019 &self,
16020 event_type: &'static str,
16021 file_extension: Option<String>,
16022 cx: &App,
16023 ) {
16024 if cfg!(any(test, feature = "test-support")) {
16025 return;
16026 }
16027
16028 let Some(project) = &self.project else { return };
16029
16030 // If None, we are in a file without an extension
16031 let file = self
16032 .buffer
16033 .read(cx)
16034 .as_singleton()
16035 .and_then(|b| b.read(cx).file());
16036 let file_extension = file_extension.or(file
16037 .as_ref()
16038 .and_then(|file| Path::new(file.file_name(cx)).extension())
16039 .and_then(|e| e.to_str())
16040 .map(|a| a.to_string()));
16041
16042 let vim_mode = cx
16043 .global::<SettingsStore>()
16044 .raw_user_settings()
16045 .get("vim_mode")
16046 == Some(&serde_json::Value::Bool(true));
16047
16048 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16049 let copilot_enabled = edit_predictions_provider
16050 == language::language_settings::EditPredictionProvider::Copilot;
16051 let copilot_enabled_for_language = self
16052 .buffer
16053 .read(cx)
16054 .language_settings(cx)
16055 .show_edit_predictions;
16056
16057 let project = project.read(cx);
16058 telemetry::event!(
16059 event_type,
16060 file_extension,
16061 vim_mode,
16062 copilot_enabled,
16063 copilot_enabled_for_language,
16064 edit_predictions_provider,
16065 is_via_ssh = project.is_via_ssh(),
16066 );
16067 }
16068
16069 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16070 /// with each line being an array of {text, highlight} objects.
16071 fn copy_highlight_json(
16072 &mut self,
16073 _: &CopyHighlightJson,
16074 window: &mut Window,
16075 cx: &mut Context<Self>,
16076 ) {
16077 #[derive(Serialize)]
16078 struct Chunk<'a> {
16079 text: String,
16080 highlight: Option<&'a str>,
16081 }
16082
16083 let snapshot = self.buffer.read(cx).snapshot(cx);
16084 let range = self
16085 .selected_text_range(false, window, cx)
16086 .and_then(|selection| {
16087 if selection.range.is_empty() {
16088 None
16089 } else {
16090 Some(selection.range)
16091 }
16092 })
16093 .unwrap_or_else(|| 0..snapshot.len());
16094
16095 let chunks = snapshot.chunks(range, true);
16096 let mut lines = Vec::new();
16097 let mut line: VecDeque<Chunk> = VecDeque::new();
16098
16099 let Some(style) = self.style.as_ref() else {
16100 return;
16101 };
16102
16103 for chunk in chunks {
16104 let highlight = chunk
16105 .syntax_highlight_id
16106 .and_then(|id| id.name(&style.syntax));
16107 let mut chunk_lines = chunk.text.split('\n').peekable();
16108 while let Some(text) = chunk_lines.next() {
16109 let mut merged_with_last_token = false;
16110 if let Some(last_token) = line.back_mut() {
16111 if last_token.highlight == highlight {
16112 last_token.text.push_str(text);
16113 merged_with_last_token = true;
16114 }
16115 }
16116
16117 if !merged_with_last_token {
16118 line.push_back(Chunk {
16119 text: text.into(),
16120 highlight,
16121 });
16122 }
16123
16124 if chunk_lines.peek().is_some() {
16125 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16126 line.pop_front();
16127 }
16128 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16129 line.pop_back();
16130 }
16131
16132 lines.push(mem::take(&mut line));
16133 }
16134 }
16135 }
16136
16137 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
16138 return;
16139 };
16140 cx.write_to_clipboard(ClipboardItem::new_string(lines));
16141 }
16142
16143 pub fn open_context_menu(
16144 &mut self,
16145 _: &OpenContextMenu,
16146 window: &mut Window,
16147 cx: &mut Context<Self>,
16148 ) {
16149 self.request_autoscroll(Autoscroll::newest(), cx);
16150 let position = self.selections.newest_display(cx).start;
16151 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
16152 }
16153
16154 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
16155 &self.inlay_hint_cache
16156 }
16157
16158 pub fn replay_insert_event(
16159 &mut self,
16160 text: &str,
16161 relative_utf16_range: Option<Range<isize>>,
16162 window: &mut Window,
16163 cx: &mut Context<Self>,
16164 ) {
16165 if !self.input_enabled {
16166 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16167 return;
16168 }
16169 if let Some(relative_utf16_range) = relative_utf16_range {
16170 let selections = self.selections.all::<OffsetUtf16>(cx);
16171 self.change_selections(None, window, cx, |s| {
16172 let new_ranges = selections.into_iter().map(|range| {
16173 let start = OffsetUtf16(
16174 range
16175 .head()
16176 .0
16177 .saturating_add_signed(relative_utf16_range.start),
16178 );
16179 let end = OffsetUtf16(
16180 range
16181 .head()
16182 .0
16183 .saturating_add_signed(relative_utf16_range.end),
16184 );
16185 start..end
16186 });
16187 s.select_ranges(new_ranges);
16188 });
16189 }
16190
16191 self.handle_input(text, window, cx);
16192 }
16193
16194 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16195 let Some(provider) = self.semantics_provider.as_ref() else {
16196 return false;
16197 };
16198
16199 let mut supports = false;
16200 self.buffer().update(cx, |this, cx| {
16201 this.for_each_buffer(|buffer| {
16202 supports |= provider.supports_inlay_hints(buffer, cx);
16203 });
16204 });
16205
16206 supports
16207 }
16208
16209 pub fn is_focused(&self, window: &Window) -> bool {
16210 self.focus_handle.is_focused(window)
16211 }
16212
16213 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16214 cx.emit(EditorEvent::Focused);
16215
16216 if let Some(descendant) = self
16217 .last_focused_descendant
16218 .take()
16219 .and_then(|descendant| descendant.upgrade())
16220 {
16221 window.focus(&descendant);
16222 } else {
16223 if let Some(blame) = self.blame.as_ref() {
16224 blame.update(cx, GitBlame::focus)
16225 }
16226
16227 self.blink_manager.update(cx, BlinkManager::enable);
16228 self.show_cursor_names(window, cx);
16229 self.buffer.update(cx, |buffer, cx| {
16230 buffer.finalize_last_transaction(cx);
16231 if self.leader_peer_id.is_none() {
16232 buffer.set_active_selections(
16233 &self.selections.disjoint_anchors(),
16234 self.selections.line_mode,
16235 self.cursor_shape,
16236 cx,
16237 );
16238 }
16239 });
16240 }
16241 }
16242
16243 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16244 cx.emit(EditorEvent::FocusedIn)
16245 }
16246
16247 fn handle_focus_out(
16248 &mut self,
16249 event: FocusOutEvent,
16250 _window: &mut Window,
16251 cx: &mut Context<Self>,
16252 ) {
16253 if event.blurred != self.focus_handle {
16254 self.last_focused_descendant = Some(event.blurred);
16255 }
16256 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16257 }
16258
16259 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16260 self.blink_manager.update(cx, BlinkManager::disable);
16261 self.buffer
16262 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16263
16264 if let Some(blame) = self.blame.as_ref() {
16265 blame.update(cx, GitBlame::blur)
16266 }
16267 if !self.hover_state.focused(window, cx) {
16268 hide_hover(self, cx);
16269 }
16270 if !self
16271 .context_menu
16272 .borrow()
16273 .as_ref()
16274 .is_some_and(|context_menu| context_menu.focused(window, cx))
16275 {
16276 self.hide_context_menu(window, cx);
16277 }
16278 self.discard_inline_completion(false, cx);
16279 cx.emit(EditorEvent::Blurred);
16280 cx.notify();
16281 }
16282
16283 pub fn register_action<A: Action>(
16284 &mut self,
16285 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16286 ) -> Subscription {
16287 let id = self.next_editor_action_id.post_inc();
16288 let listener = Arc::new(listener);
16289 self.editor_actions.borrow_mut().insert(
16290 id,
16291 Box::new(move |window, _| {
16292 let listener = listener.clone();
16293 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16294 let action = action.downcast_ref().unwrap();
16295 if phase == DispatchPhase::Bubble {
16296 listener(action, window, cx)
16297 }
16298 })
16299 }),
16300 );
16301
16302 let editor_actions = self.editor_actions.clone();
16303 Subscription::new(move || {
16304 editor_actions.borrow_mut().remove(&id);
16305 })
16306 }
16307
16308 pub fn file_header_size(&self) -> u32 {
16309 FILE_HEADER_HEIGHT
16310 }
16311
16312 pub fn restore(
16313 &mut self,
16314 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16315 window: &mut Window,
16316 cx: &mut Context<Self>,
16317 ) {
16318 let workspace = self.workspace();
16319 let project = self.project.as_ref();
16320 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16321 let mut tasks = Vec::new();
16322 for (buffer_id, changes) in revert_changes {
16323 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16324 buffer.update(cx, |buffer, cx| {
16325 buffer.edit(
16326 changes
16327 .into_iter()
16328 .map(|(range, text)| (range, text.to_string())),
16329 None,
16330 cx,
16331 );
16332 });
16333
16334 if let Some(project) =
16335 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16336 {
16337 project.update(cx, |project, cx| {
16338 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16339 })
16340 }
16341 }
16342 }
16343 tasks
16344 });
16345 cx.spawn_in(window, |_, mut cx| async move {
16346 for (buffer, task) in save_tasks {
16347 let result = task.await;
16348 if result.is_err() {
16349 let Some(path) = buffer
16350 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
16351 .ok()
16352 else {
16353 continue;
16354 };
16355 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
16356 let Some(task) = cx
16357 .update_window_entity(&workspace, |workspace, window, cx| {
16358 workspace
16359 .open_path_preview(path, None, false, false, false, window, cx)
16360 })
16361 .ok()
16362 else {
16363 continue;
16364 };
16365 task.await.log_err();
16366 }
16367 }
16368 }
16369 })
16370 .detach();
16371 self.change_selections(None, window, cx, |selections| selections.refresh());
16372 }
16373
16374 pub fn to_pixel_point(
16375 &self,
16376 source: multi_buffer::Anchor,
16377 editor_snapshot: &EditorSnapshot,
16378 window: &mut Window,
16379 ) -> Option<gpui::Point<Pixels>> {
16380 let source_point = source.to_display_point(editor_snapshot);
16381 self.display_to_pixel_point(source_point, editor_snapshot, window)
16382 }
16383
16384 pub fn display_to_pixel_point(
16385 &self,
16386 source: DisplayPoint,
16387 editor_snapshot: &EditorSnapshot,
16388 window: &mut Window,
16389 ) -> Option<gpui::Point<Pixels>> {
16390 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16391 let text_layout_details = self.text_layout_details(window);
16392 let scroll_top = text_layout_details
16393 .scroll_anchor
16394 .scroll_position(editor_snapshot)
16395 .y;
16396
16397 if source.row().as_f32() < scroll_top.floor() {
16398 return None;
16399 }
16400 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16401 let source_y = line_height * (source.row().as_f32() - scroll_top);
16402 Some(gpui::Point::new(source_x, source_y))
16403 }
16404
16405 pub fn has_visible_completions_menu(&self) -> bool {
16406 !self.edit_prediction_preview_is_active()
16407 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16408 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16409 })
16410 }
16411
16412 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16413 self.addons
16414 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16415 }
16416
16417 pub fn unregister_addon<T: Addon>(&mut self) {
16418 self.addons.remove(&std::any::TypeId::of::<T>());
16419 }
16420
16421 pub fn addon<T: Addon>(&self) -> Option<&T> {
16422 let type_id = std::any::TypeId::of::<T>();
16423 self.addons
16424 .get(&type_id)
16425 .and_then(|item| item.to_any().downcast_ref::<T>())
16426 }
16427
16428 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16429 let text_layout_details = self.text_layout_details(window);
16430 let style = &text_layout_details.editor_style;
16431 let font_id = window.text_system().resolve_font(&style.text.font());
16432 let font_size = style.text.font_size.to_pixels(window.rem_size());
16433 let line_height = style.text.line_height_in_pixels(window.rem_size());
16434 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16435
16436 gpui::Size::new(em_width, line_height)
16437 }
16438
16439 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16440 self.load_diff_task.clone()
16441 }
16442
16443 fn read_selections_from_db(
16444 &mut self,
16445 item_id: u64,
16446 workspace_id: WorkspaceId,
16447 window: &mut Window,
16448 cx: &mut Context<Editor>,
16449 ) {
16450 if !self.is_singleton(cx)
16451 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16452 {
16453 return;
16454 }
16455 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16456 return;
16457 };
16458 if selections.is_empty() {
16459 return;
16460 }
16461
16462 let snapshot = self.buffer.read(cx).snapshot(cx);
16463 self.change_selections(None, window, cx, |s| {
16464 s.select_ranges(selections.into_iter().map(|(start, end)| {
16465 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16466 }));
16467 });
16468 }
16469}
16470
16471fn insert_extra_newline_brackets(
16472 buffer: &MultiBufferSnapshot,
16473 range: Range<usize>,
16474 language: &language::LanguageScope,
16475) -> bool {
16476 let leading_whitespace_len = buffer
16477 .reversed_chars_at(range.start)
16478 .take_while(|c| c.is_whitespace() && *c != '\n')
16479 .map(|c| c.len_utf8())
16480 .sum::<usize>();
16481 let trailing_whitespace_len = buffer
16482 .chars_at(range.end)
16483 .take_while(|c| c.is_whitespace() && *c != '\n')
16484 .map(|c| c.len_utf8())
16485 .sum::<usize>();
16486 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16487
16488 language.brackets().any(|(pair, enabled)| {
16489 let pair_start = pair.start.trim_end();
16490 let pair_end = pair.end.trim_start();
16491
16492 enabled
16493 && pair.newline
16494 && buffer.contains_str_at(range.end, pair_end)
16495 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16496 })
16497}
16498
16499fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16500 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16501 [(buffer, range, _)] => (*buffer, range.clone()),
16502 _ => return false,
16503 };
16504 let pair = {
16505 let mut result: Option<BracketMatch> = None;
16506
16507 for pair in buffer
16508 .all_bracket_ranges(range.clone())
16509 .filter(move |pair| {
16510 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16511 })
16512 {
16513 let len = pair.close_range.end - pair.open_range.start;
16514
16515 if let Some(existing) = &result {
16516 let existing_len = existing.close_range.end - existing.open_range.start;
16517 if len > existing_len {
16518 continue;
16519 }
16520 }
16521
16522 result = Some(pair);
16523 }
16524
16525 result
16526 };
16527 let Some(pair) = pair else {
16528 return false;
16529 };
16530 pair.newline_only
16531 && buffer
16532 .chars_for_range(pair.open_range.end..range.start)
16533 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16534 .all(|c| c.is_whitespace() && c != '\n')
16535}
16536
16537fn get_uncommitted_diff_for_buffer(
16538 project: &Entity<Project>,
16539 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16540 buffer: Entity<MultiBuffer>,
16541 cx: &mut App,
16542) -> Task<()> {
16543 let mut tasks = Vec::new();
16544 project.update(cx, |project, cx| {
16545 for buffer in buffers {
16546 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16547 }
16548 });
16549 cx.spawn(|mut cx| async move {
16550 let diffs = future::join_all(tasks).await;
16551 buffer
16552 .update(&mut cx, |buffer, cx| {
16553 for diff in diffs.into_iter().flatten() {
16554 buffer.add_diff(diff, cx);
16555 }
16556 })
16557 .ok();
16558 })
16559}
16560
16561fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16562 let tab_size = tab_size.get() as usize;
16563 let mut width = offset;
16564
16565 for ch in text.chars() {
16566 width += if ch == '\t' {
16567 tab_size - (width % tab_size)
16568 } else {
16569 1
16570 };
16571 }
16572
16573 width - offset
16574}
16575
16576#[cfg(test)]
16577mod tests {
16578 use super::*;
16579
16580 #[test]
16581 fn test_string_size_with_expanded_tabs() {
16582 let nz = |val| NonZeroU32::new(val).unwrap();
16583 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16584 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16585 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16586 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16587 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16588 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16589 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16590 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16591 }
16592}
16593
16594/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16595struct WordBreakingTokenizer<'a> {
16596 input: &'a str,
16597}
16598
16599impl<'a> WordBreakingTokenizer<'a> {
16600 fn new(input: &'a str) -> Self {
16601 Self { input }
16602 }
16603}
16604
16605fn is_char_ideographic(ch: char) -> bool {
16606 use unicode_script::Script::*;
16607 use unicode_script::UnicodeScript;
16608 matches!(ch.script(), Han | Tangut | Yi)
16609}
16610
16611fn is_grapheme_ideographic(text: &str) -> bool {
16612 text.chars().any(is_char_ideographic)
16613}
16614
16615fn is_grapheme_whitespace(text: &str) -> bool {
16616 text.chars().any(|x| x.is_whitespace())
16617}
16618
16619fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16620 text.chars().next().map_or(false, |ch| {
16621 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16622 })
16623}
16624
16625#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16626struct WordBreakToken<'a> {
16627 token: &'a str,
16628 grapheme_len: usize,
16629 is_whitespace: bool,
16630}
16631
16632impl<'a> Iterator for WordBreakingTokenizer<'a> {
16633 /// Yields a span, the count of graphemes in the token, and whether it was
16634 /// whitespace. Note that it also breaks at word boundaries.
16635 type Item = WordBreakToken<'a>;
16636
16637 fn next(&mut self) -> Option<Self::Item> {
16638 use unicode_segmentation::UnicodeSegmentation;
16639 if self.input.is_empty() {
16640 return None;
16641 }
16642
16643 let mut iter = self.input.graphemes(true).peekable();
16644 let mut offset = 0;
16645 let mut graphemes = 0;
16646 if let Some(first_grapheme) = iter.next() {
16647 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16648 offset += first_grapheme.len();
16649 graphemes += 1;
16650 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16651 if let Some(grapheme) = iter.peek().copied() {
16652 if should_stay_with_preceding_ideograph(grapheme) {
16653 offset += grapheme.len();
16654 graphemes += 1;
16655 }
16656 }
16657 } else {
16658 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16659 let mut next_word_bound = words.peek().copied();
16660 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16661 next_word_bound = words.next();
16662 }
16663 while let Some(grapheme) = iter.peek().copied() {
16664 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16665 break;
16666 };
16667 if is_grapheme_whitespace(grapheme) != is_whitespace {
16668 break;
16669 };
16670 offset += grapheme.len();
16671 graphemes += 1;
16672 iter.next();
16673 }
16674 }
16675 let token = &self.input[..offset];
16676 self.input = &self.input[offset..];
16677 if is_whitespace {
16678 Some(WordBreakToken {
16679 token: " ",
16680 grapheme_len: 1,
16681 is_whitespace: true,
16682 })
16683 } else {
16684 Some(WordBreakToken {
16685 token,
16686 grapheme_len: graphemes,
16687 is_whitespace: false,
16688 })
16689 }
16690 } else {
16691 None
16692 }
16693 }
16694}
16695
16696#[test]
16697fn test_word_breaking_tokenizer() {
16698 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16699 ("", &[]),
16700 (" ", &[(" ", 1, true)]),
16701 ("Ʒ", &[("Ʒ", 1, false)]),
16702 ("Ǽ", &[("Ǽ", 1, false)]),
16703 ("⋑", &[("⋑", 1, false)]),
16704 ("⋑⋑", &[("⋑⋑", 2, false)]),
16705 (
16706 "原理,进而",
16707 &[
16708 ("原", 1, false),
16709 ("理,", 2, false),
16710 ("进", 1, false),
16711 ("而", 1, false),
16712 ],
16713 ),
16714 (
16715 "hello world",
16716 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16717 ),
16718 (
16719 "hello, world",
16720 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16721 ),
16722 (
16723 " hello world",
16724 &[
16725 (" ", 1, true),
16726 ("hello", 5, false),
16727 (" ", 1, true),
16728 ("world", 5, false),
16729 ],
16730 ),
16731 (
16732 "这是什么 \n 钢笔",
16733 &[
16734 ("这", 1, false),
16735 ("是", 1, false),
16736 ("什", 1, false),
16737 ("么", 1, false),
16738 (" ", 1, true),
16739 ("钢", 1, false),
16740 ("笔", 1, false),
16741 ],
16742 ),
16743 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16744 ];
16745
16746 for (input, result) in tests {
16747 assert_eq!(
16748 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16749 result
16750 .iter()
16751 .copied()
16752 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16753 token,
16754 grapheme_len,
16755 is_whitespace,
16756 })
16757 .collect::<Vec<_>>()
16758 );
16759 }
16760}
16761
16762fn wrap_with_prefix(
16763 line_prefix: String,
16764 unwrapped_text: String,
16765 wrap_column: usize,
16766 tab_size: NonZeroU32,
16767) -> String {
16768 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16769 let mut wrapped_text = String::new();
16770 let mut current_line = line_prefix.clone();
16771
16772 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16773 let mut current_line_len = line_prefix_len;
16774 for WordBreakToken {
16775 token,
16776 grapheme_len,
16777 is_whitespace,
16778 } in tokenizer
16779 {
16780 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16781 wrapped_text.push_str(current_line.trim_end());
16782 wrapped_text.push('\n');
16783 current_line.truncate(line_prefix.len());
16784 current_line_len = line_prefix_len;
16785 if !is_whitespace {
16786 current_line.push_str(token);
16787 current_line_len += grapheme_len;
16788 }
16789 } else if !is_whitespace {
16790 current_line.push_str(token);
16791 current_line_len += grapheme_len;
16792 } else if current_line_len != line_prefix_len {
16793 current_line.push(' ');
16794 current_line_len += 1;
16795 }
16796 }
16797
16798 if !current_line.is_empty() {
16799 wrapped_text.push_str(¤t_line);
16800 }
16801 wrapped_text
16802}
16803
16804#[test]
16805fn test_wrap_with_prefix() {
16806 assert_eq!(
16807 wrap_with_prefix(
16808 "# ".to_string(),
16809 "abcdefg".to_string(),
16810 4,
16811 NonZeroU32::new(4).unwrap()
16812 ),
16813 "# abcdefg"
16814 );
16815 assert_eq!(
16816 wrap_with_prefix(
16817 "".to_string(),
16818 "\thello world".to_string(),
16819 8,
16820 NonZeroU32::new(4).unwrap()
16821 ),
16822 "hello\nworld"
16823 );
16824 assert_eq!(
16825 wrap_with_prefix(
16826 "// ".to_string(),
16827 "xx \nyy zz aa bb cc".to_string(),
16828 12,
16829 NonZeroU32::new(4).unwrap()
16830 ),
16831 "// xx yy zz\n// aa bb cc"
16832 );
16833 assert_eq!(
16834 wrap_with_prefix(
16835 String::new(),
16836 "这是什么 \n 钢笔".to_string(),
16837 3,
16838 NonZeroU32::new(4).unwrap()
16839 ),
16840 "这是什\n么 钢\n笔"
16841 );
16842}
16843
16844pub trait CollaborationHub {
16845 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16846 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16847 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16848}
16849
16850impl CollaborationHub for Entity<Project> {
16851 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16852 self.read(cx).collaborators()
16853 }
16854
16855 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16856 self.read(cx).user_store().read(cx).participant_indices()
16857 }
16858
16859 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16860 let this = self.read(cx);
16861 let user_ids = this.collaborators().values().map(|c| c.user_id);
16862 this.user_store().read_with(cx, |user_store, cx| {
16863 user_store.participant_names(user_ids, cx)
16864 })
16865 }
16866}
16867
16868pub trait SemanticsProvider {
16869 fn hover(
16870 &self,
16871 buffer: &Entity<Buffer>,
16872 position: text::Anchor,
16873 cx: &mut App,
16874 ) -> Option<Task<Vec<project::Hover>>>;
16875
16876 fn inlay_hints(
16877 &self,
16878 buffer_handle: Entity<Buffer>,
16879 range: Range<text::Anchor>,
16880 cx: &mut App,
16881 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16882
16883 fn resolve_inlay_hint(
16884 &self,
16885 hint: InlayHint,
16886 buffer_handle: Entity<Buffer>,
16887 server_id: LanguageServerId,
16888 cx: &mut App,
16889 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16890
16891 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16892
16893 fn document_highlights(
16894 &self,
16895 buffer: &Entity<Buffer>,
16896 position: text::Anchor,
16897 cx: &mut App,
16898 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16899
16900 fn definitions(
16901 &self,
16902 buffer: &Entity<Buffer>,
16903 position: text::Anchor,
16904 kind: GotoDefinitionKind,
16905 cx: &mut App,
16906 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16907
16908 fn range_for_rename(
16909 &self,
16910 buffer: &Entity<Buffer>,
16911 position: text::Anchor,
16912 cx: &mut App,
16913 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16914
16915 fn perform_rename(
16916 &self,
16917 buffer: &Entity<Buffer>,
16918 position: text::Anchor,
16919 new_name: String,
16920 cx: &mut App,
16921 ) -> Option<Task<Result<ProjectTransaction>>>;
16922}
16923
16924pub trait CompletionProvider {
16925 fn completions(
16926 &self,
16927 buffer: &Entity<Buffer>,
16928 buffer_position: text::Anchor,
16929 trigger: CompletionContext,
16930 window: &mut Window,
16931 cx: &mut Context<Editor>,
16932 ) -> Task<Result<Vec<Completion>>>;
16933
16934 fn resolve_completions(
16935 &self,
16936 buffer: Entity<Buffer>,
16937 completion_indices: Vec<usize>,
16938 completions: Rc<RefCell<Box<[Completion]>>>,
16939 cx: &mut Context<Editor>,
16940 ) -> Task<Result<bool>>;
16941
16942 fn apply_additional_edits_for_completion(
16943 &self,
16944 _buffer: Entity<Buffer>,
16945 _completions: Rc<RefCell<Box<[Completion]>>>,
16946 _completion_index: usize,
16947 _push_to_history: bool,
16948 _cx: &mut Context<Editor>,
16949 ) -> Task<Result<Option<language::Transaction>>> {
16950 Task::ready(Ok(None))
16951 }
16952
16953 fn is_completion_trigger(
16954 &self,
16955 buffer: &Entity<Buffer>,
16956 position: language::Anchor,
16957 text: &str,
16958 trigger_in_words: bool,
16959 cx: &mut Context<Editor>,
16960 ) -> bool;
16961
16962 fn sort_completions(&self) -> bool {
16963 true
16964 }
16965}
16966
16967pub trait CodeActionProvider {
16968 fn id(&self) -> Arc<str>;
16969
16970 fn code_actions(
16971 &self,
16972 buffer: &Entity<Buffer>,
16973 range: Range<text::Anchor>,
16974 window: &mut Window,
16975 cx: &mut App,
16976 ) -> Task<Result<Vec<CodeAction>>>;
16977
16978 fn apply_code_action(
16979 &self,
16980 buffer_handle: Entity<Buffer>,
16981 action: CodeAction,
16982 excerpt_id: ExcerptId,
16983 push_to_history: bool,
16984 window: &mut Window,
16985 cx: &mut App,
16986 ) -> Task<Result<ProjectTransaction>>;
16987}
16988
16989impl CodeActionProvider for Entity<Project> {
16990 fn id(&self) -> Arc<str> {
16991 "project".into()
16992 }
16993
16994 fn code_actions(
16995 &self,
16996 buffer: &Entity<Buffer>,
16997 range: Range<text::Anchor>,
16998 _window: &mut Window,
16999 cx: &mut App,
17000 ) -> Task<Result<Vec<CodeAction>>> {
17001 self.update(cx, |project, cx| {
17002 project.code_actions(buffer, range, None, cx)
17003 })
17004 }
17005
17006 fn apply_code_action(
17007 &self,
17008 buffer_handle: Entity<Buffer>,
17009 action: CodeAction,
17010 _excerpt_id: ExcerptId,
17011 push_to_history: bool,
17012 _window: &mut Window,
17013 cx: &mut App,
17014 ) -> Task<Result<ProjectTransaction>> {
17015 self.update(cx, |project, cx| {
17016 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17017 })
17018 }
17019}
17020
17021fn snippet_completions(
17022 project: &Project,
17023 buffer: &Entity<Buffer>,
17024 buffer_position: text::Anchor,
17025 cx: &mut App,
17026) -> Task<Result<Vec<Completion>>> {
17027 let language = buffer.read(cx).language_at(buffer_position);
17028 let language_name = language.as_ref().map(|language| language.lsp_id());
17029 let snippet_store = project.snippets().read(cx);
17030 let snippets = snippet_store.snippets_for(language_name, cx);
17031
17032 if snippets.is_empty() {
17033 return Task::ready(Ok(vec![]));
17034 }
17035 let snapshot = buffer.read(cx).text_snapshot();
17036 let chars: String = snapshot
17037 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17038 .collect();
17039
17040 let scope = language.map(|language| language.default_scope());
17041 let executor = cx.background_executor().clone();
17042
17043 cx.background_spawn(async move {
17044 let classifier = CharClassifier::new(scope).for_completion(true);
17045 let mut last_word = chars
17046 .chars()
17047 .take_while(|c| classifier.is_word(*c))
17048 .collect::<String>();
17049 last_word = last_word.chars().rev().collect();
17050
17051 if last_word.is_empty() {
17052 return Ok(vec![]);
17053 }
17054
17055 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
17056 let to_lsp = |point: &text::Anchor| {
17057 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
17058 point_to_lsp(end)
17059 };
17060 let lsp_end = to_lsp(&buffer_position);
17061
17062 let candidates = snippets
17063 .iter()
17064 .enumerate()
17065 .flat_map(|(ix, snippet)| {
17066 snippet
17067 .prefix
17068 .iter()
17069 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
17070 })
17071 .collect::<Vec<StringMatchCandidate>>();
17072
17073 let mut matches = fuzzy::match_strings(
17074 &candidates,
17075 &last_word,
17076 last_word.chars().any(|c| c.is_uppercase()),
17077 100,
17078 &Default::default(),
17079 executor,
17080 )
17081 .await;
17082
17083 // Remove all candidates where the query's start does not match the start of any word in the candidate
17084 if let Some(query_start) = last_word.chars().next() {
17085 matches.retain(|string_match| {
17086 split_words(&string_match.string).any(|word| {
17087 // Check that the first codepoint of the word as lowercase matches the first
17088 // codepoint of the query as lowercase
17089 word.chars()
17090 .flat_map(|codepoint| codepoint.to_lowercase())
17091 .zip(query_start.to_lowercase())
17092 .all(|(word_cp, query_cp)| word_cp == query_cp)
17093 })
17094 });
17095 }
17096
17097 let matched_strings = matches
17098 .into_iter()
17099 .map(|m| m.string)
17100 .collect::<HashSet<_>>();
17101
17102 let result: Vec<Completion> = snippets
17103 .into_iter()
17104 .filter_map(|snippet| {
17105 let matching_prefix = snippet
17106 .prefix
17107 .iter()
17108 .find(|prefix| matched_strings.contains(*prefix))?;
17109 let start = as_offset - last_word.len();
17110 let start = snapshot.anchor_before(start);
17111 let range = start..buffer_position;
17112 let lsp_start = to_lsp(&start);
17113 let lsp_range = lsp::Range {
17114 start: lsp_start,
17115 end: lsp_end,
17116 };
17117 Some(Completion {
17118 old_range: range,
17119 new_text: snippet.body.clone(),
17120 source: CompletionSource::Lsp {
17121 server_id: LanguageServerId(usize::MAX),
17122 resolved: true,
17123 lsp_completion: Box::new(lsp::CompletionItem {
17124 label: snippet.prefix.first().unwrap().clone(),
17125 kind: Some(CompletionItemKind::SNIPPET),
17126 label_details: snippet.description.as_ref().map(|description| {
17127 lsp::CompletionItemLabelDetails {
17128 detail: Some(description.clone()),
17129 description: None,
17130 }
17131 }),
17132 insert_text_format: Some(InsertTextFormat::SNIPPET),
17133 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
17134 lsp::InsertReplaceEdit {
17135 new_text: snippet.body.clone(),
17136 insert: lsp_range,
17137 replace: lsp_range,
17138 },
17139 )),
17140 filter_text: Some(snippet.body.clone()),
17141 sort_text: Some(char::MAX.to_string()),
17142 ..lsp::CompletionItem::default()
17143 }),
17144 lsp_defaults: None,
17145 },
17146 label: CodeLabel {
17147 text: matching_prefix.clone(),
17148 runs: Vec::new(),
17149 filter_range: 0..matching_prefix.len(),
17150 },
17151 documentation: snippet
17152 .description
17153 .clone()
17154 .map(|description| CompletionDocumentation::SingleLine(description.into())),
17155 confirm: None,
17156 })
17157 })
17158 .collect();
17159
17160 Ok(result)
17161 })
17162}
17163
17164impl CompletionProvider for Entity<Project> {
17165 fn completions(
17166 &self,
17167 buffer: &Entity<Buffer>,
17168 buffer_position: text::Anchor,
17169 options: CompletionContext,
17170 _window: &mut Window,
17171 cx: &mut Context<Editor>,
17172 ) -> Task<Result<Vec<Completion>>> {
17173 self.update(cx, |project, cx| {
17174 let snippets = snippet_completions(project, buffer, buffer_position, cx);
17175 let project_completions = project.completions(buffer, buffer_position, options, cx);
17176 cx.background_spawn(async move {
17177 let mut completions = project_completions.await?;
17178 let snippets_completions = snippets.await?;
17179 completions.extend(snippets_completions);
17180 Ok(completions)
17181 })
17182 })
17183 }
17184
17185 fn resolve_completions(
17186 &self,
17187 buffer: Entity<Buffer>,
17188 completion_indices: Vec<usize>,
17189 completions: Rc<RefCell<Box<[Completion]>>>,
17190 cx: &mut Context<Editor>,
17191 ) -> Task<Result<bool>> {
17192 self.update(cx, |project, cx| {
17193 project.lsp_store().update(cx, |lsp_store, cx| {
17194 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
17195 })
17196 })
17197 }
17198
17199 fn apply_additional_edits_for_completion(
17200 &self,
17201 buffer: Entity<Buffer>,
17202 completions: Rc<RefCell<Box<[Completion]>>>,
17203 completion_index: usize,
17204 push_to_history: bool,
17205 cx: &mut Context<Editor>,
17206 ) -> Task<Result<Option<language::Transaction>>> {
17207 self.update(cx, |project, cx| {
17208 project.lsp_store().update(cx, |lsp_store, cx| {
17209 lsp_store.apply_additional_edits_for_completion(
17210 buffer,
17211 completions,
17212 completion_index,
17213 push_to_history,
17214 cx,
17215 )
17216 })
17217 })
17218 }
17219
17220 fn is_completion_trigger(
17221 &self,
17222 buffer: &Entity<Buffer>,
17223 position: language::Anchor,
17224 text: &str,
17225 trigger_in_words: bool,
17226 cx: &mut Context<Editor>,
17227 ) -> bool {
17228 let mut chars = text.chars();
17229 let char = if let Some(char) = chars.next() {
17230 char
17231 } else {
17232 return false;
17233 };
17234 if chars.next().is_some() {
17235 return false;
17236 }
17237
17238 let buffer = buffer.read(cx);
17239 let snapshot = buffer.snapshot();
17240 if !snapshot.settings_at(position, cx).show_completions_on_input {
17241 return false;
17242 }
17243 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17244 if trigger_in_words && classifier.is_word(char) {
17245 return true;
17246 }
17247
17248 buffer.completion_triggers().contains(text)
17249 }
17250}
17251
17252impl SemanticsProvider for Entity<Project> {
17253 fn hover(
17254 &self,
17255 buffer: &Entity<Buffer>,
17256 position: text::Anchor,
17257 cx: &mut App,
17258 ) -> Option<Task<Vec<project::Hover>>> {
17259 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17260 }
17261
17262 fn document_highlights(
17263 &self,
17264 buffer: &Entity<Buffer>,
17265 position: text::Anchor,
17266 cx: &mut App,
17267 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
17268 Some(self.update(cx, |project, cx| {
17269 project.document_highlights(buffer, position, cx)
17270 }))
17271 }
17272
17273 fn definitions(
17274 &self,
17275 buffer: &Entity<Buffer>,
17276 position: text::Anchor,
17277 kind: GotoDefinitionKind,
17278 cx: &mut App,
17279 ) -> Option<Task<Result<Vec<LocationLink>>>> {
17280 Some(self.update(cx, |project, cx| match kind {
17281 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
17282 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
17283 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
17284 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
17285 }))
17286 }
17287
17288 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
17289 // TODO: make this work for remote projects
17290 self.update(cx, |this, cx| {
17291 buffer.update(cx, |buffer, cx| {
17292 this.any_language_server_supports_inlay_hints(buffer, cx)
17293 })
17294 })
17295 }
17296
17297 fn inlay_hints(
17298 &self,
17299 buffer_handle: Entity<Buffer>,
17300 range: Range<text::Anchor>,
17301 cx: &mut App,
17302 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
17303 Some(self.update(cx, |project, cx| {
17304 project.inlay_hints(buffer_handle, range, cx)
17305 }))
17306 }
17307
17308 fn resolve_inlay_hint(
17309 &self,
17310 hint: InlayHint,
17311 buffer_handle: Entity<Buffer>,
17312 server_id: LanguageServerId,
17313 cx: &mut App,
17314 ) -> Option<Task<anyhow::Result<InlayHint>>> {
17315 Some(self.update(cx, |project, cx| {
17316 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
17317 }))
17318 }
17319
17320 fn range_for_rename(
17321 &self,
17322 buffer: &Entity<Buffer>,
17323 position: text::Anchor,
17324 cx: &mut App,
17325 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
17326 Some(self.update(cx, |project, cx| {
17327 let buffer = buffer.clone();
17328 let task = project.prepare_rename(buffer.clone(), position, cx);
17329 cx.spawn(|_, mut cx| async move {
17330 Ok(match task.await? {
17331 PrepareRenameResponse::Success(range) => Some(range),
17332 PrepareRenameResponse::InvalidPosition => None,
17333 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
17334 // Fallback on using TreeSitter info to determine identifier range
17335 buffer.update(&mut cx, |buffer, _| {
17336 let snapshot = buffer.snapshot();
17337 let (range, kind) = snapshot.surrounding_word(position);
17338 if kind != Some(CharKind::Word) {
17339 return None;
17340 }
17341 Some(
17342 snapshot.anchor_before(range.start)
17343 ..snapshot.anchor_after(range.end),
17344 )
17345 })?
17346 }
17347 })
17348 })
17349 }))
17350 }
17351
17352 fn perform_rename(
17353 &self,
17354 buffer: &Entity<Buffer>,
17355 position: text::Anchor,
17356 new_name: String,
17357 cx: &mut App,
17358 ) -> Option<Task<Result<ProjectTransaction>>> {
17359 Some(self.update(cx, |project, cx| {
17360 project.perform_rename(buffer.clone(), position, new_name, cx)
17361 }))
17362 }
17363}
17364
17365fn inlay_hint_settings(
17366 location: Anchor,
17367 snapshot: &MultiBufferSnapshot,
17368 cx: &mut Context<Editor>,
17369) -> InlayHintSettings {
17370 let file = snapshot.file_at(location);
17371 let language = snapshot.language_at(location).map(|l| l.name());
17372 language_settings(language, file, cx).inlay_hints
17373}
17374
17375fn consume_contiguous_rows(
17376 contiguous_row_selections: &mut Vec<Selection<Point>>,
17377 selection: &Selection<Point>,
17378 display_map: &DisplaySnapshot,
17379 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
17380) -> (MultiBufferRow, MultiBufferRow) {
17381 contiguous_row_selections.push(selection.clone());
17382 let start_row = MultiBufferRow(selection.start.row);
17383 let mut end_row = ending_row(selection, display_map);
17384
17385 while let Some(next_selection) = selections.peek() {
17386 if next_selection.start.row <= end_row.0 {
17387 end_row = ending_row(next_selection, display_map);
17388 contiguous_row_selections.push(selections.next().unwrap().clone());
17389 } else {
17390 break;
17391 }
17392 }
17393 (start_row, end_row)
17394}
17395
17396fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17397 if next_selection.end.column > 0 || next_selection.is_empty() {
17398 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17399 } else {
17400 MultiBufferRow(next_selection.end.row)
17401 }
17402}
17403
17404impl EditorSnapshot {
17405 pub fn remote_selections_in_range<'a>(
17406 &'a self,
17407 range: &'a Range<Anchor>,
17408 collaboration_hub: &dyn CollaborationHub,
17409 cx: &'a App,
17410 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17411 let participant_names = collaboration_hub.user_names(cx);
17412 let participant_indices = collaboration_hub.user_participant_indices(cx);
17413 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17414 let collaborators_by_replica_id = collaborators_by_peer_id
17415 .iter()
17416 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17417 .collect::<HashMap<_, _>>();
17418 self.buffer_snapshot
17419 .selections_in_range(range, false)
17420 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17421 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17422 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17423 let user_name = participant_names.get(&collaborator.user_id).cloned();
17424 Some(RemoteSelection {
17425 replica_id,
17426 selection,
17427 cursor_shape,
17428 line_mode,
17429 participant_index,
17430 peer_id: collaborator.peer_id,
17431 user_name,
17432 })
17433 })
17434 }
17435
17436 pub fn hunks_for_ranges(
17437 &self,
17438 ranges: impl IntoIterator<Item = Range<Point>>,
17439 ) -> Vec<MultiBufferDiffHunk> {
17440 let mut hunks = Vec::new();
17441 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17442 HashMap::default();
17443 for query_range in ranges {
17444 let query_rows =
17445 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17446 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17447 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17448 ) {
17449 // Include deleted hunks that are adjacent to the query range, because
17450 // otherwise they would be missed.
17451 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
17452 if hunk.status().is_deleted() {
17453 intersects_range |= hunk.row_range.start == query_rows.end;
17454 intersects_range |= hunk.row_range.end == query_rows.start;
17455 }
17456 if intersects_range {
17457 if !processed_buffer_rows
17458 .entry(hunk.buffer_id)
17459 .or_default()
17460 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17461 {
17462 continue;
17463 }
17464 hunks.push(hunk);
17465 }
17466 }
17467 }
17468
17469 hunks
17470 }
17471
17472 fn display_diff_hunks_for_rows<'a>(
17473 &'a self,
17474 display_rows: Range<DisplayRow>,
17475 folded_buffers: &'a HashSet<BufferId>,
17476 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17477 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17478 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17479
17480 self.buffer_snapshot
17481 .diff_hunks_in_range(buffer_start..buffer_end)
17482 .filter_map(|hunk| {
17483 if folded_buffers.contains(&hunk.buffer_id) {
17484 return None;
17485 }
17486
17487 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17488 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17489
17490 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17491 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17492
17493 let display_hunk = if hunk_display_start.column() != 0 {
17494 DisplayDiffHunk::Folded {
17495 display_row: hunk_display_start.row(),
17496 }
17497 } else {
17498 let mut end_row = hunk_display_end.row();
17499 if hunk_display_end.column() > 0 {
17500 end_row.0 += 1;
17501 }
17502 let is_created_file = hunk.is_created_file();
17503 DisplayDiffHunk::Unfolded {
17504 status: hunk.status(),
17505 diff_base_byte_range: hunk.diff_base_byte_range,
17506 display_row_range: hunk_display_start.row()..end_row,
17507 multi_buffer_range: Anchor::range_in_buffer(
17508 hunk.excerpt_id,
17509 hunk.buffer_id,
17510 hunk.buffer_range,
17511 ),
17512 is_created_file,
17513 }
17514 };
17515
17516 Some(display_hunk)
17517 })
17518 }
17519
17520 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17521 self.display_snapshot.buffer_snapshot.language_at(position)
17522 }
17523
17524 pub fn is_focused(&self) -> bool {
17525 self.is_focused
17526 }
17527
17528 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17529 self.placeholder_text.as_ref()
17530 }
17531
17532 pub fn scroll_position(&self) -> gpui::Point<f32> {
17533 self.scroll_anchor.scroll_position(&self.display_snapshot)
17534 }
17535
17536 fn gutter_dimensions(
17537 &self,
17538 font_id: FontId,
17539 font_size: Pixels,
17540 max_line_number_width: Pixels,
17541 cx: &App,
17542 ) -> Option<GutterDimensions> {
17543 if !self.show_gutter {
17544 return None;
17545 }
17546
17547 let descent = cx.text_system().descent(font_id, font_size);
17548 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17549 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17550
17551 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17552 matches!(
17553 ProjectSettings::get_global(cx).git.git_gutter,
17554 Some(GitGutterSetting::TrackedFiles)
17555 )
17556 });
17557 let gutter_settings = EditorSettings::get_global(cx).gutter;
17558 let show_line_numbers = self
17559 .show_line_numbers
17560 .unwrap_or(gutter_settings.line_numbers);
17561 let line_gutter_width = if show_line_numbers {
17562 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17563 let min_width_for_number_on_gutter = em_advance * 4.0;
17564 max_line_number_width.max(min_width_for_number_on_gutter)
17565 } else {
17566 0.0.into()
17567 };
17568
17569 let show_code_actions = self
17570 .show_code_actions
17571 .unwrap_or(gutter_settings.code_actions);
17572
17573 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17574
17575 let git_blame_entries_width =
17576 self.git_blame_gutter_max_author_length
17577 .map(|max_author_length| {
17578 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17579
17580 /// The number of characters to dedicate to gaps and margins.
17581 const SPACING_WIDTH: usize = 4;
17582
17583 let max_char_count = max_author_length
17584 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17585 + ::git::SHORT_SHA_LENGTH
17586 + MAX_RELATIVE_TIMESTAMP.len()
17587 + SPACING_WIDTH;
17588
17589 em_advance * max_char_count
17590 });
17591
17592 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17593 left_padding += if show_code_actions || show_runnables {
17594 em_width * 3.0
17595 } else if show_git_gutter && show_line_numbers {
17596 em_width * 2.0
17597 } else if show_git_gutter || show_line_numbers {
17598 em_width
17599 } else {
17600 px(0.)
17601 };
17602
17603 let right_padding = if gutter_settings.folds && show_line_numbers {
17604 em_width * 4.0
17605 } else if gutter_settings.folds {
17606 em_width * 3.0
17607 } else if show_line_numbers {
17608 em_width
17609 } else {
17610 px(0.)
17611 };
17612
17613 Some(GutterDimensions {
17614 left_padding,
17615 right_padding,
17616 width: line_gutter_width + left_padding + right_padding,
17617 margin: -descent,
17618 git_blame_entries_width,
17619 })
17620 }
17621
17622 pub fn render_crease_toggle(
17623 &self,
17624 buffer_row: MultiBufferRow,
17625 row_contains_cursor: bool,
17626 editor: Entity<Editor>,
17627 window: &mut Window,
17628 cx: &mut App,
17629 ) -> Option<AnyElement> {
17630 let folded = self.is_line_folded(buffer_row);
17631 let mut is_foldable = false;
17632
17633 if let Some(crease) = self
17634 .crease_snapshot
17635 .query_row(buffer_row, &self.buffer_snapshot)
17636 {
17637 is_foldable = true;
17638 match crease {
17639 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17640 if let Some(render_toggle) = render_toggle {
17641 let toggle_callback =
17642 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17643 if folded {
17644 editor.update(cx, |editor, cx| {
17645 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17646 });
17647 } else {
17648 editor.update(cx, |editor, cx| {
17649 editor.unfold_at(
17650 &crate::UnfoldAt { buffer_row },
17651 window,
17652 cx,
17653 )
17654 });
17655 }
17656 });
17657 return Some((render_toggle)(
17658 buffer_row,
17659 folded,
17660 toggle_callback,
17661 window,
17662 cx,
17663 ));
17664 }
17665 }
17666 }
17667 }
17668
17669 is_foldable |= self.starts_indent(buffer_row);
17670
17671 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17672 Some(
17673 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17674 .toggle_state(folded)
17675 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17676 if folded {
17677 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17678 } else {
17679 this.fold_at(&FoldAt { buffer_row }, window, cx);
17680 }
17681 }))
17682 .into_any_element(),
17683 )
17684 } else {
17685 None
17686 }
17687 }
17688
17689 pub fn render_crease_trailer(
17690 &self,
17691 buffer_row: MultiBufferRow,
17692 window: &mut Window,
17693 cx: &mut App,
17694 ) -> Option<AnyElement> {
17695 let folded = self.is_line_folded(buffer_row);
17696 if let Crease::Inline { render_trailer, .. } = self
17697 .crease_snapshot
17698 .query_row(buffer_row, &self.buffer_snapshot)?
17699 {
17700 let render_trailer = render_trailer.as_ref()?;
17701 Some(render_trailer(buffer_row, folded, window, cx))
17702 } else {
17703 None
17704 }
17705 }
17706}
17707
17708impl Deref for EditorSnapshot {
17709 type Target = DisplaySnapshot;
17710
17711 fn deref(&self) -> &Self::Target {
17712 &self.display_snapshot
17713 }
17714}
17715
17716#[derive(Clone, Debug, PartialEq, Eq)]
17717pub enum EditorEvent {
17718 InputIgnored {
17719 text: Arc<str>,
17720 },
17721 InputHandled {
17722 utf16_range_to_replace: Option<Range<isize>>,
17723 text: Arc<str>,
17724 },
17725 ExcerptsAdded {
17726 buffer: Entity<Buffer>,
17727 predecessor: ExcerptId,
17728 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17729 },
17730 ExcerptsRemoved {
17731 ids: Vec<ExcerptId>,
17732 },
17733 BufferFoldToggled {
17734 ids: Vec<ExcerptId>,
17735 folded: bool,
17736 },
17737 ExcerptsEdited {
17738 ids: Vec<ExcerptId>,
17739 },
17740 ExcerptsExpanded {
17741 ids: Vec<ExcerptId>,
17742 },
17743 BufferEdited,
17744 Edited {
17745 transaction_id: clock::Lamport,
17746 },
17747 Reparsed(BufferId),
17748 Focused,
17749 FocusedIn,
17750 Blurred,
17751 DirtyChanged,
17752 Saved,
17753 TitleChanged,
17754 DiffBaseChanged,
17755 SelectionsChanged {
17756 local: bool,
17757 },
17758 ScrollPositionChanged {
17759 local: bool,
17760 autoscroll: bool,
17761 },
17762 Closed,
17763 TransactionUndone {
17764 transaction_id: clock::Lamport,
17765 },
17766 TransactionBegun {
17767 transaction_id: clock::Lamport,
17768 },
17769 Reloaded,
17770 CursorShapeChanged,
17771}
17772
17773impl EventEmitter<EditorEvent> for Editor {}
17774
17775impl Focusable for Editor {
17776 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17777 self.focus_handle.clone()
17778 }
17779}
17780
17781impl Render for Editor {
17782 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17783 let settings = ThemeSettings::get_global(cx);
17784
17785 let mut text_style = match self.mode {
17786 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17787 color: cx.theme().colors().editor_foreground,
17788 font_family: settings.ui_font.family.clone(),
17789 font_features: settings.ui_font.features.clone(),
17790 font_fallbacks: settings.ui_font.fallbacks.clone(),
17791 font_size: rems(0.875).into(),
17792 font_weight: settings.ui_font.weight,
17793 line_height: relative(settings.buffer_line_height.value()),
17794 ..Default::default()
17795 },
17796 EditorMode::Full => TextStyle {
17797 color: cx.theme().colors().editor_foreground,
17798 font_family: settings.buffer_font.family.clone(),
17799 font_features: settings.buffer_font.features.clone(),
17800 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17801 font_size: settings.buffer_font_size(cx).into(),
17802 font_weight: settings.buffer_font.weight,
17803 line_height: relative(settings.buffer_line_height.value()),
17804 ..Default::default()
17805 },
17806 };
17807 if let Some(text_style_refinement) = &self.text_style_refinement {
17808 text_style.refine(text_style_refinement)
17809 }
17810
17811 let background = match self.mode {
17812 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17813 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17814 EditorMode::Full => cx.theme().colors().editor_background,
17815 };
17816
17817 EditorElement::new(
17818 &cx.entity(),
17819 EditorStyle {
17820 background,
17821 local_player: cx.theme().players().local(),
17822 text: text_style,
17823 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17824 syntax: cx.theme().syntax().clone(),
17825 status: cx.theme().status().clone(),
17826 inlay_hints_style: make_inlay_hints_style(cx),
17827 inline_completion_styles: make_suggestion_styles(cx),
17828 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17829 },
17830 )
17831 }
17832}
17833
17834impl EntityInputHandler for Editor {
17835 fn text_for_range(
17836 &mut self,
17837 range_utf16: Range<usize>,
17838 adjusted_range: &mut Option<Range<usize>>,
17839 _: &mut Window,
17840 cx: &mut Context<Self>,
17841 ) -> Option<String> {
17842 let snapshot = self.buffer.read(cx).read(cx);
17843 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17844 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17845 if (start.0..end.0) != range_utf16 {
17846 adjusted_range.replace(start.0..end.0);
17847 }
17848 Some(snapshot.text_for_range(start..end).collect())
17849 }
17850
17851 fn selected_text_range(
17852 &mut self,
17853 ignore_disabled_input: bool,
17854 _: &mut Window,
17855 cx: &mut Context<Self>,
17856 ) -> Option<UTF16Selection> {
17857 // Prevent the IME menu from appearing when holding down an alphabetic key
17858 // while input is disabled.
17859 if !ignore_disabled_input && !self.input_enabled {
17860 return None;
17861 }
17862
17863 let selection = self.selections.newest::<OffsetUtf16>(cx);
17864 let range = selection.range();
17865
17866 Some(UTF16Selection {
17867 range: range.start.0..range.end.0,
17868 reversed: selection.reversed,
17869 })
17870 }
17871
17872 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17873 let snapshot = self.buffer.read(cx).read(cx);
17874 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17875 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17876 }
17877
17878 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17879 self.clear_highlights::<InputComposition>(cx);
17880 self.ime_transaction.take();
17881 }
17882
17883 fn replace_text_in_range(
17884 &mut self,
17885 range_utf16: Option<Range<usize>>,
17886 text: &str,
17887 window: &mut Window,
17888 cx: &mut Context<Self>,
17889 ) {
17890 if !self.input_enabled {
17891 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17892 return;
17893 }
17894
17895 self.transact(window, cx, |this, window, cx| {
17896 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17897 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17898 Some(this.selection_replacement_ranges(range_utf16, cx))
17899 } else {
17900 this.marked_text_ranges(cx)
17901 };
17902
17903 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17904 let newest_selection_id = this.selections.newest_anchor().id;
17905 this.selections
17906 .all::<OffsetUtf16>(cx)
17907 .iter()
17908 .zip(ranges_to_replace.iter())
17909 .find_map(|(selection, range)| {
17910 if selection.id == newest_selection_id {
17911 Some(
17912 (range.start.0 as isize - selection.head().0 as isize)
17913 ..(range.end.0 as isize - selection.head().0 as isize),
17914 )
17915 } else {
17916 None
17917 }
17918 })
17919 });
17920
17921 cx.emit(EditorEvent::InputHandled {
17922 utf16_range_to_replace: range_to_replace,
17923 text: text.into(),
17924 });
17925
17926 if let Some(new_selected_ranges) = new_selected_ranges {
17927 this.change_selections(None, window, cx, |selections| {
17928 selections.select_ranges(new_selected_ranges)
17929 });
17930 this.backspace(&Default::default(), window, cx);
17931 }
17932
17933 this.handle_input(text, window, cx);
17934 });
17935
17936 if let Some(transaction) = self.ime_transaction {
17937 self.buffer.update(cx, |buffer, cx| {
17938 buffer.group_until_transaction(transaction, cx);
17939 });
17940 }
17941
17942 self.unmark_text(window, cx);
17943 }
17944
17945 fn replace_and_mark_text_in_range(
17946 &mut self,
17947 range_utf16: Option<Range<usize>>,
17948 text: &str,
17949 new_selected_range_utf16: Option<Range<usize>>,
17950 window: &mut Window,
17951 cx: &mut Context<Self>,
17952 ) {
17953 if !self.input_enabled {
17954 return;
17955 }
17956
17957 let transaction = self.transact(window, cx, |this, window, cx| {
17958 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17959 let snapshot = this.buffer.read(cx).read(cx);
17960 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17961 for marked_range in &mut marked_ranges {
17962 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17963 marked_range.start.0 += relative_range_utf16.start;
17964 marked_range.start =
17965 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
17966 marked_range.end =
17967 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
17968 }
17969 }
17970 Some(marked_ranges)
17971 } else if let Some(range_utf16) = range_utf16 {
17972 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17973 Some(this.selection_replacement_ranges(range_utf16, cx))
17974 } else {
17975 None
17976 };
17977
17978 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
17979 let newest_selection_id = this.selections.newest_anchor().id;
17980 this.selections
17981 .all::<OffsetUtf16>(cx)
17982 .iter()
17983 .zip(ranges_to_replace.iter())
17984 .find_map(|(selection, range)| {
17985 if selection.id == newest_selection_id {
17986 Some(
17987 (range.start.0 as isize - selection.head().0 as isize)
17988 ..(range.end.0 as isize - selection.head().0 as isize),
17989 )
17990 } else {
17991 None
17992 }
17993 })
17994 });
17995
17996 cx.emit(EditorEvent::InputHandled {
17997 utf16_range_to_replace: range_to_replace,
17998 text: text.into(),
17999 });
18000
18001 if let Some(ranges) = ranges_to_replace {
18002 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18003 }
18004
18005 let marked_ranges = {
18006 let snapshot = this.buffer.read(cx).read(cx);
18007 this.selections
18008 .disjoint_anchors()
18009 .iter()
18010 .map(|selection| {
18011 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18012 })
18013 .collect::<Vec<_>>()
18014 };
18015
18016 if text.is_empty() {
18017 this.unmark_text(window, cx);
18018 } else {
18019 this.highlight_text::<InputComposition>(
18020 marked_ranges.clone(),
18021 HighlightStyle {
18022 underline: Some(UnderlineStyle {
18023 thickness: px(1.),
18024 color: None,
18025 wavy: false,
18026 }),
18027 ..Default::default()
18028 },
18029 cx,
18030 );
18031 }
18032
18033 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
18034 let use_autoclose = this.use_autoclose;
18035 let use_auto_surround = this.use_auto_surround;
18036 this.set_use_autoclose(false);
18037 this.set_use_auto_surround(false);
18038 this.handle_input(text, window, cx);
18039 this.set_use_autoclose(use_autoclose);
18040 this.set_use_auto_surround(use_auto_surround);
18041
18042 if let Some(new_selected_range) = new_selected_range_utf16 {
18043 let snapshot = this.buffer.read(cx).read(cx);
18044 let new_selected_ranges = marked_ranges
18045 .into_iter()
18046 .map(|marked_range| {
18047 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
18048 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
18049 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
18050 snapshot.clip_offset_utf16(new_start, Bias::Left)
18051 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
18052 })
18053 .collect::<Vec<_>>();
18054
18055 drop(snapshot);
18056 this.change_selections(None, window, cx, |selections| {
18057 selections.select_ranges(new_selected_ranges)
18058 });
18059 }
18060 });
18061
18062 self.ime_transaction = self.ime_transaction.or(transaction);
18063 if let Some(transaction) = self.ime_transaction {
18064 self.buffer.update(cx, |buffer, cx| {
18065 buffer.group_until_transaction(transaction, cx);
18066 });
18067 }
18068
18069 if self.text_highlights::<InputComposition>(cx).is_none() {
18070 self.ime_transaction.take();
18071 }
18072 }
18073
18074 fn bounds_for_range(
18075 &mut self,
18076 range_utf16: Range<usize>,
18077 element_bounds: gpui::Bounds<Pixels>,
18078 window: &mut Window,
18079 cx: &mut Context<Self>,
18080 ) -> Option<gpui::Bounds<Pixels>> {
18081 let text_layout_details = self.text_layout_details(window);
18082 let gpui::Size {
18083 width: em_width,
18084 height: line_height,
18085 } = self.character_size(window);
18086
18087 let snapshot = self.snapshot(window, cx);
18088 let scroll_position = snapshot.scroll_position();
18089 let scroll_left = scroll_position.x * em_width;
18090
18091 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
18092 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
18093 + self.gutter_dimensions.width
18094 + self.gutter_dimensions.margin;
18095 let y = line_height * (start.row().as_f32() - scroll_position.y);
18096
18097 Some(Bounds {
18098 origin: element_bounds.origin + point(x, y),
18099 size: size(em_width, line_height),
18100 })
18101 }
18102
18103 fn character_index_for_point(
18104 &mut self,
18105 point: gpui::Point<Pixels>,
18106 _window: &mut Window,
18107 _cx: &mut Context<Self>,
18108 ) -> Option<usize> {
18109 let position_map = self.last_position_map.as_ref()?;
18110 if !position_map.text_hitbox.contains(&point) {
18111 return None;
18112 }
18113 let display_point = position_map.point_for_position(point).previous_valid;
18114 let anchor = position_map
18115 .snapshot
18116 .display_point_to_anchor(display_point, Bias::Left);
18117 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
18118 Some(utf16_offset.0)
18119 }
18120}
18121
18122trait SelectionExt {
18123 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
18124 fn spanned_rows(
18125 &self,
18126 include_end_if_at_line_start: bool,
18127 map: &DisplaySnapshot,
18128 ) -> Range<MultiBufferRow>;
18129}
18130
18131impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
18132 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
18133 let start = self
18134 .start
18135 .to_point(&map.buffer_snapshot)
18136 .to_display_point(map);
18137 let end = self
18138 .end
18139 .to_point(&map.buffer_snapshot)
18140 .to_display_point(map);
18141 if self.reversed {
18142 end..start
18143 } else {
18144 start..end
18145 }
18146 }
18147
18148 fn spanned_rows(
18149 &self,
18150 include_end_if_at_line_start: bool,
18151 map: &DisplaySnapshot,
18152 ) -> Range<MultiBufferRow> {
18153 let start = self.start.to_point(&map.buffer_snapshot);
18154 let mut end = self.end.to_point(&map.buffer_snapshot);
18155 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
18156 end.row -= 1;
18157 }
18158
18159 let buffer_start = map.prev_line_boundary(start).0;
18160 let buffer_end = map.next_line_boundary(end).0;
18161 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
18162 }
18163}
18164
18165impl<T: InvalidationRegion> InvalidationStack<T> {
18166 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
18167 where
18168 S: Clone + ToOffset,
18169 {
18170 while let Some(region) = self.last() {
18171 let all_selections_inside_invalidation_ranges =
18172 if selections.len() == region.ranges().len() {
18173 selections
18174 .iter()
18175 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
18176 .all(|(selection, invalidation_range)| {
18177 let head = selection.head().to_offset(buffer);
18178 invalidation_range.start <= head && invalidation_range.end >= head
18179 })
18180 } else {
18181 false
18182 };
18183
18184 if all_selections_inside_invalidation_ranges {
18185 break;
18186 } else {
18187 self.pop();
18188 }
18189 }
18190 }
18191}
18192
18193impl<T> Default for InvalidationStack<T> {
18194 fn default() -> Self {
18195 Self(Default::default())
18196 }
18197}
18198
18199impl<T> Deref for InvalidationStack<T> {
18200 type Target = Vec<T>;
18201
18202 fn deref(&self) -> &Self::Target {
18203 &self.0
18204 }
18205}
18206
18207impl<T> DerefMut for InvalidationStack<T> {
18208 fn deref_mut(&mut self) -> &mut Self::Target {
18209 &mut self.0
18210 }
18211}
18212
18213impl InvalidationRegion for SnippetState {
18214 fn ranges(&self) -> &[Range<Anchor>] {
18215 &self.ranges[self.active_index]
18216 }
18217}
18218
18219pub fn diagnostic_block_renderer(
18220 diagnostic: Diagnostic,
18221 max_message_rows: Option<u8>,
18222 allow_closing: bool,
18223) -> RenderBlock {
18224 let (text_without_backticks, code_ranges) =
18225 highlight_diagnostic_message(&diagnostic, max_message_rows);
18226
18227 Arc::new(move |cx: &mut BlockContext| {
18228 let group_id: SharedString = cx.block_id.to_string().into();
18229
18230 let mut text_style = cx.window.text_style().clone();
18231 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18232 let theme_settings = ThemeSettings::get_global(cx);
18233 text_style.font_family = theme_settings.buffer_font.family.clone();
18234 text_style.font_style = theme_settings.buffer_font.style;
18235 text_style.font_features = theme_settings.buffer_font.features.clone();
18236 text_style.font_weight = theme_settings.buffer_font.weight;
18237
18238 let multi_line_diagnostic = diagnostic.message.contains('\n');
18239
18240 let buttons = |diagnostic: &Diagnostic| {
18241 if multi_line_diagnostic {
18242 v_flex()
18243 } else {
18244 h_flex()
18245 }
18246 .when(allow_closing, |div| {
18247 div.children(diagnostic.is_primary.then(|| {
18248 IconButton::new("close-block", IconName::XCircle)
18249 .icon_color(Color::Muted)
18250 .size(ButtonSize::Compact)
18251 .style(ButtonStyle::Transparent)
18252 .visible_on_hover(group_id.clone())
18253 .on_click(move |_click, window, cx| {
18254 window.dispatch_action(Box::new(Cancel), cx)
18255 })
18256 .tooltip(|window, cx| {
18257 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18258 })
18259 }))
18260 })
18261 .child(
18262 IconButton::new("copy-block", IconName::Copy)
18263 .icon_color(Color::Muted)
18264 .size(ButtonSize::Compact)
18265 .style(ButtonStyle::Transparent)
18266 .visible_on_hover(group_id.clone())
18267 .on_click({
18268 let message = diagnostic.message.clone();
18269 move |_click, _, cx| {
18270 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
18271 }
18272 })
18273 .tooltip(Tooltip::text("Copy diagnostic message")),
18274 )
18275 };
18276
18277 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
18278 AvailableSpace::min_size(),
18279 cx.window,
18280 cx.app,
18281 );
18282
18283 h_flex()
18284 .id(cx.block_id)
18285 .group(group_id.clone())
18286 .relative()
18287 .size_full()
18288 .block_mouse_down()
18289 .pl(cx.gutter_dimensions.width)
18290 .w(cx.max_width - cx.gutter_dimensions.full_width())
18291 .child(
18292 div()
18293 .flex()
18294 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
18295 .flex_shrink(),
18296 )
18297 .child(buttons(&diagnostic))
18298 .child(div().flex().flex_shrink_0().child(
18299 StyledText::new(text_without_backticks.clone()).with_default_highlights(
18300 &text_style,
18301 code_ranges.iter().map(|range| {
18302 (
18303 range.clone(),
18304 HighlightStyle {
18305 font_weight: Some(FontWeight::BOLD),
18306 ..Default::default()
18307 },
18308 )
18309 }),
18310 ),
18311 ))
18312 .into_any_element()
18313 })
18314}
18315
18316fn inline_completion_edit_text(
18317 current_snapshot: &BufferSnapshot,
18318 edits: &[(Range<Anchor>, String)],
18319 edit_preview: &EditPreview,
18320 include_deletions: bool,
18321 cx: &App,
18322) -> HighlightedText {
18323 let edits = edits
18324 .iter()
18325 .map(|(anchor, text)| {
18326 (
18327 anchor.start.text_anchor..anchor.end.text_anchor,
18328 text.clone(),
18329 )
18330 })
18331 .collect::<Vec<_>>();
18332
18333 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
18334}
18335
18336pub fn highlight_diagnostic_message(
18337 diagnostic: &Diagnostic,
18338 mut max_message_rows: Option<u8>,
18339) -> (SharedString, Vec<Range<usize>>) {
18340 let mut text_without_backticks = String::new();
18341 let mut code_ranges = Vec::new();
18342
18343 if let Some(source) = &diagnostic.source {
18344 text_without_backticks.push_str(source);
18345 code_ranges.push(0..source.len());
18346 text_without_backticks.push_str(": ");
18347 }
18348
18349 let mut prev_offset = 0;
18350 let mut in_code_block = false;
18351 let has_row_limit = max_message_rows.is_some();
18352 let mut newline_indices = diagnostic
18353 .message
18354 .match_indices('\n')
18355 .filter(|_| has_row_limit)
18356 .map(|(ix, _)| ix)
18357 .fuse()
18358 .peekable();
18359
18360 for (quote_ix, _) in diagnostic
18361 .message
18362 .match_indices('`')
18363 .chain([(diagnostic.message.len(), "")])
18364 {
18365 let mut first_newline_ix = None;
18366 let mut last_newline_ix = None;
18367 while let Some(newline_ix) = newline_indices.peek() {
18368 if *newline_ix < quote_ix {
18369 if first_newline_ix.is_none() {
18370 first_newline_ix = Some(*newline_ix);
18371 }
18372 last_newline_ix = Some(*newline_ix);
18373
18374 if let Some(rows_left) = &mut max_message_rows {
18375 if *rows_left == 0 {
18376 break;
18377 } else {
18378 *rows_left -= 1;
18379 }
18380 }
18381 let _ = newline_indices.next();
18382 } else {
18383 break;
18384 }
18385 }
18386 let prev_len = text_without_backticks.len();
18387 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18388 text_without_backticks.push_str(new_text);
18389 if in_code_block {
18390 code_ranges.push(prev_len..text_without_backticks.len());
18391 }
18392 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18393 in_code_block = !in_code_block;
18394 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18395 text_without_backticks.push_str("...");
18396 break;
18397 }
18398 }
18399
18400 (text_without_backticks.into(), code_ranges)
18401}
18402
18403fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18404 match severity {
18405 DiagnosticSeverity::ERROR => colors.error,
18406 DiagnosticSeverity::WARNING => colors.warning,
18407 DiagnosticSeverity::INFORMATION => colors.info,
18408 DiagnosticSeverity::HINT => colors.info,
18409 _ => colors.ignored,
18410 }
18411}
18412
18413pub fn styled_runs_for_code_label<'a>(
18414 label: &'a CodeLabel,
18415 syntax_theme: &'a theme::SyntaxTheme,
18416) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18417 let fade_out = HighlightStyle {
18418 fade_out: Some(0.35),
18419 ..Default::default()
18420 };
18421
18422 let mut prev_end = label.filter_range.end;
18423 label
18424 .runs
18425 .iter()
18426 .enumerate()
18427 .flat_map(move |(ix, (range, highlight_id))| {
18428 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18429 style
18430 } else {
18431 return Default::default();
18432 };
18433 let mut muted_style = style;
18434 muted_style.highlight(fade_out);
18435
18436 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18437 if range.start >= label.filter_range.end {
18438 if range.start > prev_end {
18439 runs.push((prev_end..range.start, fade_out));
18440 }
18441 runs.push((range.clone(), muted_style));
18442 } else if range.end <= label.filter_range.end {
18443 runs.push((range.clone(), style));
18444 } else {
18445 runs.push((range.start..label.filter_range.end, style));
18446 runs.push((label.filter_range.end..range.end, muted_style));
18447 }
18448 prev_end = cmp::max(prev_end, range.end);
18449
18450 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18451 runs.push((prev_end..label.text.len(), fade_out));
18452 }
18453
18454 runs
18455 })
18456}
18457
18458pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18459 let mut prev_index = 0;
18460 let mut prev_codepoint: Option<char> = None;
18461 text.char_indices()
18462 .chain([(text.len(), '\0')])
18463 .filter_map(move |(index, codepoint)| {
18464 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18465 let is_boundary = index == text.len()
18466 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18467 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18468 if is_boundary {
18469 let chunk = &text[prev_index..index];
18470 prev_index = index;
18471 Some(chunk)
18472 } else {
18473 None
18474 }
18475 })
18476}
18477
18478pub trait RangeToAnchorExt: Sized {
18479 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18480
18481 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18482 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18483 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18484 }
18485}
18486
18487impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18488 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18489 let start_offset = self.start.to_offset(snapshot);
18490 let end_offset = self.end.to_offset(snapshot);
18491 if start_offset == end_offset {
18492 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18493 } else {
18494 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18495 }
18496 }
18497}
18498
18499pub trait RowExt {
18500 fn as_f32(&self) -> f32;
18501
18502 fn next_row(&self) -> Self;
18503
18504 fn previous_row(&self) -> Self;
18505
18506 fn minus(&self, other: Self) -> u32;
18507}
18508
18509impl RowExt for DisplayRow {
18510 fn as_f32(&self) -> f32 {
18511 self.0 as f32
18512 }
18513
18514 fn next_row(&self) -> Self {
18515 Self(self.0 + 1)
18516 }
18517
18518 fn previous_row(&self) -> Self {
18519 Self(self.0.saturating_sub(1))
18520 }
18521
18522 fn minus(&self, other: Self) -> u32 {
18523 self.0 - other.0
18524 }
18525}
18526
18527impl RowExt for MultiBufferRow {
18528 fn as_f32(&self) -> f32 {
18529 self.0 as f32
18530 }
18531
18532 fn next_row(&self) -> Self {
18533 Self(self.0 + 1)
18534 }
18535
18536 fn previous_row(&self) -> Self {
18537 Self(self.0.saturating_sub(1))
18538 }
18539
18540 fn minus(&self, other: Self) -> u32 {
18541 self.0 - other.0
18542 }
18543}
18544
18545trait RowRangeExt {
18546 type Row;
18547
18548 fn len(&self) -> usize;
18549
18550 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18551}
18552
18553impl RowRangeExt for Range<MultiBufferRow> {
18554 type Row = MultiBufferRow;
18555
18556 fn len(&self) -> usize {
18557 (self.end.0 - self.start.0) as usize
18558 }
18559
18560 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18561 (self.start.0..self.end.0).map(MultiBufferRow)
18562 }
18563}
18564
18565impl RowRangeExt for Range<DisplayRow> {
18566 type Row = DisplayRow;
18567
18568 fn len(&self) -> usize {
18569 (self.end.0 - self.start.0) as usize
18570 }
18571
18572 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18573 (self.start.0..self.end.0).map(DisplayRow)
18574 }
18575}
18576
18577/// If select range has more than one line, we
18578/// just point the cursor to range.start.
18579fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18580 if range.start.row == range.end.row {
18581 range
18582 } else {
18583 range.start..range.start
18584 }
18585}
18586pub struct KillRing(ClipboardItem);
18587impl Global for KillRing {}
18588
18589const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18590
18591fn all_edits_insertions_or_deletions(
18592 edits: &Vec<(Range<Anchor>, String)>,
18593 snapshot: &MultiBufferSnapshot,
18594) -> bool {
18595 let mut all_insertions = true;
18596 let mut all_deletions = true;
18597
18598 for (range, new_text) in edits.iter() {
18599 let range_is_empty = range.to_offset(&snapshot).is_empty();
18600 let text_is_empty = new_text.is_empty();
18601
18602 if range_is_empty != text_is_empty {
18603 if range_is_empty {
18604 all_deletions = false;
18605 } else {
18606 all_insertions = false;
18607 }
18608 } else {
18609 return false;
18610 }
18611
18612 if !all_insertions && !all_deletions {
18613 return false;
18614 }
18615 }
18616 all_insertions || all_deletions
18617}
18618
18619struct MissingEditPredictionKeybindingTooltip;
18620
18621impl Render for MissingEditPredictionKeybindingTooltip {
18622 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18623 ui::tooltip_container(window, cx, |container, _, cx| {
18624 container
18625 .flex_shrink_0()
18626 .max_w_80()
18627 .min_h(rems_from_px(124.))
18628 .justify_between()
18629 .child(
18630 v_flex()
18631 .flex_1()
18632 .text_ui_sm(cx)
18633 .child(Label::new("Conflict with Accept Keybinding"))
18634 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
18635 )
18636 .child(
18637 h_flex()
18638 .pb_1()
18639 .gap_1()
18640 .items_end()
18641 .w_full()
18642 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
18643 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
18644 }))
18645 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
18646 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
18647 })),
18648 )
18649 })
18650 }
18651}
18652
18653#[derive(Debug, Clone, Copy, PartialEq)]
18654pub struct LineHighlight {
18655 pub background: Background,
18656 pub border: Option<gpui::Hsla>,
18657}
18658
18659impl From<Hsla> for LineHighlight {
18660 fn from(hsla: Hsla) -> Self {
18661 Self {
18662 background: hsla.into(),
18663 border: None,
18664 }
18665 }
18666}
18667
18668impl From<Background> for LineHighlight {
18669 fn from(background: Background) -> Self {
18670 Self {
18671 background,
18672 border: None,
18673 }
18674 }
18675}