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::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::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
189use workspace::{
190 item::{ItemHandle, PreviewTabsSettings},
191 ItemId, RestoreOnStartupBehavior,
192};
193use workspace::{
194 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
195 WorkspaceSettings,
196};
197use workspace::{
198 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
199};
200use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
201
202use crate::hover_links::{find_url, find_url_from_range};
203use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
204
205pub const FILE_HEADER_HEIGHT: u32 = 2;
206pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
207pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
208const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
209const MAX_LINE_LEN: usize = 1024;
210const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
211const MAX_SELECTION_HISTORY_LEN: usize = 1024;
212pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
213#[doc(hidden)]
214pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
215
216pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
217pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
218pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
219
220pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
221pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
222pub(crate) const MIN_LINE_NUMBER_DIGITS: u32 = 4;
223
224const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
225 alt: true,
226 shift: true,
227 control: false,
228 platform: false,
229 function: false,
230};
231
232#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
233pub enum InlayId {
234 InlineCompletion(usize),
235 Hint(usize),
236}
237
238impl InlayId {
239 fn id(&self) -> usize {
240 match self {
241 Self::InlineCompletion(id) => *id,
242 Self::Hint(id) => *id,
243 }
244 }
245}
246
247pub enum DebugCurrentRowHighlight {}
248enum DocumentHighlightRead {}
249enum DocumentHighlightWrite {}
250enum InputComposition {}
251enum SelectedTextHighlight {}
252
253#[derive(Debug, Copy, Clone, PartialEq, Eq)]
254pub enum Navigated {
255 Yes,
256 No,
257}
258
259impl Navigated {
260 pub fn from_bool(yes: bool) -> Navigated {
261 if yes {
262 Navigated::Yes
263 } else {
264 Navigated::No
265 }
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270enum DisplayDiffHunk {
271 Folded {
272 display_row: DisplayRow,
273 },
274 Unfolded {
275 is_created_file: bool,
276 diff_base_byte_range: Range<usize>,
277 display_row_range: Range<DisplayRow>,
278 multi_buffer_range: Range<Anchor>,
279 status: DiffHunkStatus,
280 },
281}
282
283pub fn init_settings(cx: &mut App) {
284 EditorSettings::register(cx);
285}
286
287pub fn init(cx: &mut App) {
288 init_settings(cx);
289
290 workspace::register_project_item::<Editor>(cx);
291 workspace::FollowableViewRegistry::register::<Editor>(cx);
292 workspace::register_serializable_item::<Editor>(cx);
293
294 cx.observe_new(
295 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
296 workspace.register_action(Editor::new_file);
297 workspace.register_action(Editor::new_file_vertical);
298 workspace.register_action(Editor::new_file_horizontal);
299 workspace.register_action(Editor::cancel_language_server_work);
300 },
301 )
302 .detach();
303
304 cx.on_action(move |_: &workspace::NewFile, cx| {
305 let app_state = workspace::AppState::global(cx);
306 if let Some(app_state) = app_state.upgrade() {
307 workspace::open_new(
308 Default::default(),
309 app_state,
310 cx,
311 |workspace, window, cx| {
312 Editor::new_file(workspace, &Default::default(), window, cx)
313 },
314 )
315 .detach();
316 }
317 });
318 cx.on_action(move |_: &workspace::NewWindow, cx| {
319 let app_state = workspace::AppState::global(cx);
320 if let Some(app_state) = app_state.upgrade() {
321 workspace::open_new(
322 Default::default(),
323 app_state,
324 cx,
325 |workspace, window, cx| {
326 cx.activate(true);
327 Editor::new_file(workspace, &Default::default(), window, cx)
328 },
329 )
330 .detach();
331 }
332 });
333}
334
335pub struct SearchWithinRange;
336
337trait InvalidationRegion {
338 fn ranges(&self) -> &[Range<Anchor>];
339}
340
341#[derive(Clone, Debug, PartialEq)]
342pub enum SelectPhase {
343 Begin {
344 position: DisplayPoint,
345 add: bool,
346 click_count: usize,
347 },
348 BeginColumnar {
349 position: DisplayPoint,
350 reset: bool,
351 goal_column: u32,
352 },
353 Extend {
354 position: DisplayPoint,
355 click_count: usize,
356 },
357 Update {
358 position: DisplayPoint,
359 goal_column: u32,
360 scroll_delta: gpui::Point<f32>,
361 },
362 End,
363}
364
365#[derive(Clone, Debug)]
366pub enum SelectMode {
367 Character,
368 Word(Range<Anchor>),
369 Line(Range<Anchor>),
370 All,
371}
372
373#[derive(Copy, Clone, PartialEq, Eq, Debug)]
374pub enum EditorMode {
375 SingleLine { auto_width: bool },
376 AutoHeight { max_lines: usize },
377 Full,
378}
379
380#[derive(Copy, Clone, Debug)]
381pub enum SoftWrap {
382 /// Prefer not to wrap at all.
383 ///
384 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
385 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
386 GitDiff,
387 /// Prefer a single line generally, unless an overly long line is encountered.
388 None,
389 /// Soft wrap lines that exceed the editor width.
390 EditorWidth,
391 /// Soft wrap lines at the preferred line length.
392 Column(u32),
393 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
394 Bounded(u32),
395}
396
397#[derive(Clone)]
398pub struct EditorStyle {
399 pub background: Hsla,
400 pub local_player: PlayerColor,
401 pub text: TextStyle,
402 pub scrollbar_width: Pixels,
403 pub syntax: Arc<SyntaxTheme>,
404 pub status: StatusColors,
405 pub inlay_hints_style: HighlightStyle,
406 pub inline_completion_styles: InlineCompletionStyles,
407 pub unnecessary_code_fade: f32,
408}
409
410impl Default for EditorStyle {
411 fn default() -> Self {
412 Self {
413 background: Hsla::default(),
414 local_player: PlayerColor::default(),
415 text: TextStyle::default(),
416 scrollbar_width: Pixels::default(),
417 syntax: Default::default(),
418 // HACK: Status colors don't have a real default.
419 // We should look into removing the status colors from the editor
420 // style and retrieve them directly from the theme.
421 status: StatusColors::dark(),
422 inlay_hints_style: HighlightStyle::default(),
423 inline_completion_styles: InlineCompletionStyles {
424 insertion: HighlightStyle::default(),
425 whitespace: HighlightStyle::default(),
426 },
427 unnecessary_code_fade: Default::default(),
428 }
429 }
430}
431
432pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
433 let show_background = language_settings::language_settings(None, None, cx)
434 .inlay_hints
435 .show_background;
436
437 HighlightStyle {
438 color: Some(cx.theme().status().hint),
439 background_color: show_background.then(|| cx.theme().status().hint_background),
440 ..HighlightStyle::default()
441 }
442}
443
444pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
445 InlineCompletionStyles {
446 insertion: HighlightStyle {
447 color: Some(cx.theme().status().predictive),
448 ..HighlightStyle::default()
449 },
450 whitespace: HighlightStyle {
451 background_color: Some(cx.theme().status().created_background),
452 ..HighlightStyle::default()
453 },
454 }
455}
456
457type CompletionId = usize;
458
459pub(crate) enum EditDisplayMode {
460 TabAccept,
461 DiffPopover,
462 Inline,
463}
464
465enum InlineCompletion {
466 Edit {
467 edits: Vec<(Range<Anchor>, String)>,
468 edit_preview: Option<EditPreview>,
469 display_mode: EditDisplayMode,
470 snapshot: BufferSnapshot,
471 },
472 Move {
473 target: Anchor,
474 snapshot: BufferSnapshot,
475 },
476}
477
478struct InlineCompletionState {
479 inlay_ids: Vec<InlayId>,
480 completion: InlineCompletion,
481 completion_id: Option<SharedString>,
482 invalidation_range: Range<Anchor>,
483}
484
485enum EditPredictionSettings {
486 Disabled,
487 Enabled {
488 show_in_menu: bool,
489 preview_requires_modifier: bool,
490 },
491}
492
493enum InlineCompletionHighlight {}
494
495#[derive(Debug, Clone)]
496struct InlineDiagnostic {
497 message: SharedString,
498 group_id: usize,
499 is_primary: bool,
500 start: Point,
501 severity: DiagnosticSeverity,
502}
503
504pub enum MenuInlineCompletionsPolicy {
505 Never,
506 ByProvider,
507}
508
509pub enum EditPredictionPreview {
510 /// Modifier is not pressed
511 Inactive { released_too_fast: bool },
512 /// Modifier pressed
513 Active {
514 since: Instant,
515 previous_scroll_position: Option<ScrollAnchor>,
516 },
517}
518
519impl EditPredictionPreview {
520 pub fn released_too_fast(&self) -> bool {
521 match self {
522 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
523 EditPredictionPreview::Active { .. } => false,
524 }
525 }
526
527 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
528 if let EditPredictionPreview::Active {
529 previous_scroll_position,
530 ..
531 } = self
532 {
533 *previous_scroll_position = scroll_position;
534 }
535 }
536}
537
538#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
539struct EditorActionId(usize);
540
541impl EditorActionId {
542 pub fn post_inc(&mut self) -> Self {
543 let answer = self.0;
544
545 *self = Self(answer + 1);
546
547 Self(answer)
548 }
549}
550
551// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
552// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
553
554type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
555type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
556
557#[derive(Default)]
558struct ScrollbarMarkerState {
559 scrollbar_size: Size<Pixels>,
560 dirty: bool,
561 markers: Arc<[PaintQuad]>,
562 pending_refresh: Option<Task<Result<()>>>,
563}
564
565impl ScrollbarMarkerState {
566 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
567 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
568 }
569}
570
571#[derive(Clone, Debug)]
572struct RunnableTasks {
573 templates: Vec<(TaskSourceKind, TaskTemplate)>,
574 offset: multi_buffer::Anchor,
575 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
576 column: u32,
577 // Values of all named captures, including those starting with '_'
578 extra_variables: HashMap<String, String>,
579 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
580 context_range: Range<BufferOffset>,
581}
582
583impl RunnableTasks {
584 fn resolve<'a>(
585 &'a self,
586 cx: &'a task::TaskContext,
587 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
588 self.templates.iter().filter_map(|(kind, template)| {
589 template
590 .resolve_task(&kind.to_id_base(), cx)
591 .map(|task| (kind.clone(), task))
592 })
593 }
594}
595
596#[derive(Clone)]
597struct ResolvedTasks {
598 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
599 position: Anchor,
600}
601
602#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
603struct BufferOffset(usize);
604
605// Addons allow storing per-editor state in other crates (e.g. Vim)
606pub trait Addon: 'static {
607 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
608
609 fn render_buffer_header_controls(
610 &self,
611 _: &ExcerptInfo,
612 _: &Window,
613 _: &App,
614 ) -> Option<AnyElement> {
615 None
616 }
617
618 fn to_any(&self) -> &dyn std::any::Any;
619}
620
621/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
622///
623/// See the [module level documentation](self) for more information.
624pub struct Editor {
625 focus_handle: FocusHandle,
626 last_focused_descendant: Option<WeakFocusHandle>,
627 /// The text buffer being edited
628 buffer: Entity<MultiBuffer>,
629 /// Map of how text in the buffer should be displayed.
630 /// Handles soft wraps, folds, fake inlay text insertions, etc.
631 pub display_map: Entity<DisplayMap>,
632 pub selections: SelectionsCollection,
633 pub scroll_manager: ScrollManager,
634 /// When inline assist editors are linked, they all render cursors because
635 /// typing enters text into each of them, even the ones that aren't focused.
636 pub(crate) show_cursor_when_unfocused: bool,
637 columnar_selection_tail: Option<Anchor>,
638 add_selections_state: Option<AddSelectionsState>,
639 select_next_state: Option<SelectNextState>,
640 select_prev_state: Option<SelectNextState>,
641 selection_history: SelectionHistory,
642 autoclose_regions: Vec<AutocloseRegion>,
643 snippet_stack: InvalidationStack<SnippetState>,
644 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
645 ime_transaction: Option<TransactionId>,
646 active_diagnostics: Option<ActiveDiagnosticGroup>,
647 show_inline_diagnostics: bool,
648 inline_diagnostics_update: Task<()>,
649 inline_diagnostics_enabled: bool,
650 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
651 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
652 hard_wrap: Option<usize>,
653
654 // TODO: make this a access method
655 pub project: Option<Entity<Project>>,
656 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
657 completion_provider: Option<Box<dyn CompletionProvider>>,
658 collaboration_hub: Option<Box<dyn CollaborationHub>>,
659 blink_manager: Entity<BlinkManager>,
660 show_cursor_names: bool,
661 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
662 pub show_local_selections: bool,
663 mode: EditorMode,
664 show_breadcrumbs: bool,
665 show_gutter: bool,
666 show_scrollbars: bool,
667 show_line_numbers: Option<bool>,
668 use_relative_line_numbers: Option<bool>,
669 show_git_diff_gutter: Option<bool>,
670 show_code_actions: Option<bool>,
671 show_runnables: Option<bool>,
672 show_breakpoints: Option<bool>,
673 show_wrap_guides: Option<bool>,
674 show_indent_guides: Option<bool>,
675 placeholder_text: Option<Arc<str>>,
676 highlight_order: usize,
677 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
678 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
679 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
680 scrollbar_marker_state: ScrollbarMarkerState,
681 active_indent_guides_state: ActiveIndentGuidesState,
682 nav_history: Option<ItemNavHistory>,
683 context_menu: RefCell<Option<CodeContextMenu>>,
684 mouse_context_menu: Option<MouseContextMenu>,
685 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
686 signature_help_state: SignatureHelpState,
687 auto_signature_help: Option<bool>,
688 find_all_references_task_sources: Vec<Anchor>,
689 next_completion_id: CompletionId,
690 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
691 code_actions_task: Option<Task<Result<()>>>,
692 selection_highlight_task: Option<Task<()>>,
693 document_highlights_task: Option<Task<()>>,
694 linked_editing_range_task: Option<Task<Option<()>>>,
695 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
696 pending_rename: Option<RenameState>,
697 searchable: bool,
698 cursor_shape: CursorShape,
699 current_line_highlight: Option<CurrentLineHighlight>,
700 collapse_matches: bool,
701 autoindent_mode: Option<AutoindentMode>,
702 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
703 input_enabled: bool,
704 use_modal_editing: bool,
705 read_only: bool,
706 leader_peer_id: Option<PeerId>,
707 remote_id: Option<ViewId>,
708 hover_state: HoverState,
709 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
710 gutter_hovered: bool,
711 hovered_link_state: Option<HoveredLinkState>,
712 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
713 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
714 active_inline_completion: Option<InlineCompletionState>,
715 /// Used to prevent flickering as the user types while the menu is open
716 stale_inline_completion_in_menu: Option<InlineCompletionState>,
717 edit_prediction_settings: EditPredictionSettings,
718 inline_completions_hidden_for_vim_mode: bool,
719 show_inline_completions_override: Option<bool>,
720 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
721 edit_prediction_preview: EditPredictionPreview,
722 edit_prediction_indent_conflict: bool,
723 edit_prediction_requires_modifier_in_indent_conflict: bool,
724 inlay_hint_cache: InlayHintCache,
725 next_inlay_id: usize,
726 _subscriptions: Vec<Subscription>,
727 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
728 gutter_dimensions: GutterDimensions,
729 style: Option<EditorStyle>,
730 text_style_refinement: Option<TextStyleRefinement>,
731 next_editor_action_id: EditorActionId,
732 editor_actions:
733 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
734 use_autoclose: bool,
735 use_auto_surround: bool,
736 auto_replace_emoji_shortcode: bool,
737 jsx_tag_auto_close_enabled_in_any_buffer: bool,
738 show_git_blame_gutter: bool,
739 show_git_blame_inline: bool,
740 show_git_blame_inline_delay_task: Option<Task<()>>,
741 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
742 git_blame_inline_enabled: bool,
743 serialize_dirty_buffers: bool,
744 show_selection_menu: Option<bool>,
745 blame: Option<Entity<GitBlame>>,
746 blame_subscription: Option<Subscription>,
747 custom_context_menu: Option<
748 Box<
749 dyn 'static
750 + Fn(
751 &mut Self,
752 DisplayPoint,
753 &mut Window,
754 &mut Context<Self>,
755 ) -> Option<Entity<ui::ContextMenu>>,
756 >,
757 >,
758 last_bounds: Option<Bounds<Pixels>>,
759 last_position_map: Option<Rc<PositionMap>>,
760 expect_bounds_change: Option<Bounds<Pixels>>,
761 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
762 tasks_update_task: Option<Task<()>>,
763 pub breakpoint_store: Option<Entity<BreakpointStore>>,
764 /// Allow's a user to create a breakpoint by selecting this indicator
765 /// It should be None while a user is not hovering over the gutter
766 /// Otherwise it represents the point that the breakpoint will be shown
767 pub gutter_breakpoint_indicator: Option<DisplayPoint>,
768 in_project_search: bool,
769 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
770 breadcrumb_header: Option<String>,
771 focused_block: Option<FocusedBlock>,
772 next_scroll_position: NextScrollCursorCenterTopBottom,
773 addons: HashMap<TypeId, Box<dyn Addon>>,
774 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
775 load_diff_task: Option<Shared<Task<()>>>,
776 selection_mark_mode: bool,
777 toggle_fold_multiple_buffers: Task<()>,
778 _scroll_cursor_center_top_bottom_task: Task<()>,
779 serialize_selections: Task<()>,
780}
781
782#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
783enum NextScrollCursorCenterTopBottom {
784 #[default]
785 Center,
786 Top,
787 Bottom,
788}
789
790impl NextScrollCursorCenterTopBottom {
791 fn next(&self) -> Self {
792 match self {
793 Self::Center => Self::Top,
794 Self::Top => Self::Bottom,
795 Self::Bottom => Self::Center,
796 }
797 }
798}
799
800#[derive(Clone)]
801pub struct EditorSnapshot {
802 pub mode: EditorMode,
803 show_gutter: bool,
804 show_line_numbers: Option<bool>,
805 show_git_diff_gutter: Option<bool>,
806 show_code_actions: Option<bool>,
807 show_runnables: Option<bool>,
808 show_breakpoints: Option<bool>,
809 git_blame_gutter_max_author_length: Option<usize>,
810 pub display_snapshot: DisplaySnapshot,
811 pub placeholder_text: Option<Arc<str>>,
812 is_focused: bool,
813 scroll_anchor: ScrollAnchor,
814 ongoing_scroll: OngoingScroll,
815 current_line_highlight: CurrentLineHighlight,
816 gutter_hovered: bool,
817}
818
819const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
820
821#[derive(Default, Debug, Clone, Copy)]
822pub struct GutterDimensions {
823 pub left_padding: Pixels,
824 pub right_padding: Pixels,
825 pub width: Pixels,
826 pub margin: Pixels,
827 pub git_blame_entries_width: Option<Pixels>,
828}
829
830impl GutterDimensions {
831 /// The full width of the space taken up by the gutter.
832 pub fn full_width(&self) -> Pixels {
833 self.margin + self.width
834 }
835
836 /// The width of the space reserved for the fold indicators,
837 /// use alongside 'justify_end' and `gutter_width` to
838 /// right align content with the line numbers
839 pub fn fold_area_width(&self) -> Pixels {
840 self.margin + self.right_padding
841 }
842}
843
844#[derive(Debug)]
845pub struct RemoteSelection {
846 pub replica_id: ReplicaId,
847 pub selection: Selection<Anchor>,
848 pub cursor_shape: CursorShape,
849 pub peer_id: PeerId,
850 pub line_mode: bool,
851 pub participant_index: Option<ParticipantIndex>,
852 pub user_name: Option<SharedString>,
853}
854
855#[derive(Clone, Debug)]
856struct SelectionHistoryEntry {
857 selections: Arc<[Selection<Anchor>]>,
858 select_next_state: Option<SelectNextState>,
859 select_prev_state: Option<SelectNextState>,
860 add_selections_state: Option<AddSelectionsState>,
861}
862
863enum SelectionHistoryMode {
864 Normal,
865 Undoing,
866 Redoing,
867}
868
869#[derive(Clone, PartialEq, Eq, Hash)]
870struct HoveredCursor {
871 replica_id: u16,
872 selection_id: usize,
873}
874
875impl Default for SelectionHistoryMode {
876 fn default() -> Self {
877 Self::Normal
878 }
879}
880
881#[derive(Default)]
882struct SelectionHistory {
883 #[allow(clippy::type_complexity)]
884 selections_by_transaction:
885 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
886 mode: SelectionHistoryMode,
887 undo_stack: VecDeque<SelectionHistoryEntry>,
888 redo_stack: VecDeque<SelectionHistoryEntry>,
889}
890
891impl SelectionHistory {
892 fn insert_transaction(
893 &mut self,
894 transaction_id: TransactionId,
895 selections: Arc<[Selection<Anchor>]>,
896 ) {
897 self.selections_by_transaction
898 .insert(transaction_id, (selections, None));
899 }
900
901 #[allow(clippy::type_complexity)]
902 fn transaction(
903 &self,
904 transaction_id: TransactionId,
905 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
906 self.selections_by_transaction.get(&transaction_id)
907 }
908
909 #[allow(clippy::type_complexity)]
910 fn transaction_mut(
911 &mut self,
912 transaction_id: TransactionId,
913 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
914 self.selections_by_transaction.get_mut(&transaction_id)
915 }
916
917 fn push(&mut self, entry: SelectionHistoryEntry) {
918 if !entry.selections.is_empty() {
919 match self.mode {
920 SelectionHistoryMode::Normal => {
921 self.push_undo(entry);
922 self.redo_stack.clear();
923 }
924 SelectionHistoryMode::Undoing => self.push_redo(entry),
925 SelectionHistoryMode::Redoing => self.push_undo(entry),
926 }
927 }
928 }
929
930 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
931 if self
932 .undo_stack
933 .back()
934 .map_or(true, |e| e.selections != entry.selections)
935 {
936 self.undo_stack.push_back(entry);
937 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
938 self.undo_stack.pop_front();
939 }
940 }
941 }
942
943 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
944 if self
945 .redo_stack
946 .back()
947 .map_or(true, |e| e.selections != entry.selections)
948 {
949 self.redo_stack.push_back(entry);
950 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
951 self.redo_stack.pop_front();
952 }
953 }
954 }
955}
956
957struct RowHighlight {
958 index: usize,
959 range: Range<Anchor>,
960 color: Hsla,
961 should_autoscroll: bool,
962}
963
964#[derive(Clone, Debug)]
965struct AddSelectionsState {
966 above: bool,
967 stack: Vec<usize>,
968}
969
970#[derive(Clone)]
971struct SelectNextState {
972 query: AhoCorasick,
973 wordwise: bool,
974 done: bool,
975}
976
977impl std::fmt::Debug for SelectNextState {
978 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
979 f.debug_struct(std::any::type_name::<Self>())
980 .field("wordwise", &self.wordwise)
981 .field("done", &self.done)
982 .finish()
983 }
984}
985
986#[derive(Debug)]
987struct AutocloseRegion {
988 selection_id: usize,
989 range: Range<Anchor>,
990 pair: BracketPair,
991}
992
993#[derive(Debug)]
994struct SnippetState {
995 ranges: Vec<Vec<Range<Anchor>>>,
996 active_index: usize,
997 choices: Vec<Option<Vec<String>>>,
998}
999
1000#[doc(hidden)]
1001pub struct RenameState {
1002 pub range: Range<Anchor>,
1003 pub old_name: Arc<str>,
1004 pub editor: Entity<Editor>,
1005 block_id: CustomBlockId,
1006}
1007
1008struct InvalidationStack<T>(Vec<T>);
1009
1010struct RegisteredInlineCompletionProvider {
1011 provider: Arc<dyn InlineCompletionProviderHandle>,
1012 _subscription: Subscription,
1013}
1014
1015#[derive(Debug, PartialEq, Eq)]
1016struct ActiveDiagnosticGroup {
1017 primary_range: Range<Anchor>,
1018 primary_message: String,
1019 group_id: usize,
1020 blocks: HashMap<CustomBlockId, Diagnostic>,
1021 is_valid: bool,
1022}
1023
1024#[derive(Serialize, Deserialize, Clone, Debug)]
1025pub struct ClipboardSelection {
1026 /// The number of bytes in this selection.
1027 pub len: usize,
1028 /// Whether this was a full-line selection.
1029 pub is_entire_line: bool,
1030 /// The indentation of the first line when this content was originally copied.
1031 pub first_line_indent: u32,
1032}
1033
1034#[derive(Debug)]
1035pub(crate) struct NavigationData {
1036 cursor_anchor: Anchor,
1037 cursor_position: Point,
1038 scroll_anchor: ScrollAnchor,
1039 scroll_top_row: u32,
1040}
1041
1042#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1043pub enum GotoDefinitionKind {
1044 Symbol,
1045 Declaration,
1046 Type,
1047 Implementation,
1048}
1049
1050#[derive(Debug, Clone)]
1051enum InlayHintRefreshReason {
1052 ModifiersChanged(bool),
1053 Toggle(bool),
1054 SettingsChange(InlayHintSettings),
1055 NewLinesShown,
1056 BufferEdited(HashSet<Arc<Language>>),
1057 RefreshRequested,
1058 ExcerptsRemoved(Vec<ExcerptId>),
1059}
1060
1061impl InlayHintRefreshReason {
1062 fn description(&self) -> &'static str {
1063 match self {
1064 Self::ModifiersChanged(_) => "modifiers changed",
1065 Self::Toggle(_) => "toggle",
1066 Self::SettingsChange(_) => "settings change",
1067 Self::NewLinesShown => "new lines shown",
1068 Self::BufferEdited(_) => "buffer edited",
1069 Self::RefreshRequested => "refresh requested",
1070 Self::ExcerptsRemoved(_) => "excerpts removed",
1071 }
1072 }
1073}
1074
1075pub enum FormatTarget {
1076 Buffers,
1077 Ranges(Vec<Range<MultiBufferPoint>>),
1078}
1079
1080pub(crate) struct FocusedBlock {
1081 id: BlockId,
1082 focus_handle: WeakFocusHandle,
1083}
1084
1085#[derive(Clone)]
1086enum JumpData {
1087 MultiBufferRow {
1088 row: MultiBufferRow,
1089 line_offset_from_top: u32,
1090 },
1091 MultiBufferPoint {
1092 excerpt_id: ExcerptId,
1093 position: Point,
1094 anchor: text::Anchor,
1095 line_offset_from_top: u32,
1096 },
1097}
1098
1099pub enum MultibufferSelectionMode {
1100 First,
1101 All,
1102}
1103
1104#[derive(Clone, Copy, Debug, Default)]
1105pub struct RewrapOptions {
1106 pub override_language_settings: bool,
1107 pub preserve_existing_whitespace: bool,
1108}
1109
1110impl Editor {
1111 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1112 let buffer = cx.new(|cx| Buffer::local("", cx));
1113 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1114 Self::new(
1115 EditorMode::SingleLine { auto_width: false },
1116 buffer,
1117 None,
1118 window,
1119 cx,
1120 )
1121 }
1122
1123 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1124 let buffer = cx.new(|cx| Buffer::local("", cx));
1125 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1126 Self::new(EditorMode::Full, buffer, None, window, cx)
1127 }
1128
1129 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1130 let buffer = cx.new(|cx| Buffer::local("", cx));
1131 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1132 Self::new(
1133 EditorMode::SingleLine { auto_width: true },
1134 buffer,
1135 None,
1136 window,
1137 cx,
1138 )
1139 }
1140
1141 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1142 let buffer = cx.new(|cx| Buffer::local("", cx));
1143 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1144 Self::new(
1145 EditorMode::AutoHeight { max_lines },
1146 buffer,
1147 None,
1148 window,
1149 cx,
1150 )
1151 }
1152
1153 pub fn for_buffer(
1154 buffer: Entity<Buffer>,
1155 project: Option<Entity<Project>>,
1156 window: &mut Window,
1157 cx: &mut Context<Self>,
1158 ) -> Self {
1159 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1160 Self::new(EditorMode::Full, buffer, project, window, cx)
1161 }
1162
1163 pub fn for_multibuffer(
1164 buffer: Entity<MultiBuffer>,
1165 project: Option<Entity<Project>>,
1166 window: &mut Window,
1167 cx: &mut Context<Self>,
1168 ) -> Self {
1169 Self::new(EditorMode::Full, buffer, project, window, cx)
1170 }
1171
1172 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1173 let mut clone = Self::new(
1174 self.mode,
1175 self.buffer.clone(),
1176 self.project.clone(),
1177 window,
1178 cx,
1179 );
1180 self.display_map.update(cx, |display_map, cx| {
1181 let snapshot = display_map.snapshot(cx);
1182 clone.display_map.update(cx, |display_map, cx| {
1183 display_map.set_state(&snapshot, cx);
1184 });
1185 });
1186 clone.selections.clone_state(&self.selections);
1187 clone.scroll_manager.clone_state(&self.scroll_manager);
1188 clone.searchable = self.searchable;
1189 clone
1190 }
1191
1192 pub fn new(
1193 mode: EditorMode,
1194 buffer: Entity<MultiBuffer>,
1195 project: Option<Entity<Project>>,
1196 window: &mut Window,
1197 cx: &mut Context<Self>,
1198 ) -> Self {
1199 let style = window.text_style();
1200 let font_size = style.font_size.to_pixels(window.rem_size());
1201 let editor = cx.entity().downgrade();
1202 let fold_placeholder = FoldPlaceholder {
1203 constrain_width: true,
1204 render: Arc::new(move |fold_id, fold_range, cx| {
1205 let editor = editor.clone();
1206 div()
1207 .id(fold_id)
1208 .bg(cx.theme().colors().ghost_element_background)
1209 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1210 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1211 .rounded_xs()
1212 .size_full()
1213 .cursor_pointer()
1214 .child("⋯")
1215 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1216 .on_click(move |_, _window, cx| {
1217 editor
1218 .update(cx, |editor, cx| {
1219 editor.unfold_ranges(
1220 &[fold_range.start..fold_range.end],
1221 true,
1222 false,
1223 cx,
1224 );
1225 cx.stop_propagation();
1226 })
1227 .ok();
1228 })
1229 .into_any()
1230 }),
1231 merge_adjacent: true,
1232 ..Default::default()
1233 };
1234 let display_map = cx.new(|cx| {
1235 DisplayMap::new(
1236 buffer.clone(),
1237 style.font(),
1238 font_size,
1239 None,
1240 FILE_HEADER_HEIGHT,
1241 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1242 fold_placeholder,
1243 cx,
1244 )
1245 });
1246
1247 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1248
1249 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1250
1251 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1252 .then(|| language_settings::SoftWrap::None);
1253
1254 let mut project_subscriptions = Vec::new();
1255 if mode == EditorMode::Full {
1256 if let Some(project) = project.as_ref() {
1257 project_subscriptions.push(cx.subscribe_in(
1258 project,
1259 window,
1260 |editor, _, event, window, cx| match event {
1261 project::Event::RefreshCodeLens => {
1262 // we always query lens with actions, without storing them, always refreshing them
1263 }
1264 project::Event::RefreshInlayHints => {
1265 editor
1266 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1267 }
1268 project::Event::SnippetEdit(id, snippet_edits) => {
1269 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1270 let focus_handle = editor.focus_handle(cx);
1271 if focus_handle.is_focused(window) {
1272 let snapshot = buffer.read(cx).snapshot();
1273 for (range, snippet) in snippet_edits {
1274 let editor_range =
1275 language::range_from_lsp(*range).to_offset(&snapshot);
1276 editor
1277 .insert_snippet(
1278 &[editor_range],
1279 snippet.clone(),
1280 window,
1281 cx,
1282 )
1283 .ok();
1284 }
1285 }
1286 }
1287 }
1288 _ => {}
1289 },
1290 ));
1291 if let Some(task_inventory) = project
1292 .read(cx)
1293 .task_store()
1294 .read(cx)
1295 .task_inventory()
1296 .cloned()
1297 {
1298 project_subscriptions.push(cx.observe_in(
1299 &task_inventory,
1300 window,
1301 |editor, _, window, cx| {
1302 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1303 },
1304 ));
1305 };
1306
1307 project_subscriptions.push(cx.subscribe_in(
1308 &project.read(cx).breakpoint_store(),
1309 window,
1310 |editor, _, event, window, cx| match event {
1311 BreakpointStoreEvent::ActiveDebugLineChanged => {
1312 editor.go_to_active_debug_line(window, cx);
1313 }
1314 _ => {}
1315 },
1316 ));
1317 }
1318 }
1319
1320 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1321
1322 let inlay_hint_settings =
1323 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1324 let focus_handle = cx.focus_handle();
1325 cx.on_focus(&focus_handle, window, Self::handle_focus)
1326 .detach();
1327 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1328 .detach();
1329 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1330 .detach();
1331 cx.on_blur(&focus_handle, window, Self::handle_blur)
1332 .detach();
1333
1334 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1335 Some(false)
1336 } else {
1337 None
1338 };
1339
1340 let breakpoint_store = match (mode, project.as_ref()) {
1341 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1342 _ => None,
1343 };
1344
1345 let mut code_action_providers = Vec::new();
1346 let mut load_uncommitted_diff = None;
1347 if let Some(project) = project.clone() {
1348 load_uncommitted_diff = Some(
1349 get_uncommitted_diff_for_buffer(
1350 &project,
1351 buffer.read(cx).all_buffers(),
1352 buffer.clone(),
1353 cx,
1354 )
1355 .shared(),
1356 );
1357 code_action_providers.push(Rc::new(project) as Rc<_>);
1358 }
1359
1360 let mut this = Self {
1361 focus_handle,
1362 show_cursor_when_unfocused: false,
1363 last_focused_descendant: None,
1364 buffer: buffer.clone(),
1365 display_map: display_map.clone(),
1366 selections,
1367 scroll_manager: ScrollManager::new(cx),
1368 columnar_selection_tail: None,
1369 add_selections_state: None,
1370 select_next_state: None,
1371 select_prev_state: None,
1372 selection_history: Default::default(),
1373 autoclose_regions: Default::default(),
1374 snippet_stack: Default::default(),
1375 select_larger_syntax_node_stack: Vec::new(),
1376 ime_transaction: Default::default(),
1377 active_diagnostics: None,
1378 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1379 inline_diagnostics_update: Task::ready(()),
1380 inline_diagnostics: Vec::new(),
1381 soft_wrap_mode_override,
1382 hard_wrap: None,
1383 completion_provider: project.clone().map(|project| Box::new(project) as _),
1384 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1385 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1386 project,
1387 blink_manager: blink_manager.clone(),
1388 show_local_selections: true,
1389 show_scrollbars: true,
1390 mode,
1391 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1392 show_gutter: mode == EditorMode::Full,
1393 show_line_numbers: None,
1394 use_relative_line_numbers: None,
1395 show_git_diff_gutter: None,
1396 show_code_actions: None,
1397 show_runnables: None,
1398 show_breakpoints: None,
1399 show_wrap_guides: None,
1400 show_indent_guides,
1401 placeholder_text: None,
1402 highlight_order: 0,
1403 highlighted_rows: HashMap::default(),
1404 background_highlights: Default::default(),
1405 gutter_highlights: TreeMap::default(),
1406 scrollbar_marker_state: ScrollbarMarkerState::default(),
1407 active_indent_guides_state: ActiveIndentGuidesState::default(),
1408 nav_history: None,
1409 context_menu: RefCell::new(None),
1410 mouse_context_menu: None,
1411 completion_tasks: Default::default(),
1412 signature_help_state: SignatureHelpState::default(),
1413 auto_signature_help: None,
1414 find_all_references_task_sources: Vec::new(),
1415 next_completion_id: 0,
1416 next_inlay_id: 0,
1417 code_action_providers,
1418 available_code_actions: Default::default(),
1419 code_actions_task: Default::default(),
1420 selection_highlight_task: Default::default(),
1421 document_highlights_task: Default::default(),
1422 linked_editing_range_task: Default::default(),
1423 pending_rename: Default::default(),
1424 searchable: true,
1425 cursor_shape: EditorSettings::get_global(cx)
1426 .cursor_shape
1427 .unwrap_or_default(),
1428 current_line_highlight: None,
1429 autoindent_mode: Some(AutoindentMode::EachLine),
1430 collapse_matches: false,
1431 workspace: None,
1432 input_enabled: true,
1433 use_modal_editing: mode == EditorMode::Full,
1434 read_only: false,
1435 use_autoclose: true,
1436 use_auto_surround: true,
1437 auto_replace_emoji_shortcode: false,
1438 jsx_tag_auto_close_enabled_in_any_buffer: false,
1439 leader_peer_id: None,
1440 remote_id: None,
1441 hover_state: Default::default(),
1442 pending_mouse_down: None,
1443 hovered_link_state: Default::default(),
1444 edit_prediction_provider: None,
1445 active_inline_completion: None,
1446 stale_inline_completion_in_menu: None,
1447 edit_prediction_preview: EditPredictionPreview::Inactive {
1448 released_too_fast: false,
1449 },
1450 inline_diagnostics_enabled: mode == EditorMode::Full,
1451 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1452
1453 gutter_hovered: false,
1454 pixel_position_of_newest_cursor: None,
1455 last_bounds: None,
1456 last_position_map: None,
1457 expect_bounds_change: None,
1458 gutter_dimensions: GutterDimensions::default(),
1459 style: None,
1460 show_cursor_names: false,
1461 hovered_cursors: Default::default(),
1462 next_editor_action_id: EditorActionId::default(),
1463 editor_actions: Rc::default(),
1464 inline_completions_hidden_for_vim_mode: false,
1465 show_inline_completions_override: None,
1466 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1467 edit_prediction_settings: EditPredictionSettings::Disabled,
1468 edit_prediction_indent_conflict: false,
1469 edit_prediction_requires_modifier_in_indent_conflict: true,
1470 custom_context_menu: None,
1471 show_git_blame_gutter: false,
1472 show_git_blame_inline: false,
1473 show_selection_menu: None,
1474 show_git_blame_inline_delay_task: None,
1475 git_blame_inline_tooltip: None,
1476 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1477 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1478 .session
1479 .restore_unsaved_buffers,
1480 blame: None,
1481 blame_subscription: None,
1482 tasks: Default::default(),
1483
1484 breakpoint_store,
1485 gutter_breakpoint_indicator: None,
1486 _subscriptions: vec![
1487 cx.observe(&buffer, Self::on_buffer_changed),
1488 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1489 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1490 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1491 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1492 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1493 cx.observe_window_activation(window, |editor, window, cx| {
1494 let active = window.is_window_active();
1495 editor.blink_manager.update(cx, |blink_manager, cx| {
1496 if active {
1497 blink_manager.enable(cx);
1498 } else {
1499 blink_manager.disable(cx);
1500 }
1501 });
1502 }),
1503 ],
1504 tasks_update_task: None,
1505 linked_edit_ranges: Default::default(),
1506 in_project_search: false,
1507 previous_search_ranges: None,
1508 breadcrumb_header: None,
1509 focused_block: None,
1510 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1511 addons: HashMap::default(),
1512 registered_buffers: HashMap::default(),
1513 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1514 selection_mark_mode: false,
1515 toggle_fold_multiple_buffers: Task::ready(()),
1516 serialize_selections: Task::ready(()),
1517 text_style_refinement: None,
1518 load_diff_task: load_uncommitted_diff,
1519 };
1520 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1521 this._subscriptions
1522 .push(cx.observe(breakpoints, |_, _, cx| {
1523 cx.notify();
1524 }));
1525 }
1526 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1527 this._subscriptions.extend(project_subscriptions);
1528
1529 this.end_selection(window, cx);
1530 this.scroll_manager.show_scrollbar(window, cx);
1531 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1532
1533 if mode == EditorMode::Full {
1534 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1535 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1536
1537 if this.git_blame_inline_enabled {
1538 this.git_blame_inline_enabled = true;
1539 this.start_git_blame_inline(false, window, cx);
1540 }
1541
1542 this.go_to_active_debug_line(window, cx);
1543
1544 if let Some(buffer) = buffer.read(cx).as_singleton() {
1545 if let Some(project) = this.project.as_ref() {
1546 let handle = project.update(cx, |project, cx| {
1547 project.register_buffer_with_language_servers(&buffer, cx)
1548 });
1549 this.registered_buffers
1550 .insert(buffer.read(cx).remote_id(), handle);
1551 }
1552 }
1553 }
1554
1555 this.report_editor_event("Editor Opened", None, cx);
1556 this
1557 }
1558
1559 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1560 self.mouse_context_menu
1561 .as_ref()
1562 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1563 }
1564
1565 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1566 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1567 }
1568
1569 fn key_context_internal(
1570 &self,
1571 has_active_edit_prediction: bool,
1572 window: &Window,
1573 cx: &App,
1574 ) -> KeyContext {
1575 let mut key_context = KeyContext::new_with_defaults();
1576 key_context.add("Editor");
1577 let mode = match self.mode {
1578 EditorMode::SingleLine { .. } => "single_line",
1579 EditorMode::AutoHeight { .. } => "auto_height",
1580 EditorMode::Full => "full",
1581 };
1582
1583 if EditorSettings::jupyter_enabled(cx) {
1584 key_context.add("jupyter");
1585 }
1586
1587 key_context.set("mode", mode);
1588 if self.pending_rename.is_some() {
1589 key_context.add("renaming");
1590 }
1591
1592 match self.context_menu.borrow().as_ref() {
1593 Some(CodeContextMenu::Completions(_)) => {
1594 key_context.add("menu");
1595 key_context.add("showing_completions");
1596 }
1597 Some(CodeContextMenu::CodeActions(_)) => {
1598 key_context.add("menu");
1599 key_context.add("showing_code_actions")
1600 }
1601 None => {}
1602 }
1603
1604 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1605 if !self.focus_handle(cx).contains_focused(window, cx)
1606 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1607 {
1608 for addon in self.addons.values() {
1609 addon.extend_key_context(&mut key_context, cx)
1610 }
1611 }
1612
1613 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1614 if let Some(extension) = singleton_buffer
1615 .read(cx)
1616 .file()
1617 .and_then(|file| file.path().extension()?.to_str())
1618 {
1619 key_context.set("extension", extension.to_string());
1620 }
1621 } else {
1622 key_context.add("multibuffer");
1623 }
1624
1625 if has_active_edit_prediction {
1626 if self.edit_prediction_in_conflict() {
1627 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1628 } else {
1629 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1630 key_context.add("copilot_suggestion");
1631 }
1632 }
1633
1634 if self.selection_mark_mode {
1635 key_context.add("selection_mode");
1636 }
1637
1638 key_context
1639 }
1640
1641 pub fn edit_prediction_in_conflict(&self) -> bool {
1642 if !self.show_edit_predictions_in_menu() {
1643 return false;
1644 }
1645
1646 let showing_completions = self
1647 .context_menu
1648 .borrow()
1649 .as_ref()
1650 .map_or(false, |context| {
1651 matches!(context, CodeContextMenu::Completions(_))
1652 });
1653
1654 showing_completions
1655 || self.edit_prediction_requires_modifier()
1656 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1657 // bindings to insert tab characters.
1658 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1659 }
1660
1661 pub fn accept_edit_prediction_keybind(
1662 &self,
1663 window: &Window,
1664 cx: &App,
1665 ) -> AcceptEditPredictionBinding {
1666 let key_context = self.key_context_internal(true, window, cx);
1667 let in_conflict = self.edit_prediction_in_conflict();
1668
1669 AcceptEditPredictionBinding(
1670 window
1671 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1672 .into_iter()
1673 .filter(|binding| {
1674 !in_conflict
1675 || binding
1676 .keystrokes()
1677 .first()
1678 .map_or(false, |keystroke| keystroke.modifiers.modified())
1679 })
1680 .rev()
1681 .min_by_key(|binding| {
1682 binding
1683 .keystrokes()
1684 .first()
1685 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1686 }),
1687 )
1688 }
1689
1690 pub fn new_file(
1691 workspace: &mut Workspace,
1692 _: &workspace::NewFile,
1693 window: &mut Window,
1694 cx: &mut Context<Workspace>,
1695 ) {
1696 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1697 "Failed to create buffer",
1698 window,
1699 cx,
1700 |e, _, _| match e.error_code() {
1701 ErrorCode::RemoteUpgradeRequired => Some(format!(
1702 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1703 e.error_tag("required").unwrap_or("the latest version")
1704 )),
1705 _ => None,
1706 },
1707 );
1708 }
1709
1710 pub fn new_in_workspace(
1711 workspace: &mut Workspace,
1712 window: &mut Window,
1713 cx: &mut Context<Workspace>,
1714 ) -> Task<Result<Entity<Editor>>> {
1715 let project = workspace.project().clone();
1716 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1717
1718 cx.spawn_in(window, |workspace, mut cx| async move {
1719 let buffer = create.await?;
1720 workspace.update_in(&mut cx, |workspace, window, cx| {
1721 let editor =
1722 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1723 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1724 editor
1725 })
1726 })
1727 }
1728
1729 fn new_file_vertical(
1730 workspace: &mut Workspace,
1731 _: &workspace::NewFileSplitVertical,
1732 window: &mut Window,
1733 cx: &mut Context<Workspace>,
1734 ) {
1735 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1736 }
1737
1738 fn new_file_horizontal(
1739 workspace: &mut Workspace,
1740 _: &workspace::NewFileSplitHorizontal,
1741 window: &mut Window,
1742 cx: &mut Context<Workspace>,
1743 ) {
1744 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1745 }
1746
1747 fn new_file_in_direction(
1748 workspace: &mut Workspace,
1749 direction: SplitDirection,
1750 window: &mut Window,
1751 cx: &mut Context<Workspace>,
1752 ) {
1753 let project = workspace.project().clone();
1754 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1755
1756 cx.spawn_in(window, |workspace, mut cx| async move {
1757 let buffer = create.await?;
1758 workspace.update_in(&mut cx, move |workspace, window, cx| {
1759 workspace.split_item(
1760 direction,
1761 Box::new(
1762 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1763 ),
1764 window,
1765 cx,
1766 )
1767 })?;
1768 anyhow::Ok(())
1769 })
1770 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1771 match e.error_code() {
1772 ErrorCode::RemoteUpgradeRequired => Some(format!(
1773 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1774 e.error_tag("required").unwrap_or("the latest version")
1775 )),
1776 _ => None,
1777 }
1778 });
1779 }
1780
1781 pub fn leader_peer_id(&self) -> Option<PeerId> {
1782 self.leader_peer_id
1783 }
1784
1785 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1786 &self.buffer
1787 }
1788
1789 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1790 self.workspace.as_ref()?.0.upgrade()
1791 }
1792
1793 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1794 self.buffer().read(cx).title(cx)
1795 }
1796
1797 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1798 let git_blame_gutter_max_author_length = self
1799 .render_git_blame_gutter(cx)
1800 .then(|| {
1801 if let Some(blame) = self.blame.as_ref() {
1802 let max_author_length =
1803 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1804 Some(max_author_length)
1805 } else {
1806 None
1807 }
1808 })
1809 .flatten();
1810
1811 EditorSnapshot {
1812 mode: self.mode,
1813 show_gutter: self.show_gutter,
1814 show_line_numbers: self.show_line_numbers,
1815 show_git_diff_gutter: self.show_git_diff_gutter,
1816 show_code_actions: self.show_code_actions,
1817 show_runnables: self.show_runnables,
1818 show_breakpoints: self.show_breakpoints,
1819 git_blame_gutter_max_author_length,
1820 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1821 scroll_anchor: self.scroll_manager.anchor(),
1822 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1823 placeholder_text: self.placeholder_text.clone(),
1824 is_focused: self.focus_handle.is_focused(window),
1825 current_line_highlight: self
1826 .current_line_highlight
1827 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1828 gutter_hovered: self.gutter_hovered,
1829 }
1830 }
1831
1832 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1833 self.buffer.read(cx).language_at(point, cx)
1834 }
1835
1836 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1837 self.buffer.read(cx).read(cx).file_at(point).cloned()
1838 }
1839
1840 pub fn active_excerpt(
1841 &self,
1842 cx: &App,
1843 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1844 self.buffer
1845 .read(cx)
1846 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1847 }
1848
1849 pub fn mode(&self) -> EditorMode {
1850 self.mode
1851 }
1852
1853 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1854 self.collaboration_hub.as_deref()
1855 }
1856
1857 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1858 self.collaboration_hub = Some(hub);
1859 }
1860
1861 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1862 self.in_project_search = in_project_search;
1863 }
1864
1865 pub fn set_custom_context_menu(
1866 &mut self,
1867 f: impl 'static
1868 + Fn(
1869 &mut Self,
1870 DisplayPoint,
1871 &mut Window,
1872 &mut Context<Self>,
1873 ) -> Option<Entity<ui::ContextMenu>>,
1874 ) {
1875 self.custom_context_menu = Some(Box::new(f))
1876 }
1877
1878 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1879 self.completion_provider = provider;
1880 }
1881
1882 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1883 self.semantics_provider.clone()
1884 }
1885
1886 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1887 self.semantics_provider = provider;
1888 }
1889
1890 pub fn set_edit_prediction_provider<T>(
1891 &mut self,
1892 provider: Option<Entity<T>>,
1893 window: &mut Window,
1894 cx: &mut Context<Self>,
1895 ) where
1896 T: EditPredictionProvider,
1897 {
1898 self.edit_prediction_provider =
1899 provider.map(|provider| RegisteredInlineCompletionProvider {
1900 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1901 if this.focus_handle.is_focused(window) {
1902 this.update_visible_inline_completion(window, cx);
1903 }
1904 }),
1905 provider: Arc::new(provider),
1906 });
1907 self.update_edit_prediction_settings(cx);
1908 self.refresh_inline_completion(false, false, window, cx);
1909 }
1910
1911 pub fn placeholder_text(&self) -> Option<&str> {
1912 self.placeholder_text.as_deref()
1913 }
1914
1915 pub fn set_placeholder_text(
1916 &mut self,
1917 placeholder_text: impl Into<Arc<str>>,
1918 cx: &mut Context<Self>,
1919 ) {
1920 let placeholder_text = Some(placeholder_text.into());
1921 if self.placeholder_text != placeholder_text {
1922 self.placeholder_text = placeholder_text;
1923 cx.notify();
1924 }
1925 }
1926
1927 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1928 self.cursor_shape = cursor_shape;
1929
1930 // Disrupt blink for immediate user feedback that the cursor shape has changed
1931 self.blink_manager.update(cx, BlinkManager::show_cursor);
1932
1933 cx.notify();
1934 }
1935
1936 pub fn set_current_line_highlight(
1937 &mut self,
1938 current_line_highlight: Option<CurrentLineHighlight>,
1939 ) {
1940 self.current_line_highlight = current_line_highlight;
1941 }
1942
1943 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1944 self.collapse_matches = collapse_matches;
1945 }
1946
1947 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1948 let buffers = self.buffer.read(cx).all_buffers();
1949 let Some(project) = self.project.as_ref() else {
1950 return;
1951 };
1952 project.update(cx, |project, cx| {
1953 for buffer in buffers {
1954 self.registered_buffers
1955 .entry(buffer.read(cx).remote_id())
1956 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1957 }
1958 })
1959 }
1960
1961 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1962 if self.collapse_matches {
1963 return range.start..range.start;
1964 }
1965 range.clone()
1966 }
1967
1968 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1969 if self.display_map.read(cx).clip_at_line_ends != clip {
1970 self.display_map
1971 .update(cx, |map, _| map.clip_at_line_ends = clip);
1972 }
1973 }
1974
1975 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1976 self.input_enabled = input_enabled;
1977 }
1978
1979 pub fn set_inline_completions_hidden_for_vim_mode(
1980 &mut self,
1981 hidden: bool,
1982 window: &mut Window,
1983 cx: &mut Context<Self>,
1984 ) {
1985 if hidden != self.inline_completions_hidden_for_vim_mode {
1986 self.inline_completions_hidden_for_vim_mode = hidden;
1987 if hidden {
1988 self.update_visible_inline_completion(window, cx);
1989 } else {
1990 self.refresh_inline_completion(true, false, window, cx);
1991 }
1992 }
1993 }
1994
1995 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1996 self.menu_inline_completions_policy = value;
1997 }
1998
1999 pub fn set_autoindent(&mut self, autoindent: bool) {
2000 if autoindent {
2001 self.autoindent_mode = Some(AutoindentMode::EachLine);
2002 } else {
2003 self.autoindent_mode = None;
2004 }
2005 }
2006
2007 pub fn read_only(&self, cx: &App) -> bool {
2008 self.read_only || self.buffer.read(cx).read_only()
2009 }
2010
2011 pub fn set_read_only(&mut self, read_only: bool) {
2012 self.read_only = read_only;
2013 }
2014
2015 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2016 self.use_autoclose = autoclose;
2017 }
2018
2019 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2020 self.use_auto_surround = auto_surround;
2021 }
2022
2023 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2024 self.auto_replace_emoji_shortcode = auto_replace;
2025 }
2026
2027 pub fn toggle_edit_predictions(
2028 &mut self,
2029 _: &ToggleEditPrediction,
2030 window: &mut Window,
2031 cx: &mut Context<Self>,
2032 ) {
2033 if self.show_inline_completions_override.is_some() {
2034 self.set_show_edit_predictions(None, window, cx);
2035 } else {
2036 let show_edit_predictions = !self.edit_predictions_enabled();
2037 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2038 }
2039 }
2040
2041 pub fn set_show_edit_predictions(
2042 &mut self,
2043 show_edit_predictions: Option<bool>,
2044 window: &mut Window,
2045 cx: &mut Context<Self>,
2046 ) {
2047 self.show_inline_completions_override = show_edit_predictions;
2048 self.update_edit_prediction_settings(cx);
2049
2050 if let Some(false) = show_edit_predictions {
2051 self.discard_inline_completion(false, cx);
2052 } else {
2053 self.refresh_inline_completion(false, true, window, cx);
2054 }
2055 }
2056
2057 fn inline_completions_disabled_in_scope(
2058 &self,
2059 buffer: &Entity<Buffer>,
2060 buffer_position: language::Anchor,
2061 cx: &App,
2062 ) -> bool {
2063 let snapshot = buffer.read(cx).snapshot();
2064 let settings = snapshot.settings_at(buffer_position, cx);
2065
2066 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2067 return false;
2068 };
2069
2070 scope.override_name().map_or(false, |scope_name| {
2071 settings
2072 .edit_predictions_disabled_in
2073 .iter()
2074 .any(|s| s == scope_name)
2075 })
2076 }
2077
2078 pub fn set_use_modal_editing(&mut self, to: bool) {
2079 self.use_modal_editing = to;
2080 }
2081
2082 pub fn use_modal_editing(&self) -> bool {
2083 self.use_modal_editing
2084 }
2085
2086 fn selections_did_change(
2087 &mut self,
2088 local: bool,
2089 old_cursor_position: &Anchor,
2090 show_completions: bool,
2091 window: &mut Window,
2092 cx: &mut Context<Self>,
2093 ) {
2094 window.invalidate_character_coordinates();
2095
2096 // Copy selections to primary selection buffer
2097 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2098 if local {
2099 let selections = self.selections.all::<usize>(cx);
2100 let buffer_handle = self.buffer.read(cx).read(cx);
2101
2102 let mut text = String::new();
2103 for (index, selection) in selections.iter().enumerate() {
2104 let text_for_selection = buffer_handle
2105 .text_for_range(selection.start..selection.end)
2106 .collect::<String>();
2107
2108 text.push_str(&text_for_selection);
2109 if index != selections.len() - 1 {
2110 text.push('\n');
2111 }
2112 }
2113
2114 if !text.is_empty() {
2115 cx.write_to_primary(ClipboardItem::new_string(text));
2116 }
2117 }
2118
2119 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2120 self.buffer.update(cx, |buffer, cx| {
2121 buffer.set_active_selections(
2122 &self.selections.disjoint_anchors(),
2123 self.selections.line_mode,
2124 self.cursor_shape,
2125 cx,
2126 )
2127 });
2128 }
2129 let display_map = self
2130 .display_map
2131 .update(cx, |display_map, cx| display_map.snapshot(cx));
2132 let buffer = &display_map.buffer_snapshot;
2133 self.add_selections_state = None;
2134 self.select_next_state = None;
2135 self.select_prev_state = None;
2136 self.select_larger_syntax_node_stack.clear();
2137 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2138 self.snippet_stack
2139 .invalidate(&self.selections.disjoint_anchors(), buffer);
2140 self.take_rename(false, window, cx);
2141
2142 let new_cursor_position = self.selections.newest_anchor().head();
2143
2144 self.push_to_nav_history(
2145 *old_cursor_position,
2146 Some(new_cursor_position.to_point(buffer)),
2147 cx,
2148 );
2149
2150 if local {
2151 let new_cursor_position = self.selections.newest_anchor().head();
2152 let mut context_menu = self.context_menu.borrow_mut();
2153 let completion_menu = match context_menu.as_ref() {
2154 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2155 _ => {
2156 *context_menu = None;
2157 None
2158 }
2159 };
2160 if let Some(buffer_id) = new_cursor_position.buffer_id {
2161 if !self.registered_buffers.contains_key(&buffer_id) {
2162 if let Some(project) = self.project.as_ref() {
2163 project.update(cx, |project, cx| {
2164 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2165 return;
2166 };
2167 self.registered_buffers.insert(
2168 buffer_id,
2169 project.register_buffer_with_language_servers(&buffer, cx),
2170 );
2171 })
2172 }
2173 }
2174 }
2175
2176 if let Some(completion_menu) = completion_menu {
2177 let cursor_position = new_cursor_position.to_offset(buffer);
2178 let (word_range, kind) =
2179 buffer.surrounding_word(completion_menu.initial_position, true);
2180 if kind == Some(CharKind::Word)
2181 && word_range.to_inclusive().contains(&cursor_position)
2182 {
2183 let mut completion_menu = completion_menu.clone();
2184 drop(context_menu);
2185
2186 let query = Self::completion_query(buffer, cursor_position);
2187 cx.spawn(move |this, mut cx| async move {
2188 completion_menu
2189 .filter(query.as_deref(), cx.background_executor().clone())
2190 .await;
2191
2192 this.update(&mut cx, |this, cx| {
2193 let mut context_menu = this.context_menu.borrow_mut();
2194 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2195 else {
2196 return;
2197 };
2198
2199 if menu.id > completion_menu.id {
2200 return;
2201 }
2202
2203 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2204 drop(context_menu);
2205 cx.notify();
2206 })
2207 })
2208 .detach();
2209
2210 if show_completions {
2211 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2212 }
2213 } else {
2214 drop(context_menu);
2215 self.hide_context_menu(window, cx);
2216 }
2217 } else {
2218 drop(context_menu);
2219 }
2220
2221 hide_hover(self, cx);
2222
2223 if old_cursor_position.to_display_point(&display_map).row()
2224 != new_cursor_position.to_display_point(&display_map).row()
2225 {
2226 self.available_code_actions.take();
2227 }
2228 self.refresh_code_actions(window, cx);
2229 self.refresh_document_highlights(cx);
2230 self.refresh_selected_text_highlights(window, cx);
2231 refresh_matching_bracket_highlights(self, window, cx);
2232 self.update_visible_inline_completion(window, cx);
2233 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2234 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2235 if self.git_blame_inline_enabled {
2236 self.start_inline_blame_timer(window, cx);
2237 }
2238 }
2239
2240 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2241 cx.emit(EditorEvent::SelectionsChanged { local });
2242
2243 let selections = &self.selections.disjoint;
2244 if selections.len() == 1 {
2245 cx.emit(SearchEvent::ActiveMatchChanged)
2246 }
2247 if local
2248 && self.is_singleton(cx)
2249 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2250 {
2251 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2252 let background_executor = cx.background_executor().clone();
2253 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2254 let snapshot = self.buffer().read(cx).snapshot(cx);
2255 let selections = selections.clone();
2256 self.serialize_selections = cx.background_spawn(async move {
2257 background_executor.timer(Duration::from_millis(100)).await;
2258 let selections = selections
2259 .iter()
2260 .map(|selection| {
2261 (
2262 selection.start.to_offset(&snapshot),
2263 selection.end.to_offset(&snapshot),
2264 )
2265 })
2266 .collect();
2267 DB.save_editor_selections(editor_id, workspace_id, selections)
2268 .await
2269 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2270 .log_err();
2271 });
2272 }
2273 }
2274
2275 cx.notify();
2276 }
2277
2278 pub fn sync_selections(
2279 &mut self,
2280 other: Entity<Editor>,
2281 cx: &mut Context<Self>,
2282 ) -> gpui::Subscription {
2283 let other_selections = other.read(cx).selections.disjoint.to_vec();
2284 self.selections.change_with(cx, |selections| {
2285 selections.select_anchors(other_selections);
2286 });
2287
2288 let other_subscription =
2289 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2290 EditorEvent::SelectionsChanged { local: true } => {
2291 let other_selections = other.read(cx).selections.disjoint.to_vec();
2292 if other_selections.is_empty() {
2293 return;
2294 }
2295 this.selections.change_with(cx, |selections| {
2296 selections.select_anchors(other_selections);
2297 });
2298 }
2299 _ => {}
2300 });
2301
2302 let this_subscription =
2303 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2304 EditorEvent::SelectionsChanged { local: true } => {
2305 let these_selections = this.selections.disjoint.to_vec();
2306 if these_selections.is_empty() {
2307 return;
2308 }
2309 other.update(cx, |other_editor, cx| {
2310 other_editor.selections.change_with(cx, |selections| {
2311 selections.select_anchors(these_selections);
2312 })
2313 });
2314 }
2315 _ => {}
2316 });
2317
2318 Subscription::join(other_subscription, this_subscription)
2319 }
2320
2321 pub fn change_selections<R>(
2322 &mut self,
2323 autoscroll: Option<Autoscroll>,
2324 window: &mut Window,
2325 cx: &mut Context<Self>,
2326 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2327 ) -> R {
2328 self.change_selections_inner(autoscroll, true, window, cx, change)
2329 }
2330
2331 fn change_selections_inner<R>(
2332 &mut self,
2333 autoscroll: Option<Autoscroll>,
2334 request_completions: bool,
2335 window: &mut Window,
2336 cx: &mut Context<Self>,
2337 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2338 ) -> R {
2339 let old_cursor_position = self.selections.newest_anchor().head();
2340 self.push_to_selection_history();
2341
2342 let (changed, result) = self.selections.change_with(cx, change);
2343
2344 if changed {
2345 if let Some(autoscroll) = autoscroll {
2346 self.request_autoscroll(autoscroll, cx);
2347 }
2348 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2349
2350 if self.should_open_signature_help_automatically(
2351 &old_cursor_position,
2352 self.signature_help_state.backspace_pressed(),
2353 cx,
2354 ) {
2355 self.show_signature_help(&ShowSignatureHelp, window, cx);
2356 }
2357 self.signature_help_state.set_backspace_pressed(false);
2358 }
2359
2360 result
2361 }
2362
2363 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2364 where
2365 I: IntoIterator<Item = (Range<S>, T)>,
2366 S: ToOffset,
2367 T: Into<Arc<str>>,
2368 {
2369 if self.read_only(cx) {
2370 return;
2371 }
2372
2373 self.buffer
2374 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2375 }
2376
2377 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2378 where
2379 I: IntoIterator<Item = (Range<S>, T)>,
2380 S: ToOffset,
2381 T: Into<Arc<str>>,
2382 {
2383 if self.read_only(cx) {
2384 return;
2385 }
2386
2387 self.buffer.update(cx, |buffer, cx| {
2388 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2389 });
2390 }
2391
2392 pub fn edit_with_block_indent<I, S, T>(
2393 &mut self,
2394 edits: I,
2395 original_indent_columns: Vec<Option<u32>>,
2396 cx: &mut Context<Self>,
2397 ) where
2398 I: IntoIterator<Item = (Range<S>, T)>,
2399 S: ToOffset,
2400 T: Into<Arc<str>>,
2401 {
2402 if self.read_only(cx) {
2403 return;
2404 }
2405
2406 self.buffer.update(cx, |buffer, cx| {
2407 buffer.edit(
2408 edits,
2409 Some(AutoindentMode::Block {
2410 original_indent_columns,
2411 }),
2412 cx,
2413 )
2414 });
2415 }
2416
2417 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2418 self.hide_context_menu(window, cx);
2419
2420 match phase {
2421 SelectPhase::Begin {
2422 position,
2423 add,
2424 click_count,
2425 } => self.begin_selection(position, add, click_count, window, cx),
2426 SelectPhase::BeginColumnar {
2427 position,
2428 goal_column,
2429 reset,
2430 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2431 SelectPhase::Extend {
2432 position,
2433 click_count,
2434 } => self.extend_selection(position, click_count, window, cx),
2435 SelectPhase::Update {
2436 position,
2437 goal_column,
2438 scroll_delta,
2439 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2440 SelectPhase::End => self.end_selection(window, cx),
2441 }
2442 }
2443
2444 fn extend_selection(
2445 &mut self,
2446 position: DisplayPoint,
2447 click_count: usize,
2448 window: &mut Window,
2449 cx: &mut Context<Self>,
2450 ) {
2451 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2452 let tail = self.selections.newest::<usize>(cx).tail();
2453 self.begin_selection(position, false, click_count, window, cx);
2454
2455 let position = position.to_offset(&display_map, Bias::Left);
2456 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2457
2458 let mut pending_selection = self
2459 .selections
2460 .pending_anchor()
2461 .expect("extend_selection not called with pending selection");
2462 if position >= tail {
2463 pending_selection.start = tail_anchor;
2464 } else {
2465 pending_selection.end = tail_anchor;
2466 pending_selection.reversed = true;
2467 }
2468
2469 let mut pending_mode = self.selections.pending_mode().unwrap();
2470 match &mut pending_mode {
2471 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2472 _ => {}
2473 }
2474
2475 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2476 s.set_pending(pending_selection, pending_mode)
2477 });
2478 }
2479
2480 fn begin_selection(
2481 &mut self,
2482 position: DisplayPoint,
2483 add: bool,
2484 click_count: usize,
2485 window: &mut Window,
2486 cx: &mut Context<Self>,
2487 ) {
2488 if !self.focus_handle.is_focused(window) {
2489 self.last_focused_descendant = None;
2490 window.focus(&self.focus_handle);
2491 }
2492
2493 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2494 let buffer = &display_map.buffer_snapshot;
2495 let newest_selection = self.selections.newest_anchor().clone();
2496 let position = display_map.clip_point(position, Bias::Left);
2497
2498 let start;
2499 let end;
2500 let mode;
2501 let mut auto_scroll;
2502 match click_count {
2503 1 => {
2504 start = buffer.anchor_before(position.to_point(&display_map));
2505 end = start;
2506 mode = SelectMode::Character;
2507 auto_scroll = true;
2508 }
2509 2 => {
2510 let range = movement::surrounding_word(&display_map, position);
2511 start = buffer.anchor_before(range.start.to_point(&display_map));
2512 end = buffer.anchor_before(range.end.to_point(&display_map));
2513 mode = SelectMode::Word(start..end);
2514 auto_scroll = true;
2515 }
2516 3 => {
2517 let position = display_map
2518 .clip_point(position, Bias::Left)
2519 .to_point(&display_map);
2520 let line_start = display_map.prev_line_boundary(position).0;
2521 let next_line_start = buffer.clip_point(
2522 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2523 Bias::Left,
2524 );
2525 start = buffer.anchor_before(line_start);
2526 end = buffer.anchor_before(next_line_start);
2527 mode = SelectMode::Line(start..end);
2528 auto_scroll = true;
2529 }
2530 _ => {
2531 start = buffer.anchor_before(0);
2532 end = buffer.anchor_before(buffer.len());
2533 mode = SelectMode::All;
2534 auto_scroll = false;
2535 }
2536 }
2537 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2538
2539 let point_to_delete: Option<usize> = {
2540 let selected_points: Vec<Selection<Point>> =
2541 self.selections.disjoint_in_range(start..end, cx);
2542
2543 if !add || click_count > 1 {
2544 None
2545 } else if !selected_points.is_empty() {
2546 Some(selected_points[0].id)
2547 } else {
2548 let clicked_point_already_selected =
2549 self.selections.disjoint.iter().find(|selection| {
2550 selection.start.to_point(buffer) == start.to_point(buffer)
2551 || selection.end.to_point(buffer) == end.to_point(buffer)
2552 });
2553
2554 clicked_point_already_selected.map(|selection| selection.id)
2555 }
2556 };
2557
2558 let selections_count = self.selections.count();
2559
2560 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2561 if let Some(point_to_delete) = point_to_delete {
2562 s.delete(point_to_delete);
2563
2564 if selections_count == 1 {
2565 s.set_pending_anchor_range(start..end, mode);
2566 }
2567 } else {
2568 if !add {
2569 s.clear_disjoint();
2570 } else if click_count > 1 {
2571 s.delete(newest_selection.id)
2572 }
2573
2574 s.set_pending_anchor_range(start..end, mode);
2575 }
2576 });
2577 }
2578
2579 fn begin_columnar_selection(
2580 &mut self,
2581 position: DisplayPoint,
2582 goal_column: u32,
2583 reset: bool,
2584 window: &mut Window,
2585 cx: &mut Context<Self>,
2586 ) {
2587 if !self.focus_handle.is_focused(window) {
2588 self.last_focused_descendant = None;
2589 window.focus(&self.focus_handle);
2590 }
2591
2592 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2593
2594 if reset {
2595 let pointer_position = display_map
2596 .buffer_snapshot
2597 .anchor_before(position.to_point(&display_map));
2598
2599 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2600 s.clear_disjoint();
2601 s.set_pending_anchor_range(
2602 pointer_position..pointer_position,
2603 SelectMode::Character,
2604 );
2605 });
2606 }
2607
2608 let tail = self.selections.newest::<Point>(cx).tail();
2609 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2610
2611 if !reset {
2612 self.select_columns(
2613 tail.to_display_point(&display_map),
2614 position,
2615 goal_column,
2616 &display_map,
2617 window,
2618 cx,
2619 );
2620 }
2621 }
2622
2623 fn update_selection(
2624 &mut self,
2625 position: DisplayPoint,
2626 goal_column: u32,
2627 scroll_delta: gpui::Point<f32>,
2628 window: &mut Window,
2629 cx: &mut Context<Self>,
2630 ) {
2631 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2632
2633 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2634 let tail = tail.to_display_point(&display_map);
2635 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2636 } else if let Some(mut pending) = self.selections.pending_anchor() {
2637 let buffer = self.buffer.read(cx).snapshot(cx);
2638 let head;
2639 let tail;
2640 let mode = self.selections.pending_mode().unwrap();
2641 match &mode {
2642 SelectMode::Character => {
2643 head = position.to_point(&display_map);
2644 tail = pending.tail().to_point(&buffer);
2645 }
2646 SelectMode::Word(original_range) => {
2647 let original_display_range = original_range.start.to_display_point(&display_map)
2648 ..original_range.end.to_display_point(&display_map);
2649 let original_buffer_range = original_display_range.start.to_point(&display_map)
2650 ..original_display_range.end.to_point(&display_map);
2651 if movement::is_inside_word(&display_map, position)
2652 || original_display_range.contains(&position)
2653 {
2654 let word_range = movement::surrounding_word(&display_map, position);
2655 if word_range.start < original_display_range.start {
2656 head = word_range.start.to_point(&display_map);
2657 } else {
2658 head = word_range.end.to_point(&display_map);
2659 }
2660 } else {
2661 head = position.to_point(&display_map);
2662 }
2663
2664 if head <= original_buffer_range.start {
2665 tail = original_buffer_range.end;
2666 } else {
2667 tail = original_buffer_range.start;
2668 }
2669 }
2670 SelectMode::Line(original_range) => {
2671 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2672
2673 let position = display_map
2674 .clip_point(position, Bias::Left)
2675 .to_point(&display_map);
2676 let line_start = display_map.prev_line_boundary(position).0;
2677 let next_line_start = buffer.clip_point(
2678 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2679 Bias::Left,
2680 );
2681
2682 if line_start < original_range.start {
2683 head = line_start
2684 } else {
2685 head = next_line_start
2686 }
2687
2688 if head <= original_range.start {
2689 tail = original_range.end;
2690 } else {
2691 tail = original_range.start;
2692 }
2693 }
2694 SelectMode::All => {
2695 return;
2696 }
2697 };
2698
2699 if head < tail {
2700 pending.start = buffer.anchor_before(head);
2701 pending.end = buffer.anchor_before(tail);
2702 pending.reversed = true;
2703 } else {
2704 pending.start = buffer.anchor_before(tail);
2705 pending.end = buffer.anchor_before(head);
2706 pending.reversed = false;
2707 }
2708
2709 self.change_selections(None, window, cx, |s| {
2710 s.set_pending(pending, mode);
2711 });
2712 } else {
2713 log::error!("update_selection dispatched with no pending selection");
2714 return;
2715 }
2716
2717 self.apply_scroll_delta(scroll_delta, window, cx);
2718 cx.notify();
2719 }
2720
2721 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2722 self.columnar_selection_tail.take();
2723 if self.selections.pending_anchor().is_some() {
2724 let selections = self.selections.all::<usize>(cx);
2725 self.change_selections(None, window, cx, |s| {
2726 s.select(selections);
2727 s.clear_pending();
2728 });
2729 }
2730 }
2731
2732 fn select_columns(
2733 &mut self,
2734 tail: DisplayPoint,
2735 head: DisplayPoint,
2736 goal_column: u32,
2737 display_map: &DisplaySnapshot,
2738 window: &mut Window,
2739 cx: &mut Context<Self>,
2740 ) {
2741 let start_row = cmp::min(tail.row(), head.row());
2742 let end_row = cmp::max(tail.row(), head.row());
2743 let start_column = cmp::min(tail.column(), goal_column);
2744 let end_column = cmp::max(tail.column(), goal_column);
2745 let reversed = start_column < tail.column();
2746
2747 let selection_ranges = (start_row.0..=end_row.0)
2748 .map(DisplayRow)
2749 .filter_map(|row| {
2750 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2751 let start = display_map
2752 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2753 .to_point(display_map);
2754 let end = display_map
2755 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2756 .to_point(display_map);
2757 if reversed {
2758 Some(end..start)
2759 } else {
2760 Some(start..end)
2761 }
2762 } else {
2763 None
2764 }
2765 })
2766 .collect::<Vec<_>>();
2767
2768 self.change_selections(None, window, cx, |s| {
2769 s.select_ranges(selection_ranges);
2770 });
2771 cx.notify();
2772 }
2773
2774 pub fn has_pending_nonempty_selection(&self) -> bool {
2775 let pending_nonempty_selection = match self.selections.pending_anchor() {
2776 Some(Selection { start, end, .. }) => start != end,
2777 None => false,
2778 };
2779
2780 pending_nonempty_selection
2781 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2782 }
2783
2784 pub fn has_pending_selection(&self) -> bool {
2785 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2786 }
2787
2788 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2789 self.selection_mark_mode = false;
2790
2791 if self.clear_expanded_diff_hunks(cx) {
2792 cx.notify();
2793 return;
2794 }
2795 if self.dismiss_menus_and_popups(true, window, cx) {
2796 return;
2797 }
2798
2799 if self.mode == EditorMode::Full
2800 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2801 {
2802 return;
2803 }
2804
2805 cx.propagate();
2806 }
2807
2808 pub fn dismiss_menus_and_popups(
2809 &mut self,
2810 is_user_requested: bool,
2811 window: &mut Window,
2812 cx: &mut Context<Self>,
2813 ) -> bool {
2814 if self.take_rename(false, window, cx).is_some() {
2815 return true;
2816 }
2817
2818 if hide_hover(self, cx) {
2819 return true;
2820 }
2821
2822 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2823 return true;
2824 }
2825
2826 if self.hide_context_menu(window, cx).is_some() {
2827 return true;
2828 }
2829
2830 if self.mouse_context_menu.take().is_some() {
2831 return true;
2832 }
2833
2834 if is_user_requested && self.discard_inline_completion(true, cx) {
2835 return true;
2836 }
2837
2838 if self.snippet_stack.pop().is_some() {
2839 return true;
2840 }
2841
2842 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2843 self.dismiss_diagnostics(cx);
2844 return true;
2845 }
2846
2847 false
2848 }
2849
2850 fn linked_editing_ranges_for(
2851 &self,
2852 selection: Range<text::Anchor>,
2853 cx: &App,
2854 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2855 if self.linked_edit_ranges.is_empty() {
2856 return None;
2857 }
2858 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2859 selection.end.buffer_id.and_then(|end_buffer_id| {
2860 if selection.start.buffer_id != Some(end_buffer_id) {
2861 return None;
2862 }
2863 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2864 let snapshot = buffer.read(cx).snapshot();
2865 self.linked_edit_ranges
2866 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2867 .map(|ranges| (ranges, snapshot, buffer))
2868 })?;
2869 use text::ToOffset as TO;
2870 // find offset from the start of current range to current cursor position
2871 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2872
2873 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2874 let start_difference = start_offset - start_byte_offset;
2875 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2876 let end_difference = end_offset - start_byte_offset;
2877 // Current range has associated linked ranges.
2878 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2879 for range in linked_ranges.iter() {
2880 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2881 let end_offset = start_offset + end_difference;
2882 let start_offset = start_offset + start_difference;
2883 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2884 continue;
2885 }
2886 if self.selections.disjoint_anchor_ranges().any(|s| {
2887 if s.start.buffer_id != selection.start.buffer_id
2888 || s.end.buffer_id != selection.end.buffer_id
2889 {
2890 return false;
2891 }
2892 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2893 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2894 }) {
2895 continue;
2896 }
2897 let start = buffer_snapshot.anchor_after(start_offset);
2898 let end = buffer_snapshot.anchor_after(end_offset);
2899 linked_edits
2900 .entry(buffer.clone())
2901 .or_default()
2902 .push(start..end);
2903 }
2904 Some(linked_edits)
2905 }
2906
2907 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2908 let text: Arc<str> = text.into();
2909
2910 if self.read_only(cx) {
2911 return;
2912 }
2913
2914 let selections = self.selections.all_adjusted(cx);
2915 let mut bracket_inserted = false;
2916 let mut edits = Vec::new();
2917 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2918 let mut new_selections = Vec::with_capacity(selections.len());
2919 let mut new_autoclose_regions = Vec::new();
2920 let snapshot = self.buffer.read(cx).read(cx);
2921
2922 for (selection, autoclose_region) in
2923 self.selections_with_autoclose_regions(selections, &snapshot)
2924 {
2925 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2926 // Determine if the inserted text matches the opening or closing
2927 // bracket of any of this language's bracket pairs.
2928 let mut bracket_pair = None;
2929 let mut is_bracket_pair_start = false;
2930 let mut is_bracket_pair_end = false;
2931 if !text.is_empty() {
2932 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2933 // and they are removing the character that triggered IME popup.
2934 for (pair, enabled) in scope.brackets() {
2935 if !pair.close && !pair.surround {
2936 continue;
2937 }
2938
2939 if enabled && pair.start.ends_with(text.as_ref()) {
2940 let prefix_len = pair.start.len() - text.len();
2941 let preceding_text_matches_prefix = prefix_len == 0
2942 || (selection.start.column >= (prefix_len as u32)
2943 && snapshot.contains_str_at(
2944 Point::new(
2945 selection.start.row,
2946 selection.start.column - (prefix_len as u32),
2947 ),
2948 &pair.start[..prefix_len],
2949 ));
2950 if preceding_text_matches_prefix {
2951 bracket_pair = Some(pair.clone());
2952 is_bracket_pair_start = true;
2953 break;
2954 }
2955 }
2956 if pair.end.as_str() == text.as_ref() {
2957 bracket_pair = Some(pair.clone());
2958 is_bracket_pair_end = true;
2959 break;
2960 }
2961 }
2962 }
2963
2964 if let Some(bracket_pair) = bracket_pair {
2965 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2966 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2967 let auto_surround =
2968 self.use_auto_surround && snapshot_settings.use_auto_surround;
2969 if selection.is_empty() {
2970 if is_bracket_pair_start {
2971 // If the inserted text is a suffix of an opening bracket and the
2972 // selection is preceded by the rest of the opening bracket, then
2973 // insert the closing bracket.
2974 let following_text_allows_autoclose = snapshot
2975 .chars_at(selection.start)
2976 .next()
2977 .map_or(true, |c| scope.should_autoclose_before(c));
2978
2979 let preceding_text_allows_autoclose = selection.start.column == 0
2980 || snapshot.reversed_chars_at(selection.start).next().map_or(
2981 true,
2982 |c| {
2983 bracket_pair.start != bracket_pair.end
2984 || !snapshot
2985 .char_classifier_at(selection.start)
2986 .is_word(c)
2987 },
2988 );
2989
2990 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2991 && bracket_pair.start.len() == 1
2992 {
2993 let target = bracket_pair.start.chars().next().unwrap();
2994 let current_line_count = snapshot
2995 .reversed_chars_at(selection.start)
2996 .take_while(|&c| c != '\n')
2997 .filter(|&c| c == target)
2998 .count();
2999 current_line_count % 2 == 1
3000 } else {
3001 false
3002 };
3003
3004 if autoclose
3005 && bracket_pair.close
3006 && following_text_allows_autoclose
3007 && preceding_text_allows_autoclose
3008 && !is_closing_quote
3009 {
3010 let anchor = snapshot.anchor_before(selection.end);
3011 new_selections.push((selection.map(|_| anchor), text.len()));
3012 new_autoclose_regions.push((
3013 anchor,
3014 text.len(),
3015 selection.id,
3016 bracket_pair.clone(),
3017 ));
3018 edits.push((
3019 selection.range(),
3020 format!("{}{}", text, bracket_pair.end).into(),
3021 ));
3022 bracket_inserted = true;
3023 continue;
3024 }
3025 }
3026
3027 if let Some(region) = autoclose_region {
3028 // If the selection is followed by an auto-inserted closing bracket,
3029 // then don't insert that closing bracket again; just move the selection
3030 // past the closing bracket.
3031 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3032 && text.as_ref() == region.pair.end.as_str();
3033 if should_skip {
3034 let anchor = snapshot.anchor_after(selection.end);
3035 new_selections
3036 .push((selection.map(|_| anchor), region.pair.end.len()));
3037 continue;
3038 }
3039 }
3040
3041 let always_treat_brackets_as_autoclosed = snapshot
3042 .language_settings_at(selection.start, cx)
3043 .always_treat_brackets_as_autoclosed;
3044 if always_treat_brackets_as_autoclosed
3045 && is_bracket_pair_end
3046 && snapshot.contains_str_at(selection.end, text.as_ref())
3047 {
3048 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3049 // and the inserted text is a closing bracket and the selection is followed
3050 // by the closing bracket then move the selection past the closing bracket.
3051 let anchor = snapshot.anchor_after(selection.end);
3052 new_selections.push((selection.map(|_| anchor), text.len()));
3053 continue;
3054 }
3055 }
3056 // If an opening bracket is 1 character long and is typed while
3057 // text is selected, then surround that text with the bracket pair.
3058 else if auto_surround
3059 && bracket_pair.surround
3060 && is_bracket_pair_start
3061 && bracket_pair.start.chars().count() == 1
3062 {
3063 edits.push((selection.start..selection.start, text.clone()));
3064 edits.push((
3065 selection.end..selection.end,
3066 bracket_pair.end.as_str().into(),
3067 ));
3068 bracket_inserted = true;
3069 new_selections.push((
3070 Selection {
3071 id: selection.id,
3072 start: snapshot.anchor_after(selection.start),
3073 end: snapshot.anchor_before(selection.end),
3074 reversed: selection.reversed,
3075 goal: selection.goal,
3076 },
3077 0,
3078 ));
3079 continue;
3080 }
3081 }
3082 }
3083
3084 if self.auto_replace_emoji_shortcode
3085 && selection.is_empty()
3086 && text.as_ref().ends_with(':')
3087 {
3088 if let Some(possible_emoji_short_code) =
3089 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3090 {
3091 if !possible_emoji_short_code.is_empty() {
3092 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3093 let emoji_shortcode_start = Point::new(
3094 selection.start.row,
3095 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3096 );
3097
3098 // Remove shortcode from buffer
3099 edits.push((
3100 emoji_shortcode_start..selection.start,
3101 "".to_string().into(),
3102 ));
3103 new_selections.push((
3104 Selection {
3105 id: selection.id,
3106 start: snapshot.anchor_after(emoji_shortcode_start),
3107 end: snapshot.anchor_before(selection.start),
3108 reversed: selection.reversed,
3109 goal: selection.goal,
3110 },
3111 0,
3112 ));
3113
3114 // Insert emoji
3115 let selection_start_anchor = snapshot.anchor_after(selection.start);
3116 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3117 edits.push((selection.start..selection.end, emoji.to_string().into()));
3118
3119 continue;
3120 }
3121 }
3122 }
3123 }
3124
3125 // If not handling any auto-close operation, then just replace the selected
3126 // text with the given input and move the selection to the end of the
3127 // newly inserted text.
3128 let anchor = snapshot.anchor_after(selection.end);
3129 if !self.linked_edit_ranges.is_empty() {
3130 let start_anchor = snapshot.anchor_before(selection.start);
3131
3132 let is_word_char = text.chars().next().map_or(true, |char| {
3133 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3134 classifier.is_word(char)
3135 });
3136
3137 if is_word_char {
3138 if let Some(ranges) = self
3139 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3140 {
3141 for (buffer, edits) in ranges {
3142 linked_edits
3143 .entry(buffer.clone())
3144 .or_default()
3145 .extend(edits.into_iter().map(|range| (range, text.clone())));
3146 }
3147 }
3148 }
3149 }
3150
3151 new_selections.push((selection.map(|_| anchor), 0));
3152 edits.push((selection.start..selection.end, text.clone()));
3153 }
3154
3155 drop(snapshot);
3156
3157 self.transact(window, cx, |this, window, cx| {
3158 let initial_buffer_versions =
3159 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3160
3161 this.buffer.update(cx, |buffer, cx| {
3162 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3163 });
3164 for (buffer, edits) in linked_edits {
3165 buffer.update(cx, |buffer, cx| {
3166 let snapshot = buffer.snapshot();
3167 let edits = edits
3168 .into_iter()
3169 .map(|(range, text)| {
3170 use text::ToPoint as TP;
3171 let end_point = TP::to_point(&range.end, &snapshot);
3172 let start_point = TP::to_point(&range.start, &snapshot);
3173 (start_point..end_point, text)
3174 })
3175 .sorted_by_key(|(range, _)| range.start)
3176 .collect::<Vec<_>>();
3177 buffer.edit(edits, None, cx);
3178 })
3179 }
3180 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3181 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3182 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3183 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3184 .zip(new_selection_deltas)
3185 .map(|(selection, delta)| Selection {
3186 id: selection.id,
3187 start: selection.start + delta,
3188 end: selection.end + delta,
3189 reversed: selection.reversed,
3190 goal: SelectionGoal::None,
3191 })
3192 .collect::<Vec<_>>();
3193
3194 let mut i = 0;
3195 for (position, delta, selection_id, pair) in new_autoclose_regions {
3196 let position = position.to_offset(&map.buffer_snapshot) + delta;
3197 let start = map.buffer_snapshot.anchor_before(position);
3198 let end = map.buffer_snapshot.anchor_after(position);
3199 while let Some(existing_state) = this.autoclose_regions.get(i) {
3200 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3201 Ordering::Less => i += 1,
3202 Ordering::Greater => break,
3203 Ordering::Equal => {
3204 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3205 Ordering::Less => i += 1,
3206 Ordering::Equal => break,
3207 Ordering::Greater => break,
3208 }
3209 }
3210 }
3211 }
3212 this.autoclose_regions.insert(
3213 i,
3214 AutocloseRegion {
3215 selection_id,
3216 range: start..end,
3217 pair,
3218 },
3219 );
3220 }
3221
3222 let had_active_inline_completion = this.has_active_inline_completion();
3223 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3224 s.select(new_selections)
3225 });
3226
3227 if !bracket_inserted {
3228 if let Some(on_type_format_task) =
3229 this.trigger_on_type_formatting(text.to_string(), window, cx)
3230 {
3231 on_type_format_task.detach_and_log_err(cx);
3232 }
3233 }
3234
3235 let editor_settings = EditorSettings::get_global(cx);
3236 if bracket_inserted
3237 && (editor_settings.auto_signature_help
3238 || editor_settings.show_signature_help_after_edits)
3239 {
3240 this.show_signature_help(&ShowSignatureHelp, window, cx);
3241 }
3242
3243 let trigger_in_words =
3244 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3245 if this.hard_wrap.is_some() {
3246 let latest: Range<Point> = this.selections.newest(cx).range();
3247 if latest.is_empty()
3248 && this
3249 .buffer()
3250 .read(cx)
3251 .snapshot(cx)
3252 .line_len(MultiBufferRow(latest.start.row))
3253 == latest.start.column
3254 {
3255 this.rewrap_impl(
3256 RewrapOptions {
3257 override_language_settings: true,
3258 preserve_existing_whitespace: true,
3259 },
3260 cx,
3261 )
3262 }
3263 }
3264 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3265 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3266 this.refresh_inline_completion(true, false, window, cx);
3267 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3268 });
3269 }
3270
3271 fn find_possible_emoji_shortcode_at_position(
3272 snapshot: &MultiBufferSnapshot,
3273 position: Point,
3274 ) -> Option<String> {
3275 let mut chars = Vec::new();
3276 let mut found_colon = false;
3277 for char in snapshot.reversed_chars_at(position).take(100) {
3278 // Found a possible emoji shortcode in the middle of the buffer
3279 if found_colon {
3280 if char.is_whitespace() {
3281 chars.reverse();
3282 return Some(chars.iter().collect());
3283 }
3284 // If the previous character is not a whitespace, we are in the middle of a word
3285 // and we only want to complete the shortcode if the word is made up of other emojis
3286 let mut containing_word = String::new();
3287 for ch in snapshot
3288 .reversed_chars_at(position)
3289 .skip(chars.len() + 1)
3290 .take(100)
3291 {
3292 if ch.is_whitespace() {
3293 break;
3294 }
3295 containing_word.push(ch);
3296 }
3297 let containing_word = containing_word.chars().rev().collect::<String>();
3298 if util::word_consists_of_emojis(containing_word.as_str()) {
3299 chars.reverse();
3300 return Some(chars.iter().collect());
3301 }
3302 }
3303
3304 if char.is_whitespace() || !char.is_ascii() {
3305 return None;
3306 }
3307 if char == ':' {
3308 found_colon = true;
3309 } else {
3310 chars.push(char);
3311 }
3312 }
3313 // Found a possible emoji shortcode at the beginning of the buffer
3314 chars.reverse();
3315 Some(chars.iter().collect())
3316 }
3317
3318 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3319 self.transact(window, cx, |this, window, cx| {
3320 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3321 let selections = this.selections.all::<usize>(cx);
3322 let multi_buffer = this.buffer.read(cx);
3323 let buffer = multi_buffer.snapshot(cx);
3324 selections
3325 .iter()
3326 .map(|selection| {
3327 let start_point = selection.start.to_point(&buffer);
3328 let mut indent =
3329 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3330 indent.len = cmp::min(indent.len, start_point.column);
3331 let start = selection.start;
3332 let end = selection.end;
3333 let selection_is_empty = start == end;
3334 let language_scope = buffer.language_scope_at(start);
3335 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3336 &language_scope
3337 {
3338 let insert_extra_newline =
3339 insert_extra_newline_brackets(&buffer, start..end, language)
3340 || insert_extra_newline_tree_sitter(&buffer, start..end);
3341
3342 // Comment extension on newline is allowed only for cursor selections
3343 let comment_delimiter = maybe!({
3344 if !selection_is_empty {
3345 return None;
3346 }
3347
3348 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3349 return None;
3350 }
3351
3352 let delimiters = language.line_comment_prefixes();
3353 let max_len_of_delimiter =
3354 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3355 let (snapshot, range) =
3356 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3357
3358 let mut index_of_first_non_whitespace = 0;
3359 let comment_candidate = snapshot
3360 .chars_for_range(range)
3361 .skip_while(|c| {
3362 let should_skip = c.is_whitespace();
3363 if should_skip {
3364 index_of_first_non_whitespace += 1;
3365 }
3366 should_skip
3367 })
3368 .take(max_len_of_delimiter)
3369 .collect::<String>();
3370 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3371 comment_candidate.starts_with(comment_prefix.as_ref())
3372 })?;
3373 let cursor_is_placed_after_comment_marker =
3374 index_of_first_non_whitespace + comment_prefix.len()
3375 <= start_point.column as usize;
3376 if cursor_is_placed_after_comment_marker {
3377 Some(comment_prefix.clone())
3378 } else {
3379 None
3380 }
3381 });
3382 (comment_delimiter, insert_extra_newline)
3383 } else {
3384 (None, false)
3385 };
3386
3387 let capacity_for_delimiter = comment_delimiter
3388 .as_deref()
3389 .map(str::len)
3390 .unwrap_or_default();
3391 let mut new_text =
3392 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3393 new_text.push('\n');
3394 new_text.extend(indent.chars());
3395 if let Some(delimiter) = &comment_delimiter {
3396 new_text.push_str(delimiter);
3397 }
3398 if insert_extra_newline {
3399 new_text = new_text.repeat(2);
3400 }
3401
3402 let anchor = buffer.anchor_after(end);
3403 let new_selection = selection.map(|_| anchor);
3404 (
3405 (start..end, new_text),
3406 (insert_extra_newline, new_selection),
3407 )
3408 })
3409 .unzip()
3410 };
3411
3412 this.edit_with_autoindent(edits, cx);
3413 let buffer = this.buffer.read(cx).snapshot(cx);
3414 let new_selections = selection_fixup_info
3415 .into_iter()
3416 .map(|(extra_newline_inserted, new_selection)| {
3417 let mut cursor = new_selection.end.to_point(&buffer);
3418 if extra_newline_inserted {
3419 cursor.row -= 1;
3420 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3421 }
3422 new_selection.map(|_| cursor)
3423 })
3424 .collect();
3425
3426 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3427 s.select(new_selections)
3428 });
3429 this.refresh_inline_completion(true, false, window, cx);
3430 });
3431 }
3432
3433 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3434 let buffer = self.buffer.read(cx);
3435 let snapshot = buffer.snapshot(cx);
3436
3437 let mut edits = Vec::new();
3438 let mut rows = Vec::new();
3439
3440 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3441 let cursor = selection.head();
3442 let row = cursor.row;
3443
3444 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3445
3446 let newline = "\n".to_string();
3447 edits.push((start_of_line..start_of_line, newline));
3448
3449 rows.push(row + rows_inserted as u32);
3450 }
3451
3452 self.transact(window, cx, |editor, window, cx| {
3453 editor.edit(edits, cx);
3454
3455 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3456 let mut index = 0;
3457 s.move_cursors_with(|map, _, _| {
3458 let row = rows[index];
3459 index += 1;
3460
3461 let point = Point::new(row, 0);
3462 let boundary = map.next_line_boundary(point).1;
3463 let clipped = map.clip_point(boundary, Bias::Left);
3464
3465 (clipped, SelectionGoal::None)
3466 });
3467 });
3468
3469 let mut indent_edits = Vec::new();
3470 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3471 for row in rows {
3472 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3473 for (row, indent) in indents {
3474 if indent.len == 0 {
3475 continue;
3476 }
3477
3478 let text = match indent.kind {
3479 IndentKind::Space => " ".repeat(indent.len as usize),
3480 IndentKind::Tab => "\t".repeat(indent.len as usize),
3481 };
3482 let point = Point::new(row.0, 0);
3483 indent_edits.push((point..point, text));
3484 }
3485 }
3486 editor.edit(indent_edits, cx);
3487 });
3488 }
3489
3490 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3491 let buffer = self.buffer.read(cx);
3492 let snapshot = buffer.snapshot(cx);
3493
3494 let mut edits = Vec::new();
3495 let mut rows = Vec::new();
3496 let mut rows_inserted = 0;
3497
3498 for selection in self.selections.all_adjusted(cx) {
3499 let cursor = selection.head();
3500 let row = cursor.row;
3501
3502 let point = Point::new(row + 1, 0);
3503 let start_of_line = snapshot.clip_point(point, Bias::Left);
3504
3505 let newline = "\n".to_string();
3506 edits.push((start_of_line..start_of_line, newline));
3507
3508 rows_inserted += 1;
3509 rows.push(row + rows_inserted);
3510 }
3511
3512 self.transact(window, cx, |editor, window, cx| {
3513 editor.edit(edits, cx);
3514
3515 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3516 let mut index = 0;
3517 s.move_cursors_with(|map, _, _| {
3518 let row = rows[index];
3519 index += 1;
3520
3521 let point = Point::new(row, 0);
3522 let boundary = map.next_line_boundary(point).1;
3523 let clipped = map.clip_point(boundary, Bias::Left);
3524
3525 (clipped, SelectionGoal::None)
3526 });
3527 });
3528
3529 let mut indent_edits = Vec::new();
3530 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3531 for row in rows {
3532 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3533 for (row, indent) in indents {
3534 if indent.len == 0 {
3535 continue;
3536 }
3537
3538 let text = match indent.kind {
3539 IndentKind::Space => " ".repeat(indent.len as usize),
3540 IndentKind::Tab => "\t".repeat(indent.len as usize),
3541 };
3542 let point = Point::new(row.0, 0);
3543 indent_edits.push((point..point, text));
3544 }
3545 }
3546 editor.edit(indent_edits, cx);
3547 });
3548 }
3549
3550 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3551 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3552 original_indent_columns: Vec::new(),
3553 });
3554 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3555 }
3556
3557 fn insert_with_autoindent_mode(
3558 &mut self,
3559 text: &str,
3560 autoindent_mode: Option<AutoindentMode>,
3561 window: &mut Window,
3562 cx: &mut Context<Self>,
3563 ) {
3564 if self.read_only(cx) {
3565 return;
3566 }
3567
3568 let text: Arc<str> = text.into();
3569 self.transact(window, cx, |this, window, cx| {
3570 let old_selections = this.selections.all_adjusted(cx);
3571 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3572 let anchors = {
3573 let snapshot = buffer.read(cx);
3574 old_selections
3575 .iter()
3576 .map(|s| {
3577 let anchor = snapshot.anchor_after(s.head());
3578 s.map(|_| anchor)
3579 })
3580 .collect::<Vec<_>>()
3581 };
3582 buffer.edit(
3583 old_selections
3584 .iter()
3585 .map(|s| (s.start..s.end, text.clone())),
3586 autoindent_mode,
3587 cx,
3588 );
3589 anchors
3590 });
3591
3592 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3593 s.select_anchors(selection_anchors);
3594 });
3595
3596 cx.notify();
3597 });
3598 }
3599
3600 fn trigger_completion_on_input(
3601 &mut self,
3602 text: &str,
3603 trigger_in_words: bool,
3604 window: &mut Window,
3605 cx: &mut Context<Self>,
3606 ) {
3607 let ignore_completion_provider = self
3608 .context_menu
3609 .borrow()
3610 .as_ref()
3611 .map(|menu| match menu {
3612 CodeContextMenu::Completions(completions_menu) => {
3613 completions_menu.ignore_completion_provider
3614 }
3615 CodeContextMenu::CodeActions(_) => false,
3616 })
3617 .unwrap_or(false);
3618
3619 if ignore_completion_provider {
3620 self.show_word_completions(&ShowWordCompletions, window, cx);
3621 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3622 self.show_completions(
3623 &ShowCompletions {
3624 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3625 },
3626 window,
3627 cx,
3628 );
3629 } else {
3630 self.hide_context_menu(window, cx);
3631 }
3632 }
3633
3634 fn is_completion_trigger(
3635 &self,
3636 text: &str,
3637 trigger_in_words: bool,
3638 cx: &mut Context<Self>,
3639 ) -> bool {
3640 let position = self.selections.newest_anchor().head();
3641 let multibuffer = self.buffer.read(cx);
3642 let Some(buffer) = position
3643 .buffer_id
3644 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3645 else {
3646 return false;
3647 };
3648
3649 if let Some(completion_provider) = &self.completion_provider {
3650 completion_provider.is_completion_trigger(
3651 &buffer,
3652 position.text_anchor,
3653 text,
3654 trigger_in_words,
3655 cx,
3656 )
3657 } else {
3658 false
3659 }
3660 }
3661
3662 /// If any empty selections is touching the start of its innermost containing autoclose
3663 /// region, expand it to select the brackets.
3664 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3665 let selections = self.selections.all::<usize>(cx);
3666 let buffer = self.buffer.read(cx).read(cx);
3667 let new_selections = self
3668 .selections_with_autoclose_regions(selections, &buffer)
3669 .map(|(mut selection, region)| {
3670 if !selection.is_empty() {
3671 return selection;
3672 }
3673
3674 if let Some(region) = region {
3675 let mut range = region.range.to_offset(&buffer);
3676 if selection.start == range.start && range.start >= region.pair.start.len() {
3677 range.start -= region.pair.start.len();
3678 if buffer.contains_str_at(range.start, ®ion.pair.start)
3679 && buffer.contains_str_at(range.end, ®ion.pair.end)
3680 {
3681 range.end += region.pair.end.len();
3682 selection.start = range.start;
3683 selection.end = range.end;
3684
3685 return selection;
3686 }
3687 }
3688 }
3689
3690 let always_treat_brackets_as_autoclosed = buffer
3691 .language_settings_at(selection.start, cx)
3692 .always_treat_brackets_as_autoclosed;
3693
3694 if !always_treat_brackets_as_autoclosed {
3695 return selection;
3696 }
3697
3698 if let Some(scope) = buffer.language_scope_at(selection.start) {
3699 for (pair, enabled) in scope.brackets() {
3700 if !enabled || !pair.close {
3701 continue;
3702 }
3703
3704 if buffer.contains_str_at(selection.start, &pair.end) {
3705 let pair_start_len = pair.start.len();
3706 if buffer.contains_str_at(
3707 selection.start.saturating_sub(pair_start_len),
3708 &pair.start,
3709 ) {
3710 selection.start -= pair_start_len;
3711 selection.end += pair.end.len();
3712
3713 return selection;
3714 }
3715 }
3716 }
3717 }
3718
3719 selection
3720 })
3721 .collect();
3722
3723 drop(buffer);
3724 self.change_selections(None, window, cx, |selections| {
3725 selections.select(new_selections)
3726 });
3727 }
3728
3729 /// Iterate the given selections, and for each one, find the smallest surrounding
3730 /// autoclose region. This uses the ordering of the selections and the autoclose
3731 /// regions to avoid repeated comparisons.
3732 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3733 &'a self,
3734 selections: impl IntoIterator<Item = Selection<D>>,
3735 buffer: &'a MultiBufferSnapshot,
3736 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3737 let mut i = 0;
3738 let mut regions = self.autoclose_regions.as_slice();
3739 selections.into_iter().map(move |selection| {
3740 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3741
3742 let mut enclosing = None;
3743 while let Some(pair_state) = regions.get(i) {
3744 if pair_state.range.end.to_offset(buffer) < range.start {
3745 regions = ®ions[i + 1..];
3746 i = 0;
3747 } else if pair_state.range.start.to_offset(buffer) > range.end {
3748 break;
3749 } else {
3750 if pair_state.selection_id == selection.id {
3751 enclosing = Some(pair_state);
3752 }
3753 i += 1;
3754 }
3755 }
3756
3757 (selection, enclosing)
3758 })
3759 }
3760
3761 /// Remove any autoclose regions that no longer contain their selection.
3762 fn invalidate_autoclose_regions(
3763 &mut self,
3764 mut selections: &[Selection<Anchor>],
3765 buffer: &MultiBufferSnapshot,
3766 ) {
3767 self.autoclose_regions.retain(|state| {
3768 let mut i = 0;
3769 while let Some(selection) = selections.get(i) {
3770 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3771 selections = &selections[1..];
3772 continue;
3773 }
3774 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3775 break;
3776 }
3777 if selection.id == state.selection_id {
3778 return true;
3779 } else {
3780 i += 1;
3781 }
3782 }
3783 false
3784 });
3785 }
3786
3787 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3788 let offset = position.to_offset(buffer);
3789 let (word_range, kind) = buffer.surrounding_word(offset, true);
3790 if offset > word_range.start && kind == Some(CharKind::Word) {
3791 Some(
3792 buffer
3793 .text_for_range(word_range.start..offset)
3794 .collect::<String>(),
3795 )
3796 } else {
3797 None
3798 }
3799 }
3800
3801 pub fn toggle_inlay_hints(
3802 &mut self,
3803 _: &ToggleInlayHints,
3804 _: &mut Window,
3805 cx: &mut Context<Self>,
3806 ) {
3807 self.refresh_inlay_hints(
3808 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3809 cx,
3810 );
3811 }
3812
3813 pub fn inlay_hints_enabled(&self) -> bool {
3814 self.inlay_hint_cache.enabled
3815 }
3816
3817 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3818 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3819 return;
3820 }
3821
3822 let reason_description = reason.description();
3823 let ignore_debounce = matches!(
3824 reason,
3825 InlayHintRefreshReason::SettingsChange(_)
3826 | InlayHintRefreshReason::Toggle(_)
3827 | InlayHintRefreshReason::ExcerptsRemoved(_)
3828 | InlayHintRefreshReason::ModifiersChanged(_)
3829 );
3830 let (invalidate_cache, required_languages) = match reason {
3831 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3832 match self.inlay_hint_cache.modifiers_override(enabled) {
3833 Some(enabled) => {
3834 if enabled {
3835 (InvalidationStrategy::RefreshRequested, None)
3836 } else {
3837 self.splice_inlays(
3838 &self
3839 .visible_inlay_hints(cx)
3840 .iter()
3841 .map(|inlay| inlay.id)
3842 .collect::<Vec<InlayId>>(),
3843 Vec::new(),
3844 cx,
3845 );
3846 return;
3847 }
3848 }
3849 None => return,
3850 }
3851 }
3852 InlayHintRefreshReason::Toggle(enabled) => {
3853 if self.inlay_hint_cache.toggle(enabled) {
3854 if enabled {
3855 (InvalidationStrategy::RefreshRequested, None)
3856 } else {
3857 self.splice_inlays(
3858 &self
3859 .visible_inlay_hints(cx)
3860 .iter()
3861 .map(|inlay| inlay.id)
3862 .collect::<Vec<InlayId>>(),
3863 Vec::new(),
3864 cx,
3865 );
3866 return;
3867 }
3868 } else {
3869 return;
3870 }
3871 }
3872 InlayHintRefreshReason::SettingsChange(new_settings) => {
3873 match self.inlay_hint_cache.update_settings(
3874 &self.buffer,
3875 new_settings,
3876 self.visible_inlay_hints(cx),
3877 cx,
3878 ) {
3879 ControlFlow::Break(Some(InlaySplice {
3880 to_remove,
3881 to_insert,
3882 })) => {
3883 self.splice_inlays(&to_remove, to_insert, cx);
3884 return;
3885 }
3886 ControlFlow::Break(None) => return,
3887 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3888 }
3889 }
3890 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3891 if let Some(InlaySplice {
3892 to_remove,
3893 to_insert,
3894 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3895 {
3896 self.splice_inlays(&to_remove, to_insert, cx);
3897 }
3898 return;
3899 }
3900 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3901 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3902 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3903 }
3904 InlayHintRefreshReason::RefreshRequested => {
3905 (InvalidationStrategy::RefreshRequested, None)
3906 }
3907 };
3908
3909 if let Some(InlaySplice {
3910 to_remove,
3911 to_insert,
3912 }) = self.inlay_hint_cache.spawn_hint_refresh(
3913 reason_description,
3914 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3915 invalidate_cache,
3916 ignore_debounce,
3917 cx,
3918 ) {
3919 self.splice_inlays(&to_remove, to_insert, cx);
3920 }
3921 }
3922
3923 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3924 self.display_map
3925 .read(cx)
3926 .current_inlays()
3927 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3928 .cloned()
3929 .collect()
3930 }
3931
3932 pub fn excerpts_for_inlay_hints_query(
3933 &self,
3934 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3935 cx: &mut Context<Editor>,
3936 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3937 let Some(project) = self.project.as_ref() else {
3938 return HashMap::default();
3939 };
3940 let project = project.read(cx);
3941 let multi_buffer = self.buffer().read(cx);
3942 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3943 let multi_buffer_visible_start = self
3944 .scroll_manager
3945 .anchor()
3946 .anchor
3947 .to_point(&multi_buffer_snapshot);
3948 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3949 multi_buffer_visible_start
3950 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3951 Bias::Left,
3952 );
3953 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3954 multi_buffer_snapshot
3955 .range_to_buffer_ranges(multi_buffer_visible_range)
3956 .into_iter()
3957 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3958 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3959 let buffer_file = project::File::from_dyn(buffer.file())?;
3960 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3961 let worktree_entry = buffer_worktree
3962 .read(cx)
3963 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3964 if worktree_entry.is_ignored {
3965 return None;
3966 }
3967
3968 let language = buffer.language()?;
3969 if let Some(restrict_to_languages) = restrict_to_languages {
3970 if !restrict_to_languages.contains(language) {
3971 return None;
3972 }
3973 }
3974 Some((
3975 excerpt_id,
3976 (
3977 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3978 buffer.version().clone(),
3979 excerpt_visible_range,
3980 ),
3981 ))
3982 })
3983 .collect()
3984 }
3985
3986 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3987 TextLayoutDetails {
3988 text_system: window.text_system().clone(),
3989 editor_style: self.style.clone().unwrap(),
3990 rem_size: window.rem_size(),
3991 scroll_anchor: self.scroll_manager.anchor(),
3992 visible_rows: self.visible_line_count(),
3993 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3994 }
3995 }
3996
3997 pub fn splice_inlays(
3998 &self,
3999 to_remove: &[InlayId],
4000 to_insert: Vec<Inlay>,
4001 cx: &mut Context<Self>,
4002 ) {
4003 self.display_map.update(cx, |display_map, cx| {
4004 display_map.splice_inlays(to_remove, to_insert, cx)
4005 });
4006 cx.notify();
4007 }
4008
4009 fn trigger_on_type_formatting(
4010 &self,
4011 input: String,
4012 window: &mut Window,
4013 cx: &mut Context<Self>,
4014 ) -> Option<Task<Result<()>>> {
4015 if input.len() != 1 {
4016 return None;
4017 }
4018
4019 let project = self.project.as_ref()?;
4020 let position = self.selections.newest_anchor().head();
4021 let (buffer, buffer_position) = self
4022 .buffer
4023 .read(cx)
4024 .text_anchor_for_position(position, cx)?;
4025
4026 let settings = language_settings::language_settings(
4027 buffer
4028 .read(cx)
4029 .language_at(buffer_position)
4030 .map(|l| l.name()),
4031 buffer.read(cx).file(),
4032 cx,
4033 );
4034 if !settings.use_on_type_format {
4035 return None;
4036 }
4037
4038 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4039 // hence we do LSP request & edit on host side only — add formats to host's history.
4040 let push_to_lsp_host_history = true;
4041 // If this is not the host, append its history with new edits.
4042 let push_to_client_history = project.read(cx).is_via_collab();
4043
4044 let on_type_formatting = project.update(cx, |project, cx| {
4045 project.on_type_format(
4046 buffer.clone(),
4047 buffer_position,
4048 input,
4049 push_to_lsp_host_history,
4050 cx,
4051 )
4052 });
4053 Some(cx.spawn_in(window, |editor, mut cx| async move {
4054 if let Some(transaction) = on_type_formatting.await? {
4055 if push_to_client_history {
4056 buffer
4057 .update(&mut cx, |buffer, _| {
4058 buffer.push_transaction(transaction, Instant::now());
4059 })
4060 .ok();
4061 }
4062 editor.update(&mut cx, |editor, cx| {
4063 editor.refresh_document_highlights(cx);
4064 })?;
4065 }
4066 Ok(())
4067 }))
4068 }
4069
4070 pub fn show_word_completions(
4071 &mut self,
4072 _: &ShowWordCompletions,
4073 window: &mut Window,
4074 cx: &mut Context<Self>,
4075 ) {
4076 self.open_completions_menu(true, None, window, cx);
4077 }
4078
4079 pub fn show_completions(
4080 &mut self,
4081 options: &ShowCompletions,
4082 window: &mut Window,
4083 cx: &mut Context<Self>,
4084 ) {
4085 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4086 }
4087
4088 fn open_completions_menu(
4089 &mut self,
4090 ignore_completion_provider: bool,
4091 trigger: Option<&str>,
4092 window: &mut Window,
4093 cx: &mut Context<Self>,
4094 ) {
4095 if self.pending_rename.is_some() {
4096 return;
4097 }
4098 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4099 return;
4100 }
4101
4102 let position = self.selections.newest_anchor().head();
4103 if position.diff_base_anchor.is_some() {
4104 return;
4105 }
4106 let (buffer, buffer_position) =
4107 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4108 output
4109 } else {
4110 return;
4111 };
4112 let buffer_snapshot = buffer.read(cx).snapshot();
4113 let show_completion_documentation = buffer_snapshot
4114 .settings_at(buffer_position, cx)
4115 .show_completion_documentation;
4116
4117 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4118
4119 let trigger_kind = match trigger {
4120 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4121 CompletionTriggerKind::TRIGGER_CHARACTER
4122 }
4123 _ => CompletionTriggerKind::INVOKED,
4124 };
4125 let completion_context = CompletionContext {
4126 trigger_character: trigger.and_then(|trigger| {
4127 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4128 Some(String::from(trigger))
4129 } else {
4130 None
4131 }
4132 }),
4133 trigger_kind,
4134 };
4135
4136 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4137 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4138 let word_to_exclude = buffer_snapshot
4139 .text_for_range(old_range.clone())
4140 .collect::<String>();
4141 (
4142 buffer_snapshot.anchor_before(old_range.start)
4143 ..buffer_snapshot.anchor_after(old_range.end),
4144 Some(word_to_exclude),
4145 )
4146 } else {
4147 (buffer_position..buffer_position, None)
4148 };
4149
4150 let completion_settings = language_settings(
4151 buffer_snapshot
4152 .language_at(buffer_position)
4153 .map(|language| language.name()),
4154 buffer_snapshot.file(),
4155 cx,
4156 )
4157 .completions;
4158
4159 // The document can be large, so stay in reasonable bounds when searching for words,
4160 // otherwise completion pop-up might be slow to appear.
4161 const WORD_LOOKUP_ROWS: u32 = 5_000;
4162 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4163 let min_word_search = buffer_snapshot.clip_point(
4164 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4165 Bias::Left,
4166 );
4167 let max_word_search = buffer_snapshot.clip_point(
4168 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4169 Bias::Right,
4170 );
4171 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4172 ..buffer_snapshot.point_to_offset(max_word_search);
4173
4174 let provider = self
4175 .completion_provider
4176 .as_ref()
4177 .filter(|_| !ignore_completion_provider);
4178 let skip_digits = query
4179 .as_ref()
4180 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4181
4182 let (mut words, provided_completions) = match provider {
4183 Some(provider) => {
4184 let completions =
4185 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4186
4187 let words = match completion_settings.words {
4188 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4189 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4190 .background_spawn(async move {
4191 buffer_snapshot.words_in_range(WordsQuery {
4192 fuzzy_contents: None,
4193 range: word_search_range,
4194 skip_digits,
4195 })
4196 }),
4197 };
4198
4199 (words, completions)
4200 }
4201 None => (
4202 cx.background_spawn(async move {
4203 buffer_snapshot.words_in_range(WordsQuery {
4204 fuzzy_contents: None,
4205 range: word_search_range,
4206 skip_digits,
4207 })
4208 }),
4209 Task::ready(Ok(None)),
4210 ),
4211 };
4212
4213 let sort_completions = provider
4214 .as_ref()
4215 .map_or(true, |provider| provider.sort_completions());
4216
4217 let id = post_inc(&mut self.next_completion_id);
4218 let task = cx.spawn_in(window, |editor, mut cx| {
4219 async move {
4220 editor.update(&mut cx, |this, _| {
4221 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4222 })?;
4223
4224 let mut completions = Vec::new();
4225 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4226 completions.extend(provided_completions);
4227 if completion_settings.words == WordsCompletionMode::Fallback {
4228 words = Task::ready(HashMap::default());
4229 }
4230 }
4231
4232 let mut words = words.await;
4233 if let Some(word_to_exclude) = &word_to_exclude {
4234 words.remove(word_to_exclude);
4235 }
4236 for lsp_completion in &completions {
4237 words.remove(&lsp_completion.new_text);
4238 }
4239 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4240 old_range: old_range.clone(),
4241 new_text: word.clone(),
4242 label: CodeLabel::plain(word, None),
4243 documentation: None,
4244 source: CompletionSource::BufferWord {
4245 word_range,
4246 resolved: false,
4247 },
4248 confirm: None,
4249 }));
4250
4251 let menu = if completions.is_empty() {
4252 None
4253 } else {
4254 let mut menu = CompletionsMenu::new(
4255 id,
4256 sort_completions,
4257 show_completion_documentation,
4258 ignore_completion_provider,
4259 position,
4260 buffer.clone(),
4261 completions.into(),
4262 );
4263
4264 menu.filter(query.as_deref(), cx.background_executor().clone())
4265 .await;
4266
4267 menu.visible().then_some(menu)
4268 };
4269
4270 editor.update_in(&mut cx, |editor, window, cx| {
4271 match editor.context_menu.borrow().as_ref() {
4272 None => {}
4273 Some(CodeContextMenu::Completions(prev_menu)) => {
4274 if prev_menu.id > id {
4275 return;
4276 }
4277 }
4278 _ => return,
4279 }
4280
4281 if editor.focus_handle.is_focused(window) && menu.is_some() {
4282 let mut menu = menu.unwrap();
4283 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4284
4285 *editor.context_menu.borrow_mut() =
4286 Some(CodeContextMenu::Completions(menu));
4287
4288 if editor.show_edit_predictions_in_menu() {
4289 editor.update_visible_inline_completion(window, cx);
4290 } else {
4291 editor.discard_inline_completion(false, cx);
4292 }
4293
4294 cx.notify();
4295 } else if editor.completion_tasks.len() <= 1 {
4296 // If there are no more completion tasks and the last menu was
4297 // empty, we should hide it.
4298 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4299 // If it was already hidden and we don't show inline
4300 // completions in the menu, we should also show the
4301 // inline-completion when available.
4302 if was_hidden && editor.show_edit_predictions_in_menu() {
4303 editor.update_visible_inline_completion(window, cx);
4304 }
4305 }
4306 })?;
4307
4308 anyhow::Ok(())
4309 }
4310 .log_err()
4311 });
4312
4313 self.completion_tasks.push((id, task));
4314 }
4315
4316 pub fn confirm_completion(
4317 &mut self,
4318 action: &ConfirmCompletion,
4319 window: &mut Window,
4320 cx: &mut Context<Self>,
4321 ) -> Option<Task<Result<()>>> {
4322 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4323 }
4324
4325 pub fn compose_completion(
4326 &mut self,
4327 action: &ComposeCompletion,
4328 window: &mut Window,
4329 cx: &mut Context<Self>,
4330 ) -> Option<Task<Result<()>>> {
4331 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4332 }
4333
4334 fn do_completion(
4335 &mut self,
4336 item_ix: Option<usize>,
4337 intent: CompletionIntent,
4338 window: &mut Window,
4339 cx: &mut Context<Editor>,
4340 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4341 use language::ToOffset as _;
4342
4343 let completions_menu =
4344 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4345 menu
4346 } else {
4347 return None;
4348 };
4349
4350 let entries = completions_menu.entries.borrow();
4351 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4352 if self.show_edit_predictions_in_menu() {
4353 self.discard_inline_completion(true, cx);
4354 }
4355 let candidate_id = mat.candidate_id;
4356 drop(entries);
4357
4358 let buffer_handle = completions_menu.buffer;
4359 let completion = completions_menu
4360 .completions
4361 .borrow()
4362 .get(candidate_id)?
4363 .clone();
4364 cx.stop_propagation();
4365
4366 let snippet;
4367 let text;
4368
4369 if completion.is_snippet() {
4370 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4371 text = snippet.as_ref().unwrap().text.clone();
4372 } else {
4373 snippet = None;
4374 text = completion.new_text.clone();
4375 };
4376 let selections = self.selections.all::<usize>(cx);
4377 let buffer = buffer_handle.read(cx);
4378 let old_range = completion.old_range.to_offset(buffer);
4379 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4380
4381 let newest_selection = self.selections.newest_anchor();
4382 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4383 return None;
4384 }
4385
4386 let lookbehind = newest_selection
4387 .start
4388 .text_anchor
4389 .to_offset(buffer)
4390 .saturating_sub(old_range.start);
4391 let lookahead = old_range
4392 .end
4393 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4394 let mut common_prefix_len = old_text
4395 .bytes()
4396 .zip(text.bytes())
4397 .take_while(|(a, b)| a == b)
4398 .count();
4399
4400 let snapshot = self.buffer.read(cx).snapshot(cx);
4401 let mut range_to_replace: Option<Range<isize>> = None;
4402 let mut ranges = Vec::new();
4403 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4404 for selection in &selections {
4405 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4406 let start = selection.start.saturating_sub(lookbehind);
4407 let end = selection.end + lookahead;
4408 if selection.id == newest_selection.id {
4409 range_to_replace = Some(
4410 ((start + common_prefix_len) as isize - selection.start as isize)
4411 ..(end as isize - selection.start as isize),
4412 );
4413 }
4414 ranges.push(start + common_prefix_len..end);
4415 } else {
4416 common_prefix_len = 0;
4417 ranges.clear();
4418 ranges.extend(selections.iter().map(|s| {
4419 if s.id == newest_selection.id {
4420 range_to_replace = Some(
4421 old_range.start.to_offset_utf16(&snapshot).0 as isize
4422 - selection.start as isize
4423 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4424 - selection.start as isize,
4425 );
4426 old_range.clone()
4427 } else {
4428 s.start..s.end
4429 }
4430 }));
4431 break;
4432 }
4433 if !self.linked_edit_ranges.is_empty() {
4434 let start_anchor = snapshot.anchor_before(selection.head());
4435 let end_anchor = snapshot.anchor_after(selection.tail());
4436 if let Some(ranges) = self
4437 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4438 {
4439 for (buffer, edits) in ranges {
4440 linked_edits.entry(buffer.clone()).or_default().extend(
4441 edits
4442 .into_iter()
4443 .map(|range| (range, text[common_prefix_len..].to_owned())),
4444 );
4445 }
4446 }
4447 }
4448 }
4449 let text = &text[common_prefix_len..];
4450
4451 cx.emit(EditorEvent::InputHandled {
4452 utf16_range_to_replace: range_to_replace,
4453 text: text.into(),
4454 });
4455
4456 self.transact(window, cx, |this, window, cx| {
4457 if let Some(mut snippet) = snippet {
4458 snippet.text = text.to_string();
4459 for tabstop in snippet
4460 .tabstops
4461 .iter_mut()
4462 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4463 {
4464 tabstop.start -= common_prefix_len as isize;
4465 tabstop.end -= common_prefix_len as isize;
4466 }
4467
4468 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4469 } else {
4470 this.buffer.update(cx, |buffer, cx| {
4471 buffer.edit(
4472 ranges.iter().map(|range| (range.clone(), text)),
4473 this.autoindent_mode.clone(),
4474 cx,
4475 );
4476 });
4477 }
4478 for (buffer, edits) in linked_edits {
4479 buffer.update(cx, |buffer, cx| {
4480 let snapshot = buffer.snapshot();
4481 let edits = edits
4482 .into_iter()
4483 .map(|(range, text)| {
4484 use text::ToPoint as TP;
4485 let end_point = TP::to_point(&range.end, &snapshot);
4486 let start_point = TP::to_point(&range.start, &snapshot);
4487 (start_point..end_point, text)
4488 })
4489 .sorted_by_key(|(range, _)| range.start)
4490 .collect::<Vec<_>>();
4491 buffer.edit(edits, None, cx);
4492 })
4493 }
4494
4495 this.refresh_inline_completion(true, false, window, cx);
4496 });
4497
4498 let show_new_completions_on_confirm = completion
4499 .confirm
4500 .as_ref()
4501 .map_or(false, |confirm| confirm(intent, window, cx));
4502 if show_new_completions_on_confirm {
4503 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4504 }
4505
4506 let provider = self.completion_provider.as_ref()?;
4507 drop(completion);
4508 let apply_edits = provider.apply_additional_edits_for_completion(
4509 buffer_handle,
4510 completions_menu.completions.clone(),
4511 candidate_id,
4512 true,
4513 cx,
4514 );
4515
4516 let editor_settings = EditorSettings::get_global(cx);
4517 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4518 // After the code completion is finished, users often want to know what signatures are needed.
4519 // so we should automatically call signature_help
4520 self.show_signature_help(&ShowSignatureHelp, window, cx);
4521 }
4522
4523 Some(cx.foreground_executor().spawn(async move {
4524 apply_edits.await?;
4525 Ok(())
4526 }))
4527 }
4528
4529 pub fn toggle_code_actions(
4530 &mut self,
4531 action: &ToggleCodeActions,
4532 window: &mut Window,
4533 cx: &mut Context<Self>,
4534 ) {
4535 let mut context_menu = self.context_menu.borrow_mut();
4536 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4537 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4538 // Toggle if we're selecting the same one
4539 *context_menu = None;
4540 cx.notify();
4541 return;
4542 } else {
4543 // Otherwise, clear it and start a new one
4544 *context_menu = None;
4545 cx.notify();
4546 }
4547 }
4548 drop(context_menu);
4549 let snapshot = self.snapshot(window, cx);
4550 let deployed_from_indicator = action.deployed_from_indicator;
4551 let mut task = self.code_actions_task.take();
4552 let action = action.clone();
4553 cx.spawn_in(window, |editor, mut cx| async move {
4554 while let Some(prev_task) = task {
4555 prev_task.await.log_err();
4556 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4557 }
4558
4559 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4560 if editor.focus_handle.is_focused(window) {
4561 let multibuffer_point = action
4562 .deployed_from_indicator
4563 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4564 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4565 let (buffer, buffer_row) = snapshot
4566 .buffer_snapshot
4567 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4568 .and_then(|(buffer_snapshot, range)| {
4569 editor
4570 .buffer
4571 .read(cx)
4572 .buffer(buffer_snapshot.remote_id())
4573 .map(|buffer| (buffer, range.start.row))
4574 })?;
4575 let (_, code_actions) = editor
4576 .available_code_actions
4577 .clone()
4578 .and_then(|(location, code_actions)| {
4579 let snapshot = location.buffer.read(cx).snapshot();
4580 let point_range = location.range.to_point(&snapshot);
4581 let point_range = point_range.start.row..=point_range.end.row;
4582 if point_range.contains(&buffer_row) {
4583 Some((location, code_actions))
4584 } else {
4585 None
4586 }
4587 })
4588 .unzip();
4589 let buffer_id = buffer.read(cx).remote_id();
4590 let tasks = editor
4591 .tasks
4592 .get(&(buffer_id, buffer_row))
4593 .map(|t| Arc::new(t.to_owned()));
4594 if tasks.is_none() && code_actions.is_none() {
4595 return None;
4596 }
4597
4598 editor.completion_tasks.clear();
4599 editor.discard_inline_completion(false, cx);
4600 let task_context =
4601 tasks
4602 .as_ref()
4603 .zip(editor.project.clone())
4604 .map(|(tasks, project)| {
4605 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4606 });
4607
4608 Some(cx.spawn_in(window, |editor, mut cx| async move {
4609 let task_context = match task_context {
4610 Some(task_context) => task_context.await,
4611 None => None,
4612 };
4613 let resolved_tasks =
4614 tasks.zip(task_context).map(|(tasks, task_context)| {
4615 Rc::new(ResolvedTasks {
4616 templates: tasks.resolve(&task_context).collect(),
4617 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4618 multibuffer_point.row,
4619 tasks.column,
4620 )),
4621 })
4622 });
4623 let spawn_straight_away = resolved_tasks
4624 .as_ref()
4625 .map_or(false, |tasks| tasks.templates.len() == 1)
4626 && code_actions
4627 .as_ref()
4628 .map_or(true, |actions| actions.is_empty());
4629 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4630 *editor.context_menu.borrow_mut() =
4631 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4632 buffer,
4633 actions: CodeActionContents {
4634 tasks: resolved_tasks,
4635 actions: code_actions,
4636 },
4637 selected_item: Default::default(),
4638 scroll_handle: UniformListScrollHandle::default(),
4639 deployed_from_indicator,
4640 }));
4641 if spawn_straight_away {
4642 if let Some(task) = editor.confirm_code_action(
4643 &ConfirmCodeAction { item_ix: Some(0) },
4644 window,
4645 cx,
4646 ) {
4647 cx.notify();
4648 return task;
4649 }
4650 }
4651 cx.notify();
4652 Task::ready(Ok(()))
4653 }) {
4654 task.await
4655 } else {
4656 Ok(())
4657 }
4658 }))
4659 } else {
4660 Some(Task::ready(Ok(())))
4661 }
4662 })?;
4663 if let Some(task) = spawned_test_task {
4664 task.await?;
4665 }
4666
4667 Ok::<_, anyhow::Error>(())
4668 })
4669 .detach_and_log_err(cx);
4670 }
4671
4672 pub fn confirm_code_action(
4673 &mut self,
4674 action: &ConfirmCodeAction,
4675 window: &mut Window,
4676 cx: &mut Context<Self>,
4677 ) -> Option<Task<Result<()>>> {
4678 let actions_menu =
4679 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4680 menu
4681 } else {
4682 return None;
4683 };
4684 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4685 let action = actions_menu.actions.get(action_ix)?;
4686 let title = action.label();
4687 let buffer = actions_menu.buffer;
4688 let workspace = self.workspace()?;
4689
4690 match action {
4691 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4692 workspace.update(cx, |workspace, cx| {
4693 workspace::tasks::schedule_resolved_task(
4694 workspace,
4695 task_source_kind,
4696 resolved_task,
4697 false,
4698 cx,
4699 );
4700
4701 Some(Task::ready(Ok(())))
4702 })
4703 }
4704 CodeActionsItem::CodeAction {
4705 excerpt_id,
4706 action,
4707 provider,
4708 } => {
4709 let apply_code_action =
4710 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4711 let workspace = workspace.downgrade();
4712 Some(cx.spawn_in(window, |editor, cx| async move {
4713 let project_transaction = apply_code_action.await?;
4714 Self::open_project_transaction(
4715 &editor,
4716 workspace,
4717 project_transaction,
4718 title,
4719 cx,
4720 )
4721 .await
4722 }))
4723 }
4724 }
4725 }
4726
4727 pub async fn open_project_transaction(
4728 this: &WeakEntity<Editor>,
4729 workspace: WeakEntity<Workspace>,
4730 transaction: ProjectTransaction,
4731 title: String,
4732 mut cx: AsyncWindowContext,
4733 ) -> Result<()> {
4734 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4735 cx.update(|_, cx| {
4736 entries.sort_unstable_by_key(|(buffer, _)| {
4737 buffer.read(cx).file().map(|f| f.path().clone())
4738 });
4739 })?;
4740
4741 // If the project transaction's edits are all contained within this editor, then
4742 // avoid opening a new editor to display them.
4743
4744 if let Some((buffer, transaction)) = entries.first() {
4745 if entries.len() == 1 {
4746 let excerpt = this.update(&mut cx, |editor, cx| {
4747 editor
4748 .buffer()
4749 .read(cx)
4750 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4751 })?;
4752 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4753 if excerpted_buffer == *buffer {
4754 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4755 let excerpt_range = excerpt_range.to_offset(buffer);
4756 buffer
4757 .edited_ranges_for_transaction::<usize>(transaction)
4758 .all(|range| {
4759 excerpt_range.start <= range.start
4760 && excerpt_range.end >= range.end
4761 })
4762 })?;
4763
4764 if all_edits_within_excerpt {
4765 return Ok(());
4766 }
4767 }
4768 }
4769 }
4770 } else {
4771 return Ok(());
4772 }
4773
4774 let mut ranges_to_highlight = Vec::new();
4775 let excerpt_buffer = cx.new(|cx| {
4776 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4777 for (buffer_handle, transaction) in &entries {
4778 let buffer = buffer_handle.read(cx);
4779 ranges_to_highlight.extend(
4780 multibuffer.push_excerpts_with_context_lines(
4781 buffer_handle.clone(),
4782 buffer
4783 .edited_ranges_for_transaction::<usize>(transaction)
4784 .collect(),
4785 DEFAULT_MULTIBUFFER_CONTEXT,
4786 cx,
4787 ),
4788 );
4789 }
4790 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4791 multibuffer
4792 })?;
4793
4794 workspace.update_in(&mut cx, |workspace, window, cx| {
4795 let project = workspace.project().clone();
4796 let editor =
4797 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4798 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4799 editor.update(cx, |editor, cx| {
4800 editor.highlight_background::<Self>(
4801 &ranges_to_highlight,
4802 |theme| theme.editor_highlighted_line_background,
4803 cx,
4804 );
4805 });
4806 })?;
4807
4808 Ok(())
4809 }
4810
4811 pub fn clear_code_action_providers(&mut self) {
4812 self.code_action_providers.clear();
4813 self.available_code_actions.take();
4814 }
4815
4816 pub fn add_code_action_provider(
4817 &mut self,
4818 provider: Rc<dyn CodeActionProvider>,
4819 window: &mut Window,
4820 cx: &mut Context<Self>,
4821 ) {
4822 if self
4823 .code_action_providers
4824 .iter()
4825 .any(|existing_provider| existing_provider.id() == provider.id())
4826 {
4827 return;
4828 }
4829
4830 self.code_action_providers.push(provider);
4831 self.refresh_code_actions(window, cx);
4832 }
4833
4834 pub fn remove_code_action_provider(
4835 &mut self,
4836 id: Arc<str>,
4837 window: &mut Window,
4838 cx: &mut Context<Self>,
4839 ) {
4840 self.code_action_providers
4841 .retain(|provider| provider.id() != id);
4842 self.refresh_code_actions(window, cx);
4843 }
4844
4845 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4846 let buffer = self.buffer.read(cx);
4847 let newest_selection = self.selections.newest_anchor().clone();
4848 if newest_selection.head().diff_base_anchor.is_some() {
4849 return None;
4850 }
4851 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4852 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4853 if start_buffer != end_buffer {
4854 return None;
4855 }
4856
4857 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4858 cx.background_executor()
4859 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4860 .await;
4861
4862 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4863 let providers = this.code_action_providers.clone();
4864 let tasks = this
4865 .code_action_providers
4866 .iter()
4867 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4868 .collect::<Vec<_>>();
4869 (providers, tasks)
4870 })?;
4871
4872 let mut actions = Vec::new();
4873 for (provider, provider_actions) in
4874 providers.into_iter().zip(future::join_all(tasks).await)
4875 {
4876 if let Some(provider_actions) = provider_actions.log_err() {
4877 actions.extend(provider_actions.into_iter().map(|action| {
4878 AvailableCodeAction {
4879 excerpt_id: newest_selection.start.excerpt_id,
4880 action,
4881 provider: provider.clone(),
4882 }
4883 }));
4884 }
4885 }
4886
4887 this.update(&mut cx, |this, cx| {
4888 this.available_code_actions = if actions.is_empty() {
4889 None
4890 } else {
4891 Some((
4892 Location {
4893 buffer: start_buffer,
4894 range: start..end,
4895 },
4896 actions.into(),
4897 ))
4898 };
4899 cx.notify();
4900 })
4901 }));
4902 None
4903 }
4904
4905 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4906 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4907 self.show_git_blame_inline = false;
4908
4909 self.show_git_blame_inline_delay_task =
4910 Some(cx.spawn_in(window, |this, mut cx| async move {
4911 cx.background_executor().timer(delay).await;
4912
4913 this.update(&mut cx, |this, cx| {
4914 this.show_git_blame_inline = true;
4915 cx.notify();
4916 })
4917 .log_err();
4918 }));
4919 }
4920 }
4921
4922 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4923 if self.pending_rename.is_some() {
4924 return None;
4925 }
4926
4927 let provider = self.semantics_provider.clone()?;
4928 let buffer = self.buffer.read(cx);
4929 let newest_selection = self.selections.newest_anchor().clone();
4930 let cursor_position = newest_selection.head();
4931 let (cursor_buffer, cursor_buffer_position) =
4932 buffer.text_anchor_for_position(cursor_position, cx)?;
4933 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4934 if cursor_buffer != tail_buffer {
4935 return None;
4936 }
4937 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4938 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4939 cx.background_executor()
4940 .timer(Duration::from_millis(debounce))
4941 .await;
4942
4943 let highlights = if let Some(highlights) = cx
4944 .update(|cx| {
4945 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4946 })
4947 .ok()
4948 .flatten()
4949 {
4950 highlights.await.log_err()
4951 } else {
4952 None
4953 };
4954
4955 if let Some(highlights) = highlights {
4956 this.update(&mut cx, |this, cx| {
4957 if this.pending_rename.is_some() {
4958 return;
4959 }
4960
4961 let buffer_id = cursor_position.buffer_id;
4962 let buffer = this.buffer.read(cx);
4963 if !buffer
4964 .text_anchor_for_position(cursor_position, cx)
4965 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4966 {
4967 return;
4968 }
4969
4970 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4971 let mut write_ranges = Vec::new();
4972 let mut read_ranges = Vec::new();
4973 for highlight in highlights {
4974 for (excerpt_id, excerpt_range) in
4975 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4976 {
4977 let start = highlight
4978 .range
4979 .start
4980 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4981 let end = highlight
4982 .range
4983 .end
4984 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4985 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4986 continue;
4987 }
4988
4989 let range = Anchor {
4990 buffer_id,
4991 excerpt_id,
4992 text_anchor: start,
4993 diff_base_anchor: None,
4994 }..Anchor {
4995 buffer_id,
4996 excerpt_id,
4997 text_anchor: end,
4998 diff_base_anchor: None,
4999 };
5000 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5001 write_ranges.push(range);
5002 } else {
5003 read_ranges.push(range);
5004 }
5005 }
5006 }
5007
5008 this.highlight_background::<DocumentHighlightRead>(
5009 &read_ranges,
5010 |theme| theme.editor_document_highlight_read_background,
5011 cx,
5012 );
5013 this.highlight_background::<DocumentHighlightWrite>(
5014 &write_ranges,
5015 |theme| theme.editor_document_highlight_write_background,
5016 cx,
5017 );
5018 cx.notify();
5019 })
5020 .log_err();
5021 }
5022 }));
5023 None
5024 }
5025
5026 pub fn refresh_selected_text_highlights(
5027 &mut self,
5028 window: &mut Window,
5029 cx: &mut Context<Editor>,
5030 ) {
5031 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5032 return;
5033 }
5034 self.selection_highlight_task.take();
5035 if !EditorSettings::get_global(cx).selection_highlight {
5036 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5037 return;
5038 }
5039 if self.selections.count() != 1 || self.selections.line_mode {
5040 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5041 return;
5042 }
5043 let selection = self.selections.newest::<Point>(cx);
5044 if selection.is_empty() || selection.start.row != selection.end.row {
5045 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5046 return;
5047 }
5048 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5049 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
5050 cx.background_executor()
5051 .timer(Duration::from_millis(debounce))
5052 .await;
5053 let Some(Some(matches_task)) = editor
5054 .update_in(&mut cx, |editor, _, cx| {
5055 if editor.selections.count() != 1 || editor.selections.line_mode {
5056 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5057 return None;
5058 }
5059 let selection = editor.selections.newest::<Point>(cx);
5060 if selection.is_empty() || selection.start.row != selection.end.row {
5061 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5062 return None;
5063 }
5064 let buffer = editor.buffer().read(cx).snapshot(cx);
5065 let query = buffer.text_for_range(selection.range()).collect::<String>();
5066 if query.trim().is_empty() {
5067 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5068 return None;
5069 }
5070 Some(cx.background_spawn(async move {
5071 let mut ranges = Vec::new();
5072 let selection_anchors = selection.range().to_anchors(&buffer);
5073 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5074 for (search_buffer, search_range, excerpt_id) in
5075 buffer.range_to_buffer_ranges(range)
5076 {
5077 ranges.extend(
5078 project::search::SearchQuery::text(
5079 query.clone(),
5080 false,
5081 false,
5082 false,
5083 Default::default(),
5084 Default::default(),
5085 None,
5086 )
5087 .unwrap()
5088 .search(search_buffer, Some(search_range.clone()))
5089 .await
5090 .into_iter()
5091 .filter_map(
5092 |match_range| {
5093 let start = search_buffer.anchor_after(
5094 search_range.start + match_range.start,
5095 );
5096 let end = search_buffer.anchor_before(
5097 search_range.start + match_range.end,
5098 );
5099 let range = Anchor::range_in_buffer(
5100 excerpt_id,
5101 search_buffer.remote_id(),
5102 start..end,
5103 );
5104 (range != selection_anchors).then_some(range)
5105 },
5106 ),
5107 );
5108 }
5109 }
5110 ranges
5111 }))
5112 })
5113 .log_err()
5114 else {
5115 return;
5116 };
5117 let matches = matches_task.await;
5118 editor
5119 .update_in(&mut cx, |editor, _, cx| {
5120 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5121 if !matches.is_empty() {
5122 editor.highlight_background::<SelectedTextHighlight>(
5123 &matches,
5124 |theme| theme.editor_document_highlight_bracket_background,
5125 cx,
5126 )
5127 }
5128 })
5129 .log_err();
5130 }));
5131 }
5132
5133 pub fn refresh_inline_completion(
5134 &mut self,
5135 debounce: bool,
5136 user_requested: bool,
5137 window: &mut Window,
5138 cx: &mut Context<Self>,
5139 ) -> Option<()> {
5140 let provider = self.edit_prediction_provider()?;
5141 let cursor = self.selections.newest_anchor().head();
5142 let (buffer, cursor_buffer_position) =
5143 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5144
5145 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5146 self.discard_inline_completion(false, cx);
5147 return None;
5148 }
5149
5150 if !user_requested
5151 && (!self.should_show_edit_predictions()
5152 || !self.is_focused(window)
5153 || buffer.read(cx).is_empty())
5154 {
5155 self.discard_inline_completion(false, cx);
5156 return None;
5157 }
5158
5159 self.update_visible_inline_completion(window, cx);
5160 provider.refresh(
5161 self.project.clone(),
5162 buffer,
5163 cursor_buffer_position,
5164 debounce,
5165 cx,
5166 );
5167 Some(())
5168 }
5169
5170 fn show_edit_predictions_in_menu(&self) -> bool {
5171 match self.edit_prediction_settings {
5172 EditPredictionSettings::Disabled => false,
5173 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5174 }
5175 }
5176
5177 pub fn edit_predictions_enabled(&self) -> bool {
5178 match self.edit_prediction_settings {
5179 EditPredictionSettings::Disabled => false,
5180 EditPredictionSettings::Enabled { .. } => true,
5181 }
5182 }
5183
5184 fn edit_prediction_requires_modifier(&self) -> bool {
5185 match self.edit_prediction_settings {
5186 EditPredictionSettings::Disabled => false,
5187 EditPredictionSettings::Enabled {
5188 preview_requires_modifier,
5189 ..
5190 } => preview_requires_modifier,
5191 }
5192 }
5193
5194 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5195 if self.edit_prediction_provider.is_none() {
5196 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5197 } else {
5198 let selection = self.selections.newest_anchor();
5199 let cursor = selection.head();
5200
5201 if let Some((buffer, cursor_buffer_position)) =
5202 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5203 {
5204 self.edit_prediction_settings =
5205 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5206 }
5207 }
5208 }
5209
5210 fn edit_prediction_settings_at_position(
5211 &self,
5212 buffer: &Entity<Buffer>,
5213 buffer_position: language::Anchor,
5214 cx: &App,
5215 ) -> EditPredictionSettings {
5216 if self.mode != EditorMode::Full
5217 || !self.show_inline_completions_override.unwrap_or(true)
5218 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5219 {
5220 return EditPredictionSettings::Disabled;
5221 }
5222
5223 let buffer = buffer.read(cx);
5224
5225 let file = buffer.file();
5226
5227 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5228 return EditPredictionSettings::Disabled;
5229 };
5230
5231 let by_provider = matches!(
5232 self.menu_inline_completions_policy,
5233 MenuInlineCompletionsPolicy::ByProvider
5234 );
5235
5236 let show_in_menu = by_provider
5237 && self
5238 .edit_prediction_provider
5239 .as_ref()
5240 .map_or(false, |provider| {
5241 provider.provider.show_completions_in_menu()
5242 });
5243
5244 let preview_requires_modifier =
5245 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5246
5247 EditPredictionSettings::Enabled {
5248 show_in_menu,
5249 preview_requires_modifier,
5250 }
5251 }
5252
5253 fn should_show_edit_predictions(&self) -> bool {
5254 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5255 }
5256
5257 pub fn edit_prediction_preview_is_active(&self) -> bool {
5258 matches!(
5259 self.edit_prediction_preview,
5260 EditPredictionPreview::Active { .. }
5261 )
5262 }
5263
5264 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5265 let cursor = self.selections.newest_anchor().head();
5266 if let Some((buffer, cursor_position)) =
5267 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5268 {
5269 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5270 } else {
5271 false
5272 }
5273 }
5274
5275 fn edit_predictions_enabled_in_buffer(
5276 &self,
5277 buffer: &Entity<Buffer>,
5278 buffer_position: language::Anchor,
5279 cx: &App,
5280 ) -> bool {
5281 maybe!({
5282 if self.read_only(cx) {
5283 return Some(false);
5284 }
5285 let provider = self.edit_prediction_provider()?;
5286 if !provider.is_enabled(&buffer, buffer_position, cx) {
5287 return Some(false);
5288 }
5289 let buffer = buffer.read(cx);
5290 let Some(file) = buffer.file() else {
5291 return Some(true);
5292 };
5293 let settings = all_language_settings(Some(file), cx);
5294 Some(settings.edit_predictions_enabled_for_file(file, cx))
5295 })
5296 .unwrap_or(false)
5297 }
5298
5299 fn cycle_inline_completion(
5300 &mut self,
5301 direction: Direction,
5302 window: &mut Window,
5303 cx: &mut Context<Self>,
5304 ) -> Option<()> {
5305 let provider = self.edit_prediction_provider()?;
5306 let cursor = self.selections.newest_anchor().head();
5307 let (buffer, cursor_buffer_position) =
5308 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5309 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5310 return None;
5311 }
5312
5313 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5314 self.update_visible_inline_completion(window, cx);
5315
5316 Some(())
5317 }
5318
5319 pub fn show_inline_completion(
5320 &mut self,
5321 _: &ShowEditPrediction,
5322 window: &mut Window,
5323 cx: &mut Context<Self>,
5324 ) {
5325 if !self.has_active_inline_completion() {
5326 self.refresh_inline_completion(false, true, window, cx);
5327 return;
5328 }
5329
5330 self.update_visible_inline_completion(window, cx);
5331 }
5332
5333 pub fn display_cursor_names(
5334 &mut self,
5335 _: &DisplayCursorNames,
5336 window: &mut Window,
5337 cx: &mut Context<Self>,
5338 ) {
5339 self.show_cursor_names(window, cx);
5340 }
5341
5342 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5343 self.show_cursor_names = true;
5344 cx.notify();
5345 cx.spawn_in(window, |this, mut cx| async move {
5346 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5347 this.update(&mut cx, |this, cx| {
5348 this.show_cursor_names = false;
5349 cx.notify()
5350 })
5351 .ok()
5352 })
5353 .detach();
5354 }
5355
5356 pub fn next_edit_prediction(
5357 &mut self,
5358 _: &NextEditPrediction,
5359 window: &mut Window,
5360 cx: &mut Context<Self>,
5361 ) {
5362 if self.has_active_inline_completion() {
5363 self.cycle_inline_completion(Direction::Next, window, cx);
5364 } else {
5365 let is_copilot_disabled = self
5366 .refresh_inline_completion(false, true, window, cx)
5367 .is_none();
5368 if is_copilot_disabled {
5369 cx.propagate();
5370 }
5371 }
5372 }
5373
5374 pub fn previous_edit_prediction(
5375 &mut self,
5376 _: &PreviousEditPrediction,
5377 window: &mut Window,
5378 cx: &mut Context<Self>,
5379 ) {
5380 if self.has_active_inline_completion() {
5381 self.cycle_inline_completion(Direction::Prev, window, cx);
5382 } else {
5383 let is_copilot_disabled = self
5384 .refresh_inline_completion(false, true, window, cx)
5385 .is_none();
5386 if is_copilot_disabled {
5387 cx.propagate();
5388 }
5389 }
5390 }
5391
5392 pub fn accept_edit_prediction(
5393 &mut self,
5394 _: &AcceptEditPrediction,
5395 window: &mut Window,
5396 cx: &mut Context<Self>,
5397 ) {
5398 if self.show_edit_predictions_in_menu() {
5399 self.hide_context_menu(window, cx);
5400 }
5401
5402 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5403 return;
5404 };
5405
5406 self.report_inline_completion_event(
5407 active_inline_completion.completion_id.clone(),
5408 true,
5409 cx,
5410 );
5411
5412 match &active_inline_completion.completion {
5413 InlineCompletion::Move { target, .. } => {
5414 let target = *target;
5415
5416 if let Some(position_map) = &self.last_position_map {
5417 if position_map
5418 .visible_row_range
5419 .contains(&target.to_display_point(&position_map.snapshot).row())
5420 || !self.edit_prediction_requires_modifier()
5421 {
5422 self.unfold_ranges(&[target..target], true, false, cx);
5423 // Note that this is also done in vim's handler of the Tab action.
5424 self.change_selections(
5425 Some(Autoscroll::newest()),
5426 window,
5427 cx,
5428 |selections| {
5429 selections.select_anchor_ranges([target..target]);
5430 },
5431 );
5432 self.clear_row_highlights::<EditPredictionPreview>();
5433
5434 self.edit_prediction_preview
5435 .set_previous_scroll_position(None);
5436 } else {
5437 self.edit_prediction_preview
5438 .set_previous_scroll_position(Some(
5439 position_map.snapshot.scroll_anchor,
5440 ));
5441
5442 self.highlight_rows::<EditPredictionPreview>(
5443 target..target,
5444 cx.theme().colors().editor_highlighted_line_background,
5445 true,
5446 cx,
5447 );
5448 self.request_autoscroll(Autoscroll::fit(), cx);
5449 }
5450 }
5451 }
5452 InlineCompletion::Edit { edits, .. } => {
5453 if let Some(provider) = self.edit_prediction_provider() {
5454 provider.accept(cx);
5455 }
5456
5457 let snapshot = self.buffer.read(cx).snapshot(cx);
5458 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5459
5460 self.buffer.update(cx, |buffer, cx| {
5461 buffer.edit(edits.iter().cloned(), None, cx)
5462 });
5463
5464 self.change_selections(None, window, cx, |s| {
5465 s.select_anchor_ranges([last_edit_end..last_edit_end])
5466 });
5467
5468 self.update_visible_inline_completion(window, cx);
5469 if self.active_inline_completion.is_none() {
5470 self.refresh_inline_completion(true, true, window, cx);
5471 }
5472
5473 cx.notify();
5474 }
5475 }
5476
5477 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5478 }
5479
5480 pub fn accept_partial_inline_completion(
5481 &mut self,
5482 _: &AcceptPartialEditPrediction,
5483 window: &mut Window,
5484 cx: &mut Context<Self>,
5485 ) {
5486 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5487 return;
5488 };
5489 if self.selections.count() != 1 {
5490 return;
5491 }
5492
5493 self.report_inline_completion_event(
5494 active_inline_completion.completion_id.clone(),
5495 true,
5496 cx,
5497 );
5498
5499 match &active_inline_completion.completion {
5500 InlineCompletion::Move { target, .. } => {
5501 let target = *target;
5502 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5503 selections.select_anchor_ranges([target..target]);
5504 });
5505 }
5506 InlineCompletion::Edit { edits, .. } => {
5507 // Find an insertion that starts at the cursor position.
5508 let snapshot = self.buffer.read(cx).snapshot(cx);
5509 let cursor_offset = self.selections.newest::<usize>(cx).head();
5510 let insertion = edits.iter().find_map(|(range, text)| {
5511 let range = range.to_offset(&snapshot);
5512 if range.is_empty() && range.start == cursor_offset {
5513 Some(text)
5514 } else {
5515 None
5516 }
5517 });
5518
5519 if let Some(text) = insertion {
5520 let mut partial_completion = text
5521 .chars()
5522 .by_ref()
5523 .take_while(|c| c.is_alphabetic())
5524 .collect::<String>();
5525 if partial_completion.is_empty() {
5526 partial_completion = text
5527 .chars()
5528 .by_ref()
5529 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5530 .collect::<String>();
5531 }
5532
5533 cx.emit(EditorEvent::InputHandled {
5534 utf16_range_to_replace: None,
5535 text: partial_completion.clone().into(),
5536 });
5537
5538 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5539
5540 self.refresh_inline_completion(true, true, window, cx);
5541 cx.notify();
5542 } else {
5543 self.accept_edit_prediction(&Default::default(), window, cx);
5544 }
5545 }
5546 }
5547 }
5548
5549 fn discard_inline_completion(
5550 &mut self,
5551 should_report_inline_completion_event: bool,
5552 cx: &mut Context<Self>,
5553 ) -> bool {
5554 if should_report_inline_completion_event {
5555 let completion_id = self
5556 .active_inline_completion
5557 .as_ref()
5558 .and_then(|active_completion| active_completion.completion_id.clone());
5559
5560 self.report_inline_completion_event(completion_id, false, cx);
5561 }
5562
5563 if let Some(provider) = self.edit_prediction_provider() {
5564 provider.discard(cx);
5565 }
5566
5567 self.take_active_inline_completion(cx)
5568 }
5569
5570 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5571 let Some(provider) = self.edit_prediction_provider() else {
5572 return;
5573 };
5574
5575 let Some((_, buffer, _)) = self
5576 .buffer
5577 .read(cx)
5578 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5579 else {
5580 return;
5581 };
5582
5583 let extension = buffer
5584 .read(cx)
5585 .file()
5586 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5587
5588 let event_type = match accepted {
5589 true => "Edit Prediction Accepted",
5590 false => "Edit Prediction Discarded",
5591 };
5592 telemetry::event!(
5593 event_type,
5594 provider = provider.name(),
5595 prediction_id = id,
5596 suggestion_accepted = accepted,
5597 file_extension = extension,
5598 );
5599 }
5600
5601 pub fn has_active_inline_completion(&self) -> bool {
5602 self.active_inline_completion.is_some()
5603 }
5604
5605 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5606 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5607 return false;
5608 };
5609
5610 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5611 self.clear_highlights::<InlineCompletionHighlight>(cx);
5612 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5613 true
5614 }
5615
5616 /// Returns true when we're displaying the edit prediction popover below the cursor
5617 /// like we are not previewing and the LSP autocomplete menu is visible
5618 /// or we are in `when_holding_modifier` mode.
5619 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5620 if self.edit_prediction_preview_is_active()
5621 || !self.show_edit_predictions_in_menu()
5622 || !self.edit_predictions_enabled()
5623 {
5624 return false;
5625 }
5626
5627 if self.has_visible_completions_menu() {
5628 return true;
5629 }
5630
5631 has_completion && self.edit_prediction_requires_modifier()
5632 }
5633
5634 fn handle_modifiers_changed(
5635 &mut self,
5636 modifiers: Modifiers,
5637 position_map: &PositionMap,
5638 window: &mut Window,
5639 cx: &mut Context<Self>,
5640 ) {
5641 if self.show_edit_predictions_in_menu() {
5642 self.update_edit_prediction_preview(&modifiers, window, cx);
5643 }
5644
5645 self.update_selection_mode(&modifiers, position_map, window, cx);
5646
5647 let mouse_position = window.mouse_position();
5648 if !position_map.text_hitbox.is_hovered(window) {
5649 return;
5650 }
5651
5652 self.update_hovered_link(
5653 position_map.point_for_position(mouse_position),
5654 &position_map.snapshot,
5655 modifiers,
5656 window,
5657 cx,
5658 )
5659 }
5660
5661 fn update_selection_mode(
5662 &mut self,
5663 modifiers: &Modifiers,
5664 position_map: &PositionMap,
5665 window: &mut Window,
5666 cx: &mut Context<Self>,
5667 ) {
5668 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5669 return;
5670 }
5671
5672 let mouse_position = window.mouse_position();
5673 let point_for_position = position_map.point_for_position(mouse_position);
5674 let position = point_for_position.previous_valid;
5675
5676 self.select(
5677 SelectPhase::BeginColumnar {
5678 position,
5679 reset: false,
5680 goal_column: point_for_position.exact_unclipped.column(),
5681 },
5682 window,
5683 cx,
5684 );
5685 }
5686
5687 fn update_edit_prediction_preview(
5688 &mut self,
5689 modifiers: &Modifiers,
5690 window: &mut Window,
5691 cx: &mut Context<Self>,
5692 ) {
5693 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5694 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5695 return;
5696 };
5697
5698 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5699 if matches!(
5700 self.edit_prediction_preview,
5701 EditPredictionPreview::Inactive { .. }
5702 ) {
5703 self.edit_prediction_preview = EditPredictionPreview::Active {
5704 previous_scroll_position: None,
5705 since: Instant::now(),
5706 };
5707
5708 self.update_visible_inline_completion(window, cx);
5709 cx.notify();
5710 }
5711 } else if let EditPredictionPreview::Active {
5712 previous_scroll_position,
5713 since,
5714 } = self.edit_prediction_preview
5715 {
5716 if let (Some(previous_scroll_position), Some(position_map)) =
5717 (previous_scroll_position, self.last_position_map.as_ref())
5718 {
5719 self.set_scroll_position(
5720 previous_scroll_position
5721 .scroll_position(&position_map.snapshot.display_snapshot),
5722 window,
5723 cx,
5724 );
5725 }
5726
5727 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5728 released_too_fast: since.elapsed() < Duration::from_millis(200),
5729 };
5730 self.clear_row_highlights::<EditPredictionPreview>();
5731 self.update_visible_inline_completion(window, cx);
5732 cx.notify();
5733 }
5734 }
5735
5736 fn update_visible_inline_completion(
5737 &mut self,
5738 _window: &mut Window,
5739 cx: &mut Context<Self>,
5740 ) -> Option<()> {
5741 let selection = self.selections.newest_anchor();
5742 let cursor = selection.head();
5743 let multibuffer = self.buffer.read(cx).snapshot(cx);
5744 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5745 let excerpt_id = cursor.excerpt_id;
5746
5747 let show_in_menu = self.show_edit_predictions_in_menu();
5748 let completions_menu_has_precedence = !show_in_menu
5749 && (self.context_menu.borrow().is_some()
5750 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5751
5752 if completions_menu_has_precedence
5753 || !offset_selection.is_empty()
5754 || self
5755 .active_inline_completion
5756 .as_ref()
5757 .map_or(false, |completion| {
5758 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5759 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5760 !invalidation_range.contains(&offset_selection.head())
5761 })
5762 {
5763 self.discard_inline_completion(false, cx);
5764 return None;
5765 }
5766
5767 self.take_active_inline_completion(cx);
5768 let Some(provider) = self.edit_prediction_provider() else {
5769 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5770 return None;
5771 };
5772
5773 let (buffer, cursor_buffer_position) =
5774 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5775
5776 self.edit_prediction_settings =
5777 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5778
5779 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5780
5781 if self.edit_prediction_indent_conflict {
5782 let cursor_point = cursor.to_point(&multibuffer);
5783
5784 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5785
5786 if let Some((_, indent)) = indents.iter().next() {
5787 if indent.len == cursor_point.column {
5788 self.edit_prediction_indent_conflict = false;
5789 }
5790 }
5791 }
5792
5793 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5794 let edits = inline_completion
5795 .edits
5796 .into_iter()
5797 .flat_map(|(range, new_text)| {
5798 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5799 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5800 Some((start..end, new_text))
5801 })
5802 .collect::<Vec<_>>();
5803 if edits.is_empty() {
5804 return None;
5805 }
5806
5807 let first_edit_start = edits.first().unwrap().0.start;
5808 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5809 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5810
5811 let last_edit_end = edits.last().unwrap().0.end;
5812 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5813 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5814
5815 let cursor_row = cursor.to_point(&multibuffer).row;
5816
5817 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5818
5819 let mut inlay_ids = Vec::new();
5820 let invalidation_row_range;
5821 let move_invalidation_row_range = if cursor_row < edit_start_row {
5822 Some(cursor_row..edit_end_row)
5823 } else if cursor_row > edit_end_row {
5824 Some(edit_start_row..cursor_row)
5825 } else {
5826 None
5827 };
5828 let is_move =
5829 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5830 let completion = if is_move {
5831 invalidation_row_range =
5832 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5833 let target = first_edit_start;
5834 InlineCompletion::Move { target, snapshot }
5835 } else {
5836 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5837 && !self.inline_completions_hidden_for_vim_mode;
5838
5839 if show_completions_in_buffer {
5840 if edits
5841 .iter()
5842 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5843 {
5844 let mut inlays = Vec::new();
5845 for (range, new_text) in &edits {
5846 let inlay = Inlay::inline_completion(
5847 post_inc(&mut self.next_inlay_id),
5848 range.start,
5849 new_text.as_str(),
5850 );
5851 inlay_ids.push(inlay.id);
5852 inlays.push(inlay);
5853 }
5854
5855 self.splice_inlays(&[], inlays, cx);
5856 } else {
5857 let background_color = cx.theme().status().deleted_background;
5858 self.highlight_text::<InlineCompletionHighlight>(
5859 edits.iter().map(|(range, _)| range.clone()).collect(),
5860 HighlightStyle {
5861 background_color: Some(background_color),
5862 ..Default::default()
5863 },
5864 cx,
5865 );
5866 }
5867 }
5868
5869 invalidation_row_range = edit_start_row..edit_end_row;
5870
5871 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5872 if provider.show_tab_accept_marker() {
5873 EditDisplayMode::TabAccept
5874 } else {
5875 EditDisplayMode::Inline
5876 }
5877 } else {
5878 EditDisplayMode::DiffPopover
5879 };
5880
5881 InlineCompletion::Edit {
5882 edits,
5883 edit_preview: inline_completion.edit_preview,
5884 display_mode,
5885 snapshot,
5886 }
5887 };
5888
5889 let invalidation_range = multibuffer
5890 .anchor_before(Point::new(invalidation_row_range.start, 0))
5891 ..multibuffer.anchor_after(Point::new(
5892 invalidation_row_range.end,
5893 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5894 ));
5895
5896 self.stale_inline_completion_in_menu = None;
5897 self.active_inline_completion = Some(InlineCompletionState {
5898 inlay_ids,
5899 completion,
5900 completion_id: inline_completion.id,
5901 invalidation_range,
5902 });
5903
5904 cx.notify();
5905
5906 Some(())
5907 }
5908
5909 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5910 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5911 }
5912
5913 fn render_code_actions_indicator(
5914 &self,
5915 _style: &EditorStyle,
5916 row: DisplayRow,
5917 is_active: bool,
5918 breakpoint: Option<&(Anchor, Breakpoint)>,
5919 cx: &mut Context<Self>,
5920 ) -> Option<IconButton> {
5921 let color = if breakpoint.is_some() {
5922 Color::Debugger
5923 } else {
5924 Color::Muted
5925 };
5926
5927 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
5928 let bp_kind = Arc::new(
5929 breakpoint
5930 .map(|(_, bp)| bp.kind.clone())
5931 .unwrap_or(BreakpointKind::Standard),
5932 );
5933
5934 if self.available_code_actions.is_some() {
5935 Some(
5936 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5937 .shape(ui::IconButtonShape::Square)
5938 .icon_size(IconSize::XSmall)
5939 .icon_color(color)
5940 .toggle_state(is_active)
5941 .tooltip({
5942 let focus_handle = self.focus_handle.clone();
5943 move |window, cx| {
5944 Tooltip::for_action_in(
5945 "Toggle Code Actions",
5946 &ToggleCodeActions {
5947 deployed_from_indicator: None,
5948 },
5949 &focus_handle,
5950 window,
5951 cx,
5952 )
5953 }
5954 })
5955 .on_click(cx.listener(move |editor, _e, window, cx| {
5956 window.focus(&editor.focus_handle(cx));
5957 editor.toggle_code_actions(
5958 &ToggleCodeActions {
5959 deployed_from_indicator: Some(row),
5960 },
5961 window,
5962 cx,
5963 );
5964 }))
5965 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
5966 editor.set_breakpoint_context_menu(
5967 row,
5968 position,
5969 bp_kind.clone(),
5970 event.down.position,
5971 window,
5972 cx,
5973 );
5974 })),
5975 )
5976 } else {
5977 None
5978 }
5979 }
5980
5981 fn clear_tasks(&mut self) {
5982 self.tasks.clear()
5983 }
5984
5985 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5986 if self.tasks.insert(key, value).is_some() {
5987 // This case should hopefully be rare, but just in case...
5988 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5989 }
5990 }
5991
5992 /// Get all display points of breakpoints that will be rendered within editor
5993 ///
5994 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
5995 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
5996 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
5997 fn active_breakpoints(
5998 &mut self,
5999 range: Range<DisplayRow>,
6000 window: &mut Window,
6001 cx: &mut Context<Self>,
6002 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6003 let mut breakpoint_display_points = HashMap::default();
6004
6005 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6006 return breakpoint_display_points;
6007 };
6008
6009 let snapshot = self.snapshot(window, cx);
6010
6011 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6012 let Some(project) = self.project.as_ref() else {
6013 return breakpoint_display_points;
6014 };
6015
6016 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6017 let buffer_snapshot = buffer.read(cx).snapshot();
6018
6019 for breakpoint in
6020 breakpoint_store
6021 .read(cx)
6022 .breakpoints(&buffer, None, buffer_snapshot.clone(), cx)
6023 {
6024 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6025 let anchor = multi_buffer_snapshot.anchor_before(point);
6026 breakpoint_display_points.insert(
6027 snapshot
6028 .point_to_display_point(
6029 MultiBufferPoint {
6030 row: point.row,
6031 column: point.column,
6032 },
6033 Bias::Left,
6034 )
6035 .row(),
6036 (anchor, breakpoint.1.clone()),
6037 );
6038 }
6039
6040 return breakpoint_display_points;
6041 }
6042
6043 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6044 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6045 for excerpt_boundary in multi_buffer_snapshot.excerpt_boundaries_in_range(range) {
6046 let info = excerpt_boundary.next;
6047
6048 let Some(excerpt_ranges) = multi_buffer_snapshot.range_for_excerpt(info.id) else {
6049 continue;
6050 };
6051
6052 let Some(buffer) =
6053 project.read_with(cx, |this, cx| this.buffer_for_id(info.buffer_id, cx))
6054 else {
6055 continue;
6056 };
6057
6058 let breakpoints = breakpoint_store.read(cx).breakpoints(
6059 &buffer,
6060 Some(info.range.context.start..info.range.context.end),
6061 info.buffer.clone(),
6062 cx,
6063 );
6064
6065 // To translate a breakpoint's position within a singular buffer to a multi buffer
6066 // position we need to know it's excerpt starting location, it's position within
6067 // the singular buffer, and if that position is within the excerpt's range.
6068 let excerpt_head = excerpt_ranges
6069 .start
6070 .to_display_point(&snapshot.display_snapshot);
6071
6072 let buffer_start = info
6073 .buffer
6074 .summary_for_anchor::<Point>(&info.range.context.start);
6075
6076 for (anchor, breakpoint) in breakpoints {
6077 let as_row = info.buffer.summary_for_anchor::<Point>(&anchor).row;
6078 let delta = as_row - buffer_start.row;
6079
6080 let position = excerpt_head + DisplayPoint::new(DisplayRow(delta), 0);
6081
6082 let anchor = snapshot.display_point_to_anchor(position, Bias::Left);
6083
6084 breakpoint_display_points.insert(position.row(), (anchor, breakpoint.clone()));
6085 }
6086 }
6087
6088 breakpoint_display_points
6089 }
6090
6091 fn breakpoint_context_menu(
6092 &self,
6093 anchor: Anchor,
6094 kind: Arc<BreakpointKind>,
6095 window: &mut Window,
6096 cx: &mut Context<Self>,
6097 ) -> Entity<ui::ContextMenu> {
6098 let weak_editor = cx.weak_entity();
6099 let focus_handle = self.focus_handle(cx);
6100
6101 let second_entry_msg = if kind.log_message().is_some() {
6102 "Edit Log Breakpoint"
6103 } else {
6104 "Add Log Breakpoint"
6105 };
6106
6107 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6108 menu.on_blur_subscription(Subscription::new(|| {}))
6109 .context(focus_handle)
6110 .entry("Toggle Breakpoint", None, {
6111 let weak_editor = weak_editor.clone();
6112 move |_window, cx| {
6113 weak_editor
6114 .update(cx, |this, cx| {
6115 this.edit_breakpoint_at_anchor(
6116 anchor,
6117 BreakpointKind::Standard,
6118 BreakpointEditAction::Toggle,
6119 cx,
6120 );
6121 })
6122 .log_err();
6123 }
6124 })
6125 .entry(second_entry_msg, None, move |window, cx| {
6126 weak_editor
6127 .update(cx, |this, cx| {
6128 this.add_edit_breakpoint_block(anchor, kind.as_ref(), window, cx);
6129 })
6130 .log_err();
6131 })
6132 })
6133 }
6134
6135 fn render_breakpoint(
6136 &self,
6137 position: Anchor,
6138 row: DisplayRow,
6139 kind: &BreakpointKind,
6140 cx: &mut Context<Self>,
6141 ) -> IconButton {
6142 let color = if self
6143 .gutter_breakpoint_indicator
6144 .is_some_and(|gutter_bp| gutter_bp.row() == row)
6145 {
6146 Color::Hint
6147 } else {
6148 Color::Debugger
6149 };
6150
6151 let icon = match &kind {
6152 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6153 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6154 };
6155 let arc_kind = Arc::new(kind.clone());
6156 let arc_kind2 = arc_kind.clone();
6157
6158 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6159 .icon_size(IconSize::XSmall)
6160 .size(ui::ButtonSize::None)
6161 .icon_color(color)
6162 .style(ButtonStyle::Transparent)
6163 .on_click(cx.listener(move |editor, _e, window, cx| {
6164 window.focus(&editor.focus_handle(cx));
6165 editor.edit_breakpoint_at_anchor(
6166 position,
6167 arc_kind.as_ref().clone(),
6168 BreakpointEditAction::Toggle,
6169 cx,
6170 );
6171 }))
6172 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6173 editor.set_breakpoint_context_menu(
6174 row,
6175 Some(position),
6176 arc_kind2.clone(),
6177 event.down.position,
6178 window,
6179 cx,
6180 );
6181 }))
6182 }
6183
6184 fn build_tasks_context(
6185 project: &Entity<Project>,
6186 buffer: &Entity<Buffer>,
6187 buffer_row: u32,
6188 tasks: &Arc<RunnableTasks>,
6189 cx: &mut Context<Self>,
6190 ) -> Task<Option<task::TaskContext>> {
6191 let position = Point::new(buffer_row, tasks.column);
6192 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6193 let location = Location {
6194 buffer: buffer.clone(),
6195 range: range_start..range_start,
6196 };
6197 // Fill in the environmental variables from the tree-sitter captures
6198 let mut captured_task_variables = TaskVariables::default();
6199 for (capture_name, value) in tasks.extra_variables.clone() {
6200 captured_task_variables.insert(
6201 task::VariableName::Custom(capture_name.into()),
6202 value.clone(),
6203 );
6204 }
6205 project.update(cx, |project, cx| {
6206 project.task_store().update(cx, |task_store, cx| {
6207 task_store.task_context_for_location(captured_task_variables, location, cx)
6208 })
6209 })
6210 }
6211
6212 pub fn spawn_nearest_task(
6213 &mut self,
6214 action: &SpawnNearestTask,
6215 window: &mut Window,
6216 cx: &mut Context<Self>,
6217 ) {
6218 let Some((workspace, _)) = self.workspace.clone() else {
6219 return;
6220 };
6221 let Some(project) = self.project.clone() else {
6222 return;
6223 };
6224
6225 // Try to find a closest, enclosing node using tree-sitter that has a
6226 // task
6227 let Some((buffer, buffer_row, tasks)) = self
6228 .find_enclosing_node_task(cx)
6229 // Or find the task that's closest in row-distance.
6230 .or_else(|| self.find_closest_task(cx))
6231 else {
6232 return;
6233 };
6234
6235 let reveal_strategy = action.reveal;
6236 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6237 cx.spawn_in(window, |_, mut cx| async move {
6238 let context = task_context.await?;
6239 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6240
6241 let resolved = resolved_task.resolved.as_mut()?;
6242 resolved.reveal = reveal_strategy;
6243
6244 workspace
6245 .update(&mut cx, |workspace, cx| {
6246 workspace::tasks::schedule_resolved_task(
6247 workspace,
6248 task_source_kind,
6249 resolved_task,
6250 false,
6251 cx,
6252 );
6253 })
6254 .ok()
6255 })
6256 .detach();
6257 }
6258
6259 fn find_closest_task(
6260 &mut self,
6261 cx: &mut Context<Self>,
6262 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6263 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6264
6265 let ((buffer_id, row), tasks) = self
6266 .tasks
6267 .iter()
6268 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6269
6270 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6271 let tasks = Arc::new(tasks.to_owned());
6272 Some((buffer, *row, tasks))
6273 }
6274
6275 fn find_enclosing_node_task(
6276 &mut self,
6277 cx: &mut Context<Self>,
6278 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6279 let snapshot = self.buffer.read(cx).snapshot(cx);
6280 let offset = self.selections.newest::<usize>(cx).head();
6281 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6282 let buffer_id = excerpt.buffer().remote_id();
6283
6284 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6285 let mut cursor = layer.node().walk();
6286
6287 while cursor.goto_first_child_for_byte(offset).is_some() {
6288 if cursor.node().end_byte() == offset {
6289 cursor.goto_next_sibling();
6290 }
6291 }
6292
6293 // Ascend to the smallest ancestor that contains the range and has a task.
6294 loop {
6295 let node = cursor.node();
6296 let node_range = node.byte_range();
6297 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6298
6299 // Check if this node contains our offset
6300 if node_range.start <= offset && node_range.end >= offset {
6301 // If it contains offset, check for task
6302 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6303 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6304 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6305 }
6306 }
6307
6308 if !cursor.goto_parent() {
6309 break;
6310 }
6311 }
6312 None
6313 }
6314
6315 fn render_run_indicator(
6316 &self,
6317 _style: &EditorStyle,
6318 is_active: bool,
6319 row: DisplayRow,
6320 breakpoint: Option<(Anchor, Breakpoint)>,
6321 cx: &mut Context<Self>,
6322 ) -> IconButton {
6323 let color = if breakpoint.is_some() {
6324 Color::Debugger
6325 } else {
6326 Color::Muted
6327 };
6328
6329 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6330 let bp_kind = Arc::new(
6331 breakpoint
6332 .map(|(_, bp)| bp.kind)
6333 .unwrap_or(BreakpointKind::Standard),
6334 );
6335
6336 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6337 .shape(ui::IconButtonShape::Square)
6338 .icon_size(IconSize::XSmall)
6339 .icon_color(color)
6340 .toggle_state(is_active)
6341 .on_click(cx.listener(move |editor, _e, window, cx| {
6342 window.focus(&editor.focus_handle(cx));
6343 editor.toggle_code_actions(
6344 &ToggleCodeActions {
6345 deployed_from_indicator: Some(row),
6346 },
6347 window,
6348 cx,
6349 );
6350 }))
6351 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6352 editor.set_breakpoint_context_menu(
6353 row,
6354 position,
6355 bp_kind.clone(),
6356 event.down.position,
6357 window,
6358 cx,
6359 );
6360 }))
6361 }
6362
6363 pub fn context_menu_visible(&self) -> bool {
6364 !self.edit_prediction_preview_is_active()
6365 && self
6366 .context_menu
6367 .borrow()
6368 .as_ref()
6369 .map_or(false, |menu| menu.visible())
6370 }
6371
6372 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6373 self.context_menu
6374 .borrow()
6375 .as_ref()
6376 .map(|menu| menu.origin())
6377 }
6378
6379 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6380 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6381
6382 fn render_edit_prediction_popover(
6383 &mut self,
6384 text_bounds: &Bounds<Pixels>,
6385 content_origin: gpui::Point<Pixels>,
6386 editor_snapshot: &EditorSnapshot,
6387 visible_row_range: Range<DisplayRow>,
6388 scroll_top: f32,
6389 scroll_bottom: f32,
6390 line_layouts: &[LineWithInvisibles],
6391 line_height: Pixels,
6392 scroll_pixel_position: gpui::Point<Pixels>,
6393 newest_selection_head: Option<DisplayPoint>,
6394 editor_width: Pixels,
6395 style: &EditorStyle,
6396 window: &mut Window,
6397 cx: &mut App,
6398 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6399 let active_inline_completion = self.active_inline_completion.as_ref()?;
6400
6401 if self.edit_prediction_visible_in_cursor_popover(true) {
6402 return None;
6403 }
6404
6405 match &active_inline_completion.completion {
6406 InlineCompletion::Move { target, .. } => {
6407 let target_display_point = target.to_display_point(editor_snapshot);
6408
6409 if self.edit_prediction_requires_modifier() {
6410 if !self.edit_prediction_preview_is_active() {
6411 return None;
6412 }
6413
6414 self.render_edit_prediction_modifier_jump_popover(
6415 text_bounds,
6416 content_origin,
6417 visible_row_range,
6418 line_layouts,
6419 line_height,
6420 scroll_pixel_position,
6421 newest_selection_head,
6422 target_display_point,
6423 window,
6424 cx,
6425 )
6426 } else {
6427 self.render_edit_prediction_eager_jump_popover(
6428 text_bounds,
6429 content_origin,
6430 editor_snapshot,
6431 visible_row_range,
6432 scroll_top,
6433 scroll_bottom,
6434 line_height,
6435 scroll_pixel_position,
6436 target_display_point,
6437 editor_width,
6438 window,
6439 cx,
6440 )
6441 }
6442 }
6443 InlineCompletion::Edit {
6444 display_mode: EditDisplayMode::Inline,
6445 ..
6446 } => None,
6447 InlineCompletion::Edit {
6448 display_mode: EditDisplayMode::TabAccept,
6449 edits,
6450 ..
6451 } => {
6452 let range = &edits.first()?.0;
6453 let target_display_point = range.end.to_display_point(editor_snapshot);
6454
6455 self.render_edit_prediction_end_of_line_popover(
6456 "Accept",
6457 editor_snapshot,
6458 visible_row_range,
6459 target_display_point,
6460 line_height,
6461 scroll_pixel_position,
6462 content_origin,
6463 editor_width,
6464 window,
6465 cx,
6466 )
6467 }
6468 InlineCompletion::Edit {
6469 edits,
6470 edit_preview,
6471 display_mode: EditDisplayMode::DiffPopover,
6472 snapshot,
6473 } => self.render_edit_prediction_diff_popover(
6474 text_bounds,
6475 content_origin,
6476 editor_snapshot,
6477 visible_row_range,
6478 line_layouts,
6479 line_height,
6480 scroll_pixel_position,
6481 newest_selection_head,
6482 editor_width,
6483 style,
6484 edits,
6485 edit_preview,
6486 snapshot,
6487 window,
6488 cx,
6489 ),
6490 }
6491 }
6492
6493 fn render_edit_prediction_modifier_jump_popover(
6494 &mut self,
6495 text_bounds: &Bounds<Pixels>,
6496 content_origin: gpui::Point<Pixels>,
6497 visible_row_range: Range<DisplayRow>,
6498 line_layouts: &[LineWithInvisibles],
6499 line_height: Pixels,
6500 scroll_pixel_position: gpui::Point<Pixels>,
6501 newest_selection_head: Option<DisplayPoint>,
6502 target_display_point: DisplayPoint,
6503 window: &mut Window,
6504 cx: &mut App,
6505 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6506 let scrolled_content_origin =
6507 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6508
6509 const SCROLL_PADDING_Y: Pixels = px(12.);
6510
6511 if target_display_point.row() < visible_row_range.start {
6512 return self.render_edit_prediction_scroll_popover(
6513 |_| SCROLL_PADDING_Y,
6514 IconName::ArrowUp,
6515 visible_row_range,
6516 line_layouts,
6517 newest_selection_head,
6518 scrolled_content_origin,
6519 window,
6520 cx,
6521 );
6522 } else if target_display_point.row() >= visible_row_range.end {
6523 return self.render_edit_prediction_scroll_popover(
6524 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6525 IconName::ArrowDown,
6526 visible_row_range,
6527 line_layouts,
6528 newest_selection_head,
6529 scrolled_content_origin,
6530 window,
6531 cx,
6532 );
6533 }
6534
6535 const POLE_WIDTH: Pixels = px(2.);
6536
6537 let line_layout =
6538 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6539 let target_column = target_display_point.column() as usize;
6540
6541 let target_x = line_layout.x_for_index(target_column);
6542 let target_y =
6543 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6544
6545 let flag_on_right = target_x < text_bounds.size.width / 2.;
6546
6547 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6548 border_color.l += 0.001;
6549
6550 let mut element = v_flex()
6551 .items_end()
6552 .when(flag_on_right, |el| el.items_start())
6553 .child(if flag_on_right {
6554 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6555 .rounded_bl(px(0.))
6556 .rounded_tl(px(0.))
6557 .border_l_2()
6558 .border_color(border_color)
6559 } else {
6560 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6561 .rounded_br(px(0.))
6562 .rounded_tr(px(0.))
6563 .border_r_2()
6564 .border_color(border_color)
6565 })
6566 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6567 .into_any();
6568
6569 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6570
6571 let mut origin = scrolled_content_origin + point(target_x, target_y)
6572 - point(
6573 if flag_on_right {
6574 POLE_WIDTH
6575 } else {
6576 size.width - POLE_WIDTH
6577 },
6578 size.height - line_height,
6579 );
6580
6581 origin.x = origin.x.max(content_origin.x);
6582
6583 element.prepaint_at(origin, window, cx);
6584
6585 Some((element, origin))
6586 }
6587
6588 fn render_edit_prediction_scroll_popover(
6589 &mut self,
6590 to_y: impl Fn(Size<Pixels>) -> Pixels,
6591 scroll_icon: IconName,
6592 visible_row_range: Range<DisplayRow>,
6593 line_layouts: &[LineWithInvisibles],
6594 newest_selection_head: Option<DisplayPoint>,
6595 scrolled_content_origin: gpui::Point<Pixels>,
6596 window: &mut Window,
6597 cx: &mut App,
6598 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6599 let mut element = self
6600 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6601 .into_any();
6602
6603 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6604
6605 let cursor = newest_selection_head?;
6606 let cursor_row_layout =
6607 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6608 let cursor_column = cursor.column() as usize;
6609
6610 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6611
6612 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6613
6614 element.prepaint_at(origin, window, cx);
6615 Some((element, origin))
6616 }
6617
6618 fn render_edit_prediction_eager_jump_popover(
6619 &mut self,
6620 text_bounds: &Bounds<Pixels>,
6621 content_origin: gpui::Point<Pixels>,
6622 editor_snapshot: &EditorSnapshot,
6623 visible_row_range: Range<DisplayRow>,
6624 scroll_top: f32,
6625 scroll_bottom: f32,
6626 line_height: Pixels,
6627 scroll_pixel_position: gpui::Point<Pixels>,
6628 target_display_point: DisplayPoint,
6629 editor_width: Pixels,
6630 window: &mut Window,
6631 cx: &mut App,
6632 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6633 if target_display_point.row().as_f32() < scroll_top {
6634 let mut element = self
6635 .render_edit_prediction_line_popover(
6636 "Jump to Edit",
6637 Some(IconName::ArrowUp),
6638 window,
6639 cx,
6640 )?
6641 .into_any();
6642
6643 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6644 let offset = point(
6645 (text_bounds.size.width - size.width) / 2.,
6646 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6647 );
6648
6649 let origin = text_bounds.origin + offset;
6650 element.prepaint_at(origin, window, cx);
6651 Some((element, origin))
6652 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6653 let mut element = self
6654 .render_edit_prediction_line_popover(
6655 "Jump to Edit",
6656 Some(IconName::ArrowDown),
6657 window,
6658 cx,
6659 )?
6660 .into_any();
6661
6662 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6663 let offset = point(
6664 (text_bounds.size.width - size.width) / 2.,
6665 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6666 );
6667
6668 let origin = text_bounds.origin + offset;
6669 element.prepaint_at(origin, window, cx);
6670 Some((element, origin))
6671 } else {
6672 self.render_edit_prediction_end_of_line_popover(
6673 "Jump to Edit",
6674 editor_snapshot,
6675 visible_row_range,
6676 target_display_point,
6677 line_height,
6678 scroll_pixel_position,
6679 content_origin,
6680 editor_width,
6681 window,
6682 cx,
6683 )
6684 }
6685 }
6686
6687 fn render_edit_prediction_end_of_line_popover(
6688 self: &mut Editor,
6689 label: &'static str,
6690 editor_snapshot: &EditorSnapshot,
6691 visible_row_range: Range<DisplayRow>,
6692 target_display_point: DisplayPoint,
6693 line_height: Pixels,
6694 scroll_pixel_position: gpui::Point<Pixels>,
6695 content_origin: gpui::Point<Pixels>,
6696 editor_width: Pixels,
6697 window: &mut Window,
6698 cx: &mut App,
6699 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6700 let target_line_end = DisplayPoint::new(
6701 target_display_point.row(),
6702 editor_snapshot.line_len(target_display_point.row()),
6703 );
6704
6705 let mut element = self
6706 .render_edit_prediction_line_popover(label, None, window, cx)?
6707 .into_any();
6708
6709 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6710
6711 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6712
6713 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6714 let mut origin = start_point
6715 + line_origin
6716 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6717 origin.x = origin.x.max(content_origin.x);
6718
6719 let max_x = content_origin.x + editor_width - size.width;
6720
6721 if origin.x > max_x {
6722 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6723
6724 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6725 origin.y += offset;
6726 IconName::ArrowUp
6727 } else {
6728 origin.y -= offset;
6729 IconName::ArrowDown
6730 };
6731
6732 element = self
6733 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6734 .into_any();
6735
6736 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6737
6738 origin.x = content_origin.x + editor_width - size.width - px(2.);
6739 }
6740
6741 element.prepaint_at(origin, window, cx);
6742 Some((element, origin))
6743 }
6744
6745 fn render_edit_prediction_diff_popover(
6746 self: &Editor,
6747 text_bounds: &Bounds<Pixels>,
6748 content_origin: gpui::Point<Pixels>,
6749 editor_snapshot: &EditorSnapshot,
6750 visible_row_range: Range<DisplayRow>,
6751 line_layouts: &[LineWithInvisibles],
6752 line_height: Pixels,
6753 scroll_pixel_position: gpui::Point<Pixels>,
6754 newest_selection_head: Option<DisplayPoint>,
6755 editor_width: Pixels,
6756 style: &EditorStyle,
6757 edits: &Vec<(Range<Anchor>, String)>,
6758 edit_preview: &Option<language::EditPreview>,
6759 snapshot: &language::BufferSnapshot,
6760 window: &mut Window,
6761 cx: &mut App,
6762 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6763 let edit_start = edits
6764 .first()
6765 .unwrap()
6766 .0
6767 .start
6768 .to_display_point(editor_snapshot);
6769 let edit_end = edits
6770 .last()
6771 .unwrap()
6772 .0
6773 .end
6774 .to_display_point(editor_snapshot);
6775
6776 let is_visible = visible_row_range.contains(&edit_start.row())
6777 || visible_row_range.contains(&edit_end.row());
6778 if !is_visible {
6779 return None;
6780 }
6781
6782 let highlighted_edits =
6783 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6784
6785 let styled_text = highlighted_edits.to_styled_text(&style.text);
6786 let line_count = highlighted_edits.text.lines().count();
6787
6788 const BORDER_WIDTH: Pixels = px(1.);
6789
6790 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6791 let has_keybind = keybind.is_some();
6792
6793 let mut element = h_flex()
6794 .items_start()
6795 .child(
6796 h_flex()
6797 .bg(cx.theme().colors().editor_background)
6798 .border(BORDER_WIDTH)
6799 .shadow_sm()
6800 .border_color(cx.theme().colors().border)
6801 .rounded_l_lg()
6802 .when(line_count > 1, |el| el.rounded_br_lg())
6803 .pr_1()
6804 .child(styled_text),
6805 )
6806 .child(
6807 h_flex()
6808 .h(line_height + BORDER_WIDTH * px(2.))
6809 .px_1p5()
6810 .gap_1()
6811 // Workaround: For some reason, there's a gap if we don't do this
6812 .ml(-BORDER_WIDTH)
6813 .shadow(smallvec![gpui::BoxShadow {
6814 color: gpui::black().opacity(0.05),
6815 offset: point(px(1.), px(1.)),
6816 blur_radius: px(2.),
6817 spread_radius: px(0.),
6818 }])
6819 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6820 .border(BORDER_WIDTH)
6821 .border_color(cx.theme().colors().border)
6822 .rounded_r_lg()
6823 .id("edit_prediction_diff_popover_keybind")
6824 .when(!has_keybind, |el| {
6825 let status_colors = cx.theme().status();
6826
6827 el.bg(status_colors.error_background)
6828 .border_color(status_colors.error.opacity(0.6))
6829 .child(Icon::new(IconName::Info).color(Color::Error))
6830 .cursor_default()
6831 .hoverable_tooltip(move |_window, cx| {
6832 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6833 })
6834 })
6835 .children(keybind),
6836 )
6837 .into_any();
6838
6839 let longest_row =
6840 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6841 let longest_line_width = if visible_row_range.contains(&longest_row) {
6842 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6843 } else {
6844 layout_line(
6845 longest_row,
6846 editor_snapshot,
6847 style,
6848 editor_width,
6849 |_| false,
6850 window,
6851 cx,
6852 )
6853 .width
6854 };
6855
6856 let viewport_bounds =
6857 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6858 right: -EditorElement::SCROLLBAR_WIDTH,
6859 ..Default::default()
6860 });
6861
6862 let x_after_longest =
6863 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6864 - scroll_pixel_position.x;
6865
6866 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6867
6868 // Fully visible if it can be displayed within the window (allow overlapping other
6869 // panes). However, this is only allowed if the popover starts within text_bounds.
6870 let can_position_to_the_right = x_after_longest < text_bounds.right()
6871 && x_after_longest + element_bounds.width < viewport_bounds.right();
6872
6873 let mut origin = if can_position_to_the_right {
6874 point(
6875 x_after_longest,
6876 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6877 - scroll_pixel_position.y,
6878 )
6879 } else {
6880 let cursor_row = newest_selection_head.map(|head| head.row());
6881 let above_edit = edit_start
6882 .row()
6883 .0
6884 .checked_sub(line_count as u32)
6885 .map(DisplayRow);
6886 let below_edit = Some(edit_end.row() + 1);
6887 let above_cursor =
6888 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6889 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6890
6891 // Place the edit popover adjacent to the edit if there is a location
6892 // available that is onscreen and does not obscure the cursor. Otherwise,
6893 // place it adjacent to the cursor.
6894 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6895 .into_iter()
6896 .flatten()
6897 .find(|&start_row| {
6898 let end_row = start_row + line_count as u32;
6899 visible_row_range.contains(&start_row)
6900 && visible_row_range.contains(&end_row)
6901 && cursor_row.map_or(true, |cursor_row| {
6902 !((start_row..end_row).contains(&cursor_row))
6903 })
6904 })?;
6905
6906 content_origin
6907 + point(
6908 -scroll_pixel_position.x,
6909 row_target.as_f32() * line_height - scroll_pixel_position.y,
6910 )
6911 };
6912
6913 origin.x -= BORDER_WIDTH;
6914
6915 window.defer_draw(element, origin, 1);
6916
6917 // Do not return an element, since it will already be drawn due to defer_draw.
6918 None
6919 }
6920
6921 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6922 px(30.)
6923 }
6924
6925 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6926 if self.read_only(cx) {
6927 cx.theme().players().read_only()
6928 } else {
6929 self.style.as_ref().unwrap().local_player
6930 }
6931 }
6932
6933 fn render_edit_prediction_accept_keybind(
6934 &self,
6935 window: &mut Window,
6936 cx: &App,
6937 ) -> Option<AnyElement> {
6938 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6939 let accept_keystroke = accept_binding.keystroke()?;
6940
6941 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6942
6943 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6944 Color::Accent
6945 } else {
6946 Color::Muted
6947 };
6948
6949 h_flex()
6950 .px_0p5()
6951 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6952 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6953 .text_size(TextSize::XSmall.rems(cx))
6954 .child(h_flex().children(ui::render_modifiers(
6955 &accept_keystroke.modifiers,
6956 PlatformStyle::platform(),
6957 Some(modifiers_color),
6958 Some(IconSize::XSmall.rems().into()),
6959 true,
6960 )))
6961 .when(is_platform_style_mac, |parent| {
6962 parent.child(accept_keystroke.key.clone())
6963 })
6964 .when(!is_platform_style_mac, |parent| {
6965 parent.child(
6966 Key::new(
6967 util::capitalize(&accept_keystroke.key),
6968 Some(Color::Default),
6969 )
6970 .size(Some(IconSize::XSmall.rems().into())),
6971 )
6972 })
6973 .into_any()
6974 .into()
6975 }
6976
6977 fn render_edit_prediction_line_popover(
6978 &self,
6979 label: impl Into<SharedString>,
6980 icon: Option<IconName>,
6981 window: &mut Window,
6982 cx: &App,
6983 ) -> Option<Stateful<Div>> {
6984 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6985
6986 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6987 let has_keybind = keybind.is_some();
6988
6989 let result = h_flex()
6990 .id("ep-line-popover")
6991 .py_0p5()
6992 .pl_1()
6993 .pr(padding_right)
6994 .gap_1()
6995 .rounded_md()
6996 .border_1()
6997 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6998 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6999 .shadow_sm()
7000 .when(!has_keybind, |el| {
7001 let status_colors = cx.theme().status();
7002
7003 el.bg(status_colors.error_background)
7004 .border_color(status_colors.error.opacity(0.6))
7005 .pl_2()
7006 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7007 .cursor_default()
7008 .hoverable_tooltip(move |_window, cx| {
7009 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7010 })
7011 })
7012 .children(keybind)
7013 .child(
7014 Label::new(label)
7015 .size(LabelSize::Small)
7016 .when(!has_keybind, |el| {
7017 el.color(cx.theme().status().error.into()).strikethrough()
7018 }),
7019 )
7020 .when(!has_keybind, |el| {
7021 el.child(
7022 h_flex().ml_1().child(
7023 Icon::new(IconName::Info)
7024 .size(IconSize::Small)
7025 .color(cx.theme().status().error.into()),
7026 ),
7027 )
7028 })
7029 .when_some(icon, |element, icon| {
7030 element.child(
7031 div()
7032 .mt(px(1.5))
7033 .child(Icon::new(icon).size(IconSize::Small)),
7034 )
7035 });
7036
7037 Some(result)
7038 }
7039
7040 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7041 let accent_color = cx.theme().colors().text_accent;
7042 let editor_bg_color = cx.theme().colors().editor_background;
7043 editor_bg_color.blend(accent_color.opacity(0.1))
7044 }
7045
7046 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7047 let accent_color = cx.theme().colors().text_accent;
7048 let editor_bg_color = cx.theme().colors().editor_background;
7049 editor_bg_color.blend(accent_color.opacity(0.6))
7050 }
7051
7052 fn render_edit_prediction_cursor_popover(
7053 &self,
7054 min_width: Pixels,
7055 max_width: Pixels,
7056 cursor_point: Point,
7057 style: &EditorStyle,
7058 accept_keystroke: Option<&gpui::Keystroke>,
7059 _window: &Window,
7060 cx: &mut Context<Editor>,
7061 ) -> Option<AnyElement> {
7062 let provider = self.edit_prediction_provider.as_ref()?;
7063
7064 if provider.provider.needs_terms_acceptance(cx) {
7065 return Some(
7066 h_flex()
7067 .min_w(min_width)
7068 .flex_1()
7069 .px_2()
7070 .py_1()
7071 .gap_3()
7072 .elevation_2(cx)
7073 .hover(|style| style.bg(cx.theme().colors().element_hover))
7074 .id("accept-terms")
7075 .cursor_pointer()
7076 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7077 .on_click(cx.listener(|this, _event, window, cx| {
7078 cx.stop_propagation();
7079 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7080 window.dispatch_action(
7081 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7082 cx,
7083 );
7084 }))
7085 .child(
7086 h_flex()
7087 .flex_1()
7088 .gap_2()
7089 .child(Icon::new(IconName::ZedPredict))
7090 .child(Label::new("Accept Terms of Service"))
7091 .child(div().w_full())
7092 .child(
7093 Icon::new(IconName::ArrowUpRight)
7094 .color(Color::Muted)
7095 .size(IconSize::Small),
7096 )
7097 .into_any_element(),
7098 )
7099 .into_any(),
7100 );
7101 }
7102
7103 let is_refreshing = provider.provider.is_refreshing(cx);
7104
7105 fn pending_completion_container() -> Div {
7106 h_flex()
7107 .h_full()
7108 .flex_1()
7109 .gap_2()
7110 .child(Icon::new(IconName::ZedPredict))
7111 }
7112
7113 let completion = match &self.active_inline_completion {
7114 Some(prediction) => {
7115 if !self.has_visible_completions_menu() {
7116 const RADIUS: Pixels = px(6.);
7117 const BORDER_WIDTH: Pixels = px(1.);
7118
7119 return Some(
7120 h_flex()
7121 .elevation_2(cx)
7122 .border(BORDER_WIDTH)
7123 .border_color(cx.theme().colors().border)
7124 .when(accept_keystroke.is_none(), |el| {
7125 el.border_color(cx.theme().status().error)
7126 })
7127 .rounded(RADIUS)
7128 .rounded_tl(px(0.))
7129 .overflow_hidden()
7130 .child(div().px_1p5().child(match &prediction.completion {
7131 InlineCompletion::Move { target, snapshot } => {
7132 use text::ToPoint as _;
7133 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7134 {
7135 Icon::new(IconName::ZedPredictDown)
7136 } else {
7137 Icon::new(IconName::ZedPredictUp)
7138 }
7139 }
7140 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7141 }))
7142 .child(
7143 h_flex()
7144 .gap_1()
7145 .py_1()
7146 .px_2()
7147 .rounded_r(RADIUS - BORDER_WIDTH)
7148 .border_l_1()
7149 .border_color(cx.theme().colors().border)
7150 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7151 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7152 el.child(
7153 Label::new("Hold")
7154 .size(LabelSize::Small)
7155 .when(accept_keystroke.is_none(), |el| {
7156 el.strikethrough()
7157 })
7158 .line_height_style(LineHeightStyle::UiLabel),
7159 )
7160 })
7161 .id("edit_prediction_cursor_popover_keybind")
7162 .when(accept_keystroke.is_none(), |el| {
7163 let status_colors = cx.theme().status();
7164
7165 el.bg(status_colors.error_background)
7166 .border_color(status_colors.error.opacity(0.6))
7167 .child(Icon::new(IconName::Info).color(Color::Error))
7168 .cursor_default()
7169 .hoverable_tooltip(move |_window, cx| {
7170 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7171 .into()
7172 })
7173 })
7174 .when_some(
7175 accept_keystroke.as_ref(),
7176 |el, accept_keystroke| {
7177 el.child(h_flex().children(ui::render_modifiers(
7178 &accept_keystroke.modifiers,
7179 PlatformStyle::platform(),
7180 Some(Color::Default),
7181 Some(IconSize::XSmall.rems().into()),
7182 false,
7183 )))
7184 },
7185 ),
7186 )
7187 .into_any(),
7188 );
7189 }
7190
7191 self.render_edit_prediction_cursor_popover_preview(
7192 prediction,
7193 cursor_point,
7194 style,
7195 cx,
7196 )?
7197 }
7198
7199 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7200 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7201 stale_completion,
7202 cursor_point,
7203 style,
7204 cx,
7205 )?,
7206
7207 None => {
7208 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7209 }
7210 },
7211
7212 None => pending_completion_container().child(Label::new("No Prediction")),
7213 };
7214
7215 let completion = if is_refreshing {
7216 completion
7217 .with_animation(
7218 "loading-completion",
7219 Animation::new(Duration::from_secs(2))
7220 .repeat()
7221 .with_easing(pulsating_between(0.4, 0.8)),
7222 |label, delta| label.opacity(delta),
7223 )
7224 .into_any_element()
7225 } else {
7226 completion.into_any_element()
7227 };
7228
7229 let has_completion = self.active_inline_completion.is_some();
7230
7231 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7232 Some(
7233 h_flex()
7234 .min_w(min_width)
7235 .max_w(max_width)
7236 .flex_1()
7237 .elevation_2(cx)
7238 .border_color(cx.theme().colors().border)
7239 .child(
7240 div()
7241 .flex_1()
7242 .py_1()
7243 .px_2()
7244 .overflow_hidden()
7245 .child(completion),
7246 )
7247 .when_some(accept_keystroke, |el, accept_keystroke| {
7248 if !accept_keystroke.modifiers.modified() {
7249 return el;
7250 }
7251
7252 el.child(
7253 h_flex()
7254 .h_full()
7255 .border_l_1()
7256 .rounded_r_lg()
7257 .border_color(cx.theme().colors().border)
7258 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7259 .gap_1()
7260 .py_1()
7261 .px_2()
7262 .child(
7263 h_flex()
7264 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7265 .when(is_platform_style_mac, |parent| parent.gap_1())
7266 .child(h_flex().children(ui::render_modifiers(
7267 &accept_keystroke.modifiers,
7268 PlatformStyle::platform(),
7269 Some(if !has_completion {
7270 Color::Muted
7271 } else {
7272 Color::Default
7273 }),
7274 None,
7275 false,
7276 ))),
7277 )
7278 .child(Label::new("Preview").into_any_element())
7279 .opacity(if has_completion { 1.0 } else { 0.4 }),
7280 )
7281 })
7282 .into_any(),
7283 )
7284 }
7285
7286 fn render_edit_prediction_cursor_popover_preview(
7287 &self,
7288 completion: &InlineCompletionState,
7289 cursor_point: Point,
7290 style: &EditorStyle,
7291 cx: &mut Context<Editor>,
7292 ) -> Option<Div> {
7293 use text::ToPoint as _;
7294
7295 fn render_relative_row_jump(
7296 prefix: impl Into<String>,
7297 current_row: u32,
7298 target_row: u32,
7299 ) -> Div {
7300 let (row_diff, arrow) = if target_row < current_row {
7301 (current_row - target_row, IconName::ArrowUp)
7302 } else {
7303 (target_row - current_row, IconName::ArrowDown)
7304 };
7305
7306 h_flex()
7307 .child(
7308 Label::new(format!("{}{}", prefix.into(), row_diff))
7309 .color(Color::Muted)
7310 .size(LabelSize::Small),
7311 )
7312 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7313 }
7314
7315 match &completion.completion {
7316 InlineCompletion::Move {
7317 target, snapshot, ..
7318 } => Some(
7319 h_flex()
7320 .px_2()
7321 .gap_2()
7322 .flex_1()
7323 .child(
7324 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7325 Icon::new(IconName::ZedPredictDown)
7326 } else {
7327 Icon::new(IconName::ZedPredictUp)
7328 },
7329 )
7330 .child(Label::new("Jump to Edit")),
7331 ),
7332
7333 InlineCompletion::Edit {
7334 edits,
7335 edit_preview,
7336 snapshot,
7337 display_mode: _,
7338 } => {
7339 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7340
7341 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7342 &snapshot,
7343 &edits,
7344 edit_preview.as_ref()?,
7345 true,
7346 cx,
7347 )
7348 .first_line_preview();
7349
7350 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7351 .with_default_highlights(&style.text, highlighted_edits.highlights);
7352
7353 let preview = h_flex()
7354 .gap_1()
7355 .min_w_16()
7356 .child(styled_text)
7357 .when(has_more_lines, |parent| parent.child("…"));
7358
7359 let left = if first_edit_row != cursor_point.row {
7360 render_relative_row_jump("", cursor_point.row, first_edit_row)
7361 .into_any_element()
7362 } else {
7363 Icon::new(IconName::ZedPredict).into_any_element()
7364 };
7365
7366 Some(
7367 h_flex()
7368 .h_full()
7369 .flex_1()
7370 .gap_2()
7371 .pr_1()
7372 .overflow_x_hidden()
7373 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7374 .child(left)
7375 .child(preview),
7376 )
7377 }
7378 }
7379 }
7380
7381 fn render_context_menu(
7382 &self,
7383 style: &EditorStyle,
7384 max_height_in_lines: u32,
7385 y_flipped: bool,
7386 window: &mut Window,
7387 cx: &mut Context<Editor>,
7388 ) -> Option<AnyElement> {
7389 let menu = self.context_menu.borrow();
7390 let menu = menu.as_ref()?;
7391 if !menu.visible() {
7392 return None;
7393 };
7394 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7395 }
7396
7397 fn render_context_menu_aside(
7398 &mut self,
7399 max_size: Size<Pixels>,
7400 window: &mut Window,
7401 cx: &mut Context<Editor>,
7402 ) -> Option<AnyElement> {
7403 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7404 if menu.visible() {
7405 menu.render_aside(self, max_size, window, cx)
7406 } else {
7407 None
7408 }
7409 })
7410 }
7411
7412 fn hide_context_menu(
7413 &mut self,
7414 window: &mut Window,
7415 cx: &mut Context<Self>,
7416 ) -> Option<CodeContextMenu> {
7417 cx.notify();
7418 self.completion_tasks.clear();
7419 let context_menu = self.context_menu.borrow_mut().take();
7420 self.stale_inline_completion_in_menu.take();
7421 self.update_visible_inline_completion(window, cx);
7422 context_menu
7423 }
7424
7425 fn show_snippet_choices(
7426 &mut self,
7427 choices: &Vec<String>,
7428 selection: Range<Anchor>,
7429 cx: &mut Context<Self>,
7430 ) {
7431 if selection.start.buffer_id.is_none() {
7432 return;
7433 }
7434 let buffer_id = selection.start.buffer_id.unwrap();
7435 let buffer = self.buffer().read(cx).buffer(buffer_id);
7436 let id = post_inc(&mut self.next_completion_id);
7437
7438 if let Some(buffer) = buffer {
7439 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7440 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7441 ));
7442 }
7443 }
7444
7445 pub fn insert_snippet(
7446 &mut self,
7447 insertion_ranges: &[Range<usize>],
7448 snippet: Snippet,
7449 window: &mut Window,
7450 cx: &mut Context<Self>,
7451 ) -> Result<()> {
7452 struct Tabstop<T> {
7453 is_end_tabstop: bool,
7454 ranges: Vec<Range<T>>,
7455 choices: Option<Vec<String>>,
7456 }
7457
7458 let tabstops = self.buffer.update(cx, |buffer, cx| {
7459 let snippet_text: Arc<str> = snippet.text.clone().into();
7460 buffer.edit(
7461 insertion_ranges
7462 .iter()
7463 .cloned()
7464 .map(|range| (range, snippet_text.clone())),
7465 Some(AutoindentMode::EachLine),
7466 cx,
7467 );
7468
7469 let snapshot = &*buffer.read(cx);
7470 let snippet = &snippet;
7471 snippet
7472 .tabstops
7473 .iter()
7474 .map(|tabstop| {
7475 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7476 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7477 });
7478 let mut tabstop_ranges = tabstop
7479 .ranges
7480 .iter()
7481 .flat_map(|tabstop_range| {
7482 let mut delta = 0_isize;
7483 insertion_ranges.iter().map(move |insertion_range| {
7484 let insertion_start = insertion_range.start as isize + delta;
7485 delta +=
7486 snippet.text.len() as isize - insertion_range.len() as isize;
7487
7488 let start = ((insertion_start + tabstop_range.start) as usize)
7489 .min(snapshot.len());
7490 let end = ((insertion_start + tabstop_range.end) as usize)
7491 .min(snapshot.len());
7492 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7493 })
7494 })
7495 .collect::<Vec<_>>();
7496 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7497
7498 Tabstop {
7499 is_end_tabstop,
7500 ranges: tabstop_ranges,
7501 choices: tabstop.choices.clone(),
7502 }
7503 })
7504 .collect::<Vec<_>>()
7505 });
7506 if let Some(tabstop) = tabstops.first() {
7507 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7508 s.select_ranges(tabstop.ranges.iter().cloned());
7509 });
7510
7511 if let Some(choices) = &tabstop.choices {
7512 if let Some(selection) = tabstop.ranges.first() {
7513 self.show_snippet_choices(choices, selection.clone(), cx)
7514 }
7515 }
7516
7517 // If we're already at the last tabstop and it's at the end of the snippet,
7518 // we're done, we don't need to keep the state around.
7519 if !tabstop.is_end_tabstop {
7520 let choices = tabstops
7521 .iter()
7522 .map(|tabstop| tabstop.choices.clone())
7523 .collect();
7524
7525 let ranges = tabstops
7526 .into_iter()
7527 .map(|tabstop| tabstop.ranges)
7528 .collect::<Vec<_>>();
7529
7530 self.snippet_stack.push(SnippetState {
7531 active_index: 0,
7532 ranges,
7533 choices,
7534 });
7535 }
7536
7537 // Check whether the just-entered snippet ends with an auto-closable bracket.
7538 if self.autoclose_regions.is_empty() {
7539 let snapshot = self.buffer.read(cx).snapshot(cx);
7540 for selection in &mut self.selections.all::<Point>(cx) {
7541 let selection_head = selection.head();
7542 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7543 continue;
7544 };
7545
7546 let mut bracket_pair = None;
7547 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7548 let prev_chars = snapshot
7549 .reversed_chars_at(selection_head)
7550 .collect::<String>();
7551 for (pair, enabled) in scope.brackets() {
7552 if enabled
7553 && pair.close
7554 && prev_chars.starts_with(pair.start.as_str())
7555 && next_chars.starts_with(pair.end.as_str())
7556 {
7557 bracket_pair = Some(pair.clone());
7558 break;
7559 }
7560 }
7561 if let Some(pair) = bracket_pair {
7562 let start = snapshot.anchor_after(selection_head);
7563 let end = snapshot.anchor_after(selection_head);
7564 self.autoclose_regions.push(AutocloseRegion {
7565 selection_id: selection.id,
7566 range: start..end,
7567 pair,
7568 });
7569 }
7570 }
7571 }
7572 }
7573 Ok(())
7574 }
7575
7576 pub fn move_to_next_snippet_tabstop(
7577 &mut self,
7578 window: &mut Window,
7579 cx: &mut Context<Self>,
7580 ) -> bool {
7581 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7582 }
7583
7584 pub fn move_to_prev_snippet_tabstop(
7585 &mut self,
7586 window: &mut Window,
7587 cx: &mut Context<Self>,
7588 ) -> bool {
7589 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7590 }
7591
7592 pub fn move_to_snippet_tabstop(
7593 &mut self,
7594 bias: Bias,
7595 window: &mut Window,
7596 cx: &mut Context<Self>,
7597 ) -> bool {
7598 if let Some(mut snippet) = self.snippet_stack.pop() {
7599 match bias {
7600 Bias::Left => {
7601 if snippet.active_index > 0 {
7602 snippet.active_index -= 1;
7603 } else {
7604 self.snippet_stack.push(snippet);
7605 return false;
7606 }
7607 }
7608 Bias::Right => {
7609 if snippet.active_index + 1 < snippet.ranges.len() {
7610 snippet.active_index += 1;
7611 } else {
7612 self.snippet_stack.push(snippet);
7613 return false;
7614 }
7615 }
7616 }
7617 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7618 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7619 s.select_anchor_ranges(current_ranges.iter().cloned())
7620 });
7621
7622 if let Some(choices) = &snippet.choices[snippet.active_index] {
7623 if let Some(selection) = current_ranges.first() {
7624 self.show_snippet_choices(&choices, selection.clone(), cx);
7625 }
7626 }
7627
7628 // If snippet state is not at the last tabstop, push it back on the stack
7629 if snippet.active_index + 1 < snippet.ranges.len() {
7630 self.snippet_stack.push(snippet);
7631 }
7632 return true;
7633 }
7634 }
7635
7636 false
7637 }
7638
7639 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7640 self.transact(window, cx, |this, window, cx| {
7641 this.select_all(&SelectAll, window, cx);
7642 this.insert("", window, cx);
7643 });
7644 }
7645
7646 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7647 self.transact(window, cx, |this, window, cx| {
7648 this.select_autoclose_pair(window, cx);
7649 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7650 if !this.linked_edit_ranges.is_empty() {
7651 let selections = this.selections.all::<MultiBufferPoint>(cx);
7652 let snapshot = this.buffer.read(cx).snapshot(cx);
7653
7654 for selection in selections.iter() {
7655 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7656 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7657 if selection_start.buffer_id != selection_end.buffer_id {
7658 continue;
7659 }
7660 if let Some(ranges) =
7661 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7662 {
7663 for (buffer, entries) in ranges {
7664 linked_ranges.entry(buffer).or_default().extend(entries);
7665 }
7666 }
7667 }
7668 }
7669
7670 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7671 if !this.selections.line_mode {
7672 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7673 for selection in &mut selections {
7674 if selection.is_empty() {
7675 let old_head = selection.head();
7676 let mut new_head =
7677 movement::left(&display_map, old_head.to_display_point(&display_map))
7678 .to_point(&display_map);
7679 if let Some((buffer, line_buffer_range)) = display_map
7680 .buffer_snapshot
7681 .buffer_line_for_row(MultiBufferRow(old_head.row))
7682 {
7683 let indent_size =
7684 buffer.indent_size_for_line(line_buffer_range.start.row);
7685 let indent_len = match indent_size.kind {
7686 IndentKind::Space => {
7687 buffer.settings_at(line_buffer_range.start, cx).tab_size
7688 }
7689 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7690 };
7691 if old_head.column <= indent_size.len && old_head.column > 0 {
7692 let indent_len = indent_len.get();
7693 new_head = cmp::min(
7694 new_head,
7695 MultiBufferPoint::new(
7696 old_head.row,
7697 ((old_head.column - 1) / indent_len) * indent_len,
7698 ),
7699 );
7700 }
7701 }
7702
7703 selection.set_head(new_head, SelectionGoal::None);
7704 }
7705 }
7706 }
7707
7708 this.signature_help_state.set_backspace_pressed(true);
7709 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7710 s.select(selections)
7711 });
7712 this.insert("", window, cx);
7713 let empty_str: Arc<str> = Arc::from("");
7714 for (buffer, edits) in linked_ranges {
7715 let snapshot = buffer.read(cx).snapshot();
7716 use text::ToPoint as TP;
7717
7718 let edits = edits
7719 .into_iter()
7720 .map(|range| {
7721 let end_point = TP::to_point(&range.end, &snapshot);
7722 let mut start_point = TP::to_point(&range.start, &snapshot);
7723
7724 if end_point == start_point {
7725 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7726 .saturating_sub(1);
7727 start_point =
7728 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7729 };
7730
7731 (start_point..end_point, empty_str.clone())
7732 })
7733 .sorted_by_key(|(range, _)| range.start)
7734 .collect::<Vec<_>>();
7735 buffer.update(cx, |this, cx| {
7736 this.edit(edits, None, cx);
7737 })
7738 }
7739 this.refresh_inline_completion(true, false, window, cx);
7740 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7741 });
7742 }
7743
7744 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7745 self.transact(window, cx, |this, window, cx| {
7746 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7747 let line_mode = s.line_mode;
7748 s.move_with(|map, selection| {
7749 if selection.is_empty() && !line_mode {
7750 let cursor = movement::right(map, selection.head());
7751 selection.end = cursor;
7752 selection.reversed = true;
7753 selection.goal = SelectionGoal::None;
7754 }
7755 })
7756 });
7757 this.insert("", window, cx);
7758 this.refresh_inline_completion(true, false, window, cx);
7759 });
7760 }
7761
7762 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7763 if self.move_to_prev_snippet_tabstop(window, cx) {
7764 return;
7765 }
7766
7767 self.outdent(&Outdent, window, cx);
7768 }
7769
7770 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7771 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7772 return;
7773 }
7774
7775 let mut selections = self.selections.all_adjusted(cx);
7776 let buffer = self.buffer.read(cx);
7777 let snapshot = buffer.snapshot(cx);
7778 let rows_iter = selections.iter().map(|s| s.head().row);
7779 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7780
7781 let mut edits = Vec::new();
7782 let mut prev_edited_row = 0;
7783 let mut row_delta = 0;
7784 for selection in &mut selections {
7785 if selection.start.row != prev_edited_row {
7786 row_delta = 0;
7787 }
7788 prev_edited_row = selection.end.row;
7789
7790 // If the selection is non-empty, then increase the indentation of the selected lines.
7791 if !selection.is_empty() {
7792 row_delta =
7793 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7794 continue;
7795 }
7796
7797 // If the selection is empty and the cursor is in the leading whitespace before the
7798 // suggested indentation, then auto-indent the line.
7799 let cursor = selection.head();
7800 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7801 if let Some(suggested_indent) =
7802 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7803 {
7804 if cursor.column < suggested_indent.len
7805 && cursor.column <= current_indent.len
7806 && current_indent.len <= suggested_indent.len
7807 {
7808 selection.start = Point::new(cursor.row, suggested_indent.len);
7809 selection.end = selection.start;
7810 if row_delta == 0 {
7811 edits.extend(Buffer::edit_for_indent_size_adjustment(
7812 cursor.row,
7813 current_indent,
7814 suggested_indent,
7815 ));
7816 row_delta = suggested_indent.len - current_indent.len;
7817 }
7818 continue;
7819 }
7820 }
7821
7822 // Otherwise, insert a hard or soft tab.
7823 let settings = buffer.language_settings_at(cursor, cx);
7824 let tab_size = if settings.hard_tabs {
7825 IndentSize::tab()
7826 } else {
7827 let tab_size = settings.tab_size.get();
7828 let char_column = snapshot
7829 .text_for_range(Point::new(cursor.row, 0)..cursor)
7830 .flat_map(str::chars)
7831 .count()
7832 + row_delta as usize;
7833 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7834 IndentSize::spaces(chars_to_next_tab_stop)
7835 };
7836 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7837 selection.end = selection.start;
7838 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7839 row_delta += tab_size.len;
7840 }
7841
7842 self.transact(window, cx, |this, window, cx| {
7843 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7844 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7845 s.select(selections)
7846 });
7847 this.refresh_inline_completion(true, false, window, cx);
7848 });
7849 }
7850
7851 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7852 if self.read_only(cx) {
7853 return;
7854 }
7855 let mut selections = self.selections.all::<Point>(cx);
7856 let mut prev_edited_row = 0;
7857 let mut row_delta = 0;
7858 let mut edits = Vec::new();
7859 let buffer = self.buffer.read(cx);
7860 let snapshot = buffer.snapshot(cx);
7861 for selection in &mut selections {
7862 if selection.start.row != prev_edited_row {
7863 row_delta = 0;
7864 }
7865 prev_edited_row = selection.end.row;
7866
7867 row_delta =
7868 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7869 }
7870
7871 self.transact(window, cx, |this, window, cx| {
7872 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7873 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7874 s.select(selections)
7875 });
7876 });
7877 }
7878
7879 fn indent_selection(
7880 buffer: &MultiBuffer,
7881 snapshot: &MultiBufferSnapshot,
7882 selection: &mut Selection<Point>,
7883 edits: &mut Vec<(Range<Point>, String)>,
7884 delta_for_start_row: u32,
7885 cx: &App,
7886 ) -> u32 {
7887 let settings = buffer.language_settings_at(selection.start, cx);
7888 let tab_size = settings.tab_size.get();
7889 let indent_kind = if settings.hard_tabs {
7890 IndentKind::Tab
7891 } else {
7892 IndentKind::Space
7893 };
7894 let mut start_row = selection.start.row;
7895 let mut end_row = selection.end.row + 1;
7896
7897 // If a selection ends at the beginning of a line, don't indent
7898 // that last line.
7899 if selection.end.column == 0 && selection.end.row > selection.start.row {
7900 end_row -= 1;
7901 }
7902
7903 // Avoid re-indenting a row that has already been indented by a
7904 // previous selection, but still update this selection's column
7905 // to reflect that indentation.
7906 if delta_for_start_row > 0 {
7907 start_row += 1;
7908 selection.start.column += delta_for_start_row;
7909 if selection.end.row == selection.start.row {
7910 selection.end.column += delta_for_start_row;
7911 }
7912 }
7913
7914 let mut delta_for_end_row = 0;
7915 let has_multiple_rows = start_row + 1 != end_row;
7916 for row in start_row..end_row {
7917 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7918 let indent_delta = match (current_indent.kind, indent_kind) {
7919 (IndentKind::Space, IndentKind::Space) => {
7920 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7921 IndentSize::spaces(columns_to_next_tab_stop)
7922 }
7923 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7924 (_, IndentKind::Tab) => IndentSize::tab(),
7925 };
7926
7927 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7928 0
7929 } else {
7930 selection.start.column
7931 };
7932 let row_start = Point::new(row, start);
7933 edits.push((
7934 row_start..row_start,
7935 indent_delta.chars().collect::<String>(),
7936 ));
7937
7938 // Update this selection's endpoints to reflect the indentation.
7939 if row == selection.start.row {
7940 selection.start.column += indent_delta.len;
7941 }
7942 if row == selection.end.row {
7943 selection.end.column += indent_delta.len;
7944 delta_for_end_row = indent_delta.len;
7945 }
7946 }
7947
7948 if selection.start.row == selection.end.row {
7949 delta_for_start_row + delta_for_end_row
7950 } else {
7951 delta_for_end_row
7952 }
7953 }
7954
7955 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7956 if self.read_only(cx) {
7957 return;
7958 }
7959 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7960 let selections = self.selections.all::<Point>(cx);
7961 let mut deletion_ranges = Vec::new();
7962 let mut last_outdent = None;
7963 {
7964 let buffer = self.buffer.read(cx);
7965 let snapshot = buffer.snapshot(cx);
7966 for selection in &selections {
7967 let settings = buffer.language_settings_at(selection.start, cx);
7968 let tab_size = settings.tab_size.get();
7969 let mut rows = selection.spanned_rows(false, &display_map);
7970
7971 // Avoid re-outdenting a row that has already been outdented by a
7972 // previous selection.
7973 if let Some(last_row) = last_outdent {
7974 if last_row == rows.start {
7975 rows.start = rows.start.next_row();
7976 }
7977 }
7978 let has_multiple_rows = rows.len() > 1;
7979 for row in rows.iter_rows() {
7980 let indent_size = snapshot.indent_size_for_line(row);
7981 if indent_size.len > 0 {
7982 let deletion_len = match indent_size.kind {
7983 IndentKind::Space => {
7984 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7985 if columns_to_prev_tab_stop == 0 {
7986 tab_size
7987 } else {
7988 columns_to_prev_tab_stop
7989 }
7990 }
7991 IndentKind::Tab => 1,
7992 };
7993 let start = if has_multiple_rows
7994 || deletion_len > selection.start.column
7995 || indent_size.len < selection.start.column
7996 {
7997 0
7998 } else {
7999 selection.start.column - deletion_len
8000 };
8001 deletion_ranges.push(
8002 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8003 );
8004 last_outdent = Some(row);
8005 }
8006 }
8007 }
8008 }
8009
8010 self.transact(window, cx, |this, window, cx| {
8011 this.buffer.update(cx, |buffer, cx| {
8012 let empty_str: Arc<str> = Arc::default();
8013 buffer.edit(
8014 deletion_ranges
8015 .into_iter()
8016 .map(|range| (range, empty_str.clone())),
8017 None,
8018 cx,
8019 );
8020 });
8021 let selections = this.selections.all::<usize>(cx);
8022 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8023 s.select(selections)
8024 });
8025 });
8026 }
8027
8028 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8029 if self.read_only(cx) {
8030 return;
8031 }
8032 let selections = self
8033 .selections
8034 .all::<usize>(cx)
8035 .into_iter()
8036 .map(|s| s.range());
8037
8038 self.transact(window, cx, |this, window, cx| {
8039 this.buffer.update(cx, |buffer, cx| {
8040 buffer.autoindent_ranges(selections, cx);
8041 });
8042 let selections = this.selections.all::<usize>(cx);
8043 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8044 s.select(selections)
8045 });
8046 });
8047 }
8048
8049 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8050 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8051 let selections = self.selections.all::<Point>(cx);
8052
8053 let mut new_cursors = Vec::new();
8054 let mut edit_ranges = Vec::new();
8055 let mut selections = selections.iter().peekable();
8056 while let Some(selection) = selections.next() {
8057 let mut rows = selection.spanned_rows(false, &display_map);
8058 let goal_display_column = selection.head().to_display_point(&display_map).column();
8059
8060 // Accumulate contiguous regions of rows that we want to delete.
8061 while let Some(next_selection) = selections.peek() {
8062 let next_rows = next_selection.spanned_rows(false, &display_map);
8063 if next_rows.start <= rows.end {
8064 rows.end = next_rows.end;
8065 selections.next().unwrap();
8066 } else {
8067 break;
8068 }
8069 }
8070
8071 let buffer = &display_map.buffer_snapshot;
8072 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8073 let edit_end;
8074 let cursor_buffer_row;
8075 if buffer.max_point().row >= rows.end.0 {
8076 // If there's a line after the range, delete the \n from the end of the row range
8077 // and position the cursor on the next line.
8078 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8079 cursor_buffer_row = rows.end;
8080 } else {
8081 // If there isn't a line after the range, delete the \n from the line before the
8082 // start of the row range and position the cursor there.
8083 edit_start = edit_start.saturating_sub(1);
8084 edit_end = buffer.len();
8085 cursor_buffer_row = rows.start.previous_row();
8086 }
8087
8088 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8089 *cursor.column_mut() =
8090 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8091
8092 new_cursors.push((
8093 selection.id,
8094 buffer.anchor_after(cursor.to_point(&display_map)),
8095 ));
8096 edit_ranges.push(edit_start..edit_end);
8097 }
8098
8099 self.transact(window, cx, |this, window, cx| {
8100 let buffer = this.buffer.update(cx, |buffer, cx| {
8101 let empty_str: Arc<str> = Arc::default();
8102 buffer.edit(
8103 edit_ranges
8104 .into_iter()
8105 .map(|range| (range, empty_str.clone())),
8106 None,
8107 cx,
8108 );
8109 buffer.snapshot(cx)
8110 });
8111 let new_selections = new_cursors
8112 .into_iter()
8113 .map(|(id, cursor)| {
8114 let cursor = cursor.to_point(&buffer);
8115 Selection {
8116 id,
8117 start: cursor,
8118 end: cursor,
8119 reversed: false,
8120 goal: SelectionGoal::None,
8121 }
8122 })
8123 .collect();
8124
8125 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8126 s.select(new_selections);
8127 });
8128 });
8129 }
8130
8131 pub fn join_lines_impl(
8132 &mut self,
8133 insert_whitespace: bool,
8134 window: &mut Window,
8135 cx: &mut Context<Self>,
8136 ) {
8137 if self.read_only(cx) {
8138 return;
8139 }
8140 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8141 for selection in self.selections.all::<Point>(cx) {
8142 let start = MultiBufferRow(selection.start.row);
8143 // Treat single line selections as if they include the next line. Otherwise this action
8144 // would do nothing for single line selections individual cursors.
8145 let end = if selection.start.row == selection.end.row {
8146 MultiBufferRow(selection.start.row + 1)
8147 } else {
8148 MultiBufferRow(selection.end.row)
8149 };
8150
8151 if let Some(last_row_range) = row_ranges.last_mut() {
8152 if start <= last_row_range.end {
8153 last_row_range.end = end;
8154 continue;
8155 }
8156 }
8157 row_ranges.push(start..end);
8158 }
8159
8160 let snapshot = self.buffer.read(cx).snapshot(cx);
8161 let mut cursor_positions = Vec::new();
8162 for row_range in &row_ranges {
8163 let anchor = snapshot.anchor_before(Point::new(
8164 row_range.end.previous_row().0,
8165 snapshot.line_len(row_range.end.previous_row()),
8166 ));
8167 cursor_positions.push(anchor..anchor);
8168 }
8169
8170 self.transact(window, cx, |this, window, cx| {
8171 for row_range in row_ranges.into_iter().rev() {
8172 for row in row_range.iter_rows().rev() {
8173 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8174 let next_line_row = row.next_row();
8175 let indent = snapshot.indent_size_for_line(next_line_row);
8176 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8177
8178 let replace =
8179 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8180 " "
8181 } else {
8182 ""
8183 };
8184
8185 this.buffer.update(cx, |buffer, cx| {
8186 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8187 });
8188 }
8189 }
8190
8191 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8192 s.select_anchor_ranges(cursor_positions)
8193 });
8194 });
8195 }
8196
8197 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8198 self.join_lines_impl(true, window, cx);
8199 }
8200
8201 pub fn sort_lines_case_sensitive(
8202 &mut self,
8203 _: &SortLinesCaseSensitive,
8204 window: &mut Window,
8205 cx: &mut Context<Self>,
8206 ) {
8207 self.manipulate_lines(window, cx, |lines| lines.sort())
8208 }
8209
8210 pub fn sort_lines_case_insensitive(
8211 &mut self,
8212 _: &SortLinesCaseInsensitive,
8213 window: &mut Window,
8214 cx: &mut Context<Self>,
8215 ) {
8216 self.manipulate_lines(window, cx, |lines| {
8217 lines.sort_by_key(|line| line.to_lowercase())
8218 })
8219 }
8220
8221 pub fn unique_lines_case_insensitive(
8222 &mut self,
8223 _: &UniqueLinesCaseInsensitive,
8224 window: &mut Window,
8225 cx: &mut Context<Self>,
8226 ) {
8227 self.manipulate_lines(window, cx, |lines| {
8228 let mut seen = HashSet::default();
8229 lines.retain(|line| seen.insert(line.to_lowercase()));
8230 })
8231 }
8232
8233 pub fn unique_lines_case_sensitive(
8234 &mut self,
8235 _: &UniqueLinesCaseSensitive,
8236 window: &mut Window,
8237 cx: &mut Context<Self>,
8238 ) {
8239 self.manipulate_lines(window, cx, |lines| {
8240 let mut seen = HashSet::default();
8241 lines.retain(|line| seen.insert(*line));
8242 })
8243 }
8244
8245 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8246 let Some(project) = self.project.clone() else {
8247 return;
8248 };
8249 self.reload(project, window, cx)
8250 .detach_and_notify_err(window, cx);
8251 }
8252
8253 pub fn restore_file(
8254 &mut self,
8255 _: &::git::RestoreFile,
8256 window: &mut Window,
8257 cx: &mut Context<Self>,
8258 ) {
8259 let mut buffer_ids = HashSet::default();
8260 let snapshot = self.buffer().read(cx).snapshot(cx);
8261 for selection in self.selections.all::<usize>(cx) {
8262 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8263 }
8264
8265 let buffer = self.buffer().read(cx);
8266 let ranges = buffer_ids
8267 .into_iter()
8268 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8269 .collect::<Vec<_>>();
8270
8271 self.restore_hunks_in_ranges(ranges, window, cx);
8272 }
8273
8274 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8275 let selections = self
8276 .selections
8277 .all(cx)
8278 .into_iter()
8279 .map(|s| s.range())
8280 .collect();
8281 self.restore_hunks_in_ranges(selections, window, cx);
8282 }
8283
8284 fn restore_hunks_in_ranges(
8285 &mut self,
8286 ranges: Vec<Range<Point>>,
8287 window: &mut Window,
8288 cx: &mut Context<Editor>,
8289 ) {
8290 let mut revert_changes = HashMap::default();
8291 let chunk_by = self
8292 .snapshot(window, cx)
8293 .hunks_for_ranges(ranges)
8294 .into_iter()
8295 .chunk_by(|hunk| hunk.buffer_id);
8296 for (buffer_id, hunks) in &chunk_by {
8297 let hunks = hunks.collect::<Vec<_>>();
8298 for hunk in &hunks {
8299 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8300 }
8301 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8302 }
8303 drop(chunk_by);
8304 if !revert_changes.is_empty() {
8305 self.transact(window, cx, |editor, window, cx| {
8306 editor.restore(revert_changes, window, cx);
8307 });
8308 }
8309 }
8310
8311 pub fn open_active_item_in_terminal(
8312 &mut self,
8313 _: &OpenInTerminal,
8314 window: &mut Window,
8315 cx: &mut Context<Self>,
8316 ) {
8317 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8318 let project_path = buffer.read(cx).project_path(cx)?;
8319 let project = self.project.as_ref()?.read(cx);
8320 let entry = project.entry_for_path(&project_path, cx)?;
8321 let parent = match &entry.canonical_path {
8322 Some(canonical_path) => canonical_path.to_path_buf(),
8323 None => project.absolute_path(&project_path, cx)?,
8324 }
8325 .parent()?
8326 .to_path_buf();
8327 Some(parent)
8328 }) {
8329 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8330 }
8331 }
8332
8333 fn set_breakpoint_context_menu(
8334 &mut self,
8335 row: DisplayRow,
8336 position: Option<Anchor>,
8337 kind: Arc<BreakpointKind>,
8338 clicked_point: gpui::Point<Pixels>,
8339 window: &mut Window,
8340 cx: &mut Context<Self>,
8341 ) {
8342 if !cx.has_flag::<Debugger>() {
8343 return;
8344 }
8345 let source = self
8346 .buffer
8347 .read(cx)
8348 .snapshot(cx)
8349 .anchor_before(Point::new(row.0, 0u32));
8350
8351 let context_menu =
8352 self.breakpoint_context_menu(position.unwrap_or(source), kind, window, cx);
8353
8354 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8355 self,
8356 source,
8357 clicked_point,
8358 context_menu,
8359 window,
8360 cx,
8361 );
8362 }
8363
8364 fn add_edit_breakpoint_block(
8365 &mut self,
8366 anchor: Anchor,
8367 kind: &BreakpointKind,
8368 window: &mut Window,
8369 cx: &mut Context<Self>,
8370 ) {
8371 let weak_editor = cx.weak_entity();
8372 let bp_prompt =
8373 cx.new(|cx| BreakpointPromptEditor::new(weak_editor, anchor, kind.clone(), window, cx));
8374
8375 let height = bp_prompt.update(cx, |this, cx| {
8376 this.prompt
8377 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8378 });
8379 let cloned_prompt = bp_prompt.clone();
8380 let blocks = vec![BlockProperties {
8381 style: BlockStyle::Sticky,
8382 placement: BlockPlacement::Above(anchor),
8383 height,
8384 render: Arc::new(move |cx| {
8385 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8386 cloned_prompt.clone().into_any_element()
8387 }),
8388 priority: 0,
8389 }];
8390
8391 let focus_handle = bp_prompt.focus_handle(cx);
8392 window.focus(&focus_handle);
8393
8394 let block_ids = self.insert_blocks(blocks, None, cx);
8395 bp_prompt.update(cx, |prompt, _| {
8396 prompt.add_block_ids(block_ids);
8397 });
8398 }
8399
8400 pub(crate) fn breakpoint_at_cursor_head(
8401 &self,
8402 window: &mut Window,
8403 cx: &mut Context<Self>,
8404 ) -> Option<(Anchor, Breakpoint)> {
8405 let cursor_position: Point = self.selections.newest(cx).head();
8406 let snapshot = self.snapshot(window, cx);
8407 // We Set the column position to zero so this function interacts correctly
8408 // between calls by clicking on the gutter & using an action to toggle a
8409 // breakpoint. Otherwise, toggling a breakpoint through an action wouldn't
8410 // untoggle a breakpoint that was added through clicking on the gutter
8411 let cursor_position = snapshot
8412 .display_snapshot
8413 .buffer_snapshot
8414 .anchor_before(Point::new(cursor_position.row, 0));
8415
8416 let project = self.project.clone();
8417
8418 let buffer_id = cursor_position.text_anchor.buffer_id?;
8419 let enclosing_excerpt = snapshot
8420 .buffer_snapshot
8421 .excerpt_ids_for_range(cursor_position..cursor_position)
8422 .next()?;
8423 let buffer = project?.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8424 let buffer_snapshot = buffer.read(cx).snapshot();
8425
8426 let row = buffer_snapshot
8427 .summary_for_anchor::<text::PointUtf16>(&cursor_position.text_anchor)
8428 .row;
8429
8430 let bp = self
8431 .breakpoint_store
8432 .as_ref()?
8433 .read_with(cx, |breakpoint_store, cx| {
8434 breakpoint_store
8435 .breakpoints(
8436 &buffer,
8437 Some(cursor_position.text_anchor..(text::Anchor::MAX)),
8438 buffer_snapshot.clone(),
8439 cx,
8440 )
8441 .next()
8442 .and_then(move |(anchor, bp)| {
8443 let breakpoint_row = buffer_snapshot
8444 .summary_for_anchor::<text::PointUtf16>(anchor)
8445 .row;
8446
8447 if breakpoint_row == row {
8448 snapshot
8449 .buffer_snapshot
8450 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8451 .map(|anchor| (anchor, bp.clone()))
8452 } else {
8453 None
8454 }
8455 })
8456 });
8457 bp
8458 }
8459
8460 pub fn edit_log_breakpoint(
8461 &mut self,
8462 _: &EditLogBreakpoint,
8463 window: &mut Window,
8464 cx: &mut Context<Self>,
8465 ) {
8466 let (anchor, bp) = self
8467 .breakpoint_at_cursor_head(window, cx)
8468 .unwrap_or_else(|| {
8469 let cursor_position: Point = self.selections.newest(cx).head();
8470
8471 let breakpoint_position = self
8472 .snapshot(window, cx)
8473 .display_snapshot
8474 .buffer_snapshot
8475 .anchor_before(Point::new(cursor_position.row, 0));
8476
8477 (
8478 breakpoint_position,
8479 Breakpoint {
8480 kind: BreakpointKind::Standard,
8481 },
8482 )
8483 });
8484
8485 self.add_edit_breakpoint_block(anchor, &bp.kind, window, cx);
8486 }
8487
8488 pub fn toggle_breakpoint(
8489 &mut self,
8490 _: &crate::actions::ToggleBreakpoint,
8491 window: &mut Window,
8492 cx: &mut Context<Self>,
8493 ) {
8494 let edit_action = BreakpointEditAction::Toggle;
8495
8496 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8497 self.edit_breakpoint_at_anchor(anchor, breakpoint.kind, edit_action, cx);
8498 } else {
8499 let cursor_position: Point = self.selections.newest(cx).head();
8500
8501 let breakpoint_position = self
8502 .snapshot(window, cx)
8503 .display_snapshot
8504 .buffer_snapshot
8505 .anchor_before(Point::new(cursor_position.row, 0));
8506
8507 self.edit_breakpoint_at_anchor(
8508 breakpoint_position,
8509 BreakpointKind::Standard,
8510 edit_action,
8511 cx,
8512 );
8513 }
8514 }
8515
8516 pub fn edit_breakpoint_at_anchor(
8517 &mut self,
8518 breakpoint_position: Anchor,
8519 kind: BreakpointKind,
8520 edit_action: BreakpointEditAction,
8521 cx: &mut Context<Self>,
8522 ) {
8523 let Some(breakpoint_store) = &self.breakpoint_store else {
8524 return;
8525 };
8526
8527 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8528 if breakpoint_position == Anchor::min() {
8529 self.buffer()
8530 .read(cx)
8531 .excerpt_buffer_ids()
8532 .into_iter()
8533 .next()
8534 } else {
8535 None
8536 }
8537 }) else {
8538 return;
8539 };
8540
8541 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8542 return;
8543 };
8544
8545 breakpoint_store.update(cx, |breakpoint_store, cx| {
8546 breakpoint_store.toggle_breakpoint(
8547 buffer,
8548 (breakpoint_position.text_anchor, Breakpoint { kind }),
8549 edit_action,
8550 cx,
8551 );
8552 });
8553
8554 cx.notify();
8555 }
8556
8557 #[cfg(any(test, feature = "test-support"))]
8558 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8559 self.breakpoint_store.clone()
8560 }
8561
8562 pub fn prepare_restore_change(
8563 &self,
8564 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8565 hunk: &MultiBufferDiffHunk,
8566 cx: &mut App,
8567 ) -> Option<()> {
8568 if hunk.is_created_file() {
8569 return None;
8570 }
8571 let buffer = self.buffer.read(cx);
8572 let diff = buffer.diff_for(hunk.buffer_id)?;
8573 let buffer = buffer.buffer(hunk.buffer_id)?;
8574 let buffer = buffer.read(cx);
8575 let original_text = diff
8576 .read(cx)
8577 .base_text()
8578 .as_rope()
8579 .slice(hunk.diff_base_byte_range.clone());
8580 let buffer_snapshot = buffer.snapshot();
8581 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8582 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8583 probe
8584 .0
8585 .start
8586 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8587 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8588 }) {
8589 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8590 Some(())
8591 } else {
8592 None
8593 }
8594 }
8595
8596 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8597 self.manipulate_lines(window, cx, |lines| lines.reverse())
8598 }
8599
8600 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8601 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8602 }
8603
8604 fn manipulate_lines<Fn>(
8605 &mut self,
8606 window: &mut Window,
8607 cx: &mut Context<Self>,
8608 mut callback: Fn,
8609 ) where
8610 Fn: FnMut(&mut Vec<&str>),
8611 {
8612 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8613 let buffer = self.buffer.read(cx).snapshot(cx);
8614
8615 let mut edits = Vec::new();
8616
8617 let selections = self.selections.all::<Point>(cx);
8618 let mut selections = selections.iter().peekable();
8619 let mut contiguous_row_selections = Vec::new();
8620 let mut new_selections = Vec::new();
8621 let mut added_lines = 0;
8622 let mut removed_lines = 0;
8623
8624 while let Some(selection) = selections.next() {
8625 let (start_row, end_row) = consume_contiguous_rows(
8626 &mut contiguous_row_selections,
8627 selection,
8628 &display_map,
8629 &mut selections,
8630 );
8631
8632 let start_point = Point::new(start_row.0, 0);
8633 let end_point = Point::new(
8634 end_row.previous_row().0,
8635 buffer.line_len(end_row.previous_row()),
8636 );
8637 let text = buffer
8638 .text_for_range(start_point..end_point)
8639 .collect::<String>();
8640
8641 let mut lines = text.split('\n').collect_vec();
8642
8643 let lines_before = lines.len();
8644 callback(&mut lines);
8645 let lines_after = lines.len();
8646
8647 edits.push((start_point..end_point, lines.join("\n")));
8648
8649 // Selections must change based on added and removed line count
8650 let start_row =
8651 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8652 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8653 new_selections.push(Selection {
8654 id: selection.id,
8655 start: start_row,
8656 end: end_row,
8657 goal: SelectionGoal::None,
8658 reversed: selection.reversed,
8659 });
8660
8661 if lines_after > lines_before {
8662 added_lines += lines_after - lines_before;
8663 } else if lines_before > lines_after {
8664 removed_lines += lines_before - lines_after;
8665 }
8666 }
8667
8668 self.transact(window, cx, |this, window, cx| {
8669 let buffer = this.buffer.update(cx, |buffer, cx| {
8670 buffer.edit(edits, None, cx);
8671 buffer.snapshot(cx)
8672 });
8673
8674 // Recalculate offsets on newly edited buffer
8675 let new_selections = new_selections
8676 .iter()
8677 .map(|s| {
8678 let start_point = Point::new(s.start.0, 0);
8679 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8680 Selection {
8681 id: s.id,
8682 start: buffer.point_to_offset(start_point),
8683 end: buffer.point_to_offset(end_point),
8684 goal: s.goal,
8685 reversed: s.reversed,
8686 }
8687 })
8688 .collect();
8689
8690 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8691 s.select(new_selections);
8692 });
8693
8694 this.request_autoscroll(Autoscroll::fit(), cx);
8695 });
8696 }
8697
8698 pub fn convert_to_upper_case(
8699 &mut self,
8700 _: &ConvertToUpperCase,
8701 window: &mut Window,
8702 cx: &mut Context<Self>,
8703 ) {
8704 self.manipulate_text(window, cx, |text| text.to_uppercase())
8705 }
8706
8707 pub fn convert_to_lower_case(
8708 &mut self,
8709 _: &ConvertToLowerCase,
8710 window: &mut Window,
8711 cx: &mut Context<Self>,
8712 ) {
8713 self.manipulate_text(window, cx, |text| text.to_lowercase())
8714 }
8715
8716 pub fn convert_to_title_case(
8717 &mut self,
8718 _: &ConvertToTitleCase,
8719 window: &mut Window,
8720 cx: &mut Context<Self>,
8721 ) {
8722 self.manipulate_text(window, cx, |text| {
8723 text.split('\n')
8724 .map(|line| line.to_case(Case::Title))
8725 .join("\n")
8726 })
8727 }
8728
8729 pub fn convert_to_snake_case(
8730 &mut self,
8731 _: &ConvertToSnakeCase,
8732 window: &mut Window,
8733 cx: &mut Context<Self>,
8734 ) {
8735 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8736 }
8737
8738 pub fn convert_to_kebab_case(
8739 &mut self,
8740 _: &ConvertToKebabCase,
8741 window: &mut Window,
8742 cx: &mut Context<Self>,
8743 ) {
8744 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8745 }
8746
8747 pub fn convert_to_upper_camel_case(
8748 &mut self,
8749 _: &ConvertToUpperCamelCase,
8750 window: &mut Window,
8751 cx: &mut Context<Self>,
8752 ) {
8753 self.manipulate_text(window, cx, |text| {
8754 text.split('\n')
8755 .map(|line| line.to_case(Case::UpperCamel))
8756 .join("\n")
8757 })
8758 }
8759
8760 pub fn convert_to_lower_camel_case(
8761 &mut self,
8762 _: &ConvertToLowerCamelCase,
8763 window: &mut Window,
8764 cx: &mut Context<Self>,
8765 ) {
8766 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8767 }
8768
8769 pub fn convert_to_opposite_case(
8770 &mut self,
8771 _: &ConvertToOppositeCase,
8772 window: &mut Window,
8773 cx: &mut Context<Self>,
8774 ) {
8775 self.manipulate_text(window, cx, |text| {
8776 text.chars()
8777 .fold(String::with_capacity(text.len()), |mut t, c| {
8778 if c.is_uppercase() {
8779 t.extend(c.to_lowercase());
8780 } else {
8781 t.extend(c.to_uppercase());
8782 }
8783 t
8784 })
8785 })
8786 }
8787
8788 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8789 where
8790 Fn: FnMut(&str) -> String,
8791 {
8792 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8793 let buffer = self.buffer.read(cx).snapshot(cx);
8794
8795 let mut new_selections = Vec::new();
8796 let mut edits = Vec::new();
8797 let mut selection_adjustment = 0i32;
8798
8799 for selection in self.selections.all::<usize>(cx) {
8800 let selection_is_empty = selection.is_empty();
8801
8802 let (start, end) = if selection_is_empty {
8803 let word_range = movement::surrounding_word(
8804 &display_map,
8805 selection.start.to_display_point(&display_map),
8806 );
8807 let start = word_range.start.to_offset(&display_map, Bias::Left);
8808 let end = word_range.end.to_offset(&display_map, Bias::Left);
8809 (start, end)
8810 } else {
8811 (selection.start, selection.end)
8812 };
8813
8814 let text = buffer.text_for_range(start..end).collect::<String>();
8815 let old_length = text.len() as i32;
8816 let text = callback(&text);
8817
8818 new_selections.push(Selection {
8819 start: (start as i32 - selection_adjustment) as usize,
8820 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8821 goal: SelectionGoal::None,
8822 ..selection
8823 });
8824
8825 selection_adjustment += old_length - text.len() as i32;
8826
8827 edits.push((start..end, text));
8828 }
8829
8830 self.transact(window, cx, |this, window, cx| {
8831 this.buffer.update(cx, |buffer, cx| {
8832 buffer.edit(edits, None, cx);
8833 });
8834
8835 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8836 s.select(new_selections);
8837 });
8838
8839 this.request_autoscroll(Autoscroll::fit(), cx);
8840 });
8841 }
8842
8843 pub fn duplicate(
8844 &mut self,
8845 upwards: bool,
8846 whole_lines: bool,
8847 window: &mut Window,
8848 cx: &mut Context<Self>,
8849 ) {
8850 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8851 let buffer = &display_map.buffer_snapshot;
8852 let selections = self.selections.all::<Point>(cx);
8853
8854 let mut edits = Vec::new();
8855 let mut selections_iter = selections.iter().peekable();
8856 while let Some(selection) = selections_iter.next() {
8857 let mut rows = selection.spanned_rows(false, &display_map);
8858 // duplicate line-wise
8859 if whole_lines || selection.start == selection.end {
8860 // Avoid duplicating the same lines twice.
8861 while let Some(next_selection) = selections_iter.peek() {
8862 let next_rows = next_selection.spanned_rows(false, &display_map);
8863 if next_rows.start < rows.end {
8864 rows.end = next_rows.end;
8865 selections_iter.next().unwrap();
8866 } else {
8867 break;
8868 }
8869 }
8870
8871 // Copy the text from the selected row region and splice it either at the start
8872 // or end of the region.
8873 let start = Point::new(rows.start.0, 0);
8874 let end = Point::new(
8875 rows.end.previous_row().0,
8876 buffer.line_len(rows.end.previous_row()),
8877 );
8878 let text = buffer
8879 .text_for_range(start..end)
8880 .chain(Some("\n"))
8881 .collect::<String>();
8882 let insert_location = if upwards {
8883 Point::new(rows.end.0, 0)
8884 } else {
8885 start
8886 };
8887 edits.push((insert_location..insert_location, text));
8888 } else {
8889 // duplicate character-wise
8890 let start = selection.start;
8891 let end = selection.end;
8892 let text = buffer.text_for_range(start..end).collect::<String>();
8893 edits.push((selection.end..selection.end, text));
8894 }
8895 }
8896
8897 self.transact(window, cx, |this, _, cx| {
8898 this.buffer.update(cx, |buffer, cx| {
8899 buffer.edit(edits, None, cx);
8900 });
8901
8902 this.request_autoscroll(Autoscroll::fit(), cx);
8903 });
8904 }
8905
8906 pub fn duplicate_line_up(
8907 &mut self,
8908 _: &DuplicateLineUp,
8909 window: &mut Window,
8910 cx: &mut Context<Self>,
8911 ) {
8912 self.duplicate(true, true, window, cx);
8913 }
8914
8915 pub fn duplicate_line_down(
8916 &mut self,
8917 _: &DuplicateLineDown,
8918 window: &mut Window,
8919 cx: &mut Context<Self>,
8920 ) {
8921 self.duplicate(false, true, window, cx);
8922 }
8923
8924 pub fn duplicate_selection(
8925 &mut self,
8926 _: &DuplicateSelection,
8927 window: &mut Window,
8928 cx: &mut Context<Self>,
8929 ) {
8930 self.duplicate(false, false, window, cx);
8931 }
8932
8933 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8934 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8935 let buffer = self.buffer.read(cx).snapshot(cx);
8936
8937 let mut edits = Vec::new();
8938 let mut unfold_ranges = Vec::new();
8939 let mut refold_creases = Vec::new();
8940
8941 let selections = self.selections.all::<Point>(cx);
8942 let mut selections = selections.iter().peekable();
8943 let mut contiguous_row_selections = Vec::new();
8944 let mut new_selections = Vec::new();
8945
8946 while let Some(selection) = selections.next() {
8947 // Find all the selections that span a contiguous row range
8948 let (start_row, end_row) = consume_contiguous_rows(
8949 &mut contiguous_row_selections,
8950 selection,
8951 &display_map,
8952 &mut selections,
8953 );
8954
8955 // Move the text spanned by the row range to be before the line preceding the row range
8956 if start_row.0 > 0 {
8957 let range_to_move = Point::new(
8958 start_row.previous_row().0,
8959 buffer.line_len(start_row.previous_row()),
8960 )
8961 ..Point::new(
8962 end_row.previous_row().0,
8963 buffer.line_len(end_row.previous_row()),
8964 );
8965 let insertion_point = display_map
8966 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8967 .0;
8968
8969 // Don't move lines across excerpts
8970 if buffer
8971 .excerpt_containing(insertion_point..range_to_move.end)
8972 .is_some()
8973 {
8974 let text = buffer
8975 .text_for_range(range_to_move.clone())
8976 .flat_map(|s| s.chars())
8977 .skip(1)
8978 .chain(['\n'])
8979 .collect::<String>();
8980
8981 edits.push((
8982 buffer.anchor_after(range_to_move.start)
8983 ..buffer.anchor_before(range_to_move.end),
8984 String::new(),
8985 ));
8986 let insertion_anchor = buffer.anchor_after(insertion_point);
8987 edits.push((insertion_anchor..insertion_anchor, text));
8988
8989 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8990
8991 // Move selections up
8992 new_selections.extend(contiguous_row_selections.drain(..).map(
8993 |mut selection| {
8994 selection.start.row -= row_delta;
8995 selection.end.row -= row_delta;
8996 selection
8997 },
8998 ));
8999
9000 // Move folds up
9001 unfold_ranges.push(range_to_move.clone());
9002 for fold in display_map.folds_in_range(
9003 buffer.anchor_before(range_to_move.start)
9004 ..buffer.anchor_after(range_to_move.end),
9005 ) {
9006 let mut start = fold.range.start.to_point(&buffer);
9007 let mut end = fold.range.end.to_point(&buffer);
9008 start.row -= row_delta;
9009 end.row -= row_delta;
9010 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9011 }
9012 }
9013 }
9014
9015 // If we didn't move line(s), preserve the existing selections
9016 new_selections.append(&mut contiguous_row_selections);
9017 }
9018
9019 self.transact(window, cx, |this, window, cx| {
9020 this.unfold_ranges(&unfold_ranges, true, true, cx);
9021 this.buffer.update(cx, |buffer, cx| {
9022 for (range, text) in edits {
9023 buffer.edit([(range, text)], None, cx);
9024 }
9025 });
9026 this.fold_creases(refold_creases, true, window, cx);
9027 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9028 s.select(new_selections);
9029 })
9030 });
9031 }
9032
9033 pub fn move_line_down(
9034 &mut self,
9035 _: &MoveLineDown,
9036 window: &mut Window,
9037 cx: &mut Context<Self>,
9038 ) {
9039 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9040 let buffer = self.buffer.read(cx).snapshot(cx);
9041
9042 let mut edits = Vec::new();
9043 let mut unfold_ranges = Vec::new();
9044 let mut refold_creases = Vec::new();
9045
9046 let selections = self.selections.all::<Point>(cx);
9047 let mut selections = selections.iter().peekable();
9048 let mut contiguous_row_selections = Vec::new();
9049 let mut new_selections = Vec::new();
9050
9051 while let Some(selection) = selections.next() {
9052 // Find all the selections that span a contiguous row range
9053 let (start_row, end_row) = consume_contiguous_rows(
9054 &mut contiguous_row_selections,
9055 selection,
9056 &display_map,
9057 &mut selections,
9058 );
9059
9060 // Move the text spanned by the row range to be after the last line of the row range
9061 if end_row.0 <= buffer.max_point().row {
9062 let range_to_move =
9063 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9064 let insertion_point = display_map
9065 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9066 .0;
9067
9068 // Don't move lines across excerpt boundaries
9069 if buffer
9070 .excerpt_containing(range_to_move.start..insertion_point)
9071 .is_some()
9072 {
9073 let mut text = String::from("\n");
9074 text.extend(buffer.text_for_range(range_to_move.clone()));
9075 text.pop(); // Drop trailing newline
9076 edits.push((
9077 buffer.anchor_after(range_to_move.start)
9078 ..buffer.anchor_before(range_to_move.end),
9079 String::new(),
9080 ));
9081 let insertion_anchor = buffer.anchor_after(insertion_point);
9082 edits.push((insertion_anchor..insertion_anchor, text));
9083
9084 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9085
9086 // Move selections down
9087 new_selections.extend(contiguous_row_selections.drain(..).map(
9088 |mut selection| {
9089 selection.start.row += row_delta;
9090 selection.end.row += row_delta;
9091 selection
9092 },
9093 ));
9094
9095 // Move folds down
9096 unfold_ranges.push(range_to_move.clone());
9097 for fold in display_map.folds_in_range(
9098 buffer.anchor_before(range_to_move.start)
9099 ..buffer.anchor_after(range_to_move.end),
9100 ) {
9101 let mut start = fold.range.start.to_point(&buffer);
9102 let mut end = fold.range.end.to_point(&buffer);
9103 start.row += row_delta;
9104 end.row += row_delta;
9105 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9106 }
9107 }
9108 }
9109
9110 // If we didn't move line(s), preserve the existing selections
9111 new_selections.append(&mut contiguous_row_selections);
9112 }
9113
9114 self.transact(window, cx, |this, window, cx| {
9115 this.unfold_ranges(&unfold_ranges, true, true, cx);
9116 this.buffer.update(cx, |buffer, cx| {
9117 for (range, text) in edits {
9118 buffer.edit([(range, text)], None, cx);
9119 }
9120 });
9121 this.fold_creases(refold_creases, true, window, cx);
9122 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9123 s.select(new_selections)
9124 });
9125 });
9126 }
9127
9128 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9129 let text_layout_details = &self.text_layout_details(window);
9130 self.transact(window, cx, |this, window, cx| {
9131 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9132 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9133 let line_mode = s.line_mode;
9134 s.move_with(|display_map, selection| {
9135 if !selection.is_empty() || line_mode {
9136 return;
9137 }
9138
9139 let mut head = selection.head();
9140 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9141 if head.column() == display_map.line_len(head.row()) {
9142 transpose_offset = display_map
9143 .buffer_snapshot
9144 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9145 }
9146
9147 if transpose_offset == 0 {
9148 return;
9149 }
9150
9151 *head.column_mut() += 1;
9152 head = display_map.clip_point(head, Bias::Right);
9153 let goal = SelectionGoal::HorizontalPosition(
9154 display_map
9155 .x_for_display_point(head, text_layout_details)
9156 .into(),
9157 );
9158 selection.collapse_to(head, goal);
9159
9160 let transpose_start = display_map
9161 .buffer_snapshot
9162 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9163 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9164 let transpose_end = display_map
9165 .buffer_snapshot
9166 .clip_offset(transpose_offset + 1, Bias::Right);
9167 if let Some(ch) =
9168 display_map.buffer_snapshot.chars_at(transpose_start).next()
9169 {
9170 edits.push((transpose_start..transpose_offset, String::new()));
9171 edits.push((transpose_end..transpose_end, ch.to_string()));
9172 }
9173 }
9174 });
9175 edits
9176 });
9177 this.buffer
9178 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9179 let selections = this.selections.all::<usize>(cx);
9180 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9181 s.select(selections);
9182 });
9183 });
9184 }
9185
9186 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9187 self.rewrap_impl(RewrapOptions::default(), cx)
9188 }
9189
9190 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9191 let buffer = self.buffer.read(cx).snapshot(cx);
9192 let selections = self.selections.all::<Point>(cx);
9193 let mut selections = selections.iter().peekable();
9194
9195 let mut edits = Vec::new();
9196 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9197
9198 while let Some(selection) = selections.next() {
9199 let mut start_row = selection.start.row;
9200 let mut end_row = selection.end.row;
9201
9202 // Skip selections that overlap with a range that has already been rewrapped.
9203 let selection_range = start_row..end_row;
9204 if rewrapped_row_ranges
9205 .iter()
9206 .any(|range| range.overlaps(&selection_range))
9207 {
9208 continue;
9209 }
9210
9211 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9212
9213 // Since not all lines in the selection may be at the same indent
9214 // level, choose the indent size that is the most common between all
9215 // of the lines.
9216 //
9217 // If there is a tie, we use the deepest indent.
9218 let (indent_size, indent_end) = {
9219 let mut indent_size_occurrences = HashMap::default();
9220 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9221
9222 for row in start_row..=end_row {
9223 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9224 rows_by_indent_size.entry(indent).or_default().push(row);
9225 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9226 }
9227
9228 let indent_size = indent_size_occurrences
9229 .into_iter()
9230 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9231 .map(|(indent, _)| indent)
9232 .unwrap_or_default();
9233 let row = rows_by_indent_size[&indent_size][0];
9234 let indent_end = Point::new(row, indent_size.len);
9235
9236 (indent_size, indent_end)
9237 };
9238
9239 let mut line_prefix = indent_size.chars().collect::<String>();
9240
9241 let mut inside_comment = false;
9242 if let Some(comment_prefix) =
9243 buffer
9244 .language_scope_at(selection.head())
9245 .and_then(|language| {
9246 language
9247 .line_comment_prefixes()
9248 .iter()
9249 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9250 .cloned()
9251 })
9252 {
9253 line_prefix.push_str(&comment_prefix);
9254 inside_comment = true;
9255 }
9256
9257 let language_settings = buffer.language_settings_at(selection.head(), cx);
9258 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9259 RewrapBehavior::InComments => inside_comment,
9260 RewrapBehavior::InSelections => !selection.is_empty(),
9261 RewrapBehavior::Anywhere => true,
9262 };
9263
9264 let should_rewrap = options.override_language_settings
9265 || allow_rewrap_based_on_language
9266 || self.hard_wrap.is_some();
9267 if !should_rewrap {
9268 continue;
9269 }
9270
9271 if selection.is_empty() {
9272 'expand_upwards: while start_row > 0 {
9273 let prev_row = start_row - 1;
9274 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9275 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9276 {
9277 start_row = prev_row;
9278 } else {
9279 break 'expand_upwards;
9280 }
9281 }
9282
9283 'expand_downwards: while end_row < buffer.max_point().row {
9284 let next_row = end_row + 1;
9285 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9286 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9287 {
9288 end_row = next_row;
9289 } else {
9290 break 'expand_downwards;
9291 }
9292 }
9293 }
9294
9295 let start = Point::new(start_row, 0);
9296 let start_offset = start.to_offset(&buffer);
9297 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9298 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9299 let Some(lines_without_prefixes) = selection_text
9300 .lines()
9301 .map(|line| {
9302 line.strip_prefix(&line_prefix)
9303 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9304 .ok_or_else(|| {
9305 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9306 })
9307 })
9308 .collect::<Result<Vec<_>, _>>()
9309 .log_err()
9310 else {
9311 continue;
9312 };
9313
9314 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9315 buffer
9316 .language_settings_at(Point::new(start_row, 0), cx)
9317 .preferred_line_length as usize
9318 });
9319 let wrapped_text = wrap_with_prefix(
9320 line_prefix,
9321 lines_without_prefixes.join("\n"),
9322 wrap_column,
9323 tab_size,
9324 options.preserve_existing_whitespace,
9325 );
9326
9327 // TODO: should always use char-based diff while still supporting cursor behavior that
9328 // matches vim.
9329 let mut diff_options = DiffOptions::default();
9330 if options.override_language_settings {
9331 diff_options.max_word_diff_len = 0;
9332 diff_options.max_word_diff_line_count = 0;
9333 } else {
9334 diff_options.max_word_diff_len = usize::MAX;
9335 diff_options.max_word_diff_line_count = usize::MAX;
9336 }
9337
9338 for (old_range, new_text) in
9339 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9340 {
9341 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9342 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9343 edits.push((edit_start..edit_end, new_text));
9344 }
9345
9346 rewrapped_row_ranges.push(start_row..=end_row);
9347 }
9348
9349 self.buffer
9350 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9351 }
9352
9353 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9354 let mut text = String::new();
9355 let buffer = self.buffer.read(cx).snapshot(cx);
9356 let mut selections = self.selections.all::<Point>(cx);
9357 let mut clipboard_selections = Vec::with_capacity(selections.len());
9358 {
9359 let max_point = buffer.max_point();
9360 let mut is_first = true;
9361 for selection in &mut selections {
9362 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9363 if is_entire_line {
9364 selection.start = Point::new(selection.start.row, 0);
9365 if !selection.is_empty() && selection.end.column == 0 {
9366 selection.end = cmp::min(max_point, selection.end);
9367 } else {
9368 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9369 }
9370 selection.goal = SelectionGoal::None;
9371 }
9372 if is_first {
9373 is_first = false;
9374 } else {
9375 text += "\n";
9376 }
9377 let mut len = 0;
9378 for chunk in buffer.text_for_range(selection.start..selection.end) {
9379 text.push_str(chunk);
9380 len += chunk.len();
9381 }
9382 clipboard_selections.push(ClipboardSelection {
9383 len,
9384 is_entire_line,
9385 first_line_indent: buffer
9386 .indent_size_for_line(MultiBufferRow(selection.start.row))
9387 .len,
9388 });
9389 }
9390 }
9391
9392 self.transact(window, cx, |this, window, cx| {
9393 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9394 s.select(selections);
9395 });
9396 this.insert("", window, cx);
9397 });
9398 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9399 }
9400
9401 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9402 let item = self.cut_common(window, cx);
9403 cx.write_to_clipboard(item);
9404 }
9405
9406 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9407 self.change_selections(None, window, cx, |s| {
9408 s.move_with(|snapshot, sel| {
9409 if sel.is_empty() {
9410 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9411 }
9412 });
9413 });
9414 let item = self.cut_common(window, cx);
9415 cx.set_global(KillRing(item))
9416 }
9417
9418 pub fn kill_ring_yank(
9419 &mut self,
9420 _: &KillRingYank,
9421 window: &mut Window,
9422 cx: &mut Context<Self>,
9423 ) {
9424 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9425 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9426 (kill_ring.text().to_string(), kill_ring.metadata_json())
9427 } else {
9428 return;
9429 }
9430 } else {
9431 return;
9432 };
9433 self.do_paste(&text, metadata, false, window, cx);
9434 }
9435
9436 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9437 let selections = self.selections.all::<Point>(cx);
9438 let buffer = self.buffer.read(cx).read(cx);
9439 let mut text = String::new();
9440
9441 let mut clipboard_selections = Vec::with_capacity(selections.len());
9442 {
9443 let max_point = buffer.max_point();
9444 let mut is_first = true;
9445 for selection in selections.iter() {
9446 let mut start = selection.start;
9447 let mut end = selection.end;
9448 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9449 if is_entire_line {
9450 start = Point::new(start.row, 0);
9451 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9452 }
9453 if is_first {
9454 is_first = false;
9455 } else {
9456 text += "\n";
9457 }
9458 let mut len = 0;
9459 for chunk in buffer.text_for_range(start..end) {
9460 text.push_str(chunk);
9461 len += chunk.len();
9462 }
9463 clipboard_selections.push(ClipboardSelection {
9464 len,
9465 is_entire_line,
9466 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
9467 });
9468 }
9469 }
9470
9471 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9472 text,
9473 clipboard_selections,
9474 ));
9475 }
9476
9477 pub fn do_paste(
9478 &mut self,
9479 text: &String,
9480 clipboard_selections: Option<Vec<ClipboardSelection>>,
9481 handle_entire_lines: bool,
9482 window: &mut Window,
9483 cx: &mut Context<Self>,
9484 ) {
9485 if self.read_only(cx) {
9486 return;
9487 }
9488
9489 let clipboard_text = Cow::Borrowed(text);
9490
9491 self.transact(window, cx, |this, window, cx| {
9492 if let Some(mut clipboard_selections) = clipboard_selections {
9493 let old_selections = this.selections.all::<usize>(cx);
9494 let all_selections_were_entire_line =
9495 clipboard_selections.iter().all(|s| s.is_entire_line);
9496 let first_selection_indent_column =
9497 clipboard_selections.first().map(|s| s.first_line_indent);
9498 if clipboard_selections.len() != old_selections.len() {
9499 clipboard_selections.drain(..);
9500 }
9501 let cursor_offset = this.selections.last::<usize>(cx).head();
9502 let mut auto_indent_on_paste = true;
9503
9504 this.buffer.update(cx, |buffer, cx| {
9505 let snapshot = buffer.read(cx);
9506 auto_indent_on_paste = snapshot
9507 .language_settings_at(cursor_offset, cx)
9508 .auto_indent_on_paste;
9509
9510 let mut start_offset = 0;
9511 let mut edits = Vec::new();
9512 let mut original_indent_columns = Vec::new();
9513 for (ix, selection) in old_selections.iter().enumerate() {
9514 let to_insert;
9515 let entire_line;
9516 let original_indent_column;
9517 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9518 let end_offset = start_offset + clipboard_selection.len;
9519 to_insert = &clipboard_text[start_offset..end_offset];
9520 entire_line = clipboard_selection.is_entire_line;
9521 start_offset = end_offset + 1;
9522 original_indent_column = Some(clipboard_selection.first_line_indent);
9523 } else {
9524 to_insert = clipboard_text.as_str();
9525 entire_line = all_selections_were_entire_line;
9526 original_indent_column = first_selection_indent_column
9527 }
9528
9529 // If the corresponding selection was empty when this slice of the
9530 // clipboard text was written, then the entire line containing the
9531 // selection was copied. If this selection is also currently empty,
9532 // then paste the line before the current line of the buffer.
9533 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9534 let column = selection.start.to_point(&snapshot).column as usize;
9535 let line_start = selection.start - column;
9536 line_start..line_start
9537 } else {
9538 selection.range()
9539 };
9540
9541 edits.push((range, to_insert));
9542 original_indent_columns.push(original_indent_column);
9543 }
9544 drop(snapshot);
9545
9546 buffer.edit(
9547 edits,
9548 if auto_indent_on_paste {
9549 Some(AutoindentMode::Block {
9550 original_indent_columns,
9551 })
9552 } else {
9553 None
9554 },
9555 cx,
9556 );
9557 });
9558
9559 let selections = this.selections.all::<usize>(cx);
9560 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9561 s.select(selections)
9562 });
9563 } else {
9564 this.insert(&clipboard_text, window, cx);
9565 }
9566 });
9567 }
9568
9569 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9570 if let Some(item) = cx.read_from_clipboard() {
9571 let entries = item.entries();
9572
9573 match entries.first() {
9574 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9575 // of all the pasted entries.
9576 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9577 .do_paste(
9578 clipboard_string.text(),
9579 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9580 true,
9581 window,
9582 cx,
9583 ),
9584 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9585 }
9586 }
9587 }
9588
9589 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9590 if self.read_only(cx) {
9591 return;
9592 }
9593
9594 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9595 if let Some((selections, _)) =
9596 self.selection_history.transaction(transaction_id).cloned()
9597 {
9598 self.change_selections(None, window, cx, |s| {
9599 s.select_anchors(selections.to_vec());
9600 });
9601 } else {
9602 log::error!(
9603 "No entry in selection_history found for undo. \
9604 This may correspond to a bug where undo does not update the selection. \
9605 If this is occurring, please add details to \
9606 https://github.com/zed-industries/zed/issues/22692"
9607 );
9608 }
9609 self.request_autoscroll(Autoscroll::fit(), cx);
9610 self.unmark_text(window, cx);
9611 self.refresh_inline_completion(true, false, window, cx);
9612 cx.emit(EditorEvent::Edited { transaction_id });
9613 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9614 }
9615 }
9616
9617 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9618 if self.read_only(cx) {
9619 return;
9620 }
9621
9622 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9623 if let Some((_, Some(selections))) =
9624 self.selection_history.transaction(transaction_id).cloned()
9625 {
9626 self.change_selections(None, window, cx, |s| {
9627 s.select_anchors(selections.to_vec());
9628 });
9629 } else {
9630 log::error!(
9631 "No entry in selection_history found for redo. \
9632 This may correspond to a bug where undo does not update the selection. \
9633 If this is occurring, please add details to \
9634 https://github.com/zed-industries/zed/issues/22692"
9635 );
9636 }
9637 self.request_autoscroll(Autoscroll::fit(), cx);
9638 self.unmark_text(window, cx);
9639 self.refresh_inline_completion(true, false, window, cx);
9640 cx.emit(EditorEvent::Edited { transaction_id });
9641 }
9642 }
9643
9644 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9645 self.buffer
9646 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9647 }
9648
9649 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9650 self.buffer
9651 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9652 }
9653
9654 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9655 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9656 let line_mode = s.line_mode;
9657 s.move_with(|map, selection| {
9658 let cursor = if selection.is_empty() && !line_mode {
9659 movement::left(map, selection.start)
9660 } else {
9661 selection.start
9662 };
9663 selection.collapse_to(cursor, SelectionGoal::None);
9664 });
9665 })
9666 }
9667
9668 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9669 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9670 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9671 })
9672 }
9673
9674 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9675 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9676 let line_mode = s.line_mode;
9677 s.move_with(|map, selection| {
9678 let cursor = if selection.is_empty() && !line_mode {
9679 movement::right(map, selection.end)
9680 } else {
9681 selection.end
9682 };
9683 selection.collapse_to(cursor, SelectionGoal::None)
9684 });
9685 })
9686 }
9687
9688 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9689 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9690 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9691 })
9692 }
9693
9694 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9695 if self.take_rename(true, window, cx).is_some() {
9696 return;
9697 }
9698
9699 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9700 cx.propagate();
9701 return;
9702 }
9703
9704 let text_layout_details = &self.text_layout_details(window);
9705 let selection_count = self.selections.count();
9706 let first_selection = self.selections.first_anchor();
9707
9708 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9709 let line_mode = s.line_mode;
9710 s.move_with(|map, selection| {
9711 if !selection.is_empty() && !line_mode {
9712 selection.goal = SelectionGoal::None;
9713 }
9714 let (cursor, goal) = movement::up(
9715 map,
9716 selection.start,
9717 selection.goal,
9718 false,
9719 text_layout_details,
9720 );
9721 selection.collapse_to(cursor, goal);
9722 });
9723 });
9724
9725 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9726 {
9727 cx.propagate();
9728 }
9729 }
9730
9731 pub fn move_up_by_lines(
9732 &mut self,
9733 action: &MoveUpByLines,
9734 window: &mut Window,
9735 cx: &mut Context<Self>,
9736 ) {
9737 if self.take_rename(true, window, cx).is_some() {
9738 return;
9739 }
9740
9741 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9742 cx.propagate();
9743 return;
9744 }
9745
9746 let text_layout_details = &self.text_layout_details(window);
9747
9748 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9749 let line_mode = s.line_mode;
9750 s.move_with(|map, selection| {
9751 if !selection.is_empty() && !line_mode {
9752 selection.goal = SelectionGoal::None;
9753 }
9754 let (cursor, goal) = movement::up_by_rows(
9755 map,
9756 selection.start,
9757 action.lines,
9758 selection.goal,
9759 false,
9760 text_layout_details,
9761 );
9762 selection.collapse_to(cursor, goal);
9763 });
9764 })
9765 }
9766
9767 pub fn move_down_by_lines(
9768 &mut self,
9769 action: &MoveDownByLines,
9770 window: &mut Window,
9771 cx: &mut Context<Self>,
9772 ) {
9773 if self.take_rename(true, window, cx).is_some() {
9774 return;
9775 }
9776
9777 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9778 cx.propagate();
9779 return;
9780 }
9781
9782 let text_layout_details = &self.text_layout_details(window);
9783
9784 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9785 let line_mode = s.line_mode;
9786 s.move_with(|map, selection| {
9787 if !selection.is_empty() && !line_mode {
9788 selection.goal = SelectionGoal::None;
9789 }
9790 let (cursor, goal) = movement::down_by_rows(
9791 map,
9792 selection.start,
9793 action.lines,
9794 selection.goal,
9795 false,
9796 text_layout_details,
9797 );
9798 selection.collapse_to(cursor, goal);
9799 });
9800 })
9801 }
9802
9803 pub fn select_down_by_lines(
9804 &mut self,
9805 action: &SelectDownByLines,
9806 window: &mut Window,
9807 cx: &mut Context<Self>,
9808 ) {
9809 let text_layout_details = &self.text_layout_details(window);
9810 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9811 s.move_heads_with(|map, head, goal| {
9812 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9813 })
9814 })
9815 }
9816
9817 pub fn select_up_by_lines(
9818 &mut self,
9819 action: &SelectUpByLines,
9820 window: &mut Window,
9821 cx: &mut Context<Self>,
9822 ) {
9823 let text_layout_details = &self.text_layout_details(window);
9824 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9825 s.move_heads_with(|map, head, goal| {
9826 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9827 })
9828 })
9829 }
9830
9831 pub fn select_page_up(
9832 &mut self,
9833 _: &SelectPageUp,
9834 window: &mut Window,
9835 cx: &mut Context<Self>,
9836 ) {
9837 let Some(row_count) = self.visible_row_count() else {
9838 return;
9839 };
9840
9841 let text_layout_details = &self.text_layout_details(window);
9842
9843 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9844 s.move_heads_with(|map, head, goal| {
9845 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9846 })
9847 })
9848 }
9849
9850 pub fn move_page_up(
9851 &mut self,
9852 action: &MovePageUp,
9853 window: &mut Window,
9854 cx: &mut Context<Self>,
9855 ) {
9856 if self.take_rename(true, window, cx).is_some() {
9857 return;
9858 }
9859
9860 if self
9861 .context_menu
9862 .borrow_mut()
9863 .as_mut()
9864 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9865 .unwrap_or(false)
9866 {
9867 return;
9868 }
9869
9870 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9871 cx.propagate();
9872 return;
9873 }
9874
9875 let Some(row_count) = self.visible_row_count() else {
9876 return;
9877 };
9878
9879 let autoscroll = if action.center_cursor {
9880 Autoscroll::center()
9881 } else {
9882 Autoscroll::fit()
9883 };
9884
9885 let text_layout_details = &self.text_layout_details(window);
9886
9887 self.change_selections(Some(autoscroll), window, cx, |s| {
9888 let line_mode = s.line_mode;
9889 s.move_with(|map, selection| {
9890 if !selection.is_empty() && !line_mode {
9891 selection.goal = SelectionGoal::None;
9892 }
9893 let (cursor, goal) = movement::up_by_rows(
9894 map,
9895 selection.end,
9896 row_count,
9897 selection.goal,
9898 false,
9899 text_layout_details,
9900 );
9901 selection.collapse_to(cursor, goal);
9902 });
9903 });
9904 }
9905
9906 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9907 let text_layout_details = &self.text_layout_details(window);
9908 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9909 s.move_heads_with(|map, head, goal| {
9910 movement::up(map, head, goal, false, text_layout_details)
9911 })
9912 })
9913 }
9914
9915 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9916 self.take_rename(true, window, cx);
9917
9918 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9919 cx.propagate();
9920 return;
9921 }
9922
9923 let text_layout_details = &self.text_layout_details(window);
9924 let selection_count = self.selections.count();
9925 let first_selection = self.selections.first_anchor();
9926
9927 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9928 let line_mode = s.line_mode;
9929 s.move_with(|map, selection| {
9930 if !selection.is_empty() && !line_mode {
9931 selection.goal = SelectionGoal::None;
9932 }
9933 let (cursor, goal) = movement::down(
9934 map,
9935 selection.end,
9936 selection.goal,
9937 false,
9938 text_layout_details,
9939 );
9940 selection.collapse_to(cursor, goal);
9941 });
9942 });
9943
9944 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9945 {
9946 cx.propagate();
9947 }
9948 }
9949
9950 pub fn select_page_down(
9951 &mut self,
9952 _: &SelectPageDown,
9953 window: &mut Window,
9954 cx: &mut Context<Self>,
9955 ) {
9956 let Some(row_count) = self.visible_row_count() else {
9957 return;
9958 };
9959
9960 let text_layout_details = &self.text_layout_details(window);
9961
9962 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9963 s.move_heads_with(|map, head, goal| {
9964 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9965 })
9966 })
9967 }
9968
9969 pub fn move_page_down(
9970 &mut self,
9971 action: &MovePageDown,
9972 window: &mut Window,
9973 cx: &mut Context<Self>,
9974 ) {
9975 if self.take_rename(true, window, cx).is_some() {
9976 return;
9977 }
9978
9979 if self
9980 .context_menu
9981 .borrow_mut()
9982 .as_mut()
9983 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9984 .unwrap_or(false)
9985 {
9986 return;
9987 }
9988
9989 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9990 cx.propagate();
9991 return;
9992 }
9993
9994 let Some(row_count) = self.visible_row_count() else {
9995 return;
9996 };
9997
9998 let autoscroll = if action.center_cursor {
9999 Autoscroll::center()
10000 } else {
10001 Autoscroll::fit()
10002 };
10003
10004 let text_layout_details = &self.text_layout_details(window);
10005 self.change_selections(Some(autoscroll), window, cx, |s| {
10006 let line_mode = s.line_mode;
10007 s.move_with(|map, selection| {
10008 if !selection.is_empty() && !line_mode {
10009 selection.goal = SelectionGoal::None;
10010 }
10011 let (cursor, goal) = movement::down_by_rows(
10012 map,
10013 selection.end,
10014 row_count,
10015 selection.goal,
10016 false,
10017 text_layout_details,
10018 );
10019 selection.collapse_to(cursor, goal);
10020 });
10021 });
10022 }
10023
10024 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10025 let text_layout_details = &self.text_layout_details(window);
10026 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10027 s.move_heads_with(|map, head, goal| {
10028 movement::down(map, head, goal, false, text_layout_details)
10029 })
10030 });
10031 }
10032
10033 pub fn context_menu_first(
10034 &mut self,
10035 _: &ContextMenuFirst,
10036 _window: &mut Window,
10037 cx: &mut Context<Self>,
10038 ) {
10039 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10040 context_menu.select_first(self.completion_provider.as_deref(), cx);
10041 }
10042 }
10043
10044 pub fn context_menu_prev(
10045 &mut self,
10046 _: &ContextMenuPrevious,
10047 _window: &mut Window,
10048 cx: &mut Context<Self>,
10049 ) {
10050 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10051 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10052 }
10053 }
10054
10055 pub fn context_menu_next(
10056 &mut self,
10057 _: &ContextMenuNext,
10058 _window: &mut Window,
10059 cx: &mut Context<Self>,
10060 ) {
10061 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10062 context_menu.select_next(self.completion_provider.as_deref(), cx);
10063 }
10064 }
10065
10066 pub fn context_menu_last(
10067 &mut self,
10068 _: &ContextMenuLast,
10069 _window: &mut Window,
10070 cx: &mut Context<Self>,
10071 ) {
10072 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10073 context_menu.select_last(self.completion_provider.as_deref(), cx);
10074 }
10075 }
10076
10077 pub fn move_to_previous_word_start(
10078 &mut self,
10079 _: &MoveToPreviousWordStart,
10080 window: &mut Window,
10081 cx: &mut Context<Self>,
10082 ) {
10083 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10084 s.move_cursors_with(|map, head, _| {
10085 (
10086 movement::previous_word_start(map, head),
10087 SelectionGoal::None,
10088 )
10089 });
10090 })
10091 }
10092
10093 pub fn move_to_previous_subword_start(
10094 &mut self,
10095 _: &MoveToPreviousSubwordStart,
10096 window: &mut Window,
10097 cx: &mut Context<Self>,
10098 ) {
10099 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10100 s.move_cursors_with(|map, head, _| {
10101 (
10102 movement::previous_subword_start(map, head),
10103 SelectionGoal::None,
10104 )
10105 });
10106 })
10107 }
10108
10109 pub fn select_to_previous_word_start(
10110 &mut self,
10111 _: &SelectToPreviousWordStart,
10112 window: &mut Window,
10113 cx: &mut Context<Self>,
10114 ) {
10115 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10116 s.move_heads_with(|map, head, _| {
10117 (
10118 movement::previous_word_start(map, head),
10119 SelectionGoal::None,
10120 )
10121 });
10122 })
10123 }
10124
10125 pub fn select_to_previous_subword_start(
10126 &mut self,
10127 _: &SelectToPreviousSubwordStart,
10128 window: &mut Window,
10129 cx: &mut Context<Self>,
10130 ) {
10131 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10132 s.move_heads_with(|map, head, _| {
10133 (
10134 movement::previous_subword_start(map, head),
10135 SelectionGoal::None,
10136 )
10137 });
10138 })
10139 }
10140
10141 pub fn delete_to_previous_word_start(
10142 &mut self,
10143 action: &DeleteToPreviousWordStart,
10144 window: &mut Window,
10145 cx: &mut Context<Self>,
10146 ) {
10147 self.transact(window, cx, |this, window, cx| {
10148 this.select_autoclose_pair(window, cx);
10149 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10150 let line_mode = s.line_mode;
10151 s.move_with(|map, selection| {
10152 if selection.is_empty() && !line_mode {
10153 let cursor = if action.ignore_newlines {
10154 movement::previous_word_start(map, selection.head())
10155 } else {
10156 movement::previous_word_start_or_newline(map, selection.head())
10157 };
10158 selection.set_head(cursor, SelectionGoal::None);
10159 }
10160 });
10161 });
10162 this.insert("", window, cx);
10163 });
10164 }
10165
10166 pub fn delete_to_previous_subword_start(
10167 &mut self,
10168 _: &DeleteToPreviousSubwordStart,
10169 window: &mut Window,
10170 cx: &mut Context<Self>,
10171 ) {
10172 self.transact(window, cx, |this, window, cx| {
10173 this.select_autoclose_pair(window, cx);
10174 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10175 let line_mode = s.line_mode;
10176 s.move_with(|map, selection| {
10177 if selection.is_empty() && !line_mode {
10178 let cursor = movement::previous_subword_start(map, selection.head());
10179 selection.set_head(cursor, SelectionGoal::None);
10180 }
10181 });
10182 });
10183 this.insert("", window, cx);
10184 });
10185 }
10186
10187 pub fn move_to_next_word_end(
10188 &mut self,
10189 _: &MoveToNextWordEnd,
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 (movement::next_word_end(map, head), SelectionGoal::None)
10196 });
10197 })
10198 }
10199
10200 pub fn move_to_next_subword_end(
10201 &mut self,
10202 _: &MoveToNextSubwordEnd,
10203 window: &mut Window,
10204 cx: &mut Context<Self>,
10205 ) {
10206 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10207 s.move_cursors_with(|map, head, _| {
10208 (movement::next_subword_end(map, head), SelectionGoal::None)
10209 });
10210 })
10211 }
10212
10213 pub fn select_to_next_word_end(
10214 &mut self,
10215 _: &SelectToNextWordEnd,
10216 window: &mut Window,
10217 cx: &mut Context<Self>,
10218 ) {
10219 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10220 s.move_heads_with(|map, head, _| {
10221 (movement::next_word_end(map, head), SelectionGoal::None)
10222 });
10223 })
10224 }
10225
10226 pub fn select_to_next_subword_end(
10227 &mut self,
10228 _: &SelectToNextSubwordEnd,
10229 window: &mut Window,
10230 cx: &mut Context<Self>,
10231 ) {
10232 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10233 s.move_heads_with(|map, head, _| {
10234 (movement::next_subword_end(map, head), SelectionGoal::None)
10235 });
10236 })
10237 }
10238
10239 pub fn delete_to_next_word_end(
10240 &mut self,
10241 action: &DeleteToNextWordEnd,
10242 window: &mut Window,
10243 cx: &mut Context<Self>,
10244 ) {
10245 self.transact(window, cx, |this, window, cx| {
10246 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10247 let line_mode = s.line_mode;
10248 s.move_with(|map, selection| {
10249 if selection.is_empty() && !line_mode {
10250 let cursor = if action.ignore_newlines {
10251 movement::next_word_end(map, selection.head())
10252 } else {
10253 movement::next_word_end_or_newline(map, selection.head())
10254 };
10255 selection.set_head(cursor, SelectionGoal::None);
10256 }
10257 });
10258 });
10259 this.insert("", window, cx);
10260 });
10261 }
10262
10263 pub fn delete_to_next_subword_end(
10264 &mut self,
10265 _: &DeleteToNextSubwordEnd,
10266 window: &mut Window,
10267 cx: &mut Context<Self>,
10268 ) {
10269 self.transact(window, cx, |this, window, cx| {
10270 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10271 s.move_with(|map, selection| {
10272 if selection.is_empty() {
10273 let cursor = movement::next_subword_end(map, selection.head());
10274 selection.set_head(cursor, SelectionGoal::None);
10275 }
10276 });
10277 });
10278 this.insert("", window, cx);
10279 });
10280 }
10281
10282 pub fn move_to_beginning_of_line(
10283 &mut self,
10284 action: &MoveToBeginningOfLine,
10285 window: &mut Window,
10286 cx: &mut Context<Self>,
10287 ) {
10288 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10289 s.move_cursors_with(|map, head, _| {
10290 (
10291 movement::indented_line_beginning(
10292 map,
10293 head,
10294 action.stop_at_soft_wraps,
10295 action.stop_at_indent,
10296 ),
10297 SelectionGoal::None,
10298 )
10299 });
10300 })
10301 }
10302
10303 pub fn select_to_beginning_of_line(
10304 &mut self,
10305 action: &SelectToBeginningOfLine,
10306 window: &mut Window,
10307 cx: &mut Context<Self>,
10308 ) {
10309 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10310 s.move_heads_with(|map, head, _| {
10311 (
10312 movement::indented_line_beginning(
10313 map,
10314 head,
10315 action.stop_at_soft_wraps,
10316 action.stop_at_indent,
10317 ),
10318 SelectionGoal::None,
10319 )
10320 });
10321 });
10322 }
10323
10324 pub fn delete_to_beginning_of_line(
10325 &mut self,
10326 action: &DeleteToBeginningOfLine,
10327 window: &mut Window,
10328 cx: &mut Context<Self>,
10329 ) {
10330 self.transact(window, cx, |this, window, cx| {
10331 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10332 s.move_with(|_, selection| {
10333 selection.reversed = true;
10334 });
10335 });
10336
10337 this.select_to_beginning_of_line(
10338 &SelectToBeginningOfLine {
10339 stop_at_soft_wraps: false,
10340 stop_at_indent: action.stop_at_indent,
10341 },
10342 window,
10343 cx,
10344 );
10345 this.backspace(&Backspace, window, cx);
10346 });
10347 }
10348
10349 pub fn move_to_end_of_line(
10350 &mut self,
10351 action: &MoveToEndOfLine,
10352 window: &mut Window,
10353 cx: &mut Context<Self>,
10354 ) {
10355 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10356 s.move_cursors_with(|map, head, _| {
10357 (
10358 movement::line_end(map, head, action.stop_at_soft_wraps),
10359 SelectionGoal::None,
10360 )
10361 });
10362 })
10363 }
10364
10365 pub fn select_to_end_of_line(
10366 &mut self,
10367 action: &SelectToEndOfLine,
10368 window: &mut Window,
10369 cx: &mut Context<Self>,
10370 ) {
10371 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10372 s.move_heads_with(|map, head, _| {
10373 (
10374 movement::line_end(map, head, action.stop_at_soft_wraps),
10375 SelectionGoal::None,
10376 )
10377 });
10378 })
10379 }
10380
10381 pub fn delete_to_end_of_line(
10382 &mut self,
10383 _: &DeleteToEndOfLine,
10384 window: &mut Window,
10385 cx: &mut Context<Self>,
10386 ) {
10387 self.transact(window, cx, |this, window, cx| {
10388 this.select_to_end_of_line(
10389 &SelectToEndOfLine {
10390 stop_at_soft_wraps: false,
10391 },
10392 window,
10393 cx,
10394 );
10395 this.delete(&Delete, window, cx);
10396 });
10397 }
10398
10399 pub fn cut_to_end_of_line(
10400 &mut self,
10401 _: &CutToEndOfLine,
10402 window: &mut Window,
10403 cx: &mut Context<Self>,
10404 ) {
10405 self.transact(window, cx, |this, window, cx| {
10406 this.select_to_end_of_line(
10407 &SelectToEndOfLine {
10408 stop_at_soft_wraps: false,
10409 },
10410 window,
10411 cx,
10412 );
10413 this.cut(&Cut, window, cx);
10414 });
10415 }
10416
10417 pub fn move_to_start_of_paragraph(
10418 &mut self,
10419 _: &MoveToStartOfParagraph,
10420 window: &mut Window,
10421 cx: &mut Context<Self>,
10422 ) {
10423 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10424 cx.propagate();
10425 return;
10426 }
10427
10428 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10429 s.move_with(|map, selection| {
10430 selection.collapse_to(
10431 movement::start_of_paragraph(map, selection.head(), 1),
10432 SelectionGoal::None,
10433 )
10434 });
10435 })
10436 }
10437
10438 pub fn move_to_end_of_paragraph(
10439 &mut self,
10440 _: &MoveToEndOfParagraph,
10441 window: &mut Window,
10442 cx: &mut Context<Self>,
10443 ) {
10444 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10445 cx.propagate();
10446 return;
10447 }
10448
10449 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10450 s.move_with(|map, selection| {
10451 selection.collapse_to(
10452 movement::end_of_paragraph(map, selection.head(), 1),
10453 SelectionGoal::None,
10454 )
10455 });
10456 })
10457 }
10458
10459 pub fn select_to_start_of_paragraph(
10460 &mut self,
10461 _: &SelectToStartOfParagraph,
10462 window: &mut Window,
10463 cx: &mut Context<Self>,
10464 ) {
10465 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10466 cx.propagate();
10467 return;
10468 }
10469
10470 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10471 s.move_heads_with(|map, head, _| {
10472 (
10473 movement::start_of_paragraph(map, head, 1),
10474 SelectionGoal::None,
10475 )
10476 });
10477 })
10478 }
10479
10480 pub fn select_to_end_of_paragraph(
10481 &mut self,
10482 _: &SelectToEndOfParagraph,
10483 window: &mut Window,
10484 cx: &mut Context<Self>,
10485 ) {
10486 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10487 cx.propagate();
10488 return;
10489 }
10490
10491 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10492 s.move_heads_with(|map, head, _| {
10493 (
10494 movement::end_of_paragraph(map, head, 1),
10495 SelectionGoal::None,
10496 )
10497 });
10498 })
10499 }
10500
10501 pub fn move_to_start_of_excerpt(
10502 &mut self,
10503 _: &MoveToStartOfExcerpt,
10504 window: &mut Window,
10505 cx: &mut Context<Self>,
10506 ) {
10507 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10508 cx.propagate();
10509 return;
10510 }
10511
10512 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10513 s.move_with(|map, selection| {
10514 selection.collapse_to(
10515 movement::start_of_excerpt(
10516 map,
10517 selection.head(),
10518 workspace::searchable::Direction::Prev,
10519 ),
10520 SelectionGoal::None,
10521 )
10522 });
10523 })
10524 }
10525
10526 pub fn move_to_start_of_next_excerpt(
10527 &mut self,
10528 _: &MoveToStartOfNextExcerpt,
10529 window: &mut Window,
10530 cx: &mut Context<Self>,
10531 ) {
10532 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10533 cx.propagate();
10534 return;
10535 }
10536
10537 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10538 s.move_with(|map, selection| {
10539 selection.collapse_to(
10540 movement::start_of_excerpt(
10541 map,
10542 selection.head(),
10543 workspace::searchable::Direction::Next,
10544 ),
10545 SelectionGoal::None,
10546 )
10547 });
10548 })
10549 }
10550
10551 pub fn move_to_end_of_excerpt(
10552 &mut self,
10553 _: &MoveToEndOfExcerpt,
10554 window: &mut Window,
10555 cx: &mut Context<Self>,
10556 ) {
10557 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10558 cx.propagate();
10559 return;
10560 }
10561
10562 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10563 s.move_with(|map, selection| {
10564 selection.collapse_to(
10565 movement::end_of_excerpt(
10566 map,
10567 selection.head(),
10568 workspace::searchable::Direction::Next,
10569 ),
10570 SelectionGoal::None,
10571 )
10572 });
10573 })
10574 }
10575
10576 pub fn move_to_end_of_previous_excerpt(
10577 &mut self,
10578 _: &MoveToEndOfPreviousExcerpt,
10579 window: &mut Window,
10580 cx: &mut Context<Self>,
10581 ) {
10582 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10583 cx.propagate();
10584 return;
10585 }
10586
10587 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10588 s.move_with(|map, selection| {
10589 selection.collapse_to(
10590 movement::end_of_excerpt(
10591 map,
10592 selection.head(),
10593 workspace::searchable::Direction::Prev,
10594 ),
10595 SelectionGoal::None,
10596 )
10597 });
10598 })
10599 }
10600
10601 pub fn select_to_start_of_excerpt(
10602 &mut self,
10603 _: &SelectToStartOfExcerpt,
10604 window: &mut Window,
10605 cx: &mut Context<Self>,
10606 ) {
10607 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10608 cx.propagate();
10609 return;
10610 }
10611
10612 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10613 s.move_heads_with(|map, head, _| {
10614 (
10615 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10616 SelectionGoal::None,
10617 )
10618 });
10619 })
10620 }
10621
10622 pub fn select_to_start_of_next_excerpt(
10623 &mut self,
10624 _: &SelectToStartOfNextExcerpt,
10625 window: &mut Window,
10626 cx: &mut Context<Self>,
10627 ) {
10628 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10629 cx.propagate();
10630 return;
10631 }
10632
10633 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10634 s.move_heads_with(|map, head, _| {
10635 (
10636 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10637 SelectionGoal::None,
10638 )
10639 });
10640 })
10641 }
10642
10643 pub fn select_to_end_of_excerpt(
10644 &mut self,
10645 _: &SelectToEndOfExcerpt,
10646 window: &mut Window,
10647 cx: &mut Context<Self>,
10648 ) {
10649 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10650 cx.propagate();
10651 return;
10652 }
10653
10654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10655 s.move_heads_with(|map, head, _| {
10656 (
10657 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10658 SelectionGoal::None,
10659 )
10660 });
10661 })
10662 }
10663
10664 pub fn select_to_end_of_previous_excerpt(
10665 &mut self,
10666 _: &SelectToEndOfPreviousExcerpt,
10667 window: &mut Window,
10668 cx: &mut Context<Self>,
10669 ) {
10670 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10671 cx.propagate();
10672 return;
10673 }
10674
10675 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10676 s.move_heads_with(|map, head, _| {
10677 (
10678 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10679 SelectionGoal::None,
10680 )
10681 });
10682 })
10683 }
10684
10685 pub fn move_to_beginning(
10686 &mut self,
10687 _: &MoveToBeginning,
10688 window: &mut Window,
10689 cx: &mut Context<Self>,
10690 ) {
10691 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10692 cx.propagate();
10693 return;
10694 }
10695
10696 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10697 s.select_ranges(vec![0..0]);
10698 });
10699 }
10700
10701 pub fn select_to_beginning(
10702 &mut self,
10703 _: &SelectToBeginning,
10704 window: &mut Window,
10705 cx: &mut Context<Self>,
10706 ) {
10707 let mut selection = self.selections.last::<Point>(cx);
10708 selection.set_head(Point::zero(), SelectionGoal::None);
10709
10710 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10711 s.select(vec![selection]);
10712 });
10713 }
10714
10715 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10716 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10717 cx.propagate();
10718 return;
10719 }
10720
10721 let cursor = self.buffer.read(cx).read(cx).len();
10722 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10723 s.select_ranges(vec![cursor..cursor])
10724 });
10725 }
10726
10727 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10728 self.nav_history = nav_history;
10729 }
10730
10731 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10732 self.nav_history.as_ref()
10733 }
10734
10735 fn push_to_nav_history(
10736 &mut self,
10737 cursor_anchor: Anchor,
10738 new_position: Option<Point>,
10739 cx: &mut Context<Self>,
10740 ) {
10741 if let Some(nav_history) = self.nav_history.as_mut() {
10742 let buffer = self.buffer.read(cx).read(cx);
10743 let cursor_position = cursor_anchor.to_point(&buffer);
10744 let scroll_state = self.scroll_manager.anchor();
10745 let scroll_top_row = scroll_state.top_row(&buffer);
10746 drop(buffer);
10747
10748 if let Some(new_position) = new_position {
10749 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10750 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10751 return;
10752 }
10753 }
10754
10755 nav_history.push(
10756 Some(NavigationData {
10757 cursor_anchor,
10758 cursor_position,
10759 scroll_anchor: scroll_state,
10760 scroll_top_row,
10761 }),
10762 cx,
10763 );
10764 }
10765 }
10766
10767 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10768 let buffer = self.buffer.read(cx).snapshot(cx);
10769 let mut selection = self.selections.first::<usize>(cx);
10770 selection.set_head(buffer.len(), SelectionGoal::None);
10771 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10772 s.select(vec![selection]);
10773 });
10774 }
10775
10776 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10777 let end = self.buffer.read(cx).read(cx).len();
10778 self.change_selections(None, window, cx, |s| {
10779 s.select_ranges(vec![0..end]);
10780 });
10781 }
10782
10783 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10784 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10785 let mut selections = self.selections.all::<Point>(cx);
10786 let max_point = display_map.buffer_snapshot.max_point();
10787 for selection in &mut selections {
10788 let rows = selection.spanned_rows(true, &display_map);
10789 selection.start = Point::new(rows.start.0, 0);
10790 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10791 selection.reversed = false;
10792 }
10793 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10794 s.select(selections);
10795 });
10796 }
10797
10798 pub fn split_selection_into_lines(
10799 &mut self,
10800 _: &SplitSelectionIntoLines,
10801 window: &mut Window,
10802 cx: &mut Context<Self>,
10803 ) {
10804 let selections = self
10805 .selections
10806 .all::<Point>(cx)
10807 .into_iter()
10808 .map(|selection| selection.start..selection.end)
10809 .collect::<Vec<_>>();
10810 self.unfold_ranges(&selections, true, true, cx);
10811
10812 let mut new_selection_ranges = Vec::new();
10813 {
10814 let buffer = self.buffer.read(cx).read(cx);
10815 for selection in selections {
10816 for row in selection.start.row..selection.end.row {
10817 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10818 new_selection_ranges.push(cursor..cursor);
10819 }
10820
10821 let is_multiline_selection = selection.start.row != selection.end.row;
10822 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10823 // so this action feels more ergonomic when paired with other selection operations
10824 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10825 if !should_skip_last {
10826 new_selection_ranges.push(selection.end..selection.end);
10827 }
10828 }
10829 }
10830 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10831 s.select_ranges(new_selection_ranges);
10832 });
10833 }
10834
10835 pub fn add_selection_above(
10836 &mut self,
10837 _: &AddSelectionAbove,
10838 window: &mut Window,
10839 cx: &mut Context<Self>,
10840 ) {
10841 self.add_selection(true, window, cx);
10842 }
10843
10844 pub fn add_selection_below(
10845 &mut self,
10846 _: &AddSelectionBelow,
10847 window: &mut Window,
10848 cx: &mut Context<Self>,
10849 ) {
10850 self.add_selection(false, window, cx);
10851 }
10852
10853 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10854 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10855 let mut selections = self.selections.all::<Point>(cx);
10856 let text_layout_details = self.text_layout_details(window);
10857 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10858 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10859 let range = oldest_selection.display_range(&display_map).sorted();
10860
10861 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10862 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10863 let positions = start_x.min(end_x)..start_x.max(end_x);
10864
10865 selections.clear();
10866 let mut stack = Vec::new();
10867 for row in range.start.row().0..=range.end.row().0 {
10868 if let Some(selection) = self.selections.build_columnar_selection(
10869 &display_map,
10870 DisplayRow(row),
10871 &positions,
10872 oldest_selection.reversed,
10873 &text_layout_details,
10874 ) {
10875 stack.push(selection.id);
10876 selections.push(selection);
10877 }
10878 }
10879
10880 if above {
10881 stack.reverse();
10882 }
10883
10884 AddSelectionsState { above, stack }
10885 });
10886
10887 let last_added_selection = *state.stack.last().unwrap();
10888 let mut new_selections = Vec::new();
10889 if above == state.above {
10890 let end_row = if above {
10891 DisplayRow(0)
10892 } else {
10893 display_map.max_point().row()
10894 };
10895
10896 'outer: for selection in selections {
10897 if selection.id == last_added_selection {
10898 let range = selection.display_range(&display_map).sorted();
10899 debug_assert_eq!(range.start.row(), range.end.row());
10900 let mut row = range.start.row();
10901 let positions =
10902 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10903 px(start)..px(end)
10904 } else {
10905 let start_x =
10906 display_map.x_for_display_point(range.start, &text_layout_details);
10907 let end_x =
10908 display_map.x_for_display_point(range.end, &text_layout_details);
10909 start_x.min(end_x)..start_x.max(end_x)
10910 };
10911
10912 while row != end_row {
10913 if above {
10914 row.0 -= 1;
10915 } else {
10916 row.0 += 1;
10917 }
10918
10919 if let Some(new_selection) = self.selections.build_columnar_selection(
10920 &display_map,
10921 row,
10922 &positions,
10923 selection.reversed,
10924 &text_layout_details,
10925 ) {
10926 state.stack.push(new_selection.id);
10927 if above {
10928 new_selections.push(new_selection);
10929 new_selections.push(selection);
10930 } else {
10931 new_selections.push(selection);
10932 new_selections.push(new_selection);
10933 }
10934
10935 continue 'outer;
10936 }
10937 }
10938 }
10939
10940 new_selections.push(selection);
10941 }
10942 } else {
10943 new_selections = selections;
10944 new_selections.retain(|s| s.id != last_added_selection);
10945 state.stack.pop();
10946 }
10947
10948 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10949 s.select(new_selections);
10950 });
10951 if state.stack.len() > 1 {
10952 self.add_selections_state = Some(state);
10953 }
10954 }
10955
10956 pub fn select_next_match_internal(
10957 &mut self,
10958 display_map: &DisplaySnapshot,
10959 replace_newest: bool,
10960 autoscroll: Option<Autoscroll>,
10961 window: &mut Window,
10962 cx: &mut Context<Self>,
10963 ) -> Result<()> {
10964 fn select_next_match_ranges(
10965 this: &mut Editor,
10966 range: Range<usize>,
10967 replace_newest: bool,
10968 auto_scroll: Option<Autoscroll>,
10969 window: &mut Window,
10970 cx: &mut Context<Editor>,
10971 ) {
10972 this.unfold_ranges(&[range.clone()], false, true, cx);
10973 this.change_selections(auto_scroll, window, cx, |s| {
10974 if replace_newest {
10975 s.delete(s.newest_anchor().id);
10976 }
10977 s.insert_range(range.clone());
10978 });
10979 }
10980
10981 let buffer = &display_map.buffer_snapshot;
10982 let mut selections = self.selections.all::<usize>(cx);
10983 if let Some(mut select_next_state) = self.select_next_state.take() {
10984 let query = &select_next_state.query;
10985 if !select_next_state.done {
10986 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10987 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10988 let mut next_selected_range = None;
10989
10990 let bytes_after_last_selection =
10991 buffer.bytes_in_range(last_selection.end..buffer.len());
10992 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10993 let query_matches = query
10994 .stream_find_iter(bytes_after_last_selection)
10995 .map(|result| (last_selection.end, result))
10996 .chain(
10997 query
10998 .stream_find_iter(bytes_before_first_selection)
10999 .map(|result| (0, result)),
11000 );
11001
11002 for (start_offset, query_match) in query_matches {
11003 let query_match = query_match.unwrap(); // can only fail due to I/O
11004 let offset_range =
11005 start_offset + query_match.start()..start_offset + query_match.end();
11006 let display_range = offset_range.start.to_display_point(display_map)
11007 ..offset_range.end.to_display_point(display_map);
11008
11009 if !select_next_state.wordwise
11010 || (!movement::is_inside_word(display_map, display_range.start)
11011 && !movement::is_inside_word(display_map, display_range.end))
11012 {
11013 // TODO: This is n^2, because we might check all the selections
11014 if !selections
11015 .iter()
11016 .any(|selection| selection.range().overlaps(&offset_range))
11017 {
11018 next_selected_range = Some(offset_range);
11019 break;
11020 }
11021 }
11022 }
11023
11024 if let Some(next_selected_range) = next_selected_range {
11025 select_next_match_ranges(
11026 self,
11027 next_selected_range,
11028 replace_newest,
11029 autoscroll,
11030 window,
11031 cx,
11032 );
11033 } else {
11034 select_next_state.done = true;
11035 }
11036 }
11037
11038 self.select_next_state = Some(select_next_state);
11039 } else {
11040 let mut only_carets = true;
11041 let mut same_text_selected = true;
11042 let mut selected_text = None;
11043
11044 let mut selections_iter = selections.iter().peekable();
11045 while let Some(selection) = selections_iter.next() {
11046 if selection.start != selection.end {
11047 only_carets = false;
11048 }
11049
11050 if same_text_selected {
11051 if selected_text.is_none() {
11052 selected_text =
11053 Some(buffer.text_for_range(selection.range()).collect::<String>());
11054 }
11055
11056 if let Some(next_selection) = selections_iter.peek() {
11057 if next_selection.range().len() == selection.range().len() {
11058 let next_selected_text = buffer
11059 .text_for_range(next_selection.range())
11060 .collect::<String>();
11061 if Some(next_selected_text) != selected_text {
11062 same_text_selected = false;
11063 selected_text = None;
11064 }
11065 } else {
11066 same_text_selected = false;
11067 selected_text = None;
11068 }
11069 }
11070 }
11071 }
11072
11073 if only_carets {
11074 for selection in &mut selections {
11075 let word_range = movement::surrounding_word(
11076 display_map,
11077 selection.start.to_display_point(display_map),
11078 );
11079 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11080 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11081 selection.goal = SelectionGoal::None;
11082 selection.reversed = false;
11083 select_next_match_ranges(
11084 self,
11085 selection.start..selection.end,
11086 replace_newest,
11087 autoscroll,
11088 window,
11089 cx,
11090 );
11091 }
11092
11093 if selections.len() == 1 {
11094 let selection = selections
11095 .last()
11096 .expect("ensured that there's only one selection");
11097 let query = buffer
11098 .text_for_range(selection.start..selection.end)
11099 .collect::<String>();
11100 let is_empty = query.is_empty();
11101 let select_state = SelectNextState {
11102 query: AhoCorasick::new(&[query])?,
11103 wordwise: true,
11104 done: is_empty,
11105 };
11106 self.select_next_state = Some(select_state);
11107 } else {
11108 self.select_next_state = None;
11109 }
11110 } else if let Some(selected_text) = selected_text {
11111 self.select_next_state = Some(SelectNextState {
11112 query: AhoCorasick::new(&[selected_text])?,
11113 wordwise: false,
11114 done: false,
11115 });
11116 self.select_next_match_internal(
11117 display_map,
11118 replace_newest,
11119 autoscroll,
11120 window,
11121 cx,
11122 )?;
11123 }
11124 }
11125 Ok(())
11126 }
11127
11128 pub fn select_all_matches(
11129 &mut self,
11130 _action: &SelectAllMatches,
11131 window: &mut Window,
11132 cx: &mut Context<Self>,
11133 ) -> Result<()> {
11134 self.push_to_selection_history();
11135 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11136
11137 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11138 let Some(select_next_state) = self.select_next_state.as_mut() else {
11139 return Ok(());
11140 };
11141 if select_next_state.done {
11142 return Ok(());
11143 }
11144
11145 let mut new_selections = self.selections.all::<usize>(cx);
11146
11147 let buffer = &display_map.buffer_snapshot;
11148 let query_matches = select_next_state
11149 .query
11150 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11151
11152 for query_match in query_matches {
11153 let query_match = query_match.unwrap(); // can only fail due to I/O
11154 let offset_range = query_match.start()..query_match.end();
11155 let display_range = offset_range.start.to_display_point(&display_map)
11156 ..offset_range.end.to_display_point(&display_map);
11157
11158 if !select_next_state.wordwise
11159 || (!movement::is_inside_word(&display_map, display_range.start)
11160 && !movement::is_inside_word(&display_map, display_range.end))
11161 {
11162 self.selections.change_with(cx, |selections| {
11163 new_selections.push(Selection {
11164 id: selections.new_selection_id(),
11165 start: offset_range.start,
11166 end: offset_range.end,
11167 reversed: false,
11168 goal: SelectionGoal::None,
11169 });
11170 });
11171 }
11172 }
11173
11174 new_selections.sort_by_key(|selection| selection.start);
11175 let mut ix = 0;
11176 while ix + 1 < new_selections.len() {
11177 let current_selection = &new_selections[ix];
11178 let next_selection = &new_selections[ix + 1];
11179 if current_selection.range().overlaps(&next_selection.range()) {
11180 if current_selection.id < next_selection.id {
11181 new_selections.remove(ix + 1);
11182 } else {
11183 new_selections.remove(ix);
11184 }
11185 } else {
11186 ix += 1;
11187 }
11188 }
11189
11190 let reversed = self.selections.oldest::<usize>(cx).reversed;
11191
11192 for selection in new_selections.iter_mut() {
11193 selection.reversed = reversed;
11194 }
11195
11196 select_next_state.done = true;
11197 self.unfold_ranges(
11198 &new_selections
11199 .iter()
11200 .map(|selection| selection.range())
11201 .collect::<Vec<_>>(),
11202 false,
11203 false,
11204 cx,
11205 );
11206 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11207 selections.select(new_selections)
11208 });
11209
11210 Ok(())
11211 }
11212
11213 pub fn select_next(
11214 &mut self,
11215 action: &SelectNext,
11216 window: &mut Window,
11217 cx: &mut Context<Self>,
11218 ) -> Result<()> {
11219 self.push_to_selection_history();
11220 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11221 self.select_next_match_internal(
11222 &display_map,
11223 action.replace_newest,
11224 Some(Autoscroll::newest()),
11225 window,
11226 cx,
11227 )?;
11228 Ok(())
11229 }
11230
11231 pub fn select_previous(
11232 &mut self,
11233 action: &SelectPrevious,
11234 window: &mut Window,
11235 cx: &mut Context<Self>,
11236 ) -> Result<()> {
11237 self.push_to_selection_history();
11238 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11239 let buffer = &display_map.buffer_snapshot;
11240 let mut selections = self.selections.all::<usize>(cx);
11241 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11242 let query = &select_prev_state.query;
11243 if !select_prev_state.done {
11244 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11245 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11246 let mut next_selected_range = None;
11247 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11248 let bytes_before_last_selection =
11249 buffer.reversed_bytes_in_range(0..last_selection.start);
11250 let bytes_after_first_selection =
11251 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11252 let query_matches = query
11253 .stream_find_iter(bytes_before_last_selection)
11254 .map(|result| (last_selection.start, result))
11255 .chain(
11256 query
11257 .stream_find_iter(bytes_after_first_selection)
11258 .map(|result| (buffer.len(), result)),
11259 );
11260 for (end_offset, query_match) in query_matches {
11261 let query_match = query_match.unwrap(); // can only fail due to I/O
11262 let offset_range =
11263 end_offset - query_match.end()..end_offset - query_match.start();
11264 let display_range = offset_range.start.to_display_point(&display_map)
11265 ..offset_range.end.to_display_point(&display_map);
11266
11267 if !select_prev_state.wordwise
11268 || (!movement::is_inside_word(&display_map, display_range.start)
11269 && !movement::is_inside_word(&display_map, display_range.end))
11270 {
11271 next_selected_range = Some(offset_range);
11272 break;
11273 }
11274 }
11275
11276 if let Some(next_selected_range) = next_selected_range {
11277 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11278 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11279 if action.replace_newest {
11280 s.delete(s.newest_anchor().id);
11281 }
11282 s.insert_range(next_selected_range);
11283 });
11284 } else {
11285 select_prev_state.done = true;
11286 }
11287 }
11288
11289 self.select_prev_state = Some(select_prev_state);
11290 } else {
11291 let mut only_carets = true;
11292 let mut same_text_selected = true;
11293 let mut selected_text = None;
11294
11295 let mut selections_iter = selections.iter().peekable();
11296 while let Some(selection) = selections_iter.next() {
11297 if selection.start != selection.end {
11298 only_carets = false;
11299 }
11300
11301 if same_text_selected {
11302 if selected_text.is_none() {
11303 selected_text =
11304 Some(buffer.text_for_range(selection.range()).collect::<String>());
11305 }
11306
11307 if let Some(next_selection) = selections_iter.peek() {
11308 if next_selection.range().len() == selection.range().len() {
11309 let next_selected_text = buffer
11310 .text_for_range(next_selection.range())
11311 .collect::<String>();
11312 if Some(next_selected_text) != selected_text {
11313 same_text_selected = false;
11314 selected_text = None;
11315 }
11316 } else {
11317 same_text_selected = false;
11318 selected_text = None;
11319 }
11320 }
11321 }
11322 }
11323
11324 if only_carets {
11325 for selection in &mut selections {
11326 let word_range = movement::surrounding_word(
11327 &display_map,
11328 selection.start.to_display_point(&display_map),
11329 );
11330 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11331 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11332 selection.goal = SelectionGoal::None;
11333 selection.reversed = false;
11334 }
11335 if selections.len() == 1 {
11336 let selection = selections
11337 .last()
11338 .expect("ensured that there's only one selection");
11339 let query = buffer
11340 .text_for_range(selection.start..selection.end)
11341 .collect::<String>();
11342 let is_empty = query.is_empty();
11343 let select_state = SelectNextState {
11344 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11345 wordwise: true,
11346 done: is_empty,
11347 };
11348 self.select_prev_state = Some(select_state);
11349 } else {
11350 self.select_prev_state = None;
11351 }
11352
11353 self.unfold_ranges(
11354 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11355 false,
11356 true,
11357 cx,
11358 );
11359 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11360 s.select(selections);
11361 });
11362 } else if let Some(selected_text) = selected_text {
11363 self.select_prev_state = Some(SelectNextState {
11364 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11365 wordwise: false,
11366 done: false,
11367 });
11368 self.select_previous(action, window, cx)?;
11369 }
11370 }
11371 Ok(())
11372 }
11373
11374 pub fn toggle_comments(
11375 &mut self,
11376 action: &ToggleComments,
11377 window: &mut Window,
11378 cx: &mut Context<Self>,
11379 ) {
11380 if self.read_only(cx) {
11381 return;
11382 }
11383 let text_layout_details = &self.text_layout_details(window);
11384 self.transact(window, cx, |this, window, cx| {
11385 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11386 let mut edits = Vec::new();
11387 let mut selection_edit_ranges = Vec::new();
11388 let mut last_toggled_row = None;
11389 let snapshot = this.buffer.read(cx).read(cx);
11390 let empty_str: Arc<str> = Arc::default();
11391 let mut suffixes_inserted = Vec::new();
11392 let ignore_indent = action.ignore_indent;
11393
11394 fn comment_prefix_range(
11395 snapshot: &MultiBufferSnapshot,
11396 row: MultiBufferRow,
11397 comment_prefix: &str,
11398 comment_prefix_whitespace: &str,
11399 ignore_indent: bool,
11400 ) -> Range<Point> {
11401 let indent_size = if ignore_indent {
11402 0
11403 } else {
11404 snapshot.indent_size_for_line(row).len
11405 };
11406
11407 let start = Point::new(row.0, indent_size);
11408
11409 let mut line_bytes = snapshot
11410 .bytes_in_range(start..snapshot.max_point())
11411 .flatten()
11412 .copied();
11413
11414 // If this line currently begins with the line comment prefix, then record
11415 // the range containing the prefix.
11416 if line_bytes
11417 .by_ref()
11418 .take(comment_prefix.len())
11419 .eq(comment_prefix.bytes())
11420 {
11421 // Include any whitespace that matches the comment prefix.
11422 let matching_whitespace_len = line_bytes
11423 .zip(comment_prefix_whitespace.bytes())
11424 .take_while(|(a, b)| a == b)
11425 .count() as u32;
11426 let end = Point::new(
11427 start.row,
11428 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11429 );
11430 start..end
11431 } else {
11432 start..start
11433 }
11434 }
11435
11436 fn comment_suffix_range(
11437 snapshot: &MultiBufferSnapshot,
11438 row: MultiBufferRow,
11439 comment_suffix: &str,
11440 comment_suffix_has_leading_space: bool,
11441 ) -> Range<Point> {
11442 let end = Point::new(row.0, snapshot.line_len(row));
11443 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11444
11445 let mut line_end_bytes = snapshot
11446 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11447 .flatten()
11448 .copied();
11449
11450 let leading_space_len = if suffix_start_column > 0
11451 && line_end_bytes.next() == Some(b' ')
11452 && comment_suffix_has_leading_space
11453 {
11454 1
11455 } else {
11456 0
11457 };
11458
11459 // If this line currently begins with the line comment prefix, then record
11460 // the range containing the prefix.
11461 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11462 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11463 start..end
11464 } else {
11465 end..end
11466 }
11467 }
11468
11469 // TODO: Handle selections that cross excerpts
11470 for selection in &mut selections {
11471 let start_column = snapshot
11472 .indent_size_for_line(MultiBufferRow(selection.start.row))
11473 .len;
11474 let language = if let Some(language) =
11475 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11476 {
11477 language
11478 } else {
11479 continue;
11480 };
11481
11482 selection_edit_ranges.clear();
11483
11484 // If multiple selections contain a given row, avoid processing that
11485 // row more than once.
11486 let mut start_row = MultiBufferRow(selection.start.row);
11487 if last_toggled_row == Some(start_row) {
11488 start_row = start_row.next_row();
11489 }
11490 let end_row =
11491 if selection.end.row > selection.start.row && selection.end.column == 0 {
11492 MultiBufferRow(selection.end.row - 1)
11493 } else {
11494 MultiBufferRow(selection.end.row)
11495 };
11496 last_toggled_row = Some(end_row);
11497
11498 if start_row > end_row {
11499 continue;
11500 }
11501
11502 // If the language has line comments, toggle those.
11503 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11504
11505 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11506 if ignore_indent {
11507 full_comment_prefixes = full_comment_prefixes
11508 .into_iter()
11509 .map(|s| Arc::from(s.trim_end()))
11510 .collect();
11511 }
11512
11513 if !full_comment_prefixes.is_empty() {
11514 let first_prefix = full_comment_prefixes
11515 .first()
11516 .expect("prefixes is non-empty");
11517 let prefix_trimmed_lengths = full_comment_prefixes
11518 .iter()
11519 .map(|p| p.trim_end_matches(' ').len())
11520 .collect::<SmallVec<[usize; 4]>>();
11521
11522 let mut all_selection_lines_are_comments = true;
11523
11524 for row in start_row.0..=end_row.0 {
11525 let row = MultiBufferRow(row);
11526 if start_row < end_row && snapshot.is_line_blank(row) {
11527 continue;
11528 }
11529
11530 let prefix_range = full_comment_prefixes
11531 .iter()
11532 .zip(prefix_trimmed_lengths.iter().copied())
11533 .map(|(prefix, trimmed_prefix_len)| {
11534 comment_prefix_range(
11535 snapshot.deref(),
11536 row,
11537 &prefix[..trimmed_prefix_len],
11538 &prefix[trimmed_prefix_len..],
11539 ignore_indent,
11540 )
11541 })
11542 .max_by_key(|range| range.end.column - range.start.column)
11543 .expect("prefixes is non-empty");
11544
11545 if prefix_range.is_empty() {
11546 all_selection_lines_are_comments = false;
11547 }
11548
11549 selection_edit_ranges.push(prefix_range);
11550 }
11551
11552 if all_selection_lines_are_comments {
11553 edits.extend(
11554 selection_edit_ranges
11555 .iter()
11556 .cloned()
11557 .map(|range| (range, empty_str.clone())),
11558 );
11559 } else {
11560 let min_column = selection_edit_ranges
11561 .iter()
11562 .map(|range| range.start.column)
11563 .min()
11564 .unwrap_or(0);
11565 edits.extend(selection_edit_ranges.iter().map(|range| {
11566 let position = Point::new(range.start.row, min_column);
11567 (position..position, first_prefix.clone())
11568 }));
11569 }
11570 } else if let Some((full_comment_prefix, comment_suffix)) =
11571 language.block_comment_delimiters()
11572 {
11573 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11574 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11575 let prefix_range = comment_prefix_range(
11576 snapshot.deref(),
11577 start_row,
11578 comment_prefix,
11579 comment_prefix_whitespace,
11580 ignore_indent,
11581 );
11582 let suffix_range = comment_suffix_range(
11583 snapshot.deref(),
11584 end_row,
11585 comment_suffix.trim_start_matches(' '),
11586 comment_suffix.starts_with(' '),
11587 );
11588
11589 if prefix_range.is_empty() || suffix_range.is_empty() {
11590 edits.push((
11591 prefix_range.start..prefix_range.start,
11592 full_comment_prefix.clone(),
11593 ));
11594 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11595 suffixes_inserted.push((end_row, comment_suffix.len()));
11596 } else {
11597 edits.push((prefix_range, empty_str.clone()));
11598 edits.push((suffix_range, empty_str.clone()));
11599 }
11600 } else {
11601 continue;
11602 }
11603 }
11604
11605 drop(snapshot);
11606 this.buffer.update(cx, |buffer, cx| {
11607 buffer.edit(edits, None, cx);
11608 });
11609
11610 // Adjust selections so that they end before any comment suffixes that
11611 // were inserted.
11612 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11613 let mut selections = this.selections.all::<Point>(cx);
11614 let snapshot = this.buffer.read(cx).read(cx);
11615 for selection in &mut selections {
11616 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11617 match row.cmp(&MultiBufferRow(selection.end.row)) {
11618 Ordering::Less => {
11619 suffixes_inserted.next();
11620 continue;
11621 }
11622 Ordering::Greater => break,
11623 Ordering::Equal => {
11624 if selection.end.column == snapshot.line_len(row) {
11625 if selection.is_empty() {
11626 selection.start.column -= suffix_len as u32;
11627 }
11628 selection.end.column -= suffix_len as u32;
11629 }
11630 break;
11631 }
11632 }
11633 }
11634 }
11635
11636 drop(snapshot);
11637 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11638 s.select(selections)
11639 });
11640
11641 let selections = this.selections.all::<Point>(cx);
11642 let selections_on_single_row = selections.windows(2).all(|selections| {
11643 selections[0].start.row == selections[1].start.row
11644 && selections[0].end.row == selections[1].end.row
11645 && selections[0].start.row == selections[0].end.row
11646 });
11647 let selections_selecting = selections
11648 .iter()
11649 .any(|selection| selection.start != selection.end);
11650 let advance_downwards = action.advance_downwards
11651 && selections_on_single_row
11652 && !selections_selecting
11653 && !matches!(this.mode, EditorMode::SingleLine { .. });
11654
11655 if advance_downwards {
11656 let snapshot = this.buffer.read(cx).snapshot(cx);
11657
11658 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11659 s.move_cursors_with(|display_snapshot, display_point, _| {
11660 let mut point = display_point.to_point(display_snapshot);
11661 point.row += 1;
11662 point = snapshot.clip_point(point, Bias::Left);
11663 let display_point = point.to_display_point(display_snapshot);
11664 let goal = SelectionGoal::HorizontalPosition(
11665 display_snapshot
11666 .x_for_display_point(display_point, text_layout_details)
11667 .into(),
11668 );
11669 (display_point, goal)
11670 })
11671 });
11672 }
11673 });
11674 }
11675
11676 pub fn select_enclosing_symbol(
11677 &mut self,
11678 _: &SelectEnclosingSymbol,
11679 window: &mut Window,
11680 cx: &mut Context<Self>,
11681 ) {
11682 let buffer = self.buffer.read(cx).snapshot(cx);
11683 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11684
11685 fn update_selection(
11686 selection: &Selection<usize>,
11687 buffer_snap: &MultiBufferSnapshot,
11688 ) -> Option<Selection<usize>> {
11689 let cursor = selection.head();
11690 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11691 for symbol in symbols.iter().rev() {
11692 let start = symbol.range.start.to_offset(buffer_snap);
11693 let end = symbol.range.end.to_offset(buffer_snap);
11694 let new_range = start..end;
11695 if start < selection.start || end > selection.end {
11696 return Some(Selection {
11697 id: selection.id,
11698 start: new_range.start,
11699 end: new_range.end,
11700 goal: SelectionGoal::None,
11701 reversed: selection.reversed,
11702 });
11703 }
11704 }
11705 None
11706 }
11707
11708 let mut selected_larger_symbol = false;
11709 let new_selections = old_selections
11710 .iter()
11711 .map(|selection| match update_selection(selection, &buffer) {
11712 Some(new_selection) => {
11713 if new_selection.range() != selection.range() {
11714 selected_larger_symbol = true;
11715 }
11716 new_selection
11717 }
11718 None => selection.clone(),
11719 })
11720 .collect::<Vec<_>>();
11721
11722 if selected_larger_symbol {
11723 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11724 s.select(new_selections);
11725 });
11726 }
11727 }
11728
11729 pub fn select_larger_syntax_node(
11730 &mut self,
11731 _: &SelectLargerSyntaxNode,
11732 window: &mut Window,
11733 cx: &mut Context<Self>,
11734 ) {
11735 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11736 let buffer = self.buffer.read(cx).snapshot(cx);
11737 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11738
11739 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11740 let mut selected_larger_node = false;
11741 let new_selections = old_selections
11742 .iter()
11743 .map(|selection| {
11744 let old_range = selection.start..selection.end;
11745 let mut new_range = old_range.clone();
11746 let mut new_node = None;
11747 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11748 {
11749 new_node = Some(node);
11750 new_range = match containing_range {
11751 MultiOrSingleBufferOffsetRange::Single(_) => break,
11752 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11753 };
11754 if !display_map.intersects_fold(new_range.start)
11755 && !display_map.intersects_fold(new_range.end)
11756 {
11757 break;
11758 }
11759 }
11760
11761 if let Some(node) = new_node {
11762 // Log the ancestor, to support using this action as a way to explore TreeSitter
11763 // nodes. Parent and grandparent are also logged because this operation will not
11764 // visit nodes that have the same range as their parent.
11765 log::info!("Node: {node:?}");
11766 let parent = node.parent();
11767 log::info!("Parent: {parent:?}");
11768 let grandparent = parent.and_then(|x| x.parent());
11769 log::info!("Grandparent: {grandparent:?}");
11770 }
11771
11772 selected_larger_node |= new_range != old_range;
11773 Selection {
11774 id: selection.id,
11775 start: new_range.start,
11776 end: new_range.end,
11777 goal: SelectionGoal::None,
11778 reversed: selection.reversed,
11779 }
11780 })
11781 .collect::<Vec<_>>();
11782
11783 if selected_larger_node {
11784 stack.push(old_selections);
11785 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11786 s.select(new_selections);
11787 });
11788 }
11789 self.select_larger_syntax_node_stack = stack;
11790 }
11791
11792 pub fn select_smaller_syntax_node(
11793 &mut self,
11794 _: &SelectSmallerSyntaxNode,
11795 window: &mut Window,
11796 cx: &mut Context<Self>,
11797 ) {
11798 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11799 if let Some(selections) = stack.pop() {
11800 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11801 s.select(selections.to_vec());
11802 });
11803 }
11804 self.select_larger_syntax_node_stack = stack;
11805 }
11806
11807 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11808 if !EditorSettings::get_global(cx).gutter.runnables {
11809 self.clear_tasks();
11810 return Task::ready(());
11811 }
11812 let project = self.project.as_ref().map(Entity::downgrade);
11813 cx.spawn_in(window, |this, mut cx| async move {
11814 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11815 let Some(project) = project.and_then(|p| p.upgrade()) else {
11816 return;
11817 };
11818 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11819 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11820 }) else {
11821 return;
11822 };
11823
11824 let hide_runnables = project
11825 .update(&mut cx, |project, cx| {
11826 // Do not display any test indicators in non-dev server remote projects.
11827 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11828 })
11829 .unwrap_or(true);
11830 if hide_runnables {
11831 return;
11832 }
11833 let new_rows =
11834 cx.background_spawn({
11835 let snapshot = display_snapshot.clone();
11836 async move {
11837 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11838 }
11839 })
11840 .await;
11841
11842 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11843 this.update(&mut cx, |this, _| {
11844 this.clear_tasks();
11845 for (key, value) in rows {
11846 this.insert_tasks(key, value);
11847 }
11848 })
11849 .ok();
11850 })
11851 }
11852 fn fetch_runnable_ranges(
11853 snapshot: &DisplaySnapshot,
11854 range: Range<Anchor>,
11855 ) -> Vec<language::RunnableRange> {
11856 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11857 }
11858
11859 fn runnable_rows(
11860 project: Entity<Project>,
11861 snapshot: DisplaySnapshot,
11862 runnable_ranges: Vec<RunnableRange>,
11863 mut cx: AsyncWindowContext,
11864 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11865 runnable_ranges
11866 .into_iter()
11867 .filter_map(|mut runnable| {
11868 let tasks = cx
11869 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11870 .ok()?;
11871 if tasks.is_empty() {
11872 return None;
11873 }
11874
11875 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11876
11877 let row = snapshot
11878 .buffer_snapshot
11879 .buffer_line_for_row(MultiBufferRow(point.row))?
11880 .1
11881 .start
11882 .row;
11883
11884 let context_range =
11885 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11886 Some((
11887 (runnable.buffer_id, row),
11888 RunnableTasks {
11889 templates: tasks,
11890 offset: snapshot
11891 .buffer_snapshot
11892 .anchor_before(runnable.run_range.start),
11893 context_range,
11894 column: point.column,
11895 extra_variables: runnable.extra_captures,
11896 },
11897 ))
11898 })
11899 .collect()
11900 }
11901
11902 fn templates_with_tags(
11903 project: &Entity<Project>,
11904 runnable: &mut Runnable,
11905 cx: &mut App,
11906 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11907 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11908 let (worktree_id, file) = project
11909 .buffer_for_id(runnable.buffer, cx)
11910 .and_then(|buffer| buffer.read(cx).file())
11911 .map(|file| (file.worktree_id(cx), file.clone()))
11912 .unzip();
11913
11914 (
11915 project.task_store().read(cx).task_inventory().cloned(),
11916 worktree_id,
11917 file,
11918 )
11919 });
11920
11921 let tags = mem::take(&mut runnable.tags);
11922 let mut tags: Vec<_> = tags
11923 .into_iter()
11924 .flat_map(|tag| {
11925 let tag = tag.0.clone();
11926 inventory
11927 .as_ref()
11928 .into_iter()
11929 .flat_map(|inventory| {
11930 inventory.read(cx).list_tasks(
11931 file.clone(),
11932 Some(runnable.language.clone()),
11933 worktree_id,
11934 cx,
11935 )
11936 })
11937 .filter(move |(_, template)| {
11938 template.tags.iter().any(|source_tag| source_tag == &tag)
11939 })
11940 })
11941 .sorted_by_key(|(kind, _)| kind.to_owned())
11942 .collect();
11943 if let Some((leading_tag_source, _)) = tags.first() {
11944 // Strongest source wins; if we have worktree tag binding, prefer that to
11945 // global and language bindings;
11946 // if we have a global binding, prefer that to language binding.
11947 let first_mismatch = tags
11948 .iter()
11949 .position(|(tag_source, _)| tag_source != leading_tag_source);
11950 if let Some(index) = first_mismatch {
11951 tags.truncate(index);
11952 }
11953 }
11954
11955 tags
11956 }
11957
11958 pub fn move_to_enclosing_bracket(
11959 &mut self,
11960 _: &MoveToEnclosingBracket,
11961 window: &mut Window,
11962 cx: &mut Context<Self>,
11963 ) {
11964 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11965 s.move_offsets_with(|snapshot, selection| {
11966 let Some(enclosing_bracket_ranges) =
11967 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11968 else {
11969 return;
11970 };
11971
11972 let mut best_length = usize::MAX;
11973 let mut best_inside = false;
11974 let mut best_in_bracket_range = false;
11975 let mut best_destination = None;
11976 for (open, close) in enclosing_bracket_ranges {
11977 let close = close.to_inclusive();
11978 let length = close.end() - open.start;
11979 let inside = selection.start >= open.end && selection.end <= *close.start();
11980 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11981 || close.contains(&selection.head());
11982
11983 // If best is next to a bracket and current isn't, skip
11984 if !in_bracket_range && best_in_bracket_range {
11985 continue;
11986 }
11987
11988 // Prefer smaller lengths unless best is inside and current isn't
11989 if length > best_length && (best_inside || !inside) {
11990 continue;
11991 }
11992
11993 best_length = length;
11994 best_inside = inside;
11995 best_in_bracket_range = in_bracket_range;
11996 best_destination = Some(
11997 if close.contains(&selection.start) && close.contains(&selection.end) {
11998 if inside {
11999 open.end
12000 } else {
12001 open.start
12002 }
12003 } else if inside {
12004 *close.start()
12005 } else {
12006 *close.end()
12007 },
12008 );
12009 }
12010
12011 if let Some(destination) = best_destination {
12012 selection.collapse_to(destination, SelectionGoal::None);
12013 }
12014 })
12015 });
12016 }
12017
12018 pub fn undo_selection(
12019 &mut self,
12020 _: &UndoSelection,
12021 window: &mut Window,
12022 cx: &mut Context<Self>,
12023 ) {
12024 self.end_selection(window, cx);
12025 self.selection_history.mode = SelectionHistoryMode::Undoing;
12026 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12027 self.change_selections(None, window, cx, |s| {
12028 s.select_anchors(entry.selections.to_vec())
12029 });
12030 self.select_next_state = entry.select_next_state;
12031 self.select_prev_state = entry.select_prev_state;
12032 self.add_selections_state = entry.add_selections_state;
12033 self.request_autoscroll(Autoscroll::newest(), cx);
12034 }
12035 self.selection_history.mode = SelectionHistoryMode::Normal;
12036 }
12037
12038 pub fn redo_selection(
12039 &mut self,
12040 _: &RedoSelection,
12041 window: &mut Window,
12042 cx: &mut Context<Self>,
12043 ) {
12044 self.end_selection(window, cx);
12045 self.selection_history.mode = SelectionHistoryMode::Redoing;
12046 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12047 self.change_selections(None, window, cx, |s| {
12048 s.select_anchors(entry.selections.to_vec())
12049 });
12050 self.select_next_state = entry.select_next_state;
12051 self.select_prev_state = entry.select_prev_state;
12052 self.add_selections_state = entry.add_selections_state;
12053 self.request_autoscroll(Autoscroll::newest(), cx);
12054 }
12055 self.selection_history.mode = SelectionHistoryMode::Normal;
12056 }
12057
12058 pub fn expand_excerpts(
12059 &mut self,
12060 action: &ExpandExcerpts,
12061 _: &mut Window,
12062 cx: &mut Context<Self>,
12063 ) {
12064 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12065 }
12066
12067 pub fn expand_excerpts_down(
12068 &mut self,
12069 action: &ExpandExcerptsDown,
12070 _: &mut Window,
12071 cx: &mut Context<Self>,
12072 ) {
12073 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12074 }
12075
12076 pub fn expand_excerpts_up(
12077 &mut self,
12078 action: &ExpandExcerptsUp,
12079 _: &mut Window,
12080 cx: &mut Context<Self>,
12081 ) {
12082 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12083 }
12084
12085 pub fn expand_excerpts_for_direction(
12086 &mut self,
12087 lines: u32,
12088 direction: ExpandExcerptDirection,
12089
12090 cx: &mut Context<Self>,
12091 ) {
12092 let selections = self.selections.disjoint_anchors();
12093
12094 let lines = if lines == 0 {
12095 EditorSettings::get_global(cx).expand_excerpt_lines
12096 } else {
12097 lines
12098 };
12099
12100 self.buffer.update(cx, |buffer, cx| {
12101 let snapshot = buffer.snapshot(cx);
12102 let mut excerpt_ids = selections
12103 .iter()
12104 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12105 .collect::<Vec<_>>();
12106 excerpt_ids.sort();
12107 excerpt_ids.dedup();
12108 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12109 })
12110 }
12111
12112 pub fn expand_excerpt(
12113 &mut self,
12114 excerpt: ExcerptId,
12115 direction: ExpandExcerptDirection,
12116 cx: &mut Context<Self>,
12117 ) {
12118 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12119 self.buffer.update(cx, |buffer, cx| {
12120 buffer.expand_excerpts([excerpt], lines, direction, cx)
12121 })
12122 }
12123
12124 pub fn go_to_singleton_buffer_point(
12125 &mut self,
12126 point: Point,
12127 window: &mut Window,
12128 cx: &mut Context<Self>,
12129 ) {
12130 self.go_to_singleton_buffer_range(point..point, window, cx);
12131 }
12132
12133 pub fn go_to_singleton_buffer_range(
12134 &mut self,
12135 range: Range<Point>,
12136 window: &mut Window,
12137 cx: &mut Context<Self>,
12138 ) {
12139 let multibuffer = self.buffer().read(cx);
12140 let Some(buffer) = multibuffer.as_singleton() else {
12141 return;
12142 };
12143 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12144 return;
12145 };
12146 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12147 return;
12148 };
12149 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12150 s.select_anchor_ranges([start..end])
12151 });
12152 }
12153
12154 fn go_to_diagnostic(
12155 &mut self,
12156 _: &GoToDiagnostic,
12157 window: &mut Window,
12158 cx: &mut Context<Self>,
12159 ) {
12160 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12161 }
12162
12163 fn go_to_prev_diagnostic(
12164 &mut self,
12165 _: &GoToPreviousDiagnostic,
12166 window: &mut Window,
12167 cx: &mut Context<Self>,
12168 ) {
12169 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12170 }
12171
12172 pub fn go_to_diagnostic_impl(
12173 &mut self,
12174 direction: Direction,
12175 window: &mut Window,
12176 cx: &mut Context<Self>,
12177 ) {
12178 let buffer = self.buffer.read(cx).snapshot(cx);
12179 let selection = self.selections.newest::<usize>(cx);
12180
12181 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12182 if direction == Direction::Next {
12183 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12184 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12185 return;
12186 };
12187 self.activate_diagnostics(
12188 buffer_id,
12189 popover.local_diagnostic.diagnostic.group_id,
12190 window,
12191 cx,
12192 );
12193 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12194 let primary_range_start = active_diagnostics.primary_range.start;
12195 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12196 let mut new_selection = s.newest_anchor().clone();
12197 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12198 s.select_anchors(vec![new_selection.clone()]);
12199 });
12200 self.refresh_inline_completion(false, true, window, cx);
12201 }
12202 return;
12203 }
12204 }
12205
12206 let active_group_id = self
12207 .active_diagnostics
12208 .as_ref()
12209 .map(|active_group| active_group.group_id);
12210 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12211 active_diagnostics
12212 .primary_range
12213 .to_offset(&buffer)
12214 .to_inclusive()
12215 });
12216 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12217 if active_primary_range.contains(&selection.head()) {
12218 *active_primary_range.start()
12219 } else {
12220 selection.head()
12221 }
12222 } else {
12223 selection.head()
12224 };
12225
12226 let snapshot = self.snapshot(window, cx);
12227 let primary_diagnostics_before = buffer
12228 .diagnostics_in_range::<usize>(0..search_start)
12229 .filter(|entry| entry.diagnostic.is_primary)
12230 .filter(|entry| entry.range.start != entry.range.end)
12231 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12232 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12233 .collect::<Vec<_>>();
12234 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12235 primary_diagnostics_before
12236 .iter()
12237 .position(|entry| entry.diagnostic.group_id == active_group_id)
12238 });
12239
12240 let primary_diagnostics_after = buffer
12241 .diagnostics_in_range::<usize>(search_start..buffer.len())
12242 .filter(|entry| entry.diagnostic.is_primary)
12243 .filter(|entry| entry.range.start != entry.range.end)
12244 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12245 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12246 .collect::<Vec<_>>();
12247 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12248 primary_diagnostics_after
12249 .iter()
12250 .enumerate()
12251 .rev()
12252 .find_map(|(i, entry)| {
12253 if entry.diagnostic.group_id == active_group_id {
12254 Some(i)
12255 } else {
12256 None
12257 }
12258 })
12259 });
12260
12261 let next_primary_diagnostic = match direction {
12262 Direction::Prev => primary_diagnostics_before
12263 .iter()
12264 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12265 .rev()
12266 .next(),
12267 Direction::Next => primary_diagnostics_after
12268 .iter()
12269 .skip(
12270 last_same_group_diagnostic_after
12271 .map(|index| index + 1)
12272 .unwrap_or(0),
12273 )
12274 .next(),
12275 };
12276
12277 // Cycle around to the start of the buffer, potentially moving back to the start of
12278 // the currently active diagnostic.
12279 let cycle_around = || match direction {
12280 Direction::Prev => primary_diagnostics_after
12281 .iter()
12282 .rev()
12283 .chain(primary_diagnostics_before.iter().rev())
12284 .next(),
12285 Direction::Next => primary_diagnostics_before
12286 .iter()
12287 .chain(primary_diagnostics_after.iter())
12288 .next(),
12289 };
12290
12291 if let Some((primary_range, group_id)) = next_primary_diagnostic
12292 .or_else(cycle_around)
12293 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12294 {
12295 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12296 return;
12297 };
12298 self.activate_diagnostics(buffer_id, group_id, window, cx);
12299 if self.active_diagnostics.is_some() {
12300 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12301 s.select(vec![Selection {
12302 id: selection.id,
12303 start: primary_range.start,
12304 end: primary_range.start,
12305 reversed: false,
12306 goal: SelectionGoal::None,
12307 }]);
12308 });
12309 self.refresh_inline_completion(false, true, window, cx);
12310 }
12311 }
12312 }
12313
12314 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12315 let snapshot = self.snapshot(window, cx);
12316 let selection = self.selections.newest::<Point>(cx);
12317 self.go_to_hunk_before_or_after_position(
12318 &snapshot,
12319 selection.head(),
12320 Direction::Next,
12321 window,
12322 cx,
12323 );
12324 }
12325
12326 fn go_to_hunk_before_or_after_position(
12327 &mut self,
12328 snapshot: &EditorSnapshot,
12329 position: Point,
12330 direction: Direction,
12331 window: &mut Window,
12332 cx: &mut Context<Editor>,
12333 ) {
12334 let row = if direction == Direction::Next {
12335 self.hunk_after_position(snapshot, position)
12336 .map(|hunk| hunk.row_range.start)
12337 } else {
12338 self.hunk_before_position(snapshot, position)
12339 };
12340
12341 if let Some(row) = row {
12342 let destination = Point::new(row.0, 0);
12343 let autoscroll = Autoscroll::center();
12344
12345 self.unfold_ranges(&[destination..destination], false, false, cx);
12346 self.change_selections(Some(autoscroll), window, cx, |s| {
12347 s.select_ranges([destination..destination]);
12348 });
12349 }
12350 }
12351
12352 fn hunk_after_position(
12353 &mut self,
12354 snapshot: &EditorSnapshot,
12355 position: Point,
12356 ) -> Option<MultiBufferDiffHunk> {
12357 snapshot
12358 .buffer_snapshot
12359 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12360 .find(|hunk| hunk.row_range.start.0 > position.row)
12361 .or_else(|| {
12362 snapshot
12363 .buffer_snapshot
12364 .diff_hunks_in_range(Point::zero()..position)
12365 .find(|hunk| hunk.row_range.end.0 < position.row)
12366 })
12367 }
12368
12369 fn go_to_prev_hunk(
12370 &mut self,
12371 _: &GoToPreviousHunk,
12372 window: &mut Window,
12373 cx: &mut Context<Self>,
12374 ) {
12375 let snapshot = self.snapshot(window, cx);
12376 let selection = self.selections.newest::<Point>(cx);
12377 self.go_to_hunk_before_or_after_position(
12378 &snapshot,
12379 selection.head(),
12380 Direction::Prev,
12381 window,
12382 cx,
12383 );
12384 }
12385
12386 fn hunk_before_position(
12387 &mut self,
12388 snapshot: &EditorSnapshot,
12389 position: Point,
12390 ) -> Option<MultiBufferRow> {
12391 snapshot
12392 .buffer_snapshot
12393 .diff_hunk_before(position)
12394 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12395 }
12396
12397 fn go_to_line<T: 'static>(
12398 &mut self,
12399 position: Anchor,
12400 highlight_color: Option<Hsla>,
12401 window: &mut Window,
12402 cx: &mut Context<Self>,
12403 ) {
12404 let snapshot = self.snapshot(window, cx).display_snapshot;
12405 let position = position.to_point(&snapshot.buffer_snapshot);
12406 let start = snapshot
12407 .buffer_snapshot
12408 .clip_point(Point::new(position.row, 0), Bias::Left);
12409 let end = start + Point::new(1, 0);
12410 let start = snapshot.buffer_snapshot.anchor_before(start);
12411 let end = snapshot.buffer_snapshot.anchor_before(end);
12412
12413 self.clear_row_highlights::<T>();
12414 self.highlight_rows::<T>(
12415 start..end,
12416 highlight_color
12417 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12418 true,
12419 cx,
12420 );
12421 self.request_autoscroll(Autoscroll::center(), cx);
12422 }
12423
12424 pub fn go_to_definition(
12425 &mut self,
12426 _: &GoToDefinition,
12427 window: &mut Window,
12428 cx: &mut Context<Self>,
12429 ) -> Task<Result<Navigated>> {
12430 let definition =
12431 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12432 cx.spawn_in(window, |editor, mut cx| async move {
12433 if definition.await? == Navigated::Yes {
12434 return Ok(Navigated::Yes);
12435 }
12436 match editor.update_in(&mut cx, |editor, window, cx| {
12437 editor.find_all_references(&FindAllReferences, window, cx)
12438 })? {
12439 Some(references) => references.await,
12440 None => Ok(Navigated::No),
12441 }
12442 })
12443 }
12444
12445 pub fn go_to_declaration(
12446 &mut self,
12447 _: &GoToDeclaration,
12448 window: &mut Window,
12449 cx: &mut Context<Self>,
12450 ) -> Task<Result<Navigated>> {
12451 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12452 }
12453
12454 pub fn go_to_declaration_split(
12455 &mut self,
12456 _: &GoToDeclaration,
12457 window: &mut Window,
12458 cx: &mut Context<Self>,
12459 ) -> Task<Result<Navigated>> {
12460 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12461 }
12462
12463 pub fn go_to_implementation(
12464 &mut self,
12465 _: &GoToImplementation,
12466 window: &mut Window,
12467 cx: &mut Context<Self>,
12468 ) -> Task<Result<Navigated>> {
12469 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12470 }
12471
12472 pub fn go_to_implementation_split(
12473 &mut self,
12474 _: &GoToImplementationSplit,
12475 window: &mut Window,
12476 cx: &mut Context<Self>,
12477 ) -> Task<Result<Navigated>> {
12478 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12479 }
12480
12481 pub fn go_to_type_definition(
12482 &mut self,
12483 _: &GoToTypeDefinition,
12484 window: &mut Window,
12485 cx: &mut Context<Self>,
12486 ) -> Task<Result<Navigated>> {
12487 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12488 }
12489
12490 pub fn go_to_definition_split(
12491 &mut self,
12492 _: &GoToDefinitionSplit,
12493 window: &mut Window,
12494 cx: &mut Context<Self>,
12495 ) -> Task<Result<Navigated>> {
12496 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12497 }
12498
12499 pub fn go_to_type_definition_split(
12500 &mut self,
12501 _: &GoToTypeDefinitionSplit,
12502 window: &mut Window,
12503 cx: &mut Context<Self>,
12504 ) -> Task<Result<Navigated>> {
12505 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12506 }
12507
12508 fn go_to_definition_of_kind(
12509 &mut self,
12510 kind: GotoDefinitionKind,
12511 split: bool,
12512 window: &mut Window,
12513 cx: &mut Context<Self>,
12514 ) -> Task<Result<Navigated>> {
12515 let Some(provider) = self.semantics_provider.clone() else {
12516 return Task::ready(Ok(Navigated::No));
12517 };
12518 let head = self.selections.newest::<usize>(cx).head();
12519 let buffer = self.buffer.read(cx);
12520 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12521 text_anchor
12522 } else {
12523 return Task::ready(Ok(Navigated::No));
12524 };
12525
12526 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12527 return Task::ready(Ok(Navigated::No));
12528 };
12529
12530 cx.spawn_in(window, |editor, mut cx| async move {
12531 let definitions = definitions.await?;
12532 let navigated = editor
12533 .update_in(&mut cx, |editor, window, cx| {
12534 editor.navigate_to_hover_links(
12535 Some(kind),
12536 definitions
12537 .into_iter()
12538 .filter(|location| {
12539 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12540 })
12541 .map(HoverLink::Text)
12542 .collect::<Vec<_>>(),
12543 split,
12544 window,
12545 cx,
12546 )
12547 })?
12548 .await?;
12549 anyhow::Ok(navigated)
12550 })
12551 }
12552
12553 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12554 let selection = self.selections.newest_anchor();
12555 let head = selection.head();
12556 let tail = selection.tail();
12557
12558 let Some((buffer, start_position)) =
12559 self.buffer.read(cx).text_anchor_for_position(head, cx)
12560 else {
12561 return;
12562 };
12563
12564 let end_position = if head != tail {
12565 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12566 return;
12567 };
12568 Some(pos)
12569 } else {
12570 None
12571 };
12572
12573 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
12574 let url = if let Some(end_pos) = end_position {
12575 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12576 } else {
12577 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12578 };
12579
12580 if let Some(url) = url {
12581 editor.update(&mut cx, |_, cx| {
12582 cx.open_url(&url);
12583 })
12584 } else {
12585 Ok(())
12586 }
12587 });
12588
12589 url_finder.detach();
12590 }
12591
12592 pub fn open_selected_filename(
12593 &mut self,
12594 _: &OpenSelectedFilename,
12595 window: &mut Window,
12596 cx: &mut Context<Self>,
12597 ) {
12598 let Some(workspace) = self.workspace() else {
12599 return;
12600 };
12601
12602 let position = self.selections.newest_anchor().head();
12603
12604 let Some((buffer, buffer_position)) =
12605 self.buffer.read(cx).text_anchor_for_position(position, cx)
12606 else {
12607 return;
12608 };
12609
12610 let project = self.project.clone();
12611
12612 cx.spawn_in(window, |_, mut cx| async move {
12613 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
12614
12615 if let Some((_, path)) = result {
12616 workspace
12617 .update_in(&mut cx, |workspace, window, cx| {
12618 workspace.open_resolved_path(path, window, cx)
12619 })?
12620 .await?;
12621 }
12622 anyhow::Ok(())
12623 })
12624 .detach();
12625 }
12626
12627 pub(crate) fn navigate_to_hover_links(
12628 &mut self,
12629 kind: Option<GotoDefinitionKind>,
12630 mut definitions: Vec<HoverLink>,
12631 split: bool,
12632 window: &mut Window,
12633 cx: &mut Context<Editor>,
12634 ) -> Task<Result<Navigated>> {
12635 // If there is one definition, just open it directly
12636 if definitions.len() == 1 {
12637 let definition = definitions.pop().unwrap();
12638
12639 enum TargetTaskResult {
12640 Location(Option<Location>),
12641 AlreadyNavigated,
12642 }
12643
12644 let target_task = match definition {
12645 HoverLink::Text(link) => {
12646 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12647 }
12648 HoverLink::InlayHint(lsp_location, server_id) => {
12649 let computation =
12650 self.compute_target_location(lsp_location, server_id, window, cx);
12651 cx.background_spawn(async move {
12652 let location = computation.await?;
12653 Ok(TargetTaskResult::Location(location))
12654 })
12655 }
12656 HoverLink::Url(url) => {
12657 cx.open_url(&url);
12658 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12659 }
12660 HoverLink::File(path) => {
12661 if let Some(workspace) = self.workspace() {
12662 cx.spawn_in(window, |_, mut cx| async move {
12663 workspace
12664 .update_in(&mut cx, |workspace, window, cx| {
12665 workspace.open_resolved_path(path, window, cx)
12666 })?
12667 .await
12668 .map(|_| TargetTaskResult::AlreadyNavigated)
12669 })
12670 } else {
12671 Task::ready(Ok(TargetTaskResult::Location(None)))
12672 }
12673 }
12674 };
12675 cx.spawn_in(window, |editor, mut cx| async move {
12676 let target = match target_task.await.context("target resolution task")? {
12677 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12678 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12679 TargetTaskResult::Location(Some(target)) => target,
12680 };
12681
12682 editor.update_in(&mut cx, |editor, window, cx| {
12683 let Some(workspace) = editor.workspace() else {
12684 return Navigated::No;
12685 };
12686 let pane = workspace.read(cx).active_pane().clone();
12687
12688 let range = target.range.to_point(target.buffer.read(cx));
12689 let range = editor.range_for_match(&range);
12690 let range = collapse_multiline_range(range);
12691
12692 if !split
12693 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12694 {
12695 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12696 } else {
12697 window.defer(cx, move |window, cx| {
12698 let target_editor: Entity<Self> =
12699 workspace.update(cx, |workspace, cx| {
12700 let pane = if split {
12701 workspace.adjacent_pane(window, cx)
12702 } else {
12703 workspace.active_pane().clone()
12704 };
12705
12706 workspace.open_project_item(
12707 pane,
12708 target.buffer.clone(),
12709 true,
12710 true,
12711 window,
12712 cx,
12713 )
12714 });
12715 target_editor.update(cx, |target_editor, cx| {
12716 // When selecting a definition in a different buffer, disable the nav history
12717 // to avoid creating a history entry at the previous cursor location.
12718 pane.update(cx, |pane, _| pane.disable_history());
12719 target_editor.go_to_singleton_buffer_range(range, window, cx);
12720 pane.update(cx, |pane, _| pane.enable_history());
12721 });
12722 });
12723 }
12724 Navigated::Yes
12725 })
12726 })
12727 } else if !definitions.is_empty() {
12728 cx.spawn_in(window, |editor, mut cx| async move {
12729 let (title, location_tasks, workspace) = editor
12730 .update_in(&mut cx, |editor, window, cx| {
12731 let tab_kind = match kind {
12732 Some(GotoDefinitionKind::Implementation) => "Implementations",
12733 _ => "Definitions",
12734 };
12735 let title = definitions
12736 .iter()
12737 .find_map(|definition| match definition {
12738 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12739 let buffer = origin.buffer.read(cx);
12740 format!(
12741 "{} for {}",
12742 tab_kind,
12743 buffer
12744 .text_for_range(origin.range.clone())
12745 .collect::<String>()
12746 )
12747 }),
12748 HoverLink::InlayHint(_, _) => None,
12749 HoverLink::Url(_) => None,
12750 HoverLink::File(_) => None,
12751 })
12752 .unwrap_or(tab_kind.to_string());
12753 let location_tasks = definitions
12754 .into_iter()
12755 .map(|definition| match definition {
12756 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12757 HoverLink::InlayHint(lsp_location, server_id) => editor
12758 .compute_target_location(lsp_location, server_id, window, cx),
12759 HoverLink::Url(_) => Task::ready(Ok(None)),
12760 HoverLink::File(_) => Task::ready(Ok(None)),
12761 })
12762 .collect::<Vec<_>>();
12763 (title, location_tasks, editor.workspace().clone())
12764 })
12765 .context("location tasks preparation")?;
12766
12767 let locations = future::join_all(location_tasks)
12768 .await
12769 .into_iter()
12770 .filter_map(|location| location.transpose())
12771 .collect::<Result<_>>()
12772 .context("location tasks")?;
12773
12774 let Some(workspace) = workspace else {
12775 return Ok(Navigated::No);
12776 };
12777 let opened = workspace
12778 .update_in(&mut cx, |workspace, window, cx| {
12779 Self::open_locations_in_multibuffer(
12780 workspace,
12781 locations,
12782 title,
12783 split,
12784 MultibufferSelectionMode::First,
12785 window,
12786 cx,
12787 )
12788 })
12789 .ok();
12790
12791 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12792 })
12793 } else {
12794 Task::ready(Ok(Navigated::No))
12795 }
12796 }
12797
12798 fn compute_target_location(
12799 &self,
12800 lsp_location: lsp::Location,
12801 server_id: LanguageServerId,
12802 window: &mut Window,
12803 cx: &mut Context<Self>,
12804 ) -> Task<anyhow::Result<Option<Location>>> {
12805 let Some(project) = self.project.clone() else {
12806 return Task::ready(Ok(None));
12807 };
12808
12809 cx.spawn_in(window, move |editor, mut cx| async move {
12810 let location_task = editor.update(&mut cx, |_, cx| {
12811 project.update(cx, |project, cx| {
12812 let language_server_name = project
12813 .language_server_statuses(cx)
12814 .find(|(id, _)| server_id == *id)
12815 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12816 language_server_name.map(|language_server_name| {
12817 project.open_local_buffer_via_lsp(
12818 lsp_location.uri.clone(),
12819 server_id,
12820 language_server_name,
12821 cx,
12822 )
12823 })
12824 })
12825 })?;
12826 let location = match location_task {
12827 Some(task) => Some({
12828 let target_buffer_handle = task.await.context("open local buffer")?;
12829 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12830 let target_start = target_buffer
12831 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12832 let target_end = target_buffer
12833 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12834 target_buffer.anchor_after(target_start)
12835 ..target_buffer.anchor_before(target_end)
12836 })?;
12837 Location {
12838 buffer: target_buffer_handle,
12839 range,
12840 }
12841 }),
12842 None => None,
12843 };
12844 Ok(location)
12845 })
12846 }
12847
12848 pub fn find_all_references(
12849 &mut self,
12850 _: &FindAllReferences,
12851 window: &mut Window,
12852 cx: &mut Context<Self>,
12853 ) -> Option<Task<Result<Navigated>>> {
12854 let selection = self.selections.newest::<usize>(cx);
12855 let multi_buffer = self.buffer.read(cx);
12856 let head = selection.head();
12857
12858 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12859 let head_anchor = multi_buffer_snapshot.anchor_at(
12860 head,
12861 if head < selection.tail() {
12862 Bias::Right
12863 } else {
12864 Bias::Left
12865 },
12866 );
12867
12868 match self
12869 .find_all_references_task_sources
12870 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12871 {
12872 Ok(_) => {
12873 log::info!(
12874 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12875 );
12876 return None;
12877 }
12878 Err(i) => {
12879 self.find_all_references_task_sources.insert(i, head_anchor);
12880 }
12881 }
12882
12883 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12884 let workspace = self.workspace()?;
12885 let project = workspace.read(cx).project().clone();
12886 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12887 Some(cx.spawn_in(window, |editor, mut cx| async move {
12888 let _cleanup = defer({
12889 let mut cx = cx.clone();
12890 move || {
12891 let _ = editor.update(&mut cx, |editor, _| {
12892 if let Ok(i) =
12893 editor
12894 .find_all_references_task_sources
12895 .binary_search_by(|anchor| {
12896 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12897 })
12898 {
12899 editor.find_all_references_task_sources.remove(i);
12900 }
12901 });
12902 }
12903 });
12904
12905 let locations = references.await?;
12906 if locations.is_empty() {
12907 return anyhow::Ok(Navigated::No);
12908 }
12909
12910 workspace.update_in(&mut cx, |workspace, window, cx| {
12911 let title = locations
12912 .first()
12913 .as_ref()
12914 .map(|location| {
12915 let buffer = location.buffer.read(cx);
12916 format!(
12917 "References to `{}`",
12918 buffer
12919 .text_for_range(location.range.clone())
12920 .collect::<String>()
12921 )
12922 })
12923 .unwrap();
12924 Self::open_locations_in_multibuffer(
12925 workspace,
12926 locations,
12927 title,
12928 false,
12929 MultibufferSelectionMode::First,
12930 window,
12931 cx,
12932 );
12933 Navigated::Yes
12934 })
12935 }))
12936 }
12937
12938 /// Opens a multibuffer with the given project locations in it
12939 pub fn open_locations_in_multibuffer(
12940 workspace: &mut Workspace,
12941 mut locations: Vec<Location>,
12942 title: String,
12943 split: bool,
12944 multibuffer_selection_mode: MultibufferSelectionMode,
12945 window: &mut Window,
12946 cx: &mut Context<Workspace>,
12947 ) {
12948 // If there are multiple definitions, open them in a multibuffer
12949 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12950 let mut locations = locations.into_iter().peekable();
12951 let mut ranges = Vec::new();
12952 let capability = workspace.project().read(cx).capability();
12953
12954 let excerpt_buffer = cx.new(|cx| {
12955 let mut multibuffer = MultiBuffer::new(capability);
12956 while let Some(location) = locations.next() {
12957 let buffer = location.buffer.read(cx);
12958 let mut ranges_for_buffer = Vec::new();
12959 let range = location.range.to_offset(buffer);
12960 ranges_for_buffer.push(range.clone());
12961
12962 while let Some(next_location) = locations.peek() {
12963 if next_location.buffer == location.buffer {
12964 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12965 locations.next();
12966 } else {
12967 break;
12968 }
12969 }
12970
12971 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12972 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12973 location.buffer.clone(),
12974 ranges_for_buffer,
12975 DEFAULT_MULTIBUFFER_CONTEXT,
12976 cx,
12977 ))
12978 }
12979
12980 multibuffer.with_title(title)
12981 });
12982
12983 let editor = cx.new(|cx| {
12984 Editor::for_multibuffer(
12985 excerpt_buffer,
12986 Some(workspace.project().clone()),
12987 window,
12988 cx,
12989 )
12990 });
12991 editor.update(cx, |editor, cx| {
12992 match multibuffer_selection_mode {
12993 MultibufferSelectionMode::First => {
12994 if let Some(first_range) = ranges.first() {
12995 editor.change_selections(None, window, cx, |selections| {
12996 selections.clear_disjoint();
12997 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12998 });
12999 }
13000 editor.highlight_background::<Self>(
13001 &ranges,
13002 |theme| theme.editor_highlighted_line_background,
13003 cx,
13004 );
13005 }
13006 MultibufferSelectionMode::All => {
13007 editor.change_selections(None, window, cx, |selections| {
13008 selections.clear_disjoint();
13009 selections.select_anchor_ranges(ranges);
13010 });
13011 }
13012 }
13013 editor.register_buffers_with_language_servers(cx);
13014 });
13015
13016 let item = Box::new(editor);
13017 let item_id = item.item_id();
13018
13019 if split {
13020 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13021 } else {
13022 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13023 let (preview_item_id, preview_item_idx) =
13024 workspace.active_pane().update(cx, |pane, _| {
13025 (pane.preview_item_id(), pane.preview_item_idx())
13026 });
13027
13028 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13029
13030 if let Some(preview_item_id) = preview_item_id {
13031 workspace.active_pane().update(cx, |pane, cx| {
13032 pane.remove_item(preview_item_id, false, false, window, cx);
13033 });
13034 }
13035 } else {
13036 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13037 }
13038 }
13039 workspace.active_pane().update(cx, |pane, cx| {
13040 pane.set_preview_item_id(Some(item_id), cx);
13041 });
13042 }
13043
13044 pub fn rename(
13045 &mut self,
13046 _: &Rename,
13047 window: &mut Window,
13048 cx: &mut Context<Self>,
13049 ) -> Option<Task<Result<()>>> {
13050 use language::ToOffset as _;
13051
13052 let provider = self.semantics_provider.clone()?;
13053 let selection = self.selections.newest_anchor().clone();
13054 let (cursor_buffer, cursor_buffer_position) = self
13055 .buffer
13056 .read(cx)
13057 .text_anchor_for_position(selection.head(), cx)?;
13058 let (tail_buffer, cursor_buffer_position_end) = self
13059 .buffer
13060 .read(cx)
13061 .text_anchor_for_position(selection.tail(), cx)?;
13062 if tail_buffer != cursor_buffer {
13063 return None;
13064 }
13065
13066 let snapshot = cursor_buffer.read(cx).snapshot();
13067 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13068 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13069 let prepare_rename = provider
13070 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13071 .unwrap_or_else(|| Task::ready(Ok(None)));
13072 drop(snapshot);
13073
13074 Some(cx.spawn_in(window, |this, mut cx| async move {
13075 let rename_range = if let Some(range) = prepare_rename.await? {
13076 Some(range)
13077 } else {
13078 this.update(&mut cx, |this, cx| {
13079 let buffer = this.buffer.read(cx).snapshot(cx);
13080 let mut buffer_highlights = this
13081 .document_highlights_for_position(selection.head(), &buffer)
13082 .filter(|highlight| {
13083 highlight.start.excerpt_id == selection.head().excerpt_id
13084 && highlight.end.excerpt_id == selection.head().excerpt_id
13085 });
13086 buffer_highlights
13087 .next()
13088 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13089 })?
13090 };
13091 if let Some(rename_range) = rename_range {
13092 this.update_in(&mut cx, |this, window, cx| {
13093 let snapshot = cursor_buffer.read(cx).snapshot();
13094 let rename_buffer_range = rename_range.to_offset(&snapshot);
13095 let cursor_offset_in_rename_range =
13096 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13097 let cursor_offset_in_rename_range_end =
13098 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13099
13100 this.take_rename(false, window, cx);
13101 let buffer = this.buffer.read(cx).read(cx);
13102 let cursor_offset = selection.head().to_offset(&buffer);
13103 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13104 let rename_end = rename_start + rename_buffer_range.len();
13105 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13106 let mut old_highlight_id = None;
13107 let old_name: Arc<str> = buffer
13108 .chunks(rename_start..rename_end, true)
13109 .map(|chunk| {
13110 if old_highlight_id.is_none() {
13111 old_highlight_id = chunk.syntax_highlight_id;
13112 }
13113 chunk.text
13114 })
13115 .collect::<String>()
13116 .into();
13117
13118 drop(buffer);
13119
13120 // Position the selection in the rename editor so that it matches the current selection.
13121 this.show_local_selections = false;
13122 let rename_editor = cx.new(|cx| {
13123 let mut editor = Editor::single_line(window, cx);
13124 editor.buffer.update(cx, |buffer, cx| {
13125 buffer.edit([(0..0, old_name.clone())], None, cx)
13126 });
13127 let rename_selection_range = match cursor_offset_in_rename_range
13128 .cmp(&cursor_offset_in_rename_range_end)
13129 {
13130 Ordering::Equal => {
13131 editor.select_all(&SelectAll, window, cx);
13132 return editor;
13133 }
13134 Ordering::Less => {
13135 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13136 }
13137 Ordering::Greater => {
13138 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13139 }
13140 };
13141 if rename_selection_range.end > old_name.len() {
13142 editor.select_all(&SelectAll, window, cx);
13143 } else {
13144 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13145 s.select_ranges([rename_selection_range]);
13146 });
13147 }
13148 editor
13149 });
13150 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13151 if e == &EditorEvent::Focused {
13152 cx.emit(EditorEvent::FocusedIn)
13153 }
13154 })
13155 .detach();
13156
13157 let write_highlights =
13158 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13159 let read_highlights =
13160 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13161 let ranges = write_highlights
13162 .iter()
13163 .flat_map(|(_, ranges)| ranges.iter())
13164 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13165 .cloned()
13166 .collect();
13167
13168 this.highlight_text::<Rename>(
13169 ranges,
13170 HighlightStyle {
13171 fade_out: Some(0.6),
13172 ..Default::default()
13173 },
13174 cx,
13175 );
13176 let rename_focus_handle = rename_editor.focus_handle(cx);
13177 window.focus(&rename_focus_handle);
13178 let block_id = this.insert_blocks(
13179 [BlockProperties {
13180 style: BlockStyle::Flex,
13181 placement: BlockPlacement::Below(range.start),
13182 height: 1,
13183 render: Arc::new({
13184 let rename_editor = rename_editor.clone();
13185 move |cx: &mut BlockContext| {
13186 let mut text_style = cx.editor_style.text.clone();
13187 if let Some(highlight_style) = old_highlight_id
13188 .and_then(|h| h.style(&cx.editor_style.syntax))
13189 {
13190 text_style = text_style.highlight(highlight_style);
13191 }
13192 div()
13193 .block_mouse_down()
13194 .pl(cx.anchor_x)
13195 .child(EditorElement::new(
13196 &rename_editor,
13197 EditorStyle {
13198 background: cx.theme().system().transparent,
13199 local_player: cx.editor_style.local_player,
13200 text: text_style,
13201 scrollbar_width: cx.editor_style.scrollbar_width,
13202 syntax: cx.editor_style.syntax.clone(),
13203 status: cx.editor_style.status.clone(),
13204 inlay_hints_style: HighlightStyle {
13205 font_weight: Some(FontWeight::BOLD),
13206 ..make_inlay_hints_style(cx.app)
13207 },
13208 inline_completion_styles: make_suggestion_styles(
13209 cx.app,
13210 ),
13211 ..EditorStyle::default()
13212 },
13213 ))
13214 .into_any_element()
13215 }
13216 }),
13217 priority: 0,
13218 }],
13219 Some(Autoscroll::fit()),
13220 cx,
13221 )[0];
13222 this.pending_rename = Some(RenameState {
13223 range,
13224 old_name,
13225 editor: rename_editor,
13226 block_id,
13227 });
13228 })?;
13229 }
13230
13231 Ok(())
13232 }))
13233 }
13234
13235 pub fn confirm_rename(
13236 &mut self,
13237 _: &ConfirmRename,
13238 window: &mut Window,
13239 cx: &mut Context<Self>,
13240 ) -> Option<Task<Result<()>>> {
13241 let rename = self.take_rename(false, window, cx)?;
13242 let workspace = self.workspace()?.downgrade();
13243 let (buffer, start) = self
13244 .buffer
13245 .read(cx)
13246 .text_anchor_for_position(rename.range.start, cx)?;
13247 let (end_buffer, _) = self
13248 .buffer
13249 .read(cx)
13250 .text_anchor_for_position(rename.range.end, cx)?;
13251 if buffer != end_buffer {
13252 return None;
13253 }
13254
13255 let old_name = rename.old_name;
13256 let new_name = rename.editor.read(cx).text(cx);
13257
13258 let rename = self.semantics_provider.as_ref()?.perform_rename(
13259 &buffer,
13260 start,
13261 new_name.clone(),
13262 cx,
13263 )?;
13264
13265 Some(cx.spawn_in(window, |editor, mut cx| async move {
13266 let project_transaction = rename.await?;
13267 Self::open_project_transaction(
13268 &editor,
13269 workspace,
13270 project_transaction,
13271 format!("Rename: {} → {}", old_name, new_name),
13272 cx.clone(),
13273 )
13274 .await?;
13275
13276 editor.update(&mut cx, |editor, cx| {
13277 editor.refresh_document_highlights(cx);
13278 })?;
13279 Ok(())
13280 }))
13281 }
13282
13283 fn take_rename(
13284 &mut self,
13285 moving_cursor: bool,
13286 window: &mut Window,
13287 cx: &mut Context<Self>,
13288 ) -> Option<RenameState> {
13289 let rename = self.pending_rename.take()?;
13290 if rename.editor.focus_handle(cx).is_focused(window) {
13291 window.focus(&self.focus_handle);
13292 }
13293
13294 self.remove_blocks(
13295 [rename.block_id].into_iter().collect(),
13296 Some(Autoscroll::fit()),
13297 cx,
13298 );
13299 self.clear_highlights::<Rename>(cx);
13300 self.show_local_selections = true;
13301
13302 if moving_cursor {
13303 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13304 editor.selections.newest::<usize>(cx).head()
13305 });
13306
13307 // Update the selection to match the position of the selection inside
13308 // the rename editor.
13309 let snapshot = self.buffer.read(cx).read(cx);
13310 let rename_range = rename.range.to_offset(&snapshot);
13311 let cursor_in_editor = snapshot
13312 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13313 .min(rename_range.end);
13314 drop(snapshot);
13315
13316 self.change_selections(None, window, cx, |s| {
13317 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13318 });
13319 } else {
13320 self.refresh_document_highlights(cx);
13321 }
13322
13323 Some(rename)
13324 }
13325
13326 pub fn pending_rename(&self) -> Option<&RenameState> {
13327 self.pending_rename.as_ref()
13328 }
13329
13330 fn format(
13331 &mut self,
13332 _: &Format,
13333 window: &mut Window,
13334 cx: &mut Context<Self>,
13335 ) -> Option<Task<Result<()>>> {
13336 let project = match &self.project {
13337 Some(project) => project.clone(),
13338 None => return None,
13339 };
13340
13341 Some(self.perform_format(
13342 project,
13343 FormatTrigger::Manual,
13344 FormatTarget::Buffers,
13345 window,
13346 cx,
13347 ))
13348 }
13349
13350 fn format_selections(
13351 &mut self,
13352 _: &FormatSelections,
13353 window: &mut Window,
13354 cx: &mut Context<Self>,
13355 ) -> Option<Task<Result<()>>> {
13356 let project = match &self.project {
13357 Some(project) => project.clone(),
13358 None => return None,
13359 };
13360
13361 let ranges = self
13362 .selections
13363 .all_adjusted(cx)
13364 .into_iter()
13365 .map(|selection| selection.range())
13366 .collect_vec();
13367
13368 Some(self.perform_format(
13369 project,
13370 FormatTrigger::Manual,
13371 FormatTarget::Ranges(ranges),
13372 window,
13373 cx,
13374 ))
13375 }
13376
13377 fn perform_format(
13378 &mut self,
13379 project: Entity<Project>,
13380 trigger: FormatTrigger,
13381 target: FormatTarget,
13382 window: &mut Window,
13383 cx: &mut Context<Self>,
13384 ) -> Task<Result<()>> {
13385 let buffer = self.buffer.clone();
13386 let (buffers, target) = match target {
13387 FormatTarget::Buffers => {
13388 let mut buffers = buffer.read(cx).all_buffers();
13389 if trigger == FormatTrigger::Save {
13390 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13391 }
13392 (buffers, LspFormatTarget::Buffers)
13393 }
13394 FormatTarget::Ranges(selection_ranges) => {
13395 let multi_buffer = buffer.read(cx);
13396 let snapshot = multi_buffer.read(cx);
13397 let mut buffers = HashSet::default();
13398 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13399 BTreeMap::new();
13400 for selection_range in selection_ranges {
13401 for (buffer, buffer_range, _) in
13402 snapshot.range_to_buffer_ranges(selection_range)
13403 {
13404 let buffer_id = buffer.remote_id();
13405 let start = buffer.anchor_before(buffer_range.start);
13406 let end = buffer.anchor_after(buffer_range.end);
13407 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13408 buffer_id_to_ranges
13409 .entry(buffer_id)
13410 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13411 .or_insert_with(|| vec![start..end]);
13412 }
13413 }
13414 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13415 }
13416 };
13417
13418 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13419 let format = project.update(cx, |project, cx| {
13420 project.format(buffers, target, true, trigger, cx)
13421 });
13422
13423 cx.spawn_in(window, |_, mut cx| async move {
13424 let transaction = futures::select_biased! {
13425 transaction = format.log_err().fuse() => transaction,
13426 () = timeout => {
13427 log::warn!("timed out waiting for formatting");
13428 None
13429 }
13430 };
13431
13432 buffer
13433 .update(&mut cx, |buffer, cx| {
13434 if let Some(transaction) = transaction {
13435 if !buffer.is_singleton() {
13436 buffer.push_transaction(&transaction.0, cx);
13437 }
13438 }
13439 cx.notify();
13440 })
13441 .ok();
13442
13443 Ok(())
13444 })
13445 }
13446
13447 fn organize_imports(
13448 &mut self,
13449 _: &OrganizeImports,
13450 window: &mut Window,
13451 cx: &mut Context<Self>,
13452 ) -> Option<Task<Result<()>>> {
13453 let project = match &self.project {
13454 Some(project) => project.clone(),
13455 None => return None,
13456 };
13457 Some(self.perform_code_action_kind(
13458 project,
13459 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13460 window,
13461 cx,
13462 ))
13463 }
13464
13465 fn perform_code_action_kind(
13466 &mut self,
13467 project: Entity<Project>,
13468 kind: CodeActionKind,
13469 window: &mut Window,
13470 cx: &mut Context<Self>,
13471 ) -> Task<Result<()>> {
13472 let buffer = self.buffer.clone();
13473 let buffers = buffer.read(cx).all_buffers();
13474 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13475 let apply_action = project.update(cx, |project, cx| {
13476 project.apply_code_action_kind(buffers, kind, true, cx)
13477 });
13478 cx.spawn_in(window, |_, mut cx| async move {
13479 let transaction = futures::select_biased! {
13480 () = timeout => {
13481 log::warn!("timed out waiting for executing code action");
13482 None
13483 }
13484 transaction = apply_action.log_err().fuse() => transaction,
13485 };
13486 buffer
13487 .update(&mut cx, |buffer, cx| {
13488 // check if we need this
13489 if let Some(transaction) = transaction {
13490 if !buffer.is_singleton() {
13491 buffer.push_transaction(&transaction.0, cx);
13492 }
13493 }
13494 cx.notify();
13495 })
13496 .ok();
13497 Ok(())
13498 })
13499 }
13500
13501 fn restart_language_server(
13502 &mut self,
13503 _: &RestartLanguageServer,
13504 _: &mut Window,
13505 cx: &mut Context<Self>,
13506 ) {
13507 if let Some(project) = self.project.clone() {
13508 self.buffer.update(cx, |multi_buffer, cx| {
13509 project.update(cx, |project, cx| {
13510 project.restart_language_servers_for_buffers(
13511 multi_buffer.all_buffers().into_iter().collect(),
13512 cx,
13513 );
13514 });
13515 })
13516 }
13517 }
13518
13519 fn cancel_language_server_work(
13520 workspace: &mut Workspace,
13521 _: &actions::CancelLanguageServerWork,
13522 _: &mut Window,
13523 cx: &mut Context<Workspace>,
13524 ) {
13525 let project = workspace.project();
13526 let buffers = workspace
13527 .active_item(cx)
13528 .and_then(|item| item.act_as::<Editor>(cx))
13529 .map_or(HashSet::default(), |editor| {
13530 editor.read(cx).buffer.read(cx).all_buffers()
13531 });
13532 project.update(cx, |project, cx| {
13533 project.cancel_language_server_work_for_buffers(buffers, cx);
13534 });
13535 }
13536
13537 fn show_character_palette(
13538 &mut self,
13539 _: &ShowCharacterPalette,
13540 window: &mut Window,
13541 _: &mut Context<Self>,
13542 ) {
13543 window.show_character_palette();
13544 }
13545
13546 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13547 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13548 let buffer = self.buffer.read(cx).snapshot(cx);
13549 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13550 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13551 let is_valid = buffer
13552 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13553 .any(|entry| {
13554 entry.diagnostic.is_primary
13555 && !entry.range.is_empty()
13556 && entry.range.start == primary_range_start
13557 && entry.diagnostic.message == active_diagnostics.primary_message
13558 });
13559
13560 if is_valid != active_diagnostics.is_valid {
13561 active_diagnostics.is_valid = is_valid;
13562 if is_valid {
13563 let mut new_styles = HashMap::default();
13564 for (block_id, diagnostic) in &active_diagnostics.blocks {
13565 new_styles.insert(
13566 *block_id,
13567 diagnostic_block_renderer(diagnostic.clone(), None, true),
13568 );
13569 }
13570 self.display_map.update(cx, |display_map, _cx| {
13571 display_map.replace_blocks(new_styles);
13572 });
13573 } else {
13574 self.dismiss_diagnostics(cx);
13575 }
13576 }
13577 }
13578 }
13579
13580 fn activate_diagnostics(
13581 &mut self,
13582 buffer_id: BufferId,
13583 group_id: usize,
13584 window: &mut Window,
13585 cx: &mut Context<Self>,
13586 ) {
13587 self.dismiss_diagnostics(cx);
13588 let snapshot = self.snapshot(window, cx);
13589 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13590 let buffer = self.buffer.read(cx).snapshot(cx);
13591
13592 let mut primary_range = None;
13593 let mut primary_message = None;
13594 let diagnostic_group = buffer
13595 .diagnostic_group(buffer_id, group_id)
13596 .filter_map(|entry| {
13597 let start = entry.range.start;
13598 let end = entry.range.end;
13599 if snapshot.is_line_folded(MultiBufferRow(start.row))
13600 && (start.row == end.row
13601 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13602 {
13603 return None;
13604 }
13605 if entry.diagnostic.is_primary {
13606 primary_range = Some(entry.range.clone());
13607 primary_message = Some(entry.diagnostic.message.clone());
13608 }
13609 Some(entry)
13610 })
13611 .collect::<Vec<_>>();
13612 let primary_range = primary_range?;
13613 let primary_message = primary_message?;
13614
13615 let blocks = display_map
13616 .insert_blocks(
13617 diagnostic_group.iter().map(|entry| {
13618 let diagnostic = entry.diagnostic.clone();
13619 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13620 BlockProperties {
13621 style: BlockStyle::Fixed,
13622 placement: BlockPlacement::Below(
13623 buffer.anchor_after(entry.range.start),
13624 ),
13625 height: message_height,
13626 render: diagnostic_block_renderer(diagnostic, None, true),
13627 priority: 0,
13628 }
13629 }),
13630 cx,
13631 )
13632 .into_iter()
13633 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13634 .collect();
13635
13636 Some(ActiveDiagnosticGroup {
13637 primary_range: buffer.anchor_before(primary_range.start)
13638 ..buffer.anchor_after(primary_range.end),
13639 primary_message,
13640 group_id,
13641 blocks,
13642 is_valid: true,
13643 })
13644 });
13645 }
13646
13647 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13648 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13649 self.display_map.update(cx, |display_map, cx| {
13650 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13651 });
13652 cx.notify();
13653 }
13654 }
13655
13656 /// Disable inline diagnostics rendering for this editor.
13657 pub fn disable_inline_diagnostics(&mut self) {
13658 self.inline_diagnostics_enabled = false;
13659 self.inline_diagnostics_update = Task::ready(());
13660 self.inline_diagnostics.clear();
13661 }
13662
13663 pub fn inline_diagnostics_enabled(&self) -> bool {
13664 self.inline_diagnostics_enabled
13665 }
13666
13667 pub fn show_inline_diagnostics(&self) -> bool {
13668 self.show_inline_diagnostics
13669 }
13670
13671 pub fn toggle_inline_diagnostics(
13672 &mut self,
13673 _: &ToggleInlineDiagnostics,
13674 window: &mut Window,
13675 cx: &mut Context<'_, Editor>,
13676 ) {
13677 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13678 self.refresh_inline_diagnostics(false, window, cx);
13679 }
13680
13681 fn refresh_inline_diagnostics(
13682 &mut self,
13683 debounce: bool,
13684 window: &mut Window,
13685 cx: &mut Context<Self>,
13686 ) {
13687 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13688 self.inline_diagnostics_update = Task::ready(());
13689 self.inline_diagnostics.clear();
13690 return;
13691 }
13692
13693 let debounce_ms = ProjectSettings::get_global(cx)
13694 .diagnostics
13695 .inline
13696 .update_debounce_ms;
13697 let debounce = if debounce && debounce_ms > 0 {
13698 Some(Duration::from_millis(debounce_ms))
13699 } else {
13700 None
13701 };
13702 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
13703 if let Some(debounce) = debounce {
13704 cx.background_executor().timer(debounce).await;
13705 }
13706 let Some(snapshot) = editor
13707 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13708 .ok()
13709 else {
13710 return;
13711 };
13712
13713 let new_inline_diagnostics = cx
13714 .background_spawn(async move {
13715 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13716 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13717 let message = diagnostic_entry
13718 .diagnostic
13719 .message
13720 .split_once('\n')
13721 .map(|(line, _)| line)
13722 .map(SharedString::new)
13723 .unwrap_or_else(|| {
13724 SharedString::from(diagnostic_entry.diagnostic.message)
13725 });
13726 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13727 let (Ok(i) | Err(i)) = inline_diagnostics
13728 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13729 inline_diagnostics.insert(
13730 i,
13731 (
13732 start_anchor,
13733 InlineDiagnostic {
13734 message,
13735 group_id: diagnostic_entry.diagnostic.group_id,
13736 start: diagnostic_entry.range.start.to_point(&snapshot),
13737 is_primary: diagnostic_entry.diagnostic.is_primary,
13738 severity: diagnostic_entry.diagnostic.severity,
13739 },
13740 ),
13741 );
13742 }
13743 inline_diagnostics
13744 })
13745 .await;
13746
13747 editor
13748 .update(&mut cx, |editor, cx| {
13749 editor.inline_diagnostics = new_inline_diagnostics;
13750 cx.notify();
13751 })
13752 .ok();
13753 });
13754 }
13755
13756 pub fn set_selections_from_remote(
13757 &mut self,
13758 selections: Vec<Selection<Anchor>>,
13759 pending_selection: Option<Selection<Anchor>>,
13760 window: &mut Window,
13761 cx: &mut Context<Self>,
13762 ) {
13763 let old_cursor_position = self.selections.newest_anchor().head();
13764 self.selections.change_with(cx, |s| {
13765 s.select_anchors(selections);
13766 if let Some(pending_selection) = pending_selection {
13767 s.set_pending(pending_selection, SelectMode::Character);
13768 } else {
13769 s.clear_pending();
13770 }
13771 });
13772 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13773 }
13774
13775 fn push_to_selection_history(&mut self) {
13776 self.selection_history.push(SelectionHistoryEntry {
13777 selections: self.selections.disjoint_anchors(),
13778 select_next_state: self.select_next_state.clone(),
13779 select_prev_state: self.select_prev_state.clone(),
13780 add_selections_state: self.add_selections_state.clone(),
13781 });
13782 }
13783
13784 pub fn transact(
13785 &mut self,
13786 window: &mut Window,
13787 cx: &mut Context<Self>,
13788 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13789 ) -> Option<TransactionId> {
13790 self.start_transaction_at(Instant::now(), window, cx);
13791 update(self, window, cx);
13792 self.end_transaction_at(Instant::now(), cx)
13793 }
13794
13795 pub fn start_transaction_at(
13796 &mut self,
13797 now: Instant,
13798 window: &mut Window,
13799 cx: &mut Context<Self>,
13800 ) {
13801 self.end_selection(window, cx);
13802 if let Some(tx_id) = self
13803 .buffer
13804 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13805 {
13806 self.selection_history
13807 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13808 cx.emit(EditorEvent::TransactionBegun {
13809 transaction_id: tx_id,
13810 })
13811 }
13812 }
13813
13814 pub fn end_transaction_at(
13815 &mut self,
13816 now: Instant,
13817 cx: &mut Context<Self>,
13818 ) -> Option<TransactionId> {
13819 if let Some(transaction_id) = self
13820 .buffer
13821 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13822 {
13823 if let Some((_, end_selections)) =
13824 self.selection_history.transaction_mut(transaction_id)
13825 {
13826 *end_selections = Some(self.selections.disjoint_anchors());
13827 } else {
13828 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13829 }
13830
13831 cx.emit(EditorEvent::Edited { transaction_id });
13832 Some(transaction_id)
13833 } else {
13834 None
13835 }
13836 }
13837
13838 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13839 if self.selection_mark_mode {
13840 self.change_selections(None, window, cx, |s| {
13841 s.move_with(|_, sel| {
13842 sel.collapse_to(sel.head(), SelectionGoal::None);
13843 });
13844 })
13845 }
13846 self.selection_mark_mode = true;
13847 cx.notify();
13848 }
13849
13850 pub fn swap_selection_ends(
13851 &mut self,
13852 _: &actions::SwapSelectionEnds,
13853 window: &mut Window,
13854 cx: &mut Context<Self>,
13855 ) {
13856 self.change_selections(None, window, cx, |s| {
13857 s.move_with(|_, sel| {
13858 if sel.start != sel.end {
13859 sel.reversed = !sel.reversed
13860 }
13861 });
13862 });
13863 self.request_autoscroll(Autoscroll::newest(), cx);
13864 cx.notify();
13865 }
13866
13867 pub fn toggle_fold(
13868 &mut self,
13869 _: &actions::ToggleFold,
13870 window: &mut Window,
13871 cx: &mut Context<Self>,
13872 ) {
13873 if self.is_singleton(cx) {
13874 let selection = self.selections.newest::<Point>(cx);
13875
13876 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13877 let range = if selection.is_empty() {
13878 let point = selection.head().to_display_point(&display_map);
13879 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13880 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13881 .to_point(&display_map);
13882 start..end
13883 } else {
13884 selection.range()
13885 };
13886 if display_map.folds_in_range(range).next().is_some() {
13887 self.unfold_lines(&Default::default(), window, cx)
13888 } else {
13889 self.fold(&Default::default(), window, cx)
13890 }
13891 } else {
13892 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13893 let buffer_ids: HashSet<_> = self
13894 .selections
13895 .disjoint_anchor_ranges()
13896 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13897 .collect();
13898
13899 let should_unfold = buffer_ids
13900 .iter()
13901 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13902
13903 for buffer_id in buffer_ids {
13904 if should_unfold {
13905 self.unfold_buffer(buffer_id, cx);
13906 } else {
13907 self.fold_buffer(buffer_id, cx);
13908 }
13909 }
13910 }
13911 }
13912
13913 pub fn toggle_fold_recursive(
13914 &mut self,
13915 _: &actions::ToggleFoldRecursive,
13916 window: &mut Window,
13917 cx: &mut Context<Self>,
13918 ) {
13919 let selection = self.selections.newest::<Point>(cx);
13920
13921 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13922 let range = if selection.is_empty() {
13923 let point = selection.head().to_display_point(&display_map);
13924 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13925 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13926 .to_point(&display_map);
13927 start..end
13928 } else {
13929 selection.range()
13930 };
13931 if display_map.folds_in_range(range).next().is_some() {
13932 self.unfold_recursive(&Default::default(), window, cx)
13933 } else {
13934 self.fold_recursive(&Default::default(), window, cx)
13935 }
13936 }
13937
13938 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13939 if self.is_singleton(cx) {
13940 let mut to_fold = Vec::new();
13941 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13942 let selections = self.selections.all_adjusted(cx);
13943
13944 for selection in selections {
13945 let range = selection.range().sorted();
13946 let buffer_start_row = range.start.row;
13947
13948 if range.start.row != range.end.row {
13949 let mut found = false;
13950 let mut row = range.start.row;
13951 while row <= range.end.row {
13952 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13953 {
13954 found = true;
13955 row = crease.range().end.row + 1;
13956 to_fold.push(crease);
13957 } else {
13958 row += 1
13959 }
13960 }
13961 if found {
13962 continue;
13963 }
13964 }
13965
13966 for row in (0..=range.start.row).rev() {
13967 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13968 if crease.range().end.row >= buffer_start_row {
13969 to_fold.push(crease);
13970 if row <= range.start.row {
13971 break;
13972 }
13973 }
13974 }
13975 }
13976 }
13977
13978 self.fold_creases(to_fold, true, window, cx);
13979 } else {
13980 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13981 let buffer_ids = self
13982 .selections
13983 .disjoint_anchor_ranges()
13984 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13985 .collect::<HashSet<_>>();
13986 for buffer_id in buffer_ids {
13987 self.fold_buffer(buffer_id, cx);
13988 }
13989 }
13990 }
13991
13992 fn fold_at_level(
13993 &mut self,
13994 fold_at: &FoldAtLevel,
13995 window: &mut Window,
13996 cx: &mut Context<Self>,
13997 ) {
13998 if !self.buffer.read(cx).is_singleton() {
13999 return;
14000 }
14001
14002 let fold_at_level = fold_at.0;
14003 let snapshot = self.buffer.read(cx).snapshot(cx);
14004 let mut to_fold = Vec::new();
14005 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14006
14007 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14008 while start_row < end_row {
14009 match self
14010 .snapshot(window, cx)
14011 .crease_for_buffer_row(MultiBufferRow(start_row))
14012 {
14013 Some(crease) => {
14014 let nested_start_row = crease.range().start.row + 1;
14015 let nested_end_row = crease.range().end.row;
14016
14017 if current_level < fold_at_level {
14018 stack.push((nested_start_row, nested_end_row, current_level + 1));
14019 } else if current_level == fold_at_level {
14020 to_fold.push(crease);
14021 }
14022
14023 start_row = nested_end_row + 1;
14024 }
14025 None => start_row += 1,
14026 }
14027 }
14028 }
14029
14030 self.fold_creases(to_fold, true, window, cx);
14031 }
14032
14033 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14034 if self.buffer.read(cx).is_singleton() {
14035 let mut fold_ranges = Vec::new();
14036 let snapshot = self.buffer.read(cx).snapshot(cx);
14037
14038 for row in 0..snapshot.max_row().0 {
14039 if let Some(foldable_range) = self
14040 .snapshot(window, cx)
14041 .crease_for_buffer_row(MultiBufferRow(row))
14042 {
14043 fold_ranges.push(foldable_range);
14044 }
14045 }
14046
14047 self.fold_creases(fold_ranges, true, window, cx);
14048 } else {
14049 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
14050 editor
14051 .update_in(&mut cx, |editor, _, cx| {
14052 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14053 editor.fold_buffer(buffer_id, cx);
14054 }
14055 })
14056 .ok();
14057 });
14058 }
14059 }
14060
14061 pub fn fold_function_bodies(
14062 &mut self,
14063 _: &actions::FoldFunctionBodies,
14064 window: &mut Window,
14065 cx: &mut Context<Self>,
14066 ) {
14067 let snapshot = self.buffer.read(cx).snapshot(cx);
14068
14069 let ranges = snapshot
14070 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14071 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14072 .collect::<Vec<_>>();
14073
14074 let creases = ranges
14075 .into_iter()
14076 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14077 .collect();
14078
14079 self.fold_creases(creases, true, window, cx);
14080 }
14081
14082 pub fn fold_recursive(
14083 &mut self,
14084 _: &actions::FoldRecursive,
14085 window: &mut Window,
14086 cx: &mut Context<Self>,
14087 ) {
14088 let mut to_fold = Vec::new();
14089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14090 let selections = self.selections.all_adjusted(cx);
14091
14092 for selection in selections {
14093 let range = selection.range().sorted();
14094 let buffer_start_row = range.start.row;
14095
14096 if range.start.row != range.end.row {
14097 let mut found = false;
14098 for row in range.start.row..=range.end.row {
14099 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14100 found = true;
14101 to_fold.push(crease);
14102 }
14103 }
14104 if found {
14105 continue;
14106 }
14107 }
14108
14109 for row in (0..=range.start.row).rev() {
14110 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14111 if crease.range().end.row >= buffer_start_row {
14112 to_fold.push(crease);
14113 } else {
14114 break;
14115 }
14116 }
14117 }
14118 }
14119
14120 self.fold_creases(to_fold, true, window, cx);
14121 }
14122
14123 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14124 let buffer_row = fold_at.buffer_row;
14125 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14126
14127 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14128 let autoscroll = self
14129 .selections
14130 .all::<Point>(cx)
14131 .iter()
14132 .any(|selection| crease.range().overlaps(&selection.range()));
14133
14134 self.fold_creases(vec![crease], autoscroll, window, cx);
14135 }
14136 }
14137
14138 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14139 if self.is_singleton(cx) {
14140 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14141 let buffer = &display_map.buffer_snapshot;
14142 let selections = self.selections.all::<Point>(cx);
14143 let ranges = selections
14144 .iter()
14145 .map(|s| {
14146 let range = s.display_range(&display_map).sorted();
14147 let mut start = range.start.to_point(&display_map);
14148 let mut end = range.end.to_point(&display_map);
14149 start.column = 0;
14150 end.column = buffer.line_len(MultiBufferRow(end.row));
14151 start..end
14152 })
14153 .collect::<Vec<_>>();
14154
14155 self.unfold_ranges(&ranges, true, true, cx);
14156 } else {
14157 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14158 let buffer_ids = self
14159 .selections
14160 .disjoint_anchor_ranges()
14161 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14162 .collect::<HashSet<_>>();
14163 for buffer_id in buffer_ids {
14164 self.unfold_buffer(buffer_id, cx);
14165 }
14166 }
14167 }
14168
14169 pub fn unfold_recursive(
14170 &mut self,
14171 _: &UnfoldRecursive,
14172 _window: &mut Window,
14173 cx: &mut Context<Self>,
14174 ) {
14175 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14176 let selections = self.selections.all::<Point>(cx);
14177 let ranges = selections
14178 .iter()
14179 .map(|s| {
14180 let mut range = s.display_range(&display_map).sorted();
14181 *range.start.column_mut() = 0;
14182 *range.end.column_mut() = display_map.line_len(range.end.row());
14183 let start = range.start.to_point(&display_map);
14184 let end = range.end.to_point(&display_map);
14185 start..end
14186 })
14187 .collect::<Vec<_>>();
14188
14189 self.unfold_ranges(&ranges, true, true, cx);
14190 }
14191
14192 pub fn unfold_at(
14193 &mut self,
14194 unfold_at: &UnfoldAt,
14195 _window: &mut Window,
14196 cx: &mut Context<Self>,
14197 ) {
14198 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14199
14200 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14201 ..Point::new(
14202 unfold_at.buffer_row.0,
14203 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14204 );
14205
14206 let autoscroll = self
14207 .selections
14208 .all::<Point>(cx)
14209 .iter()
14210 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14211
14212 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14213 }
14214
14215 pub fn unfold_all(
14216 &mut self,
14217 _: &actions::UnfoldAll,
14218 _window: &mut Window,
14219 cx: &mut Context<Self>,
14220 ) {
14221 if self.buffer.read(cx).is_singleton() {
14222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14223 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14224 } else {
14225 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
14226 editor
14227 .update(&mut cx, |editor, cx| {
14228 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14229 editor.unfold_buffer(buffer_id, cx);
14230 }
14231 })
14232 .ok();
14233 });
14234 }
14235 }
14236
14237 pub fn fold_selected_ranges(
14238 &mut self,
14239 _: &FoldSelectedRanges,
14240 window: &mut Window,
14241 cx: &mut Context<Self>,
14242 ) {
14243 let selections = self.selections.all::<Point>(cx);
14244 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14245 let line_mode = self.selections.line_mode;
14246 let ranges = selections
14247 .into_iter()
14248 .map(|s| {
14249 if line_mode {
14250 let start = Point::new(s.start.row, 0);
14251 let end = Point::new(
14252 s.end.row,
14253 display_map
14254 .buffer_snapshot
14255 .line_len(MultiBufferRow(s.end.row)),
14256 );
14257 Crease::simple(start..end, display_map.fold_placeholder.clone())
14258 } else {
14259 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14260 }
14261 })
14262 .collect::<Vec<_>>();
14263 self.fold_creases(ranges, true, window, cx);
14264 }
14265
14266 pub fn fold_ranges<T: ToOffset + Clone>(
14267 &mut self,
14268 ranges: Vec<Range<T>>,
14269 auto_scroll: bool,
14270 window: &mut Window,
14271 cx: &mut Context<Self>,
14272 ) {
14273 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14274 let ranges = ranges
14275 .into_iter()
14276 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14277 .collect::<Vec<_>>();
14278 self.fold_creases(ranges, auto_scroll, window, cx);
14279 }
14280
14281 pub fn fold_creases<T: ToOffset + Clone>(
14282 &mut self,
14283 creases: Vec<Crease<T>>,
14284 auto_scroll: bool,
14285 window: &mut Window,
14286 cx: &mut Context<Self>,
14287 ) {
14288 if creases.is_empty() {
14289 return;
14290 }
14291
14292 let mut buffers_affected = HashSet::default();
14293 let multi_buffer = self.buffer().read(cx);
14294 for crease in &creases {
14295 if let Some((_, buffer, _)) =
14296 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14297 {
14298 buffers_affected.insert(buffer.read(cx).remote_id());
14299 };
14300 }
14301
14302 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14303
14304 if auto_scroll {
14305 self.request_autoscroll(Autoscroll::fit(), cx);
14306 }
14307
14308 cx.notify();
14309
14310 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14311 // Clear diagnostics block when folding a range that contains it.
14312 let snapshot = self.snapshot(window, cx);
14313 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14314 drop(snapshot);
14315 self.active_diagnostics = Some(active_diagnostics);
14316 self.dismiss_diagnostics(cx);
14317 } else {
14318 self.active_diagnostics = Some(active_diagnostics);
14319 }
14320 }
14321
14322 self.scrollbar_marker_state.dirty = true;
14323 }
14324
14325 /// Removes any folds whose ranges intersect any of the given ranges.
14326 pub fn unfold_ranges<T: ToOffset + Clone>(
14327 &mut self,
14328 ranges: &[Range<T>],
14329 inclusive: bool,
14330 auto_scroll: bool,
14331 cx: &mut Context<Self>,
14332 ) {
14333 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14334 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14335 });
14336 }
14337
14338 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14339 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14340 return;
14341 }
14342 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14343 self.display_map.update(cx, |display_map, cx| {
14344 display_map.fold_buffers([buffer_id], cx)
14345 });
14346 cx.emit(EditorEvent::BufferFoldToggled {
14347 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14348 folded: true,
14349 });
14350 cx.notify();
14351 }
14352
14353 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14354 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14355 return;
14356 }
14357 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14358 self.display_map.update(cx, |display_map, cx| {
14359 display_map.unfold_buffers([buffer_id], cx);
14360 });
14361 cx.emit(EditorEvent::BufferFoldToggled {
14362 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14363 folded: false,
14364 });
14365 cx.notify();
14366 }
14367
14368 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14369 self.display_map.read(cx).is_buffer_folded(buffer)
14370 }
14371
14372 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14373 self.display_map.read(cx).folded_buffers()
14374 }
14375
14376 /// Removes any folds with the given ranges.
14377 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14378 &mut self,
14379 ranges: &[Range<T>],
14380 type_id: TypeId,
14381 auto_scroll: bool,
14382 cx: &mut Context<Self>,
14383 ) {
14384 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14385 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14386 });
14387 }
14388
14389 fn remove_folds_with<T: ToOffset + Clone>(
14390 &mut self,
14391 ranges: &[Range<T>],
14392 auto_scroll: bool,
14393 cx: &mut Context<Self>,
14394 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14395 ) {
14396 if ranges.is_empty() {
14397 return;
14398 }
14399
14400 let mut buffers_affected = HashSet::default();
14401 let multi_buffer = self.buffer().read(cx);
14402 for range in ranges {
14403 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14404 buffers_affected.insert(buffer.read(cx).remote_id());
14405 };
14406 }
14407
14408 self.display_map.update(cx, update);
14409
14410 if auto_scroll {
14411 self.request_autoscroll(Autoscroll::fit(), cx);
14412 }
14413
14414 cx.notify();
14415 self.scrollbar_marker_state.dirty = true;
14416 self.active_indent_guides_state.dirty = true;
14417 }
14418
14419 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14420 self.display_map.read(cx).fold_placeholder.clone()
14421 }
14422
14423 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14424 self.buffer.update(cx, |buffer, cx| {
14425 buffer.set_all_diff_hunks_expanded(cx);
14426 });
14427 }
14428
14429 pub fn expand_all_diff_hunks(
14430 &mut self,
14431 _: &ExpandAllDiffHunks,
14432 _window: &mut Window,
14433 cx: &mut Context<Self>,
14434 ) {
14435 self.buffer.update(cx, |buffer, cx| {
14436 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14437 });
14438 }
14439
14440 pub fn toggle_selected_diff_hunks(
14441 &mut self,
14442 _: &ToggleSelectedDiffHunks,
14443 _window: &mut Window,
14444 cx: &mut Context<Self>,
14445 ) {
14446 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14447 self.toggle_diff_hunks_in_ranges(ranges, cx);
14448 }
14449
14450 pub fn diff_hunks_in_ranges<'a>(
14451 &'a self,
14452 ranges: &'a [Range<Anchor>],
14453 buffer: &'a MultiBufferSnapshot,
14454 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14455 ranges.iter().flat_map(move |range| {
14456 let end_excerpt_id = range.end.excerpt_id;
14457 let range = range.to_point(buffer);
14458 let mut peek_end = range.end;
14459 if range.end.row < buffer.max_row().0 {
14460 peek_end = Point::new(range.end.row + 1, 0);
14461 }
14462 buffer
14463 .diff_hunks_in_range(range.start..peek_end)
14464 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14465 })
14466 }
14467
14468 pub fn has_stageable_diff_hunks_in_ranges(
14469 &self,
14470 ranges: &[Range<Anchor>],
14471 snapshot: &MultiBufferSnapshot,
14472 ) -> bool {
14473 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14474 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14475 }
14476
14477 pub fn toggle_staged_selected_diff_hunks(
14478 &mut self,
14479 _: &::git::ToggleStaged,
14480 _: &mut Window,
14481 cx: &mut Context<Self>,
14482 ) {
14483 let snapshot = self.buffer.read(cx).snapshot(cx);
14484 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14485 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14486 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14487 }
14488
14489 pub fn stage_and_next(
14490 &mut self,
14491 _: &::git::StageAndNext,
14492 window: &mut Window,
14493 cx: &mut Context<Self>,
14494 ) {
14495 self.do_stage_or_unstage_and_next(true, window, cx);
14496 }
14497
14498 pub fn unstage_and_next(
14499 &mut self,
14500 _: &::git::UnstageAndNext,
14501 window: &mut Window,
14502 cx: &mut Context<Self>,
14503 ) {
14504 self.do_stage_or_unstage_and_next(false, window, cx);
14505 }
14506
14507 pub fn stage_or_unstage_diff_hunks(
14508 &mut self,
14509 stage: bool,
14510 ranges: Vec<Range<Anchor>>,
14511 cx: &mut Context<Self>,
14512 ) {
14513 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14514 cx.spawn(|this, mut cx| async move {
14515 task.await?;
14516 this.update(&mut cx, |this, cx| {
14517 let snapshot = this.buffer.read(cx).snapshot(cx);
14518 let chunk_by = this
14519 .diff_hunks_in_ranges(&ranges, &snapshot)
14520 .chunk_by(|hunk| hunk.buffer_id);
14521 for (buffer_id, hunks) in &chunk_by {
14522 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14523 }
14524 })
14525 })
14526 .detach_and_log_err(cx);
14527 }
14528
14529 fn save_buffers_for_ranges_if_needed(
14530 &mut self,
14531 ranges: &[Range<Anchor>],
14532 cx: &mut Context<'_, Editor>,
14533 ) -> Task<Result<()>> {
14534 let multibuffer = self.buffer.read(cx);
14535 let snapshot = multibuffer.read(cx);
14536 let buffer_ids: HashSet<_> = ranges
14537 .iter()
14538 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14539 .collect();
14540 drop(snapshot);
14541
14542 let mut buffers = HashSet::default();
14543 for buffer_id in buffer_ids {
14544 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14545 let buffer = buffer_entity.read(cx);
14546 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14547 {
14548 buffers.insert(buffer_entity);
14549 }
14550 }
14551 }
14552
14553 if let Some(project) = &self.project {
14554 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14555 } else {
14556 Task::ready(Ok(()))
14557 }
14558 }
14559
14560 fn do_stage_or_unstage_and_next(
14561 &mut self,
14562 stage: bool,
14563 window: &mut Window,
14564 cx: &mut Context<Self>,
14565 ) {
14566 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14567
14568 if ranges.iter().any(|range| range.start != range.end) {
14569 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14570 return;
14571 }
14572
14573 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14574 let snapshot = self.snapshot(window, cx);
14575 let position = self.selections.newest::<Point>(cx).head();
14576 let mut row = snapshot
14577 .buffer_snapshot
14578 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14579 .find(|hunk| hunk.row_range.start.0 > position.row)
14580 .map(|hunk| hunk.row_range.start);
14581
14582 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14583 // Outside of the project diff editor, wrap around to the beginning.
14584 if !all_diff_hunks_expanded {
14585 row = row.or_else(|| {
14586 snapshot
14587 .buffer_snapshot
14588 .diff_hunks_in_range(Point::zero()..position)
14589 .find(|hunk| hunk.row_range.end.0 < position.row)
14590 .map(|hunk| hunk.row_range.start)
14591 });
14592 }
14593
14594 if let Some(row) = row {
14595 let destination = Point::new(row.0, 0);
14596 let autoscroll = Autoscroll::center();
14597
14598 self.unfold_ranges(&[destination..destination], false, false, cx);
14599 self.change_selections(Some(autoscroll), window, cx, |s| {
14600 s.select_ranges([destination..destination]);
14601 });
14602 }
14603 }
14604
14605 fn do_stage_or_unstage(
14606 &self,
14607 stage: bool,
14608 buffer_id: BufferId,
14609 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14610 cx: &mut App,
14611 ) -> Option<()> {
14612 let project = self.project.as_ref()?;
14613 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14614 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14615 let buffer_snapshot = buffer.read(cx).snapshot();
14616 let file_exists = buffer_snapshot
14617 .file()
14618 .is_some_and(|file| file.disk_state().exists());
14619 diff.update(cx, |diff, cx| {
14620 diff.stage_or_unstage_hunks(
14621 stage,
14622 &hunks
14623 .map(|hunk| buffer_diff::DiffHunk {
14624 buffer_range: hunk.buffer_range,
14625 diff_base_byte_range: hunk.diff_base_byte_range,
14626 secondary_status: hunk.secondary_status,
14627 range: Point::zero()..Point::zero(), // unused
14628 })
14629 .collect::<Vec<_>>(),
14630 &buffer_snapshot,
14631 file_exists,
14632 cx,
14633 )
14634 });
14635 None
14636 }
14637
14638 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14639 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14640 self.buffer
14641 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14642 }
14643
14644 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14645 self.buffer.update(cx, |buffer, cx| {
14646 let ranges = vec![Anchor::min()..Anchor::max()];
14647 if !buffer.all_diff_hunks_expanded()
14648 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14649 {
14650 buffer.collapse_diff_hunks(ranges, cx);
14651 true
14652 } else {
14653 false
14654 }
14655 })
14656 }
14657
14658 fn toggle_diff_hunks_in_ranges(
14659 &mut self,
14660 ranges: Vec<Range<Anchor>>,
14661 cx: &mut Context<'_, Editor>,
14662 ) {
14663 self.buffer.update(cx, |buffer, cx| {
14664 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14665 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14666 })
14667 }
14668
14669 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14670 self.buffer.update(cx, |buffer, cx| {
14671 let snapshot = buffer.snapshot(cx);
14672 let excerpt_id = range.end.excerpt_id;
14673 let point_range = range.to_point(&snapshot);
14674 let expand = !buffer.single_hunk_is_expanded(range, cx);
14675 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14676 })
14677 }
14678
14679 pub(crate) fn apply_all_diff_hunks(
14680 &mut self,
14681 _: &ApplyAllDiffHunks,
14682 window: &mut Window,
14683 cx: &mut Context<Self>,
14684 ) {
14685 let buffers = self.buffer.read(cx).all_buffers();
14686 for branch_buffer in buffers {
14687 branch_buffer.update(cx, |branch_buffer, cx| {
14688 branch_buffer.merge_into_base(Vec::new(), cx);
14689 });
14690 }
14691
14692 if let Some(project) = self.project.clone() {
14693 self.save(true, project, window, cx).detach_and_log_err(cx);
14694 }
14695 }
14696
14697 pub(crate) fn apply_selected_diff_hunks(
14698 &mut self,
14699 _: &ApplyDiffHunk,
14700 window: &mut Window,
14701 cx: &mut Context<Self>,
14702 ) {
14703 let snapshot = self.snapshot(window, cx);
14704 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14705 let mut ranges_by_buffer = HashMap::default();
14706 self.transact(window, cx, |editor, _window, cx| {
14707 for hunk in hunks {
14708 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14709 ranges_by_buffer
14710 .entry(buffer.clone())
14711 .or_insert_with(Vec::new)
14712 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14713 }
14714 }
14715
14716 for (buffer, ranges) in ranges_by_buffer {
14717 buffer.update(cx, |buffer, cx| {
14718 buffer.merge_into_base(ranges, cx);
14719 });
14720 }
14721 });
14722
14723 if let Some(project) = self.project.clone() {
14724 self.save(true, project, window, cx).detach_and_log_err(cx);
14725 }
14726 }
14727
14728 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14729 if hovered != self.gutter_hovered {
14730 self.gutter_hovered = hovered;
14731 cx.notify();
14732 }
14733 }
14734
14735 pub fn insert_blocks(
14736 &mut self,
14737 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14738 autoscroll: Option<Autoscroll>,
14739 cx: &mut Context<Self>,
14740 ) -> Vec<CustomBlockId> {
14741 let blocks = self
14742 .display_map
14743 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14744 if let Some(autoscroll) = autoscroll {
14745 self.request_autoscroll(autoscroll, cx);
14746 }
14747 cx.notify();
14748 blocks
14749 }
14750
14751 pub fn resize_blocks(
14752 &mut self,
14753 heights: HashMap<CustomBlockId, u32>,
14754 autoscroll: Option<Autoscroll>,
14755 cx: &mut Context<Self>,
14756 ) {
14757 self.display_map
14758 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14759 if let Some(autoscroll) = autoscroll {
14760 self.request_autoscroll(autoscroll, cx);
14761 }
14762 cx.notify();
14763 }
14764
14765 pub fn replace_blocks(
14766 &mut self,
14767 renderers: HashMap<CustomBlockId, RenderBlock>,
14768 autoscroll: Option<Autoscroll>,
14769 cx: &mut Context<Self>,
14770 ) {
14771 self.display_map
14772 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14773 if let Some(autoscroll) = autoscroll {
14774 self.request_autoscroll(autoscroll, cx);
14775 }
14776 cx.notify();
14777 }
14778
14779 pub fn remove_blocks(
14780 &mut self,
14781 block_ids: HashSet<CustomBlockId>,
14782 autoscroll: Option<Autoscroll>,
14783 cx: &mut Context<Self>,
14784 ) {
14785 self.display_map.update(cx, |display_map, cx| {
14786 display_map.remove_blocks(block_ids, cx)
14787 });
14788 if let Some(autoscroll) = autoscroll {
14789 self.request_autoscroll(autoscroll, cx);
14790 }
14791 cx.notify();
14792 }
14793
14794 pub fn row_for_block(
14795 &self,
14796 block_id: CustomBlockId,
14797 cx: &mut Context<Self>,
14798 ) -> Option<DisplayRow> {
14799 self.display_map
14800 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14801 }
14802
14803 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14804 self.focused_block = Some(focused_block);
14805 }
14806
14807 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14808 self.focused_block.take()
14809 }
14810
14811 pub fn insert_creases(
14812 &mut self,
14813 creases: impl IntoIterator<Item = Crease<Anchor>>,
14814 cx: &mut Context<Self>,
14815 ) -> Vec<CreaseId> {
14816 self.display_map
14817 .update(cx, |map, cx| map.insert_creases(creases, cx))
14818 }
14819
14820 pub fn remove_creases(
14821 &mut self,
14822 ids: impl IntoIterator<Item = CreaseId>,
14823 cx: &mut Context<Self>,
14824 ) {
14825 self.display_map
14826 .update(cx, |map, cx| map.remove_creases(ids, cx));
14827 }
14828
14829 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14830 self.display_map
14831 .update(cx, |map, cx| map.snapshot(cx))
14832 .longest_row()
14833 }
14834
14835 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14836 self.display_map
14837 .update(cx, |map, cx| map.snapshot(cx))
14838 .max_point()
14839 }
14840
14841 pub fn text(&self, cx: &App) -> String {
14842 self.buffer.read(cx).read(cx).text()
14843 }
14844
14845 pub fn is_empty(&self, cx: &App) -> bool {
14846 self.buffer.read(cx).read(cx).is_empty()
14847 }
14848
14849 pub fn text_option(&self, cx: &App) -> Option<String> {
14850 let text = self.text(cx);
14851 let text = text.trim();
14852
14853 if text.is_empty() {
14854 return None;
14855 }
14856
14857 Some(text.to_string())
14858 }
14859
14860 pub fn set_text(
14861 &mut self,
14862 text: impl Into<Arc<str>>,
14863 window: &mut Window,
14864 cx: &mut Context<Self>,
14865 ) {
14866 self.transact(window, cx, |this, _, cx| {
14867 this.buffer
14868 .read(cx)
14869 .as_singleton()
14870 .expect("you can only call set_text on editors for singleton buffers")
14871 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14872 });
14873 }
14874
14875 pub fn display_text(&self, cx: &mut App) -> String {
14876 self.display_map
14877 .update(cx, |map, cx| map.snapshot(cx))
14878 .text()
14879 }
14880
14881 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14882 let mut wrap_guides = smallvec::smallvec![];
14883
14884 if self.show_wrap_guides == Some(false) {
14885 return wrap_guides;
14886 }
14887
14888 let settings = self.buffer.read(cx).language_settings(cx);
14889 if settings.show_wrap_guides {
14890 match self.soft_wrap_mode(cx) {
14891 SoftWrap::Column(soft_wrap) => {
14892 wrap_guides.push((soft_wrap as usize, true));
14893 }
14894 SoftWrap::Bounded(soft_wrap) => {
14895 wrap_guides.push((soft_wrap as usize, true));
14896 }
14897 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14898 }
14899 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14900 }
14901
14902 wrap_guides
14903 }
14904
14905 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14906 let settings = self.buffer.read(cx).language_settings(cx);
14907 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14908 match mode {
14909 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14910 SoftWrap::None
14911 }
14912 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14913 language_settings::SoftWrap::PreferredLineLength => {
14914 SoftWrap::Column(settings.preferred_line_length)
14915 }
14916 language_settings::SoftWrap::Bounded => {
14917 SoftWrap::Bounded(settings.preferred_line_length)
14918 }
14919 }
14920 }
14921
14922 pub fn set_soft_wrap_mode(
14923 &mut self,
14924 mode: language_settings::SoftWrap,
14925
14926 cx: &mut Context<Self>,
14927 ) {
14928 self.soft_wrap_mode_override = Some(mode);
14929 cx.notify();
14930 }
14931
14932 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
14933 self.hard_wrap = hard_wrap;
14934 cx.notify();
14935 }
14936
14937 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14938 self.text_style_refinement = Some(style);
14939 }
14940
14941 /// called by the Element so we know what style we were most recently rendered with.
14942 pub(crate) fn set_style(
14943 &mut self,
14944 style: EditorStyle,
14945 window: &mut Window,
14946 cx: &mut Context<Self>,
14947 ) {
14948 let rem_size = window.rem_size();
14949 self.display_map.update(cx, |map, cx| {
14950 map.set_font(
14951 style.text.font(),
14952 style.text.font_size.to_pixels(rem_size),
14953 cx,
14954 )
14955 });
14956 self.style = Some(style);
14957 }
14958
14959 pub fn style(&self) -> Option<&EditorStyle> {
14960 self.style.as_ref()
14961 }
14962
14963 // Called by the element. This method is not designed to be called outside of the editor
14964 // element's layout code because it does not notify when rewrapping is computed synchronously.
14965 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14966 self.display_map
14967 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14968 }
14969
14970 pub fn set_soft_wrap(&mut self) {
14971 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14972 }
14973
14974 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14975 if self.soft_wrap_mode_override.is_some() {
14976 self.soft_wrap_mode_override.take();
14977 } else {
14978 let soft_wrap = match self.soft_wrap_mode(cx) {
14979 SoftWrap::GitDiff => return,
14980 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14981 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14982 language_settings::SoftWrap::None
14983 }
14984 };
14985 self.soft_wrap_mode_override = Some(soft_wrap);
14986 }
14987 cx.notify();
14988 }
14989
14990 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14991 let Some(workspace) = self.workspace() else {
14992 return;
14993 };
14994 let fs = workspace.read(cx).app_state().fs.clone();
14995 let current_show = TabBarSettings::get_global(cx).show;
14996 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14997 setting.show = Some(!current_show);
14998 });
14999 }
15000
15001 pub fn toggle_indent_guides(
15002 &mut self,
15003 _: &ToggleIndentGuides,
15004 _: &mut Window,
15005 cx: &mut Context<Self>,
15006 ) {
15007 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15008 self.buffer
15009 .read(cx)
15010 .language_settings(cx)
15011 .indent_guides
15012 .enabled
15013 });
15014 self.show_indent_guides = Some(!currently_enabled);
15015 cx.notify();
15016 }
15017
15018 fn should_show_indent_guides(&self) -> Option<bool> {
15019 self.show_indent_guides
15020 }
15021
15022 pub fn toggle_line_numbers(
15023 &mut self,
15024 _: &ToggleLineNumbers,
15025 _: &mut Window,
15026 cx: &mut Context<Self>,
15027 ) {
15028 let mut editor_settings = EditorSettings::get_global(cx).clone();
15029 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15030 EditorSettings::override_global(editor_settings, cx);
15031 }
15032
15033 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15034 if let Some(show_line_numbers) = self.show_line_numbers {
15035 return show_line_numbers;
15036 }
15037 EditorSettings::get_global(cx).gutter.line_numbers
15038 }
15039
15040 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15041 self.use_relative_line_numbers
15042 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15043 }
15044
15045 pub fn toggle_relative_line_numbers(
15046 &mut self,
15047 _: &ToggleRelativeLineNumbers,
15048 _: &mut Window,
15049 cx: &mut Context<Self>,
15050 ) {
15051 let is_relative = self.should_use_relative_line_numbers(cx);
15052 self.set_relative_line_number(Some(!is_relative), cx)
15053 }
15054
15055 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15056 self.use_relative_line_numbers = is_relative;
15057 cx.notify();
15058 }
15059
15060 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15061 self.show_gutter = show_gutter;
15062 cx.notify();
15063 }
15064
15065 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15066 self.show_scrollbars = show_scrollbars;
15067 cx.notify();
15068 }
15069
15070 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15071 self.show_line_numbers = Some(show_line_numbers);
15072 cx.notify();
15073 }
15074
15075 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15076 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15077 cx.notify();
15078 }
15079
15080 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15081 self.show_code_actions = Some(show_code_actions);
15082 cx.notify();
15083 }
15084
15085 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15086 self.show_runnables = Some(show_runnables);
15087 cx.notify();
15088 }
15089
15090 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15091 self.show_breakpoints = Some(show_breakpoints);
15092 cx.notify();
15093 }
15094
15095 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15096 if self.display_map.read(cx).masked != masked {
15097 self.display_map.update(cx, |map, _| map.masked = masked);
15098 }
15099 cx.notify()
15100 }
15101
15102 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15103 self.show_wrap_guides = Some(show_wrap_guides);
15104 cx.notify();
15105 }
15106
15107 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15108 self.show_indent_guides = Some(show_indent_guides);
15109 cx.notify();
15110 }
15111
15112 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15113 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15114 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15115 if let Some(dir) = file.abs_path(cx).parent() {
15116 return Some(dir.to_owned());
15117 }
15118 }
15119
15120 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15121 return Some(project_path.path.to_path_buf());
15122 }
15123 }
15124
15125 None
15126 }
15127
15128 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15129 self.active_excerpt(cx)?
15130 .1
15131 .read(cx)
15132 .file()
15133 .and_then(|f| f.as_local())
15134 }
15135
15136 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15137 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15138 let buffer = buffer.read(cx);
15139 if let Some(project_path) = buffer.project_path(cx) {
15140 let project = self.project.as_ref()?.read(cx);
15141 project.absolute_path(&project_path, cx)
15142 } else {
15143 buffer
15144 .file()
15145 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15146 }
15147 })
15148 }
15149
15150 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15151 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15152 let project_path = buffer.read(cx).project_path(cx)?;
15153 let project = self.project.as_ref()?.read(cx);
15154 let entry = project.entry_for_path(&project_path, cx)?;
15155 let path = entry.path.to_path_buf();
15156 Some(path)
15157 })
15158 }
15159
15160 pub fn reveal_in_finder(
15161 &mut self,
15162 _: &RevealInFileManager,
15163 _window: &mut Window,
15164 cx: &mut Context<Self>,
15165 ) {
15166 if let Some(target) = self.target_file(cx) {
15167 cx.reveal_path(&target.abs_path(cx));
15168 }
15169 }
15170
15171 pub fn copy_path(
15172 &mut self,
15173 _: &zed_actions::workspace::CopyPath,
15174 _window: &mut Window,
15175 cx: &mut Context<Self>,
15176 ) {
15177 if let Some(path) = self.target_file_abs_path(cx) {
15178 if let Some(path) = path.to_str() {
15179 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15180 }
15181 }
15182 }
15183
15184 pub fn copy_relative_path(
15185 &mut self,
15186 _: &zed_actions::workspace::CopyRelativePath,
15187 _window: &mut Window,
15188 cx: &mut Context<Self>,
15189 ) {
15190 if let Some(path) = self.target_file_path(cx) {
15191 if let Some(path) = path.to_str() {
15192 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15193 }
15194 }
15195 }
15196
15197 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15198 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15199 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15200 } else {
15201 None
15202 }
15203 }
15204
15205 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15206 let _ = maybe!({
15207 let breakpoint_store = self.breakpoint_store.as_ref()?;
15208
15209 let Some((_, _, active_position)) =
15210 breakpoint_store.read(cx).active_position().cloned()
15211 else {
15212 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15213 return None;
15214 };
15215
15216 let snapshot = self
15217 .project
15218 .as_ref()?
15219 .read(cx)
15220 .buffer_for_id(active_position.buffer_id?, cx)?
15221 .read(cx)
15222 .snapshot();
15223
15224 for (id, ExcerptRange { context, .. }) in self
15225 .buffer
15226 .read(cx)
15227 .excerpts_for_buffer(active_position.buffer_id?, cx)
15228 {
15229 if context.start.cmp(&active_position, &snapshot).is_ge()
15230 || context.end.cmp(&active_position, &snapshot).is_lt()
15231 {
15232 continue;
15233 }
15234 let snapshot = self.buffer.read(cx).snapshot(cx);
15235 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15236
15237 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15238 self.go_to_line::<DebugCurrentRowHighlight>(
15239 multibuffer_anchor,
15240 Some(cx.theme().colors().editor_debugger_active_line_background),
15241 window,
15242 cx,
15243 );
15244
15245 cx.notify();
15246 }
15247
15248 Some(())
15249 });
15250 }
15251
15252 pub fn copy_file_name_without_extension(
15253 &mut self,
15254 _: &CopyFileNameWithoutExtension,
15255 _: &mut Window,
15256 cx: &mut Context<Self>,
15257 ) {
15258 if let Some(file) = self.target_file(cx) {
15259 if let Some(file_stem) = file.path().file_stem() {
15260 if let Some(name) = file_stem.to_str() {
15261 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15262 }
15263 }
15264 }
15265 }
15266
15267 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15268 if let Some(file) = self.target_file(cx) {
15269 if let Some(file_name) = file.path().file_name() {
15270 if let Some(name) = file_name.to_str() {
15271 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15272 }
15273 }
15274 }
15275 }
15276
15277 pub fn toggle_git_blame(
15278 &mut self,
15279 _: &::git::Blame,
15280 window: &mut Window,
15281 cx: &mut Context<Self>,
15282 ) {
15283 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15284
15285 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15286 self.start_git_blame(true, window, cx);
15287 }
15288
15289 cx.notify();
15290 }
15291
15292 pub fn toggle_git_blame_inline(
15293 &mut self,
15294 _: &ToggleGitBlameInline,
15295 window: &mut Window,
15296 cx: &mut Context<Self>,
15297 ) {
15298 self.toggle_git_blame_inline_internal(true, window, cx);
15299 cx.notify();
15300 }
15301
15302 pub fn git_blame_inline_enabled(&self) -> bool {
15303 self.git_blame_inline_enabled
15304 }
15305
15306 pub fn toggle_selection_menu(
15307 &mut self,
15308 _: &ToggleSelectionMenu,
15309 _: &mut Window,
15310 cx: &mut Context<Self>,
15311 ) {
15312 self.show_selection_menu = self
15313 .show_selection_menu
15314 .map(|show_selections_menu| !show_selections_menu)
15315 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15316
15317 cx.notify();
15318 }
15319
15320 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15321 self.show_selection_menu
15322 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15323 }
15324
15325 fn start_git_blame(
15326 &mut self,
15327 user_triggered: bool,
15328 window: &mut Window,
15329 cx: &mut Context<Self>,
15330 ) {
15331 if let Some(project) = self.project.as_ref() {
15332 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15333 return;
15334 };
15335
15336 if buffer.read(cx).file().is_none() {
15337 return;
15338 }
15339
15340 let focused = self.focus_handle(cx).contains_focused(window, cx);
15341
15342 let project = project.clone();
15343 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15344 self.blame_subscription =
15345 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15346 self.blame = Some(blame);
15347 }
15348 }
15349
15350 fn toggle_git_blame_inline_internal(
15351 &mut self,
15352 user_triggered: bool,
15353 window: &mut Window,
15354 cx: &mut Context<Self>,
15355 ) {
15356 if self.git_blame_inline_enabled {
15357 self.git_blame_inline_enabled = false;
15358 self.show_git_blame_inline = false;
15359 self.show_git_blame_inline_delay_task.take();
15360 } else {
15361 self.git_blame_inline_enabled = true;
15362 self.start_git_blame_inline(user_triggered, window, cx);
15363 }
15364
15365 cx.notify();
15366 }
15367
15368 fn start_git_blame_inline(
15369 &mut self,
15370 user_triggered: bool,
15371 window: &mut Window,
15372 cx: &mut Context<Self>,
15373 ) {
15374 self.start_git_blame(user_triggered, window, cx);
15375
15376 if ProjectSettings::get_global(cx)
15377 .git
15378 .inline_blame_delay()
15379 .is_some()
15380 {
15381 self.start_inline_blame_timer(window, cx);
15382 } else {
15383 self.show_git_blame_inline = true
15384 }
15385 }
15386
15387 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15388 self.blame.as_ref()
15389 }
15390
15391 pub fn show_git_blame_gutter(&self) -> bool {
15392 self.show_git_blame_gutter
15393 }
15394
15395 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15396 self.show_git_blame_gutter && self.has_blame_entries(cx)
15397 }
15398
15399 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15400 self.show_git_blame_inline
15401 && (self.focus_handle.is_focused(window)
15402 || self
15403 .git_blame_inline_tooltip
15404 .as_ref()
15405 .and_then(|t| t.upgrade())
15406 .is_some())
15407 && !self.newest_selection_head_on_empty_line(cx)
15408 && self.has_blame_entries(cx)
15409 }
15410
15411 fn has_blame_entries(&self, cx: &App) -> bool {
15412 self.blame()
15413 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15414 }
15415
15416 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15417 let cursor_anchor = self.selections.newest_anchor().head();
15418
15419 let snapshot = self.buffer.read(cx).snapshot(cx);
15420 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15421
15422 snapshot.line_len(buffer_row) == 0
15423 }
15424
15425 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15426 let buffer_and_selection = maybe!({
15427 let selection = self.selections.newest::<Point>(cx);
15428 let selection_range = selection.range();
15429
15430 let multi_buffer = self.buffer().read(cx);
15431 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15432 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15433
15434 let (buffer, range, _) = if selection.reversed {
15435 buffer_ranges.first()
15436 } else {
15437 buffer_ranges.last()
15438 }?;
15439
15440 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15441 ..text::ToPoint::to_point(&range.end, &buffer).row;
15442 Some((
15443 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15444 selection,
15445 ))
15446 });
15447
15448 let Some((buffer, selection)) = buffer_and_selection else {
15449 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15450 };
15451
15452 let Some(project) = self.project.as_ref() else {
15453 return Task::ready(Err(anyhow!("editor does not have project")));
15454 };
15455
15456 project.update(cx, |project, cx| {
15457 project.get_permalink_to_line(&buffer, selection, cx)
15458 })
15459 }
15460
15461 pub fn copy_permalink_to_line(
15462 &mut self,
15463 _: &CopyPermalinkToLine,
15464 window: &mut Window,
15465 cx: &mut Context<Self>,
15466 ) {
15467 let permalink_task = self.get_permalink_to_line(cx);
15468 let workspace = self.workspace();
15469
15470 cx.spawn_in(window, |_, mut cx| async move {
15471 match permalink_task.await {
15472 Ok(permalink) => {
15473 cx.update(|_, cx| {
15474 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15475 })
15476 .ok();
15477 }
15478 Err(err) => {
15479 let message = format!("Failed to copy permalink: {err}");
15480
15481 Err::<(), anyhow::Error>(err).log_err();
15482
15483 if let Some(workspace) = workspace {
15484 workspace
15485 .update_in(&mut cx, |workspace, _, cx| {
15486 struct CopyPermalinkToLine;
15487
15488 workspace.show_toast(
15489 Toast::new(
15490 NotificationId::unique::<CopyPermalinkToLine>(),
15491 message,
15492 ),
15493 cx,
15494 )
15495 })
15496 .ok();
15497 }
15498 }
15499 }
15500 })
15501 .detach();
15502 }
15503
15504 pub fn copy_file_location(
15505 &mut self,
15506 _: &CopyFileLocation,
15507 _: &mut Window,
15508 cx: &mut Context<Self>,
15509 ) {
15510 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15511 if let Some(file) = self.target_file(cx) {
15512 if let Some(path) = file.path().to_str() {
15513 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15514 }
15515 }
15516 }
15517
15518 pub fn open_permalink_to_line(
15519 &mut self,
15520 _: &OpenPermalinkToLine,
15521 window: &mut Window,
15522 cx: &mut Context<Self>,
15523 ) {
15524 let permalink_task = self.get_permalink_to_line(cx);
15525 let workspace = self.workspace();
15526
15527 cx.spawn_in(window, |_, mut cx| async move {
15528 match permalink_task.await {
15529 Ok(permalink) => {
15530 cx.update(|_, cx| {
15531 cx.open_url(permalink.as_ref());
15532 })
15533 .ok();
15534 }
15535 Err(err) => {
15536 let message = format!("Failed to open permalink: {err}");
15537
15538 Err::<(), anyhow::Error>(err).log_err();
15539
15540 if let Some(workspace) = workspace {
15541 workspace
15542 .update(&mut cx, |workspace, cx| {
15543 struct OpenPermalinkToLine;
15544
15545 workspace.show_toast(
15546 Toast::new(
15547 NotificationId::unique::<OpenPermalinkToLine>(),
15548 message,
15549 ),
15550 cx,
15551 )
15552 })
15553 .ok();
15554 }
15555 }
15556 }
15557 })
15558 .detach();
15559 }
15560
15561 pub fn insert_uuid_v4(
15562 &mut self,
15563 _: &InsertUuidV4,
15564 window: &mut Window,
15565 cx: &mut Context<Self>,
15566 ) {
15567 self.insert_uuid(UuidVersion::V4, window, cx);
15568 }
15569
15570 pub fn insert_uuid_v7(
15571 &mut self,
15572 _: &InsertUuidV7,
15573 window: &mut Window,
15574 cx: &mut Context<Self>,
15575 ) {
15576 self.insert_uuid(UuidVersion::V7, window, cx);
15577 }
15578
15579 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15580 self.transact(window, cx, |this, window, cx| {
15581 let edits = this
15582 .selections
15583 .all::<Point>(cx)
15584 .into_iter()
15585 .map(|selection| {
15586 let uuid = match version {
15587 UuidVersion::V4 => uuid::Uuid::new_v4(),
15588 UuidVersion::V7 => uuid::Uuid::now_v7(),
15589 };
15590
15591 (selection.range(), uuid.to_string())
15592 });
15593 this.edit(edits, cx);
15594 this.refresh_inline_completion(true, false, window, cx);
15595 });
15596 }
15597
15598 pub fn open_selections_in_multibuffer(
15599 &mut self,
15600 _: &OpenSelectionsInMultibuffer,
15601 window: &mut Window,
15602 cx: &mut Context<Self>,
15603 ) {
15604 let multibuffer = self.buffer.read(cx);
15605
15606 let Some(buffer) = multibuffer.as_singleton() else {
15607 return;
15608 };
15609
15610 let Some(workspace) = self.workspace() else {
15611 return;
15612 };
15613
15614 let locations = self
15615 .selections
15616 .disjoint_anchors()
15617 .iter()
15618 .map(|range| Location {
15619 buffer: buffer.clone(),
15620 range: range.start.text_anchor..range.end.text_anchor,
15621 })
15622 .collect::<Vec<_>>();
15623
15624 let title = multibuffer.title(cx).to_string();
15625
15626 cx.spawn_in(window, |_, mut cx| async move {
15627 workspace.update_in(&mut cx, |workspace, window, cx| {
15628 Self::open_locations_in_multibuffer(
15629 workspace,
15630 locations,
15631 format!("Selections for '{title}'"),
15632 false,
15633 MultibufferSelectionMode::All,
15634 window,
15635 cx,
15636 );
15637 })
15638 })
15639 .detach();
15640 }
15641
15642 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15643 /// last highlight added will be used.
15644 ///
15645 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15646 pub fn highlight_rows<T: 'static>(
15647 &mut self,
15648 range: Range<Anchor>,
15649 color: Hsla,
15650 should_autoscroll: bool,
15651 cx: &mut Context<Self>,
15652 ) {
15653 let snapshot = self.buffer().read(cx).snapshot(cx);
15654 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15655 let ix = row_highlights.binary_search_by(|highlight| {
15656 Ordering::Equal
15657 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15658 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15659 });
15660
15661 if let Err(mut ix) = ix {
15662 let index = post_inc(&mut self.highlight_order);
15663
15664 // If this range intersects with the preceding highlight, then merge it with
15665 // the preceding highlight. Otherwise insert a new highlight.
15666 let mut merged = false;
15667 if ix > 0 {
15668 let prev_highlight = &mut row_highlights[ix - 1];
15669 if prev_highlight
15670 .range
15671 .end
15672 .cmp(&range.start, &snapshot)
15673 .is_ge()
15674 {
15675 ix -= 1;
15676 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15677 prev_highlight.range.end = range.end;
15678 }
15679 merged = true;
15680 prev_highlight.index = index;
15681 prev_highlight.color = color;
15682 prev_highlight.should_autoscroll = should_autoscroll;
15683 }
15684 }
15685
15686 if !merged {
15687 row_highlights.insert(
15688 ix,
15689 RowHighlight {
15690 range: range.clone(),
15691 index,
15692 color,
15693 should_autoscroll,
15694 },
15695 );
15696 }
15697
15698 // If any of the following highlights intersect with this one, merge them.
15699 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15700 let highlight = &row_highlights[ix];
15701 if next_highlight
15702 .range
15703 .start
15704 .cmp(&highlight.range.end, &snapshot)
15705 .is_le()
15706 {
15707 if next_highlight
15708 .range
15709 .end
15710 .cmp(&highlight.range.end, &snapshot)
15711 .is_gt()
15712 {
15713 row_highlights[ix].range.end = next_highlight.range.end;
15714 }
15715 row_highlights.remove(ix + 1);
15716 } else {
15717 break;
15718 }
15719 }
15720 }
15721 }
15722
15723 /// Remove any highlighted row ranges of the given type that intersect the
15724 /// given ranges.
15725 pub fn remove_highlighted_rows<T: 'static>(
15726 &mut self,
15727 ranges_to_remove: Vec<Range<Anchor>>,
15728 cx: &mut Context<Self>,
15729 ) {
15730 let snapshot = self.buffer().read(cx).snapshot(cx);
15731 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15732 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15733 row_highlights.retain(|highlight| {
15734 while let Some(range_to_remove) = ranges_to_remove.peek() {
15735 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15736 Ordering::Less | Ordering::Equal => {
15737 ranges_to_remove.next();
15738 }
15739 Ordering::Greater => {
15740 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15741 Ordering::Less | Ordering::Equal => {
15742 return false;
15743 }
15744 Ordering::Greater => break,
15745 }
15746 }
15747 }
15748 }
15749
15750 true
15751 })
15752 }
15753
15754 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15755 pub fn clear_row_highlights<T: 'static>(&mut self) {
15756 self.highlighted_rows.remove(&TypeId::of::<T>());
15757 }
15758
15759 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15760 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15761 self.highlighted_rows
15762 .get(&TypeId::of::<T>())
15763 .map_or(&[] as &[_], |vec| vec.as_slice())
15764 .iter()
15765 .map(|highlight| (highlight.range.clone(), highlight.color))
15766 }
15767
15768 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15769 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15770 /// Allows to ignore certain kinds of highlights.
15771 pub fn highlighted_display_rows(
15772 &self,
15773 window: &mut Window,
15774 cx: &mut App,
15775 ) -> BTreeMap<DisplayRow, LineHighlight> {
15776 let snapshot = self.snapshot(window, cx);
15777 let mut used_highlight_orders = HashMap::default();
15778 self.highlighted_rows
15779 .iter()
15780 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15781 .fold(
15782 BTreeMap::<DisplayRow, LineHighlight>::new(),
15783 |mut unique_rows, highlight| {
15784 let start = highlight.range.start.to_display_point(&snapshot);
15785 let end = highlight.range.end.to_display_point(&snapshot);
15786 let start_row = start.row().0;
15787 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15788 && end.column() == 0
15789 {
15790 end.row().0.saturating_sub(1)
15791 } else {
15792 end.row().0
15793 };
15794 for row in start_row..=end_row {
15795 let used_index =
15796 used_highlight_orders.entry(row).or_insert(highlight.index);
15797 if highlight.index >= *used_index {
15798 *used_index = highlight.index;
15799 unique_rows.insert(DisplayRow(row), highlight.color.into());
15800 }
15801 }
15802 unique_rows
15803 },
15804 )
15805 }
15806
15807 pub fn highlighted_display_row_for_autoscroll(
15808 &self,
15809 snapshot: &DisplaySnapshot,
15810 ) -> Option<DisplayRow> {
15811 self.highlighted_rows
15812 .values()
15813 .flat_map(|highlighted_rows| highlighted_rows.iter())
15814 .filter_map(|highlight| {
15815 if highlight.should_autoscroll {
15816 Some(highlight.range.start.to_display_point(snapshot).row())
15817 } else {
15818 None
15819 }
15820 })
15821 .min()
15822 }
15823
15824 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
15825 self.highlight_background::<SearchWithinRange>(
15826 ranges,
15827 |colors| colors.editor_document_highlight_read_background,
15828 cx,
15829 )
15830 }
15831
15832 pub fn set_breadcrumb_header(&mut self, new_header: String) {
15833 self.breadcrumb_header = Some(new_header);
15834 }
15835
15836 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
15837 self.clear_background_highlights::<SearchWithinRange>(cx);
15838 }
15839
15840 pub fn highlight_background<T: 'static>(
15841 &mut self,
15842 ranges: &[Range<Anchor>],
15843 color_fetcher: fn(&ThemeColors) -> Hsla,
15844 cx: &mut Context<Self>,
15845 ) {
15846 self.background_highlights
15847 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15848 self.scrollbar_marker_state.dirty = true;
15849 cx.notify();
15850 }
15851
15852 pub fn clear_background_highlights<T: 'static>(
15853 &mut self,
15854 cx: &mut Context<Self>,
15855 ) -> Option<BackgroundHighlight> {
15856 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15857 if !text_highlights.1.is_empty() {
15858 self.scrollbar_marker_state.dirty = true;
15859 cx.notify();
15860 }
15861 Some(text_highlights)
15862 }
15863
15864 pub fn highlight_gutter<T: 'static>(
15865 &mut self,
15866 ranges: &[Range<Anchor>],
15867 color_fetcher: fn(&App) -> Hsla,
15868 cx: &mut Context<Self>,
15869 ) {
15870 self.gutter_highlights
15871 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15872 cx.notify();
15873 }
15874
15875 pub fn clear_gutter_highlights<T: 'static>(
15876 &mut self,
15877 cx: &mut Context<Self>,
15878 ) -> Option<GutterHighlight> {
15879 cx.notify();
15880 self.gutter_highlights.remove(&TypeId::of::<T>())
15881 }
15882
15883 #[cfg(feature = "test-support")]
15884 pub fn all_text_background_highlights(
15885 &self,
15886 window: &mut Window,
15887 cx: &mut Context<Self>,
15888 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15889 let snapshot = self.snapshot(window, cx);
15890 let buffer = &snapshot.buffer_snapshot;
15891 let start = buffer.anchor_before(0);
15892 let end = buffer.anchor_after(buffer.len());
15893 let theme = cx.theme().colors();
15894 self.background_highlights_in_range(start..end, &snapshot, theme)
15895 }
15896
15897 #[cfg(feature = "test-support")]
15898 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15899 let snapshot = self.buffer().read(cx).snapshot(cx);
15900
15901 let highlights = self
15902 .background_highlights
15903 .get(&TypeId::of::<items::BufferSearchHighlights>());
15904
15905 if let Some((_color, ranges)) = highlights {
15906 ranges
15907 .iter()
15908 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15909 .collect_vec()
15910 } else {
15911 vec![]
15912 }
15913 }
15914
15915 fn document_highlights_for_position<'a>(
15916 &'a self,
15917 position: Anchor,
15918 buffer: &'a MultiBufferSnapshot,
15919 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15920 let read_highlights = self
15921 .background_highlights
15922 .get(&TypeId::of::<DocumentHighlightRead>())
15923 .map(|h| &h.1);
15924 let write_highlights = self
15925 .background_highlights
15926 .get(&TypeId::of::<DocumentHighlightWrite>())
15927 .map(|h| &h.1);
15928 let left_position = position.bias_left(buffer);
15929 let right_position = position.bias_right(buffer);
15930 read_highlights
15931 .into_iter()
15932 .chain(write_highlights)
15933 .flat_map(move |ranges| {
15934 let start_ix = match ranges.binary_search_by(|probe| {
15935 let cmp = probe.end.cmp(&left_position, buffer);
15936 if cmp.is_ge() {
15937 Ordering::Greater
15938 } else {
15939 Ordering::Less
15940 }
15941 }) {
15942 Ok(i) | Err(i) => i,
15943 };
15944
15945 ranges[start_ix..]
15946 .iter()
15947 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15948 })
15949 }
15950
15951 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15952 self.background_highlights
15953 .get(&TypeId::of::<T>())
15954 .map_or(false, |(_, highlights)| !highlights.is_empty())
15955 }
15956
15957 pub fn background_highlights_in_range(
15958 &self,
15959 search_range: Range<Anchor>,
15960 display_snapshot: &DisplaySnapshot,
15961 theme: &ThemeColors,
15962 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15963 let mut results = Vec::new();
15964 for (color_fetcher, ranges) in self.background_highlights.values() {
15965 let color = color_fetcher(theme);
15966 let start_ix = match ranges.binary_search_by(|probe| {
15967 let cmp = probe
15968 .end
15969 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15970 if cmp.is_gt() {
15971 Ordering::Greater
15972 } else {
15973 Ordering::Less
15974 }
15975 }) {
15976 Ok(i) | Err(i) => i,
15977 };
15978 for range in &ranges[start_ix..] {
15979 if range
15980 .start
15981 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15982 .is_ge()
15983 {
15984 break;
15985 }
15986
15987 let start = range.start.to_display_point(display_snapshot);
15988 let end = range.end.to_display_point(display_snapshot);
15989 results.push((start..end, color))
15990 }
15991 }
15992 results
15993 }
15994
15995 pub fn background_highlight_row_ranges<T: 'static>(
15996 &self,
15997 search_range: Range<Anchor>,
15998 display_snapshot: &DisplaySnapshot,
15999 count: usize,
16000 ) -> Vec<RangeInclusive<DisplayPoint>> {
16001 let mut results = Vec::new();
16002 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16003 return vec![];
16004 };
16005
16006 let start_ix = match ranges.binary_search_by(|probe| {
16007 let cmp = probe
16008 .end
16009 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16010 if cmp.is_gt() {
16011 Ordering::Greater
16012 } else {
16013 Ordering::Less
16014 }
16015 }) {
16016 Ok(i) | Err(i) => i,
16017 };
16018 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16019 if let (Some(start_display), Some(end_display)) = (start, end) {
16020 results.push(
16021 start_display.to_display_point(display_snapshot)
16022 ..=end_display.to_display_point(display_snapshot),
16023 );
16024 }
16025 };
16026 let mut start_row: Option<Point> = None;
16027 let mut end_row: Option<Point> = None;
16028 if ranges.len() > count {
16029 return Vec::new();
16030 }
16031 for range in &ranges[start_ix..] {
16032 if range
16033 .start
16034 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16035 .is_ge()
16036 {
16037 break;
16038 }
16039 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16040 if let Some(current_row) = &end_row {
16041 if end.row == current_row.row {
16042 continue;
16043 }
16044 }
16045 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16046 if start_row.is_none() {
16047 assert_eq!(end_row, None);
16048 start_row = Some(start);
16049 end_row = Some(end);
16050 continue;
16051 }
16052 if let Some(current_end) = end_row.as_mut() {
16053 if start.row > current_end.row + 1 {
16054 push_region(start_row, end_row);
16055 start_row = Some(start);
16056 end_row = Some(end);
16057 } else {
16058 // Merge two hunks.
16059 *current_end = end;
16060 }
16061 } else {
16062 unreachable!();
16063 }
16064 }
16065 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16066 push_region(start_row, end_row);
16067 results
16068 }
16069
16070 pub fn gutter_highlights_in_range(
16071 &self,
16072 search_range: Range<Anchor>,
16073 display_snapshot: &DisplaySnapshot,
16074 cx: &App,
16075 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16076 let mut results = Vec::new();
16077 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16078 let color = color_fetcher(cx);
16079 let start_ix = match ranges.binary_search_by(|probe| {
16080 let cmp = probe
16081 .end
16082 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16083 if cmp.is_gt() {
16084 Ordering::Greater
16085 } else {
16086 Ordering::Less
16087 }
16088 }) {
16089 Ok(i) | Err(i) => i,
16090 };
16091 for range in &ranges[start_ix..] {
16092 if range
16093 .start
16094 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16095 .is_ge()
16096 {
16097 break;
16098 }
16099
16100 let start = range.start.to_display_point(display_snapshot);
16101 let end = range.end.to_display_point(display_snapshot);
16102 results.push((start..end, color))
16103 }
16104 }
16105 results
16106 }
16107
16108 /// Get the text ranges corresponding to the redaction query
16109 pub fn redacted_ranges(
16110 &self,
16111 search_range: Range<Anchor>,
16112 display_snapshot: &DisplaySnapshot,
16113 cx: &App,
16114 ) -> Vec<Range<DisplayPoint>> {
16115 display_snapshot
16116 .buffer_snapshot
16117 .redacted_ranges(search_range, |file| {
16118 if let Some(file) = file {
16119 file.is_private()
16120 && EditorSettings::get(
16121 Some(SettingsLocation {
16122 worktree_id: file.worktree_id(cx),
16123 path: file.path().as_ref(),
16124 }),
16125 cx,
16126 )
16127 .redact_private_values
16128 } else {
16129 false
16130 }
16131 })
16132 .map(|range| {
16133 range.start.to_display_point(display_snapshot)
16134 ..range.end.to_display_point(display_snapshot)
16135 })
16136 .collect()
16137 }
16138
16139 pub fn highlight_text<T: 'static>(
16140 &mut self,
16141 ranges: Vec<Range<Anchor>>,
16142 style: HighlightStyle,
16143 cx: &mut Context<Self>,
16144 ) {
16145 self.display_map.update(cx, |map, _| {
16146 map.highlight_text(TypeId::of::<T>(), ranges, style)
16147 });
16148 cx.notify();
16149 }
16150
16151 pub(crate) fn highlight_inlays<T: 'static>(
16152 &mut self,
16153 highlights: Vec<InlayHighlight>,
16154 style: HighlightStyle,
16155 cx: &mut Context<Self>,
16156 ) {
16157 self.display_map.update(cx, |map, _| {
16158 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16159 });
16160 cx.notify();
16161 }
16162
16163 pub fn text_highlights<'a, T: 'static>(
16164 &'a self,
16165 cx: &'a App,
16166 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16167 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16168 }
16169
16170 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16171 let cleared = self
16172 .display_map
16173 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16174 if cleared {
16175 cx.notify();
16176 }
16177 }
16178
16179 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16180 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16181 && self.focus_handle.is_focused(window)
16182 }
16183
16184 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16185 self.show_cursor_when_unfocused = is_enabled;
16186 cx.notify();
16187 }
16188
16189 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16190 cx.notify();
16191 }
16192
16193 fn on_buffer_event(
16194 &mut self,
16195 multibuffer: &Entity<MultiBuffer>,
16196 event: &multi_buffer::Event,
16197 window: &mut Window,
16198 cx: &mut Context<Self>,
16199 ) {
16200 match event {
16201 multi_buffer::Event::Edited {
16202 singleton_buffer_edited,
16203 edited_buffer: buffer_edited,
16204 } => {
16205 self.scrollbar_marker_state.dirty = true;
16206 self.active_indent_guides_state.dirty = true;
16207 self.refresh_active_diagnostics(cx);
16208 self.refresh_code_actions(window, cx);
16209 if self.has_active_inline_completion() {
16210 self.update_visible_inline_completion(window, cx);
16211 }
16212 if let Some(buffer) = buffer_edited {
16213 let buffer_id = buffer.read(cx).remote_id();
16214 if !self.registered_buffers.contains_key(&buffer_id) {
16215 if let Some(project) = self.project.as_ref() {
16216 project.update(cx, |project, cx| {
16217 self.registered_buffers.insert(
16218 buffer_id,
16219 project.register_buffer_with_language_servers(&buffer, cx),
16220 );
16221 })
16222 }
16223 }
16224 }
16225 cx.emit(EditorEvent::BufferEdited);
16226 cx.emit(SearchEvent::MatchesInvalidated);
16227 if *singleton_buffer_edited {
16228 if let Some(project) = &self.project {
16229 #[allow(clippy::mutable_key_type)]
16230 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16231 multibuffer
16232 .all_buffers()
16233 .into_iter()
16234 .filter_map(|buffer| {
16235 buffer.update(cx, |buffer, cx| {
16236 let language = buffer.language()?;
16237 let should_discard = project.update(cx, |project, cx| {
16238 project.is_local()
16239 && !project.has_language_servers_for(buffer, cx)
16240 });
16241 should_discard.not().then_some(language.clone())
16242 })
16243 })
16244 .collect::<HashSet<_>>()
16245 });
16246 if !languages_affected.is_empty() {
16247 self.refresh_inlay_hints(
16248 InlayHintRefreshReason::BufferEdited(languages_affected),
16249 cx,
16250 );
16251 }
16252 }
16253 }
16254
16255 let Some(project) = &self.project else { return };
16256 let (telemetry, is_via_ssh) = {
16257 let project = project.read(cx);
16258 let telemetry = project.client().telemetry().clone();
16259 let is_via_ssh = project.is_via_ssh();
16260 (telemetry, is_via_ssh)
16261 };
16262 refresh_linked_ranges(self, window, cx);
16263 telemetry.log_edit_event("editor", is_via_ssh);
16264 }
16265 multi_buffer::Event::ExcerptsAdded {
16266 buffer,
16267 predecessor,
16268 excerpts,
16269 } => {
16270 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16271 let buffer_id = buffer.read(cx).remote_id();
16272 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16273 if let Some(project) = &self.project {
16274 get_uncommitted_diff_for_buffer(
16275 project,
16276 [buffer.clone()],
16277 self.buffer.clone(),
16278 cx,
16279 )
16280 .detach();
16281 }
16282 }
16283 cx.emit(EditorEvent::ExcerptsAdded {
16284 buffer: buffer.clone(),
16285 predecessor: *predecessor,
16286 excerpts: excerpts.clone(),
16287 });
16288 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16289 }
16290 multi_buffer::Event::ExcerptsRemoved { ids } => {
16291 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16292 let buffer = self.buffer.read(cx);
16293 self.registered_buffers
16294 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16295 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16296 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16297 }
16298 multi_buffer::Event::ExcerptsEdited {
16299 excerpt_ids,
16300 buffer_ids,
16301 } => {
16302 self.display_map.update(cx, |map, cx| {
16303 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16304 });
16305 cx.emit(EditorEvent::ExcerptsEdited {
16306 ids: excerpt_ids.clone(),
16307 })
16308 }
16309 multi_buffer::Event::ExcerptsExpanded { ids } => {
16310 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16311 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16312 }
16313 multi_buffer::Event::Reparsed(buffer_id) => {
16314 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16315 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16316
16317 cx.emit(EditorEvent::Reparsed(*buffer_id));
16318 }
16319 multi_buffer::Event::DiffHunksToggled => {
16320 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16321 }
16322 multi_buffer::Event::LanguageChanged(buffer_id) => {
16323 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16324 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16325 cx.emit(EditorEvent::Reparsed(*buffer_id));
16326 cx.notify();
16327 }
16328 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16329 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16330 multi_buffer::Event::FileHandleChanged
16331 | multi_buffer::Event::Reloaded
16332 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16333 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16334 multi_buffer::Event::DiagnosticsUpdated => {
16335 self.refresh_active_diagnostics(cx);
16336 self.refresh_inline_diagnostics(true, window, cx);
16337 self.scrollbar_marker_state.dirty = true;
16338 cx.notify();
16339 }
16340 _ => {}
16341 };
16342 }
16343
16344 fn on_display_map_changed(
16345 &mut self,
16346 _: Entity<DisplayMap>,
16347 _: &mut Window,
16348 cx: &mut Context<Self>,
16349 ) {
16350 cx.notify();
16351 }
16352
16353 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16354 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16355 self.update_edit_prediction_settings(cx);
16356 self.refresh_inline_completion(true, false, window, cx);
16357 self.refresh_inlay_hints(
16358 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16359 self.selections.newest_anchor().head(),
16360 &self.buffer.read(cx).snapshot(cx),
16361 cx,
16362 )),
16363 cx,
16364 );
16365
16366 let old_cursor_shape = self.cursor_shape;
16367
16368 {
16369 let editor_settings = EditorSettings::get_global(cx);
16370 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16371 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16372 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16373 }
16374
16375 if old_cursor_shape != self.cursor_shape {
16376 cx.emit(EditorEvent::CursorShapeChanged);
16377 }
16378
16379 let project_settings = ProjectSettings::get_global(cx);
16380 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16381
16382 if self.mode == EditorMode::Full {
16383 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16384 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16385 if self.show_inline_diagnostics != show_inline_diagnostics {
16386 self.show_inline_diagnostics = show_inline_diagnostics;
16387 self.refresh_inline_diagnostics(false, window, cx);
16388 }
16389
16390 if self.git_blame_inline_enabled != inline_blame_enabled {
16391 self.toggle_git_blame_inline_internal(false, window, cx);
16392 }
16393 }
16394
16395 cx.notify();
16396 }
16397
16398 pub fn set_searchable(&mut self, searchable: bool) {
16399 self.searchable = searchable;
16400 }
16401
16402 pub fn searchable(&self) -> bool {
16403 self.searchable
16404 }
16405
16406 fn open_proposed_changes_editor(
16407 &mut self,
16408 _: &OpenProposedChangesEditor,
16409 window: &mut Window,
16410 cx: &mut Context<Self>,
16411 ) {
16412 let Some(workspace) = self.workspace() else {
16413 cx.propagate();
16414 return;
16415 };
16416
16417 let selections = self.selections.all::<usize>(cx);
16418 let multi_buffer = self.buffer.read(cx);
16419 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16420 let mut new_selections_by_buffer = HashMap::default();
16421 for selection in selections {
16422 for (buffer, range, _) in
16423 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16424 {
16425 let mut range = range.to_point(buffer);
16426 range.start.column = 0;
16427 range.end.column = buffer.line_len(range.end.row);
16428 new_selections_by_buffer
16429 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16430 .or_insert(Vec::new())
16431 .push(range)
16432 }
16433 }
16434
16435 let proposed_changes_buffers = new_selections_by_buffer
16436 .into_iter()
16437 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16438 .collect::<Vec<_>>();
16439 let proposed_changes_editor = cx.new(|cx| {
16440 ProposedChangesEditor::new(
16441 "Proposed changes",
16442 proposed_changes_buffers,
16443 self.project.clone(),
16444 window,
16445 cx,
16446 )
16447 });
16448
16449 window.defer(cx, move |window, cx| {
16450 workspace.update(cx, |workspace, cx| {
16451 workspace.active_pane().update(cx, |pane, cx| {
16452 pane.add_item(
16453 Box::new(proposed_changes_editor),
16454 true,
16455 true,
16456 None,
16457 window,
16458 cx,
16459 );
16460 });
16461 });
16462 });
16463 }
16464
16465 pub fn open_excerpts_in_split(
16466 &mut self,
16467 _: &OpenExcerptsSplit,
16468 window: &mut Window,
16469 cx: &mut Context<Self>,
16470 ) {
16471 self.open_excerpts_common(None, true, window, cx)
16472 }
16473
16474 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16475 self.open_excerpts_common(None, false, window, cx)
16476 }
16477
16478 fn open_excerpts_common(
16479 &mut self,
16480 jump_data: Option<JumpData>,
16481 split: bool,
16482 window: &mut Window,
16483 cx: &mut Context<Self>,
16484 ) {
16485 let Some(workspace) = self.workspace() else {
16486 cx.propagate();
16487 return;
16488 };
16489
16490 if self.buffer.read(cx).is_singleton() {
16491 cx.propagate();
16492 return;
16493 }
16494
16495 let mut new_selections_by_buffer = HashMap::default();
16496 match &jump_data {
16497 Some(JumpData::MultiBufferPoint {
16498 excerpt_id,
16499 position,
16500 anchor,
16501 line_offset_from_top,
16502 }) => {
16503 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16504 if let Some(buffer) = multi_buffer_snapshot
16505 .buffer_id_for_excerpt(*excerpt_id)
16506 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16507 {
16508 let buffer_snapshot = buffer.read(cx).snapshot();
16509 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16510 language::ToPoint::to_point(anchor, &buffer_snapshot)
16511 } else {
16512 buffer_snapshot.clip_point(*position, Bias::Left)
16513 };
16514 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16515 new_selections_by_buffer.insert(
16516 buffer,
16517 (
16518 vec![jump_to_offset..jump_to_offset],
16519 Some(*line_offset_from_top),
16520 ),
16521 );
16522 }
16523 }
16524 Some(JumpData::MultiBufferRow {
16525 row,
16526 line_offset_from_top,
16527 }) => {
16528 let point = MultiBufferPoint::new(row.0, 0);
16529 if let Some((buffer, buffer_point, _)) =
16530 self.buffer.read(cx).point_to_buffer_point(point, cx)
16531 {
16532 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16533 new_selections_by_buffer
16534 .entry(buffer)
16535 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16536 .0
16537 .push(buffer_offset..buffer_offset)
16538 }
16539 }
16540 None => {
16541 let selections = self.selections.all::<usize>(cx);
16542 let multi_buffer = self.buffer.read(cx);
16543 for selection in selections {
16544 for (snapshot, range, _, anchor) in multi_buffer
16545 .snapshot(cx)
16546 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16547 {
16548 if let Some(anchor) = anchor {
16549 // selection is in a deleted hunk
16550 let Some(buffer_id) = anchor.buffer_id else {
16551 continue;
16552 };
16553 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16554 continue;
16555 };
16556 let offset = text::ToOffset::to_offset(
16557 &anchor.text_anchor,
16558 &buffer_handle.read(cx).snapshot(),
16559 );
16560 let range = offset..offset;
16561 new_selections_by_buffer
16562 .entry(buffer_handle)
16563 .or_insert((Vec::new(), None))
16564 .0
16565 .push(range)
16566 } else {
16567 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16568 else {
16569 continue;
16570 };
16571 new_selections_by_buffer
16572 .entry(buffer_handle)
16573 .or_insert((Vec::new(), None))
16574 .0
16575 .push(range)
16576 }
16577 }
16578 }
16579 }
16580 }
16581
16582 if new_selections_by_buffer.is_empty() {
16583 return;
16584 }
16585
16586 // We defer the pane interaction because we ourselves are a workspace item
16587 // and activating a new item causes the pane to call a method on us reentrantly,
16588 // which panics if we're on the stack.
16589 window.defer(cx, move |window, cx| {
16590 workspace.update(cx, |workspace, cx| {
16591 let pane = if split {
16592 workspace.adjacent_pane(window, cx)
16593 } else {
16594 workspace.active_pane().clone()
16595 };
16596
16597 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16598 let editor = buffer
16599 .read(cx)
16600 .file()
16601 .is_none()
16602 .then(|| {
16603 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16604 // so `workspace.open_project_item` will never find them, always opening a new editor.
16605 // Instead, we try to activate the existing editor in the pane first.
16606 let (editor, pane_item_index) =
16607 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16608 let editor = item.downcast::<Editor>()?;
16609 let singleton_buffer =
16610 editor.read(cx).buffer().read(cx).as_singleton()?;
16611 if singleton_buffer == buffer {
16612 Some((editor, i))
16613 } else {
16614 None
16615 }
16616 })?;
16617 pane.update(cx, |pane, cx| {
16618 pane.activate_item(pane_item_index, true, true, window, cx)
16619 });
16620 Some(editor)
16621 })
16622 .flatten()
16623 .unwrap_or_else(|| {
16624 workspace.open_project_item::<Self>(
16625 pane.clone(),
16626 buffer,
16627 true,
16628 true,
16629 window,
16630 cx,
16631 )
16632 });
16633
16634 editor.update(cx, |editor, cx| {
16635 let autoscroll = match scroll_offset {
16636 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16637 None => Autoscroll::newest(),
16638 };
16639 let nav_history = editor.nav_history.take();
16640 editor.change_selections(Some(autoscroll), window, cx, |s| {
16641 s.select_ranges(ranges);
16642 });
16643 editor.nav_history = nav_history;
16644 });
16645 }
16646 })
16647 });
16648 }
16649
16650 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16651 let snapshot = self.buffer.read(cx).read(cx);
16652 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16653 Some(
16654 ranges
16655 .iter()
16656 .map(move |range| {
16657 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16658 })
16659 .collect(),
16660 )
16661 }
16662
16663 fn selection_replacement_ranges(
16664 &self,
16665 range: Range<OffsetUtf16>,
16666 cx: &mut App,
16667 ) -> Vec<Range<OffsetUtf16>> {
16668 let selections = self.selections.all::<OffsetUtf16>(cx);
16669 let newest_selection = selections
16670 .iter()
16671 .max_by_key(|selection| selection.id)
16672 .unwrap();
16673 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16674 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16675 let snapshot = self.buffer.read(cx).read(cx);
16676 selections
16677 .into_iter()
16678 .map(|mut selection| {
16679 selection.start.0 =
16680 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16681 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16682 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16683 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16684 })
16685 .collect()
16686 }
16687
16688 fn report_editor_event(
16689 &self,
16690 event_type: &'static str,
16691 file_extension: Option<String>,
16692 cx: &App,
16693 ) {
16694 if cfg!(any(test, feature = "test-support")) {
16695 return;
16696 }
16697
16698 let Some(project) = &self.project else { return };
16699
16700 // If None, we are in a file without an extension
16701 let file = self
16702 .buffer
16703 .read(cx)
16704 .as_singleton()
16705 .and_then(|b| b.read(cx).file());
16706 let file_extension = file_extension.or(file
16707 .as_ref()
16708 .and_then(|file| Path::new(file.file_name(cx)).extension())
16709 .and_then(|e| e.to_str())
16710 .map(|a| a.to_string()));
16711
16712 let vim_mode = cx
16713 .global::<SettingsStore>()
16714 .raw_user_settings()
16715 .get("vim_mode")
16716 == Some(&serde_json::Value::Bool(true));
16717
16718 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16719 let copilot_enabled = edit_predictions_provider
16720 == language::language_settings::EditPredictionProvider::Copilot;
16721 let copilot_enabled_for_language = self
16722 .buffer
16723 .read(cx)
16724 .language_settings(cx)
16725 .show_edit_predictions;
16726
16727 let project = project.read(cx);
16728 telemetry::event!(
16729 event_type,
16730 file_extension,
16731 vim_mode,
16732 copilot_enabled,
16733 copilot_enabled_for_language,
16734 edit_predictions_provider,
16735 is_via_ssh = project.is_via_ssh(),
16736 );
16737 }
16738
16739 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16740 /// with each line being an array of {text, highlight} objects.
16741 fn copy_highlight_json(
16742 &mut self,
16743 _: &CopyHighlightJson,
16744 window: &mut Window,
16745 cx: &mut Context<Self>,
16746 ) {
16747 #[derive(Serialize)]
16748 struct Chunk<'a> {
16749 text: String,
16750 highlight: Option<&'a str>,
16751 }
16752
16753 let snapshot = self.buffer.read(cx).snapshot(cx);
16754 let range = self
16755 .selected_text_range(false, window, cx)
16756 .and_then(|selection| {
16757 if selection.range.is_empty() {
16758 None
16759 } else {
16760 Some(selection.range)
16761 }
16762 })
16763 .unwrap_or_else(|| 0..snapshot.len());
16764
16765 let chunks = snapshot.chunks(range, true);
16766 let mut lines = Vec::new();
16767 let mut line: VecDeque<Chunk> = VecDeque::new();
16768
16769 let Some(style) = self.style.as_ref() else {
16770 return;
16771 };
16772
16773 for chunk in chunks {
16774 let highlight = chunk
16775 .syntax_highlight_id
16776 .and_then(|id| id.name(&style.syntax));
16777 let mut chunk_lines = chunk.text.split('\n').peekable();
16778 while let Some(text) = chunk_lines.next() {
16779 let mut merged_with_last_token = false;
16780 if let Some(last_token) = line.back_mut() {
16781 if last_token.highlight == highlight {
16782 last_token.text.push_str(text);
16783 merged_with_last_token = true;
16784 }
16785 }
16786
16787 if !merged_with_last_token {
16788 line.push_back(Chunk {
16789 text: text.into(),
16790 highlight,
16791 });
16792 }
16793
16794 if chunk_lines.peek().is_some() {
16795 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16796 line.pop_front();
16797 }
16798 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16799 line.pop_back();
16800 }
16801
16802 lines.push(mem::take(&mut line));
16803 }
16804 }
16805 }
16806
16807 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
16808 return;
16809 };
16810 cx.write_to_clipboard(ClipboardItem::new_string(lines));
16811 }
16812
16813 pub fn open_context_menu(
16814 &mut self,
16815 _: &OpenContextMenu,
16816 window: &mut Window,
16817 cx: &mut Context<Self>,
16818 ) {
16819 self.request_autoscroll(Autoscroll::newest(), cx);
16820 let position = self.selections.newest_display(cx).start;
16821 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
16822 }
16823
16824 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
16825 &self.inlay_hint_cache
16826 }
16827
16828 pub fn replay_insert_event(
16829 &mut self,
16830 text: &str,
16831 relative_utf16_range: Option<Range<isize>>,
16832 window: &mut Window,
16833 cx: &mut Context<Self>,
16834 ) {
16835 if !self.input_enabled {
16836 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16837 return;
16838 }
16839 if let Some(relative_utf16_range) = relative_utf16_range {
16840 let selections = self.selections.all::<OffsetUtf16>(cx);
16841 self.change_selections(None, window, cx, |s| {
16842 let new_ranges = selections.into_iter().map(|range| {
16843 let start = OffsetUtf16(
16844 range
16845 .head()
16846 .0
16847 .saturating_add_signed(relative_utf16_range.start),
16848 );
16849 let end = OffsetUtf16(
16850 range
16851 .head()
16852 .0
16853 .saturating_add_signed(relative_utf16_range.end),
16854 );
16855 start..end
16856 });
16857 s.select_ranges(new_ranges);
16858 });
16859 }
16860
16861 self.handle_input(text, window, cx);
16862 }
16863
16864 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16865 let Some(provider) = self.semantics_provider.as_ref() else {
16866 return false;
16867 };
16868
16869 let mut supports = false;
16870 self.buffer().update(cx, |this, cx| {
16871 this.for_each_buffer(|buffer| {
16872 supports |= provider.supports_inlay_hints(buffer, cx);
16873 });
16874 });
16875
16876 supports
16877 }
16878
16879 pub fn is_focused(&self, window: &Window) -> bool {
16880 self.focus_handle.is_focused(window)
16881 }
16882
16883 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16884 cx.emit(EditorEvent::Focused);
16885
16886 if let Some(descendant) = self
16887 .last_focused_descendant
16888 .take()
16889 .and_then(|descendant| descendant.upgrade())
16890 {
16891 window.focus(&descendant);
16892 } else {
16893 if let Some(blame) = self.blame.as_ref() {
16894 blame.update(cx, GitBlame::focus)
16895 }
16896
16897 self.blink_manager.update(cx, BlinkManager::enable);
16898 self.show_cursor_names(window, cx);
16899 self.buffer.update(cx, |buffer, cx| {
16900 buffer.finalize_last_transaction(cx);
16901 if self.leader_peer_id.is_none() {
16902 buffer.set_active_selections(
16903 &self.selections.disjoint_anchors(),
16904 self.selections.line_mode,
16905 self.cursor_shape,
16906 cx,
16907 );
16908 }
16909 });
16910 }
16911 }
16912
16913 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16914 cx.emit(EditorEvent::FocusedIn)
16915 }
16916
16917 fn handle_focus_out(
16918 &mut self,
16919 event: FocusOutEvent,
16920 _window: &mut Window,
16921 cx: &mut Context<Self>,
16922 ) {
16923 if event.blurred != self.focus_handle {
16924 self.last_focused_descendant = Some(event.blurred);
16925 }
16926 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16927 }
16928
16929 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16930 self.blink_manager.update(cx, BlinkManager::disable);
16931 self.buffer
16932 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16933
16934 if let Some(blame) = self.blame.as_ref() {
16935 blame.update(cx, GitBlame::blur)
16936 }
16937 if !self.hover_state.focused(window, cx) {
16938 hide_hover(self, cx);
16939 }
16940 if !self
16941 .context_menu
16942 .borrow()
16943 .as_ref()
16944 .is_some_and(|context_menu| context_menu.focused(window, cx))
16945 {
16946 self.hide_context_menu(window, cx);
16947 }
16948 self.discard_inline_completion(false, cx);
16949 cx.emit(EditorEvent::Blurred);
16950 cx.notify();
16951 }
16952
16953 pub fn register_action<A: Action>(
16954 &mut self,
16955 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16956 ) -> Subscription {
16957 let id = self.next_editor_action_id.post_inc();
16958 let listener = Arc::new(listener);
16959 self.editor_actions.borrow_mut().insert(
16960 id,
16961 Box::new(move |window, _| {
16962 let listener = listener.clone();
16963 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16964 let action = action.downcast_ref().unwrap();
16965 if phase == DispatchPhase::Bubble {
16966 listener(action, window, cx)
16967 }
16968 })
16969 }),
16970 );
16971
16972 let editor_actions = self.editor_actions.clone();
16973 Subscription::new(move || {
16974 editor_actions.borrow_mut().remove(&id);
16975 })
16976 }
16977
16978 pub fn file_header_size(&self) -> u32 {
16979 FILE_HEADER_HEIGHT
16980 }
16981
16982 pub fn restore(
16983 &mut self,
16984 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16985 window: &mut Window,
16986 cx: &mut Context<Self>,
16987 ) {
16988 let workspace = self.workspace();
16989 let project = self.project.as_ref();
16990 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16991 let mut tasks = Vec::new();
16992 for (buffer_id, changes) in revert_changes {
16993 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16994 buffer.update(cx, |buffer, cx| {
16995 buffer.edit(
16996 changes
16997 .into_iter()
16998 .map(|(range, text)| (range, text.to_string())),
16999 None,
17000 cx,
17001 );
17002 });
17003
17004 if let Some(project) =
17005 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17006 {
17007 project.update(cx, |project, cx| {
17008 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17009 })
17010 }
17011 }
17012 }
17013 tasks
17014 });
17015 cx.spawn_in(window, |_, mut cx| async move {
17016 for (buffer, task) in save_tasks {
17017 let result = task.await;
17018 if result.is_err() {
17019 let Some(path) = buffer
17020 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
17021 .ok()
17022 else {
17023 continue;
17024 };
17025 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17026 let Some(task) = cx
17027 .update_window_entity(&workspace, |workspace, window, cx| {
17028 workspace
17029 .open_path_preview(path, None, false, false, false, window, cx)
17030 })
17031 .ok()
17032 else {
17033 continue;
17034 };
17035 task.await.log_err();
17036 }
17037 }
17038 }
17039 })
17040 .detach();
17041 self.change_selections(None, window, cx, |selections| selections.refresh());
17042 }
17043
17044 pub fn to_pixel_point(
17045 &self,
17046 source: multi_buffer::Anchor,
17047 editor_snapshot: &EditorSnapshot,
17048 window: &mut Window,
17049 ) -> Option<gpui::Point<Pixels>> {
17050 let source_point = source.to_display_point(editor_snapshot);
17051 self.display_to_pixel_point(source_point, editor_snapshot, window)
17052 }
17053
17054 pub fn display_to_pixel_point(
17055 &self,
17056 source: DisplayPoint,
17057 editor_snapshot: &EditorSnapshot,
17058 window: &mut Window,
17059 ) -> Option<gpui::Point<Pixels>> {
17060 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17061 let text_layout_details = self.text_layout_details(window);
17062 let scroll_top = text_layout_details
17063 .scroll_anchor
17064 .scroll_position(editor_snapshot)
17065 .y;
17066
17067 if source.row().as_f32() < scroll_top.floor() {
17068 return None;
17069 }
17070 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17071 let source_y = line_height * (source.row().as_f32() - scroll_top);
17072 Some(gpui::Point::new(source_x, source_y))
17073 }
17074
17075 pub fn has_visible_completions_menu(&self) -> bool {
17076 !self.edit_prediction_preview_is_active()
17077 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17078 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17079 })
17080 }
17081
17082 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17083 self.addons
17084 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17085 }
17086
17087 pub fn unregister_addon<T: Addon>(&mut self) {
17088 self.addons.remove(&std::any::TypeId::of::<T>());
17089 }
17090
17091 pub fn addon<T: Addon>(&self) -> Option<&T> {
17092 let type_id = std::any::TypeId::of::<T>();
17093 self.addons
17094 .get(&type_id)
17095 .and_then(|item| item.to_any().downcast_ref::<T>())
17096 }
17097
17098 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17099 let text_layout_details = self.text_layout_details(window);
17100 let style = &text_layout_details.editor_style;
17101 let font_id = window.text_system().resolve_font(&style.text.font());
17102 let font_size = style.text.font_size.to_pixels(window.rem_size());
17103 let line_height = style.text.line_height_in_pixels(window.rem_size());
17104 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17105
17106 gpui::Size::new(em_width, line_height)
17107 }
17108
17109 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17110 self.load_diff_task.clone()
17111 }
17112
17113 fn read_selections_from_db(
17114 &mut self,
17115 item_id: u64,
17116 workspace_id: WorkspaceId,
17117 window: &mut Window,
17118 cx: &mut Context<Editor>,
17119 ) {
17120 if !self.is_singleton(cx)
17121 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
17122 {
17123 return;
17124 }
17125 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
17126 return;
17127 };
17128 if selections.is_empty() {
17129 return;
17130 }
17131
17132 let snapshot = self.buffer.read(cx).snapshot(cx);
17133 self.change_selections(None, window, cx, |s| {
17134 s.select_ranges(selections.into_iter().map(|(start, end)| {
17135 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
17136 }));
17137 });
17138 }
17139}
17140
17141fn insert_extra_newline_brackets(
17142 buffer: &MultiBufferSnapshot,
17143 range: Range<usize>,
17144 language: &language::LanguageScope,
17145) -> bool {
17146 let leading_whitespace_len = buffer
17147 .reversed_chars_at(range.start)
17148 .take_while(|c| c.is_whitespace() && *c != '\n')
17149 .map(|c| c.len_utf8())
17150 .sum::<usize>();
17151 let trailing_whitespace_len = buffer
17152 .chars_at(range.end)
17153 .take_while(|c| c.is_whitespace() && *c != '\n')
17154 .map(|c| c.len_utf8())
17155 .sum::<usize>();
17156 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17157
17158 language.brackets().any(|(pair, enabled)| {
17159 let pair_start = pair.start.trim_end();
17160 let pair_end = pair.end.trim_start();
17161
17162 enabled
17163 && pair.newline
17164 && buffer.contains_str_at(range.end, pair_end)
17165 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17166 })
17167}
17168
17169fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17170 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17171 [(buffer, range, _)] => (*buffer, range.clone()),
17172 _ => return false,
17173 };
17174 let pair = {
17175 let mut result: Option<BracketMatch> = None;
17176
17177 for pair in buffer
17178 .all_bracket_ranges(range.clone())
17179 .filter(move |pair| {
17180 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17181 })
17182 {
17183 let len = pair.close_range.end - pair.open_range.start;
17184
17185 if let Some(existing) = &result {
17186 let existing_len = existing.close_range.end - existing.open_range.start;
17187 if len > existing_len {
17188 continue;
17189 }
17190 }
17191
17192 result = Some(pair);
17193 }
17194
17195 result
17196 };
17197 let Some(pair) = pair else {
17198 return false;
17199 };
17200 pair.newline_only
17201 && buffer
17202 .chars_for_range(pair.open_range.end..range.start)
17203 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17204 .all(|c| c.is_whitespace() && c != '\n')
17205}
17206
17207fn get_uncommitted_diff_for_buffer(
17208 project: &Entity<Project>,
17209 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17210 buffer: Entity<MultiBuffer>,
17211 cx: &mut App,
17212) -> Task<()> {
17213 let mut tasks = Vec::new();
17214 project.update(cx, |project, cx| {
17215 for buffer in buffers {
17216 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17217 }
17218 });
17219 cx.spawn(|mut cx| async move {
17220 let diffs = future::join_all(tasks).await;
17221 buffer
17222 .update(&mut cx, |buffer, cx| {
17223 for diff in diffs.into_iter().flatten() {
17224 buffer.add_diff(diff, cx);
17225 }
17226 })
17227 .ok();
17228 })
17229}
17230
17231fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17232 let tab_size = tab_size.get() as usize;
17233 let mut width = offset;
17234
17235 for ch in text.chars() {
17236 width += if ch == '\t' {
17237 tab_size - (width % tab_size)
17238 } else {
17239 1
17240 };
17241 }
17242
17243 width - offset
17244}
17245
17246#[cfg(test)]
17247mod tests {
17248 use super::*;
17249
17250 #[test]
17251 fn test_string_size_with_expanded_tabs() {
17252 let nz = |val| NonZeroU32::new(val).unwrap();
17253 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17254 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17255 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17256 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17257 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17258 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17259 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17260 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17261 }
17262}
17263
17264/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17265struct WordBreakingTokenizer<'a> {
17266 input: &'a str,
17267}
17268
17269impl<'a> WordBreakingTokenizer<'a> {
17270 fn new(input: &'a str) -> Self {
17271 Self { input }
17272 }
17273}
17274
17275fn is_char_ideographic(ch: char) -> bool {
17276 use unicode_script::Script::*;
17277 use unicode_script::UnicodeScript;
17278 matches!(ch.script(), Han | Tangut | Yi)
17279}
17280
17281fn is_grapheme_ideographic(text: &str) -> bool {
17282 text.chars().any(is_char_ideographic)
17283}
17284
17285fn is_grapheme_whitespace(text: &str) -> bool {
17286 text.chars().any(|x| x.is_whitespace())
17287}
17288
17289fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17290 text.chars().next().map_or(false, |ch| {
17291 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17292 })
17293}
17294
17295#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17296enum WordBreakToken<'a> {
17297 Word { token: &'a str, grapheme_len: usize },
17298 InlineWhitespace { token: &'a str, grapheme_len: usize },
17299 Newline,
17300}
17301
17302impl<'a> Iterator for WordBreakingTokenizer<'a> {
17303 /// Yields a span, the count of graphemes in the token, and whether it was
17304 /// whitespace. Note that it also breaks at word boundaries.
17305 type Item = WordBreakToken<'a>;
17306
17307 fn next(&mut self) -> Option<Self::Item> {
17308 use unicode_segmentation::UnicodeSegmentation;
17309 if self.input.is_empty() {
17310 return None;
17311 }
17312
17313 let mut iter = self.input.graphemes(true).peekable();
17314 let mut offset = 0;
17315 let mut grapheme_len = 0;
17316 if let Some(first_grapheme) = iter.next() {
17317 let is_newline = first_grapheme == "\n";
17318 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17319 offset += first_grapheme.len();
17320 grapheme_len += 1;
17321 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17322 if let Some(grapheme) = iter.peek().copied() {
17323 if should_stay_with_preceding_ideograph(grapheme) {
17324 offset += grapheme.len();
17325 grapheme_len += 1;
17326 }
17327 }
17328 } else {
17329 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17330 let mut next_word_bound = words.peek().copied();
17331 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17332 next_word_bound = words.next();
17333 }
17334 while let Some(grapheme) = iter.peek().copied() {
17335 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17336 break;
17337 };
17338 if is_grapheme_whitespace(grapheme) != is_whitespace
17339 || (grapheme == "\n") != is_newline
17340 {
17341 break;
17342 };
17343 offset += grapheme.len();
17344 grapheme_len += 1;
17345 iter.next();
17346 }
17347 }
17348 let token = &self.input[..offset];
17349 self.input = &self.input[offset..];
17350 if token == "\n" {
17351 Some(WordBreakToken::Newline)
17352 } else if is_whitespace {
17353 Some(WordBreakToken::InlineWhitespace {
17354 token,
17355 grapheme_len,
17356 })
17357 } else {
17358 Some(WordBreakToken::Word {
17359 token,
17360 grapheme_len,
17361 })
17362 }
17363 } else {
17364 None
17365 }
17366 }
17367}
17368
17369#[test]
17370fn test_word_breaking_tokenizer() {
17371 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17372 ("", &[]),
17373 (" ", &[whitespace(" ", 2)]),
17374 ("Ʒ", &[word("Ʒ", 1)]),
17375 ("Ǽ", &[word("Ǽ", 1)]),
17376 ("⋑", &[word("⋑", 1)]),
17377 ("⋑⋑", &[word("⋑⋑", 2)]),
17378 (
17379 "原理,进而",
17380 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17381 ),
17382 (
17383 "hello world",
17384 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17385 ),
17386 (
17387 "hello, world",
17388 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17389 ),
17390 (
17391 " hello world",
17392 &[
17393 whitespace(" ", 2),
17394 word("hello", 5),
17395 whitespace(" ", 1),
17396 word("world", 5),
17397 ],
17398 ),
17399 (
17400 "这是什么 \n 钢笔",
17401 &[
17402 word("这", 1),
17403 word("是", 1),
17404 word("什", 1),
17405 word("么", 1),
17406 whitespace(" ", 1),
17407 newline(),
17408 whitespace(" ", 1),
17409 word("钢", 1),
17410 word("笔", 1),
17411 ],
17412 ),
17413 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17414 ];
17415
17416 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17417 WordBreakToken::Word {
17418 token,
17419 grapheme_len,
17420 }
17421 }
17422
17423 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17424 WordBreakToken::InlineWhitespace {
17425 token,
17426 grapheme_len,
17427 }
17428 }
17429
17430 fn newline() -> WordBreakToken<'static> {
17431 WordBreakToken::Newline
17432 }
17433
17434 for (input, result) in tests {
17435 assert_eq!(
17436 WordBreakingTokenizer::new(input)
17437 .collect::<Vec<_>>()
17438 .as_slice(),
17439 *result,
17440 );
17441 }
17442}
17443
17444fn wrap_with_prefix(
17445 line_prefix: String,
17446 unwrapped_text: String,
17447 wrap_column: usize,
17448 tab_size: NonZeroU32,
17449 preserve_existing_whitespace: bool,
17450) -> String {
17451 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17452 let mut wrapped_text = String::new();
17453 let mut current_line = line_prefix.clone();
17454
17455 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17456 let mut current_line_len = line_prefix_len;
17457 let mut in_whitespace = false;
17458 for token in tokenizer {
17459 let have_preceding_whitespace = in_whitespace;
17460 match token {
17461 WordBreakToken::Word {
17462 token,
17463 grapheme_len,
17464 } => {
17465 in_whitespace = false;
17466 if current_line_len + grapheme_len > wrap_column
17467 && current_line_len != line_prefix_len
17468 {
17469 wrapped_text.push_str(current_line.trim_end());
17470 wrapped_text.push('\n');
17471 current_line.truncate(line_prefix.len());
17472 current_line_len = line_prefix_len;
17473 }
17474 current_line.push_str(token);
17475 current_line_len += grapheme_len;
17476 }
17477 WordBreakToken::InlineWhitespace {
17478 mut token,
17479 mut grapheme_len,
17480 } => {
17481 in_whitespace = true;
17482 if have_preceding_whitespace && !preserve_existing_whitespace {
17483 continue;
17484 }
17485 if !preserve_existing_whitespace {
17486 token = " ";
17487 grapheme_len = 1;
17488 }
17489 if current_line_len + grapheme_len > wrap_column {
17490 wrapped_text.push_str(current_line.trim_end());
17491 wrapped_text.push('\n');
17492 current_line.truncate(line_prefix.len());
17493 current_line_len = line_prefix_len;
17494 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17495 current_line.push_str(token);
17496 current_line_len += grapheme_len;
17497 }
17498 }
17499 WordBreakToken::Newline => {
17500 in_whitespace = true;
17501 if preserve_existing_whitespace {
17502 wrapped_text.push_str(current_line.trim_end());
17503 wrapped_text.push('\n');
17504 current_line.truncate(line_prefix.len());
17505 current_line_len = line_prefix_len;
17506 } else if have_preceding_whitespace {
17507 continue;
17508 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17509 {
17510 wrapped_text.push_str(current_line.trim_end());
17511 wrapped_text.push('\n');
17512 current_line.truncate(line_prefix.len());
17513 current_line_len = line_prefix_len;
17514 } else if current_line_len != line_prefix_len {
17515 current_line.push(' ');
17516 current_line_len += 1;
17517 }
17518 }
17519 }
17520 }
17521
17522 if !current_line.is_empty() {
17523 wrapped_text.push_str(¤t_line);
17524 }
17525 wrapped_text
17526}
17527
17528#[test]
17529fn test_wrap_with_prefix() {
17530 assert_eq!(
17531 wrap_with_prefix(
17532 "# ".to_string(),
17533 "abcdefg".to_string(),
17534 4,
17535 NonZeroU32::new(4).unwrap(),
17536 false,
17537 ),
17538 "# abcdefg"
17539 );
17540 assert_eq!(
17541 wrap_with_prefix(
17542 "".to_string(),
17543 "\thello world".to_string(),
17544 8,
17545 NonZeroU32::new(4).unwrap(),
17546 false,
17547 ),
17548 "hello\nworld"
17549 );
17550 assert_eq!(
17551 wrap_with_prefix(
17552 "// ".to_string(),
17553 "xx \nyy zz aa bb cc".to_string(),
17554 12,
17555 NonZeroU32::new(4).unwrap(),
17556 false,
17557 ),
17558 "// xx yy zz\n// aa bb cc"
17559 );
17560 assert_eq!(
17561 wrap_with_prefix(
17562 String::new(),
17563 "这是什么 \n 钢笔".to_string(),
17564 3,
17565 NonZeroU32::new(4).unwrap(),
17566 false,
17567 ),
17568 "这是什\n么 钢\n笔"
17569 );
17570}
17571
17572pub trait CollaborationHub {
17573 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17574 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17575 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17576}
17577
17578impl CollaborationHub for Entity<Project> {
17579 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17580 self.read(cx).collaborators()
17581 }
17582
17583 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17584 self.read(cx).user_store().read(cx).participant_indices()
17585 }
17586
17587 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17588 let this = self.read(cx);
17589 let user_ids = this.collaborators().values().map(|c| c.user_id);
17590 this.user_store().read_with(cx, |user_store, cx| {
17591 user_store.participant_names(user_ids, cx)
17592 })
17593 }
17594}
17595
17596pub trait SemanticsProvider {
17597 fn hover(
17598 &self,
17599 buffer: &Entity<Buffer>,
17600 position: text::Anchor,
17601 cx: &mut App,
17602 ) -> Option<Task<Vec<project::Hover>>>;
17603
17604 fn inlay_hints(
17605 &self,
17606 buffer_handle: Entity<Buffer>,
17607 range: Range<text::Anchor>,
17608 cx: &mut App,
17609 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17610
17611 fn resolve_inlay_hint(
17612 &self,
17613 hint: InlayHint,
17614 buffer_handle: Entity<Buffer>,
17615 server_id: LanguageServerId,
17616 cx: &mut App,
17617 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17618
17619 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17620
17621 fn document_highlights(
17622 &self,
17623 buffer: &Entity<Buffer>,
17624 position: text::Anchor,
17625 cx: &mut App,
17626 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17627
17628 fn definitions(
17629 &self,
17630 buffer: &Entity<Buffer>,
17631 position: text::Anchor,
17632 kind: GotoDefinitionKind,
17633 cx: &mut App,
17634 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17635
17636 fn range_for_rename(
17637 &self,
17638 buffer: &Entity<Buffer>,
17639 position: text::Anchor,
17640 cx: &mut App,
17641 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17642
17643 fn perform_rename(
17644 &self,
17645 buffer: &Entity<Buffer>,
17646 position: text::Anchor,
17647 new_name: String,
17648 cx: &mut App,
17649 ) -> Option<Task<Result<ProjectTransaction>>>;
17650}
17651
17652pub trait CompletionProvider {
17653 fn completions(
17654 &self,
17655 buffer: &Entity<Buffer>,
17656 buffer_position: text::Anchor,
17657 trigger: CompletionContext,
17658 window: &mut Window,
17659 cx: &mut Context<Editor>,
17660 ) -> Task<Result<Option<Vec<Completion>>>>;
17661
17662 fn resolve_completions(
17663 &self,
17664 buffer: Entity<Buffer>,
17665 completion_indices: Vec<usize>,
17666 completions: Rc<RefCell<Box<[Completion]>>>,
17667 cx: &mut Context<Editor>,
17668 ) -> Task<Result<bool>>;
17669
17670 fn apply_additional_edits_for_completion(
17671 &self,
17672 _buffer: Entity<Buffer>,
17673 _completions: Rc<RefCell<Box<[Completion]>>>,
17674 _completion_index: usize,
17675 _push_to_history: bool,
17676 _cx: &mut Context<Editor>,
17677 ) -> Task<Result<Option<language::Transaction>>> {
17678 Task::ready(Ok(None))
17679 }
17680
17681 fn is_completion_trigger(
17682 &self,
17683 buffer: &Entity<Buffer>,
17684 position: language::Anchor,
17685 text: &str,
17686 trigger_in_words: bool,
17687 cx: &mut Context<Editor>,
17688 ) -> bool;
17689
17690 fn sort_completions(&self) -> bool {
17691 true
17692 }
17693}
17694
17695pub trait CodeActionProvider {
17696 fn id(&self) -> Arc<str>;
17697
17698 fn code_actions(
17699 &self,
17700 buffer: &Entity<Buffer>,
17701 range: Range<text::Anchor>,
17702 window: &mut Window,
17703 cx: &mut App,
17704 ) -> Task<Result<Vec<CodeAction>>>;
17705
17706 fn apply_code_action(
17707 &self,
17708 buffer_handle: Entity<Buffer>,
17709 action: CodeAction,
17710 excerpt_id: ExcerptId,
17711 push_to_history: bool,
17712 window: &mut Window,
17713 cx: &mut App,
17714 ) -> Task<Result<ProjectTransaction>>;
17715}
17716
17717impl CodeActionProvider for Entity<Project> {
17718 fn id(&self) -> Arc<str> {
17719 "project".into()
17720 }
17721
17722 fn code_actions(
17723 &self,
17724 buffer: &Entity<Buffer>,
17725 range: Range<text::Anchor>,
17726 _window: &mut Window,
17727 cx: &mut App,
17728 ) -> Task<Result<Vec<CodeAction>>> {
17729 self.update(cx, |project, cx| {
17730 let code_lens = project.code_lens(buffer, range.clone(), cx);
17731 let code_actions = project.code_actions(buffer, range, None, cx);
17732 cx.background_spawn(async move {
17733 let (code_lens, code_actions) = join(code_lens, code_actions).await;
17734 Ok(code_lens
17735 .context("code lens fetch")?
17736 .into_iter()
17737 .chain(code_actions.context("code action fetch")?)
17738 .collect())
17739 })
17740 })
17741 }
17742
17743 fn apply_code_action(
17744 &self,
17745 buffer_handle: Entity<Buffer>,
17746 action: CodeAction,
17747 _excerpt_id: ExcerptId,
17748 push_to_history: bool,
17749 _window: &mut Window,
17750 cx: &mut App,
17751 ) -> Task<Result<ProjectTransaction>> {
17752 self.update(cx, |project, cx| {
17753 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17754 })
17755 }
17756}
17757
17758fn snippet_completions(
17759 project: &Project,
17760 buffer: &Entity<Buffer>,
17761 buffer_position: text::Anchor,
17762 cx: &mut App,
17763) -> Task<Result<Vec<Completion>>> {
17764 let language = buffer.read(cx).language_at(buffer_position);
17765 let language_name = language.as_ref().map(|language| language.lsp_id());
17766 let snippet_store = project.snippets().read(cx);
17767 let snippets = snippet_store.snippets_for(language_name, cx);
17768
17769 if snippets.is_empty() {
17770 return Task::ready(Ok(vec![]));
17771 }
17772 let snapshot = buffer.read(cx).text_snapshot();
17773 let chars: String = snapshot
17774 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17775 .collect();
17776
17777 let scope = language.map(|language| language.default_scope());
17778 let executor = cx.background_executor().clone();
17779
17780 cx.background_spawn(async move {
17781 let classifier = CharClassifier::new(scope).for_completion(true);
17782 let mut last_word = chars
17783 .chars()
17784 .take_while(|c| classifier.is_word(*c))
17785 .collect::<String>();
17786 last_word = last_word.chars().rev().collect();
17787
17788 if last_word.is_empty() {
17789 return Ok(vec![]);
17790 }
17791
17792 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
17793 let to_lsp = |point: &text::Anchor| {
17794 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
17795 point_to_lsp(end)
17796 };
17797 let lsp_end = to_lsp(&buffer_position);
17798
17799 let candidates = snippets
17800 .iter()
17801 .enumerate()
17802 .flat_map(|(ix, snippet)| {
17803 snippet
17804 .prefix
17805 .iter()
17806 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
17807 })
17808 .collect::<Vec<StringMatchCandidate>>();
17809
17810 let mut matches = fuzzy::match_strings(
17811 &candidates,
17812 &last_word,
17813 last_word.chars().any(|c| c.is_uppercase()),
17814 100,
17815 &Default::default(),
17816 executor,
17817 )
17818 .await;
17819
17820 // Remove all candidates where the query's start does not match the start of any word in the candidate
17821 if let Some(query_start) = last_word.chars().next() {
17822 matches.retain(|string_match| {
17823 split_words(&string_match.string).any(|word| {
17824 // Check that the first codepoint of the word as lowercase matches the first
17825 // codepoint of the query as lowercase
17826 word.chars()
17827 .flat_map(|codepoint| codepoint.to_lowercase())
17828 .zip(query_start.to_lowercase())
17829 .all(|(word_cp, query_cp)| word_cp == query_cp)
17830 })
17831 });
17832 }
17833
17834 let matched_strings = matches
17835 .into_iter()
17836 .map(|m| m.string)
17837 .collect::<HashSet<_>>();
17838
17839 let result: Vec<Completion> = snippets
17840 .into_iter()
17841 .filter_map(|snippet| {
17842 let matching_prefix = snippet
17843 .prefix
17844 .iter()
17845 .find(|prefix| matched_strings.contains(*prefix))?;
17846 let start = as_offset - last_word.len();
17847 let start = snapshot.anchor_before(start);
17848 let range = start..buffer_position;
17849 let lsp_start = to_lsp(&start);
17850 let lsp_range = lsp::Range {
17851 start: lsp_start,
17852 end: lsp_end,
17853 };
17854 Some(Completion {
17855 old_range: range,
17856 new_text: snippet.body.clone(),
17857 source: CompletionSource::Lsp {
17858 server_id: LanguageServerId(usize::MAX),
17859 resolved: true,
17860 lsp_completion: Box::new(lsp::CompletionItem {
17861 label: snippet.prefix.first().unwrap().clone(),
17862 kind: Some(CompletionItemKind::SNIPPET),
17863 label_details: snippet.description.as_ref().map(|description| {
17864 lsp::CompletionItemLabelDetails {
17865 detail: Some(description.clone()),
17866 description: None,
17867 }
17868 }),
17869 insert_text_format: Some(InsertTextFormat::SNIPPET),
17870 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
17871 lsp::InsertReplaceEdit {
17872 new_text: snippet.body.clone(),
17873 insert: lsp_range,
17874 replace: lsp_range,
17875 },
17876 )),
17877 filter_text: Some(snippet.body.clone()),
17878 sort_text: Some(char::MAX.to_string()),
17879 ..lsp::CompletionItem::default()
17880 }),
17881 lsp_defaults: None,
17882 },
17883 label: CodeLabel {
17884 text: matching_prefix.clone(),
17885 runs: Vec::new(),
17886 filter_range: 0..matching_prefix.len(),
17887 },
17888 documentation: snippet
17889 .description
17890 .clone()
17891 .map(|description| CompletionDocumentation::SingleLine(description.into())),
17892 confirm: None,
17893 })
17894 })
17895 .collect();
17896
17897 Ok(result)
17898 })
17899}
17900
17901impl CompletionProvider for Entity<Project> {
17902 fn completions(
17903 &self,
17904 buffer: &Entity<Buffer>,
17905 buffer_position: text::Anchor,
17906 options: CompletionContext,
17907 _window: &mut Window,
17908 cx: &mut Context<Editor>,
17909 ) -> Task<Result<Option<Vec<Completion>>>> {
17910 self.update(cx, |project, cx| {
17911 let snippets = snippet_completions(project, buffer, buffer_position, cx);
17912 let project_completions = project.completions(buffer, buffer_position, options, cx);
17913 cx.background_spawn(async move {
17914 let snippets_completions = snippets.await?;
17915 match project_completions.await? {
17916 Some(mut completions) => {
17917 completions.extend(snippets_completions);
17918 Ok(Some(completions))
17919 }
17920 None => {
17921 if snippets_completions.is_empty() {
17922 Ok(None)
17923 } else {
17924 Ok(Some(snippets_completions))
17925 }
17926 }
17927 }
17928 })
17929 })
17930 }
17931
17932 fn resolve_completions(
17933 &self,
17934 buffer: Entity<Buffer>,
17935 completion_indices: Vec<usize>,
17936 completions: Rc<RefCell<Box<[Completion]>>>,
17937 cx: &mut Context<Editor>,
17938 ) -> Task<Result<bool>> {
17939 self.update(cx, |project, cx| {
17940 project.lsp_store().update(cx, |lsp_store, cx| {
17941 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
17942 })
17943 })
17944 }
17945
17946 fn apply_additional_edits_for_completion(
17947 &self,
17948 buffer: Entity<Buffer>,
17949 completions: Rc<RefCell<Box<[Completion]>>>,
17950 completion_index: usize,
17951 push_to_history: bool,
17952 cx: &mut Context<Editor>,
17953 ) -> Task<Result<Option<language::Transaction>>> {
17954 self.update(cx, |project, cx| {
17955 project.lsp_store().update(cx, |lsp_store, cx| {
17956 lsp_store.apply_additional_edits_for_completion(
17957 buffer,
17958 completions,
17959 completion_index,
17960 push_to_history,
17961 cx,
17962 )
17963 })
17964 })
17965 }
17966
17967 fn is_completion_trigger(
17968 &self,
17969 buffer: &Entity<Buffer>,
17970 position: language::Anchor,
17971 text: &str,
17972 trigger_in_words: bool,
17973 cx: &mut Context<Editor>,
17974 ) -> bool {
17975 let mut chars = text.chars();
17976 let char = if let Some(char) = chars.next() {
17977 char
17978 } else {
17979 return false;
17980 };
17981 if chars.next().is_some() {
17982 return false;
17983 }
17984
17985 let buffer = buffer.read(cx);
17986 let snapshot = buffer.snapshot();
17987 if !snapshot.settings_at(position, cx).show_completions_on_input {
17988 return false;
17989 }
17990 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17991 if trigger_in_words && classifier.is_word(char) {
17992 return true;
17993 }
17994
17995 buffer.completion_triggers().contains(text)
17996 }
17997}
17998
17999impl SemanticsProvider for Entity<Project> {
18000 fn hover(
18001 &self,
18002 buffer: &Entity<Buffer>,
18003 position: text::Anchor,
18004 cx: &mut App,
18005 ) -> Option<Task<Vec<project::Hover>>> {
18006 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18007 }
18008
18009 fn document_highlights(
18010 &self,
18011 buffer: &Entity<Buffer>,
18012 position: text::Anchor,
18013 cx: &mut App,
18014 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18015 Some(self.update(cx, |project, cx| {
18016 project.document_highlights(buffer, position, cx)
18017 }))
18018 }
18019
18020 fn definitions(
18021 &self,
18022 buffer: &Entity<Buffer>,
18023 position: text::Anchor,
18024 kind: GotoDefinitionKind,
18025 cx: &mut App,
18026 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18027 Some(self.update(cx, |project, cx| match kind {
18028 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18029 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18030 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18031 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18032 }))
18033 }
18034
18035 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18036 // TODO: make this work for remote projects
18037 self.update(cx, |this, cx| {
18038 buffer.update(cx, |buffer, cx| {
18039 this.any_language_server_supports_inlay_hints(buffer, cx)
18040 })
18041 })
18042 }
18043
18044 fn inlay_hints(
18045 &self,
18046 buffer_handle: Entity<Buffer>,
18047 range: Range<text::Anchor>,
18048 cx: &mut App,
18049 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18050 Some(self.update(cx, |project, cx| {
18051 project.inlay_hints(buffer_handle, range, cx)
18052 }))
18053 }
18054
18055 fn resolve_inlay_hint(
18056 &self,
18057 hint: InlayHint,
18058 buffer_handle: Entity<Buffer>,
18059 server_id: LanguageServerId,
18060 cx: &mut App,
18061 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18062 Some(self.update(cx, |project, cx| {
18063 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18064 }))
18065 }
18066
18067 fn range_for_rename(
18068 &self,
18069 buffer: &Entity<Buffer>,
18070 position: text::Anchor,
18071 cx: &mut App,
18072 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18073 Some(self.update(cx, |project, cx| {
18074 let buffer = buffer.clone();
18075 let task = project.prepare_rename(buffer.clone(), position, cx);
18076 cx.spawn(|_, mut cx| async move {
18077 Ok(match task.await? {
18078 PrepareRenameResponse::Success(range) => Some(range),
18079 PrepareRenameResponse::InvalidPosition => None,
18080 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18081 // Fallback on using TreeSitter info to determine identifier range
18082 buffer.update(&mut cx, |buffer, _| {
18083 let snapshot = buffer.snapshot();
18084 let (range, kind) = snapshot.surrounding_word(position);
18085 if kind != Some(CharKind::Word) {
18086 return None;
18087 }
18088 Some(
18089 snapshot.anchor_before(range.start)
18090 ..snapshot.anchor_after(range.end),
18091 )
18092 })?
18093 }
18094 })
18095 })
18096 }))
18097 }
18098
18099 fn perform_rename(
18100 &self,
18101 buffer: &Entity<Buffer>,
18102 position: text::Anchor,
18103 new_name: String,
18104 cx: &mut App,
18105 ) -> Option<Task<Result<ProjectTransaction>>> {
18106 Some(self.update(cx, |project, cx| {
18107 project.perform_rename(buffer.clone(), position, new_name, cx)
18108 }))
18109 }
18110}
18111
18112fn inlay_hint_settings(
18113 location: Anchor,
18114 snapshot: &MultiBufferSnapshot,
18115 cx: &mut Context<Editor>,
18116) -> InlayHintSettings {
18117 let file = snapshot.file_at(location);
18118 let language = snapshot.language_at(location).map(|l| l.name());
18119 language_settings(language, file, cx).inlay_hints
18120}
18121
18122fn consume_contiguous_rows(
18123 contiguous_row_selections: &mut Vec<Selection<Point>>,
18124 selection: &Selection<Point>,
18125 display_map: &DisplaySnapshot,
18126 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18127) -> (MultiBufferRow, MultiBufferRow) {
18128 contiguous_row_selections.push(selection.clone());
18129 let start_row = MultiBufferRow(selection.start.row);
18130 let mut end_row = ending_row(selection, display_map);
18131
18132 while let Some(next_selection) = selections.peek() {
18133 if next_selection.start.row <= end_row.0 {
18134 end_row = ending_row(next_selection, display_map);
18135 contiguous_row_selections.push(selections.next().unwrap().clone());
18136 } else {
18137 break;
18138 }
18139 }
18140 (start_row, end_row)
18141}
18142
18143fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18144 if next_selection.end.column > 0 || next_selection.is_empty() {
18145 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18146 } else {
18147 MultiBufferRow(next_selection.end.row)
18148 }
18149}
18150
18151impl EditorSnapshot {
18152 pub fn remote_selections_in_range<'a>(
18153 &'a self,
18154 range: &'a Range<Anchor>,
18155 collaboration_hub: &dyn CollaborationHub,
18156 cx: &'a App,
18157 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18158 let participant_names = collaboration_hub.user_names(cx);
18159 let participant_indices = collaboration_hub.user_participant_indices(cx);
18160 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18161 let collaborators_by_replica_id = collaborators_by_peer_id
18162 .iter()
18163 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18164 .collect::<HashMap<_, _>>();
18165 self.buffer_snapshot
18166 .selections_in_range(range, false)
18167 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18168 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18169 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18170 let user_name = participant_names.get(&collaborator.user_id).cloned();
18171 Some(RemoteSelection {
18172 replica_id,
18173 selection,
18174 cursor_shape,
18175 line_mode,
18176 participant_index,
18177 peer_id: collaborator.peer_id,
18178 user_name,
18179 })
18180 })
18181 }
18182
18183 pub fn hunks_for_ranges(
18184 &self,
18185 ranges: impl IntoIterator<Item = Range<Point>>,
18186 ) -> Vec<MultiBufferDiffHunk> {
18187 let mut hunks = Vec::new();
18188 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18189 HashMap::default();
18190 for query_range in ranges {
18191 let query_rows =
18192 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18193 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18194 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18195 ) {
18196 // Include deleted hunks that are adjacent to the query range, because
18197 // otherwise they would be missed.
18198 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18199 if hunk.status().is_deleted() {
18200 intersects_range |= hunk.row_range.start == query_rows.end;
18201 intersects_range |= hunk.row_range.end == query_rows.start;
18202 }
18203 if intersects_range {
18204 if !processed_buffer_rows
18205 .entry(hunk.buffer_id)
18206 .or_default()
18207 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18208 {
18209 continue;
18210 }
18211 hunks.push(hunk);
18212 }
18213 }
18214 }
18215
18216 hunks
18217 }
18218
18219 fn display_diff_hunks_for_rows<'a>(
18220 &'a self,
18221 display_rows: Range<DisplayRow>,
18222 folded_buffers: &'a HashSet<BufferId>,
18223 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18224 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18225 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18226
18227 self.buffer_snapshot
18228 .diff_hunks_in_range(buffer_start..buffer_end)
18229 .filter_map(|hunk| {
18230 if folded_buffers.contains(&hunk.buffer_id) {
18231 return None;
18232 }
18233
18234 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18235 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18236
18237 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18238 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18239
18240 let display_hunk = if hunk_display_start.column() != 0 {
18241 DisplayDiffHunk::Folded {
18242 display_row: hunk_display_start.row(),
18243 }
18244 } else {
18245 let mut end_row = hunk_display_end.row();
18246 if hunk_display_end.column() > 0 {
18247 end_row.0 += 1;
18248 }
18249 let is_created_file = hunk.is_created_file();
18250 DisplayDiffHunk::Unfolded {
18251 status: hunk.status(),
18252 diff_base_byte_range: hunk.diff_base_byte_range,
18253 display_row_range: hunk_display_start.row()..end_row,
18254 multi_buffer_range: Anchor::range_in_buffer(
18255 hunk.excerpt_id,
18256 hunk.buffer_id,
18257 hunk.buffer_range,
18258 ),
18259 is_created_file,
18260 }
18261 };
18262
18263 Some(display_hunk)
18264 })
18265 }
18266
18267 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18268 self.display_snapshot.buffer_snapshot.language_at(position)
18269 }
18270
18271 pub fn is_focused(&self) -> bool {
18272 self.is_focused
18273 }
18274
18275 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18276 self.placeholder_text.as_ref()
18277 }
18278
18279 pub fn scroll_position(&self) -> gpui::Point<f32> {
18280 self.scroll_anchor.scroll_position(&self.display_snapshot)
18281 }
18282
18283 fn gutter_dimensions(
18284 &self,
18285 font_id: FontId,
18286 font_size: Pixels,
18287 max_line_number_width: Pixels,
18288 cx: &App,
18289 ) -> Option<GutterDimensions> {
18290 if !self.show_gutter {
18291 return None;
18292 }
18293
18294 let descent = cx.text_system().descent(font_id, font_size);
18295 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18296 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18297
18298 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18299 matches!(
18300 ProjectSettings::get_global(cx).git.git_gutter,
18301 Some(GitGutterSetting::TrackedFiles)
18302 )
18303 });
18304 let gutter_settings = EditorSettings::get_global(cx).gutter;
18305 let show_line_numbers = self
18306 .show_line_numbers
18307 .unwrap_or(gutter_settings.line_numbers);
18308 let line_gutter_width = if show_line_numbers {
18309 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18310 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18311 max_line_number_width.max(min_width_for_number_on_gutter)
18312 } else {
18313 0.0.into()
18314 };
18315
18316 let show_code_actions = self
18317 .show_code_actions
18318 .unwrap_or(gutter_settings.code_actions);
18319
18320 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18321 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18322
18323 let git_blame_entries_width =
18324 self.git_blame_gutter_max_author_length
18325 .map(|max_author_length| {
18326 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18327
18328 /// The number of characters to dedicate to gaps and margins.
18329 const SPACING_WIDTH: usize = 4;
18330
18331 let max_char_count = max_author_length
18332 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18333 + ::git::SHORT_SHA_LENGTH
18334 + MAX_RELATIVE_TIMESTAMP.len()
18335 + SPACING_WIDTH;
18336
18337 em_advance * max_char_count
18338 });
18339
18340 let is_singleton = self.buffer_snapshot.is_singleton();
18341
18342 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18343 left_padding += if !is_singleton {
18344 em_width * 4.0
18345 } else if show_code_actions || show_runnables || show_breakpoints {
18346 em_width * 3.0
18347 } else if show_git_gutter && show_line_numbers {
18348 em_width * 2.0
18349 } else if show_git_gutter || show_line_numbers {
18350 em_width
18351 } else {
18352 px(0.)
18353 };
18354
18355 let shows_folds = is_singleton && gutter_settings.folds;
18356
18357 let right_padding = if shows_folds && show_line_numbers {
18358 em_width * 4.0
18359 } else if shows_folds || (!is_singleton && show_line_numbers) {
18360 em_width * 3.0
18361 } else if show_line_numbers {
18362 em_width
18363 } else {
18364 px(0.)
18365 };
18366
18367 Some(GutterDimensions {
18368 left_padding,
18369 right_padding,
18370 width: line_gutter_width + left_padding + right_padding,
18371 margin: -descent,
18372 git_blame_entries_width,
18373 })
18374 }
18375
18376 pub fn render_crease_toggle(
18377 &self,
18378 buffer_row: MultiBufferRow,
18379 row_contains_cursor: bool,
18380 editor: Entity<Editor>,
18381 window: &mut Window,
18382 cx: &mut App,
18383 ) -> Option<AnyElement> {
18384 let folded = self.is_line_folded(buffer_row);
18385 let mut is_foldable = false;
18386
18387 if let Some(crease) = self
18388 .crease_snapshot
18389 .query_row(buffer_row, &self.buffer_snapshot)
18390 {
18391 is_foldable = true;
18392 match crease {
18393 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18394 if let Some(render_toggle) = render_toggle {
18395 let toggle_callback =
18396 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18397 if folded {
18398 editor.update(cx, |editor, cx| {
18399 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18400 });
18401 } else {
18402 editor.update(cx, |editor, cx| {
18403 editor.unfold_at(
18404 &crate::UnfoldAt { buffer_row },
18405 window,
18406 cx,
18407 )
18408 });
18409 }
18410 });
18411 return Some((render_toggle)(
18412 buffer_row,
18413 folded,
18414 toggle_callback,
18415 window,
18416 cx,
18417 ));
18418 }
18419 }
18420 }
18421 }
18422
18423 is_foldable |= self.starts_indent(buffer_row);
18424
18425 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18426 Some(
18427 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18428 .toggle_state(folded)
18429 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18430 if folded {
18431 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18432 } else {
18433 this.fold_at(&FoldAt { buffer_row }, window, cx);
18434 }
18435 }))
18436 .into_any_element(),
18437 )
18438 } else {
18439 None
18440 }
18441 }
18442
18443 pub fn render_crease_trailer(
18444 &self,
18445 buffer_row: MultiBufferRow,
18446 window: &mut Window,
18447 cx: &mut App,
18448 ) -> Option<AnyElement> {
18449 let folded = self.is_line_folded(buffer_row);
18450 if let Crease::Inline { render_trailer, .. } = self
18451 .crease_snapshot
18452 .query_row(buffer_row, &self.buffer_snapshot)?
18453 {
18454 let render_trailer = render_trailer.as_ref()?;
18455 Some(render_trailer(buffer_row, folded, window, cx))
18456 } else {
18457 None
18458 }
18459 }
18460}
18461
18462impl Deref for EditorSnapshot {
18463 type Target = DisplaySnapshot;
18464
18465 fn deref(&self) -> &Self::Target {
18466 &self.display_snapshot
18467 }
18468}
18469
18470#[derive(Clone, Debug, PartialEq, Eq)]
18471pub enum EditorEvent {
18472 InputIgnored {
18473 text: Arc<str>,
18474 },
18475 InputHandled {
18476 utf16_range_to_replace: Option<Range<isize>>,
18477 text: Arc<str>,
18478 },
18479 ExcerptsAdded {
18480 buffer: Entity<Buffer>,
18481 predecessor: ExcerptId,
18482 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18483 },
18484 ExcerptsRemoved {
18485 ids: Vec<ExcerptId>,
18486 },
18487 BufferFoldToggled {
18488 ids: Vec<ExcerptId>,
18489 folded: bool,
18490 },
18491 ExcerptsEdited {
18492 ids: Vec<ExcerptId>,
18493 },
18494 ExcerptsExpanded {
18495 ids: Vec<ExcerptId>,
18496 },
18497 BufferEdited,
18498 Edited {
18499 transaction_id: clock::Lamport,
18500 },
18501 Reparsed(BufferId),
18502 Focused,
18503 FocusedIn,
18504 Blurred,
18505 DirtyChanged,
18506 Saved,
18507 TitleChanged,
18508 DiffBaseChanged,
18509 SelectionsChanged {
18510 local: bool,
18511 },
18512 ScrollPositionChanged {
18513 local: bool,
18514 autoscroll: bool,
18515 },
18516 Closed,
18517 TransactionUndone {
18518 transaction_id: clock::Lamport,
18519 },
18520 TransactionBegun {
18521 transaction_id: clock::Lamport,
18522 },
18523 Reloaded,
18524 CursorShapeChanged,
18525}
18526
18527impl EventEmitter<EditorEvent> for Editor {}
18528
18529impl Focusable for Editor {
18530 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18531 self.focus_handle.clone()
18532 }
18533}
18534
18535impl Render for Editor {
18536 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18537 let settings = ThemeSettings::get_global(cx);
18538
18539 let mut text_style = match self.mode {
18540 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18541 color: cx.theme().colors().editor_foreground,
18542 font_family: settings.ui_font.family.clone(),
18543 font_features: settings.ui_font.features.clone(),
18544 font_fallbacks: settings.ui_font.fallbacks.clone(),
18545 font_size: rems(0.875).into(),
18546 font_weight: settings.ui_font.weight,
18547 line_height: relative(settings.buffer_line_height.value()),
18548 ..Default::default()
18549 },
18550 EditorMode::Full => TextStyle {
18551 color: cx.theme().colors().editor_foreground,
18552 font_family: settings.buffer_font.family.clone(),
18553 font_features: settings.buffer_font.features.clone(),
18554 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18555 font_size: settings.buffer_font_size(cx).into(),
18556 font_weight: settings.buffer_font.weight,
18557 line_height: relative(settings.buffer_line_height.value()),
18558 ..Default::default()
18559 },
18560 };
18561 if let Some(text_style_refinement) = &self.text_style_refinement {
18562 text_style.refine(text_style_refinement)
18563 }
18564
18565 let background = match self.mode {
18566 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18567 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18568 EditorMode::Full => cx.theme().colors().editor_background,
18569 };
18570
18571 EditorElement::new(
18572 &cx.entity(),
18573 EditorStyle {
18574 background,
18575 local_player: cx.theme().players().local(),
18576 text: text_style,
18577 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18578 syntax: cx.theme().syntax().clone(),
18579 status: cx.theme().status().clone(),
18580 inlay_hints_style: make_inlay_hints_style(cx),
18581 inline_completion_styles: make_suggestion_styles(cx),
18582 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18583 },
18584 )
18585 }
18586}
18587
18588impl EntityInputHandler for Editor {
18589 fn text_for_range(
18590 &mut self,
18591 range_utf16: Range<usize>,
18592 adjusted_range: &mut Option<Range<usize>>,
18593 _: &mut Window,
18594 cx: &mut Context<Self>,
18595 ) -> Option<String> {
18596 let snapshot = self.buffer.read(cx).read(cx);
18597 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18598 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18599 if (start.0..end.0) != range_utf16 {
18600 adjusted_range.replace(start.0..end.0);
18601 }
18602 Some(snapshot.text_for_range(start..end).collect())
18603 }
18604
18605 fn selected_text_range(
18606 &mut self,
18607 ignore_disabled_input: bool,
18608 _: &mut Window,
18609 cx: &mut Context<Self>,
18610 ) -> Option<UTF16Selection> {
18611 // Prevent the IME menu from appearing when holding down an alphabetic key
18612 // while input is disabled.
18613 if !ignore_disabled_input && !self.input_enabled {
18614 return None;
18615 }
18616
18617 let selection = self.selections.newest::<OffsetUtf16>(cx);
18618 let range = selection.range();
18619
18620 Some(UTF16Selection {
18621 range: range.start.0..range.end.0,
18622 reversed: selection.reversed,
18623 })
18624 }
18625
18626 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18627 let snapshot = self.buffer.read(cx).read(cx);
18628 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18629 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18630 }
18631
18632 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18633 self.clear_highlights::<InputComposition>(cx);
18634 self.ime_transaction.take();
18635 }
18636
18637 fn replace_text_in_range(
18638 &mut self,
18639 range_utf16: Option<Range<usize>>,
18640 text: &str,
18641 window: &mut Window,
18642 cx: &mut Context<Self>,
18643 ) {
18644 if !self.input_enabled {
18645 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18646 return;
18647 }
18648
18649 self.transact(window, cx, |this, window, cx| {
18650 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18651 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18652 Some(this.selection_replacement_ranges(range_utf16, cx))
18653 } else {
18654 this.marked_text_ranges(cx)
18655 };
18656
18657 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
18658 let newest_selection_id = this.selections.newest_anchor().id;
18659 this.selections
18660 .all::<OffsetUtf16>(cx)
18661 .iter()
18662 .zip(ranges_to_replace.iter())
18663 .find_map(|(selection, range)| {
18664 if selection.id == newest_selection_id {
18665 Some(
18666 (range.start.0 as isize - selection.head().0 as isize)
18667 ..(range.end.0 as isize - selection.head().0 as isize),
18668 )
18669 } else {
18670 None
18671 }
18672 })
18673 });
18674
18675 cx.emit(EditorEvent::InputHandled {
18676 utf16_range_to_replace: range_to_replace,
18677 text: text.into(),
18678 });
18679
18680 if let Some(new_selected_ranges) = new_selected_ranges {
18681 this.change_selections(None, window, cx, |selections| {
18682 selections.select_ranges(new_selected_ranges)
18683 });
18684 this.backspace(&Default::default(), window, cx);
18685 }
18686
18687 this.handle_input(text, window, cx);
18688 });
18689
18690 if let Some(transaction) = self.ime_transaction {
18691 self.buffer.update(cx, |buffer, cx| {
18692 buffer.group_until_transaction(transaction, cx);
18693 });
18694 }
18695
18696 self.unmark_text(window, cx);
18697 }
18698
18699 fn replace_and_mark_text_in_range(
18700 &mut self,
18701 range_utf16: Option<Range<usize>>,
18702 text: &str,
18703 new_selected_range_utf16: Option<Range<usize>>,
18704 window: &mut Window,
18705 cx: &mut Context<Self>,
18706 ) {
18707 if !self.input_enabled {
18708 return;
18709 }
18710
18711 let transaction = self.transact(window, cx, |this, window, cx| {
18712 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
18713 let snapshot = this.buffer.read(cx).read(cx);
18714 if let Some(relative_range_utf16) = range_utf16.as_ref() {
18715 for marked_range in &mut marked_ranges {
18716 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
18717 marked_range.start.0 += relative_range_utf16.start;
18718 marked_range.start =
18719 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
18720 marked_range.end =
18721 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
18722 }
18723 }
18724 Some(marked_ranges)
18725 } else if let Some(range_utf16) = range_utf16 {
18726 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18727 Some(this.selection_replacement_ranges(range_utf16, cx))
18728 } else {
18729 None
18730 };
18731
18732 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
18733 let newest_selection_id = this.selections.newest_anchor().id;
18734 this.selections
18735 .all::<OffsetUtf16>(cx)
18736 .iter()
18737 .zip(ranges_to_replace.iter())
18738 .find_map(|(selection, range)| {
18739 if selection.id == newest_selection_id {
18740 Some(
18741 (range.start.0 as isize - selection.head().0 as isize)
18742 ..(range.end.0 as isize - selection.head().0 as isize),
18743 )
18744 } else {
18745 None
18746 }
18747 })
18748 });
18749
18750 cx.emit(EditorEvent::InputHandled {
18751 utf16_range_to_replace: range_to_replace,
18752 text: text.into(),
18753 });
18754
18755 if let Some(ranges) = ranges_to_replace {
18756 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18757 }
18758
18759 let marked_ranges = {
18760 let snapshot = this.buffer.read(cx).read(cx);
18761 this.selections
18762 .disjoint_anchors()
18763 .iter()
18764 .map(|selection| {
18765 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18766 })
18767 .collect::<Vec<_>>()
18768 };
18769
18770 if text.is_empty() {
18771 this.unmark_text(window, cx);
18772 } else {
18773 this.highlight_text::<InputComposition>(
18774 marked_ranges.clone(),
18775 HighlightStyle {
18776 underline: Some(UnderlineStyle {
18777 thickness: px(1.),
18778 color: None,
18779 wavy: false,
18780 }),
18781 ..Default::default()
18782 },
18783 cx,
18784 );
18785 }
18786
18787 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
18788 let use_autoclose = this.use_autoclose;
18789 let use_auto_surround = this.use_auto_surround;
18790 this.set_use_autoclose(false);
18791 this.set_use_auto_surround(false);
18792 this.handle_input(text, window, cx);
18793 this.set_use_autoclose(use_autoclose);
18794 this.set_use_auto_surround(use_auto_surround);
18795
18796 if let Some(new_selected_range) = new_selected_range_utf16 {
18797 let snapshot = this.buffer.read(cx).read(cx);
18798 let new_selected_ranges = marked_ranges
18799 .into_iter()
18800 .map(|marked_range| {
18801 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
18802 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
18803 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
18804 snapshot.clip_offset_utf16(new_start, Bias::Left)
18805 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
18806 })
18807 .collect::<Vec<_>>();
18808
18809 drop(snapshot);
18810 this.change_selections(None, window, cx, |selections| {
18811 selections.select_ranges(new_selected_ranges)
18812 });
18813 }
18814 });
18815
18816 self.ime_transaction = self.ime_transaction.or(transaction);
18817 if let Some(transaction) = self.ime_transaction {
18818 self.buffer.update(cx, |buffer, cx| {
18819 buffer.group_until_transaction(transaction, cx);
18820 });
18821 }
18822
18823 if self.text_highlights::<InputComposition>(cx).is_none() {
18824 self.ime_transaction.take();
18825 }
18826 }
18827
18828 fn bounds_for_range(
18829 &mut self,
18830 range_utf16: Range<usize>,
18831 element_bounds: gpui::Bounds<Pixels>,
18832 window: &mut Window,
18833 cx: &mut Context<Self>,
18834 ) -> Option<gpui::Bounds<Pixels>> {
18835 let text_layout_details = self.text_layout_details(window);
18836 let gpui::Size {
18837 width: em_width,
18838 height: line_height,
18839 } = self.character_size(window);
18840
18841 let snapshot = self.snapshot(window, cx);
18842 let scroll_position = snapshot.scroll_position();
18843 let scroll_left = scroll_position.x * em_width;
18844
18845 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
18846 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
18847 + self.gutter_dimensions.width
18848 + self.gutter_dimensions.margin;
18849 let y = line_height * (start.row().as_f32() - scroll_position.y);
18850
18851 Some(Bounds {
18852 origin: element_bounds.origin + point(x, y),
18853 size: size(em_width, line_height),
18854 })
18855 }
18856
18857 fn character_index_for_point(
18858 &mut self,
18859 point: gpui::Point<Pixels>,
18860 _window: &mut Window,
18861 _cx: &mut Context<Self>,
18862 ) -> Option<usize> {
18863 let position_map = self.last_position_map.as_ref()?;
18864 if !position_map.text_hitbox.contains(&point) {
18865 return None;
18866 }
18867 let display_point = position_map.point_for_position(point).previous_valid;
18868 let anchor = position_map
18869 .snapshot
18870 .display_point_to_anchor(display_point, Bias::Left);
18871 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
18872 Some(utf16_offset.0)
18873 }
18874}
18875
18876trait SelectionExt {
18877 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
18878 fn spanned_rows(
18879 &self,
18880 include_end_if_at_line_start: bool,
18881 map: &DisplaySnapshot,
18882 ) -> Range<MultiBufferRow>;
18883}
18884
18885impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
18886 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
18887 let start = self
18888 .start
18889 .to_point(&map.buffer_snapshot)
18890 .to_display_point(map);
18891 let end = self
18892 .end
18893 .to_point(&map.buffer_snapshot)
18894 .to_display_point(map);
18895 if self.reversed {
18896 end..start
18897 } else {
18898 start..end
18899 }
18900 }
18901
18902 fn spanned_rows(
18903 &self,
18904 include_end_if_at_line_start: bool,
18905 map: &DisplaySnapshot,
18906 ) -> Range<MultiBufferRow> {
18907 let start = self.start.to_point(&map.buffer_snapshot);
18908 let mut end = self.end.to_point(&map.buffer_snapshot);
18909 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
18910 end.row -= 1;
18911 }
18912
18913 let buffer_start = map.prev_line_boundary(start).0;
18914 let buffer_end = map.next_line_boundary(end).0;
18915 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
18916 }
18917}
18918
18919impl<T: InvalidationRegion> InvalidationStack<T> {
18920 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
18921 where
18922 S: Clone + ToOffset,
18923 {
18924 while let Some(region) = self.last() {
18925 let all_selections_inside_invalidation_ranges =
18926 if selections.len() == region.ranges().len() {
18927 selections
18928 .iter()
18929 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
18930 .all(|(selection, invalidation_range)| {
18931 let head = selection.head().to_offset(buffer);
18932 invalidation_range.start <= head && invalidation_range.end >= head
18933 })
18934 } else {
18935 false
18936 };
18937
18938 if all_selections_inside_invalidation_ranges {
18939 break;
18940 } else {
18941 self.pop();
18942 }
18943 }
18944 }
18945}
18946
18947impl<T> Default for InvalidationStack<T> {
18948 fn default() -> Self {
18949 Self(Default::default())
18950 }
18951}
18952
18953impl<T> Deref for InvalidationStack<T> {
18954 type Target = Vec<T>;
18955
18956 fn deref(&self) -> &Self::Target {
18957 &self.0
18958 }
18959}
18960
18961impl<T> DerefMut for InvalidationStack<T> {
18962 fn deref_mut(&mut self) -> &mut Self::Target {
18963 &mut self.0
18964 }
18965}
18966
18967impl InvalidationRegion for SnippetState {
18968 fn ranges(&self) -> &[Range<Anchor>] {
18969 &self.ranges[self.active_index]
18970 }
18971}
18972
18973pub fn diagnostic_block_renderer(
18974 diagnostic: Diagnostic,
18975 max_message_rows: Option<u8>,
18976 allow_closing: bool,
18977) -> RenderBlock {
18978 let (text_without_backticks, code_ranges) =
18979 highlight_diagnostic_message(&diagnostic, max_message_rows);
18980
18981 Arc::new(move |cx: &mut BlockContext| {
18982 let group_id: SharedString = cx.block_id.to_string().into();
18983
18984 let mut text_style = cx.window.text_style().clone();
18985 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18986 let theme_settings = ThemeSettings::get_global(cx);
18987 text_style.font_family = theme_settings.buffer_font.family.clone();
18988 text_style.font_style = theme_settings.buffer_font.style;
18989 text_style.font_features = theme_settings.buffer_font.features.clone();
18990 text_style.font_weight = theme_settings.buffer_font.weight;
18991
18992 let multi_line_diagnostic = diagnostic.message.contains('\n');
18993
18994 let buttons = |diagnostic: &Diagnostic| {
18995 if multi_line_diagnostic {
18996 v_flex()
18997 } else {
18998 h_flex()
18999 }
19000 .when(allow_closing, |div| {
19001 div.children(diagnostic.is_primary.then(|| {
19002 IconButton::new("close-block", IconName::XCircle)
19003 .icon_color(Color::Muted)
19004 .size(ButtonSize::Compact)
19005 .style(ButtonStyle::Transparent)
19006 .visible_on_hover(group_id.clone())
19007 .on_click(move |_click, window, cx| {
19008 window.dispatch_action(Box::new(Cancel), cx)
19009 })
19010 .tooltip(|window, cx| {
19011 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19012 })
19013 }))
19014 })
19015 .child(
19016 IconButton::new("copy-block", IconName::Copy)
19017 .icon_color(Color::Muted)
19018 .size(ButtonSize::Compact)
19019 .style(ButtonStyle::Transparent)
19020 .visible_on_hover(group_id.clone())
19021 .on_click({
19022 let message = diagnostic.message.clone();
19023 move |_click, _, cx| {
19024 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19025 }
19026 })
19027 .tooltip(Tooltip::text("Copy diagnostic message")),
19028 )
19029 };
19030
19031 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19032 AvailableSpace::min_size(),
19033 cx.window,
19034 cx.app,
19035 );
19036
19037 h_flex()
19038 .id(cx.block_id)
19039 .group(group_id.clone())
19040 .relative()
19041 .size_full()
19042 .block_mouse_down()
19043 .pl(cx.gutter_dimensions.width)
19044 .w(cx.max_width - cx.gutter_dimensions.full_width())
19045 .child(
19046 div()
19047 .flex()
19048 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19049 .flex_shrink(),
19050 )
19051 .child(buttons(&diagnostic))
19052 .child(div().flex().flex_shrink_0().child(
19053 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19054 &text_style,
19055 code_ranges.iter().map(|range| {
19056 (
19057 range.clone(),
19058 HighlightStyle {
19059 font_weight: Some(FontWeight::BOLD),
19060 ..Default::default()
19061 },
19062 )
19063 }),
19064 ),
19065 ))
19066 .into_any_element()
19067 })
19068}
19069
19070fn inline_completion_edit_text(
19071 current_snapshot: &BufferSnapshot,
19072 edits: &[(Range<Anchor>, String)],
19073 edit_preview: &EditPreview,
19074 include_deletions: bool,
19075 cx: &App,
19076) -> HighlightedText {
19077 let edits = edits
19078 .iter()
19079 .map(|(anchor, text)| {
19080 (
19081 anchor.start.text_anchor..anchor.end.text_anchor,
19082 text.clone(),
19083 )
19084 })
19085 .collect::<Vec<_>>();
19086
19087 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19088}
19089
19090pub fn highlight_diagnostic_message(
19091 diagnostic: &Diagnostic,
19092 mut max_message_rows: Option<u8>,
19093) -> (SharedString, Vec<Range<usize>>) {
19094 let mut text_without_backticks = String::new();
19095 let mut code_ranges = Vec::new();
19096
19097 if let Some(source) = &diagnostic.source {
19098 text_without_backticks.push_str(source);
19099 code_ranges.push(0..source.len());
19100 text_without_backticks.push_str(": ");
19101 }
19102
19103 let mut prev_offset = 0;
19104 let mut in_code_block = false;
19105 let has_row_limit = max_message_rows.is_some();
19106 let mut newline_indices = diagnostic
19107 .message
19108 .match_indices('\n')
19109 .filter(|_| has_row_limit)
19110 .map(|(ix, _)| ix)
19111 .fuse()
19112 .peekable();
19113
19114 for (quote_ix, _) in diagnostic
19115 .message
19116 .match_indices('`')
19117 .chain([(diagnostic.message.len(), "")])
19118 {
19119 let mut first_newline_ix = None;
19120 let mut last_newline_ix = None;
19121 while let Some(newline_ix) = newline_indices.peek() {
19122 if *newline_ix < quote_ix {
19123 if first_newline_ix.is_none() {
19124 first_newline_ix = Some(*newline_ix);
19125 }
19126 last_newline_ix = Some(*newline_ix);
19127
19128 if let Some(rows_left) = &mut max_message_rows {
19129 if *rows_left == 0 {
19130 break;
19131 } else {
19132 *rows_left -= 1;
19133 }
19134 }
19135 let _ = newline_indices.next();
19136 } else {
19137 break;
19138 }
19139 }
19140 let prev_len = text_without_backticks.len();
19141 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19142 text_without_backticks.push_str(new_text);
19143 if in_code_block {
19144 code_ranges.push(prev_len..text_without_backticks.len());
19145 }
19146 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19147 in_code_block = !in_code_block;
19148 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19149 text_without_backticks.push_str("...");
19150 break;
19151 }
19152 }
19153
19154 (text_without_backticks.into(), code_ranges)
19155}
19156
19157fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19158 match severity {
19159 DiagnosticSeverity::ERROR => colors.error,
19160 DiagnosticSeverity::WARNING => colors.warning,
19161 DiagnosticSeverity::INFORMATION => colors.info,
19162 DiagnosticSeverity::HINT => colors.info,
19163 _ => colors.ignored,
19164 }
19165}
19166
19167pub fn styled_runs_for_code_label<'a>(
19168 label: &'a CodeLabel,
19169 syntax_theme: &'a theme::SyntaxTheme,
19170) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19171 let fade_out = HighlightStyle {
19172 fade_out: Some(0.35),
19173 ..Default::default()
19174 };
19175
19176 let mut prev_end = label.filter_range.end;
19177 label
19178 .runs
19179 .iter()
19180 .enumerate()
19181 .flat_map(move |(ix, (range, highlight_id))| {
19182 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19183 style
19184 } else {
19185 return Default::default();
19186 };
19187 let mut muted_style = style;
19188 muted_style.highlight(fade_out);
19189
19190 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19191 if range.start >= label.filter_range.end {
19192 if range.start > prev_end {
19193 runs.push((prev_end..range.start, fade_out));
19194 }
19195 runs.push((range.clone(), muted_style));
19196 } else if range.end <= label.filter_range.end {
19197 runs.push((range.clone(), style));
19198 } else {
19199 runs.push((range.start..label.filter_range.end, style));
19200 runs.push((label.filter_range.end..range.end, muted_style));
19201 }
19202 prev_end = cmp::max(prev_end, range.end);
19203
19204 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19205 runs.push((prev_end..label.text.len(), fade_out));
19206 }
19207
19208 runs
19209 })
19210}
19211
19212pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19213 let mut prev_index = 0;
19214 let mut prev_codepoint: Option<char> = None;
19215 text.char_indices()
19216 .chain([(text.len(), '\0')])
19217 .filter_map(move |(index, codepoint)| {
19218 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19219 let is_boundary = index == text.len()
19220 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19221 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19222 if is_boundary {
19223 let chunk = &text[prev_index..index];
19224 prev_index = index;
19225 Some(chunk)
19226 } else {
19227 None
19228 }
19229 })
19230}
19231
19232pub trait RangeToAnchorExt: Sized {
19233 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19234
19235 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19236 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19237 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19238 }
19239}
19240
19241impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19242 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19243 let start_offset = self.start.to_offset(snapshot);
19244 let end_offset = self.end.to_offset(snapshot);
19245 if start_offset == end_offset {
19246 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19247 } else {
19248 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19249 }
19250 }
19251}
19252
19253pub trait RowExt {
19254 fn as_f32(&self) -> f32;
19255
19256 fn next_row(&self) -> Self;
19257
19258 fn previous_row(&self) -> Self;
19259
19260 fn minus(&self, other: Self) -> u32;
19261}
19262
19263impl RowExt for DisplayRow {
19264 fn as_f32(&self) -> f32 {
19265 self.0 as f32
19266 }
19267
19268 fn next_row(&self) -> Self {
19269 Self(self.0 + 1)
19270 }
19271
19272 fn previous_row(&self) -> Self {
19273 Self(self.0.saturating_sub(1))
19274 }
19275
19276 fn minus(&self, other: Self) -> u32 {
19277 self.0 - other.0
19278 }
19279}
19280
19281impl RowExt for MultiBufferRow {
19282 fn as_f32(&self) -> f32 {
19283 self.0 as f32
19284 }
19285
19286 fn next_row(&self) -> Self {
19287 Self(self.0 + 1)
19288 }
19289
19290 fn previous_row(&self) -> Self {
19291 Self(self.0.saturating_sub(1))
19292 }
19293
19294 fn minus(&self, other: Self) -> u32 {
19295 self.0 - other.0
19296 }
19297}
19298
19299trait RowRangeExt {
19300 type Row;
19301
19302 fn len(&self) -> usize;
19303
19304 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19305}
19306
19307impl RowRangeExt for Range<MultiBufferRow> {
19308 type Row = MultiBufferRow;
19309
19310 fn len(&self) -> usize {
19311 (self.end.0 - self.start.0) as usize
19312 }
19313
19314 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19315 (self.start.0..self.end.0).map(MultiBufferRow)
19316 }
19317}
19318
19319impl RowRangeExt for Range<DisplayRow> {
19320 type Row = DisplayRow;
19321
19322 fn len(&self) -> usize {
19323 (self.end.0 - self.start.0) as usize
19324 }
19325
19326 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19327 (self.start.0..self.end.0).map(DisplayRow)
19328 }
19329}
19330
19331/// If select range has more than one line, we
19332/// just point the cursor to range.start.
19333fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19334 if range.start.row == range.end.row {
19335 range
19336 } else {
19337 range.start..range.start
19338 }
19339}
19340pub struct KillRing(ClipboardItem);
19341impl Global for KillRing {}
19342
19343const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19344
19345struct BreakpointPromptEditor {
19346 pub(crate) prompt: Entity<Editor>,
19347 editor: WeakEntity<Editor>,
19348 breakpoint_anchor: Anchor,
19349 kind: BreakpointKind,
19350 block_ids: HashSet<CustomBlockId>,
19351 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19352 _subscriptions: Vec<Subscription>,
19353}
19354
19355impl BreakpointPromptEditor {
19356 const MAX_LINES: u8 = 4;
19357
19358 fn new(
19359 editor: WeakEntity<Editor>,
19360 breakpoint_anchor: Anchor,
19361 kind: BreakpointKind,
19362 window: &mut Window,
19363 cx: &mut Context<Self>,
19364 ) -> Self {
19365 let buffer = cx.new(|cx| {
19366 Buffer::local(
19367 kind.log_message()
19368 .map(|msg| msg.to_string())
19369 .unwrap_or_default(),
19370 cx,
19371 )
19372 });
19373 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19374
19375 let prompt = cx.new(|cx| {
19376 let mut prompt = Editor::new(
19377 EditorMode::AutoHeight {
19378 max_lines: Self::MAX_LINES as usize,
19379 },
19380 buffer,
19381 None,
19382 window,
19383 cx,
19384 );
19385 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19386 prompt.set_show_cursor_when_unfocused(false, cx);
19387 prompt.set_placeholder_text(
19388 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19389 cx,
19390 );
19391
19392 prompt
19393 });
19394
19395 Self {
19396 prompt,
19397 editor,
19398 breakpoint_anchor,
19399 kind,
19400 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19401 block_ids: Default::default(),
19402 _subscriptions: vec![],
19403 }
19404 }
19405
19406 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19407 self.block_ids.extend(block_ids)
19408 }
19409
19410 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19411 if let Some(editor) = self.editor.upgrade() {
19412 let log_message = self
19413 .prompt
19414 .read(cx)
19415 .buffer
19416 .read(cx)
19417 .as_singleton()
19418 .expect("A multi buffer in breakpoint prompt isn't possible")
19419 .read(cx)
19420 .as_rope()
19421 .to_string();
19422
19423 editor.update(cx, |editor, cx| {
19424 editor.edit_breakpoint_at_anchor(
19425 self.breakpoint_anchor,
19426 self.kind.clone(),
19427 BreakpointEditAction::EditLogMessage(log_message.into()),
19428 cx,
19429 );
19430
19431 editor.remove_blocks(self.block_ids.clone(), None, cx);
19432 cx.focus_self(window);
19433 });
19434 }
19435 }
19436
19437 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19438 self.editor
19439 .update(cx, |editor, cx| {
19440 editor.remove_blocks(self.block_ids.clone(), None, cx);
19441 window.focus(&editor.focus_handle);
19442 })
19443 .log_err();
19444 }
19445
19446 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19447 let settings = ThemeSettings::get_global(cx);
19448 let text_style = TextStyle {
19449 color: if self.prompt.read(cx).read_only(cx) {
19450 cx.theme().colors().text_disabled
19451 } else {
19452 cx.theme().colors().text
19453 },
19454 font_family: settings.buffer_font.family.clone(),
19455 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19456 font_size: settings.buffer_font_size(cx).into(),
19457 font_weight: settings.buffer_font.weight,
19458 line_height: relative(settings.buffer_line_height.value()),
19459 ..Default::default()
19460 };
19461 EditorElement::new(
19462 &self.prompt,
19463 EditorStyle {
19464 background: cx.theme().colors().editor_background,
19465 local_player: cx.theme().players().local(),
19466 text: text_style,
19467 ..Default::default()
19468 },
19469 )
19470 }
19471}
19472
19473impl Render for BreakpointPromptEditor {
19474 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19475 let gutter_dimensions = *self.gutter_dimensions.lock();
19476 h_flex()
19477 .key_context("Editor")
19478 .bg(cx.theme().colors().editor_background)
19479 .border_y_1()
19480 .border_color(cx.theme().status().info_border)
19481 .size_full()
19482 .py(window.line_height() / 2.5)
19483 .on_action(cx.listener(Self::confirm))
19484 .on_action(cx.listener(Self::cancel))
19485 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19486 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19487 }
19488}
19489
19490impl Focusable for BreakpointPromptEditor {
19491 fn focus_handle(&self, cx: &App) -> FocusHandle {
19492 self.prompt.focus_handle(cx)
19493 }
19494}
19495
19496fn all_edits_insertions_or_deletions(
19497 edits: &Vec<(Range<Anchor>, String)>,
19498 snapshot: &MultiBufferSnapshot,
19499) -> bool {
19500 let mut all_insertions = true;
19501 let mut all_deletions = true;
19502
19503 for (range, new_text) in edits.iter() {
19504 let range_is_empty = range.to_offset(&snapshot).is_empty();
19505 let text_is_empty = new_text.is_empty();
19506
19507 if range_is_empty != text_is_empty {
19508 if range_is_empty {
19509 all_deletions = false;
19510 } else {
19511 all_insertions = false;
19512 }
19513 } else {
19514 return false;
19515 }
19516
19517 if !all_insertions && !all_deletions {
19518 return false;
19519 }
19520 }
19521 all_insertions || all_deletions
19522}
19523
19524struct MissingEditPredictionKeybindingTooltip;
19525
19526impl Render for MissingEditPredictionKeybindingTooltip {
19527 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19528 ui::tooltip_container(window, cx, |container, _, cx| {
19529 container
19530 .flex_shrink_0()
19531 .max_w_80()
19532 .min_h(rems_from_px(124.))
19533 .justify_between()
19534 .child(
19535 v_flex()
19536 .flex_1()
19537 .text_ui_sm(cx)
19538 .child(Label::new("Conflict with Accept Keybinding"))
19539 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19540 )
19541 .child(
19542 h_flex()
19543 .pb_1()
19544 .gap_1()
19545 .items_end()
19546 .w_full()
19547 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19548 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19549 }))
19550 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19551 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19552 })),
19553 )
19554 })
19555 }
19556}
19557
19558#[derive(Debug, Clone, Copy, PartialEq)]
19559pub struct LineHighlight {
19560 pub background: Background,
19561 pub border: Option<gpui::Hsla>,
19562}
19563
19564impl From<Hsla> for LineHighlight {
19565 fn from(hsla: Hsla) -> Self {
19566 Self {
19567 background: hsla.into(),
19568 border: None,
19569 }
19570 }
19571}
19572
19573impl From<Background> for LineHighlight {
19574 fn from(background: Background) -> Self {
19575 Self {
19576 background,
19577 border: None,
19578 }
19579 }
19580}