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 feature_flags::{Debugger, FeatureFlagAppExt};
72use futures::{
73 future::{self, join, Shared},
74 FutureExt,
75};
76use fuzzy::StringMatchCandidate;
77
78use ::git::Restore;
79use code_context_menus::{
80 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
81 CompletionsMenu, ContextMenuOrigin,
82};
83use git::blame::GitBlame;
84use gpui::{
85 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
86 AnimationExt, AnyElement, App, AppContext, AsyncWindowContext, AvailableSpace, Background,
87 Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity,
88 EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight,
89 Global, HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
90 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
91 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
92 WeakEntity, WeakFocusHandle, Window,
93};
94use highlight_matching_bracket::refresh_matching_bracket_highlights;
95use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
96use hover_popover::{hide_hover, HoverState};
97use indent_guides::ActiveIndentGuidesState;
98use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
99pub use inline_completion::Direction;
100use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
101pub use items::MAX_TAB_TITLE_LEN;
102use itertools::Itertools;
103use language::{
104 language_settings::{
105 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
106 WordsCompletionMode,
107 },
108 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
109 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
110 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
111 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
112};
113use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
114use linked_editing_ranges::refresh_linked_ranges;
115use mouse_context_menu::MouseContextMenu;
116use persistence::DB;
117use project::{
118 debugger::breakpoint_store::{BreakpointEditAction, BreakpointStore, BreakpointStoreEvent},
119 ProjectPath,
120};
121
122pub use proposed_changes_editor::{
123 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
124};
125use smallvec::smallvec;
126use std::{cell::OnceCell, iter::Peekable};
127use task::{ResolvedTask, TaskTemplate, TaskVariables};
128
129pub use lsp::CompletionContext;
130use lsp::{
131 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
132 InsertTextFormat, LanguageServerId, LanguageServerName,
133};
134
135use language::BufferSnapshot;
136use movement::TextLayoutDetails;
137pub use multi_buffer::{
138 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
139 ToOffset, ToPoint,
140};
141use multi_buffer::{
142 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
143 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
144};
145use parking_lot::Mutex;
146use project::{
147 debugger::breakpoint_store::{Breakpoint, BreakpointKind},
148 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
149 project_settings::{GitGutterSetting, ProjectSettings},
150 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
151 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
152 TaskSourceKind,
153};
154use rand::prelude::*;
155use rpc::{proto::*, ErrorExt};
156use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
157use selections_collection::{
158 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
159};
160use serde::{Deserialize, Serialize};
161use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
162use smallvec::SmallVec;
163use snippet::Snippet;
164use std::sync::Arc;
165use std::{
166 any::TypeId,
167 borrow::Cow,
168 cell::RefCell,
169 cmp::{self, Ordering, Reverse},
170 mem,
171 num::NonZeroU32,
172 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
173 path::{Path, PathBuf},
174 rc::Rc,
175 time::{Duration, Instant},
176};
177pub use sum_tree::Bias;
178use sum_tree::TreeMap;
179use text::{BufferId, OffsetUtf16, Rope};
180use theme::{
181 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
182 ThemeColors, ThemeSettings,
183};
184use ui::{
185 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
186 Tooltip,
187};
188use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
189use workspace::{
190 item::{ItemHandle, PreviewTabsSettings},
191 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
192 searchable::SearchEvent,
193 Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
194 RestoreOnStartupBehavior, SplitDirection, TabBarSettings, Toast, ViewId, Workspace,
195 WorkspaceId, WorkspaceSettings, SERIALIZATION_THROTTLE_TIME,
196};
197
198use crate::hover_links::{find_url, find_url_from_range};
199use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
200
201pub const FILE_HEADER_HEIGHT: u32 = 2;
202pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
203pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
204const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
205const MAX_LINE_LEN: usize = 1024;
206const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
207const MAX_SELECTION_HISTORY_LEN: usize = 1024;
208pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
209#[doc(hidden)]
210pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
211
212pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
213pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
214pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
215
216pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
217pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
218pub(crate) const MIN_LINE_NUMBER_DIGITS: u32 = 4;
219
220const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
221 alt: true,
222 shift: true,
223 control: false,
224 platform: false,
225 function: false,
226};
227
228#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
229pub enum InlayId {
230 InlineCompletion(usize),
231 Hint(usize),
232}
233
234impl InlayId {
235 fn id(&self) -> usize {
236 match self {
237 Self::InlineCompletion(id) => *id,
238 Self::Hint(id) => *id,
239 }
240 }
241}
242
243pub enum DebugCurrentRowHighlight {}
244enum DocumentHighlightRead {}
245enum DocumentHighlightWrite {}
246enum InputComposition {}
247enum SelectedTextHighlight {}
248
249#[derive(Debug, Copy, Clone, PartialEq, Eq)]
250pub enum Navigated {
251 Yes,
252 No,
253}
254
255impl Navigated {
256 pub fn from_bool(yes: bool) -> Navigated {
257 if yes {
258 Navigated::Yes
259 } else {
260 Navigated::No
261 }
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq)]
266enum DisplayDiffHunk {
267 Folded {
268 display_row: DisplayRow,
269 },
270 Unfolded {
271 is_created_file: bool,
272 diff_base_byte_range: Range<usize>,
273 display_row_range: Range<DisplayRow>,
274 multi_buffer_range: Range<Anchor>,
275 status: DiffHunkStatus,
276 },
277}
278
279pub fn init_settings(cx: &mut App) {
280 EditorSettings::register(cx);
281}
282
283pub fn init(cx: &mut App) {
284 init_settings(cx);
285
286 workspace::register_project_item::<Editor>(cx);
287 workspace::FollowableViewRegistry::register::<Editor>(cx);
288 workspace::register_serializable_item::<Editor>(cx);
289
290 cx.observe_new(
291 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
292 workspace.register_action(Editor::new_file);
293 workspace.register_action(Editor::new_file_vertical);
294 workspace.register_action(Editor::new_file_horizontal);
295 workspace.register_action(Editor::cancel_language_server_work);
296 },
297 )
298 .detach();
299
300 cx.on_action(move |_: &workspace::NewFile, cx| {
301 let app_state = workspace::AppState::global(cx);
302 if let Some(app_state) = app_state.upgrade() {
303 workspace::open_new(
304 Default::default(),
305 app_state,
306 cx,
307 |workspace, window, cx| {
308 Editor::new_file(workspace, &Default::default(), window, cx)
309 },
310 )
311 .detach();
312 }
313 });
314 cx.on_action(move |_: &workspace::NewWindow, cx| {
315 let app_state = workspace::AppState::global(cx);
316 if let Some(app_state) = app_state.upgrade() {
317 workspace::open_new(
318 Default::default(),
319 app_state,
320 cx,
321 |workspace, window, cx| {
322 cx.activate(true);
323 Editor::new_file(workspace, &Default::default(), window, cx)
324 },
325 )
326 .detach();
327 }
328 });
329}
330
331pub struct SearchWithinRange;
332
333trait InvalidationRegion {
334 fn ranges(&self) -> &[Range<Anchor>];
335}
336
337#[derive(Clone, Debug, PartialEq)]
338pub enum SelectPhase {
339 Begin {
340 position: DisplayPoint,
341 add: bool,
342 click_count: usize,
343 },
344 BeginColumnar {
345 position: DisplayPoint,
346 reset: bool,
347 goal_column: u32,
348 },
349 Extend {
350 position: DisplayPoint,
351 click_count: usize,
352 },
353 Update {
354 position: DisplayPoint,
355 goal_column: u32,
356 scroll_delta: gpui::Point<f32>,
357 },
358 End,
359}
360
361#[derive(Clone, Debug)]
362pub enum SelectMode {
363 Character,
364 Word(Range<Anchor>),
365 Line(Range<Anchor>),
366 All,
367}
368
369#[derive(Copy, Clone, PartialEq, Eq, Debug)]
370pub enum EditorMode {
371 SingleLine { auto_width: bool },
372 AutoHeight { max_lines: usize },
373 Full,
374}
375
376#[derive(Copy, Clone, Debug)]
377pub enum SoftWrap {
378 /// Prefer not to wrap at all.
379 ///
380 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
381 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
382 GitDiff,
383 /// Prefer a single line generally, unless an overly long line is encountered.
384 None,
385 /// Soft wrap lines that exceed the editor width.
386 EditorWidth,
387 /// Soft wrap lines at the preferred line length.
388 Column(u32),
389 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
390 Bounded(u32),
391}
392
393#[derive(Clone)]
394pub struct EditorStyle {
395 pub background: Hsla,
396 pub local_player: PlayerColor,
397 pub text: TextStyle,
398 pub scrollbar_width: Pixels,
399 pub syntax: Arc<SyntaxTheme>,
400 pub status: StatusColors,
401 pub inlay_hints_style: HighlightStyle,
402 pub inline_completion_styles: InlineCompletionStyles,
403 pub unnecessary_code_fade: f32,
404}
405
406impl Default for EditorStyle {
407 fn default() -> Self {
408 Self {
409 background: Hsla::default(),
410 local_player: PlayerColor::default(),
411 text: TextStyle::default(),
412 scrollbar_width: Pixels::default(),
413 syntax: Default::default(),
414 // HACK: Status colors don't have a real default.
415 // We should look into removing the status colors from the editor
416 // style and retrieve them directly from the theme.
417 status: StatusColors::dark(),
418 inlay_hints_style: HighlightStyle::default(),
419 inline_completion_styles: InlineCompletionStyles {
420 insertion: HighlightStyle::default(),
421 whitespace: HighlightStyle::default(),
422 },
423 unnecessary_code_fade: Default::default(),
424 }
425 }
426}
427
428pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
429 let show_background = language_settings::language_settings(None, None, cx)
430 .inlay_hints
431 .show_background;
432
433 HighlightStyle {
434 color: Some(cx.theme().status().hint),
435 background_color: show_background.then(|| cx.theme().status().hint_background),
436 ..HighlightStyle::default()
437 }
438}
439
440pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
441 InlineCompletionStyles {
442 insertion: HighlightStyle {
443 color: Some(cx.theme().status().predictive),
444 ..HighlightStyle::default()
445 },
446 whitespace: HighlightStyle {
447 background_color: Some(cx.theme().status().created_background),
448 ..HighlightStyle::default()
449 },
450 }
451}
452
453type CompletionId = usize;
454
455pub(crate) enum EditDisplayMode {
456 TabAccept,
457 DiffPopover,
458 Inline,
459}
460
461enum InlineCompletion {
462 Edit {
463 edits: Vec<(Range<Anchor>, String)>,
464 edit_preview: Option<EditPreview>,
465 display_mode: EditDisplayMode,
466 snapshot: BufferSnapshot,
467 },
468 Move {
469 target: Anchor,
470 snapshot: BufferSnapshot,
471 },
472}
473
474struct InlineCompletionState {
475 inlay_ids: Vec<InlayId>,
476 completion: InlineCompletion,
477 completion_id: Option<SharedString>,
478 invalidation_range: Range<Anchor>,
479}
480
481enum EditPredictionSettings {
482 Disabled,
483 Enabled {
484 show_in_menu: bool,
485 preview_requires_modifier: bool,
486 },
487}
488
489enum InlineCompletionHighlight {}
490
491#[derive(Debug, Clone)]
492struct InlineDiagnostic {
493 message: SharedString,
494 group_id: usize,
495 is_primary: bool,
496 start: Point,
497 severity: DiagnosticSeverity,
498}
499
500pub enum MenuInlineCompletionsPolicy {
501 Never,
502 ByProvider,
503}
504
505pub enum EditPredictionPreview {
506 /// Modifier is not pressed
507 Inactive { released_too_fast: bool },
508 /// Modifier pressed
509 Active {
510 since: Instant,
511 previous_scroll_position: Option<ScrollAnchor>,
512 },
513}
514
515impl EditPredictionPreview {
516 pub fn released_too_fast(&self) -> bool {
517 match self {
518 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
519 EditPredictionPreview::Active { .. } => false,
520 }
521 }
522
523 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
524 if let EditPredictionPreview::Active {
525 previous_scroll_position,
526 ..
527 } = self
528 {
529 *previous_scroll_position = scroll_position;
530 }
531 }
532}
533
534#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
535struct EditorActionId(usize);
536
537impl EditorActionId {
538 pub fn post_inc(&mut self) -> Self {
539 let answer = self.0;
540
541 *self = Self(answer + 1);
542
543 Self(answer)
544 }
545}
546
547// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
548// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
549
550type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
551type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
552
553#[derive(Default)]
554struct ScrollbarMarkerState {
555 scrollbar_size: Size<Pixels>,
556 dirty: bool,
557 markers: Arc<[PaintQuad]>,
558 pending_refresh: Option<Task<Result<()>>>,
559}
560
561impl ScrollbarMarkerState {
562 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
563 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
564 }
565}
566
567#[derive(Clone, Debug)]
568struct RunnableTasks {
569 templates: Vec<(TaskSourceKind, TaskTemplate)>,
570 offset: multi_buffer::Anchor,
571 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
572 column: u32,
573 // Values of all named captures, including those starting with '_'
574 extra_variables: HashMap<String, String>,
575 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
576 context_range: Range<BufferOffset>,
577}
578
579impl RunnableTasks {
580 fn resolve<'a>(
581 &'a self,
582 cx: &'a task::TaskContext,
583 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
584 self.templates.iter().filter_map(|(kind, template)| {
585 template
586 .resolve_task(&kind.to_id_base(), cx)
587 .map(|task| (kind.clone(), task))
588 })
589 }
590}
591
592#[derive(Clone)]
593struct ResolvedTasks {
594 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
595 position: Anchor,
596}
597
598#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
599struct BufferOffset(usize);
600
601// Addons allow storing per-editor state in other crates (e.g. Vim)
602pub trait Addon: 'static {
603 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
604
605 fn render_buffer_header_controls(
606 &self,
607 _: &ExcerptInfo,
608 _: &Window,
609 _: &App,
610 ) -> Option<AnyElement> {
611 None
612 }
613
614 fn to_any(&self) -> &dyn std::any::Any;
615}
616
617/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
618///
619/// See the [module level documentation](self) for more information.
620pub struct Editor {
621 focus_handle: FocusHandle,
622 last_focused_descendant: Option<WeakFocusHandle>,
623 /// The text buffer being edited
624 buffer: Entity<MultiBuffer>,
625 /// Map of how text in the buffer should be displayed.
626 /// Handles soft wraps, folds, fake inlay text insertions, etc.
627 pub display_map: Entity<DisplayMap>,
628 pub selections: SelectionsCollection,
629 pub scroll_manager: ScrollManager,
630 /// When inline assist editors are linked, they all render cursors because
631 /// typing enters text into each of them, even the ones that aren't focused.
632 pub(crate) show_cursor_when_unfocused: bool,
633 columnar_selection_tail: Option<Anchor>,
634 add_selections_state: Option<AddSelectionsState>,
635 select_next_state: Option<SelectNextState>,
636 select_prev_state: Option<SelectNextState>,
637 selection_history: SelectionHistory,
638 autoclose_regions: Vec<AutocloseRegion>,
639 snippet_stack: InvalidationStack<SnippetState>,
640 select_syntax_node_history: SelectSyntaxNodeHistory,
641 ime_transaction: Option<TransactionId>,
642 active_diagnostics: Option<ActiveDiagnosticGroup>,
643 show_inline_diagnostics: bool,
644 inline_diagnostics_update: Task<()>,
645 inline_diagnostics_enabled: bool,
646 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
647 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
648 hard_wrap: Option<usize>,
649
650 // TODO: make this a access method
651 pub project: Option<Entity<Project>>,
652 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
653 completion_provider: Option<Box<dyn CompletionProvider>>,
654 collaboration_hub: Option<Box<dyn CollaborationHub>>,
655 blink_manager: Entity<BlinkManager>,
656 show_cursor_names: bool,
657 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
658 pub show_local_selections: bool,
659 mode: EditorMode,
660 show_breadcrumbs: bool,
661 show_gutter: bool,
662 show_scrollbars: bool,
663 show_line_numbers: Option<bool>,
664 use_relative_line_numbers: Option<bool>,
665 show_git_diff_gutter: Option<bool>,
666 show_code_actions: Option<bool>,
667 show_runnables: Option<bool>,
668 show_breakpoints: Option<bool>,
669 show_wrap_guides: Option<bool>,
670 show_indent_guides: Option<bool>,
671 placeholder_text: Option<Arc<str>>,
672 highlight_order: usize,
673 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
674 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
675 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
676 scrollbar_marker_state: ScrollbarMarkerState,
677 active_indent_guides_state: ActiveIndentGuidesState,
678 nav_history: Option<ItemNavHistory>,
679 context_menu: RefCell<Option<CodeContextMenu>>,
680 mouse_context_menu: Option<MouseContextMenu>,
681 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
682 signature_help_state: SignatureHelpState,
683 auto_signature_help: Option<bool>,
684 find_all_references_task_sources: Vec<Anchor>,
685 next_completion_id: CompletionId,
686 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
687 code_actions_task: Option<Task<Result<()>>>,
688 selection_highlight_task: Option<Task<()>>,
689 document_highlights_task: Option<Task<()>>,
690 linked_editing_range_task: Option<Task<Option<()>>>,
691 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
692 pending_rename: Option<RenameState>,
693 searchable: bool,
694 cursor_shape: CursorShape,
695 current_line_highlight: Option<CurrentLineHighlight>,
696 collapse_matches: bool,
697 autoindent_mode: Option<AutoindentMode>,
698 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
699 input_enabled: bool,
700 use_modal_editing: bool,
701 read_only: bool,
702 leader_peer_id: Option<PeerId>,
703 remote_id: Option<ViewId>,
704 hover_state: HoverState,
705 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
706 gutter_hovered: bool,
707 hovered_link_state: Option<HoveredLinkState>,
708 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
709 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
710 active_inline_completion: Option<InlineCompletionState>,
711 /// Used to prevent flickering as the user types while the menu is open
712 stale_inline_completion_in_menu: Option<InlineCompletionState>,
713 edit_prediction_settings: EditPredictionSettings,
714 inline_completions_hidden_for_vim_mode: bool,
715 show_inline_completions_override: Option<bool>,
716 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
717 edit_prediction_preview: EditPredictionPreview,
718 edit_prediction_indent_conflict: bool,
719 edit_prediction_requires_modifier_in_indent_conflict: bool,
720 inlay_hint_cache: InlayHintCache,
721 next_inlay_id: usize,
722 _subscriptions: Vec<Subscription>,
723 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
724 gutter_dimensions: GutterDimensions,
725 style: Option<EditorStyle>,
726 text_style_refinement: Option<TextStyleRefinement>,
727 next_editor_action_id: EditorActionId,
728 editor_actions:
729 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
730 use_autoclose: bool,
731 use_auto_surround: bool,
732 auto_replace_emoji_shortcode: bool,
733 jsx_tag_auto_close_enabled_in_any_buffer: bool,
734 show_git_blame_gutter: bool,
735 show_git_blame_inline: bool,
736 show_git_blame_inline_delay_task: Option<Task<()>>,
737 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
738 git_blame_inline_enabled: bool,
739 serialize_dirty_buffers: bool,
740 show_selection_menu: Option<bool>,
741 blame: Option<Entity<GitBlame>>,
742 blame_subscription: Option<Subscription>,
743 custom_context_menu: Option<
744 Box<
745 dyn 'static
746 + Fn(
747 &mut Self,
748 DisplayPoint,
749 &mut Window,
750 &mut Context<Self>,
751 ) -> Option<Entity<ui::ContextMenu>>,
752 >,
753 >,
754 last_bounds: Option<Bounds<Pixels>>,
755 last_position_map: Option<Rc<PositionMap>>,
756 expect_bounds_change: Option<Bounds<Pixels>>,
757 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
758 tasks_update_task: Option<Task<()>>,
759 pub breakpoint_store: Option<Entity<BreakpointStore>>,
760 /// Allow's a user to create a breakpoint by selecting this indicator
761 /// It should be None while a user is not hovering over the gutter
762 /// Otherwise it represents the point that the breakpoint will be shown
763 pub gutter_breakpoint_indicator: Option<DisplayPoint>,
764 in_project_search: bool,
765 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
766 breadcrumb_header: Option<String>,
767 focused_block: Option<FocusedBlock>,
768 next_scroll_position: NextScrollCursorCenterTopBottom,
769 addons: HashMap<TypeId, Box<dyn Addon>>,
770 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
771 load_diff_task: Option<Shared<Task<()>>>,
772 selection_mark_mode: bool,
773 toggle_fold_multiple_buffers: Task<()>,
774 _scroll_cursor_center_top_bottom_task: Task<()>,
775 serialize_selections: Task<()>,
776 serialize_folds: Task<()>,
777}
778
779#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
780enum NextScrollCursorCenterTopBottom {
781 #[default]
782 Center,
783 Top,
784 Bottom,
785}
786
787impl NextScrollCursorCenterTopBottom {
788 fn next(&self) -> Self {
789 match self {
790 Self::Center => Self::Top,
791 Self::Top => Self::Bottom,
792 Self::Bottom => Self::Center,
793 }
794 }
795}
796
797#[derive(Clone)]
798pub struct EditorSnapshot {
799 pub mode: EditorMode,
800 show_gutter: bool,
801 show_line_numbers: Option<bool>,
802 show_git_diff_gutter: Option<bool>,
803 show_code_actions: Option<bool>,
804 show_runnables: Option<bool>,
805 show_breakpoints: Option<bool>,
806 git_blame_gutter_max_author_length: Option<usize>,
807 pub display_snapshot: DisplaySnapshot,
808 pub placeholder_text: Option<Arc<str>>,
809 is_focused: bool,
810 scroll_anchor: ScrollAnchor,
811 ongoing_scroll: OngoingScroll,
812 current_line_highlight: CurrentLineHighlight,
813 gutter_hovered: bool,
814}
815
816const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
817
818#[derive(Default, Debug, Clone, Copy)]
819pub struct GutterDimensions {
820 pub left_padding: Pixels,
821 pub right_padding: Pixels,
822 pub width: Pixels,
823 pub margin: Pixels,
824 pub git_blame_entries_width: Option<Pixels>,
825}
826
827impl GutterDimensions {
828 /// The full width of the space taken up by the gutter.
829 pub fn full_width(&self) -> Pixels {
830 self.margin + self.width
831 }
832
833 /// The width of the space reserved for the fold indicators,
834 /// use alongside 'justify_end' and `gutter_width` to
835 /// right align content with the line numbers
836 pub fn fold_area_width(&self) -> Pixels {
837 self.margin + self.right_padding
838 }
839}
840
841#[derive(Debug)]
842pub struct RemoteSelection {
843 pub replica_id: ReplicaId,
844 pub selection: Selection<Anchor>,
845 pub cursor_shape: CursorShape,
846 pub peer_id: PeerId,
847 pub line_mode: bool,
848 pub participant_index: Option<ParticipantIndex>,
849 pub user_name: Option<SharedString>,
850}
851
852#[derive(Clone, Debug)]
853struct SelectionHistoryEntry {
854 selections: Arc<[Selection<Anchor>]>,
855 select_next_state: Option<SelectNextState>,
856 select_prev_state: Option<SelectNextState>,
857 add_selections_state: Option<AddSelectionsState>,
858}
859
860enum SelectionHistoryMode {
861 Normal,
862 Undoing,
863 Redoing,
864}
865
866#[derive(Clone, PartialEq, Eq, Hash)]
867struct HoveredCursor {
868 replica_id: u16,
869 selection_id: usize,
870}
871
872impl Default for SelectionHistoryMode {
873 fn default() -> Self {
874 Self::Normal
875 }
876}
877
878#[derive(Default)]
879struct SelectionHistory {
880 #[allow(clippy::type_complexity)]
881 selections_by_transaction:
882 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
883 mode: SelectionHistoryMode,
884 undo_stack: VecDeque<SelectionHistoryEntry>,
885 redo_stack: VecDeque<SelectionHistoryEntry>,
886}
887
888impl SelectionHistory {
889 fn insert_transaction(
890 &mut self,
891 transaction_id: TransactionId,
892 selections: Arc<[Selection<Anchor>]>,
893 ) {
894 self.selections_by_transaction
895 .insert(transaction_id, (selections, None));
896 }
897
898 #[allow(clippy::type_complexity)]
899 fn transaction(
900 &self,
901 transaction_id: TransactionId,
902 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
903 self.selections_by_transaction.get(&transaction_id)
904 }
905
906 #[allow(clippy::type_complexity)]
907 fn transaction_mut(
908 &mut self,
909 transaction_id: TransactionId,
910 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
911 self.selections_by_transaction.get_mut(&transaction_id)
912 }
913
914 fn push(&mut self, entry: SelectionHistoryEntry) {
915 if !entry.selections.is_empty() {
916 match self.mode {
917 SelectionHistoryMode::Normal => {
918 self.push_undo(entry);
919 self.redo_stack.clear();
920 }
921 SelectionHistoryMode::Undoing => self.push_redo(entry),
922 SelectionHistoryMode::Redoing => self.push_undo(entry),
923 }
924 }
925 }
926
927 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
928 if self
929 .undo_stack
930 .back()
931 .map_or(true, |e| e.selections != entry.selections)
932 {
933 self.undo_stack.push_back(entry);
934 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
935 self.undo_stack.pop_front();
936 }
937 }
938 }
939
940 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
941 if self
942 .redo_stack
943 .back()
944 .map_or(true, |e| e.selections != entry.selections)
945 {
946 self.redo_stack.push_back(entry);
947 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
948 self.redo_stack.pop_front();
949 }
950 }
951 }
952}
953
954struct RowHighlight {
955 index: usize,
956 range: Range<Anchor>,
957 color: Hsla,
958 should_autoscroll: bool,
959}
960
961#[derive(Clone, Debug)]
962struct AddSelectionsState {
963 above: bool,
964 stack: Vec<usize>,
965}
966
967#[derive(Clone)]
968struct SelectNextState {
969 query: AhoCorasick,
970 wordwise: bool,
971 done: bool,
972}
973
974impl std::fmt::Debug for SelectNextState {
975 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
976 f.debug_struct(std::any::type_name::<Self>())
977 .field("wordwise", &self.wordwise)
978 .field("done", &self.done)
979 .finish()
980 }
981}
982
983#[derive(Debug)]
984struct AutocloseRegion {
985 selection_id: usize,
986 range: Range<Anchor>,
987 pair: BracketPair,
988}
989
990#[derive(Debug)]
991struct SnippetState {
992 ranges: Vec<Vec<Range<Anchor>>>,
993 active_index: usize,
994 choices: Vec<Option<Vec<String>>>,
995}
996
997#[doc(hidden)]
998pub struct RenameState {
999 pub range: Range<Anchor>,
1000 pub old_name: Arc<str>,
1001 pub editor: Entity<Editor>,
1002 block_id: CustomBlockId,
1003}
1004
1005struct InvalidationStack<T>(Vec<T>);
1006
1007struct RegisteredInlineCompletionProvider {
1008 provider: Arc<dyn InlineCompletionProviderHandle>,
1009 _subscription: Subscription,
1010}
1011
1012#[derive(Debug, PartialEq, Eq)]
1013struct ActiveDiagnosticGroup {
1014 primary_range: Range<Anchor>,
1015 primary_message: String,
1016 group_id: usize,
1017 blocks: HashMap<CustomBlockId, Diagnostic>,
1018 is_valid: bool,
1019}
1020
1021#[derive(Serialize, Deserialize, Clone, Debug)]
1022pub struct ClipboardSelection {
1023 /// The number of bytes in this selection.
1024 pub len: usize,
1025 /// Whether this was a full-line selection.
1026 pub is_entire_line: bool,
1027 /// The indentation of the first line when this content was originally copied.
1028 pub first_line_indent: u32,
1029}
1030
1031// selections, scroll behavior, was newest selection reversed
1032type SelectSyntaxNodeHistoryState = (
1033 Box<[Selection<usize>]>,
1034 SelectSyntaxNodeScrollBehavior,
1035 bool,
1036);
1037
1038#[derive(Default)]
1039struct SelectSyntaxNodeHistory {
1040 stack: Vec<SelectSyntaxNodeHistoryState>,
1041 // disable temporarily to allow changing selections without losing the stack
1042 pub disable_clearing: bool,
1043}
1044
1045impl SelectSyntaxNodeHistory {
1046 pub fn try_clear(&mut self) {
1047 if !self.disable_clearing {
1048 self.stack.clear();
1049 }
1050 }
1051
1052 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1053 self.stack.push(selection);
1054 }
1055
1056 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1057 self.stack.pop()
1058 }
1059}
1060
1061enum SelectSyntaxNodeScrollBehavior {
1062 CursorTop,
1063 CenterSelection,
1064 CursorBottom,
1065}
1066
1067#[derive(Debug)]
1068pub(crate) struct NavigationData {
1069 cursor_anchor: Anchor,
1070 cursor_position: Point,
1071 scroll_anchor: ScrollAnchor,
1072 scroll_top_row: u32,
1073}
1074
1075#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1076pub enum GotoDefinitionKind {
1077 Symbol,
1078 Declaration,
1079 Type,
1080 Implementation,
1081}
1082
1083#[derive(Debug, Clone)]
1084enum InlayHintRefreshReason {
1085 ModifiersChanged(bool),
1086 Toggle(bool),
1087 SettingsChange(InlayHintSettings),
1088 NewLinesShown,
1089 BufferEdited(HashSet<Arc<Language>>),
1090 RefreshRequested,
1091 ExcerptsRemoved(Vec<ExcerptId>),
1092}
1093
1094impl InlayHintRefreshReason {
1095 fn description(&self) -> &'static str {
1096 match self {
1097 Self::ModifiersChanged(_) => "modifiers changed",
1098 Self::Toggle(_) => "toggle",
1099 Self::SettingsChange(_) => "settings change",
1100 Self::NewLinesShown => "new lines shown",
1101 Self::BufferEdited(_) => "buffer edited",
1102 Self::RefreshRequested => "refresh requested",
1103 Self::ExcerptsRemoved(_) => "excerpts removed",
1104 }
1105 }
1106}
1107
1108pub enum FormatTarget {
1109 Buffers,
1110 Ranges(Vec<Range<MultiBufferPoint>>),
1111}
1112
1113pub(crate) struct FocusedBlock {
1114 id: BlockId,
1115 focus_handle: WeakFocusHandle,
1116}
1117
1118#[derive(Clone)]
1119enum JumpData {
1120 MultiBufferRow {
1121 row: MultiBufferRow,
1122 line_offset_from_top: u32,
1123 },
1124 MultiBufferPoint {
1125 excerpt_id: ExcerptId,
1126 position: Point,
1127 anchor: text::Anchor,
1128 line_offset_from_top: u32,
1129 },
1130}
1131
1132pub enum MultibufferSelectionMode {
1133 First,
1134 All,
1135}
1136
1137#[derive(Clone, Copy, Debug, Default)]
1138pub struct RewrapOptions {
1139 pub override_language_settings: bool,
1140 pub preserve_existing_whitespace: bool,
1141}
1142
1143impl Editor {
1144 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1145 let buffer = cx.new(|cx| Buffer::local("", cx));
1146 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1147 Self::new(
1148 EditorMode::SingleLine { auto_width: false },
1149 buffer,
1150 None,
1151 window,
1152 cx,
1153 )
1154 }
1155
1156 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1157 let buffer = cx.new(|cx| Buffer::local("", cx));
1158 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1159 Self::new(EditorMode::Full, buffer, None, window, cx)
1160 }
1161
1162 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1163 let buffer = cx.new(|cx| Buffer::local("", cx));
1164 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1165 Self::new(
1166 EditorMode::SingleLine { auto_width: true },
1167 buffer,
1168 None,
1169 window,
1170 cx,
1171 )
1172 }
1173
1174 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1175 let buffer = cx.new(|cx| Buffer::local("", cx));
1176 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1177 Self::new(
1178 EditorMode::AutoHeight { max_lines },
1179 buffer,
1180 None,
1181 window,
1182 cx,
1183 )
1184 }
1185
1186 pub fn for_buffer(
1187 buffer: Entity<Buffer>,
1188 project: Option<Entity<Project>>,
1189 window: &mut Window,
1190 cx: &mut Context<Self>,
1191 ) -> Self {
1192 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1193 Self::new(EditorMode::Full, buffer, project, window, cx)
1194 }
1195
1196 pub fn for_multibuffer(
1197 buffer: Entity<MultiBuffer>,
1198 project: Option<Entity<Project>>,
1199 window: &mut Window,
1200 cx: &mut Context<Self>,
1201 ) -> Self {
1202 Self::new(EditorMode::Full, buffer, project, window, cx)
1203 }
1204
1205 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1206 let mut clone = Self::new(
1207 self.mode,
1208 self.buffer.clone(),
1209 self.project.clone(),
1210 window,
1211 cx,
1212 );
1213 self.display_map.update(cx, |display_map, cx| {
1214 let snapshot = display_map.snapshot(cx);
1215 clone.display_map.update(cx, |display_map, cx| {
1216 display_map.set_state(&snapshot, cx);
1217 });
1218 });
1219 clone.folds_did_change(cx);
1220 clone.selections.clone_state(&self.selections);
1221 clone.scroll_manager.clone_state(&self.scroll_manager);
1222 clone.searchable = self.searchable;
1223 clone
1224 }
1225
1226 pub fn new(
1227 mode: EditorMode,
1228 buffer: Entity<MultiBuffer>,
1229 project: Option<Entity<Project>>,
1230 window: &mut Window,
1231 cx: &mut Context<Self>,
1232 ) -> Self {
1233 let style = window.text_style();
1234 let font_size = style.font_size.to_pixels(window.rem_size());
1235 let editor = cx.entity().downgrade();
1236 let fold_placeholder = FoldPlaceholder {
1237 constrain_width: true,
1238 render: Arc::new(move |fold_id, fold_range, cx| {
1239 let editor = editor.clone();
1240 div()
1241 .id(fold_id)
1242 .bg(cx.theme().colors().ghost_element_background)
1243 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1244 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1245 .rounded_xs()
1246 .size_full()
1247 .cursor_pointer()
1248 .child("⋯")
1249 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1250 .on_click(move |_, _window, cx| {
1251 editor
1252 .update(cx, |editor, cx| {
1253 editor.unfold_ranges(
1254 &[fold_range.start..fold_range.end],
1255 true,
1256 false,
1257 cx,
1258 );
1259 cx.stop_propagation();
1260 })
1261 .ok();
1262 })
1263 .into_any()
1264 }),
1265 merge_adjacent: true,
1266 ..Default::default()
1267 };
1268 let display_map = cx.new(|cx| {
1269 DisplayMap::new(
1270 buffer.clone(),
1271 style.font(),
1272 font_size,
1273 None,
1274 FILE_HEADER_HEIGHT,
1275 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1276 fold_placeholder,
1277 cx,
1278 )
1279 });
1280
1281 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1282
1283 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1284
1285 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1286 .then(|| language_settings::SoftWrap::None);
1287
1288 let mut project_subscriptions = Vec::new();
1289 if mode == EditorMode::Full {
1290 if let Some(project) = project.as_ref() {
1291 project_subscriptions.push(cx.subscribe_in(
1292 project,
1293 window,
1294 |editor, _, event, window, cx| match event {
1295 project::Event::RefreshCodeLens => {
1296 // we always query lens with actions, without storing them, always refreshing them
1297 }
1298 project::Event::RefreshInlayHints => {
1299 editor
1300 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1301 }
1302 project::Event::SnippetEdit(id, snippet_edits) => {
1303 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1304 let focus_handle = editor.focus_handle(cx);
1305 if focus_handle.is_focused(window) {
1306 let snapshot = buffer.read(cx).snapshot();
1307 for (range, snippet) in snippet_edits {
1308 let editor_range =
1309 language::range_from_lsp(*range).to_offset(&snapshot);
1310 editor
1311 .insert_snippet(
1312 &[editor_range],
1313 snippet.clone(),
1314 window,
1315 cx,
1316 )
1317 .ok();
1318 }
1319 }
1320 }
1321 }
1322 _ => {}
1323 },
1324 ));
1325 if let Some(task_inventory) = project
1326 .read(cx)
1327 .task_store()
1328 .read(cx)
1329 .task_inventory()
1330 .cloned()
1331 {
1332 project_subscriptions.push(cx.observe_in(
1333 &task_inventory,
1334 window,
1335 |editor, _, window, cx| {
1336 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1337 },
1338 ));
1339 };
1340
1341 project_subscriptions.push(cx.subscribe_in(
1342 &project.read(cx).breakpoint_store(),
1343 window,
1344 |editor, _, event, window, cx| match event {
1345 BreakpointStoreEvent::ActiveDebugLineChanged => {
1346 editor.go_to_active_debug_line(window, cx);
1347 }
1348 _ => {}
1349 },
1350 ));
1351 }
1352 }
1353
1354 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1355
1356 let inlay_hint_settings =
1357 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1358 let focus_handle = cx.focus_handle();
1359 cx.on_focus(&focus_handle, window, Self::handle_focus)
1360 .detach();
1361 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1362 .detach();
1363 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1364 .detach();
1365 cx.on_blur(&focus_handle, window, Self::handle_blur)
1366 .detach();
1367
1368 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1369 Some(false)
1370 } else {
1371 None
1372 };
1373
1374 let breakpoint_store = match (mode, project.as_ref()) {
1375 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1376 _ => None,
1377 };
1378
1379 let mut code_action_providers = Vec::new();
1380 let mut load_uncommitted_diff = None;
1381 if let Some(project) = project.clone() {
1382 load_uncommitted_diff = Some(
1383 get_uncommitted_diff_for_buffer(
1384 &project,
1385 buffer.read(cx).all_buffers(),
1386 buffer.clone(),
1387 cx,
1388 )
1389 .shared(),
1390 );
1391 code_action_providers.push(Rc::new(project) as Rc<_>);
1392 }
1393
1394 let mut this = Self {
1395 focus_handle,
1396 show_cursor_when_unfocused: false,
1397 last_focused_descendant: None,
1398 buffer: buffer.clone(),
1399 display_map: display_map.clone(),
1400 selections,
1401 scroll_manager: ScrollManager::new(cx),
1402 columnar_selection_tail: None,
1403 add_selections_state: None,
1404 select_next_state: None,
1405 select_prev_state: None,
1406 selection_history: Default::default(),
1407 autoclose_regions: Default::default(),
1408 snippet_stack: Default::default(),
1409 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
1410 ime_transaction: Default::default(),
1411 active_diagnostics: None,
1412 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1413 inline_diagnostics_update: Task::ready(()),
1414 inline_diagnostics: Vec::new(),
1415 soft_wrap_mode_override,
1416 hard_wrap: None,
1417 completion_provider: project.clone().map(|project| Box::new(project) as _),
1418 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1419 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1420 project,
1421 blink_manager: blink_manager.clone(),
1422 show_local_selections: true,
1423 show_scrollbars: true,
1424 mode,
1425 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1426 show_gutter: mode == EditorMode::Full,
1427 show_line_numbers: None,
1428 use_relative_line_numbers: None,
1429 show_git_diff_gutter: None,
1430 show_code_actions: None,
1431 show_runnables: None,
1432 show_breakpoints: None,
1433 show_wrap_guides: None,
1434 show_indent_guides,
1435 placeholder_text: None,
1436 highlight_order: 0,
1437 highlighted_rows: HashMap::default(),
1438 background_highlights: Default::default(),
1439 gutter_highlights: TreeMap::default(),
1440 scrollbar_marker_state: ScrollbarMarkerState::default(),
1441 active_indent_guides_state: ActiveIndentGuidesState::default(),
1442 nav_history: None,
1443 context_menu: RefCell::new(None),
1444 mouse_context_menu: None,
1445 completion_tasks: Default::default(),
1446 signature_help_state: SignatureHelpState::default(),
1447 auto_signature_help: None,
1448 find_all_references_task_sources: Vec::new(),
1449 next_completion_id: 0,
1450 next_inlay_id: 0,
1451 code_action_providers,
1452 available_code_actions: Default::default(),
1453 code_actions_task: Default::default(),
1454 selection_highlight_task: Default::default(),
1455 document_highlights_task: Default::default(),
1456 linked_editing_range_task: Default::default(),
1457 pending_rename: Default::default(),
1458 searchable: true,
1459 cursor_shape: EditorSettings::get_global(cx)
1460 .cursor_shape
1461 .unwrap_or_default(),
1462 current_line_highlight: None,
1463 autoindent_mode: Some(AutoindentMode::EachLine),
1464 collapse_matches: false,
1465 workspace: None,
1466 input_enabled: true,
1467 use_modal_editing: mode == EditorMode::Full,
1468 read_only: false,
1469 use_autoclose: true,
1470 use_auto_surround: true,
1471 auto_replace_emoji_shortcode: false,
1472 jsx_tag_auto_close_enabled_in_any_buffer: false,
1473 leader_peer_id: None,
1474 remote_id: None,
1475 hover_state: Default::default(),
1476 pending_mouse_down: None,
1477 hovered_link_state: Default::default(),
1478 edit_prediction_provider: None,
1479 active_inline_completion: None,
1480 stale_inline_completion_in_menu: None,
1481 edit_prediction_preview: EditPredictionPreview::Inactive {
1482 released_too_fast: false,
1483 },
1484 inline_diagnostics_enabled: mode == EditorMode::Full,
1485 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1486
1487 gutter_hovered: false,
1488 pixel_position_of_newest_cursor: None,
1489 last_bounds: None,
1490 last_position_map: None,
1491 expect_bounds_change: None,
1492 gutter_dimensions: GutterDimensions::default(),
1493 style: None,
1494 show_cursor_names: false,
1495 hovered_cursors: Default::default(),
1496 next_editor_action_id: EditorActionId::default(),
1497 editor_actions: Rc::default(),
1498 inline_completions_hidden_for_vim_mode: false,
1499 show_inline_completions_override: None,
1500 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1501 edit_prediction_settings: EditPredictionSettings::Disabled,
1502 edit_prediction_indent_conflict: false,
1503 edit_prediction_requires_modifier_in_indent_conflict: true,
1504 custom_context_menu: None,
1505 show_git_blame_gutter: false,
1506 show_git_blame_inline: false,
1507 show_selection_menu: None,
1508 show_git_blame_inline_delay_task: None,
1509 git_blame_inline_tooltip: None,
1510 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1511 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1512 .session
1513 .restore_unsaved_buffers,
1514 blame: None,
1515 blame_subscription: None,
1516 tasks: Default::default(),
1517
1518 breakpoint_store,
1519 gutter_breakpoint_indicator: None,
1520 _subscriptions: vec![
1521 cx.observe(&buffer, Self::on_buffer_changed),
1522 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1523 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1524 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1525 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1526 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1527 cx.observe_window_activation(window, |editor, window, cx| {
1528 let active = window.is_window_active();
1529 editor.blink_manager.update(cx, |blink_manager, cx| {
1530 if active {
1531 blink_manager.enable(cx);
1532 } else {
1533 blink_manager.disable(cx);
1534 }
1535 });
1536 }),
1537 ],
1538 tasks_update_task: None,
1539 linked_edit_ranges: Default::default(),
1540 in_project_search: false,
1541 previous_search_ranges: None,
1542 breadcrumb_header: None,
1543 focused_block: None,
1544 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1545 addons: HashMap::default(),
1546 registered_buffers: HashMap::default(),
1547 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1548 selection_mark_mode: false,
1549 toggle_fold_multiple_buffers: Task::ready(()),
1550 serialize_selections: Task::ready(()),
1551 serialize_folds: Task::ready(()),
1552 text_style_refinement: None,
1553 load_diff_task: load_uncommitted_diff,
1554 };
1555 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1556 this._subscriptions
1557 .push(cx.observe(breakpoints, |_, _, cx| {
1558 cx.notify();
1559 }));
1560 }
1561 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1562 this._subscriptions.extend(project_subscriptions);
1563
1564 this.end_selection(window, cx);
1565 this.scroll_manager.show_scrollbar(window, cx);
1566 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1567
1568 if mode == EditorMode::Full {
1569 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1570 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1571
1572 if this.git_blame_inline_enabled {
1573 this.git_blame_inline_enabled = true;
1574 this.start_git_blame_inline(false, window, cx);
1575 }
1576
1577 this.go_to_active_debug_line(window, cx);
1578
1579 if let Some(buffer) = buffer.read(cx).as_singleton() {
1580 if let Some(project) = this.project.as_ref() {
1581 let handle = project.update(cx, |project, cx| {
1582 project.register_buffer_with_language_servers(&buffer, cx)
1583 });
1584 this.registered_buffers
1585 .insert(buffer.read(cx).remote_id(), handle);
1586 }
1587 }
1588 }
1589
1590 this.report_editor_event("Editor Opened", None, cx);
1591 this
1592 }
1593
1594 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1595 self.mouse_context_menu
1596 .as_ref()
1597 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1598 }
1599
1600 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1601 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1602 }
1603
1604 fn key_context_internal(
1605 &self,
1606 has_active_edit_prediction: bool,
1607 window: &Window,
1608 cx: &App,
1609 ) -> KeyContext {
1610 let mut key_context = KeyContext::new_with_defaults();
1611 key_context.add("Editor");
1612 let mode = match self.mode {
1613 EditorMode::SingleLine { .. } => "single_line",
1614 EditorMode::AutoHeight { .. } => "auto_height",
1615 EditorMode::Full => "full",
1616 };
1617
1618 if EditorSettings::jupyter_enabled(cx) {
1619 key_context.add("jupyter");
1620 }
1621
1622 key_context.set("mode", mode);
1623 if self.pending_rename.is_some() {
1624 key_context.add("renaming");
1625 }
1626
1627 match self.context_menu.borrow().as_ref() {
1628 Some(CodeContextMenu::Completions(_)) => {
1629 key_context.add("menu");
1630 key_context.add("showing_completions");
1631 }
1632 Some(CodeContextMenu::CodeActions(_)) => {
1633 key_context.add("menu");
1634 key_context.add("showing_code_actions")
1635 }
1636 None => {}
1637 }
1638
1639 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1640 if !self.focus_handle(cx).contains_focused(window, cx)
1641 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1642 {
1643 for addon in self.addons.values() {
1644 addon.extend_key_context(&mut key_context, cx)
1645 }
1646 }
1647
1648 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1649 if let Some(extension) = singleton_buffer
1650 .read(cx)
1651 .file()
1652 .and_then(|file| file.path().extension()?.to_str())
1653 {
1654 key_context.set("extension", extension.to_string());
1655 }
1656 } else {
1657 key_context.add("multibuffer");
1658 }
1659
1660 if has_active_edit_prediction {
1661 if self.edit_prediction_in_conflict() {
1662 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1663 } else {
1664 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1665 key_context.add("copilot_suggestion");
1666 }
1667 }
1668
1669 if self.selection_mark_mode {
1670 key_context.add("selection_mode");
1671 }
1672
1673 key_context
1674 }
1675
1676 pub fn edit_prediction_in_conflict(&self) -> bool {
1677 if !self.show_edit_predictions_in_menu() {
1678 return false;
1679 }
1680
1681 let showing_completions = self
1682 .context_menu
1683 .borrow()
1684 .as_ref()
1685 .map_or(false, |context| {
1686 matches!(context, CodeContextMenu::Completions(_))
1687 });
1688
1689 showing_completions
1690 || self.edit_prediction_requires_modifier()
1691 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1692 // bindings to insert tab characters.
1693 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1694 }
1695
1696 pub fn accept_edit_prediction_keybind(
1697 &self,
1698 window: &Window,
1699 cx: &App,
1700 ) -> AcceptEditPredictionBinding {
1701 let key_context = self.key_context_internal(true, window, cx);
1702 let in_conflict = self.edit_prediction_in_conflict();
1703
1704 AcceptEditPredictionBinding(
1705 window
1706 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1707 .into_iter()
1708 .filter(|binding| {
1709 !in_conflict
1710 || binding
1711 .keystrokes()
1712 .first()
1713 .map_or(false, |keystroke| keystroke.modifiers.modified())
1714 })
1715 .rev()
1716 .min_by_key(|binding| {
1717 binding
1718 .keystrokes()
1719 .first()
1720 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1721 }),
1722 )
1723 }
1724
1725 pub fn new_file(
1726 workspace: &mut Workspace,
1727 _: &workspace::NewFile,
1728 window: &mut Window,
1729 cx: &mut Context<Workspace>,
1730 ) {
1731 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1732 "Failed to create buffer",
1733 window,
1734 cx,
1735 |e, _, _| match e.error_code() {
1736 ErrorCode::RemoteUpgradeRequired => Some(format!(
1737 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1738 e.error_tag("required").unwrap_or("the latest version")
1739 )),
1740 _ => None,
1741 },
1742 );
1743 }
1744
1745 pub fn new_in_workspace(
1746 workspace: &mut Workspace,
1747 window: &mut Window,
1748 cx: &mut Context<Workspace>,
1749 ) -> Task<Result<Entity<Editor>>> {
1750 let project = workspace.project().clone();
1751 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1752
1753 cx.spawn_in(window, async move |workspace, cx| {
1754 let buffer = create.await?;
1755 workspace.update_in(cx, |workspace, window, cx| {
1756 let editor =
1757 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1758 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1759 editor
1760 })
1761 })
1762 }
1763
1764 fn new_file_vertical(
1765 workspace: &mut Workspace,
1766 _: &workspace::NewFileSplitVertical,
1767 window: &mut Window,
1768 cx: &mut Context<Workspace>,
1769 ) {
1770 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1771 }
1772
1773 fn new_file_horizontal(
1774 workspace: &mut Workspace,
1775 _: &workspace::NewFileSplitHorizontal,
1776 window: &mut Window,
1777 cx: &mut Context<Workspace>,
1778 ) {
1779 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1780 }
1781
1782 fn new_file_in_direction(
1783 workspace: &mut Workspace,
1784 direction: SplitDirection,
1785 window: &mut Window,
1786 cx: &mut Context<Workspace>,
1787 ) {
1788 let project = workspace.project().clone();
1789 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1790
1791 cx.spawn_in(window, async move |workspace, cx| {
1792 let buffer = create.await?;
1793 workspace.update_in(cx, move |workspace, window, cx| {
1794 workspace.split_item(
1795 direction,
1796 Box::new(
1797 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1798 ),
1799 window,
1800 cx,
1801 )
1802 })?;
1803 anyhow::Ok(())
1804 })
1805 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1806 match e.error_code() {
1807 ErrorCode::RemoteUpgradeRequired => Some(format!(
1808 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1809 e.error_tag("required").unwrap_or("the latest version")
1810 )),
1811 _ => None,
1812 }
1813 });
1814 }
1815
1816 pub fn leader_peer_id(&self) -> Option<PeerId> {
1817 self.leader_peer_id
1818 }
1819
1820 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1821 &self.buffer
1822 }
1823
1824 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1825 self.workspace.as_ref()?.0.upgrade()
1826 }
1827
1828 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1829 self.buffer().read(cx).title(cx)
1830 }
1831
1832 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1833 let git_blame_gutter_max_author_length = self
1834 .render_git_blame_gutter(cx)
1835 .then(|| {
1836 if let Some(blame) = self.blame.as_ref() {
1837 let max_author_length =
1838 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1839 Some(max_author_length)
1840 } else {
1841 None
1842 }
1843 })
1844 .flatten();
1845
1846 EditorSnapshot {
1847 mode: self.mode,
1848 show_gutter: self.show_gutter,
1849 show_line_numbers: self.show_line_numbers,
1850 show_git_diff_gutter: self.show_git_diff_gutter,
1851 show_code_actions: self.show_code_actions,
1852 show_runnables: self.show_runnables,
1853 show_breakpoints: self.show_breakpoints,
1854 git_blame_gutter_max_author_length,
1855 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1856 scroll_anchor: self.scroll_manager.anchor(),
1857 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1858 placeholder_text: self.placeholder_text.clone(),
1859 is_focused: self.focus_handle.is_focused(window),
1860 current_line_highlight: self
1861 .current_line_highlight
1862 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1863 gutter_hovered: self.gutter_hovered,
1864 }
1865 }
1866
1867 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1868 self.buffer.read(cx).language_at(point, cx)
1869 }
1870
1871 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1872 self.buffer.read(cx).read(cx).file_at(point).cloned()
1873 }
1874
1875 pub fn active_excerpt(
1876 &self,
1877 cx: &App,
1878 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1879 self.buffer
1880 .read(cx)
1881 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1882 }
1883
1884 pub fn mode(&self) -> EditorMode {
1885 self.mode
1886 }
1887
1888 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1889 self.collaboration_hub.as_deref()
1890 }
1891
1892 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1893 self.collaboration_hub = Some(hub);
1894 }
1895
1896 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1897 self.in_project_search = in_project_search;
1898 }
1899
1900 pub fn set_custom_context_menu(
1901 &mut self,
1902 f: impl 'static
1903 + Fn(
1904 &mut Self,
1905 DisplayPoint,
1906 &mut Window,
1907 &mut Context<Self>,
1908 ) -> Option<Entity<ui::ContextMenu>>,
1909 ) {
1910 self.custom_context_menu = Some(Box::new(f))
1911 }
1912
1913 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1914 self.completion_provider = provider;
1915 }
1916
1917 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1918 self.semantics_provider.clone()
1919 }
1920
1921 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1922 self.semantics_provider = provider;
1923 }
1924
1925 pub fn set_edit_prediction_provider<T>(
1926 &mut self,
1927 provider: Option<Entity<T>>,
1928 window: &mut Window,
1929 cx: &mut Context<Self>,
1930 ) where
1931 T: EditPredictionProvider,
1932 {
1933 self.edit_prediction_provider =
1934 provider.map(|provider| RegisteredInlineCompletionProvider {
1935 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1936 if this.focus_handle.is_focused(window) {
1937 this.update_visible_inline_completion(window, cx);
1938 }
1939 }),
1940 provider: Arc::new(provider),
1941 });
1942 self.update_edit_prediction_settings(cx);
1943 self.refresh_inline_completion(false, false, window, cx);
1944 }
1945
1946 pub fn placeholder_text(&self) -> Option<&str> {
1947 self.placeholder_text.as_deref()
1948 }
1949
1950 pub fn set_placeholder_text(
1951 &mut self,
1952 placeholder_text: impl Into<Arc<str>>,
1953 cx: &mut Context<Self>,
1954 ) {
1955 let placeholder_text = Some(placeholder_text.into());
1956 if self.placeholder_text != placeholder_text {
1957 self.placeholder_text = placeholder_text;
1958 cx.notify();
1959 }
1960 }
1961
1962 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1963 self.cursor_shape = cursor_shape;
1964
1965 // Disrupt blink for immediate user feedback that the cursor shape has changed
1966 self.blink_manager.update(cx, BlinkManager::show_cursor);
1967
1968 cx.notify();
1969 }
1970
1971 pub fn set_current_line_highlight(
1972 &mut self,
1973 current_line_highlight: Option<CurrentLineHighlight>,
1974 ) {
1975 self.current_line_highlight = current_line_highlight;
1976 }
1977
1978 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1979 self.collapse_matches = collapse_matches;
1980 }
1981
1982 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1983 let buffers = self.buffer.read(cx).all_buffers();
1984 let Some(project) = self.project.as_ref() else {
1985 return;
1986 };
1987 project.update(cx, |project, cx| {
1988 for buffer in buffers {
1989 self.registered_buffers
1990 .entry(buffer.read(cx).remote_id())
1991 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1992 }
1993 })
1994 }
1995
1996 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1997 if self.collapse_matches {
1998 return range.start..range.start;
1999 }
2000 range.clone()
2001 }
2002
2003 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2004 if self.display_map.read(cx).clip_at_line_ends != clip {
2005 self.display_map
2006 .update(cx, |map, _| map.clip_at_line_ends = clip);
2007 }
2008 }
2009
2010 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2011 self.input_enabled = input_enabled;
2012 }
2013
2014 pub fn set_inline_completions_hidden_for_vim_mode(
2015 &mut self,
2016 hidden: bool,
2017 window: &mut Window,
2018 cx: &mut Context<Self>,
2019 ) {
2020 if hidden != self.inline_completions_hidden_for_vim_mode {
2021 self.inline_completions_hidden_for_vim_mode = hidden;
2022 if hidden {
2023 self.update_visible_inline_completion(window, cx);
2024 } else {
2025 self.refresh_inline_completion(true, false, window, cx);
2026 }
2027 }
2028 }
2029
2030 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2031 self.menu_inline_completions_policy = value;
2032 }
2033
2034 pub fn set_autoindent(&mut self, autoindent: bool) {
2035 if autoindent {
2036 self.autoindent_mode = Some(AutoindentMode::EachLine);
2037 } else {
2038 self.autoindent_mode = None;
2039 }
2040 }
2041
2042 pub fn read_only(&self, cx: &App) -> bool {
2043 self.read_only || self.buffer.read(cx).read_only()
2044 }
2045
2046 pub fn set_read_only(&mut self, read_only: bool) {
2047 self.read_only = read_only;
2048 }
2049
2050 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2051 self.use_autoclose = autoclose;
2052 }
2053
2054 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2055 self.use_auto_surround = auto_surround;
2056 }
2057
2058 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2059 self.auto_replace_emoji_shortcode = auto_replace;
2060 }
2061
2062 pub fn toggle_edit_predictions(
2063 &mut self,
2064 _: &ToggleEditPrediction,
2065 window: &mut Window,
2066 cx: &mut Context<Self>,
2067 ) {
2068 if self.show_inline_completions_override.is_some() {
2069 self.set_show_edit_predictions(None, window, cx);
2070 } else {
2071 let show_edit_predictions = !self.edit_predictions_enabled();
2072 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2073 }
2074 }
2075
2076 pub fn set_show_edit_predictions(
2077 &mut self,
2078 show_edit_predictions: Option<bool>,
2079 window: &mut Window,
2080 cx: &mut Context<Self>,
2081 ) {
2082 self.show_inline_completions_override = show_edit_predictions;
2083 self.update_edit_prediction_settings(cx);
2084
2085 if let Some(false) = show_edit_predictions {
2086 self.discard_inline_completion(false, cx);
2087 } else {
2088 self.refresh_inline_completion(false, true, window, cx);
2089 }
2090 }
2091
2092 fn inline_completions_disabled_in_scope(
2093 &self,
2094 buffer: &Entity<Buffer>,
2095 buffer_position: language::Anchor,
2096 cx: &App,
2097 ) -> bool {
2098 let snapshot = buffer.read(cx).snapshot();
2099 let settings = snapshot.settings_at(buffer_position, cx);
2100
2101 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2102 return false;
2103 };
2104
2105 scope.override_name().map_or(false, |scope_name| {
2106 settings
2107 .edit_predictions_disabled_in
2108 .iter()
2109 .any(|s| s == scope_name)
2110 })
2111 }
2112
2113 pub fn set_use_modal_editing(&mut self, to: bool) {
2114 self.use_modal_editing = to;
2115 }
2116
2117 pub fn use_modal_editing(&self) -> bool {
2118 self.use_modal_editing
2119 }
2120
2121 fn selections_did_change(
2122 &mut self,
2123 local: bool,
2124 old_cursor_position: &Anchor,
2125 show_completions: bool,
2126 window: &mut Window,
2127 cx: &mut Context<Self>,
2128 ) {
2129 window.invalidate_character_coordinates();
2130
2131 // Copy selections to primary selection buffer
2132 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2133 if local {
2134 let selections = self.selections.all::<usize>(cx);
2135 let buffer_handle = self.buffer.read(cx).read(cx);
2136
2137 let mut text = String::new();
2138 for (index, selection) in selections.iter().enumerate() {
2139 let text_for_selection = buffer_handle
2140 .text_for_range(selection.start..selection.end)
2141 .collect::<String>();
2142
2143 text.push_str(&text_for_selection);
2144 if index != selections.len() - 1 {
2145 text.push('\n');
2146 }
2147 }
2148
2149 if !text.is_empty() {
2150 cx.write_to_primary(ClipboardItem::new_string(text));
2151 }
2152 }
2153
2154 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2155 self.buffer.update(cx, |buffer, cx| {
2156 buffer.set_active_selections(
2157 &self.selections.disjoint_anchors(),
2158 self.selections.line_mode,
2159 self.cursor_shape,
2160 cx,
2161 )
2162 });
2163 }
2164 let display_map = self
2165 .display_map
2166 .update(cx, |display_map, cx| display_map.snapshot(cx));
2167 let buffer = &display_map.buffer_snapshot;
2168 self.add_selections_state = None;
2169 self.select_next_state = None;
2170 self.select_prev_state = None;
2171 self.select_syntax_node_history.try_clear();
2172 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2173 self.snippet_stack
2174 .invalidate(&self.selections.disjoint_anchors(), buffer);
2175 self.take_rename(false, window, cx);
2176
2177 let new_cursor_position = self.selections.newest_anchor().head();
2178
2179 self.push_to_nav_history(
2180 *old_cursor_position,
2181 Some(new_cursor_position.to_point(buffer)),
2182 false,
2183 cx,
2184 );
2185
2186 if local {
2187 let new_cursor_position = self.selections.newest_anchor().head();
2188 let mut context_menu = self.context_menu.borrow_mut();
2189 let completion_menu = match context_menu.as_ref() {
2190 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2191 _ => {
2192 *context_menu = None;
2193 None
2194 }
2195 };
2196 if let Some(buffer_id) = new_cursor_position.buffer_id {
2197 if !self.registered_buffers.contains_key(&buffer_id) {
2198 if let Some(project) = self.project.as_ref() {
2199 project.update(cx, |project, cx| {
2200 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2201 return;
2202 };
2203 self.registered_buffers.insert(
2204 buffer_id,
2205 project.register_buffer_with_language_servers(&buffer, cx),
2206 );
2207 })
2208 }
2209 }
2210 }
2211
2212 if let Some(completion_menu) = completion_menu {
2213 let cursor_position = new_cursor_position.to_offset(buffer);
2214 let (word_range, kind) =
2215 buffer.surrounding_word(completion_menu.initial_position, true);
2216 if kind == Some(CharKind::Word)
2217 && word_range.to_inclusive().contains(&cursor_position)
2218 {
2219 let mut completion_menu = completion_menu.clone();
2220 drop(context_menu);
2221
2222 let query = Self::completion_query(buffer, cursor_position);
2223 cx.spawn(async move |this, cx| {
2224 completion_menu
2225 .filter(query.as_deref(), cx.background_executor().clone())
2226 .await;
2227
2228 this.update(cx, |this, cx| {
2229 let mut context_menu = this.context_menu.borrow_mut();
2230 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2231 else {
2232 return;
2233 };
2234
2235 if menu.id > completion_menu.id {
2236 return;
2237 }
2238
2239 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2240 drop(context_menu);
2241 cx.notify();
2242 })
2243 })
2244 .detach();
2245
2246 if show_completions {
2247 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2248 }
2249 } else {
2250 drop(context_menu);
2251 self.hide_context_menu(window, cx);
2252 }
2253 } else {
2254 drop(context_menu);
2255 }
2256
2257 hide_hover(self, cx);
2258
2259 if old_cursor_position.to_display_point(&display_map).row()
2260 != new_cursor_position.to_display_point(&display_map).row()
2261 {
2262 self.available_code_actions.take();
2263 }
2264 self.refresh_code_actions(window, cx);
2265 self.refresh_document_highlights(cx);
2266 self.refresh_selected_text_highlights(window, cx);
2267 refresh_matching_bracket_highlights(self, window, cx);
2268 self.update_visible_inline_completion(window, cx);
2269 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2270 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2271 if self.git_blame_inline_enabled {
2272 self.start_inline_blame_timer(window, cx);
2273 }
2274 }
2275
2276 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2277 cx.emit(EditorEvent::SelectionsChanged { local });
2278
2279 let selections = &self.selections.disjoint;
2280 if selections.len() == 1 {
2281 cx.emit(SearchEvent::ActiveMatchChanged)
2282 }
2283 if local
2284 && self.is_singleton(cx)
2285 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2286 {
2287 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2288 let background_executor = cx.background_executor().clone();
2289 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2290 let snapshot = self.buffer().read(cx).snapshot(cx);
2291 let selections = selections.clone();
2292 self.serialize_selections = cx.background_spawn(async move {
2293 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2294 let selections = selections
2295 .iter()
2296 .map(|selection| {
2297 (
2298 selection.start.to_offset(&snapshot),
2299 selection.end.to_offset(&snapshot),
2300 )
2301 })
2302 .collect();
2303 DB.save_editor_selections(editor_id, workspace_id, selections)
2304 .await
2305 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2306 .log_err();
2307 });
2308 }
2309 }
2310
2311 cx.notify();
2312 }
2313
2314 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
2315 if !self.is_singleton(cx)
2316 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
2317 {
2318 return;
2319 }
2320
2321 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
2322 return;
2323 };
2324 let background_executor = cx.background_executor().clone();
2325 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2326 let snapshot = self.buffer().read(cx).snapshot(cx);
2327 let folds = self.display_map.update(cx, |display_map, cx| {
2328 display_map
2329 .snapshot(cx)
2330 .folds_in_range(0..snapshot.len())
2331 .map(|fold| {
2332 (
2333 fold.range.start.to_offset(&snapshot),
2334 fold.range.end.to_offset(&snapshot),
2335 )
2336 })
2337 .collect()
2338 });
2339 self.serialize_folds = cx.background_spawn(async move {
2340 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2341 DB.save_editor_folds(editor_id, workspace_id, folds)
2342 .await
2343 .with_context(|| format!("persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"))
2344 .log_err();
2345 });
2346 }
2347
2348 pub fn sync_selections(
2349 &mut self,
2350 other: Entity<Editor>,
2351 cx: &mut Context<Self>,
2352 ) -> gpui::Subscription {
2353 let other_selections = other.read(cx).selections.disjoint.to_vec();
2354 self.selections.change_with(cx, |selections| {
2355 selections.select_anchors(other_selections);
2356 });
2357
2358 let other_subscription =
2359 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2360 EditorEvent::SelectionsChanged { local: true } => {
2361 let other_selections = other.read(cx).selections.disjoint.to_vec();
2362 if other_selections.is_empty() {
2363 return;
2364 }
2365 this.selections.change_with(cx, |selections| {
2366 selections.select_anchors(other_selections);
2367 });
2368 }
2369 _ => {}
2370 });
2371
2372 let this_subscription =
2373 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2374 EditorEvent::SelectionsChanged { local: true } => {
2375 let these_selections = this.selections.disjoint.to_vec();
2376 if these_selections.is_empty() {
2377 return;
2378 }
2379 other.update(cx, |other_editor, cx| {
2380 other_editor.selections.change_with(cx, |selections| {
2381 selections.select_anchors(these_selections);
2382 })
2383 });
2384 }
2385 _ => {}
2386 });
2387
2388 Subscription::join(other_subscription, this_subscription)
2389 }
2390
2391 pub fn change_selections<R>(
2392 &mut self,
2393 autoscroll: Option<Autoscroll>,
2394 window: &mut Window,
2395 cx: &mut Context<Self>,
2396 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2397 ) -> R {
2398 self.change_selections_inner(autoscroll, true, window, cx, change)
2399 }
2400
2401 fn change_selections_inner<R>(
2402 &mut self,
2403 autoscroll: Option<Autoscroll>,
2404 request_completions: bool,
2405 window: &mut Window,
2406 cx: &mut Context<Self>,
2407 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2408 ) -> R {
2409 let old_cursor_position = self.selections.newest_anchor().head();
2410 self.push_to_selection_history();
2411
2412 let (changed, result) = self.selections.change_with(cx, change);
2413
2414 if changed {
2415 if let Some(autoscroll) = autoscroll {
2416 self.request_autoscroll(autoscroll, cx);
2417 }
2418 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2419
2420 if self.should_open_signature_help_automatically(
2421 &old_cursor_position,
2422 self.signature_help_state.backspace_pressed(),
2423 cx,
2424 ) {
2425 self.show_signature_help(&ShowSignatureHelp, window, cx);
2426 }
2427 self.signature_help_state.set_backspace_pressed(false);
2428 }
2429
2430 result
2431 }
2432
2433 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2434 where
2435 I: IntoIterator<Item = (Range<S>, T)>,
2436 S: ToOffset,
2437 T: Into<Arc<str>>,
2438 {
2439 if self.read_only(cx) {
2440 return;
2441 }
2442
2443 self.buffer
2444 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2445 }
2446
2447 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2448 where
2449 I: IntoIterator<Item = (Range<S>, T)>,
2450 S: ToOffset,
2451 T: Into<Arc<str>>,
2452 {
2453 if self.read_only(cx) {
2454 return;
2455 }
2456
2457 self.buffer.update(cx, |buffer, cx| {
2458 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2459 });
2460 }
2461
2462 pub fn edit_with_block_indent<I, S, T>(
2463 &mut self,
2464 edits: I,
2465 original_indent_columns: Vec<Option<u32>>,
2466 cx: &mut Context<Self>,
2467 ) where
2468 I: IntoIterator<Item = (Range<S>, T)>,
2469 S: ToOffset,
2470 T: Into<Arc<str>>,
2471 {
2472 if self.read_only(cx) {
2473 return;
2474 }
2475
2476 self.buffer.update(cx, |buffer, cx| {
2477 buffer.edit(
2478 edits,
2479 Some(AutoindentMode::Block {
2480 original_indent_columns,
2481 }),
2482 cx,
2483 )
2484 });
2485 }
2486
2487 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2488 self.hide_context_menu(window, cx);
2489
2490 match phase {
2491 SelectPhase::Begin {
2492 position,
2493 add,
2494 click_count,
2495 } => self.begin_selection(position, add, click_count, window, cx),
2496 SelectPhase::BeginColumnar {
2497 position,
2498 goal_column,
2499 reset,
2500 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2501 SelectPhase::Extend {
2502 position,
2503 click_count,
2504 } => self.extend_selection(position, click_count, window, cx),
2505 SelectPhase::Update {
2506 position,
2507 goal_column,
2508 scroll_delta,
2509 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2510 SelectPhase::End => self.end_selection(window, cx),
2511 }
2512 }
2513
2514 fn extend_selection(
2515 &mut self,
2516 position: DisplayPoint,
2517 click_count: usize,
2518 window: &mut Window,
2519 cx: &mut Context<Self>,
2520 ) {
2521 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2522 let tail = self.selections.newest::<usize>(cx).tail();
2523 self.begin_selection(position, false, click_count, window, cx);
2524
2525 let position = position.to_offset(&display_map, Bias::Left);
2526 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2527
2528 let mut pending_selection = self
2529 .selections
2530 .pending_anchor()
2531 .expect("extend_selection not called with pending selection");
2532 if position >= tail {
2533 pending_selection.start = tail_anchor;
2534 } else {
2535 pending_selection.end = tail_anchor;
2536 pending_selection.reversed = true;
2537 }
2538
2539 let mut pending_mode = self.selections.pending_mode().unwrap();
2540 match &mut pending_mode {
2541 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2542 _ => {}
2543 }
2544
2545 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2546 s.set_pending(pending_selection, pending_mode)
2547 });
2548 }
2549
2550 fn begin_selection(
2551 &mut self,
2552 position: DisplayPoint,
2553 add: bool,
2554 click_count: usize,
2555 window: &mut Window,
2556 cx: &mut Context<Self>,
2557 ) {
2558 if !self.focus_handle.is_focused(window) {
2559 self.last_focused_descendant = None;
2560 window.focus(&self.focus_handle);
2561 }
2562
2563 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2564 let buffer = &display_map.buffer_snapshot;
2565 let newest_selection = self.selections.newest_anchor().clone();
2566 let position = display_map.clip_point(position, Bias::Left);
2567
2568 let start;
2569 let end;
2570 let mode;
2571 let mut auto_scroll;
2572 match click_count {
2573 1 => {
2574 start = buffer.anchor_before(position.to_point(&display_map));
2575 end = start;
2576 mode = SelectMode::Character;
2577 auto_scroll = true;
2578 }
2579 2 => {
2580 let range = movement::surrounding_word(&display_map, position);
2581 start = buffer.anchor_before(range.start.to_point(&display_map));
2582 end = buffer.anchor_before(range.end.to_point(&display_map));
2583 mode = SelectMode::Word(start..end);
2584 auto_scroll = true;
2585 }
2586 3 => {
2587 let position = display_map
2588 .clip_point(position, Bias::Left)
2589 .to_point(&display_map);
2590 let line_start = display_map.prev_line_boundary(position).0;
2591 let next_line_start = buffer.clip_point(
2592 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2593 Bias::Left,
2594 );
2595 start = buffer.anchor_before(line_start);
2596 end = buffer.anchor_before(next_line_start);
2597 mode = SelectMode::Line(start..end);
2598 auto_scroll = true;
2599 }
2600 _ => {
2601 start = buffer.anchor_before(0);
2602 end = buffer.anchor_before(buffer.len());
2603 mode = SelectMode::All;
2604 auto_scroll = false;
2605 }
2606 }
2607 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2608
2609 let point_to_delete: Option<usize> = {
2610 let selected_points: Vec<Selection<Point>> =
2611 self.selections.disjoint_in_range(start..end, cx);
2612
2613 if !add || click_count > 1 {
2614 None
2615 } else if !selected_points.is_empty() {
2616 Some(selected_points[0].id)
2617 } else {
2618 let clicked_point_already_selected =
2619 self.selections.disjoint.iter().find(|selection| {
2620 selection.start.to_point(buffer) == start.to_point(buffer)
2621 || selection.end.to_point(buffer) == end.to_point(buffer)
2622 });
2623
2624 clicked_point_already_selected.map(|selection| selection.id)
2625 }
2626 };
2627
2628 let selections_count = self.selections.count();
2629
2630 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2631 if let Some(point_to_delete) = point_to_delete {
2632 s.delete(point_to_delete);
2633
2634 if selections_count == 1 {
2635 s.set_pending_anchor_range(start..end, mode);
2636 }
2637 } else {
2638 if !add {
2639 s.clear_disjoint();
2640 } else if click_count > 1 {
2641 s.delete(newest_selection.id)
2642 }
2643
2644 s.set_pending_anchor_range(start..end, mode);
2645 }
2646 });
2647 }
2648
2649 fn begin_columnar_selection(
2650 &mut self,
2651 position: DisplayPoint,
2652 goal_column: u32,
2653 reset: bool,
2654 window: &mut Window,
2655 cx: &mut Context<Self>,
2656 ) {
2657 if !self.focus_handle.is_focused(window) {
2658 self.last_focused_descendant = None;
2659 window.focus(&self.focus_handle);
2660 }
2661
2662 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2663
2664 if reset {
2665 let pointer_position = display_map
2666 .buffer_snapshot
2667 .anchor_before(position.to_point(&display_map));
2668
2669 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2670 s.clear_disjoint();
2671 s.set_pending_anchor_range(
2672 pointer_position..pointer_position,
2673 SelectMode::Character,
2674 );
2675 });
2676 }
2677
2678 let tail = self.selections.newest::<Point>(cx).tail();
2679 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2680
2681 if !reset {
2682 self.select_columns(
2683 tail.to_display_point(&display_map),
2684 position,
2685 goal_column,
2686 &display_map,
2687 window,
2688 cx,
2689 );
2690 }
2691 }
2692
2693 fn update_selection(
2694 &mut self,
2695 position: DisplayPoint,
2696 goal_column: u32,
2697 scroll_delta: gpui::Point<f32>,
2698 window: &mut Window,
2699 cx: &mut Context<Self>,
2700 ) {
2701 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2702
2703 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2704 let tail = tail.to_display_point(&display_map);
2705 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2706 } else if let Some(mut pending) = self.selections.pending_anchor() {
2707 let buffer = self.buffer.read(cx).snapshot(cx);
2708 let head;
2709 let tail;
2710 let mode = self.selections.pending_mode().unwrap();
2711 match &mode {
2712 SelectMode::Character => {
2713 head = position.to_point(&display_map);
2714 tail = pending.tail().to_point(&buffer);
2715 }
2716 SelectMode::Word(original_range) => {
2717 let original_display_range = original_range.start.to_display_point(&display_map)
2718 ..original_range.end.to_display_point(&display_map);
2719 let original_buffer_range = original_display_range.start.to_point(&display_map)
2720 ..original_display_range.end.to_point(&display_map);
2721 if movement::is_inside_word(&display_map, position)
2722 || original_display_range.contains(&position)
2723 {
2724 let word_range = movement::surrounding_word(&display_map, position);
2725 if word_range.start < original_display_range.start {
2726 head = word_range.start.to_point(&display_map);
2727 } else {
2728 head = word_range.end.to_point(&display_map);
2729 }
2730 } else {
2731 head = position.to_point(&display_map);
2732 }
2733
2734 if head <= original_buffer_range.start {
2735 tail = original_buffer_range.end;
2736 } else {
2737 tail = original_buffer_range.start;
2738 }
2739 }
2740 SelectMode::Line(original_range) => {
2741 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2742
2743 let position = display_map
2744 .clip_point(position, Bias::Left)
2745 .to_point(&display_map);
2746 let line_start = display_map.prev_line_boundary(position).0;
2747 let next_line_start = buffer.clip_point(
2748 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2749 Bias::Left,
2750 );
2751
2752 if line_start < original_range.start {
2753 head = line_start
2754 } else {
2755 head = next_line_start
2756 }
2757
2758 if head <= original_range.start {
2759 tail = original_range.end;
2760 } else {
2761 tail = original_range.start;
2762 }
2763 }
2764 SelectMode::All => {
2765 return;
2766 }
2767 };
2768
2769 if head < tail {
2770 pending.start = buffer.anchor_before(head);
2771 pending.end = buffer.anchor_before(tail);
2772 pending.reversed = true;
2773 } else {
2774 pending.start = buffer.anchor_before(tail);
2775 pending.end = buffer.anchor_before(head);
2776 pending.reversed = false;
2777 }
2778
2779 self.change_selections(None, window, cx, |s| {
2780 s.set_pending(pending, mode);
2781 });
2782 } else {
2783 log::error!("update_selection dispatched with no pending selection");
2784 return;
2785 }
2786
2787 self.apply_scroll_delta(scroll_delta, window, cx);
2788 cx.notify();
2789 }
2790
2791 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2792 self.columnar_selection_tail.take();
2793 if self.selections.pending_anchor().is_some() {
2794 let selections = self.selections.all::<usize>(cx);
2795 self.change_selections(None, window, cx, |s| {
2796 s.select(selections);
2797 s.clear_pending();
2798 });
2799 }
2800 }
2801
2802 fn select_columns(
2803 &mut self,
2804 tail: DisplayPoint,
2805 head: DisplayPoint,
2806 goal_column: u32,
2807 display_map: &DisplaySnapshot,
2808 window: &mut Window,
2809 cx: &mut Context<Self>,
2810 ) {
2811 let start_row = cmp::min(tail.row(), head.row());
2812 let end_row = cmp::max(tail.row(), head.row());
2813 let start_column = cmp::min(tail.column(), goal_column);
2814 let end_column = cmp::max(tail.column(), goal_column);
2815 let reversed = start_column < tail.column();
2816
2817 let selection_ranges = (start_row.0..=end_row.0)
2818 .map(DisplayRow)
2819 .filter_map(|row| {
2820 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2821 let start = display_map
2822 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2823 .to_point(display_map);
2824 let end = display_map
2825 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2826 .to_point(display_map);
2827 if reversed {
2828 Some(end..start)
2829 } else {
2830 Some(start..end)
2831 }
2832 } else {
2833 None
2834 }
2835 })
2836 .collect::<Vec<_>>();
2837
2838 self.change_selections(None, window, cx, |s| {
2839 s.select_ranges(selection_ranges);
2840 });
2841 cx.notify();
2842 }
2843
2844 pub fn has_pending_nonempty_selection(&self) -> bool {
2845 let pending_nonempty_selection = match self.selections.pending_anchor() {
2846 Some(Selection { start, end, .. }) => start != end,
2847 None => false,
2848 };
2849
2850 pending_nonempty_selection
2851 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2852 }
2853
2854 pub fn has_pending_selection(&self) -> bool {
2855 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2856 }
2857
2858 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2859 self.selection_mark_mode = false;
2860
2861 if self.clear_expanded_diff_hunks(cx) {
2862 cx.notify();
2863 return;
2864 }
2865 if self.dismiss_menus_and_popups(true, window, cx) {
2866 return;
2867 }
2868
2869 if self.mode == EditorMode::Full
2870 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2871 {
2872 return;
2873 }
2874
2875 cx.propagate();
2876 }
2877
2878 pub fn dismiss_menus_and_popups(
2879 &mut self,
2880 is_user_requested: bool,
2881 window: &mut Window,
2882 cx: &mut Context<Self>,
2883 ) -> bool {
2884 if self.take_rename(false, window, cx).is_some() {
2885 return true;
2886 }
2887
2888 if hide_hover(self, cx) {
2889 return true;
2890 }
2891
2892 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2893 return true;
2894 }
2895
2896 if self.hide_context_menu(window, cx).is_some() {
2897 return true;
2898 }
2899
2900 if self.mouse_context_menu.take().is_some() {
2901 return true;
2902 }
2903
2904 if is_user_requested && self.discard_inline_completion(true, cx) {
2905 return true;
2906 }
2907
2908 if self.snippet_stack.pop().is_some() {
2909 return true;
2910 }
2911
2912 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2913 self.dismiss_diagnostics(cx);
2914 return true;
2915 }
2916
2917 false
2918 }
2919
2920 fn linked_editing_ranges_for(
2921 &self,
2922 selection: Range<text::Anchor>,
2923 cx: &App,
2924 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2925 if self.linked_edit_ranges.is_empty() {
2926 return None;
2927 }
2928 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2929 selection.end.buffer_id.and_then(|end_buffer_id| {
2930 if selection.start.buffer_id != Some(end_buffer_id) {
2931 return None;
2932 }
2933 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2934 let snapshot = buffer.read(cx).snapshot();
2935 self.linked_edit_ranges
2936 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2937 .map(|ranges| (ranges, snapshot, buffer))
2938 })?;
2939 use text::ToOffset as TO;
2940 // find offset from the start of current range to current cursor position
2941 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2942
2943 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2944 let start_difference = start_offset - start_byte_offset;
2945 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2946 let end_difference = end_offset - start_byte_offset;
2947 // Current range has associated linked ranges.
2948 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2949 for range in linked_ranges.iter() {
2950 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2951 let end_offset = start_offset + end_difference;
2952 let start_offset = start_offset + start_difference;
2953 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2954 continue;
2955 }
2956 if self.selections.disjoint_anchor_ranges().any(|s| {
2957 if s.start.buffer_id != selection.start.buffer_id
2958 || s.end.buffer_id != selection.end.buffer_id
2959 {
2960 return false;
2961 }
2962 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2963 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2964 }) {
2965 continue;
2966 }
2967 let start = buffer_snapshot.anchor_after(start_offset);
2968 let end = buffer_snapshot.anchor_after(end_offset);
2969 linked_edits
2970 .entry(buffer.clone())
2971 .or_default()
2972 .push(start..end);
2973 }
2974 Some(linked_edits)
2975 }
2976
2977 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2978 let text: Arc<str> = text.into();
2979
2980 if self.read_only(cx) {
2981 return;
2982 }
2983
2984 let selections = self.selections.all_adjusted(cx);
2985 let mut bracket_inserted = false;
2986 let mut edits = Vec::new();
2987 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2988 let mut new_selections = Vec::with_capacity(selections.len());
2989 let mut new_autoclose_regions = Vec::new();
2990 let snapshot = self.buffer.read(cx).read(cx);
2991
2992 for (selection, autoclose_region) in
2993 self.selections_with_autoclose_regions(selections, &snapshot)
2994 {
2995 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2996 // Determine if the inserted text matches the opening or closing
2997 // bracket of any of this language's bracket pairs.
2998 let mut bracket_pair = None;
2999 let mut is_bracket_pair_start = false;
3000 let mut is_bracket_pair_end = false;
3001 if !text.is_empty() {
3002 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3003 // and they are removing the character that triggered IME popup.
3004 for (pair, enabled) in scope.brackets() {
3005 if !pair.close && !pair.surround {
3006 continue;
3007 }
3008
3009 if enabled && pair.start.ends_with(text.as_ref()) {
3010 let prefix_len = pair.start.len() - text.len();
3011 let preceding_text_matches_prefix = prefix_len == 0
3012 || (selection.start.column >= (prefix_len as u32)
3013 && snapshot.contains_str_at(
3014 Point::new(
3015 selection.start.row,
3016 selection.start.column - (prefix_len as u32),
3017 ),
3018 &pair.start[..prefix_len],
3019 ));
3020 if preceding_text_matches_prefix {
3021 bracket_pair = Some(pair.clone());
3022 is_bracket_pair_start = true;
3023 break;
3024 }
3025 }
3026 if pair.end.as_str() == text.as_ref() {
3027 bracket_pair = Some(pair.clone());
3028 is_bracket_pair_end = true;
3029 break;
3030 }
3031 }
3032 }
3033
3034 if let Some(bracket_pair) = bracket_pair {
3035 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3036 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3037 let auto_surround =
3038 self.use_auto_surround && snapshot_settings.use_auto_surround;
3039 if selection.is_empty() {
3040 if is_bracket_pair_start {
3041 // If the inserted text is a suffix of an opening bracket and the
3042 // selection is preceded by the rest of the opening bracket, then
3043 // insert the closing bracket.
3044 let following_text_allows_autoclose = snapshot
3045 .chars_at(selection.start)
3046 .next()
3047 .map_or(true, |c| scope.should_autoclose_before(c));
3048
3049 let preceding_text_allows_autoclose = selection.start.column == 0
3050 || snapshot.reversed_chars_at(selection.start).next().map_or(
3051 true,
3052 |c| {
3053 bracket_pair.start != bracket_pair.end
3054 || !snapshot
3055 .char_classifier_at(selection.start)
3056 .is_word(c)
3057 },
3058 );
3059
3060 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3061 && bracket_pair.start.len() == 1
3062 {
3063 let target = bracket_pair.start.chars().next().unwrap();
3064 let current_line_count = snapshot
3065 .reversed_chars_at(selection.start)
3066 .take_while(|&c| c != '\n')
3067 .filter(|&c| c == target)
3068 .count();
3069 current_line_count % 2 == 1
3070 } else {
3071 false
3072 };
3073
3074 if autoclose
3075 && bracket_pair.close
3076 && following_text_allows_autoclose
3077 && preceding_text_allows_autoclose
3078 && !is_closing_quote
3079 {
3080 let anchor = snapshot.anchor_before(selection.end);
3081 new_selections.push((selection.map(|_| anchor), text.len()));
3082 new_autoclose_regions.push((
3083 anchor,
3084 text.len(),
3085 selection.id,
3086 bracket_pair.clone(),
3087 ));
3088 edits.push((
3089 selection.range(),
3090 format!("{}{}", text, bracket_pair.end).into(),
3091 ));
3092 bracket_inserted = true;
3093 continue;
3094 }
3095 }
3096
3097 if let Some(region) = autoclose_region {
3098 // If the selection is followed by an auto-inserted closing bracket,
3099 // then don't insert that closing bracket again; just move the selection
3100 // past the closing bracket.
3101 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3102 && text.as_ref() == region.pair.end.as_str();
3103 if should_skip {
3104 let anchor = snapshot.anchor_after(selection.end);
3105 new_selections
3106 .push((selection.map(|_| anchor), region.pair.end.len()));
3107 continue;
3108 }
3109 }
3110
3111 let always_treat_brackets_as_autoclosed = snapshot
3112 .language_settings_at(selection.start, cx)
3113 .always_treat_brackets_as_autoclosed;
3114 if always_treat_brackets_as_autoclosed
3115 && is_bracket_pair_end
3116 && snapshot.contains_str_at(selection.end, text.as_ref())
3117 {
3118 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3119 // and the inserted text is a closing bracket and the selection is followed
3120 // by the closing bracket then move the selection past the closing bracket.
3121 let anchor = snapshot.anchor_after(selection.end);
3122 new_selections.push((selection.map(|_| anchor), text.len()));
3123 continue;
3124 }
3125 }
3126 // If an opening bracket is 1 character long and is typed while
3127 // text is selected, then surround that text with the bracket pair.
3128 else if auto_surround
3129 && bracket_pair.surround
3130 && is_bracket_pair_start
3131 && bracket_pair.start.chars().count() == 1
3132 {
3133 edits.push((selection.start..selection.start, text.clone()));
3134 edits.push((
3135 selection.end..selection.end,
3136 bracket_pair.end.as_str().into(),
3137 ));
3138 bracket_inserted = true;
3139 new_selections.push((
3140 Selection {
3141 id: selection.id,
3142 start: snapshot.anchor_after(selection.start),
3143 end: snapshot.anchor_before(selection.end),
3144 reversed: selection.reversed,
3145 goal: selection.goal,
3146 },
3147 0,
3148 ));
3149 continue;
3150 }
3151 }
3152 }
3153
3154 if self.auto_replace_emoji_shortcode
3155 && selection.is_empty()
3156 && text.as_ref().ends_with(':')
3157 {
3158 if let Some(possible_emoji_short_code) =
3159 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3160 {
3161 if !possible_emoji_short_code.is_empty() {
3162 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3163 let emoji_shortcode_start = Point::new(
3164 selection.start.row,
3165 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3166 );
3167
3168 // Remove shortcode from buffer
3169 edits.push((
3170 emoji_shortcode_start..selection.start,
3171 "".to_string().into(),
3172 ));
3173 new_selections.push((
3174 Selection {
3175 id: selection.id,
3176 start: snapshot.anchor_after(emoji_shortcode_start),
3177 end: snapshot.anchor_before(selection.start),
3178 reversed: selection.reversed,
3179 goal: selection.goal,
3180 },
3181 0,
3182 ));
3183
3184 // Insert emoji
3185 let selection_start_anchor = snapshot.anchor_after(selection.start);
3186 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3187 edits.push((selection.start..selection.end, emoji.to_string().into()));
3188
3189 continue;
3190 }
3191 }
3192 }
3193 }
3194
3195 // If not handling any auto-close operation, then just replace the selected
3196 // text with the given input and move the selection to the end of the
3197 // newly inserted text.
3198 let anchor = snapshot.anchor_after(selection.end);
3199 if !self.linked_edit_ranges.is_empty() {
3200 let start_anchor = snapshot.anchor_before(selection.start);
3201
3202 let is_word_char = text.chars().next().map_or(true, |char| {
3203 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3204 classifier.is_word(char)
3205 });
3206
3207 if is_word_char {
3208 if let Some(ranges) = self
3209 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3210 {
3211 for (buffer, edits) in ranges {
3212 linked_edits
3213 .entry(buffer.clone())
3214 .or_default()
3215 .extend(edits.into_iter().map(|range| (range, text.clone())));
3216 }
3217 }
3218 }
3219 }
3220
3221 new_selections.push((selection.map(|_| anchor), 0));
3222 edits.push((selection.start..selection.end, text.clone()));
3223 }
3224
3225 drop(snapshot);
3226
3227 self.transact(window, cx, |this, window, cx| {
3228 let initial_buffer_versions =
3229 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3230
3231 this.buffer.update(cx, |buffer, cx| {
3232 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3233 });
3234 for (buffer, edits) in linked_edits {
3235 buffer.update(cx, |buffer, cx| {
3236 let snapshot = buffer.snapshot();
3237 let edits = edits
3238 .into_iter()
3239 .map(|(range, text)| {
3240 use text::ToPoint as TP;
3241 let end_point = TP::to_point(&range.end, &snapshot);
3242 let start_point = TP::to_point(&range.start, &snapshot);
3243 (start_point..end_point, text)
3244 })
3245 .sorted_by_key(|(range, _)| range.start)
3246 .collect::<Vec<_>>();
3247 buffer.edit(edits, None, cx);
3248 })
3249 }
3250 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3251 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3252 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3253 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3254 .zip(new_selection_deltas)
3255 .map(|(selection, delta)| Selection {
3256 id: selection.id,
3257 start: selection.start + delta,
3258 end: selection.end + delta,
3259 reversed: selection.reversed,
3260 goal: SelectionGoal::None,
3261 })
3262 .collect::<Vec<_>>();
3263
3264 let mut i = 0;
3265 for (position, delta, selection_id, pair) in new_autoclose_regions {
3266 let position = position.to_offset(&map.buffer_snapshot) + delta;
3267 let start = map.buffer_snapshot.anchor_before(position);
3268 let end = map.buffer_snapshot.anchor_after(position);
3269 while let Some(existing_state) = this.autoclose_regions.get(i) {
3270 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3271 Ordering::Less => i += 1,
3272 Ordering::Greater => break,
3273 Ordering::Equal => {
3274 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3275 Ordering::Less => i += 1,
3276 Ordering::Equal => break,
3277 Ordering::Greater => break,
3278 }
3279 }
3280 }
3281 }
3282 this.autoclose_regions.insert(
3283 i,
3284 AutocloseRegion {
3285 selection_id,
3286 range: start..end,
3287 pair,
3288 },
3289 );
3290 }
3291
3292 let had_active_inline_completion = this.has_active_inline_completion();
3293 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3294 s.select(new_selections)
3295 });
3296
3297 if !bracket_inserted {
3298 if let Some(on_type_format_task) =
3299 this.trigger_on_type_formatting(text.to_string(), window, cx)
3300 {
3301 on_type_format_task.detach_and_log_err(cx);
3302 }
3303 }
3304
3305 let editor_settings = EditorSettings::get_global(cx);
3306 if bracket_inserted
3307 && (editor_settings.auto_signature_help
3308 || editor_settings.show_signature_help_after_edits)
3309 {
3310 this.show_signature_help(&ShowSignatureHelp, window, cx);
3311 }
3312
3313 let trigger_in_words =
3314 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3315 if this.hard_wrap.is_some() {
3316 let latest: Range<Point> = this.selections.newest(cx).range();
3317 if latest.is_empty()
3318 && this
3319 .buffer()
3320 .read(cx)
3321 .snapshot(cx)
3322 .line_len(MultiBufferRow(latest.start.row))
3323 == latest.start.column
3324 {
3325 this.rewrap_impl(
3326 RewrapOptions {
3327 override_language_settings: true,
3328 preserve_existing_whitespace: true,
3329 },
3330 cx,
3331 )
3332 }
3333 }
3334 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3335 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3336 this.refresh_inline_completion(true, false, window, cx);
3337 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3338 });
3339 }
3340
3341 fn find_possible_emoji_shortcode_at_position(
3342 snapshot: &MultiBufferSnapshot,
3343 position: Point,
3344 ) -> Option<String> {
3345 let mut chars = Vec::new();
3346 let mut found_colon = false;
3347 for char in snapshot.reversed_chars_at(position).take(100) {
3348 // Found a possible emoji shortcode in the middle of the buffer
3349 if found_colon {
3350 if char.is_whitespace() {
3351 chars.reverse();
3352 return Some(chars.iter().collect());
3353 }
3354 // If the previous character is not a whitespace, we are in the middle of a word
3355 // and we only want to complete the shortcode if the word is made up of other emojis
3356 let mut containing_word = String::new();
3357 for ch in snapshot
3358 .reversed_chars_at(position)
3359 .skip(chars.len() + 1)
3360 .take(100)
3361 {
3362 if ch.is_whitespace() {
3363 break;
3364 }
3365 containing_word.push(ch);
3366 }
3367 let containing_word = containing_word.chars().rev().collect::<String>();
3368 if util::word_consists_of_emojis(containing_word.as_str()) {
3369 chars.reverse();
3370 return Some(chars.iter().collect());
3371 }
3372 }
3373
3374 if char.is_whitespace() || !char.is_ascii() {
3375 return None;
3376 }
3377 if char == ':' {
3378 found_colon = true;
3379 } else {
3380 chars.push(char);
3381 }
3382 }
3383 // Found a possible emoji shortcode at the beginning of the buffer
3384 chars.reverse();
3385 Some(chars.iter().collect())
3386 }
3387
3388 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3389 self.transact(window, cx, |this, window, cx| {
3390 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3391 let selections = this.selections.all::<usize>(cx);
3392 let multi_buffer = this.buffer.read(cx);
3393 let buffer = multi_buffer.snapshot(cx);
3394 selections
3395 .iter()
3396 .map(|selection| {
3397 let start_point = selection.start.to_point(&buffer);
3398 let mut indent =
3399 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3400 indent.len = cmp::min(indent.len, start_point.column);
3401 let start = selection.start;
3402 let end = selection.end;
3403 let selection_is_empty = start == end;
3404 let language_scope = buffer.language_scope_at(start);
3405 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3406 &language_scope
3407 {
3408 let insert_extra_newline =
3409 insert_extra_newline_brackets(&buffer, start..end, language)
3410 || insert_extra_newline_tree_sitter(&buffer, start..end);
3411
3412 // Comment extension on newline is allowed only for cursor selections
3413 let comment_delimiter = maybe!({
3414 if !selection_is_empty {
3415 return None;
3416 }
3417
3418 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3419 return None;
3420 }
3421
3422 let delimiters = language.line_comment_prefixes();
3423 let max_len_of_delimiter =
3424 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3425 let (snapshot, range) =
3426 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3427
3428 let mut index_of_first_non_whitespace = 0;
3429 let comment_candidate = snapshot
3430 .chars_for_range(range)
3431 .skip_while(|c| {
3432 let should_skip = c.is_whitespace();
3433 if should_skip {
3434 index_of_first_non_whitespace += 1;
3435 }
3436 should_skip
3437 })
3438 .take(max_len_of_delimiter)
3439 .collect::<String>();
3440 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3441 comment_candidate.starts_with(comment_prefix.as_ref())
3442 })?;
3443 let cursor_is_placed_after_comment_marker =
3444 index_of_first_non_whitespace + comment_prefix.len()
3445 <= start_point.column as usize;
3446 if cursor_is_placed_after_comment_marker {
3447 Some(comment_prefix.clone())
3448 } else {
3449 None
3450 }
3451 });
3452 (comment_delimiter, insert_extra_newline)
3453 } else {
3454 (None, false)
3455 };
3456
3457 let capacity_for_delimiter = comment_delimiter
3458 .as_deref()
3459 .map(str::len)
3460 .unwrap_or_default();
3461 let mut new_text =
3462 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3463 new_text.push('\n');
3464 new_text.extend(indent.chars());
3465 if let Some(delimiter) = &comment_delimiter {
3466 new_text.push_str(delimiter);
3467 }
3468 if insert_extra_newline {
3469 new_text = new_text.repeat(2);
3470 }
3471
3472 let anchor = buffer.anchor_after(end);
3473 let new_selection = selection.map(|_| anchor);
3474 (
3475 (start..end, new_text),
3476 (insert_extra_newline, new_selection),
3477 )
3478 })
3479 .unzip()
3480 };
3481
3482 this.edit_with_autoindent(edits, cx);
3483 let buffer = this.buffer.read(cx).snapshot(cx);
3484 let new_selections = selection_fixup_info
3485 .into_iter()
3486 .map(|(extra_newline_inserted, new_selection)| {
3487 let mut cursor = new_selection.end.to_point(&buffer);
3488 if extra_newline_inserted {
3489 cursor.row -= 1;
3490 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3491 }
3492 new_selection.map(|_| cursor)
3493 })
3494 .collect();
3495
3496 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3497 s.select(new_selections)
3498 });
3499 this.refresh_inline_completion(true, false, window, cx);
3500 });
3501 }
3502
3503 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3504 let buffer = self.buffer.read(cx);
3505 let snapshot = buffer.snapshot(cx);
3506
3507 let mut edits = Vec::new();
3508 let mut rows = Vec::new();
3509
3510 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3511 let cursor = selection.head();
3512 let row = cursor.row;
3513
3514 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3515
3516 let newline = "\n".to_string();
3517 edits.push((start_of_line..start_of_line, newline));
3518
3519 rows.push(row + rows_inserted as u32);
3520 }
3521
3522 self.transact(window, cx, |editor, window, cx| {
3523 editor.edit(edits, cx);
3524
3525 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3526 let mut index = 0;
3527 s.move_cursors_with(|map, _, _| {
3528 let row = rows[index];
3529 index += 1;
3530
3531 let point = Point::new(row, 0);
3532 let boundary = map.next_line_boundary(point).1;
3533 let clipped = map.clip_point(boundary, Bias::Left);
3534
3535 (clipped, SelectionGoal::None)
3536 });
3537 });
3538
3539 let mut indent_edits = Vec::new();
3540 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3541 for row in rows {
3542 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3543 for (row, indent) in indents {
3544 if indent.len == 0 {
3545 continue;
3546 }
3547
3548 let text = match indent.kind {
3549 IndentKind::Space => " ".repeat(indent.len as usize),
3550 IndentKind::Tab => "\t".repeat(indent.len as usize),
3551 };
3552 let point = Point::new(row.0, 0);
3553 indent_edits.push((point..point, text));
3554 }
3555 }
3556 editor.edit(indent_edits, cx);
3557 });
3558 }
3559
3560 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3561 let buffer = self.buffer.read(cx);
3562 let snapshot = buffer.snapshot(cx);
3563
3564 let mut edits = Vec::new();
3565 let mut rows = Vec::new();
3566 let mut rows_inserted = 0;
3567
3568 for selection in self.selections.all_adjusted(cx) {
3569 let cursor = selection.head();
3570 let row = cursor.row;
3571
3572 let point = Point::new(row + 1, 0);
3573 let start_of_line = snapshot.clip_point(point, Bias::Left);
3574
3575 let newline = "\n".to_string();
3576 edits.push((start_of_line..start_of_line, newline));
3577
3578 rows_inserted += 1;
3579 rows.push(row + rows_inserted);
3580 }
3581
3582 self.transact(window, cx, |editor, window, cx| {
3583 editor.edit(edits, cx);
3584
3585 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3586 let mut index = 0;
3587 s.move_cursors_with(|map, _, _| {
3588 let row = rows[index];
3589 index += 1;
3590
3591 let point = Point::new(row, 0);
3592 let boundary = map.next_line_boundary(point).1;
3593 let clipped = map.clip_point(boundary, Bias::Left);
3594
3595 (clipped, SelectionGoal::None)
3596 });
3597 });
3598
3599 let mut indent_edits = Vec::new();
3600 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3601 for row in rows {
3602 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3603 for (row, indent) in indents {
3604 if indent.len == 0 {
3605 continue;
3606 }
3607
3608 let text = match indent.kind {
3609 IndentKind::Space => " ".repeat(indent.len as usize),
3610 IndentKind::Tab => "\t".repeat(indent.len as usize),
3611 };
3612 let point = Point::new(row.0, 0);
3613 indent_edits.push((point..point, text));
3614 }
3615 }
3616 editor.edit(indent_edits, cx);
3617 });
3618 }
3619
3620 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3621 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3622 original_indent_columns: Vec::new(),
3623 });
3624 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3625 }
3626
3627 fn insert_with_autoindent_mode(
3628 &mut self,
3629 text: &str,
3630 autoindent_mode: Option<AutoindentMode>,
3631 window: &mut Window,
3632 cx: &mut Context<Self>,
3633 ) {
3634 if self.read_only(cx) {
3635 return;
3636 }
3637
3638 let text: Arc<str> = text.into();
3639 self.transact(window, cx, |this, window, cx| {
3640 let old_selections = this.selections.all_adjusted(cx);
3641 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3642 let anchors = {
3643 let snapshot = buffer.read(cx);
3644 old_selections
3645 .iter()
3646 .map(|s| {
3647 let anchor = snapshot.anchor_after(s.head());
3648 s.map(|_| anchor)
3649 })
3650 .collect::<Vec<_>>()
3651 };
3652 buffer.edit(
3653 old_selections
3654 .iter()
3655 .map(|s| (s.start..s.end, text.clone())),
3656 autoindent_mode,
3657 cx,
3658 );
3659 anchors
3660 });
3661
3662 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3663 s.select_anchors(selection_anchors);
3664 });
3665
3666 cx.notify();
3667 });
3668 }
3669
3670 fn trigger_completion_on_input(
3671 &mut self,
3672 text: &str,
3673 trigger_in_words: bool,
3674 window: &mut Window,
3675 cx: &mut Context<Self>,
3676 ) {
3677 let ignore_completion_provider = self
3678 .context_menu
3679 .borrow()
3680 .as_ref()
3681 .map(|menu| match menu {
3682 CodeContextMenu::Completions(completions_menu) => {
3683 completions_menu.ignore_completion_provider
3684 }
3685 CodeContextMenu::CodeActions(_) => false,
3686 })
3687 .unwrap_or(false);
3688
3689 if ignore_completion_provider {
3690 self.show_word_completions(&ShowWordCompletions, window, cx);
3691 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3692 self.show_completions(
3693 &ShowCompletions {
3694 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3695 },
3696 window,
3697 cx,
3698 );
3699 } else {
3700 self.hide_context_menu(window, cx);
3701 }
3702 }
3703
3704 fn is_completion_trigger(
3705 &self,
3706 text: &str,
3707 trigger_in_words: bool,
3708 cx: &mut Context<Self>,
3709 ) -> bool {
3710 let position = self.selections.newest_anchor().head();
3711 let multibuffer = self.buffer.read(cx);
3712 let Some(buffer) = position
3713 .buffer_id
3714 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3715 else {
3716 return false;
3717 };
3718
3719 if let Some(completion_provider) = &self.completion_provider {
3720 completion_provider.is_completion_trigger(
3721 &buffer,
3722 position.text_anchor,
3723 text,
3724 trigger_in_words,
3725 cx,
3726 )
3727 } else {
3728 false
3729 }
3730 }
3731
3732 /// If any empty selections is touching the start of its innermost containing autoclose
3733 /// region, expand it to select the brackets.
3734 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3735 let selections = self.selections.all::<usize>(cx);
3736 let buffer = self.buffer.read(cx).read(cx);
3737 let new_selections = self
3738 .selections_with_autoclose_regions(selections, &buffer)
3739 .map(|(mut selection, region)| {
3740 if !selection.is_empty() {
3741 return selection;
3742 }
3743
3744 if let Some(region) = region {
3745 let mut range = region.range.to_offset(&buffer);
3746 if selection.start == range.start && range.start >= region.pair.start.len() {
3747 range.start -= region.pair.start.len();
3748 if buffer.contains_str_at(range.start, ®ion.pair.start)
3749 && buffer.contains_str_at(range.end, ®ion.pair.end)
3750 {
3751 range.end += region.pair.end.len();
3752 selection.start = range.start;
3753 selection.end = range.end;
3754
3755 return selection;
3756 }
3757 }
3758 }
3759
3760 let always_treat_brackets_as_autoclosed = buffer
3761 .language_settings_at(selection.start, cx)
3762 .always_treat_brackets_as_autoclosed;
3763
3764 if !always_treat_brackets_as_autoclosed {
3765 return selection;
3766 }
3767
3768 if let Some(scope) = buffer.language_scope_at(selection.start) {
3769 for (pair, enabled) in scope.brackets() {
3770 if !enabled || !pair.close {
3771 continue;
3772 }
3773
3774 if buffer.contains_str_at(selection.start, &pair.end) {
3775 let pair_start_len = pair.start.len();
3776 if buffer.contains_str_at(
3777 selection.start.saturating_sub(pair_start_len),
3778 &pair.start,
3779 ) {
3780 selection.start -= pair_start_len;
3781 selection.end += pair.end.len();
3782
3783 return selection;
3784 }
3785 }
3786 }
3787 }
3788
3789 selection
3790 })
3791 .collect();
3792
3793 drop(buffer);
3794 self.change_selections(None, window, cx, |selections| {
3795 selections.select(new_selections)
3796 });
3797 }
3798
3799 /// Iterate the given selections, and for each one, find the smallest surrounding
3800 /// autoclose region. This uses the ordering of the selections and the autoclose
3801 /// regions to avoid repeated comparisons.
3802 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3803 &'a self,
3804 selections: impl IntoIterator<Item = Selection<D>>,
3805 buffer: &'a MultiBufferSnapshot,
3806 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3807 let mut i = 0;
3808 let mut regions = self.autoclose_regions.as_slice();
3809 selections.into_iter().map(move |selection| {
3810 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3811
3812 let mut enclosing = None;
3813 while let Some(pair_state) = regions.get(i) {
3814 if pair_state.range.end.to_offset(buffer) < range.start {
3815 regions = ®ions[i + 1..];
3816 i = 0;
3817 } else if pair_state.range.start.to_offset(buffer) > range.end {
3818 break;
3819 } else {
3820 if pair_state.selection_id == selection.id {
3821 enclosing = Some(pair_state);
3822 }
3823 i += 1;
3824 }
3825 }
3826
3827 (selection, enclosing)
3828 })
3829 }
3830
3831 /// Remove any autoclose regions that no longer contain their selection.
3832 fn invalidate_autoclose_regions(
3833 &mut self,
3834 mut selections: &[Selection<Anchor>],
3835 buffer: &MultiBufferSnapshot,
3836 ) {
3837 self.autoclose_regions.retain(|state| {
3838 let mut i = 0;
3839 while let Some(selection) = selections.get(i) {
3840 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3841 selections = &selections[1..];
3842 continue;
3843 }
3844 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3845 break;
3846 }
3847 if selection.id == state.selection_id {
3848 return true;
3849 } else {
3850 i += 1;
3851 }
3852 }
3853 false
3854 });
3855 }
3856
3857 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3858 let offset = position.to_offset(buffer);
3859 let (word_range, kind) = buffer.surrounding_word(offset, true);
3860 if offset > word_range.start && kind == Some(CharKind::Word) {
3861 Some(
3862 buffer
3863 .text_for_range(word_range.start..offset)
3864 .collect::<String>(),
3865 )
3866 } else {
3867 None
3868 }
3869 }
3870
3871 pub fn toggle_inlay_hints(
3872 &mut self,
3873 _: &ToggleInlayHints,
3874 _: &mut Window,
3875 cx: &mut Context<Self>,
3876 ) {
3877 self.refresh_inlay_hints(
3878 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3879 cx,
3880 );
3881 }
3882
3883 pub fn inlay_hints_enabled(&self) -> bool {
3884 self.inlay_hint_cache.enabled
3885 }
3886
3887 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3888 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3889 return;
3890 }
3891
3892 let reason_description = reason.description();
3893 let ignore_debounce = matches!(
3894 reason,
3895 InlayHintRefreshReason::SettingsChange(_)
3896 | InlayHintRefreshReason::Toggle(_)
3897 | InlayHintRefreshReason::ExcerptsRemoved(_)
3898 | InlayHintRefreshReason::ModifiersChanged(_)
3899 );
3900 let (invalidate_cache, required_languages) = match reason {
3901 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3902 match self.inlay_hint_cache.modifiers_override(enabled) {
3903 Some(enabled) => {
3904 if enabled {
3905 (InvalidationStrategy::RefreshRequested, None)
3906 } else {
3907 self.splice_inlays(
3908 &self
3909 .visible_inlay_hints(cx)
3910 .iter()
3911 .map(|inlay| inlay.id)
3912 .collect::<Vec<InlayId>>(),
3913 Vec::new(),
3914 cx,
3915 );
3916 return;
3917 }
3918 }
3919 None => return,
3920 }
3921 }
3922 InlayHintRefreshReason::Toggle(enabled) => {
3923 if self.inlay_hint_cache.toggle(enabled) {
3924 if enabled {
3925 (InvalidationStrategy::RefreshRequested, None)
3926 } else {
3927 self.splice_inlays(
3928 &self
3929 .visible_inlay_hints(cx)
3930 .iter()
3931 .map(|inlay| inlay.id)
3932 .collect::<Vec<InlayId>>(),
3933 Vec::new(),
3934 cx,
3935 );
3936 return;
3937 }
3938 } else {
3939 return;
3940 }
3941 }
3942 InlayHintRefreshReason::SettingsChange(new_settings) => {
3943 match self.inlay_hint_cache.update_settings(
3944 &self.buffer,
3945 new_settings,
3946 self.visible_inlay_hints(cx),
3947 cx,
3948 ) {
3949 ControlFlow::Break(Some(InlaySplice {
3950 to_remove,
3951 to_insert,
3952 })) => {
3953 self.splice_inlays(&to_remove, to_insert, cx);
3954 return;
3955 }
3956 ControlFlow::Break(None) => return,
3957 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3958 }
3959 }
3960 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3961 if let Some(InlaySplice {
3962 to_remove,
3963 to_insert,
3964 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3965 {
3966 self.splice_inlays(&to_remove, to_insert, cx);
3967 }
3968 return;
3969 }
3970 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3971 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3972 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3973 }
3974 InlayHintRefreshReason::RefreshRequested => {
3975 (InvalidationStrategy::RefreshRequested, None)
3976 }
3977 };
3978
3979 if let Some(InlaySplice {
3980 to_remove,
3981 to_insert,
3982 }) = self.inlay_hint_cache.spawn_hint_refresh(
3983 reason_description,
3984 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3985 invalidate_cache,
3986 ignore_debounce,
3987 cx,
3988 ) {
3989 self.splice_inlays(&to_remove, to_insert, cx);
3990 }
3991 }
3992
3993 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3994 self.display_map
3995 .read(cx)
3996 .current_inlays()
3997 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3998 .cloned()
3999 .collect()
4000 }
4001
4002 pub fn excerpts_for_inlay_hints_query(
4003 &self,
4004 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4005 cx: &mut Context<Editor>,
4006 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
4007 let Some(project) = self.project.as_ref() else {
4008 return HashMap::default();
4009 };
4010 let project = project.read(cx);
4011 let multi_buffer = self.buffer().read(cx);
4012 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4013 let multi_buffer_visible_start = self
4014 .scroll_manager
4015 .anchor()
4016 .anchor
4017 .to_point(&multi_buffer_snapshot);
4018 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4019 multi_buffer_visible_start
4020 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4021 Bias::Left,
4022 );
4023 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4024 multi_buffer_snapshot
4025 .range_to_buffer_ranges(multi_buffer_visible_range)
4026 .into_iter()
4027 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4028 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
4029 let buffer_file = project::File::from_dyn(buffer.file())?;
4030 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4031 let worktree_entry = buffer_worktree
4032 .read(cx)
4033 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4034 if worktree_entry.is_ignored {
4035 return None;
4036 }
4037
4038 let language = buffer.language()?;
4039 if let Some(restrict_to_languages) = restrict_to_languages {
4040 if !restrict_to_languages.contains(language) {
4041 return None;
4042 }
4043 }
4044 Some((
4045 excerpt_id,
4046 (
4047 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4048 buffer.version().clone(),
4049 excerpt_visible_range,
4050 ),
4051 ))
4052 })
4053 .collect()
4054 }
4055
4056 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4057 TextLayoutDetails {
4058 text_system: window.text_system().clone(),
4059 editor_style: self.style.clone().unwrap(),
4060 rem_size: window.rem_size(),
4061 scroll_anchor: self.scroll_manager.anchor(),
4062 visible_rows: self.visible_line_count(),
4063 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4064 }
4065 }
4066
4067 pub fn splice_inlays(
4068 &self,
4069 to_remove: &[InlayId],
4070 to_insert: Vec<Inlay>,
4071 cx: &mut Context<Self>,
4072 ) {
4073 self.display_map.update(cx, |display_map, cx| {
4074 display_map.splice_inlays(to_remove, to_insert, cx)
4075 });
4076 cx.notify();
4077 }
4078
4079 fn trigger_on_type_formatting(
4080 &self,
4081 input: String,
4082 window: &mut Window,
4083 cx: &mut Context<Self>,
4084 ) -> Option<Task<Result<()>>> {
4085 if input.len() != 1 {
4086 return None;
4087 }
4088
4089 let project = self.project.as_ref()?;
4090 let position = self.selections.newest_anchor().head();
4091 let (buffer, buffer_position) = self
4092 .buffer
4093 .read(cx)
4094 .text_anchor_for_position(position, cx)?;
4095
4096 let settings = language_settings::language_settings(
4097 buffer
4098 .read(cx)
4099 .language_at(buffer_position)
4100 .map(|l| l.name()),
4101 buffer.read(cx).file(),
4102 cx,
4103 );
4104 if !settings.use_on_type_format {
4105 return None;
4106 }
4107
4108 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4109 // hence we do LSP request & edit on host side only — add formats to host's history.
4110 let push_to_lsp_host_history = true;
4111 // If this is not the host, append its history with new edits.
4112 let push_to_client_history = project.read(cx).is_via_collab();
4113
4114 let on_type_formatting = project.update(cx, |project, cx| {
4115 project.on_type_format(
4116 buffer.clone(),
4117 buffer_position,
4118 input,
4119 push_to_lsp_host_history,
4120 cx,
4121 )
4122 });
4123 Some(cx.spawn_in(window, async move |editor, cx| {
4124 if let Some(transaction) = on_type_formatting.await? {
4125 if push_to_client_history {
4126 buffer
4127 .update(cx, |buffer, _| {
4128 buffer.push_transaction(transaction, Instant::now());
4129 })
4130 .ok();
4131 }
4132 editor.update(cx, |editor, cx| {
4133 editor.refresh_document_highlights(cx);
4134 })?;
4135 }
4136 Ok(())
4137 }))
4138 }
4139
4140 pub fn show_word_completions(
4141 &mut self,
4142 _: &ShowWordCompletions,
4143 window: &mut Window,
4144 cx: &mut Context<Self>,
4145 ) {
4146 self.open_completions_menu(true, None, window, cx);
4147 }
4148
4149 pub fn show_completions(
4150 &mut self,
4151 options: &ShowCompletions,
4152 window: &mut Window,
4153 cx: &mut Context<Self>,
4154 ) {
4155 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4156 }
4157
4158 fn open_completions_menu(
4159 &mut self,
4160 ignore_completion_provider: bool,
4161 trigger: Option<&str>,
4162 window: &mut Window,
4163 cx: &mut Context<Self>,
4164 ) {
4165 if self.pending_rename.is_some() {
4166 return;
4167 }
4168 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4169 return;
4170 }
4171
4172 let position = self.selections.newest_anchor().head();
4173 if position.diff_base_anchor.is_some() {
4174 return;
4175 }
4176 let (buffer, buffer_position) =
4177 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4178 output
4179 } else {
4180 return;
4181 };
4182 let buffer_snapshot = buffer.read(cx).snapshot();
4183 let show_completion_documentation = buffer_snapshot
4184 .settings_at(buffer_position, cx)
4185 .show_completion_documentation;
4186
4187 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4188
4189 let trigger_kind = match trigger {
4190 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4191 CompletionTriggerKind::TRIGGER_CHARACTER
4192 }
4193 _ => CompletionTriggerKind::INVOKED,
4194 };
4195 let completion_context = CompletionContext {
4196 trigger_character: trigger.and_then(|trigger| {
4197 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4198 Some(String::from(trigger))
4199 } else {
4200 None
4201 }
4202 }),
4203 trigger_kind,
4204 };
4205
4206 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4207 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4208 let word_to_exclude = buffer_snapshot
4209 .text_for_range(old_range.clone())
4210 .collect::<String>();
4211 (
4212 buffer_snapshot.anchor_before(old_range.start)
4213 ..buffer_snapshot.anchor_after(old_range.end),
4214 Some(word_to_exclude),
4215 )
4216 } else {
4217 (buffer_position..buffer_position, None)
4218 };
4219
4220 let completion_settings = language_settings(
4221 buffer_snapshot
4222 .language_at(buffer_position)
4223 .map(|language| language.name()),
4224 buffer_snapshot.file(),
4225 cx,
4226 )
4227 .completions;
4228
4229 // The document can be large, so stay in reasonable bounds when searching for words,
4230 // otherwise completion pop-up might be slow to appear.
4231 const WORD_LOOKUP_ROWS: u32 = 5_000;
4232 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4233 let min_word_search = buffer_snapshot.clip_point(
4234 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4235 Bias::Left,
4236 );
4237 let max_word_search = buffer_snapshot.clip_point(
4238 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4239 Bias::Right,
4240 );
4241 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4242 ..buffer_snapshot.point_to_offset(max_word_search);
4243
4244 let provider = self
4245 .completion_provider
4246 .as_ref()
4247 .filter(|_| !ignore_completion_provider);
4248 let skip_digits = query
4249 .as_ref()
4250 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4251
4252 let (mut words, provided_completions) = match provider {
4253 Some(provider) => {
4254 let completions =
4255 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4256
4257 let words = match completion_settings.words {
4258 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4259 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4260 .background_spawn(async move {
4261 buffer_snapshot.words_in_range(WordsQuery {
4262 fuzzy_contents: None,
4263 range: word_search_range,
4264 skip_digits,
4265 })
4266 }),
4267 };
4268
4269 (words, completions)
4270 }
4271 None => (
4272 cx.background_spawn(async move {
4273 buffer_snapshot.words_in_range(WordsQuery {
4274 fuzzy_contents: None,
4275 range: word_search_range,
4276 skip_digits,
4277 })
4278 }),
4279 Task::ready(Ok(None)),
4280 ),
4281 };
4282
4283 let sort_completions = provider
4284 .as_ref()
4285 .map_or(true, |provider| provider.sort_completions());
4286
4287 let id = post_inc(&mut self.next_completion_id);
4288 let task = cx.spawn_in(window, async move |editor, cx| {
4289 async move {
4290 editor.update(cx, |this, _| {
4291 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4292 })?;
4293
4294 let mut completions = Vec::new();
4295 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4296 completions.extend(provided_completions);
4297 if completion_settings.words == WordsCompletionMode::Fallback {
4298 words = Task::ready(HashMap::default());
4299 }
4300 }
4301
4302 let mut words = words.await;
4303 if let Some(word_to_exclude) = &word_to_exclude {
4304 words.remove(word_to_exclude);
4305 }
4306 for lsp_completion in &completions {
4307 words.remove(&lsp_completion.new_text);
4308 }
4309 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4310 old_range: old_range.clone(),
4311 new_text: word.clone(),
4312 label: CodeLabel::plain(word, None),
4313 documentation: None,
4314 source: CompletionSource::BufferWord {
4315 word_range,
4316 resolved: false,
4317 },
4318 confirm: None,
4319 }));
4320
4321 let menu = if completions.is_empty() {
4322 None
4323 } else {
4324 let mut menu = CompletionsMenu::new(
4325 id,
4326 sort_completions,
4327 show_completion_documentation,
4328 ignore_completion_provider,
4329 position,
4330 buffer.clone(),
4331 completions.into(),
4332 );
4333
4334 menu.filter(query.as_deref(), cx.background_executor().clone())
4335 .await;
4336
4337 menu.visible().then_some(menu)
4338 };
4339
4340 editor.update_in(cx, |editor, window, cx| {
4341 match editor.context_menu.borrow().as_ref() {
4342 None => {}
4343 Some(CodeContextMenu::Completions(prev_menu)) => {
4344 if prev_menu.id > id {
4345 return;
4346 }
4347 }
4348 _ => return,
4349 }
4350
4351 if editor.focus_handle.is_focused(window) && menu.is_some() {
4352 let mut menu = menu.unwrap();
4353 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4354
4355 *editor.context_menu.borrow_mut() =
4356 Some(CodeContextMenu::Completions(menu));
4357
4358 if editor.show_edit_predictions_in_menu() {
4359 editor.update_visible_inline_completion(window, cx);
4360 } else {
4361 editor.discard_inline_completion(false, cx);
4362 }
4363
4364 cx.notify();
4365 } else if editor.completion_tasks.len() <= 1 {
4366 // If there are no more completion tasks and the last menu was
4367 // empty, we should hide it.
4368 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4369 // If it was already hidden and we don't show inline
4370 // completions in the menu, we should also show the
4371 // inline-completion when available.
4372 if was_hidden && editor.show_edit_predictions_in_menu() {
4373 editor.update_visible_inline_completion(window, cx);
4374 }
4375 }
4376 })?;
4377
4378 anyhow::Ok(())
4379 }
4380 .log_err()
4381 .await
4382 });
4383
4384 self.completion_tasks.push((id, task));
4385 }
4386
4387 pub fn confirm_completion(
4388 &mut self,
4389 action: &ConfirmCompletion,
4390 window: &mut Window,
4391 cx: &mut Context<Self>,
4392 ) -> Option<Task<Result<()>>> {
4393 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4394 }
4395
4396 pub fn compose_completion(
4397 &mut self,
4398 action: &ComposeCompletion,
4399 window: &mut Window,
4400 cx: &mut Context<Self>,
4401 ) -> Option<Task<Result<()>>> {
4402 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4403 }
4404
4405 fn do_completion(
4406 &mut self,
4407 item_ix: Option<usize>,
4408 intent: CompletionIntent,
4409 window: &mut Window,
4410 cx: &mut Context<Editor>,
4411 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4412 use language::ToOffset as _;
4413
4414 let completions_menu =
4415 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4416 menu
4417 } else {
4418 return None;
4419 };
4420
4421 let entries = completions_menu.entries.borrow();
4422 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4423 if self.show_edit_predictions_in_menu() {
4424 self.discard_inline_completion(true, cx);
4425 }
4426 let candidate_id = mat.candidate_id;
4427 drop(entries);
4428
4429 let buffer_handle = completions_menu.buffer;
4430 let completion = completions_menu
4431 .completions
4432 .borrow()
4433 .get(candidate_id)?
4434 .clone();
4435 cx.stop_propagation();
4436
4437 let snippet;
4438 let text;
4439
4440 if completion.is_snippet() {
4441 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4442 text = snippet.as_ref().unwrap().text.clone();
4443 } else {
4444 snippet = None;
4445 text = completion.new_text.clone();
4446 };
4447 let selections = self.selections.all::<usize>(cx);
4448 let buffer = buffer_handle.read(cx);
4449 let old_range = completion.old_range.to_offset(buffer);
4450 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4451
4452 let newest_selection = self.selections.newest_anchor();
4453 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4454 return None;
4455 }
4456
4457 let lookbehind = newest_selection
4458 .start
4459 .text_anchor
4460 .to_offset(buffer)
4461 .saturating_sub(old_range.start);
4462 let lookahead = old_range
4463 .end
4464 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4465 let mut common_prefix_len = old_text
4466 .bytes()
4467 .zip(text.bytes())
4468 .take_while(|(a, b)| a == b)
4469 .count();
4470
4471 let snapshot = self.buffer.read(cx).snapshot(cx);
4472 let mut range_to_replace: Option<Range<isize>> = None;
4473 let mut ranges = Vec::new();
4474 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4475 for selection in &selections {
4476 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4477 let start = selection.start.saturating_sub(lookbehind);
4478 let end = selection.end + lookahead;
4479 if selection.id == newest_selection.id {
4480 range_to_replace = Some(
4481 ((start + common_prefix_len) as isize - selection.start as isize)
4482 ..(end as isize - selection.start as isize),
4483 );
4484 }
4485 ranges.push(start + common_prefix_len..end);
4486 } else {
4487 common_prefix_len = 0;
4488 ranges.clear();
4489 ranges.extend(selections.iter().map(|s| {
4490 if s.id == newest_selection.id {
4491 range_to_replace = Some(
4492 old_range.start.to_offset_utf16(&snapshot).0 as isize
4493 - selection.start as isize
4494 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4495 - selection.start as isize,
4496 );
4497 old_range.clone()
4498 } else {
4499 s.start..s.end
4500 }
4501 }));
4502 break;
4503 }
4504 if !self.linked_edit_ranges.is_empty() {
4505 let start_anchor = snapshot.anchor_before(selection.head());
4506 let end_anchor = snapshot.anchor_after(selection.tail());
4507 if let Some(ranges) = self
4508 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4509 {
4510 for (buffer, edits) in ranges {
4511 linked_edits.entry(buffer.clone()).or_default().extend(
4512 edits
4513 .into_iter()
4514 .map(|range| (range, text[common_prefix_len..].to_owned())),
4515 );
4516 }
4517 }
4518 }
4519 }
4520 let text = &text[common_prefix_len..];
4521
4522 cx.emit(EditorEvent::InputHandled {
4523 utf16_range_to_replace: range_to_replace,
4524 text: text.into(),
4525 });
4526
4527 self.transact(window, cx, |this, window, cx| {
4528 if let Some(mut snippet) = snippet {
4529 snippet.text = text.to_string();
4530 for tabstop in snippet
4531 .tabstops
4532 .iter_mut()
4533 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4534 {
4535 tabstop.start -= common_prefix_len as isize;
4536 tabstop.end -= common_prefix_len as isize;
4537 }
4538
4539 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4540 } else {
4541 this.buffer.update(cx, |buffer, cx| {
4542 buffer.edit(
4543 ranges.iter().map(|range| (range.clone(), text)),
4544 this.autoindent_mode.clone(),
4545 cx,
4546 );
4547 });
4548 }
4549 for (buffer, edits) in linked_edits {
4550 buffer.update(cx, |buffer, cx| {
4551 let snapshot = buffer.snapshot();
4552 let edits = edits
4553 .into_iter()
4554 .map(|(range, text)| {
4555 use text::ToPoint as TP;
4556 let end_point = TP::to_point(&range.end, &snapshot);
4557 let start_point = TP::to_point(&range.start, &snapshot);
4558 (start_point..end_point, text)
4559 })
4560 .sorted_by_key(|(range, _)| range.start)
4561 .collect::<Vec<_>>();
4562 buffer.edit(edits, None, cx);
4563 })
4564 }
4565
4566 this.refresh_inline_completion(true, false, window, cx);
4567 });
4568
4569 let show_new_completions_on_confirm = completion
4570 .confirm
4571 .as_ref()
4572 .map_or(false, |confirm| confirm(intent, window, cx));
4573 if show_new_completions_on_confirm {
4574 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4575 }
4576
4577 let provider = self.completion_provider.as_ref()?;
4578 drop(completion);
4579 let apply_edits = provider.apply_additional_edits_for_completion(
4580 buffer_handle,
4581 completions_menu.completions.clone(),
4582 candidate_id,
4583 true,
4584 cx,
4585 );
4586
4587 let editor_settings = EditorSettings::get_global(cx);
4588 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4589 // After the code completion is finished, users often want to know what signatures are needed.
4590 // so we should automatically call signature_help
4591 self.show_signature_help(&ShowSignatureHelp, window, cx);
4592 }
4593
4594 Some(cx.foreground_executor().spawn(async move {
4595 apply_edits.await?;
4596 Ok(())
4597 }))
4598 }
4599
4600 pub fn toggle_code_actions(
4601 &mut self,
4602 action: &ToggleCodeActions,
4603 window: &mut Window,
4604 cx: &mut Context<Self>,
4605 ) {
4606 let mut context_menu = self.context_menu.borrow_mut();
4607 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4608 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4609 // Toggle if we're selecting the same one
4610 *context_menu = None;
4611 cx.notify();
4612 return;
4613 } else {
4614 // Otherwise, clear it and start a new one
4615 *context_menu = None;
4616 cx.notify();
4617 }
4618 }
4619 drop(context_menu);
4620 let snapshot = self.snapshot(window, cx);
4621 let deployed_from_indicator = action.deployed_from_indicator;
4622 let mut task = self.code_actions_task.take();
4623 let action = action.clone();
4624 cx.spawn_in(window, async move |editor, cx| {
4625 while let Some(prev_task) = task {
4626 prev_task.await.log_err();
4627 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4628 }
4629
4630 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4631 if editor.focus_handle.is_focused(window) {
4632 let multibuffer_point = action
4633 .deployed_from_indicator
4634 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4635 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4636 let (buffer, buffer_row) = snapshot
4637 .buffer_snapshot
4638 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4639 .and_then(|(buffer_snapshot, range)| {
4640 editor
4641 .buffer
4642 .read(cx)
4643 .buffer(buffer_snapshot.remote_id())
4644 .map(|buffer| (buffer, range.start.row))
4645 })?;
4646 let (_, code_actions) = editor
4647 .available_code_actions
4648 .clone()
4649 .and_then(|(location, code_actions)| {
4650 let snapshot = location.buffer.read(cx).snapshot();
4651 let point_range = location.range.to_point(&snapshot);
4652 let point_range = point_range.start.row..=point_range.end.row;
4653 if point_range.contains(&buffer_row) {
4654 Some((location, code_actions))
4655 } else {
4656 None
4657 }
4658 })
4659 .unzip();
4660 let buffer_id = buffer.read(cx).remote_id();
4661 let tasks = editor
4662 .tasks
4663 .get(&(buffer_id, buffer_row))
4664 .map(|t| Arc::new(t.to_owned()));
4665 if tasks.is_none() && code_actions.is_none() {
4666 return None;
4667 }
4668
4669 editor.completion_tasks.clear();
4670 editor.discard_inline_completion(false, cx);
4671 let task_context =
4672 tasks
4673 .as_ref()
4674 .zip(editor.project.clone())
4675 .map(|(tasks, project)| {
4676 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4677 });
4678
4679 Some(cx.spawn_in(window, async move |editor, cx| {
4680 let task_context = match task_context {
4681 Some(task_context) => task_context.await,
4682 None => None,
4683 };
4684 let resolved_tasks =
4685 tasks.zip(task_context).map(|(tasks, task_context)| {
4686 Rc::new(ResolvedTasks {
4687 templates: tasks.resolve(&task_context).collect(),
4688 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4689 multibuffer_point.row,
4690 tasks.column,
4691 )),
4692 })
4693 });
4694 let spawn_straight_away = resolved_tasks
4695 .as_ref()
4696 .map_or(false, |tasks| tasks.templates.len() == 1)
4697 && code_actions
4698 .as_ref()
4699 .map_or(true, |actions| actions.is_empty());
4700 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4701 *editor.context_menu.borrow_mut() =
4702 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4703 buffer,
4704 actions: CodeActionContents {
4705 tasks: resolved_tasks,
4706 actions: code_actions,
4707 },
4708 selected_item: Default::default(),
4709 scroll_handle: UniformListScrollHandle::default(),
4710 deployed_from_indicator,
4711 }));
4712 if spawn_straight_away {
4713 if let Some(task) = editor.confirm_code_action(
4714 &ConfirmCodeAction { item_ix: Some(0) },
4715 window,
4716 cx,
4717 ) {
4718 cx.notify();
4719 return task;
4720 }
4721 }
4722 cx.notify();
4723 Task::ready(Ok(()))
4724 }) {
4725 task.await
4726 } else {
4727 Ok(())
4728 }
4729 }))
4730 } else {
4731 Some(Task::ready(Ok(())))
4732 }
4733 })?;
4734 if let Some(task) = spawned_test_task {
4735 task.await?;
4736 }
4737
4738 Ok::<_, anyhow::Error>(())
4739 })
4740 .detach_and_log_err(cx);
4741 }
4742
4743 pub fn confirm_code_action(
4744 &mut self,
4745 action: &ConfirmCodeAction,
4746 window: &mut Window,
4747 cx: &mut Context<Self>,
4748 ) -> Option<Task<Result<()>>> {
4749 let actions_menu =
4750 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4751 menu
4752 } else {
4753 return None;
4754 };
4755 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4756 let action = actions_menu.actions.get(action_ix)?;
4757 let title = action.label();
4758 let buffer = actions_menu.buffer;
4759 let workspace = self.workspace()?;
4760
4761 match action {
4762 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4763 workspace.update(cx, |workspace, cx| {
4764 workspace::tasks::schedule_resolved_task(
4765 workspace,
4766 task_source_kind,
4767 resolved_task,
4768 false,
4769 cx,
4770 );
4771
4772 Some(Task::ready(Ok(())))
4773 })
4774 }
4775 CodeActionsItem::CodeAction {
4776 excerpt_id,
4777 action,
4778 provider,
4779 } => {
4780 let apply_code_action =
4781 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4782 let workspace = workspace.downgrade();
4783 Some(cx.spawn_in(window, async move |editor, cx| {
4784 let project_transaction = apply_code_action.await?;
4785 Self::open_project_transaction(
4786 &editor,
4787 workspace,
4788 project_transaction,
4789 title,
4790 cx,
4791 )
4792 .await
4793 }))
4794 }
4795 }
4796 }
4797
4798 pub async fn open_project_transaction(
4799 this: &WeakEntity<Editor>,
4800 workspace: WeakEntity<Workspace>,
4801 transaction: ProjectTransaction,
4802 title: String,
4803 cx: &mut AsyncWindowContext,
4804 ) -> Result<()> {
4805 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4806 cx.update(|_, cx| {
4807 entries.sort_unstable_by_key(|(buffer, _)| {
4808 buffer.read(cx).file().map(|f| f.path().clone())
4809 });
4810 })?;
4811
4812 // If the project transaction's edits are all contained within this editor, then
4813 // avoid opening a new editor to display them.
4814
4815 if let Some((buffer, transaction)) = entries.first() {
4816 if entries.len() == 1 {
4817 let excerpt = this.update(cx, |editor, cx| {
4818 editor
4819 .buffer()
4820 .read(cx)
4821 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4822 })?;
4823 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4824 if excerpted_buffer == *buffer {
4825 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4826 let excerpt_range = excerpt_range.to_offset(buffer);
4827 buffer
4828 .edited_ranges_for_transaction::<usize>(transaction)
4829 .all(|range| {
4830 excerpt_range.start <= range.start
4831 && excerpt_range.end >= range.end
4832 })
4833 })?;
4834
4835 if all_edits_within_excerpt {
4836 return Ok(());
4837 }
4838 }
4839 }
4840 }
4841 } else {
4842 return Ok(());
4843 }
4844
4845 let mut ranges_to_highlight = Vec::new();
4846 let excerpt_buffer = cx.new(|cx| {
4847 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4848 for (buffer_handle, transaction) in &entries {
4849 let buffer = buffer_handle.read(cx);
4850 ranges_to_highlight.extend(
4851 multibuffer.push_excerpts_with_context_lines(
4852 buffer_handle.clone(),
4853 buffer
4854 .edited_ranges_for_transaction::<usize>(transaction)
4855 .collect(),
4856 DEFAULT_MULTIBUFFER_CONTEXT,
4857 cx,
4858 ),
4859 );
4860 }
4861 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4862 multibuffer
4863 })?;
4864
4865 workspace.update_in(cx, |workspace, window, cx| {
4866 let project = workspace.project().clone();
4867 let editor =
4868 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4869 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4870 editor.update(cx, |editor, cx| {
4871 editor.highlight_background::<Self>(
4872 &ranges_to_highlight,
4873 |theme| theme.editor_highlighted_line_background,
4874 cx,
4875 );
4876 });
4877 })?;
4878
4879 Ok(())
4880 }
4881
4882 pub fn clear_code_action_providers(&mut self) {
4883 self.code_action_providers.clear();
4884 self.available_code_actions.take();
4885 }
4886
4887 pub fn add_code_action_provider(
4888 &mut self,
4889 provider: Rc<dyn CodeActionProvider>,
4890 window: &mut Window,
4891 cx: &mut Context<Self>,
4892 ) {
4893 if self
4894 .code_action_providers
4895 .iter()
4896 .any(|existing_provider| existing_provider.id() == provider.id())
4897 {
4898 return;
4899 }
4900
4901 self.code_action_providers.push(provider);
4902 self.refresh_code_actions(window, cx);
4903 }
4904
4905 pub fn remove_code_action_provider(
4906 &mut self,
4907 id: Arc<str>,
4908 window: &mut Window,
4909 cx: &mut Context<Self>,
4910 ) {
4911 self.code_action_providers
4912 .retain(|provider| provider.id() != id);
4913 self.refresh_code_actions(window, cx);
4914 }
4915
4916 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4917 let buffer = self.buffer.read(cx);
4918 let newest_selection = self.selections.newest_anchor().clone();
4919 if newest_selection.head().diff_base_anchor.is_some() {
4920 return None;
4921 }
4922 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4923 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4924 if start_buffer != end_buffer {
4925 return None;
4926 }
4927
4928 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4929 cx.background_executor()
4930 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4931 .await;
4932
4933 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4934 let providers = this.code_action_providers.clone();
4935 let tasks = this
4936 .code_action_providers
4937 .iter()
4938 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4939 .collect::<Vec<_>>();
4940 (providers, tasks)
4941 })?;
4942
4943 let mut actions = Vec::new();
4944 for (provider, provider_actions) in
4945 providers.into_iter().zip(future::join_all(tasks).await)
4946 {
4947 if let Some(provider_actions) = provider_actions.log_err() {
4948 actions.extend(provider_actions.into_iter().map(|action| {
4949 AvailableCodeAction {
4950 excerpt_id: newest_selection.start.excerpt_id,
4951 action,
4952 provider: provider.clone(),
4953 }
4954 }));
4955 }
4956 }
4957
4958 this.update(cx, |this, cx| {
4959 this.available_code_actions = if actions.is_empty() {
4960 None
4961 } else {
4962 Some((
4963 Location {
4964 buffer: start_buffer,
4965 range: start..end,
4966 },
4967 actions.into(),
4968 ))
4969 };
4970 cx.notify();
4971 })
4972 }));
4973 None
4974 }
4975
4976 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4977 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4978 self.show_git_blame_inline = false;
4979
4980 self.show_git_blame_inline_delay_task =
4981 Some(cx.spawn_in(window, async move |this, cx| {
4982 cx.background_executor().timer(delay).await;
4983
4984 this.update(cx, |this, cx| {
4985 this.show_git_blame_inline = true;
4986 cx.notify();
4987 })
4988 .log_err();
4989 }));
4990 }
4991 }
4992
4993 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4994 if self.pending_rename.is_some() {
4995 return None;
4996 }
4997
4998 let provider = self.semantics_provider.clone()?;
4999 let buffer = self.buffer.read(cx);
5000 let newest_selection = self.selections.newest_anchor().clone();
5001 let cursor_position = newest_selection.head();
5002 let (cursor_buffer, cursor_buffer_position) =
5003 buffer.text_anchor_for_position(cursor_position, cx)?;
5004 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5005 if cursor_buffer != tail_buffer {
5006 return None;
5007 }
5008 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
5009 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
5010 cx.background_executor()
5011 .timer(Duration::from_millis(debounce))
5012 .await;
5013
5014 let highlights = if let Some(highlights) = cx
5015 .update(|cx| {
5016 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5017 })
5018 .ok()
5019 .flatten()
5020 {
5021 highlights.await.log_err()
5022 } else {
5023 None
5024 };
5025
5026 if let Some(highlights) = highlights {
5027 this.update(cx, |this, cx| {
5028 if this.pending_rename.is_some() {
5029 return;
5030 }
5031
5032 let buffer_id = cursor_position.buffer_id;
5033 let buffer = this.buffer.read(cx);
5034 if !buffer
5035 .text_anchor_for_position(cursor_position, cx)
5036 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5037 {
5038 return;
5039 }
5040
5041 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5042 let mut write_ranges = Vec::new();
5043 let mut read_ranges = Vec::new();
5044 for highlight in highlights {
5045 for (excerpt_id, excerpt_range) in
5046 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5047 {
5048 let start = highlight
5049 .range
5050 .start
5051 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5052 let end = highlight
5053 .range
5054 .end
5055 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5056 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5057 continue;
5058 }
5059
5060 let range = Anchor {
5061 buffer_id,
5062 excerpt_id,
5063 text_anchor: start,
5064 diff_base_anchor: None,
5065 }..Anchor {
5066 buffer_id,
5067 excerpt_id,
5068 text_anchor: end,
5069 diff_base_anchor: None,
5070 };
5071 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5072 write_ranges.push(range);
5073 } else {
5074 read_ranges.push(range);
5075 }
5076 }
5077 }
5078
5079 this.highlight_background::<DocumentHighlightRead>(
5080 &read_ranges,
5081 |theme| theme.editor_document_highlight_read_background,
5082 cx,
5083 );
5084 this.highlight_background::<DocumentHighlightWrite>(
5085 &write_ranges,
5086 |theme| theme.editor_document_highlight_write_background,
5087 cx,
5088 );
5089 cx.notify();
5090 })
5091 .log_err();
5092 }
5093 }));
5094 None
5095 }
5096
5097 pub fn refresh_selected_text_highlights(
5098 &mut self,
5099 window: &mut Window,
5100 cx: &mut Context<Editor>,
5101 ) {
5102 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5103 return;
5104 }
5105 self.selection_highlight_task.take();
5106 if !EditorSettings::get_global(cx).selection_highlight {
5107 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5108 return;
5109 }
5110 if self.selections.count() != 1 || self.selections.line_mode {
5111 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5112 return;
5113 }
5114 let selection = self.selections.newest::<Point>(cx);
5115 if selection.is_empty() || selection.start.row != selection.end.row {
5116 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5117 return;
5118 }
5119 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5120 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5121 cx.background_executor()
5122 .timer(Duration::from_millis(debounce))
5123 .await;
5124 let Some(Some(matches_task)) = editor
5125 .update_in(cx, |editor, _, cx| {
5126 if editor.selections.count() != 1 || editor.selections.line_mode {
5127 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5128 return None;
5129 }
5130 let selection = editor.selections.newest::<Point>(cx);
5131 if selection.is_empty() || selection.start.row != selection.end.row {
5132 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5133 return None;
5134 }
5135 let buffer = editor.buffer().read(cx).snapshot(cx);
5136 let query = buffer.text_for_range(selection.range()).collect::<String>();
5137 if query.trim().is_empty() {
5138 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5139 return None;
5140 }
5141 Some(cx.background_spawn(async move {
5142 let mut ranges = Vec::new();
5143 let selection_anchors = selection.range().to_anchors(&buffer);
5144 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5145 for (search_buffer, search_range, excerpt_id) in
5146 buffer.range_to_buffer_ranges(range)
5147 {
5148 ranges.extend(
5149 project::search::SearchQuery::text(
5150 query.clone(),
5151 false,
5152 false,
5153 false,
5154 Default::default(),
5155 Default::default(),
5156 None,
5157 )
5158 .unwrap()
5159 .search(search_buffer, Some(search_range.clone()))
5160 .await
5161 .into_iter()
5162 .filter_map(
5163 |match_range| {
5164 let start = search_buffer.anchor_after(
5165 search_range.start + match_range.start,
5166 );
5167 let end = search_buffer.anchor_before(
5168 search_range.start + match_range.end,
5169 );
5170 let range = Anchor::range_in_buffer(
5171 excerpt_id,
5172 search_buffer.remote_id(),
5173 start..end,
5174 );
5175 (range != selection_anchors).then_some(range)
5176 },
5177 ),
5178 );
5179 }
5180 }
5181 ranges
5182 }))
5183 })
5184 .log_err()
5185 else {
5186 return;
5187 };
5188 let matches = matches_task.await;
5189 editor
5190 .update_in(cx, |editor, _, cx| {
5191 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5192 if !matches.is_empty() {
5193 editor.highlight_background::<SelectedTextHighlight>(
5194 &matches,
5195 |theme| theme.editor_document_highlight_bracket_background,
5196 cx,
5197 )
5198 }
5199 })
5200 .log_err();
5201 }));
5202 }
5203
5204 pub fn refresh_inline_completion(
5205 &mut self,
5206 debounce: bool,
5207 user_requested: bool,
5208 window: &mut Window,
5209 cx: &mut Context<Self>,
5210 ) -> Option<()> {
5211 let provider = self.edit_prediction_provider()?;
5212 let cursor = self.selections.newest_anchor().head();
5213 let (buffer, cursor_buffer_position) =
5214 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5215
5216 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5217 self.discard_inline_completion(false, cx);
5218 return None;
5219 }
5220
5221 if !user_requested
5222 && (!self.should_show_edit_predictions()
5223 || !self.is_focused(window)
5224 || buffer.read(cx).is_empty())
5225 {
5226 self.discard_inline_completion(false, cx);
5227 return None;
5228 }
5229
5230 self.update_visible_inline_completion(window, cx);
5231 provider.refresh(
5232 self.project.clone(),
5233 buffer,
5234 cursor_buffer_position,
5235 debounce,
5236 cx,
5237 );
5238 Some(())
5239 }
5240
5241 fn show_edit_predictions_in_menu(&self) -> bool {
5242 match self.edit_prediction_settings {
5243 EditPredictionSettings::Disabled => false,
5244 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5245 }
5246 }
5247
5248 pub fn edit_predictions_enabled(&self) -> bool {
5249 match self.edit_prediction_settings {
5250 EditPredictionSettings::Disabled => false,
5251 EditPredictionSettings::Enabled { .. } => true,
5252 }
5253 }
5254
5255 fn edit_prediction_requires_modifier(&self) -> bool {
5256 match self.edit_prediction_settings {
5257 EditPredictionSettings::Disabled => false,
5258 EditPredictionSettings::Enabled {
5259 preview_requires_modifier,
5260 ..
5261 } => preview_requires_modifier,
5262 }
5263 }
5264
5265 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5266 if self.edit_prediction_provider.is_none() {
5267 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5268 } else {
5269 let selection = self.selections.newest_anchor();
5270 let cursor = selection.head();
5271
5272 if let Some((buffer, cursor_buffer_position)) =
5273 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5274 {
5275 self.edit_prediction_settings =
5276 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5277 }
5278 }
5279 }
5280
5281 fn edit_prediction_settings_at_position(
5282 &self,
5283 buffer: &Entity<Buffer>,
5284 buffer_position: language::Anchor,
5285 cx: &App,
5286 ) -> EditPredictionSettings {
5287 if self.mode != EditorMode::Full
5288 || !self.show_inline_completions_override.unwrap_or(true)
5289 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5290 {
5291 return EditPredictionSettings::Disabled;
5292 }
5293
5294 let buffer = buffer.read(cx);
5295
5296 let file = buffer.file();
5297
5298 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5299 return EditPredictionSettings::Disabled;
5300 };
5301
5302 let by_provider = matches!(
5303 self.menu_inline_completions_policy,
5304 MenuInlineCompletionsPolicy::ByProvider
5305 );
5306
5307 let show_in_menu = by_provider
5308 && self
5309 .edit_prediction_provider
5310 .as_ref()
5311 .map_or(false, |provider| {
5312 provider.provider.show_completions_in_menu()
5313 });
5314
5315 let preview_requires_modifier =
5316 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5317
5318 EditPredictionSettings::Enabled {
5319 show_in_menu,
5320 preview_requires_modifier,
5321 }
5322 }
5323
5324 fn should_show_edit_predictions(&self) -> bool {
5325 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5326 }
5327
5328 pub fn edit_prediction_preview_is_active(&self) -> bool {
5329 matches!(
5330 self.edit_prediction_preview,
5331 EditPredictionPreview::Active { .. }
5332 )
5333 }
5334
5335 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5336 let cursor = self.selections.newest_anchor().head();
5337 if let Some((buffer, cursor_position)) =
5338 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5339 {
5340 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5341 } else {
5342 false
5343 }
5344 }
5345
5346 fn edit_predictions_enabled_in_buffer(
5347 &self,
5348 buffer: &Entity<Buffer>,
5349 buffer_position: language::Anchor,
5350 cx: &App,
5351 ) -> bool {
5352 maybe!({
5353 if self.read_only(cx) {
5354 return Some(false);
5355 }
5356 let provider = self.edit_prediction_provider()?;
5357 if !provider.is_enabled(&buffer, buffer_position, cx) {
5358 return Some(false);
5359 }
5360 let buffer = buffer.read(cx);
5361 let Some(file) = buffer.file() else {
5362 return Some(true);
5363 };
5364 let settings = all_language_settings(Some(file), cx);
5365 Some(settings.edit_predictions_enabled_for_file(file, cx))
5366 })
5367 .unwrap_or(false)
5368 }
5369
5370 fn cycle_inline_completion(
5371 &mut self,
5372 direction: Direction,
5373 window: &mut Window,
5374 cx: &mut Context<Self>,
5375 ) -> Option<()> {
5376 let provider = self.edit_prediction_provider()?;
5377 let cursor = self.selections.newest_anchor().head();
5378 let (buffer, cursor_buffer_position) =
5379 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5380 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5381 return None;
5382 }
5383
5384 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5385 self.update_visible_inline_completion(window, cx);
5386
5387 Some(())
5388 }
5389
5390 pub fn show_inline_completion(
5391 &mut self,
5392 _: &ShowEditPrediction,
5393 window: &mut Window,
5394 cx: &mut Context<Self>,
5395 ) {
5396 if !self.has_active_inline_completion() {
5397 self.refresh_inline_completion(false, true, window, cx);
5398 return;
5399 }
5400
5401 self.update_visible_inline_completion(window, cx);
5402 }
5403
5404 pub fn display_cursor_names(
5405 &mut self,
5406 _: &DisplayCursorNames,
5407 window: &mut Window,
5408 cx: &mut Context<Self>,
5409 ) {
5410 self.show_cursor_names(window, cx);
5411 }
5412
5413 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5414 self.show_cursor_names = true;
5415 cx.notify();
5416 cx.spawn_in(window, async move |this, cx| {
5417 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5418 this.update(cx, |this, cx| {
5419 this.show_cursor_names = false;
5420 cx.notify()
5421 })
5422 .ok()
5423 })
5424 .detach();
5425 }
5426
5427 pub fn next_edit_prediction(
5428 &mut self,
5429 _: &NextEditPrediction,
5430 window: &mut Window,
5431 cx: &mut Context<Self>,
5432 ) {
5433 if self.has_active_inline_completion() {
5434 self.cycle_inline_completion(Direction::Next, window, cx);
5435 } else {
5436 let is_copilot_disabled = self
5437 .refresh_inline_completion(false, true, window, cx)
5438 .is_none();
5439 if is_copilot_disabled {
5440 cx.propagate();
5441 }
5442 }
5443 }
5444
5445 pub fn previous_edit_prediction(
5446 &mut self,
5447 _: &PreviousEditPrediction,
5448 window: &mut Window,
5449 cx: &mut Context<Self>,
5450 ) {
5451 if self.has_active_inline_completion() {
5452 self.cycle_inline_completion(Direction::Prev, window, cx);
5453 } else {
5454 let is_copilot_disabled = self
5455 .refresh_inline_completion(false, true, window, cx)
5456 .is_none();
5457 if is_copilot_disabled {
5458 cx.propagate();
5459 }
5460 }
5461 }
5462
5463 pub fn accept_edit_prediction(
5464 &mut self,
5465 _: &AcceptEditPrediction,
5466 window: &mut Window,
5467 cx: &mut Context<Self>,
5468 ) {
5469 if self.show_edit_predictions_in_menu() {
5470 self.hide_context_menu(window, cx);
5471 }
5472
5473 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5474 return;
5475 };
5476
5477 self.report_inline_completion_event(
5478 active_inline_completion.completion_id.clone(),
5479 true,
5480 cx,
5481 );
5482
5483 match &active_inline_completion.completion {
5484 InlineCompletion::Move { target, .. } => {
5485 let target = *target;
5486
5487 if let Some(position_map) = &self.last_position_map {
5488 if position_map
5489 .visible_row_range
5490 .contains(&target.to_display_point(&position_map.snapshot).row())
5491 || !self.edit_prediction_requires_modifier()
5492 {
5493 self.unfold_ranges(&[target..target], true, false, cx);
5494 // Note that this is also done in vim's handler of the Tab action.
5495 self.change_selections(
5496 Some(Autoscroll::newest()),
5497 window,
5498 cx,
5499 |selections| {
5500 selections.select_anchor_ranges([target..target]);
5501 },
5502 );
5503 self.clear_row_highlights::<EditPredictionPreview>();
5504
5505 self.edit_prediction_preview
5506 .set_previous_scroll_position(None);
5507 } else {
5508 self.edit_prediction_preview
5509 .set_previous_scroll_position(Some(
5510 position_map.snapshot.scroll_anchor,
5511 ));
5512
5513 self.highlight_rows::<EditPredictionPreview>(
5514 target..target,
5515 cx.theme().colors().editor_highlighted_line_background,
5516 true,
5517 cx,
5518 );
5519 self.request_autoscroll(Autoscroll::fit(), cx);
5520 }
5521 }
5522 }
5523 InlineCompletion::Edit { edits, .. } => {
5524 if let Some(provider) = self.edit_prediction_provider() {
5525 provider.accept(cx);
5526 }
5527
5528 let snapshot = self.buffer.read(cx).snapshot(cx);
5529 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5530
5531 self.buffer.update(cx, |buffer, cx| {
5532 buffer.edit(edits.iter().cloned(), None, cx)
5533 });
5534
5535 self.change_selections(None, window, cx, |s| {
5536 s.select_anchor_ranges([last_edit_end..last_edit_end])
5537 });
5538
5539 self.update_visible_inline_completion(window, cx);
5540 if self.active_inline_completion.is_none() {
5541 self.refresh_inline_completion(true, true, window, cx);
5542 }
5543
5544 cx.notify();
5545 }
5546 }
5547
5548 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5549 }
5550
5551 pub fn accept_partial_inline_completion(
5552 &mut self,
5553 _: &AcceptPartialEditPrediction,
5554 window: &mut Window,
5555 cx: &mut Context<Self>,
5556 ) {
5557 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5558 return;
5559 };
5560 if self.selections.count() != 1 {
5561 return;
5562 }
5563
5564 self.report_inline_completion_event(
5565 active_inline_completion.completion_id.clone(),
5566 true,
5567 cx,
5568 );
5569
5570 match &active_inline_completion.completion {
5571 InlineCompletion::Move { target, .. } => {
5572 let target = *target;
5573 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5574 selections.select_anchor_ranges([target..target]);
5575 });
5576 }
5577 InlineCompletion::Edit { edits, .. } => {
5578 // Find an insertion that starts at the cursor position.
5579 let snapshot = self.buffer.read(cx).snapshot(cx);
5580 let cursor_offset = self.selections.newest::<usize>(cx).head();
5581 let insertion = edits.iter().find_map(|(range, text)| {
5582 let range = range.to_offset(&snapshot);
5583 if range.is_empty() && range.start == cursor_offset {
5584 Some(text)
5585 } else {
5586 None
5587 }
5588 });
5589
5590 if let Some(text) = insertion {
5591 let mut partial_completion = text
5592 .chars()
5593 .by_ref()
5594 .take_while(|c| c.is_alphabetic())
5595 .collect::<String>();
5596 if partial_completion.is_empty() {
5597 partial_completion = text
5598 .chars()
5599 .by_ref()
5600 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5601 .collect::<String>();
5602 }
5603
5604 cx.emit(EditorEvent::InputHandled {
5605 utf16_range_to_replace: None,
5606 text: partial_completion.clone().into(),
5607 });
5608
5609 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5610
5611 self.refresh_inline_completion(true, true, window, cx);
5612 cx.notify();
5613 } else {
5614 self.accept_edit_prediction(&Default::default(), window, cx);
5615 }
5616 }
5617 }
5618 }
5619
5620 fn discard_inline_completion(
5621 &mut self,
5622 should_report_inline_completion_event: bool,
5623 cx: &mut Context<Self>,
5624 ) -> bool {
5625 if should_report_inline_completion_event {
5626 let completion_id = self
5627 .active_inline_completion
5628 .as_ref()
5629 .and_then(|active_completion| active_completion.completion_id.clone());
5630
5631 self.report_inline_completion_event(completion_id, false, cx);
5632 }
5633
5634 if let Some(provider) = self.edit_prediction_provider() {
5635 provider.discard(cx);
5636 }
5637
5638 self.take_active_inline_completion(cx)
5639 }
5640
5641 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5642 let Some(provider) = self.edit_prediction_provider() else {
5643 return;
5644 };
5645
5646 let Some((_, buffer, _)) = self
5647 .buffer
5648 .read(cx)
5649 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5650 else {
5651 return;
5652 };
5653
5654 let extension = buffer
5655 .read(cx)
5656 .file()
5657 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5658
5659 let event_type = match accepted {
5660 true => "Edit Prediction Accepted",
5661 false => "Edit Prediction Discarded",
5662 };
5663 telemetry::event!(
5664 event_type,
5665 provider = provider.name(),
5666 prediction_id = id,
5667 suggestion_accepted = accepted,
5668 file_extension = extension,
5669 );
5670 }
5671
5672 pub fn has_active_inline_completion(&self) -> bool {
5673 self.active_inline_completion.is_some()
5674 }
5675
5676 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5677 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5678 return false;
5679 };
5680
5681 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5682 self.clear_highlights::<InlineCompletionHighlight>(cx);
5683 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5684 true
5685 }
5686
5687 /// Returns true when we're displaying the edit prediction popover below the cursor
5688 /// like we are not previewing and the LSP autocomplete menu is visible
5689 /// or we are in `when_holding_modifier` mode.
5690 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5691 if self.edit_prediction_preview_is_active()
5692 || !self.show_edit_predictions_in_menu()
5693 || !self.edit_predictions_enabled()
5694 {
5695 return false;
5696 }
5697
5698 if self.has_visible_completions_menu() {
5699 return true;
5700 }
5701
5702 has_completion && self.edit_prediction_requires_modifier()
5703 }
5704
5705 fn handle_modifiers_changed(
5706 &mut self,
5707 modifiers: Modifiers,
5708 position_map: &PositionMap,
5709 window: &mut Window,
5710 cx: &mut Context<Self>,
5711 ) {
5712 if self.show_edit_predictions_in_menu() {
5713 self.update_edit_prediction_preview(&modifiers, window, cx);
5714 }
5715
5716 self.update_selection_mode(&modifiers, position_map, window, cx);
5717
5718 let mouse_position = window.mouse_position();
5719 if !position_map.text_hitbox.is_hovered(window) {
5720 return;
5721 }
5722
5723 self.update_hovered_link(
5724 position_map.point_for_position(mouse_position),
5725 &position_map.snapshot,
5726 modifiers,
5727 window,
5728 cx,
5729 )
5730 }
5731
5732 fn update_selection_mode(
5733 &mut self,
5734 modifiers: &Modifiers,
5735 position_map: &PositionMap,
5736 window: &mut Window,
5737 cx: &mut Context<Self>,
5738 ) {
5739 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5740 return;
5741 }
5742
5743 let mouse_position = window.mouse_position();
5744 let point_for_position = position_map.point_for_position(mouse_position);
5745 let position = point_for_position.previous_valid;
5746
5747 self.select(
5748 SelectPhase::BeginColumnar {
5749 position,
5750 reset: false,
5751 goal_column: point_for_position.exact_unclipped.column(),
5752 },
5753 window,
5754 cx,
5755 );
5756 }
5757
5758 fn update_edit_prediction_preview(
5759 &mut self,
5760 modifiers: &Modifiers,
5761 window: &mut Window,
5762 cx: &mut Context<Self>,
5763 ) {
5764 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5765 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5766 return;
5767 };
5768
5769 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5770 if matches!(
5771 self.edit_prediction_preview,
5772 EditPredictionPreview::Inactive { .. }
5773 ) {
5774 self.edit_prediction_preview = EditPredictionPreview::Active {
5775 previous_scroll_position: None,
5776 since: Instant::now(),
5777 };
5778
5779 self.update_visible_inline_completion(window, cx);
5780 cx.notify();
5781 }
5782 } else if let EditPredictionPreview::Active {
5783 previous_scroll_position,
5784 since,
5785 } = self.edit_prediction_preview
5786 {
5787 if let (Some(previous_scroll_position), Some(position_map)) =
5788 (previous_scroll_position, self.last_position_map.as_ref())
5789 {
5790 self.set_scroll_position(
5791 previous_scroll_position
5792 .scroll_position(&position_map.snapshot.display_snapshot),
5793 window,
5794 cx,
5795 );
5796 }
5797
5798 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5799 released_too_fast: since.elapsed() < Duration::from_millis(200),
5800 };
5801 self.clear_row_highlights::<EditPredictionPreview>();
5802 self.update_visible_inline_completion(window, cx);
5803 cx.notify();
5804 }
5805 }
5806
5807 fn update_visible_inline_completion(
5808 &mut self,
5809 _window: &mut Window,
5810 cx: &mut Context<Self>,
5811 ) -> Option<()> {
5812 let selection = self.selections.newest_anchor();
5813 let cursor = selection.head();
5814 let multibuffer = self.buffer.read(cx).snapshot(cx);
5815 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5816 let excerpt_id = cursor.excerpt_id;
5817
5818 let show_in_menu = self.show_edit_predictions_in_menu();
5819 let completions_menu_has_precedence = !show_in_menu
5820 && (self.context_menu.borrow().is_some()
5821 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5822
5823 if completions_menu_has_precedence
5824 || !offset_selection.is_empty()
5825 || self
5826 .active_inline_completion
5827 .as_ref()
5828 .map_or(false, |completion| {
5829 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5830 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5831 !invalidation_range.contains(&offset_selection.head())
5832 })
5833 {
5834 self.discard_inline_completion(false, cx);
5835 return None;
5836 }
5837
5838 self.take_active_inline_completion(cx);
5839 let Some(provider) = self.edit_prediction_provider() else {
5840 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5841 return None;
5842 };
5843
5844 let (buffer, cursor_buffer_position) =
5845 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5846
5847 self.edit_prediction_settings =
5848 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5849
5850 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5851
5852 if self.edit_prediction_indent_conflict {
5853 let cursor_point = cursor.to_point(&multibuffer);
5854
5855 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5856
5857 if let Some((_, indent)) = indents.iter().next() {
5858 if indent.len == cursor_point.column {
5859 self.edit_prediction_indent_conflict = false;
5860 }
5861 }
5862 }
5863
5864 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5865 let edits = inline_completion
5866 .edits
5867 .into_iter()
5868 .flat_map(|(range, new_text)| {
5869 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5870 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5871 Some((start..end, new_text))
5872 })
5873 .collect::<Vec<_>>();
5874 if edits.is_empty() {
5875 return None;
5876 }
5877
5878 let first_edit_start = edits.first().unwrap().0.start;
5879 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5880 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5881
5882 let last_edit_end = edits.last().unwrap().0.end;
5883 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5884 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5885
5886 let cursor_row = cursor.to_point(&multibuffer).row;
5887
5888 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5889
5890 let mut inlay_ids = Vec::new();
5891 let invalidation_row_range;
5892 let move_invalidation_row_range = if cursor_row < edit_start_row {
5893 Some(cursor_row..edit_end_row)
5894 } else if cursor_row > edit_end_row {
5895 Some(edit_start_row..cursor_row)
5896 } else {
5897 None
5898 };
5899 let is_move =
5900 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5901 let completion = if is_move {
5902 invalidation_row_range =
5903 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5904 let target = first_edit_start;
5905 InlineCompletion::Move { target, snapshot }
5906 } else {
5907 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5908 && !self.inline_completions_hidden_for_vim_mode;
5909
5910 if show_completions_in_buffer {
5911 if edits
5912 .iter()
5913 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5914 {
5915 let mut inlays = Vec::new();
5916 for (range, new_text) in &edits {
5917 let inlay = Inlay::inline_completion(
5918 post_inc(&mut self.next_inlay_id),
5919 range.start,
5920 new_text.as_str(),
5921 );
5922 inlay_ids.push(inlay.id);
5923 inlays.push(inlay);
5924 }
5925
5926 self.splice_inlays(&[], inlays, cx);
5927 } else {
5928 let background_color = cx.theme().status().deleted_background;
5929 self.highlight_text::<InlineCompletionHighlight>(
5930 edits.iter().map(|(range, _)| range.clone()).collect(),
5931 HighlightStyle {
5932 background_color: Some(background_color),
5933 ..Default::default()
5934 },
5935 cx,
5936 );
5937 }
5938 }
5939
5940 invalidation_row_range = edit_start_row..edit_end_row;
5941
5942 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5943 if provider.show_tab_accept_marker() {
5944 EditDisplayMode::TabAccept
5945 } else {
5946 EditDisplayMode::Inline
5947 }
5948 } else {
5949 EditDisplayMode::DiffPopover
5950 };
5951
5952 InlineCompletion::Edit {
5953 edits,
5954 edit_preview: inline_completion.edit_preview,
5955 display_mode,
5956 snapshot,
5957 }
5958 };
5959
5960 let invalidation_range = multibuffer
5961 .anchor_before(Point::new(invalidation_row_range.start, 0))
5962 ..multibuffer.anchor_after(Point::new(
5963 invalidation_row_range.end,
5964 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5965 ));
5966
5967 self.stale_inline_completion_in_menu = None;
5968 self.active_inline_completion = Some(InlineCompletionState {
5969 inlay_ids,
5970 completion,
5971 completion_id: inline_completion.id,
5972 invalidation_range,
5973 });
5974
5975 cx.notify();
5976
5977 Some(())
5978 }
5979
5980 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5981 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5982 }
5983
5984 fn render_code_actions_indicator(
5985 &self,
5986 _style: &EditorStyle,
5987 row: DisplayRow,
5988 is_active: bool,
5989 breakpoint: Option<&(Anchor, Breakpoint)>,
5990 cx: &mut Context<Self>,
5991 ) -> Option<IconButton> {
5992 let color = Color::Muted;
5993
5994 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
5995 let bp_kind = Arc::new(
5996 breakpoint
5997 .map(|(_, bp)| bp.kind.clone())
5998 .unwrap_or(BreakpointKind::Standard),
5999 );
6000
6001 if self.available_code_actions.is_some() {
6002 Some(
6003 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
6004 .shape(ui::IconButtonShape::Square)
6005 .icon_size(IconSize::XSmall)
6006 .icon_color(color)
6007 .toggle_state(is_active)
6008 .tooltip({
6009 let focus_handle = self.focus_handle.clone();
6010 move |window, cx| {
6011 Tooltip::for_action_in(
6012 "Toggle Code Actions",
6013 &ToggleCodeActions {
6014 deployed_from_indicator: None,
6015 },
6016 &focus_handle,
6017 window,
6018 cx,
6019 )
6020 }
6021 })
6022 .on_click(cx.listener(move |editor, _e, window, cx| {
6023 window.focus(&editor.focus_handle(cx));
6024 editor.toggle_code_actions(
6025 &ToggleCodeActions {
6026 deployed_from_indicator: Some(row),
6027 },
6028 window,
6029 cx,
6030 );
6031 }))
6032 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6033 editor.set_breakpoint_context_menu(
6034 row,
6035 position,
6036 bp_kind.clone(),
6037 event.down.position,
6038 window,
6039 cx,
6040 );
6041 })),
6042 )
6043 } else {
6044 None
6045 }
6046 }
6047
6048 fn clear_tasks(&mut self) {
6049 self.tasks.clear()
6050 }
6051
6052 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6053 if self.tasks.insert(key, value).is_some() {
6054 // This case should hopefully be rare, but just in case...
6055 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6056 }
6057 }
6058
6059 /// Get all display points of breakpoints that will be rendered within editor
6060 ///
6061 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6062 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6063 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6064 fn active_breakpoints(
6065 &mut self,
6066 range: Range<DisplayRow>,
6067 window: &mut Window,
6068 cx: &mut Context<Self>,
6069 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6070 let mut breakpoint_display_points = HashMap::default();
6071
6072 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6073 return breakpoint_display_points;
6074 };
6075
6076 let snapshot = self.snapshot(window, cx);
6077
6078 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6079 let Some(project) = self.project.as_ref() else {
6080 return breakpoint_display_points;
6081 };
6082
6083 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6084 let buffer_snapshot = buffer.read(cx).snapshot();
6085
6086 for breakpoint in
6087 breakpoint_store
6088 .read(cx)
6089 .breakpoints(&buffer, None, buffer_snapshot.clone(), cx)
6090 {
6091 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6092 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6093 anchor.text_anchor = breakpoint.0;
6094
6095 breakpoint_display_points.insert(
6096 snapshot
6097 .point_to_display_point(
6098 MultiBufferPoint {
6099 row: point.row,
6100 column: point.column,
6101 },
6102 Bias::Left,
6103 )
6104 .row(),
6105 (anchor, breakpoint.1.clone()),
6106 );
6107 }
6108
6109 return breakpoint_display_points;
6110 }
6111
6112 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6113 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6114 for excerpt_boundary in multi_buffer_snapshot.excerpt_boundaries_in_range(range) {
6115 let info = excerpt_boundary.next;
6116
6117 let Some(excerpt_ranges) = multi_buffer_snapshot.range_for_excerpt(info.id) else {
6118 continue;
6119 };
6120
6121 let Some(buffer) =
6122 project.read_with(cx, |this, cx| this.buffer_for_id(info.buffer_id, cx))
6123 else {
6124 continue;
6125 };
6126
6127 if buffer.read(cx).file().is_none() {
6128 continue;
6129 }
6130 let breakpoints = breakpoint_store.read(cx).breakpoints(
6131 &buffer,
6132 Some(info.range.context.start..info.range.context.end),
6133 info.buffer.clone(),
6134 cx,
6135 );
6136
6137 // To translate a breakpoint's position within a singular buffer to a multi buffer
6138 // position we need to know it's excerpt starting location, it's position within
6139 // the singular buffer, and if that position is within the excerpt's range.
6140 let excerpt_head = excerpt_ranges
6141 .start
6142 .to_display_point(&snapshot.display_snapshot);
6143
6144 let buffer_start = info
6145 .buffer
6146 .summary_for_anchor::<Point>(&info.range.context.start);
6147
6148 for (anchor, breakpoint) in breakpoints {
6149 let as_row = info.buffer.summary_for_anchor::<Point>(&anchor).row;
6150 let delta = as_row - buffer_start.row;
6151
6152 let position = excerpt_head + DisplayPoint::new(DisplayRow(delta), 0);
6153
6154 let anchor = snapshot.display_point_to_anchor(position, Bias::Left);
6155
6156 breakpoint_display_points.insert(position.row(), (anchor, breakpoint.clone()));
6157 }
6158 }
6159
6160 breakpoint_display_points
6161 }
6162
6163 fn breakpoint_context_menu(
6164 &self,
6165 anchor: Anchor,
6166 kind: Arc<BreakpointKind>,
6167 window: &mut Window,
6168 cx: &mut Context<Self>,
6169 ) -> Entity<ui::ContextMenu> {
6170 let weak_editor = cx.weak_entity();
6171 let focus_handle = self.focus_handle(cx);
6172
6173 let second_entry_msg = if kind.log_message().is_some() {
6174 "Edit Log Breakpoint"
6175 } else {
6176 "Add Log Breakpoint"
6177 };
6178
6179 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6180 menu.on_blur_subscription(Subscription::new(|| {}))
6181 .context(focus_handle)
6182 .entry("Toggle Breakpoint", None, {
6183 let weak_editor = weak_editor.clone();
6184 move |_window, cx| {
6185 weak_editor
6186 .update(cx, |this, cx| {
6187 this.edit_breakpoint_at_anchor(
6188 anchor,
6189 BreakpointKind::Standard,
6190 BreakpointEditAction::Toggle,
6191 cx,
6192 );
6193 })
6194 .log_err();
6195 }
6196 })
6197 .entry(second_entry_msg, None, move |window, cx| {
6198 weak_editor
6199 .update(cx, |this, cx| {
6200 this.add_edit_breakpoint_block(anchor, kind.as_ref(), window, cx);
6201 })
6202 .log_err();
6203 })
6204 })
6205 }
6206
6207 fn render_breakpoint(
6208 &self,
6209 position: Anchor,
6210 row: DisplayRow,
6211 kind: &BreakpointKind,
6212 cx: &mut Context<Self>,
6213 ) -> IconButton {
6214 let color = if self
6215 .gutter_breakpoint_indicator
6216 .is_some_and(|gutter_bp| gutter_bp.row() == row)
6217 {
6218 Color::Hint
6219 } else {
6220 Color::Debugger
6221 };
6222
6223 let icon = match &kind {
6224 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6225 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6226 };
6227 let arc_kind = Arc::new(kind.clone());
6228 let arc_kind2 = arc_kind.clone();
6229
6230 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6231 .icon_size(IconSize::XSmall)
6232 .size(ui::ButtonSize::None)
6233 .icon_color(color)
6234 .style(ButtonStyle::Transparent)
6235 .on_click(cx.listener(move |editor, _e, window, cx| {
6236 window.focus(&editor.focus_handle(cx));
6237 editor.edit_breakpoint_at_anchor(
6238 position,
6239 arc_kind.as_ref().clone(),
6240 BreakpointEditAction::Toggle,
6241 cx,
6242 );
6243 }))
6244 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6245 editor.set_breakpoint_context_menu(
6246 row,
6247 Some(position),
6248 arc_kind2.clone(),
6249 event.down.position,
6250 window,
6251 cx,
6252 );
6253 }))
6254 }
6255
6256 fn build_tasks_context(
6257 project: &Entity<Project>,
6258 buffer: &Entity<Buffer>,
6259 buffer_row: u32,
6260 tasks: &Arc<RunnableTasks>,
6261 cx: &mut Context<Self>,
6262 ) -> Task<Option<task::TaskContext>> {
6263 let position = Point::new(buffer_row, tasks.column);
6264 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6265 let location = Location {
6266 buffer: buffer.clone(),
6267 range: range_start..range_start,
6268 };
6269 // Fill in the environmental variables from the tree-sitter captures
6270 let mut captured_task_variables = TaskVariables::default();
6271 for (capture_name, value) in tasks.extra_variables.clone() {
6272 captured_task_variables.insert(
6273 task::VariableName::Custom(capture_name.into()),
6274 value.clone(),
6275 );
6276 }
6277 project.update(cx, |project, cx| {
6278 project.task_store().update(cx, |task_store, cx| {
6279 task_store.task_context_for_location(captured_task_variables, location, cx)
6280 })
6281 })
6282 }
6283
6284 pub fn spawn_nearest_task(
6285 &mut self,
6286 action: &SpawnNearestTask,
6287 window: &mut Window,
6288 cx: &mut Context<Self>,
6289 ) {
6290 let Some((workspace, _)) = self.workspace.clone() else {
6291 return;
6292 };
6293 let Some(project) = self.project.clone() else {
6294 return;
6295 };
6296
6297 // Try to find a closest, enclosing node using tree-sitter that has a
6298 // task
6299 let Some((buffer, buffer_row, tasks)) = self
6300 .find_enclosing_node_task(cx)
6301 // Or find the task that's closest in row-distance.
6302 .or_else(|| self.find_closest_task(cx))
6303 else {
6304 return;
6305 };
6306
6307 let reveal_strategy = action.reveal;
6308 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6309 cx.spawn_in(window, async move |_, cx| {
6310 let context = task_context.await?;
6311 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6312
6313 let resolved = resolved_task.resolved.as_mut()?;
6314 resolved.reveal = reveal_strategy;
6315
6316 workspace
6317 .update(cx, |workspace, cx| {
6318 workspace::tasks::schedule_resolved_task(
6319 workspace,
6320 task_source_kind,
6321 resolved_task,
6322 false,
6323 cx,
6324 );
6325 })
6326 .ok()
6327 })
6328 .detach();
6329 }
6330
6331 fn find_closest_task(
6332 &mut self,
6333 cx: &mut Context<Self>,
6334 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6335 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6336
6337 let ((buffer_id, row), tasks) = self
6338 .tasks
6339 .iter()
6340 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6341
6342 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6343 let tasks = Arc::new(tasks.to_owned());
6344 Some((buffer, *row, tasks))
6345 }
6346
6347 fn find_enclosing_node_task(
6348 &mut self,
6349 cx: &mut Context<Self>,
6350 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6351 let snapshot = self.buffer.read(cx).snapshot(cx);
6352 let offset = self.selections.newest::<usize>(cx).head();
6353 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6354 let buffer_id = excerpt.buffer().remote_id();
6355
6356 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6357 let mut cursor = layer.node().walk();
6358
6359 while cursor.goto_first_child_for_byte(offset).is_some() {
6360 if cursor.node().end_byte() == offset {
6361 cursor.goto_next_sibling();
6362 }
6363 }
6364
6365 // Ascend to the smallest ancestor that contains the range and has a task.
6366 loop {
6367 let node = cursor.node();
6368 let node_range = node.byte_range();
6369 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6370
6371 // Check if this node contains our offset
6372 if node_range.start <= offset && node_range.end >= offset {
6373 // If it contains offset, check for task
6374 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6375 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6376 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6377 }
6378 }
6379
6380 if !cursor.goto_parent() {
6381 break;
6382 }
6383 }
6384 None
6385 }
6386
6387 fn render_run_indicator(
6388 &self,
6389 _style: &EditorStyle,
6390 is_active: bool,
6391 row: DisplayRow,
6392 breakpoint: Option<(Anchor, Breakpoint)>,
6393 cx: &mut Context<Self>,
6394 ) -> IconButton {
6395 let color = Color::Muted;
6396
6397 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6398 let bp_kind = Arc::new(
6399 breakpoint
6400 .map(|(_, bp)| bp.kind)
6401 .unwrap_or(BreakpointKind::Standard),
6402 );
6403
6404 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6405 .shape(ui::IconButtonShape::Square)
6406 .icon_size(IconSize::XSmall)
6407 .icon_color(color)
6408 .toggle_state(is_active)
6409 .on_click(cx.listener(move |editor, _e, window, cx| {
6410 window.focus(&editor.focus_handle(cx));
6411 editor.toggle_code_actions(
6412 &ToggleCodeActions {
6413 deployed_from_indicator: Some(row),
6414 },
6415 window,
6416 cx,
6417 );
6418 }))
6419 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6420 editor.set_breakpoint_context_menu(
6421 row,
6422 position,
6423 bp_kind.clone(),
6424 event.down.position,
6425 window,
6426 cx,
6427 );
6428 }))
6429 }
6430
6431 pub fn context_menu_visible(&self) -> bool {
6432 !self.edit_prediction_preview_is_active()
6433 && self
6434 .context_menu
6435 .borrow()
6436 .as_ref()
6437 .map_or(false, |menu| menu.visible())
6438 }
6439
6440 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6441 self.context_menu
6442 .borrow()
6443 .as_ref()
6444 .map(|menu| menu.origin())
6445 }
6446
6447 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6448 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6449
6450 fn render_edit_prediction_popover(
6451 &mut self,
6452 text_bounds: &Bounds<Pixels>,
6453 content_origin: gpui::Point<Pixels>,
6454 editor_snapshot: &EditorSnapshot,
6455 visible_row_range: Range<DisplayRow>,
6456 scroll_top: f32,
6457 scroll_bottom: f32,
6458 line_layouts: &[LineWithInvisibles],
6459 line_height: Pixels,
6460 scroll_pixel_position: gpui::Point<Pixels>,
6461 newest_selection_head: Option<DisplayPoint>,
6462 editor_width: Pixels,
6463 style: &EditorStyle,
6464 window: &mut Window,
6465 cx: &mut App,
6466 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6467 let active_inline_completion = self.active_inline_completion.as_ref()?;
6468
6469 if self.edit_prediction_visible_in_cursor_popover(true) {
6470 return None;
6471 }
6472
6473 match &active_inline_completion.completion {
6474 InlineCompletion::Move { target, .. } => {
6475 let target_display_point = target.to_display_point(editor_snapshot);
6476
6477 if self.edit_prediction_requires_modifier() {
6478 if !self.edit_prediction_preview_is_active() {
6479 return None;
6480 }
6481
6482 self.render_edit_prediction_modifier_jump_popover(
6483 text_bounds,
6484 content_origin,
6485 visible_row_range,
6486 line_layouts,
6487 line_height,
6488 scroll_pixel_position,
6489 newest_selection_head,
6490 target_display_point,
6491 window,
6492 cx,
6493 )
6494 } else {
6495 self.render_edit_prediction_eager_jump_popover(
6496 text_bounds,
6497 content_origin,
6498 editor_snapshot,
6499 visible_row_range,
6500 scroll_top,
6501 scroll_bottom,
6502 line_height,
6503 scroll_pixel_position,
6504 target_display_point,
6505 editor_width,
6506 window,
6507 cx,
6508 )
6509 }
6510 }
6511 InlineCompletion::Edit {
6512 display_mode: EditDisplayMode::Inline,
6513 ..
6514 } => None,
6515 InlineCompletion::Edit {
6516 display_mode: EditDisplayMode::TabAccept,
6517 edits,
6518 ..
6519 } => {
6520 let range = &edits.first()?.0;
6521 let target_display_point = range.end.to_display_point(editor_snapshot);
6522
6523 self.render_edit_prediction_end_of_line_popover(
6524 "Accept",
6525 editor_snapshot,
6526 visible_row_range,
6527 target_display_point,
6528 line_height,
6529 scroll_pixel_position,
6530 content_origin,
6531 editor_width,
6532 window,
6533 cx,
6534 )
6535 }
6536 InlineCompletion::Edit {
6537 edits,
6538 edit_preview,
6539 display_mode: EditDisplayMode::DiffPopover,
6540 snapshot,
6541 } => self.render_edit_prediction_diff_popover(
6542 text_bounds,
6543 content_origin,
6544 editor_snapshot,
6545 visible_row_range,
6546 line_layouts,
6547 line_height,
6548 scroll_pixel_position,
6549 newest_selection_head,
6550 editor_width,
6551 style,
6552 edits,
6553 edit_preview,
6554 snapshot,
6555 window,
6556 cx,
6557 ),
6558 }
6559 }
6560
6561 fn render_edit_prediction_modifier_jump_popover(
6562 &mut self,
6563 text_bounds: &Bounds<Pixels>,
6564 content_origin: gpui::Point<Pixels>,
6565 visible_row_range: Range<DisplayRow>,
6566 line_layouts: &[LineWithInvisibles],
6567 line_height: Pixels,
6568 scroll_pixel_position: gpui::Point<Pixels>,
6569 newest_selection_head: Option<DisplayPoint>,
6570 target_display_point: DisplayPoint,
6571 window: &mut Window,
6572 cx: &mut App,
6573 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6574 let scrolled_content_origin =
6575 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6576
6577 const SCROLL_PADDING_Y: Pixels = px(12.);
6578
6579 if target_display_point.row() < visible_row_range.start {
6580 return self.render_edit_prediction_scroll_popover(
6581 |_| SCROLL_PADDING_Y,
6582 IconName::ArrowUp,
6583 visible_row_range,
6584 line_layouts,
6585 newest_selection_head,
6586 scrolled_content_origin,
6587 window,
6588 cx,
6589 );
6590 } else if target_display_point.row() >= visible_row_range.end {
6591 return self.render_edit_prediction_scroll_popover(
6592 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6593 IconName::ArrowDown,
6594 visible_row_range,
6595 line_layouts,
6596 newest_selection_head,
6597 scrolled_content_origin,
6598 window,
6599 cx,
6600 );
6601 }
6602
6603 const POLE_WIDTH: Pixels = px(2.);
6604
6605 let line_layout =
6606 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6607 let target_column = target_display_point.column() as usize;
6608
6609 let target_x = line_layout.x_for_index(target_column);
6610 let target_y =
6611 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6612
6613 let flag_on_right = target_x < text_bounds.size.width / 2.;
6614
6615 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6616 border_color.l += 0.001;
6617
6618 let mut element = v_flex()
6619 .items_end()
6620 .when(flag_on_right, |el| el.items_start())
6621 .child(if flag_on_right {
6622 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6623 .rounded_bl(px(0.))
6624 .rounded_tl(px(0.))
6625 .border_l_2()
6626 .border_color(border_color)
6627 } else {
6628 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6629 .rounded_br(px(0.))
6630 .rounded_tr(px(0.))
6631 .border_r_2()
6632 .border_color(border_color)
6633 })
6634 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6635 .into_any();
6636
6637 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6638
6639 let mut origin = scrolled_content_origin + point(target_x, target_y)
6640 - point(
6641 if flag_on_right {
6642 POLE_WIDTH
6643 } else {
6644 size.width - POLE_WIDTH
6645 },
6646 size.height - line_height,
6647 );
6648
6649 origin.x = origin.x.max(content_origin.x);
6650
6651 element.prepaint_at(origin, window, cx);
6652
6653 Some((element, origin))
6654 }
6655
6656 fn render_edit_prediction_scroll_popover(
6657 &mut self,
6658 to_y: impl Fn(Size<Pixels>) -> Pixels,
6659 scroll_icon: IconName,
6660 visible_row_range: Range<DisplayRow>,
6661 line_layouts: &[LineWithInvisibles],
6662 newest_selection_head: Option<DisplayPoint>,
6663 scrolled_content_origin: gpui::Point<Pixels>,
6664 window: &mut Window,
6665 cx: &mut App,
6666 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6667 let mut element = self
6668 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6669 .into_any();
6670
6671 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6672
6673 let cursor = newest_selection_head?;
6674 let cursor_row_layout =
6675 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6676 let cursor_column = cursor.column() as usize;
6677
6678 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6679
6680 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6681
6682 element.prepaint_at(origin, window, cx);
6683 Some((element, origin))
6684 }
6685
6686 fn render_edit_prediction_eager_jump_popover(
6687 &mut self,
6688 text_bounds: &Bounds<Pixels>,
6689 content_origin: gpui::Point<Pixels>,
6690 editor_snapshot: &EditorSnapshot,
6691 visible_row_range: Range<DisplayRow>,
6692 scroll_top: f32,
6693 scroll_bottom: f32,
6694 line_height: Pixels,
6695 scroll_pixel_position: gpui::Point<Pixels>,
6696 target_display_point: DisplayPoint,
6697 editor_width: Pixels,
6698 window: &mut Window,
6699 cx: &mut App,
6700 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6701 if target_display_point.row().as_f32() < scroll_top {
6702 let mut element = self
6703 .render_edit_prediction_line_popover(
6704 "Jump to Edit",
6705 Some(IconName::ArrowUp),
6706 window,
6707 cx,
6708 )?
6709 .into_any();
6710
6711 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6712 let offset = point(
6713 (text_bounds.size.width - size.width) / 2.,
6714 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6715 );
6716
6717 let origin = text_bounds.origin + offset;
6718 element.prepaint_at(origin, window, cx);
6719 Some((element, origin))
6720 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6721 let mut element = self
6722 .render_edit_prediction_line_popover(
6723 "Jump to Edit",
6724 Some(IconName::ArrowDown),
6725 window,
6726 cx,
6727 )?
6728 .into_any();
6729
6730 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6731 let offset = point(
6732 (text_bounds.size.width - size.width) / 2.,
6733 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6734 );
6735
6736 let origin = text_bounds.origin + offset;
6737 element.prepaint_at(origin, window, cx);
6738 Some((element, origin))
6739 } else {
6740 self.render_edit_prediction_end_of_line_popover(
6741 "Jump to Edit",
6742 editor_snapshot,
6743 visible_row_range,
6744 target_display_point,
6745 line_height,
6746 scroll_pixel_position,
6747 content_origin,
6748 editor_width,
6749 window,
6750 cx,
6751 )
6752 }
6753 }
6754
6755 fn render_edit_prediction_end_of_line_popover(
6756 self: &mut Editor,
6757 label: &'static str,
6758 editor_snapshot: &EditorSnapshot,
6759 visible_row_range: Range<DisplayRow>,
6760 target_display_point: DisplayPoint,
6761 line_height: Pixels,
6762 scroll_pixel_position: gpui::Point<Pixels>,
6763 content_origin: gpui::Point<Pixels>,
6764 editor_width: Pixels,
6765 window: &mut Window,
6766 cx: &mut App,
6767 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6768 let target_line_end = DisplayPoint::new(
6769 target_display_point.row(),
6770 editor_snapshot.line_len(target_display_point.row()),
6771 );
6772
6773 let mut element = self
6774 .render_edit_prediction_line_popover(label, None, window, cx)?
6775 .into_any();
6776
6777 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6778
6779 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6780
6781 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6782 let mut origin = start_point
6783 + line_origin
6784 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6785 origin.x = origin.x.max(content_origin.x);
6786
6787 let max_x = content_origin.x + editor_width - size.width;
6788
6789 if origin.x > max_x {
6790 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6791
6792 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6793 origin.y += offset;
6794 IconName::ArrowUp
6795 } else {
6796 origin.y -= offset;
6797 IconName::ArrowDown
6798 };
6799
6800 element = self
6801 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6802 .into_any();
6803
6804 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6805
6806 origin.x = content_origin.x + editor_width - size.width - px(2.);
6807 }
6808
6809 element.prepaint_at(origin, window, cx);
6810 Some((element, origin))
6811 }
6812
6813 fn render_edit_prediction_diff_popover(
6814 self: &Editor,
6815 text_bounds: &Bounds<Pixels>,
6816 content_origin: gpui::Point<Pixels>,
6817 editor_snapshot: &EditorSnapshot,
6818 visible_row_range: Range<DisplayRow>,
6819 line_layouts: &[LineWithInvisibles],
6820 line_height: Pixels,
6821 scroll_pixel_position: gpui::Point<Pixels>,
6822 newest_selection_head: Option<DisplayPoint>,
6823 editor_width: Pixels,
6824 style: &EditorStyle,
6825 edits: &Vec<(Range<Anchor>, String)>,
6826 edit_preview: &Option<language::EditPreview>,
6827 snapshot: &language::BufferSnapshot,
6828 window: &mut Window,
6829 cx: &mut App,
6830 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6831 let edit_start = edits
6832 .first()
6833 .unwrap()
6834 .0
6835 .start
6836 .to_display_point(editor_snapshot);
6837 let edit_end = edits
6838 .last()
6839 .unwrap()
6840 .0
6841 .end
6842 .to_display_point(editor_snapshot);
6843
6844 let is_visible = visible_row_range.contains(&edit_start.row())
6845 || visible_row_range.contains(&edit_end.row());
6846 if !is_visible {
6847 return None;
6848 }
6849
6850 let highlighted_edits =
6851 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6852
6853 let styled_text = highlighted_edits.to_styled_text(&style.text);
6854 let line_count = highlighted_edits.text.lines().count();
6855
6856 const BORDER_WIDTH: Pixels = px(1.);
6857
6858 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6859 let has_keybind = keybind.is_some();
6860
6861 let mut element = h_flex()
6862 .items_start()
6863 .child(
6864 h_flex()
6865 .bg(cx.theme().colors().editor_background)
6866 .border(BORDER_WIDTH)
6867 .shadow_sm()
6868 .border_color(cx.theme().colors().border)
6869 .rounded_l_lg()
6870 .when(line_count > 1, |el| el.rounded_br_lg())
6871 .pr_1()
6872 .child(styled_text),
6873 )
6874 .child(
6875 h_flex()
6876 .h(line_height + BORDER_WIDTH * px(2.))
6877 .px_1p5()
6878 .gap_1()
6879 // Workaround: For some reason, there's a gap if we don't do this
6880 .ml(-BORDER_WIDTH)
6881 .shadow(smallvec![gpui::BoxShadow {
6882 color: gpui::black().opacity(0.05),
6883 offset: point(px(1.), px(1.)),
6884 blur_radius: px(2.),
6885 spread_radius: px(0.),
6886 }])
6887 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6888 .border(BORDER_WIDTH)
6889 .border_color(cx.theme().colors().border)
6890 .rounded_r_lg()
6891 .id("edit_prediction_diff_popover_keybind")
6892 .when(!has_keybind, |el| {
6893 let status_colors = cx.theme().status();
6894
6895 el.bg(status_colors.error_background)
6896 .border_color(status_colors.error.opacity(0.6))
6897 .child(Icon::new(IconName::Info).color(Color::Error))
6898 .cursor_default()
6899 .hoverable_tooltip(move |_window, cx| {
6900 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6901 })
6902 })
6903 .children(keybind),
6904 )
6905 .into_any();
6906
6907 let longest_row =
6908 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6909 let longest_line_width = if visible_row_range.contains(&longest_row) {
6910 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6911 } else {
6912 layout_line(
6913 longest_row,
6914 editor_snapshot,
6915 style,
6916 editor_width,
6917 |_| false,
6918 window,
6919 cx,
6920 )
6921 .width
6922 };
6923
6924 let viewport_bounds =
6925 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6926 right: -EditorElement::SCROLLBAR_WIDTH,
6927 ..Default::default()
6928 });
6929
6930 let x_after_longest =
6931 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6932 - scroll_pixel_position.x;
6933
6934 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6935
6936 // Fully visible if it can be displayed within the window (allow overlapping other
6937 // panes). However, this is only allowed if the popover starts within text_bounds.
6938 let can_position_to_the_right = x_after_longest < text_bounds.right()
6939 && x_after_longest + element_bounds.width < viewport_bounds.right();
6940
6941 let mut origin = if can_position_to_the_right {
6942 point(
6943 x_after_longest,
6944 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6945 - scroll_pixel_position.y,
6946 )
6947 } else {
6948 let cursor_row = newest_selection_head.map(|head| head.row());
6949 let above_edit = edit_start
6950 .row()
6951 .0
6952 .checked_sub(line_count as u32)
6953 .map(DisplayRow);
6954 let below_edit = Some(edit_end.row() + 1);
6955 let above_cursor =
6956 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6957 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6958
6959 // Place the edit popover adjacent to the edit if there is a location
6960 // available that is onscreen and does not obscure the cursor. Otherwise,
6961 // place it adjacent to the cursor.
6962 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6963 .into_iter()
6964 .flatten()
6965 .find(|&start_row| {
6966 let end_row = start_row + line_count as u32;
6967 visible_row_range.contains(&start_row)
6968 && visible_row_range.contains(&end_row)
6969 && cursor_row.map_or(true, |cursor_row| {
6970 !((start_row..end_row).contains(&cursor_row))
6971 })
6972 })?;
6973
6974 content_origin
6975 + point(
6976 -scroll_pixel_position.x,
6977 row_target.as_f32() * line_height - scroll_pixel_position.y,
6978 )
6979 };
6980
6981 origin.x -= BORDER_WIDTH;
6982
6983 window.defer_draw(element, origin, 1);
6984
6985 // Do not return an element, since it will already be drawn due to defer_draw.
6986 None
6987 }
6988
6989 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6990 px(30.)
6991 }
6992
6993 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6994 if self.read_only(cx) {
6995 cx.theme().players().read_only()
6996 } else {
6997 self.style.as_ref().unwrap().local_player
6998 }
6999 }
7000
7001 fn render_edit_prediction_accept_keybind(
7002 &self,
7003 window: &mut Window,
7004 cx: &App,
7005 ) -> Option<AnyElement> {
7006 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
7007 let accept_keystroke = accept_binding.keystroke()?;
7008
7009 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7010
7011 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
7012 Color::Accent
7013 } else {
7014 Color::Muted
7015 };
7016
7017 h_flex()
7018 .px_0p5()
7019 .when(is_platform_style_mac, |parent| parent.gap_0p5())
7020 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7021 .text_size(TextSize::XSmall.rems(cx))
7022 .child(h_flex().children(ui::render_modifiers(
7023 &accept_keystroke.modifiers,
7024 PlatformStyle::platform(),
7025 Some(modifiers_color),
7026 Some(IconSize::XSmall.rems().into()),
7027 true,
7028 )))
7029 .when(is_platform_style_mac, |parent| {
7030 parent.child(accept_keystroke.key.clone())
7031 })
7032 .when(!is_platform_style_mac, |parent| {
7033 parent.child(
7034 Key::new(
7035 util::capitalize(&accept_keystroke.key),
7036 Some(Color::Default),
7037 )
7038 .size(Some(IconSize::XSmall.rems().into())),
7039 )
7040 })
7041 .into_any()
7042 .into()
7043 }
7044
7045 fn render_edit_prediction_line_popover(
7046 &self,
7047 label: impl Into<SharedString>,
7048 icon: Option<IconName>,
7049 window: &mut Window,
7050 cx: &App,
7051 ) -> Option<Stateful<Div>> {
7052 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7053
7054 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7055 let has_keybind = keybind.is_some();
7056
7057 let result = h_flex()
7058 .id("ep-line-popover")
7059 .py_0p5()
7060 .pl_1()
7061 .pr(padding_right)
7062 .gap_1()
7063 .rounded_md()
7064 .border_1()
7065 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7066 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7067 .shadow_sm()
7068 .when(!has_keybind, |el| {
7069 let status_colors = cx.theme().status();
7070
7071 el.bg(status_colors.error_background)
7072 .border_color(status_colors.error.opacity(0.6))
7073 .pl_2()
7074 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7075 .cursor_default()
7076 .hoverable_tooltip(move |_window, cx| {
7077 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7078 })
7079 })
7080 .children(keybind)
7081 .child(
7082 Label::new(label)
7083 .size(LabelSize::Small)
7084 .when(!has_keybind, |el| {
7085 el.color(cx.theme().status().error.into()).strikethrough()
7086 }),
7087 )
7088 .when(!has_keybind, |el| {
7089 el.child(
7090 h_flex().ml_1().child(
7091 Icon::new(IconName::Info)
7092 .size(IconSize::Small)
7093 .color(cx.theme().status().error.into()),
7094 ),
7095 )
7096 })
7097 .when_some(icon, |element, icon| {
7098 element.child(
7099 div()
7100 .mt(px(1.5))
7101 .child(Icon::new(icon).size(IconSize::Small)),
7102 )
7103 });
7104
7105 Some(result)
7106 }
7107
7108 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7109 let accent_color = cx.theme().colors().text_accent;
7110 let editor_bg_color = cx.theme().colors().editor_background;
7111 editor_bg_color.blend(accent_color.opacity(0.1))
7112 }
7113
7114 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7115 let accent_color = cx.theme().colors().text_accent;
7116 let editor_bg_color = cx.theme().colors().editor_background;
7117 editor_bg_color.blend(accent_color.opacity(0.6))
7118 }
7119
7120 fn render_edit_prediction_cursor_popover(
7121 &self,
7122 min_width: Pixels,
7123 max_width: Pixels,
7124 cursor_point: Point,
7125 style: &EditorStyle,
7126 accept_keystroke: Option<&gpui::Keystroke>,
7127 _window: &Window,
7128 cx: &mut Context<Editor>,
7129 ) -> Option<AnyElement> {
7130 let provider = self.edit_prediction_provider.as_ref()?;
7131
7132 if provider.provider.needs_terms_acceptance(cx) {
7133 return Some(
7134 h_flex()
7135 .min_w(min_width)
7136 .flex_1()
7137 .px_2()
7138 .py_1()
7139 .gap_3()
7140 .elevation_2(cx)
7141 .hover(|style| style.bg(cx.theme().colors().element_hover))
7142 .id("accept-terms")
7143 .cursor_pointer()
7144 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7145 .on_click(cx.listener(|this, _event, window, cx| {
7146 cx.stop_propagation();
7147 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7148 window.dispatch_action(
7149 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7150 cx,
7151 );
7152 }))
7153 .child(
7154 h_flex()
7155 .flex_1()
7156 .gap_2()
7157 .child(Icon::new(IconName::ZedPredict))
7158 .child(Label::new("Accept Terms of Service"))
7159 .child(div().w_full())
7160 .child(
7161 Icon::new(IconName::ArrowUpRight)
7162 .color(Color::Muted)
7163 .size(IconSize::Small),
7164 )
7165 .into_any_element(),
7166 )
7167 .into_any(),
7168 );
7169 }
7170
7171 let is_refreshing = provider.provider.is_refreshing(cx);
7172
7173 fn pending_completion_container() -> Div {
7174 h_flex()
7175 .h_full()
7176 .flex_1()
7177 .gap_2()
7178 .child(Icon::new(IconName::ZedPredict))
7179 }
7180
7181 let completion = match &self.active_inline_completion {
7182 Some(prediction) => {
7183 if !self.has_visible_completions_menu() {
7184 const RADIUS: Pixels = px(6.);
7185 const BORDER_WIDTH: Pixels = px(1.);
7186
7187 return Some(
7188 h_flex()
7189 .elevation_2(cx)
7190 .border(BORDER_WIDTH)
7191 .border_color(cx.theme().colors().border)
7192 .when(accept_keystroke.is_none(), |el| {
7193 el.border_color(cx.theme().status().error)
7194 })
7195 .rounded(RADIUS)
7196 .rounded_tl(px(0.))
7197 .overflow_hidden()
7198 .child(div().px_1p5().child(match &prediction.completion {
7199 InlineCompletion::Move { target, snapshot } => {
7200 use text::ToPoint as _;
7201 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7202 {
7203 Icon::new(IconName::ZedPredictDown)
7204 } else {
7205 Icon::new(IconName::ZedPredictUp)
7206 }
7207 }
7208 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7209 }))
7210 .child(
7211 h_flex()
7212 .gap_1()
7213 .py_1()
7214 .px_2()
7215 .rounded_r(RADIUS - BORDER_WIDTH)
7216 .border_l_1()
7217 .border_color(cx.theme().colors().border)
7218 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7219 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7220 el.child(
7221 Label::new("Hold")
7222 .size(LabelSize::Small)
7223 .when(accept_keystroke.is_none(), |el| {
7224 el.strikethrough()
7225 })
7226 .line_height_style(LineHeightStyle::UiLabel),
7227 )
7228 })
7229 .id("edit_prediction_cursor_popover_keybind")
7230 .when(accept_keystroke.is_none(), |el| {
7231 let status_colors = cx.theme().status();
7232
7233 el.bg(status_colors.error_background)
7234 .border_color(status_colors.error.opacity(0.6))
7235 .child(Icon::new(IconName::Info).color(Color::Error))
7236 .cursor_default()
7237 .hoverable_tooltip(move |_window, cx| {
7238 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7239 .into()
7240 })
7241 })
7242 .when_some(
7243 accept_keystroke.as_ref(),
7244 |el, accept_keystroke| {
7245 el.child(h_flex().children(ui::render_modifiers(
7246 &accept_keystroke.modifiers,
7247 PlatformStyle::platform(),
7248 Some(Color::Default),
7249 Some(IconSize::XSmall.rems().into()),
7250 false,
7251 )))
7252 },
7253 ),
7254 )
7255 .into_any(),
7256 );
7257 }
7258
7259 self.render_edit_prediction_cursor_popover_preview(
7260 prediction,
7261 cursor_point,
7262 style,
7263 cx,
7264 )?
7265 }
7266
7267 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7268 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7269 stale_completion,
7270 cursor_point,
7271 style,
7272 cx,
7273 )?,
7274
7275 None => {
7276 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7277 }
7278 },
7279
7280 None => pending_completion_container().child(Label::new("No Prediction")),
7281 };
7282
7283 let completion = if is_refreshing {
7284 completion
7285 .with_animation(
7286 "loading-completion",
7287 Animation::new(Duration::from_secs(2))
7288 .repeat()
7289 .with_easing(pulsating_between(0.4, 0.8)),
7290 |label, delta| label.opacity(delta),
7291 )
7292 .into_any_element()
7293 } else {
7294 completion.into_any_element()
7295 };
7296
7297 let has_completion = self.active_inline_completion.is_some();
7298
7299 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7300 Some(
7301 h_flex()
7302 .min_w(min_width)
7303 .max_w(max_width)
7304 .flex_1()
7305 .elevation_2(cx)
7306 .border_color(cx.theme().colors().border)
7307 .child(
7308 div()
7309 .flex_1()
7310 .py_1()
7311 .px_2()
7312 .overflow_hidden()
7313 .child(completion),
7314 )
7315 .when_some(accept_keystroke, |el, accept_keystroke| {
7316 if !accept_keystroke.modifiers.modified() {
7317 return el;
7318 }
7319
7320 el.child(
7321 h_flex()
7322 .h_full()
7323 .border_l_1()
7324 .rounded_r_lg()
7325 .border_color(cx.theme().colors().border)
7326 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7327 .gap_1()
7328 .py_1()
7329 .px_2()
7330 .child(
7331 h_flex()
7332 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7333 .when(is_platform_style_mac, |parent| parent.gap_1())
7334 .child(h_flex().children(ui::render_modifiers(
7335 &accept_keystroke.modifiers,
7336 PlatformStyle::platform(),
7337 Some(if !has_completion {
7338 Color::Muted
7339 } else {
7340 Color::Default
7341 }),
7342 None,
7343 false,
7344 ))),
7345 )
7346 .child(Label::new("Preview").into_any_element())
7347 .opacity(if has_completion { 1.0 } else { 0.4 }),
7348 )
7349 })
7350 .into_any(),
7351 )
7352 }
7353
7354 fn render_edit_prediction_cursor_popover_preview(
7355 &self,
7356 completion: &InlineCompletionState,
7357 cursor_point: Point,
7358 style: &EditorStyle,
7359 cx: &mut Context<Editor>,
7360 ) -> Option<Div> {
7361 use text::ToPoint as _;
7362
7363 fn render_relative_row_jump(
7364 prefix: impl Into<String>,
7365 current_row: u32,
7366 target_row: u32,
7367 ) -> Div {
7368 let (row_diff, arrow) = if target_row < current_row {
7369 (current_row - target_row, IconName::ArrowUp)
7370 } else {
7371 (target_row - current_row, IconName::ArrowDown)
7372 };
7373
7374 h_flex()
7375 .child(
7376 Label::new(format!("{}{}", prefix.into(), row_diff))
7377 .color(Color::Muted)
7378 .size(LabelSize::Small),
7379 )
7380 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7381 }
7382
7383 match &completion.completion {
7384 InlineCompletion::Move {
7385 target, snapshot, ..
7386 } => Some(
7387 h_flex()
7388 .px_2()
7389 .gap_2()
7390 .flex_1()
7391 .child(
7392 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7393 Icon::new(IconName::ZedPredictDown)
7394 } else {
7395 Icon::new(IconName::ZedPredictUp)
7396 },
7397 )
7398 .child(Label::new("Jump to Edit")),
7399 ),
7400
7401 InlineCompletion::Edit {
7402 edits,
7403 edit_preview,
7404 snapshot,
7405 display_mode: _,
7406 } => {
7407 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7408
7409 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7410 &snapshot,
7411 &edits,
7412 edit_preview.as_ref()?,
7413 true,
7414 cx,
7415 )
7416 .first_line_preview();
7417
7418 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7419 .with_default_highlights(&style.text, highlighted_edits.highlights);
7420
7421 let preview = h_flex()
7422 .gap_1()
7423 .min_w_16()
7424 .child(styled_text)
7425 .when(has_more_lines, |parent| parent.child("…"));
7426
7427 let left = if first_edit_row != cursor_point.row {
7428 render_relative_row_jump("", cursor_point.row, first_edit_row)
7429 .into_any_element()
7430 } else {
7431 Icon::new(IconName::ZedPredict).into_any_element()
7432 };
7433
7434 Some(
7435 h_flex()
7436 .h_full()
7437 .flex_1()
7438 .gap_2()
7439 .pr_1()
7440 .overflow_x_hidden()
7441 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7442 .child(left)
7443 .child(preview),
7444 )
7445 }
7446 }
7447 }
7448
7449 fn render_context_menu(
7450 &self,
7451 style: &EditorStyle,
7452 max_height_in_lines: u32,
7453 y_flipped: bool,
7454 window: &mut Window,
7455 cx: &mut Context<Editor>,
7456 ) -> Option<AnyElement> {
7457 let menu = self.context_menu.borrow();
7458 let menu = menu.as_ref()?;
7459 if !menu.visible() {
7460 return None;
7461 };
7462 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7463 }
7464
7465 fn render_context_menu_aside(
7466 &mut self,
7467 max_size: Size<Pixels>,
7468 window: &mut Window,
7469 cx: &mut Context<Editor>,
7470 ) -> Option<AnyElement> {
7471 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7472 if menu.visible() {
7473 menu.render_aside(self, max_size, window, cx)
7474 } else {
7475 None
7476 }
7477 })
7478 }
7479
7480 fn hide_context_menu(
7481 &mut self,
7482 window: &mut Window,
7483 cx: &mut Context<Self>,
7484 ) -> Option<CodeContextMenu> {
7485 cx.notify();
7486 self.completion_tasks.clear();
7487 let context_menu = self.context_menu.borrow_mut().take();
7488 self.stale_inline_completion_in_menu.take();
7489 self.update_visible_inline_completion(window, cx);
7490 context_menu
7491 }
7492
7493 fn show_snippet_choices(
7494 &mut self,
7495 choices: &Vec<String>,
7496 selection: Range<Anchor>,
7497 cx: &mut Context<Self>,
7498 ) {
7499 if selection.start.buffer_id.is_none() {
7500 return;
7501 }
7502 let buffer_id = selection.start.buffer_id.unwrap();
7503 let buffer = self.buffer().read(cx).buffer(buffer_id);
7504 let id = post_inc(&mut self.next_completion_id);
7505
7506 if let Some(buffer) = buffer {
7507 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7508 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7509 ));
7510 }
7511 }
7512
7513 pub fn insert_snippet(
7514 &mut self,
7515 insertion_ranges: &[Range<usize>],
7516 snippet: Snippet,
7517 window: &mut Window,
7518 cx: &mut Context<Self>,
7519 ) -> Result<()> {
7520 struct Tabstop<T> {
7521 is_end_tabstop: bool,
7522 ranges: Vec<Range<T>>,
7523 choices: Option<Vec<String>>,
7524 }
7525
7526 let tabstops = self.buffer.update(cx, |buffer, cx| {
7527 let snippet_text: Arc<str> = snippet.text.clone().into();
7528 buffer.edit(
7529 insertion_ranges
7530 .iter()
7531 .cloned()
7532 .map(|range| (range, snippet_text.clone())),
7533 Some(AutoindentMode::EachLine),
7534 cx,
7535 );
7536
7537 let snapshot = &*buffer.read(cx);
7538 let snippet = &snippet;
7539 snippet
7540 .tabstops
7541 .iter()
7542 .map(|tabstop| {
7543 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7544 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7545 });
7546 let mut tabstop_ranges = tabstop
7547 .ranges
7548 .iter()
7549 .flat_map(|tabstop_range| {
7550 let mut delta = 0_isize;
7551 insertion_ranges.iter().map(move |insertion_range| {
7552 let insertion_start = insertion_range.start as isize + delta;
7553 delta +=
7554 snippet.text.len() as isize - insertion_range.len() as isize;
7555
7556 let start = ((insertion_start + tabstop_range.start) as usize)
7557 .min(snapshot.len());
7558 let end = ((insertion_start + tabstop_range.end) as usize)
7559 .min(snapshot.len());
7560 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7561 })
7562 })
7563 .collect::<Vec<_>>();
7564 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7565
7566 Tabstop {
7567 is_end_tabstop,
7568 ranges: tabstop_ranges,
7569 choices: tabstop.choices.clone(),
7570 }
7571 })
7572 .collect::<Vec<_>>()
7573 });
7574 if let Some(tabstop) = tabstops.first() {
7575 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7576 s.select_ranges(tabstop.ranges.iter().cloned());
7577 });
7578
7579 if let Some(choices) = &tabstop.choices {
7580 if let Some(selection) = tabstop.ranges.first() {
7581 self.show_snippet_choices(choices, selection.clone(), cx)
7582 }
7583 }
7584
7585 // If we're already at the last tabstop and it's at the end of the snippet,
7586 // we're done, we don't need to keep the state around.
7587 if !tabstop.is_end_tabstop {
7588 let choices = tabstops
7589 .iter()
7590 .map(|tabstop| tabstop.choices.clone())
7591 .collect();
7592
7593 let ranges = tabstops
7594 .into_iter()
7595 .map(|tabstop| tabstop.ranges)
7596 .collect::<Vec<_>>();
7597
7598 self.snippet_stack.push(SnippetState {
7599 active_index: 0,
7600 ranges,
7601 choices,
7602 });
7603 }
7604
7605 // Check whether the just-entered snippet ends with an auto-closable bracket.
7606 if self.autoclose_regions.is_empty() {
7607 let snapshot = self.buffer.read(cx).snapshot(cx);
7608 for selection in &mut self.selections.all::<Point>(cx) {
7609 let selection_head = selection.head();
7610 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7611 continue;
7612 };
7613
7614 let mut bracket_pair = None;
7615 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7616 let prev_chars = snapshot
7617 .reversed_chars_at(selection_head)
7618 .collect::<String>();
7619 for (pair, enabled) in scope.brackets() {
7620 if enabled
7621 && pair.close
7622 && prev_chars.starts_with(pair.start.as_str())
7623 && next_chars.starts_with(pair.end.as_str())
7624 {
7625 bracket_pair = Some(pair.clone());
7626 break;
7627 }
7628 }
7629 if let Some(pair) = bracket_pair {
7630 let start = snapshot.anchor_after(selection_head);
7631 let end = snapshot.anchor_after(selection_head);
7632 self.autoclose_regions.push(AutocloseRegion {
7633 selection_id: selection.id,
7634 range: start..end,
7635 pair,
7636 });
7637 }
7638 }
7639 }
7640 }
7641 Ok(())
7642 }
7643
7644 pub fn move_to_next_snippet_tabstop(
7645 &mut self,
7646 window: &mut Window,
7647 cx: &mut Context<Self>,
7648 ) -> bool {
7649 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7650 }
7651
7652 pub fn move_to_prev_snippet_tabstop(
7653 &mut self,
7654 window: &mut Window,
7655 cx: &mut Context<Self>,
7656 ) -> bool {
7657 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7658 }
7659
7660 pub fn move_to_snippet_tabstop(
7661 &mut self,
7662 bias: Bias,
7663 window: &mut Window,
7664 cx: &mut Context<Self>,
7665 ) -> bool {
7666 if let Some(mut snippet) = self.snippet_stack.pop() {
7667 match bias {
7668 Bias::Left => {
7669 if snippet.active_index > 0 {
7670 snippet.active_index -= 1;
7671 } else {
7672 self.snippet_stack.push(snippet);
7673 return false;
7674 }
7675 }
7676 Bias::Right => {
7677 if snippet.active_index + 1 < snippet.ranges.len() {
7678 snippet.active_index += 1;
7679 } else {
7680 self.snippet_stack.push(snippet);
7681 return false;
7682 }
7683 }
7684 }
7685 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7686 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7687 s.select_anchor_ranges(current_ranges.iter().cloned())
7688 });
7689
7690 if let Some(choices) = &snippet.choices[snippet.active_index] {
7691 if let Some(selection) = current_ranges.first() {
7692 self.show_snippet_choices(&choices, selection.clone(), cx);
7693 }
7694 }
7695
7696 // If snippet state is not at the last tabstop, push it back on the stack
7697 if snippet.active_index + 1 < snippet.ranges.len() {
7698 self.snippet_stack.push(snippet);
7699 }
7700 return true;
7701 }
7702 }
7703
7704 false
7705 }
7706
7707 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7708 self.transact(window, cx, |this, window, cx| {
7709 this.select_all(&SelectAll, window, cx);
7710 this.insert("", window, cx);
7711 });
7712 }
7713
7714 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7715 self.transact(window, cx, |this, window, cx| {
7716 this.select_autoclose_pair(window, cx);
7717 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7718 if !this.linked_edit_ranges.is_empty() {
7719 let selections = this.selections.all::<MultiBufferPoint>(cx);
7720 let snapshot = this.buffer.read(cx).snapshot(cx);
7721
7722 for selection in selections.iter() {
7723 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7724 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7725 if selection_start.buffer_id != selection_end.buffer_id {
7726 continue;
7727 }
7728 if let Some(ranges) =
7729 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7730 {
7731 for (buffer, entries) in ranges {
7732 linked_ranges.entry(buffer).or_default().extend(entries);
7733 }
7734 }
7735 }
7736 }
7737
7738 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7739 if !this.selections.line_mode {
7740 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7741 for selection in &mut selections {
7742 if selection.is_empty() {
7743 let old_head = selection.head();
7744 let mut new_head =
7745 movement::left(&display_map, old_head.to_display_point(&display_map))
7746 .to_point(&display_map);
7747 if let Some((buffer, line_buffer_range)) = display_map
7748 .buffer_snapshot
7749 .buffer_line_for_row(MultiBufferRow(old_head.row))
7750 {
7751 let indent_size =
7752 buffer.indent_size_for_line(line_buffer_range.start.row);
7753 let indent_len = match indent_size.kind {
7754 IndentKind::Space => {
7755 buffer.settings_at(line_buffer_range.start, cx).tab_size
7756 }
7757 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7758 };
7759 if old_head.column <= indent_size.len && old_head.column > 0 {
7760 let indent_len = indent_len.get();
7761 new_head = cmp::min(
7762 new_head,
7763 MultiBufferPoint::new(
7764 old_head.row,
7765 ((old_head.column - 1) / indent_len) * indent_len,
7766 ),
7767 );
7768 }
7769 }
7770
7771 selection.set_head(new_head, SelectionGoal::None);
7772 }
7773 }
7774 }
7775
7776 this.signature_help_state.set_backspace_pressed(true);
7777 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7778 s.select(selections)
7779 });
7780 this.insert("", window, cx);
7781 let empty_str: Arc<str> = Arc::from("");
7782 for (buffer, edits) in linked_ranges {
7783 let snapshot = buffer.read(cx).snapshot();
7784 use text::ToPoint as TP;
7785
7786 let edits = edits
7787 .into_iter()
7788 .map(|range| {
7789 let end_point = TP::to_point(&range.end, &snapshot);
7790 let mut start_point = TP::to_point(&range.start, &snapshot);
7791
7792 if end_point == start_point {
7793 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7794 .saturating_sub(1);
7795 start_point =
7796 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7797 };
7798
7799 (start_point..end_point, empty_str.clone())
7800 })
7801 .sorted_by_key(|(range, _)| range.start)
7802 .collect::<Vec<_>>();
7803 buffer.update(cx, |this, cx| {
7804 this.edit(edits, None, cx);
7805 })
7806 }
7807 this.refresh_inline_completion(true, false, window, cx);
7808 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7809 });
7810 }
7811
7812 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7813 self.transact(window, cx, |this, window, cx| {
7814 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7815 let line_mode = s.line_mode;
7816 s.move_with(|map, selection| {
7817 if selection.is_empty() && !line_mode {
7818 let cursor = movement::right(map, selection.head());
7819 selection.end = cursor;
7820 selection.reversed = true;
7821 selection.goal = SelectionGoal::None;
7822 }
7823 })
7824 });
7825 this.insert("", window, cx);
7826 this.refresh_inline_completion(true, false, window, cx);
7827 });
7828 }
7829
7830 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7831 if self.move_to_prev_snippet_tabstop(window, cx) {
7832 return;
7833 }
7834
7835 self.outdent(&Outdent, window, cx);
7836 }
7837
7838 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7839 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7840 return;
7841 }
7842
7843 let mut selections = self.selections.all_adjusted(cx);
7844 let buffer = self.buffer.read(cx);
7845 let snapshot = buffer.snapshot(cx);
7846 let rows_iter = selections.iter().map(|s| s.head().row);
7847 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7848
7849 let mut edits = Vec::new();
7850 let mut prev_edited_row = 0;
7851 let mut row_delta = 0;
7852 for selection in &mut selections {
7853 if selection.start.row != prev_edited_row {
7854 row_delta = 0;
7855 }
7856 prev_edited_row = selection.end.row;
7857
7858 // If the selection is non-empty, then increase the indentation of the selected lines.
7859 if !selection.is_empty() {
7860 row_delta =
7861 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7862 continue;
7863 }
7864
7865 // If the selection is empty and the cursor is in the leading whitespace before the
7866 // suggested indentation, then auto-indent the line.
7867 let cursor = selection.head();
7868 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7869 if let Some(suggested_indent) =
7870 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7871 {
7872 if cursor.column < suggested_indent.len
7873 && cursor.column <= current_indent.len
7874 && current_indent.len <= suggested_indent.len
7875 {
7876 selection.start = Point::new(cursor.row, suggested_indent.len);
7877 selection.end = selection.start;
7878 if row_delta == 0 {
7879 edits.extend(Buffer::edit_for_indent_size_adjustment(
7880 cursor.row,
7881 current_indent,
7882 suggested_indent,
7883 ));
7884 row_delta = suggested_indent.len - current_indent.len;
7885 }
7886 continue;
7887 }
7888 }
7889
7890 // Otherwise, insert a hard or soft tab.
7891 let settings = buffer.language_settings_at(cursor, cx);
7892 let tab_size = if settings.hard_tabs {
7893 IndentSize::tab()
7894 } else {
7895 let tab_size = settings.tab_size.get();
7896 let char_column = snapshot
7897 .text_for_range(Point::new(cursor.row, 0)..cursor)
7898 .flat_map(str::chars)
7899 .count()
7900 + row_delta as usize;
7901 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7902 IndentSize::spaces(chars_to_next_tab_stop)
7903 };
7904 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7905 selection.end = selection.start;
7906 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7907 row_delta += tab_size.len;
7908 }
7909
7910 self.transact(window, cx, |this, window, cx| {
7911 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7912 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7913 s.select(selections)
7914 });
7915 this.refresh_inline_completion(true, false, window, cx);
7916 });
7917 }
7918
7919 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7920 if self.read_only(cx) {
7921 return;
7922 }
7923 let mut selections = self.selections.all::<Point>(cx);
7924 let mut prev_edited_row = 0;
7925 let mut row_delta = 0;
7926 let mut edits = Vec::new();
7927 let buffer = self.buffer.read(cx);
7928 let snapshot = buffer.snapshot(cx);
7929 for selection in &mut selections {
7930 if selection.start.row != prev_edited_row {
7931 row_delta = 0;
7932 }
7933 prev_edited_row = selection.end.row;
7934
7935 row_delta =
7936 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7937 }
7938
7939 self.transact(window, cx, |this, window, cx| {
7940 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7941 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7942 s.select(selections)
7943 });
7944 });
7945 }
7946
7947 fn indent_selection(
7948 buffer: &MultiBuffer,
7949 snapshot: &MultiBufferSnapshot,
7950 selection: &mut Selection<Point>,
7951 edits: &mut Vec<(Range<Point>, String)>,
7952 delta_for_start_row: u32,
7953 cx: &App,
7954 ) -> u32 {
7955 let settings = buffer.language_settings_at(selection.start, cx);
7956 let tab_size = settings.tab_size.get();
7957 let indent_kind = if settings.hard_tabs {
7958 IndentKind::Tab
7959 } else {
7960 IndentKind::Space
7961 };
7962 let mut start_row = selection.start.row;
7963 let mut end_row = selection.end.row + 1;
7964
7965 // If a selection ends at the beginning of a line, don't indent
7966 // that last line.
7967 if selection.end.column == 0 && selection.end.row > selection.start.row {
7968 end_row -= 1;
7969 }
7970
7971 // Avoid re-indenting a row that has already been indented by a
7972 // previous selection, but still update this selection's column
7973 // to reflect that indentation.
7974 if delta_for_start_row > 0 {
7975 start_row += 1;
7976 selection.start.column += delta_for_start_row;
7977 if selection.end.row == selection.start.row {
7978 selection.end.column += delta_for_start_row;
7979 }
7980 }
7981
7982 let mut delta_for_end_row = 0;
7983 let has_multiple_rows = start_row + 1 != end_row;
7984 for row in start_row..end_row {
7985 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7986 let indent_delta = match (current_indent.kind, indent_kind) {
7987 (IndentKind::Space, IndentKind::Space) => {
7988 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7989 IndentSize::spaces(columns_to_next_tab_stop)
7990 }
7991 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7992 (_, IndentKind::Tab) => IndentSize::tab(),
7993 };
7994
7995 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7996 0
7997 } else {
7998 selection.start.column
7999 };
8000 let row_start = Point::new(row, start);
8001 edits.push((
8002 row_start..row_start,
8003 indent_delta.chars().collect::<String>(),
8004 ));
8005
8006 // Update this selection's endpoints to reflect the indentation.
8007 if row == selection.start.row {
8008 selection.start.column += indent_delta.len;
8009 }
8010 if row == selection.end.row {
8011 selection.end.column += indent_delta.len;
8012 delta_for_end_row = indent_delta.len;
8013 }
8014 }
8015
8016 if selection.start.row == selection.end.row {
8017 delta_for_start_row + delta_for_end_row
8018 } else {
8019 delta_for_end_row
8020 }
8021 }
8022
8023 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
8024 if self.read_only(cx) {
8025 return;
8026 }
8027 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8028 let selections = self.selections.all::<Point>(cx);
8029 let mut deletion_ranges = Vec::new();
8030 let mut last_outdent = None;
8031 {
8032 let buffer = self.buffer.read(cx);
8033 let snapshot = buffer.snapshot(cx);
8034 for selection in &selections {
8035 let settings = buffer.language_settings_at(selection.start, cx);
8036 let tab_size = settings.tab_size.get();
8037 let mut rows = selection.spanned_rows(false, &display_map);
8038
8039 // Avoid re-outdenting a row that has already been outdented by a
8040 // previous selection.
8041 if let Some(last_row) = last_outdent {
8042 if last_row == rows.start {
8043 rows.start = rows.start.next_row();
8044 }
8045 }
8046 let has_multiple_rows = rows.len() > 1;
8047 for row in rows.iter_rows() {
8048 let indent_size = snapshot.indent_size_for_line(row);
8049 if indent_size.len > 0 {
8050 let deletion_len = match indent_size.kind {
8051 IndentKind::Space => {
8052 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8053 if columns_to_prev_tab_stop == 0 {
8054 tab_size
8055 } else {
8056 columns_to_prev_tab_stop
8057 }
8058 }
8059 IndentKind::Tab => 1,
8060 };
8061 let start = if has_multiple_rows
8062 || deletion_len > selection.start.column
8063 || indent_size.len < selection.start.column
8064 {
8065 0
8066 } else {
8067 selection.start.column - deletion_len
8068 };
8069 deletion_ranges.push(
8070 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8071 );
8072 last_outdent = Some(row);
8073 }
8074 }
8075 }
8076 }
8077
8078 self.transact(window, cx, |this, window, cx| {
8079 this.buffer.update(cx, |buffer, cx| {
8080 let empty_str: Arc<str> = Arc::default();
8081 buffer.edit(
8082 deletion_ranges
8083 .into_iter()
8084 .map(|range| (range, empty_str.clone())),
8085 None,
8086 cx,
8087 );
8088 });
8089 let selections = this.selections.all::<usize>(cx);
8090 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8091 s.select(selections)
8092 });
8093 });
8094 }
8095
8096 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8097 if self.read_only(cx) {
8098 return;
8099 }
8100 let selections = self
8101 .selections
8102 .all::<usize>(cx)
8103 .into_iter()
8104 .map(|s| s.range());
8105
8106 self.transact(window, cx, |this, window, cx| {
8107 this.buffer.update(cx, |buffer, cx| {
8108 buffer.autoindent_ranges(selections, cx);
8109 });
8110 let selections = this.selections.all::<usize>(cx);
8111 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8112 s.select(selections)
8113 });
8114 });
8115 }
8116
8117 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8119 let selections = self.selections.all::<Point>(cx);
8120
8121 let mut new_cursors = Vec::new();
8122 let mut edit_ranges = Vec::new();
8123 let mut selections = selections.iter().peekable();
8124 while let Some(selection) = selections.next() {
8125 let mut rows = selection.spanned_rows(false, &display_map);
8126 let goal_display_column = selection.head().to_display_point(&display_map).column();
8127
8128 // Accumulate contiguous regions of rows that we want to delete.
8129 while let Some(next_selection) = selections.peek() {
8130 let next_rows = next_selection.spanned_rows(false, &display_map);
8131 if next_rows.start <= rows.end {
8132 rows.end = next_rows.end;
8133 selections.next().unwrap();
8134 } else {
8135 break;
8136 }
8137 }
8138
8139 let buffer = &display_map.buffer_snapshot;
8140 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8141 let edit_end;
8142 let cursor_buffer_row;
8143 if buffer.max_point().row >= rows.end.0 {
8144 // If there's a line after the range, delete the \n from the end of the row range
8145 // and position the cursor on the next line.
8146 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8147 cursor_buffer_row = rows.end;
8148 } else {
8149 // If there isn't a line after the range, delete the \n from the line before the
8150 // start of the row range and position the cursor there.
8151 edit_start = edit_start.saturating_sub(1);
8152 edit_end = buffer.len();
8153 cursor_buffer_row = rows.start.previous_row();
8154 }
8155
8156 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8157 *cursor.column_mut() =
8158 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8159
8160 new_cursors.push((
8161 selection.id,
8162 buffer.anchor_after(cursor.to_point(&display_map)),
8163 ));
8164 edit_ranges.push(edit_start..edit_end);
8165 }
8166
8167 self.transact(window, cx, |this, window, cx| {
8168 let buffer = this.buffer.update(cx, |buffer, cx| {
8169 let empty_str: Arc<str> = Arc::default();
8170 buffer.edit(
8171 edit_ranges
8172 .into_iter()
8173 .map(|range| (range, empty_str.clone())),
8174 None,
8175 cx,
8176 );
8177 buffer.snapshot(cx)
8178 });
8179 let new_selections = new_cursors
8180 .into_iter()
8181 .map(|(id, cursor)| {
8182 let cursor = cursor.to_point(&buffer);
8183 Selection {
8184 id,
8185 start: cursor,
8186 end: cursor,
8187 reversed: false,
8188 goal: SelectionGoal::None,
8189 }
8190 })
8191 .collect();
8192
8193 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8194 s.select(new_selections);
8195 });
8196 });
8197 }
8198
8199 pub fn join_lines_impl(
8200 &mut self,
8201 insert_whitespace: bool,
8202 window: &mut Window,
8203 cx: &mut Context<Self>,
8204 ) {
8205 if self.read_only(cx) {
8206 return;
8207 }
8208 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8209 for selection in self.selections.all::<Point>(cx) {
8210 let start = MultiBufferRow(selection.start.row);
8211 // Treat single line selections as if they include the next line. Otherwise this action
8212 // would do nothing for single line selections individual cursors.
8213 let end = if selection.start.row == selection.end.row {
8214 MultiBufferRow(selection.start.row + 1)
8215 } else {
8216 MultiBufferRow(selection.end.row)
8217 };
8218
8219 if let Some(last_row_range) = row_ranges.last_mut() {
8220 if start <= last_row_range.end {
8221 last_row_range.end = end;
8222 continue;
8223 }
8224 }
8225 row_ranges.push(start..end);
8226 }
8227
8228 let snapshot = self.buffer.read(cx).snapshot(cx);
8229 let mut cursor_positions = Vec::new();
8230 for row_range in &row_ranges {
8231 let anchor = snapshot.anchor_before(Point::new(
8232 row_range.end.previous_row().0,
8233 snapshot.line_len(row_range.end.previous_row()),
8234 ));
8235 cursor_positions.push(anchor..anchor);
8236 }
8237
8238 self.transact(window, cx, |this, window, cx| {
8239 for row_range in row_ranges.into_iter().rev() {
8240 for row in row_range.iter_rows().rev() {
8241 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8242 let next_line_row = row.next_row();
8243 let indent = snapshot.indent_size_for_line(next_line_row);
8244 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8245
8246 let replace =
8247 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8248 " "
8249 } else {
8250 ""
8251 };
8252
8253 this.buffer.update(cx, |buffer, cx| {
8254 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8255 });
8256 }
8257 }
8258
8259 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8260 s.select_anchor_ranges(cursor_positions)
8261 });
8262 });
8263 }
8264
8265 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8266 self.join_lines_impl(true, window, cx);
8267 }
8268
8269 pub fn sort_lines_case_sensitive(
8270 &mut self,
8271 _: &SortLinesCaseSensitive,
8272 window: &mut Window,
8273 cx: &mut Context<Self>,
8274 ) {
8275 self.manipulate_lines(window, cx, |lines| lines.sort())
8276 }
8277
8278 pub fn sort_lines_case_insensitive(
8279 &mut self,
8280 _: &SortLinesCaseInsensitive,
8281 window: &mut Window,
8282 cx: &mut Context<Self>,
8283 ) {
8284 self.manipulate_lines(window, cx, |lines| {
8285 lines.sort_by_key(|line| line.to_lowercase())
8286 })
8287 }
8288
8289 pub fn unique_lines_case_insensitive(
8290 &mut self,
8291 _: &UniqueLinesCaseInsensitive,
8292 window: &mut Window,
8293 cx: &mut Context<Self>,
8294 ) {
8295 self.manipulate_lines(window, cx, |lines| {
8296 let mut seen = HashSet::default();
8297 lines.retain(|line| seen.insert(line.to_lowercase()));
8298 })
8299 }
8300
8301 pub fn unique_lines_case_sensitive(
8302 &mut self,
8303 _: &UniqueLinesCaseSensitive,
8304 window: &mut Window,
8305 cx: &mut Context<Self>,
8306 ) {
8307 self.manipulate_lines(window, cx, |lines| {
8308 let mut seen = HashSet::default();
8309 lines.retain(|line| seen.insert(*line));
8310 })
8311 }
8312
8313 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8314 let Some(project) = self.project.clone() else {
8315 return;
8316 };
8317 self.reload(project, window, cx)
8318 .detach_and_notify_err(window, cx);
8319 }
8320
8321 pub fn restore_file(
8322 &mut self,
8323 _: &::git::RestoreFile,
8324 window: &mut Window,
8325 cx: &mut Context<Self>,
8326 ) {
8327 let mut buffer_ids = HashSet::default();
8328 let snapshot = self.buffer().read(cx).snapshot(cx);
8329 for selection in self.selections.all::<usize>(cx) {
8330 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8331 }
8332
8333 let buffer = self.buffer().read(cx);
8334 let ranges = buffer_ids
8335 .into_iter()
8336 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8337 .collect::<Vec<_>>();
8338
8339 self.restore_hunks_in_ranges(ranges, window, cx);
8340 }
8341
8342 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8343 let selections = self
8344 .selections
8345 .all(cx)
8346 .into_iter()
8347 .map(|s| s.range())
8348 .collect();
8349 self.restore_hunks_in_ranges(selections, window, cx);
8350 }
8351
8352 fn restore_hunks_in_ranges(
8353 &mut self,
8354 ranges: Vec<Range<Point>>,
8355 window: &mut Window,
8356 cx: &mut Context<Editor>,
8357 ) {
8358 let mut revert_changes = HashMap::default();
8359 let chunk_by = self
8360 .snapshot(window, cx)
8361 .hunks_for_ranges(ranges)
8362 .into_iter()
8363 .chunk_by(|hunk| hunk.buffer_id);
8364 for (buffer_id, hunks) in &chunk_by {
8365 let hunks = hunks.collect::<Vec<_>>();
8366 for hunk in &hunks {
8367 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8368 }
8369 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8370 }
8371 drop(chunk_by);
8372 if !revert_changes.is_empty() {
8373 self.transact(window, cx, |editor, window, cx| {
8374 editor.restore(revert_changes, window, cx);
8375 });
8376 }
8377 }
8378
8379 pub fn open_active_item_in_terminal(
8380 &mut self,
8381 _: &OpenInTerminal,
8382 window: &mut Window,
8383 cx: &mut Context<Self>,
8384 ) {
8385 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8386 let project_path = buffer.read(cx).project_path(cx)?;
8387 let project = self.project.as_ref()?.read(cx);
8388 let entry = project.entry_for_path(&project_path, cx)?;
8389 let parent = match &entry.canonical_path {
8390 Some(canonical_path) => canonical_path.to_path_buf(),
8391 None => project.absolute_path(&project_path, cx)?,
8392 }
8393 .parent()?
8394 .to_path_buf();
8395 Some(parent)
8396 }) {
8397 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8398 }
8399 }
8400
8401 fn set_breakpoint_context_menu(
8402 &mut self,
8403 row: DisplayRow,
8404 position: Option<Anchor>,
8405 kind: Arc<BreakpointKind>,
8406 clicked_point: gpui::Point<Pixels>,
8407 window: &mut Window,
8408 cx: &mut Context<Self>,
8409 ) {
8410 if !cx.has_flag::<Debugger>() {
8411 return;
8412 }
8413 let source = self
8414 .buffer
8415 .read(cx)
8416 .snapshot(cx)
8417 .anchor_before(Point::new(row.0, 0u32));
8418
8419 let context_menu =
8420 self.breakpoint_context_menu(position.unwrap_or(source), kind, window, cx);
8421
8422 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8423 self,
8424 source,
8425 clicked_point,
8426 context_menu,
8427 window,
8428 cx,
8429 );
8430 }
8431
8432 fn add_edit_breakpoint_block(
8433 &mut self,
8434 anchor: Anchor,
8435 kind: &BreakpointKind,
8436 window: &mut Window,
8437 cx: &mut Context<Self>,
8438 ) {
8439 let weak_editor = cx.weak_entity();
8440 let bp_prompt =
8441 cx.new(|cx| BreakpointPromptEditor::new(weak_editor, anchor, kind.clone(), window, cx));
8442
8443 let height = bp_prompt.update(cx, |this, cx| {
8444 this.prompt
8445 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8446 });
8447 let cloned_prompt = bp_prompt.clone();
8448 let blocks = vec![BlockProperties {
8449 style: BlockStyle::Sticky,
8450 placement: BlockPlacement::Above(anchor),
8451 height,
8452 render: Arc::new(move |cx| {
8453 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8454 cloned_prompt.clone().into_any_element()
8455 }),
8456 priority: 0,
8457 }];
8458
8459 let focus_handle = bp_prompt.focus_handle(cx);
8460 window.focus(&focus_handle);
8461
8462 let block_ids = self.insert_blocks(blocks, None, cx);
8463 bp_prompt.update(cx, |prompt, _| {
8464 prompt.add_block_ids(block_ids);
8465 });
8466 }
8467
8468 pub(crate) fn breakpoint_at_cursor_head(
8469 &self,
8470 window: &mut Window,
8471 cx: &mut Context<Self>,
8472 ) -> Option<(Anchor, Breakpoint)> {
8473 let cursor_position: Point = self.selections.newest(cx).head();
8474 let snapshot = self.snapshot(window, cx);
8475 // We Set the column position to zero so this function interacts correctly
8476 // between calls by clicking on the gutter & using an action to toggle a
8477 // breakpoint. Otherwise, toggling a breakpoint through an action wouldn't
8478 // untoggle a breakpoint that was added through clicking on the gutter
8479 let cursor_position = snapshot
8480 .display_snapshot
8481 .buffer_snapshot
8482 .anchor_before(Point::new(cursor_position.row, 0));
8483
8484 let project = self.project.clone();
8485
8486 let buffer_id = cursor_position.text_anchor.buffer_id?;
8487 let enclosing_excerpt = snapshot
8488 .buffer_snapshot
8489 .excerpt_ids_for_range(cursor_position..cursor_position)
8490 .next()?;
8491 let buffer = project?.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8492 let buffer_snapshot = buffer.read(cx).snapshot();
8493
8494 let row = buffer_snapshot
8495 .summary_for_anchor::<text::PointUtf16>(&cursor_position.text_anchor)
8496 .row;
8497
8498 let bp = self
8499 .breakpoint_store
8500 .as_ref()?
8501 .read_with(cx, |breakpoint_store, cx| {
8502 breakpoint_store
8503 .breakpoints(
8504 &buffer,
8505 Some(cursor_position.text_anchor..(text::Anchor::MAX)),
8506 buffer_snapshot.clone(),
8507 cx,
8508 )
8509 .next()
8510 .and_then(move |(anchor, bp)| {
8511 let breakpoint_row = buffer_snapshot
8512 .summary_for_anchor::<text::PointUtf16>(anchor)
8513 .row;
8514
8515 if breakpoint_row == row {
8516 snapshot
8517 .buffer_snapshot
8518 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8519 .map(|anchor| (anchor, bp.clone()))
8520 } else {
8521 None
8522 }
8523 })
8524 });
8525 bp
8526 }
8527
8528 pub fn edit_log_breakpoint(
8529 &mut self,
8530 _: &EditLogBreakpoint,
8531 window: &mut Window,
8532 cx: &mut Context<Self>,
8533 ) {
8534 let (anchor, bp) = self
8535 .breakpoint_at_cursor_head(window, cx)
8536 .unwrap_or_else(|| {
8537 let cursor_position: Point = self.selections.newest(cx).head();
8538
8539 let breakpoint_position = self
8540 .snapshot(window, cx)
8541 .display_snapshot
8542 .buffer_snapshot
8543 .anchor_before(Point::new(cursor_position.row, 0));
8544
8545 (
8546 breakpoint_position,
8547 Breakpoint {
8548 kind: BreakpointKind::Standard,
8549 },
8550 )
8551 });
8552
8553 self.add_edit_breakpoint_block(anchor, &bp.kind, window, cx);
8554 }
8555
8556 pub fn toggle_breakpoint(
8557 &mut self,
8558 _: &crate::actions::ToggleBreakpoint,
8559 window: &mut Window,
8560 cx: &mut Context<Self>,
8561 ) {
8562 let edit_action = BreakpointEditAction::Toggle;
8563
8564 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8565 self.edit_breakpoint_at_anchor(anchor, breakpoint.kind, edit_action, cx);
8566 } else {
8567 let cursor_position: Point = self.selections.newest(cx).head();
8568
8569 let breakpoint_position = self
8570 .snapshot(window, cx)
8571 .display_snapshot
8572 .buffer_snapshot
8573 .anchor_before(Point::new(cursor_position.row, 0));
8574
8575 self.edit_breakpoint_at_anchor(
8576 breakpoint_position,
8577 BreakpointKind::Standard,
8578 edit_action,
8579 cx,
8580 );
8581 }
8582 }
8583
8584 pub fn edit_breakpoint_at_anchor(
8585 &mut self,
8586 breakpoint_position: Anchor,
8587 kind: BreakpointKind,
8588 edit_action: BreakpointEditAction,
8589 cx: &mut Context<Self>,
8590 ) {
8591 let Some(breakpoint_store) = &self.breakpoint_store else {
8592 return;
8593 };
8594
8595 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8596 if breakpoint_position == Anchor::min() {
8597 self.buffer()
8598 .read(cx)
8599 .excerpt_buffer_ids()
8600 .into_iter()
8601 .next()
8602 } else {
8603 None
8604 }
8605 }) else {
8606 return;
8607 };
8608
8609 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8610 return;
8611 };
8612
8613 breakpoint_store.update(cx, |breakpoint_store, cx| {
8614 breakpoint_store.toggle_breakpoint(
8615 buffer,
8616 (breakpoint_position.text_anchor, Breakpoint { kind }),
8617 edit_action,
8618 cx,
8619 );
8620 });
8621
8622 cx.notify();
8623 }
8624
8625 #[cfg(any(test, feature = "test-support"))]
8626 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8627 self.breakpoint_store.clone()
8628 }
8629
8630 pub fn prepare_restore_change(
8631 &self,
8632 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8633 hunk: &MultiBufferDiffHunk,
8634 cx: &mut App,
8635 ) -> Option<()> {
8636 if hunk.is_created_file() {
8637 return None;
8638 }
8639 let buffer = self.buffer.read(cx);
8640 let diff = buffer.diff_for(hunk.buffer_id)?;
8641 let buffer = buffer.buffer(hunk.buffer_id)?;
8642 let buffer = buffer.read(cx);
8643 let original_text = diff
8644 .read(cx)
8645 .base_text()
8646 .as_rope()
8647 .slice(hunk.diff_base_byte_range.clone());
8648 let buffer_snapshot = buffer.snapshot();
8649 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8650 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8651 probe
8652 .0
8653 .start
8654 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8655 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8656 }) {
8657 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8658 Some(())
8659 } else {
8660 None
8661 }
8662 }
8663
8664 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8665 self.manipulate_lines(window, cx, |lines| lines.reverse())
8666 }
8667
8668 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8669 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8670 }
8671
8672 fn manipulate_lines<Fn>(
8673 &mut self,
8674 window: &mut Window,
8675 cx: &mut Context<Self>,
8676 mut callback: Fn,
8677 ) where
8678 Fn: FnMut(&mut Vec<&str>),
8679 {
8680 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8681 let buffer = self.buffer.read(cx).snapshot(cx);
8682
8683 let mut edits = Vec::new();
8684
8685 let selections = self.selections.all::<Point>(cx);
8686 let mut selections = selections.iter().peekable();
8687 let mut contiguous_row_selections = Vec::new();
8688 let mut new_selections = Vec::new();
8689 let mut added_lines = 0;
8690 let mut removed_lines = 0;
8691
8692 while let Some(selection) = selections.next() {
8693 let (start_row, end_row) = consume_contiguous_rows(
8694 &mut contiguous_row_selections,
8695 selection,
8696 &display_map,
8697 &mut selections,
8698 );
8699
8700 let start_point = Point::new(start_row.0, 0);
8701 let end_point = Point::new(
8702 end_row.previous_row().0,
8703 buffer.line_len(end_row.previous_row()),
8704 );
8705 let text = buffer
8706 .text_for_range(start_point..end_point)
8707 .collect::<String>();
8708
8709 let mut lines = text.split('\n').collect_vec();
8710
8711 let lines_before = lines.len();
8712 callback(&mut lines);
8713 let lines_after = lines.len();
8714
8715 edits.push((start_point..end_point, lines.join("\n")));
8716
8717 // Selections must change based on added and removed line count
8718 let start_row =
8719 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8720 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8721 new_selections.push(Selection {
8722 id: selection.id,
8723 start: start_row,
8724 end: end_row,
8725 goal: SelectionGoal::None,
8726 reversed: selection.reversed,
8727 });
8728
8729 if lines_after > lines_before {
8730 added_lines += lines_after - lines_before;
8731 } else if lines_before > lines_after {
8732 removed_lines += lines_before - lines_after;
8733 }
8734 }
8735
8736 self.transact(window, cx, |this, window, cx| {
8737 let buffer = this.buffer.update(cx, |buffer, cx| {
8738 buffer.edit(edits, None, cx);
8739 buffer.snapshot(cx)
8740 });
8741
8742 // Recalculate offsets on newly edited buffer
8743 let new_selections = new_selections
8744 .iter()
8745 .map(|s| {
8746 let start_point = Point::new(s.start.0, 0);
8747 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8748 Selection {
8749 id: s.id,
8750 start: buffer.point_to_offset(start_point),
8751 end: buffer.point_to_offset(end_point),
8752 goal: s.goal,
8753 reversed: s.reversed,
8754 }
8755 })
8756 .collect();
8757
8758 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8759 s.select(new_selections);
8760 });
8761
8762 this.request_autoscroll(Autoscroll::fit(), cx);
8763 });
8764 }
8765
8766 pub fn convert_to_upper_case(
8767 &mut self,
8768 _: &ConvertToUpperCase,
8769 window: &mut Window,
8770 cx: &mut Context<Self>,
8771 ) {
8772 self.manipulate_text(window, cx, |text| text.to_uppercase())
8773 }
8774
8775 pub fn convert_to_lower_case(
8776 &mut self,
8777 _: &ConvertToLowerCase,
8778 window: &mut Window,
8779 cx: &mut Context<Self>,
8780 ) {
8781 self.manipulate_text(window, cx, |text| text.to_lowercase())
8782 }
8783
8784 pub fn convert_to_title_case(
8785 &mut self,
8786 _: &ConvertToTitleCase,
8787 window: &mut Window,
8788 cx: &mut Context<Self>,
8789 ) {
8790 self.manipulate_text(window, cx, |text| {
8791 text.split('\n')
8792 .map(|line| line.to_case(Case::Title))
8793 .join("\n")
8794 })
8795 }
8796
8797 pub fn convert_to_snake_case(
8798 &mut self,
8799 _: &ConvertToSnakeCase,
8800 window: &mut Window,
8801 cx: &mut Context<Self>,
8802 ) {
8803 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8804 }
8805
8806 pub fn convert_to_kebab_case(
8807 &mut self,
8808 _: &ConvertToKebabCase,
8809 window: &mut Window,
8810 cx: &mut Context<Self>,
8811 ) {
8812 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8813 }
8814
8815 pub fn convert_to_upper_camel_case(
8816 &mut self,
8817 _: &ConvertToUpperCamelCase,
8818 window: &mut Window,
8819 cx: &mut Context<Self>,
8820 ) {
8821 self.manipulate_text(window, cx, |text| {
8822 text.split('\n')
8823 .map(|line| line.to_case(Case::UpperCamel))
8824 .join("\n")
8825 })
8826 }
8827
8828 pub fn convert_to_lower_camel_case(
8829 &mut self,
8830 _: &ConvertToLowerCamelCase,
8831 window: &mut Window,
8832 cx: &mut Context<Self>,
8833 ) {
8834 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8835 }
8836
8837 pub fn convert_to_opposite_case(
8838 &mut self,
8839 _: &ConvertToOppositeCase,
8840 window: &mut Window,
8841 cx: &mut Context<Self>,
8842 ) {
8843 self.manipulate_text(window, cx, |text| {
8844 text.chars()
8845 .fold(String::with_capacity(text.len()), |mut t, c| {
8846 if c.is_uppercase() {
8847 t.extend(c.to_lowercase());
8848 } else {
8849 t.extend(c.to_uppercase());
8850 }
8851 t
8852 })
8853 })
8854 }
8855
8856 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8857 where
8858 Fn: FnMut(&str) -> String,
8859 {
8860 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8861 let buffer = self.buffer.read(cx).snapshot(cx);
8862
8863 let mut new_selections = Vec::new();
8864 let mut edits = Vec::new();
8865 let mut selection_adjustment = 0i32;
8866
8867 for selection in self.selections.all::<usize>(cx) {
8868 let selection_is_empty = selection.is_empty();
8869
8870 let (start, end) = if selection_is_empty {
8871 let word_range = movement::surrounding_word(
8872 &display_map,
8873 selection.start.to_display_point(&display_map),
8874 );
8875 let start = word_range.start.to_offset(&display_map, Bias::Left);
8876 let end = word_range.end.to_offset(&display_map, Bias::Left);
8877 (start, end)
8878 } else {
8879 (selection.start, selection.end)
8880 };
8881
8882 let text = buffer.text_for_range(start..end).collect::<String>();
8883 let old_length = text.len() as i32;
8884 let text = callback(&text);
8885
8886 new_selections.push(Selection {
8887 start: (start as i32 - selection_adjustment) as usize,
8888 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8889 goal: SelectionGoal::None,
8890 ..selection
8891 });
8892
8893 selection_adjustment += old_length - text.len() as i32;
8894
8895 edits.push((start..end, text));
8896 }
8897
8898 self.transact(window, cx, |this, window, cx| {
8899 this.buffer.update(cx, |buffer, cx| {
8900 buffer.edit(edits, None, cx);
8901 });
8902
8903 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8904 s.select(new_selections);
8905 });
8906
8907 this.request_autoscroll(Autoscroll::fit(), cx);
8908 });
8909 }
8910
8911 pub fn duplicate(
8912 &mut self,
8913 upwards: bool,
8914 whole_lines: bool,
8915 window: &mut Window,
8916 cx: &mut Context<Self>,
8917 ) {
8918 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8919 let buffer = &display_map.buffer_snapshot;
8920 let selections = self.selections.all::<Point>(cx);
8921
8922 let mut edits = Vec::new();
8923 let mut selections_iter = selections.iter().peekable();
8924 while let Some(selection) = selections_iter.next() {
8925 let mut rows = selection.spanned_rows(false, &display_map);
8926 // duplicate line-wise
8927 if whole_lines || selection.start == selection.end {
8928 // Avoid duplicating the same lines twice.
8929 while let Some(next_selection) = selections_iter.peek() {
8930 let next_rows = next_selection.spanned_rows(false, &display_map);
8931 if next_rows.start < rows.end {
8932 rows.end = next_rows.end;
8933 selections_iter.next().unwrap();
8934 } else {
8935 break;
8936 }
8937 }
8938
8939 // Copy the text from the selected row region and splice it either at the start
8940 // or end of the region.
8941 let start = Point::new(rows.start.0, 0);
8942 let end = Point::new(
8943 rows.end.previous_row().0,
8944 buffer.line_len(rows.end.previous_row()),
8945 );
8946 let text = buffer
8947 .text_for_range(start..end)
8948 .chain(Some("\n"))
8949 .collect::<String>();
8950 let insert_location = if upwards {
8951 Point::new(rows.end.0, 0)
8952 } else {
8953 start
8954 };
8955 edits.push((insert_location..insert_location, text));
8956 } else {
8957 // duplicate character-wise
8958 let start = selection.start;
8959 let end = selection.end;
8960 let text = buffer.text_for_range(start..end).collect::<String>();
8961 edits.push((selection.end..selection.end, text));
8962 }
8963 }
8964
8965 self.transact(window, cx, |this, _, cx| {
8966 this.buffer.update(cx, |buffer, cx| {
8967 buffer.edit(edits, None, cx);
8968 });
8969
8970 this.request_autoscroll(Autoscroll::fit(), cx);
8971 });
8972 }
8973
8974 pub fn duplicate_line_up(
8975 &mut self,
8976 _: &DuplicateLineUp,
8977 window: &mut Window,
8978 cx: &mut Context<Self>,
8979 ) {
8980 self.duplicate(true, true, window, cx);
8981 }
8982
8983 pub fn duplicate_line_down(
8984 &mut self,
8985 _: &DuplicateLineDown,
8986 window: &mut Window,
8987 cx: &mut Context<Self>,
8988 ) {
8989 self.duplicate(false, true, window, cx);
8990 }
8991
8992 pub fn duplicate_selection(
8993 &mut self,
8994 _: &DuplicateSelection,
8995 window: &mut Window,
8996 cx: &mut Context<Self>,
8997 ) {
8998 self.duplicate(false, false, window, cx);
8999 }
9000
9001 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
9002 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9003 let buffer = self.buffer.read(cx).snapshot(cx);
9004
9005 let mut edits = Vec::new();
9006 let mut unfold_ranges = Vec::new();
9007 let mut refold_creases = Vec::new();
9008
9009 let selections = self.selections.all::<Point>(cx);
9010 let mut selections = selections.iter().peekable();
9011 let mut contiguous_row_selections = Vec::new();
9012 let mut new_selections = Vec::new();
9013
9014 while let Some(selection) = selections.next() {
9015 // Find all the selections that span a contiguous row range
9016 let (start_row, end_row) = consume_contiguous_rows(
9017 &mut contiguous_row_selections,
9018 selection,
9019 &display_map,
9020 &mut selections,
9021 );
9022
9023 // Move the text spanned by the row range to be before the line preceding the row range
9024 if start_row.0 > 0 {
9025 let range_to_move = Point::new(
9026 start_row.previous_row().0,
9027 buffer.line_len(start_row.previous_row()),
9028 )
9029 ..Point::new(
9030 end_row.previous_row().0,
9031 buffer.line_len(end_row.previous_row()),
9032 );
9033 let insertion_point = display_map
9034 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9035 .0;
9036
9037 // Don't move lines across excerpts
9038 if buffer
9039 .excerpt_containing(insertion_point..range_to_move.end)
9040 .is_some()
9041 {
9042 let text = buffer
9043 .text_for_range(range_to_move.clone())
9044 .flat_map(|s| s.chars())
9045 .skip(1)
9046 .chain(['\n'])
9047 .collect::<String>();
9048
9049 edits.push((
9050 buffer.anchor_after(range_to_move.start)
9051 ..buffer.anchor_before(range_to_move.end),
9052 String::new(),
9053 ));
9054 let insertion_anchor = buffer.anchor_after(insertion_point);
9055 edits.push((insertion_anchor..insertion_anchor, text));
9056
9057 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9058
9059 // Move selections up
9060 new_selections.extend(contiguous_row_selections.drain(..).map(
9061 |mut selection| {
9062 selection.start.row -= row_delta;
9063 selection.end.row -= row_delta;
9064 selection
9065 },
9066 ));
9067
9068 // Move folds up
9069 unfold_ranges.push(range_to_move.clone());
9070 for fold in display_map.folds_in_range(
9071 buffer.anchor_before(range_to_move.start)
9072 ..buffer.anchor_after(range_to_move.end),
9073 ) {
9074 let mut start = fold.range.start.to_point(&buffer);
9075 let mut end = fold.range.end.to_point(&buffer);
9076 start.row -= row_delta;
9077 end.row -= row_delta;
9078 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9079 }
9080 }
9081 }
9082
9083 // If we didn't move line(s), preserve the existing selections
9084 new_selections.append(&mut contiguous_row_selections);
9085 }
9086
9087 self.transact(window, cx, |this, window, cx| {
9088 this.unfold_ranges(&unfold_ranges, true, true, cx);
9089 this.buffer.update(cx, |buffer, cx| {
9090 for (range, text) in edits {
9091 buffer.edit([(range, text)], None, cx);
9092 }
9093 });
9094 this.fold_creases(refold_creases, true, window, cx);
9095 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9096 s.select(new_selections);
9097 })
9098 });
9099 }
9100
9101 pub fn move_line_down(
9102 &mut self,
9103 _: &MoveLineDown,
9104 window: &mut Window,
9105 cx: &mut Context<Self>,
9106 ) {
9107 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9108 let buffer = self.buffer.read(cx).snapshot(cx);
9109
9110 let mut edits = Vec::new();
9111 let mut unfold_ranges = Vec::new();
9112 let mut refold_creases = Vec::new();
9113
9114 let selections = self.selections.all::<Point>(cx);
9115 let mut selections = selections.iter().peekable();
9116 let mut contiguous_row_selections = Vec::new();
9117 let mut new_selections = Vec::new();
9118
9119 while let Some(selection) = selections.next() {
9120 // Find all the selections that span a contiguous row range
9121 let (start_row, end_row) = consume_contiguous_rows(
9122 &mut contiguous_row_selections,
9123 selection,
9124 &display_map,
9125 &mut selections,
9126 );
9127
9128 // Move the text spanned by the row range to be after the last line of the row range
9129 if end_row.0 <= buffer.max_point().row {
9130 let range_to_move =
9131 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9132 let insertion_point = display_map
9133 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9134 .0;
9135
9136 // Don't move lines across excerpt boundaries
9137 if buffer
9138 .excerpt_containing(range_to_move.start..insertion_point)
9139 .is_some()
9140 {
9141 let mut text = String::from("\n");
9142 text.extend(buffer.text_for_range(range_to_move.clone()));
9143 text.pop(); // Drop trailing newline
9144 edits.push((
9145 buffer.anchor_after(range_to_move.start)
9146 ..buffer.anchor_before(range_to_move.end),
9147 String::new(),
9148 ));
9149 let insertion_anchor = buffer.anchor_after(insertion_point);
9150 edits.push((insertion_anchor..insertion_anchor, text));
9151
9152 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9153
9154 // Move selections down
9155 new_selections.extend(contiguous_row_selections.drain(..).map(
9156 |mut selection| {
9157 selection.start.row += row_delta;
9158 selection.end.row += row_delta;
9159 selection
9160 },
9161 ));
9162
9163 // Move folds down
9164 unfold_ranges.push(range_to_move.clone());
9165 for fold in display_map.folds_in_range(
9166 buffer.anchor_before(range_to_move.start)
9167 ..buffer.anchor_after(range_to_move.end),
9168 ) {
9169 let mut start = fold.range.start.to_point(&buffer);
9170 let mut end = fold.range.end.to_point(&buffer);
9171 start.row += row_delta;
9172 end.row += row_delta;
9173 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9174 }
9175 }
9176 }
9177
9178 // If we didn't move line(s), preserve the existing selections
9179 new_selections.append(&mut contiguous_row_selections);
9180 }
9181
9182 self.transact(window, cx, |this, window, cx| {
9183 this.unfold_ranges(&unfold_ranges, true, true, cx);
9184 this.buffer.update(cx, |buffer, cx| {
9185 for (range, text) in edits {
9186 buffer.edit([(range, text)], None, cx);
9187 }
9188 });
9189 this.fold_creases(refold_creases, true, window, cx);
9190 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9191 s.select(new_selections)
9192 });
9193 });
9194 }
9195
9196 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9197 let text_layout_details = &self.text_layout_details(window);
9198 self.transact(window, cx, |this, window, cx| {
9199 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9200 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9201 let line_mode = s.line_mode;
9202 s.move_with(|display_map, selection| {
9203 if !selection.is_empty() || line_mode {
9204 return;
9205 }
9206
9207 let mut head = selection.head();
9208 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9209 if head.column() == display_map.line_len(head.row()) {
9210 transpose_offset = display_map
9211 .buffer_snapshot
9212 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9213 }
9214
9215 if transpose_offset == 0 {
9216 return;
9217 }
9218
9219 *head.column_mut() += 1;
9220 head = display_map.clip_point(head, Bias::Right);
9221 let goal = SelectionGoal::HorizontalPosition(
9222 display_map
9223 .x_for_display_point(head, text_layout_details)
9224 .into(),
9225 );
9226 selection.collapse_to(head, goal);
9227
9228 let transpose_start = display_map
9229 .buffer_snapshot
9230 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9231 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9232 let transpose_end = display_map
9233 .buffer_snapshot
9234 .clip_offset(transpose_offset + 1, Bias::Right);
9235 if let Some(ch) =
9236 display_map.buffer_snapshot.chars_at(transpose_start).next()
9237 {
9238 edits.push((transpose_start..transpose_offset, String::new()));
9239 edits.push((transpose_end..transpose_end, ch.to_string()));
9240 }
9241 }
9242 });
9243 edits
9244 });
9245 this.buffer
9246 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9247 let selections = this.selections.all::<usize>(cx);
9248 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9249 s.select(selections);
9250 });
9251 });
9252 }
9253
9254 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9255 self.rewrap_impl(RewrapOptions::default(), cx)
9256 }
9257
9258 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9259 let buffer = self.buffer.read(cx).snapshot(cx);
9260 let selections = self.selections.all::<Point>(cx);
9261 let mut selections = selections.iter().peekable();
9262
9263 let mut edits = Vec::new();
9264 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9265
9266 while let Some(selection) = selections.next() {
9267 let mut start_row = selection.start.row;
9268 let mut end_row = selection.end.row;
9269
9270 // Skip selections that overlap with a range that has already been rewrapped.
9271 let selection_range = start_row..end_row;
9272 if rewrapped_row_ranges
9273 .iter()
9274 .any(|range| range.overlaps(&selection_range))
9275 {
9276 continue;
9277 }
9278
9279 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9280
9281 // Since not all lines in the selection may be at the same indent
9282 // level, choose the indent size that is the most common between all
9283 // of the lines.
9284 //
9285 // If there is a tie, we use the deepest indent.
9286 let (indent_size, indent_end) = {
9287 let mut indent_size_occurrences = HashMap::default();
9288 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9289
9290 for row in start_row..=end_row {
9291 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9292 rows_by_indent_size.entry(indent).or_default().push(row);
9293 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9294 }
9295
9296 let indent_size = indent_size_occurrences
9297 .into_iter()
9298 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9299 .map(|(indent, _)| indent)
9300 .unwrap_or_default();
9301 let row = rows_by_indent_size[&indent_size][0];
9302 let indent_end = Point::new(row, indent_size.len);
9303
9304 (indent_size, indent_end)
9305 };
9306
9307 let mut line_prefix = indent_size.chars().collect::<String>();
9308
9309 let mut inside_comment = false;
9310 if let Some(comment_prefix) =
9311 buffer
9312 .language_scope_at(selection.head())
9313 .and_then(|language| {
9314 language
9315 .line_comment_prefixes()
9316 .iter()
9317 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9318 .cloned()
9319 })
9320 {
9321 line_prefix.push_str(&comment_prefix);
9322 inside_comment = true;
9323 }
9324
9325 let language_settings = buffer.language_settings_at(selection.head(), cx);
9326 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9327 RewrapBehavior::InComments => inside_comment,
9328 RewrapBehavior::InSelections => !selection.is_empty(),
9329 RewrapBehavior::Anywhere => true,
9330 };
9331
9332 let should_rewrap = options.override_language_settings
9333 || allow_rewrap_based_on_language
9334 || self.hard_wrap.is_some();
9335 if !should_rewrap {
9336 continue;
9337 }
9338
9339 if selection.is_empty() {
9340 'expand_upwards: while start_row > 0 {
9341 let prev_row = start_row - 1;
9342 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9343 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9344 {
9345 start_row = prev_row;
9346 } else {
9347 break 'expand_upwards;
9348 }
9349 }
9350
9351 'expand_downwards: while end_row < buffer.max_point().row {
9352 let next_row = end_row + 1;
9353 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9354 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9355 {
9356 end_row = next_row;
9357 } else {
9358 break 'expand_downwards;
9359 }
9360 }
9361 }
9362
9363 let start = Point::new(start_row, 0);
9364 let start_offset = start.to_offset(&buffer);
9365 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9366 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9367 let Some(lines_without_prefixes) = selection_text
9368 .lines()
9369 .map(|line| {
9370 line.strip_prefix(&line_prefix)
9371 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9372 .ok_or_else(|| {
9373 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9374 })
9375 })
9376 .collect::<Result<Vec<_>, _>>()
9377 .log_err()
9378 else {
9379 continue;
9380 };
9381
9382 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9383 buffer
9384 .language_settings_at(Point::new(start_row, 0), cx)
9385 .preferred_line_length as usize
9386 });
9387 let wrapped_text = wrap_with_prefix(
9388 line_prefix,
9389 lines_without_prefixes.join("\n"),
9390 wrap_column,
9391 tab_size,
9392 options.preserve_existing_whitespace,
9393 );
9394
9395 // TODO: should always use char-based diff while still supporting cursor behavior that
9396 // matches vim.
9397 let mut diff_options = DiffOptions::default();
9398 if options.override_language_settings {
9399 diff_options.max_word_diff_len = 0;
9400 diff_options.max_word_diff_line_count = 0;
9401 } else {
9402 diff_options.max_word_diff_len = usize::MAX;
9403 diff_options.max_word_diff_line_count = usize::MAX;
9404 }
9405
9406 for (old_range, new_text) in
9407 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9408 {
9409 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9410 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9411 edits.push((edit_start..edit_end, new_text));
9412 }
9413
9414 rewrapped_row_ranges.push(start_row..=end_row);
9415 }
9416
9417 self.buffer
9418 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9419 }
9420
9421 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9422 let mut text = String::new();
9423 let buffer = self.buffer.read(cx).snapshot(cx);
9424 let mut selections = self.selections.all::<Point>(cx);
9425 let mut clipboard_selections = Vec::with_capacity(selections.len());
9426 {
9427 let max_point = buffer.max_point();
9428 let mut is_first = true;
9429 for selection in &mut selections {
9430 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9431 if is_entire_line {
9432 selection.start = Point::new(selection.start.row, 0);
9433 if !selection.is_empty() && selection.end.column == 0 {
9434 selection.end = cmp::min(max_point, selection.end);
9435 } else {
9436 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9437 }
9438 selection.goal = SelectionGoal::None;
9439 }
9440 if is_first {
9441 is_first = false;
9442 } else {
9443 text += "\n";
9444 }
9445 let mut len = 0;
9446 for chunk in buffer.text_for_range(selection.start..selection.end) {
9447 text.push_str(chunk);
9448 len += chunk.len();
9449 }
9450 clipboard_selections.push(ClipboardSelection {
9451 len,
9452 is_entire_line,
9453 first_line_indent: buffer
9454 .indent_size_for_line(MultiBufferRow(selection.start.row))
9455 .len,
9456 });
9457 }
9458 }
9459
9460 self.transact(window, cx, |this, window, cx| {
9461 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9462 s.select(selections);
9463 });
9464 this.insert("", window, cx);
9465 });
9466 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9467 }
9468
9469 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9470 let item = self.cut_common(window, cx);
9471 cx.write_to_clipboard(item);
9472 }
9473
9474 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9475 self.change_selections(None, window, cx, |s| {
9476 s.move_with(|snapshot, sel| {
9477 if sel.is_empty() {
9478 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9479 }
9480 });
9481 });
9482 let item = self.cut_common(window, cx);
9483 cx.set_global(KillRing(item))
9484 }
9485
9486 pub fn kill_ring_yank(
9487 &mut self,
9488 _: &KillRingYank,
9489 window: &mut Window,
9490 cx: &mut Context<Self>,
9491 ) {
9492 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9493 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9494 (kill_ring.text().to_string(), kill_ring.metadata_json())
9495 } else {
9496 return;
9497 }
9498 } else {
9499 return;
9500 };
9501 self.do_paste(&text, metadata, false, window, cx);
9502 }
9503
9504 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9505 self.do_copy(true, cx);
9506 }
9507
9508 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9509 self.do_copy(false, cx);
9510 }
9511
9512 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9513 let selections = self.selections.all::<Point>(cx);
9514 let buffer = self.buffer.read(cx).read(cx);
9515 let mut text = String::new();
9516
9517 let mut clipboard_selections = Vec::with_capacity(selections.len());
9518 {
9519 let max_point = buffer.max_point();
9520 let mut is_first = true;
9521 for selection in &selections {
9522 let mut start = selection.start;
9523 let mut end = selection.end;
9524 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9525 if is_entire_line {
9526 start = Point::new(start.row, 0);
9527 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9528 }
9529
9530 let mut trimmed_selections = Vec::new();
9531 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9532 let row = MultiBufferRow(start.row);
9533 let first_indent = buffer.indent_size_for_line(row);
9534 if first_indent.len == 0 || start.column > first_indent.len {
9535 trimmed_selections.push(start..end);
9536 } else {
9537 trimmed_selections.push(
9538 Point::new(row.0, first_indent.len)
9539 ..Point::new(row.0, buffer.line_len(row)),
9540 );
9541 for row in start.row + 1..=end.row {
9542 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9543 if row_indent_size.len >= first_indent.len {
9544 trimmed_selections.push(
9545 Point::new(row, first_indent.len)
9546 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9547 );
9548 } else {
9549 trimmed_selections.clear();
9550 trimmed_selections.push(start..end);
9551 break;
9552 }
9553 }
9554 }
9555 } else {
9556 trimmed_selections.push(start..end);
9557 }
9558
9559 for trimmed_range in trimmed_selections {
9560 if is_first {
9561 is_first = false;
9562 } else {
9563 text += "\n";
9564 }
9565 let mut len = 0;
9566 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9567 text.push_str(chunk);
9568 len += chunk.len();
9569 }
9570 clipboard_selections.push(ClipboardSelection {
9571 len,
9572 is_entire_line,
9573 first_line_indent: buffer
9574 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9575 .len,
9576 });
9577 }
9578 }
9579 }
9580
9581 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9582 text,
9583 clipboard_selections,
9584 ));
9585 }
9586
9587 pub fn do_paste(
9588 &mut self,
9589 text: &String,
9590 clipboard_selections: Option<Vec<ClipboardSelection>>,
9591 handle_entire_lines: bool,
9592 window: &mut Window,
9593 cx: &mut Context<Self>,
9594 ) {
9595 if self.read_only(cx) {
9596 return;
9597 }
9598
9599 let clipboard_text = Cow::Borrowed(text);
9600
9601 self.transact(window, cx, |this, window, cx| {
9602 if let Some(mut clipboard_selections) = clipboard_selections {
9603 let old_selections = this.selections.all::<usize>(cx);
9604 let all_selections_were_entire_line =
9605 clipboard_selections.iter().all(|s| s.is_entire_line);
9606 let first_selection_indent_column =
9607 clipboard_selections.first().map(|s| s.first_line_indent);
9608 if clipboard_selections.len() != old_selections.len() {
9609 clipboard_selections.drain(..);
9610 }
9611 let cursor_offset = this.selections.last::<usize>(cx).head();
9612 let mut auto_indent_on_paste = true;
9613
9614 this.buffer.update(cx, |buffer, cx| {
9615 let snapshot = buffer.read(cx);
9616 auto_indent_on_paste = snapshot
9617 .language_settings_at(cursor_offset, cx)
9618 .auto_indent_on_paste;
9619
9620 let mut start_offset = 0;
9621 let mut edits = Vec::new();
9622 let mut original_indent_columns = Vec::new();
9623 for (ix, selection) in old_selections.iter().enumerate() {
9624 let to_insert;
9625 let entire_line;
9626 let original_indent_column;
9627 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9628 let end_offset = start_offset + clipboard_selection.len;
9629 to_insert = &clipboard_text[start_offset..end_offset];
9630 entire_line = clipboard_selection.is_entire_line;
9631 start_offset = end_offset + 1;
9632 original_indent_column = Some(clipboard_selection.first_line_indent);
9633 } else {
9634 to_insert = clipboard_text.as_str();
9635 entire_line = all_selections_were_entire_line;
9636 original_indent_column = first_selection_indent_column
9637 }
9638
9639 // If the corresponding selection was empty when this slice of the
9640 // clipboard text was written, then the entire line containing the
9641 // selection was copied. If this selection is also currently empty,
9642 // then paste the line before the current line of the buffer.
9643 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9644 let column = selection.start.to_point(&snapshot).column as usize;
9645 let line_start = selection.start - column;
9646 line_start..line_start
9647 } else {
9648 selection.range()
9649 };
9650
9651 edits.push((range, to_insert));
9652 original_indent_columns.push(original_indent_column);
9653 }
9654 drop(snapshot);
9655
9656 buffer.edit(
9657 edits,
9658 if auto_indent_on_paste {
9659 Some(AutoindentMode::Block {
9660 original_indent_columns,
9661 })
9662 } else {
9663 None
9664 },
9665 cx,
9666 );
9667 });
9668
9669 let selections = this.selections.all::<usize>(cx);
9670 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9671 s.select(selections)
9672 });
9673 } else {
9674 this.insert(&clipboard_text, window, cx);
9675 }
9676 });
9677 }
9678
9679 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9680 if let Some(item) = cx.read_from_clipboard() {
9681 let entries = item.entries();
9682
9683 match entries.first() {
9684 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9685 // of all the pasted entries.
9686 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9687 .do_paste(
9688 clipboard_string.text(),
9689 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9690 true,
9691 window,
9692 cx,
9693 ),
9694 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9695 }
9696 }
9697 }
9698
9699 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9700 if self.read_only(cx) {
9701 return;
9702 }
9703
9704 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9705 if let Some((selections, _)) =
9706 self.selection_history.transaction(transaction_id).cloned()
9707 {
9708 self.change_selections(None, window, cx, |s| {
9709 s.select_anchors(selections.to_vec());
9710 });
9711 } else {
9712 log::error!(
9713 "No entry in selection_history found for undo. \
9714 This may correspond to a bug where undo does not update the selection. \
9715 If this is occurring, please add details to \
9716 https://github.com/zed-industries/zed/issues/22692"
9717 );
9718 }
9719 self.request_autoscroll(Autoscroll::fit(), cx);
9720 self.unmark_text(window, cx);
9721 self.refresh_inline_completion(true, false, window, cx);
9722 cx.emit(EditorEvent::Edited { transaction_id });
9723 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9724 }
9725 }
9726
9727 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9728 if self.read_only(cx) {
9729 return;
9730 }
9731
9732 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9733 if let Some((_, Some(selections))) =
9734 self.selection_history.transaction(transaction_id).cloned()
9735 {
9736 self.change_selections(None, window, cx, |s| {
9737 s.select_anchors(selections.to_vec());
9738 });
9739 } else {
9740 log::error!(
9741 "No entry in selection_history found for redo. \
9742 This may correspond to a bug where undo does not update the selection. \
9743 If this is occurring, please add details to \
9744 https://github.com/zed-industries/zed/issues/22692"
9745 );
9746 }
9747 self.request_autoscroll(Autoscroll::fit(), cx);
9748 self.unmark_text(window, cx);
9749 self.refresh_inline_completion(true, false, window, cx);
9750 cx.emit(EditorEvent::Edited { transaction_id });
9751 }
9752 }
9753
9754 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9755 self.buffer
9756 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9757 }
9758
9759 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9760 self.buffer
9761 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9762 }
9763
9764 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9765 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9766 let line_mode = s.line_mode;
9767 s.move_with(|map, selection| {
9768 let cursor = if selection.is_empty() && !line_mode {
9769 movement::left(map, selection.start)
9770 } else {
9771 selection.start
9772 };
9773 selection.collapse_to(cursor, SelectionGoal::None);
9774 });
9775 })
9776 }
9777
9778 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9779 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9780 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9781 })
9782 }
9783
9784 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9785 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9786 let line_mode = s.line_mode;
9787 s.move_with(|map, selection| {
9788 let cursor = if selection.is_empty() && !line_mode {
9789 movement::right(map, selection.end)
9790 } else {
9791 selection.end
9792 };
9793 selection.collapse_to(cursor, SelectionGoal::None)
9794 });
9795 })
9796 }
9797
9798 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9799 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9800 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9801 })
9802 }
9803
9804 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9805 if self.take_rename(true, window, cx).is_some() {
9806 return;
9807 }
9808
9809 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9810 cx.propagate();
9811 return;
9812 }
9813
9814 let text_layout_details = &self.text_layout_details(window);
9815 let selection_count = self.selections.count();
9816 let first_selection = self.selections.first_anchor();
9817
9818 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9819 let line_mode = s.line_mode;
9820 s.move_with(|map, selection| {
9821 if !selection.is_empty() && !line_mode {
9822 selection.goal = SelectionGoal::None;
9823 }
9824 let (cursor, goal) = movement::up(
9825 map,
9826 selection.start,
9827 selection.goal,
9828 false,
9829 text_layout_details,
9830 );
9831 selection.collapse_to(cursor, goal);
9832 });
9833 });
9834
9835 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9836 {
9837 cx.propagate();
9838 }
9839 }
9840
9841 pub fn move_up_by_lines(
9842 &mut self,
9843 action: &MoveUpByLines,
9844 window: &mut Window,
9845 cx: &mut Context<Self>,
9846 ) {
9847 if self.take_rename(true, window, cx).is_some() {
9848 return;
9849 }
9850
9851 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9852 cx.propagate();
9853 return;
9854 }
9855
9856 let text_layout_details = &self.text_layout_details(window);
9857
9858 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9859 let line_mode = s.line_mode;
9860 s.move_with(|map, selection| {
9861 if !selection.is_empty() && !line_mode {
9862 selection.goal = SelectionGoal::None;
9863 }
9864 let (cursor, goal) = movement::up_by_rows(
9865 map,
9866 selection.start,
9867 action.lines,
9868 selection.goal,
9869 false,
9870 text_layout_details,
9871 );
9872 selection.collapse_to(cursor, goal);
9873 });
9874 })
9875 }
9876
9877 pub fn move_down_by_lines(
9878 &mut self,
9879 action: &MoveDownByLines,
9880 window: &mut Window,
9881 cx: &mut Context<Self>,
9882 ) {
9883 if self.take_rename(true, window, cx).is_some() {
9884 return;
9885 }
9886
9887 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9888 cx.propagate();
9889 return;
9890 }
9891
9892 let text_layout_details = &self.text_layout_details(window);
9893
9894 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9895 let line_mode = s.line_mode;
9896 s.move_with(|map, selection| {
9897 if !selection.is_empty() && !line_mode {
9898 selection.goal = SelectionGoal::None;
9899 }
9900 let (cursor, goal) = movement::down_by_rows(
9901 map,
9902 selection.start,
9903 action.lines,
9904 selection.goal,
9905 false,
9906 text_layout_details,
9907 );
9908 selection.collapse_to(cursor, goal);
9909 });
9910 })
9911 }
9912
9913 pub fn select_down_by_lines(
9914 &mut self,
9915 action: &SelectDownByLines,
9916 window: &mut Window,
9917 cx: &mut Context<Self>,
9918 ) {
9919 let text_layout_details = &self.text_layout_details(window);
9920 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9921 s.move_heads_with(|map, head, goal| {
9922 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9923 })
9924 })
9925 }
9926
9927 pub fn select_up_by_lines(
9928 &mut self,
9929 action: &SelectUpByLines,
9930 window: &mut Window,
9931 cx: &mut Context<Self>,
9932 ) {
9933 let text_layout_details = &self.text_layout_details(window);
9934 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9935 s.move_heads_with(|map, head, goal| {
9936 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9937 })
9938 })
9939 }
9940
9941 pub fn select_page_up(
9942 &mut self,
9943 _: &SelectPageUp,
9944 window: &mut Window,
9945 cx: &mut Context<Self>,
9946 ) {
9947 let Some(row_count) = self.visible_row_count() else {
9948 return;
9949 };
9950
9951 let text_layout_details = &self.text_layout_details(window);
9952
9953 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9954 s.move_heads_with(|map, head, goal| {
9955 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9956 })
9957 })
9958 }
9959
9960 pub fn move_page_up(
9961 &mut self,
9962 action: &MovePageUp,
9963 window: &mut Window,
9964 cx: &mut Context<Self>,
9965 ) {
9966 if self.take_rename(true, window, cx).is_some() {
9967 return;
9968 }
9969
9970 if self
9971 .context_menu
9972 .borrow_mut()
9973 .as_mut()
9974 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9975 .unwrap_or(false)
9976 {
9977 return;
9978 }
9979
9980 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9981 cx.propagate();
9982 return;
9983 }
9984
9985 let Some(row_count) = self.visible_row_count() else {
9986 return;
9987 };
9988
9989 let autoscroll = if action.center_cursor {
9990 Autoscroll::center()
9991 } else {
9992 Autoscroll::fit()
9993 };
9994
9995 let text_layout_details = &self.text_layout_details(window);
9996
9997 self.change_selections(Some(autoscroll), window, cx, |s| {
9998 let line_mode = s.line_mode;
9999 s.move_with(|map, selection| {
10000 if !selection.is_empty() && !line_mode {
10001 selection.goal = SelectionGoal::None;
10002 }
10003 let (cursor, goal) = movement::up_by_rows(
10004 map,
10005 selection.end,
10006 row_count,
10007 selection.goal,
10008 false,
10009 text_layout_details,
10010 );
10011 selection.collapse_to(cursor, goal);
10012 });
10013 });
10014 }
10015
10016 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
10017 let text_layout_details = &self.text_layout_details(window);
10018 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10019 s.move_heads_with(|map, head, goal| {
10020 movement::up(map, head, goal, false, text_layout_details)
10021 })
10022 })
10023 }
10024
10025 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
10026 self.take_rename(true, window, cx);
10027
10028 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10029 cx.propagate();
10030 return;
10031 }
10032
10033 let text_layout_details = &self.text_layout_details(window);
10034 let selection_count = self.selections.count();
10035 let first_selection = self.selections.first_anchor();
10036
10037 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10038 let line_mode = s.line_mode;
10039 s.move_with(|map, selection| {
10040 if !selection.is_empty() && !line_mode {
10041 selection.goal = SelectionGoal::None;
10042 }
10043 let (cursor, goal) = movement::down(
10044 map,
10045 selection.end,
10046 selection.goal,
10047 false,
10048 text_layout_details,
10049 );
10050 selection.collapse_to(cursor, goal);
10051 });
10052 });
10053
10054 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10055 {
10056 cx.propagate();
10057 }
10058 }
10059
10060 pub fn select_page_down(
10061 &mut self,
10062 _: &SelectPageDown,
10063 window: &mut Window,
10064 cx: &mut Context<Self>,
10065 ) {
10066 let Some(row_count) = self.visible_row_count() else {
10067 return;
10068 };
10069
10070 let text_layout_details = &self.text_layout_details(window);
10071
10072 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10073 s.move_heads_with(|map, head, goal| {
10074 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10075 })
10076 })
10077 }
10078
10079 pub fn move_page_down(
10080 &mut self,
10081 action: &MovePageDown,
10082 window: &mut Window,
10083 cx: &mut Context<Self>,
10084 ) {
10085 if self.take_rename(true, window, cx).is_some() {
10086 return;
10087 }
10088
10089 if self
10090 .context_menu
10091 .borrow_mut()
10092 .as_mut()
10093 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10094 .unwrap_or(false)
10095 {
10096 return;
10097 }
10098
10099 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10100 cx.propagate();
10101 return;
10102 }
10103
10104 let Some(row_count) = self.visible_row_count() else {
10105 return;
10106 };
10107
10108 let autoscroll = if action.center_cursor {
10109 Autoscroll::center()
10110 } else {
10111 Autoscroll::fit()
10112 };
10113
10114 let text_layout_details = &self.text_layout_details(window);
10115 self.change_selections(Some(autoscroll), window, cx, |s| {
10116 let line_mode = s.line_mode;
10117 s.move_with(|map, selection| {
10118 if !selection.is_empty() && !line_mode {
10119 selection.goal = SelectionGoal::None;
10120 }
10121 let (cursor, goal) = movement::down_by_rows(
10122 map,
10123 selection.end,
10124 row_count,
10125 selection.goal,
10126 false,
10127 text_layout_details,
10128 );
10129 selection.collapse_to(cursor, goal);
10130 });
10131 });
10132 }
10133
10134 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10135 let text_layout_details = &self.text_layout_details(window);
10136 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10137 s.move_heads_with(|map, head, goal| {
10138 movement::down(map, head, goal, false, text_layout_details)
10139 })
10140 });
10141 }
10142
10143 pub fn context_menu_first(
10144 &mut self,
10145 _: &ContextMenuFirst,
10146 _window: &mut Window,
10147 cx: &mut Context<Self>,
10148 ) {
10149 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10150 context_menu.select_first(self.completion_provider.as_deref(), cx);
10151 }
10152 }
10153
10154 pub fn context_menu_prev(
10155 &mut self,
10156 _: &ContextMenuPrevious,
10157 _window: &mut Window,
10158 cx: &mut Context<Self>,
10159 ) {
10160 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10161 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10162 }
10163 }
10164
10165 pub fn context_menu_next(
10166 &mut self,
10167 _: &ContextMenuNext,
10168 _window: &mut Window,
10169 cx: &mut Context<Self>,
10170 ) {
10171 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10172 context_menu.select_next(self.completion_provider.as_deref(), cx);
10173 }
10174 }
10175
10176 pub fn context_menu_last(
10177 &mut self,
10178 _: &ContextMenuLast,
10179 _window: &mut Window,
10180 cx: &mut Context<Self>,
10181 ) {
10182 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10183 context_menu.select_last(self.completion_provider.as_deref(), cx);
10184 }
10185 }
10186
10187 pub fn move_to_previous_word_start(
10188 &mut self,
10189 _: &MoveToPreviousWordStart,
10190 window: &mut Window,
10191 cx: &mut Context<Self>,
10192 ) {
10193 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10194 s.move_cursors_with(|map, head, _| {
10195 (
10196 movement::previous_word_start(map, head),
10197 SelectionGoal::None,
10198 )
10199 });
10200 })
10201 }
10202
10203 pub fn move_to_previous_subword_start(
10204 &mut self,
10205 _: &MoveToPreviousSubwordStart,
10206 window: &mut Window,
10207 cx: &mut Context<Self>,
10208 ) {
10209 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10210 s.move_cursors_with(|map, head, _| {
10211 (
10212 movement::previous_subword_start(map, head),
10213 SelectionGoal::None,
10214 )
10215 });
10216 })
10217 }
10218
10219 pub fn select_to_previous_word_start(
10220 &mut self,
10221 _: &SelectToPreviousWordStart,
10222 window: &mut Window,
10223 cx: &mut Context<Self>,
10224 ) {
10225 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10226 s.move_heads_with(|map, head, _| {
10227 (
10228 movement::previous_word_start(map, head),
10229 SelectionGoal::None,
10230 )
10231 });
10232 })
10233 }
10234
10235 pub fn select_to_previous_subword_start(
10236 &mut self,
10237 _: &SelectToPreviousSubwordStart,
10238 window: &mut Window,
10239 cx: &mut Context<Self>,
10240 ) {
10241 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10242 s.move_heads_with(|map, head, _| {
10243 (
10244 movement::previous_subword_start(map, head),
10245 SelectionGoal::None,
10246 )
10247 });
10248 })
10249 }
10250
10251 pub fn delete_to_previous_word_start(
10252 &mut self,
10253 action: &DeleteToPreviousWordStart,
10254 window: &mut Window,
10255 cx: &mut Context<Self>,
10256 ) {
10257 self.transact(window, cx, |this, window, cx| {
10258 this.select_autoclose_pair(window, cx);
10259 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10260 let line_mode = s.line_mode;
10261 s.move_with(|map, selection| {
10262 if selection.is_empty() && !line_mode {
10263 let cursor = if action.ignore_newlines {
10264 movement::previous_word_start(map, selection.head())
10265 } else {
10266 movement::previous_word_start_or_newline(map, selection.head())
10267 };
10268 selection.set_head(cursor, SelectionGoal::None);
10269 }
10270 });
10271 });
10272 this.insert("", window, cx);
10273 });
10274 }
10275
10276 pub fn delete_to_previous_subword_start(
10277 &mut self,
10278 _: &DeleteToPreviousSubwordStart,
10279 window: &mut Window,
10280 cx: &mut Context<Self>,
10281 ) {
10282 self.transact(window, cx, |this, window, cx| {
10283 this.select_autoclose_pair(window, cx);
10284 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10285 let line_mode = s.line_mode;
10286 s.move_with(|map, selection| {
10287 if selection.is_empty() && !line_mode {
10288 let cursor = movement::previous_subword_start(map, selection.head());
10289 selection.set_head(cursor, SelectionGoal::None);
10290 }
10291 });
10292 });
10293 this.insert("", window, cx);
10294 });
10295 }
10296
10297 pub fn move_to_next_word_end(
10298 &mut self,
10299 _: &MoveToNextWordEnd,
10300 window: &mut Window,
10301 cx: &mut Context<Self>,
10302 ) {
10303 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10304 s.move_cursors_with(|map, head, _| {
10305 (movement::next_word_end(map, head), SelectionGoal::None)
10306 });
10307 })
10308 }
10309
10310 pub fn move_to_next_subword_end(
10311 &mut self,
10312 _: &MoveToNextSubwordEnd,
10313 window: &mut Window,
10314 cx: &mut Context<Self>,
10315 ) {
10316 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10317 s.move_cursors_with(|map, head, _| {
10318 (movement::next_subword_end(map, head), SelectionGoal::None)
10319 });
10320 })
10321 }
10322
10323 pub fn select_to_next_word_end(
10324 &mut self,
10325 _: &SelectToNextWordEnd,
10326 window: &mut Window,
10327 cx: &mut Context<Self>,
10328 ) {
10329 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10330 s.move_heads_with(|map, head, _| {
10331 (movement::next_word_end(map, head), SelectionGoal::None)
10332 });
10333 })
10334 }
10335
10336 pub fn select_to_next_subword_end(
10337 &mut self,
10338 _: &SelectToNextSubwordEnd,
10339 window: &mut Window,
10340 cx: &mut Context<Self>,
10341 ) {
10342 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10343 s.move_heads_with(|map, head, _| {
10344 (movement::next_subword_end(map, head), SelectionGoal::None)
10345 });
10346 })
10347 }
10348
10349 pub fn delete_to_next_word_end(
10350 &mut self,
10351 action: &DeleteToNextWordEnd,
10352 window: &mut Window,
10353 cx: &mut Context<Self>,
10354 ) {
10355 self.transact(window, cx, |this, window, cx| {
10356 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10357 let line_mode = s.line_mode;
10358 s.move_with(|map, selection| {
10359 if selection.is_empty() && !line_mode {
10360 let cursor = if action.ignore_newlines {
10361 movement::next_word_end(map, selection.head())
10362 } else {
10363 movement::next_word_end_or_newline(map, selection.head())
10364 };
10365 selection.set_head(cursor, SelectionGoal::None);
10366 }
10367 });
10368 });
10369 this.insert("", window, cx);
10370 });
10371 }
10372
10373 pub fn delete_to_next_subword_end(
10374 &mut self,
10375 _: &DeleteToNextSubwordEnd,
10376 window: &mut Window,
10377 cx: &mut Context<Self>,
10378 ) {
10379 self.transact(window, cx, |this, window, cx| {
10380 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10381 s.move_with(|map, selection| {
10382 if selection.is_empty() {
10383 let cursor = movement::next_subword_end(map, selection.head());
10384 selection.set_head(cursor, SelectionGoal::None);
10385 }
10386 });
10387 });
10388 this.insert("", window, cx);
10389 });
10390 }
10391
10392 pub fn move_to_beginning_of_line(
10393 &mut self,
10394 action: &MoveToBeginningOfLine,
10395 window: &mut Window,
10396 cx: &mut Context<Self>,
10397 ) {
10398 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10399 s.move_cursors_with(|map, head, _| {
10400 (
10401 movement::indented_line_beginning(
10402 map,
10403 head,
10404 action.stop_at_soft_wraps,
10405 action.stop_at_indent,
10406 ),
10407 SelectionGoal::None,
10408 )
10409 });
10410 })
10411 }
10412
10413 pub fn select_to_beginning_of_line(
10414 &mut self,
10415 action: &SelectToBeginningOfLine,
10416 window: &mut Window,
10417 cx: &mut Context<Self>,
10418 ) {
10419 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10420 s.move_heads_with(|map, head, _| {
10421 (
10422 movement::indented_line_beginning(
10423 map,
10424 head,
10425 action.stop_at_soft_wraps,
10426 action.stop_at_indent,
10427 ),
10428 SelectionGoal::None,
10429 )
10430 });
10431 });
10432 }
10433
10434 pub fn delete_to_beginning_of_line(
10435 &mut self,
10436 action: &DeleteToBeginningOfLine,
10437 window: &mut Window,
10438 cx: &mut Context<Self>,
10439 ) {
10440 self.transact(window, cx, |this, window, cx| {
10441 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10442 s.move_with(|_, selection| {
10443 selection.reversed = true;
10444 });
10445 });
10446
10447 this.select_to_beginning_of_line(
10448 &SelectToBeginningOfLine {
10449 stop_at_soft_wraps: false,
10450 stop_at_indent: action.stop_at_indent,
10451 },
10452 window,
10453 cx,
10454 );
10455 this.backspace(&Backspace, window, cx);
10456 });
10457 }
10458
10459 pub fn move_to_end_of_line(
10460 &mut self,
10461 action: &MoveToEndOfLine,
10462 window: &mut Window,
10463 cx: &mut Context<Self>,
10464 ) {
10465 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10466 s.move_cursors_with(|map, head, _| {
10467 (
10468 movement::line_end(map, head, action.stop_at_soft_wraps),
10469 SelectionGoal::None,
10470 )
10471 });
10472 })
10473 }
10474
10475 pub fn select_to_end_of_line(
10476 &mut self,
10477 action: &SelectToEndOfLine,
10478 window: &mut Window,
10479 cx: &mut Context<Self>,
10480 ) {
10481 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10482 s.move_heads_with(|map, head, _| {
10483 (
10484 movement::line_end(map, head, action.stop_at_soft_wraps),
10485 SelectionGoal::None,
10486 )
10487 });
10488 })
10489 }
10490
10491 pub fn delete_to_end_of_line(
10492 &mut self,
10493 _: &DeleteToEndOfLine,
10494 window: &mut Window,
10495 cx: &mut Context<Self>,
10496 ) {
10497 self.transact(window, cx, |this, window, cx| {
10498 this.select_to_end_of_line(
10499 &SelectToEndOfLine {
10500 stop_at_soft_wraps: false,
10501 },
10502 window,
10503 cx,
10504 );
10505 this.delete(&Delete, window, cx);
10506 });
10507 }
10508
10509 pub fn cut_to_end_of_line(
10510 &mut self,
10511 _: &CutToEndOfLine,
10512 window: &mut Window,
10513 cx: &mut Context<Self>,
10514 ) {
10515 self.transact(window, cx, |this, window, cx| {
10516 this.select_to_end_of_line(
10517 &SelectToEndOfLine {
10518 stop_at_soft_wraps: false,
10519 },
10520 window,
10521 cx,
10522 );
10523 this.cut(&Cut, window, cx);
10524 });
10525 }
10526
10527 pub fn move_to_start_of_paragraph(
10528 &mut self,
10529 _: &MoveToStartOfParagraph,
10530 window: &mut Window,
10531 cx: &mut Context<Self>,
10532 ) {
10533 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10534 cx.propagate();
10535 return;
10536 }
10537
10538 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10539 s.move_with(|map, selection| {
10540 selection.collapse_to(
10541 movement::start_of_paragraph(map, selection.head(), 1),
10542 SelectionGoal::None,
10543 )
10544 });
10545 })
10546 }
10547
10548 pub fn move_to_end_of_paragraph(
10549 &mut self,
10550 _: &MoveToEndOfParagraph,
10551 window: &mut Window,
10552 cx: &mut Context<Self>,
10553 ) {
10554 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10555 cx.propagate();
10556 return;
10557 }
10558
10559 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10560 s.move_with(|map, selection| {
10561 selection.collapse_to(
10562 movement::end_of_paragraph(map, selection.head(), 1),
10563 SelectionGoal::None,
10564 )
10565 });
10566 })
10567 }
10568
10569 pub fn select_to_start_of_paragraph(
10570 &mut self,
10571 _: &SelectToStartOfParagraph,
10572 window: &mut Window,
10573 cx: &mut Context<Self>,
10574 ) {
10575 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10576 cx.propagate();
10577 return;
10578 }
10579
10580 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10581 s.move_heads_with(|map, head, _| {
10582 (
10583 movement::start_of_paragraph(map, head, 1),
10584 SelectionGoal::None,
10585 )
10586 });
10587 })
10588 }
10589
10590 pub fn select_to_end_of_paragraph(
10591 &mut self,
10592 _: &SelectToEndOfParagraph,
10593 window: &mut Window,
10594 cx: &mut Context<Self>,
10595 ) {
10596 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10597 cx.propagate();
10598 return;
10599 }
10600
10601 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10602 s.move_heads_with(|map, head, _| {
10603 (
10604 movement::end_of_paragraph(map, head, 1),
10605 SelectionGoal::None,
10606 )
10607 });
10608 })
10609 }
10610
10611 pub fn move_to_start_of_excerpt(
10612 &mut self,
10613 _: &MoveToStartOfExcerpt,
10614 window: &mut Window,
10615 cx: &mut Context<Self>,
10616 ) {
10617 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10618 cx.propagate();
10619 return;
10620 }
10621
10622 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10623 s.move_with(|map, selection| {
10624 selection.collapse_to(
10625 movement::start_of_excerpt(
10626 map,
10627 selection.head(),
10628 workspace::searchable::Direction::Prev,
10629 ),
10630 SelectionGoal::None,
10631 )
10632 });
10633 })
10634 }
10635
10636 pub fn move_to_start_of_next_excerpt(
10637 &mut self,
10638 _: &MoveToStartOfNextExcerpt,
10639 window: &mut Window,
10640 cx: &mut Context<Self>,
10641 ) {
10642 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10643 cx.propagate();
10644 return;
10645 }
10646
10647 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10648 s.move_with(|map, selection| {
10649 selection.collapse_to(
10650 movement::start_of_excerpt(
10651 map,
10652 selection.head(),
10653 workspace::searchable::Direction::Next,
10654 ),
10655 SelectionGoal::None,
10656 )
10657 });
10658 })
10659 }
10660
10661 pub fn move_to_end_of_excerpt(
10662 &mut self,
10663 _: &MoveToEndOfExcerpt,
10664 window: &mut Window,
10665 cx: &mut Context<Self>,
10666 ) {
10667 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10668 cx.propagate();
10669 return;
10670 }
10671
10672 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10673 s.move_with(|map, selection| {
10674 selection.collapse_to(
10675 movement::end_of_excerpt(
10676 map,
10677 selection.head(),
10678 workspace::searchable::Direction::Next,
10679 ),
10680 SelectionGoal::None,
10681 )
10682 });
10683 })
10684 }
10685
10686 pub fn move_to_end_of_previous_excerpt(
10687 &mut self,
10688 _: &MoveToEndOfPreviousExcerpt,
10689 window: &mut Window,
10690 cx: &mut Context<Self>,
10691 ) {
10692 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10693 cx.propagate();
10694 return;
10695 }
10696
10697 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10698 s.move_with(|map, selection| {
10699 selection.collapse_to(
10700 movement::end_of_excerpt(
10701 map,
10702 selection.head(),
10703 workspace::searchable::Direction::Prev,
10704 ),
10705 SelectionGoal::None,
10706 )
10707 });
10708 })
10709 }
10710
10711 pub fn select_to_start_of_excerpt(
10712 &mut self,
10713 _: &SelectToStartOfExcerpt,
10714 window: &mut Window,
10715 cx: &mut Context<Self>,
10716 ) {
10717 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10718 cx.propagate();
10719 return;
10720 }
10721
10722 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10723 s.move_heads_with(|map, head, _| {
10724 (
10725 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10726 SelectionGoal::None,
10727 )
10728 });
10729 })
10730 }
10731
10732 pub fn select_to_start_of_next_excerpt(
10733 &mut self,
10734 _: &SelectToStartOfNextExcerpt,
10735 window: &mut Window,
10736 cx: &mut Context<Self>,
10737 ) {
10738 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10739 cx.propagate();
10740 return;
10741 }
10742
10743 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10744 s.move_heads_with(|map, head, _| {
10745 (
10746 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10747 SelectionGoal::None,
10748 )
10749 });
10750 })
10751 }
10752
10753 pub fn select_to_end_of_excerpt(
10754 &mut self,
10755 _: &SelectToEndOfExcerpt,
10756 window: &mut Window,
10757 cx: &mut Context<Self>,
10758 ) {
10759 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10760 cx.propagate();
10761 return;
10762 }
10763
10764 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10765 s.move_heads_with(|map, head, _| {
10766 (
10767 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10768 SelectionGoal::None,
10769 )
10770 });
10771 })
10772 }
10773
10774 pub fn select_to_end_of_previous_excerpt(
10775 &mut self,
10776 _: &SelectToEndOfPreviousExcerpt,
10777 window: &mut Window,
10778 cx: &mut Context<Self>,
10779 ) {
10780 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10781 cx.propagate();
10782 return;
10783 }
10784
10785 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10786 s.move_heads_with(|map, head, _| {
10787 (
10788 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10789 SelectionGoal::None,
10790 )
10791 });
10792 })
10793 }
10794
10795 pub fn move_to_beginning(
10796 &mut self,
10797 _: &MoveToBeginning,
10798 window: &mut Window,
10799 cx: &mut Context<Self>,
10800 ) {
10801 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10802 cx.propagate();
10803 return;
10804 }
10805
10806 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10807 s.select_ranges(vec![0..0]);
10808 });
10809 }
10810
10811 pub fn select_to_beginning(
10812 &mut self,
10813 _: &SelectToBeginning,
10814 window: &mut Window,
10815 cx: &mut Context<Self>,
10816 ) {
10817 let mut selection = self.selections.last::<Point>(cx);
10818 selection.set_head(Point::zero(), SelectionGoal::None);
10819
10820 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10821 s.select(vec![selection]);
10822 });
10823 }
10824
10825 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10826 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10827 cx.propagate();
10828 return;
10829 }
10830
10831 let cursor = self.buffer.read(cx).read(cx).len();
10832 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10833 s.select_ranges(vec![cursor..cursor])
10834 });
10835 }
10836
10837 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10838 self.nav_history = nav_history;
10839 }
10840
10841 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10842 self.nav_history.as_ref()
10843 }
10844
10845 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10846 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10847 }
10848
10849 fn push_to_nav_history(
10850 &mut self,
10851 cursor_anchor: Anchor,
10852 new_position: Option<Point>,
10853 is_deactivate: bool,
10854 cx: &mut Context<Self>,
10855 ) {
10856 if let Some(nav_history) = self.nav_history.as_mut() {
10857 let buffer = self.buffer.read(cx).read(cx);
10858 let cursor_position = cursor_anchor.to_point(&buffer);
10859 let scroll_state = self.scroll_manager.anchor();
10860 let scroll_top_row = scroll_state.top_row(&buffer);
10861 drop(buffer);
10862
10863 if let Some(new_position) = new_position {
10864 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10865 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10866 return;
10867 }
10868 }
10869
10870 nav_history.push(
10871 Some(NavigationData {
10872 cursor_anchor,
10873 cursor_position,
10874 scroll_anchor: scroll_state,
10875 scroll_top_row,
10876 }),
10877 cx,
10878 );
10879 cx.emit(EditorEvent::PushedToNavHistory {
10880 anchor: cursor_anchor,
10881 is_deactivate,
10882 })
10883 }
10884 }
10885
10886 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10887 let buffer = self.buffer.read(cx).snapshot(cx);
10888 let mut selection = self.selections.first::<usize>(cx);
10889 selection.set_head(buffer.len(), SelectionGoal::None);
10890 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10891 s.select(vec![selection]);
10892 });
10893 }
10894
10895 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10896 let end = self.buffer.read(cx).read(cx).len();
10897 self.change_selections(None, window, cx, |s| {
10898 s.select_ranges(vec![0..end]);
10899 });
10900 }
10901
10902 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10903 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10904 let mut selections = self.selections.all::<Point>(cx);
10905 let max_point = display_map.buffer_snapshot.max_point();
10906 for selection in &mut selections {
10907 let rows = selection.spanned_rows(true, &display_map);
10908 selection.start = Point::new(rows.start.0, 0);
10909 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10910 selection.reversed = false;
10911 }
10912 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10913 s.select(selections);
10914 });
10915 }
10916
10917 pub fn split_selection_into_lines(
10918 &mut self,
10919 _: &SplitSelectionIntoLines,
10920 window: &mut Window,
10921 cx: &mut Context<Self>,
10922 ) {
10923 let selections = self
10924 .selections
10925 .all::<Point>(cx)
10926 .into_iter()
10927 .map(|selection| selection.start..selection.end)
10928 .collect::<Vec<_>>();
10929 self.unfold_ranges(&selections, true, true, cx);
10930
10931 let mut new_selection_ranges = Vec::new();
10932 {
10933 let buffer = self.buffer.read(cx).read(cx);
10934 for selection in selections {
10935 for row in selection.start.row..selection.end.row {
10936 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10937 new_selection_ranges.push(cursor..cursor);
10938 }
10939
10940 let is_multiline_selection = selection.start.row != selection.end.row;
10941 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10942 // so this action feels more ergonomic when paired with other selection operations
10943 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10944 if !should_skip_last {
10945 new_selection_ranges.push(selection.end..selection.end);
10946 }
10947 }
10948 }
10949 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10950 s.select_ranges(new_selection_ranges);
10951 });
10952 }
10953
10954 pub fn add_selection_above(
10955 &mut self,
10956 _: &AddSelectionAbove,
10957 window: &mut Window,
10958 cx: &mut Context<Self>,
10959 ) {
10960 self.add_selection(true, window, cx);
10961 }
10962
10963 pub fn add_selection_below(
10964 &mut self,
10965 _: &AddSelectionBelow,
10966 window: &mut Window,
10967 cx: &mut Context<Self>,
10968 ) {
10969 self.add_selection(false, window, cx);
10970 }
10971
10972 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10973 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10974 let mut selections = self.selections.all::<Point>(cx);
10975 let text_layout_details = self.text_layout_details(window);
10976 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10977 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10978 let range = oldest_selection.display_range(&display_map).sorted();
10979
10980 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10981 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10982 let positions = start_x.min(end_x)..start_x.max(end_x);
10983
10984 selections.clear();
10985 let mut stack = Vec::new();
10986 for row in range.start.row().0..=range.end.row().0 {
10987 if let Some(selection) = self.selections.build_columnar_selection(
10988 &display_map,
10989 DisplayRow(row),
10990 &positions,
10991 oldest_selection.reversed,
10992 &text_layout_details,
10993 ) {
10994 stack.push(selection.id);
10995 selections.push(selection);
10996 }
10997 }
10998
10999 if above {
11000 stack.reverse();
11001 }
11002
11003 AddSelectionsState { above, stack }
11004 });
11005
11006 let last_added_selection = *state.stack.last().unwrap();
11007 let mut new_selections = Vec::new();
11008 if above == state.above {
11009 let end_row = if above {
11010 DisplayRow(0)
11011 } else {
11012 display_map.max_point().row()
11013 };
11014
11015 'outer: for selection in selections {
11016 if selection.id == last_added_selection {
11017 let range = selection.display_range(&display_map).sorted();
11018 debug_assert_eq!(range.start.row(), range.end.row());
11019 let mut row = range.start.row();
11020 let positions =
11021 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
11022 px(start)..px(end)
11023 } else {
11024 let start_x =
11025 display_map.x_for_display_point(range.start, &text_layout_details);
11026 let end_x =
11027 display_map.x_for_display_point(range.end, &text_layout_details);
11028 start_x.min(end_x)..start_x.max(end_x)
11029 };
11030
11031 while row != end_row {
11032 if above {
11033 row.0 -= 1;
11034 } else {
11035 row.0 += 1;
11036 }
11037
11038 if let Some(new_selection) = self.selections.build_columnar_selection(
11039 &display_map,
11040 row,
11041 &positions,
11042 selection.reversed,
11043 &text_layout_details,
11044 ) {
11045 state.stack.push(new_selection.id);
11046 if above {
11047 new_selections.push(new_selection);
11048 new_selections.push(selection);
11049 } else {
11050 new_selections.push(selection);
11051 new_selections.push(new_selection);
11052 }
11053
11054 continue 'outer;
11055 }
11056 }
11057 }
11058
11059 new_selections.push(selection);
11060 }
11061 } else {
11062 new_selections = selections;
11063 new_selections.retain(|s| s.id != last_added_selection);
11064 state.stack.pop();
11065 }
11066
11067 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11068 s.select(new_selections);
11069 });
11070 if state.stack.len() > 1 {
11071 self.add_selections_state = Some(state);
11072 }
11073 }
11074
11075 pub fn select_next_match_internal(
11076 &mut self,
11077 display_map: &DisplaySnapshot,
11078 replace_newest: bool,
11079 autoscroll: Option<Autoscroll>,
11080 window: &mut Window,
11081 cx: &mut Context<Self>,
11082 ) -> Result<()> {
11083 fn select_next_match_ranges(
11084 this: &mut Editor,
11085 range: Range<usize>,
11086 replace_newest: bool,
11087 auto_scroll: Option<Autoscroll>,
11088 window: &mut Window,
11089 cx: &mut Context<Editor>,
11090 ) {
11091 this.unfold_ranges(&[range.clone()], false, true, cx);
11092 this.change_selections(auto_scroll, window, cx, |s| {
11093 if replace_newest {
11094 s.delete(s.newest_anchor().id);
11095 }
11096 s.insert_range(range.clone());
11097 });
11098 }
11099
11100 let buffer = &display_map.buffer_snapshot;
11101 let mut selections = self.selections.all::<usize>(cx);
11102 if let Some(mut select_next_state) = self.select_next_state.take() {
11103 let query = &select_next_state.query;
11104 if !select_next_state.done {
11105 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11106 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11107 let mut next_selected_range = None;
11108
11109 let bytes_after_last_selection =
11110 buffer.bytes_in_range(last_selection.end..buffer.len());
11111 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11112 let query_matches = query
11113 .stream_find_iter(bytes_after_last_selection)
11114 .map(|result| (last_selection.end, result))
11115 .chain(
11116 query
11117 .stream_find_iter(bytes_before_first_selection)
11118 .map(|result| (0, result)),
11119 );
11120
11121 for (start_offset, query_match) in query_matches {
11122 let query_match = query_match.unwrap(); // can only fail due to I/O
11123 let offset_range =
11124 start_offset + query_match.start()..start_offset + query_match.end();
11125 let display_range = offset_range.start.to_display_point(display_map)
11126 ..offset_range.end.to_display_point(display_map);
11127
11128 if !select_next_state.wordwise
11129 || (!movement::is_inside_word(display_map, display_range.start)
11130 && !movement::is_inside_word(display_map, display_range.end))
11131 {
11132 // TODO: This is n^2, because we might check all the selections
11133 if !selections
11134 .iter()
11135 .any(|selection| selection.range().overlaps(&offset_range))
11136 {
11137 next_selected_range = Some(offset_range);
11138 break;
11139 }
11140 }
11141 }
11142
11143 if let Some(next_selected_range) = next_selected_range {
11144 select_next_match_ranges(
11145 self,
11146 next_selected_range,
11147 replace_newest,
11148 autoscroll,
11149 window,
11150 cx,
11151 );
11152 } else {
11153 select_next_state.done = true;
11154 }
11155 }
11156
11157 self.select_next_state = Some(select_next_state);
11158 } else {
11159 let mut only_carets = true;
11160 let mut same_text_selected = true;
11161 let mut selected_text = None;
11162
11163 let mut selections_iter = selections.iter().peekable();
11164 while let Some(selection) = selections_iter.next() {
11165 if selection.start != selection.end {
11166 only_carets = false;
11167 }
11168
11169 if same_text_selected {
11170 if selected_text.is_none() {
11171 selected_text =
11172 Some(buffer.text_for_range(selection.range()).collect::<String>());
11173 }
11174
11175 if let Some(next_selection) = selections_iter.peek() {
11176 if next_selection.range().len() == selection.range().len() {
11177 let next_selected_text = buffer
11178 .text_for_range(next_selection.range())
11179 .collect::<String>();
11180 if Some(next_selected_text) != selected_text {
11181 same_text_selected = false;
11182 selected_text = None;
11183 }
11184 } else {
11185 same_text_selected = false;
11186 selected_text = None;
11187 }
11188 }
11189 }
11190 }
11191
11192 if only_carets {
11193 for selection in &mut selections {
11194 let word_range = movement::surrounding_word(
11195 display_map,
11196 selection.start.to_display_point(display_map),
11197 );
11198 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11199 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11200 selection.goal = SelectionGoal::None;
11201 selection.reversed = false;
11202 select_next_match_ranges(
11203 self,
11204 selection.start..selection.end,
11205 replace_newest,
11206 autoscroll,
11207 window,
11208 cx,
11209 );
11210 }
11211
11212 if selections.len() == 1 {
11213 let selection = selections
11214 .last()
11215 .expect("ensured that there's only one selection");
11216 let query = buffer
11217 .text_for_range(selection.start..selection.end)
11218 .collect::<String>();
11219 let is_empty = query.is_empty();
11220 let select_state = SelectNextState {
11221 query: AhoCorasick::new(&[query])?,
11222 wordwise: true,
11223 done: is_empty,
11224 };
11225 self.select_next_state = Some(select_state);
11226 } else {
11227 self.select_next_state = None;
11228 }
11229 } else if let Some(selected_text) = selected_text {
11230 self.select_next_state = Some(SelectNextState {
11231 query: AhoCorasick::new(&[selected_text])?,
11232 wordwise: false,
11233 done: false,
11234 });
11235 self.select_next_match_internal(
11236 display_map,
11237 replace_newest,
11238 autoscroll,
11239 window,
11240 cx,
11241 )?;
11242 }
11243 }
11244 Ok(())
11245 }
11246
11247 pub fn select_all_matches(
11248 &mut self,
11249 _action: &SelectAllMatches,
11250 window: &mut Window,
11251 cx: &mut Context<Self>,
11252 ) -> Result<()> {
11253 self.push_to_selection_history();
11254 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11255
11256 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11257 let Some(select_next_state) = self.select_next_state.as_mut() else {
11258 return Ok(());
11259 };
11260 if select_next_state.done {
11261 return Ok(());
11262 }
11263
11264 let mut new_selections = self.selections.all::<usize>(cx);
11265
11266 let buffer = &display_map.buffer_snapshot;
11267 let query_matches = select_next_state
11268 .query
11269 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11270
11271 for query_match in query_matches {
11272 let query_match = query_match.unwrap(); // can only fail due to I/O
11273 let offset_range = query_match.start()..query_match.end();
11274 let display_range = offset_range.start.to_display_point(&display_map)
11275 ..offset_range.end.to_display_point(&display_map);
11276
11277 if !select_next_state.wordwise
11278 || (!movement::is_inside_word(&display_map, display_range.start)
11279 && !movement::is_inside_word(&display_map, display_range.end))
11280 {
11281 self.selections.change_with(cx, |selections| {
11282 new_selections.push(Selection {
11283 id: selections.new_selection_id(),
11284 start: offset_range.start,
11285 end: offset_range.end,
11286 reversed: false,
11287 goal: SelectionGoal::None,
11288 });
11289 });
11290 }
11291 }
11292
11293 new_selections.sort_by_key(|selection| selection.start);
11294 let mut ix = 0;
11295 while ix + 1 < new_selections.len() {
11296 let current_selection = &new_selections[ix];
11297 let next_selection = &new_selections[ix + 1];
11298 if current_selection.range().overlaps(&next_selection.range()) {
11299 if current_selection.id < next_selection.id {
11300 new_selections.remove(ix + 1);
11301 } else {
11302 new_selections.remove(ix);
11303 }
11304 } else {
11305 ix += 1;
11306 }
11307 }
11308
11309 let reversed = self.selections.oldest::<usize>(cx).reversed;
11310
11311 for selection in new_selections.iter_mut() {
11312 selection.reversed = reversed;
11313 }
11314
11315 select_next_state.done = true;
11316 self.unfold_ranges(
11317 &new_selections
11318 .iter()
11319 .map(|selection| selection.range())
11320 .collect::<Vec<_>>(),
11321 false,
11322 false,
11323 cx,
11324 );
11325 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11326 selections.select(new_selections)
11327 });
11328
11329 Ok(())
11330 }
11331
11332 pub fn select_next(
11333 &mut self,
11334 action: &SelectNext,
11335 window: &mut Window,
11336 cx: &mut Context<Self>,
11337 ) -> Result<()> {
11338 self.push_to_selection_history();
11339 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11340 self.select_next_match_internal(
11341 &display_map,
11342 action.replace_newest,
11343 Some(Autoscroll::newest()),
11344 window,
11345 cx,
11346 )?;
11347 Ok(())
11348 }
11349
11350 pub fn select_previous(
11351 &mut self,
11352 action: &SelectPrevious,
11353 window: &mut Window,
11354 cx: &mut Context<Self>,
11355 ) -> Result<()> {
11356 self.push_to_selection_history();
11357 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11358 let buffer = &display_map.buffer_snapshot;
11359 let mut selections = self.selections.all::<usize>(cx);
11360 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11361 let query = &select_prev_state.query;
11362 if !select_prev_state.done {
11363 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11364 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11365 let mut next_selected_range = None;
11366 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11367 let bytes_before_last_selection =
11368 buffer.reversed_bytes_in_range(0..last_selection.start);
11369 let bytes_after_first_selection =
11370 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11371 let query_matches = query
11372 .stream_find_iter(bytes_before_last_selection)
11373 .map(|result| (last_selection.start, result))
11374 .chain(
11375 query
11376 .stream_find_iter(bytes_after_first_selection)
11377 .map(|result| (buffer.len(), result)),
11378 );
11379 for (end_offset, query_match) in query_matches {
11380 let query_match = query_match.unwrap(); // can only fail due to I/O
11381 let offset_range =
11382 end_offset - query_match.end()..end_offset - query_match.start();
11383 let display_range = offset_range.start.to_display_point(&display_map)
11384 ..offset_range.end.to_display_point(&display_map);
11385
11386 if !select_prev_state.wordwise
11387 || (!movement::is_inside_word(&display_map, display_range.start)
11388 && !movement::is_inside_word(&display_map, display_range.end))
11389 {
11390 next_selected_range = Some(offset_range);
11391 break;
11392 }
11393 }
11394
11395 if let Some(next_selected_range) = next_selected_range {
11396 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11397 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11398 if action.replace_newest {
11399 s.delete(s.newest_anchor().id);
11400 }
11401 s.insert_range(next_selected_range);
11402 });
11403 } else {
11404 select_prev_state.done = true;
11405 }
11406 }
11407
11408 self.select_prev_state = Some(select_prev_state);
11409 } else {
11410 let mut only_carets = true;
11411 let mut same_text_selected = true;
11412 let mut selected_text = None;
11413
11414 let mut selections_iter = selections.iter().peekable();
11415 while let Some(selection) = selections_iter.next() {
11416 if selection.start != selection.end {
11417 only_carets = false;
11418 }
11419
11420 if same_text_selected {
11421 if selected_text.is_none() {
11422 selected_text =
11423 Some(buffer.text_for_range(selection.range()).collect::<String>());
11424 }
11425
11426 if let Some(next_selection) = selections_iter.peek() {
11427 if next_selection.range().len() == selection.range().len() {
11428 let next_selected_text = buffer
11429 .text_for_range(next_selection.range())
11430 .collect::<String>();
11431 if Some(next_selected_text) != selected_text {
11432 same_text_selected = false;
11433 selected_text = None;
11434 }
11435 } else {
11436 same_text_selected = false;
11437 selected_text = None;
11438 }
11439 }
11440 }
11441 }
11442
11443 if only_carets {
11444 for selection in &mut selections {
11445 let word_range = movement::surrounding_word(
11446 &display_map,
11447 selection.start.to_display_point(&display_map),
11448 );
11449 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11450 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11451 selection.goal = SelectionGoal::None;
11452 selection.reversed = false;
11453 }
11454 if selections.len() == 1 {
11455 let selection = selections
11456 .last()
11457 .expect("ensured that there's only one selection");
11458 let query = buffer
11459 .text_for_range(selection.start..selection.end)
11460 .collect::<String>();
11461 let is_empty = query.is_empty();
11462 let select_state = SelectNextState {
11463 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11464 wordwise: true,
11465 done: is_empty,
11466 };
11467 self.select_prev_state = Some(select_state);
11468 } else {
11469 self.select_prev_state = None;
11470 }
11471
11472 self.unfold_ranges(
11473 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11474 false,
11475 true,
11476 cx,
11477 );
11478 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11479 s.select(selections);
11480 });
11481 } else if let Some(selected_text) = selected_text {
11482 self.select_prev_state = Some(SelectNextState {
11483 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11484 wordwise: false,
11485 done: false,
11486 });
11487 self.select_previous(action, window, cx)?;
11488 }
11489 }
11490 Ok(())
11491 }
11492
11493 pub fn toggle_comments(
11494 &mut self,
11495 action: &ToggleComments,
11496 window: &mut Window,
11497 cx: &mut Context<Self>,
11498 ) {
11499 if self.read_only(cx) {
11500 return;
11501 }
11502 let text_layout_details = &self.text_layout_details(window);
11503 self.transact(window, cx, |this, window, cx| {
11504 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11505 let mut edits = Vec::new();
11506 let mut selection_edit_ranges = Vec::new();
11507 let mut last_toggled_row = None;
11508 let snapshot = this.buffer.read(cx).read(cx);
11509 let empty_str: Arc<str> = Arc::default();
11510 let mut suffixes_inserted = Vec::new();
11511 let ignore_indent = action.ignore_indent;
11512
11513 fn comment_prefix_range(
11514 snapshot: &MultiBufferSnapshot,
11515 row: MultiBufferRow,
11516 comment_prefix: &str,
11517 comment_prefix_whitespace: &str,
11518 ignore_indent: bool,
11519 ) -> Range<Point> {
11520 let indent_size = if ignore_indent {
11521 0
11522 } else {
11523 snapshot.indent_size_for_line(row).len
11524 };
11525
11526 let start = Point::new(row.0, indent_size);
11527
11528 let mut line_bytes = snapshot
11529 .bytes_in_range(start..snapshot.max_point())
11530 .flatten()
11531 .copied();
11532
11533 // If this line currently begins with the line comment prefix, then record
11534 // the range containing the prefix.
11535 if line_bytes
11536 .by_ref()
11537 .take(comment_prefix.len())
11538 .eq(comment_prefix.bytes())
11539 {
11540 // Include any whitespace that matches the comment prefix.
11541 let matching_whitespace_len = line_bytes
11542 .zip(comment_prefix_whitespace.bytes())
11543 .take_while(|(a, b)| a == b)
11544 .count() as u32;
11545 let end = Point::new(
11546 start.row,
11547 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11548 );
11549 start..end
11550 } else {
11551 start..start
11552 }
11553 }
11554
11555 fn comment_suffix_range(
11556 snapshot: &MultiBufferSnapshot,
11557 row: MultiBufferRow,
11558 comment_suffix: &str,
11559 comment_suffix_has_leading_space: bool,
11560 ) -> Range<Point> {
11561 let end = Point::new(row.0, snapshot.line_len(row));
11562 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11563
11564 let mut line_end_bytes = snapshot
11565 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11566 .flatten()
11567 .copied();
11568
11569 let leading_space_len = if suffix_start_column > 0
11570 && line_end_bytes.next() == Some(b' ')
11571 && comment_suffix_has_leading_space
11572 {
11573 1
11574 } else {
11575 0
11576 };
11577
11578 // If this line currently begins with the line comment prefix, then record
11579 // the range containing the prefix.
11580 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11581 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11582 start..end
11583 } else {
11584 end..end
11585 }
11586 }
11587
11588 // TODO: Handle selections that cross excerpts
11589 for selection in &mut selections {
11590 let start_column = snapshot
11591 .indent_size_for_line(MultiBufferRow(selection.start.row))
11592 .len;
11593 let language = if let Some(language) =
11594 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11595 {
11596 language
11597 } else {
11598 continue;
11599 };
11600
11601 selection_edit_ranges.clear();
11602
11603 // If multiple selections contain a given row, avoid processing that
11604 // row more than once.
11605 let mut start_row = MultiBufferRow(selection.start.row);
11606 if last_toggled_row == Some(start_row) {
11607 start_row = start_row.next_row();
11608 }
11609 let end_row =
11610 if selection.end.row > selection.start.row && selection.end.column == 0 {
11611 MultiBufferRow(selection.end.row - 1)
11612 } else {
11613 MultiBufferRow(selection.end.row)
11614 };
11615 last_toggled_row = Some(end_row);
11616
11617 if start_row > end_row {
11618 continue;
11619 }
11620
11621 // If the language has line comments, toggle those.
11622 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11623
11624 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11625 if ignore_indent {
11626 full_comment_prefixes = full_comment_prefixes
11627 .into_iter()
11628 .map(|s| Arc::from(s.trim_end()))
11629 .collect();
11630 }
11631
11632 if !full_comment_prefixes.is_empty() {
11633 let first_prefix = full_comment_prefixes
11634 .first()
11635 .expect("prefixes is non-empty");
11636 let prefix_trimmed_lengths = full_comment_prefixes
11637 .iter()
11638 .map(|p| p.trim_end_matches(' ').len())
11639 .collect::<SmallVec<[usize; 4]>>();
11640
11641 let mut all_selection_lines_are_comments = true;
11642
11643 for row in start_row.0..=end_row.0 {
11644 let row = MultiBufferRow(row);
11645 if start_row < end_row && snapshot.is_line_blank(row) {
11646 continue;
11647 }
11648
11649 let prefix_range = full_comment_prefixes
11650 .iter()
11651 .zip(prefix_trimmed_lengths.iter().copied())
11652 .map(|(prefix, trimmed_prefix_len)| {
11653 comment_prefix_range(
11654 snapshot.deref(),
11655 row,
11656 &prefix[..trimmed_prefix_len],
11657 &prefix[trimmed_prefix_len..],
11658 ignore_indent,
11659 )
11660 })
11661 .max_by_key(|range| range.end.column - range.start.column)
11662 .expect("prefixes is non-empty");
11663
11664 if prefix_range.is_empty() {
11665 all_selection_lines_are_comments = false;
11666 }
11667
11668 selection_edit_ranges.push(prefix_range);
11669 }
11670
11671 if all_selection_lines_are_comments {
11672 edits.extend(
11673 selection_edit_ranges
11674 .iter()
11675 .cloned()
11676 .map(|range| (range, empty_str.clone())),
11677 );
11678 } else {
11679 let min_column = selection_edit_ranges
11680 .iter()
11681 .map(|range| range.start.column)
11682 .min()
11683 .unwrap_or(0);
11684 edits.extend(selection_edit_ranges.iter().map(|range| {
11685 let position = Point::new(range.start.row, min_column);
11686 (position..position, first_prefix.clone())
11687 }));
11688 }
11689 } else if let Some((full_comment_prefix, comment_suffix)) =
11690 language.block_comment_delimiters()
11691 {
11692 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11693 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11694 let prefix_range = comment_prefix_range(
11695 snapshot.deref(),
11696 start_row,
11697 comment_prefix,
11698 comment_prefix_whitespace,
11699 ignore_indent,
11700 );
11701 let suffix_range = comment_suffix_range(
11702 snapshot.deref(),
11703 end_row,
11704 comment_suffix.trim_start_matches(' '),
11705 comment_suffix.starts_with(' '),
11706 );
11707
11708 if prefix_range.is_empty() || suffix_range.is_empty() {
11709 edits.push((
11710 prefix_range.start..prefix_range.start,
11711 full_comment_prefix.clone(),
11712 ));
11713 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11714 suffixes_inserted.push((end_row, comment_suffix.len()));
11715 } else {
11716 edits.push((prefix_range, empty_str.clone()));
11717 edits.push((suffix_range, empty_str.clone()));
11718 }
11719 } else {
11720 continue;
11721 }
11722 }
11723
11724 drop(snapshot);
11725 this.buffer.update(cx, |buffer, cx| {
11726 buffer.edit(edits, None, cx);
11727 });
11728
11729 // Adjust selections so that they end before any comment suffixes that
11730 // were inserted.
11731 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11732 let mut selections = this.selections.all::<Point>(cx);
11733 let snapshot = this.buffer.read(cx).read(cx);
11734 for selection in &mut selections {
11735 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11736 match row.cmp(&MultiBufferRow(selection.end.row)) {
11737 Ordering::Less => {
11738 suffixes_inserted.next();
11739 continue;
11740 }
11741 Ordering::Greater => break,
11742 Ordering::Equal => {
11743 if selection.end.column == snapshot.line_len(row) {
11744 if selection.is_empty() {
11745 selection.start.column -= suffix_len as u32;
11746 }
11747 selection.end.column -= suffix_len as u32;
11748 }
11749 break;
11750 }
11751 }
11752 }
11753 }
11754
11755 drop(snapshot);
11756 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11757 s.select(selections)
11758 });
11759
11760 let selections = this.selections.all::<Point>(cx);
11761 let selections_on_single_row = selections.windows(2).all(|selections| {
11762 selections[0].start.row == selections[1].start.row
11763 && selections[0].end.row == selections[1].end.row
11764 && selections[0].start.row == selections[0].end.row
11765 });
11766 let selections_selecting = selections
11767 .iter()
11768 .any(|selection| selection.start != selection.end);
11769 let advance_downwards = action.advance_downwards
11770 && selections_on_single_row
11771 && !selections_selecting
11772 && !matches!(this.mode, EditorMode::SingleLine { .. });
11773
11774 if advance_downwards {
11775 let snapshot = this.buffer.read(cx).snapshot(cx);
11776
11777 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11778 s.move_cursors_with(|display_snapshot, display_point, _| {
11779 let mut point = display_point.to_point(display_snapshot);
11780 point.row += 1;
11781 point = snapshot.clip_point(point, Bias::Left);
11782 let display_point = point.to_display_point(display_snapshot);
11783 let goal = SelectionGoal::HorizontalPosition(
11784 display_snapshot
11785 .x_for_display_point(display_point, text_layout_details)
11786 .into(),
11787 );
11788 (display_point, goal)
11789 })
11790 });
11791 }
11792 });
11793 }
11794
11795 pub fn select_enclosing_symbol(
11796 &mut self,
11797 _: &SelectEnclosingSymbol,
11798 window: &mut Window,
11799 cx: &mut Context<Self>,
11800 ) {
11801 let buffer = self.buffer.read(cx).snapshot(cx);
11802 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11803
11804 fn update_selection(
11805 selection: &Selection<usize>,
11806 buffer_snap: &MultiBufferSnapshot,
11807 ) -> Option<Selection<usize>> {
11808 let cursor = selection.head();
11809 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11810 for symbol in symbols.iter().rev() {
11811 let start = symbol.range.start.to_offset(buffer_snap);
11812 let end = symbol.range.end.to_offset(buffer_snap);
11813 let new_range = start..end;
11814 if start < selection.start || end > selection.end {
11815 return Some(Selection {
11816 id: selection.id,
11817 start: new_range.start,
11818 end: new_range.end,
11819 goal: SelectionGoal::None,
11820 reversed: selection.reversed,
11821 });
11822 }
11823 }
11824 None
11825 }
11826
11827 let mut selected_larger_symbol = false;
11828 let new_selections = old_selections
11829 .iter()
11830 .map(|selection| match update_selection(selection, &buffer) {
11831 Some(new_selection) => {
11832 if new_selection.range() != selection.range() {
11833 selected_larger_symbol = true;
11834 }
11835 new_selection
11836 }
11837 None => selection.clone(),
11838 })
11839 .collect::<Vec<_>>();
11840
11841 if selected_larger_symbol {
11842 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11843 s.select(new_selections);
11844 });
11845 }
11846 }
11847
11848 pub fn select_larger_syntax_node(
11849 &mut self,
11850 _: &SelectLargerSyntaxNode,
11851 window: &mut Window,
11852 cx: &mut Context<Self>,
11853 ) {
11854 let Some(visible_row_count) = self.visible_row_count() else {
11855 return;
11856 };
11857 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
11858 if old_selections.is_empty() {
11859 return;
11860 }
11861
11862 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11863 let buffer = self.buffer.read(cx).snapshot(cx);
11864
11865 let mut selected_larger_node = false;
11866 let mut new_selections = old_selections
11867 .iter()
11868 .map(|selection| {
11869 let old_range = selection.start..selection.end;
11870 let mut new_range = old_range.clone();
11871 let mut new_node = None;
11872 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11873 {
11874 new_node = Some(node);
11875 new_range = match containing_range {
11876 MultiOrSingleBufferOffsetRange::Single(_) => break,
11877 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11878 };
11879 if !display_map.intersects_fold(new_range.start)
11880 && !display_map.intersects_fold(new_range.end)
11881 {
11882 break;
11883 }
11884 }
11885
11886 if let Some(node) = new_node {
11887 // Log the ancestor, to support using this action as a way to explore TreeSitter
11888 // nodes. Parent and grandparent are also logged because this operation will not
11889 // visit nodes that have the same range as their parent.
11890 log::info!("Node: {node:?}");
11891 let parent = node.parent();
11892 log::info!("Parent: {parent:?}");
11893 let grandparent = parent.and_then(|x| x.parent());
11894 log::info!("Grandparent: {grandparent:?}");
11895 }
11896
11897 selected_larger_node |= new_range != old_range;
11898 Selection {
11899 id: selection.id,
11900 start: new_range.start,
11901 end: new_range.end,
11902 goal: SelectionGoal::None,
11903 reversed: selection.reversed,
11904 }
11905 })
11906 .collect::<Vec<_>>();
11907
11908 if !selected_larger_node {
11909 return; // don't put this call in the history
11910 }
11911
11912 // scroll based on transformation done to the last selection created by the user
11913 let (last_old, last_new) = old_selections
11914 .last()
11915 .zip(new_selections.last().cloned())
11916 .expect("old_selections isn't empty");
11917
11918 // revert selection
11919 let is_selection_reversed = {
11920 let should_newest_selection_be_reversed = last_old.start != last_new.start;
11921 new_selections.last_mut().expect("checked above").reversed =
11922 should_newest_selection_be_reversed;
11923 should_newest_selection_be_reversed
11924 };
11925
11926 if selected_larger_node {
11927 self.select_syntax_node_history.disable_clearing = true;
11928 self.change_selections(None, window, cx, |s| {
11929 s.select(new_selections.clone());
11930 });
11931 self.select_syntax_node_history.disable_clearing = false;
11932 }
11933
11934 let start_row = last_new.start.to_display_point(&display_map).row().0;
11935 let end_row = last_new.end.to_display_point(&display_map).row().0;
11936 let selection_height = end_row - start_row + 1;
11937 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
11938
11939 // if fits on screen (considering margin), keep it in the middle, else, scroll to selection head
11940 let scroll_behavior = if visible_row_count >= selection_height + scroll_margin_rows * 2 {
11941 let middle_row = (end_row + start_row) / 2;
11942 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
11943 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
11944 SelectSyntaxNodeScrollBehavior::CenterSelection
11945 } else if is_selection_reversed {
11946 self.scroll_cursor_top(&Default::default(), window, cx);
11947 SelectSyntaxNodeScrollBehavior::CursorTop
11948 } else {
11949 self.scroll_cursor_bottom(&Default::default(), window, cx);
11950 SelectSyntaxNodeScrollBehavior::CursorBottom
11951 };
11952
11953 self.select_syntax_node_history.push((
11954 old_selections,
11955 scroll_behavior,
11956 is_selection_reversed,
11957 ));
11958 }
11959
11960 pub fn select_smaller_syntax_node(
11961 &mut self,
11962 _: &SelectSmallerSyntaxNode,
11963 window: &mut Window,
11964 cx: &mut Context<Self>,
11965 ) {
11966 let Some(visible_row_count) = self.visible_row_count() else {
11967 return;
11968 };
11969
11970 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
11971 self.select_syntax_node_history.pop()
11972 {
11973 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11974
11975 if let Some(selection) = selections.last_mut() {
11976 selection.reversed = is_selection_reversed;
11977 }
11978
11979 self.select_syntax_node_history.disable_clearing = true;
11980 self.change_selections(None, window, cx, |s| {
11981 s.select(selections.to_vec());
11982 });
11983 self.select_syntax_node_history.disable_clearing = false;
11984
11985 let newest = self.selections.newest::<usize>(cx);
11986 let start_row = newest.start.to_display_point(&display_map).row().0;
11987 let end_row = newest.end.to_display_point(&display_map).row().0;
11988
11989 match scroll_behavior {
11990 SelectSyntaxNodeScrollBehavior::CursorTop => {
11991 self.scroll_cursor_top(&Default::default(), window, cx);
11992 }
11993 SelectSyntaxNodeScrollBehavior::CenterSelection => {
11994 let middle_row = (end_row + start_row) / 2;
11995 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
11996 // centralize the selection, not the cursor
11997 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
11998 }
11999 SelectSyntaxNodeScrollBehavior::CursorBottom => {
12000 self.scroll_cursor_bottom(&Default::default(), window, cx);
12001 }
12002 }
12003 }
12004 }
12005
12006 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
12007 if !EditorSettings::get_global(cx).gutter.runnables {
12008 self.clear_tasks();
12009 return Task::ready(());
12010 }
12011 let project = self.project.as_ref().map(Entity::downgrade);
12012 cx.spawn_in(window, async move |this, cx| {
12013 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
12014 let Some(project) = project.and_then(|p| p.upgrade()) else {
12015 return;
12016 };
12017 let Ok(display_snapshot) = this.update(cx, |this, cx| {
12018 this.display_map.update(cx, |map, cx| map.snapshot(cx))
12019 }) else {
12020 return;
12021 };
12022
12023 let hide_runnables = project
12024 .update(cx, |project, cx| {
12025 // Do not display any test indicators in non-dev server remote projects.
12026 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
12027 })
12028 .unwrap_or(true);
12029 if hide_runnables {
12030 return;
12031 }
12032 let new_rows =
12033 cx.background_spawn({
12034 let snapshot = display_snapshot.clone();
12035 async move {
12036 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
12037 }
12038 })
12039 .await;
12040
12041 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
12042 this.update(cx, |this, _| {
12043 this.clear_tasks();
12044 for (key, value) in rows {
12045 this.insert_tasks(key, value);
12046 }
12047 })
12048 .ok();
12049 })
12050 }
12051 fn fetch_runnable_ranges(
12052 snapshot: &DisplaySnapshot,
12053 range: Range<Anchor>,
12054 ) -> Vec<language::RunnableRange> {
12055 snapshot.buffer_snapshot.runnable_ranges(range).collect()
12056 }
12057
12058 fn runnable_rows(
12059 project: Entity<Project>,
12060 snapshot: DisplaySnapshot,
12061 runnable_ranges: Vec<RunnableRange>,
12062 mut cx: AsyncWindowContext,
12063 ) -> Vec<((BufferId, u32), RunnableTasks)> {
12064 runnable_ranges
12065 .into_iter()
12066 .filter_map(|mut runnable| {
12067 let tasks = cx
12068 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
12069 .ok()?;
12070 if tasks.is_empty() {
12071 return None;
12072 }
12073
12074 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
12075
12076 let row = snapshot
12077 .buffer_snapshot
12078 .buffer_line_for_row(MultiBufferRow(point.row))?
12079 .1
12080 .start
12081 .row;
12082
12083 let context_range =
12084 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
12085 Some((
12086 (runnable.buffer_id, row),
12087 RunnableTasks {
12088 templates: tasks,
12089 offset: snapshot
12090 .buffer_snapshot
12091 .anchor_before(runnable.run_range.start),
12092 context_range,
12093 column: point.column,
12094 extra_variables: runnable.extra_captures,
12095 },
12096 ))
12097 })
12098 .collect()
12099 }
12100
12101 fn templates_with_tags(
12102 project: &Entity<Project>,
12103 runnable: &mut Runnable,
12104 cx: &mut App,
12105 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
12106 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
12107 let (worktree_id, file) = project
12108 .buffer_for_id(runnable.buffer, cx)
12109 .and_then(|buffer| buffer.read(cx).file())
12110 .map(|file| (file.worktree_id(cx), file.clone()))
12111 .unzip();
12112
12113 (
12114 project.task_store().read(cx).task_inventory().cloned(),
12115 worktree_id,
12116 file,
12117 )
12118 });
12119
12120 let tags = mem::take(&mut runnable.tags);
12121 let mut tags: Vec<_> = tags
12122 .into_iter()
12123 .flat_map(|tag| {
12124 let tag = tag.0.clone();
12125 inventory
12126 .as_ref()
12127 .into_iter()
12128 .flat_map(|inventory| {
12129 inventory.read(cx).list_tasks(
12130 file.clone(),
12131 Some(runnable.language.clone()),
12132 worktree_id,
12133 cx,
12134 )
12135 })
12136 .filter(move |(_, template)| {
12137 template.tags.iter().any(|source_tag| source_tag == &tag)
12138 })
12139 })
12140 .sorted_by_key(|(kind, _)| kind.to_owned())
12141 .collect();
12142 if let Some((leading_tag_source, _)) = tags.first() {
12143 // Strongest source wins; if we have worktree tag binding, prefer that to
12144 // global and language bindings;
12145 // if we have a global binding, prefer that to language binding.
12146 let first_mismatch = tags
12147 .iter()
12148 .position(|(tag_source, _)| tag_source != leading_tag_source);
12149 if let Some(index) = first_mismatch {
12150 tags.truncate(index);
12151 }
12152 }
12153
12154 tags
12155 }
12156
12157 pub fn move_to_enclosing_bracket(
12158 &mut self,
12159 _: &MoveToEnclosingBracket,
12160 window: &mut Window,
12161 cx: &mut Context<Self>,
12162 ) {
12163 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12164 s.move_offsets_with(|snapshot, selection| {
12165 let Some(enclosing_bracket_ranges) =
12166 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12167 else {
12168 return;
12169 };
12170
12171 let mut best_length = usize::MAX;
12172 let mut best_inside = false;
12173 let mut best_in_bracket_range = false;
12174 let mut best_destination = None;
12175 for (open, close) in enclosing_bracket_ranges {
12176 let close = close.to_inclusive();
12177 let length = close.end() - open.start;
12178 let inside = selection.start >= open.end && selection.end <= *close.start();
12179 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12180 || close.contains(&selection.head());
12181
12182 // If best is next to a bracket and current isn't, skip
12183 if !in_bracket_range && best_in_bracket_range {
12184 continue;
12185 }
12186
12187 // Prefer smaller lengths unless best is inside and current isn't
12188 if length > best_length && (best_inside || !inside) {
12189 continue;
12190 }
12191
12192 best_length = length;
12193 best_inside = inside;
12194 best_in_bracket_range = in_bracket_range;
12195 best_destination = Some(
12196 if close.contains(&selection.start) && close.contains(&selection.end) {
12197 if inside {
12198 open.end
12199 } else {
12200 open.start
12201 }
12202 } else if inside {
12203 *close.start()
12204 } else {
12205 *close.end()
12206 },
12207 );
12208 }
12209
12210 if let Some(destination) = best_destination {
12211 selection.collapse_to(destination, SelectionGoal::None);
12212 }
12213 })
12214 });
12215 }
12216
12217 pub fn undo_selection(
12218 &mut self,
12219 _: &UndoSelection,
12220 window: &mut Window,
12221 cx: &mut Context<Self>,
12222 ) {
12223 self.end_selection(window, cx);
12224 self.selection_history.mode = SelectionHistoryMode::Undoing;
12225 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12226 self.change_selections(None, window, cx, |s| {
12227 s.select_anchors(entry.selections.to_vec())
12228 });
12229 self.select_next_state = entry.select_next_state;
12230 self.select_prev_state = entry.select_prev_state;
12231 self.add_selections_state = entry.add_selections_state;
12232 self.request_autoscroll(Autoscroll::newest(), cx);
12233 }
12234 self.selection_history.mode = SelectionHistoryMode::Normal;
12235 }
12236
12237 pub fn redo_selection(
12238 &mut self,
12239 _: &RedoSelection,
12240 window: &mut Window,
12241 cx: &mut Context<Self>,
12242 ) {
12243 self.end_selection(window, cx);
12244 self.selection_history.mode = SelectionHistoryMode::Redoing;
12245 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12246 self.change_selections(None, window, cx, |s| {
12247 s.select_anchors(entry.selections.to_vec())
12248 });
12249 self.select_next_state = entry.select_next_state;
12250 self.select_prev_state = entry.select_prev_state;
12251 self.add_selections_state = entry.add_selections_state;
12252 self.request_autoscroll(Autoscroll::newest(), cx);
12253 }
12254 self.selection_history.mode = SelectionHistoryMode::Normal;
12255 }
12256
12257 pub fn expand_excerpts(
12258 &mut self,
12259 action: &ExpandExcerpts,
12260 _: &mut Window,
12261 cx: &mut Context<Self>,
12262 ) {
12263 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12264 }
12265
12266 pub fn expand_excerpts_down(
12267 &mut self,
12268 action: &ExpandExcerptsDown,
12269 _: &mut Window,
12270 cx: &mut Context<Self>,
12271 ) {
12272 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12273 }
12274
12275 pub fn expand_excerpts_up(
12276 &mut self,
12277 action: &ExpandExcerptsUp,
12278 _: &mut Window,
12279 cx: &mut Context<Self>,
12280 ) {
12281 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12282 }
12283
12284 pub fn expand_excerpts_for_direction(
12285 &mut self,
12286 lines: u32,
12287 direction: ExpandExcerptDirection,
12288
12289 cx: &mut Context<Self>,
12290 ) {
12291 let selections = self.selections.disjoint_anchors();
12292
12293 let lines = if lines == 0 {
12294 EditorSettings::get_global(cx).expand_excerpt_lines
12295 } else {
12296 lines
12297 };
12298
12299 self.buffer.update(cx, |buffer, cx| {
12300 let snapshot = buffer.snapshot(cx);
12301 let mut excerpt_ids = selections
12302 .iter()
12303 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12304 .collect::<Vec<_>>();
12305 excerpt_ids.sort();
12306 excerpt_ids.dedup();
12307 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12308 })
12309 }
12310
12311 pub fn expand_excerpt(
12312 &mut self,
12313 excerpt: ExcerptId,
12314 direction: ExpandExcerptDirection,
12315 window: &mut Window,
12316 cx: &mut Context<Self>,
12317 ) {
12318 let current_scroll_position = self.scroll_position(cx);
12319 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12320 self.buffer.update(cx, |buffer, cx| {
12321 buffer.expand_excerpts([excerpt], lines, direction, cx)
12322 });
12323 if direction == ExpandExcerptDirection::Down {
12324 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12325 self.set_scroll_position(new_scroll_position, window, cx);
12326 }
12327 }
12328
12329 pub fn go_to_singleton_buffer_point(
12330 &mut self,
12331 point: Point,
12332 window: &mut Window,
12333 cx: &mut Context<Self>,
12334 ) {
12335 self.go_to_singleton_buffer_range(point..point, window, cx);
12336 }
12337
12338 pub fn go_to_singleton_buffer_range(
12339 &mut self,
12340 range: Range<Point>,
12341 window: &mut Window,
12342 cx: &mut Context<Self>,
12343 ) {
12344 let multibuffer = self.buffer().read(cx);
12345 let Some(buffer) = multibuffer.as_singleton() else {
12346 return;
12347 };
12348 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12349 return;
12350 };
12351 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12352 return;
12353 };
12354 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12355 s.select_anchor_ranges([start..end])
12356 });
12357 }
12358
12359 fn go_to_diagnostic(
12360 &mut self,
12361 _: &GoToDiagnostic,
12362 window: &mut Window,
12363 cx: &mut Context<Self>,
12364 ) {
12365 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12366 }
12367
12368 fn go_to_prev_diagnostic(
12369 &mut self,
12370 _: &GoToPreviousDiagnostic,
12371 window: &mut Window,
12372 cx: &mut Context<Self>,
12373 ) {
12374 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12375 }
12376
12377 pub fn go_to_diagnostic_impl(
12378 &mut self,
12379 direction: Direction,
12380 window: &mut Window,
12381 cx: &mut Context<Self>,
12382 ) {
12383 let buffer = self.buffer.read(cx).snapshot(cx);
12384 let selection = self.selections.newest::<usize>(cx);
12385
12386 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12387 if direction == Direction::Next {
12388 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12389 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12390 return;
12391 };
12392 self.activate_diagnostics(
12393 buffer_id,
12394 popover.local_diagnostic.diagnostic.group_id,
12395 window,
12396 cx,
12397 );
12398 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12399 let primary_range_start = active_diagnostics.primary_range.start;
12400 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12401 let mut new_selection = s.newest_anchor().clone();
12402 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12403 s.select_anchors(vec![new_selection.clone()]);
12404 });
12405 self.refresh_inline_completion(false, true, window, cx);
12406 }
12407 return;
12408 }
12409 }
12410
12411 let active_group_id = self
12412 .active_diagnostics
12413 .as_ref()
12414 .map(|active_group| active_group.group_id);
12415 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12416 active_diagnostics
12417 .primary_range
12418 .to_offset(&buffer)
12419 .to_inclusive()
12420 });
12421 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12422 if active_primary_range.contains(&selection.head()) {
12423 *active_primary_range.start()
12424 } else {
12425 selection.head()
12426 }
12427 } else {
12428 selection.head()
12429 };
12430
12431 let snapshot = self.snapshot(window, cx);
12432 let primary_diagnostics_before = buffer
12433 .diagnostics_in_range::<usize>(0..search_start)
12434 .filter(|entry| entry.diagnostic.is_primary)
12435 .filter(|entry| entry.range.start != entry.range.end)
12436 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12437 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12438 .collect::<Vec<_>>();
12439 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12440 primary_diagnostics_before
12441 .iter()
12442 .position(|entry| entry.diagnostic.group_id == active_group_id)
12443 });
12444
12445 let primary_diagnostics_after = buffer
12446 .diagnostics_in_range::<usize>(search_start..buffer.len())
12447 .filter(|entry| entry.diagnostic.is_primary)
12448 .filter(|entry| entry.range.start != entry.range.end)
12449 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12450 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12451 .collect::<Vec<_>>();
12452 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12453 primary_diagnostics_after
12454 .iter()
12455 .enumerate()
12456 .rev()
12457 .find_map(|(i, entry)| {
12458 if entry.diagnostic.group_id == active_group_id {
12459 Some(i)
12460 } else {
12461 None
12462 }
12463 })
12464 });
12465
12466 let next_primary_diagnostic = match direction {
12467 Direction::Prev => primary_diagnostics_before
12468 .iter()
12469 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12470 .rev()
12471 .next(),
12472 Direction::Next => primary_diagnostics_after
12473 .iter()
12474 .skip(
12475 last_same_group_diagnostic_after
12476 .map(|index| index + 1)
12477 .unwrap_or(0),
12478 )
12479 .next(),
12480 };
12481
12482 // Cycle around to the start of the buffer, potentially moving back to the start of
12483 // the currently active diagnostic.
12484 let cycle_around = || match direction {
12485 Direction::Prev => primary_diagnostics_after
12486 .iter()
12487 .rev()
12488 .chain(primary_diagnostics_before.iter().rev())
12489 .next(),
12490 Direction::Next => primary_diagnostics_before
12491 .iter()
12492 .chain(primary_diagnostics_after.iter())
12493 .next(),
12494 };
12495
12496 if let Some((primary_range, group_id)) = next_primary_diagnostic
12497 .or_else(cycle_around)
12498 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12499 {
12500 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12501 return;
12502 };
12503 self.activate_diagnostics(buffer_id, group_id, window, cx);
12504 if self.active_diagnostics.is_some() {
12505 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12506 s.select(vec![Selection {
12507 id: selection.id,
12508 start: primary_range.start,
12509 end: primary_range.start,
12510 reversed: false,
12511 goal: SelectionGoal::None,
12512 }]);
12513 });
12514 self.refresh_inline_completion(false, true, window, cx);
12515 }
12516 }
12517 }
12518
12519 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12520 let snapshot = self.snapshot(window, cx);
12521 let selection = self.selections.newest::<Point>(cx);
12522 self.go_to_hunk_before_or_after_position(
12523 &snapshot,
12524 selection.head(),
12525 Direction::Next,
12526 window,
12527 cx,
12528 );
12529 }
12530
12531 fn go_to_hunk_before_or_after_position(
12532 &mut self,
12533 snapshot: &EditorSnapshot,
12534 position: Point,
12535 direction: Direction,
12536 window: &mut Window,
12537 cx: &mut Context<Editor>,
12538 ) {
12539 let row = if direction == Direction::Next {
12540 self.hunk_after_position(snapshot, position)
12541 .map(|hunk| hunk.row_range.start)
12542 } else {
12543 self.hunk_before_position(snapshot, position)
12544 };
12545
12546 if let Some(row) = row {
12547 let destination = Point::new(row.0, 0);
12548 let autoscroll = Autoscroll::center();
12549
12550 self.unfold_ranges(&[destination..destination], false, false, cx);
12551 self.change_selections(Some(autoscroll), window, cx, |s| {
12552 s.select_ranges([destination..destination]);
12553 });
12554 }
12555 }
12556
12557 fn hunk_after_position(
12558 &mut self,
12559 snapshot: &EditorSnapshot,
12560 position: Point,
12561 ) -> Option<MultiBufferDiffHunk> {
12562 snapshot
12563 .buffer_snapshot
12564 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12565 .find(|hunk| hunk.row_range.start.0 > position.row)
12566 .or_else(|| {
12567 snapshot
12568 .buffer_snapshot
12569 .diff_hunks_in_range(Point::zero()..position)
12570 .find(|hunk| hunk.row_range.end.0 < position.row)
12571 })
12572 }
12573
12574 fn go_to_prev_hunk(
12575 &mut self,
12576 _: &GoToPreviousHunk,
12577 window: &mut Window,
12578 cx: &mut Context<Self>,
12579 ) {
12580 let snapshot = self.snapshot(window, cx);
12581 let selection = self.selections.newest::<Point>(cx);
12582 self.go_to_hunk_before_or_after_position(
12583 &snapshot,
12584 selection.head(),
12585 Direction::Prev,
12586 window,
12587 cx,
12588 );
12589 }
12590
12591 fn hunk_before_position(
12592 &mut self,
12593 snapshot: &EditorSnapshot,
12594 position: Point,
12595 ) -> Option<MultiBufferRow> {
12596 snapshot
12597 .buffer_snapshot
12598 .diff_hunk_before(position)
12599 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12600 }
12601
12602 fn go_to_line<T: 'static>(
12603 &mut self,
12604 position: Anchor,
12605 highlight_color: Option<Hsla>,
12606 window: &mut Window,
12607 cx: &mut Context<Self>,
12608 ) {
12609 let snapshot = self.snapshot(window, cx).display_snapshot;
12610 let position = position.to_point(&snapshot.buffer_snapshot);
12611 let start = snapshot
12612 .buffer_snapshot
12613 .clip_point(Point::new(position.row, 0), Bias::Left);
12614 let end = start + Point::new(1, 0);
12615 let start = snapshot.buffer_snapshot.anchor_before(start);
12616 let end = snapshot.buffer_snapshot.anchor_before(end);
12617
12618 self.clear_row_highlights::<T>();
12619 self.highlight_rows::<T>(
12620 start..end,
12621 highlight_color
12622 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12623 true,
12624 cx,
12625 );
12626 self.request_autoscroll(Autoscroll::center(), cx);
12627 }
12628
12629 pub fn go_to_definition(
12630 &mut self,
12631 _: &GoToDefinition,
12632 window: &mut Window,
12633 cx: &mut Context<Self>,
12634 ) -> Task<Result<Navigated>> {
12635 let definition =
12636 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12637 cx.spawn_in(window, async move |editor, cx| {
12638 if definition.await? == Navigated::Yes {
12639 return Ok(Navigated::Yes);
12640 }
12641 match editor.update_in(cx, |editor, window, cx| {
12642 editor.find_all_references(&FindAllReferences, window, cx)
12643 })? {
12644 Some(references) => references.await,
12645 None => Ok(Navigated::No),
12646 }
12647 })
12648 }
12649
12650 pub fn go_to_declaration(
12651 &mut self,
12652 _: &GoToDeclaration,
12653 window: &mut Window,
12654 cx: &mut Context<Self>,
12655 ) -> Task<Result<Navigated>> {
12656 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12657 }
12658
12659 pub fn go_to_declaration_split(
12660 &mut self,
12661 _: &GoToDeclaration,
12662 window: &mut Window,
12663 cx: &mut Context<Self>,
12664 ) -> Task<Result<Navigated>> {
12665 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12666 }
12667
12668 pub fn go_to_implementation(
12669 &mut self,
12670 _: &GoToImplementation,
12671 window: &mut Window,
12672 cx: &mut Context<Self>,
12673 ) -> Task<Result<Navigated>> {
12674 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12675 }
12676
12677 pub fn go_to_implementation_split(
12678 &mut self,
12679 _: &GoToImplementationSplit,
12680 window: &mut Window,
12681 cx: &mut Context<Self>,
12682 ) -> Task<Result<Navigated>> {
12683 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12684 }
12685
12686 pub fn go_to_type_definition(
12687 &mut self,
12688 _: &GoToTypeDefinition,
12689 window: &mut Window,
12690 cx: &mut Context<Self>,
12691 ) -> Task<Result<Navigated>> {
12692 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12693 }
12694
12695 pub fn go_to_definition_split(
12696 &mut self,
12697 _: &GoToDefinitionSplit,
12698 window: &mut Window,
12699 cx: &mut Context<Self>,
12700 ) -> Task<Result<Navigated>> {
12701 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12702 }
12703
12704 pub fn go_to_type_definition_split(
12705 &mut self,
12706 _: &GoToTypeDefinitionSplit,
12707 window: &mut Window,
12708 cx: &mut Context<Self>,
12709 ) -> Task<Result<Navigated>> {
12710 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12711 }
12712
12713 fn go_to_definition_of_kind(
12714 &mut self,
12715 kind: GotoDefinitionKind,
12716 split: bool,
12717 window: &mut Window,
12718 cx: &mut Context<Self>,
12719 ) -> Task<Result<Navigated>> {
12720 let Some(provider) = self.semantics_provider.clone() else {
12721 return Task::ready(Ok(Navigated::No));
12722 };
12723 let head = self.selections.newest::<usize>(cx).head();
12724 let buffer = self.buffer.read(cx);
12725 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12726 text_anchor
12727 } else {
12728 return Task::ready(Ok(Navigated::No));
12729 };
12730
12731 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12732 return Task::ready(Ok(Navigated::No));
12733 };
12734
12735 cx.spawn_in(window, async move |editor, cx| {
12736 let definitions = definitions.await?;
12737 let navigated = editor
12738 .update_in(cx, |editor, window, cx| {
12739 editor.navigate_to_hover_links(
12740 Some(kind),
12741 definitions
12742 .into_iter()
12743 .filter(|location| {
12744 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12745 })
12746 .map(HoverLink::Text)
12747 .collect::<Vec<_>>(),
12748 split,
12749 window,
12750 cx,
12751 )
12752 })?
12753 .await?;
12754 anyhow::Ok(navigated)
12755 })
12756 }
12757
12758 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12759 let selection = self.selections.newest_anchor();
12760 let head = selection.head();
12761 let tail = selection.tail();
12762
12763 let Some((buffer, start_position)) =
12764 self.buffer.read(cx).text_anchor_for_position(head, cx)
12765 else {
12766 return;
12767 };
12768
12769 let end_position = if head != tail {
12770 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12771 return;
12772 };
12773 Some(pos)
12774 } else {
12775 None
12776 };
12777
12778 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12779 let url = if let Some(end_pos) = end_position {
12780 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12781 } else {
12782 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12783 };
12784
12785 if let Some(url) = url {
12786 editor.update(cx, |_, cx| {
12787 cx.open_url(&url);
12788 })
12789 } else {
12790 Ok(())
12791 }
12792 });
12793
12794 url_finder.detach();
12795 }
12796
12797 pub fn open_selected_filename(
12798 &mut self,
12799 _: &OpenSelectedFilename,
12800 window: &mut Window,
12801 cx: &mut Context<Self>,
12802 ) {
12803 let Some(workspace) = self.workspace() else {
12804 return;
12805 };
12806
12807 let position = self.selections.newest_anchor().head();
12808
12809 let Some((buffer, buffer_position)) =
12810 self.buffer.read(cx).text_anchor_for_position(position, cx)
12811 else {
12812 return;
12813 };
12814
12815 let project = self.project.clone();
12816
12817 cx.spawn_in(window, async move |_, cx| {
12818 let result = find_file(&buffer, project, buffer_position, cx).await;
12819
12820 if let Some((_, path)) = result {
12821 workspace
12822 .update_in(cx, |workspace, window, cx| {
12823 workspace.open_resolved_path(path, window, cx)
12824 })?
12825 .await?;
12826 }
12827 anyhow::Ok(())
12828 })
12829 .detach();
12830 }
12831
12832 pub(crate) fn navigate_to_hover_links(
12833 &mut self,
12834 kind: Option<GotoDefinitionKind>,
12835 mut definitions: Vec<HoverLink>,
12836 split: bool,
12837 window: &mut Window,
12838 cx: &mut Context<Editor>,
12839 ) -> Task<Result<Navigated>> {
12840 // If there is one definition, just open it directly
12841 if definitions.len() == 1 {
12842 let definition = definitions.pop().unwrap();
12843
12844 enum TargetTaskResult {
12845 Location(Option<Location>),
12846 AlreadyNavigated,
12847 }
12848
12849 let target_task = match definition {
12850 HoverLink::Text(link) => {
12851 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12852 }
12853 HoverLink::InlayHint(lsp_location, server_id) => {
12854 let computation =
12855 self.compute_target_location(lsp_location, server_id, window, cx);
12856 cx.background_spawn(async move {
12857 let location = computation.await?;
12858 Ok(TargetTaskResult::Location(location))
12859 })
12860 }
12861 HoverLink::Url(url) => {
12862 cx.open_url(&url);
12863 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12864 }
12865 HoverLink::File(path) => {
12866 if let Some(workspace) = self.workspace() {
12867 cx.spawn_in(window, async move |_, cx| {
12868 workspace
12869 .update_in(cx, |workspace, window, cx| {
12870 workspace.open_resolved_path(path, window, cx)
12871 })?
12872 .await
12873 .map(|_| TargetTaskResult::AlreadyNavigated)
12874 })
12875 } else {
12876 Task::ready(Ok(TargetTaskResult::Location(None)))
12877 }
12878 }
12879 };
12880 cx.spawn_in(window, async move |editor, cx| {
12881 let target = match target_task.await.context("target resolution task")? {
12882 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12883 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12884 TargetTaskResult::Location(Some(target)) => target,
12885 };
12886
12887 editor.update_in(cx, |editor, window, cx| {
12888 let Some(workspace) = editor.workspace() else {
12889 return Navigated::No;
12890 };
12891 let pane = workspace.read(cx).active_pane().clone();
12892
12893 let range = target.range.to_point(target.buffer.read(cx));
12894 let range = editor.range_for_match(&range);
12895 let range = collapse_multiline_range(range);
12896
12897 if !split
12898 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12899 {
12900 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12901 } else {
12902 window.defer(cx, move |window, cx| {
12903 let target_editor: Entity<Self> =
12904 workspace.update(cx, |workspace, cx| {
12905 let pane = if split {
12906 workspace.adjacent_pane(window, cx)
12907 } else {
12908 workspace.active_pane().clone()
12909 };
12910
12911 workspace.open_project_item(
12912 pane,
12913 target.buffer.clone(),
12914 true,
12915 true,
12916 window,
12917 cx,
12918 )
12919 });
12920 target_editor.update(cx, |target_editor, cx| {
12921 // When selecting a definition in a different buffer, disable the nav history
12922 // to avoid creating a history entry at the previous cursor location.
12923 pane.update(cx, |pane, _| pane.disable_history());
12924 target_editor.go_to_singleton_buffer_range(range, window, cx);
12925 pane.update(cx, |pane, _| pane.enable_history());
12926 });
12927 });
12928 }
12929 Navigated::Yes
12930 })
12931 })
12932 } else if !definitions.is_empty() {
12933 cx.spawn_in(window, async move |editor, cx| {
12934 let (title, location_tasks, workspace) = editor
12935 .update_in(cx, |editor, window, cx| {
12936 let tab_kind = match kind {
12937 Some(GotoDefinitionKind::Implementation) => "Implementations",
12938 _ => "Definitions",
12939 };
12940 let title = definitions
12941 .iter()
12942 .find_map(|definition| match definition {
12943 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12944 let buffer = origin.buffer.read(cx);
12945 format!(
12946 "{} for {}",
12947 tab_kind,
12948 buffer
12949 .text_for_range(origin.range.clone())
12950 .collect::<String>()
12951 )
12952 }),
12953 HoverLink::InlayHint(_, _) => None,
12954 HoverLink::Url(_) => None,
12955 HoverLink::File(_) => None,
12956 })
12957 .unwrap_or(tab_kind.to_string());
12958 let location_tasks = definitions
12959 .into_iter()
12960 .map(|definition| match definition {
12961 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12962 HoverLink::InlayHint(lsp_location, server_id) => editor
12963 .compute_target_location(lsp_location, server_id, window, cx),
12964 HoverLink::Url(_) => Task::ready(Ok(None)),
12965 HoverLink::File(_) => Task::ready(Ok(None)),
12966 })
12967 .collect::<Vec<_>>();
12968 (title, location_tasks, editor.workspace().clone())
12969 })
12970 .context("location tasks preparation")?;
12971
12972 let locations = future::join_all(location_tasks)
12973 .await
12974 .into_iter()
12975 .filter_map(|location| location.transpose())
12976 .collect::<Result<_>>()
12977 .context("location tasks")?;
12978
12979 let Some(workspace) = workspace else {
12980 return Ok(Navigated::No);
12981 };
12982 let opened = workspace
12983 .update_in(cx, |workspace, window, cx| {
12984 Self::open_locations_in_multibuffer(
12985 workspace,
12986 locations,
12987 title,
12988 split,
12989 MultibufferSelectionMode::First,
12990 window,
12991 cx,
12992 )
12993 })
12994 .ok();
12995
12996 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12997 })
12998 } else {
12999 Task::ready(Ok(Navigated::No))
13000 }
13001 }
13002
13003 fn compute_target_location(
13004 &self,
13005 lsp_location: lsp::Location,
13006 server_id: LanguageServerId,
13007 window: &mut Window,
13008 cx: &mut Context<Self>,
13009 ) -> Task<anyhow::Result<Option<Location>>> {
13010 let Some(project) = self.project.clone() else {
13011 return Task::ready(Ok(None));
13012 };
13013
13014 cx.spawn_in(window, async move |editor, cx| {
13015 let location_task = editor.update(cx, |_, cx| {
13016 project.update(cx, |project, cx| {
13017 let language_server_name = project
13018 .language_server_statuses(cx)
13019 .find(|(id, _)| server_id == *id)
13020 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
13021 language_server_name.map(|language_server_name| {
13022 project.open_local_buffer_via_lsp(
13023 lsp_location.uri.clone(),
13024 server_id,
13025 language_server_name,
13026 cx,
13027 )
13028 })
13029 })
13030 })?;
13031 let location = match location_task {
13032 Some(task) => Some({
13033 let target_buffer_handle = task.await.context("open local buffer")?;
13034 let range = target_buffer_handle.update(cx, |target_buffer, _| {
13035 let target_start = target_buffer
13036 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
13037 let target_end = target_buffer
13038 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
13039 target_buffer.anchor_after(target_start)
13040 ..target_buffer.anchor_before(target_end)
13041 })?;
13042 Location {
13043 buffer: target_buffer_handle,
13044 range,
13045 }
13046 }),
13047 None => None,
13048 };
13049 Ok(location)
13050 })
13051 }
13052
13053 pub fn find_all_references(
13054 &mut self,
13055 _: &FindAllReferences,
13056 window: &mut Window,
13057 cx: &mut Context<Self>,
13058 ) -> Option<Task<Result<Navigated>>> {
13059 let selection = self.selections.newest::<usize>(cx);
13060 let multi_buffer = self.buffer.read(cx);
13061 let head = selection.head();
13062
13063 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13064 let head_anchor = multi_buffer_snapshot.anchor_at(
13065 head,
13066 if head < selection.tail() {
13067 Bias::Right
13068 } else {
13069 Bias::Left
13070 },
13071 );
13072
13073 match self
13074 .find_all_references_task_sources
13075 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13076 {
13077 Ok(_) => {
13078 log::info!(
13079 "Ignoring repeated FindAllReferences invocation with the position of already running task"
13080 );
13081 return None;
13082 }
13083 Err(i) => {
13084 self.find_all_references_task_sources.insert(i, head_anchor);
13085 }
13086 }
13087
13088 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
13089 let workspace = self.workspace()?;
13090 let project = workspace.read(cx).project().clone();
13091 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
13092 Some(cx.spawn_in(window, async move |editor, cx| {
13093 let _cleanup = cx.on_drop(&editor, move |editor, _| {
13094 if let Ok(i) = editor
13095 .find_all_references_task_sources
13096 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13097 {
13098 editor.find_all_references_task_sources.remove(i);
13099 }
13100 });
13101
13102 let locations = references.await?;
13103 if locations.is_empty() {
13104 return anyhow::Ok(Navigated::No);
13105 }
13106
13107 workspace.update_in(cx, |workspace, window, cx| {
13108 let title = locations
13109 .first()
13110 .as_ref()
13111 .map(|location| {
13112 let buffer = location.buffer.read(cx);
13113 format!(
13114 "References to `{}`",
13115 buffer
13116 .text_for_range(location.range.clone())
13117 .collect::<String>()
13118 )
13119 })
13120 .unwrap();
13121 Self::open_locations_in_multibuffer(
13122 workspace,
13123 locations,
13124 title,
13125 false,
13126 MultibufferSelectionMode::First,
13127 window,
13128 cx,
13129 );
13130 Navigated::Yes
13131 })
13132 }))
13133 }
13134
13135 /// Opens a multibuffer with the given project locations in it
13136 pub fn open_locations_in_multibuffer(
13137 workspace: &mut Workspace,
13138 mut locations: Vec<Location>,
13139 title: String,
13140 split: bool,
13141 multibuffer_selection_mode: MultibufferSelectionMode,
13142 window: &mut Window,
13143 cx: &mut Context<Workspace>,
13144 ) {
13145 // If there are multiple definitions, open them in a multibuffer
13146 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13147 let mut locations = locations.into_iter().peekable();
13148 let mut ranges = Vec::new();
13149 let capability = workspace.project().read(cx).capability();
13150
13151 let excerpt_buffer = cx.new(|cx| {
13152 let mut multibuffer = MultiBuffer::new(capability);
13153 while let Some(location) = locations.next() {
13154 let buffer = location.buffer.read(cx);
13155 let mut ranges_for_buffer = Vec::new();
13156 let range = location.range.to_offset(buffer);
13157 ranges_for_buffer.push(range.clone());
13158
13159 while let Some(next_location) = locations.peek() {
13160 if next_location.buffer == location.buffer {
13161 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13162 locations.next();
13163 } else {
13164 break;
13165 }
13166 }
13167
13168 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13169 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13170 location.buffer.clone(),
13171 ranges_for_buffer,
13172 DEFAULT_MULTIBUFFER_CONTEXT,
13173 cx,
13174 ))
13175 }
13176
13177 multibuffer.with_title(title)
13178 });
13179
13180 let editor = cx.new(|cx| {
13181 Editor::for_multibuffer(
13182 excerpt_buffer,
13183 Some(workspace.project().clone()),
13184 window,
13185 cx,
13186 )
13187 });
13188 editor.update(cx, |editor, cx| {
13189 match multibuffer_selection_mode {
13190 MultibufferSelectionMode::First => {
13191 if let Some(first_range) = ranges.first() {
13192 editor.change_selections(None, window, cx, |selections| {
13193 selections.clear_disjoint();
13194 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13195 });
13196 }
13197 editor.highlight_background::<Self>(
13198 &ranges,
13199 |theme| theme.editor_highlighted_line_background,
13200 cx,
13201 );
13202 }
13203 MultibufferSelectionMode::All => {
13204 editor.change_selections(None, window, cx, |selections| {
13205 selections.clear_disjoint();
13206 selections.select_anchor_ranges(ranges);
13207 });
13208 }
13209 }
13210 editor.register_buffers_with_language_servers(cx);
13211 });
13212
13213 let item = Box::new(editor);
13214 let item_id = item.item_id();
13215
13216 if split {
13217 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13218 } else {
13219 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13220 let (preview_item_id, preview_item_idx) =
13221 workspace.active_pane().update(cx, |pane, _| {
13222 (pane.preview_item_id(), pane.preview_item_idx())
13223 });
13224
13225 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13226
13227 if let Some(preview_item_id) = preview_item_id {
13228 workspace.active_pane().update(cx, |pane, cx| {
13229 pane.remove_item(preview_item_id, false, false, window, cx);
13230 });
13231 }
13232 } else {
13233 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13234 }
13235 }
13236 workspace.active_pane().update(cx, |pane, cx| {
13237 pane.set_preview_item_id(Some(item_id), cx);
13238 });
13239 }
13240
13241 pub fn rename(
13242 &mut self,
13243 _: &Rename,
13244 window: &mut Window,
13245 cx: &mut Context<Self>,
13246 ) -> Option<Task<Result<()>>> {
13247 use language::ToOffset as _;
13248
13249 let provider = self.semantics_provider.clone()?;
13250 let selection = self.selections.newest_anchor().clone();
13251 let (cursor_buffer, cursor_buffer_position) = self
13252 .buffer
13253 .read(cx)
13254 .text_anchor_for_position(selection.head(), cx)?;
13255 let (tail_buffer, cursor_buffer_position_end) = self
13256 .buffer
13257 .read(cx)
13258 .text_anchor_for_position(selection.tail(), cx)?;
13259 if tail_buffer != cursor_buffer {
13260 return None;
13261 }
13262
13263 let snapshot = cursor_buffer.read(cx).snapshot();
13264 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13265 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13266 let prepare_rename = provider
13267 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13268 .unwrap_or_else(|| Task::ready(Ok(None)));
13269 drop(snapshot);
13270
13271 Some(cx.spawn_in(window, async move |this, cx| {
13272 let rename_range = if let Some(range) = prepare_rename.await? {
13273 Some(range)
13274 } else {
13275 this.update(cx, |this, cx| {
13276 let buffer = this.buffer.read(cx).snapshot(cx);
13277 let mut buffer_highlights = this
13278 .document_highlights_for_position(selection.head(), &buffer)
13279 .filter(|highlight| {
13280 highlight.start.excerpt_id == selection.head().excerpt_id
13281 && highlight.end.excerpt_id == selection.head().excerpt_id
13282 });
13283 buffer_highlights
13284 .next()
13285 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13286 })?
13287 };
13288 if let Some(rename_range) = rename_range {
13289 this.update_in(cx, |this, window, cx| {
13290 let snapshot = cursor_buffer.read(cx).snapshot();
13291 let rename_buffer_range = rename_range.to_offset(&snapshot);
13292 let cursor_offset_in_rename_range =
13293 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13294 let cursor_offset_in_rename_range_end =
13295 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13296
13297 this.take_rename(false, window, cx);
13298 let buffer = this.buffer.read(cx).read(cx);
13299 let cursor_offset = selection.head().to_offset(&buffer);
13300 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13301 let rename_end = rename_start + rename_buffer_range.len();
13302 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13303 let mut old_highlight_id = None;
13304 let old_name: Arc<str> = buffer
13305 .chunks(rename_start..rename_end, true)
13306 .map(|chunk| {
13307 if old_highlight_id.is_none() {
13308 old_highlight_id = chunk.syntax_highlight_id;
13309 }
13310 chunk.text
13311 })
13312 .collect::<String>()
13313 .into();
13314
13315 drop(buffer);
13316
13317 // Position the selection in the rename editor so that it matches the current selection.
13318 this.show_local_selections = false;
13319 let rename_editor = cx.new(|cx| {
13320 let mut editor = Editor::single_line(window, cx);
13321 editor.buffer.update(cx, |buffer, cx| {
13322 buffer.edit([(0..0, old_name.clone())], None, cx)
13323 });
13324 let rename_selection_range = match cursor_offset_in_rename_range
13325 .cmp(&cursor_offset_in_rename_range_end)
13326 {
13327 Ordering::Equal => {
13328 editor.select_all(&SelectAll, window, cx);
13329 return editor;
13330 }
13331 Ordering::Less => {
13332 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13333 }
13334 Ordering::Greater => {
13335 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13336 }
13337 };
13338 if rename_selection_range.end > old_name.len() {
13339 editor.select_all(&SelectAll, window, cx);
13340 } else {
13341 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13342 s.select_ranges([rename_selection_range]);
13343 });
13344 }
13345 editor
13346 });
13347 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13348 if e == &EditorEvent::Focused {
13349 cx.emit(EditorEvent::FocusedIn)
13350 }
13351 })
13352 .detach();
13353
13354 let write_highlights =
13355 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13356 let read_highlights =
13357 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13358 let ranges = write_highlights
13359 .iter()
13360 .flat_map(|(_, ranges)| ranges.iter())
13361 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13362 .cloned()
13363 .collect();
13364
13365 this.highlight_text::<Rename>(
13366 ranges,
13367 HighlightStyle {
13368 fade_out: Some(0.6),
13369 ..Default::default()
13370 },
13371 cx,
13372 );
13373 let rename_focus_handle = rename_editor.focus_handle(cx);
13374 window.focus(&rename_focus_handle);
13375 let block_id = this.insert_blocks(
13376 [BlockProperties {
13377 style: BlockStyle::Flex,
13378 placement: BlockPlacement::Below(range.start),
13379 height: 1,
13380 render: Arc::new({
13381 let rename_editor = rename_editor.clone();
13382 move |cx: &mut BlockContext| {
13383 let mut text_style = cx.editor_style.text.clone();
13384 if let Some(highlight_style) = old_highlight_id
13385 .and_then(|h| h.style(&cx.editor_style.syntax))
13386 {
13387 text_style = text_style.highlight(highlight_style);
13388 }
13389 div()
13390 .block_mouse_down()
13391 .pl(cx.anchor_x)
13392 .child(EditorElement::new(
13393 &rename_editor,
13394 EditorStyle {
13395 background: cx.theme().system().transparent,
13396 local_player: cx.editor_style.local_player,
13397 text: text_style,
13398 scrollbar_width: cx.editor_style.scrollbar_width,
13399 syntax: cx.editor_style.syntax.clone(),
13400 status: cx.editor_style.status.clone(),
13401 inlay_hints_style: HighlightStyle {
13402 font_weight: Some(FontWeight::BOLD),
13403 ..make_inlay_hints_style(cx.app)
13404 },
13405 inline_completion_styles: make_suggestion_styles(
13406 cx.app,
13407 ),
13408 ..EditorStyle::default()
13409 },
13410 ))
13411 .into_any_element()
13412 }
13413 }),
13414 priority: 0,
13415 }],
13416 Some(Autoscroll::fit()),
13417 cx,
13418 )[0];
13419 this.pending_rename = Some(RenameState {
13420 range,
13421 old_name,
13422 editor: rename_editor,
13423 block_id,
13424 });
13425 })?;
13426 }
13427
13428 Ok(())
13429 }))
13430 }
13431
13432 pub fn confirm_rename(
13433 &mut self,
13434 _: &ConfirmRename,
13435 window: &mut Window,
13436 cx: &mut Context<Self>,
13437 ) -> Option<Task<Result<()>>> {
13438 let rename = self.take_rename(false, window, cx)?;
13439 let workspace = self.workspace()?.downgrade();
13440 let (buffer, start) = self
13441 .buffer
13442 .read(cx)
13443 .text_anchor_for_position(rename.range.start, cx)?;
13444 let (end_buffer, _) = self
13445 .buffer
13446 .read(cx)
13447 .text_anchor_for_position(rename.range.end, cx)?;
13448 if buffer != end_buffer {
13449 return None;
13450 }
13451
13452 let old_name = rename.old_name;
13453 let new_name = rename.editor.read(cx).text(cx);
13454
13455 let rename = self.semantics_provider.as_ref()?.perform_rename(
13456 &buffer,
13457 start,
13458 new_name.clone(),
13459 cx,
13460 )?;
13461
13462 Some(cx.spawn_in(window, async move |editor, cx| {
13463 let project_transaction = rename.await?;
13464 Self::open_project_transaction(
13465 &editor,
13466 workspace,
13467 project_transaction,
13468 format!("Rename: {} → {}", old_name, new_name),
13469 cx,
13470 )
13471 .await?;
13472
13473 editor.update(cx, |editor, cx| {
13474 editor.refresh_document_highlights(cx);
13475 })?;
13476 Ok(())
13477 }))
13478 }
13479
13480 fn take_rename(
13481 &mut self,
13482 moving_cursor: bool,
13483 window: &mut Window,
13484 cx: &mut Context<Self>,
13485 ) -> Option<RenameState> {
13486 let rename = self.pending_rename.take()?;
13487 if rename.editor.focus_handle(cx).is_focused(window) {
13488 window.focus(&self.focus_handle);
13489 }
13490
13491 self.remove_blocks(
13492 [rename.block_id].into_iter().collect(),
13493 Some(Autoscroll::fit()),
13494 cx,
13495 );
13496 self.clear_highlights::<Rename>(cx);
13497 self.show_local_selections = true;
13498
13499 if moving_cursor {
13500 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13501 editor.selections.newest::<usize>(cx).head()
13502 });
13503
13504 // Update the selection to match the position of the selection inside
13505 // the rename editor.
13506 let snapshot = self.buffer.read(cx).read(cx);
13507 let rename_range = rename.range.to_offset(&snapshot);
13508 let cursor_in_editor = snapshot
13509 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13510 .min(rename_range.end);
13511 drop(snapshot);
13512
13513 self.change_selections(None, window, cx, |s| {
13514 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13515 });
13516 } else {
13517 self.refresh_document_highlights(cx);
13518 }
13519
13520 Some(rename)
13521 }
13522
13523 pub fn pending_rename(&self) -> Option<&RenameState> {
13524 self.pending_rename.as_ref()
13525 }
13526
13527 fn format(
13528 &mut self,
13529 _: &Format,
13530 window: &mut Window,
13531 cx: &mut Context<Self>,
13532 ) -> Option<Task<Result<()>>> {
13533 let project = match &self.project {
13534 Some(project) => project.clone(),
13535 None => return None,
13536 };
13537
13538 Some(self.perform_format(
13539 project,
13540 FormatTrigger::Manual,
13541 FormatTarget::Buffers,
13542 window,
13543 cx,
13544 ))
13545 }
13546
13547 fn format_selections(
13548 &mut self,
13549 _: &FormatSelections,
13550 window: &mut Window,
13551 cx: &mut Context<Self>,
13552 ) -> Option<Task<Result<()>>> {
13553 let project = match &self.project {
13554 Some(project) => project.clone(),
13555 None => return None,
13556 };
13557
13558 let ranges = self
13559 .selections
13560 .all_adjusted(cx)
13561 .into_iter()
13562 .map(|selection| selection.range())
13563 .collect_vec();
13564
13565 Some(self.perform_format(
13566 project,
13567 FormatTrigger::Manual,
13568 FormatTarget::Ranges(ranges),
13569 window,
13570 cx,
13571 ))
13572 }
13573
13574 fn perform_format(
13575 &mut self,
13576 project: Entity<Project>,
13577 trigger: FormatTrigger,
13578 target: FormatTarget,
13579 window: &mut Window,
13580 cx: &mut Context<Self>,
13581 ) -> Task<Result<()>> {
13582 let buffer = self.buffer.clone();
13583 let (buffers, target) = match target {
13584 FormatTarget::Buffers => {
13585 let mut buffers = buffer.read(cx).all_buffers();
13586 if trigger == FormatTrigger::Save {
13587 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13588 }
13589 (buffers, LspFormatTarget::Buffers)
13590 }
13591 FormatTarget::Ranges(selection_ranges) => {
13592 let multi_buffer = buffer.read(cx);
13593 let snapshot = multi_buffer.read(cx);
13594 let mut buffers = HashSet::default();
13595 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13596 BTreeMap::new();
13597 for selection_range in selection_ranges {
13598 for (buffer, buffer_range, _) in
13599 snapshot.range_to_buffer_ranges(selection_range)
13600 {
13601 let buffer_id = buffer.remote_id();
13602 let start = buffer.anchor_before(buffer_range.start);
13603 let end = buffer.anchor_after(buffer_range.end);
13604 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13605 buffer_id_to_ranges
13606 .entry(buffer_id)
13607 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13608 .or_insert_with(|| vec![start..end]);
13609 }
13610 }
13611 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13612 }
13613 };
13614
13615 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13616 let format = project.update(cx, |project, cx| {
13617 project.format(buffers, target, true, trigger, cx)
13618 });
13619
13620 cx.spawn_in(window, async move |_, cx| {
13621 let transaction = futures::select_biased! {
13622 transaction = format.log_err().fuse() => transaction,
13623 () = timeout => {
13624 log::warn!("timed out waiting for formatting");
13625 None
13626 }
13627 };
13628
13629 buffer
13630 .update(cx, |buffer, cx| {
13631 if let Some(transaction) = transaction {
13632 if !buffer.is_singleton() {
13633 buffer.push_transaction(&transaction.0, cx);
13634 }
13635 }
13636 cx.notify();
13637 })
13638 .ok();
13639
13640 Ok(())
13641 })
13642 }
13643
13644 fn organize_imports(
13645 &mut self,
13646 _: &OrganizeImports,
13647 window: &mut Window,
13648 cx: &mut Context<Self>,
13649 ) -> Option<Task<Result<()>>> {
13650 let project = match &self.project {
13651 Some(project) => project.clone(),
13652 None => return None,
13653 };
13654 Some(self.perform_code_action_kind(
13655 project,
13656 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13657 window,
13658 cx,
13659 ))
13660 }
13661
13662 fn perform_code_action_kind(
13663 &mut self,
13664 project: Entity<Project>,
13665 kind: CodeActionKind,
13666 window: &mut Window,
13667 cx: &mut Context<Self>,
13668 ) -> Task<Result<()>> {
13669 let buffer = self.buffer.clone();
13670 let buffers = buffer.read(cx).all_buffers();
13671 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13672 let apply_action = project.update(cx, |project, cx| {
13673 project.apply_code_action_kind(buffers, kind, true, cx)
13674 });
13675 cx.spawn_in(window, async move |_, cx| {
13676 let transaction = futures::select_biased! {
13677 () = timeout => {
13678 log::warn!("timed out waiting for executing code action");
13679 None
13680 }
13681 transaction = apply_action.log_err().fuse() => transaction,
13682 };
13683 buffer
13684 .update(cx, |buffer, cx| {
13685 // check if we need this
13686 if let Some(transaction) = transaction {
13687 if !buffer.is_singleton() {
13688 buffer.push_transaction(&transaction.0, cx);
13689 }
13690 }
13691 cx.notify();
13692 })
13693 .ok();
13694 Ok(())
13695 })
13696 }
13697
13698 fn restart_language_server(
13699 &mut self,
13700 _: &RestartLanguageServer,
13701 _: &mut Window,
13702 cx: &mut Context<Self>,
13703 ) {
13704 if let Some(project) = self.project.clone() {
13705 self.buffer.update(cx, |multi_buffer, cx| {
13706 project.update(cx, |project, cx| {
13707 project.restart_language_servers_for_buffers(
13708 multi_buffer.all_buffers().into_iter().collect(),
13709 cx,
13710 );
13711 });
13712 })
13713 }
13714 }
13715
13716 fn cancel_language_server_work(
13717 workspace: &mut Workspace,
13718 _: &actions::CancelLanguageServerWork,
13719 _: &mut Window,
13720 cx: &mut Context<Workspace>,
13721 ) {
13722 let project = workspace.project();
13723 let buffers = workspace
13724 .active_item(cx)
13725 .and_then(|item| item.act_as::<Editor>(cx))
13726 .map_or(HashSet::default(), |editor| {
13727 editor.read(cx).buffer.read(cx).all_buffers()
13728 });
13729 project.update(cx, |project, cx| {
13730 project.cancel_language_server_work_for_buffers(buffers, cx);
13731 });
13732 }
13733
13734 fn show_character_palette(
13735 &mut self,
13736 _: &ShowCharacterPalette,
13737 window: &mut Window,
13738 _: &mut Context<Self>,
13739 ) {
13740 window.show_character_palette();
13741 }
13742
13743 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13744 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13745 let buffer = self.buffer.read(cx).snapshot(cx);
13746 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13747 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13748 let is_valid = buffer
13749 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13750 .any(|entry| {
13751 entry.diagnostic.is_primary
13752 && !entry.range.is_empty()
13753 && entry.range.start == primary_range_start
13754 && entry.diagnostic.message == active_diagnostics.primary_message
13755 });
13756
13757 if is_valid != active_diagnostics.is_valid {
13758 active_diagnostics.is_valid = is_valid;
13759 if is_valid {
13760 let mut new_styles = HashMap::default();
13761 for (block_id, diagnostic) in &active_diagnostics.blocks {
13762 new_styles.insert(
13763 *block_id,
13764 diagnostic_block_renderer(diagnostic.clone(), None, true),
13765 );
13766 }
13767 self.display_map.update(cx, |display_map, _cx| {
13768 display_map.replace_blocks(new_styles);
13769 });
13770 } else {
13771 self.dismiss_diagnostics(cx);
13772 }
13773 }
13774 }
13775 }
13776
13777 fn activate_diagnostics(
13778 &mut self,
13779 buffer_id: BufferId,
13780 group_id: usize,
13781 window: &mut Window,
13782 cx: &mut Context<Self>,
13783 ) {
13784 self.dismiss_diagnostics(cx);
13785 let snapshot = self.snapshot(window, cx);
13786 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13787 let buffer = self.buffer.read(cx).snapshot(cx);
13788
13789 let mut primary_range = None;
13790 let mut primary_message = None;
13791 let diagnostic_group = buffer
13792 .diagnostic_group(buffer_id, group_id)
13793 .filter_map(|entry| {
13794 let start = entry.range.start;
13795 let end = entry.range.end;
13796 if snapshot.is_line_folded(MultiBufferRow(start.row))
13797 && (start.row == end.row
13798 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13799 {
13800 return None;
13801 }
13802 if entry.diagnostic.is_primary {
13803 primary_range = Some(entry.range.clone());
13804 primary_message = Some(entry.diagnostic.message.clone());
13805 }
13806 Some(entry)
13807 })
13808 .collect::<Vec<_>>();
13809 let primary_range = primary_range?;
13810 let primary_message = primary_message?;
13811
13812 let blocks = display_map
13813 .insert_blocks(
13814 diagnostic_group.iter().map(|entry| {
13815 let diagnostic = entry.diagnostic.clone();
13816 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13817 BlockProperties {
13818 style: BlockStyle::Fixed,
13819 placement: BlockPlacement::Below(
13820 buffer.anchor_after(entry.range.start),
13821 ),
13822 height: message_height,
13823 render: diagnostic_block_renderer(diagnostic, None, true),
13824 priority: 0,
13825 }
13826 }),
13827 cx,
13828 )
13829 .into_iter()
13830 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13831 .collect();
13832
13833 Some(ActiveDiagnosticGroup {
13834 primary_range: buffer.anchor_before(primary_range.start)
13835 ..buffer.anchor_after(primary_range.end),
13836 primary_message,
13837 group_id,
13838 blocks,
13839 is_valid: true,
13840 })
13841 });
13842 }
13843
13844 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13845 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13846 self.display_map.update(cx, |display_map, cx| {
13847 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13848 });
13849 cx.notify();
13850 }
13851 }
13852
13853 /// Disable inline diagnostics rendering for this editor.
13854 pub fn disable_inline_diagnostics(&mut self) {
13855 self.inline_diagnostics_enabled = false;
13856 self.inline_diagnostics_update = Task::ready(());
13857 self.inline_diagnostics.clear();
13858 }
13859
13860 pub fn inline_diagnostics_enabled(&self) -> bool {
13861 self.inline_diagnostics_enabled
13862 }
13863
13864 pub fn show_inline_diagnostics(&self) -> bool {
13865 self.show_inline_diagnostics
13866 }
13867
13868 pub fn toggle_inline_diagnostics(
13869 &mut self,
13870 _: &ToggleInlineDiagnostics,
13871 window: &mut Window,
13872 cx: &mut Context<'_, Editor>,
13873 ) {
13874 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13875 self.refresh_inline_diagnostics(false, window, cx);
13876 }
13877
13878 fn refresh_inline_diagnostics(
13879 &mut self,
13880 debounce: bool,
13881 window: &mut Window,
13882 cx: &mut Context<Self>,
13883 ) {
13884 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13885 self.inline_diagnostics_update = Task::ready(());
13886 self.inline_diagnostics.clear();
13887 return;
13888 }
13889
13890 let debounce_ms = ProjectSettings::get_global(cx)
13891 .diagnostics
13892 .inline
13893 .update_debounce_ms;
13894 let debounce = if debounce && debounce_ms > 0 {
13895 Some(Duration::from_millis(debounce_ms))
13896 } else {
13897 None
13898 };
13899 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
13900 if let Some(debounce) = debounce {
13901 cx.background_executor().timer(debounce).await;
13902 }
13903 let Some(snapshot) = editor
13904 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13905 .ok()
13906 else {
13907 return;
13908 };
13909
13910 let new_inline_diagnostics = cx
13911 .background_spawn(async move {
13912 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13913 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13914 let message = diagnostic_entry
13915 .diagnostic
13916 .message
13917 .split_once('\n')
13918 .map(|(line, _)| line)
13919 .map(SharedString::new)
13920 .unwrap_or_else(|| {
13921 SharedString::from(diagnostic_entry.diagnostic.message)
13922 });
13923 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13924 let (Ok(i) | Err(i)) = inline_diagnostics
13925 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13926 inline_diagnostics.insert(
13927 i,
13928 (
13929 start_anchor,
13930 InlineDiagnostic {
13931 message,
13932 group_id: diagnostic_entry.diagnostic.group_id,
13933 start: diagnostic_entry.range.start.to_point(&snapshot),
13934 is_primary: diagnostic_entry.diagnostic.is_primary,
13935 severity: diagnostic_entry.diagnostic.severity,
13936 },
13937 ),
13938 );
13939 }
13940 inline_diagnostics
13941 })
13942 .await;
13943
13944 editor
13945 .update(cx, |editor, cx| {
13946 editor.inline_diagnostics = new_inline_diagnostics;
13947 cx.notify();
13948 })
13949 .ok();
13950 });
13951 }
13952
13953 pub fn set_selections_from_remote(
13954 &mut self,
13955 selections: Vec<Selection<Anchor>>,
13956 pending_selection: Option<Selection<Anchor>>,
13957 window: &mut Window,
13958 cx: &mut Context<Self>,
13959 ) {
13960 let old_cursor_position = self.selections.newest_anchor().head();
13961 self.selections.change_with(cx, |s| {
13962 s.select_anchors(selections);
13963 if let Some(pending_selection) = pending_selection {
13964 s.set_pending(pending_selection, SelectMode::Character);
13965 } else {
13966 s.clear_pending();
13967 }
13968 });
13969 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13970 }
13971
13972 fn push_to_selection_history(&mut self) {
13973 self.selection_history.push(SelectionHistoryEntry {
13974 selections: self.selections.disjoint_anchors(),
13975 select_next_state: self.select_next_state.clone(),
13976 select_prev_state: self.select_prev_state.clone(),
13977 add_selections_state: self.add_selections_state.clone(),
13978 });
13979 }
13980
13981 pub fn transact(
13982 &mut self,
13983 window: &mut Window,
13984 cx: &mut Context<Self>,
13985 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13986 ) -> Option<TransactionId> {
13987 self.start_transaction_at(Instant::now(), window, cx);
13988 update(self, window, cx);
13989 self.end_transaction_at(Instant::now(), cx)
13990 }
13991
13992 pub fn start_transaction_at(
13993 &mut self,
13994 now: Instant,
13995 window: &mut Window,
13996 cx: &mut Context<Self>,
13997 ) {
13998 self.end_selection(window, cx);
13999 if let Some(tx_id) = self
14000 .buffer
14001 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
14002 {
14003 self.selection_history
14004 .insert_transaction(tx_id, self.selections.disjoint_anchors());
14005 cx.emit(EditorEvent::TransactionBegun {
14006 transaction_id: tx_id,
14007 })
14008 }
14009 }
14010
14011 pub fn end_transaction_at(
14012 &mut self,
14013 now: Instant,
14014 cx: &mut Context<Self>,
14015 ) -> Option<TransactionId> {
14016 if let Some(transaction_id) = self
14017 .buffer
14018 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
14019 {
14020 if let Some((_, end_selections)) =
14021 self.selection_history.transaction_mut(transaction_id)
14022 {
14023 *end_selections = Some(self.selections.disjoint_anchors());
14024 } else {
14025 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
14026 }
14027
14028 cx.emit(EditorEvent::Edited { transaction_id });
14029 Some(transaction_id)
14030 } else {
14031 None
14032 }
14033 }
14034
14035 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
14036 if self.selection_mark_mode {
14037 self.change_selections(None, window, cx, |s| {
14038 s.move_with(|_, sel| {
14039 sel.collapse_to(sel.head(), SelectionGoal::None);
14040 });
14041 })
14042 }
14043 self.selection_mark_mode = true;
14044 cx.notify();
14045 }
14046
14047 pub fn swap_selection_ends(
14048 &mut self,
14049 _: &actions::SwapSelectionEnds,
14050 window: &mut Window,
14051 cx: &mut Context<Self>,
14052 ) {
14053 self.change_selections(None, window, cx, |s| {
14054 s.move_with(|_, sel| {
14055 if sel.start != sel.end {
14056 sel.reversed = !sel.reversed
14057 }
14058 });
14059 });
14060 self.request_autoscroll(Autoscroll::newest(), cx);
14061 cx.notify();
14062 }
14063
14064 pub fn toggle_fold(
14065 &mut self,
14066 _: &actions::ToggleFold,
14067 window: &mut Window,
14068 cx: &mut Context<Self>,
14069 ) {
14070 if self.is_singleton(cx) {
14071 let selection = self.selections.newest::<Point>(cx);
14072
14073 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14074 let range = if selection.is_empty() {
14075 let point = selection.head().to_display_point(&display_map);
14076 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14077 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14078 .to_point(&display_map);
14079 start..end
14080 } else {
14081 selection.range()
14082 };
14083 if display_map.folds_in_range(range).next().is_some() {
14084 self.unfold_lines(&Default::default(), window, cx)
14085 } else {
14086 self.fold(&Default::default(), window, cx)
14087 }
14088 } else {
14089 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14090 let buffer_ids: HashSet<_> = self
14091 .selections
14092 .disjoint_anchor_ranges()
14093 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14094 .collect();
14095
14096 let should_unfold = buffer_ids
14097 .iter()
14098 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
14099
14100 for buffer_id in buffer_ids {
14101 if should_unfold {
14102 self.unfold_buffer(buffer_id, cx);
14103 } else {
14104 self.fold_buffer(buffer_id, cx);
14105 }
14106 }
14107 }
14108 }
14109
14110 pub fn toggle_fold_recursive(
14111 &mut self,
14112 _: &actions::ToggleFoldRecursive,
14113 window: &mut Window,
14114 cx: &mut Context<Self>,
14115 ) {
14116 let selection = self.selections.newest::<Point>(cx);
14117
14118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14119 let range = if selection.is_empty() {
14120 let point = selection.head().to_display_point(&display_map);
14121 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14122 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14123 .to_point(&display_map);
14124 start..end
14125 } else {
14126 selection.range()
14127 };
14128 if display_map.folds_in_range(range).next().is_some() {
14129 self.unfold_recursive(&Default::default(), window, cx)
14130 } else {
14131 self.fold_recursive(&Default::default(), window, cx)
14132 }
14133 }
14134
14135 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14136 if self.is_singleton(cx) {
14137 let mut to_fold = Vec::new();
14138 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14139 let selections = self.selections.all_adjusted(cx);
14140
14141 for selection in selections {
14142 let range = selection.range().sorted();
14143 let buffer_start_row = range.start.row;
14144
14145 if range.start.row != range.end.row {
14146 let mut found = false;
14147 let mut row = range.start.row;
14148 while row <= range.end.row {
14149 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14150 {
14151 found = true;
14152 row = crease.range().end.row + 1;
14153 to_fold.push(crease);
14154 } else {
14155 row += 1
14156 }
14157 }
14158 if found {
14159 continue;
14160 }
14161 }
14162
14163 for row in (0..=range.start.row).rev() {
14164 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14165 if crease.range().end.row >= buffer_start_row {
14166 to_fold.push(crease);
14167 if row <= range.start.row {
14168 break;
14169 }
14170 }
14171 }
14172 }
14173 }
14174
14175 self.fold_creases(to_fold, true, window, cx);
14176 } else {
14177 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14178 let buffer_ids = self
14179 .selections
14180 .disjoint_anchor_ranges()
14181 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14182 .collect::<HashSet<_>>();
14183 for buffer_id in buffer_ids {
14184 self.fold_buffer(buffer_id, cx);
14185 }
14186 }
14187 }
14188
14189 fn fold_at_level(
14190 &mut self,
14191 fold_at: &FoldAtLevel,
14192 window: &mut Window,
14193 cx: &mut Context<Self>,
14194 ) {
14195 if !self.buffer.read(cx).is_singleton() {
14196 return;
14197 }
14198
14199 let fold_at_level = fold_at.0;
14200 let snapshot = self.buffer.read(cx).snapshot(cx);
14201 let mut to_fold = Vec::new();
14202 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14203
14204 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14205 while start_row < end_row {
14206 match self
14207 .snapshot(window, cx)
14208 .crease_for_buffer_row(MultiBufferRow(start_row))
14209 {
14210 Some(crease) => {
14211 let nested_start_row = crease.range().start.row + 1;
14212 let nested_end_row = crease.range().end.row;
14213
14214 if current_level < fold_at_level {
14215 stack.push((nested_start_row, nested_end_row, current_level + 1));
14216 } else if current_level == fold_at_level {
14217 to_fold.push(crease);
14218 }
14219
14220 start_row = nested_end_row + 1;
14221 }
14222 None => start_row += 1,
14223 }
14224 }
14225 }
14226
14227 self.fold_creases(to_fold, true, window, cx);
14228 }
14229
14230 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14231 if self.buffer.read(cx).is_singleton() {
14232 let mut fold_ranges = Vec::new();
14233 let snapshot = self.buffer.read(cx).snapshot(cx);
14234
14235 for row in 0..snapshot.max_row().0 {
14236 if let Some(foldable_range) = self
14237 .snapshot(window, cx)
14238 .crease_for_buffer_row(MultiBufferRow(row))
14239 {
14240 fold_ranges.push(foldable_range);
14241 }
14242 }
14243
14244 self.fold_creases(fold_ranges, true, window, cx);
14245 } else {
14246 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14247 editor
14248 .update_in(cx, |editor, _, cx| {
14249 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14250 editor.fold_buffer(buffer_id, cx);
14251 }
14252 })
14253 .ok();
14254 });
14255 }
14256 }
14257
14258 pub fn fold_function_bodies(
14259 &mut self,
14260 _: &actions::FoldFunctionBodies,
14261 window: &mut Window,
14262 cx: &mut Context<Self>,
14263 ) {
14264 let snapshot = self.buffer.read(cx).snapshot(cx);
14265
14266 let ranges = snapshot
14267 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14268 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14269 .collect::<Vec<_>>();
14270
14271 let creases = ranges
14272 .into_iter()
14273 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14274 .collect();
14275
14276 self.fold_creases(creases, true, window, cx);
14277 }
14278
14279 pub fn fold_recursive(
14280 &mut self,
14281 _: &actions::FoldRecursive,
14282 window: &mut Window,
14283 cx: &mut Context<Self>,
14284 ) {
14285 let mut to_fold = Vec::new();
14286 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14287 let selections = self.selections.all_adjusted(cx);
14288
14289 for selection in selections {
14290 let range = selection.range().sorted();
14291 let buffer_start_row = range.start.row;
14292
14293 if range.start.row != range.end.row {
14294 let mut found = false;
14295 for row in range.start.row..=range.end.row {
14296 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14297 found = true;
14298 to_fold.push(crease);
14299 }
14300 }
14301 if found {
14302 continue;
14303 }
14304 }
14305
14306 for row in (0..=range.start.row).rev() {
14307 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14308 if crease.range().end.row >= buffer_start_row {
14309 to_fold.push(crease);
14310 } else {
14311 break;
14312 }
14313 }
14314 }
14315 }
14316
14317 self.fold_creases(to_fold, true, window, cx);
14318 }
14319
14320 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14321 let buffer_row = fold_at.buffer_row;
14322 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14323
14324 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14325 let autoscroll = self
14326 .selections
14327 .all::<Point>(cx)
14328 .iter()
14329 .any(|selection| crease.range().overlaps(&selection.range()));
14330
14331 self.fold_creases(vec![crease], autoscroll, window, cx);
14332 }
14333 }
14334
14335 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14336 if self.is_singleton(cx) {
14337 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14338 let buffer = &display_map.buffer_snapshot;
14339 let selections = self.selections.all::<Point>(cx);
14340 let ranges = selections
14341 .iter()
14342 .map(|s| {
14343 let range = s.display_range(&display_map).sorted();
14344 let mut start = range.start.to_point(&display_map);
14345 let mut end = range.end.to_point(&display_map);
14346 start.column = 0;
14347 end.column = buffer.line_len(MultiBufferRow(end.row));
14348 start..end
14349 })
14350 .collect::<Vec<_>>();
14351
14352 self.unfold_ranges(&ranges, true, true, cx);
14353 } else {
14354 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14355 let buffer_ids = self
14356 .selections
14357 .disjoint_anchor_ranges()
14358 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14359 .collect::<HashSet<_>>();
14360 for buffer_id in buffer_ids {
14361 self.unfold_buffer(buffer_id, cx);
14362 }
14363 }
14364 }
14365
14366 pub fn unfold_recursive(
14367 &mut self,
14368 _: &UnfoldRecursive,
14369 _window: &mut Window,
14370 cx: &mut Context<Self>,
14371 ) {
14372 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14373 let selections = self.selections.all::<Point>(cx);
14374 let ranges = selections
14375 .iter()
14376 .map(|s| {
14377 let mut range = s.display_range(&display_map).sorted();
14378 *range.start.column_mut() = 0;
14379 *range.end.column_mut() = display_map.line_len(range.end.row());
14380 let start = range.start.to_point(&display_map);
14381 let end = range.end.to_point(&display_map);
14382 start..end
14383 })
14384 .collect::<Vec<_>>();
14385
14386 self.unfold_ranges(&ranges, true, true, cx);
14387 }
14388
14389 pub fn unfold_at(
14390 &mut self,
14391 unfold_at: &UnfoldAt,
14392 _window: &mut Window,
14393 cx: &mut Context<Self>,
14394 ) {
14395 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14396
14397 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14398 ..Point::new(
14399 unfold_at.buffer_row.0,
14400 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14401 );
14402
14403 let autoscroll = self
14404 .selections
14405 .all::<Point>(cx)
14406 .iter()
14407 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14408
14409 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14410 }
14411
14412 pub fn unfold_all(
14413 &mut self,
14414 _: &actions::UnfoldAll,
14415 _window: &mut Window,
14416 cx: &mut Context<Self>,
14417 ) {
14418 if self.buffer.read(cx).is_singleton() {
14419 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14420 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14421 } else {
14422 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14423 editor
14424 .update(cx, |editor, cx| {
14425 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14426 editor.unfold_buffer(buffer_id, cx);
14427 }
14428 })
14429 .ok();
14430 });
14431 }
14432 }
14433
14434 pub fn fold_selected_ranges(
14435 &mut self,
14436 _: &FoldSelectedRanges,
14437 window: &mut Window,
14438 cx: &mut Context<Self>,
14439 ) {
14440 let selections = self.selections.all::<Point>(cx);
14441 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14442 let line_mode = self.selections.line_mode;
14443 let ranges = selections
14444 .into_iter()
14445 .map(|s| {
14446 if line_mode {
14447 let start = Point::new(s.start.row, 0);
14448 let end = Point::new(
14449 s.end.row,
14450 display_map
14451 .buffer_snapshot
14452 .line_len(MultiBufferRow(s.end.row)),
14453 );
14454 Crease::simple(start..end, display_map.fold_placeholder.clone())
14455 } else {
14456 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14457 }
14458 })
14459 .collect::<Vec<_>>();
14460 self.fold_creases(ranges, true, window, cx);
14461 }
14462
14463 pub fn fold_ranges<T: ToOffset + Clone>(
14464 &mut self,
14465 ranges: Vec<Range<T>>,
14466 auto_scroll: bool,
14467 window: &mut Window,
14468 cx: &mut Context<Self>,
14469 ) {
14470 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14471 let ranges = ranges
14472 .into_iter()
14473 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14474 .collect::<Vec<_>>();
14475 self.fold_creases(ranges, auto_scroll, window, cx);
14476 }
14477
14478 pub fn fold_creases<T: ToOffset + Clone>(
14479 &mut self,
14480 creases: Vec<Crease<T>>,
14481 auto_scroll: bool,
14482 window: &mut Window,
14483 cx: &mut Context<Self>,
14484 ) {
14485 if creases.is_empty() {
14486 return;
14487 }
14488
14489 let mut buffers_affected = HashSet::default();
14490 let multi_buffer = self.buffer().read(cx);
14491 for crease in &creases {
14492 if let Some((_, buffer, _)) =
14493 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14494 {
14495 buffers_affected.insert(buffer.read(cx).remote_id());
14496 };
14497 }
14498
14499 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14500
14501 if auto_scroll {
14502 self.request_autoscroll(Autoscroll::fit(), cx);
14503 }
14504
14505 cx.notify();
14506
14507 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14508 // Clear diagnostics block when folding a range that contains it.
14509 let snapshot = self.snapshot(window, cx);
14510 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14511 drop(snapshot);
14512 self.active_diagnostics = Some(active_diagnostics);
14513 self.dismiss_diagnostics(cx);
14514 } else {
14515 self.active_diagnostics = Some(active_diagnostics);
14516 }
14517 }
14518
14519 self.scrollbar_marker_state.dirty = true;
14520 self.folds_did_change(cx);
14521 }
14522
14523 /// Removes any folds whose ranges intersect any of the given ranges.
14524 pub fn unfold_ranges<T: ToOffset + Clone>(
14525 &mut self,
14526 ranges: &[Range<T>],
14527 inclusive: bool,
14528 auto_scroll: bool,
14529 cx: &mut Context<Self>,
14530 ) {
14531 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14532 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14533 });
14534 self.folds_did_change(cx);
14535 }
14536
14537 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14538 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14539 return;
14540 }
14541 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14542 self.display_map.update(cx, |display_map, cx| {
14543 display_map.fold_buffers([buffer_id], cx)
14544 });
14545 cx.emit(EditorEvent::BufferFoldToggled {
14546 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14547 folded: true,
14548 });
14549 cx.notify();
14550 }
14551
14552 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14553 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14554 return;
14555 }
14556 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14557 self.display_map.update(cx, |display_map, cx| {
14558 display_map.unfold_buffers([buffer_id], cx);
14559 });
14560 cx.emit(EditorEvent::BufferFoldToggled {
14561 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14562 folded: false,
14563 });
14564 cx.notify();
14565 }
14566
14567 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14568 self.display_map.read(cx).is_buffer_folded(buffer)
14569 }
14570
14571 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14572 self.display_map.read(cx).folded_buffers()
14573 }
14574
14575 /// Removes any folds with the given ranges.
14576 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14577 &mut self,
14578 ranges: &[Range<T>],
14579 type_id: TypeId,
14580 auto_scroll: bool,
14581 cx: &mut Context<Self>,
14582 ) {
14583 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14584 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14585 });
14586 self.folds_did_change(cx);
14587 }
14588
14589 fn remove_folds_with<T: ToOffset + Clone>(
14590 &mut self,
14591 ranges: &[Range<T>],
14592 auto_scroll: bool,
14593 cx: &mut Context<Self>,
14594 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14595 ) {
14596 if ranges.is_empty() {
14597 return;
14598 }
14599
14600 let mut buffers_affected = HashSet::default();
14601 let multi_buffer = self.buffer().read(cx);
14602 for range in ranges {
14603 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14604 buffers_affected.insert(buffer.read(cx).remote_id());
14605 };
14606 }
14607
14608 self.display_map.update(cx, update);
14609
14610 if auto_scroll {
14611 self.request_autoscroll(Autoscroll::fit(), cx);
14612 }
14613
14614 cx.notify();
14615 self.scrollbar_marker_state.dirty = true;
14616 self.active_indent_guides_state.dirty = true;
14617 }
14618
14619 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14620 self.display_map.read(cx).fold_placeholder.clone()
14621 }
14622
14623 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14624 self.buffer.update(cx, |buffer, cx| {
14625 buffer.set_all_diff_hunks_expanded(cx);
14626 });
14627 }
14628
14629 pub fn expand_all_diff_hunks(
14630 &mut self,
14631 _: &ExpandAllDiffHunks,
14632 _window: &mut Window,
14633 cx: &mut Context<Self>,
14634 ) {
14635 self.buffer.update(cx, |buffer, cx| {
14636 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14637 });
14638 }
14639
14640 pub fn toggle_selected_diff_hunks(
14641 &mut self,
14642 _: &ToggleSelectedDiffHunks,
14643 _window: &mut Window,
14644 cx: &mut Context<Self>,
14645 ) {
14646 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14647 self.toggle_diff_hunks_in_ranges(ranges, cx);
14648 }
14649
14650 pub fn diff_hunks_in_ranges<'a>(
14651 &'a self,
14652 ranges: &'a [Range<Anchor>],
14653 buffer: &'a MultiBufferSnapshot,
14654 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14655 ranges.iter().flat_map(move |range| {
14656 let end_excerpt_id = range.end.excerpt_id;
14657 let range = range.to_point(buffer);
14658 let mut peek_end = range.end;
14659 if range.end.row < buffer.max_row().0 {
14660 peek_end = Point::new(range.end.row + 1, 0);
14661 }
14662 buffer
14663 .diff_hunks_in_range(range.start..peek_end)
14664 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14665 })
14666 }
14667
14668 pub fn has_stageable_diff_hunks_in_ranges(
14669 &self,
14670 ranges: &[Range<Anchor>],
14671 snapshot: &MultiBufferSnapshot,
14672 ) -> bool {
14673 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14674 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14675 }
14676
14677 pub fn toggle_staged_selected_diff_hunks(
14678 &mut self,
14679 _: &::git::ToggleStaged,
14680 _: &mut Window,
14681 cx: &mut Context<Self>,
14682 ) {
14683 let snapshot = self.buffer.read(cx).snapshot(cx);
14684 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14685 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14686 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14687 }
14688
14689 pub fn stage_and_next(
14690 &mut self,
14691 _: &::git::StageAndNext,
14692 window: &mut Window,
14693 cx: &mut Context<Self>,
14694 ) {
14695 self.do_stage_or_unstage_and_next(true, window, cx);
14696 }
14697
14698 pub fn unstage_and_next(
14699 &mut self,
14700 _: &::git::UnstageAndNext,
14701 window: &mut Window,
14702 cx: &mut Context<Self>,
14703 ) {
14704 self.do_stage_or_unstage_and_next(false, window, cx);
14705 }
14706
14707 pub fn stage_or_unstage_diff_hunks(
14708 &mut self,
14709 stage: bool,
14710 ranges: Vec<Range<Anchor>>,
14711 cx: &mut Context<Self>,
14712 ) {
14713 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14714 cx.spawn(async move |this, cx| {
14715 task.await?;
14716 this.update(cx, |this, cx| {
14717 let snapshot = this.buffer.read(cx).snapshot(cx);
14718 let chunk_by = this
14719 .diff_hunks_in_ranges(&ranges, &snapshot)
14720 .chunk_by(|hunk| hunk.buffer_id);
14721 for (buffer_id, hunks) in &chunk_by {
14722 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14723 }
14724 })
14725 })
14726 .detach_and_log_err(cx);
14727 }
14728
14729 fn save_buffers_for_ranges_if_needed(
14730 &mut self,
14731 ranges: &[Range<Anchor>],
14732 cx: &mut Context<'_, Editor>,
14733 ) -> Task<Result<()>> {
14734 let multibuffer = self.buffer.read(cx);
14735 let snapshot = multibuffer.read(cx);
14736 let buffer_ids: HashSet<_> = ranges
14737 .iter()
14738 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14739 .collect();
14740 drop(snapshot);
14741
14742 let mut buffers = HashSet::default();
14743 for buffer_id in buffer_ids {
14744 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14745 let buffer = buffer_entity.read(cx);
14746 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14747 {
14748 buffers.insert(buffer_entity);
14749 }
14750 }
14751 }
14752
14753 if let Some(project) = &self.project {
14754 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14755 } else {
14756 Task::ready(Ok(()))
14757 }
14758 }
14759
14760 fn do_stage_or_unstage_and_next(
14761 &mut self,
14762 stage: bool,
14763 window: &mut Window,
14764 cx: &mut Context<Self>,
14765 ) {
14766 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14767
14768 if ranges.iter().any(|range| range.start != range.end) {
14769 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14770 return;
14771 }
14772
14773 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14774 let snapshot = self.snapshot(window, cx);
14775 let position = self.selections.newest::<Point>(cx).head();
14776 let mut row = snapshot
14777 .buffer_snapshot
14778 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14779 .find(|hunk| hunk.row_range.start.0 > position.row)
14780 .map(|hunk| hunk.row_range.start);
14781
14782 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14783 // Outside of the project diff editor, wrap around to the beginning.
14784 if !all_diff_hunks_expanded {
14785 row = row.or_else(|| {
14786 snapshot
14787 .buffer_snapshot
14788 .diff_hunks_in_range(Point::zero()..position)
14789 .find(|hunk| hunk.row_range.end.0 < position.row)
14790 .map(|hunk| hunk.row_range.start)
14791 });
14792 }
14793
14794 if let Some(row) = row {
14795 let destination = Point::new(row.0, 0);
14796 let autoscroll = Autoscroll::center();
14797
14798 self.unfold_ranges(&[destination..destination], false, false, cx);
14799 self.change_selections(Some(autoscroll), window, cx, |s| {
14800 s.select_ranges([destination..destination]);
14801 });
14802 }
14803 }
14804
14805 fn do_stage_or_unstage(
14806 &self,
14807 stage: bool,
14808 buffer_id: BufferId,
14809 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14810 cx: &mut App,
14811 ) -> Option<()> {
14812 let project = self.project.as_ref()?;
14813 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14814 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14815 let buffer_snapshot = buffer.read(cx).snapshot();
14816 let file_exists = buffer_snapshot
14817 .file()
14818 .is_some_and(|file| file.disk_state().exists());
14819 diff.update(cx, |diff, cx| {
14820 diff.stage_or_unstage_hunks(
14821 stage,
14822 &hunks
14823 .map(|hunk| buffer_diff::DiffHunk {
14824 buffer_range: hunk.buffer_range,
14825 diff_base_byte_range: hunk.diff_base_byte_range,
14826 secondary_status: hunk.secondary_status,
14827 range: Point::zero()..Point::zero(), // unused
14828 })
14829 .collect::<Vec<_>>(),
14830 &buffer_snapshot,
14831 file_exists,
14832 cx,
14833 )
14834 });
14835 None
14836 }
14837
14838 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14839 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14840 self.buffer
14841 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14842 }
14843
14844 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14845 self.buffer.update(cx, |buffer, cx| {
14846 let ranges = vec![Anchor::min()..Anchor::max()];
14847 if !buffer.all_diff_hunks_expanded()
14848 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14849 {
14850 buffer.collapse_diff_hunks(ranges, cx);
14851 true
14852 } else {
14853 false
14854 }
14855 })
14856 }
14857
14858 fn toggle_diff_hunks_in_ranges(
14859 &mut self,
14860 ranges: Vec<Range<Anchor>>,
14861 cx: &mut Context<'_, Editor>,
14862 ) {
14863 self.buffer.update(cx, |buffer, cx| {
14864 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14865 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14866 })
14867 }
14868
14869 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14870 self.buffer.update(cx, |buffer, cx| {
14871 let snapshot = buffer.snapshot(cx);
14872 let excerpt_id = range.end.excerpt_id;
14873 let point_range = range.to_point(&snapshot);
14874 let expand = !buffer.single_hunk_is_expanded(range, cx);
14875 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14876 })
14877 }
14878
14879 pub(crate) fn apply_all_diff_hunks(
14880 &mut self,
14881 _: &ApplyAllDiffHunks,
14882 window: &mut Window,
14883 cx: &mut Context<Self>,
14884 ) {
14885 let buffers = self.buffer.read(cx).all_buffers();
14886 for branch_buffer in buffers {
14887 branch_buffer.update(cx, |branch_buffer, cx| {
14888 branch_buffer.merge_into_base(Vec::new(), cx);
14889 });
14890 }
14891
14892 if let Some(project) = self.project.clone() {
14893 self.save(true, project, window, cx).detach_and_log_err(cx);
14894 }
14895 }
14896
14897 pub(crate) fn apply_selected_diff_hunks(
14898 &mut self,
14899 _: &ApplyDiffHunk,
14900 window: &mut Window,
14901 cx: &mut Context<Self>,
14902 ) {
14903 let snapshot = self.snapshot(window, cx);
14904 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14905 let mut ranges_by_buffer = HashMap::default();
14906 self.transact(window, cx, |editor, _window, cx| {
14907 for hunk in hunks {
14908 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14909 ranges_by_buffer
14910 .entry(buffer.clone())
14911 .or_insert_with(Vec::new)
14912 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14913 }
14914 }
14915
14916 for (buffer, ranges) in ranges_by_buffer {
14917 buffer.update(cx, |buffer, cx| {
14918 buffer.merge_into_base(ranges, cx);
14919 });
14920 }
14921 });
14922
14923 if let Some(project) = self.project.clone() {
14924 self.save(true, project, window, cx).detach_and_log_err(cx);
14925 }
14926 }
14927
14928 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14929 if hovered != self.gutter_hovered {
14930 self.gutter_hovered = hovered;
14931 cx.notify();
14932 }
14933 }
14934
14935 pub fn insert_blocks(
14936 &mut self,
14937 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14938 autoscroll: Option<Autoscroll>,
14939 cx: &mut Context<Self>,
14940 ) -> Vec<CustomBlockId> {
14941 let blocks = self
14942 .display_map
14943 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14944 if let Some(autoscroll) = autoscroll {
14945 self.request_autoscroll(autoscroll, cx);
14946 }
14947 cx.notify();
14948 blocks
14949 }
14950
14951 pub fn resize_blocks(
14952 &mut self,
14953 heights: HashMap<CustomBlockId, u32>,
14954 autoscroll: Option<Autoscroll>,
14955 cx: &mut Context<Self>,
14956 ) {
14957 self.display_map
14958 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14959 if let Some(autoscroll) = autoscroll {
14960 self.request_autoscroll(autoscroll, cx);
14961 }
14962 cx.notify();
14963 }
14964
14965 pub fn replace_blocks(
14966 &mut self,
14967 renderers: HashMap<CustomBlockId, RenderBlock>,
14968 autoscroll: Option<Autoscroll>,
14969 cx: &mut Context<Self>,
14970 ) {
14971 self.display_map
14972 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14973 if let Some(autoscroll) = autoscroll {
14974 self.request_autoscroll(autoscroll, cx);
14975 }
14976 cx.notify();
14977 }
14978
14979 pub fn remove_blocks(
14980 &mut self,
14981 block_ids: HashSet<CustomBlockId>,
14982 autoscroll: Option<Autoscroll>,
14983 cx: &mut Context<Self>,
14984 ) {
14985 self.display_map.update(cx, |display_map, cx| {
14986 display_map.remove_blocks(block_ids, cx)
14987 });
14988 if let Some(autoscroll) = autoscroll {
14989 self.request_autoscroll(autoscroll, cx);
14990 }
14991 cx.notify();
14992 }
14993
14994 pub fn row_for_block(
14995 &self,
14996 block_id: CustomBlockId,
14997 cx: &mut Context<Self>,
14998 ) -> Option<DisplayRow> {
14999 self.display_map
15000 .update(cx, |map, cx| map.row_for_block(block_id, cx))
15001 }
15002
15003 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
15004 self.focused_block = Some(focused_block);
15005 }
15006
15007 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
15008 self.focused_block.take()
15009 }
15010
15011 pub fn insert_creases(
15012 &mut self,
15013 creases: impl IntoIterator<Item = Crease<Anchor>>,
15014 cx: &mut Context<Self>,
15015 ) -> Vec<CreaseId> {
15016 self.display_map
15017 .update(cx, |map, cx| map.insert_creases(creases, cx))
15018 }
15019
15020 pub fn remove_creases(
15021 &mut self,
15022 ids: impl IntoIterator<Item = CreaseId>,
15023 cx: &mut Context<Self>,
15024 ) {
15025 self.display_map
15026 .update(cx, |map, cx| map.remove_creases(ids, cx));
15027 }
15028
15029 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
15030 self.display_map
15031 .update(cx, |map, cx| map.snapshot(cx))
15032 .longest_row()
15033 }
15034
15035 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
15036 self.display_map
15037 .update(cx, |map, cx| map.snapshot(cx))
15038 .max_point()
15039 }
15040
15041 pub fn text(&self, cx: &App) -> String {
15042 self.buffer.read(cx).read(cx).text()
15043 }
15044
15045 pub fn is_empty(&self, cx: &App) -> bool {
15046 self.buffer.read(cx).read(cx).is_empty()
15047 }
15048
15049 pub fn text_option(&self, cx: &App) -> Option<String> {
15050 let text = self.text(cx);
15051 let text = text.trim();
15052
15053 if text.is_empty() {
15054 return None;
15055 }
15056
15057 Some(text.to_string())
15058 }
15059
15060 pub fn set_text(
15061 &mut self,
15062 text: impl Into<Arc<str>>,
15063 window: &mut Window,
15064 cx: &mut Context<Self>,
15065 ) {
15066 self.transact(window, cx, |this, _, cx| {
15067 this.buffer
15068 .read(cx)
15069 .as_singleton()
15070 .expect("you can only call set_text on editors for singleton buffers")
15071 .update(cx, |buffer, cx| buffer.set_text(text, cx));
15072 });
15073 }
15074
15075 pub fn display_text(&self, cx: &mut App) -> String {
15076 self.display_map
15077 .update(cx, |map, cx| map.snapshot(cx))
15078 .text()
15079 }
15080
15081 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
15082 let mut wrap_guides = smallvec::smallvec![];
15083
15084 if self.show_wrap_guides == Some(false) {
15085 return wrap_guides;
15086 }
15087
15088 let settings = self.buffer.read(cx).language_settings(cx);
15089 if settings.show_wrap_guides {
15090 match self.soft_wrap_mode(cx) {
15091 SoftWrap::Column(soft_wrap) => {
15092 wrap_guides.push((soft_wrap as usize, true));
15093 }
15094 SoftWrap::Bounded(soft_wrap) => {
15095 wrap_guides.push((soft_wrap as usize, true));
15096 }
15097 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
15098 }
15099 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
15100 }
15101
15102 wrap_guides
15103 }
15104
15105 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
15106 let settings = self.buffer.read(cx).language_settings(cx);
15107 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
15108 match mode {
15109 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
15110 SoftWrap::None
15111 }
15112 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15113 language_settings::SoftWrap::PreferredLineLength => {
15114 SoftWrap::Column(settings.preferred_line_length)
15115 }
15116 language_settings::SoftWrap::Bounded => {
15117 SoftWrap::Bounded(settings.preferred_line_length)
15118 }
15119 }
15120 }
15121
15122 pub fn set_soft_wrap_mode(
15123 &mut self,
15124 mode: language_settings::SoftWrap,
15125
15126 cx: &mut Context<Self>,
15127 ) {
15128 self.soft_wrap_mode_override = Some(mode);
15129 cx.notify();
15130 }
15131
15132 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15133 self.hard_wrap = hard_wrap;
15134 cx.notify();
15135 }
15136
15137 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15138 self.text_style_refinement = Some(style);
15139 }
15140
15141 /// called by the Element so we know what style we were most recently rendered with.
15142 pub(crate) fn set_style(
15143 &mut self,
15144 style: EditorStyle,
15145 window: &mut Window,
15146 cx: &mut Context<Self>,
15147 ) {
15148 let rem_size = window.rem_size();
15149 self.display_map.update(cx, |map, cx| {
15150 map.set_font(
15151 style.text.font(),
15152 style.text.font_size.to_pixels(rem_size),
15153 cx,
15154 )
15155 });
15156 self.style = Some(style);
15157 }
15158
15159 pub fn style(&self) -> Option<&EditorStyle> {
15160 self.style.as_ref()
15161 }
15162
15163 // Called by the element. This method is not designed to be called outside of the editor
15164 // element's layout code because it does not notify when rewrapping is computed synchronously.
15165 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15166 self.display_map
15167 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15168 }
15169
15170 pub fn set_soft_wrap(&mut self) {
15171 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15172 }
15173
15174 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15175 if self.soft_wrap_mode_override.is_some() {
15176 self.soft_wrap_mode_override.take();
15177 } else {
15178 let soft_wrap = match self.soft_wrap_mode(cx) {
15179 SoftWrap::GitDiff => return,
15180 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15181 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15182 language_settings::SoftWrap::None
15183 }
15184 };
15185 self.soft_wrap_mode_override = Some(soft_wrap);
15186 }
15187 cx.notify();
15188 }
15189
15190 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15191 let Some(workspace) = self.workspace() else {
15192 return;
15193 };
15194 let fs = workspace.read(cx).app_state().fs.clone();
15195 let current_show = TabBarSettings::get_global(cx).show;
15196 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15197 setting.show = Some(!current_show);
15198 });
15199 }
15200
15201 pub fn toggle_indent_guides(
15202 &mut self,
15203 _: &ToggleIndentGuides,
15204 _: &mut Window,
15205 cx: &mut Context<Self>,
15206 ) {
15207 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15208 self.buffer
15209 .read(cx)
15210 .language_settings(cx)
15211 .indent_guides
15212 .enabled
15213 });
15214 self.show_indent_guides = Some(!currently_enabled);
15215 cx.notify();
15216 }
15217
15218 fn should_show_indent_guides(&self) -> Option<bool> {
15219 self.show_indent_guides
15220 }
15221
15222 pub fn toggle_line_numbers(
15223 &mut self,
15224 _: &ToggleLineNumbers,
15225 _: &mut Window,
15226 cx: &mut Context<Self>,
15227 ) {
15228 let mut editor_settings = EditorSettings::get_global(cx).clone();
15229 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15230 EditorSettings::override_global(editor_settings, cx);
15231 }
15232
15233 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15234 if let Some(show_line_numbers) = self.show_line_numbers {
15235 return show_line_numbers;
15236 }
15237 EditorSettings::get_global(cx).gutter.line_numbers
15238 }
15239
15240 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15241 self.use_relative_line_numbers
15242 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15243 }
15244
15245 pub fn toggle_relative_line_numbers(
15246 &mut self,
15247 _: &ToggleRelativeLineNumbers,
15248 _: &mut Window,
15249 cx: &mut Context<Self>,
15250 ) {
15251 let is_relative = self.should_use_relative_line_numbers(cx);
15252 self.set_relative_line_number(Some(!is_relative), cx)
15253 }
15254
15255 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15256 self.use_relative_line_numbers = is_relative;
15257 cx.notify();
15258 }
15259
15260 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15261 self.show_gutter = show_gutter;
15262 cx.notify();
15263 }
15264
15265 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15266 self.show_scrollbars = show_scrollbars;
15267 cx.notify();
15268 }
15269
15270 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15271 self.show_line_numbers = Some(show_line_numbers);
15272 cx.notify();
15273 }
15274
15275 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15276 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15277 cx.notify();
15278 }
15279
15280 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15281 self.show_code_actions = Some(show_code_actions);
15282 cx.notify();
15283 }
15284
15285 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15286 self.show_runnables = Some(show_runnables);
15287 cx.notify();
15288 }
15289
15290 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15291 self.show_breakpoints = Some(show_breakpoints);
15292 cx.notify();
15293 }
15294
15295 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15296 if self.display_map.read(cx).masked != masked {
15297 self.display_map.update(cx, |map, _| map.masked = masked);
15298 }
15299 cx.notify()
15300 }
15301
15302 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15303 self.show_wrap_guides = Some(show_wrap_guides);
15304 cx.notify();
15305 }
15306
15307 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15308 self.show_indent_guides = Some(show_indent_guides);
15309 cx.notify();
15310 }
15311
15312 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15313 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15314 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15315 if let Some(dir) = file.abs_path(cx).parent() {
15316 return Some(dir.to_owned());
15317 }
15318 }
15319
15320 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15321 return Some(project_path.path.to_path_buf());
15322 }
15323 }
15324
15325 None
15326 }
15327
15328 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15329 self.active_excerpt(cx)?
15330 .1
15331 .read(cx)
15332 .file()
15333 .and_then(|f| f.as_local())
15334 }
15335
15336 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15337 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15338 let buffer = buffer.read(cx);
15339 if let Some(project_path) = buffer.project_path(cx) {
15340 let project = self.project.as_ref()?.read(cx);
15341 project.absolute_path(&project_path, cx)
15342 } else {
15343 buffer
15344 .file()
15345 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15346 }
15347 })
15348 }
15349
15350 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15351 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15352 let project_path = buffer.read(cx).project_path(cx)?;
15353 let project = self.project.as_ref()?.read(cx);
15354 let entry = project.entry_for_path(&project_path, cx)?;
15355 let path = entry.path.to_path_buf();
15356 Some(path)
15357 })
15358 }
15359
15360 pub fn reveal_in_finder(
15361 &mut self,
15362 _: &RevealInFileManager,
15363 _window: &mut Window,
15364 cx: &mut Context<Self>,
15365 ) {
15366 if let Some(target) = self.target_file(cx) {
15367 cx.reveal_path(&target.abs_path(cx));
15368 }
15369 }
15370
15371 pub fn copy_path(
15372 &mut self,
15373 _: &zed_actions::workspace::CopyPath,
15374 _window: &mut Window,
15375 cx: &mut Context<Self>,
15376 ) {
15377 if let Some(path) = self.target_file_abs_path(cx) {
15378 if let Some(path) = path.to_str() {
15379 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15380 }
15381 }
15382 }
15383
15384 pub fn copy_relative_path(
15385 &mut self,
15386 _: &zed_actions::workspace::CopyRelativePath,
15387 _window: &mut Window,
15388 cx: &mut Context<Self>,
15389 ) {
15390 if let Some(path) = self.target_file_path(cx) {
15391 if let Some(path) = path.to_str() {
15392 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15393 }
15394 }
15395 }
15396
15397 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15398 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15399 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15400 } else {
15401 None
15402 }
15403 }
15404
15405 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15406 let _ = maybe!({
15407 let breakpoint_store = self.breakpoint_store.as_ref()?;
15408
15409 let Some((_, _, active_position)) =
15410 breakpoint_store.read(cx).active_position().cloned()
15411 else {
15412 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15413 return None;
15414 };
15415
15416 let snapshot = self
15417 .project
15418 .as_ref()?
15419 .read(cx)
15420 .buffer_for_id(active_position.buffer_id?, cx)?
15421 .read(cx)
15422 .snapshot();
15423
15424 for (id, ExcerptRange { context, .. }) in self
15425 .buffer
15426 .read(cx)
15427 .excerpts_for_buffer(active_position.buffer_id?, cx)
15428 {
15429 if context.start.cmp(&active_position, &snapshot).is_ge()
15430 || context.end.cmp(&active_position, &snapshot).is_lt()
15431 {
15432 continue;
15433 }
15434 let snapshot = self.buffer.read(cx).snapshot(cx);
15435 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15436
15437 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15438 self.go_to_line::<DebugCurrentRowHighlight>(
15439 multibuffer_anchor,
15440 Some(cx.theme().colors().editor_debugger_active_line_background),
15441 window,
15442 cx,
15443 );
15444
15445 cx.notify();
15446 }
15447
15448 Some(())
15449 });
15450 }
15451
15452 pub fn copy_file_name_without_extension(
15453 &mut self,
15454 _: &CopyFileNameWithoutExtension,
15455 _: &mut Window,
15456 cx: &mut Context<Self>,
15457 ) {
15458 if let Some(file) = self.target_file(cx) {
15459 if let Some(file_stem) = file.path().file_stem() {
15460 if let Some(name) = file_stem.to_str() {
15461 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15462 }
15463 }
15464 }
15465 }
15466
15467 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15468 if let Some(file) = self.target_file(cx) {
15469 if let Some(file_name) = file.path().file_name() {
15470 if let Some(name) = file_name.to_str() {
15471 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15472 }
15473 }
15474 }
15475 }
15476
15477 pub fn toggle_git_blame(
15478 &mut self,
15479 _: &::git::Blame,
15480 window: &mut Window,
15481 cx: &mut Context<Self>,
15482 ) {
15483 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15484
15485 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15486 self.start_git_blame(true, window, cx);
15487 }
15488
15489 cx.notify();
15490 }
15491
15492 pub fn toggle_git_blame_inline(
15493 &mut self,
15494 _: &ToggleGitBlameInline,
15495 window: &mut Window,
15496 cx: &mut Context<Self>,
15497 ) {
15498 self.toggle_git_blame_inline_internal(true, window, cx);
15499 cx.notify();
15500 }
15501
15502 pub fn git_blame_inline_enabled(&self) -> bool {
15503 self.git_blame_inline_enabled
15504 }
15505
15506 pub fn toggle_selection_menu(
15507 &mut self,
15508 _: &ToggleSelectionMenu,
15509 _: &mut Window,
15510 cx: &mut Context<Self>,
15511 ) {
15512 self.show_selection_menu = self
15513 .show_selection_menu
15514 .map(|show_selections_menu| !show_selections_menu)
15515 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15516
15517 cx.notify();
15518 }
15519
15520 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15521 self.show_selection_menu
15522 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15523 }
15524
15525 fn start_git_blame(
15526 &mut self,
15527 user_triggered: bool,
15528 window: &mut Window,
15529 cx: &mut Context<Self>,
15530 ) {
15531 if let Some(project) = self.project.as_ref() {
15532 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15533 return;
15534 };
15535
15536 if buffer.read(cx).file().is_none() {
15537 return;
15538 }
15539
15540 let focused = self.focus_handle(cx).contains_focused(window, cx);
15541
15542 let project = project.clone();
15543 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15544 self.blame_subscription =
15545 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15546 self.blame = Some(blame);
15547 }
15548 }
15549
15550 fn toggle_git_blame_inline_internal(
15551 &mut self,
15552 user_triggered: bool,
15553 window: &mut Window,
15554 cx: &mut Context<Self>,
15555 ) {
15556 if self.git_blame_inline_enabled {
15557 self.git_blame_inline_enabled = false;
15558 self.show_git_blame_inline = false;
15559 self.show_git_blame_inline_delay_task.take();
15560 } else {
15561 self.git_blame_inline_enabled = true;
15562 self.start_git_blame_inline(user_triggered, window, cx);
15563 }
15564
15565 cx.notify();
15566 }
15567
15568 fn start_git_blame_inline(
15569 &mut self,
15570 user_triggered: bool,
15571 window: &mut Window,
15572 cx: &mut Context<Self>,
15573 ) {
15574 self.start_git_blame(user_triggered, window, cx);
15575
15576 if ProjectSettings::get_global(cx)
15577 .git
15578 .inline_blame_delay()
15579 .is_some()
15580 {
15581 self.start_inline_blame_timer(window, cx);
15582 } else {
15583 self.show_git_blame_inline = true
15584 }
15585 }
15586
15587 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15588 self.blame.as_ref()
15589 }
15590
15591 pub fn show_git_blame_gutter(&self) -> bool {
15592 self.show_git_blame_gutter
15593 }
15594
15595 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15596 self.show_git_blame_gutter && self.has_blame_entries(cx)
15597 }
15598
15599 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15600 self.show_git_blame_inline
15601 && (self.focus_handle.is_focused(window)
15602 || self
15603 .git_blame_inline_tooltip
15604 .as_ref()
15605 .and_then(|t| t.upgrade())
15606 .is_some())
15607 && !self.newest_selection_head_on_empty_line(cx)
15608 && self.has_blame_entries(cx)
15609 }
15610
15611 fn has_blame_entries(&self, cx: &App) -> bool {
15612 self.blame()
15613 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15614 }
15615
15616 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15617 let cursor_anchor = self.selections.newest_anchor().head();
15618
15619 let snapshot = self.buffer.read(cx).snapshot(cx);
15620 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15621
15622 snapshot.line_len(buffer_row) == 0
15623 }
15624
15625 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15626 let buffer_and_selection = maybe!({
15627 let selection = self.selections.newest::<Point>(cx);
15628 let selection_range = selection.range();
15629
15630 let multi_buffer = self.buffer().read(cx);
15631 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15632 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15633
15634 let (buffer, range, _) = if selection.reversed {
15635 buffer_ranges.first()
15636 } else {
15637 buffer_ranges.last()
15638 }?;
15639
15640 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15641 ..text::ToPoint::to_point(&range.end, &buffer).row;
15642 Some((
15643 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15644 selection,
15645 ))
15646 });
15647
15648 let Some((buffer, selection)) = buffer_and_selection else {
15649 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15650 };
15651
15652 let Some(project) = self.project.as_ref() else {
15653 return Task::ready(Err(anyhow!("editor does not have project")));
15654 };
15655
15656 project.update(cx, |project, cx| {
15657 project.get_permalink_to_line(&buffer, selection, cx)
15658 })
15659 }
15660
15661 pub fn copy_permalink_to_line(
15662 &mut self,
15663 _: &CopyPermalinkToLine,
15664 window: &mut Window,
15665 cx: &mut Context<Self>,
15666 ) {
15667 let permalink_task = self.get_permalink_to_line(cx);
15668 let workspace = self.workspace();
15669
15670 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15671 Ok(permalink) => {
15672 cx.update(|_, cx| {
15673 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15674 })
15675 .ok();
15676 }
15677 Err(err) => {
15678 let message = format!("Failed to copy permalink: {err}");
15679
15680 Err::<(), anyhow::Error>(err).log_err();
15681
15682 if let Some(workspace) = workspace {
15683 workspace
15684 .update_in(cx, |workspace, _, cx| {
15685 struct CopyPermalinkToLine;
15686
15687 workspace.show_toast(
15688 Toast::new(
15689 NotificationId::unique::<CopyPermalinkToLine>(),
15690 message,
15691 ),
15692 cx,
15693 )
15694 })
15695 .ok();
15696 }
15697 }
15698 })
15699 .detach();
15700 }
15701
15702 pub fn copy_file_location(
15703 &mut self,
15704 _: &CopyFileLocation,
15705 _: &mut Window,
15706 cx: &mut Context<Self>,
15707 ) {
15708 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15709 if let Some(file) = self.target_file(cx) {
15710 if let Some(path) = file.path().to_str() {
15711 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15712 }
15713 }
15714 }
15715
15716 pub fn open_permalink_to_line(
15717 &mut self,
15718 _: &OpenPermalinkToLine,
15719 window: &mut Window,
15720 cx: &mut Context<Self>,
15721 ) {
15722 let permalink_task = self.get_permalink_to_line(cx);
15723 let workspace = self.workspace();
15724
15725 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15726 Ok(permalink) => {
15727 cx.update(|_, cx| {
15728 cx.open_url(permalink.as_ref());
15729 })
15730 .ok();
15731 }
15732 Err(err) => {
15733 let message = format!("Failed to open permalink: {err}");
15734
15735 Err::<(), anyhow::Error>(err).log_err();
15736
15737 if let Some(workspace) = workspace {
15738 workspace
15739 .update(cx, |workspace, cx| {
15740 struct OpenPermalinkToLine;
15741
15742 workspace.show_toast(
15743 Toast::new(
15744 NotificationId::unique::<OpenPermalinkToLine>(),
15745 message,
15746 ),
15747 cx,
15748 )
15749 })
15750 .ok();
15751 }
15752 }
15753 })
15754 .detach();
15755 }
15756
15757 pub fn insert_uuid_v4(
15758 &mut self,
15759 _: &InsertUuidV4,
15760 window: &mut Window,
15761 cx: &mut Context<Self>,
15762 ) {
15763 self.insert_uuid(UuidVersion::V4, window, cx);
15764 }
15765
15766 pub fn insert_uuid_v7(
15767 &mut self,
15768 _: &InsertUuidV7,
15769 window: &mut Window,
15770 cx: &mut Context<Self>,
15771 ) {
15772 self.insert_uuid(UuidVersion::V7, window, cx);
15773 }
15774
15775 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15776 self.transact(window, cx, |this, window, cx| {
15777 let edits = this
15778 .selections
15779 .all::<Point>(cx)
15780 .into_iter()
15781 .map(|selection| {
15782 let uuid = match version {
15783 UuidVersion::V4 => uuid::Uuid::new_v4(),
15784 UuidVersion::V7 => uuid::Uuid::now_v7(),
15785 };
15786
15787 (selection.range(), uuid.to_string())
15788 });
15789 this.edit(edits, cx);
15790 this.refresh_inline_completion(true, false, window, cx);
15791 });
15792 }
15793
15794 pub fn open_selections_in_multibuffer(
15795 &mut self,
15796 _: &OpenSelectionsInMultibuffer,
15797 window: &mut Window,
15798 cx: &mut Context<Self>,
15799 ) {
15800 let multibuffer = self.buffer.read(cx);
15801
15802 let Some(buffer) = multibuffer.as_singleton() else {
15803 return;
15804 };
15805
15806 let Some(workspace) = self.workspace() else {
15807 return;
15808 };
15809
15810 let locations = self
15811 .selections
15812 .disjoint_anchors()
15813 .iter()
15814 .map(|range| Location {
15815 buffer: buffer.clone(),
15816 range: range.start.text_anchor..range.end.text_anchor,
15817 })
15818 .collect::<Vec<_>>();
15819
15820 let title = multibuffer.title(cx).to_string();
15821
15822 cx.spawn_in(window, async move |_, cx| {
15823 workspace.update_in(cx, |workspace, window, cx| {
15824 Self::open_locations_in_multibuffer(
15825 workspace,
15826 locations,
15827 format!("Selections for '{title}'"),
15828 false,
15829 MultibufferSelectionMode::All,
15830 window,
15831 cx,
15832 );
15833 })
15834 })
15835 .detach();
15836 }
15837
15838 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15839 /// last highlight added will be used.
15840 ///
15841 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15842 pub fn highlight_rows<T: 'static>(
15843 &mut self,
15844 range: Range<Anchor>,
15845 color: Hsla,
15846 should_autoscroll: bool,
15847 cx: &mut Context<Self>,
15848 ) {
15849 let snapshot = self.buffer().read(cx).snapshot(cx);
15850 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15851 let ix = row_highlights.binary_search_by(|highlight| {
15852 Ordering::Equal
15853 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15854 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15855 });
15856
15857 if let Err(mut ix) = ix {
15858 let index = post_inc(&mut self.highlight_order);
15859
15860 // If this range intersects with the preceding highlight, then merge it with
15861 // the preceding highlight. Otherwise insert a new highlight.
15862 let mut merged = false;
15863 if ix > 0 {
15864 let prev_highlight = &mut row_highlights[ix - 1];
15865 if prev_highlight
15866 .range
15867 .end
15868 .cmp(&range.start, &snapshot)
15869 .is_ge()
15870 {
15871 ix -= 1;
15872 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15873 prev_highlight.range.end = range.end;
15874 }
15875 merged = true;
15876 prev_highlight.index = index;
15877 prev_highlight.color = color;
15878 prev_highlight.should_autoscroll = should_autoscroll;
15879 }
15880 }
15881
15882 if !merged {
15883 row_highlights.insert(
15884 ix,
15885 RowHighlight {
15886 range: range.clone(),
15887 index,
15888 color,
15889 should_autoscroll,
15890 },
15891 );
15892 }
15893
15894 // If any of the following highlights intersect with this one, merge them.
15895 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15896 let highlight = &row_highlights[ix];
15897 if next_highlight
15898 .range
15899 .start
15900 .cmp(&highlight.range.end, &snapshot)
15901 .is_le()
15902 {
15903 if next_highlight
15904 .range
15905 .end
15906 .cmp(&highlight.range.end, &snapshot)
15907 .is_gt()
15908 {
15909 row_highlights[ix].range.end = next_highlight.range.end;
15910 }
15911 row_highlights.remove(ix + 1);
15912 } else {
15913 break;
15914 }
15915 }
15916 }
15917 }
15918
15919 /// Remove any highlighted row ranges of the given type that intersect the
15920 /// given ranges.
15921 pub fn remove_highlighted_rows<T: 'static>(
15922 &mut self,
15923 ranges_to_remove: Vec<Range<Anchor>>,
15924 cx: &mut Context<Self>,
15925 ) {
15926 let snapshot = self.buffer().read(cx).snapshot(cx);
15927 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15928 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15929 row_highlights.retain(|highlight| {
15930 while let Some(range_to_remove) = ranges_to_remove.peek() {
15931 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15932 Ordering::Less | Ordering::Equal => {
15933 ranges_to_remove.next();
15934 }
15935 Ordering::Greater => {
15936 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15937 Ordering::Less | Ordering::Equal => {
15938 return false;
15939 }
15940 Ordering::Greater => break,
15941 }
15942 }
15943 }
15944 }
15945
15946 true
15947 })
15948 }
15949
15950 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15951 pub fn clear_row_highlights<T: 'static>(&mut self) {
15952 self.highlighted_rows.remove(&TypeId::of::<T>());
15953 }
15954
15955 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15956 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15957 self.highlighted_rows
15958 .get(&TypeId::of::<T>())
15959 .map_or(&[] as &[_], |vec| vec.as_slice())
15960 .iter()
15961 .map(|highlight| (highlight.range.clone(), highlight.color))
15962 }
15963
15964 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15965 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15966 /// Allows to ignore certain kinds of highlights.
15967 pub fn highlighted_display_rows(
15968 &self,
15969 window: &mut Window,
15970 cx: &mut App,
15971 ) -> BTreeMap<DisplayRow, LineHighlight> {
15972 let snapshot = self.snapshot(window, cx);
15973 let mut used_highlight_orders = HashMap::default();
15974 self.highlighted_rows
15975 .iter()
15976 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15977 .fold(
15978 BTreeMap::<DisplayRow, LineHighlight>::new(),
15979 |mut unique_rows, highlight| {
15980 let start = highlight.range.start.to_display_point(&snapshot);
15981 let end = highlight.range.end.to_display_point(&snapshot);
15982 let start_row = start.row().0;
15983 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15984 && end.column() == 0
15985 {
15986 end.row().0.saturating_sub(1)
15987 } else {
15988 end.row().0
15989 };
15990 for row in start_row..=end_row {
15991 let used_index =
15992 used_highlight_orders.entry(row).or_insert(highlight.index);
15993 if highlight.index >= *used_index {
15994 *used_index = highlight.index;
15995 unique_rows.insert(DisplayRow(row), highlight.color.into());
15996 }
15997 }
15998 unique_rows
15999 },
16000 )
16001 }
16002
16003 pub fn highlighted_display_row_for_autoscroll(
16004 &self,
16005 snapshot: &DisplaySnapshot,
16006 ) -> Option<DisplayRow> {
16007 self.highlighted_rows
16008 .values()
16009 .flat_map(|highlighted_rows| highlighted_rows.iter())
16010 .filter_map(|highlight| {
16011 if highlight.should_autoscroll {
16012 Some(highlight.range.start.to_display_point(snapshot).row())
16013 } else {
16014 None
16015 }
16016 })
16017 .min()
16018 }
16019
16020 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
16021 self.highlight_background::<SearchWithinRange>(
16022 ranges,
16023 |colors| colors.editor_document_highlight_read_background,
16024 cx,
16025 )
16026 }
16027
16028 pub fn set_breadcrumb_header(&mut self, new_header: String) {
16029 self.breadcrumb_header = Some(new_header);
16030 }
16031
16032 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
16033 self.clear_background_highlights::<SearchWithinRange>(cx);
16034 }
16035
16036 pub fn highlight_background<T: 'static>(
16037 &mut self,
16038 ranges: &[Range<Anchor>],
16039 color_fetcher: fn(&ThemeColors) -> Hsla,
16040 cx: &mut Context<Self>,
16041 ) {
16042 self.background_highlights
16043 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16044 self.scrollbar_marker_state.dirty = true;
16045 cx.notify();
16046 }
16047
16048 pub fn clear_background_highlights<T: 'static>(
16049 &mut self,
16050 cx: &mut Context<Self>,
16051 ) -> Option<BackgroundHighlight> {
16052 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
16053 if !text_highlights.1.is_empty() {
16054 self.scrollbar_marker_state.dirty = true;
16055 cx.notify();
16056 }
16057 Some(text_highlights)
16058 }
16059
16060 pub fn highlight_gutter<T: 'static>(
16061 &mut self,
16062 ranges: &[Range<Anchor>],
16063 color_fetcher: fn(&App) -> Hsla,
16064 cx: &mut Context<Self>,
16065 ) {
16066 self.gutter_highlights
16067 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16068 cx.notify();
16069 }
16070
16071 pub fn clear_gutter_highlights<T: 'static>(
16072 &mut self,
16073 cx: &mut Context<Self>,
16074 ) -> Option<GutterHighlight> {
16075 cx.notify();
16076 self.gutter_highlights.remove(&TypeId::of::<T>())
16077 }
16078
16079 #[cfg(feature = "test-support")]
16080 pub fn all_text_background_highlights(
16081 &self,
16082 window: &mut Window,
16083 cx: &mut Context<Self>,
16084 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16085 let snapshot = self.snapshot(window, cx);
16086 let buffer = &snapshot.buffer_snapshot;
16087 let start = buffer.anchor_before(0);
16088 let end = buffer.anchor_after(buffer.len());
16089 let theme = cx.theme().colors();
16090 self.background_highlights_in_range(start..end, &snapshot, theme)
16091 }
16092
16093 #[cfg(feature = "test-support")]
16094 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
16095 let snapshot = self.buffer().read(cx).snapshot(cx);
16096
16097 let highlights = self
16098 .background_highlights
16099 .get(&TypeId::of::<items::BufferSearchHighlights>());
16100
16101 if let Some((_color, ranges)) = highlights {
16102 ranges
16103 .iter()
16104 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
16105 .collect_vec()
16106 } else {
16107 vec![]
16108 }
16109 }
16110
16111 fn document_highlights_for_position<'a>(
16112 &'a self,
16113 position: Anchor,
16114 buffer: &'a MultiBufferSnapshot,
16115 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16116 let read_highlights = self
16117 .background_highlights
16118 .get(&TypeId::of::<DocumentHighlightRead>())
16119 .map(|h| &h.1);
16120 let write_highlights = self
16121 .background_highlights
16122 .get(&TypeId::of::<DocumentHighlightWrite>())
16123 .map(|h| &h.1);
16124 let left_position = position.bias_left(buffer);
16125 let right_position = position.bias_right(buffer);
16126 read_highlights
16127 .into_iter()
16128 .chain(write_highlights)
16129 .flat_map(move |ranges| {
16130 let start_ix = match ranges.binary_search_by(|probe| {
16131 let cmp = probe.end.cmp(&left_position, buffer);
16132 if cmp.is_ge() {
16133 Ordering::Greater
16134 } else {
16135 Ordering::Less
16136 }
16137 }) {
16138 Ok(i) | Err(i) => i,
16139 };
16140
16141 ranges[start_ix..]
16142 .iter()
16143 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16144 })
16145 }
16146
16147 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16148 self.background_highlights
16149 .get(&TypeId::of::<T>())
16150 .map_or(false, |(_, highlights)| !highlights.is_empty())
16151 }
16152
16153 pub fn background_highlights_in_range(
16154 &self,
16155 search_range: Range<Anchor>,
16156 display_snapshot: &DisplaySnapshot,
16157 theme: &ThemeColors,
16158 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16159 let mut results = Vec::new();
16160 for (color_fetcher, ranges) in self.background_highlights.values() {
16161 let color = color_fetcher(theme);
16162 let start_ix = match ranges.binary_search_by(|probe| {
16163 let cmp = probe
16164 .end
16165 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16166 if cmp.is_gt() {
16167 Ordering::Greater
16168 } else {
16169 Ordering::Less
16170 }
16171 }) {
16172 Ok(i) | Err(i) => i,
16173 };
16174 for range in &ranges[start_ix..] {
16175 if range
16176 .start
16177 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16178 .is_ge()
16179 {
16180 break;
16181 }
16182
16183 let start = range.start.to_display_point(display_snapshot);
16184 let end = range.end.to_display_point(display_snapshot);
16185 results.push((start..end, color))
16186 }
16187 }
16188 results
16189 }
16190
16191 pub fn background_highlight_row_ranges<T: 'static>(
16192 &self,
16193 search_range: Range<Anchor>,
16194 display_snapshot: &DisplaySnapshot,
16195 count: usize,
16196 ) -> Vec<RangeInclusive<DisplayPoint>> {
16197 let mut results = Vec::new();
16198 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16199 return vec![];
16200 };
16201
16202 let start_ix = match ranges.binary_search_by(|probe| {
16203 let cmp = probe
16204 .end
16205 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16206 if cmp.is_gt() {
16207 Ordering::Greater
16208 } else {
16209 Ordering::Less
16210 }
16211 }) {
16212 Ok(i) | Err(i) => i,
16213 };
16214 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16215 if let (Some(start_display), Some(end_display)) = (start, end) {
16216 results.push(
16217 start_display.to_display_point(display_snapshot)
16218 ..=end_display.to_display_point(display_snapshot),
16219 );
16220 }
16221 };
16222 let mut start_row: Option<Point> = None;
16223 let mut end_row: Option<Point> = None;
16224 if ranges.len() > count {
16225 return Vec::new();
16226 }
16227 for range in &ranges[start_ix..] {
16228 if range
16229 .start
16230 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16231 .is_ge()
16232 {
16233 break;
16234 }
16235 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16236 if let Some(current_row) = &end_row {
16237 if end.row == current_row.row {
16238 continue;
16239 }
16240 }
16241 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16242 if start_row.is_none() {
16243 assert_eq!(end_row, None);
16244 start_row = Some(start);
16245 end_row = Some(end);
16246 continue;
16247 }
16248 if let Some(current_end) = end_row.as_mut() {
16249 if start.row > current_end.row + 1 {
16250 push_region(start_row, end_row);
16251 start_row = Some(start);
16252 end_row = Some(end);
16253 } else {
16254 // Merge two hunks.
16255 *current_end = end;
16256 }
16257 } else {
16258 unreachable!();
16259 }
16260 }
16261 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16262 push_region(start_row, end_row);
16263 results
16264 }
16265
16266 pub fn gutter_highlights_in_range(
16267 &self,
16268 search_range: Range<Anchor>,
16269 display_snapshot: &DisplaySnapshot,
16270 cx: &App,
16271 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16272 let mut results = Vec::new();
16273 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16274 let color = color_fetcher(cx);
16275 let start_ix = match ranges.binary_search_by(|probe| {
16276 let cmp = probe
16277 .end
16278 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16279 if cmp.is_gt() {
16280 Ordering::Greater
16281 } else {
16282 Ordering::Less
16283 }
16284 }) {
16285 Ok(i) | Err(i) => i,
16286 };
16287 for range in &ranges[start_ix..] {
16288 if range
16289 .start
16290 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16291 .is_ge()
16292 {
16293 break;
16294 }
16295
16296 let start = range.start.to_display_point(display_snapshot);
16297 let end = range.end.to_display_point(display_snapshot);
16298 results.push((start..end, color))
16299 }
16300 }
16301 results
16302 }
16303
16304 /// Get the text ranges corresponding to the redaction query
16305 pub fn redacted_ranges(
16306 &self,
16307 search_range: Range<Anchor>,
16308 display_snapshot: &DisplaySnapshot,
16309 cx: &App,
16310 ) -> Vec<Range<DisplayPoint>> {
16311 display_snapshot
16312 .buffer_snapshot
16313 .redacted_ranges(search_range, |file| {
16314 if let Some(file) = file {
16315 file.is_private()
16316 && EditorSettings::get(
16317 Some(SettingsLocation {
16318 worktree_id: file.worktree_id(cx),
16319 path: file.path().as_ref(),
16320 }),
16321 cx,
16322 )
16323 .redact_private_values
16324 } else {
16325 false
16326 }
16327 })
16328 .map(|range| {
16329 range.start.to_display_point(display_snapshot)
16330 ..range.end.to_display_point(display_snapshot)
16331 })
16332 .collect()
16333 }
16334
16335 pub fn highlight_text<T: 'static>(
16336 &mut self,
16337 ranges: Vec<Range<Anchor>>,
16338 style: HighlightStyle,
16339 cx: &mut Context<Self>,
16340 ) {
16341 self.display_map.update(cx, |map, _| {
16342 map.highlight_text(TypeId::of::<T>(), ranges, style)
16343 });
16344 cx.notify();
16345 }
16346
16347 pub(crate) fn highlight_inlays<T: 'static>(
16348 &mut self,
16349 highlights: Vec<InlayHighlight>,
16350 style: HighlightStyle,
16351 cx: &mut Context<Self>,
16352 ) {
16353 self.display_map.update(cx, |map, _| {
16354 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16355 });
16356 cx.notify();
16357 }
16358
16359 pub fn text_highlights<'a, T: 'static>(
16360 &'a self,
16361 cx: &'a App,
16362 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16363 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16364 }
16365
16366 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16367 let cleared = self
16368 .display_map
16369 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16370 if cleared {
16371 cx.notify();
16372 }
16373 }
16374
16375 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16376 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16377 && self.focus_handle.is_focused(window)
16378 }
16379
16380 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16381 self.show_cursor_when_unfocused = is_enabled;
16382 cx.notify();
16383 }
16384
16385 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16386 cx.notify();
16387 }
16388
16389 fn on_buffer_event(
16390 &mut self,
16391 multibuffer: &Entity<MultiBuffer>,
16392 event: &multi_buffer::Event,
16393 window: &mut Window,
16394 cx: &mut Context<Self>,
16395 ) {
16396 match event {
16397 multi_buffer::Event::Edited {
16398 singleton_buffer_edited,
16399 edited_buffer: buffer_edited,
16400 } => {
16401 self.scrollbar_marker_state.dirty = true;
16402 self.active_indent_guides_state.dirty = true;
16403 self.refresh_active_diagnostics(cx);
16404 self.refresh_code_actions(window, cx);
16405 if self.has_active_inline_completion() {
16406 self.update_visible_inline_completion(window, cx);
16407 }
16408 if let Some(buffer) = buffer_edited {
16409 let buffer_id = buffer.read(cx).remote_id();
16410 if !self.registered_buffers.contains_key(&buffer_id) {
16411 if let Some(project) = self.project.as_ref() {
16412 project.update(cx, |project, cx| {
16413 self.registered_buffers.insert(
16414 buffer_id,
16415 project.register_buffer_with_language_servers(&buffer, cx),
16416 );
16417 })
16418 }
16419 }
16420 }
16421 cx.emit(EditorEvent::BufferEdited);
16422 cx.emit(SearchEvent::MatchesInvalidated);
16423 if *singleton_buffer_edited {
16424 if let Some(project) = &self.project {
16425 #[allow(clippy::mutable_key_type)]
16426 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16427 multibuffer
16428 .all_buffers()
16429 .into_iter()
16430 .filter_map(|buffer| {
16431 buffer.update(cx, |buffer, cx| {
16432 let language = buffer.language()?;
16433 let should_discard = project.update(cx, |project, cx| {
16434 project.is_local()
16435 && !project.has_language_servers_for(buffer, cx)
16436 });
16437 should_discard.not().then_some(language.clone())
16438 })
16439 })
16440 .collect::<HashSet<_>>()
16441 });
16442 if !languages_affected.is_empty() {
16443 self.refresh_inlay_hints(
16444 InlayHintRefreshReason::BufferEdited(languages_affected),
16445 cx,
16446 );
16447 }
16448 }
16449 }
16450
16451 let Some(project) = &self.project else { return };
16452 let (telemetry, is_via_ssh) = {
16453 let project = project.read(cx);
16454 let telemetry = project.client().telemetry().clone();
16455 let is_via_ssh = project.is_via_ssh();
16456 (telemetry, is_via_ssh)
16457 };
16458 refresh_linked_ranges(self, window, cx);
16459 telemetry.log_edit_event("editor", is_via_ssh);
16460 }
16461 multi_buffer::Event::ExcerptsAdded {
16462 buffer,
16463 predecessor,
16464 excerpts,
16465 } => {
16466 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16467 let buffer_id = buffer.read(cx).remote_id();
16468 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16469 if let Some(project) = &self.project {
16470 get_uncommitted_diff_for_buffer(
16471 project,
16472 [buffer.clone()],
16473 self.buffer.clone(),
16474 cx,
16475 )
16476 .detach();
16477 }
16478 }
16479 cx.emit(EditorEvent::ExcerptsAdded {
16480 buffer: buffer.clone(),
16481 predecessor: *predecessor,
16482 excerpts: excerpts.clone(),
16483 });
16484 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16485 }
16486 multi_buffer::Event::ExcerptsRemoved { ids } => {
16487 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16488 let buffer = self.buffer.read(cx);
16489 self.registered_buffers
16490 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16491 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16492 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16493 }
16494 multi_buffer::Event::ExcerptsEdited {
16495 excerpt_ids,
16496 buffer_ids,
16497 } => {
16498 self.display_map.update(cx, |map, cx| {
16499 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16500 });
16501 cx.emit(EditorEvent::ExcerptsEdited {
16502 ids: excerpt_ids.clone(),
16503 })
16504 }
16505 multi_buffer::Event::ExcerptsExpanded { ids } => {
16506 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16507 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16508 }
16509 multi_buffer::Event::Reparsed(buffer_id) => {
16510 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16511 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16512
16513 cx.emit(EditorEvent::Reparsed(*buffer_id));
16514 }
16515 multi_buffer::Event::DiffHunksToggled => {
16516 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16517 }
16518 multi_buffer::Event::LanguageChanged(buffer_id) => {
16519 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16520 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16521 cx.emit(EditorEvent::Reparsed(*buffer_id));
16522 cx.notify();
16523 }
16524 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16525 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16526 multi_buffer::Event::FileHandleChanged
16527 | multi_buffer::Event::Reloaded
16528 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16529 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16530 multi_buffer::Event::DiagnosticsUpdated => {
16531 self.refresh_active_diagnostics(cx);
16532 self.refresh_inline_diagnostics(true, window, cx);
16533 self.scrollbar_marker_state.dirty = true;
16534 cx.notify();
16535 }
16536 _ => {}
16537 };
16538 }
16539
16540 fn on_display_map_changed(
16541 &mut self,
16542 _: Entity<DisplayMap>,
16543 _: &mut Window,
16544 cx: &mut Context<Self>,
16545 ) {
16546 cx.notify();
16547 }
16548
16549 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16550 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16551 self.update_edit_prediction_settings(cx);
16552 self.refresh_inline_completion(true, false, window, cx);
16553 self.refresh_inlay_hints(
16554 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16555 self.selections.newest_anchor().head(),
16556 &self.buffer.read(cx).snapshot(cx),
16557 cx,
16558 )),
16559 cx,
16560 );
16561
16562 let old_cursor_shape = self.cursor_shape;
16563
16564 {
16565 let editor_settings = EditorSettings::get_global(cx);
16566 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16567 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16568 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16569 }
16570
16571 if old_cursor_shape != self.cursor_shape {
16572 cx.emit(EditorEvent::CursorShapeChanged);
16573 }
16574
16575 let project_settings = ProjectSettings::get_global(cx);
16576 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16577
16578 if self.mode == EditorMode::Full {
16579 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16580 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16581 if self.show_inline_diagnostics != show_inline_diagnostics {
16582 self.show_inline_diagnostics = show_inline_diagnostics;
16583 self.refresh_inline_diagnostics(false, window, cx);
16584 }
16585
16586 if self.git_blame_inline_enabled != inline_blame_enabled {
16587 self.toggle_git_blame_inline_internal(false, window, cx);
16588 }
16589 }
16590
16591 cx.notify();
16592 }
16593
16594 pub fn set_searchable(&mut self, searchable: bool) {
16595 self.searchable = searchable;
16596 }
16597
16598 pub fn searchable(&self) -> bool {
16599 self.searchable
16600 }
16601
16602 fn open_proposed_changes_editor(
16603 &mut self,
16604 _: &OpenProposedChangesEditor,
16605 window: &mut Window,
16606 cx: &mut Context<Self>,
16607 ) {
16608 let Some(workspace) = self.workspace() else {
16609 cx.propagate();
16610 return;
16611 };
16612
16613 let selections = self.selections.all::<usize>(cx);
16614 let multi_buffer = self.buffer.read(cx);
16615 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16616 let mut new_selections_by_buffer = HashMap::default();
16617 for selection in selections {
16618 for (buffer, range, _) in
16619 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16620 {
16621 let mut range = range.to_point(buffer);
16622 range.start.column = 0;
16623 range.end.column = buffer.line_len(range.end.row);
16624 new_selections_by_buffer
16625 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16626 .or_insert(Vec::new())
16627 .push(range)
16628 }
16629 }
16630
16631 let proposed_changes_buffers = new_selections_by_buffer
16632 .into_iter()
16633 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16634 .collect::<Vec<_>>();
16635 let proposed_changes_editor = cx.new(|cx| {
16636 ProposedChangesEditor::new(
16637 "Proposed changes",
16638 proposed_changes_buffers,
16639 self.project.clone(),
16640 window,
16641 cx,
16642 )
16643 });
16644
16645 window.defer(cx, move |window, cx| {
16646 workspace.update(cx, |workspace, cx| {
16647 workspace.active_pane().update(cx, |pane, cx| {
16648 pane.add_item(
16649 Box::new(proposed_changes_editor),
16650 true,
16651 true,
16652 None,
16653 window,
16654 cx,
16655 );
16656 });
16657 });
16658 });
16659 }
16660
16661 pub fn open_excerpts_in_split(
16662 &mut self,
16663 _: &OpenExcerptsSplit,
16664 window: &mut Window,
16665 cx: &mut Context<Self>,
16666 ) {
16667 self.open_excerpts_common(None, true, window, cx)
16668 }
16669
16670 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16671 self.open_excerpts_common(None, false, window, cx)
16672 }
16673
16674 fn open_excerpts_common(
16675 &mut self,
16676 jump_data: Option<JumpData>,
16677 split: bool,
16678 window: &mut Window,
16679 cx: &mut Context<Self>,
16680 ) {
16681 let Some(workspace) = self.workspace() else {
16682 cx.propagate();
16683 return;
16684 };
16685
16686 if self.buffer.read(cx).is_singleton() {
16687 cx.propagate();
16688 return;
16689 }
16690
16691 let mut new_selections_by_buffer = HashMap::default();
16692 match &jump_data {
16693 Some(JumpData::MultiBufferPoint {
16694 excerpt_id,
16695 position,
16696 anchor,
16697 line_offset_from_top,
16698 }) => {
16699 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16700 if let Some(buffer) = multi_buffer_snapshot
16701 .buffer_id_for_excerpt(*excerpt_id)
16702 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16703 {
16704 let buffer_snapshot = buffer.read(cx).snapshot();
16705 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16706 language::ToPoint::to_point(anchor, &buffer_snapshot)
16707 } else {
16708 buffer_snapshot.clip_point(*position, Bias::Left)
16709 };
16710 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16711 new_selections_by_buffer.insert(
16712 buffer,
16713 (
16714 vec![jump_to_offset..jump_to_offset],
16715 Some(*line_offset_from_top),
16716 ),
16717 );
16718 }
16719 }
16720 Some(JumpData::MultiBufferRow {
16721 row,
16722 line_offset_from_top,
16723 }) => {
16724 let point = MultiBufferPoint::new(row.0, 0);
16725 if let Some((buffer, buffer_point, _)) =
16726 self.buffer.read(cx).point_to_buffer_point(point, cx)
16727 {
16728 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16729 new_selections_by_buffer
16730 .entry(buffer)
16731 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16732 .0
16733 .push(buffer_offset..buffer_offset)
16734 }
16735 }
16736 None => {
16737 let selections = self.selections.all::<usize>(cx);
16738 let multi_buffer = self.buffer.read(cx);
16739 for selection in selections {
16740 for (snapshot, range, _, anchor) in multi_buffer
16741 .snapshot(cx)
16742 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16743 {
16744 if let Some(anchor) = anchor {
16745 // selection is in a deleted hunk
16746 let Some(buffer_id) = anchor.buffer_id else {
16747 continue;
16748 };
16749 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16750 continue;
16751 };
16752 let offset = text::ToOffset::to_offset(
16753 &anchor.text_anchor,
16754 &buffer_handle.read(cx).snapshot(),
16755 );
16756 let range = offset..offset;
16757 new_selections_by_buffer
16758 .entry(buffer_handle)
16759 .or_insert((Vec::new(), None))
16760 .0
16761 .push(range)
16762 } else {
16763 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16764 else {
16765 continue;
16766 };
16767 new_selections_by_buffer
16768 .entry(buffer_handle)
16769 .or_insert((Vec::new(), None))
16770 .0
16771 .push(range)
16772 }
16773 }
16774 }
16775 }
16776 }
16777
16778 if new_selections_by_buffer.is_empty() {
16779 return;
16780 }
16781
16782 // We defer the pane interaction because we ourselves are a workspace item
16783 // and activating a new item causes the pane to call a method on us reentrantly,
16784 // which panics if we're on the stack.
16785 window.defer(cx, move |window, cx| {
16786 workspace.update(cx, |workspace, cx| {
16787 let pane = if split {
16788 workspace.adjacent_pane(window, cx)
16789 } else {
16790 workspace.active_pane().clone()
16791 };
16792
16793 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16794 let editor = buffer
16795 .read(cx)
16796 .file()
16797 .is_none()
16798 .then(|| {
16799 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16800 // so `workspace.open_project_item` will never find them, always opening a new editor.
16801 // Instead, we try to activate the existing editor in the pane first.
16802 let (editor, pane_item_index) =
16803 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16804 let editor = item.downcast::<Editor>()?;
16805 let singleton_buffer =
16806 editor.read(cx).buffer().read(cx).as_singleton()?;
16807 if singleton_buffer == buffer {
16808 Some((editor, i))
16809 } else {
16810 None
16811 }
16812 })?;
16813 pane.update(cx, |pane, cx| {
16814 pane.activate_item(pane_item_index, true, true, window, cx)
16815 });
16816 Some(editor)
16817 })
16818 .flatten()
16819 .unwrap_or_else(|| {
16820 workspace.open_project_item::<Self>(
16821 pane.clone(),
16822 buffer,
16823 true,
16824 true,
16825 window,
16826 cx,
16827 )
16828 });
16829
16830 editor.update(cx, |editor, cx| {
16831 let autoscroll = match scroll_offset {
16832 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16833 None => Autoscroll::newest(),
16834 };
16835 let nav_history = editor.nav_history.take();
16836 editor.change_selections(Some(autoscroll), window, cx, |s| {
16837 s.select_ranges(ranges);
16838 });
16839 editor.nav_history = nav_history;
16840 });
16841 }
16842 })
16843 });
16844 }
16845
16846 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16847 let snapshot = self.buffer.read(cx).read(cx);
16848 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16849 Some(
16850 ranges
16851 .iter()
16852 .map(move |range| {
16853 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16854 })
16855 .collect(),
16856 )
16857 }
16858
16859 fn selection_replacement_ranges(
16860 &self,
16861 range: Range<OffsetUtf16>,
16862 cx: &mut App,
16863 ) -> Vec<Range<OffsetUtf16>> {
16864 let selections = self.selections.all::<OffsetUtf16>(cx);
16865 let newest_selection = selections
16866 .iter()
16867 .max_by_key(|selection| selection.id)
16868 .unwrap();
16869 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16870 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16871 let snapshot = self.buffer.read(cx).read(cx);
16872 selections
16873 .into_iter()
16874 .map(|mut selection| {
16875 selection.start.0 =
16876 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16877 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16878 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16879 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16880 })
16881 .collect()
16882 }
16883
16884 fn report_editor_event(
16885 &self,
16886 event_type: &'static str,
16887 file_extension: Option<String>,
16888 cx: &App,
16889 ) {
16890 if cfg!(any(test, feature = "test-support")) {
16891 return;
16892 }
16893
16894 let Some(project) = &self.project else { return };
16895
16896 // If None, we are in a file without an extension
16897 let file = self
16898 .buffer
16899 .read(cx)
16900 .as_singleton()
16901 .and_then(|b| b.read(cx).file());
16902 let file_extension = file_extension.or(file
16903 .as_ref()
16904 .and_then(|file| Path::new(file.file_name(cx)).extension())
16905 .and_then(|e| e.to_str())
16906 .map(|a| a.to_string()));
16907
16908 let vim_mode = cx
16909 .global::<SettingsStore>()
16910 .raw_user_settings()
16911 .get("vim_mode")
16912 == Some(&serde_json::Value::Bool(true));
16913
16914 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16915 let copilot_enabled = edit_predictions_provider
16916 == language::language_settings::EditPredictionProvider::Copilot;
16917 let copilot_enabled_for_language = self
16918 .buffer
16919 .read(cx)
16920 .language_settings(cx)
16921 .show_edit_predictions;
16922
16923 let project = project.read(cx);
16924 telemetry::event!(
16925 event_type,
16926 file_extension,
16927 vim_mode,
16928 copilot_enabled,
16929 copilot_enabled_for_language,
16930 edit_predictions_provider,
16931 is_via_ssh = project.is_via_ssh(),
16932 );
16933 }
16934
16935 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16936 /// with each line being an array of {text, highlight} objects.
16937 fn copy_highlight_json(
16938 &mut self,
16939 _: &CopyHighlightJson,
16940 window: &mut Window,
16941 cx: &mut Context<Self>,
16942 ) {
16943 #[derive(Serialize)]
16944 struct Chunk<'a> {
16945 text: String,
16946 highlight: Option<&'a str>,
16947 }
16948
16949 let snapshot = self.buffer.read(cx).snapshot(cx);
16950 let range = self
16951 .selected_text_range(false, window, cx)
16952 .and_then(|selection| {
16953 if selection.range.is_empty() {
16954 None
16955 } else {
16956 Some(selection.range)
16957 }
16958 })
16959 .unwrap_or_else(|| 0..snapshot.len());
16960
16961 let chunks = snapshot.chunks(range, true);
16962 let mut lines = Vec::new();
16963 let mut line: VecDeque<Chunk> = VecDeque::new();
16964
16965 let Some(style) = self.style.as_ref() else {
16966 return;
16967 };
16968
16969 for chunk in chunks {
16970 let highlight = chunk
16971 .syntax_highlight_id
16972 .and_then(|id| id.name(&style.syntax));
16973 let mut chunk_lines = chunk.text.split('\n').peekable();
16974 while let Some(text) = chunk_lines.next() {
16975 let mut merged_with_last_token = false;
16976 if let Some(last_token) = line.back_mut() {
16977 if last_token.highlight == highlight {
16978 last_token.text.push_str(text);
16979 merged_with_last_token = true;
16980 }
16981 }
16982
16983 if !merged_with_last_token {
16984 line.push_back(Chunk {
16985 text: text.into(),
16986 highlight,
16987 });
16988 }
16989
16990 if chunk_lines.peek().is_some() {
16991 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16992 line.pop_front();
16993 }
16994 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16995 line.pop_back();
16996 }
16997
16998 lines.push(mem::take(&mut line));
16999 }
17000 }
17001 }
17002
17003 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
17004 return;
17005 };
17006 cx.write_to_clipboard(ClipboardItem::new_string(lines));
17007 }
17008
17009 pub fn open_context_menu(
17010 &mut self,
17011 _: &OpenContextMenu,
17012 window: &mut Window,
17013 cx: &mut Context<Self>,
17014 ) {
17015 self.request_autoscroll(Autoscroll::newest(), cx);
17016 let position = self.selections.newest_display(cx).start;
17017 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
17018 }
17019
17020 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
17021 &self.inlay_hint_cache
17022 }
17023
17024 pub fn replay_insert_event(
17025 &mut self,
17026 text: &str,
17027 relative_utf16_range: Option<Range<isize>>,
17028 window: &mut Window,
17029 cx: &mut Context<Self>,
17030 ) {
17031 if !self.input_enabled {
17032 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17033 return;
17034 }
17035 if let Some(relative_utf16_range) = relative_utf16_range {
17036 let selections = self.selections.all::<OffsetUtf16>(cx);
17037 self.change_selections(None, window, cx, |s| {
17038 let new_ranges = selections.into_iter().map(|range| {
17039 let start = OffsetUtf16(
17040 range
17041 .head()
17042 .0
17043 .saturating_add_signed(relative_utf16_range.start),
17044 );
17045 let end = OffsetUtf16(
17046 range
17047 .head()
17048 .0
17049 .saturating_add_signed(relative_utf16_range.end),
17050 );
17051 start..end
17052 });
17053 s.select_ranges(new_ranges);
17054 });
17055 }
17056
17057 self.handle_input(text, window, cx);
17058 }
17059
17060 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
17061 let Some(provider) = self.semantics_provider.as_ref() else {
17062 return false;
17063 };
17064
17065 let mut supports = false;
17066 self.buffer().update(cx, |this, cx| {
17067 this.for_each_buffer(|buffer| {
17068 supports |= provider.supports_inlay_hints(buffer, cx);
17069 });
17070 });
17071
17072 supports
17073 }
17074
17075 pub fn is_focused(&self, window: &Window) -> bool {
17076 self.focus_handle.is_focused(window)
17077 }
17078
17079 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17080 cx.emit(EditorEvent::Focused);
17081
17082 if let Some(descendant) = self
17083 .last_focused_descendant
17084 .take()
17085 .and_then(|descendant| descendant.upgrade())
17086 {
17087 window.focus(&descendant);
17088 } else {
17089 if let Some(blame) = self.blame.as_ref() {
17090 blame.update(cx, GitBlame::focus)
17091 }
17092
17093 self.blink_manager.update(cx, BlinkManager::enable);
17094 self.show_cursor_names(window, cx);
17095 self.buffer.update(cx, |buffer, cx| {
17096 buffer.finalize_last_transaction(cx);
17097 if self.leader_peer_id.is_none() {
17098 buffer.set_active_selections(
17099 &self.selections.disjoint_anchors(),
17100 self.selections.line_mode,
17101 self.cursor_shape,
17102 cx,
17103 );
17104 }
17105 });
17106 }
17107 }
17108
17109 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17110 cx.emit(EditorEvent::FocusedIn)
17111 }
17112
17113 fn handle_focus_out(
17114 &mut self,
17115 event: FocusOutEvent,
17116 _window: &mut Window,
17117 cx: &mut Context<Self>,
17118 ) {
17119 if event.blurred != self.focus_handle {
17120 self.last_focused_descendant = Some(event.blurred);
17121 }
17122 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17123 }
17124
17125 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17126 self.blink_manager.update(cx, BlinkManager::disable);
17127 self.buffer
17128 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17129
17130 if let Some(blame) = self.blame.as_ref() {
17131 blame.update(cx, GitBlame::blur)
17132 }
17133 if !self.hover_state.focused(window, cx) {
17134 hide_hover(self, cx);
17135 }
17136 if !self
17137 .context_menu
17138 .borrow()
17139 .as_ref()
17140 .is_some_and(|context_menu| context_menu.focused(window, cx))
17141 {
17142 self.hide_context_menu(window, cx);
17143 }
17144 self.discard_inline_completion(false, cx);
17145 cx.emit(EditorEvent::Blurred);
17146 cx.notify();
17147 }
17148
17149 pub fn register_action<A: Action>(
17150 &mut self,
17151 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17152 ) -> Subscription {
17153 let id = self.next_editor_action_id.post_inc();
17154 let listener = Arc::new(listener);
17155 self.editor_actions.borrow_mut().insert(
17156 id,
17157 Box::new(move |window, _| {
17158 let listener = listener.clone();
17159 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17160 let action = action.downcast_ref().unwrap();
17161 if phase == DispatchPhase::Bubble {
17162 listener(action, window, cx)
17163 }
17164 })
17165 }),
17166 );
17167
17168 let editor_actions = self.editor_actions.clone();
17169 Subscription::new(move || {
17170 editor_actions.borrow_mut().remove(&id);
17171 })
17172 }
17173
17174 pub fn file_header_size(&self) -> u32 {
17175 FILE_HEADER_HEIGHT
17176 }
17177
17178 pub fn restore(
17179 &mut self,
17180 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17181 window: &mut Window,
17182 cx: &mut Context<Self>,
17183 ) {
17184 let workspace = self.workspace();
17185 let project = self.project.as_ref();
17186 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17187 let mut tasks = Vec::new();
17188 for (buffer_id, changes) in revert_changes {
17189 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17190 buffer.update(cx, |buffer, cx| {
17191 buffer.edit(
17192 changes
17193 .into_iter()
17194 .map(|(range, text)| (range, text.to_string())),
17195 None,
17196 cx,
17197 );
17198 });
17199
17200 if let Some(project) =
17201 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17202 {
17203 project.update(cx, |project, cx| {
17204 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17205 })
17206 }
17207 }
17208 }
17209 tasks
17210 });
17211 cx.spawn_in(window, async move |_, cx| {
17212 for (buffer, task) in save_tasks {
17213 let result = task.await;
17214 if result.is_err() {
17215 let Some(path) = buffer
17216 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17217 .ok()
17218 else {
17219 continue;
17220 };
17221 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17222 let Some(task) = cx
17223 .update_window_entity(&workspace, |workspace, window, cx| {
17224 workspace
17225 .open_path_preview(path, None, false, false, false, window, cx)
17226 })
17227 .ok()
17228 else {
17229 continue;
17230 };
17231 task.await.log_err();
17232 }
17233 }
17234 }
17235 })
17236 .detach();
17237 self.change_selections(None, window, cx, |selections| selections.refresh());
17238 }
17239
17240 pub fn to_pixel_point(
17241 &self,
17242 source: multi_buffer::Anchor,
17243 editor_snapshot: &EditorSnapshot,
17244 window: &mut Window,
17245 ) -> Option<gpui::Point<Pixels>> {
17246 let source_point = source.to_display_point(editor_snapshot);
17247 self.display_to_pixel_point(source_point, editor_snapshot, window)
17248 }
17249
17250 pub fn display_to_pixel_point(
17251 &self,
17252 source: DisplayPoint,
17253 editor_snapshot: &EditorSnapshot,
17254 window: &mut Window,
17255 ) -> Option<gpui::Point<Pixels>> {
17256 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17257 let text_layout_details = self.text_layout_details(window);
17258 let scroll_top = text_layout_details
17259 .scroll_anchor
17260 .scroll_position(editor_snapshot)
17261 .y;
17262
17263 if source.row().as_f32() < scroll_top.floor() {
17264 return None;
17265 }
17266 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17267 let source_y = line_height * (source.row().as_f32() - scroll_top);
17268 Some(gpui::Point::new(source_x, source_y))
17269 }
17270
17271 pub fn has_visible_completions_menu(&self) -> bool {
17272 !self.edit_prediction_preview_is_active()
17273 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17274 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17275 })
17276 }
17277
17278 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17279 self.addons
17280 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17281 }
17282
17283 pub fn unregister_addon<T: Addon>(&mut self) {
17284 self.addons.remove(&std::any::TypeId::of::<T>());
17285 }
17286
17287 pub fn addon<T: Addon>(&self) -> Option<&T> {
17288 let type_id = std::any::TypeId::of::<T>();
17289 self.addons
17290 .get(&type_id)
17291 .and_then(|item| item.to_any().downcast_ref::<T>())
17292 }
17293
17294 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17295 let text_layout_details = self.text_layout_details(window);
17296 let style = &text_layout_details.editor_style;
17297 let font_id = window.text_system().resolve_font(&style.text.font());
17298 let font_size = style.text.font_size.to_pixels(window.rem_size());
17299 let line_height = style.text.line_height_in_pixels(window.rem_size());
17300 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17301
17302 gpui::Size::new(em_width, line_height)
17303 }
17304
17305 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17306 self.load_diff_task.clone()
17307 }
17308
17309 fn read_metadata_from_db(
17310 &mut self,
17311 item_id: u64,
17312 workspace_id: WorkspaceId,
17313 window: &mut Window,
17314 cx: &mut Context<Editor>,
17315 ) {
17316 if self.is_singleton(cx)
17317 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17318 {
17319 let buffer_snapshot = OnceCell::new();
17320
17321 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17322 if !selections.is_empty() {
17323 let snapshot =
17324 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17325 self.change_selections(None, window, cx, |s| {
17326 s.select_ranges(selections.into_iter().map(|(start, end)| {
17327 snapshot.clip_offset(start, Bias::Left)
17328 ..snapshot.clip_offset(end, Bias::Right)
17329 }));
17330 });
17331 }
17332 };
17333
17334 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17335 if !folds.is_empty() {
17336 let snapshot =
17337 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17338 self.fold_ranges(
17339 folds
17340 .into_iter()
17341 .map(|(start, end)| {
17342 snapshot.clip_offset(start, Bias::Left)
17343 ..snapshot.clip_offset(end, Bias::Right)
17344 })
17345 .collect(),
17346 false,
17347 window,
17348 cx,
17349 );
17350 }
17351 }
17352 }
17353
17354 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17355 }
17356}
17357
17358fn insert_extra_newline_brackets(
17359 buffer: &MultiBufferSnapshot,
17360 range: Range<usize>,
17361 language: &language::LanguageScope,
17362) -> bool {
17363 let leading_whitespace_len = buffer
17364 .reversed_chars_at(range.start)
17365 .take_while(|c| c.is_whitespace() && *c != '\n')
17366 .map(|c| c.len_utf8())
17367 .sum::<usize>();
17368 let trailing_whitespace_len = buffer
17369 .chars_at(range.end)
17370 .take_while(|c| c.is_whitespace() && *c != '\n')
17371 .map(|c| c.len_utf8())
17372 .sum::<usize>();
17373 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17374
17375 language.brackets().any(|(pair, enabled)| {
17376 let pair_start = pair.start.trim_end();
17377 let pair_end = pair.end.trim_start();
17378
17379 enabled
17380 && pair.newline
17381 && buffer.contains_str_at(range.end, pair_end)
17382 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17383 })
17384}
17385
17386fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17387 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17388 [(buffer, range, _)] => (*buffer, range.clone()),
17389 _ => return false,
17390 };
17391 let pair = {
17392 let mut result: Option<BracketMatch> = None;
17393
17394 for pair in buffer
17395 .all_bracket_ranges(range.clone())
17396 .filter(move |pair| {
17397 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17398 })
17399 {
17400 let len = pair.close_range.end - pair.open_range.start;
17401
17402 if let Some(existing) = &result {
17403 let existing_len = existing.close_range.end - existing.open_range.start;
17404 if len > existing_len {
17405 continue;
17406 }
17407 }
17408
17409 result = Some(pair);
17410 }
17411
17412 result
17413 };
17414 let Some(pair) = pair else {
17415 return false;
17416 };
17417 pair.newline_only
17418 && buffer
17419 .chars_for_range(pair.open_range.end..range.start)
17420 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17421 .all(|c| c.is_whitespace() && c != '\n')
17422}
17423
17424fn get_uncommitted_diff_for_buffer(
17425 project: &Entity<Project>,
17426 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17427 buffer: Entity<MultiBuffer>,
17428 cx: &mut App,
17429) -> Task<()> {
17430 let mut tasks = Vec::new();
17431 project.update(cx, |project, cx| {
17432 for buffer in buffers {
17433 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17434 }
17435 });
17436 cx.spawn(async move |cx| {
17437 let diffs = future::join_all(tasks).await;
17438 buffer
17439 .update(cx, |buffer, cx| {
17440 for diff in diffs.into_iter().flatten() {
17441 buffer.add_diff(diff, cx);
17442 }
17443 })
17444 .ok();
17445 })
17446}
17447
17448fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17449 let tab_size = tab_size.get() as usize;
17450 let mut width = offset;
17451
17452 for ch in text.chars() {
17453 width += if ch == '\t' {
17454 tab_size - (width % tab_size)
17455 } else {
17456 1
17457 };
17458 }
17459
17460 width - offset
17461}
17462
17463#[cfg(test)]
17464mod tests {
17465 use super::*;
17466
17467 #[test]
17468 fn test_string_size_with_expanded_tabs() {
17469 let nz = |val| NonZeroU32::new(val).unwrap();
17470 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17471 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17472 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17473 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17474 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17475 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17476 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17477 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17478 }
17479}
17480
17481/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17482struct WordBreakingTokenizer<'a> {
17483 input: &'a str,
17484}
17485
17486impl<'a> WordBreakingTokenizer<'a> {
17487 fn new(input: &'a str) -> Self {
17488 Self { input }
17489 }
17490}
17491
17492fn is_char_ideographic(ch: char) -> bool {
17493 use unicode_script::Script::*;
17494 use unicode_script::UnicodeScript;
17495 matches!(ch.script(), Han | Tangut | Yi)
17496}
17497
17498fn is_grapheme_ideographic(text: &str) -> bool {
17499 text.chars().any(is_char_ideographic)
17500}
17501
17502fn is_grapheme_whitespace(text: &str) -> bool {
17503 text.chars().any(|x| x.is_whitespace())
17504}
17505
17506fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17507 text.chars().next().map_or(false, |ch| {
17508 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17509 })
17510}
17511
17512#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17513enum WordBreakToken<'a> {
17514 Word { token: &'a str, grapheme_len: usize },
17515 InlineWhitespace { token: &'a str, grapheme_len: usize },
17516 Newline,
17517}
17518
17519impl<'a> Iterator for WordBreakingTokenizer<'a> {
17520 /// Yields a span, the count of graphemes in the token, and whether it was
17521 /// whitespace. Note that it also breaks at word boundaries.
17522 type Item = WordBreakToken<'a>;
17523
17524 fn next(&mut self) -> Option<Self::Item> {
17525 use unicode_segmentation::UnicodeSegmentation;
17526 if self.input.is_empty() {
17527 return None;
17528 }
17529
17530 let mut iter = self.input.graphemes(true).peekable();
17531 let mut offset = 0;
17532 let mut grapheme_len = 0;
17533 if let Some(first_grapheme) = iter.next() {
17534 let is_newline = first_grapheme == "\n";
17535 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17536 offset += first_grapheme.len();
17537 grapheme_len += 1;
17538 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17539 if let Some(grapheme) = iter.peek().copied() {
17540 if should_stay_with_preceding_ideograph(grapheme) {
17541 offset += grapheme.len();
17542 grapheme_len += 1;
17543 }
17544 }
17545 } else {
17546 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17547 let mut next_word_bound = words.peek().copied();
17548 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17549 next_word_bound = words.next();
17550 }
17551 while let Some(grapheme) = iter.peek().copied() {
17552 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17553 break;
17554 };
17555 if is_grapheme_whitespace(grapheme) != is_whitespace
17556 || (grapheme == "\n") != is_newline
17557 {
17558 break;
17559 };
17560 offset += grapheme.len();
17561 grapheme_len += 1;
17562 iter.next();
17563 }
17564 }
17565 let token = &self.input[..offset];
17566 self.input = &self.input[offset..];
17567 if token == "\n" {
17568 Some(WordBreakToken::Newline)
17569 } else if is_whitespace {
17570 Some(WordBreakToken::InlineWhitespace {
17571 token,
17572 grapheme_len,
17573 })
17574 } else {
17575 Some(WordBreakToken::Word {
17576 token,
17577 grapheme_len,
17578 })
17579 }
17580 } else {
17581 None
17582 }
17583 }
17584}
17585
17586#[test]
17587fn test_word_breaking_tokenizer() {
17588 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17589 ("", &[]),
17590 (" ", &[whitespace(" ", 2)]),
17591 ("Ʒ", &[word("Ʒ", 1)]),
17592 ("Ǽ", &[word("Ǽ", 1)]),
17593 ("⋑", &[word("⋑", 1)]),
17594 ("⋑⋑", &[word("⋑⋑", 2)]),
17595 (
17596 "原理,进而",
17597 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17598 ),
17599 (
17600 "hello world",
17601 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17602 ),
17603 (
17604 "hello, world",
17605 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17606 ),
17607 (
17608 " hello world",
17609 &[
17610 whitespace(" ", 2),
17611 word("hello", 5),
17612 whitespace(" ", 1),
17613 word("world", 5),
17614 ],
17615 ),
17616 (
17617 "这是什么 \n 钢笔",
17618 &[
17619 word("这", 1),
17620 word("是", 1),
17621 word("什", 1),
17622 word("么", 1),
17623 whitespace(" ", 1),
17624 newline(),
17625 whitespace(" ", 1),
17626 word("钢", 1),
17627 word("笔", 1),
17628 ],
17629 ),
17630 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17631 ];
17632
17633 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17634 WordBreakToken::Word {
17635 token,
17636 grapheme_len,
17637 }
17638 }
17639
17640 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17641 WordBreakToken::InlineWhitespace {
17642 token,
17643 grapheme_len,
17644 }
17645 }
17646
17647 fn newline() -> WordBreakToken<'static> {
17648 WordBreakToken::Newline
17649 }
17650
17651 for (input, result) in tests {
17652 assert_eq!(
17653 WordBreakingTokenizer::new(input)
17654 .collect::<Vec<_>>()
17655 .as_slice(),
17656 *result,
17657 );
17658 }
17659}
17660
17661fn wrap_with_prefix(
17662 line_prefix: String,
17663 unwrapped_text: String,
17664 wrap_column: usize,
17665 tab_size: NonZeroU32,
17666 preserve_existing_whitespace: bool,
17667) -> String {
17668 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17669 let mut wrapped_text = String::new();
17670 let mut current_line = line_prefix.clone();
17671
17672 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17673 let mut current_line_len = line_prefix_len;
17674 let mut in_whitespace = false;
17675 for token in tokenizer {
17676 let have_preceding_whitespace = in_whitespace;
17677 match token {
17678 WordBreakToken::Word {
17679 token,
17680 grapheme_len,
17681 } => {
17682 in_whitespace = false;
17683 if current_line_len + grapheme_len > wrap_column
17684 && current_line_len != line_prefix_len
17685 {
17686 wrapped_text.push_str(current_line.trim_end());
17687 wrapped_text.push('\n');
17688 current_line.truncate(line_prefix.len());
17689 current_line_len = line_prefix_len;
17690 }
17691 current_line.push_str(token);
17692 current_line_len += grapheme_len;
17693 }
17694 WordBreakToken::InlineWhitespace {
17695 mut token,
17696 mut grapheme_len,
17697 } => {
17698 in_whitespace = true;
17699 if have_preceding_whitespace && !preserve_existing_whitespace {
17700 continue;
17701 }
17702 if !preserve_existing_whitespace {
17703 token = " ";
17704 grapheme_len = 1;
17705 }
17706 if current_line_len + grapheme_len > wrap_column {
17707 wrapped_text.push_str(current_line.trim_end());
17708 wrapped_text.push('\n');
17709 current_line.truncate(line_prefix.len());
17710 current_line_len = line_prefix_len;
17711 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17712 current_line.push_str(token);
17713 current_line_len += grapheme_len;
17714 }
17715 }
17716 WordBreakToken::Newline => {
17717 in_whitespace = true;
17718 if preserve_existing_whitespace {
17719 wrapped_text.push_str(current_line.trim_end());
17720 wrapped_text.push('\n');
17721 current_line.truncate(line_prefix.len());
17722 current_line_len = line_prefix_len;
17723 } else if have_preceding_whitespace {
17724 continue;
17725 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17726 {
17727 wrapped_text.push_str(current_line.trim_end());
17728 wrapped_text.push('\n');
17729 current_line.truncate(line_prefix.len());
17730 current_line_len = line_prefix_len;
17731 } else if current_line_len != line_prefix_len {
17732 current_line.push(' ');
17733 current_line_len += 1;
17734 }
17735 }
17736 }
17737 }
17738
17739 if !current_line.is_empty() {
17740 wrapped_text.push_str(¤t_line);
17741 }
17742 wrapped_text
17743}
17744
17745#[test]
17746fn test_wrap_with_prefix() {
17747 assert_eq!(
17748 wrap_with_prefix(
17749 "# ".to_string(),
17750 "abcdefg".to_string(),
17751 4,
17752 NonZeroU32::new(4).unwrap(),
17753 false,
17754 ),
17755 "# abcdefg"
17756 );
17757 assert_eq!(
17758 wrap_with_prefix(
17759 "".to_string(),
17760 "\thello world".to_string(),
17761 8,
17762 NonZeroU32::new(4).unwrap(),
17763 false,
17764 ),
17765 "hello\nworld"
17766 );
17767 assert_eq!(
17768 wrap_with_prefix(
17769 "// ".to_string(),
17770 "xx \nyy zz aa bb cc".to_string(),
17771 12,
17772 NonZeroU32::new(4).unwrap(),
17773 false,
17774 ),
17775 "// xx yy zz\n// aa bb cc"
17776 );
17777 assert_eq!(
17778 wrap_with_prefix(
17779 String::new(),
17780 "这是什么 \n 钢笔".to_string(),
17781 3,
17782 NonZeroU32::new(4).unwrap(),
17783 false,
17784 ),
17785 "这是什\n么 钢\n笔"
17786 );
17787}
17788
17789pub trait CollaborationHub {
17790 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17791 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17792 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17793}
17794
17795impl CollaborationHub for Entity<Project> {
17796 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17797 self.read(cx).collaborators()
17798 }
17799
17800 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17801 self.read(cx).user_store().read(cx).participant_indices()
17802 }
17803
17804 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17805 let this = self.read(cx);
17806 let user_ids = this.collaborators().values().map(|c| c.user_id);
17807 this.user_store().read_with(cx, |user_store, cx| {
17808 user_store.participant_names(user_ids, cx)
17809 })
17810 }
17811}
17812
17813pub trait SemanticsProvider {
17814 fn hover(
17815 &self,
17816 buffer: &Entity<Buffer>,
17817 position: text::Anchor,
17818 cx: &mut App,
17819 ) -> Option<Task<Vec<project::Hover>>>;
17820
17821 fn inlay_hints(
17822 &self,
17823 buffer_handle: Entity<Buffer>,
17824 range: Range<text::Anchor>,
17825 cx: &mut App,
17826 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17827
17828 fn resolve_inlay_hint(
17829 &self,
17830 hint: InlayHint,
17831 buffer_handle: Entity<Buffer>,
17832 server_id: LanguageServerId,
17833 cx: &mut App,
17834 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17835
17836 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17837
17838 fn document_highlights(
17839 &self,
17840 buffer: &Entity<Buffer>,
17841 position: text::Anchor,
17842 cx: &mut App,
17843 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17844
17845 fn definitions(
17846 &self,
17847 buffer: &Entity<Buffer>,
17848 position: text::Anchor,
17849 kind: GotoDefinitionKind,
17850 cx: &mut App,
17851 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17852
17853 fn range_for_rename(
17854 &self,
17855 buffer: &Entity<Buffer>,
17856 position: text::Anchor,
17857 cx: &mut App,
17858 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17859
17860 fn perform_rename(
17861 &self,
17862 buffer: &Entity<Buffer>,
17863 position: text::Anchor,
17864 new_name: String,
17865 cx: &mut App,
17866 ) -> Option<Task<Result<ProjectTransaction>>>;
17867}
17868
17869pub trait CompletionProvider {
17870 fn completions(
17871 &self,
17872 buffer: &Entity<Buffer>,
17873 buffer_position: text::Anchor,
17874 trigger: CompletionContext,
17875 window: &mut Window,
17876 cx: &mut Context<Editor>,
17877 ) -> Task<Result<Option<Vec<Completion>>>>;
17878
17879 fn resolve_completions(
17880 &self,
17881 buffer: Entity<Buffer>,
17882 completion_indices: Vec<usize>,
17883 completions: Rc<RefCell<Box<[Completion]>>>,
17884 cx: &mut Context<Editor>,
17885 ) -> Task<Result<bool>>;
17886
17887 fn apply_additional_edits_for_completion(
17888 &self,
17889 _buffer: Entity<Buffer>,
17890 _completions: Rc<RefCell<Box<[Completion]>>>,
17891 _completion_index: usize,
17892 _push_to_history: bool,
17893 _cx: &mut Context<Editor>,
17894 ) -> Task<Result<Option<language::Transaction>>> {
17895 Task::ready(Ok(None))
17896 }
17897
17898 fn is_completion_trigger(
17899 &self,
17900 buffer: &Entity<Buffer>,
17901 position: language::Anchor,
17902 text: &str,
17903 trigger_in_words: bool,
17904 cx: &mut Context<Editor>,
17905 ) -> bool;
17906
17907 fn sort_completions(&self) -> bool {
17908 true
17909 }
17910}
17911
17912pub trait CodeActionProvider {
17913 fn id(&self) -> Arc<str>;
17914
17915 fn code_actions(
17916 &self,
17917 buffer: &Entity<Buffer>,
17918 range: Range<text::Anchor>,
17919 window: &mut Window,
17920 cx: &mut App,
17921 ) -> Task<Result<Vec<CodeAction>>>;
17922
17923 fn apply_code_action(
17924 &self,
17925 buffer_handle: Entity<Buffer>,
17926 action: CodeAction,
17927 excerpt_id: ExcerptId,
17928 push_to_history: bool,
17929 window: &mut Window,
17930 cx: &mut App,
17931 ) -> Task<Result<ProjectTransaction>>;
17932}
17933
17934impl CodeActionProvider for Entity<Project> {
17935 fn id(&self) -> Arc<str> {
17936 "project".into()
17937 }
17938
17939 fn code_actions(
17940 &self,
17941 buffer: &Entity<Buffer>,
17942 range: Range<text::Anchor>,
17943 _window: &mut Window,
17944 cx: &mut App,
17945 ) -> Task<Result<Vec<CodeAction>>> {
17946 self.update(cx, |project, cx| {
17947 let code_lens = project.code_lens(buffer, range.clone(), cx);
17948 let code_actions = project.code_actions(buffer, range, None, cx);
17949 cx.background_spawn(async move {
17950 let (code_lens, code_actions) = join(code_lens, code_actions).await;
17951 Ok(code_lens
17952 .context("code lens fetch")?
17953 .into_iter()
17954 .chain(code_actions.context("code action fetch")?)
17955 .collect())
17956 })
17957 })
17958 }
17959
17960 fn apply_code_action(
17961 &self,
17962 buffer_handle: Entity<Buffer>,
17963 action: CodeAction,
17964 _excerpt_id: ExcerptId,
17965 push_to_history: bool,
17966 _window: &mut Window,
17967 cx: &mut App,
17968 ) -> Task<Result<ProjectTransaction>> {
17969 self.update(cx, |project, cx| {
17970 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17971 })
17972 }
17973}
17974
17975fn snippet_completions(
17976 project: &Project,
17977 buffer: &Entity<Buffer>,
17978 buffer_position: text::Anchor,
17979 cx: &mut App,
17980) -> Task<Result<Vec<Completion>>> {
17981 let language = buffer.read(cx).language_at(buffer_position);
17982 let language_name = language.as_ref().map(|language| language.lsp_id());
17983 let snippet_store = project.snippets().read(cx);
17984 let snippets = snippet_store.snippets_for(language_name, cx);
17985
17986 if snippets.is_empty() {
17987 return Task::ready(Ok(vec![]));
17988 }
17989 let snapshot = buffer.read(cx).text_snapshot();
17990 let chars: String = snapshot
17991 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17992 .collect();
17993
17994 let scope = language.map(|language| language.default_scope());
17995 let executor = cx.background_executor().clone();
17996
17997 cx.background_spawn(async move {
17998 let classifier = CharClassifier::new(scope).for_completion(true);
17999 let mut last_word = chars
18000 .chars()
18001 .take_while(|c| classifier.is_word(*c))
18002 .collect::<String>();
18003 last_word = last_word.chars().rev().collect();
18004
18005 if last_word.is_empty() {
18006 return Ok(vec![]);
18007 }
18008
18009 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
18010 let to_lsp = |point: &text::Anchor| {
18011 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
18012 point_to_lsp(end)
18013 };
18014 let lsp_end = to_lsp(&buffer_position);
18015
18016 let candidates = snippets
18017 .iter()
18018 .enumerate()
18019 .flat_map(|(ix, snippet)| {
18020 snippet
18021 .prefix
18022 .iter()
18023 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
18024 })
18025 .collect::<Vec<StringMatchCandidate>>();
18026
18027 let mut matches = fuzzy::match_strings(
18028 &candidates,
18029 &last_word,
18030 last_word.chars().any(|c| c.is_uppercase()),
18031 100,
18032 &Default::default(),
18033 executor,
18034 )
18035 .await;
18036
18037 // Remove all candidates where the query's start does not match the start of any word in the candidate
18038 if let Some(query_start) = last_word.chars().next() {
18039 matches.retain(|string_match| {
18040 split_words(&string_match.string).any(|word| {
18041 // Check that the first codepoint of the word as lowercase matches the first
18042 // codepoint of the query as lowercase
18043 word.chars()
18044 .flat_map(|codepoint| codepoint.to_lowercase())
18045 .zip(query_start.to_lowercase())
18046 .all(|(word_cp, query_cp)| word_cp == query_cp)
18047 })
18048 });
18049 }
18050
18051 let matched_strings = matches
18052 .into_iter()
18053 .map(|m| m.string)
18054 .collect::<HashSet<_>>();
18055
18056 let result: Vec<Completion> = snippets
18057 .into_iter()
18058 .filter_map(|snippet| {
18059 let matching_prefix = snippet
18060 .prefix
18061 .iter()
18062 .find(|prefix| matched_strings.contains(*prefix))?;
18063 let start = as_offset - last_word.len();
18064 let start = snapshot.anchor_before(start);
18065 let range = start..buffer_position;
18066 let lsp_start = to_lsp(&start);
18067 let lsp_range = lsp::Range {
18068 start: lsp_start,
18069 end: lsp_end,
18070 };
18071 Some(Completion {
18072 old_range: range,
18073 new_text: snippet.body.clone(),
18074 source: CompletionSource::Lsp {
18075 server_id: LanguageServerId(usize::MAX),
18076 resolved: true,
18077 lsp_completion: Box::new(lsp::CompletionItem {
18078 label: snippet.prefix.first().unwrap().clone(),
18079 kind: Some(CompletionItemKind::SNIPPET),
18080 label_details: snippet.description.as_ref().map(|description| {
18081 lsp::CompletionItemLabelDetails {
18082 detail: Some(description.clone()),
18083 description: None,
18084 }
18085 }),
18086 insert_text_format: Some(InsertTextFormat::SNIPPET),
18087 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18088 lsp::InsertReplaceEdit {
18089 new_text: snippet.body.clone(),
18090 insert: lsp_range,
18091 replace: lsp_range,
18092 },
18093 )),
18094 filter_text: Some(snippet.body.clone()),
18095 sort_text: Some(char::MAX.to_string()),
18096 ..lsp::CompletionItem::default()
18097 }),
18098 lsp_defaults: None,
18099 },
18100 label: CodeLabel {
18101 text: matching_prefix.clone(),
18102 runs: Vec::new(),
18103 filter_range: 0..matching_prefix.len(),
18104 },
18105 documentation: snippet
18106 .description
18107 .clone()
18108 .map(|description| CompletionDocumentation::SingleLine(description.into())),
18109 confirm: None,
18110 })
18111 })
18112 .collect();
18113
18114 Ok(result)
18115 })
18116}
18117
18118impl CompletionProvider for Entity<Project> {
18119 fn completions(
18120 &self,
18121 buffer: &Entity<Buffer>,
18122 buffer_position: text::Anchor,
18123 options: CompletionContext,
18124 _window: &mut Window,
18125 cx: &mut Context<Editor>,
18126 ) -> Task<Result<Option<Vec<Completion>>>> {
18127 self.update(cx, |project, cx| {
18128 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18129 let project_completions = project.completions(buffer, buffer_position, options, cx);
18130 cx.background_spawn(async move {
18131 let snippets_completions = snippets.await?;
18132 match project_completions.await? {
18133 Some(mut completions) => {
18134 completions.extend(snippets_completions);
18135 Ok(Some(completions))
18136 }
18137 None => {
18138 if snippets_completions.is_empty() {
18139 Ok(None)
18140 } else {
18141 Ok(Some(snippets_completions))
18142 }
18143 }
18144 }
18145 })
18146 })
18147 }
18148
18149 fn resolve_completions(
18150 &self,
18151 buffer: Entity<Buffer>,
18152 completion_indices: Vec<usize>,
18153 completions: Rc<RefCell<Box<[Completion]>>>,
18154 cx: &mut Context<Editor>,
18155 ) -> Task<Result<bool>> {
18156 self.update(cx, |project, cx| {
18157 project.lsp_store().update(cx, |lsp_store, cx| {
18158 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18159 })
18160 })
18161 }
18162
18163 fn apply_additional_edits_for_completion(
18164 &self,
18165 buffer: Entity<Buffer>,
18166 completions: Rc<RefCell<Box<[Completion]>>>,
18167 completion_index: usize,
18168 push_to_history: bool,
18169 cx: &mut Context<Editor>,
18170 ) -> Task<Result<Option<language::Transaction>>> {
18171 self.update(cx, |project, cx| {
18172 project.lsp_store().update(cx, |lsp_store, cx| {
18173 lsp_store.apply_additional_edits_for_completion(
18174 buffer,
18175 completions,
18176 completion_index,
18177 push_to_history,
18178 cx,
18179 )
18180 })
18181 })
18182 }
18183
18184 fn is_completion_trigger(
18185 &self,
18186 buffer: &Entity<Buffer>,
18187 position: language::Anchor,
18188 text: &str,
18189 trigger_in_words: bool,
18190 cx: &mut Context<Editor>,
18191 ) -> bool {
18192 let mut chars = text.chars();
18193 let char = if let Some(char) = chars.next() {
18194 char
18195 } else {
18196 return false;
18197 };
18198 if chars.next().is_some() {
18199 return false;
18200 }
18201
18202 let buffer = buffer.read(cx);
18203 let snapshot = buffer.snapshot();
18204 if !snapshot.settings_at(position, cx).show_completions_on_input {
18205 return false;
18206 }
18207 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18208 if trigger_in_words && classifier.is_word(char) {
18209 return true;
18210 }
18211
18212 buffer.completion_triggers().contains(text)
18213 }
18214}
18215
18216impl SemanticsProvider for Entity<Project> {
18217 fn hover(
18218 &self,
18219 buffer: &Entity<Buffer>,
18220 position: text::Anchor,
18221 cx: &mut App,
18222 ) -> Option<Task<Vec<project::Hover>>> {
18223 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18224 }
18225
18226 fn document_highlights(
18227 &self,
18228 buffer: &Entity<Buffer>,
18229 position: text::Anchor,
18230 cx: &mut App,
18231 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18232 Some(self.update(cx, |project, cx| {
18233 project.document_highlights(buffer, position, cx)
18234 }))
18235 }
18236
18237 fn definitions(
18238 &self,
18239 buffer: &Entity<Buffer>,
18240 position: text::Anchor,
18241 kind: GotoDefinitionKind,
18242 cx: &mut App,
18243 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18244 Some(self.update(cx, |project, cx| match kind {
18245 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18246 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18247 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18248 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18249 }))
18250 }
18251
18252 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18253 // TODO: make this work for remote projects
18254 self.update(cx, |this, cx| {
18255 buffer.update(cx, |buffer, cx| {
18256 this.any_language_server_supports_inlay_hints(buffer, cx)
18257 })
18258 })
18259 }
18260
18261 fn inlay_hints(
18262 &self,
18263 buffer_handle: Entity<Buffer>,
18264 range: Range<text::Anchor>,
18265 cx: &mut App,
18266 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18267 Some(self.update(cx, |project, cx| {
18268 project.inlay_hints(buffer_handle, range, cx)
18269 }))
18270 }
18271
18272 fn resolve_inlay_hint(
18273 &self,
18274 hint: InlayHint,
18275 buffer_handle: Entity<Buffer>,
18276 server_id: LanguageServerId,
18277 cx: &mut App,
18278 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18279 Some(self.update(cx, |project, cx| {
18280 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18281 }))
18282 }
18283
18284 fn range_for_rename(
18285 &self,
18286 buffer: &Entity<Buffer>,
18287 position: text::Anchor,
18288 cx: &mut App,
18289 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18290 Some(self.update(cx, |project, cx| {
18291 let buffer = buffer.clone();
18292 let task = project.prepare_rename(buffer.clone(), position, cx);
18293 cx.spawn(async move |_, cx| {
18294 Ok(match task.await? {
18295 PrepareRenameResponse::Success(range) => Some(range),
18296 PrepareRenameResponse::InvalidPosition => None,
18297 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18298 // Fallback on using TreeSitter info to determine identifier range
18299 buffer.update(cx, |buffer, _| {
18300 let snapshot = buffer.snapshot();
18301 let (range, kind) = snapshot.surrounding_word(position);
18302 if kind != Some(CharKind::Word) {
18303 return None;
18304 }
18305 Some(
18306 snapshot.anchor_before(range.start)
18307 ..snapshot.anchor_after(range.end),
18308 )
18309 })?
18310 }
18311 })
18312 })
18313 }))
18314 }
18315
18316 fn perform_rename(
18317 &self,
18318 buffer: &Entity<Buffer>,
18319 position: text::Anchor,
18320 new_name: String,
18321 cx: &mut App,
18322 ) -> Option<Task<Result<ProjectTransaction>>> {
18323 Some(self.update(cx, |project, cx| {
18324 project.perform_rename(buffer.clone(), position, new_name, cx)
18325 }))
18326 }
18327}
18328
18329fn inlay_hint_settings(
18330 location: Anchor,
18331 snapshot: &MultiBufferSnapshot,
18332 cx: &mut Context<Editor>,
18333) -> InlayHintSettings {
18334 let file = snapshot.file_at(location);
18335 let language = snapshot.language_at(location).map(|l| l.name());
18336 language_settings(language, file, cx).inlay_hints
18337}
18338
18339fn consume_contiguous_rows(
18340 contiguous_row_selections: &mut Vec<Selection<Point>>,
18341 selection: &Selection<Point>,
18342 display_map: &DisplaySnapshot,
18343 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18344) -> (MultiBufferRow, MultiBufferRow) {
18345 contiguous_row_selections.push(selection.clone());
18346 let start_row = MultiBufferRow(selection.start.row);
18347 let mut end_row = ending_row(selection, display_map);
18348
18349 while let Some(next_selection) = selections.peek() {
18350 if next_selection.start.row <= end_row.0 {
18351 end_row = ending_row(next_selection, display_map);
18352 contiguous_row_selections.push(selections.next().unwrap().clone());
18353 } else {
18354 break;
18355 }
18356 }
18357 (start_row, end_row)
18358}
18359
18360fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18361 if next_selection.end.column > 0 || next_selection.is_empty() {
18362 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18363 } else {
18364 MultiBufferRow(next_selection.end.row)
18365 }
18366}
18367
18368impl EditorSnapshot {
18369 pub fn remote_selections_in_range<'a>(
18370 &'a self,
18371 range: &'a Range<Anchor>,
18372 collaboration_hub: &dyn CollaborationHub,
18373 cx: &'a App,
18374 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18375 let participant_names = collaboration_hub.user_names(cx);
18376 let participant_indices = collaboration_hub.user_participant_indices(cx);
18377 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18378 let collaborators_by_replica_id = collaborators_by_peer_id
18379 .iter()
18380 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18381 .collect::<HashMap<_, _>>();
18382 self.buffer_snapshot
18383 .selections_in_range(range, false)
18384 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18385 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18386 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18387 let user_name = participant_names.get(&collaborator.user_id).cloned();
18388 Some(RemoteSelection {
18389 replica_id,
18390 selection,
18391 cursor_shape,
18392 line_mode,
18393 participant_index,
18394 peer_id: collaborator.peer_id,
18395 user_name,
18396 })
18397 })
18398 }
18399
18400 pub fn hunks_for_ranges(
18401 &self,
18402 ranges: impl IntoIterator<Item = Range<Point>>,
18403 ) -> Vec<MultiBufferDiffHunk> {
18404 let mut hunks = Vec::new();
18405 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18406 HashMap::default();
18407 for query_range in ranges {
18408 let query_rows =
18409 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18410 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18411 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18412 ) {
18413 // Include deleted hunks that are adjacent to the query range, because
18414 // otherwise they would be missed.
18415 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18416 if hunk.status().is_deleted() {
18417 intersects_range |= hunk.row_range.start == query_rows.end;
18418 intersects_range |= hunk.row_range.end == query_rows.start;
18419 }
18420 if intersects_range {
18421 if !processed_buffer_rows
18422 .entry(hunk.buffer_id)
18423 .or_default()
18424 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18425 {
18426 continue;
18427 }
18428 hunks.push(hunk);
18429 }
18430 }
18431 }
18432
18433 hunks
18434 }
18435
18436 fn display_diff_hunks_for_rows<'a>(
18437 &'a self,
18438 display_rows: Range<DisplayRow>,
18439 folded_buffers: &'a HashSet<BufferId>,
18440 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18441 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18442 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18443
18444 self.buffer_snapshot
18445 .diff_hunks_in_range(buffer_start..buffer_end)
18446 .filter_map(|hunk| {
18447 if folded_buffers.contains(&hunk.buffer_id) {
18448 return None;
18449 }
18450
18451 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18452 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18453
18454 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18455 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18456
18457 let display_hunk = if hunk_display_start.column() != 0 {
18458 DisplayDiffHunk::Folded {
18459 display_row: hunk_display_start.row(),
18460 }
18461 } else {
18462 let mut end_row = hunk_display_end.row();
18463 if hunk_display_end.column() > 0 {
18464 end_row.0 += 1;
18465 }
18466 let is_created_file = hunk.is_created_file();
18467 DisplayDiffHunk::Unfolded {
18468 status: hunk.status(),
18469 diff_base_byte_range: hunk.diff_base_byte_range,
18470 display_row_range: hunk_display_start.row()..end_row,
18471 multi_buffer_range: Anchor::range_in_buffer(
18472 hunk.excerpt_id,
18473 hunk.buffer_id,
18474 hunk.buffer_range,
18475 ),
18476 is_created_file,
18477 }
18478 };
18479
18480 Some(display_hunk)
18481 })
18482 }
18483
18484 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18485 self.display_snapshot.buffer_snapshot.language_at(position)
18486 }
18487
18488 pub fn is_focused(&self) -> bool {
18489 self.is_focused
18490 }
18491
18492 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18493 self.placeholder_text.as_ref()
18494 }
18495
18496 pub fn scroll_position(&self) -> gpui::Point<f32> {
18497 self.scroll_anchor.scroll_position(&self.display_snapshot)
18498 }
18499
18500 fn gutter_dimensions(
18501 &self,
18502 font_id: FontId,
18503 font_size: Pixels,
18504 max_line_number_width: Pixels,
18505 cx: &App,
18506 ) -> Option<GutterDimensions> {
18507 if !self.show_gutter {
18508 return None;
18509 }
18510
18511 let descent = cx.text_system().descent(font_id, font_size);
18512 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18513 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18514
18515 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18516 matches!(
18517 ProjectSettings::get_global(cx).git.git_gutter,
18518 Some(GitGutterSetting::TrackedFiles)
18519 )
18520 });
18521 let gutter_settings = EditorSettings::get_global(cx).gutter;
18522 let show_line_numbers = self
18523 .show_line_numbers
18524 .unwrap_or(gutter_settings.line_numbers);
18525 let line_gutter_width = if show_line_numbers {
18526 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18527 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18528 max_line_number_width.max(min_width_for_number_on_gutter)
18529 } else {
18530 0.0.into()
18531 };
18532
18533 let show_code_actions = self
18534 .show_code_actions
18535 .unwrap_or(gutter_settings.code_actions);
18536
18537 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18538 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18539
18540 let git_blame_entries_width =
18541 self.git_blame_gutter_max_author_length
18542 .map(|max_author_length| {
18543 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18544
18545 /// The number of characters to dedicate to gaps and margins.
18546 const SPACING_WIDTH: usize = 4;
18547
18548 let max_char_count = max_author_length
18549 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18550 + ::git::SHORT_SHA_LENGTH
18551 + MAX_RELATIVE_TIMESTAMP.len()
18552 + SPACING_WIDTH;
18553
18554 em_advance * max_char_count
18555 });
18556
18557 let is_singleton = self.buffer_snapshot.is_singleton();
18558
18559 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18560 left_padding += if !is_singleton {
18561 em_width * 4.0
18562 } else if show_code_actions || show_runnables || show_breakpoints {
18563 em_width * 3.0
18564 } else if show_git_gutter && show_line_numbers {
18565 em_width * 2.0
18566 } else if show_git_gutter || show_line_numbers {
18567 em_width
18568 } else {
18569 px(0.)
18570 };
18571
18572 let shows_folds = is_singleton && gutter_settings.folds;
18573
18574 let right_padding = if shows_folds && show_line_numbers {
18575 em_width * 4.0
18576 } else if shows_folds || (!is_singleton && show_line_numbers) {
18577 em_width * 3.0
18578 } else if show_line_numbers {
18579 em_width
18580 } else {
18581 px(0.)
18582 };
18583
18584 Some(GutterDimensions {
18585 left_padding,
18586 right_padding,
18587 width: line_gutter_width + left_padding + right_padding,
18588 margin: -descent,
18589 git_blame_entries_width,
18590 })
18591 }
18592
18593 pub fn render_crease_toggle(
18594 &self,
18595 buffer_row: MultiBufferRow,
18596 row_contains_cursor: bool,
18597 editor: Entity<Editor>,
18598 window: &mut Window,
18599 cx: &mut App,
18600 ) -> Option<AnyElement> {
18601 let folded = self.is_line_folded(buffer_row);
18602 let mut is_foldable = false;
18603
18604 if let Some(crease) = self
18605 .crease_snapshot
18606 .query_row(buffer_row, &self.buffer_snapshot)
18607 {
18608 is_foldable = true;
18609 match crease {
18610 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18611 if let Some(render_toggle) = render_toggle {
18612 let toggle_callback =
18613 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18614 if folded {
18615 editor.update(cx, |editor, cx| {
18616 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18617 });
18618 } else {
18619 editor.update(cx, |editor, cx| {
18620 editor.unfold_at(
18621 &crate::UnfoldAt { buffer_row },
18622 window,
18623 cx,
18624 )
18625 });
18626 }
18627 });
18628 return Some((render_toggle)(
18629 buffer_row,
18630 folded,
18631 toggle_callback,
18632 window,
18633 cx,
18634 ));
18635 }
18636 }
18637 }
18638 }
18639
18640 is_foldable |= self.starts_indent(buffer_row);
18641
18642 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18643 Some(
18644 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18645 .toggle_state(folded)
18646 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18647 if folded {
18648 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18649 } else {
18650 this.fold_at(&FoldAt { buffer_row }, window, cx);
18651 }
18652 }))
18653 .into_any_element(),
18654 )
18655 } else {
18656 None
18657 }
18658 }
18659
18660 pub fn render_crease_trailer(
18661 &self,
18662 buffer_row: MultiBufferRow,
18663 window: &mut Window,
18664 cx: &mut App,
18665 ) -> Option<AnyElement> {
18666 let folded = self.is_line_folded(buffer_row);
18667 if let Crease::Inline { render_trailer, .. } = self
18668 .crease_snapshot
18669 .query_row(buffer_row, &self.buffer_snapshot)?
18670 {
18671 let render_trailer = render_trailer.as_ref()?;
18672 Some(render_trailer(buffer_row, folded, window, cx))
18673 } else {
18674 None
18675 }
18676 }
18677}
18678
18679impl Deref for EditorSnapshot {
18680 type Target = DisplaySnapshot;
18681
18682 fn deref(&self) -> &Self::Target {
18683 &self.display_snapshot
18684 }
18685}
18686
18687#[derive(Clone, Debug, PartialEq, Eq)]
18688pub enum EditorEvent {
18689 InputIgnored {
18690 text: Arc<str>,
18691 },
18692 InputHandled {
18693 utf16_range_to_replace: Option<Range<isize>>,
18694 text: Arc<str>,
18695 },
18696 ExcerptsAdded {
18697 buffer: Entity<Buffer>,
18698 predecessor: ExcerptId,
18699 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18700 },
18701 ExcerptsRemoved {
18702 ids: Vec<ExcerptId>,
18703 },
18704 BufferFoldToggled {
18705 ids: Vec<ExcerptId>,
18706 folded: bool,
18707 },
18708 ExcerptsEdited {
18709 ids: Vec<ExcerptId>,
18710 },
18711 ExcerptsExpanded {
18712 ids: Vec<ExcerptId>,
18713 },
18714 BufferEdited,
18715 Edited {
18716 transaction_id: clock::Lamport,
18717 },
18718 Reparsed(BufferId),
18719 Focused,
18720 FocusedIn,
18721 Blurred,
18722 DirtyChanged,
18723 Saved,
18724 TitleChanged,
18725 DiffBaseChanged,
18726 SelectionsChanged {
18727 local: bool,
18728 },
18729 ScrollPositionChanged {
18730 local: bool,
18731 autoscroll: bool,
18732 },
18733 Closed,
18734 TransactionUndone {
18735 transaction_id: clock::Lamport,
18736 },
18737 TransactionBegun {
18738 transaction_id: clock::Lamport,
18739 },
18740 Reloaded,
18741 CursorShapeChanged,
18742 PushedToNavHistory {
18743 anchor: Anchor,
18744 is_deactivate: bool,
18745 },
18746}
18747
18748impl EventEmitter<EditorEvent> for Editor {}
18749
18750impl Focusable for Editor {
18751 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18752 self.focus_handle.clone()
18753 }
18754}
18755
18756impl Render for Editor {
18757 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18758 let settings = ThemeSettings::get_global(cx);
18759
18760 let mut text_style = match self.mode {
18761 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18762 color: cx.theme().colors().editor_foreground,
18763 font_family: settings.ui_font.family.clone(),
18764 font_features: settings.ui_font.features.clone(),
18765 font_fallbacks: settings.ui_font.fallbacks.clone(),
18766 font_size: rems(0.875).into(),
18767 font_weight: settings.ui_font.weight,
18768 line_height: relative(settings.buffer_line_height.value()),
18769 ..Default::default()
18770 },
18771 EditorMode::Full => TextStyle {
18772 color: cx.theme().colors().editor_foreground,
18773 font_family: settings.buffer_font.family.clone(),
18774 font_features: settings.buffer_font.features.clone(),
18775 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18776 font_size: settings.buffer_font_size(cx).into(),
18777 font_weight: settings.buffer_font.weight,
18778 line_height: relative(settings.buffer_line_height.value()),
18779 ..Default::default()
18780 },
18781 };
18782 if let Some(text_style_refinement) = &self.text_style_refinement {
18783 text_style.refine(text_style_refinement)
18784 }
18785
18786 let background = match self.mode {
18787 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18788 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18789 EditorMode::Full => cx.theme().colors().editor_background,
18790 };
18791
18792 EditorElement::new(
18793 &cx.entity(),
18794 EditorStyle {
18795 background,
18796 local_player: cx.theme().players().local(),
18797 text: text_style,
18798 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18799 syntax: cx.theme().syntax().clone(),
18800 status: cx.theme().status().clone(),
18801 inlay_hints_style: make_inlay_hints_style(cx),
18802 inline_completion_styles: make_suggestion_styles(cx),
18803 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18804 },
18805 )
18806 }
18807}
18808
18809impl EntityInputHandler for Editor {
18810 fn text_for_range(
18811 &mut self,
18812 range_utf16: Range<usize>,
18813 adjusted_range: &mut Option<Range<usize>>,
18814 _: &mut Window,
18815 cx: &mut Context<Self>,
18816 ) -> Option<String> {
18817 let snapshot = self.buffer.read(cx).read(cx);
18818 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18819 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18820 if (start.0..end.0) != range_utf16 {
18821 adjusted_range.replace(start.0..end.0);
18822 }
18823 Some(snapshot.text_for_range(start..end).collect())
18824 }
18825
18826 fn selected_text_range(
18827 &mut self,
18828 ignore_disabled_input: bool,
18829 _: &mut Window,
18830 cx: &mut Context<Self>,
18831 ) -> Option<UTF16Selection> {
18832 // Prevent the IME menu from appearing when holding down an alphabetic key
18833 // while input is disabled.
18834 if !ignore_disabled_input && !self.input_enabled {
18835 return None;
18836 }
18837
18838 let selection = self.selections.newest::<OffsetUtf16>(cx);
18839 let range = selection.range();
18840
18841 Some(UTF16Selection {
18842 range: range.start.0..range.end.0,
18843 reversed: selection.reversed,
18844 })
18845 }
18846
18847 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18848 let snapshot = self.buffer.read(cx).read(cx);
18849 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18850 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18851 }
18852
18853 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18854 self.clear_highlights::<InputComposition>(cx);
18855 self.ime_transaction.take();
18856 }
18857
18858 fn replace_text_in_range(
18859 &mut self,
18860 range_utf16: Option<Range<usize>>,
18861 text: &str,
18862 window: &mut Window,
18863 cx: &mut Context<Self>,
18864 ) {
18865 if !self.input_enabled {
18866 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18867 return;
18868 }
18869
18870 self.transact(window, cx, |this, window, cx| {
18871 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18872 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18873 Some(this.selection_replacement_ranges(range_utf16, cx))
18874 } else {
18875 this.marked_text_ranges(cx)
18876 };
18877
18878 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
18879 let newest_selection_id = this.selections.newest_anchor().id;
18880 this.selections
18881 .all::<OffsetUtf16>(cx)
18882 .iter()
18883 .zip(ranges_to_replace.iter())
18884 .find_map(|(selection, range)| {
18885 if selection.id == newest_selection_id {
18886 Some(
18887 (range.start.0 as isize - selection.head().0 as isize)
18888 ..(range.end.0 as isize - selection.head().0 as isize),
18889 )
18890 } else {
18891 None
18892 }
18893 })
18894 });
18895
18896 cx.emit(EditorEvent::InputHandled {
18897 utf16_range_to_replace: range_to_replace,
18898 text: text.into(),
18899 });
18900
18901 if let Some(new_selected_ranges) = new_selected_ranges {
18902 this.change_selections(None, window, cx, |selections| {
18903 selections.select_ranges(new_selected_ranges)
18904 });
18905 this.backspace(&Default::default(), window, cx);
18906 }
18907
18908 this.handle_input(text, window, cx);
18909 });
18910
18911 if let Some(transaction) = self.ime_transaction {
18912 self.buffer.update(cx, |buffer, cx| {
18913 buffer.group_until_transaction(transaction, cx);
18914 });
18915 }
18916
18917 self.unmark_text(window, cx);
18918 }
18919
18920 fn replace_and_mark_text_in_range(
18921 &mut self,
18922 range_utf16: Option<Range<usize>>,
18923 text: &str,
18924 new_selected_range_utf16: Option<Range<usize>>,
18925 window: &mut Window,
18926 cx: &mut Context<Self>,
18927 ) {
18928 if !self.input_enabled {
18929 return;
18930 }
18931
18932 let transaction = self.transact(window, cx, |this, window, cx| {
18933 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
18934 let snapshot = this.buffer.read(cx).read(cx);
18935 if let Some(relative_range_utf16) = range_utf16.as_ref() {
18936 for marked_range in &mut marked_ranges {
18937 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
18938 marked_range.start.0 += relative_range_utf16.start;
18939 marked_range.start =
18940 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
18941 marked_range.end =
18942 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
18943 }
18944 }
18945 Some(marked_ranges)
18946 } else if let Some(range_utf16) = range_utf16 {
18947 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18948 Some(this.selection_replacement_ranges(range_utf16, cx))
18949 } else {
18950 None
18951 };
18952
18953 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
18954 let newest_selection_id = this.selections.newest_anchor().id;
18955 this.selections
18956 .all::<OffsetUtf16>(cx)
18957 .iter()
18958 .zip(ranges_to_replace.iter())
18959 .find_map(|(selection, range)| {
18960 if selection.id == newest_selection_id {
18961 Some(
18962 (range.start.0 as isize - selection.head().0 as isize)
18963 ..(range.end.0 as isize - selection.head().0 as isize),
18964 )
18965 } else {
18966 None
18967 }
18968 })
18969 });
18970
18971 cx.emit(EditorEvent::InputHandled {
18972 utf16_range_to_replace: range_to_replace,
18973 text: text.into(),
18974 });
18975
18976 if let Some(ranges) = ranges_to_replace {
18977 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18978 }
18979
18980 let marked_ranges = {
18981 let snapshot = this.buffer.read(cx).read(cx);
18982 this.selections
18983 .disjoint_anchors()
18984 .iter()
18985 .map(|selection| {
18986 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18987 })
18988 .collect::<Vec<_>>()
18989 };
18990
18991 if text.is_empty() {
18992 this.unmark_text(window, cx);
18993 } else {
18994 this.highlight_text::<InputComposition>(
18995 marked_ranges.clone(),
18996 HighlightStyle {
18997 underline: Some(UnderlineStyle {
18998 thickness: px(1.),
18999 color: None,
19000 wavy: false,
19001 }),
19002 ..Default::default()
19003 },
19004 cx,
19005 );
19006 }
19007
19008 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
19009 let use_autoclose = this.use_autoclose;
19010 let use_auto_surround = this.use_auto_surround;
19011 this.set_use_autoclose(false);
19012 this.set_use_auto_surround(false);
19013 this.handle_input(text, window, cx);
19014 this.set_use_autoclose(use_autoclose);
19015 this.set_use_auto_surround(use_auto_surround);
19016
19017 if let Some(new_selected_range) = new_selected_range_utf16 {
19018 let snapshot = this.buffer.read(cx).read(cx);
19019 let new_selected_ranges = marked_ranges
19020 .into_iter()
19021 .map(|marked_range| {
19022 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
19023 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
19024 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
19025 snapshot.clip_offset_utf16(new_start, Bias::Left)
19026 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
19027 })
19028 .collect::<Vec<_>>();
19029
19030 drop(snapshot);
19031 this.change_selections(None, window, cx, |selections| {
19032 selections.select_ranges(new_selected_ranges)
19033 });
19034 }
19035 });
19036
19037 self.ime_transaction = self.ime_transaction.or(transaction);
19038 if let Some(transaction) = self.ime_transaction {
19039 self.buffer.update(cx, |buffer, cx| {
19040 buffer.group_until_transaction(transaction, cx);
19041 });
19042 }
19043
19044 if self.text_highlights::<InputComposition>(cx).is_none() {
19045 self.ime_transaction.take();
19046 }
19047 }
19048
19049 fn bounds_for_range(
19050 &mut self,
19051 range_utf16: Range<usize>,
19052 element_bounds: gpui::Bounds<Pixels>,
19053 window: &mut Window,
19054 cx: &mut Context<Self>,
19055 ) -> Option<gpui::Bounds<Pixels>> {
19056 let text_layout_details = self.text_layout_details(window);
19057 let gpui::Size {
19058 width: em_width,
19059 height: line_height,
19060 } = self.character_size(window);
19061
19062 let snapshot = self.snapshot(window, cx);
19063 let scroll_position = snapshot.scroll_position();
19064 let scroll_left = scroll_position.x * em_width;
19065
19066 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
19067 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
19068 + self.gutter_dimensions.width
19069 + self.gutter_dimensions.margin;
19070 let y = line_height * (start.row().as_f32() - scroll_position.y);
19071
19072 Some(Bounds {
19073 origin: element_bounds.origin + point(x, y),
19074 size: size(em_width, line_height),
19075 })
19076 }
19077
19078 fn character_index_for_point(
19079 &mut self,
19080 point: gpui::Point<Pixels>,
19081 _window: &mut Window,
19082 _cx: &mut Context<Self>,
19083 ) -> Option<usize> {
19084 let position_map = self.last_position_map.as_ref()?;
19085 if !position_map.text_hitbox.contains(&point) {
19086 return None;
19087 }
19088 let display_point = position_map.point_for_position(point).previous_valid;
19089 let anchor = position_map
19090 .snapshot
19091 .display_point_to_anchor(display_point, Bias::Left);
19092 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
19093 Some(utf16_offset.0)
19094 }
19095}
19096
19097trait SelectionExt {
19098 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
19099 fn spanned_rows(
19100 &self,
19101 include_end_if_at_line_start: bool,
19102 map: &DisplaySnapshot,
19103 ) -> Range<MultiBufferRow>;
19104}
19105
19106impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
19107 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
19108 let start = self
19109 .start
19110 .to_point(&map.buffer_snapshot)
19111 .to_display_point(map);
19112 let end = self
19113 .end
19114 .to_point(&map.buffer_snapshot)
19115 .to_display_point(map);
19116 if self.reversed {
19117 end..start
19118 } else {
19119 start..end
19120 }
19121 }
19122
19123 fn spanned_rows(
19124 &self,
19125 include_end_if_at_line_start: bool,
19126 map: &DisplaySnapshot,
19127 ) -> Range<MultiBufferRow> {
19128 let start = self.start.to_point(&map.buffer_snapshot);
19129 let mut end = self.end.to_point(&map.buffer_snapshot);
19130 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19131 end.row -= 1;
19132 }
19133
19134 let buffer_start = map.prev_line_boundary(start).0;
19135 let buffer_end = map.next_line_boundary(end).0;
19136 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19137 }
19138}
19139
19140impl<T: InvalidationRegion> InvalidationStack<T> {
19141 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19142 where
19143 S: Clone + ToOffset,
19144 {
19145 while let Some(region) = self.last() {
19146 let all_selections_inside_invalidation_ranges =
19147 if selections.len() == region.ranges().len() {
19148 selections
19149 .iter()
19150 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19151 .all(|(selection, invalidation_range)| {
19152 let head = selection.head().to_offset(buffer);
19153 invalidation_range.start <= head && invalidation_range.end >= head
19154 })
19155 } else {
19156 false
19157 };
19158
19159 if all_selections_inside_invalidation_ranges {
19160 break;
19161 } else {
19162 self.pop();
19163 }
19164 }
19165 }
19166}
19167
19168impl<T> Default for InvalidationStack<T> {
19169 fn default() -> Self {
19170 Self(Default::default())
19171 }
19172}
19173
19174impl<T> Deref for InvalidationStack<T> {
19175 type Target = Vec<T>;
19176
19177 fn deref(&self) -> &Self::Target {
19178 &self.0
19179 }
19180}
19181
19182impl<T> DerefMut for InvalidationStack<T> {
19183 fn deref_mut(&mut self) -> &mut Self::Target {
19184 &mut self.0
19185 }
19186}
19187
19188impl InvalidationRegion for SnippetState {
19189 fn ranges(&self) -> &[Range<Anchor>] {
19190 &self.ranges[self.active_index]
19191 }
19192}
19193
19194pub fn diagnostic_block_renderer(
19195 diagnostic: Diagnostic,
19196 max_message_rows: Option<u8>,
19197 allow_closing: bool,
19198) -> RenderBlock {
19199 let (text_without_backticks, code_ranges) =
19200 highlight_diagnostic_message(&diagnostic, max_message_rows);
19201
19202 Arc::new(move |cx: &mut BlockContext| {
19203 let group_id: SharedString = cx.block_id.to_string().into();
19204
19205 let mut text_style = cx.window.text_style().clone();
19206 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19207 let theme_settings = ThemeSettings::get_global(cx);
19208 text_style.font_family = theme_settings.buffer_font.family.clone();
19209 text_style.font_style = theme_settings.buffer_font.style;
19210 text_style.font_features = theme_settings.buffer_font.features.clone();
19211 text_style.font_weight = theme_settings.buffer_font.weight;
19212
19213 let multi_line_diagnostic = diagnostic.message.contains('\n');
19214
19215 let buttons = |diagnostic: &Diagnostic| {
19216 if multi_line_diagnostic {
19217 v_flex()
19218 } else {
19219 h_flex()
19220 }
19221 .when(allow_closing, |div| {
19222 div.children(diagnostic.is_primary.then(|| {
19223 IconButton::new("close-block", IconName::XCircle)
19224 .icon_color(Color::Muted)
19225 .size(ButtonSize::Compact)
19226 .style(ButtonStyle::Transparent)
19227 .visible_on_hover(group_id.clone())
19228 .on_click(move |_click, window, cx| {
19229 window.dispatch_action(Box::new(Cancel), cx)
19230 })
19231 .tooltip(|window, cx| {
19232 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19233 })
19234 }))
19235 })
19236 .child(
19237 IconButton::new("copy-block", IconName::Copy)
19238 .icon_color(Color::Muted)
19239 .size(ButtonSize::Compact)
19240 .style(ButtonStyle::Transparent)
19241 .visible_on_hover(group_id.clone())
19242 .on_click({
19243 let message = diagnostic.message.clone();
19244 move |_click, _, cx| {
19245 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19246 }
19247 })
19248 .tooltip(Tooltip::text("Copy diagnostic message")),
19249 )
19250 };
19251
19252 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19253 AvailableSpace::min_size(),
19254 cx.window,
19255 cx.app,
19256 );
19257
19258 h_flex()
19259 .id(cx.block_id)
19260 .group(group_id.clone())
19261 .relative()
19262 .size_full()
19263 .block_mouse_down()
19264 .pl(cx.gutter_dimensions.width)
19265 .w(cx.max_width - cx.gutter_dimensions.full_width())
19266 .child(
19267 div()
19268 .flex()
19269 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19270 .flex_shrink(),
19271 )
19272 .child(buttons(&diagnostic))
19273 .child(div().flex().flex_shrink_0().child(
19274 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19275 &text_style,
19276 code_ranges.iter().map(|range| {
19277 (
19278 range.clone(),
19279 HighlightStyle {
19280 font_weight: Some(FontWeight::BOLD),
19281 ..Default::default()
19282 },
19283 )
19284 }),
19285 ),
19286 ))
19287 .into_any_element()
19288 })
19289}
19290
19291fn inline_completion_edit_text(
19292 current_snapshot: &BufferSnapshot,
19293 edits: &[(Range<Anchor>, String)],
19294 edit_preview: &EditPreview,
19295 include_deletions: bool,
19296 cx: &App,
19297) -> HighlightedText {
19298 let edits = edits
19299 .iter()
19300 .map(|(anchor, text)| {
19301 (
19302 anchor.start.text_anchor..anchor.end.text_anchor,
19303 text.clone(),
19304 )
19305 })
19306 .collect::<Vec<_>>();
19307
19308 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19309}
19310
19311pub fn highlight_diagnostic_message(
19312 diagnostic: &Diagnostic,
19313 mut max_message_rows: Option<u8>,
19314) -> (SharedString, Vec<Range<usize>>) {
19315 let mut text_without_backticks = String::new();
19316 let mut code_ranges = Vec::new();
19317
19318 if let Some(source) = &diagnostic.source {
19319 text_without_backticks.push_str(source);
19320 code_ranges.push(0..source.len());
19321 text_without_backticks.push_str(": ");
19322 }
19323
19324 let mut prev_offset = 0;
19325 let mut in_code_block = false;
19326 let has_row_limit = max_message_rows.is_some();
19327 let mut newline_indices = diagnostic
19328 .message
19329 .match_indices('\n')
19330 .filter(|_| has_row_limit)
19331 .map(|(ix, _)| ix)
19332 .fuse()
19333 .peekable();
19334
19335 for (quote_ix, _) in diagnostic
19336 .message
19337 .match_indices('`')
19338 .chain([(diagnostic.message.len(), "")])
19339 {
19340 let mut first_newline_ix = None;
19341 let mut last_newline_ix = None;
19342 while let Some(newline_ix) = newline_indices.peek() {
19343 if *newline_ix < quote_ix {
19344 if first_newline_ix.is_none() {
19345 first_newline_ix = Some(*newline_ix);
19346 }
19347 last_newline_ix = Some(*newline_ix);
19348
19349 if let Some(rows_left) = &mut max_message_rows {
19350 if *rows_left == 0 {
19351 break;
19352 } else {
19353 *rows_left -= 1;
19354 }
19355 }
19356 let _ = newline_indices.next();
19357 } else {
19358 break;
19359 }
19360 }
19361 let prev_len = text_without_backticks.len();
19362 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19363 text_without_backticks.push_str(new_text);
19364 if in_code_block {
19365 code_ranges.push(prev_len..text_without_backticks.len());
19366 }
19367 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19368 in_code_block = !in_code_block;
19369 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19370 text_without_backticks.push_str("...");
19371 break;
19372 }
19373 }
19374
19375 (text_without_backticks.into(), code_ranges)
19376}
19377
19378fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19379 match severity {
19380 DiagnosticSeverity::ERROR => colors.error,
19381 DiagnosticSeverity::WARNING => colors.warning,
19382 DiagnosticSeverity::INFORMATION => colors.info,
19383 DiagnosticSeverity::HINT => colors.info,
19384 _ => colors.ignored,
19385 }
19386}
19387
19388pub fn styled_runs_for_code_label<'a>(
19389 label: &'a CodeLabel,
19390 syntax_theme: &'a theme::SyntaxTheme,
19391) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19392 let fade_out = HighlightStyle {
19393 fade_out: Some(0.35),
19394 ..Default::default()
19395 };
19396
19397 let mut prev_end = label.filter_range.end;
19398 label
19399 .runs
19400 .iter()
19401 .enumerate()
19402 .flat_map(move |(ix, (range, highlight_id))| {
19403 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19404 style
19405 } else {
19406 return Default::default();
19407 };
19408 let mut muted_style = style;
19409 muted_style.highlight(fade_out);
19410
19411 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19412 if range.start >= label.filter_range.end {
19413 if range.start > prev_end {
19414 runs.push((prev_end..range.start, fade_out));
19415 }
19416 runs.push((range.clone(), muted_style));
19417 } else if range.end <= label.filter_range.end {
19418 runs.push((range.clone(), style));
19419 } else {
19420 runs.push((range.start..label.filter_range.end, style));
19421 runs.push((label.filter_range.end..range.end, muted_style));
19422 }
19423 prev_end = cmp::max(prev_end, range.end);
19424
19425 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19426 runs.push((prev_end..label.text.len(), fade_out));
19427 }
19428
19429 runs
19430 })
19431}
19432
19433pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19434 let mut prev_index = 0;
19435 let mut prev_codepoint: Option<char> = None;
19436 text.char_indices()
19437 .chain([(text.len(), '\0')])
19438 .filter_map(move |(index, codepoint)| {
19439 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19440 let is_boundary = index == text.len()
19441 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19442 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19443 if is_boundary {
19444 let chunk = &text[prev_index..index];
19445 prev_index = index;
19446 Some(chunk)
19447 } else {
19448 None
19449 }
19450 })
19451}
19452
19453pub trait RangeToAnchorExt: Sized {
19454 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19455
19456 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19457 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19458 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19459 }
19460}
19461
19462impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19463 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19464 let start_offset = self.start.to_offset(snapshot);
19465 let end_offset = self.end.to_offset(snapshot);
19466 if start_offset == end_offset {
19467 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19468 } else {
19469 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19470 }
19471 }
19472}
19473
19474pub trait RowExt {
19475 fn as_f32(&self) -> f32;
19476
19477 fn next_row(&self) -> Self;
19478
19479 fn previous_row(&self) -> Self;
19480
19481 fn minus(&self, other: Self) -> u32;
19482}
19483
19484impl RowExt for DisplayRow {
19485 fn as_f32(&self) -> f32 {
19486 self.0 as f32
19487 }
19488
19489 fn next_row(&self) -> Self {
19490 Self(self.0 + 1)
19491 }
19492
19493 fn previous_row(&self) -> Self {
19494 Self(self.0.saturating_sub(1))
19495 }
19496
19497 fn minus(&self, other: Self) -> u32 {
19498 self.0 - other.0
19499 }
19500}
19501
19502impl RowExt for MultiBufferRow {
19503 fn as_f32(&self) -> f32 {
19504 self.0 as f32
19505 }
19506
19507 fn next_row(&self) -> Self {
19508 Self(self.0 + 1)
19509 }
19510
19511 fn previous_row(&self) -> Self {
19512 Self(self.0.saturating_sub(1))
19513 }
19514
19515 fn minus(&self, other: Self) -> u32 {
19516 self.0 - other.0
19517 }
19518}
19519
19520trait RowRangeExt {
19521 type Row;
19522
19523 fn len(&self) -> usize;
19524
19525 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19526}
19527
19528impl RowRangeExt for Range<MultiBufferRow> {
19529 type Row = MultiBufferRow;
19530
19531 fn len(&self) -> usize {
19532 (self.end.0 - self.start.0) as usize
19533 }
19534
19535 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19536 (self.start.0..self.end.0).map(MultiBufferRow)
19537 }
19538}
19539
19540impl RowRangeExt for Range<DisplayRow> {
19541 type Row = DisplayRow;
19542
19543 fn len(&self) -> usize {
19544 (self.end.0 - self.start.0) as usize
19545 }
19546
19547 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19548 (self.start.0..self.end.0).map(DisplayRow)
19549 }
19550}
19551
19552/// If select range has more than one line, we
19553/// just point the cursor to range.start.
19554fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19555 if range.start.row == range.end.row {
19556 range
19557 } else {
19558 range.start..range.start
19559 }
19560}
19561pub struct KillRing(ClipboardItem);
19562impl Global for KillRing {}
19563
19564const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19565
19566struct BreakpointPromptEditor {
19567 pub(crate) prompt: Entity<Editor>,
19568 editor: WeakEntity<Editor>,
19569 breakpoint_anchor: Anchor,
19570 kind: BreakpointKind,
19571 block_ids: HashSet<CustomBlockId>,
19572 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19573 _subscriptions: Vec<Subscription>,
19574}
19575
19576impl BreakpointPromptEditor {
19577 const MAX_LINES: u8 = 4;
19578
19579 fn new(
19580 editor: WeakEntity<Editor>,
19581 breakpoint_anchor: Anchor,
19582 kind: BreakpointKind,
19583 window: &mut Window,
19584 cx: &mut Context<Self>,
19585 ) -> Self {
19586 let buffer = cx.new(|cx| {
19587 Buffer::local(
19588 kind.log_message()
19589 .map(|msg| msg.to_string())
19590 .unwrap_or_default(),
19591 cx,
19592 )
19593 });
19594 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19595
19596 let prompt = cx.new(|cx| {
19597 let mut prompt = Editor::new(
19598 EditorMode::AutoHeight {
19599 max_lines: Self::MAX_LINES as usize,
19600 },
19601 buffer,
19602 None,
19603 window,
19604 cx,
19605 );
19606 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19607 prompt.set_show_cursor_when_unfocused(false, cx);
19608 prompt.set_placeholder_text(
19609 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19610 cx,
19611 );
19612
19613 prompt
19614 });
19615
19616 Self {
19617 prompt,
19618 editor,
19619 breakpoint_anchor,
19620 kind,
19621 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19622 block_ids: Default::default(),
19623 _subscriptions: vec![],
19624 }
19625 }
19626
19627 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19628 self.block_ids.extend(block_ids)
19629 }
19630
19631 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19632 if let Some(editor) = self.editor.upgrade() {
19633 let log_message = self
19634 .prompt
19635 .read(cx)
19636 .buffer
19637 .read(cx)
19638 .as_singleton()
19639 .expect("A multi buffer in breakpoint prompt isn't possible")
19640 .read(cx)
19641 .as_rope()
19642 .to_string();
19643
19644 editor.update(cx, |editor, cx| {
19645 editor.edit_breakpoint_at_anchor(
19646 self.breakpoint_anchor,
19647 self.kind.clone(),
19648 BreakpointEditAction::EditLogMessage(log_message.into()),
19649 cx,
19650 );
19651
19652 editor.remove_blocks(self.block_ids.clone(), None, cx);
19653 cx.focus_self(window);
19654 });
19655 }
19656 }
19657
19658 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19659 self.editor
19660 .update(cx, |editor, cx| {
19661 editor.remove_blocks(self.block_ids.clone(), None, cx);
19662 window.focus(&editor.focus_handle);
19663 })
19664 .log_err();
19665 }
19666
19667 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19668 let settings = ThemeSettings::get_global(cx);
19669 let text_style = TextStyle {
19670 color: if self.prompt.read(cx).read_only(cx) {
19671 cx.theme().colors().text_disabled
19672 } else {
19673 cx.theme().colors().text
19674 },
19675 font_family: settings.buffer_font.family.clone(),
19676 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19677 font_size: settings.buffer_font_size(cx).into(),
19678 font_weight: settings.buffer_font.weight,
19679 line_height: relative(settings.buffer_line_height.value()),
19680 ..Default::default()
19681 };
19682 EditorElement::new(
19683 &self.prompt,
19684 EditorStyle {
19685 background: cx.theme().colors().editor_background,
19686 local_player: cx.theme().players().local(),
19687 text: text_style,
19688 ..Default::default()
19689 },
19690 )
19691 }
19692}
19693
19694impl Render for BreakpointPromptEditor {
19695 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19696 let gutter_dimensions = *self.gutter_dimensions.lock();
19697 h_flex()
19698 .key_context("Editor")
19699 .bg(cx.theme().colors().editor_background)
19700 .border_y_1()
19701 .border_color(cx.theme().status().info_border)
19702 .size_full()
19703 .py(window.line_height() / 2.5)
19704 .on_action(cx.listener(Self::confirm))
19705 .on_action(cx.listener(Self::cancel))
19706 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19707 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19708 }
19709}
19710
19711impl Focusable for BreakpointPromptEditor {
19712 fn focus_handle(&self, cx: &App) -> FocusHandle {
19713 self.prompt.focus_handle(cx)
19714 }
19715}
19716
19717fn all_edits_insertions_or_deletions(
19718 edits: &Vec<(Range<Anchor>, String)>,
19719 snapshot: &MultiBufferSnapshot,
19720) -> bool {
19721 let mut all_insertions = true;
19722 let mut all_deletions = true;
19723
19724 for (range, new_text) in edits.iter() {
19725 let range_is_empty = range.to_offset(&snapshot).is_empty();
19726 let text_is_empty = new_text.is_empty();
19727
19728 if range_is_empty != text_is_empty {
19729 if range_is_empty {
19730 all_deletions = false;
19731 } else {
19732 all_insertions = false;
19733 }
19734 } else {
19735 return false;
19736 }
19737
19738 if !all_insertions && !all_deletions {
19739 return false;
19740 }
19741 }
19742 all_insertions || all_deletions
19743}
19744
19745struct MissingEditPredictionKeybindingTooltip;
19746
19747impl Render for MissingEditPredictionKeybindingTooltip {
19748 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19749 ui::tooltip_container(window, cx, |container, _, cx| {
19750 container
19751 .flex_shrink_0()
19752 .max_w_80()
19753 .min_h(rems_from_px(124.))
19754 .justify_between()
19755 .child(
19756 v_flex()
19757 .flex_1()
19758 .text_ui_sm(cx)
19759 .child(Label::new("Conflict with Accept Keybinding"))
19760 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19761 )
19762 .child(
19763 h_flex()
19764 .pb_1()
19765 .gap_1()
19766 .items_end()
19767 .w_full()
19768 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19769 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19770 }))
19771 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19772 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19773 })),
19774 )
19775 })
19776 }
19777}
19778
19779#[derive(Debug, Clone, Copy, PartialEq)]
19780pub struct LineHighlight {
19781 pub background: Background,
19782 pub border: Option<gpui::Hsla>,
19783}
19784
19785impl From<Hsla> for LineHighlight {
19786 fn from(hsla: Hsla) -> Self {
19787 Self {
19788 background: hsla.into(),
19789 border: None,
19790 }
19791 }
19792}
19793
19794impl From<Background> for LineHighlight {
19795 fn from(background: Background) -> Self {
19796 Self {
19797 background,
19798 border: None,
19799 }
19800 }
19801}