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::{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, async move |workspace, cx| {
1719 let buffer = create.await?;
1720 workspace.update_in(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, async move |workspace, cx| {
1757 let buffer = create.await?;
1758 workspace.update_in(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(async move |this, cx| {
2188 completion_menu
2189 .filter(query.as_deref(), cx.background_executor().clone())
2190 .await;
2191
2192 this.update(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, async move |editor, cx| {
4054 if let Some(transaction) = on_type_formatting.await? {
4055 if push_to_client_history {
4056 buffer
4057 .update(cx, |buffer, _| {
4058 buffer.push_transaction(transaction, Instant::now());
4059 })
4060 .ok();
4061 }
4062 editor.update(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, async move |editor, cx| {
4219 async move {
4220 editor.update(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(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 .await
4312 });
4313
4314 self.completion_tasks.push((id, task));
4315 }
4316
4317 pub fn confirm_completion(
4318 &mut self,
4319 action: &ConfirmCompletion,
4320 window: &mut Window,
4321 cx: &mut Context<Self>,
4322 ) -> Option<Task<Result<()>>> {
4323 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4324 }
4325
4326 pub fn compose_completion(
4327 &mut self,
4328 action: &ComposeCompletion,
4329 window: &mut Window,
4330 cx: &mut Context<Self>,
4331 ) -> Option<Task<Result<()>>> {
4332 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4333 }
4334
4335 fn do_completion(
4336 &mut self,
4337 item_ix: Option<usize>,
4338 intent: CompletionIntent,
4339 window: &mut Window,
4340 cx: &mut Context<Editor>,
4341 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4342 use language::ToOffset as _;
4343
4344 let completions_menu =
4345 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4346 menu
4347 } else {
4348 return None;
4349 };
4350
4351 let entries = completions_menu.entries.borrow();
4352 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4353 if self.show_edit_predictions_in_menu() {
4354 self.discard_inline_completion(true, cx);
4355 }
4356 let candidate_id = mat.candidate_id;
4357 drop(entries);
4358
4359 let buffer_handle = completions_menu.buffer;
4360 let completion = completions_menu
4361 .completions
4362 .borrow()
4363 .get(candidate_id)?
4364 .clone();
4365 cx.stop_propagation();
4366
4367 let snippet;
4368 let text;
4369
4370 if completion.is_snippet() {
4371 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4372 text = snippet.as_ref().unwrap().text.clone();
4373 } else {
4374 snippet = None;
4375 text = completion.new_text.clone();
4376 };
4377 let selections = self.selections.all::<usize>(cx);
4378 let buffer = buffer_handle.read(cx);
4379 let old_range = completion.old_range.to_offset(buffer);
4380 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4381
4382 let newest_selection = self.selections.newest_anchor();
4383 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4384 return None;
4385 }
4386
4387 let lookbehind = newest_selection
4388 .start
4389 .text_anchor
4390 .to_offset(buffer)
4391 .saturating_sub(old_range.start);
4392 let lookahead = old_range
4393 .end
4394 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4395 let mut common_prefix_len = old_text
4396 .bytes()
4397 .zip(text.bytes())
4398 .take_while(|(a, b)| a == b)
4399 .count();
4400
4401 let snapshot = self.buffer.read(cx).snapshot(cx);
4402 let mut range_to_replace: Option<Range<isize>> = None;
4403 let mut ranges = Vec::new();
4404 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4405 for selection in &selections {
4406 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4407 let start = selection.start.saturating_sub(lookbehind);
4408 let end = selection.end + lookahead;
4409 if selection.id == newest_selection.id {
4410 range_to_replace = Some(
4411 ((start + common_prefix_len) as isize - selection.start as isize)
4412 ..(end as isize - selection.start as isize),
4413 );
4414 }
4415 ranges.push(start + common_prefix_len..end);
4416 } else {
4417 common_prefix_len = 0;
4418 ranges.clear();
4419 ranges.extend(selections.iter().map(|s| {
4420 if s.id == newest_selection.id {
4421 range_to_replace = Some(
4422 old_range.start.to_offset_utf16(&snapshot).0 as isize
4423 - selection.start as isize
4424 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4425 - selection.start as isize,
4426 );
4427 old_range.clone()
4428 } else {
4429 s.start..s.end
4430 }
4431 }));
4432 break;
4433 }
4434 if !self.linked_edit_ranges.is_empty() {
4435 let start_anchor = snapshot.anchor_before(selection.head());
4436 let end_anchor = snapshot.anchor_after(selection.tail());
4437 if let Some(ranges) = self
4438 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4439 {
4440 for (buffer, edits) in ranges {
4441 linked_edits.entry(buffer.clone()).or_default().extend(
4442 edits
4443 .into_iter()
4444 .map(|range| (range, text[common_prefix_len..].to_owned())),
4445 );
4446 }
4447 }
4448 }
4449 }
4450 let text = &text[common_prefix_len..];
4451
4452 cx.emit(EditorEvent::InputHandled {
4453 utf16_range_to_replace: range_to_replace,
4454 text: text.into(),
4455 });
4456
4457 self.transact(window, cx, |this, window, cx| {
4458 if let Some(mut snippet) = snippet {
4459 snippet.text = text.to_string();
4460 for tabstop in snippet
4461 .tabstops
4462 .iter_mut()
4463 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4464 {
4465 tabstop.start -= common_prefix_len as isize;
4466 tabstop.end -= common_prefix_len as isize;
4467 }
4468
4469 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4470 } else {
4471 this.buffer.update(cx, |buffer, cx| {
4472 buffer.edit(
4473 ranges.iter().map(|range| (range.clone(), text)),
4474 this.autoindent_mode.clone(),
4475 cx,
4476 );
4477 });
4478 }
4479 for (buffer, edits) in linked_edits {
4480 buffer.update(cx, |buffer, cx| {
4481 let snapshot = buffer.snapshot();
4482 let edits = edits
4483 .into_iter()
4484 .map(|(range, text)| {
4485 use text::ToPoint as TP;
4486 let end_point = TP::to_point(&range.end, &snapshot);
4487 let start_point = TP::to_point(&range.start, &snapshot);
4488 (start_point..end_point, text)
4489 })
4490 .sorted_by_key(|(range, _)| range.start)
4491 .collect::<Vec<_>>();
4492 buffer.edit(edits, None, cx);
4493 })
4494 }
4495
4496 this.refresh_inline_completion(true, false, window, cx);
4497 });
4498
4499 let show_new_completions_on_confirm = completion
4500 .confirm
4501 .as_ref()
4502 .map_or(false, |confirm| confirm(intent, window, cx));
4503 if show_new_completions_on_confirm {
4504 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4505 }
4506
4507 let provider = self.completion_provider.as_ref()?;
4508 drop(completion);
4509 let apply_edits = provider.apply_additional_edits_for_completion(
4510 buffer_handle,
4511 completions_menu.completions.clone(),
4512 candidate_id,
4513 true,
4514 cx,
4515 );
4516
4517 let editor_settings = EditorSettings::get_global(cx);
4518 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4519 // After the code completion is finished, users often want to know what signatures are needed.
4520 // so we should automatically call signature_help
4521 self.show_signature_help(&ShowSignatureHelp, window, cx);
4522 }
4523
4524 Some(cx.foreground_executor().spawn(async move {
4525 apply_edits.await?;
4526 Ok(())
4527 }))
4528 }
4529
4530 pub fn toggle_code_actions(
4531 &mut self,
4532 action: &ToggleCodeActions,
4533 window: &mut Window,
4534 cx: &mut Context<Self>,
4535 ) {
4536 let mut context_menu = self.context_menu.borrow_mut();
4537 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4538 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4539 // Toggle if we're selecting the same one
4540 *context_menu = None;
4541 cx.notify();
4542 return;
4543 } else {
4544 // Otherwise, clear it and start a new one
4545 *context_menu = None;
4546 cx.notify();
4547 }
4548 }
4549 drop(context_menu);
4550 let snapshot = self.snapshot(window, cx);
4551 let deployed_from_indicator = action.deployed_from_indicator;
4552 let mut task = self.code_actions_task.take();
4553 let action = action.clone();
4554 cx.spawn_in(window, async move |editor, cx| {
4555 while let Some(prev_task) = task {
4556 prev_task.await.log_err();
4557 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4558 }
4559
4560 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4561 if editor.focus_handle.is_focused(window) {
4562 let multibuffer_point = action
4563 .deployed_from_indicator
4564 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4565 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4566 let (buffer, buffer_row) = snapshot
4567 .buffer_snapshot
4568 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4569 .and_then(|(buffer_snapshot, range)| {
4570 editor
4571 .buffer
4572 .read(cx)
4573 .buffer(buffer_snapshot.remote_id())
4574 .map(|buffer| (buffer, range.start.row))
4575 })?;
4576 let (_, code_actions) = editor
4577 .available_code_actions
4578 .clone()
4579 .and_then(|(location, code_actions)| {
4580 let snapshot = location.buffer.read(cx).snapshot();
4581 let point_range = location.range.to_point(&snapshot);
4582 let point_range = point_range.start.row..=point_range.end.row;
4583 if point_range.contains(&buffer_row) {
4584 Some((location, code_actions))
4585 } else {
4586 None
4587 }
4588 })
4589 .unzip();
4590 let buffer_id = buffer.read(cx).remote_id();
4591 let tasks = editor
4592 .tasks
4593 .get(&(buffer_id, buffer_row))
4594 .map(|t| Arc::new(t.to_owned()));
4595 if tasks.is_none() && code_actions.is_none() {
4596 return None;
4597 }
4598
4599 editor.completion_tasks.clear();
4600 editor.discard_inline_completion(false, cx);
4601 let task_context =
4602 tasks
4603 .as_ref()
4604 .zip(editor.project.clone())
4605 .map(|(tasks, project)| {
4606 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4607 });
4608
4609 Some(cx.spawn_in(window, async move |editor, cx| {
4610 let task_context = match task_context {
4611 Some(task_context) => task_context.await,
4612 None => None,
4613 };
4614 let resolved_tasks =
4615 tasks.zip(task_context).map(|(tasks, task_context)| {
4616 Rc::new(ResolvedTasks {
4617 templates: tasks.resolve(&task_context).collect(),
4618 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4619 multibuffer_point.row,
4620 tasks.column,
4621 )),
4622 })
4623 });
4624 let spawn_straight_away = resolved_tasks
4625 .as_ref()
4626 .map_or(false, |tasks| tasks.templates.len() == 1)
4627 && code_actions
4628 .as_ref()
4629 .map_or(true, |actions| actions.is_empty());
4630 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4631 *editor.context_menu.borrow_mut() =
4632 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4633 buffer,
4634 actions: CodeActionContents {
4635 tasks: resolved_tasks,
4636 actions: code_actions,
4637 },
4638 selected_item: Default::default(),
4639 scroll_handle: UniformListScrollHandle::default(),
4640 deployed_from_indicator,
4641 }));
4642 if spawn_straight_away {
4643 if let Some(task) = editor.confirm_code_action(
4644 &ConfirmCodeAction { item_ix: Some(0) },
4645 window,
4646 cx,
4647 ) {
4648 cx.notify();
4649 return task;
4650 }
4651 }
4652 cx.notify();
4653 Task::ready(Ok(()))
4654 }) {
4655 task.await
4656 } else {
4657 Ok(())
4658 }
4659 }))
4660 } else {
4661 Some(Task::ready(Ok(())))
4662 }
4663 })?;
4664 if let Some(task) = spawned_test_task {
4665 task.await?;
4666 }
4667
4668 Ok::<_, anyhow::Error>(())
4669 })
4670 .detach_and_log_err(cx);
4671 }
4672
4673 pub fn confirm_code_action(
4674 &mut self,
4675 action: &ConfirmCodeAction,
4676 window: &mut Window,
4677 cx: &mut Context<Self>,
4678 ) -> Option<Task<Result<()>>> {
4679 let actions_menu =
4680 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4681 menu
4682 } else {
4683 return None;
4684 };
4685 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4686 let action = actions_menu.actions.get(action_ix)?;
4687 let title = action.label();
4688 let buffer = actions_menu.buffer;
4689 let workspace = self.workspace()?;
4690
4691 match action {
4692 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4693 workspace.update(cx, |workspace, cx| {
4694 workspace::tasks::schedule_resolved_task(
4695 workspace,
4696 task_source_kind,
4697 resolved_task,
4698 false,
4699 cx,
4700 );
4701
4702 Some(Task::ready(Ok(())))
4703 })
4704 }
4705 CodeActionsItem::CodeAction {
4706 excerpt_id,
4707 action,
4708 provider,
4709 } => {
4710 let apply_code_action =
4711 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4712 let workspace = workspace.downgrade();
4713 Some(cx.spawn_in(window, async move |editor, cx| {
4714 let project_transaction = apply_code_action.await?;
4715 Self::open_project_transaction(
4716 &editor,
4717 workspace,
4718 project_transaction,
4719 title,
4720 cx,
4721 )
4722 .await
4723 }))
4724 }
4725 }
4726 }
4727
4728 pub async fn open_project_transaction(
4729 this: &WeakEntity<Editor>,
4730 workspace: WeakEntity<Workspace>,
4731 transaction: ProjectTransaction,
4732 title: String,
4733 cx: &mut AsyncWindowContext,
4734 ) -> Result<()> {
4735 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4736 cx.update(|_, cx| {
4737 entries.sort_unstable_by_key(|(buffer, _)| {
4738 buffer.read(cx).file().map(|f| f.path().clone())
4739 });
4740 })?;
4741
4742 // If the project transaction's edits are all contained within this editor, then
4743 // avoid opening a new editor to display them.
4744
4745 if let Some((buffer, transaction)) = entries.first() {
4746 if entries.len() == 1 {
4747 let excerpt = this.update(cx, |editor, cx| {
4748 editor
4749 .buffer()
4750 .read(cx)
4751 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4752 })?;
4753 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4754 if excerpted_buffer == *buffer {
4755 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4756 let excerpt_range = excerpt_range.to_offset(buffer);
4757 buffer
4758 .edited_ranges_for_transaction::<usize>(transaction)
4759 .all(|range| {
4760 excerpt_range.start <= range.start
4761 && excerpt_range.end >= range.end
4762 })
4763 })?;
4764
4765 if all_edits_within_excerpt {
4766 return Ok(());
4767 }
4768 }
4769 }
4770 }
4771 } else {
4772 return Ok(());
4773 }
4774
4775 let mut ranges_to_highlight = Vec::new();
4776 let excerpt_buffer = cx.new(|cx| {
4777 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4778 for (buffer_handle, transaction) in &entries {
4779 let buffer = buffer_handle.read(cx);
4780 ranges_to_highlight.extend(
4781 multibuffer.push_excerpts_with_context_lines(
4782 buffer_handle.clone(),
4783 buffer
4784 .edited_ranges_for_transaction::<usize>(transaction)
4785 .collect(),
4786 DEFAULT_MULTIBUFFER_CONTEXT,
4787 cx,
4788 ),
4789 );
4790 }
4791 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4792 multibuffer
4793 })?;
4794
4795 workspace.update_in(cx, |workspace, window, cx| {
4796 let project = workspace.project().clone();
4797 let editor =
4798 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4799 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4800 editor.update(cx, |editor, cx| {
4801 editor.highlight_background::<Self>(
4802 &ranges_to_highlight,
4803 |theme| theme.editor_highlighted_line_background,
4804 cx,
4805 );
4806 });
4807 })?;
4808
4809 Ok(())
4810 }
4811
4812 pub fn clear_code_action_providers(&mut self) {
4813 self.code_action_providers.clear();
4814 self.available_code_actions.take();
4815 }
4816
4817 pub fn add_code_action_provider(
4818 &mut self,
4819 provider: Rc<dyn CodeActionProvider>,
4820 window: &mut Window,
4821 cx: &mut Context<Self>,
4822 ) {
4823 if self
4824 .code_action_providers
4825 .iter()
4826 .any(|existing_provider| existing_provider.id() == provider.id())
4827 {
4828 return;
4829 }
4830
4831 self.code_action_providers.push(provider);
4832 self.refresh_code_actions(window, cx);
4833 }
4834
4835 pub fn remove_code_action_provider(
4836 &mut self,
4837 id: Arc<str>,
4838 window: &mut Window,
4839 cx: &mut Context<Self>,
4840 ) {
4841 self.code_action_providers
4842 .retain(|provider| provider.id() != id);
4843 self.refresh_code_actions(window, cx);
4844 }
4845
4846 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4847 let buffer = self.buffer.read(cx);
4848 let newest_selection = self.selections.newest_anchor().clone();
4849 if newest_selection.head().diff_base_anchor.is_some() {
4850 return None;
4851 }
4852 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4853 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4854 if start_buffer != end_buffer {
4855 return None;
4856 }
4857
4858 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4859 cx.background_executor()
4860 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4861 .await;
4862
4863 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4864 let providers = this.code_action_providers.clone();
4865 let tasks = this
4866 .code_action_providers
4867 .iter()
4868 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4869 .collect::<Vec<_>>();
4870 (providers, tasks)
4871 })?;
4872
4873 let mut actions = Vec::new();
4874 for (provider, provider_actions) in
4875 providers.into_iter().zip(future::join_all(tasks).await)
4876 {
4877 if let Some(provider_actions) = provider_actions.log_err() {
4878 actions.extend(provider_actions.into_iter().map(|action| {
4879 AvailableCodeAction {
4880 excerpt_id: newest_selection.start.excerpt_id,
4881 action,
4882 provider: provider.clone(),
4883 }
4884 }));
4885 }
4886 }
4887
4888 this.update(cx, |this, cx| {
4889 this.available_code_actions = if actions.is_empty() {
4890 None
4891 } else {
4892 Some((
4893 Location {
4894 buffer: start_buffer,
4895 range: start..end,
4896 },
4897 actions.into(),
4898 ))
4899 };
4900 cx.notify();
4901 })
4902 }));
4903 None
4904 }
4905
4906 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4907 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4908 self.show_git_blame_inline = false;
4909
4910 self.show_git_blame_inline_delay_task =
4911 Some(cx.spawn_in(window, async move |this, cx| {
4912 cx.background_executor().timer(delay).await;
4913
4914 this.update(cx, |this, cx| {
4915 this.show_git_blame_inline = true;
4916 cx.notify();
4917 })
4918 .log_err();
4919 }));
4920 }
4921 }
4922
4923 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4924 if self.pending_rename.is_some() {
4925 return None;
4926 }
4927
4928 let provider = self.semantics_provider.clone()?;
4929 let buffer = self.buffer.read(cx);
4930 let newest_selection = self.selections.newest_anchor().clone();
4931 let cursor_position = newest_selection.head();
4932 let (cursor_buffer, cursor_buffer_position) =
4933 buffer.text_anchor_for_position(cursor_position, cx)?;
4934 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4935 if cursor_buffer != tail_buffer {
4936 return None;
4937 }
4938 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4939 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
4940 cx.background_executor()
4941 .timer(Duration::from_millis(debounce))
4942 .await;
4943
4944 let highlights = if let Some(highlights) = cx
4945 .update(|cx| {
4946 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4947 })
4948 .ok()
4949 .flatten()
4950 {
4951 highlights.await.log_err()
4952 } else {
4953 None
4954 };
4955
4956 if let Some(highlights) = highlights {
4957 this.update(cx, |this, cx| {
4958 if this.pending_rename.is_some() {
4959 return;
4960 }
4961
4962 let buffer_id = cursor_position.buffer_id;
4963 let buffer = this.buffer.read(cx);
4964 if !buffer
4965 .text_anchor_for_position(cursor_position, cx)
4966 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4967 {
4968 return;
4969 }
4970
4971 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4972 let mut write_ranges = Vec::new();
4973 let mut read_ranges = Vec::new();
4974 for highlight in highlights {
4975 for (excerpt_id, excerpt_range) in
4976 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4977 {
4978 let start = highlight
4979 .range
4980 .start
4981 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4982 let end = highlight
4983 .range
4984 .end
4985 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4986 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4987 continue;
4988 }
4989
4990 let range = Anchor {
4991 buffer_id,
4992 excerpt_id,
4993 text_anchor: start,
4994 diff_base_anchor: None,
4995 }..Anchor {
4996 buffer_id,
4997 excerpt_id,
4998 text_anchor: end,
4999 diff_base_anchor: None,
5000 };
5001 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5002 write_ranges.push(range);
5003 } else {
5004 read_ranges.push(range);
5005 }
5006 }
5007 }
5008
5009 this.highlight_background::<DocumentHighlightRead>(
5010 &read_ranges,
5011 |theme| theme.editor_document_highlight_read_background,
5012 cx,
5013 );
5014 this.highlight_background::<DocumentHighlightWrite>(
5015 &write_ranges,
5016 |theme| theme.editor_document_highlight_write_background,
5017 cx,
5018 );
5019 cx.notify();
5020 })
5021 .log_err();
5022 }
5023 }));
5024 None
5025 }
5026
5027 pub fn refresh_selected_text_highlights(
5028 &mut self,
5029 window: &mut Window,
5030 cx: &mut Context<Editor>,
5031 ) {
5032 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5033 return;
5034 }
5035 self.selection_highlight_task.take();
5036 if !EditorSettings::get_global(cx).selection_highlight {
5037 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5038 return;
5039 }
5040 if self.selections.count() != 1 || self.selections.line_mode {
5041 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5042 return;
5043 }
5044 let selection = self.selections.newest::<Point>(cx);
5045 if selection.is_empty() || selection.start.row != selection.end.row {
5046 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5047 return;
5048 }
5049 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5050 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5051 cx.background_executor()
5052 .timer(Duration::from_millis(debounce))
5053 .await;
5054 let Some(Some(matches_task)) = editor
5055 .update_in(cx, |editor, _, cx| {
5056 if editor.selections.count() != 1 || editor.selections.line_mode {
5057 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5058 return None;
5059 }
5060 let selection = editor.selections.newest::<Point>(cx);
5061 if selection.is_empty() || selection.start.row != selection.end.row {
5062 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5063 return None;
5064 }
5065 let buffer = editor.buffer().read(cx).snapshot(cx);
5066 let query = buffer.text_for_range(selection.range()).collect::<String>();
5067 if query.trim().is_empty() {
5068 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5069 return None;
5070 }
5071 Some(cx.background_spawn(async move {
5072 let mut ranges = Vec::new();
5073 let selection_anchors = selection.range().to_anchors(&buffer);
5074 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5075 for (search_buffer, search_range, excerpt_id) in
5076 buffer.range_to_buffer_ranges(range)
5077 {
5078 ranges.extend(
5079 project::search::SearchQuery::text(
5080 query.clone(),
5081 false,
5082 false,
5083 false,
5084 Default::default(),
5085 Default::default(),
5086 None,
5087 )
5088 .unwrap()
5089 .search(search_buffer, Some(search_range.clone()))
5090 .await
5091 .into_iter()
5092 .filter_map(
5093 |match_range| {
5094 let start = search_buffer.anchor_after(
5095 search_range.start + match_range.start,
5096 );
5097 let end = search_buffer.anchor_before(
5098 search_range.start + match_range.end,
5099 );
5100 let range = Anchor::range_in_buffer(
5101 excerpt_id,
5102 search_buffer.remote_id(),
5103 start..end,
5104 );
5105 (range != selection_anchors).then_some(range)
5106 },
5107 ),
5108 );
5109 }
5110 }
5111 ranges
5112 }))
5113 })
5114 .log_err()
5115 else {
5116 return;
5117 };
5118 let matches = matches_task.await;
5119 editor
5120 .update_in(cx, |editor, _, cx| {
5121 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5122 if !matches.is_empty() {
5123 editor.highlight_background::<SelectedTextHighlight>(
5124 &matches,
5125 |theme| theme.editor_document_highlight_bracket_background,
5126 cx,
5127 )
5128 }
5129 })
5130 .log_err();
5131 }));
5132 }
5133
5134 pub fn refresh_inline_completion(
5135 &mut self,
5136 debounce: bool,
5137 user_requested: bool,
5138 window: &mut Window,
5139 cx: &mut Context<Self>,
5140 ) -> Option<()> {
5141 let provider = self.edit_prediction_provider()?;
5142 let cursor = self.selections.newest_anchor().head();
5143 let (buffer, cursor_buffer_position) =
5144 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5145
5146 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5147 self.discard_inline_completion(false, cx);
5148 return None;
5149 }
5150
5151 if !user_requested
5152 && (!self.should_show_edit_predictions()
5153 || !self.is_focused(window)
5154 || buffer.read(cx).is_empty())
5155 {
5156 self.discard_inline_completion(false, cx);
5157 return None;
5158 }
5159
5160 self.update_visible_inline_completion(window, cx);
5161 provider.refresh(
5162 self.project.clone(),
5163 buffer,
5164 cursor_buffer_position,
5165 debounce,
5166 cx,
5167 );
5168 Some(())
5169 }
5170
5171 fn show_edit_predictions_in_menu(&self) -> bool {
5172 match self.edit_prediction_settings {
5173 EditPredictionSettings::Disabled => false,
5174 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5175 }
5176 }
5177
5178 pub fn edit_predictions_enabled(&self) -> bool {
5179 match self.edit_prediction_settings {
5180 EditPredictionSettings::Disabled => false,
5181 EditPredictionSettings::Enabled { .. } => true,
5182 }
5183 }
5184
5185 fn edit_prediction_requires_modifier(&self) -> bool {
5186 match self.edit_prediction_settings {
5187 EditPredictionSettings::Disabled => false,
5188 EditPredictionSettings::Enabled {
5189 preview_requires_modifier,
5190 ..
5191 } => preview_requires_modifier,
5192 }
5193 }
5194
5195 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5196 if self.edit_prediction_provider.is_none() {
5197 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5198 } else {
5199 let selection = self.selections.newest_anchor();
5200 let cursor = selection.head();
5201
5202 if let Some((buffer, cursor_buffer_position)) =
5203 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5204 {
5205 self.edit_prediction_settings =
5206 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5207 }
5208 }
5209 }
5210
5211 fn edit_prediction_settings_at_position(
5212 &self,
5213 buffer: &Entity<Buffer>,
5214 buffer_position: language::Anchor,
5215 cx: &App,
5216 ) -> EditPredictionSettings {
5217 if self.mode != EditorMode::Full
5218 || !self.show_inline_completions_override.unwrap_or(true)
5219 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5220 {
5221 return EditPredictionSettings::Disabled;
5222 }
5223
5224 let buffer = buffer.read(cx);
5225
5226 let file = buffer.file();
5227
5228 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5229 return EditPredictionSettings::Disabled;
5230 };
5231
5232 let by_provider = matches!(
5233 self.menu_inline_completions_policy,
5234 MenuInlineCompletionsPolicy::ByProvider
5235 );
5236
5237 let show_in_menu = by_provider
5238 && self
5239 .edit_prediction_provider
5240 .as_ref()
5241 .map_or(false, |provider| {
5242 provider.provider.show_completions_in_menu()
5243 });
5244
5245 let preview_requires_modifier =
5246 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5247
5248 EditPredictionSettings::Enabled {
5249 show_in_menu,
5250 preview_requires_modifier,
5251 }
5252 }
5253
5254 fn should_show_edit_predictions(&self) -> bool {
5255 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5256 }
5257
5258 pub fn edit_prediction_preview_is_active(&self) -> bool {
5259 matches!(
5260 self.edit_prediction_preview,
5261 EditPredictionPreview::Active { .. }
5262 )
5263 }
5264
5265 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5266 let cursor = self.selections.newest_anchor().head();
5267 if let Some((buffer, cursor_position)) =
5268 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5269 {
5270 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5271 } else {
5272 false
5273 }
5274 }
5275
5276 fn edit_predictions_enabled_in_buffer(
5277 &self,
5278 buffer: &Entity<Buffer>,
5279 buffer_position: language::Anchor,
5280 cx: &App,
5281 ) -> bool {
5282 maybe!({
5283 if self.read_only(cx) {
5284 return Some(false);
5285 }
5286 let provider = self.edit_prediction_provider()?;
5287 if !provider.is_enabled(&buffer, buffer_position, cx) {
5288 return Some(false);
5289 }
5290 let buffer = buffer.read(cx);
5291 let Some(file) = buffer.file() else {
5292 return Some(true);
5293 };
5294 let settings = all_language_settings(Some(file), cx);
5295 Some(settings.edit_predictions_enabled_for_file(file, cx))
5296 })
5297 .unwrap_or(false)
5298 }
5299
5300 fn cycle_inline_completion(
5301 &mut self,
5302 direction: Direction,
5303 window: &mut Window,
5304 cx: &mut Context<Self>,
5305 ) -> Option<()> {
5306 let provider = self.edit_prediction_provider()?;
5307 let cursor = self.selections.newest_anchor().head();
5308 let (buffer, cursor_buffer_position) =
5309 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5310 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5311 return None;
5312 }
5313
5314 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5315 self.update_visible_inline_completion(window, cx);
5316
5317 Some(())
5318 }
5319
5320 pub fn show_inline_completion(
5321 &mut self,
5322 _: &ShowEditPrediction,
5323 window: &mut Window,
5324 cx: &mut Context<Self>,
5325 ) {
5326 if !self.has_active_inline_completion() {
5327 self.refresh_inline_completion(false, true, window, cx);
5328 return;
5329 }
5330
5331 self.update_visible_inline_completion(window, cx);
5332 }
5333
5334 pub fn display_cursor_names(
5335 &mut self,
5336 _: &DisplayCursorNames,
5337 window: &mut Window,
5338 cx: &mut Context<Self>,
5339 ) {
5340 self.show_cursor_names(window, cx);
5341 }
5342
5343 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5344 self.show_cursor_names = true;
5345 cx.notify();
5346 cx.spawn_in(window, async move |this, cx| {
5347 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5348 this.update(cx, |this, cx| {
5349 this.show_cursor_names = false;
5350 cx.notify()
5351 })
5352 .ok()
5353 })
5354 .detach();
5355 }
5356
5357 pub fn next_edit_prediction(
5358 &mut self,
5359 _: &NextEditPrediction,
5360 window: &mut Window,
5361 cx: &mut Context<Self>,
5362 ) {
5363 if self.has_active_inline_completion() {
5364 self.cycle_inline_completion(Direction::Next, window, cx);
5365 } else {
5366 let is_copilot_disabled = self
5367 .refresh_inline_completion(false, true, window, cx)
5368 .is_none();
5369 if is_copilot_disabled {
5370 cx.propagate();
5371 }
5372 }
5373 }
5374
5375 pub fn previous_edit_prediction(
5376 &mut self,
5377 _: &PreviousEditPrediction,
5378 window: &mut Window,
5379 cx: &mut Context<Self>,
5380 ) {
5381 if self.has_active_inline_completion() {
5382 self.cycle_inline_completion(Direction::Prev, window, cx);
5383 } else {
5384 let is_copilot_disabled = self
5385 .refresh_inline_completion(false, true, window, cx)
5386 .is_none();
5387 if is_copilot_disabled {
5388 cx.propagate();
5389 }
5390 }
5391 }
5392
5393 pub fn accept_edit_prediction(
5394 &mut self,
5395 _: &AcceptEditPrediction,
5396 window: &mut Window,
5397 cx: &mut Context<Self>,
5398 ) {
5399 if self.show_edit_predictions_in_menu() {
5400 self.hide_context_menu(window, cx);
5401 }
5402
5403 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5404 return;
5405 };
5406
5407 self.report_inline_completion_event(
5408 active_inline_completion.completion_id.clone(),
5409 true,
5410 cx,
5411 );
5412
5413 match &active_inline_completion.completion {
5414 InlineCompletion::Move { target, .. } => {
5415 let target = *target;
5416
5417 if let Some(position_map) = &self.last_position_map {
5418 if position_map
5419 .visible_row_range
5420 .contains(&target.to_display_point(&position_map.snapshot).row())
5421 || !self.edit_prediction_requires_modifier()
5422 {
5423 self.unfold_ranges(&[target..target], true, false, cx);
5424 // Note that this is also done in vim's handler of the Tab action.
5425 self.change_selections(
5426 Some(Autoscroll::newest()),
5427 window,
5428 cx,
5429 |selections| {
5430 selections.select_anchor_ranges([target..target]);
5431 },
5432 );
5433 self.clear_row_highlights::<EditPredictionPreview>();
5434
5435 self.edit_prediction_preview
5436 .set_previous_scroll_position(None);
5437 } else {
5438 self.edit_prediction_preview
5439 .set_previous_scroll_position(Some(
5440 position_map.snapshot.scroll_anchor,
5441 ));
5442
5443 self.highlight_rows::<EditPredictionPreview>(
5444 target..target,
5445 cx.theme().colors().editor_highlighted_line_background,
5446 true,
5447 cx,
5448 );
5449 self.request_autoscroll(Autoscroll::fit(), cx);
5450 }
5451 }
5452 }
5453 InlineCompletion::Edit { edits, .. } => {
5454 if let Some(provider) = self.edit_prediction_provider() {
5455 provider.accept(cx);
5456 }
5457
5458 let snapshot = self.buffer.read(cx).snapshot(cx);
5459 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5460
5461 self.buffer.update(cx, |buffer, cx| {
5462 buffer.edit(edits.iter().cloned(), None, cx)
5463 });
5464
5465 self.change_selections(None, window, cx, |s| {
5466 s.select_anchor_ranges([last_edit_end..last_edit_end])
5467 });
5468
5469 self.update_visible_inline_completion(window, cx);
5470 if self.active_inline_completion.is_none() {
5471 self.refresh_inline_completion(true, true, window, cx);
5472 }
5473
5474 cx.notify();
5475 }
5476 }
5477
5478 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5479 }
5480
5481 pub fn accept_partial_inline_completion(
5482 &mut self,
5483 _: &AcceptPartialEditPrediction,
5484 window: &mut Window,
5485 cx: &mut Context<Self>,
5486 ) {
5487 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5488 return;
5489 };
5490 if self.selections.count() != 1 {
5491 return;
5492 }
5493
5494 self.report_inline_completion_event(
5495 active_inline_completion.completion_id.clone(),
5496 true,
5497 cx,
5498 );
5499
5500 match &active_inline_completion.completion {
5501 InlineCompletion::Move { target, .. } => {
5502 let target = *target;
5503 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5504 selections.select_anchor_ranges([target..target]);
5505 });
5506 }
5507 InlineCompletion::Edit { edits, .. } => {
5508 // Find an insertion that starts at the cursor position.
5509 let snapshot = self.buffer.read(cx).snapshot(cx);
5510 let cursor_offset = self.selections.newest::<usize>(cx).head();
5511 let insertion = edits.iter().find_map(|(range, text)| {
5512 let range = range.to_offset(&snapshot);
5513 if range.is_empty() && range.start == cursor_offset {
5514 Some(text)
5515 } else {
5516 None
5517 }
5518 });
5519
5520 if let Some(text) = insertion {
5521 let mut partial_completion = text
5522 .chars()
5523 .by_ref()
5524 .take_while(|c| c.is_alphabetic())
5525 .collect::<String>();
5526 if partial_completion.is_empty() {
5527 partial_completion = text
5528 .chars()
5529 .by_ref()
5530 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5531 .collect::<String>();
5532 }
5533
5534 cx.emit(EditorEvent::InputHandled {
5535 utf16_range_to_replace: None,
5536 text: partial_completion.clone().into(),
5537 });
5538
5539 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5540
5541 self.refresh_inline_completion(true, true, window, cx);
5542 cx.notify();
5543 } else {
5544 self.accept_edit_prediction(&Default::default(), window, cx);
5545 }
5546 }
5547 }
5548 }
5549
5550 fn discard_inline_completion(
5551 &mut self,
5552 should_report_inline_completion_event: bool,
5553 cx: &mut Context<Self>,
5554 ) -> bool {
5555 if should_report_inline_completion_event {
5556 let completion_id = self
5557 .active_inline_completion
5558 .as_ref()
5559 .and_then(|active_completion| active_completion.completion_id.clone());
5560
5561 self.report_inline_completion_event(completion_id, false, cx);
5562 }
5563
5564 if let Some(provider) = self.edit_prediction_provider() {
5565 provider.discard(cx);
5566 }
5567
5568 self.take_active_inline_completion(cx)
5569 }
5570
5571 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5572 let Some(provider) = self.edit_prediction_provider() else {
5573 return;
5574 };
5575
5576 let Some((_, buffer, _)) = self
5577 .buffer
5578 .read(cx)
5579 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5580 else {
5581 return;
5582 };
5583
5584 let extension = buffer
5585 .read(cx)
5586 .file()
5587 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5588
5589 let event_type = match accepted {
5590 true => "Edit Prediction Accepted",
5591 false => "Edit Prediction Discarded",
5592 };
5593 telemetry::event!(
5594 event_type,
5595 provider = provider.name(),
5596 prediction_id = id,
5597 suggestion_accepted = accepted,
5598 file_extension = extension,
5599 );
5600 }
5601
5602 pub fn has_active_inline_completion(&self) -> bool {
5603 self.active_inline_completion.is_some()
5604 }
5605
5606 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5607 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5608 return false;
5609 };
5610
5611 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5612 self.clear_highlights::<InlineCompletionHighlight>(cx);
5613 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5614 true
5615 }
5616
5617 /// Returns true when we're displaying the edit prediction popover below the cursor
5618 /// like we are not previewing and the LSP autocomplete menu is visible
5619 /// or we are in `when_holding_modifier` mode.
5620 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5621 if self.edit_prediction_preview_is_active()
5622 || !self.show_edit_predictions_in_menu()
5623 || !self.edit_predictions_enabled()
5624 {
5625 return false;
5626 }
5627
5628 if self.has_visible_completions_menu() {
5629 return true;
5630 }
5631
5632 has_completion && self.edit_prediction_requires_modifier()
5633 }
5634
5635 fn handle_modifiers_changed(
5636 &mut self,
5637 modifiers: Modifiers,
5638 position_map: &PositionMap,
5639 window: &mut Window,
5640 cx: &mut Context<Self>,
5641 ) {
5642 if self.show_edit_predictions_in_menu() {
5643 self.update_edit_prediction_preview(&modifiers, window, cx);
5644 }
5645
5646 self.update_selection_mode(&modifiers, position_map, window, cx);
5647
5648 let mouse_position = window.mouse_position();
5649 if !position_map.text_hitbox.is_hovered(window) {
5650 return;
5651 }
5652
5653 self.update_hovered_link(
5654 position_map.point_for_position(mouse_position),
5655 &position_map.snapshot,
5656 modifiers,
5657 window,
5658 cx,
5659 )
5660 }
5661
5662 fn update_selection_mode(
5663 &mut self,
5664 modifiers: &Modifiers,
5665 position_map: &PositionMap,
5666 window: &mut Window,
5667 cx: &mut Context<Self>,
5668 ) {
5669 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5670 return;
5671 }
5672
5673 let mouse_position = window.mouse_position();
5674 let point_for_position = position_map.point_for_position(mouse_position);
5675 let position = point_for_position.previous_valid;
5676
5677 self.select(
5678 SelectPhase::BeginColumnar {
5679 position,
5680 reset: false,
5681 goal_column: point_for_position.exact_unclipped.column(),
5682 },
5683 window,
5684 cx,
5685 );
5686 }
5687
5688 fn update_edit_prediction_preview(
5689 &mut self,
5690 modifiers: &Modifiers,
5691 window: &mut Window,
5692 cx: &mut Context<Self>,
5693 ) {
5694 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5695 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5696 return;
5697 };
5698
5699 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5700 if matches!(
5701 self.edit_prediction_preview,
5702 EditPredictionPreview::Inactive { .. }
5703 ) {
5704 self.edit_prediction_preview = EditPredictionPreview::Active {
5705 previous_scroll_position: None,
5706 since: Instant::now(),
5707 };
5708
5709 self.update_visible_inline_completion(window, cx);
5710 cx.notify();
5711 }
5712 } else if let EditPredictionPreview::Active {
5713 previous_scroll_position,
5714 since,
5715 } = self.edit_prediction_preview
5716 {
5717 if let (Some(previous_scroll_position), Some(position_map)) =
5718 (previous_scroll_position, self.last_position_map.as_ref())
5719 {
5720 self.set_scroll_position(
5721 previous_scroll_position
5722 .scroll_position(&position_map.snapshot.display_snapshot),
5723 window,
5724 cx,
5725 );
5726 }
5727
5728 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5729 released_too_fast: since.elapsed() < Duration::from_millis(200),
5730 };
5731 self.clear_row_highlights::<EditPredictionPreview>();
5732 self.update_visible_inline_completion(window, cx);
5733 cx.notify();
5734 }
5735 }
5736
5737 fn update_visible_inline_completion(
5738 &mut self,
5739 _window: &mut Window,
5740 cx: &mut Context<Self>,
5741 ) -> Option<()> {
5742 let selection = self.selections.newest_anchor();
5743 let cursor = selection.head();
5744 let multibuffer = self.buffer.read(cx).snapshot(cx);
5745 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5746 let excerpt_id = cursor.excerpt_id;
5747
5748 let show_in_menu = self.show_edit_predictions_in_menu();
5749 let completions_menu_has_precedence = !show_in_menu
5750 && (self.context_menu.borrow().is_some()
5751 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5752
5753 if completions_menu_has_precedence
5754 || !offset_selection.is_empty()
5755 || self
5756 .active_inline_completion
5757 .as_ref()
5758 .map_or(false, |completion| {
5759 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5760 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5761 !invalidation_range.contains(&offset_selection.head())
5762 })
5763 {
5764 self.discard_inline_completion(false, cx);
5765 return None;
5766 }
5767
5768 self.take_active_inline_completion(cx);
5769 let Some(provider) = self.edit_prediction_provider() else {
5770 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5771 return None;
5772 };
5773
5774 let (buffer, cursor_buffer_position) =
5775 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5776
5777 self.edit_prediction_settings =
5778 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5779
5780 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5781
5782 if self.edit_prediction_indent_conflict {
5783 let cursor_point = cursor.to_point(&multibuffer);
5784
5785 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5786
5787 if let Some((_, indent)) = indents.iter().next() {
5788 if indent.len == cursor_point.column {
5789 self.edit_prediction_indent_conflict = false;
5790 }
5791 }
5792 }
5793
5794 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5795 let edits = inline_completion
5796 .edits
5797 .into_iter()
5798 .flat_map(|(range, new_text)| {
5799 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5800 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5801 Some((start..end, new_text))
5802 })
5803 .collect::<Vec<_>>();
5804 if edits.is_empty() {
5805 return None;
5806 }
5807
5808 let first_edit_start = edits.first().unwrap().0.start;
5809 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5810 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5811
5812 let last_edit_end = edits.last().unwrap().0.end;
5813 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5814 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5815
5816 let cursor_row = cursor.to_point(&multibuffer).row;
5817
5818 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5819
5820 let mut inlay_ids = Vec::new();
5821 let invalidation_row_range;
5822 let move_invalidation_row_range = if cursor_row < edit_start_row {
5823 Some(cursor_row..edit_end_row)
5824 } else if cursor_row > edit_end_row {
5825 Some(edit_start_row..cursor_row)
5826 } else {
5827 None
5828 };
5829 let is_move =
5830 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5831 let completion = if is_move {
5832 invalidation_row_range =
5833 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5834 let target = first_edit_start;
5835 InlineCompletion::Move { target, snapshot }
5836 } else {
5837 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5838 && !self.inline_completions_hidden_for_vim_mode;
5839
5840 if show_completions_in_buffer {
5841 if edits
5842 .iter()
5843 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5844 {
5845 let mut inlays = Vec::new();
5846 for (range, new_text) in &edits {
5847 let inlay = Inlay::inline_completion(
5848 post_inc(&mut self.next_inlay_id),
5849 range.start,
5850 new_text.as_str(),
5851 );
5852 inlay_ids.push(inlay.id);
5853 inlays.push(inlay);
5854 }
5855
5856 self.splice_inlays(&[], inlays, cx);
5857 } else {
5858 let background_color = cx.theme().status().deleted_background;
5859 self.highlight_text::<InlineCompletionHighlight>(
5860 edits.iter().map(|(range, _)| range.clone()).collect(),
5861 HighlightStyle {
5862 background_color: Some(background_color),
5863 ..Default::default()
5864 },
5865 cx,
5866 );
5867 }
5868 }
5869
5870 invalidation_row_range = edit_start_row..edit_end_row;
5871
5872 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5873 if provider.show_tab_accept_marker() {
5874 EditDisplayMode::TabAccept
5875 } else {
5876 EditDisplayMode::Inline
5877 }
5878 } else {
5879 EditDisplayMode::DiffPopover
5880 };
5881
5882 InlineCompletion::Edit {
5883 edits,
5884 edit_preview: inline_completion.edit_preview,
5885 display_mode,
5886 snapshot,
5887 }
5888 };
5889
5890 let invalidation_range = multibuffer
5891 .anchor_before(Point::new(invalidation_row_range.start, 0))
5892 ..multibuffer.anchor_after(Point::new(
5893 invalidation_row_range.end,
5894 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5895 ));
5896
5897 self.stale_inline_completion_in_menu = None;
5898 self.active_inline_completion = Some(InlineCompletionState {
5899 inlay_ids,
5900 completion,
5901 completion_id: inline_completion.id,
5902 invalidation_range,
5903 });
5904
5905 cx.notify();
5906
5907 Some(())
5908 }
5909
5910 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5911 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5912 }
5913
5914 fn render_code_actions_indicator(
5915 &self,
5916 _style: &EditorStyle,
5917 row: DisplayRow,
5918 is_active: bool,
5919 breakpoint: Option<&(Anchor, Breakpoint)>,
5920 cx: &mut Context<Self>,
5921 ) -> Option<IconButton> {
5922 let color = Color::Muted;
5923
5924 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
5925 let bp_kind = Arc::new(
5926 breakpoint
5927 .map(|(_, bp)| bp.kind.clone())
5928 .unwrap_or(BreakpointKind::Standard),
5929 );
5930
5931 if self.available_code_actions.is_some() {
5932 Some(
5933 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5934 .shape(ui::IconButtonShape::Square)
5935 .icon_size(IconSize::XSmall)
5936 .icon_color(color)
5937 .toggle_state(is_active)
5938 .tooltip({
5939 let focus_handle = self.focus_handle.clone();
5940 move |window, cx| {
5941 Tooltip::for_action_in(
5942 "Toggle Code Actions",
5943 &ToggleCodeActions {
5944 deployed_from_indicator: None,
5945 },
5946 &focus_handle,
5947 window,
5948 cx,
5949 )
5950 }
5951 })
5952 .on_click(cx.listener(move |editor, _e, window, cx| {
5953 window.focus(&editor.focus_handle(cx));
5954 editor.toggle_code_actions(
5955 &ToggleCodeActions {
5956 deployed_from_indicator: Some(row),
5957 },
5958 window,
5959 cx,
5960 );
5961 }))
5962 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
5963 editor.set_breakpoint_context_menu(
5964 row,
5965 position,
5966 bp_kind.clone(),
5967 event.down.position,
5968 window,
5969 cx,
5970 );
5971 })),
5972 )
5973 } else {
5974 None
5975 }
5976 }
5977
5978 fn clear_tasks(&mut self) {
5979 self.tasks.clear()
5980 }
5981
5982 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5983 if self.tasks.insert(key, value).is_some() {
5984 // This case should hopefully be rare, but just in case...
5985 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5986 }
5987 }
5988
5989 /// Get all display points of breakpoints that will be rendered within editor
5990 ///
5991 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
5992 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
5993 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
5994 fn active_breakpoints(
5995 &mut self,
5996 range: Range<DisplayRow>,
5997 window: &mut Window,
5998 cx: &mut Context<Self>,
5999 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6000 let mut breakpoint_display_points = HashMap::default();
6001
6002 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6003 return breakpoint_display_points;
6004 };
6005
6006 let snapshot = self.snapshot(window, cx);
6007
6008 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6009 let Some(project) = self.project.as_ref() else {
6010 return breakpoint_display_points;
6011 };
6012
6013 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6014 let buffer_snapshot = buffer.read(cx).snapshot();
6015
6016 for breakpoint in
6017 breakpoint_store
6018 .read(cx)
6019 .breakpoints(&buffer, None, buffer_snapshot.clone(), cx)
6020 {
6021 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6022 let anchor = multi_buffer_snapshot.anchor_before(point);
6023 breakpoint_display_points.insert(
6024 snapshot
6025 .point_to_display_point(
6026 MultiBufferPoint {
6027 row: point.row,
6028 column: point.column,
6029 },
6030 Bias::Left,
6031 )
6032 .row(),
6033 (anchor, breakpoint.1.clone()),
6034 );
6035 }
6036
6037 return breakpoint_display_points;
6038 }
6039
6040 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6041 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6042 for excerpt_boundary in multi_buffer_snapshot.excerpt_boundaries_in_range(range) {
6043 let info = excerpt_boundary.next;
6044
6045 let Some(excerpt_ranges) = multi_buffer_snapshot.range_for_excerpt(info.id) else {
6046 continue;
6047 };
6048
6049 let Some(buffer) =
6050 project.read_with(cx, |this, cx| this.buffer_for_id(info.buffer_id, cx))
6051 else {
6052 continue;
6053 };
6054
6055 let breakpoints = breakpoint_store.read(cx).breakpoints(
6056 &buffer,
6057 Some(info.range.context.start..info.range.context.end),
6058 info.buffer.clone(),
6059 cx,
6060 );
6061
6062 // To translate a breakpoint's position within a singular buffer to a multi buffer
6063 // position we need to know it's excerpt starting location, it's position within
6064 // the singular buffer, and if that position is within the excerpt's range.
6065 let excerpt_head = excerpt_ranges
6066 .start
6067 .to_display_point(&snapshot.display_snapshot);
6068
6069 let buffer_start = info
6070 .buffer
6071 .summary_for_anchor::<Point>(&info.range.context.start);
6072
6073 for (anchor, breakpoint) in breakpoints {
6074 let as_row = info.buffer.summary_for_anchor::<Point>(&anchor).row;
6075 let delta = as_row - buffer_start.row;
6076
6077 let position = excerpt_head + DisplayPoint::new(DisplayRow(delta), 0);
6078
6079 let anchor = snapshot.display_point_to_anchor(position, Bias::Left);
6080
6081 breakpoint_display_points.insert(position.row(), (anchor, breakpoint.clone()));
6082 }
6083 }
6084
6085 breakpoint_display_points
6086 }
6087
6088 fn breakpoint_context_menu(
6089 &self,
6090 anchor: Anchor,
6091 kind: Arc<BreakpointKind>,
6092 window: &mut Window,
6093 cx: &mut Context<Self>,
6094 ) -> Entity<ui::ContextMenu> {
6095 let weak_editor = cx.weak_entity();
6096 let focus_handle = self.focus_handle(cx);
6097
6098 let second_entry_msg = if kind.log_message().is_some() {
6099 "Edit Log Breakpoint"
6100 } else {
6101 "Add Log Breakpoint"
6102 };
6103
6104 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6105 menu.on_blur_subscription(Subscription::new(|| {}))
6106 .context(focus_handle)
6107 .entry("Toggle Breakpoint", None, {
6108 let weak_editor = weak_editor.clone();
6109 move |_window, cx| {
6110 weak_editor
6111 .update(cx, |this, cx| {
6112 this.edit_breakpoint_at_anchor(
6113 anchor,
6114 BreakpointKind::Standard,
6115 BreakpointEditAction::Toggle,
6116 cx,
6117 );
6118 })
6119 .log_err();
6120 }
6121 })
6122 .entry(second_entry_msg, None, move |window, cx| {
6123 weak_editor
6124 .update(cx, |this, cx| {
6125 this.add_edit_breakpoint_block(anchor, kind.as_ref(), window, cx);
6126 })
6127 .log_err();
6128 })
6129 })
6130 }
6131
6132 fn render_breakpoint(
6133 &self,
6134 position: Anchor,
6135 row: DisplayRow,
6136 kind: &BreakpointKind,
6137 cx: &mut Context<Self>,
6138 ) -> IconButton {
6139 let color = if self
6140 .gutter_breakpoint_indicator
6141 .is_some_and(|gutter_bp| gutter_bp.row() == row)
6142 {
6143 Color::Hint
6144 } else {
6145 Color::Debugger
6146 };
6147
6148 let icon = match &kind {
6149 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6150 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6151 };
6152 let arc_kind = Arc::new(kind.clone());
6153 let arc_kind2 = arc_kind.clone();
6154
6155 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6156 .icon_size(IconSize::XSmall)
6157 .size(ui::ButtonSize::None)
6158 .icon_color(color)
6159 .style(ButtonStyle::Transparent)
6160 .on_click(cx.listener(move |editor, _e, window, cx| {
6161 window.focus(&editor.focus_handle(cx));
6162 editor.edit_breakpoint_at_anchor(
6163 position,
6164 arc_kind.as_ref().clone(),
6165 BreakpointEditAction::Toggle,
6166 cx,
6167 );
6168 }))
6169 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6170 editor.set_breakpoint_context_menu(
6171 row,
6172 Some(position),
6173 arc_kind2.clone(),
6174 event.down.position,
6175 window,
6176 cx,
6177 );
6178 }))
6179 }
6180
6181 fn build_tasks_context(
6182 project: &Entity<Project>,
6183 buffer: &Entity<Buffer>,
6184 buffer_row: u32,
6185 tasks: &Arc<RunnableTasks>,
6186 cx: &mut Context<Self>,
6187 ) -> Task<Option<task::TaskContext>> {
6188 let position = Point::new(buffer_row, tasks.column);
6189 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6190 let location = Location {
6191 buffer: buffer.clone(),
6192 range: range_start..range_start,
6193 };
6194 // Fill in the environmental variables from the tree-sitter captures
6195 let mut captured_task_variables = TaskVariables::default();
6196 for (capture_name, value) in tasks.extra_variables.clone() {
6197 captured_task_variables.insert(
6198 task::VariableName::Custom(capture_name.into()),
6199 value.clone(),
6200 );
6201 }
6202 project.update(cx, |project, cx| {
6203 project.task_store().update(cx, |task_store, cx| {
6204 task_store.task_context_for_location(captured_task_variables, location, cx)
6205 })
6206 })
6207 }
6208
6209 pub fn spawn_nearest_task(
6210 &mut self,
6211 action: &SpawnNearestTask,
6212 window: &mut Window,
6213 cx: &mut Context<Self>,
6214 ) {
6215 let Some((workspace, _)) = self.workspace.clone() else {
6216 return;
6217 };
6218 let Some(project) = self.project.clone() else {
6219 return;
6220 };
6221
6222 // Try to find a closest, enclosing node using tree-sitter that has a
6223 // task
6224 let Some((buffer, buffer_row, tasks)) = self
6225 .find_enclosing_node_task(cx)
6226 // Or find the task that's closest in row-distance.
6227 .or_else(|| self.find_closest_task(cx))
6228 else {
6229 return;
6230 };
6231
6232 let reveal_strategy = action.reveal;
6233 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6234 cx.spawn_in(window, async move |_, cx| {
6235 let context = task_context.await?;
6236 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6237
6238 let resolved = resolved_task.resolved.as_mut()?;
6239 resolved.reveal = reveal_strategy;
6240
6241 workspace
6242 .update(cx, |workspace, cx| {
6243 workspace::tasks::schedule_resolved_task(
6244 workspace,
6245 task_source_kind,
6246 resolved_task,
6247 false,
6248 cx,
6249 );
6250 })
6251 .ok()
6252 })
6253 .detach();
6254 }
6255
6256 fn find_closest_task(
6257 &mut self,
6258 cx: &mut Context<Self>,
6259 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6260 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6261
6262 let ((buffer_id, row), tasks) = self
6263 .tasks
6264 .iter()
6265 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6266
6267 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6268 let tasks = Arc::new(tasks.to_owned());
6269 Some((buffer, *row, tasks))
6270 }
6271
6272 fn find_enclosing_node_task(
6273 &mut self,
6274 cx: &mut Context<Self>,
6275 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6276 let snapshot = self.buffer.read(cx).snapshot(cx);
6277 let offset = self.selections.newest::<usize>(cx).head();
6278 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6279 let buffer_id = excerpt.buffer().remote_id();
6280
6281 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6282 let mut cursor = layer.node().walk();
6283
6284 while cursor.goto_first_child_for_byte(offset).is_some() {
6285 if cursor.node().end_byte() == offset {
6286 cursor.goto_next_sibling();
6287 }
6288 }
6289
6290 // Ascend to the smallest ancestor that contains the range and has a task.
6291 loop {
6292 let node = cursor.node();
6293 let node_range = node.byte_range();
6294 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6295
6296 // Check if this node contains our offset
6297 if node_range.start <= offset && node_range.end >= offset {
6298 // If it contains offset, check for task
6299 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6300 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6301 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6302 }
6303 }
6304
6305 if !cursor.goto_parent() {
6306 break;
6307 }
6308 }
6309 None
6310 }
6311
6312 fn render_run_indicator(
6313 &self,
6314 _style: &EditorStyle,
6315 is_active: bool,
6316 row: DisplayRow,
6317 breakpoint: Option<(Anchor, Breakpoint)>,
6318 cx: &mut Context<Self>,
6319 ) -> IconButton {
6320 let color = if breakpoint.is_some() {
6321 Color::Debugger
6322 } else {
6323 Color::Muted
6324 };
6325
6326 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6327 let bp_kind = Arc::new(
6328 breakpoint
6329 .map(|(_, bp)| bp.kind)
6330 .unwrap_or(BreakpointKind::Standard),
6331 );
6332
6333 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6334 .shape(ui::IconButtonShape::Square)
6335 .icon_size(IconSize::XSmall)
6336 .icon_color(color)
6337 .toggle_state(is_active)
6338 .on_click(cx.listener(move |editor, _e, window, cx| {
6339 window.focus(&editor.focus_handle(cx));
6340 editor.toggle_code_actions(
6341 &ToggleCodeActions {
6342 deployed_from_indicator: Some(row),
6343 },
6344 window,
6345 cx,
6346 );
6347 }))
6348 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6349 editor.set_breakpoint_context_menu(
6350 row,
6351 position,
6352 bp_kind.clone(),
6353 event.down.position,
6354 window,
6355 cx,
6356 );
6357 }))
6358 }
6359
6360 pub fn context_menu_visible(&self) -> bool {
6361 !self.edit_prediction_preview_is_active()
6362 && self
6363 .context_menu
6364 .borrow()
6365 .as_ref()
6366 .map_or(false, |menu| menu.visible())
6367 }
6368
6369 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6370 self.context_menu
6371 .borrow()
6372 .as_ref()
6373 .map(|menu| menu.origin())
6374 }
6375
6376 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6377 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6378
6379 fn render_edit_prediction_popover(
6380 &mut self,
6381 text_bounds: &Bounds<Pixels>,
6382 content_origin: gpui::Point<Pixels>,
6383 editor_snapshot: &EditorSnapshot,
6384 visible_row_range: Range<DisplayRow>,
6385 scroll_top: f32,
6386 scroll_bottom: f32,
6387 line_layouts: &[LineWithInvisibles],
6388 line_height: Pixels,
6389 scroll_pixel_position: gpui::Point<Pixels>,
6390 newest_selection_head: Option<DisplayPoint>,
6391 editor_width: Pixels,
6392 style: &EditorStyle,
6393 window: &mut Window,
6394 cx: &mut App,
6395 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6396 let active_inline_completion = self.active_inline_completion.as_ref()?;
6397
6398 if self.edit_prediction_visible_in_cursor_popover(true) {
6399 return None;
6400 }
6401
6402 match &active_inline_completion.completion {
6403 InlineCompletion::Move { target, .. } => {
6404 let target_display_point = target.to_display_point(editor_snapshot);
6405
6406 if self.edit_prediction_requires_modifier() {
6407 if !self.edit_prediction_preview_is_active() {
6408 return None;
6409 }
6410
6411 self.render_edit_prediction_modifier_jump_popover(
6412 text_bounds,
6413 content_origin,
6414 visible_row_range,
6415 line_layouts,
6416 line_height,
6417 scroll_pixel_position,
6418 newest_selection_head,
6419 target_display_point,
6420 window,
6421 cx,
6422 )
6423 } else {
6424 self.render_edit_prediction_eager_jump_popover(
6425 text_bounds,
6426 content_origin,
6427 editor_snapshot,
6428 visible_row_range,
6429 scroll_top,
6430 scroll_bottom,
6431 line_height,
6432 scroll_pixel_position,
6433 target_display_point,
6434 editor_width,
6435 window,
6436 cx,
6437 )
6438 }
6439 }
6440 InlineCompletion::Edit {
6441 display_mode: EditDisplayMode::Inline,
6442 ..
6443 } => None,
6444 InlineCompletion::Edit {
6445 display_mode: EditDisplayMode::TabAccept,
6446 edits,
6447 ..
6448 } => {
6449 let range = &edits.first()?.0;
6450 let target_display_point = range.end.to_display_point(editor_snapshot);
6451
6452 self.render_edit_prediction_end_of_line_popover(
6453 "Accept",
6454 editor_snapshot,
6455 visible_row_range,
6456 target_display_point,
6457 line_height,
6458 scroll_pixel_position,
6459 content_origin,
6460 editor_width,
6461 window,
6462 cx,
6463 )
6464 }
6465 InlineCompletion::Edit {
6466 edits,
6467 edit_preview,
6468 display_mode: EditDisplayMode::DiffPopover,
6469 snapshot,
6470 } => self.render_edit_prediction_diff_popover(
6471 text_bounds,
6472 content_origin,
6473 editor_snapshot,
6474 visible_row_range,
6475 line_layouts,
6476 line_height,
6477 scroll_pixel_position,
6478 newest_selection_head,
6479 editor_width,
6480 style,
6481 edits,
6482 edit_preview,
6483 snapshot,
6484 window,
6485 cx,
6486 ),
6487 }
6488 }
6489
6490 fn render_edit_prediction_modifier_jump_popover(
6491 &mut self,
6492 text_bounds: &Bounds<Pixels>,
6493 content_origin: gpui::Point<Pixels>,
6494 visible_row_range: Range<DisplayRow>,
6495 line_layouts: &[LineWithInvisibles],
6496 line_height: Pixels,
6497 scroll_pixel_position: gpui::Point<Pixels>,
6498 newest_selection_head: Option<DisplayPoint>,
6499 target_display_point: DisplayPoint,
6500 window: &mut Window,
6501 cx: &mut App,
6502 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6503 let scrolled_content_origin =
6504 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6505
6506 const SCROLL_PADDING_Y: Pixels = px(12.);
6507
6508 if target_display_point.row() < visible_row_range.start {
6509 return self.render_edit_prediction_scroll_popover(
6510 |_| SCROLL_PADDING_Y,
6511 IconName::ArrowUp,
6512 visible_row_range,
6513 line_layouts,
6514 newest_selection_head,
6515 scrolled_content_origin,
6516 window,
6517 cx,
6518 );
6519 } else if target_display_point.row() >= visible_row_range.end {
6520 return self.render_edit_prediction_scroll_popover(
6521 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6522 IconName::ArrowDown,
6523 visible_row_range,
6524 line_layouts,
6525 newest_selection_head,
6526 scrolled_content_origin,
6527 window,
6528 cx,
6529 );
6530 }
6531
6532 const POLE_WIDTH: Pixels = px(2.);
6533
6534 let line_layout =
6535 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6536 let target_column = target_display_point.column() as usize;
6537
6538 let target_x = line_layout.x_for_index(target_column);
6539 let target_y =
6540 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6541
6542 let flag_on_right = target_x < text_bounds.size.width / 2.;
6543
6544 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6545 border_color.l += 0.001;
6546
6547 let mut element = v_flex()
6548 .items_end()
6549 .when(flag_on_right, |el| el.items_start())
6550 .child(if flag_on_right {
6551 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6552 .rounded_bl(px(0.))
6553 .rounded_tl(px(0.))
6554 .border_l_2()
6555 .border_color(border_color)
6556 } else {
6557 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6558 .rounded_br(px(0.))
6559 .rounded_tr(px(0.))
6560 .border_r_2()
6561 .border_color(border_color)
6562 })
6563 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6564 .into_any();
6565
6566 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6567
6568 let mut origin = scrolled_content_origin + point(target_x, target_y)
6569 - point(
6570 if flag_on_right {
6571 POLE_WIDTH
6572 } else {
6573 size.width - POLE_WIDTH
6574 },
6575 size.height - line_height,
6576 );
6577
6578 origin.x = origin.x.max(content_origin.x);
6579
6580 element.prepaint_at(origin, window, cx);
6581
6582 Some((element, origin))
6583 }
6584
6585 fn render_edit_prediction_scroll_popover(
6586 &mut self,
6587 to_y: impl Fn(Size<Pixels>) -> Pixels,
6588 scroll_icon: IconName,
6589 visible_row_range: Range<DisplayRow>,
6590 line_layouts: &[LineWithInvisibles],
6591 newest_selection_head: Option<DisplayPoint>,
6592 scrolled_content_origin: gpui::Point<Pixels>,
6593 window: &mut Window,
6594 cx: &mut App,
6595 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6596 let mut element = self
6597 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6598 .into_any();
6599
6600 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6601
6602 let cursor = newest_selection_head?;
6603 let cursor_row_layout =
6604 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6605 let cursor_column = cursor.column() as usize;
6606
6607 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6608
6609 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6610
6611 element.prepaint_at(origin, window, cx);
6612 Some((element, origin))
6613 }
6614
6615 fn render_edit_prediction_eager_jump_popover(
6616 &mut self,
6617 text_bounds: &Bounds<Pixels>,
6618 content_origin: gpui::Point<Pixels>,
6619 editor_snapshot: &EditorSnapshot,
6620 visible_row_range: Range<DisplayRow>,
6621 scroll_top: f32,
6622 scroll_bottom: f32,
6623 line_height: Pixels,
6624 scroll_pixel_position: gpui::Point<Pixels>,
6625 target_display_point: DisplayPoint,
6626 editor_width: Pixels,
6627 window: &mut Window,
6628 cx: &mut App,
6629 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6630 if target_display_point.row().as_f32() < scroll_top {
6631 let mut element = self
6632 .render_edit_prediction_line_popover(
6633 "Jump to Edit",
6634 Some(IconName::ArrowUp),
6635 window,
6636 cx,
6637 )?
6638 .into_any();
6639
6640 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6641 let offset = point(
6642 (text_bounds.size.width - size.width) / 2.,
6643 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6644 );
6645
6646 let origin = text_bounds.origin + offset;
6647 element.prepaint_at(origin, window, cx);
6648 Some((element, origin))
6649 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6650 let mut element = self
6651 .render_edit_prediction_line_popover(
6652 "Jump to Edit",
6653 Some(IconName::ArrowDown),
6654 window,
6655 cx,
6656 )?
6657 .into_any();
6658
6659 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6660 let offset = point(
6661 (text_bounds.size.width - size.width) / 2.,
6662 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6663 );
6664
6665 let origin = text_bounds.origin + offset;
6666 element.prepaint_at(origin, window, cx);
6667 Some((element, origin))
6668 } else {
6669 self.render_edit_prediction_end_of_line_popover(
6670 "Jump to Edit",
6671 editor_snapshot,
6672 visible_row_range,
6673 target_display_point,
6674 line_height,
6675 scroll_pixel_position,
6676 content_origin,
6677 editor_width,
6678 window,
6679 cx,
6680 )
6681 }
6682 }
6683
6684 fn render_edit_prediction_end_of_line_popover(
6685 self: &mut Editor,
6686 label: &'static str,
6687 editor_snapshot: &EditorSnapshot,
6688 visible_row_range: Range<DisplayRow>,
6689 target_display_point: DisplayPoint,
6690 line_height: Pixels,
6691 scroll_pixel_position: gpui::Point<Pixels>,
6692 content_origin: gpui::Point<Pixels>,
6693 editor_width: Pixels,
6694 window: &mut Window,
6695 cx: &mut App,
6696 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6697 let target_line_end = DisplayPoint::new(
6698 target_display_point.row(),
6699 editor_snapshot.line_len(target_display_point.row()),
6700 );
6701
6702 let mut element = self
6703 .render_edit_prediction_line_popover(label, None, window, cx)?
6704 .into_any();
6705
6706 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6707
6708 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6709
6710 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6711 let mut origin = start_point
6712 + line_origin
6713 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6714 origin.x = origin.x.max(content_origin.x);
6715
6716 let max_x = content_origin.x + editor_width - size.width;
6717
6718 if origin.x > max_x {
6719 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6720
6721 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6722 origin.y += offset;
6723 IconName::ArrowUp
6724 } else {
6725 origin.y -= offset;
6726 IconName::ArrowDown
6727 };
6728
6729 element = self
6730 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6731 .into_any();
6732
6733 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6734
6735 origin.x = content_origin.x + editor_width - size.width - px(2.);
6736 }
6737
6738 element.prepaint_at(origin, window, cx);
6739 Some((element, origin))
6740 }
6741
6742 fn render_edit_prediction_diff_popover(
6743 self: &Editor,
6744 text_bounds: &Bounds<Pixels>,
6745 content_origin: gpui::Point<Pixels>,
6746 editor_snapshot: &EditorSnapshot,
6747 visible_row_range: Range<DisplayRow>,
6748 line_layouts: &[LineWithInvisibles],
6749 line_height: Pixels,
6750 scroll_pixel_position: gpui::Point<Pixels>,
6751 newest_selection_head: Option<DisplayPoint>,
6752 editor_width: Pixels,
6753 style: &EditorStyle,
6754 edits: &Vec<(Range<Anchor>, String)>,
6755 edit_preview: &Option<language::EditPreview>,
6756 snapshot: &language::BufferSnapshot,
6757 window: &mut Window,
6758 cx: &mut App,
6759 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6760 let edit_start = edits
6761 .first()
6762 .unwrap()
6763 .0
6764 .start
6765 .to_display_point(editor_snapshot);
6766 let edit_end = edits
6767 .last()
6768 .unwrap()
6769 .0
6770 .end
6771 .to_display_point(editor_snapshot);
6772
6773 let is_visible = visible_row_range.contains(&edit_start.row())
6774 || visible_row_range.contains(&edit_end.row());
6775 if !is_visible {
6776 return None;
6777 }
6778
6779 let highlighted_edits =
6780 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6781
6782 let styled_text = highlighted_edits.to_styled_text(&style.text);
6783 let line_count = highlighted_edits.text.lines().count();
6784
6785 const BORDER_WIDTH: Pixels = px(1.);
6786
6787 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6788 let has_keybind = keybind.is_some();
6789
6790 let mut element = h_flex()
6791 .items_start()
6792 .child(
6793 h_flex()
6794 .bg(cx.theme().colors().editor_background)
6795 .border(BORDER_WIDTH)
6796 .shadow_sm()
6797 .border_color(cx.theme().colors().border)
6798 .rounded_l_lg()
6799 .when(line_count > 1, |el| el.rounded_br_lg())
6800 .pr_1()
6801 .child(styled_text),
6802 )
6803 .child(
6804 h_flex()
6805 .h(line_height + BORDER_WIDTH * px(2.))
6806 .px_1p5()
6807 .gap_1()
6808 // Workaround: For some reason, there's a gap if we don't do this
6809 .ml(-BORDER_WIDTH)
6810 .shadow(smallvec![gpui::BoxShadow {
6811 color: gpui::black().opacity(0.05),
6812 offset: point(px(1.), px(1.)),
6813 blur_radius: px(2.),
6814 spread_radius: px(0.),
6815 }])
6816 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6817 .border(BORDER_WIDTH)
6818 .border_color(cx.theme().colors().border)
6819 .rounded_r_lg()
6820 .id("edit_prediction_diff_popover_keybind")
6821 .when(!has_keybind, |el| {
6822 let status_colors = cx.theme().status();
6823
6824 el.bg(status_colors.error_background)
6825 .border_color(status_colors.error.opacity(0.6))
6826 .child(Icon::new(IconName::Info).color(Color::Error))
6827 .cursor_default()
6828 .hoverable_tooltip(move |_window, cx| {
6829 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6830 })
6831 })
6832 .children(keybind),
6833 )
6834 .into_any();
6835
6836 let longest_row =
6837 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6838 let longest_line_width = if visible_row_range.contains(&longest_row) {
6839 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6840 } else {
6841 layout_line(
6842 longest_row,
6843 editor_snapshot,
6844 style,
6845 editor_width,
6846 |_| false,
6847 window,
6848 cx,
6849 )
6850 .width
6851 };
6852
6853 let viewport_bounds =
6854 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6855 right: -EditorElement::SCROLLBAR_WIDTH,
6856 ..Default::default()
6857 });
6858
6859 let x_after_longest =
6860 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6861 - scroll_pixel_position.x;
6862
6863 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6864
6865 // Fully visible if it can be displayed within the window (allow overlapping other
6866 // panes). However, this is only allowed if the popover starts within text_bounds.
6867 let can_position_to_the_right = x_after_longest < text_bounds.right()
6868 && x_after_longest + element_bounds.width < viewport_bounds.right();
6869
6870 let mut origin = if can_position_to_the_right {
6871 point(
6872 x_after_longest,
6873 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6874 - scroll_pixel_position.y,
6875 )
6876 } else {
6877 let cursor_row = newest_selection_head.map(|head| head.row());
6878 let above_edit = edit_start
6879 .row()
6880 .0
6881 .checked_sub(line_count as u32)
6882 .map(DisplayRow);
6883 let below_edit = Some(edit_end.row() + 1);
6884 let above_cursor =
6885 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6886 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6887
6888 // Place the edit popover adjacent to the edit if there is a location
6889 // available that is onscreen and does not obscure the cursor. Otherwise,
6890 // place it adjacent to the cursor.
6891 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6892 .into_iter()
6893 .flatten()
6894 .find(|&start_row| {
6895 let end_row = start_row + line_count as u32;
6896 visible_row_range.contains(&start_row)
6897 && visible_row_range.contains(&end_row)
6898 && cursor_row.map_or(true, |cursor_row| {
6899 !((start_row..end_row).contains(&cursor_row))
6900 })
6901 })?;
6902
6903 content_origin
6904 + point(
6905 -scroll_pixel_position.x,
6906 row_target.as_f32() * line_height - scroll_pixel_position.y,
6907 )
6908 };
6909
6910 origin.x -= BORDER_WIDTH;
6911
6912 window.defer_draw(element, origin, 1);
6913
6914 // Do not return an element, since it will already be drawn due to defer_draw.
6915 None
6916 }
6917
6918 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6919 px(30.)
6920 }
6921
6922 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6923 if self.read_only(cx) {
6924 cx.theme().players().read_only()
6925 } else {
6926 self.style.as_ref().unwrap().local_player
6927 }
6928 }
6929
6930 fn render_edit_prediction_accept_keybind(
6931 &self,
6932 window: &mut Window,
6933 cx: &App,
6934 ) -> Option<AnyElement> {
6935 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6936 let accept_keystroke = accept_binding.keystroke()?;
6937
6938 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6939
6940 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6941 Color::Accent
6942 } else {
6943 Color::Muted
6944 };
6945
6946 h_flex()
6947 .px_0p5()
6948 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6949 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6950 .text_size(TextSize::XSmall.rems(cx))
6951 .child(h_flex().children(ui::render_modifiers(
6952 &accept_keystroke.modifiers,
6953 PlatformStyle::platform(),
6954 Some(modifiers_color),
6955 Some(IconSize::XSmall.rems().into()),
6956 true,
6957 )))
6958 .when(is_platform_style_mac, |parent| {
6959 parent.child(accept_keystroke.key.clone())
6960 })
6961 .when(!is_platform_style_mac, |parent| {
6962 parent.child(
6963 Key::new(
6964 util::capitalize(&accept_keystroke.key),
6965 Some(Color::Default),
6966 )
6967 .size(Some(IconSize::XSmall.rems().into())),
6968 )
6969 })
6970 .into_any()
6971 .into()
6972 }
6973
6974 fn render_edit_prediction_line_popover(
6975 &self,
6976 label: impl Into<SharedString>,
6977 icon: Option<IconName>,
6978 window: &mut Window,
6979 cx: &App,
6980 ) -> Option<Stateful<Div>> {
6981 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6982
6983 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6984 let has_keybind = keybind.is_some();
6985
6986 let result = h_flex()
6987 .id("ep-line-popover")
6988 .py_0p5()
6989 .pl_1()
6990 .pr(padding_right)
6991 .gap_1()
6992 .rounded_md()
6993 .border_1()
6994 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6995 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6996 .shadow_sm()
6997 .when(!has_keybind, |el| {
6998 let status_colors = cx.theme().status();
6999
7000 el.bg(status_colors.error_background)
7001 .border_color(status_colors.error.opacity(0.6))
7002 .pl_2()
7003 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7004 .cursor_default()
7005 .hoverable_tooltip(move |_window, cx| {
7006 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7007 })
7008 })
7009 .children(keybind)
7010 .child(
7011 Label::new(label)
7012 .size(LabelSize::Small)
7013 .when(!has_keybind, |el| {
7014 el.color(cx.theme().status().error.into()).strikethrough()
7015 }),
7016 )
7017 .when(!has_keybind, |el| {
7018 el.child(
7019 h_flex().ml_1().child(
7020 Icon::new(IconName::Info)
7021 .size(IconSize::Small)
7022 .color(cx.theme().status().error.into()),
7023 ),
7024 )
7025 })
7026 .when_some(icon, |element, icon| {
7027 element.child(
7028 div()
7029 .mt(px(1.5))
7030 .child(Icon::new(icon).size(IconSize::Small)),
7031 )
7032 });
7033
7034 Some(result)
7035 }
7036
7037 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7038 let accent_color = cx.theme().colors().text_accent;
7039 let editor_bg_color = cx.theme().colors().editor_background;
7040 editor_bg_color.blend(accent_color.opacity(0.1))
7041 }
7042
7043 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7044 let accent_color = cx.theme().colors().text_accent;
7045 let editor_bg_color = cx.theme().colors().editor_background;
7046 editor_bg_color.blend(accent_color.opacity(0.6))
7047 }
7048
7049 fn render_edit_prediction_cursor_popover(
7050 &self,
7051 min_width: Pixels,
7052 max_width: Pixels,
7053 cursor_point: Point,
7054 style: &EditorStyle,
7055 accept_keystroke: Option<&gpui::Keystroke>,
7056 _window: &Window,
7057 cx: &mut Context<Editor>,
7058 ) -> Option<AnyElement> {
7059 let provider = self.edit_prediction_provider.as_ref()?;
7060
7061 if provider.provider.needs_terms_acceptance(cx) {
7062 return Some(
7063 h_flex()
7064 .min_w(min_width)
7065 .flex_1()
7066 .px_2()
7067 .py_1()
7068 .gap_3()
7069 .elevation_2(cx)
7070 .hover(|style| style.bg(cx.theme().colors().element_hover))
7071 .id("accept-terms")
7072 .cursor_pointer()
7073 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7074 .on_click(cx.listener(|this, _event, window, cx| {
7075 cx.stop_propagation();
7076 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7077 window.dispatch_action(
7078 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7079 cx,
7080 );
7081 }))
7082 .child(
7083 h_flex()
7084 .flex_1()
7085 .gap_2()
7086 .child(Icon::new(IconName::ZedPredict))
7087 .child(Label::new("Accept Terms of Service"))
7088 .child(div().w_full())
7089 .child(
7090 Icon::new(IconName::ArrowUpRight)
7091 .color(Color::Muted)
7092 .size(IconSize::Small),
7093 )
7094 .into_any_element(),
7095 )
7096 .into_any(),
7097 );
7098 }
7099
7100 let is_refreshing = provider.provider.is_refreshing(cx);
7101
7102 fn pending_completion_container() -> Div {
7103 h_flex()
7104 .h_full()
7105 .flex_1()
7106 .gap_2()
7107 .child(Icon::new(IconName::ZedPredict))
7108 }
7109
7110 let completion = match &self.active_inline_completion {
7111 Some(prediction) => {
7112 if !self.has_visible_completions_menu() {
7113 const RADIUS: Pixels = px(6.);
7114 const BORDER_WIDTH: Pixels = px(1.);
7115
7116 return Some(
7117 h_flex()
7118 .elevation_2(cx)
7119 .border(BORDER_WIDTH)
7120 .border_color(cx.theme().colors().border)
7121 .when(accept_keystroke.is_none(), |el| {
7122 el.border_color(cx.theme().status().error)
7123 })
7124 .rounded(RADIUS)
7125 .rounded_tl(px(0.))
7126 .overflow_hidden()
7127 .child(div().px_1p5().child(match &prediction.completion {
7128 InlineCompletion::Move { target, snapshot } => {
7129 use text::ToPoint as _;
7130 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7131 {
7132 Icon::new(IconName::ZedPredictDown)
7133 } else {
7134 Icon::new(IconName::ZedPredictUp)
7135 }
7136 }
7137 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7138 }))
7139 .child(
7140 h_flex()
7141 .gap_1()
7142 .py_1()
7143 .px_2()
7144 .rounded_r(RADIUS - BORDER_WIDTH)
7145 .border_l_1()
7146 .border_color(cx.theme().colors().border)
7147 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7148 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7149 el.child(
7150 Label::new("Hold")
7151 .size(LabelSize::Small)
7152 .when(accept_keystroke.is_none(), |el| {
7153 el.strikethrough()
7154 })
7155 .line_height_style(LineHeightStyle::UiLabel),
7156 )
7157 })
7158 .id("edit_prediction_cursor_popover_keybind")
7159 .when(accept_keystroke.is_none(), |el| {
7160 let status_colors = cx.theme().status();
7161
7162 el.bg(status_colors.error_background)
7163 .border_color(status_colors.error.opacity(0.6))
7164 .child(Icon::new(IconName::Info).color(Color::Error))
7165 .cursor_default()
7166 .hoverable_tooltip(move |_window, cx| {
7167 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7168 .into()
7169 })
7170 })
7171 .when_some(
7172 accept_keystroke.as_ref(),
7173 |el, accept_keystroke| {
7174 el.child(h_flex().children(ui::render_modifiers(
7175 &accept_keystroke.modifiers,
7176 PlatformStyle::platform(),
7177 Some(Color::Default),
7178 Some(IconSize::XSmall.rems().into()),
7179 false,
7180 )))
7181 },
7182 ),
7183 )
7184 .into_any(),
7185 );
7186 }
7187
7188 self.render_edit_prediction_cursor_popover_preview(
7189 prediction,
7190 cursor_point,
7191 style,
7192 cx,
7193 )?
7194 }
7195
7196 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7197 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7198 stale_completion,
7199 cursor_point,
7200 style,
7201 cx,
7202 )?,
7203
7204 None => {
7205 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7206 }
7207 },
7208
7209 None => pending_completion_container().child(Label::new("No Prediction")),
7210 };
7211
7212 let completion = if is_refreshing {
7213 completion
7214 .with_animation(
7215 "loading-completion",
7216 Animation::new(Duration::from_secs(2))
7217 .repeat()
7218 .with_easing(pulsating_between(0.4, 0.8)),
7219 |label, delta| label.opacity(delta),
7220 )
7221 .into_any_element()
7222 } else {
7223 completion.into_any_element()
7224 };
7225
7226 let has_completion = self.active_inline_completion.is_some();
7227
7228 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7229 Some(
7230 h_flex()
7231 .min_w(min_width)
7232 .max_w(max_width)
7233 .flex_1()
7234 .elevation_2(cx)
7235 .border_color(cx.theme().colors().border)
7236 .child(
7237 div()
7238 .flex_1()
7239 .py_1()
7240 .px_2()
7241 .overflow_hidden()
7242 .child(completion),
7243 )
7244 .when_some(accept_keystroke, |el, accept_keystroke| {
7245 if !accept_keystroke.modifiers.modified() {
7246 return el;
7247 }
7248
7249 el.child(
7250 h_flex()
7251 .h_full()
7252 .border_l_1()
7253 .rounded_r_lg()
7254 .border_color(cx.theme().colors().border)
7255 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7256 .gap_1()
7257 .py_1()
7258 .px_2()
7259 .child(
7260 h_flex()
7261 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7262 .when(is_platform_style_mac, |parent| parent.gap_1())
7263 .child(h_flex().children(ui::render_modifiers(
7264 &accept_keystroke.modifiers,
7265 PlatformStyle::platform(),
7266 Some(if !has_completion {
7267 Color::Muted
7268 } else {
7269 Color::Default
7270 }),
7271 None,
7272 false,
7273 ))),
7274 )
7275 .child(Label::new("Preview").into_any_element())
7276 .opacity(if has_completion { 1.0 } else { 0.4 }),
7277 )
7278 })
7279 .into_any(),
7280 )
7281 }
7282
7283 fn render_edit_prediction_cursor_popover_preview(
7284 &self,
7285 completion: &InlineCompletionState,
7286 cursor_point: Point,
7287 style: &EditorStyle,
7288 cx: &mut Context<Editor>,
7289 ) -> Option<Div> {
7290 use text::ToPoint as _;
7291
7292 fn render_relative_row_jump(
7293 prefix: impl Into<String>,
7294 current_row: u32,
7295 target_row: u32,
7296 ) -> Div {
7297 let (row_diff, arrow) = if target_row < current_row {
7298 (current_row - target_row, IconName::ArrowUp)
7299 } else {
7300 (target_row - current_row, IconName::ArrowDown)
7301 };
7302
7303 h_flex()
7304 .child(
7305 Label::new(format!("{}{}", prefix.into(), row_diff))
7306 .color(Color::Muted)
7307 .size(LabelSize::Small),
7308 )
7309 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7310 }
7311
7312 match &completion.completion {
7313 InlineCompletion::Move {
7314 target, snapshot, ..
7315 } => Some(
7316 h_flex()
7317 .px_2()
7318 .gap_2()
7319 .flex_1()
7320 .child(
7321 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7322 Icon::new(IconName::ZedPredictDown)
7323 } else {
7324 Icon::new(IconName::ZedPredictUp)
7325 },
7326 )
7327 .child(Label::new("Jump to Edit")),
7328 ),
7329
7330 InlineCompletion::Edit {
7331 edits,
7332 edit_preview,
7333 snapshot,
7334 display_mode: _,
7335 } => {
7336 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7337
7338 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7339 &snapshot,
7340 &edits,
7341 edit_preview.as_ref()?,
7342 true,
7343 cx,
7344 )
7345 .first_line_preview();
7346
7347 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7348 .with_default_highlights(&style.text, highlighted_edits.highlights);
7349
7350 let preview = h_flex()
7351 .gap_1()
7352 .min_w_16()
7353 .child(styled_text)
7354 .when(has_more_lines, |parent| parent.child("…"));
7355
7356 let left = if first_edit_row != cursor_point.row {
7357 render_relative_row_jump("", cursor_point.row, first_edit_row)
7358 .into_any_element()
7359 } else {
7360 Icon::new(IconName::ZedPredict).into_any_element()
7361 };
7362
7363 Some(
7364 h_flex()
7365 .h_full()
7366 .flex_1()
7367 .gap_2()
7368 .pr_1()
7369 .overflow_x_hidden()
7370 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7371 .child(left)
7372 .child(preview),
7373 )
7374 }
7375 }
7376 }
7377
7378 fn render_context_menu(
7379 &self,
7380 style: &EditorStyle,
7381 max_height_in_lines: u32,
7382 y_flipped: bool,
7383 window: &mut Window,
7384 cx: &mut Context<Editor>,
7385 ) -> Option<AnyElement> {
7386 let menu = self.context_menu.borrow();
7387 let menu = menu.as_ref()?;
7388 if !menu.visible() {
7389 return None;
7390 };
7391 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7392 }
7393
7394 fn render_context_menu_aside(
7395 &mut self,
7396 max_size: Size<Pixels>,
7397 window: &mut Window,
7398 cx: &mut Context<Editor>,
7399 ) -> Option<AnyElement> {
7400 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7401 if menu.visible() {
7402 menu.render_aside(self, max_size, window, cx)
7403 } else {
7404 None
7405 }
7406 })
7407 }
7408
7409 fn hide_context_menu(
7410 &mut self,
7411 window: &mut Window,
7412 cx: &mut Context<Self>,
7413 ) -> Option<CodeContextMenu> {
7414 cx.notify();
7415 self.completion_tasks.clear();
7416 let context_menu = self.context_menu.borrow_mut().take();
7417 self.stale_inline_completion_in_menu.take();
7418 self.update_visible_inline_completion(window, cx);
7419 context_menu
7420 }
7421
7422 fn show_snippet_choices(
7423 &mut self,
7424 choices: &Vec<String>,
7425 selection: Range<Anchor>,
7426 cx: &mut Context<Self>,
7427 ) {
7428 if selection.start.buffer_id.is_none() {
7429 return;
7430 }
7431 let buffer_id = selection.start.buffer_id.unwrap();
7432 let buffer = self.buffer().read(cx).buffer(buffer_id);
7433 let id = post_inc(&mut self.next_completion_id);
7434
7435 if let Some(buffer) = buffer {
7436 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7437 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7438 ));
7439 }
7440 }
7441
7442 pub fn insert_snippet(
7443 &mut self,
7444 insertion_ranges: &[Range<usize>],
7445 snippet: Snippet,
7446 window: &mut Window,
7447 cx: &mut Context<Self>,
7448 ) -> Result<()> {
7449 struct Tabstop<T> {
7450 is_end_tabstop: bool,
7451 ranges: Vec<Range<T>>,
7452 choices: Option<Vec<String>>,
7453 }
7454
7455 let tabstops = self.buffer.update(cx, |buffer, cx| {
7456 let snippet_text: Arc<str> = snippet.text.clone().into();
7457 buffer.edit(
7458 insertion_ranges
7459 .iter()
7460 .cloned()
7461 .map(|range| (range, snippet_text.clone())),
7462 Some(AutoindentMode::EachLine),
7463 cx,
7464 );
7465
7466 let snapshot = &*buffer.read(cx);
7467 let snippet = &snippet;
7468 snippet
7469 .tabstops
7470 .iter()
7471 .map(|tabstop| {
7472 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7473 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7474 });
7475 let mut tabstop_ranges = tabstop
7476 .ranges
7477 .iter()
7478 .flat_map(|tabstop_range| {
7479 let mut delta = 0_isize;
7480 insertion_ranges.iter().map(move |insertion_range| {
7481 let insertion_start = insertion_range.start as isize + delta;
7482 delta +=
7483 snippet.text.len() as isize - insertion_range.len() as isize;
7484
7485 let start = ((insertion_start + tabstop_range.start) as usize)
7486 .min(snapshot.len());
7487 let end = ((insertion_start + tabstop_range.end) as usize)
7488 .min(snapshot.len());
7489 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7490 })
7491 })
7492 .collect::<Vec<_>>();
7493 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7494
7495 Tabstop {
7496 is_end_tabstop,
7497 ranges: tabstop_ranges,
7498 choices: tabstop.choices.clone(),
7499 }
7500 })
7501 .collect::<Vec<_>>()
7502 });
7503 if let Some(tabstop) = tabstops.first() {
7504 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7505 s.select_ranges(tabstop.ranges.iter().cloned());
7506 });
7507
7508 if let Some(choices) = &tabstop.choices {
7509 if let Some(selection) = tabstop.ranges.first() {
7510 self.show_snippet_choices(choices, selection.clone(), cx)
7511 }
7512 }
7513
7514 // If we're already at the last tabstop and it's at the end of the snippet,
7515 // we're done, we don't need to keep the state around.
7516 if !tabstop.is_end_tabstop {
7517 let choices = tabstops
7518 .iter()
7519 .map(|tabstop| tabstop.choices.clone())
7520 .collect();
7521
7522 let ranges = tabstops
7523 .into_iter()
7524 .map(|tabstop| tabstop.ranges)
7525 .collect::<Vec<_>>();
7526
7527 self.snippet_stack.push(SnippetState {
7528 active_index: 0,
7529 ranges,
7530 choices,
7531 });
7532 }
7533
7534 // Check whether the just-entered snippet ends with an auto-closable bracket.
7535 if self.autoclose_regions.is_empty() {
7536 let snapshot = self.buffer.read(cx).snapshot(cx);
7537 for selection in &mut self.selections.all::<Point>(cx) {
7538 let selection_head = selection.head();
7539 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7540 continue;
7541 };
7542
7543 let mut bracket_pair = None;
7544 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7545 let prev_chars = snapshot
7546 .reversed_chars_at(selection_head)
7547 .collect::<String>();
7548 for (pair, enabled) in scope.brackets() {
7549 if enabled
7550 && pair.close
7551 && prev_chars.starts_with(pair.start.as_str())
7552 && next_chars.starts_with(pair.end.as_str())
7553 {
7554 bracket_pair = Some(pair.clone());
7555 break;
7556 }
7557 }
7558 if let Some(pair) = bracket_pair {
7559 let start = snapshot.anchor_after(selection_head);
7560 let end = snapshot.anchor_after(selection_head);
7561 self.autoclose_regions.push(AutocloseRegion {
7562 selection_id: selection.id,
7563 range: start..end,
7564 pair,
7565 });
7566 }
7567 }
7568 }
7569 }
7570 Ok(())
7571 }
7572
7573 pub fn move_to_next_snippet_tabstop(
7574 &mut self,
7575 window: &mut Window,
7576 cx: &mut Context<Self>,
7577 ) -> bool {
7578 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7579 }
7580
7581 pub fn move_to_prev_snippet_tabstop(
7582 &mut self,
7583 window: &mut Window,
7584 cx: &mut Context<Self>,
7585 ) -> bool {
7586 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7587 }
7588
7589 pub fn move_to_snippet_tabstop(
7590 &mut self,
7591 bias: Bias,
7592 window: &mut Window,
7593 cx: &mut Context<Self>,
7594 ) -> bool {
7595 if let Some(mut snippet) = self.snippet_stack.pop() {
7596 match bias {
7597 Bias::Left => {
7598 if snippet.active_index > 0 {
7599 snippet.active_index -= 1;
7600 } else {
7601 self.snippet_stack.push(snippet);
7602 return false;
7603 }
7604 }
7605 Bias::Right => {
7606 if snippet.active_index + 1 < snippet.ranges.len() {
7607 snippet.active_index += 1;
7608 } else {
7609 self.snippet_stack.push(snippet);
7610 return false;
7611 }
7612 }
7613 }
7614 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7615 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7616 s.select_anchor_ranges(current_ranges.iter().cloned())
7617 });
7618
7619 if let Some(choices) = &snippet.choices[snippet.active_index] {
7620 if let Some(selection) = current_ranges.first() {
7621 self.show_snippet_choices(&choices, selection.clone(), cx);
7622 }
7623 }
7624
7625 // If snippet state is not at the last tabstop, push it back on the stack
7626 if snippet.active_index + 1 < snippet.ranges.len() {
7627 self.snippet_stack.push(snippet);
7628 }
7629 return true;
7630 }
7631 }
7632
7633 false
7634 }
7635
7636 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7637 self.transact(window, cx, |this, window, cx| {
7638 this.select_all(&SelectAll, window, cx);
7639 this.insert("", window, cx);
7640 });
7641 }
7642
7643 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7644 self.transact(window, cx, |this, window, cx| {
7645 this.select_autoclose_pair(window, cx);
7646 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7647 if !this.linked_edit_ranges.is_empty() {
7648 let selections = this.selections.all::<MultiBufferPoint>(cx);
7649 let snapshot = this.buffer.read(cx).snapshot(cx);
7650
7651 for selection in selections.iter() {
7652 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7653 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7654 if selection_start.buffer_id != selection_end.buffer_id {
7655 continue;
7656 }
7657 if let Some(ranges) =
7658 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7659 {
7660 for (buffer, entries) in ranges {
7661 linked_ranges.entry(buffer).or_default().extend(entries);
7662 }
7663 }
7664 }
7665 }
7666
7667 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7668 if !this.selections.line_mode {
7669 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7670 for selection in &mut selections {
7671 if selection.is_empty() {
7672 let old_head = selection.head();
7673 let mut new_head =
7674 movement::left(&display_map, old_head.to_display_point(&display_map))
7675 .to_point(&display_map);
7676 if let Some((buffer, line_buffer_range)) = display_map
7677 .buffer_snapshot
7678 .buffer_line_for_row(MultiBufferRow(old_head.row))
7679 {
7680 let indent_size =
7681 buffer.indent_size_for_line(line_buffer_range.start.row);
7682 let indent_len = match indent_size.kind {
7683 IndentKind::Space => {
7684 buffer.settings_at(line_buffer_range.start, cx).tab_size
7685 }
7686 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7687 };
7688 if old_head.column <= indent_size.len && old_head.column > 0 {
7689 let indent_len = indent_len.get();
7690 new_head = cmp::min(
7691 new_head,
7692 MultiBufferPoint::new(
7693 old_head.row,
7694 ((old_head.column - 1) / indent_len) * indent_len,
7695 ),
7696 );
7697 }
7698 }
7699
7700 selection.set_head(new_head, SelectionGoal::None);
7701 }
7702 }
7703 }
7704
7705 this.signature_help_state.set_backspace_pressed(true);
7706 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7707 s.select(selections)
7708 });
7709 this.insert("", window, cx);
7710 let empty_str: Arc<str> = Arc::from("");
7711 for (buffer, edits) in linked_ranges {
7712 let snapshot = buffer.read(cx).snapshot();
7713 use text::ToPoint as TP;
7714
7715 let edits = edits
7716 .into_iter()
7717 .map(|range| {
7718 let end_point = TP::to_point(&range.end, &snapshot);
7719 let mut start_point = TP::to_point(&range.start, &snapshot);
7720
7721 if end_point == start_point {
7722 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7723 .saturating_sub(1);
7724 start_point =
7725 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7726 };
7727
7728 (start_point..end_point, empty_str.clone())
7729 })
7730 .sorted_by_key(|(range, _)| range.start)
7731 .collect::<Vec<_>>();
7732 buffer.update(cx, |this, cx| {
7733 this.edit(edits, None, cx);
7734 })
7735 }
7736 this.refresh_inline_completion(true, false, window, cx);
7737 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7738 });
7739 }
7740
7741 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7742 self.transact(window, cx, |this, window, cx| {
7743 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7744 let line_mode = s.line_mode;
7745 s.move_with(|map, selection| {
7746 if selection.is_empty() && !line_mode {
7747 let cursor = movement::right(map, selection.head());
7748 selection.end = cursor;
7749 selection.reversed = true;
7750 selection.goal = SelectionGoal::None;
7751 }
7752 })
7753 });
7754 this.insert("", window, cx);
7755 this.refresh_inline_completion(true, false, window, cx);
7756 });
7757 }
7758
7759 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7760 if self.move_to_prev_snippet_tabstop(window, cx) {
7761 return;
7762 }
7763
7764 self.outdent(&Outdent, window, cx);
7765 }
7766
7767 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7768 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7769 return;
7770 }
7771
7772 let mut selections = self.selections.all_adjusted(cx);
7773 let buffer = self.buffer.read(cx);
7774 let snapshot = buffer.snapshot(cx);
7775 let rows_iter = selections.iter().map(|s| s.head().row);
7776 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7777
7778 let mut edits = Vec::new();
7779 let mut prev_edited_row = 0;
7780 let mut row_delta = 0;
7781 for selection in &mut selections {
7782 if selection.start.row != prev_edited_row {
7783 row_delta = 0;
7784 }
7785 prev_edited_row = selection.end.row;
7786
7787 // If the selection is non-empty, then increase the indentation of the selected lines.
7788 if !selection.is_empty() {
7789 row_delta =
7790 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7791 continue;
7792 }
7793
7794 // If the selection is empty and the cursor is in the leading whitespace before the
7795 // suggested indentation, then auto-indent the line.
7796 let cursor = selection.head();
7797 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7798 if let Some(suggested_indent) =
7799 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7800 {
7801 if cursor.column < suggested_indent.len
7802 && cursor.column <= current_indent.len
7803 && current_indent.len <= suggested_indent.len
7804 {
7805 selection.start = Point::new(cursor.row, suggested_indent.len);
7806 selection.end = selection.start;
7807 if row_delta == 0 {
7808 edits.extend(Buffer::edit_for_indent_size_adjustment(
7809 cursor.row,
7810 current_indent,
7811 suggested_indent,
7812 ));
7813 row_delta = suggested_indent.len - current_indent.len;
7814 }
7815 continue;
7816 }
7817 }
7818
7819 // Otherwise, insert a hard or soft tab.
7820 let settings = buffer.language_settings_at(cursor, cx);
7821 let tab_size = if settings.hard_tabs {
7822 IndentSize::tab()
7823 } else {
7824 let tab_size = settings.tab_size.get();
7825 let char_column = snapshot
7826 .text_for_range(Point::new(cursor.row, 0)..cursor)
7827 .flat_map(str::chars)
7828 .count()
7829 + row_delta as usize;
7830 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7831 IndentSize::spaces(chars_to_next_tab_stop)
7832 };
7833 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7834 selection.end = selection.start;
7835 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7836 row_delta += tab_size.len;
7837 }
7838
7839 self.transact(window, cx, |this, window, cx| {
7840 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7841 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7842 s.select(selections)
7843 });
7844 this.refresh_inline_completion(true, false, window, cx);
7845 });
7846 }
7847
7848 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7849 if self.read_only(cx) {
7850 return;
7851 }
7852 let mut selections = self.selections.all::<Point>(cx);
7853 let mut prev_edited_row = 0;
7854 let mut row_delta = 0;
7855 let mut edits = Vec::new();
7856 let buffer = self.buffer.read(cx);
7857 let snapshot = buffer.snapshot(cx);
7858 for selection in &mut selections {
7859 if selection.start.row != prev_edited_row {
7860 row_delta = 0;
7861 }
7862 prev_edited_row = selection.end.row;
7863
7864 row_delta =
7865 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7866 }
7867
7868 self.transact(window, cx, |this, window, cx| {
7869 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7870 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7871 s.select(selections)
7872 });
7873 });
7874 }
7875
7876 fn indent_selection(
7877 buffer: &MultiBuffer,
7878 snapshot: &MultiBufferSnapshot,
7879 selection: &mut Selection<Point>,
7880 edits: &mut Vec<(Range<Point>, String)>,
7881 delta_for_start_row: u32,
7882 cx: &App,
7883 ) -> u32 {
7884 let settings = buffer.language_settings_at(selection.start, cx);
7885 let tab_size = settings.tab_size.get();
7886 let indent_kind = if settings.hard_tabs {
7887 IndentKind::Tab
7888 } else {
7889 IndentKind::Space
7890 };
7891 let mut start_row = selection.start.row;
7892 let mut end_row = selection.end.row + 1;
7893
7894 // If a selection ends at the beginning of a line, don't indent
7895 // that last line.
7896 if selection.end.column == 0 && selection.end.row > selection.start.row {
7897 end_row -= 1;
7898 }
7899
7900 // Avoid re-indenting a row that has already been indented by a
7901 // previous selection, but still update this selection's column
7902 // to reflect that indentation.
7903 if delta_for_start_row > 0 {
7904 start_row += 1;
7905 selection.start.column += delta_for_start_row;
7906 if selection.end.row == selection.start.row {
7907 selection.end.column += delta_for_start_row;
7908 }
7909 }
7910
7911 let mut delta_for_end_row = 0;
7912 let has_multiple_rows = start_row + 1 != end_row;
7913 for row in start_row..end_row {
7914 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7915 let indent_delta = match (current_indent.kind, indent_kind) {
7916 (IndentKind::Space, IndentKind::Space) => {
7917 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7918 IndentSize::spaces(columns_to_next_tab_stop)
7919 }
7920 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7921 (_, IndentKind::Tab) => IndentSize::tab(),
7922 };
7923
7924 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7925 0
7926 } else {
7927 selection.start.column
7928 };
7929 let row_start = Point::new(row, start);
7930 edits.push((
7931 row_start..row_start,
7932 indent_delta.chars().collect::<String>(),
7933 ));
7934
7935 // Update this selection's endpoints to reflect the indentation.
7936 if row == selection.start.row {
7937 selection.start.column += indent_delta.len;
7938 }
7939 if row == selection.end.row {
7940 selection.end.column += indent_delta.len;
7941 delta_for_end_row = indent_delta.len;
7942 }
7943 }
7944
7945 if selection.start.row == selection.end.row {
7946 delta_for_start_row + delta_for_end_row
7947 } else {
7948 delta_for_end_row
7949 }
7950 }
7951
7952 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7953 if self.read_only(cx) {
7954 return;
7955 }
7956 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7957 let selections = self.selections.all::<Point>(cx);
7958 let mut deletion_ranges = Vec::new();
7959 let mut last_outdent = None;
7960 {
7961 let buffer = self.buffer.read(cx);
7962 let snapshot = buffer.snapshot(cx);
7963 for selection in &selections {
7964 let settings = buffer.language_settings_at(selection.start, cx);
7965 let tab_size = settings.tab_size.get();
7966 let mut rows = selection.spanned_rows(false, &display_map);
7967
7968 // Avoid re-outdenting a row that has already been outdented by a
7969 // previous selection.
7970 if let Some(last_row) = last_outdent {
7971 if last_row == rows.start {
7972 rows.start = rows.start.next_row();
7973 }
7974 }
7975 let has_multiple_rows = rows.len() > 1;
7976 for row in rows.iter_rows() {
7977 let indent_size = snapshot.indent_size_for_line(row);
7978 if indent_size.len > 0 {
7979 let deletion_len = match indent_size.kind {
7980 IndentKind::Space => {
7981 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7982 if columns_to_prev_tab_stop == 0 {
7983 tab_size
7984 } else {
7985 columns_to_prev_tab_stop
7986 }
7987 }
7988 IndentKind::Tab => 1,
7989 };
7990 let start = if has_multiple_rows
7991 || deletion_len > selection.start.column
7992 || indent_size.len < selection.start.column
7993 {
7994 0
7995 } else {
7996 selection.start.column - deletion_len
7997 };
7998 deletion_ranges.push(
7999 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8000 );
8001 last_outdent = Some(row);
8002 }
8003 }
8004 }
8005 }
8006
8007 self.transact(window, cx, |this, window, cx| {
8008 this.buffer.update(cx, |buffer, cx| {
8009 let empty_str: Arc<str> = Arc::default();
8010 buffer.edit(
8011 deletion_ranges
8012 .into_iter()
8013 .map(|range| (range, empty_str.clone())),
8014 None,
8015 cx,
8016 );
8017 });
8018 let selections = this.selections.all::<usize>(cx);
8019 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8020 s.select(selections)
8021 });
8022 });
8023 }
8024
8025 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8026 if self.read_only(cx) {
8027 return;
8028 }
8029 let selections = self
8030 .selections
8031 .all::<usize>(cx)
8032 .into_iter()
8033 .map(|s| s.range());
8034
8035 self.transact(window, cx, |this, window, cx| {
8036 this.buffer.update(cx, |buffer, cx| {
8037 buffer.autoindent_ranges(selections, cx);
8038 });
8039 let selections = this.selections.all::<usize>(cx);
8040 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8041 s.select(selections)
8042 });
8043 });
8044 }
8045
8046 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8047 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8048 let selections = self.selections.all::<Point>(cx);
8049
8050 let mut new_cursors = Vec::new();
8051 let mut edit_ranges = Vec::new();
8052 let mut selections = selections.iter().peekable();
8053 while let Some(selection) = selections.next() {
8054 let mut rows = selection.spanned_rows(false, &display_map);
8055 let goal_display_column = selection.head().to_display_point(&display_map).column();
8056
8057 // Accumulate contiguous regions of rows that we want to delete.
8058 while let Some(next_selection) = selections.peek() {
8059 let next_rows = next_selection.spanned_rows(false, &display_map);
8060 if next_rows.start <= rows.end {
8061 rows.end = next_rows.end;
8062 selections.next().unwrap();
8063 } else {
8064 break;
8065 }
8066 }
8067
8068 let buffer = &display_map.buffer_snapshot;
8069 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8070 let edit_end;
8071 let cursor_buffer_row;
8072 if buffer.max_point().row >= rows.end.0 {
8073 // If there's a line after the range, delete the \n from the end of the row range
8074 // and position the cursor on the next line.
8075 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8076 cursor_buffer_row = rows.end;
8077 } else {
8078 // If there isn't a line after the range, delete the \n from the line before the
8079 // start of the row range and position the cursor there.
8080 edit_start = edit_start.saturating_sub(1);
8081 edit_end = buffer.len();
8082 cursor_buffer_row = rows.start.previous_row();
8083 }
8084
8085 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8086 *cursor.column_mut() =
8087 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8088
8089 new_cursors.push((
8090 selection.id,
8091 buffer.anchor_after(cursor.to_point(&display_map)),
8092 ));
8093 edit_ranges.push(edit_start..edit_end);
8094 }
8095
8096 self.transact(window, cx, |this, window, cx| {
8097 let buffer = this.buffer.update(cx, |buffer, cx| {
8098 let empty_str: Arc<str> = Arc::default();
8099 buffer.edit(
8100 edit_ranges
8101 .into_iter()
8102 .map(|range| (range, empty_str.clone())),
8103 None,
8104 cx,
8105 );
8106 buffer.snapshot(cx)
8107 });
8108 let new_selections = new_cursors
8109 .into_iter()
8110 .map(|(id, cursor)| {
8111 let cursor = cursor.to_point(&buffer);
8112 Selection {
8113 id,
8114 start: cursor,
8115 end: cursor,
8116 reversed: false,
8117 goal: SelectionGoal::None,
8118 }
8119 })
8120 .collect();
8121
8122 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8123 s.select(new_selections);
8124 });
8125 });
8126 }
8127
8128 pub fn join_lines_impl(
8129 &mut self,
8130 insert_whitespace: bool,
8131 window: &mut Window,
8132 cx: &mut Context<Self>,
8133 ) {
8134 if self.read_only(cx) {
8135 return;
8136 }
8137 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8138 for selection in self.selections.all::<Point>(cx) {
8139 let start = MultiBufferRow(selection.start.row);
8140 // Treat single line selections as if they include the next line. Otherwise this action
8141 // would do nothing for single line selections individual cursors.
8142 let end = if selection.start.row == selection.end.row {
8143 MultiBufferRow(selection.start.row + 1)
8144 } else {
8145 MultiBufferRow(selection.end.row)
8146 };
8147
8148 if let Some(last_row_range) = row_ranges.last_mut() {
8149 if start <= last_row_range.end {
8150 last_row_range.end = end;
8151 continue;
8152 }
8153 }
8154 row_ranges.push(start..end);
8155 }
8156
8157 let snapshot = self.buffer.read(cx).snapshot(cx);
8158 let mut cursor_positions = Vec::new();
8159 for row_range in &row_ranges {
8160 let anchor = snapshot.anchor_before(Point::new(
8161 row_range.end.previous_row().0,
8162 snapshot.line_len(row_range.end.previous_row()),
8163 ));
8164 cursor_positions.push(anchor..anchor);
8165 }
8166
8167 self.transact(window, cx, |this, window, cx| {
8168 for row_range in row_ranges.into_iter().rev() {
8169 for row in row_range.iter_rows().rev() {
8170 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8171 let next_line_row = row.next_row();
8172 let indent = snapshot.indent_size_for_line(next_line_row);
8173 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8174
8175 let replace =
8176 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8177 " "
8178 } else {
8179 ""
8180 };
8181
8182 this.buffer.update(cx, |buffer, cx| {
8183 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8184 });
8185 }
8186 }
8187
8188 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8189 s.select_anchor_ranges(cursor_positions)
8190 });
8191 });
8192 }
8193
8194 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8195 self.join_lines_impl(true, window, cx);
8196 }
8197
8198 pub fn sort_lines_case_sensitive(
8199 &mut self,
8200 _: &SortLinesCaseSensitive,
8201 window: &mut Window,
8202 cx: &mut Context<Self>,
8203 ) {
8204 self.manipulate_lines(window, cx, |lines| lines.sort())
8205 }
8206
8207 pub fn sort_lines_case_insensitive(
8208 &mut self,
8209 _: &SortLinesCaseInsensitive,
8210 window: &mut Window,
8211 cx: &mut Context<Self>,
8212 ) {
8213 self.manipulate_lines(window, cx, |lines| {
8214 lines.sort_by_key(|line| line.to_lowercase())
8215 })
8216 }
8217
8218 pub fn unique_lines_case_insensitive(
8219 &mut self,
8220 _: &UniqueLinesCaseInsensitive,
8221 window: &mut Window,
8222 cx: &mut Context<Self>,
8223 ) {
8224 self.manipulate_lines(window, cx, |lines| {
8225 let mut seen = HashSet::default();
8226 lines.retain(|line| seen.insert(line.to_lowercase()));
8227 })
8228 }
8229
8230 pub fn unique_lines_case_sensitive(
8231 &mut self,
8232 _: &UniqueLinesCaseSensitive,
8233 window: &mut Window,
8234 cx: &mut Context<Self>,
8235 ) {
8236 self.manipulate_lines(window, cx, |lines| {
8237 let mut seen = HashSet::default();
8238 lines.retain(|line| seen.insert(*line));
8239 })
8240 }
8241
8242 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8243 let Some(project) = self.project.clone() else {
8244 return;
8245 };
8246 self.reload(project, window, cx)
8247 .detach_and_notify_err(window, cx);
8248 }
8249
8250 pub fn restore_file(
8251 &mut self,
8252 _: &::git::RestoreFile,
8253 window: &mut Window,
8254 cx: &mut Context<Self>,
8255 ) {
8256 let mut buffer_ids = HashSet::default();
8257 let snapshot = self.buffer().read(cx).snapshot(cx);
8258 for selection in self.selections.all::<usize>(cx) {
8259 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8260 }
8261
8262 let buffer = self.buffer().read(cx);
8263 let ranges = buffer_ids
8264 .into_iter()
8265 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8266 .collect::<Vec<_>>();
8267
8268 self.restore_hunks_in_ranges(ranges, window, cx);
8269 }
8270
8271 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8272 let selections = self
8273 .selections
8274 .all(cx)
8275 .into_iter()
8276 .map(|s| s.range())
8277 .collect();
8278 self.restore_hunks_in_ranges(selections, window, cx);
8279 }
8280
8281 fn restore_hunks_in_ranges(
8282 &mut self,
8283 ranges: Vec<Range<Point>>,
8284 window: &mut Window,
8285 cx: &mut Context<Editor>,
8286 ) {
8287 let mut revert_changes = HashMap::default();
8288 let chunk_by = self
8289 .snapshot(window, cx)
8290 .hunks_for_ranges(ranges)
8291 .into_iter()
8292 .chunk_by(|hunk| hunk.buffer_id);
8293 for (buffer_id, hunks) in &chunk_by {
8294 let hunks = hunks.collect::<Vec<_>>();
8295 for hunk in &hunks {
8296 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8297 }
8298 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8299 }
8300 drop(chunk_by);
8301 if !revert_changes.is_empty() {
8302 self.transact(window, cx, |editor, window, cx| {
8303 editor.restore(revert_changes, window, cx);
8304 });
8305 }
8306 }
8307
8308 pub fn open_active_item_in_terminal(
8309 &mut self,
8310 _: &OpenInTerminal,
8311 window: &mut Window,
8312 cx: &mut Context<Self>,
8313 ) {
8314 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8315 let project_path = buffer.read(cx).project_path(cx)?;
8316 let project = self.project.as_ref()?.read(cx);
8317 let entry = project.entry_for_path(&project_path, cx)?;
8318 let parent = match &entry.canonical_path {
8319 Some(canonical_path) => canonical_path.to_path_buf(),
8320 None => project.absolute_path(&project_path, cx)?,
8321 }
8322 .parent()?
8323 .to_path_buf();
8324 Some(parent)
8325 }) {
8326 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8327 }
8328 }
8329
8330 fn set_breakpoint_context_menu(
8331 &mut self,
8332 row: DisplayRow,
8333 position: Option<Anchor>,
8334 kind: Arc<BreakpointKind>,
8335 clicked_point: gpui::Point<Pixels>,
8336 window: &mut Window,
8337 cx: &mut Context<Self>,
8338 ) {
8339 if !cx.has_flag::<Debugger>() {
8340 return;
8341 }
8342 let source = self
8343 .buffer
8344 .read(cx)
8345 .snapshot(cx)
8346 .anchor_before(Point::new(row.0, 0u32));
8347
8348 let context_menu =
8349 self.breakpoint_context_menu(position.unwrap_or(source), kind, window, cx);
8350
8351 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8352 self,
8353 source,
8354 clicked_point,
8355 context_menu,
8356 window,
8357 cx,
8358 );
8359 }
8360
8361 fn add_edit_breakpoint_block(
8362 &mut self,
8363 anchor: Anchor,
8364 kind: &BreakpointKind,
8365 window: &mut Window,
8366 cx: &mut Context<Self>,
8367 ) {
8368 let weak_editor = cx.weak_entity();
8369 let bp_prompt =
8370 cx.new(|cx| BreakpointPromptEditor::new(weak_editor, anchor, kind.clone(), window, cx));
8371
8372 let height = bp_prompt.update(cx, |this, cx| {
8373 this.prompt
8374 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8375 });
8376 let cloned_prompt = bp_prompt.clone();
8377 let blocks = vec![BlockProperties {
8378 style: BlockStyle::Sticky,
8379 placement: BlockPlacement::Above(anchor),
8380 height,
8381 render: Arc::new(move |cx| {
8382 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8383 cloned_prompt.clone().into_any_element()
8384 }),
8385 priority: 0,
8386 }];
8387
8388 let focus_handle = bp_prompt.focus_handle(cx);
8389 window.focus(&focus_handle);
8390
8391 let block_ids = self.insert_blocks(blocks, None, cx);
8392 bp_prompt.update(cx, |prompt, _| {
8393 prompt.add_block_ids(block_ids);
8394 });
8395 }
8396
8397 pub(crate) fn breakpoint_at_cursor_head(
8398 &self,
8399 window: &mut Window,
8400 cx: &mut Context<Self>,
8401 ) -> Option<(Anchor, Breakpoint)> {
8402 let cursor_position: Point = self.selections.newest(cx).head();
8403 let snapshot = self.snapshot(window, cx);
8404 // We Set the column position to zero so this function interacts correctly
8405 // between calls by clicking on the gutter & using an action to toggle a
8406 // breakpoint. Otherwise, toggling a breakpoint through an action wouldn't
8407 // untoggle a breakpoint that was added through clicking on the gutter
8408 let cursor_position = snapshot
8409 .display_snapshot
8410 .buffer_snapshot
8411 .anchor_before(Point::new(cursor_position.row, 0));
8412
8413 let project = self.project.clone();
8414
8415 let buffer_id = cursor_position.text_anchor.buffer_id?;
8416 let enclosing_excerpt = snapshot
8417 .buffer_snapshot
8418 .excerpt_ids_for_range(cursor_position..cursor_position)
8419 .next()?;
8420 let buffer = project?.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8421 let buffer_snapshot = buffer.read(cx).snapshot();
8422
8423 let row = buffer_snapshot
8424 .summary_for_anchor::<text::PointUtf16>(&cursor_position.text_anchor)
8425 .row;
8426
8427 let bp = self
8428 .breakpoint_store
8429 .as_ref()?
8430 .read_with(cx, |breakpoint_store, cx| {
8431 breakpoint_store
8432 .breakpoints(
8433 &buffer,
8434 Some(cursor_position.text_anchor..(text::Anchor::MAX)),
8435 buffer_snapshot.clone(),
8436 cx,
8437 )
8438 .next()
8439 .and_then(move |(anchor, bp)| {
8440 let breakpoint_row = buffer_snapshot
8441 .summary_for_anchor::<text::PointUtf16>(anchor)
8442 .row;
8443
8444 if breakpoint_row == row {
8445 snapshot
8446 .buffer_snapshot
8447 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8448 .map(|anchor| (anchor, bp.clone()))
8449 } else {
8450 None
8451 }
8452 })
8453 });
8454 bp
8455 }
8456
8457 pub fn edit_log_breakpoint(
8458 &mut self,
8459 _: &EditLogBreakpoint,
8460 window: &mut Window,
8461 cx: &mut Context<Self>,
8462 ) {
8463 let (anchor, bp) = self
8464 .breakpoint_at_cursor_head(window, cx)
8465 .unwrap_or_else(|| {
8466 let cursor_position: Point = self.selections.newest(cx).head();
8467
8468 let breakpoint_position = self
8469 .snapshot(window, cx)
8470 .display_snapshot
8471 .buffer_snapshot
8472 .anchor_before(Point::new(cursor_position.row, 0));
8473
8474 (
8475 breakpoint_position,
8476 Breakpoint {
8477 kind: BreakpointKind::Standard,
8478 },
8479 )
8480 });
8481
8482 self.add_edit_breakpoint_block(anchor, &bp.kind, window, cx);
8483 }
8484
8485 pub fn toggle_breakpoint(
8486 &mut self,
8487 _: &crate::actions::ToggleBreakpoint,
8488 window: &mut Window,
8489 cx: &mut Context<Self>,
8490 ) {
8491 let edit_action = BreakpointEditAction::Toggle;
8492
8493 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8494 self.edit_breakpoint_at_anchor(anchor, breakpoint.kind, edit_action, cx);
8495 } else {
8496 let cursor_position: Point = self.selections.newest(cx).head();
8497
8498 let breakpoint_position = self
8499 .snapshot(window, cx)
8500 .display_snapshot
8501 .buffer_snapshot
8502 .anchor_before(Point::new(cursor_position.row, 0));
8503
8504 self.edit_breakpoint_at_anchor(
8505 breakpoint_position,
8506 BreakpointKind::Standard,
8507 edit_action,
8508 cx,
8509 );
8510 }
8511 }
8512
8513 pub fn edit_breakpoint_at_anchor(
8514 &mut self,
8515 breakpoint_position: Anchor,
8516 kind: BreakpointKind,
8517 edit_action: BreakpointEditAction,
8518 cx: &mut Context<Self>,
8519 ) {
8520 let Some(breakpoint_store) = &self.breakpoint_store else {
8521 return;
8522 };
8523
8524 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8525 if breakpoint_position == Anchor::min() {
8526 self.buffer()
8527 .read(cx)
8528 .excerpt_buffer_ids()
8529 .into_iter()
8530 .next()
8531 } else {
8532 None
8533 }
8534 }) else {
8535 return;
8536 };
8537
8538 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8539 return;
8540 };
8541
8542 breakpoint_store.update(cx, |breakpoint_store, cx| {
8543 breakpoint_store.toggle_breakpoint(
8544 buffer,
8545 (breakpoint_position.text_anchor, Breakpoint { kind }),
8546 edit_action,
8547 cx,
8548 );
8549 });
8550
8551 cx.notify();
8552 }
8553
8554 #[cfg(any(test, feature = "test-support"))]
8555 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8556 self.breakpoint_store.clone()
8557 }
8558
8559 pub fn prepare_restore_change(
8560 &self,
8561 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8562 hunk: &MultiBufferDiffHunk,
8563 cx: &mut App,
8564 ) -> Option<()> {
8565 if hunk.is_created_file() {
8566 return None;
8567 }
8568 let buffer = self.buffer.read(cx);
8569 let diff = buffer.diff_for(hunk.buffer_id)?;
8570 let buffer = buffer.buffer(hunk.buffer_id)?;
8571 let buffer = buffer.read(cx);
8572 let original_text = diff
8573 .read(cx)
8574 .base_text()
8575 .as_rope()
8576 .slice(hunk.diff_base_byte_range.clone());
8577 let buffer_snapshot = buffer.snapshot();
8578 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8579 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8580 probe
8581 .0
8582 .start
8583 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8584 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8585 }) {
8586 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8587 Some(())
8588 } else {
8589 None
8590 }
8591 }
8592
8593 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8594 self.manipulate_lines(window, cx, |lines| lines.reverse())
8595 }
8596
8597 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8598 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8599 }
8600
8601 fn manipulate_lines<Fn>(
8602 &mut self,
8603 window: &mut Window,
8604 cx: &mut Context<Self>,
8605 mut callback: Fn,
8606 ) where
8607 Fn: FnMut(&mut Vec<&str>),
8608 {
8609 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8610 let buffer = self.buffer.read(cx).snapshot(cx);
8611
8612 let mut edits = Vec::new();
8613
8614 let selections = self.selections.all::<Point>(cx);
8615 let mut selections = selections.iter().peekable();
8616 let mut contiguous_row_selections = Vec::new();
8617 let mut new_selections = Vec::new();
8618 let mut added_lines = 0;
8619 let mut removed_lines = 0;
8620
8621 while let Some(selection) = selections.next() {
8622 let (start_row, end_row) = consume_contiguous_rows(
8623 &mut contiguous_row_selections,
8624 selection,
8625 &display_map,
8626 &mut selections,
8627 );
8628
8629 let start_point = Point::new(start_row.0, 0);
8630 let end_point = Point::new(
8631 end_row.previous_row().0,
8632 buffer.line_len(end_row.previous_row()),
8633 );
8634 let text = buffer
8635 .text_for_range(start_point..end_point)
8636 .collect::<String>();
8637
8638 let mut lines = text.split('\n').collect_vec();
8639
8640 let lines_before = lines.len();
8641 callback(&mut lines);
8642 let lines_after = lines.len();
8643
8644 edits.push((start_point..end_point, lines.join("\n")));
8645
8646 // Selections must change based on added and removed line count
8647 let start_row =
8648 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8649 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8650 new_selections.push(Selection {
8651 id: selection.id,
8652 start: start_row,
8653 end: end_row,
8654 goal: SelectionGoal::None,
8655 reversed: selection.reversed,
8656 });
8657
8658 if lines_after > lines_before {
8659 added_lines += lines_after - lines_before;
8660 } else if lines_before > lines_after {
8661 removed_lines += lines_before - lines_after;
8662 }
8663 }
8664
8665 self.transact(window, cx, |this, window, cx| {
8666 let buffer = this.buffer.update(cx, |buffer, cx| {
8667 buffer.edit(edits, None, cx);
8668 buffer.snapshot(cx)
8669 });
8670
8671 // Recalculate offsets on newly edited buffer
8672 let new_selections = new_selections
8673 .iter()
8674 .map(|s| {
8675 let start_point = Point::new(s.start.0, 0);
8676 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8677 Selection {
8678 id: s.id,
8679 start: buffer.point_to_offset(start_point),
8680 end: buffer.point_to_offset(end_point),
8681 goal: s.goal,
8682 reversed: s.reversed,
8683 }
8684 })
8685 .collect();
8686
8687 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8688 s.select(new_selections);
8689 });
8690
8691 this.request_autoscroll(Autoscroll::fit(), cx);
8692 });
8693 }
8694
8695 pub fn convert_to_upper_case(
8696 &mut self,
8697 _: &ConvertToUpperCase,
8698 window: &mut Window,
8699 cx: &mut Context<Self>,
8700 ) {
8701 self.manipulate_text(window, cx, |text| text.to_uppercase())
8702 }
8703
8704 pub fn convert_to_lower_case(
8705 &mut self,
8706 _: &ConvertToLowerCase,
8707 window: &mut Window,
8708 cx: &mut Context<Self>,
8709 ) {
8710 self.manipulate_text(window, cx, |text| text.to_lowercase())
8711 }
8712
8713 pub fn convert_to_title_case(
8714 &mut self,
8715 _: &ConvertToTitleCase,
8716 window: &mut Window,
8717 cx: &mut Context<Self>,
8718 ) {
8719 self.manipulate_text(window, cx, |text| {
8720 text.split('\n')
8721 .map(|line| line.to_case(Case::Title))
8722 .join("\n")
8723 })
8724 }
8725
8726 pub fn convert_to_snake_case(
8727 &mut self,
8728 _: &ConvertToSnakeCase,
8729 window: &mut Window,
8730 cx: &mut Context<Self>,
8731 ) {
8732 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8733 }
8734
8735 pub fn convert_to_kebab_case(
8736 &mut self,
8737 _: &ConvertToKebabCase,
8738 window: &mut Window,
8739 cx: &mut Context<Self>,
8740 ) {
8741 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8742 }
8743
8744 pub fn convert_to_upper_camel_case(
8745 &mut self,
8746 _: &ConvertToUpperCamelCase,
8747 window: &mut Window,
8748 cx: &mut Context<Self>,
8749 ) {
8750 self.manipulate_text(window, cx, |text| {
8751 text.split('\n')
8752 .map(|line| line.to_case(Case::UpperCamel))
8753 .join("\n")
8754 })
8755 }
8756
8757 pub fn convert_to_lower_camel_case(
8758 &mut self,
8759 _: &ConvertToLowerCamelCase,
8760 window: &mut Window,
8761 cx: &mut Context<Self>,
8762 ) {
8763 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8764 }
8765
8766 pub fn convert_to_opposite_case(
8767 &mut self,
8768 _: &ConvertToOppositeCase,
8769 window: &mut Window,
8770 cx: &mut Context<Self>,
8771 ) {
8772 self.manipulate_text(window, cx, |text| {
8773 text.chars()
8774 .fold(String::with_capacity(text.len()), |mut t, c| {
8775 if c.is_uppercase() {
8776 t.extend(c.to_lowercase());
8777 } else {
8778 t.extend(c.to_uppercase());
8779 }
8780 t
8781 })
8782 })
8783 }
8784
8785 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8786 where
8787 Fn: FnMut(&str) -> String,
8788 {
8789 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8790 let buffer = self.buffer.read(cx).snapshot(cx);
8791
8792 let mut new_selections = Vec::new();
8793 let mut edits = Vec::new();
8794 let mut selection_adjustment = 0i32;
8795
8796 for selection in self.selections.all::<usize>(cx) {
8797 let selection_is_empty = selection.is_empty();
8798
8799 let (start, end) = if selection_is_empty {
8800 let word_range = movement::surrounding_word(
8801 &display_map,
8802 selection.start.to_display_point(&display_map),
8803 );
8804 let start = word_range.start.to_offset(&display_map, Bias::Left);
8805 let end = word_range.end.to_offset(&display_map, Bias::Left);
8806 (start, end)
8807 } else {
8808 (selection.start, selection.end)
8809 };
8810
8811 let text = buffer.text_for_range(start..end).collect::<String>();
8812 let old_length = text.len() as i32;
8813 let text = callback(&text);
8814
8815 new_selections.push(Selection {
8816 start: (start as i32 - selection_adjustment) as usize,
8817 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8818 goal: SelectionGoal::None,
8819 ..selection
8820 });
8821
8822 selection_adjustment += old_length - text.len() as i32;
8823
8824 edits.push((start..end, text));
8825 }
8826
8827 self.transact(window, cx, |this, window, cx| {
8828 this.buffer.update(cx, |buffer, cx| {
8829 buffer.edit(edits, None, cx);
8830 });
8831
8832 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8833 s.select(new_selections);
8834 });
8835
8836 this.request_autoscroll(Autoscroll::fit(), cx);
8837 });
8838 }
8839
8840 pub fn duplicate(
8841 &mut self,
8842 upwards: bool,
8843 whole_lines: bool,
8844 window: &mut Window,
8845 cx: &mut Context<Self>,
8846 ) {
8847 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8848 let buffer = &display_map.buffer_snapshot;
8849 let selections = self.selections.all::<Point>(cx);
8850
8851 let mut edits = Vec::new();
8852 let mut selections_iter = selections.iter().peekable();
8853 while let Some(selection) = selections_iter.next() {
8854 let mut rows = selection.spanned_rows(false, &display_map);
8855 // duplicate line-wise
8856 if whole_lines || selection.start == selection.end {
8857 // Avoid duplicating the same lines twice.
8858 while let Some(next_selection) = selections_iter.peek() {
8859 let next_rows = next_selection.spanned_rows(false, &display_map);
8860 if next_rows.start < rows.end {
8861 rows.end = next_rows.end;
8862 selections_iter.next().unwrap();
8863 } else {
8864 break;
8865 }
8866 }
8867
8868 // Copy the text from the selected row region and splice it either at the start
8869 // or end of the region.
8870 let start = Point::new(rows.start.0, 0);
8871 let end = Point::new(
8872 rows.end.previous_row().0,
8873 buffer.line_len(rows.end.previous_row()),
8874 );
8875 let text = buffer
8876 .text_for_range(start..end)
8877 .chain(Some("\n"))
8878 .collect::<String>();
8879 let insert_location = if upwards {
8880 Point::new(rows.end.0, 0)
8881 } else {
8882 start
8883 };
8884 edits.push((insert_location..insert_location, text));
8885 } else {
8886 // duplicate character-wise
8887 let start = selection.start;
8888 let end = selection.end;
8889 let text = buffer.text_for_range(start..end).collect::<String>();
8890 edits.push((selection.end..selection.end, text));
8891 }
8892 }
8893
8894 self.transact(window, cx, |this, _, cx| {
8895 this.buffer.update(cx, |buffer, cx| {
8896 buffer.edit(edits, None, cx);
8897 });
8898
8899 this.request_autoscroll(Autoscroll::fit(), cx);
8900 });
8901 }
8902
8903 pub fn duplicate_line_up(
8904 &mut self,
8905 _: &DuplicateLineUp,
8906 window: &mut Window,
8907 cx: &mut Context<Self>,
8908 ) {
8909 self.duplicate(true, true, window, cx);
8910 }
8911
8912 pub fn duplicate_line_down(
8913 &mut self,
8914 _: &DuplicateLineDown,
8915 window: &mut Window,
8916 cx: &mut Context<Self>,
8917 ) {
8918 self.duplicate(false, true, window, cx);
8919 }
8920
8921 pub fn duplicate_selection(
8922 &mut self,
8923 _: &DuplicateSelection,
8924 window: &mut Window,
8925 cx: &mut Context<Self>,
8926 ) {
8927 self.duplicate(false, false, window, cx);
8928 }
8929
8930 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8931 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8932 let buffer = self.buffer.read(cx).snapshot(cx);
8933
8934 let mut edits = Vec::new();
8935 let mut unfold_ranges = Vec::new();
8936 let mut refold_creases = Vec::new();
8937
8938 let selections = self.selections.all::<Point>(cx);
8939 let mut selections = selections.iter().peekable();
8940 let mut contiguous_row_selections = Vec::new();
8941 let mut new_selections = Vec::new();
8942
8943 while let Some(selection) = selections.next() {
8944 // Find all the selections that span a contiguous row range
8945 let (start_row, end_row) = consume_contiguous_rows(
8946 &mut contiguous_row_selections,
8947 selection,
8948 &display_map,
8949 &mut selections,
8950 );
8951
8952 // Move the text spanned by the row range to be before the line preceding the row range
8953 if start_row.0 > 0 {
8954 let range_to_move = Point::new(
8955 start_row.previous_row().0,
8956 buffer.line_len(start_row.previous_row()),
8957 )
8958 ..Point::new(
8959 end_row.previous_row().0,
8960 buffer.line_len(end_row.previous_row()),
8961 );
8962 let insertion_point = display_map
8963 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8964 .0;
8965
8966 // Don't move lines across excerpts
8967 if buffer
8968 .excerpt_containing(insertion_point..range_to_move.end)
8969 .is_some()
8970 {
8971 let text = buffer
8972 .text_for_range(range_to_move.clone())
8973 .flat_map(|s| s.chars())
8974 .skip(1)
8975 .chain(['\n'])
8976 .collect::<String>();
8977
8978 edits.push((
8979 buffer.anchor_after(range_to_move.start)
8980 ..buffer.anchor_before(range_to_move.end),
8981 String::new(),
8982 ));
8983 let insertion_anchor = buffer.anchor_after(insertion_point);
8984 edits.push((insertion_anchor..insertion_anchor, text));
8985
8986 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8987
8988 // Move selections up
8989 new_selections.extend(contiguous_row_selections.drain(..).map(
8990 |mut selection| {
8991 selection.start.row -= row_delta;
8992 selection.end.row -= row_delta;
8993 selection
8994 },
8995 ));
8996
8997 // Move folds up
8998 unfold_ranges.push(range_to_move.clone());
8999 for fold in display_map.folds_in_range(
9000 buffer.anchor_before(range_to_move.start)
9001 ..buffer.anchor_after(range_to_move.end),
9002 ) {
9003 let mut start = fold.range.start.to_point(&buffer);
9004 let mut end = fold.range.end.to_point(&buffer);
9005 start.row -= row_delta;
9006 end.row -= row_delta;
9007 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9008 }
9009 }
9010 }
9011
9012 // If we didn't move line(s), preserve the existing selections
9013 new_selections.append(&mut contiguous_row_selections);
9014 }
9015
9016 self.transact(window, cx, |this, window, cx| {
9017 this.unfold_ranges(&unfold_ranges, true, true, cx);
9018 this.buffer.update(cx, |buffer, cx| {
9019 for (range, text) in edits {
9020 buffer.edit([(range, text)], None, cx);
9021 }
9022 });
9023 this.fold_creases(refold_creases, true, window, cx);
9024 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9025 s.select(new_selections);
9026 })
9027 });
9028 }
9029
9030 pub fn move_line_down(
9031 &mut self,
9032 _: &MoveLineDown,
9033 window: &mut Window,
9034 cx: &mut Context<Self>,
9035 ) {
9036 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9037 let buffer = self.buffer.read(cx).snapshot(cx);
9038
9039 let mut edits = Vec::new();
9040 let mut unfold_ranges = Vec::new();
9041 let mut refold_creases = Vec::new();
9042
9043 let selections = self.selections.all::<Point>(cx);
9044 let mut selections = selections.iter().peekable();
9045 let mut contiguous_row_selections = Vec::new();
9046 let mut new_selections = Vec::new();
9047
9048 while let Some(selection) = selections.next() {
9049 // Find all the selections that span a contiguous row range
9050 let (start_row, end_row) = consume_contiguous_rows(
9051 &mut contiguous_row_selections,
9052 selection,
9053 &display_map,
9054 &mut selections,
9055 );
9056
9057 // Move the text spanned by the row range to be after the last line of the row range
9058 if end_row.0 <= buffer.max_point().row {
9059 let range_to_move =
9060 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9061 let insertion_point = display_map
9062 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9063 .0;
9064
9065 // Don't move lines across excerpt boundaries
9066 if buffer
9067 .excerpt_containing(range_to_move.start..insertion_point)
9068 .is_some()
9069 {
9070 let mut text = String::from("\n");
9071 text.extend(buffer.text_for_range(range_to_move.clone()));
9072 text.pop(); // Drop trailing newline
9073 edits.push((
9074 buffer.anchor_after(range_to_move.start)
9075 ..buffer.anchor_before(range_to_move.end),
9076 String::new(),
9077 ));
9078 let insertion_anchor = buffer.anchor_after(insertion_point);
9079 edits.push((insertion_anchor..insertion_anchor, text));
9080
9081 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9082
9083 // Move selections down
9084 new_selections.extend(contiguous_row_selections.drain(..).map(
9085 |mut selection| {
9086 selection.start.row += row_delta;
9087 selection.end.row += row_delta;
9088 selection
9089 },
9090 ));
9091
9092 // Move folds down
9093 unfold_ranges.push(range_to_move.clone());
9094 for fold in display_map.folds_in_range(
9095 buffer.anchor_before(range_to_move.start)
9096 ..buffer.anchor_after(range_to_move.end),
9097 ) {
9098 let mut start = fold.range.start.to_point(&buffer);
9099 let mut end = fold.range.end.to_point(&buffer);
9100 start.row += row_delta;
9101 end.row += row_delta;
9102 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9103 }
9104 }
9105 }
9106
9107 // If we didn't move line(s), preserve the existing selections
9108 new_selections.append(&mut contiguous_row_selections);
9109 }
9110
9111 self.transact(window, cx, |this, window, cx| {
9112 this.unfold_ranges(&unfold_ranges, true, true, cx);
9113 this.buffer.update(cx, |buffer, cx| {
9114 for (range, text) in edits {
9115 buffer.edit([(range, text)], None, cx);
9116 }
9117 });
9118 this.fold_creases(refold_creases, true, window, cx);
9119 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9120 s.select(new_selections)
9121 });
9122 });
9123 }
9124
9125 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9126 let text_layout_details = &self.text_layout_details(window);
9127 self.transact(window, cx, |this, window, cx| {
9128 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9129 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9130 let line_mode = s.line_mode;
9131 s.move_with(|display_map, selection| {
9132 if !selection.is_empty() || line_mode {
9133 return;
9134 }
9135
9136 let mut head = selection.head();
9137 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9138 if head.column() == display_map.line_len(head.row()) {
9139 transpose_offset = display_map
9140 .buffer_snapshot
9141 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9142 }
9143
9144 if transpose_offset == 0 {
9145 return;
9146 }
9147
9148 *head.column_mut() += 1;
9149 head = display_map.clip_point(head, Bias::Right);
9150 let goal = SelectionGoal::HorizontalPosition(
9151 display_map
9152 .x_for_display_point(head, text_layout_details)
9153 .into(),
9154 );
9155 selection.collapse_to(head, goal);
9156
9157 let transpose_start = display_map
9158 .buffer_snapshot
9159 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9160 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9161 let transpose_end = display_map
9162 .buffer_snapshot
9163 .clip_offset(transpose_offset + 1, Bias::Right);
9164 if let Some(ch) =
9165 display_map.buffer_snapshot.chars_at(transpose_start).next()
9166 {
9167 edits.push((transpose_start..transpose_offset, String::new()));
9168 edits.push((transpose_end..transpose_end, ch.to_string()));
9169 }
9170 }
9171 });
9172 edits
9173 });
9174 this.buffer
9175 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9176 let selections = this.selections.all::<usize>(cx);
9177 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9178 s.select(selections);
9179 });
9180 });
9181 }
9182
9183 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9184 self.rewrap_impl(RewrapOptions::default(), cx)
9185 }
9186
9187 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9188 let buffer = self.buffer.read(cx).snapshot(cx);
9189 let selections = self.selections.all::<Point>(cx);
9190 let mut selections = selections.iter().peekable();
9191
9192 let mut edits = Vec::new();
9193 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9194
9195 while let Some(selection) = selections.next() {
9196 let mut start_row = selection.start.row;
9197 let mut end_row = selection.end.row;
9198
9199 // Skip selections that overlap with a range that has already been rewrapped.
9200 let selection_range = start_row..end_row;
9201 if rewrapped_row_ranges
9202 .iter()
9203 .any(|range| range.overlaps(&selection_range))
9204 {
9205 continue;
9206 }
9207
9208 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9209
9210 // Since not all lines in the selection may be at the same indent
9211 // level, choose the indent size that is the most common between all
9212 // of the lines.
9213 //
9214 // If there is a tie, we use the deepest indent.
9215 let (indent_size, indent_end) = {
9216 let mut indent_size_occurrences = HashMap::default();
9217 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9218
9219 for row in start_row..=end_row {
9220 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9221 rows_by_indent_size.entry(indent).or_default().push(row);
9222 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9223 }
9224
9225 let indent_size = indent_size_occurrences
9226 .into_iter()
9227 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9228 .map(|(indent, _)| indent)
9229 .unwrap_or_default();
9230 let row = rows_by_indent_size[&indent_size][0];
9231 let indent_end = Point::new(row, indent_size.len);
9232
9233 (indent_size, indent_end)
9234 };
9235
9236 let mut line_prefix = indent_size.chars().collect::<String>();
9237
9238 let mut inside_comment = false;
9239 if let Some(comment_prefix) =
9240 buffer
9241 .language_scope_at(selection.head())
9242 .and_then(|language| {
9243 language
9244 .line_comment_prefixes()
9245 .iter()
9246 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9247 .cloned()
9248 })
9249 {
9250 line_prefix.push_str(&comment_prefix);
9251 inside_comment = true;
9252 }
9253
9254 let language_settings = buffer.language_settings_at(selection.head(), cx);
9255 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9256 RewrapBehavior::InComments => inside_comment,
9257 RewrapBehavior::InSelections => !selection.is_empty(),
9258 RewrapBehavior::Anywhere => true,
9259 };
9260
9261 let should_rewrap = options.override_language_settings
9262 || allow_rewrap_based_on_language
9263 || self.hard_wrap.is_some();
9264 if !should_rewrap {
9265 continue;
9266 }
9267
9268 if selection.is_empty() {
9269 'expand_upwards: while start_row > 0 {
9270 let prev_row = start_row - 1;
9271 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9272 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9273 {
9274 start_row = prev_row;
9275 } else {
9276 break 'expand_upwards;
9277 }
9278 }
9279
9280 'expand_downwards: while end_row < buffer.max_point().row {
9281 let next_row = end_row + 1;
9282 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9283 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9284 {
9285 end_row = next_row;
9286 } else {
9287 break 'expand_downwards;
9288 }
9289 }
9290 }
9291
9292 let start = Point::new(start_row, 0);
9293 let start_offset = start.to_offset(&buffer);
9294 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9295 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9296 let Some(lines_without_prefixes) = selection_text
9297 .lines()
9298 .map(|line| {
9299 line.strip_prefix(&line_prefix)
9300 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9301 .ok_or_else(|| {
9302 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9303 })
9304 })
9305 .collect::<Result<Vec<_>, _>>()
9306 .log_err()
9307 else {
9308 continue;
9309 };
9310
9311 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9312 buffer
9313 .language_settings_at(Point::new(start_row, 0), cx)
9314 .preferred_line_length as usize
9315 });
9316 let wrapped_text = wrap_with_prefix(
9317 line_prefix,
9318 lines_without_prefixes.join("\n"),
9319 wrap_column,
9320 tab_size,
9321 options.preserve_existing_whitespace,
9322 );
9323
9324 // TODO: should always use char-based diff while still supporting cursor behavior that
9325 // matches vim.
9326 let mut diff_options = DiffOptions::default();
9327 if options.override_language_settings {
9328 diff_options.max_word_diff_len = 0;
9329 diff_options.max_word_diff_line_count = 0;
9330 } else {
9331 diff_options.max_word_diff_len = usize::MAX;
9332 diff_options.max_word_diff_line_count = usize::MAX;
9333 }
9334
9335 for (old_range, new_text) in
9336 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9337 {
9338 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9339 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9340 edits.push((edit_start..edit_end, new_text));
9341 }
9342
9343 rewrapped_row_ranges.push(start_row..=end_row);
9344 }
9345
9346 self.buffer
9347 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9348 }
9349
9350 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9351 let mut text = String::new();
9352 let buffer = self.buffer.read(cx).snapshot(cx);
9353 let mut selections = self.selections.all::<Point>(cx);
9354 let mut clipboard_selections = Vec::with_capacity(selections.len());
9355 {
9356 let max_point = buffer.max_point();
9357 let mut is_first = true;
9358 for selection in &mut selections {
9359 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9360 if is_entire_line {
9361 selection.start = Point::new(selection.start.row, 0);
9362 if !selection.is_empty() && selection.end.column == 0 {
9363 selection.end = cmp::min(max_point, selection.end);
9364 } else {
9365 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9366 }
9367 selection.goal = SelectionGoal::None;
9368 }
9369 if is_first {
9370 is_first = false;
9371 } else {
9372 text += "\n";
9373 }
9374 let mut len = 0;
9375 for chunk in buffer.text_for_range(selection.start..selection.end) {
9376 text.push_str(chunk);
9377 len += chunk.len();
9378 }
9379 clipboard_selections.push(ClipboardSelection {
9380 len,
9381 is_entire_line,
9382 first_line_indent: buffer
9383 .indent_size_for_line(MultiBufferRow(selection.start.row))
9384 .len,
9385 });
9386 }
9387 }
9388
9389 self.transact(window, cx, |this, window, cx| {
9390 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9391 s.select(selections);
9392 });
9393 this.insert("", window, cx);
9394 });
9395 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9396 }
9397
9398 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9399 let item = self.cut_common(window, cx);
9400 cx.write_to_clipboard(item);
9401 }
9402
9403 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9404 self.change_selections(None, window, cx, |s| {
9405 s.move_with(|snapshot, sel| {
9406 if sel.is_empty() {
9407 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9408 }
9409 });
9410 });
9411 let item = self.cut_common(window, cx);
9412 cx.set_global(KillRing(item))
9413 }
9414
9415 pub fn kill_ring_yank(
9416 &mut self,
9417 _: &KillRingYank,
9418 window: &mut Window,
9419 cx: &mut Context<Self>,
9420 ) {
9421 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9422 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9423 (kill_ring.text().to_string(), kill_ring.metadata_json())
9424 } else {
9425 return;
9426 }
9427 } else {
9428 return;
9429 };
9430 self.do_paste(&text, metadata, false, window, cx);
9431 }
9432
9433 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9434 let selections = self.selections.all::<Point>(cx);
9435 let buffer = self.buffer.read(cx).read(cx);
9436 let mut text = String::new();
9437
9438 let mut clipboard_selections = Vec::with_capacity(selections.len());
9439 {
9440 let max_point = buffer.max_point();
9441 let mut is_first = true;
9442 for selection in selections.iter() {
9443 let mut start = selection.start;
9444 let mut end = selection.end;
9445 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9446 if is_entire_line {
9447 start = Point::new(start.row, 0);
9448 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9449 }
9450 if is_first {
9451 is_first = false;
9452 } else {
9453 text += "\n";
9454 }
9455 let mut len = 0;
9456 for chunk in buffer.text_for_range(start..end) {
9457 text.push_str(chunk);
9458 len += chunk.len();
9459 }
9460 clipboard_selections.push(ClipboardSelection {
9461 len,
9462 is_entire_line,
9463 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
9464 });
9465 }
9466 }
9467
9468 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9469 text,
9470 clipboard_selections,
9471 ));
9472 }
9473
9474 pub fn do_paste(
9475 &mut self,
9476 text: &String,
9477 clipboard_selections: Option<Vec<ClipboardSelection>>,
9478 handle_entire_lines: bool,
9479 window: &mut Window,
9480 cx: &mut Context<Self>,
9481 ) {
9482 if self.read_only(cx) {
9483 return;
9484 }
9485
9486 let clipboard_text = Cow::Borrowed(text);
9487
9488 self.transact(window, cx, |this, window, cx| {
9489 if let Some(mut clipboard_selections) = clipboard_selections {
9490 let old_selections = this.selections.all::<usize>(cx);
9491 let all_selections_were_entire_line =
9492 clipboard_selections.iter().all(|s| s.is_entire_line);
9493 let first_selection_indent_column =
9494 clipboard_selections.first().map(|s| s.first_line_indent);
9495 if clipboard_selections.len() != old_selections.len() {
9496 clipboard_selections.drain(..);
9497 }
9498 let cursor_offset = this.selections.last::<usize>(cx).head();
9499 let mut auto_indent_on_paste = true;
9500
9501 this.buffer.update(cx, |buffer, cx| {
9502 let snapshot = buffer.read(cx);
9503 auto_indent_on_paste = snapshot
9504 .language_settings_at(cursor_offset, cx)
9505 .auto_indent_on_paste;
9506
9507 let mut start_offset = 0;
9508 let mut edits = Vec::new();
9509 let mut original_indent_columns = Vec::new();
9510 for (ix, selection) in old_selections.iter().enumerate() {
9511 let to_insert;
9512 let entire_line;
9513 let original_indent_column;
9514 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9515 let end_offset = start_offset + clipboard_selection.len;
9516 to_insert = &clipboard_text[start_offset..end_offset];
9517 entire_line = clipboard_selection.is_entire_line;
9518 start_offset = end_offset + 1;
9519 original_indent_column = Some(clipboard_selection.first_line_indent);
9520 } else {
9521 to_insert = clipboard_text.as_str();
9522 entire_line = all_selections_were_entire_line;
9523 original_indent_column = first_selection_indent_column
9524 }
9525
9526 // If the corresponding selection was empty when this slice of the
9527 // clipboard text was written, then the entire line containing the
9528 // selection was copied. If this selection is also currently empty,
9529 // then paste the line before the current line of the buffer.
9530 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9531 let column = selection.start.to_point(&snapshot).column as usize;
9532 let line_start = selection.start - column;
9533 line_start..line_start
9534 } else {
9535 selection.range()
9536 };
9537
9538 edits.push((range, to_insert));
9539 original_indent_columns.push(original_indent_column);
9540 }
9541 drop(snapshot);
9542
9543 buffer.edit(
9544 edits,
9545 if auto_indent_on_paste {
9546 Some(AutoindentMode::Block {
9547 original_indent_columns,
9548 })
9549 } else {
9550 None
9551 },
9552 cx,
9553 );
9554 });
9555
9556 let selections = this.selections.all::<usize>(cx);
9557 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9558 s.select(selections)
9559 });
9560 } else {
9561 this.insert(&clipboard_text, window, cx);
9562 }
9563 });
9564 }
9565
9566 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9567 if let Some(item) = cx.read_from_clipboard() {
9568 let entries = item.entries();
9569
9570 match entries.first() {
9571 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9572 // of all the pasted entries.
9573 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9574 .do_paste(
9575 clipboard_string.text(),
9576 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9577 true,
9578 window,
9579 cx,
9580 ),
9581 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9582 }
9583 }
9584 }
9585
9586 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9587 if self.read_only(cx) {
9588 return;
9589 }
9590
9591 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9592 if let Some((selections, _)) =
9593 self.selection_history.transaction(transaction_id).cloned()
9594 {
9595 self.change_selections(None, window, cx, |s| {
9596 s.select_anchors(selections.to_vec());
9597 });
9598 } else {
9599 log::error!(
9600 "No entry in selection_history found for undo. \
9601 This may correspond to a bug where undo does not update the selection. \
9602 If this is occurring, please add details to \
9603 https://github.com/zed-industries/zed/issues/22692"
9604 );
9605 }
9606 self.request_autoscroll(Autoscroll::fit(), cx);
9607 self.unmark_text(window, cx);
9608 self.refresh_inline_completion(true, false, window, cx);
9609 cx.emit(EditorEvent::Edited { transaction_id });
9610 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9611 }
9612 }
9613
9614 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9615 if self.read_only(cx) {
9616 return;
9617 }
9618
9619 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9620 if let Some((_, Some(selections))) =
9621 self.selection_history.transaction(transaction_id).cloned()
9622 {
9623 self.change_selections(None, window, cx, |s| {
9624 s.select_anchors(selections.to_vec());
9625 });
9626 } else {
9627 log::error!(
9628 "No entry in selection_history found for redo. \
9629 This may correspond to a bug where undo does not update the selection. \
9630 If this is occurring, please add details to \
9631 https://github.com/zed-industries/zed/issues/22692"
9632 );
9633 }
9634 self.request_autoscroll(Autoscroll::fit(), cx);
9635 self.unmark_text(window, cx);
9636 self.refresh_inline_completion(true, false, window, cx);
9637 cx.emit(EditorEvent::Edited { transaction_id });
9638 }
9639 }
9640
9641 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9642 self.buffer
9643 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9644 }
9645
9646 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9647 self.buffer
9648 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9649 }
9650
9651 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9652 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9653 let line_mode = s.line_mode;
9654 s.move_with(|map, selection| {
9655 let cursor = if selection.is_empty() && !line_mode {
9656 movement::left(map, selection.start)
9657 } else {
9658 selection.start
9659 };
9660 selection.collapse_to(cursor, SelectionGoal::None);
9661 });
9662 })
9663 }
9664
9665 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9666 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9667 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9668 })
9669 }
9670
9671 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9672 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9673 let line_mode = s.line_mode;
9674 s.move_with(|map, selection| {
9675 let cursor = if selection.is_empty() && !line_mode {
9676 movement::right(map, selection.end)
9677 } else {
9678 selection.end
9679 };
9680 selection.collapse_to(cursor, SelectionGoal::None)
9681 });
9682 })
9683 }
9684
9685 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9686 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9687 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9688 })
9689 }
9690
9691 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9692 if self.take_rename(true, window, cx).is_some() {
9693 return;
9694 }
9695
9696 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9697 cx.propagate();
9698 return;
9699 }
9700
9701 let text_layout_details = &self.text_layout_details(window);
9702 let selection_count = self.selections.count();
9703 let first_selection = self.selections.first_anchor();
9704
9705 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9706 let line_mode = s.line_mode;
9707 s.move_with(|map, selection| {
9708 if !selection.is_empty() && !line_mode {
9709 selection.goal = SelectionGoal::None;
9710 }
9711 let (cursor, goal) = movement::up(
9712 map,
9713 selection.start,
9714 selection.goal,
9715 false,
9716 text_layout_details,
9717 );
9718 selection.collapse_to(cursor, goal);
9719 });
9720 });
9721
9722 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9723 {
9724 cx.propagate();
9725 }
9726 }
9727
9728 pub fn move_up_by_lines(
9729 &mut self,
9730 action: &MoveUpByLines,
9731 window: &mut Window,
9732 cx: &mut Context<Self>,
9733 ) {
9734 if self.take_rename(true, window, cx).is_some() {
9735 return;
9736 }
9737
9738 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9739 cx.propagate();
9740 return;
9741 }
9742
9743 let text_layout_details = &self.text_layout_details(window);
9744
9745 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9746 let line_mode = s.line_mode;
9747 s.move_with(|map, selection| {
9748 if !selection.is_empty() && !line_mode {
9749 selection.goal = SelectionGoal::None;
9750 }
9751 let (cursor, goal) = movement::up_by_rows(
9752 map,
9753 selection.start,
9754 action.lines,
9755 selection.goal,
9756 false,
9757 text_layout_details,
9758 );
9759 selection.collapse_to(cursor, goal);
9760 });
9761 })
9762 }
9763
9764 pub fn move_down_by_lines(
9765 &mut self,
9766 action: &MoveDownByLines,
9767 window: &mut Window,
9768 cx: &mut Context<Self>,
9769 ) {
9770 if self.take_rename(true, window, cx).is_some() {
9771 return;
9772 }
9773
9774 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9775 cx.propagate();
9776 return;
9777 }
9778
9779 let text_layout_details = &self.text_layout_details(window);
9780
9781 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9782 let line_mode = s.line_mode;
9783 s.move_with(|map, selection| {
9784 if !selection.is_empty() && !line_mode {
9785 selection.goal = SelectionGoal::None;
9786 }
9787 let (cursor, goal) = movement::down_by_rows(
9788 map,
9789 selection.start,
9790 action.lines,
9791 selection.goal,
9792 false,
9793 text_layout_details,
9794 );
9795 selection.collapse_to(cursor, goal);
9796 });
9797 })
9798 }
9799
9800 pub fn select_down_by_lines(
9801 &mut self,
9802 action: &SelectDownByLines,
9803 window: &mut Window,
9804 cx: &mut Context<Self>,
9805 ) {
9806 let text_layout_details = &self.text_layout_details(window);
9807 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9808 s.move_heads_with(|map, head, goal| {
9809 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9810 })
9811 })
9812 }
9813
9814 pub fn select_up_by_lines(
9815 &mut self,
9816 action: &SelectUpByLines,
9817 window: &mut Window,
9818 cx: &mut Context<Self>,
9819 ) {
9820 let text_layout_details = &self.text_layout_details(window);
9821 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9822 s.move_heads_with(|map, head, goal| {
9823 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9824 })
9825 })
9826 }
9827
9828 pub fn select_page_up(
9829 &mut self,
9830 _: &SelectPageUp,
9831 window: &mut Window,
9832 cx: &mut Context<Self>,
9833 ) {
9834 let Some(row_count) = self.visible_row_count() else {
9835 return;
9836 };
9837
9838 let text_layout_details = &self.text_layout_details(window);
9839
9840 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9841 s.move_heads_with(|map, head, goal| {
9842 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9843 })
9844 })
9845 }
9846
9847 pub fn move_page_up(
9848 &mut self,
9849 action: &MovePageUp,
9850 window: &mut Window,
9851 cx: &mut Context<Self>,
9852 ) {
9853 if self.take_rename(true, window, cx).is_some() {
9854 return;
9855 }
9856
9857 if self
9858 .context_menu
9859 .borrow_mut()
9860 .as_mut()
9861 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9862 .unwrap_or(false)
9863 {
9864 return;
9865 }
9866
9867 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9868 cx.propagate();
9869 return;
9870 }
9871
9872 let Some(row_count) = self.visible_row_count() else {
9873 return;
9874 };
9875
9876 let autoscroll = if action.center_cursor {
9877 Autoscroll::center()
9878 } else {
9879 Autoscroll::fit()
9880 };
9881
9882 let text_layout_details = &self.text_layout_details(window);
9883
9884 self.change_selections(Some(autoscroll), window, cx, |s| {
9885 let line_mode = s.line_mode;
9886 s.move_with(|map, selection| {
9887 if !selection.is_empty() && !line_mode {
9888 selection.goal = SelectionGoal::None;
9889 }
9890 let (cursor, goal) = movement::up_by_rows(
9891 map,
9892 selection.end,
9893 row_count,
9894 selection.goal,
9895 false,
9896 text_layout_details,
9897 );
9898 selection.collapse_to(cursor, goal);
9899 });
9900 });
9901 }
9902
9903 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9904 let text_layout_details = &self.text_layout_details(window);
9905 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9906 s.move_heads_with(|map, head, goal| {
9907 movement::up(map, head, goal, false, text_layout_details)
9908 })
9909 })
9910 }
9911
9912 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9913 self.take_rename(true, window, cx);
9914
9915 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9916 cx.propagate();
9917 return;
9918 }
9919
9920 let text_layout_details = &self.text_layout_details(window);
9921 let selection_count = self.selections.count();
9922 let first_selection = self.selections.first_anchor();
9923
9924 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9925 let line_mode = s.line_mode;
9926 s.move_with(|map, selection| {
9927 if !selection.is_empty() && !line_mode {
9928 selection.goal = SelectionGoal::None;
9929 }
9930 let (cursor, goal) = movement::down(
9931 map,
9932 selection.end,
9933 selection.goal,
9934 false,
9935 text_layout_details,
9936 );
9937 selection.collapse_to(cursor, goal);
9938 });
9939 });
9940
9941 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9942 {
9943 cx.propagate();
9944 }
9945 }
9946
9947 pub fn select_page_down(
9948 &mut self,
9949 _: &SelectPageDown,
9950 window: &mut Window,
9951 cx: &mut Context<Self>,
9952 ) {
9953 let Some(row_count) = self.visible_row_count() else {
9954 return;
9955 };
9956
9957 let text_layout_details = &self.text_layout_details(window);
9958
9959 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9960 s.move_heads_with(|map, head, goal| {
9961 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9962 })
9963 })
9964 }
9965
9966 pub fn move_page_down(
9967 &mut self,
9968 action: &MovePageDown,
9969 window: &mut Window,
9970 cx: &mut Context<Self>,
9971 ) {
9972 if self.take_rename(true, window, cx).is_some() {
9973 return;
9974 }
9975
9976 if self
9977 .context_menu
9978 .borrow_mut()
9979 .as_mut()
9980 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9981 .unwrap_or(false)
9982 {
9983 return;
9984 }
9985
9986 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9987 cx.propagate();
9988 return;
9989 }
9990
9991 let Some(row_count) = self.visible_row_count() else {
9992 return;
9993 };
9994
9995 let autoscroll = if action.center_cursor {
9996 Autoscroll::center()
9997 } else {
9998 Autoscroll::fit()
9999 };
10000
10001 let text_layout_details = &self.text_layout_details(window);
10002 self.change_selections(Some(autoscroll), window, cx, |s| {
10003 let line_mode = s.line_mode;
10004 s.move_with(|map, selection| {
10005 if !selection.is_empty() && !line_mode {
10006 selection.goal = SelectionGoal::None;
10007 }
10008 let (cursor, goal) = movement::down_by_rows(
10009 map,
10010 selection.end,
10011 row_count,
10012 selection.goal,
10013 false,
10014 text_layout_details,
10015 );
10016 selection.collapse_to(cursor, goal);
10017 });
10018 });
10019 }
10020
10021 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10022 let text_layout_details = &self.text_layout_details(window);
10023 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10024 s.move_heads_with(|map, head, goal| {
10025 movement::down(map, head, goal, false, text_layout_details)
10026 })
10027 });
10028 }
10029
10030 pub fn context_menu_first(
10031 &mut self,
10032 _: &ContextMenuFirst,
10033 _window: &mut Window,
10034 cx: &mut Context<Self>,
10035 ) {
10036 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10037 context_menu.select_first(self.completion_provider.as_deref(), cx);
10038 }
10039 }
10040
10041 pub fn context_menu_prev(
10042 &mut self,
10043 _: &ContextMenuPrevious,
10044 _window: &mut Window,
10045 cx: &mut Context<Self>,
10046 ) {
10047 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10048 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10049 }
10050 }
10051
10052 pub fn context_menu_next(
10053 &mut self,
10054 _: &ContextMenuNext,
10055 _window: &mut Window,
10056 cx: &mut Context<Self>,
10057 ) {
10058 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10059 context_menu.select_next(self.completion_provider.as_deref(), cx);
10060 }
10061 }
10062
10063 pub fn context_menu_last(
10064 &mut self,
10065 _: &ContextMenuLast,
10066 _window: &mut Window,
10067 cx: &mut Context<Self>,
10068 ) {
10069 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10070 context_menu.select_last(self.completion_provider.as_deref(), cx);
10071 }
10072 }
10073
10074 pub fn move_to_previous_word_start(
10075 &mut self,
10076 _: &MoveToPreviousWordStart,
10077 window: &mut Window,
10078 cx: &mut Context<Self>,
10079 ) {
10080 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10081 s.move_cursors_with(|map, head, _| {
10082 (
10083 movement::previous_word_start(map, head),
10084 SelectionGoal::None,
10085 )
10086 });
10087 })
10088 }
10089
10090 pub fn move_to_previous_subword_start(
10091 &mut self,
10092 _: &MoveToPreviousSubwordStart,
10093 window: &mut Window,
10094 cx: &mut Context<Self>,
10095 ) {
10096 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10097 s.move_cursors_with(|map, head, _| {
10098 (
10099 movement::previous_subword_start(map, head),
10100 SelectionGoal::None,
10101 )
10102 });
10103 })
10104 }
10105
10106 pub fn select_to_previous_word_start(
10107 &mut self,
10108 _: &SelectToPreviousWordStart,
10109 window: &mut Window,
10110 cx: &mut Context<Self>,
10111 ) {
10112 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10113 s.move_heads_with(|map, head, _| {
10114 (
10115 movement::previous_word_start(map, head),
10116 SelectionGoal::None,
10117 )
10118 });
10119 })
10120 }
10121
10122 pub fn select_to_previous_subword_start(
10123 &mut self,
10124 _: &SelectToPreviousSubwordStart,
10125 window: &mut Window,
10126 cx: &mut Context<Self>,
10127 ) {
10128 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10129 s.move_heads_with(|map, head, _| {
10130 (
10131 movement::previous_subword_start(map, head),
10132 SelectionGoal::None,
10133 )
10134 });
10135 })
10136 }
10137
10138 pub fn delete_to_previous_word_start(
10139 &mut self,
10140 action: &DeleteToPreviousWordStart,
10141 window: &mut Window,
10142 cx: &mut Context<Self>,
10143 ) {
10144 self.transact(window, cx, |this, window, cx| {
10145 this.select_autoclose_pair(window, cx);
10146 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10147 let line_mode = s.line_mode;
10148 s.move_with(|map, selection| {
10149 if selection.is_empty() && !line_mode {
10150 let cursor = if action.ignore_newlines {
10151 movement::previous_word_start(map, selection.head())
10152 } else {
10153 movement::previous_word_start_or_newline(map, selection.head())
10154 };
10155 selection.set_head(cursor, SelectionGoal::None);
10156 }
10157 });
10158 });
10159 this.insert("", window, cx);
10160 });
10161 }
10162
10163 pub fn delete_to_previous_subword_start(
10164 &mut self,
10165 _: &DeleteToPreviousSubwordStart,
10166 window: &mut Window,
10167 cx: &mut Context<Self>,
10168 ) {
10169 self.transact(window, cx, |this, window, cx| {
10170 this.select_autoclose_pair(window, cx);
10171 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10172 let line_mode = s.line_mode;
10173 s.move_with(|map, selection| {
10174 if selection.is_empty() && !line_mode {
10175 let cursor = movement::previous_subword_start(map, selection.head());
10176 selection.set_head(cursor, SelectionGoal::None);
10177 }
10178 });
10179 });
10180 this.insert("", window, cx);
10181 });
10182 }
10183
10184 pub fn move_to_next_word_end(
10185 &mut self,
10186 _: &MoveToNextWordEnd,
10187 window: &mut Window,
10188 cx: &mut Context<Self>,
10189 ) {
10190 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10191 s.move_cursors_with(|map, head, _| {
10192 (movement::next_word_end(map, head), SelectionGoal::None)
10193 });
10194 })
10195 }
10196
10197 pub fn move_to_next_subword_end(
10198 &mut self,
10199 _: &MoveToNextSubwordEnd,
10200 window: &mut Window,
10201 cx: &mut Context<Self>,
10202 ) {
10203 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10204 s.move_cursors_with(|map, head, _| {
10205 (movement::next_subword_end(map, head), SelectionGoal::None)
10206 });
10207 })
10208 }
10209
10210 pub fn select_to_next_word_end(
10211 &mut self,
10212 _: &SelectToNextWordEnd,
10213 window: &mut Window,
10214 cx: &mut Context<Self>,
10215 ) {
10216 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10217 s.move_heads_with(|map, head, _| {
10218 (movement::next_word_end(map, head), SelectionGoal::None)
10219 });
10220 })
10221 }
10222
10223 pub fn select_to_next_subword_end(
10224 &mut self,
10225 _: &SelectToNextSubwordEnd,
10226 window: &mut Window,
10227 cx: &mut Context<Self>,
10228 ) {
10229 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10230 s.move_heads_with(|map, head, _| {
10231 (movement::next_subword_end(map, head), SelectionGoal::None)
10232 });
10233 })
10234 }
10235
10236 pub fn delete_to_next_word_end(
10237 &mut self,
10238 action: &DeleteToNextWordEnd,
10239 window: &mut Window,
10240 cx: &mut Context<Self>,
10241 ) {
10242 self.transact(window, cx, |this, window, cx| {
10243 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10244 let line_mode = s.line_mode;
10245 s.move_with(|map, selection| {
10246 if selection.is_empty() && !line_mode {
10247 let cursor = if action.ignore_newlines {
10248 movement::next_word_end(map, selection.head())
10249 } else {
10250 movement::next_word_end_or_newline(map, selection.head())
10251 };
10252 selection.set_head(cursor, SelectionGoal::None);
10253 }
10254 });
10255 });
10256 this.insert("", window, cx);
10257 });
10258 }
10259
10260 pub fn delete_to_next_subword_end(
10261 &mut self,
10262 _: &DeleteToNextSubwordEnd,
10263 window: &mut Window,
10264 cx: &mut Context<Self>,
10265 ) {
10266 self.transact(window, cx, |this, window, cx| {
10267 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10268 s.move_with(|map, selection| {
10269 if selection.is_empty() {
10270 let cursor = movement::next_subword_end(map, selection.head());
10271 selection.set_head(cursor, SelectionGoal::None);
10272 }
10273 });
10274 });
10275 this.insert("", window, cx);
10276 });
10277 }
10278
10279 pub fn move_to_beginning_of_line(
10280 &mut self,
10281 action: &MoveToBeginningOfLine,
10282 window: &mut Window,
10283 cx: &mut Context<Self>,
10284 ) {
10285 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10286 s.move_cursors_with(|map, head, _| {
10287 (
10288 movement::indented_line_beginning(
10289 map,
10290 head,
10291 action.stop_at_soft_wraps,
10292 action.stop_at_indent,
10293 ),
10294 SelectionGoal::None,
10295 )
10296 });
10297 })
10298 }
10299
10300 pub fn select_to_beginning_of_line(
10301 &mut self,
10302 action: &SelectToBeginningOfLine,
10303 window: &mut Window,
10304 cx: &mut Context<Self>,
10305 ) {
10306 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10307 s.move_heads_with(|map, head, _| {
10308 (
10309 movement::indented_line_beginning(
10310 map,
10311 head,
10312 action.stop_at_soft_wraps,
10313 action.stop_at_indent,
10314 ),
10315 SelectionGoal::None,
10316 )
10317 });
10318 });
10319 }
10320
10321 pub fn delete_to_beginning_of_line(
10322 &mut self,
10323 action: &DeleteToBeginningOfLine,
10324 window: &mut Window,
10325 cx: &mut Context<Self>,
10326 ) {
10327 self.transact(window, cx, |this, window, cx| {
10328 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10329 s.move_with(|_, selection| {
10330 selection.reversed = true;
10331 });
10332 });
10333
10334 this.select_to_beginning_of_line(
10335 &SelectToBeginningOfLine {
10336 stop_at_soft_wraps: false,
10337 stop_at_indent: action.stop_at_indent,
10338 },
10339 window,
10340 cx,
10341 );
10342 this.backspace(&Backspace, window, cx);
10343 });
10344 }
10345
10346 pub fn move_to_end_of_line(
10347 &mut self,
10348 action: &MoveToEndOfLine,
10349 window: &mut Window,
10350 cx: &mut Context<Self>,
10351 ) {
10352 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10353 s.move_cursors_with(|map, head, _| {
10354 (
10355 movement::line_end(map, head, action.stop_at_soft_wraps),
10356 SelectionGoal::None,
10357 )
10358 });
10359 })
10360 }
10361
10362 pub fn select_to_end_of_line(
10363 &mut self,
10364 action: &SelectToEndOfLine,
10365 window: &mut Window,
10366 cx: &mut Context<Self>,
10367 ) {
10368 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10369 s.move_heads_with(|map, head, _| {
10370 (
10371 movement::line_end(map, head, action.stop_at_soft_wraps),
10372 SelectionGoal::None,
10373 )
10374 });
10375 })
10376 }
10377
10378 pub fn delete_to_end_of_line(
10379 &mut self,
10380 _: &DeleteToEndOfLine,
10381 window: &mut Window,
10382 cx: &mut Context<Self>,
10383 ) {
10384 self.transact(window, cx, |this, window, cx| {
10385 this.select_to_end_of_line(
10386 &SelectToEndOfLine {
10387 stop_at_soft_wraps: false,
10388 },
10389 window,
10390 cx,
10391 );
10392 this.delete(&Delete, window, cx);
10393 });
10394 }
10395
10396 pub fn cut_to_end_of_line(
10397 &mut self,
10398 _: &CutToEndOfLine,
10399 window: &mut Window,
10400 cx: &mut Context<Self>,
10401 ) {
10402 self.transact(window, cx, |this, window, cx| {
10403 this.select_to_end_of_line(
10404 &SelectToEndOfLine {
10405 stop_at_soft_wraps: false,
10406 },
10407 window,
10408 cx,
10409 );
10410 this.cut(&Cut, window, cx);
10411 });
10412 }
10413
10414 pub fn move_to_start_of_paragraph(
10415 &mut self,
10416 _: &MoveToStartOfParagraph,
10417 window: &mut Window,
10418 cx: &mut Context<Self>,
10419 ) {
10420 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10421 cx.propagate();
10422 return;
10423 }
10424
10425 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10426 s.move_with(|map, selection| {
10427 selection.collapse_to(
10428 movement::start_of_paragraph(map, selection.head(), 1),
10429 SelectionGoal::None,
10430 )
10431 });
10432 })
10433 }
10434
10435 pub fn move_to_end_of_paragraph(
10436 &mut self,
10437 _: &MoveToEndOfParagraph,
10438 window: &mut Window,
10439 cx: &mut Context<Self>,
10440 ) {
10441 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10442 cx.propagate();
10443 return;
10444 }
10445
10446 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10447 s.move_with(|map, selection| {
10448 selection.collapse_to(
10449 movement::end_of_paragraph(map, selection.head(), 1),
10450 SelectionGoal::None,
10451 )
10452 });
10453 })
10454 }
10455
10456 pub fn select_to_start_of_paragraph(
10457 &mut self,
10458 _: &SelectToStartOfParagraph,
10459 window: &mut Window,
10460 cx: &mut Context<Self>,
10461 ) {
10462 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10463 cx.propagate();
10464 return;
10465 }
10466
10467 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10468 s.move_heads_with(|map, head, _| {
10469 (
10470 movement::start_of_paragraph(map, head, 1),
10471 SelectionGoal::None,
10472 )
10473 });
10474 })
10475 }
10476
10477 pub fn select_to_end_of_paragraph(
10478 &mut self,
10479 _: &SelectToEndOfParagraph,
10480 window: &mut Window,
10481 cx: &mut Context<Self>,
10482 ) {
10483 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10484 cx.propagate();
10485 return;
10486 }
10487
10488 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10489 s.move_heads_with(|map, head, _| {
10490 (
10491 movement::end_of_paragraph(map, head, 1),
10492 SelectionGoal::None,
10493 )
10494 });
10495 })
10496 }
10497
10498 pub fn move_to_start_of_excerpt(
10499 &mut self,
10500 _: &MoveToStartOfExcerpt,
10501 window: &mut Window,
10502 cx: &mut Context<Self>,
10503 ) {
10504 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10505 cx.propagate();
10506 return;
10507 }
10508
10509 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10510 s.move_with(|map, selection| {
10511 selection.collapse_to(
10512 movement::start_of_excerpt(
10513 map,
10514 selection.head(),
10515 workspace::searchable::Direction::Prev,
10516 ),
10517 SelectionGoal::None,
10518 )
10519 });
10520 })
10521 }
10522
10523 pub fn move_to_start_of_next_excerpt(
10524 &mut self,
10525 _: &MoveToStartOfNextExcerpt,
10526 window: &mut Window,
10527 cx: &mut Context<Self>,
10528 ) {
10529 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10530 cx.propagate();
10531 return;
10532 }
10533
10534 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10535 s.move_with(|map, selection| {
10536 selection.collapse_to(
10537 movement::start_of_excerpt(
10538 map,
10539 selection.head(),
10540 workspace::searchable::Direction::Next,
10541 ),
10542 SelectionGoal::None,
10543 )
10544 });
10545 })
10546 }
10547
10548 pub fn move_to_end_of_excerpt(
10549 &mut self,
10550 _: &MoveToEndOfExcerpt,
10551 window: &mut Window,
10552 cx: &mut Context<Self>,
10553 ) {
10554 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10555 cx.propagate();
10556 return;
10557 }
10558
10559 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10560 s.move_with(|map, selection| {
10561 selection.collapse_to(
10562 movement::end_of_excerpt(
10563 map,
10564 selection.head(),
10565 workspace::searchable::Direction::Next,
10566 ),
10567 SelectionGoal::None,
10568 )
10569 });
10570 })
10571 }
10572
10573 pub fn move_to_end_of_previous_excerpt(
10574 &mut self,
10575 _: &MoveToEndOfPreviousExcerpt,
10576 window: &mut Window,
10577 cx: &mut Context<Self>,
10578 ) {
10579 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10580 cx.propagate();
10581 return;
10582 }
10583
10584 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10585 s.move_with(|map, selection| {
10586 selection.collapse_to(
10587 movement::end_of_excerpt(
10588 map,
10589 selection.head(),
10590 workspace::searchable::Direction::Prev,
10591 ),
10592 SelectionGoal::None,
10593 )
10594 });
10595 })
10596 }
10597
10598 pub fn select_to_start_of_excerpt(
10599 &mut self,
10600 _: &SelectToStartOfExcerpt,
10601 window: &mut Window,
10602 cx: &mut Context<Self>,
10603 ) {
10604 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10605 cx.propagate();
10606 return;
10607 }
10608
10609 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10610 s.move_heads_with(|map, head, _| {
10611 (
10612 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10613 SelectionGoal::None,
10614 )
10615 });
10616 })
10617 }
10618
10619 pub fn select_to_start_of_next_excerpt(
10620 &mut self,
10621 _: &SelectToStartOfNextExcerpt,
10622 window: &mut Window,
10623 cx: &mut Context<Self>,
10624 ) {
10625 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10626 cx.propagate();
10627 return;
10628 }
10629
10630 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10631 s.move_heads_with(|map, head, _| {
10632 (
10633 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10634 SelectionGoal::None,
10635 )
10636 });
10637 })
10638 }
10639
10640 pub fn select_to_end_of_excerpt(
10641 &mut self,
10642 _: &SelectToEndOfExcerpt,
10643 window: &mut Window,
10644 cx: &mut Context<Self>,
10645 ) {
10646 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10647 cx.propagate();
10648 return;
10649 }
10650
10651 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10652 s.move_heads_with(|map, head, _| {
10653 (
10654 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10655 SelectionGoal::None,
10656 )
10657 });
10658 })
10659 }
10660
10661 pub fn select_to_end_of_previous_excerpt(
10662 &mut self,
10663 _: &SelectToEndOfPreviousExcerpt,
10664 window: &mut Window,
10665 cx: &mut Context<Self>,
10666 ) {
10667 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10668 cx.propagate();
10669 return;
10670 }
10671
10672 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10673 s.move_heads_with(|map, head, _| {
10674 (
10675 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10676 SelectionGoal::None,
10677 )
10678 });
10679 })
10680 }
10681
10682 pub fn move_to_beginning(
10683 &mut self,
10684 _: &MoveToBeginning,
10685 window: &mut Window,
10686 cx: &mut Context<Self>,
10687 ) {
10688 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10689 cx.propagate();
10690 return;
10691 }
10692
10693 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10694 s.select_ranges(vec![0..0]);
10695 });
10696 }
10697
10698 pub fn select_to_beginning(
10699 &mut self,
10700 _: &SelectToBeginning,
10701 window: &mut Window,
10702 cx: &mut Context<Self>,
10703 ) {
10704 let mut selection = self.selections.last::<Point>(cx);
10705 selection.set_head(Point::zero(), SelectionGoal::None);
10706
10707 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10708 s.select(vec![selection]);
10709 });
10710 }
10711
10712 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10713 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10714 cx.propagate();
10715 return;
10716 }
10717
10718 let cursor = self.buffer.read(cx).read(cx).len();
10719 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10720 s.select_ranges(vec![cursor..cursor])
10721 });
10722 }
10723
10724 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10725 self.nav_history = nav_history;
10726 }
10727
10728 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10729 self.nav_history.as_ref()
10730 }
10731
10732 fn push_to_nav_history(
10733 &mut self,
10734 cursor_anchor: Anchor,
10735 new_position: Option<Point>,
10736 cx: &mut Context<Self>,
10737 ) {
10738 if let Some(nav_history) = self.nav_history.as_mut() {
10739 let buffer = self.buffer.read(cx).read(cx);
10740 let cursor_position = cursor_anchor.to_point(&buffer);
10741 let scroll_state = self.scroll_manager.anchor();
10742 let scroll_top_row = scroll_state.top_row(&buffer);
10743 drop(buffer);
10744
10745 if let Some(new_position) = new_position {
10746 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10747 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10748 return;
10749 }
10750 }
10751
10752 nav_history.push(
10753 Some(NavigationData {
10754 cursor_anchor,
10755 cursor_position,
10756 scroll_anchor: scroll_state,
10757 scroll_top_row,
10758 }),
10759 cx,
10760 );
10761 }
10762 }
10763
10764 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10765 let buffer = self.buffer.read(cx).snapshot(cx);
10766 let mut selection = self.selections.first::<usize>(cx);
10767 selection.set_head(buffer.len(), SelectionGoal::None);
10768 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10769 s.select(vec![selection]);
10770 });
10771 }
10772
10773 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10774 let end = self.buffer.read(cx).read(cx).len();
10775 self.change_selections(None, window, cx, |s| {
10776 s.select_ranges(vec![0..end]);
10777 });
10778 }
10779
10780 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10781 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10782 let mut selections = self.selections.all::<Point>(cx);
10783 let max_point = display_map.buffer_snapshot.max_point();
10784 for selection in &mut selections {
10785 let rows = selection.spanned_rows(true, &display_map);
10786 selection.start = Point::new(rows.start.0, 0);
10787 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10788 selection.reversed = false;
10789 }
10790 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10791 s.select(selections);
10792 });
10793 }
10794
10795 pub fn split_selection_into_lines(
10796 &mut self,
10797 _: &SplitSelectionIntoLines,
10798 window: &mut Window,
10799 cx: &mut Context<Self>,
10800 ) {
10801 let selections = self
10802 .selections
10803 .all::<Point>(cx)
10804 .into_iter()
10805 .map(|selection| selection.start..selection.end)
10806 .collect::<Vec<_>>();
10807 self.unfold_ranges(&selections, true, true, cx);
10808
10809 let mut new_selection_ranges = Vec::new();
10810 {
10811 let buffer = self.buffer.read(cx).read(cx);
10812 for selection in selections {
10813 for row in selection.start.row..selection.end.row {
10814 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10815 new_selection_ranges.push(cursor..cursor);
10816 }
10817
10818 let is_multiline_selection = selection.start.row != selection.end.row;
10819 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10820 // so this action feels more ergonomic when paired with other selection operations
10821 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10822 if !should_skip_last {
10823 new_selection_ranges.push(selection.end..selection.end);
10824 }
10825 }
10826 }
10827 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10828 s.select_ranges(new_selection_ranges);
10829 });
10830 }
10831
10832 pub fn add_selection_above(
10833 &mut self,
10834 _: &AddSelectionAbove,
10835 window: &mut Window,
10836 cx: &mut Context<Self>,
10837 ) {
10838 self.add_selection(true, window, cx);
10839 }
10840
10841 pub fn add_selection_below(
10842 &mut self,
10843 _: &AddSelectionBelow,
10844 window: &mut Window,
10845 cx: &mut Context<Self>,
10846 ) {
10847 self.add_selection(false, window, cx);
10848 }
10849
10850 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10851 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10852 let mut selections = self.selections.all::<Point>(cx);
10853 let text_layout_details = self.text_layout_details(window);
10854 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10855 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10856 let range = oldest_selection.display_range(&display_map).sorted();
10857
10858 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10859 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10860 let positions = start_x.min(end_x)..start_x.max(end_x);
10861
10862 selections.clear();
10863 let mut stack = Vec::new();
10864 for row in range.start.row().0..=range.end.row().0 {
10865 if let Some(selection) = self.selections.build_columnar_selection(
10866 &display_map,
10867 DisplayRow(row),
10868 &positions,
10869 oldest_selection.reversed,
10870 &text_layout_details,
10871 ) {
10872 stack.push(selection.id);
10873 selections.push(selection);
10874 }
10875 }
10876
10877 if above {
10878 stack.reverse();
10879 }
10880
10881 AddSelectionsState { above, stack }
10882 });
10883
10884 let last_added_selection = *state.stack.last().unwrap();
10885 let mut new_selections = Vec::new();
10886 if above == state.above {
10887 let end_row = if above {
10888 DisplayRow(0)
10889 } else {
10890 display_map.max_point().row()
10891 };
10892
10893 'outer: for selection in selections {
10894 if selection.id == last_added_selection {
10895 let range = selection.display_range(&display_map).sorted();
10896 debug_assert_eq!(range.start.row(), range.end.row());
10897 let mut row = range.start.row();
10898 let positions =
10899 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10900 px(start)..px(end)
10901 } else {
10902 let start_x =
10903 display_map.x_for_display_point(range.start, &text_layout_details);
10904 let end_x =
10905 display_map.x_for_display_point(range.end, &text_layout_details);
10906 start_x.min(end_x)..start_x.max(end_x)
10907 };
10908
10909 while row != end_row {
10910 if above {
10911 row.0 -= 1;
10912 } else {
10913 row.0 += 1;
10914 }
10915
10916 if let Some(new_selection) = self.selections.build_columnar_selection(
10917 &display_map,
10918 row,
10919 &positions,
10920 selection.reversed,
10921 &text_layout_details,
10922 ) {
10923 state.stack.push(new_selection.id);
10924 if above {
10925 new_selections.push(new_selection);
10926 new_selections.push(selection);
10927 } else {
10928 new_selections.push(selection);
10929 new_selections.push(new_selection);
10930 }
10931
10932 continue 'outer;
10933 }
10934 }
10935 }
10936
10937 new_selections.push(selection);
10938 }
10939 } else {
10940 new_selections = selections;
10941 new_selections.retain(|s| s.id != last_added_selection);
10942 state.stack.pop();
10943 }
10944
10945 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10946 s.select(new_selections);
10947 });
10948 if state.stack.len() > 1 {
10949 self.add_selections_state = Some(state);
10950 }
10951 }
10952
10953 pub fn select_next_match_internal(
10954 &mut self,
10955 display_map: &DisplaySnapshot,
10956 replace_newest: bool,
10957 autoscroll: Option<Autoscroll>,
10958 window: &mut Window,
10959 cx: &mut Context<Self>,
10960 ) -> Result<()> {
10961 fn select_next_match_ranges(
10962 this: &mut Editor,
10963 range: Range<usize>,
10964 replace_newest: bool,
10965 auto_scroll: Option<Autoscroll>,
10966 window: &mut Window,
10967 cx: &mut Context<Editor>,
10968 ) {
10969 this.unfold_ranges(&[range.clone()], false, true, cx);
10970 this.change_selections(auto_scroll, window, cx, |s| {
10971 if replace_newest {
10972 s.delete(s.newest_anchor().id);
10973 }
10974 s.insert_range(range.clone());
10975 });
10976 }
10977
10978 let buffer = &display_map.buffer_snapshot;
10979 let mut selections = self.selections.all::<usize>(cx);
10980 if let Some(mut select_next_state) = self.select_next_state.take() {
10981 let query = &select_next_state.query;
10982 if !select_next_state.done {
10983 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10984 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10985 let mut next_selected_range = None;
10986
10987 let bytes_after_last_selection =
10988 buffer.bytes_in_range(last_selection.end..buffer.len());
10989 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10990 let query_matches = query
10991 .stream_find_iter(bytes_after_last_selection)
10992 .map(|result| (last_selection.end, result))
10993 .chain(
10994 query
10995 .stream_find_iter(bytes_before_first_selection)
10996 .map(|result| (0, result)),
10997 );
10998
10999 for (start_offset, query_match) in query_matches {
11000 let query_match = query_match.unwrap(); // can only fail due to I/O
11001 let offset_range =
11002 start_offset + query_match.start()..start_offset + query_match.end();
11003 let display_range = offset_range.start.to_display_point(display_map)
11004 ..offset_range.end.to_display_point(display_map);
11005
11006 if !select_next_state.wordwise
11007 || (!movement::is_inside_word(display_map, display_range.start)
11008 && !movement::is_inside_word(display_map, display_range.end))
11009 {
11010 // TODO: This is n^2, because we might check all the selections
11011 if !selections
11012 .iter()
11013 .any(|selection| selection.range().overlaps(&offset_range))
11014 {
11015 next_selected_range = Some(offset_range);
11016 break;
11017 }
11018 }
11019 }
11020
11021 if let Some(next_selected_range) = next_selected_range {
11022 select_next_match_ranges(
11023 self,
11024 next_selected_range,
11025 replace_newest,
11026 autoscroll,
11027 window,
11028 cx,
11029 );
11030 } else {
11031 select_next_state.done = true;
11032 }
11033 }
11034
11035 self.select_next_state = Some(select_next_state);
11036 } else {
11037 let mut only_carets = true;
11038 let mut same_text_selected = true;
11039 let mut selected_text = None;
11040
11041 let mut selections_iter = selections.iter().peekable();
11042 while let Some(selection) = selections_iter.next() {
11043 if selection.start != selection.end {
11044 only_carets = false;
11045 }
11046
11047 if same_text_selected {
11048 if selected_text.is_none() {
11049 selected_text =
11050 Some(buffer.text_for_range(selection.range()).collect::<String>());
11051 }
11052
11053 if let Some(next_selection) = selections_iter.peek() {
11054 if next_selection.range().len() == selection.range().len() {
11055 let next_selected_text = buffer
11056 .text_for_range(next_selection.range())
11057 .collect::<String>();
11058 if Some(next_selected_text) != selected_text {
11059 same_text_selected = false;
11060 selected_text = None;
11061 }
11062 } else {
11063 same_text_selected = false;
11064 selected_text = None;
11065 }
11066 }
11067 }
11068 }
11069
11070 if only_carets {
11071 for selection in &mut selections {
11072 let word_range = movement::surrounding_word(
11073 display_map,
11074 selection.start.to_display_point(display_map),
11075 );
11076 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11077 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11078 selection.goal = SelectionGoal::None;
11079 selection.reversed = false;
11080 select_next_match_ranges(
11081 self,
11082 selection.start..selection.end,
11083 replace_newest,
11084 autoscroll,
11085 window,
11086 cx,
11087 );
11088 }
11089
11090 if selections.len() == 1 {
11091 let selection = selections
11092 .last()
11093 .expect("ensured that there's only one selection");
11094 let query = buffer
11095 .text_for_range(selection.start..selection.end)
11096 .collect::<String>();
11097 let is_empty = query.is_empty();
11098 let select_state = SelectNextState {
11099 query: AhoCorasick::new(&[query])?,
11100 wordwise: true,
11101 done: is_empty,
11102 };
11103 self.select_next_state = Some(select_state);
11104 } else {
11105 self.select_next_state = None;
11106 }
11107 } else if let Some(selected_text) = selected_text {
11108 self.select_next_state = Some(SelectNextState {
11109 query: AhoCorasick::new(&[selected_text])?,
11110 wordwise: false,
11111 done: false,
11112 });
11113 self.select_next_match_internal(
11114 display_map,
11115 replace_newest,
11116 autoscroll,
11117 window,
11118 cx,
11119 )?;
11120 }
11121 }
11122 Ok(())
11123 }
11124
11125 pub fn select_all_matches(
11126 &mut self,
11127 _action: &SelectAllMatches,
11128 window: &mut Window,
11129 cx: &mut Context<Self>,
11130 ) -> Result<()> {
11131 self.push_to_selection_history();
11132 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11133
11134 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11135 let Some(select_next_state) = self.select_next_state.as_mut() else {
11136 return Ok(());
11137 };
11138 if select_next_state.done {
11139 return Ok(());
11140 }
11141
11142 let mut new_selections = self.selections.all::<usize>(cx);
11143
11144 let buffer = &display_map.buffer_snapshot;
11145 let query_matches = select_next_state
11146 .query
11147 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11148
11149 for query_match in query_matches {
11150 let query_match = query_match.unwrap(); // can only fail due to I/O
11151 let offset_range = query_match.start()..query_match.end();
11152 let display_range = offset_range.start.to_display_point(&display_map)
11153 ..offset_range.end.to_display_point(&display_map);
11154
11155 if !select_next_state.wordwise
11156 || (!movement::is_inside_word(&display_map, display_range.start)
11157 && !movement::is_inside_word(&display_map, display_range.end))
11158 {
11159 self.selections.change_with(cx, |selections| {
11160 new_selections.push(Selection {
11161 id: selections.new_selection_id(),
11162 start: offset_range.start,
11163 end: offset_range.end,
11164 reversed: false,
11165 goal: SelectionGoal::None,
11166 });
11167 });
11168 }
11169 }
11170
11171 new_selections.sort_by_key(|selection| selection.start);
11172 let mut ix = 0;
11173 while ix + 1 < new_selections.len() {
11174 let current_selection = &new_selections[ix];
11175 let next_selection = &new_selections[ix + 1];
11176 if current_selection.range().overlaps(&next_selection.range()) {
11177 if current_selection.id < next_selection.id {
11178 new_selections.remove(ix + 1);
11179 } else {
11180 new_selections.remove(ix);
11181 }
11182 } else {
11183 ix += 1;
11184 }
11185 }
11186
11187 let reversed = self.selections.oldest::<usize>(cx).reversed;
11188
11189 for selection in new_selections.iter_mut() {
11190 selection.reversed = reversed;
11191 }
11192
11193 select_next_state.done = true;
11194 self.unfold_ranges(
11195 &new_selections
11196 .iter()
11197 .map(|selection| selection.range())
11198 .collect::<Vec<_>>(),
11199 false,
11200 false,
11201 cx,
11202 );
11203 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11204 selections.select(new_selections)
11205 });
11206
11207 Ok(())
11208 }
11209
11210 pub fn select_next(
11211 &mut self,
11212 action: &SelectNext,
11213 window: &mut Window,
11214 cx: &mut Context<Self>,
11215 ) -> Result<()> {
11216 self.push_to_selection_history();
11217 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11218 self.select_next_match_internal(
11219 &display_map,
11220 action.replace_newest,
11221 Some(Autoscroll::newest()),
11222 window,
11223 cx,
11224 )?;
11225 Ok(())
11226 }
11227
11228 pub fn select_previous(
11229 &mut self,
11230 action: &SelectPrevious,
11231 window: &mut Window,
11232 cx: &mut Context<Self>,
11233 ) -> Result<()> {
11234 self.push_to_selection_history();
11235 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11236 let buffer = &display_map.buffer_snapshot;
11237 let mut selections = self.selections.all::<usize>(cx);
11238 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11239 let query = &select_prev_state.query;
11240 if !select_prev_state.done {
11241 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11242 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11243 let mut next_selected_range = None;
11244 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11245 let bytes_before_last_selection =
11246 buffer.reversed_bytes_in_range(0..last_selection.start);
11247 let bytes_after_first_selection =
11248 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11249 let query_matches = query
11250 .stream_find_iter(bytes_before_last_selection)
11251 .map(|result| (last_selection.start, result))
11252 .chain(
11253 query
11254 .stream_find_iter(bytes_after_first_selection)
11255 .map(|result| (buffer.len(), result)),
11256 );
11257 for (end_offset, query_match) in query_matches {
11258 let query_match = query_match.unwrap(); // can only fail due to I/O
11259 let offset_range =
11260 end_offset - query_match.end()..end_offset - query_match.start();
11261 let display_range = offset_range.start.to_display_point(&display_map)
11262 ..offset_range.end.to_display_point(&display_map);
11263
11264 if !select_prev_state.wordwise
11265 || (!movement::is_inside_word(&display_map, display_range.start)
11266 && !movement::is_inside_word(&display_map, display_range.end))
11267 {
11268 next_selected_range = Some(offset_range);
11269 break;
11270 }
11271 }
11272
11273 if let Some(next_selected_range) = next_selected_range {
11274 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11275 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11276 if action.replace_newest {
11277 s.delete(s.newest_anchor().id);
11278 }
11279 s.insert_range(next_selected_range);
11280 });
11281 } else {
11282 select_prev_state.done = true;
11283 }
11284 }
11285
11286 self.select_prev_state = Some(select_prev_state);
11287 } else {
11288 let mut only_carets = true;
11289 let mut same_text_selected = true;
11290 let mut selected_text = None;
11291
11292 let mut selections_iter = selections.iter().peekable();
11293 while let Some(selection) = selections_iter.next() {
11294 if selection.start != selection.end {
11295 only_carets = false;
11296 }
11297
11298 if same_text_selected {
11299 if selected_text.is_none() {
11300 selected_text =
11301 Some(buffer.text_for_range(selection.range()).collect::<String>());
11302 }
11303
11304 if let Some(next_selection) = selections_iter.peek() {
11305 if next_selection.range().len() == selection.range().len() {
11306 let next_selected_text = buffer
11307 .text_for_range(next_selection.range())
11308 .collect::<String>();
11309 if Some(next_selected_text) != selected_text {
11310 same_text_selected = false;
11311 selected_text = None;
11312 }
11313 } else {
11314 same_text_selected = false;
11315 selected_text = None;
11316 }
11317 }
11318 }
11319 }
11320
11321 if only_carets {
11322 for selection in &mut selections {
11323 let word_range = movement::surrounding_word(
11324 &display_map,
11325 selection.start.to_display_point(&display_map),
11326 );
11327 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11328 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11329 selection.goal = SelectionGoal::None;
11330 selection.reversed = false;
11331 }
11332 if selections.len() == 1 {
11333 let selection = selections
11334 .last()
11335 .expect("ensured that there's only one selection");
11336 let query = buffer
11337 .text_for_range(selection.start..selection.end)
11338 .collect::<String>();
11339 let is_empty = query.is_empty();
11340 let select_state = SelectNextState {
11341 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11342 wordwise: true,
11343 done: is_empty,
11344 };
11345 self.select_prev_state = Some(select_state);
11346 } else {
11347 self.select_prev_state = None;
11348 }
11349
11350 self.unfold_ranges(
11351 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11352 false,
11353 true,
11354 cx,
11355 );
11356 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11357 s.select(selections);
11358 });
11359 } else if let Some(selected_text) = selected_text {
11360 self.select_prev_state = Some(SelectNextState {
11361 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11362 wordwise: false,
11363 done: false,
11364 });
11365 self.select_previous(action, window, cx)?;
11366 }
11367 }
11368 Ok(())
11369 }
11370
11371 pub fn toggle_comments(
11372 &mut self,
11373 action: &ToggleComments,
11374 window: &mut Window,
11375 cx: &mut Context<Self>,
11376 ) {
11377 if self.read_only(cx) {
11378 return;
11379 }
11380 let text_layout_details = &self.text_layout_details(window);
11381 self.transact(window, cx, |this, window, cx| {
11382 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11383 let mut edits = Vec::new();
11384 let mut selection_edit_ranges = Vec::new();
11385 let mut last_toggled_row = None;
11386 let snapshot = this.buffer.read(cx).read(cx);
11387 let empty_str: Arc<str> = Arc::default();
11388 let mut suffixes_inserted = Vec::new();
11389 let ignore_indent = action.ignore_indent;
11390
11391 fn comment_prefix_range(
11392 snapshot: &MultiBufferSnapshot,
11393 row: MultiBufferRow,
11394 comment_prefix: &str,
11395 comment_prefix_whitespace: &str,
11396 ignore_indent: bool,
11397 ) -> Range<Point> {
11398 let indent_size = if ignore_indent {
11399 0
11400 } else {
11401 snapshot.indent_size_for_line(row).len
11402 };
11403
11404 let start = Point::new(row.0, indent_size);
11405
11406 let mut line_bytes = snapshot
11407 .bytes_in_range(start..snapshot.max_point())
11408 .flatten()
11409 .copied();
11410
11411 // If this line currently begins with the line comment prefix, then record
11412 // the range containing the prefix.
11413 if line_bytes
11414 .by_ref()
11415 .take(comment_prefix.len())
11416 .eq(comment_prefix.bytes())
11417 {
11418 // Include any whitespace that matches the comment prefix.
11419 let matching_whitespace_len = line_bytes
11420 .zip(comment_prefix_whitespace.bytes())
11421 .take_while(|(a, b)| a == b)
11422 .count() as u32;
11423 let end = Point::new(
11424 start.row,
11425 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11426 );
11427 start..end
11428 } else {
11429 start..start
11430 }
11431 }
11432
11433 fn comment_suffix_range(
11434 snapshot: &MultiBufferSnapshot,
11435 row: MultiBufferRow,
11436 comment_suffix: &str,
11437 comment_suffix_has_leading_space: bool,
11438 ) -> Range<Point> {
11439 let end = Point::new(row.0, snapshot.line_len(row));
11440 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11441
11442 let mut line_end_bytes = snapshot
11443 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11444 .flatten()
11445 .copied();
11446
11447 let leading_space_len = if suffix_start_column > 0
11448 && line_end_bytes.next() == Some(b' ')
11449 && comment_suffix_has_leading_space
11450 {
11451 1
11452 } else {
11453 0
11454 };
11455
11456 // If this line currently begins with the line comment prefix, then record
11457 // the range containing the prefix.
11458 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11459 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11460 start..end
11461 } else {
11462 end..end
11463 }
11464 }
11465
11466 // TODO: Handle selections that cross excerpts
11467 for selection in &mut selections {
11468 let start_column = snapshot
11469 .indent_size_for_line(MultiBufferRow(selection.start.row))
11470 .len;
11471 let language = if let Some(language) =
11472 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11473 {
11474 language
11475 } else {
11476 continue;
11477 };
11478
11479 selection_edit_ranges.clear();
11480
11481 // If multiple selections contain a given row, avoid processing that
11482 // row more than once.
11483 let mut start_row = MultiBufferRow(selection.start.row);
11484 if last_toggled_row == Some(start_row) {
11485 start_row = start_row.next_row();
11486 }
11487 let end_row =
11488 if selection.end.row > selection.start.row && selection.end.column == 0 {
11489 MultiBufferRow(selection.end.row - 1)
11490 } else {
11491 MultiBufferRow(selection.end.row)
11492 };
11493 last_toggled_row = Some(end_row);
11494
11495 if start_row > end_row {
11496 continue;
11497 }
11498
11499 // If the language has line comments, toggle those.
11500 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11501
11502 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11503 if ignore_indent {
11504 full_comment_prefixes = full_comment_prefixes
11505 .into_iter()
11506 .map(|s| Arc::from(s.trim_end()))
11507 .collect();
11508 }
11509
11510 if !full_comment_prefixes.is_empty() {
11511 let first_prefix = full_comment_prefixes
11512 .first()
11513 .expect("prefixes is non-empty");
11514 let prefix_trimmed_lengths = full_comment_prefixes
11515 .iter()
11516 .map(|p| p.trim_end_matches(' ').len())
11517 .collect::<SmallVec<[usize; 4]>>();
11518
11519 let mut all_selection_lines_are_comments = true;
11520
11521 for row in start_row.0..=end_row.0 {
11522 let row = MultiBufferRow(row);
11523 if start_row < end_row && snapshot.is_line_blank(row) {
11524 continue;
11525 }
11526
11527 let prefix_range = full_comment_prefixes
11528 .iter()
11529 .zip(prefix_trimmed_lengths.iter().copied())
11530 .map(|(prefix, trimmed_prefix_len)| {
11531 comment_prefix_range(
11532 snapshot.deref(),
11533 row,
11534 &prefix[..trimmed_prefix_len],
11535 &prefix[trimmed_prefix_len..],
11536 ignore_indent,
11537 )
11538 })
11539 .max_by_key(|range| range.end.column - range.start.column)
11540 .expect("prefixes is non-empty");
11541
11542 if prefix_range.is_empty() {
11543 all_selection_lines_are_comments = false;
11544 }
11545
11546 selection_edit_ranges.push(prefix_range);
11547 }
11548
11549 if all_selection_lines_are_comments {
11550 edits.extend(
11551 selection_edit_ranges
11552 .iter()
11553 .cloned()
11554 .map(|range| (range, empty_str.clone())),
11555 );
11556 } else {
11557 let min_column = selection_edit_ranges
11558 .iter()
11559 .map(|range| range.start.column)
11560 .min()
11561 .unwrap_or(0);
11562 edits.extend(selection_edit_ranges.iter().map(|range| {
11563 let position = Point::new(range.start.row, min_column);
11564 (position..position, first_prefix.clone())
11565 }));
11566 }
11567 } else if let Some((full_comment_prefix, comment_suffix)) =
11568 language.block_comment_delimiters()
11569 {
11570 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11571 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11572 let prefix_range = comment_prefix_range(
11573 snapshot.deref(),
11574 start_row,
11575 comment_prefix,
11576 comment_prefix_whitespace,
11577 ignore_indent,
11578 );
11579 let suffix_range = comment_suffix_range(
11580 snapshot.deref(),
11581 end_row,
11582 comment_suffix.trim_start_matches(' '),
11583 comment_suffix.starts_with(' '),
11584 );
11585
11586 if prefix_range.is_empty() || suffix_range.is_empty() {
11587 edits.push((
11588 prefix_range.start..prefix_range.start,
11589 full_comment_prefix.clone(),
11590 ));
11591 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11592 suffixes_inserted.push((end_row, comment_suffix.len()));
11593 } else {
11594 edits.push((prefix_range, empty_str.clone()));
11595 edits.push((suffix_range, empty_str.clone()));
11596 }
11597 } else {
11598 continue;
11599 }
11600 }
11601
11602 drop(snapshot);
11603 this.buffer.update(cx, |buffer, cx| {
11604 buffer.edit(edits, None, cx);
11605 });
11606
11607 // Adjust selections so that they end before any comment suffixes that
11608 // were inserted.
11609 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11610 let mut selections = this.selections.all::<Point>(cx);
11611 let snapshot = this.buffer.read(cx).read(cx);
11612 for selection in &mut selections {
11613 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11614 match row.cmp(&MultiBufferRow(selection.end.row)) {
11615 Ordering::Less => {
11616 suffixes_inserted.next();
11617 continue;
11618 }
11619 Ordering::Greater => break,
11620 Ordering::Equal => {
11621 if selection.end.column == snapshot.line_len(row) {
11622 if selection.is_empty() {
11623 selection.start.column -= suffix_len as u32;
11624 }
11625 selection.end.column -= suffix_len as u32;
11626 }
11627 break;
11628 }
11629 }
11630 }
11631 }
11632
11633 drop(snapshot);
11634 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11635 s.select(selections)
11636 });
11637
11638 let selections = this.selections.all::<Point>(cx);
11639 let selections_on_single_row = selections.windows(2).all(|selections| {
11640 selections[0].start.row == selections[1].start.row
11641 && selections[0].end.row == selections[1].end.row
11642 && selections[0].start.row == selections[0].end.row
11643 });
11644 let selections_selecting = selections
11645 .iter()
11646 .any(|selection| selection.start != selection.end);
11647 let advance_downwards = action.advance_downwards
11648 && selections_on_single_row
11649 && !selections_selecting
11650 && !matches!(this.mode, EditorMode::SingleLine { .. });
11651
11652 if advance_downwards {
11653 let snapshot = this.buffer.read(cx).snapshot(cx);
11654
11655 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11656 s.move_cursors_with(|display_snapshot, display_point, _| {
11657 let mut point = display_point.to_point(display_snapshot);
11658 point.row += 1;
11659 point = snapshot.clip_point(point, Bias::Left);
11660 let display_point = point.to_display_point(display_snapshot);
11661 let goal = SelectionGoal::HorizontalPosition(
11662 display_snapshot
11663 .x_for_display_point(display_point, text_layout_details)
11664 .into(),
11665 );
11666 (display_point, goal)
11667 })
11668 });
11669 }
11670 });
11671 }
11672
11673 pub fn select_enclosing_symbol(
11674 &mut self,
11675 _: &SelectEnclosingSymbol,
11676 window: &mut Window,
11677 cx: &mut Context<Self>,
11678 ) {
11679 let buffer = self.buffer.read(cx).snapshot(cx);
11680 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11681
11682 fn update_selection(
11683 selection: &Selection<usize>,
11684 buffer_snap: &MultiBufferSnapshot,
11685 ) -> Option<Selection<usize>> {
11686 let cursor = selection.head();
11687 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11688 for symbol in symbols.iter().rev() {
11689 let start = symbol.range.start.to_offset(buffer_snap);
11690 let end = symbol.range.end.to_offset(buffer_snap);
11691 let new_range = start..end;
11692 if start < selection.start || end > selection.end {
11693 return Some(Selection {
11694 id: selection.id,
11695 start: new_range.start,
11696 end: new_range.end,
11697 goal: SelectionGoal::None,
11698 reversed: selection.reversed,
11699 });
11700 }
11701 }
11702 None
11703 }
11704
11705 let mut selected_larger_symbol = false;
11706 let new_selections = old_selections
11707 .iter()
11708 .map(|selection| match update_selection(selection, &buffer) {
11709 Some(new_selection) => {
11710 if new_selection.range() != selection.range() {
11711 selected_larger_symbol = true;
11712 }
11713 new_selection
11714 }
11715 None => selection.clone(),
11716 })
11717 .collect::<Vec<_>>();
11718
11719 if selected_larger_symbol {
11720 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11721 s.select(new_selections);
11722 });
11723 }
11724 }
11725
11726 pub fn select_larger_syntax_node(
11727 &mut self,
11728 _: &SelectLargerSyntaxNode,
11729 window: &mut Window,
11730 cx: &mut Context<Self>,
11731 ) {
11732 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11733 let buffer = self.buffer.read(cx).snapshot(cx);
11734 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11735
11736 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11737 let mut selected_larger_node = false;
11738 let new_selections = old_selections
11739 .iter()
11740 .map(|selection| {
11741 let old_range = selection.start..selection.end;
11742 let mut new_range = old_range.clone();
11743 let mut new_node = None;
11744 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11745 {
11746 new_node = Some(node);
11747 new_range = match containing_range {
11748 MultiOrSingleBufferOffsetRange::Single(_) => break,
11749 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11750 };
11751 if !display_map.intersects_fold(new_range.start)
11752 && !display_map.intersects_fold(new_range.end)
11753 {
11754 break;
11755 }
11756 }
11757
11758 if let Some(node) = new_node {
11759 // Log the ancestor, to support using this action as a way to explore TreeSitter
11760 // nodes. Parent and grandparent are also logged because this operation will not
11761 // visit nodes that have the same range as their parent.
11762 log::info!("Node: {node:?}");
11763 let parent = node.parent();
11764 log::info!("Parent: {parent:?}");
11765 let grandparent = parent.and_then(|x| x.parent());
11766 log::info!("Grandparent: {grandparent:?}");
11767 }
11768
11769 selected_larger_node |= new_range != old_range;
11770 Selection {
11771 id: selection.id,
11772 start: new_range.start,
11773 end: new_range.end,
11774 goal: SelectionGoal::None,
11775 reversed: selection.reversed,
11776 }
11777 })
11778 .collect::<Vec<_>>();
11779
11780 if selected_larger_node {
11781 stack.push(old_selections);
11782 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11783 s.select(new_selections);
11784 });
11785 }
11786 self.select_larger_syntax_node_stack = stack;
11787 }
11788
11789 pub fn select_smaller_syntax_node(
11790 &mut self,
11791 _: &SelectSmallerSyntaxNode,
11792 window: &mut Window,
11793 cx: &mut Context<Self>,
11794 ) {
11795 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11796 if let Some(selections) = stack.pop() {
11797 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11798 s.select(selections.to_vec());
11799 });
11800 }
11801 self.select_larger_syntax_node_stack = stack;
11802 }
11803
11804 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11805 if !EditorSettings::get_global(cx).gutter.runnables {
11806 self.clear_tasks();
11807 return Task::ready(());
11808 }
11809 let project = self.project.as_ref().map(Entity::downgrade);
11810 cx.spawn_in(window, async move |this, cx| {
11811 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11812 let Some(project) = project.and_then(|p| p.upgrade()) else {
11813 return;
11814 };
11815 let Ok(display_snapshot) = this.update(cx, |this, cx| {
11816 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11817 }) else {
11818 return;
11819 };
11820
11821 let hide_runnables = project
11822 .update(cx, |project, cx| {
11823 // Do not display any test indicators in non-dev server remote projects.
11824 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11825 })
11826 .unwrap_or(true);
11827 if hide_runnables {
11828 return;
11829 }
11830 let new_rows =
11831 cx.background_spawn({
11832 let snapshot = display_snapshot.clone();
11833 async move {
11834 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11835 }
11836 })
11837 .await;
11838
11839 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11840 this.update(cx, |this, _| {
11841 this.clear_tasks();
11842 for (key, value) in rows {
11843 this.insert_tasks(key, value);
11844 }
11845 })
11846 .ok();
11847 })
11848 }
11849 fn fetch_runnable_ranges(
11850 snapshot: &DisplaySnapshot,
11851 range: Range<Anchor>,
11852 ) -> Vec<language::RunnableRange> {
11853 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11854 }
11855
11856 fn runnable_rows(
11857 project: Entity<Project>,
11858 snapshot: DisplaySnapshot,
11859 runnable_ranges: Vec<RunnableRange>,
11860 mut cx: AsyncWindowContext,
11861 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11862 runnable_ranges
11863 .into_iter()
11864 .filter_map(|mut runnable| {
11865 let tasks = cx
11866 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11867 .ok()?;
11868 if tasks.is_empty() {
11869 return None;
11870 }
11871
11872 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11873
11874 let row = snapshot
11875 .buffer_snapshot
11876 .buffer_line_for_row(MultiBufferRow(point.row))?
11877 .1
11878 .start
11879 .row;
11880
11881 let context_range =
11882 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11883 Some((
11884 (runnable.buffer_id, row),
11885 RunnableTasks {
11886 templates: tasks,
11887 offset: snapshot
11888 .buffer_snapshot
11889 .anchor_before(runnable.run_range.start),
11890 context_range,
11891 column: point.column,
11892 extra_variables: runnable.extra_captures,
11893 },
11894 ))
11895 })
11896 .collect()
11897 }
11898
11899 fn templates_with_tags(
11900 project: &Entity<Project>,
11901 runnable: &mut Runnable,
11902 cx: &mut App,
11903 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11904 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11905 let (worktree_id, file) = project
11906 .buffer_for_id(runnable.buffer, cx)
11907 .and_then(|buffer| buffer.read(cx).file())
11908 .map(|file| (file.worktree_id(cx), file.clone()))
11909 .unzip();
11910
11911 (
11912 project.task_store().read(cx).task_inventory().cloned(),
11913 worktree_id,
11914 file,
11915 )
11916 });
11917
11918 let tags = mem::take(&mut runnable.tags);
11919 let mut tags: Vec<_> = tags
11920 .into_iter()
11921 .flat_map(|tag| {
11922 let tag = tag.0.clone();
11923 inventory
11924 .as_ref()
11925 .into_iter()
11926 .flat_map(|inventory| {
11927 inventory.read(cx).list_tasks(
11928 file.clone(),
11929 Some(runnable.language.clone()),
11930 worktree_id,
11931 cx,
11932 )
11933 })
11934 .filter(move |(_, template)| {
11935 template.tags.iter().any(|source_tag| source_tag == &tag)
11936 })
11937 })
11938 .sorted_by_key(|(kind, _)| kind.to_owned())
11939 .collect();
11940 if let Some((leading_tag_source, _)) = tags.first() {
11941 // Strongest source wins; if we have worktree tag binding, prefer that to
11942 // global and language bindings;
11943 // if we have a global binding, prefer that to language binding.
11944 let first_mismatch = tags
11945 .iter()
11946 .position(|(tag_source, _)| tag_source != leading_tag_source);
11947 if let Some(index) = first_mismatch {
11948 tags.truncate(index);
11949 }
11950 }
11951
11952 tags
11953 }
11954
11955 pub fn move_to_enclosing_bracket(
11956 &mut self,
11957 _: &MoveToEnclosingBracket,
11958 window: &mut Window,
11959 cx: &mut Context<Self>,
11960 ) {
11961 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11962 s.move_offsets_with(|snapshot, selection| {
11963 let Some(enclosing_bracket_ranges) =
11964 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11965 else {
11966 return;
11967 };
11968
11969 let mut best_length = usize::MAX;
11970 let mut best_inside = false;
11971 let mut best_in_bracket_range = false;
11972 let mut best_destination = None;
11973 for (open, close) in enclosing_bracket_ranges {
11974 let close = close.to_inclusive();
11975 let length = close.end() - open.start;
11976 let inside = selection.start >= open.end && selection.end <= *close.start();
11977 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11978 || close.contains(&selection.head());
11979
11980 // If best is next to a bracket and current isn't, skip
11981 if !in_bracket_range && best_in_bracket_range {
11982 continue;
11983 }
11984
11985 // Prefer smaller lengths unless best is inside and current isn't
11986 if length > best_length && (best_inside || !inside) {
11987 continue;
11988 }
11989
11990 best_length = length;
11991 best_inside = inside;
11992 best_in_bracket_range = in_bracket_range;
11993 best_destination = Some(
11994 if close.contains(&selection.start) && close.contains(&selection.end) {
11995 if inside {
11996 open.end
11997 } else {
11998 open.start
11999 }
12000 } else if inside {
12001 *close.start()
12002 } else {
12003 *close.end()
12004 },
12005 );
12006 }
12007
12008 if let Some(destination) = best_destination {
12009 selection.collapse_to(destination, SelectionGoal::None);
12010 }
12011 })
12012 });
12013 }
12014
12015 pub fn undo_selection(
12016 &mut self,
12017 _: &UndoSelection,
12018 window: &mut Window,
12019 cx: &mut Context<Self>,
12020 ) {
12021 self.end_selection(window, cx);
12022 self.selection_history.mode = SelectionHistoryMode::Undoing;
12023 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12024 self.change_selections(None, window, cx, |s| {
12025 s.select_anchors(entry.selections.to_vec())
12026 });
12027 self.select_next_state = entry.select_next_state;
12028 self.select_prev_state = entry.select_prev_state;
12029 self.add_selections_state = entry.add_selections_state;
12030 self.request_autoscroll(Autoscroll::newest(), cx);
12031 }
12032 self.selection_history.mode = SelectionHistoryMode::Normal;
12033 }
12034
12035 pub fn redo_selection(
12036 &mut self,
12037 _: &RedoSelection,
12038 window: &mut Window,
12039 cx: &mut Context<Self>,
12040 ) {
12041 self.end_selection(window, cx);
12042 self.selection_history.mode = SelectionHistoryMode::Redoing;
12043 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12044 self.change_selections(None, window, cx, |s| {
12045 s.select_anchors(entry.selections.to_vec())
12046 });
12047 self.select_next_state = entry.select_next_state;
12048 self.select_prev_state = entry.select_prev_state;
12049 self.add_selections_state = entry.add_selections_state;
12050 self.request_autoscroll(Autoscroll::newest(), cx);
12051 }
12052 self.selection_history.mode = SelectionHistoryMode::Normal;
12053 }
12054
12055 pub fn expand_excerpts(
12056 &mut self,
12057 action: &ExpandExcerpts,
12058 _: &mut Window,
12059 cx: &mut Context<Self>,
12060 ) {
12061 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12062 }
12063
12064 pub fn expand_excerpts_down(
12065 &mut self,
12066 action: &ExpandExcerptsDown,
12067 _: &mut Window,
12068 cx: &mut Context<Self>,
12069 ) {
12070 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12071 }
12072
12073 pub fn expand_excerpts_up(
12074 &mut self,
12075 action: &ExpandExcerptsUp,
12076 _: &mut Window,
12077 cx: &mut Context<Self>,
12078 ) {
12079 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12080 }
12081
12082 pub fn expand_excerpts_for_direction(
12083 &mut self,
12084 lines: u32,
12085 direction: ExpandExcerptDirection,
12086
12087 cx: &mut Context<Self>,
12088 ) {
12089 let selections = self.selections.disjoint_anchors();
12090
12091 let lines = if lines == 0 {
12092 EditorSettings::get_global(cx).expand_excerpt_lines
12093 } else {
12094 lines
12095 };
12096
12097 self.buffer.update(cx, |buffer, cx| {
12098 let snapshot = buffer.snapshot(cx);
12099 let mut excerpt_ids = selections
12100 .iter()
12101 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12102 .collect::<Vec<_>>();
12103 excerpt_ids.sort();
12104 excerpt_ids.dedup();
12105 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12106 })
12107 }
12108
12109 pub fn expand_excerpt(
12110 &mut self,
12111 excerpt: ExcerptId,
12112 direction: ExpandExcerptDirection,
12113 cx: &mut Context<Self>,
12114 ) {
12115 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12116 self.buffer.update(cx, |buffer, cx| {
12117 buffer.expand_excerpts([excerpt], lines, direction, cx)
12118 })
12119 }
12120
12121 pub fn go_to_singleton_buffer_point(
12122 &mut self,
12123 point: Point,
12124 window: &mut Window,
12125 cx: &mut Context<Self>,
12126 ) {
12127 self.go_to_singleton_buffer_range(point..point, window, cx);
12128 }
12129
12130 pub fn go_to_singleton_buffer_range(
12131 &mut self,
12132 range: Range<Point>,
12133 window: &mut Window,
12134 cx: &mut Context<Self>,
12135 ) {
12136 let multibuffer = self.buffer().read(cx);
12137 let Some(buffer) = multibuffer.as_singleton() else {
12138 return;
12139 };
12140 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12141 return;
12142 };
12143 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12144 return;
12145 };
12146 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12147 s.select_anchor_ranges([start..end])
12148 });
12149 }
12150
12151 fn go_to_diagnostic(
12152 &mut self,
12153 _: &GoToDiagnostic,
12154 window: &mut Window,
12155 cx: &mut Context<Self>,
12156 ) {
12157 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12158 }
12159
12160 fn go_to_prev_diagnostic(
12161 &mut self,
12162 _: &GoToPreviousDiagnostic,
12163 window: &mut Window,
12164 cx: &mut Context<Self>,
12165 ) {
12166 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12167 }
12168
12169 pub fn go_to_diagnostic_impl(
12170 &mut self,
12171 direction: Direction,
12172 window: &mut Window,
12173 cx: &mut Context<Self>,
12174 ) {
12175 let buffer = self.buffer.read(cx).snapshot(cx);
12176 let selection = self.selections.newest::<usize>(cx);
12177
12178 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12179 if direction == Direction::Next {
12180 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12181 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12182 return;
12183 };
12184 self.activate_diagnostics(
12185 buffer_id,
12186 popover.local_diagnostic.diagnostic.group_id,
12187 window,
12188 cx,
12189 );
12190 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12191 let primary_range_start = active_diagnostics.primary_range.start;
12192 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12193 let mut new_selection = s.newest_anchor().clone();
12194 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12195 s.select_anchors(vec![new_selection.clone()]);
12196 });
12197 self.refresh_inline_completion(false, true, window, cx);
12198 }
12199 return;
12200 }
12201 }
12202
12203 let active_group_id = self
12204 .active_diagnostics
12205 .as_ref()
12206 .map(|active_group| active_group.group_id);
12207 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12208 active_diagnostics
12209 .primary_range
12210 .to_offset(&buffer)
12211 .to_inclusive()
12212 });
12213 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12214 if active_primary_range.contains(&selection.head()) {
12215 *active_primary_range.start()
12216 } else {
12217 selection.head()
12218 }
12219 } else {
12220 selection.head()
12221 };
12222
12223 let snapshot = self.snapshot(window, cx);
12224 let primary_diagnostics_before = buffer
12225 .diagnostics_in_range::<usize>(0..search_start)
12226 .filter(|entry| entry.diagnostic.is_primary)
12227 .filter(|entry| entry.range.start != entry.range.end)
12228 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12229 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12230 .collect::<Vec<_>>();
12231 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12232 primary_diagnostics_before
12233 .iter()
12234 .position(|entry| entry.diagnostic.group_id == active_group_id)
12235 });
12236
12237 let primary_diagnostics_after = buffer
12238 .diagnostics_in_range::<usize>(search_start..buffer.len())
12239 .filter(|entry| entry.diagnostic.is_primary)
12240 .filter(|entry| entry.range.start != entry.range.end)
12241 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12242 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12243 .collect::<Vec<_>>();
12244 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12245 primary_diagnostics_after
12246 .iter()
12247 .enumerate()
12248 .rev()
12249 .find_map(|(i, entry)| {
12250 if entry.diagnostic.group_id == active_group_id {
12251 Some(i)
12252 } else {
12253 None
12254 }
12255 })
12256 });
12257
12258 let next_primary_diagnostic = match direction {
12259 Direction::Prev => primary_diagnostics_before
12260 .iter()
12261 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12262 .rev()
12263 .next(),
12264 Direction::Next => primary_diagnostics_after
12265 .iter()
12266 .skip(
12267 last_same_group_diagnostic_after
12268 .map(|index| index + 1)
12269 .unwrap_or(0),
12270 )
12271 .next(),
12272 };
12273
12274 // Cycle around to the start of the buffer, potentially moving back to the start of
12275 // the currently active diagnostic.
12276 let cycle_around = || match direction {
12277 Direction::Prev => primary_diagnostics_after
12278 .iter()
12279 .rev()
12280 .chain(primary_diagnostics_before.iter().rev())
12281 .next(),
12282 Direction::Next => primary_diagnostics_before
12283 .iter()
12284 .chain(primary_diagnostics_after.iter())
12285 .next(),
12286 };
12287
12288 if let Some((primary_range, group_id)) = next_primary_diagnostic
12289 .or_else(cycle_around)
12290 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12291 {
12292 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12293 return;
12294 };
12295 self.activate_diagnostics(buffer_id, group_id, window, cx);
12296 if self.active_diagnostics.is_some() {
12297 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12298 s.select(vec![Selection {
12299 id: selection.id,
12300 start: primary_range.start,
12301 end: primary_range.start,
12302 reversed: false,
12303 goal: SelectionGoal::None,
12304 }]);
12305 });
12306 self.refresh_inline_completion(false, true, window, cx);
12307 }
12308 }
12309 }
12310
12311 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12312 let snapshot = self.snapshot(window, cx);
12313 let selection = self.selections.newest::<Point>(cx);
12314 self.go_to_hunk_before_or_after_position(
12315 &snapshot,
12316 selection.head(),
12317 Direction::Next,
12318 window,
12319 cx,
12320 );
12321 }
12322
12323 fn go_to_hunk_before_or_after_position(
12324 &mut self,
12325 snapshot: &EditorSnapshot,
12326 position: Point,
12327 direction: Direction,
12328 window: &mut Window,
12329 cx: &mut Context<Editor>,
12330 ) {
12331 let row = if direction == Direction::Next {
12332 self.hunk_after_position(snapshot, position)
12333 .map(|hunk| hunk.row_range.start)
12334 } else {
12335 self.hunk_before_position(snapshot, position)
12336 };
12337
12338 if let Some(row) = row {
12339 let destination = Point::new(row.0, 0);
12340 let autoscroll = Autoscroll::center();
12341
12342 self.unfold_ranges(&[destination..destination], false, false, cx);
12343 self.change_selections(Some(autoscroll), window, cx, |s| {
12344 s.select_ranges([destination..destination]);
12345 });
12346 }
12347 }
12348
12349 fn hunk_after_position(
12350 &mut self,
12351 snapshot: &EditorSnapshot,
12352 position: Point,
12353 ) -> Option<MultiBufferDiffHunk> {
12354 snapshot
12355 .buffer_snapshot
12356 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12357 .find(|hunk| hunk.row_range.start.0 > position.row)
12358 .or_else(|| {
12359 snapshot
12360 .buffer_snapshot
12361 .diff_hunks_in_range(Point::zero()..position)
12362 .find(|hunk| hunk.row_range.end.0 < position.row)
12363 })
12364 }
12365
12366 fn go_to_prev_hunk(
12367 &mut self,
12368 _: &GoToPreviousHunk,
12369 window: &mut Window,
12370 cx: &mut Context<Self>,
12371 ) {
12372 let snapshot = self.snapshot(window, cx);
12373 let selection = self.selections.newest::<Point>(cx);
12374 self.go_to_hunk_before_or_after_position(
12375 &snapshot,
12376 selection.head(),
12377 Direction::Prev,
12378 window,
12379 cx,
12380 );
12381 }
12382
12383 fn hunk_before_position(
12384 &mut self,
12385 snapshot: &EditorSnapshot,
12386 position: Point,
12387 ) -> Option<MultiBufferRow> {
12388 snapshot
12389 .buffer_snapshot
12390 .diff_hunk_before(position)
12391 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12392 }
12393
12394 fn go_to_line<T: 'static>(
12395 &mut self,
12396 position: Anchor,
12397 highlight_color: Option<Hsla>,
12398 window: &mut Window,
12399 cx: &mut Context<Self>,
12400 ) {
12401 let snapshot = self.snapshot(window, cx).display_snapshot;
12402 let position = position.to_point(&snapshot.buffer_snapshot);
12403 let start = snapshot
12404 .buffer_snapshot
12405 .clip_point(Point::new(position.row, 0), Bias::Left);
12406 let end = start + Point::new(1, 0);
12407 let start = snapshot.buffer_snapshot.anchor_before(start);
12408 let end = snapshot.buffer_snapshot.anchor_before(end);
12409
12410 self.clear_row_highlights::<T>();
12411 self.highlight_rows::<T>(
12412 start..end,
12413 highlight_color
12414 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12415 true,
12416 cx,
12417 );
12418 self.request_autoscroll(Autoscroll::center(), cx);
12419 }
12420
12421 pub fn go_to_definition(
12422 &mut self,
12423 _: &GoToDefinition,
12424 window: &mut Window,
12425 cx: &mut Context<Self>,
12426 ) -> Task<Result<Navigated>> {
12427 let definition =
12428 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12429 cx.spawn_in(window, async move |editor, cx| {
12430 if definition.await? == Navigated::Yes {
12431 return Ok(Navigated::Yes);
12432 }
12433 match editor.update_in(cx, |editor, window, cx| {
12434 editor.find_all_references(&FindAllReferences, window, cx)
12435 })? {
12436 Some(references) => references.await,
12437 None => Ok(Navigated::No),
12438 }
12439 })
12440 }
12441
12442 pub fn go_to_declaration(
12443 &mut self,
12444 _: &GoToDeclaration,
12445 window: &mut Window,
12446 cx: &mut Context<Self>,
12447 ) -> Task<Result<Navigated>> {
12448 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12449 }
12450
12451 pub fn go_to_declaration_split(
12452 &mut self,
12453 _: &GoToDeclaration,
12454 window: &mut Window,
12455 cx: &mut Context<Self>,
12456 ) -> Task<Result<Navigated>> {
12457 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12458 }
12459
12460 pub fn go_to_implementation(
12461 &mut self,
12462 _: &GoToImplementation,
12463 window: &mut Window,
12464 cx: &mut Context<Self>,
12465 ) -> Task<Result<Navigated>> {
12466 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12467 }
12468
12469 pub fn go_to_implementation_split(
12470 &mut self,
12471 _: &GoToImplementationSplit,
12472 window: &mut Window,
12473 cx: &mut Context<Self>,
12474 ) -> Task<Result<Navigated>> {
12475 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12476 }
12477
12478 pub fn go_to_type_definition(
12479 &mut self,
12480 _: &GoToTypeDefinition,
12481 window: &mut Window,
12482 cx: &mut Context<Self>,
12483 ) -> Task<Result<Navigated>> {
12484 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12485 }
12486
12487 pub fn go_to_definition_split(
12488 &mut self,
12489 _: &GoToDefinitionSplit,
12490 window: &mut Window,
12491 cx: &mut Context<Self>,
12492 ) -> Task<Result<Navigated>> {
12493 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12494 }
12495
12496 pub fn go_to_type_definition_split(
12497 &mut self,
12498 _: &GoToTypeDefinitionSplit,
12499 window: &mut Window,
12500 cx: &mut Context<Self>,
12501 ) -> Task<Result<Navigated>> {
12502 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12503 }
12504
12505 fn go_to_definition_of_kind(
12506 &mut self,
12507 kind: GotoDefinitionKind,
12508 split: bool,
12509 window: &mut Window,
12510 cx: &mut Context<Self>,
12511 ) -> Task<Result<Navigated>> {
12512 let Some(provider) = self.semantics_provider.clone() else {
12513 return Task::ready(Ok(Navigated::No));
12514 };
12515 let head = self.selections.newest::<usize>(cx).head();
12516 let buffer = self.buffer.read(cx);
12517 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12518 text_anchor
12519 } else {
12520 return Task::ready(Ok(Navigated::No));
12521 };
12522
12523 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12524 return Task::ready(Ok(Navigated::No));
12525 };
12526
12527 cx.spawn_in(window, async move |editor, cx| {
12528 let definitions = definitions.await?;
12529 let navigated = editor
12530 .update_in(cx, |editor, window, cx| {
12531 editor.navigate_to_hover_links(
12532 Some(kind),
12533 definitions
12534 .into_iter()
12535 .filter(|location| {
12536 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12537 })
12538 .map(HoverLink::Text)
12539 .collect::<Vec<_>>(),
12540 split,
12541 window,
12542 cx,
12543 )
12544 })?
12545 .await?;
12546 anyhow::Ok(navigated)
12547 })
12548 }
12549
12550 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12551 let selection = self.selections.newest_anchor();
12552 let head = selection.head();
12553 let tail = selection.tail();
12554
12555 let Some((buffer, start_position)) =
12556 self.buffer.read(cx).text_anchor_for_position(head, cx)
12557 else {
12558 return;
12559 };
12560
12561 let end_position = if head != tail {
12562 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12563 return;
12564 };
12565 Some(pos)
12566 } else {
12567 None
12568 };
12569
12570 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12571 let url = if let Some(end_pos) = end_position {
12572 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12573 } else {
12574 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12575 };
12576
12577 if let Some(url) = url {
12578 editor.update(cx, |_, cx| {
12579 cx.open_url(&url);
12580 })
12581 } else {
12582 Ok(())
12583 }
12584 });
12585
12586 url_finder.detach();
12587 }
12588
12589 pub fn open_selected_filename(
12590 &mut self,
12591 _: &OpenSelectedFilename,
12592 window: &mut Window,
12593 cx: &mut Context<Self>,
12594 ) {
12595 let Some(workspace) = self.workspace() else {
12596 return;
12597 };
12598
12599 let position = self.selections.newest_anchor().head();
12600
12601 let Some((buffer, buffer_position)) =
12602 self.buffer.read(cx).text_anchor_for_position(position, cx)
12603 else {
12604 return;
12605 };
12606
12607 let project = self.project.clone();
12608
12609 cx.spawn_in(window, async move |_, cx| {
12610 let result = find_file(&buffer, project, buffer_position, cx).await;
12611
12612 if let Some((_, path)) = result {
12613 workspace
12614 .update_in(cx, |workspace, window, cx| {
12615 workspace.open_resolved_path(path, window, cx)
12616 })?
12617 .await?;
12618 }
12619 anyhow::Ok(())
12620 })
12621 .detach();
12622 }
12623
12624 pub(crate) fn navigate_to_hover_links(
12625 &mut self,
12626 kind: Option<GotoDefinitionKind>,
12627 mut definitions: Vec<HoverLink>,
12628 split: bool,
12629 window: &mut Window,
12630 cx: &mut Context<Editor>,
12631 ) -> Task<Result<Navigated>> {
12632 // If there is one definition, just open it directly
12633 if definitions.len() == 1 {
12634 let definition = definitions.pop().unwrap();
12635
12636 enum TargetTaskResult {
12637 Location(Option<Location>),
12638 AlreadyNavigated,
12639 }
12640
12641 let target_task = match definition {
12642 HoverLink::Text(link) => {
12643 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12644 }
12645 HoverLink::InlayHint(lsp_location, server_id) => {
12646 let computation =
12647 self.compute_target_location(lsp_location, server_id, window, cx);
12648 cx.background_spawn(async move {
12649 let location = computation.await?;
12650 Ok(TargetTaskResult::Location(location))
12651 })
12652 }
12653 HoverLink::Url(url) => {
12654 cx.open_url(&url);
12655 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12656 }
12657 HoverLink::File(path) => {
12658 if let Some(workspace) = self.workspace() {
12659 cx.spawn_in(window, async move |_, cx| {
12660 workspace
12661 .update_in(cx, |workspace, window, cx| {
12662 workspace.open_resolved_path(path, window, cx)
12663 })?
12664 .await
12665 .map(|_| TargetTaskResult::AlreadyNavigated)
12666 })
12667 } else {
12668 Task::ready(Ok(TargetTaskResult::Location(None)))
12669 }
12670 }
12671 };
12672 cx.spawn_in(window, async move |editor, cx| {
12673 let target = match target_task.await.context("target resolution task")? {
12674 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12675 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12676 TargetTaskResult::Location(Some(target)) => target,
12677 };
12678
12679 editor.update_in(cx, |editor, window, cx| {
12680 let Some(workspace) = editor.workspace() else {
12681 return Navigated::No;
12682 };
12683 let pane = workspace.read(cx).active_pane().clone();
12684
12685 let range = target.range.to_point(target.buffer.read(cx));
12686 let range = editor.range_for_match(&range);
12687 let range = collapse_multiline_range(range);
12688
12689 if !split
12690 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12691 {
12692 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12693 } else {
12694 window.defer(cx, move |window, cx| {
12695 let target_editor: Entity<Self> =
12696 workspace.update(cx, |workspace, cx| {
12697 let pane = if split {
12698 workspace.adjacent_pane(window, cx)
12699 } else {
12700 workspace.active_pane().clone()
12701 };
12702
12703 workspace.open_project_item(
12704 pane,
12705 target.buffer.clone(),
12706 true,
12707 true,
12708 window,
12709 cx,
12710 )
12711 });
12712 target_editor.update(cx, |target_editor, cx| {
12713 // When selecting a definition in a different buffer, disable the nav history
12714 // to avoid creating a history entry at the previous cursor location.
12715 pane.update(cx, |pane, _| pane.disable_history());
12716 target_editor.go_to_singleton_buffer_range(range, window, cx);
12717 pane.update(cx, |pane, _| pane.enable_history());
12718 });
12719 });
12720 }
12721 Navigated::Yes
12722 })
12723 })
12724 } else if !definitions.is_empty() {
12725 cx.spawn_in(window, async move |editor, cx| {
12726 let (title, location_tasks, workspace) = editor
12727 .update_in(cx, |editor, window, cx| {
12728 let tab_kind = match kind {
12729 Some(GotoDefinitionKind::Implementation) => "Implementations",
12730 _ => "Definitions",
12731 };
12732 let title = definitions
12733 .iter()
12734 .find_map(|definition| match definition {
12735 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12736 let buffer = origin.buffer.read(cx);
12737 format!(
12738 "{} for {}",
12739 tab_kind,
12740 buffer
12741 .text_for_range(origin.range.clone())
12742 .collect::<String>()
12743 )
12744 }),
12745 HoverLink::InlayHint(_, _) => None,
12746 HoverLink::Url(_) => None,
12747 HoverLink::File(_) => None,
12748 })
12749 .unwrap_or(tab_kind.to_string());
12750 let location_tasks = definitions
12751 .into_iter()
12752 .map(|definition| match definition {
12753 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12754 HoverLink::InlayHint(lsp_location, server_id) => editor
12755 .compute_target_location(lsp_location, server_id, window, cx),
12756 HoverLink::Url(_) => Task::ready(Ok(None)),
12757 HoverLink::File(_) => Task::ready(Ok(None)),
12758 })
12759 .collect::<Vec<_>>();
12760 (title, location_tasks, editor.workspace().clone())
12761 })
12762 .context("location tasks preparation")?;
12763
12764 let locations = future::join_all(location_tasks)
12765 .await
12766 .into_iter()
12767 .filter_map(|location| location.transpose())
12768 .collect::<Result<_>>()
12769 .context("location tasks")?;
12770
12771 let Some(workspace) = workspace else {
12772 return Ok(Navigated::No);
12773 };
12774 let opened = workspace
12775 .update_in(cx, |workspace, window, cx| {
12776 Self::open_locations_in_multibuffer(
12777 workspace,
12778 locations,
12779 title,
12780 split,
12781 MultibufferSelectionMode::First,
12782 window,
12783 cx,
12784 )
12785 })
12786 .ok();
12787
12788 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12789 })
12790 } else {
12791 Task::ready(Ok(Navigated::No))
12792 }
12793 }
12794
12795 fn compute_target_location(
12796 &self,
12797 lsp_location: lsp::Location,
12798 server_id: LanguageServerId,
12799 window: &mut Window,
12800 cx: &mut Context<Self>,
12801 ) -> Task<anyhow::Result<Option<Location>>> {
12802 let Some(project) = self.project.clone() else {
12803 return Task::ready(Ok(None));
12804 };
12805
12806 cx.spawn_in(window, async move |editor, cx| {
12807 let location_task = editor.update(cx, |_, cx| {
12808 project.update(cx, |project, cx| {
12809 let language_server_name = project
12810 .language_server_statuses(cx)
12811 .find(|(id, _)| server_id == *id)
12812 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12813 language_server_name.map(|language_server_name| {
12814 project.open_local_buffer_via_lsp(
12815 lsp_location.uri.clone(),
12816 server_id,
12817 language_server_name,
12818 cx,
12819 )
12820 })
12821 })
12822 })?;
12823 let location = match location_task {
12824 Some(task) => Some({
12825 let target_buffer_handle = task.await.context("open local buffer")?;
12826 let range = target_buffer_handle.update(cx, |target_buffer, _| {
12827 let target_start = target_buffer
12828 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12829 let target_end = target_buffer
12830 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12831 target_buffer.anchor_after(target_start)
12832 ..target_buffer.anchor_before(target_end)
12833 })?;
12834 Location {
12835 buffer: target_buffer_handle,
12836 range,
12837 }
12838 }),
12839 None => None,
12840 };
12841 Ok(location)
12842 })
12843 }
12844
12845 pub fn find_all_references(
12846 &mut self,
12847 _: &FindAllReferences,
12848 window: &mut Window,
12849 cx: &mut Context<Self>,
12850 ) -> Option<Task<Result<Navigated>>> {
12851 let selection = self.selections.newest::<usize>(cx);
12852 let multi_buffer = self.buffer.read(cx);
12853 let head = selection.head();
12854
12855 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12856 let head_anchor = multi_buffer_snapshot.anchor_at(
12857 head,
12858 if head < selection.tail() {
12859 Bias::Right
12860 } else {
12861 Bias::Left
12862 },
12863 );
12864
12865 match self
12866 .find_all_references_task_sources
12867 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12868 {
12869 Ok(_) => {
12870 log::info!(
12871 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12872 );
12873 return None;
12874 }
12875 Err(i) => {
12876 self.find_all_references_task_sources.insert(i, head_anchor);
12877 }
12878 }
12879
12880 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12881 let workspace = self.workspace()?;
12882 let project = workspace.read(cx).project().clone();
12883 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12884 Some(cx.spawn_in(window, async move |editor, cx| {
12885 let _cleanup = cx.on_drop(&editor, move |editor, _| {
12886 if let Ok(i) = editor
12887 .find_all_references_task_sources
12888 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12889 {
12890 editor.find_all_references_task_sources.remove(i);
12891 }
12892 });
12893
12894 let locations = references.await?;
12895 if locations.is_empty() {
12896 return anyhow::Ok(Navigated::No);
12897 }
12898
12899 workspace.update_in(cx, |workspace, window, cx| {
12900 let title = locations
12901 .first()
12902 .as_ref()
12903 .map(|location| {
12904 let buffer = location.buffer.read(cx);
12905 format!(
12906 "References to `{}`",
12907 buffer
12908 .text_for_range(location.range.clone())
12909 .collect::<String>()
12910 )
12911 })
12912 .unwrap();
12913 Self::open_locations_in_multibuffer(
12914 workspace,
12915 locations,
12916 title,
12917 false,
12918 MultibufferSelectionMode::First,
12919 window,
12920 cx,
12921 );
12922 Navigated::Yes
12923 })
12924 }))
12925 }
12926
12927 /// Opens a multibuffer with the given project locations in it
12928 pub fn open_locations_in_multibuffer(
12929 workspace: &mut Workspace,
12930 mut locations: Vec<Location>,
12931 title: String,
12932 split: bool,
12933 multibuffer_selection_mode: MultibufferSelectionMode,
12934 window: &mut Window,
12935 cx: &mut Context<Workspace>,
12936 ) {
12937 // If there are multiple definitions, open them in a multibuffer
12938 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12939 let mut locations = locations.into_iter().peekable();
12940 let mut ranges = Vec::new();
12941 let capability = workspace.project().read(cx).capability();
12942
12943 let excerpt_buffer = cx.new(|cx| {
12944 let mut multibuffer = MultiBuffer::new(capability);
12945 while let Some(location) = locations.next() {
12946 let buffer = location.buffer.read(cx);
12947 let mut ranges_for_buffer = Vec::new();
12948 let range = location.range.to_offset(buffer);
12949 ranges_for_buffer.push(range.clone());
12950
12951 while let Some(next_location) = locations.peek() {
12952 if next_location.buffer == location.buffer {
12953 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12954 locations.next();
12955 } else {
12956 break;
12957 }
12958 }
12959
12960 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12961 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12962 location.buffer.clone(),
12963 ranges_for_buffer,
12964 DEFAULT_MULTIBUFFER_CONTEXT,
12965 cx,
12966 ))
12967 }
12968
12969 multibuffer.with_title(title)
12970 });
12971
12972 let editor = cx.new(|cx| {
12973 Editor::for_multibuffer(
12974 excerpt_buffer,
12975 Some(workspace.project().clone()),
12976 window,
12977 cx,
12978 )
12979 });
12980 editor.update(cx, |editor, cx| {
12981 match multibuffer_selection_mode {
12982 MultibufferSelectionMode::First => {
12983 if let Some(first_range) = ranges.first() {
12984 editor.change_selections(None, window, cx, |selections| {
12985 selections.clear_disjoint();
12986 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12987 });
12988 }
12989 editor.highlight_background::<Self>(
12990 &ranges,
12991 |theme| theme.editor_highlighted_line_background,
12992 cx,
12993 );
12994 }
12995 MultibufferSelectionMode::All => {
12996 editor.change_selections(None, window, cx, |selections| {
12997 selections.clear_disjoint();
12998 selections.select_anchor_ranges(ranges);
12999 });
13000 }
13001 }
13002 editor.register_buffers_with_language_servers(cx);
13003 });
13004
13005 let item = Box::new(editor);
13006 let item_id = item.item_id();
13007
13008 if split {
13009 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13010 } else {
13011 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13012 let (preview_item_id, preview_item_idx) =
13013 workspace.active_pane().update(cx, |pane, _| {
13014 (pane.preview_item_id(), pane.preview_item_idx())
13015 });
13016
13017 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13018
13019 if let Some(preview_item_id) = preview_item_id {
13020 workspace.active_pane().update(cx, |pane, cx| {
13021 pane.remove_item(preview_item_id, false, false, window, cx);
13022 });
13023 }
13024 } else {
13025 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13026 }
13027 }
13028 workspace.active_pane().update(cx, |pane, cx| {
13029 pane.set_preview_item_id(Some(item_id), cx);
13030 });
13031 }
13032
13033 pub fn rename(
13034 &mut self,
13035 _: &Rename,
13036 window: &mut Window,
13037 cx: &mut Context<Self>,
13038 ) -> Option<Task<Result<()>>> {
13039 use language::ToOffset as _;
13040
13041 let provider = self.semantics_provider.clone()?;
13042 let selection = self.selections.newest_anchor().clone();
13043 let (cursor_buffer, cursor_buffer_position) = self
13044 .buffer
13045 .read(cx)
13046 .text_anchor_for_position(selection.head(), cx)?;
13047 let (tail_buffer, cursor_buffer_position_end) = self
13048 .buffer
13049 .read(cx)
13050 .text_anchor_for_position(selection.tail(), cx)?;
13051 if tail_buffer != cursor_buffer {
13052 return None;
13053 }
13054
13055 let snapshot = cursor_buffer.read(cx).snapshot();
13056 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13057 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13058 let prepare_rename = provider
13059 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13060 .unwrap_or_else(|| Task::ready(Ok(None)));
13061 drop(snapshot);
13062
13063 Some(cx.spawn_in(window, async move |this, cx| {
13064 let rename_range = if let Some(range) = prepare_rename.await? {
13065 Some(range)
13066 } else {
13067 this.update(cx, |this, cx| {
13068 let buffer = this.buffer.read(cx).snapshot(cx);
13069 let mut buffer_highlights = this
13070 .document_highlights_for_position(selection.head(), &buffer)
13071 .filter(|highlight| {
13072 highlight.start.excerpt_id == selection.head().excerpt_id
13073 && highlight.end.excerpt_id == selection.head().excerpt_id
13074 });
13075 buffer_highlights
13076 .next()
13077 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13078 })?
13079 };
13080 if let Some(rename_range) = rename_range {
13081 this.update_in(cx, |this, window, cx| {
13082 let snapshot = cursor_buffer.read(cx).snapshot();
13083 let rename_buffer_range = rename_range.to_offset(&snapshot);
13084 let cursor_offset_in_rename_range =
13085 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13086 let cursor_offset_in_rename_range_end =
13087 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13088
13089 this.take_rename(false, window, cx);
13090 let buffer = this.buffer.read(cx).read(cx);
13091 let cursor_offset = selection.head().to_offset(&buffer);
13092 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13093 let rename_end = rename_start + rename_buffer_range.len();
13094 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13095 let mut old_highlight_id = None;
13096 let old_name: Arc<str> = buffer
13097 .chunks(rename_start..rename_end, true)
13098 .map(|chunk| {
13099 if old_highlight_id.is_none() {
13100 old_highlight_id = chunk.syntax_highlight_id;
13101 }
13102 chunk.text
13103 })
13104 .collect::<String>()
13105 .into();
13106
13107 drop(buffer);
13108
13109 // Position the selection in the rename editor so that it matches the current selection.
13110 this.show_local_selections = false;
13111 let rename_editor = cx.new(|cx| {
13112 let mut editor = Editor::single_line(window, cx);
13113 editor.buffer.update(cx, |buffer, cx| {
13114 buffer.edit([(0..0, old_name.clone())], None, cx)
13115 });
13116 let rename_selection_range = match cursor_offset_in_rename_range
13117 .cmp(&cursor_offset_in_rename_range_end)
13118 {
13119 Ordering::Equal => {
13120 editor.select_all(&SelectAll, window, cx);
13121 return editor;
13122 }
13123 Ordering::Less => {
13124 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13125 }
13126 Ordering::Greater => {
13127 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13128 }
13129 };
13130 if rename_selection_range.end > old_name.len() {
13131 editor.select_all(&SelectAll, window, cx);
13132 } else {
13133 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13134 s.select_ranges([rename_selection_range]);
13135 });
13136 }
13137 editor
13138 });
13139 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13140 if e == &EditorEvent::Focused {
13141 cx.emit(EditorEvent::FocusedIn)
13142 }
13143 })
13144 .detach();
13145
13146 let write_highlights =
13147 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13148 let read_highlights =
13149 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13150 let ranges = write_highlights
13151 .iter()
13152 .flat_map(|(_, ranges)| ranges.iter())
13153 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13154 .cloned()
13155 .collect();
13156
13157 this.highlight_text::<Rename>(
13158 ranges,
13159 HighlightStyle {
13160 fade_out: Some(0.6),
13161 ..Default::default()
13162 },
13163 cx,
13164 );
13165 let rename_focus_handle = rename_editor.focus_handle(cx);
13166 window.focus(&rename_focus_handle);
13167 let block_id = this.insert_blocks(
13168 [BlockProperties {
13169 style: BlockStyle::Flex,
13170 placement: BlockPlacement::Below(range.start),
13171 height: 1,
13172 render: Arc::new({
13173 let rename_editor = rename_editor.clone();
13174 move |cx: &mut BlockContext| {
13175 let mut text_style = cx.editor_style.text.clone();
13176 if let Some(highlight_style) = old_highlight_id
13177 .and_then(|h| h.style(&cx.editor_style.syntax))
13178 {
13179 text_style = text_style.highlight(highlight_style);
13180 }
13181 div()
13182 .block_mouse_down()
13183 .pl(cx.anchor_x)
13184 .child(EditorElement::new(
13185 &rename_editor,
13186 EditorStyle {
13187 background: cx.theme().system().transparent,
13188 local_player: cx.editor_style.local_player,
13189 text: text_style,
13190 scrollbar_width: cx.editor_style.scrollbar_width,
13191 syntax: cx.editor_style.syntax.clone(),
13192 status: cx.editor_style.status.clone(),
13193 inlay_hints_style: HighlightStyle {
13194 font_weight: Some(FontWeight::BOLD),
13195 ..make_inlay_hints_style(cx.app)
13196 },
13197 inline_completion_styles: make_suggestion_styles(
13198 cx.app,
13199 ),
13200 ..EditorStyle::default()
13201 },
13202 ))
13203 .into_any_element()
13204 }
13205 }),
13206 priority: 0,
13207 }],
13208 Some(Autoscroll::fit()),
13209 cx,
13210 )[0];
13211 this.pending_rename = Some(RenameState {
13212 range,
13213 old_name,
13214 editor: rename_editor,
13215 block_id,
13216 });
13217 })?;
13218 }
13219
13220 Ok(())
13221 }))
13222 }
13223
13224 pub fn confirm_rename(
13225 &mut self,
13226 _: &ConfirmRename,
13227 window: &mut Window,
13228 cx: &mut Context<Self>,
13229 ) -> Option<Task<Result<()>>> {
13230 let rename = self.take_rename(false, window, cx)?;
13231 let workspace = self.workspace()?.downgrade();
13232 let (buffer, start) = self
13233 .buffer
13234 .read(cx)
13235 .text_anchor_for_position(rename.range.start, cx)?;
13236 let (end_buffer, _) = self
13237 .buffer
13238 .read(cx)
13239 .text_anchor_for_position(rename.range.end, cx)?;
13240 if buffer != end_buffer {
13241 return None;
13242 }
13243
13244 let old_name = rename.old_name;
13245 let new_name = rename.editor.read(cx).text(cx);
13246
13247 let rename = self.semantics_provider.as_ref()?.perform_rename(
13248 &buffer,
13249 start,
13250 new_name.clone(),
13251 cx,
13252 )?;
13253
13254 Some(cx.spawn_in(window, async move |editor, cx| {
13255 let project_transaction = rename.await?;
13256 Self::open_project_transaction(
13257 &editor,
13258 workspace,
13259 project_transaction,
13260 format!("Rename: {} → {}", old_name, new_name),
13261 cx,
13262 )
13263 .await?;
13264
13265 editor.update(cx, |editor, cx| {
13266 editor.refresh_document_highlights(cx);
13267 })?;
13268 Ok(())
13269 }))
13270 }
13271
13272 fn take_rename(
13273 &mut self,
13274 moving_cursor: bool,
13275 window: &mut Window,
13276 cx: &mut Context<Self>,
13277 ) -> Option<RenameState> {
13278 let rename = self.pending_rename.take()?;
13279 if rename.editor.focus_handle(cx).is_focused(window) {
13280 window.focus(&self.focus_handle);
13281 }
13282
13283 self.remove_blocks(
13284 [rename.block_id].into_iter().collect(),
13285 Some(Autoscroll::fit()),
13286 cx,
13287 );
13288 self.clear_highlights::<Rename>(cx);
13289 self.show_local_selections = true;
13290
13291 if moving_cursor {
13292 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13293 editor.selections.newest::<usize>(cx).head()
13294 });
13295
13296 // Update the selection to match the position of the selection inside
13297 // the rename editor.
13298 let snapshot = self.buffer.read(cx).read(cx);
13299 let rename_range = rename.range.to_offset(&snapshot);
13300 let cursor_in_editor = snapshot
13301 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13302 .min(rename_range.end);
13303 drop(snapshot);
13304
13305 self.change_selections(None, window, cx, |s| {
13306 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13307 });
13308 } else {
13309 self.refresh_document_highlights(cx);
13310 }
13311
13312 Some(rename)
13313 }
13314
13315 pub fn pending_rename(&self) -> Option<&RenameState> {
13316 self.pending_rename.as_ref()
13317 }
13318
13319 fn format(
13320 &mut self,
13321 _: &Format,
13322 window: &mut Window,
13323 cx: &mut Context<Self>,
13324 ) -> Option<Task<Result<()>>> {
13325 let project = match &self.project {
13326 Some(project) => project.clone(),
13327 None => return None,
13328 };
13329
13330 Some(self.perform_format(
13331 project,
13332 FormatTrigger::Manual,
13333 FormatTarget::Buffers,
13334 window,
13335 cx,
13336 ))
13337 }
13338
13339 fn format_selections(
13340 &mut self,
13341 _: &FormatSelections,
13342 window: &mut Window,
13343 cx: &mut Context<Self>,
13344 ) -> Option<Task<Result<()>>> {
13345 let project = match &self.project {
13346 Some(project) => project.clone(),
13347 None => return None,
13348 };
13349
13350 let ranges = self
13351 .selections
13352 .all_adjusted(cx)
13353 .into_iter()
13354 .map(|selection| selection.range())
13355 .collect_vec();
13356
13357 Some(self.perform_format(
13358 project,
13359 FormatTrigger::Manual,
13360 FormatTarget::Ranges(ranges),
13361 window,
13362 cx,
13363 ))
13364 }
13365
13366 fn perform_format(
13367 &mut self,
13368 project: Entity<Project>,
13369 trigger: FormatTrigger,
13370 target: FormatTarget,
13371 window: &mut Window,
13372 cx: &mut Context<Self>,
13373 ) -> Task<Result<()>> {
13374 let buffer = self.buffer.clone();
13375 let (buffers, target) = match target {
13376 FormatTarget::Buffers => {
13377 let mut buffers = buffer.read(cx).all_buffers();
13378 if trigger == FormatTrigger::Save {
13379 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13380 }
13381 (buffers, LspFormatTarget::Buffers)
13382 }
13383 FormatTarget::Ranges(selection_ranges) => {
13384 let multi_buffer = buffer.read(cx);
13385 let snapshot = multi_buffer.read(cx);
13386 let mut buffers = HashSet::default();
13387 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13388 BTreeMap::new();
13389 for selection_range in selection_ranges {
13390 for (buffer, buffer_range, _) in
13391 snapshot.range_to_buffer_ranges(selection_range)
13392 {
13393 let buffer_id = buffer.remote_id();
13394 let start = buffer.anchor_before(buffer_range.start);
13395 let end = buffer.anchor_after(buffer_range.end);
13396 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13397 buffer_id_to_ranges
13398 .entry(buffer_id)
13399 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13400 .or_insert_with(|| vec![start..end]);
13401 }
13402 }
13403 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13404 }
13405 };
13406
13407 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13408 let format = project.update(cx, |project, cx| {
13409 project.format(buffers, target, true, trigger, cx)
13410 });
13411
13412 cx.spawn_in(window, async move |_, cx| {
13413 let transaction = futures::select_biased! {
13414 transaction = format.log_err().fuse() => transaction,
13415 () = timeout => {
13416 log::warn!("timed out waiting for formatting");
13417 None
13418 }
13419 };
13420
13421 buffer
13422 .update(cx, |buffer, cx| {
13423 if let Some(transaction) = transaction {
13424 if !buffer.is_singleton() {
13425 buffer.push_transaction(&transaction.0, cx);
13426 }
13427 }
13428 cx.notify();
13429 })
13430 .ok();
13431
13432 Ok(())
13433 })
13434 }
13435
13436 fn organize_imports(
13437 &mut self,
13438 _: &OrganizeImports,
13439 window: &mut Window,
13440 cx: &mut Context<Self>,
13441 ) -> Option<Task<Result<()>>> {
13442 let project = match &self.project {
13443 Some(project) => project.clone(),
13444 None => return None,
13445 };
13446 Some(self.perform_code_action_kind(
13447 project,
13448 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13449 window,
13450 cx,
13451 ))
13452 }
13453
13454 fn perform_code_action_kind(
13455 &mut self,
13456 project: Entity<Project>,
13457 kind: CodeActionKind,
13458 window: &mut Window,
13459 cx: &mut Context<Self>,
13460 ) -> Task<Result<()>> {
13461 let buffer = self.buffer.clone();
13462 let buffers = buffer.read(cx).all_buffers();
13463 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13464 let apply_action = project.update(cx, |project, cx| {
13465 project.apply_code_action_kind(buffers, kind, true, cx)
13466 });
13467 cx.spawn_in(window, async move |_, cx| {
13468 let transaction = futures::select_biased! {
13469 () = timeout => {
13470 log::warn!("timed out waiting for executing code action");
13471 None
13472 }
13473 transaction = apply_action.log_err().fuse() => transaction,
13474 };
13475 buffer
13476 .update(cx, |buffer, cx| {
13477 // check if we need this
13478 if let Some(transaction) = transaction {
13479 if !buffer.is_singleton() {
13480 buffer.push_transaction(&transaction.0, cx);
13481 }
13482 }
13483 cx.notify();
13484 })
13485 .ok();
13486 Ok(())
13487 })
13488 }
13489
13490 fn restart_language_server(
13491 &mut self,
13492 _: &RestartLanguageServer,
13493 _: &mut Window,
13494 cx: &mut Context<Self>,
13495 ) {
13496 if let Some(project) = self.project.clone() {
13497 self.buffer.update(cx, |multi_buffer, cx| {
13498 project.update(cx, |project, cx| {
13499 project.restart_language_servers_for_buffers(
13500 multi_buffer.all_buffers().into_iter().collect(),
13501 cx,
13502 );
13503 });
13504 })
13505 }
13506 }
13507
13508 fn cancel_language_server_work(
13509 workspace: &mut Workspace,
13510 _: &actions::CancelLanguageServerWork,
13511 _: &mut Window,
13512 cx: &mut Context<Workspace>,
13513 ) {
13514 let project = workspace.project();
13515 let buffers = workspace
13516 .active_item(cx)
13517 .and_then(|item| item.act_as::<Editor>(cx))
13518 .map_or(HashSet::default(), |editor| {
13519 editor.read(cx).buffer.read(cx).all_buffers()
13520 });
13521 project.update(cx, |project, cx| {
13522 project.cancel_language_server_work_for_buffers(buffers, cx);
13523 });
13524 }
13525
13526 fn show_character_palette(
13527 &mut self,
13528 _: &ShowCharacterPalette,
13529 window: &mut Window,
13530 _: &mut Context<Self>,
13531 ) {
13532 window.show_character_palette();
13533 }
13534
13535 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13536 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13537 let buffer = self.buffer.read(cx).snapshot(cx);
13538 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13539 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13540 let is_valid = buffer
13541 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13542 .any(|entry| {
13543 entry.diagnostic.is_primary
13544 && !entry.range.is_empty()
13545 && entry.range.start == primary_range_start
13546 && entry.diagnostic.message == active_diagnostics.primary_message
13547 });
13548
13549 if is_valid != active_diagnostics.is_valid {
13550 active_diagnostics.is_valid = is_valid;
13551 if is_valid {
13552 let mut new_styles = HashMap::default();
13553 for (block_id, diagnostic) in &active_diagnostics.blocks {
13554 new_styles.insert(
13555 *block_id,
13556 diagnostic_block_renderer(diagnostic.clone(), None, true),
13557 );
13558 }
13559 self.display_map.update(cx, |display_map, _cx| {
13560 display_map.replace_blocks(new_styles);
13561 });
13562 } else {
13563 self.dismiss_diagnostics(cx);
13564 }
13565 }
13566 }
13567 }
13568
13569 fn activate_diagnostics(
13570 &mut self,
13571 buffer_id: BufferId,
13572 group_id: usize,
13573 window: &mut Window,
13574 cx: &mut Context<Self>,
13575 ) {
13576 self.dismiss_diagnostics(cx);
13577 let snapshot = self.snapshot(window, cx);
13578 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13579 let buffer = self.buffer.read(cx).snapshot(cx);
13580
13581 let mut primary_range = None;
13582 let mut primary_message = None;
13583 let diagnostic_group = buffer
13584 .diagnostic_group(buffer_id, group_id)
13585 .filter_map(|entry| {
13586 let start = entry.range.start;
13587 let end = entry.range.end;
13588 if snapshot.is_line_folded(MultiBufferRow(start.row))
13589 && (start.row == end.row
13590 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13591 {
13592 return None;
13593 }
13594 if entry.diagnostic.is_primary {
13595 primary_range = Some(entry.range.clone());
13596 primary_message = Some(entry.diagnostic.message.clone());
13597 }
13598 Some(entry)
13599 })
13600 .collect::<Vec<_>>();
13601 let primary_range = primary_range?;
13602 let primary_message = primary_message?;
13603
13604 let blocks = display_map
13605 .insert_blocks(
13606 diagnostic_group.iter().map(|entry| {
13607 let diagnostic = entry.diagnostic.clone();
13608 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13609 BlockProperties {
13610 style: BlockStyle::Fixed,
13611 placement: BlockPlacement::Below(
13612 buffer.anchor_after(entry.range.start),
13613 ),
13614 height: message_height,
13615 render: diagnostic_block_renderer(diagnostic, None, true),
13616 priority: 0,
13617 }
13618 }),
13619 cx,
13620 )
13621 .into_iter()
13622 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13623 .collect();
13624
13625 Some(ActiveDiagnosticGroup {
13626 primary_range: buffer.anchor_before(primary_range.start)
13627 ..buffer.anchor_after(primary_range.end),
13628 primary_message,
13629 group_id,
13630 blocks,
13631 is_valid: true,
13632 })
13633 });
13634 }
13635
13636 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13637 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13638 self.display_map.update(cx, |display_map, cx| {
13639 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13640 });
13641 cx.notify();
13642 }
13643 }
13644
13645 /// Disable inline diagnostics rendering for this editor.
13646 pub fn disable_inline_diagnostics(&mut self) {
13647 self.inline_diagnostics_enabled = false;
13648 self.inline_diagnostics_update = Task::ready(());
13649 self.inline_diagnostics.clear();
13650 }
13651
13652 pub fn inline_diagnostics_enabled(&self) -> bool {
13653 self.inline_diagnostics_enabled
13654 }
13655
13656 pub fn show_inline_diagnostics(&self) -> bool {
13657 self.show_inline_diagnostics
13658 }
13659
13660 pub fn toggle_inline_diagnostics(
13661 &mut self,
13662 _: &ToggleInlineDiagnostics,
13663 window: &mut Window,
13664 cx: &mut Context<'_, Editor>,
13665 ) {
13666 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13667 self.refresh_inline_diagnostics(false, window, cx);
13668 }
13669
13670 fn refresh_inline_diagnostics(
13671 &mut self,
13672 debounce: bool,
13673 window: &mut Window,
13674 cx: &mut Context<Self>,
13675 ) {
13676 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13677 self.inline_diagnostics_update = Task::ready(());
13678 self.inline_diagnostics.clear();
13679 return;
13680 }
13681
13682 let debounce_ms = ProjectSettings::get_global(cx)
13683 .diagnostics
13684 .inline
13685 .update_debounce_ms;
13686 let debounce = if debounce && debounce_ms > 0 {
13687 Some(Duration::from_millis(debounce_ms))
13688 } else {
13689 None
13690 };
13691 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
13692 if let Some(debounce) = debounce {
13693 cx.background_executor().timer(debounce).await;
13694 }
13695 let Some(snapshot) = editor
13696 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13697 .ok()
13698 else {
13699 return;
13700 };
13701
13702 let new_inline_diagnostics = cx
13703 .background_spawn(async move {
13704 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13705 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13706 let message = diagnostic_entry
13707 .diagnostic
13708 .message
13709 .split_once('\n')
13710 .map(|(line, _)| line)
13711 .map(SharedString::new)
13712 .unwrap_or_else(|| {
13713 SharedString::from(diagnostic_entry.diagnostic.message)
13714 });
13715 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13716 let (Ok(i) | Err(i)) = inline_diagnostics
13717 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13718 inline_diagnostics.insert(
13719 i,
13720 (
13721 start_anchor,
13722 InlineDiagnostic {
13723 message,
13724 group_id: diagnostic_entry.diagnostic.group_id,
13725 start: diagnostic_entry.range.start.to_point(&snapshot),
13726 is_primary: diagnostic_entry.diagnostic.is_primary,
13727 severity: diagnostic_entry.diagnostic.severity,
13728 },
13729 ),
13730 );
13731 }
13732 inline_diagnostics
13733 })
13734 .await;
13735
13736 editor
13737 .update(cx, |editor, cx| {
13738 editor.inline_diagnostics = new_inline_diagnostics;
13739 cx.notify();
13740 })
13741 .ok();
13742 });
13743 }
13744
13745 pub fn set_selections_from_remote(
13746 &mut self,
13747 selections: Vec<Selection<Anchor>>,
13748 pending_selection: Option<Selection<Anchor>>,
13749 window: &mut Window,
13750 cx: &mut Context<Self>,
13751 ) {
13752 let old_cursor_position = self.selections.newest_anchor().head();
13753 self.selections.change_with(cx, |s| {
13754 s.select_anchors(selections);
13755 if let Some(pending_selection) = pending_selection {
13756 s.set_pending(pending_selection, SelectMode::Character);
13757 } else {
13758 s.clear_pending();
13759 }
13760 });
13761 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13762 }
13763
13764 fn push_to_selection_history(&mut self) {
13765 self.selection_history.push(SelectionHistoryEntry {
13766 selections: self.selections.disjoint_anchors(),
13767 select_next_state: self.select_next_state.clone(),
13768 select_prev_state: self.select_prev_state.clone(),
13769 add_selections_state: self.add_selections_state.clone(),
13770 });
13771 }
13772
13773 pub fn transact(
13774 &mut self,
13775 window: &mut Window,
13776 cx: &mut Context<Self>,
13777 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13778 ) -> Option<TransactionId> {
13779 self.start_transaction_at(Instant::now(), window, cx);
13780 update(self, window, cx);
13781 self.end_transaction_at(Instant::now(), cx)
13782 }
13783
13784 pub fn start_transaction_at(
13785 &mut self,
13786 now: Instant,
13787 window: &mut Window,
13788 cx: &mut Context<Self>,
13789 ) {
13790 self.end_selection(window, cx);
13791 if let Some(tx_id) = self
13792 .buffer
13793 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13794 {
13795 self.selection_history
13796 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13797 cx.emit(EditorEvent::TransactionBegun {
13798 transaction_id: tx_id,
13799 })
13800 }
13801 }
13802
13803 pub fn end_transaction_at(
13804 &mut self,
13805 now: Instant,
13806 cx: &mut Context<Self>,
13807 ) -> Option<TransactionId> {
13808 if let Some(transaction_id) = self
13809 .buffer
13810 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13811 {
13812 if let Some((_, end_selections)) =
13813 self.selection_history.transaction_mut(transaction_id)
13814 {
13815 *end_selections = Some(self.selections.disjoint_anchors());
13816 } else {
13817 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13818 }
13819
13820 cx.emit(EditorEvent::Edited { transaction_id });
13821 Some(transaction_id)
13822 } else {
13823 None
13824 }
13825 }
13826
13827 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13828 if self.selection_mark_mode {
13829 self.change_selections(None, window, cx, |s| {
13830 s.move_with(|_, sel| {
13831 sel.collapse_to(sel.head(), SelectionGoal::None);
13832 });
13833 })
13834 }
13835 self.selection_mark_mode = true;
13836 cx.notify();
13837 }
13838
13839 pub fn swap_selection_ends(
13840 &mut self,
13841 _: &actions::SwapSelectionEnds,
13842 window: &mut Window,
13843 cx: &mut Context<Self>,
13844 ) {
13845 self.change_selections(None, window, cx, |s| {
13846 s.move_with(|_, sel| {
13847 if sel.start != sel.end {
13848 sel.reversed = !sel.reversed
13849 }
13850 });
13851 });
13852 self.request_autoscroll(Autoscroll::newest(), cx);
13853 cx.notify();
13854 }
13855
13856 pub fn toggle_fold(
13857 &mut self,
13858 _: &actions::ToggleFold,
13859 window: &mut Window,
13860 cx: &mut Context<Self>,
13861 ) {
13862 if self.is_singleton(cx) {
13863 let selection = self.selections.newest::<Point>(cx);
13864
13865 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13866 let range = if selection.is_empty() {
13867 let point = selection.head().to_display_point(&display_map);
13868 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13869 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13870 .to_point(&display_map);
13871 start..end
13872 } else {
13873 selection.range()
13874 };
13875 if display_map.folds_in_range(range).next().is_some() {
13876 self.unfold_lines(&Default::default(), window, cx)
13877 } else {
13878 self.fold(&Default::default(), window, cx)
13879 }
13880 } else {
13881 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13882 let buffer_ids: HashSet<_> = self
13883 .selections
13884 .disjoint_anchor_ranges()
13885 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13886 .collect();
13887
13888 let should_unfold = buffer_ids
13889 .iter()
13890 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13891
13892 for buffer_id in buffer_ids {
13893 if should_unfold {
13894 self.unfold_buffer(buffer_id, cx);
13895 } else {
13896 self.fold_buffer(buffer_id, cx);
13897 }
13898 }
13899 }
13900 }
13901
13902 pub fn toggle_fold_recursive(
13903 &mut self,
13904 _: &actions::ToggleFoldRecursive,
13905 window: &mut Window,
13906 cx: &mut Context<Self>,
13907 ) {
13908 let selection = self.selections.newest::<Point>(cx);
13909
13910 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13911 let range = if selection.is_empty() {
13912 let point = selection.head().to_display_point(&display_map);
13913 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13914 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13915 .to_point(&display_map);
13916 start..end
13917 } else {
13918 selection.range()
13919 };
13920 if display_map.folds_in_range(range).next().is_some() {
13921 self.unfold_recursive(&Default::default(), window, cx)
13922 } else {
13923 self.fold_recursive(&Default::default(), window, cx)
13924 }
13925 }
13926
13927 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13928 if self.is_singleton(cx) {
13929 let mut to_fold = Vec::new();
13930 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13931 let selections = self.selections.all_adjusted(cx);
13932
13933 for selection in selections {
13934 let range = selection.range().sorted();
13935 let buffer_start_row = range.start.row;
13936
13937 if range.start.row != range.end.row {
13938 let mut found = false;
13939 let mut row = range.start.row;
13940 while row <= range.end.row {
13941 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13942 {
13943 found = true;
13944 row = crease.range().end.row + 1;
13945 to_fold.push(crease);
13946 } else {
13947 row += 1
13948 }
13949 }
13950 if found {
13951 continue;
13952 }
13953 }
13954
13955 for row in (0..=range.start.row).rev() {
13956 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13957 if crease.range().end.row >= buffer_start_row {
13958 to_fold.push(crease);
13959 if row <= range.start.row {
13960 break;
13961 }
13962 }
13963 }
13964 }
13965 }
13966
13967 self.fold_creases(to_fold, true, window, cx);
13968 } else {
13969 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13970 let buffer_ids = self
13971 .selections
13972 .disjoint_anchor_ranges()
13973 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13974 .collect::<HashSet<_>>();
13975 for buffer_id in buffer_ids {
13976 self.fold_buffer(buffer_id, cx);
13977 }
13978 }
13979 }
13980
13981 fn fold_at_level(
13982 &mut self,
13983 fold_at: &FoldAtLevel,
13984 window: &mut Window,
13985 cx: &mut Context<Self>,
13986 ) {
13987 if !self.buffer.read(cx).is_singleton() {
13988 return;
13989 }
13990
13991 let fold_at_level = fold_at.0;
13992 let snapshot = self.buffer.read(cx).snapshot(cx);
13993 let mut to_fold = Vec::new();
13994 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13995
13996 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13997 while start_row < end_row {
13998 match self
13999 .snapshot(window, cx)
14000 .crease_for_buffer_row(MultiBufferRow(start_row))
14001 {
14002 Some(crease) => {
14003 let nested_start_row = crease.range().start.row + 1;
14004 let nested_end_row = crease.range().end.row;
14005
14006 if current_level < fold_at_level {
14007 stack.push((nested_start_row, nested_end_row, current_level + 1));
14008 } else if current_level == fold_at_level {
14009 to_fold.push(crease);
14010 }
14011
14012 start_row = nested_end_row + 1;
14013 }
14014 None => start_row += 1,
14015 }
14016 }
14017 }
14018
14019 self.fold_creases(to_fold, true, window, cx);
14020 }
14021
14022 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14023 if self.buffer.read(cx).is_singleton() {
14024 let mut fold_ranges = Vec::new();
14025 let snapshot = self.buffer.read(cx).snapshot(cx);
14026
14027 for row in 0..snapshot.max_row().0 {
14028 if let Some(foldable_range) = self
14029 .snapshot(window, cx)
14030 .crease_for_buffer_row(MultiBufferRow(row))
14031 {
14032 fold_ranges.push(foldable_range);
14033 }
14034 }
14035
14036 self.fold_creases(fold_ranges, true, window, cx);
14037 } else {
14038 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14039 editor
14040 .update_in(cx, |editor, _, cx| {
14041 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14042 editor.fold_buffer(buffer_id, cx);
14043 }
14044 })
14045 .ok();
14046 });
14047 }
14048 }
14049
14050 pub fn fold_function_bodies(
14051 &mut self,
14052 _: &actions::FoldFunctionBodies,
14053 window: &mut Window,
14054 cx: &mut Context<Self>,
14055 ) {
14056 let snapshot = self.buffer.read(cx).snapshot(cx);
14057
14058 let ranges = snapshot
14059 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14060 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14061 .collect::<Vec<_>>();
14062
14063 let creases = ranges
14064 .into_iter()
14065 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14066 .collect();
14067
14068 self.fold_creases(creases, true, window, cx);
14069 }
14070
14071 pub fn fold_recursive(
14072 &mut self,
14073 _: &actions::FoldRecursive,
14074 window: &mut Window,
14075 cx: &mut Context<Self>,
14076 ) {
14077 let mut to_fold = Vec::new();
14078 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14079 let selections = self.selections.all_adjusted(cx);
14080
14081 for selection in selections {
14082 let range = selection.range().sorted();
14083 let buffer_start_row = range.start.row;
14084
14085 if range.start.row != range.end.row {
14086 let mut found = false;
14087 for row in range.start.row..=range.end.row {
14088 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14089 found = true;
14090 to_fold.push(crease);
14091 }
14092 }
14093 if found {
14094 continue;
14095 }
14096 }
14097
14098 for row in (0..=range.start.row).rev() {
14099 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14100 if crease.range().end.row >= buffer_start_row {
14101 to_fold.push(crease);
14102 } else {
14103 break;
14104 }
14105 }
14106 }
14107 }
14108
14109 self.fold_creases(to_fold, true, window, cx);
14110 }
14111
14112 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14113 let buffer_row = fold_at.buffer_row;
14114 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14115
14116 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14117 let autoscroll = self
14118 .selections
14119 .all::<Point>(cx)
14120 .iter()
14121 .any(|selection| crease.range().overlaps(&selection.range()));
14122
14123 self.fold_creases(vec![crease], autoscroll, window, cx);
14124 }
14125 }
14126
14127 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14128 if self.is_singleton(cx) {
14129 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14130 let buffer = &display_map.buffer_snapshot;
14131 let selections = self.selections.all::<Point>(cx);
14132 let ranges = selections
14133 .iter()
14134 .map(|s| {
14135 let range = s.display_range(&display_map).sorted();
14136 let mut start = range.start.to_point(&display_map);
14137 let mut end = range.end.to_point(&display_map);
14138 start.column = 0;
14139 end.column = buffer.line_len(MultiBufferRow(end.row));
14140 start..end
14141 })
14142 .collect::<Vec<_>>();
14143
14144 self.unfold_ranges(&ranges, true, true, cx);
14145 } else {
14146 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14147 let buffer_ids = self
14148 .selections
14149 .disjoint_anchor_ranges()
14150 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14151 .collect::<HashSet<_>>();
14152 for buffer_id in buffer_ids {
14153 self.unfold_buffer(buffer_id, cx);
14154 }
14155 }
14156 }
14157
14158 pub fn unfold_recursive(
14159 &mut self,
14160 _: &UnfoldRecursive,
14161 _window: &mut Window,
14162 cx: &mut Context<Self>,
14163 ) {
14164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14165 let selections = self.selections.all::<Point>(cx);
14166 let ranges = selections
14167 .iter()
14168 .map(|s| {
14169 let mut range = s.display_range(&display_map).sorted();
14170 *range.start.column_mut() = 0;
14171 *range.end.column_mut() = display_map.line_len(range.end.row());
14172 let start = range.start.to_point(&display_map);
14173 let end = range.end.to_point(&display_map);
14174 start..end
14175 })
14176 .collect::<Vec<_>>();
14177
14178 self.unfold_ranges(&ranges, true, true, cx);
14179 }
14180
14181 pub fn unfold_at(
14182 &mut self,
14183 unfold_at: &UnfoldAt,
14184 _window: &mut Window,
14185 cx: &mut Context<Self>,
14186 ) {
14187 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14188
14189 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14190 ..Point::new(
14191 unfold_at.buffer_row.0,
14192 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14193 );
14194
14195 let autoscroll = self
14196 .selections
14197 .all::<Point>(cx)
14198 .iter()
14199 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14200
14201 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14202 }
14203
14204 pub fn unfold_all(
14205 &mut self,
14206 _: &actions::UnfoldAll,
14207 _window: &mut Window,
14208 cx: &mut Context<Self>,
14209 ) {
14210 if self.buffer.read(cx).is_singleton() {
14211 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14212 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14213 } else {
14214 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14215 editor
14216 .update(cx, |editor, cx| {
14217 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14218 editor.unfold_buffer(buffer_id, cx);
14219 }
14220 })
14221 .ok();
14222 });
14223 }
14224 }
14225
14226 pub fn fold_selected_ranges(
14227 &mut self,
14228 _: &FoldSelectedRanges,
14229 window: &mut Window,
14230 cx: &mut Context<Self>,
14231 ) {
14232 let selections = self.selections.all::<Point>(cx);
14233 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14234 let line_mode = self.selections.line_mode;
14235 let ranges = selections
14236 .into_iter()
14237 .map(|s| {
14238 if line_mode {
14239 let start = Point::new(s.start.row, 0);
14240 let end = Point::new(
14241 s.end.row,
14242 display_map
14243 .buffer_snapshot
14244 .line_len(MultiBufferRow(s.end.row)),
14245 );
14246 Crease::simple(start..end, display_map.fold_placeholder.clone())
14247 } else {
14248 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14249 }
14250 })
14251 .collect::<Vec<_>>();
14252 self.fold_creases(ranges, true, window, cx);
14253 }
14254
14255 pub fn fold_ranges<T: ToOffset + Clone>(
14256 &mut self,
14257 ranges: Vec<Range<T>>,
14258 auto_scroll: bool,
14259 window: &mut Window,
14260 cx: &mut Context<Self>,
14261 ) {
14262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14263 let ranges = ranges
14264 .into_iter()
14265 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14266 .collect::<Vec<_>>();
14267 self.fold_creases(ranges, auto_scroll, window, cx);
14268 }
14269
14270 pub fn fold_creases<T: ToOffset + Clone>(
14271 &mut self,
14272 creases: Vec<Crease<T>>,
14273 auto_scroll: bool,
14274 window: &mut Window,
14275 cx: &mut Context<Self>,
14276 ) {
14277 if creases.is_empty() {
14278 return;
14279 }
14280
14281 let mut buffers_affected = HashSet::default();
14282 let multi_buffer = self.buffer().read(cx);
14283 for crease in &creases {
14284 if let Some((_, buffer, _)) =
14285 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14286 {
14287 buffers_affected.insert(buffer.read(cx).remote_id());
14288 };
14289 }
14290
14291 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14292
14293 if auto_scroll {
14294 self.request_autoscroll(Autoscroll::fit(), cx);
14295 }
14296
14297 cx.notify();
14298
14299 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14300 // Clear diagnostics block when folding a range that contains it.
14301 let snapshot = self.snapshot(window, cx);
14302 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14303 drop(snapshot);
14304 self.active_diagnostics = Some(active_diagnostics);
14305 self.dismiss_diagnostics(cx);
14306 } else {
14307 self.active_diagnostics = Some(active_diagnostics);
14308 }
14309 }
14310
14311 self.scrollbar_marker_state.dirty = true;
14312 }
14313
14314 /// Removes any folds whose ranges intersect any of the given ranges.
14315 pub fn unfold_ranges<T: ToOffset + Clone>(
14316 &mut self,
14317 ranges: &[Range<T>],
14318 inclusive: bool,
14319 auto_scroll: bool,
14320 cx: &mut Context<Self>,
14321 ) {
14322 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14323 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14324 });
14325 }
14326
14327 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14328 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14329 return;
14330 }
14331 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14332 self.display_map.update(cx, |display_map, cx| {
14333 display_map.fold_buffers([buffer_id], cx)
14334 });
14335 cx.emit(EditorEvent::BufferFoldToggled {
14336 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14337 folded: true,
14338 });
14339 cx.notify();
14340 }
14341
14342 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14343 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14344 return;
14345 }
14346 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14347 self.display_map.update(cx, |display_map, cx| {
14348 display_map.unfold_buffers([buffer_id], cx);
14349 });
14350 cx.emit(EditorEvent::BufferFoldToggled {
14351 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14352 folded: false,
14353 });
14354 cx.notify();
14355 }
14356
14357 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14358 self.display_map.read(cx).is_buffer_folded(buffer)
14359 }
14360
14361 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14362 self.display_map.read(cx).folded_buffers()
14363 }
14364
14365 /// Removes any folds with the given ranges.
14366 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14367 &mut self,
14368 ranges: &[Range<T>],
14369 type_id: TypeId,
14370 auto_scroll: bool,
14371 cx: &mut Context<Self>,
14372 ) {
14373 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14374 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14375 });
14376 }
14377
14378 fn remove_folds_with<T: ToOffset + Clone>(
14379 &mut self,
14380 ranges: &[Range<T>],
14381 auto_scroll: bool,
14382 cx: &mut Context<Self>,
14383 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14384 ) {
14385 if ranges.is_empty() {
14386 return;
14387 }
14388
14389 let mut buffers_affected = HashSet::default();
14390 let multi_buffer = self.buffer().read(cx);
14391 for range in ranges {
14392 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14393 buffers_affected.insert(buffer.read(cx).remote_id());
14394 };
14395 }
14396
14397 self.display_map.update(cx, update);
14398
14399 if auto_scroll {
14400 self.request_autoscroll(Autoscroll::fit(), cx);
14401 }
14402
14403 cx.notify();
14404 self.scrollbar_marker_state.dirty = true;
14405 self.active_indent_guides_state.dirty = true;
14406 }
14407
14408 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14409 self.display_map.read(cx).fold_placeholder.clone()
14410 }
14411
14412 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14413 self.buffer.update(cx, |buffer, cx| {
14414 buffer.set_all_diff_hunks_expanded(cx);
14415 });
14416 }
14417
14418 pub fn expand_all_diff_hunks(
14419 &mut self,
14420 _: &ExpandAllDiffHunks,
14421 _window: &mut Window,
14422 cx: &mut Context<Self>,
14423 ) {
14424 self.buffer.update(cx, |buffer, cx| {
14425 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14426 });
14427 }
14428
14429 pub fn toggle_selected_diff_hunks(
14430 &mut self,
14431 _: &ToggleSelectedDiffHunks,
14432 _window: &mut Window,
14433 cx: &mut Context<Self>,
14434 ) {
14435 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14436 self.toggle_diff_hunks_in_ranges(ranges, cx);
14437 }
14438
14439 pub fn diff_hunks_in_ranges<'a>(
14440 &'a self,
14441 ranges: &'a [Range<Anchor>],
14442 buffer: &'a MultiBufferSnapshot,
14443 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14444 ranges.iter().flat_map(move |range| {
14445 let end_excerpt_id = range.end.excerpt_id;
14446 let range = range.to_point(buffer);
14447 let mut peek_end = range.end;
14448 if range.end.row < buffer.max_row().0 {
14449 peek_end = Point::new(range.end.row + 1, 0);
14450 }
14451 buffer
14452 .diff_hunks_in_range(range.start..peek_end)
14453 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14454 })
14455 }
14456
14457 pub fn has_stageable_diff_hunks_in_ranges(
14458 &self,
14459 ranges: &[Range<Anchor>],
14460 snapshot: &MultiBufferSnapshot,
14461 ) -> bool {
14462 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14463 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14464 }
14465
14466 pub fn toggle_staged_selected_diff_hunks(
14467 &mut self,
14468 _: &::git::ToggleStaged,
14469 _: &mut Window,
14470 cx: &mut Context<Self>,
14471 ) {
14472 let snapshot = self.buffer.read(cx).snapshot(cx);
14473 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14474 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14475 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14476 }
14477
14478 pub fn stage_and_next(
14479 &mut self,
14480 _: &::git::StageAndNext,
14481 window: &mut Window,
14482 cx: &mut Context<Self>,
14483 ) {
14484 self.do_stage_or_unstage_and_next(true, window, cx);
14485 }
14486
14487 pub fn unstage_and_next(
14488 &mut self,
14489 _: &::git::UnstageAndNext,
14490 window: &mut Window,
14491 cx: &mut Context<Self>,
14492 ) {
14493 self.do_stage_or_unstage_and_next(false, window, cx);
14494 }
14495
14496 pub fn stage_or_unstage_diff_hunks(
14497 &mut self,
14498 stage: bool,
14499 ranges: Vec<Range<Anchor>>,
14500 cx: &mut Context<Self>,
14501 ) {
14502 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14503 cx.spawn(async move |this, cx| {
14504 task.await?;
14505 this.update(cx, |this, cx| {
14506 let snapshot = this.buffer.read(cx).snapshot(cx);
14507 let chunk_by = this
14508 .diff_hunks_in_ranges(&ranges, &snapshot)
14509 .chunk_by(|hunk| hunk.buffer_id);
14510 for (buffer_id, hunks) in &chunk_by {
14511 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14512 }
14513 })
14514 })
14515 .detach_and_log_err(cx);
14516 }
14517
14518 fn save_buffers_for_ranges_if_needed(
14519 &mut self,
14520 ranges: &[Range<Anchor>],
14521 cx: &mut Context<'_, Editor>,
14522 ) -> Task<Result<()>> {
14523 let multibuffer = self.buffer.read(cx);
14524 let snapshot = multibuffer.read(cx);
14525 let buffer_ids: HashSet<_> = ranges
14526 .iter()
14527 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14528 .collect();
14529 drop(snapshot);
14530
14531 let mut buffers = HashSet::default();
14532 for buffer_id in buffer_ids {
14533 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14534 let buffer = buffer_entity.read(cx);
14535 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14536 {
14537 buffers.insert(buffer_entity);
14538 }
14539 }
14540 }
14541
14542 if let Some(project) = &self.project {
14543 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14544 } else {
14545 Task::ready(Ok(()))
14546 }
14547 }
14548
14549 fn do_stage_or_unstage_and_next(
14550 &mut self,
14551 stage: bool,
14552 window: &mut Window,
14553 cx: &mut Context<Self>,
14554 ) {
14555 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14556
14557 if ranges.iter().any(|range| range.start != range.end) {
14558 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14559 return;
14560 }
14561
14562 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14563 let snapshot = self.snapshot(window, cx);
14564 let position = self.selections.newest::<Point>(cx).head();
14565 let mut row = snapshot
14566 .buffer_snapshot
14567 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14568 .find(|hunk| hunk.row_range.start.0 > position.row)
14569 .map(|hunk| hunk.row_range.start);
14570
14571 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14572 // Outside of the project diff editor, wrap around to the beginning.
14573 if !all_diff_hunks_expanded {
14574 row = row.or_else(|| {
14575 snapshot
14576 .buffer_snapshot
14577 .diff_hunks_in_range(Point::zero()..position)
14578 .find(|hunk| hunk.row_range.end.0 < position.row)
14579 .map(|hunk| hunk.row_range.start)
14580 });
14581 }
14582
14583 if let Some(row) = row {
14584 let destination = Point::new(row.0, 0);
14585 let autoscroll = Autoscroll::center();
14586
14587 self.unfold_ranges(&[destination..destination], false, false, cx);
14588 self.change_selections(Some(autoscroll), window, cx, |s| {
14589 s.select_ranges([destination..destination]);
14590 });
14591 }
14592 }
14593
14594 fn do_stage_or_unstage(
14595 &self,
14596 stage: bool,
14597 buffer_id: BufferId,
14598 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14599 cx: &mut App,
14600 ) -> Option<()> {
14601 let project = self.project.as_ref()?;
14602 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14603 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14604 let buffer_snapshot = buffer.read(cx).snapshot();
14605 let file_exists = buffer_snapshot
14606 .file()
14607 .is_some_and(|file| file.disk_state().exists());
14608 diff.update(cx, |diff, cx| {
14609 diff.stage_or_unstage_hunks(
14610 stage,
14611 &hunks
14612 .map(|hunk| buffer_diff::DiffHunk {
14613 buffer_range: hunk.buffer_range,
14614 diff_base_byte_range: hunk.diff_base_byte_range,
14615 secondary_status: hunk.secondary_status,
14616 range: Point::zero()..Point::zero(), // unused
14617 })
14618 .collect::<Vec<_>>(),
14619 &buffer_snapshot,
14620 file_exists,
14621 cx,
14622 )
14623 });
14624 None
14625 }
14626
14627 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14628 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14629 self.buffer
14630 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14631 }
14632
14633 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14634 self.buffer.update(cx, |buffer, cx| {
14635 let ranges = vec![Anchor::min()..Anchor::max()];
14636 if !buffer.all_diff_hunks_expanded()
14637 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14638 {
14639 buffer.collapse_diff_hunks(ranges, cx);
14640 true
14641 } else {
14642 false
14643 }
14644 })
14645 }
14646
14647 fn toggle_diff_hunks_in_ranges(
14648 &mut self,
14649 ranges: Vec<Range<Anchor>>,
14650 cx: &mut Context<'_, Editor>,
14651 ) {
14652 self.buffer.update(cx, |buffer, cx| {
14653 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14654 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14655 })
14656 }
14657
14658 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14659 self.buffer.update(cx, |buffer, cx| {
14660 let snapshot = buffer.snapshot(cx);
14661 let excerpt_id = range.end.excerpt_id;
14662 let point_range = range.to_point(&snapshot);
14663 let expand = !buffer.single_hunk_is_expanded(range, cx);
14664 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14665 })
14666 }
14667
14668 pub(crate) fn apply_all_diff_hunks(
14669 &mut self,
14670 _: &ApplyAllDiffHunks,
14671 window: &mut Window,
14672 cx: &mut Context<Self>,
14673 ) {
14674 let buffers = self.buffer.read(cx).all_buffers();
14675 for branch_buffer in buffers {
14676 branch_buffer.update(cx, |branch_buffer, cx| {
14677 branch_buffer.merge_into_base(Vec::new(), cx);
14678 });
14679 }
14680
14681 if let Some(project) = self.project.clone() {
14682 self.save(true, project, window, cx).detach_and_log_err(cx);
14683 }
14684 }
14685
14686 pub(crate) fn apply_selected_diff_hunks(
14687 &mut self,
14688 _: &ApplyDiffHunk,
14689 window: &mut Window,
14690 cx: &mut Context<Self>,
14691 ) {
14692 let snapshot = self.snapshot(window, cx);
14693 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14694 let mut ranges_by_buffer = HashMap::default();
14695 self.transact(window, cx, |editor, _window, cx| {
14696 for hunk in hunks {
14697 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14698 ranges_by_buffer
14699 .entry(buffer.clone())
14700 .or_insert_with(Vec::new)
14701 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14702 }
14703 }
14704
14705 for (buffer, ranges) in ranges_by_buffer {
14706 buffer.update(cx, |buffer, cx| {
14707 buffer.merge_into_base(ranges, cx);
14708 });
14709 }
14710 });
14711
14712 if let Some(project) = self.project.clone() {
14713 self.save(true, project, window, cx).detach_and_log_err(cx);
14714 }
14715 }
14716
14717 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14718 if hovered != self.gutter_hovered {
14719 self.gutter_hovered = hovered;
14720 cx.notify();
14721 }
14722 }
14723
14724 pub fn insert_blocks(
14725 &mut self,
14726 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14727 autoscroll: Option<Autoscroll>,
14728 cx: &mut Context<Self>,
14729 ) -> Vec<CustomBlockId> {
14730 let blocks = self
14731 .display_map
14732 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14733 if let Some(autoscroll) = autoscroll {
14734 self.request_autoscroll(autoscroll, cx);
14735 }
14736 cx.notify();
14737 blocks
14738 }
14739
14740 pub fn resize_blocks(
14741 &mut self,
14742 heights: HashMap<CustomBlockId, u32>,
14743 autoscroll: Option<Autoscroll>,
14744 cx: &mut Context<Self>,
14745 ) {
14746 self.display_map
14747 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14748 if let Some(autoscroll) = autoscroll {
14749 self.request_autoscroll(autoscroll, cx);
14750 }
14751 cx.notify();
14752 }
14753
14754 pub fn replace_blocks(
14755 &mut self,
14756 renderers: HashMap<CustomBlockId, RenderBlock>,
14757 autoscroll: Option<Autoscroll>,
14758 cx: &mut Context<Self>,
14759 ) {
14760 self.display_map
14761 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14762 if let Some(autoscroll) = autoscroll {
14763 self.request_autoscroll(autoscroll, cx);
14764 }
14765 cx.notify();
14766 }
14767
14768 pub fn remove_blocks(
14769 &mut self,
14770 block_ids: HashSet<CustomBlockId>,
14771 autoscroll: Option<Autoscroll>,
14772 cx: &mut Context<Self>,
14773 ) {
14774 self.display_map.update(cx, |display_map, cx| {
14775 display_map.remove_blocks(block_ids, cx)
14776 });
14777 if let Some(autoscroll) = autoscroll {
14778 self.request_autoscroll(autoscroll, cx);
14779 }
14780 cx.notify();
14781 }
14782
14783 pub fn row_for_block(
14784 &self,
14785 block_id: CustomBlockId,
14786 cx: &mut Context<Self>,
14787 ) -> Option<DisplayRow> {
14788 self.display_map
14789 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14790 }
14791
14792 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14793 self.focused_block = Some(focused_block);
14794 }
14795
14796 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14797 self.focused_block.take()
14798 }
14799
14800 pub fn insert_creases(
14801 &mut self,
14802 creases: impl IntoIterator<Item = Crease<Anchor>>,
14803 cx: &mut Context<Self>,
14804 ) -> Vec<CreaseId> {
14805 self.display_map
14806 .update(cx, |map, cx| map.insert_creases(creases, cx))
14807 }
14808
14809 pub fn remove_creases(
14810 &mut self,
14811 ids: impl IntoIterator<Item = CreaseId>,
14812 cx: &mut Context<Self>,
14813 ) {
14814 self.display_map
14815 .update(cx, |map, cx| map.remove_creases(ids, cx));
14816 }
14817
14818 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14819 self.display_map
14820 .update(cx, |map, cx| map.snapshot(cx))
14821 .longest_row()
14822 }
14823
14824 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14825 self.display_map
14826 .update(cx, |map, cx| map.snapshot(cx))
14827 .max_point()
14828 }
14829
14830 pub fn text(&self, cx: &App) -> String {
14831 self.buffer.read(cx).read(cx).text()
14832 }
14833
14834 pub fn is_empty(&self, cx: &App) -> bool {
14835 self.buffer.read(cx).read(cx).is_empty()
14836 }
14837
14838 pub fn text_option(&self, cx: &App) -> Option<String> {
14839 let text = self.text(cx);
14840 let text = text.trim();
14841
14842 if text.is_empty() {
14843 return None;
14844 }
14845
14846 Some(text.to_string())
14847 }
14848
14849 pub fn set_text(
14850 &mut self,
14851 text: impl Into<Arc<str>>,
14852 window: &mut Window,
14853 cx: &mut Context<Self>,
14854 ) {
14855 self.transact(window, cx, |this, _, cx| {
14856 this.buffer
14857 .read(cx)
14858 .as_singleton()
14859 .expect("you can only call set_text on editors for singleton buffers")
14860 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14861 });
14862 }
14863
14864 pub fn display_text(&self, cx: &mut App) -> String {
14865 self.display_map
14866 .update(cx, |map, cx| map.snapshot(cx))
14867 .text()
14868 }
14869
14870 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14871 let mut wrap_guides = smallvec::smallvec![];
14872
14873 if self.show_wrap_guides == Some(false) {
14874 return wrap_guides;
14875 }
14876
14877 let settings = self.buffer.read(cx).language_settings(cx);
14878 if settings.show_wrap_guides {
14879 match self.soft_wrap_mode(cx) {
14880 SoftWrap::Column(soft_wrap) => {
14881 wrap_guides.push((soft_wrap as usize, true));
14882 }
14883 SoftWrap::Bounded(soft_wrap) => {
14884 wrap_guides.push((soft_wrap as usize, true));
14885 }
14886 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14887 }
14888 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14889 }
14890
14891 wrap_guides
14892 }
14893
14894 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14895 let settings = self.buffer.read(cx).language_settings(cx);
14896 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14897 match mode {
14898 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14899 SoftWrap::None
14900 }
14901 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14902 language_settings::SoftWrap::PreferredLineLength => {
14903 SoftWrap::Column(settings.preferred_line_length)
14904 }
14905 language_settings::SoftWrap::Bounded => {
14906 SoftWrap::Bounded(settings.preferred_line_length)
14907 }
14908 }
14909 }
14910
14911 pub fn set_soft_wrap_mode(
14912 &mut self,
14913 mode: language_settings::SoftWrap,
14914
14915 cx: &mut Context<Self>,
14916 ) {
14917 self.soft_wrap_mode_override = Some(mode);
14918 cx.notify();
14919 }
14920
14921 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
14922 self.hard_wrap = hard_wrap;
14923 cx.notify();
14924 }
14925
14926 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14927 self.text_style_refinement = Some(style);
14928 }
14929
14930 /// called by the Element so we know what style we were most recently rendered with.
14931 pub(crate) fn set_style(
14932 &mut self,
14933 style: EditorStyle,
14934 window: &mut Window,
14935 cx: &mut Context<Self>,
14936 ) {
14937 let rem_size = window.rem_size();
14938 self.display_map.update(cx, |map, cx| {
14939 map.set_font(
14940 style.text.font(),
14941 style.text.font_size.to_pixels(rem_size),
14942 cx,
14943 )
14944 });
14945 self.style = Some(style);
14946 }
14947
14948 pub fn style(&self) -> Option<&EditorStyle> {
14949 self.style.as_ref()
14950 }
14951
14952 // Called by the element. This method is not designed to be called outside of the editor
14953 // element's layout code because it does not notify when rewrapping is computed synchronously.
14954 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14955 self.display_map
14956 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14957 }
14958
14959 pub fn set_soft_wrap(&mut self) {
14960 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14961 }
14962
14963 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14964 if self.soft_wrap_mode_override.is_some() {
14965 self.soft_wrap_mode_override.take();
14966 } else {
14967 let soft_wrap = match self.soft_wrap_mode(cx) {
14968 SoftWrap::GitDiff => return,
14969 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14970 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14971 language_settings::SoftWrap::None
14972 }
14973 };
14974 self.soft_wrap_mode_override = Some(soft_wrap);
14975 }
14976 cx.notify();
14977 }
14978
14979 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14980 let Some(workspace) = self.workspace() else {
14981 return;
14982 };
14983 let fs = workspace.read(cx).app_state().fs.clone();
14984 let current_show = TabBarSettings::get_global(cx).show;
14985 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14986 setting.show = Some(!current_show);
14987 });
14988 }
14989
14990 pub fn toggle_indent_guides(
14991 &mut self,
14992 _: &ToggleIndentGuides,
14993 _: &mut Window,
14994 cx: &mut Context<Self>,
14995 ) {
14996 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14997 self.buffer
14998 .read(cx)
14999 .language_settings(cx)
15000 .indent_guides
15001 .enabled
15002 });
15003 self.show_indent_guides = Some(!currently_enabled);
15004 cx.notify();
15005 }
15006
15007 fn should_show_indent_guides(&self) -> Option<bool> {
15008 self.show_indent_guides
15009 }
15010
15011 pub fn toggle_line_numbers(
15012 &mut self,
15013 _: &ToggleLineNumbers,
15014 _: &mut Window,
15015 cx: &mut Context<Self>,
15016 ) {
15017 let mut editor_settings = EditorSettings::get_global(cx).clone();
15018 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15019 EditorSettings::override_global(editor_settings, cx);
15020 }
15021
15022 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15023 if let Some(show_line_numbers) = self.show_line_numbers {
15024 return show_line_numbers;
15025 }
15026 EditorSettings::get_global(cx).gutter.line_numbers
15027 }
15028
15029 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15030 self.use_relative_line_numbers
15031 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15032 }
15033
15034 pub fn toggle_relative_line_numbers(
15035 &mut self,
15036 _: &ToggleRelativeLineNumbers,
15037 _: &mut Window,
15038 cx: &mut Context<Self>,
15039 ) {
15040 let is_relative = self.should_use_relative_line_numbers(cx);
15041 self.set_relative_line_number(Some(!is_relative), cx)
15042 }
15043
15044 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15045 self.use_relative_line_numbers = is_relative;
15046 cx.notify();
15047 }
15048
15049 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15050 self.show_gutter = show_gutter;
15051 cx.notify();
15052 }
15053
15054 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15055 self.show_scrollbars = show_scrollbars;
15056 cx.notify();
15057 }
15058
15059 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15060 self.show_line_numbers = Some(show_line_numbers);
15061 cx.notify();
15062 }
15063
15064 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15065 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15066 cx.notify();
15067 }
15068
15069 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15070 self.show_code_actions = Some(show_code_actions);
15071 cx.notify();
15072 }
15073
15074 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15075 self.show_runnables = Some(show_runnables);
15076 cx.notify();
15077 }
15078
15079 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15080 self.show_breakpoints = Some(show_breakpoints);
15081 cx.notify();
15082 }
15083
15084 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15085 if self.display_map.read(cx).masked != masked {
15086 self.display_map.update(cx, |map, _| map.masked = masked);
15087 }
15088 cx.notify()
15089 }
15090
15091 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15092 self.show_wrap_guides = Some(show_wrap_guides);
15093 cx.notify();
15094 }
15095
15096 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15097 self.show_indent_guides = Some(show_indent_guides);
15098 cx.notify();
15099 }
15100
15101 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15102 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15103 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15104 if let Some(dir) = file.abs_path(cx).parent() {
15105 return Some(dir.to_owned());
15106 }
15107 }
15108
15109 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15110 return Some(project_path.path.to_path_buf());
15111 }
15112 }
15113
15114 None
15115 }
15116
15117 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15118 self.active_excerpt(cx)?
15119 .1
15120 .read(cx)
15121 .file()
15122 .and_then(|f| f.as_local())
15123 }
15124
15125 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15126 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15127 let buffer = buffer.read(cx);
15128 if let Some(project_path) = buffer.project_path(cx) {
15129 let project = self.project.as_ref()?.read(cx);
15130 project.absolute_path(&project_path, cx)
15131 } else {
15132 buffer
15133 .file()
15134 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15135 }
15136 })
15137 }
15138
15139 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15140 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15141 let project_path = buffer.read(cx).project_path(cx)?;
15142 let project = self.project.as_ref()?.read(cx);
15143 let entry = project.entry_for_path(&project_path, cx)?;
15144 let path = entry.path.to_path_buf();
15145 Some(path)
15146 })
15147 }
15148
15149 pub fn reveal_in_finder(
15150 &mut self,
15151 _: &RevealInFileManager,
15152 _window: &mut Window,
15153 cx: &mut Context<Self>,
15154 ) {
15155 if let Some(target) = self.target_file(cx) {
15156 cx.reveal_path(&target.abs_path(cx));
15157 }
15158 }
15159
15160 pub fn copy_path(
15161 &mut self,
15162 _: &zed_actions::workspace::CopyPath,
15163 _window: &mut Window,
15164 cx: &mut Context<Self>,
15165 ) {
15166 if let Some(path) = self.target_file_abs_path(cx) {
15167 if let Some(path) = path.to_str() {
15168 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15169 }
15170 }
15171 }
15172
15173 pub fn copy_relative_path(
15174 &mut self,
15175 _: &zed_actions::workspace::CopyRelativePath,
15176 _window: &mut Window,
15177 cx: &mut Context<Self>,
15178 ) {
15179 if let Some(path) = self.target_file_path(cx) {
15180 if let Some(path) = path.to_str() {
15181 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15182 }
15183 }
15184 }
15185
15186 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15187 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15188 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15189 } else {
15190 None
15191 }
15192 }
15193
15194 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15195 let _ = maybe!({
15196 let breakpoint_store = self.breakpoint_store.as_ref()?;
15197
15198 let Some((_, _, active_position)) =
15199 breakpoint_store.read(cx).active_position().cloned()
15200 else {
15201 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15202 return None;
15203 };
15204
15205 let snapshot = self
15206 .project
15207 .as_ref()?
15208 .read(cx)
15209 .buffer_for_id(active_position.buffer_id?, cx)?
15210 .read(cx)
15211 .snapshot();
15212
15213 for (id, ExcerptRange { context, .. }) in self
15214 .buffer
15215 .read(cx)
15216 .excerpts_for_buffer(active_position.buffer_id?, cx)
15217 {
15218 if context.start.cmp(&active_position, &snapshot).is_ge()
15219 || context.end.cmp(&active_position, &snapshot).is_lt()
15220 {
15221 continue;
15222 }
15223 let snapshot = self.buffer.read(cx).snapshot(cx);
15224 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15225
15226 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15227 self.go_to_line::<DebugCurrentRowHighlight>(
15228 multibuffer_anchor,
15229 Some(cx.theme().colors().editor_debugger_active_line_background),
15230 window,
15231 cx,
15232 );
15233
15234 cx.notify();
15235 }
15236
15237 Some(())
15238 });
15239 }
15240
15241 pub fn copy_file_name_without_extension(
15242 &mut self,
15243 _: &CopyFileNameWithoutExtension,
15244 _: &mut Window,
15245 cx: &mut Context<Self>,
15246 ) {
15247 if let Some(file) = self.target_file(cx) {
15248 if let Some(file_stem) = file.path().file_stem() {
15249 if let Some(name) = file_stem.to_str() {
15250 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15251 }
15252 }
15253 }
15254 }
15255
15256 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15257 if let Some(file) = self.target_file(cx) {
15258 if let Some(file_name) = file.path().file_name() {
15259 if let Some(name) = file_name.to_str() {
15260 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15261 }
15262 }
15263 }
15264 }
15265
15266 pub fn toggle_git_blame(
15267 &mut self,
15268 _: &::git::Blame,
15269 window: &mut Window,
15270 cx: &mut Context<Self>,
15271 ) {
15272 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15273
15274 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15275 self.start_git_blame(true, window, cx);
15276 }
15277
15278 cx.notify();
15279 }
15280
15281 pub fn toggle_git_blame_inline(
15282 &mut self,
15283 _: &ToggleGitBlameInline,
15284 window: &mut Window,
15285 cx: &mut Context<Self>,
15286 ) {
15287 self.toggle_git_blame_inline_internal(true, window, cx);
15288 cx.notify();
15289 }
15290
15291 pub fn git_blame_inline_enabled(&self) -> bool {
15292 self.git_blame_inline_enabled
15293 }
15294
15295 pub fn toggle_selection_menu(
15296 &mut self,
15297 _: &ToggleSelectionMenu,
15298 _: &mut Window,
15299 cx: &mut Context<Self>,
15300 ) {
15301 self.show_selection_menu = self
15302 .show_selection_menu
15303 .map(|show_selections_menu| !show_selections_menu)
15304 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15305
15306 cx.notify();
15307 }
15308
15309 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15310 self.show_selection_menu
15311 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15312 }
15313
15314 fn start_git_blame(
15315 &mut self,
15316 user_triggered: bool,
15317 window: &mut Window,
15318 cx: &mut Context<Self>,
15319 ) {
15320 if let Some(project) = self.project.as_ref() {
15321 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15322 return;
15323 };
15324
15325 if buffer.read(cx).file().is_none() {
15326 return;
15327 }
15328
15329 let focused = self.focus_handle(cx).contains_focused(window, cx);
15330
15331 let project = project.clone();
15332 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15333 self.blame_subscription =
15334 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15335 self.blame = Some(blame);
15336 }
15337 }
15338
15339 fn toggle_git_blame_inline_internal(
15340 &mut self,
15341 user_triggered: bool,
15342 window: &mut Window,
15343 cx: &mut Context<Self>,
15344 ) {
15345 if self.git_blame_inline_enabled {
15346 self.git_blame_inline_enabled = false;
15347 self.show_git_blame_inline = false;
15348 self.show_git_blame_inline_delay_task.take();
15349 } else {
15350 self.git_blame_inline_enabled = true;
15351 self.start_git_blame_inline(user_triggered, window, cx);
15352 }
15353
15354 cx.notify();
15355 }
15356
15357 fn start_git_blame_inline(
15358 &mut self,
15359 user_triggered: bool,
15360 window: &mut Window,
15361 cx: &mut Context<Self>,
15362 ) {
15363 self.start_git_blame(user_triggered, window, cx);
15364
15365 if ProjectSettings::get_global(cx)
15366 .git
15367 .inline_blame_delay()
15368 .is_some()
15369 {
15370 self.start_inline_blame_timer(window, cx);
15371 } else {
15372 self.show_git_blame_inline = true
15373 }
15374 }
15375
15376 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15377 self.blame.as_ref()
15378 }
15379
15380 pub fn show_git_blame_gutter(&self) -> bool {
15381 self.show_git_blame_gutter
15382 }
15383
15384 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15385 self.show_git_blame_gutter && self.has_blame_entries(cx)
15386 }
15387
15388 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15389 self.show_git_blame_inline
15390 && (self.focus_handle.is_focused(window)
15391 || self
15392 .git_blame_inline_tooltip
15393 .as_ref()
15394 .and_then(|t| t.upgrade())
15395 .is_some())
15396 && !self.newest_selection_head_on_empty_line(cx)
15397 && self.has_blame_entries(cx)
15398 }
15399
15400 fn has_blame_entries(&self, cx: &App) -> bool {
15401 self.blame()
15402 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15403 }
15404
15405 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15406 let cursor_anchor = self.selections.newest_anchor().head();
15407
15408 let snapshot = self.buffer.read(cx).snapshot(cx);
15409 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15410
15411 snapshot.line_len(buffer_row) == 0
15412 }
15413
15414 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15415 let buffer_and_selection = maybe!({
15416 let selection = self.selections.newest::<Point>(cx);
15417 let selection_range = selection.range();
15418
15419 let multi_buffer = self.buffer().read(cx);
15420 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15421 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15422
15423 let (buffer, range, _) = if selection.reversed {
15424 buffer_ranges.first()
15425 } else {
15426 buffer_ranges.last()
15427 }?;
15428
15429 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15430 ..text::ToPoint::to_point(&range.end, &buffer).row;
15431 Some((
15432 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15433 selection,
15434 ))
15435 });
15436
15437 let Some((buffer, selection)) = buffer_and_selection else {
15438 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15439 };
15440
15441 let Some(project) = self.project.as_ref() else {
15442 return Task::ready(Err(anyhow!("editor does not have project")));
15443 };
15444
15445 project.update(cx, |project, cx| {
15446 project.get_permalink_to_line(&buffer, selection, cx)
15447 })
15448 }
15449
15450 pub fn copy_permalink_to_line(
15451 &mut self,
15452 _: &CopyPermalinkToLine,
15453 window: &mut Window,
15454 cx: &mut Context<Self>,
15455 ) {
15456 let permalink_task = self.get_permalink_to_line(cx);
15457 let workspace = self.workspace();
15458
15459 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15460 Ok(permalink) => {
15461 cx.update(|_, cx| {
15462 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15463 })
15464 .ok();
15465 }
15466 Err(err) => {
15467 let message = format!("Failed to copy permalink: {err}");
15468
15469 Err::<(), anyhow::Error>(err).log_err();
15470
15471 if let Some(workspace) = workspace {
15472 workspace
15473 .update_in(cx, |workspace, _, cx| {
15474 struct CopyPermalinkToLine;
15475
15476 workspace.show_toast(
15477 Toast::new(
15478 NotificationId::unique::<CopyPermalinkToLine>(),
15479 message,
15480 ),
15481 cx,
15482 )
15483 })
15484 .ok();
15485 }
15486 }
15487 })
15488 .detach();
15489 }
15490
15491 pub fn copy_file_location(
15492 &mut self,
15493 _: &CopyFileLocation,
15494 _: &mut Window,
15495 cx: &mut Context<Self>,
15496 ) {
15497 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15498 if let Some(file) = self.target_file(cx) {
15499 if let Some(path) = file.path().to_str() {
15500 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15501 }
15502 }
15503 }
15504
15505 pub fn open_permalink_to_line(
15506 &mut self,
15507 _: &OpenPermalinkToLine,
15508 window: &mut Window,
15509 cx: &mut Context<Self>,
15510 ) {
15511 let permalink_task = self.get_permalink_to_line(cx);
15512 let workspace = self.workspace();
15513
15514 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15515 Ok(permalink) => {
15516 cx.update(|_, cx| {
15517 cx.open_url(permalink.as_ref());
15518 })
15519 .ok();
15520 }
15521 Err(err) => {
15522 let message = format!("Failed to open permalink: {err}");
15523
15524 Err::<(), anyhow::Error>(err).log_err();
15525
15526 if let Some(workspace) = workspace {
15527 workspace
15528 .update(cx, |workspace, cx| {
15529 struct OpenPermalinkToLine;
15530
15531 workspace.show_toast(
15532 Toast::new(
15533 NotificationId::unique::<OpenPermalinkToLine>(),
15534 message,
15535 ),
15536 cx,
15537 )
15538 })
15539 .ok();
15540 }
15541 }
15542 })
15543 .detach();
15544 }
15545
15546 pub fn insert_uuid_v4(
15547 &mut self,
15548 _: &InsertUuidV4,
15549 window: &mut Window,
15550 cx: &mut Context<Self>,
15551 ) {
15552 self.insert_uuid(UuidVersion::V4, window, cx);
15553 }
15554
15555 pub fn insert_uuid_v7(
15556 &mut self,
15557 _: &InsertUuidV7,
15558 window: &mut Window,
15559 cx: &mut Context<Self>,
15560 ) {
15561 self.insert_uuid(UuidVersion::V7, window, cx);
15562 }
15563
15564 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15565 self.transact(window, cx, |this, window, cx| {
15566 let edits = this
15567 .selections
15568 .all::<Point>(cx)
15569 .into_iter()
15570 .map(|selection| {
15571 let uuid = match version {
15572 UuidVersion::V4 => uuid::Uuid::new_v4(),
15573 UuidVersion::V7 => uuid::Uuid::now_v7(),
15574 };
15575
15576 (selection.range(), uuid.to_string())
15577 });
15578 this.edit(edits, cx);
15579 this.refresh_inline_completion(true, false, window, cx);
15580 });
15581 }
15582
15583 pub fn open_selections_in_multibuffer(
15584 &mut self,
15585 _: &OpenSelectionsInMultibuffer,
15586 window: &mut Window,
15587 cx: &mut Context<Self>,
15588 ) {
15589 let multibuffer = self.buffer.read(cx);
15590
15591 let Some(buffer) = multibuffer.as_singleton() else {
15592 return;
15593 };
15594
15595 let Some(workspace) = self.workspace() else {
15596 return;
15597 };
15598
15599 let locations = self
15600 .selections
15601 .disjoint_anchors()
15602 .iter()
15603 .map(|range| Location {
15604 buffer: buffer.clone(),
15605 range: range.start.text_anchor..range.end.text_anchor,
15606 })
15607 .collect::<Vec<_>>();
15608
15609 let title = multibuffer.title(cx).to_string();
15610
15611 cx.spawn_in(window, async move |_, cx| {
15612 workspace.update_in(cx, |workspace, window, cx| {
15613 Self::open_locations_in_multibuffer(
15614 workspace,
15615 locations,
15616 format!("Selections for '{title}'"),
15617 false,
15618 MultibufferSelectionMode::All,
15619 window,
15620 cx,
15621 );
15622 })
15623 })
15624 .detach();
15625 }
15626
15627 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15628 /// last highlight added will be used.
15629 ///
15630 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15631 pub fn highlight_rows<T: 'static>(
15632 &mut self,
15633 range: Range<Anchor>,
15634 color: Hsla,
15635 should_autoscroll: bool,
15636 cx: &mut Context<Self>,
15637 ) {
15638 let snapshot = self.buffer().read(cx).snapshot(cx);
15639 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15640 let ix = row_highlights.binary_search_by(|highlight| {
15641 Ordering::Equal
15642 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15643 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15644 });
15645
15646 if let Err(mut ix) = ix {
15647 let index = post_inc(&mut self.highlight_order);
15648
15649 // If this range intersects with the preceding highlight, then merge it with
15650 // the preceding highlight. Otherwise insert a new highlight.
15651 let mut merged = false;
15652 if ix > 0 {
15653 let prev_highlight = &mut row_highlights[ix - 1];
15654 if prev_highlight
15655 .range
15656 .end
15657 .cmp(&range.start, &snapshot)
15658 .is_ge()
15659 {
15660 ix -= 1;
15661 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15662 prev_highlight.range.end = range.end;
15663 }
15664 merged = true;
15665 prev_highlight.index = index;
15666 prev_highlight.color = color;
15667 prev_highlight.should_autoscroll = should_autoscroll;
15668 }
15669 }
15670
15671 if !merged {
15672 row_highlights.insert(
15673 ix,
15674 RowHighlight {
15675 range: range.clone(),
15676 index,
15677 color,
15678 should_autoscroll,
15679 },
15680 );
15681 }
15682
15683 // If any of the following highlights intersect with this one, merge them.
15684 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15685 let highlight = &row_highlights[ix];
15686 if next_highlight
15687 .range
15688 .start
15689 .cmp(&highlight.range.end, &snapshot)
15690 .is_le()
15691 {
15692 if next_highlight
15693 .range
15694 .end
15695 .cmp(&highlight.range.end, &snapshot)
15696 .is_gt()
15697 {
15698 row_highlights[ix].range.end = next_highlight.range.end;
15699 }
15700 row_highlights.remove(ix + 1);
15701 } else {
15702 break;
15703 }
15704 }
15705 }
15706 }
15707
15708 /// Remove any highlighted row ranges of the given type that intersect the
15709 /// given ranges.
15710 pub fn remove_highlighted_rows<T: 'static>(
15711 &mut self,
15712 ranges_to_remove: Vec<Range<Anchor>>,
15713 cx: &mut Context<Self>,
15714 ) {
15715 let snapshot = self.buffer().read(cx).snapshot(cx);
15716 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15717 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15718 row_highlights.retain(|highlight| {
15719 while let Some(range_to_remove) = ranges_to_remove.peek() {
15720 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15721 Ordering::Less | Ordering::Equal => {
15722 ranges_to_remove.next();
15723 }
15724 Ordering::Greater => {
15725 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15726 Ordering::Less | Ordering::Equal => {
15727 return false;
15728 }
15729 Ordering::Greater => break,
15730 }
15731 }
15732 }
15733 }
15734
15735 true
15736 })
15737 }
15738
15739 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15740 pub fn clear_row_highlights<T: 'static>(&mut self) {
15741 self.highlighted_rows.remove(&TypeId::of::<T>());
15742 }
15743
15744 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15745 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15746 self.highlighted_rows
15747 .get(&TypeId::of::<T>())
15748 .map_or(&[] as &[_], |vec| vec.as_slice())
15749 .iter()
15750 .map(|highlight| (highlight.range.clone(), highlight.color))
15751 }
15752
15753 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15754 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15755 /// Allows to ignore certain kinds of highlights.
15756 pub fn highlighted_display_rows(
15757 &self,
15758 window: &mut Window,
15759 cx: &mut App,
15760 ) -> BTreeMap<DisplayRow, LineHighlight> {
15761 let snapshot = self.snapshot(window, cx);
15762 let mut used_highlight_orders = HashMap::default();
15763 self.highlighted_rows
15764 .iter()
15765 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15766 .fold(
15767 BTreeMap::<DisplayRow, LineHighlight>::new(),
15768 |mut unique_rows, highlight| {
15769 let start = highlight.range.start.to_display_point(&snapshot);
15770 let end = highlight.range.end.to_display_point(&snapshot);
15771 let start_row = start.row().0;
15772 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15773 && end.column() == 0
15774 {
15775 end.row().0.saturating_sub(1)
15776 } else {
15777 end.row().0
15778 };
15779 for row in start_row..=end_row {
15780 let used_index =
15781 used_highlight_orders.entry(row).or_insert(highlight.index);
15782 if highlight.index >= *used_index {
15783 *used_index = highlight.index;
15784 unique_rows.insert(DisplayRow(row), highlight.color.into());
15785 }
15786 }
15787 unique_rows
15788 },
15789 )
15790 }
15791
15792 pub fn highlighted_display_row_for_autoscroll(
15793 &self,
15794 snapshot: &DisplaySnapshot,
15795 ) -> Option<DisplayRow> {
15796 self.highlighted_rows
15797 .values()
15798 .flat_map(|highlighted_rows| highlighted_rows.iter())
15799 .filter_map(|highlight| {
15800 if highlight.should_autoscroll {
15801 Some(highlight.range.start.to_display_point(snapshot).row())
15802 } else {
15803 None
15804 }
15805 })
15806 .min()
15807 }
15808
15809 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
15810 self.highlight_background::<SearchWithinRange>(
15811 ranges,
15812 |colors| colors.editor_document_highlight_read_background,
15813 cx,
15814 )
15815 }
15816
15817 pub fn set_breadcrumb_header(&mut self, new_header: String) {
15818 self.breadcrumb_header = Some(new_header);
15819 }
15820
15821 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
15822 self.clear_background_highlights::<SearchWithinRange>(cx);
15823 }
15824
15825 pub fn highlight_background<T: 'static>(
15826 &mut self,
15827 ranges: &[Range<Anchor>],
15828 color_fetcher: fn(&ThemeColors) -> Hsla,
15829 cx: &mut Context<Self>,
15830 ) {
15831 self.background_highlights
15832 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15833 self.scrollbar_marker_state.dirty = true;
15834 cx.notify();
15835 }
15836
15837 pub fn clear_background_highlights<T: 'static>(
15838 &mut self,
15839 cx: &mut Context<Self>,
15840 ) -> Option<BackgroundHighlight> {
15841 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15842 if !text_highlights.1.is_empty() {
15843 self.scrollbar_marker_state.dirty = true;
15844 cx.notify();
15845 }
15846 Some(text_highlights)
15847 }
15848
15849 pub fn highlight_gutter<T: 'static>(
15850 &mut self,
15851 ranges: &[Range<Anchor>],
15852 color_fetcher: fn(&App) -> Hsla,
15853 cx: &mut Context<Self>,
15854 ) {
15855 self.gutter_highlights
15856 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15857 cx.notify();
15858 }
15859
15860 pub fn clear_gutter_highlights<T: 'static>(
15861 &mut self,
15862 cx: &mut Context<Self>,
15863 ) -> Option<GutterHighlight> {
15864 cx.notify();
15865 self.gutter_highlights.remove(&TypeId::of::<T>())
15866 }
15867
15868 #[cfg(feature = "test-support")]
15869 pub fn all_text_background_highlights(
15870 &self,
15871 window: &mut Window,
15872 cx: &mut Context<Self>,
15873 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15874 let snapshot = self.snapshot(window, cx);
15875 let buffer = &snapshot.buffer_snapshot;
15876 let start = buffer.anchor_before(0);
15877 let end = buffer.anchor_after(buffer.len());
15878 let theme = cx.theme().colors();
15879 self.background_highlights_in_range(start..end, &snapshot, theme)
15880 }
15881
15882 #[cfg(feature = "test-support")]
15883 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15884 let snapshot = self.buffer().read(cx).snapshot(cx);
15885
15886 let highlights = self
15887 .background_highlights
15888 .get(&TypeId::of::<items::BufferSearchHighlights>());
15889
15890 if let Some((_color, ranges)) = highlights {
15891 ranges
15892 .iter()
15893 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15894 .collect_vec()
15895 } else {
15896 vec![]
15897 }
15898 }
15899
15900 fn document_highlights_for_position<'a>(
15901 &'a self,
15902 position: Anchor,
15903 buffer: &'a MultiBufferSnapshot,
15904 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15905 let read_highlights = self
15906 .background_highlights
15907 .get(&TypeId::of::<DocumentHighlightRead>())
15908 .map(|h| &h.1);
15909 let write_highlights = self
15910 .background_highlights
15911 .get(&TypeId::of::<DocumentHighlightWrite>())
15912 .map(|h| &h.1);
15913 let left_position = position.bias_left(buffer);
15914 let right_position = position.bias_right(buffer);
15915 read_highlights
15916 .into_iter()
15917 .chain(write_highlights)
15918 .flat_map(move |ranges| {
15919 let start_ix = match ranges.binary_search_by(|probe| {
15920 let cmp = probe.end.cmp(&left_position, buffer);
15921 if cmp.is_ge() {
15922 Ordering::Greater
15923 } else {
15924 Ordering::Less
15925 }
15926 }) {
15927 Ok(i) | Err(i) => i,
15928 };
15929
15930 ranges[start_ix..]
15931 .iter()
15932 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15933 })
15934 }
15935
15936 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15937 self.background_highlights
15938 .get(&TypeId::of::<T>())
15939 .map_or(false, |(_, highlights)| !highlights.is_empty())
15940 }
15941
15942 pub fn background_highlights_in_range(
15943 &self,
15944 search_range: Range<Anchor>,
15945 display_snapshot: &DisplaySnapshot,
15946 theme: &ThemeColors,
15947 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15948 let mut results = Vec::new();
15949 for (color_fetcher, ranges) in self.background_highlights.values() {
15950 let color = color_fetcher(theme);
15951 let start_ix = match ranges.binary_search_by(|probe| {
15952 let cmp = probe
15953 .end
15954 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15955 if cmp.is_gt() {
15956 Ordering::Greater
15957 } else {
15958 Ordering::Less
15959 }
15960 }) {
15961 Ok(i) | Err(i) => i,
15962 };
15963 for range in &ranges[start_ix..] {
15964 if range
15965 .start
15966 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15967 .is_ge()
15968 {
15969 break;
15970 }
15971
15972 let start = range.start.to_display_point(display_snapshot);
15973 let end = range.end.to_display_point(display_snapshot);
15974 results.push((start..end, color))
15975 }
15976 }
15977 results
15978 }
15979
15980 pub fn background_highlight_row_ranges<T: 'static>(
15981 &self,
15982 search_range: Range<Anchor>,
15983 display_snapshot: &DisplaySnapshot,
15984 count: usize,
15985 ) -> Vec<RangeInclusive<DisplayPoint>> {
15986 let mut results = Vec::new();
15987 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15988 return vec![];
15989 };
15990
15991 let start_ix = match ranges.binary_search_by(|probe| {
15992 let cmp = probe
15993 .end
15994 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15995 if cmp.is_gt() {
15996 Ordering::Greater
15997 } else {
15998 Ordering::Less
15999 }
16000 }) {
16001 Ok(i) | Err(i) => i,
16002 };
16003 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16004 if let (Some(start_display), Some(end_display)) = (start, end) {
16005 results.push(
16006 start_display.to_display_point(display_snapshot)
16007 ..=end_display.to_display_point(display_snapshot),
16008 );
16009 }
16010 };
16011 let mut start_row: Option<Point> = None;
16012 let mut end_row: Option<Point> = None;
16013 if ranges.len() > count {
16014 return Vec::new();
16015 }
16016 for range in &ranges[start_ix..] {
16017 if range
16018 .start
16019 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16020 .is_ge()
16021 {
16022 break;
16023 }
16024 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16025 if let Some(current_row) = &end_row {
16026 if end.row == current_row.row {
16027 continue;
16028 }
16029 }
16030 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16031 if start_row.is_none() {
16032 assert_eq!(end_row, None);
16033 start_row = Some(start);
16034 end_row = Some(end);
16035 continue;
16036 }
16037 if let Some(current_end) = end_row.as_mut() {
16038 if start.row > current_end.row + 1 {
16039 push_region(start_row, end_row);
16040 start_row = Some(start);
16041 end_row = Some(end);
16042 } else {
16043 // Merge two hunks.
16044 *current_end = end;
16045 }
16046 } else {
16047 unreachable!();
16048 }
16049 }
16050 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16051 push_region(start_row, end_row);
16052 results
16053 }
16054
16055 pub fn gutter_highlights_in_range(
16056 &self,
16057 search_range: Range<Anchor>,
16058 display_snapshot: &DisplaySnapshot,
16059 cx: &App,
16060 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16061 let mut results = Vec::new();
16062 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16063 let color = color_fetcher(cx);
16064 let start_ix = match ranges.binary_search_by(|probe| {
16065 let cmp = probe
16066 .end
16067 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16068 if cmp.is_gt() {
16069 Ordering::Greater
16070 } else {
16071 Ordering::Less
16072 }
16073 }) {
16074 Ok(i) | Err(i) => i,
16075 };
16076 for range in &ranges[start_ix..] {
16077 if range
16078 .start
16079 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16080 .is_ge()
16081 {
16082 break;
16083 }
16084
16085 let start = range.start.to_display_point(display_snapshot);
16086 let end = range.end.to_display_point(display_snapshot);
16087 results.push((start..end, color))
16088 }
16089 }
16090 results
16091 }
16092
16093 /// Get the text ranges corresponding to the redaction query
16094 pub fn redacted_ranges(
16095 &self,
16096 search_range: Range<Anchor>,
16097 display_snapshot: &DisplaySnapshot,
16098 cx: &App,
16099 ) -> Vec<Range<DisplayPoint>> {
16100 display_snapshot
16101 .buffer_snapshot
16102 .redacted_ranges(search_range, |file| {
16103 if let Some(file) = file {
16104 file.is_private()
16105 && EditorSettings::get(
16106 Some(SettingsLocation {
16107 worktree_id: file.worktree_id(cx),
16108 path: file.path().as_ref(),
16109 }),
16110 cx,
16111 )
16112 .redact_private_values
16113 } else {
16114 false
16115 }
16116 })
16117 .map(|range| {
16118 range.start.to_display_point(display_snapshot)
16119 ..range.end.to_display_point(display_snapshot)
16120 })
16121 .collect()
16122 }
16123
16124 pub fn highlight_text<T: 'static>(
16125 &mut self,
16126 ranges: Vec<Range<Anchor>>,
16127 style: HighlightStyle,
16128 cx: &mut Context<Self>,
16129 ) {
16130 self.display_map.update(cx, |map, _| {
16131 map.highlight_text(TypeId::of::<T>(), ranges, style)
16132 });
16133 cx.notify();
16134 }
16135
16136 pub(crate) fn highlight_inlays<T: 'static>(
16137 &mut self,
16138 highlights: Vec<InlayHighlight>,
16139 style: HighlightStyle,
16140 cx: &mut Context<Self>,
16141 ) {
16142 self.display_map.update(cx, |map, _| {
16143 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16144 });
16145 cx.notify();
16146 }
16147
16148 pub fn text_highlights<'a, T: 'static>(
16149 &'a self,
16150 cx: &'a App,
16151 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16152 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16153 }
16154
16155 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16156 let cleared = self
16157 .display_map
16158 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16159 if cleared {
16160 cx.notify();
16161 }
16162 }
16163
16164 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16165 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16166 && self.focus_handle.is_focused(window)
16167 }
16168
16169 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16170 self.show_cursor_when_unfocused = is_enabled;
16171 cx.notify();
16172 }
16173
16174 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16175 cx.notify();
16176 }
16177
16178 fn on_buffer_event(
16179 &mut self,
16180 multibuffer: &Entity<MultiBuffer>,
16181 event: &multi_buffer::Event,
16182 window: &mut Window,
16183 cx: &mut Context<Self>,
16184 ) {
16185 match event {
16186 multi_buffer::Event::Edited {
16187 singleton_buffer_edited,
16188 edited_buffer: buffer_edited,
16189 } => {
16190 self.scrollbar_marker_state.dirty = true;
16191 self.active_indent_guides_state.dirty = true;
16192 self.refresh_active_diagnostics(cx);
16193 self.refresh_code_actions(window, cx);
16194 if self.has_active_inline_completion() {
16195 self.update_visible_inline_completion(window, cx);
16196 }
16197 if let Some(buffer) = buffer_edited {
16198 let buffer_id = buffer.read(cx).remote_id();
16199 if !self.registered_buffers.contains_key(&buffer_id) {
16200 if let Some(project) = self.project.as_ref() {
16201 project.update(cx, |project, cx| {
16202 self.registered_buffers.insert(
16203 buffer_id,
16204 project.register_buffer_with_language_servers(&buffer, cx),
16205 );
16206 })
16207 }
16208 }
16209 }
16210 cx.emit(EditorEvent::BufferEdited);
16211 cx.emit(SearchEvent::MatchesInvalidated);
16212 if *singleton_buffer_edited {
16213 if let Some(project) = &self.project {
16214 #[allow(clippy::mutable_key_type)]
16215 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16216 multibuffer
16217 .all_buffers()
16218 .into_iter()
16219 .filter_map(|buffer| {
16220 buffer.update(cx, |buffer, cx| {
16221 let language = buffer.language()?;
16222 let should_discard = project.update(cx, |project, cx| {
16223 project.is_local()
16224 && !project.has_language_servers_for(buffer, cx)
16225 });
16226 should_discard.not().then_some(language.clone())
16227 })
16228 })
16229 .collect::<HashSet<_>>()
16230 });
16231 if !languages_affected.is_empty() {
16232 self.refresh_inlay_hints(
16233 InlayHintRefreshReason::BufferEdited(languages_affected),
16234 cx,
16235 );
16236 }
16237 }
16238 }
16239
16240 let Some(project) = &self.project else { return };
16241 let (telemetry, is_via_ssh) = {
16242 let project = project.read(cx);
16243 let telemetry = project.client().telemetry().clone();
16244 let is_via_ssh = project.is_via_ssh();
16245 (telemetry, is_via_ssh)
16246 };
16247 refresh_linked_ranges(self, window, cx);
16248 telemetry.log_edit_event("editor", is_via_ssh);
16249 }
16250 multi_buffer::Event::ExcerptsAdded {
16251 buffer,
16252 predecessor,
16253 excerpts,
16254 } => {
16255 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16256 let buffer_id = buffer.read(cx).remote_id();
16257 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16258 if let Some(project) = &self.project {
16259 get_uncommitted_diff_for_buffer(
16260 project,
16261 [buffer.clone()],
16262 self.buffer.clone(),
16263 cx,
16264 )
16265 .detach();
16266 }
16267 }
16268 cx.emit(EditorEvent::ExcerptsAdded {
16269 buffer: buffer.clone(),
16270 predecessor: *predecessor,
16271 excerpts: excerpts.clone(),
16272 });
16273 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16274 }
16275 multi_buffer::Event::ExcerptsRemoved { ids } => {
16276 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16277 let buffer = self.buffer.read(cx);
16278 self.registered_buffers
16279 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16280 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16281 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16282 }
16283 multi_buffer::Event::ExcerptsEdited {
16284 excerpt_ids,
16285 buffer_ids,
16286 } => {
16287 self.display_map.update(cx, |map, cx| {
16288 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16289 });
16290 cx.emit(EditorEvent::ExcerptsEdited {
16291 ids: excerpt_ids.clone(),
16292 })
16293 }
16294 multi_buffer::Event::ExcerptsExpanded { ids } => {
16295 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16296 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16297 }
16298 multi_buffer::Event::Reparsed(buffer_id) => {
16299 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16300 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16301
16302 cx.emit(EditorEvent::Reparsed(*buffer_id));
16303 }
16304 multi_buffer::Event::DiffHunksToggled => {
16305 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16306 }
16307 multi_buffer::Event::LanguageChanged(buffer_id) => {
16308 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16309 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16310 cx.emit(EditorEvent::Reparsed(*buffer_id));
16311 cx.notify();
16312 }
16313 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16314 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16315 multi_buffer::Event::FileHandleChanged
16316 | multi_buffer::Event::Reloaded
16317 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16318 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16319 multi_buffer::Event::DiagnosticsUpdated => {
16320 self.refresh_active_diagnostics(cx);
16321 self.refresh_inline_diagnostics(true, window, cx);
16322 self.scrollbar_marker_state.dirty = true;
16323 cx.notify();
16324 }
16325 _ => {}
16326 };
16327 }
16328
16329 fn on_display_map_changed(
16330 &mut self,
16331 _: Entity<DisplayMap>,
16332 _: &mut Window,
16333 cx: &mut Context<Self>,
16334 ) {
16335 cx.notify();
16336 }
16337
16338 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16339 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16340 self.update_edit_prediction_settings(cx);
16341 self.refresh_inline_completion(true, false, window, cx);
16342 self.refresh_inlay_hints(
16343 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16344 self.selections.newest_anchor().head(),
16345 &self.buffer.read(cx).snapshot(cx),
16346 cx,
16347 )),
16348 cx,
16349 );
16350
16351 let old_cursor_shape = self.cursor_shape;
16352
16353 {
16354 let editor_settings = EditorSettings::get_global(cx);
16355 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16356 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16357 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16358 }
16359
16360 if old_cursor_shape != self.cursor_shape {
16361 cx.emit(EditorEvent::CursorShapeChanged);
16362 }
16363
16364 let project_settings = ProjectSettings::get_global(cx);
16365 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16366
16367 if self.mode == EditorMode::Full {
16368 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16369 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16370 if self.show_inline_diagnostics != show_inline_diagnostics {
16371 self.show_inline_diagnostics = show_inline_diagnostics;
16372 self.refresh_inline_diagnostics(false, window, cx);
16373 }
16374
16375 if self.git_blame_inline_enabled != inline_blame_enabled {
16376 self.toggle_git_blame_inline_internal(false, window, cx);
16377 }
16378 }
16379
16380 cx.notify();
16381 }
16382
16383 pub fn set_searchable(&mut self, searchable: bool) {
16384 self.searchable = searchable;
16385 }
16386
16387 pub fn searchable(&self) -> bool {
16388 self.searchable
16389 }
16390
16391 fn open_proposed_changes_editor(
16392 &mut self,
16393 _: &OpenProposedChangesEditor,
16394 window: &mut Window,
16395 cx: &mut Context<Self>,
16396 ) {
16397 let Some(workspace) = self.workspace() else {
16398 cx.propagate();
16399 return;
16400 };
16401
16402 let selections = self.selections.all::<usize>(cx);
16403 let multi_buffer = self.buffer.read(cx);
16404 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16405 let mut new_selections_by_buffer = HashMap::default();
16406 for selection in selections {
16407 for (buffer, range, _) in
16408 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16409 {
16410 let mut range = range.to_point(buffer);
16411 range.start.column = 0;
16412 range.end.column = buffer.line_len(range.end.row);
16413 new_selections_by_buffer
16414 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16415 .or_insert(Vec::new())
16416 .push(range)
16417 }
16418 }
16419
16420 let proposed_changes_buffers = new_selections_by_buffer
16421 .into_iter()
16422 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16423 .collect::<Vec<_>>();
16424 let proposed_changes_editor = cx.new(|cx| {
16425 ProposedChangesEditor::new(
16426 "Proposed changes",
16427 proposed_changes_buffers,
16428 self.project.clone(),
16429 window,
16430 cx,
16431 )
16432 });
16433
16434 window.defer(cx, move |window, cx| {
16435 workspace.update(cx, |workspace, cx| {
16436 workspace.active_pane().update(cx, |pane, cx| {
16437 pane.add_item(
16438 Box::new(proposed_changes_editor),
16439 true,
16440 true,
16441 None,
16442 window,
16443 cx,
16444 );
16445 });
16446 });
16447 });
16448 }
16449
16450 pub fn open_excerpts_in_split(
16451 &mut self,
16452 _: &OpenExcerptsSplit,
16453 window: &mut Window,
16454 cx: &mut Context<Self>,
16455 ) {
16456 self.open_excerpts_common(None, true, window, cx)
16457 }
16458
16459 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16460 self.open_excerpts_common(None, false, window, cx)
16461 }
16462
16463 fn open_excerpts_common(
16464 &mut self,
16465 jump_data: Option<JumpData>,
16466 split: bool,
16467 window: &mut Window,
16468 cx: &mut Context<Self>,
16469 ) {
16470 let Some(workspace) = self.workspace() else {
16471 cx.propagate();
16472 return;
16473 };
16474
16475 if self.buffer.read(cx).is_singleton() {
16476 cx.propagate();
16477 return;
16478 }
16479
16480 let mut new_selections_by_buffer = HashMap::default();
16481 match &jump_data {
16482 Some(JumpData::MultiBufferPoint {
16483 excerpt_id,
16484 position,
16485 anchor,
16486 line_offset_from_top,
16487 }) => {
16488 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16489 if let Some(buffer) = multi_buffer_snapshot
16490 .buffer_id_for_excerpt(*excerpt_id)
16491 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16492 {
16493 let buffer_snapshot = buffer.read(cx).snapshot();
16494 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16495 language::ToPoint::to_point(anchor, &buffer_snapshot)
16496 } else {
16497 buffer_snapshot.clip_point(*position, Bias::Left)
16498 };
16499 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16500 new_selections_by_buffer.insert(
16501 buffer,
16502 (
16503 vec![jump_to_offset..jump_to_offset],
16504 Some(*line_offset_from_top),
16505 ),
16506 );
16507 }
16508 }
16509 Some(JumpData::MultiBufferRow {
16510 row,
16511 line_offset_from_top,
16512 }) => {
16513 let point = MultiBufferPoint::new(row.0, 0);
16514 if let Some((buffer, buffer_point, _)) =
16515 self.buffer.read(cx).point_to_buffer_point(point, cx)
16516 {
16517 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16518 new_selections_by_buffer
16519 .entry(buffer)
16520 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16521 .0
16522 .push(buffer_offset..buffer_offset)
16523 }
16524 }
16525 None => {
16526 let selections = self.selections.all::<usize>(cx);
16527 let multi_buffer = self.buffer.read(cx);
16528 for selection in selections {
16529 for (snapshot, range, _, anchor) in multi_buffer
16530 .snapshot(cx)
16531 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16532 {
16533 if let Some(anchor) = anchor {
16534 // selection is in a deleted hunk
16535 let Some(buffer_id) = anchor.buffer_id else {
16536 continue;
16537 };
16538 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16539 continue;
16540 };
16541 let offset = text::ToOffset::to_offset(
16542 &anchor.text_anchor,
16543 &buffer_handle.read(cx).snapshot(),
16544 );
16545 let range = offset..offset;
16546 new_selections_by_buffer
16547 .entry(buffer_handle)
16548 .or_insert((Vec::new(), None))
16549 .0
16550 .push(range)
16551 } else {
16552 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16553 else {
16554 continue;
16555 };
16556 new_selections_by_buffer
16557 .entry(buffer_handle)
16558 .or_insert((Vec::new(), None))
16559 .0
16560 .push(range)
16561 }
16562 }
16563 }
16564 }
16565 }
16566
16567 if new_selections_by_buffer.is_empty() {
16568 return;
16569 }
16570
16571 // We defer the pane interaction because we ourselves are a workspace item
16572 // and activating a new item causes the pane to call a method on us reentrantly,
16573 // which panics if we're on the stack.
16574 window.defer(cx, move |window, cx| {
16575 workspace.update(cx, |workspace, cx| {
16576 let pane = if split {
16577 workspace.adjacent_pane(window, cx)
16578 } else {
16579 workspace.active_pane().clone()
16580 };
16581
16582 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16583 let editor = buffer
16584 .read(cx)
16585 .file()
16586 .is_none()
16587 .then(|| {
16588 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16589 // so `workspace.open_project_item` will never find them, always opening a new editor.
16590 // Instead, we try to activate the existing editor in the pane first.
16591 let (editor, pane_item_index) =
16592 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16593 let editor = item.downcast::<Editor>()?;
16594 let singleton_buffer =
16595 editor.read(cx).buffer().read(cx).as_singleton()?;
16596 if singleton_buffer == buffer {
16597 Some((editor, i))
16598 } else {
16599 None
16600 }
16601 })?;
16602 pane.update(cx, |pane, cx| {
16603 pane.activate_item(pane_item_index, true, true, window, cx)
16604 });
16605 Some(editor)
16606 })
16607 .flatten()
16608 .unwrap_or_else(|| {
16609 workspace.open_project_item::<Self>(
16610 pane.clone(),
16611 buffer,
16612 true,
16613 true,
16614 window,
16615 cx,
16616 )
16617 });
16618
16619 editor.update(cx, |editor, cx| {
16620 let autoscroll = match scroll_offset {
16621 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16622 None => Autoscroll::newest(),
16623 };
16624 let nav_history = editor.nav_history.take();
16625 editor.change_selections(Some(autoscroll), window, cx, |s| {
16626 s.select_ranges(ranges);
16627 });
16628 editor.nav_history = nav_history;
16629 });
16630 }
16631 })
16632 });
16633 }
16634
16635 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16636 let snapshot = self.buffer.read(cx).read(cx);
16637 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16638 Some(
16639 ranges
16640 .iter()
16641 .map(move |range| {
16642 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16643 })
16644 .collect(),
16645 )
16646 }
16647
16648 fn selection_replacement_ranges(
16649 &self,
16650 range: Range<OffsetUtf16>,
16651 cx: &mut App,
16652 ) -> Vec<Range<OffsetUtf16>> {
16653 let selections = self.selections.all::<OffsetUtf16>(cx);
16654 let newest_selection = selections
16655 .iter()
16656 .max_by_key(|selection| selection.id)
16657 .unwrap();
16658 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16659 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16660 let snapshot = self.buffer.read(cx).read(cx);
16661 selections
16662 .into_iter()
16663 .map(|mut selection| {
16664 selection.start.0 =
16665 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16666 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16667 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16668 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16669 })
16670 .collect()
16671 }
16672
16673 fn report_editor_event(
16674 &self,
16675 event_type: &'static str,
16676 file_extension: Option<String>,
16677 cx: &App,
16678 ) {
16679 if cfg!(any(test, feature = "test-support")) {
16680 return;
16681 }
16682
16683 let Some(project) = &self.project else { return };
16684
16685 // If None, we are in a file without an extension
16686 let file = self
16687 .buffer
16688 .read(cx)
16689 .as_singleton()
16690 .and_then(|b| b.read(cx).file());
16691 let file_extension = file_extension.or(file
16692 .as_ref()
16693 .and_then(|file| Path::new(file.file_name(cx)).extension())
16694 .and_then(|e| e.to_str())
16695 .map(|a| a.to_string()));
16696
16697 let vim_mode = cx
16698 .global::<SettingsStore>()
16699 .raw_user_settings()
16700 .get("vim_mode")
16701 == Some(&serde_json::Value::Bool(true));
16702
16703 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16704 let copilot_enabled = edit_predictions_provider
16705 == language::language_settings::EditPredictionProvider::Copilot;
16706 let copilot_enabled_for_language = self
16707 .buffer
16708 .read(cx)
16709 .language_settings(cx)
16710 .show_edit_predictions;
16711
16712 let project = project.read(cx);
16713 telemetry::event!(
16714 event_type,
16715 file_extension,
16716 vim_mode,
16717 copilot_enabled,
16718 copilot_enabled_for_language,
16719 edit_predictions_provider,
16720 is_via_ssh = project.is_via_ssh(),
16721 );
16722 }
16723
16724 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16725 /// with each line being an array of {text, highlight} objects.
16726 fn copy_highlight_json(
16727 &mut self,
16728 _: &CopyHighlightJson,
16729 window: &mut Window,
16730 cx: &mut Context<Self>,
16731 ) {
16732 #[derive(Serialize)]
16733 struct Chunk<'a> {
16734 text: String,
16735 highlight: Option<&'a str>,
16736 }
16737
16738 let snapshot = self.buffer.read(cx).snapshot(cx);
16739 let range = self
16740 .selected_text_range(false, window, cx)
16741 .and_then(|selection| {
16742 if selection.range.is_empty() {
16743 None
16744 } else {
16745 Some(selection.range)
16746 }
16747 })
16748 .unwrap_or_else(|| 0..snapshot.len());
16749
16750 let chunks = snapshot.chunks(range, true);
16751 let mut lines = Vec::new();
16752 let mut line: VecDeque<Chunk> = VecDeque::new();
16753
16754 let Some(style) = self.style.as_ref() else {
16755 return;
16756 };
16757
16758 for chunk in chunks {
16759 let highlight = chunk
16760 .syntax_highlight_id
16761 .and_then(|id| id.name(&style.syntax));
16762 let mut chunk_lines = chunk.text.split('\n').peekable();
16763 while let Some(text) = chunk_lines.next() {
16764 let mut merged_with_last_token = false;
16765 if let Some(last_token) = line.back_mut() {
16766 if last_token.highlight == highlight {
16767 last_token.text.push_str(text);
16768 merged_with_last_token = true;
16769 }
16770 }
16771
16772 if !merged_with_last_token {
16773 line.push_back(Chunk {
16774 text: text.into(),
16775 highlight,
16776 });
16777 }
16778
16779 if chunk_lines.peek().is_some() {
16780 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16781 line.pop_front();
16782 }
16783 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16784 line.pop_back();
16785 }
16786
16787 lines.push(mem::take(&mut line));
16788 }
16789 }
16790 }
16791
16792 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
16793 return;
16794 };
16795 cx.write_to_clipboard(ClipboardItem::new_string(lines));
16796 }
16797
16798 pub fn open_context_menu(
16799 &mut self,
16800 _: &OpenContextMenu,
16801 window: &mut Window,
16802 cx: &mut Context<Self>,
16803 ) {
16804 self.request_autoscroll(Autoscroll::newest(), cx);
16805 let position = self.selections.newest_display(cx).start;
16806 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
16807 }
16808
16809 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
16810 &self.inlay_hint_cache
16811 }
16812
16813 pub fn replay_insert_event(
16814 &mut self,
16815 text: &str,
16816 relative_utf16_range: Option<Range<isize>>,
16817 window: &mut Window,
16818 cx: &mut Context<Self>,
16819 ) {
16820 if !self.input_enabled {
16821 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16822 return;
16823 }
16824 if let Some(relative_utf16_range) = relative_utf16_range {
16825 let selections = self.selections.all::<OffsetUtf16>(cx);
16826 self.change_selections(None, window, cx, |s| {
16827 let new_ranges = selections.into_iter().map(|range| {
16828 let start = OffsetUtf16(
16829 range
16830 .head()
16831 .0
16832 .saturating_add_signed(relative_utf16_range.start),
16833 );
16834 let end = OffsetUtf16(
16835 range
16836 .head()
16837 .0
16838 .saturating_add_signed(relative_utf16_range.end),
16839 );
16840 start..end
16841 });
16842 s.select_ranges(new_ranges);
16843 });
16844 }
16845
16846 self.handle_input(text, window, cx);
16847 }
16848
16849 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16850 let Some(provider) = self.semantics_provider.as_ref() else {
16851 return false;
16852 };
16853
16854 let mut supports = false;
16855 self.buffer().update(cx, |this, cx| {
16856 this.for_each_buffer(|buffer| {
16857 supports |= provider.supports_inlay_hints(buffer, cx);
16858 });
16859 });
16860
16861 supports
16862 }
16863
16864 pub fn is_focused(&self, window: &Window) -> bool {
16865 self.focus_handle.is_focused(window)
16866 }
16867
16868 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16869 cx.emit(EditorEvent::Focused);
16870
16871 if let Some(descendant) = self
16872 .last_focused_descendant
16873 .take()
16874 .and_then(|descendant| descendant.upgrade())
16875 {
16876 window.focus(&descendant);
16877 } else {
16878 if let Some(blame) = self.blame.as_ref() {
16879 blame.update(cx, GitBlame::focus)
16880 }
16881
16882 self.blink_manager.update(cx, BlinkManager::enable);
16883 self.show_cursor_names(window, cx);
16884 self.buffer.update(cx, |buffer, cx| {
16885 buffer.finalize_last_transaction(cx);
16886 if self.leader_peer_id.is_none() {
16887 buffer.set_active_selections(
16888 &self.selections.disjoint_anchors(),
16889 self.selections.line_mode,
16890 self.cursor_shape,
16891 cx,
16892 );
16893 }
16894 });
16895 }
16896 }
16897
16898 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16899 cx.emit(EditorEvent::FocusedIn)
16900 }
16901
16902 fn handle_focus_out(
16903 &mut self,
16904 event: FocusOutEvent,
16905 _window: &mut Window,
16906 cx: &mut Context<Self>,
16907 ) {
16908 if event.blurred != self.focus_handle {
16909 self.last_focused_descendant = Some(event.blurred);
16910 }
16911 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16912 }
16913
16914 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16915 self.blink_manager.update(cx, BlinkManager::disable);
16916 self.buffer
16917 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16918
16919 if let Some(blame) = self.blame.as_ref() {
16920 blame.update(cx, GitBlame::blur)
16921 }
16922 if !self.hover_state.focused(window, cx) {
16923 hide_hover(self, cx);
16924 }
16925 if !self
16926 .context_menu
16927 .borrow()
16928 .as_ref()
16929 .is_some_and(|context_menu| context_menu.focused(window, cx))
16930 {
16931 self.hide_context_menu(window, cx);
16932 }
16933 self.discard_inline_completion(false, cx);
16934 cx.emit(EditorEvent::Blurred);
16935 cx.notify();
16936 }
16937
16938 pub fn register_action<A: Action>(
16939 &mut self,
16940 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16941 ) -> Subscription {
16942 let id = self.next_editor_action_id.post_inc();
16943 let listener = Arc::new(listener);
16944 self.editor_actions.borrow_mut().insert(
16945 id,
16946 Box::new(move |window, _| {
16947 let listener = listener.clone();
16948 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16949 let action = action.downcast_ref().unwrap();
16950 if phase == DispatchPhase::Bubble {
16951 listener(action, window, cx)
16952 }
16953 })
16954 }),
16955 );
16956
16957 let editor_actions = self.editor_actions.clone();
16958 Subscription::new(move || {
16959 editor_actions.borrow_mut().remove(&id);
16960 })
16961 }
16962
16963 pub fn file_header_size(&self) -> u32 {
16964 FILE_HEADER_HEIGHT
16965 }
16966
16967 pub fn restore(
16968 &mut self,
16969 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16970 window: &mut Window,
16971 cx: &mut Context<Self>,
16972 ) {
16973 let workspace = self.workspace();
16974 let project = self.project.as_ref();
16975 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16976 let mut tasks = Vec::new();
16977 for (buffer_id, changes) in revert_changes {
16978 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16979 buffer.update(cx, |buffer, cx| {
16980 buffer.edit(
16981 changes
16982 .into_iter()
16983 .map(|(range, text)| (range, text.to_string())),
16984 None,
16985 cx,
16986 );
16987 });
16988
16989 if let Some(project) =
16990 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16991 {
16992 project.update(cx, |project, cx| {
16993 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16994 })
16995 }
16996 }
16997 }
16998 tasks
16999 });
17000 cx.spawn_in(window, async move |_, cx| {
17001 for (buffer, task) in save_tasks {
17002 let result = task.await;
17003 if result.is_err() {
17004 let Some(path) = buffer
17005 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17006 .ok()
17007 else {
17008 continue;
17009 };
17010 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17011 let Some(task) = cx
17012 .update_window_entity(&workspace, |workspace, window, cx| {
17013 workspace
17014 .open_path_preview(path, None, false, false, false, window, cx)
17015 })
17016 .ok()
17017 else {
17018 continue;
17019 };
17020 task.await.log_err();
17021 }
17022 }
17023 }
17024 })
17025 .detach();
17026 self.change_selections(None, window, cx, |selections| selections.refresh());
17027 }
17028
17029 pub fn to_pixel_point(
17030 &self,
17031 source: multi_buffer::Anchor,
17032 editor_snapshot: &EditorSnapshot,
17033 window: &mut Window,
17034 ) -> Option<gpui::Point<Pixels>> {
17035 let source_point = source.to_display_point(editor_snapshot);
17036 self.display_to_pixel_point(source_point, editor_snapshot, window)
17037 }
17038
17039 pub fn display_to_pixel_point(
17040 &self,
17041 source: DisplayPoint,
17042 editor_snapshot: &EditorSnapshot,
17043 window: &mut Window,
17044 ) -> Option<gpui::Point<Pixels>> {
17045 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17046 let text_layout_details = self.text_layout_details(window);
17047 let scroll_top = text_layout_details
17048 .scroll_anchor
17049 .scroll_position(editor_snapshot)
17050 .y;
17051
17052 if source.row().as_f32() < scroll_top.floor() {
17053 return None;
17054 }
17055 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17056 let source_y = line_height * (source.row().as_f32() - scroll_top);
17057 Some(gpui::Point::new(source_x, source_y))
17058 }
17059
17060 pub fn has_visible_completions_menu(&self) -> bool {
17061 !self.edit_prediction_preview_is_active()
17062 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17063 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17064 })
17065 }
17066
17067 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17068 self.addons
17069 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17070 }
17071
17072 pub fn unregister_addon<T: Addon>(&mut self) {
17073 self.addons.remove(&std::any::TypeId::of::<T>());
17074 }
17075
17076 pub fn addon<T: Addon>(&self) -> Option<&T> {
17077 let type_id = std::any::TypeId::of::<T>();
17078 self.addons
17079 .get(&type_id)
17080 .and_then(|item| item.to_any().downcast_ref::<T>())
17081 }
17082
17083 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17084 let text_layout_details = self.text_layout_details(window);
17085 let style = &text_layout_details.editor_style;
17086 let font_id = window.text_system().resolve_font(&style.text.font());
17087 let font_size = style.text.font_size.to_pixels(window.rem_size());
17088 let line_height = style.text.line_height_in_pixels(window.rem_size());
17089 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17090
17091 gpui::Size::new(em_width, line_height)
17092 }
17093
17094 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17095 self.load_diff_task.clone()
17096 }
17097
17098 fn read_selections_from_db(
17099 &mut self,
17100 item_id: u64,
17101 workspace_id: WorkspaceId,
17102 window: &mut Window,
17103 cx: &mut Context<Editor>,
17104 ) {
17105 if !self.is_singleton(cx)
17106 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
17107 {
17108 return;
17109 }
17110 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
17111 return;
17112 };
17113 if selections.is_empty() {
17114 return;
17115 }
17116
17117 let snapshot = self.buffer.read(cx).snapshot(cx);
17118 self.change_selections(None, window, cx, |s| {
17119 s.select_ranges(selections.into_iter().map(|(start, end)| {
17120 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
17121 }));
17122 });
17123 }
17124}
17125
17126fn insert_extra_newline_brackets(
17127 buffer: &MultiBufferSnapshot,
17128 range: Range<usize>,
17129 language: &language::LanguageScope,
17130) -> bool {
17131 let leading_whitespace_len = buffer
17132 .reversed_chars_at(range.start)
17133 .take_while(|c| c.is_whitespace() && *c != '\n')
17134 .map(|c| c.len_utf8())
17135 .sum::<usize>();
17136 let trailing_whitespace_len = buffer
17137 .chars_at(range.end)
17138 .take_while(|c| c.is_whitespace() && *c != '\n')
17139 .map(|c| c.len_utf8())
17140 .sum::<usize>();
17141 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17142
17143 language.brackets().any(|(pair, enabled)| {
17144 let pair_start = pair.start.trim_end();
17145 let pair_end = pair.end.trim_start();
17146
17147 enabled
17148 && pair.newline
17149 && buffer.contains_str_at(range.end, pair_end)
17150 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17151 })
17152}
17153
17154fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17155 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17156 [(buffer, range, _)] => (*buffer, range.clone()),
17157 _ => return false,
17158 };
17159 let pair = {
17160 let mut result: Option<BracketMatch> = None;
17161
17162 for pair in buffer
17163 .all_bracket_ranges(range.clone())
17164 .filter(move |pair| {
17165 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17166 })
17167 {
17168 let len = pair.close_range.end - pair.open_range.start;
17169
17170 if let Some(existing) = &result {
17171 let existing_len = existing.close_range.end - existing.open_range.start;
17172 if len > existing_len {
17173 continue;
17174 }
17175 }
17176
17177 result = Some(pair);
17178 }
17179
17180 result
17181 };
17182 let Some(pair) = pair else {
17183 return false;
17184 };
17185 pair.newline_only
17186 && buffer
17187 .chars_for_range(pair.open_range.end..range.start)
17188 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17189 .all(|c| c.is_whitespace() && c != '\n')
17190}
17191
17192fn get_uncommitted_diff_for_buffer(
17193 project: &Entity<Project>,
17194 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17195 buffer: Entity<MultiBuffer>,
17196 cx: &mut App,
17197) -> Task<()> {
17198 let mut tasks = Vec::new();
17199 project.update(cx, |project, cx| {
17200 for buffer in buffers {
17201 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17202 }
17203 });
17204 cx.spawn(async move |cx| {
17205 let diffs = future::join_all(tasks).await;
17206 buffer
17207 .update(cx, |buffer, cx| {
17208 for diff in diffs.into_iter().flatten() {
17209 buffer.add_diff(diff, cx);
17210 }
17211 })
17212 .ok();
17213 })
17214}
17215
17216fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17217 let tab_size = tab_size.get() as usize;
17218 let mut width = offset;
17219
17220 for ch in text.chars() {
17221 width += if ch == '\t' {
17222 tab_size - (width % tab_size)
17223 } else {
17224 1
17225 };
17226 }
17227
17228 width - offset
17229}
17230
17231#[cfg(test)]
17232mod tests {
17233 use super::*;
17234
17235 #[test]
17236 fn test_string_size_with_expanded_tabs() {
17237 let nz = |val| NonZeroU32::new(val).unwrap();
17238 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17239 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17240 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17241 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17242 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17243 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17244 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17245 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17246 }
17247}
17248
17249/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17250struct WordBreakingTokenizer<'a> {
17251 input: &'a str,
17252}
17253
17254impl<'a> WordBreakingTokenizer<'a> {
17255 fn new(input: &'a str) -> Self {
17256 Self { input }
17257 }
17258}
17259
17260fn is_char_ideographic(ch: char) -> bool {
17261 use unicode_script::Script::*;
17262 use unicode_script::UnicodeScript;
17263 matches!(ch.script(), Han | Tangut | Yi)
17264}
17265
17266fn is_grapheme_ideographic(text: &str) -> bool {
17267 text.chars().any(is_char_ideographic)
17268}
17269
17270fn is_grapheme_whitespace(text: &str) -> bool {
17271 text.chars().any(|x| x.is_whitespace())
17272}
17273
17274fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17275 text.chars().next().map_or(false, |ch| {
17276 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17277 })
17278}
17279
17280#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17281enum WordBreakToken<'a> {
17282 Word { token: &'a str, grapheme_len: usize },
17283 InlineWhitespace { token: &'a str, grapheme_len: usize },
17284 Newline,
17285}
17286
17287impl<'a> Iterator for WordBreakingTokenizer<'a> {
17288 /// Yields a span, the count of graphemes in the token, and whether it was
17289 /// whitespace. Note that it also breaks at word boundaries.
17290 type Item = WordBreakToken<'a>;
17291
17292 fn next(&mut self) -> Option<Self::Item> {
17293 use unicode_segmentation::UnicodeSegmentation;
17294 if self.input.is_empty() {
17295 return None;
17296 }
17297
17298 let mut iter = self.input.graphemes(true).peekable();
17299 let mut offset = 0;
17300 let mut grapheme_len = 0;
17301 if let Some(first_grapheme) = iter.next() {
17302 let is_newline = first_grapheme == "\n";
17303 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17304 offset += first_grapheme.len();
17305 grapheme_len += 1;
17306 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17307 if let Some(grapheme) = iter.peek().copied() {
17308 if should_stay_with_preceding_ideograph(grapheme) {
17309 offset += grapheme.len();
17310 grapheme_len += 1;
17311 }
17312 }
17313 } else {
17314 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17315 let mut next_word_bound = words.peek().copied();
17316 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17317 next_word_bound = words.next();
17318 }
17319 while let Some(grapheme) = iter.peek().copied() {
17320 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17321 break;
17322 };
17323 if is_grapheme_whitespace(grapheme) != is_whitespace
17324 || (grapheme == "\n") != is_newline
17325 {
17326 break;
17327 };
17328 offset += grapheme.len();
17329 grapheme_len += 1;
17330 iter.next();
17331 }
17332 }
17333 let token = &self.input[..offset];
17334 self.input = &self.input[offset..];
17335 if token == "\n" {
17336 Some(WordBreakToken::Newline)
17337 } else if is_whitespace {
17338 Some(WordBreakToken::InlineWhitespace {
17339 token,
17340 grapheme_len,
17341 })
17342 } else {
17343 Some(WordBreakToken::Word {
17344 token,
17345 grapheme_len,
17346 })
17347 }
17348 } else {
17349 None
17350 }
17351 }
17352}
17353
17354#[test]
17355fn test_word_breaking_tokenizer() {
17356 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17357 ("", &[]),
17358 (" ", &[whitespace(" ", 2)]),
17359 ("Ʒ", &[word("Ʒ", 1)]),
17360 ("Ǽ", &[word("Ǽ", 1)]),
17361 ("⋑", &[word("⋑", 1)]),
17362 ("⋑⋑", &[word("⋑⋑", 2)]),
17363 (
17364 "原理,进而",
17365 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17366 ),
17367 (
17368 "hello world",
17369 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17370 ),
17371 (
17372 "hello, world",
17373 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17374 ),
17375 (
17376 " hello world",
17377 &[
17378 whitespace(" ", 2),
17379 word("hello", 5),
17380 whitespace(" ", 1),
17381 word("world", 5),
17382 ],
17383 ),
17384 (
17385 "这是什么 \n 钢笔",
17386 &[
17387 word("这", 1),
17388 word("是", 1),
17389 word("什", 1),
17390 word("么", 1),
17391 whitespace(" ", 1),
17392 newline(),
17393 whitespace(" ", 1),
17394 word("钢", 1),
17395 word("笔", 1),
17396 ],
17397 ),
17398 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17399 ];
17400
17401 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17402 WordBreakToken::Word {
17403 token,
17404 grapheme_len,
17405 }
17406 }
17407
17408 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17409 WordBreakToken::InlineWhitespace {
17410 token,
17411 grapheme_len,
17412 }
17413 }
17414
17415 fn newline() -> WordBreakToken<'static> {
17416 WordBreakToken::Newline
17417 }
17418
17419 for (input, result) in tests {
17420 assert_eq!(
17421 WordBreakingTokenizer::new(input)
17422 .collect::<Vec<_>>()
17423 .as_slice(),
17424 *result,
17425 );
17426 }
17427}
17428
17429fn wrap_with_prefix(
17430 line_prefix: String,
17431 unwrapped_text: String,
17432 wrap_column: usize,
17433 tab_size: NonZeroU32,
17434 preserve_existing_whitespace: bool,
17435) -> String {
17436 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17437 let mut wrapped_text = String::new();
17438 let mut current_line = line_prefix.clone();
17439
17440 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17441 let mut current_line_len = line_prefix_len;
17442 let mut in_whitespace = false;
17443 for token in tokenizer {
17444 let have_preceding_whitespace = in_whitespace;
17445 match token {
17446 WordBreakToken::Word {
17447 token,
17448 grapheme_len,
17449 } => {
17450 in_whitespace = false;
17451 if current_line_len + grapheme_len > wrap_column
17452 && current_line_len != line_prefix_len
17453 {
17454 wrapped_text.push_str(current_line.trim_end());
17455 wrapped_text.push('\n');
17456 current_line.truncate(line_prefix.len());
17457 current_line_len = line_prefix_len;
17458 }
17459 current_line.push_str(token);
17460 current_line_len += grapheme_len;
17461 }
17462 WordBreakToken::InlineWhitespace {
17463 mut token,
17464 mut grapheme_len,
17465 } => {
17466 in_whitespace = true;
17467 if have_preceding_whitespace && !preserve_existing_whitespace {
17468 continue;
17469 }
17470 if !preserve_existing_whitespace {
17471 token = " ";
17472 grapheme_len = 1;
17473 }
17474 if current_line_len + grapheme_len > wrap_column {
17475 wrapped_text.push_str(current_line.trim_end());
17476 wrapped_text.push('\n');
17477 current_line.truncate(line_prefix.len());
17478 current_line_len = line_prefix_len;
17479 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17480 current_line.push_str(token);
17481 current_line_len += grapheme_len;
17482 }
17483 }
17484 WordBreakToken::Newline => {
17485 in_whitespace = true;
17486 if preserve_existing_whitespace {
17487 wrapped_text.push_str(current_line.trim_end());
17488 wrapped_text.push('\n');
17489 current_line.truncate(line_prefix.len());
17490 current_line_len = line_prefix_len;
17491 } else if have_preceding_whitespace {
17492 continue;
17493 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17494 {
17495 wrapped_text.push_str(current_line.trim_end());
17496 wrapped_text.push('\n');
17497 current_line.truncate(line_prefix.len());
17498 current_line_len = line_prefix_len;
17499 } else if current_line_len != line_prefix_len {
17500 current_line.push(' ');
17501 current_line_len += 1;
17502 }
17503 }
17504 }
17505 }
17506
17507 if !current_line.is_empty() {
17508 wrapped_text.push_str(¤t_line);
17509 }
17510 wrapped_text
17511}
17512
17513#[test]
17514fn test_wrap_with_prefix() {
17515 assert_eq!(
17516 wrap_with_prefix(
17517 "# ".to_string(),
17518 "abcdefg".to_string(),
17519 4,
17520 NonZeroU32::new(4).unwrap(),
17521 false,
17522 ),
17523 "# abcdefg"
17524 );
17525 assert_eq!(
17526 wrap_with_prefix(
17527 "".to_string(),
17528 "\thello world".to_string(),
17529 8,
17530 NonZeroU32::new(4).unwrap(),
17531 false,
17532 ),
17533 "hello\nworld"
17534 );
17535 assert_eq!(
17536 wrap_with_prefix(
17537 "// ".to_string(),
17538 "xx \nyy zz aa bb cc".to_string(),
17539 12,
17540 NonZeroU32::new(4).unwrap(),
17541 false,
17542 ),
17543 "// xx yy zz\n// aa bb cc"
17544 );
17545 assert_eq!(
17546 wrap_with_prefix(
17547 String::new(),
17548 "这是什么 \n 钢笔".to_string(),
17549 3,
17550 NonZeroU32::new(4).unwrap(),
17551 false,
17552 ),
17553 "这是什\n么 钢\n笔"
17554 );
17555}
17556
17557pub trait CollaborationHub {
17558 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17559 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17560 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17561}
17562
17563impl CollaborationHub for Entity<Project> {
17564 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17565 self.read(cx).collaborators()
17566 }
17567
17568 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17569 self.read(cx).user_store().read(cx).participant_indices()
17570 }
17571
17572 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17573 let this = self.read(cx);
17574 let user_ids = this.collaborators().values().map(|c| c.user_id);
17575 this.user_store().read_with(cx, |user_store, cx| {
17576 user_store.participant_names(user_ids, cx)
17577 })
17578 }
17579}
17580
17581pub trait SemanticsProvider {
17582 fn hover(
17583 &self,
17584 buffer: &Entity<Buffer>,
17585 position: text::Anchor,
17586 cx: &mut App,
17587 ) -> Option<Task<Vec<project::Hover>>>;
17588
17589 fn inlay_hints(
17590 &self,
17591 buffer_handle: Entity<Buffer>,
17592 range: Range<text::Anchor>,
17593 cx: &mut App,
17594 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17595
17596 fn resolve_inlay_hint(
17597 &self,
17598 hint: InlayHint,
17599 buffer_handle: Entity<Buffer>,
17600 server_id: LanguageServerId,
17601 cx: &mut App,
17602 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17603
17604 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17605
17606 fn document_highlights(
17607 &self,
17608 buffer: &Entity<Buffer>,
17609 position: text::Anchor,
17610 cx: &mut App,
17611 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17612
17613 fn definitions(
17614 &self,
17615 buffer: &Entity<Buffer>,
17616 position: text::Anchor,
17617 kind: GotoDefinitionKind,
17618 cx: &mut App,
17619 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17620
17621 fn range_for_rename(
17622 &self,
17623 buffer: &Entity<Buffer>,
17624 position: text::Anchor,
17625 cx: &mut App,
17626 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17627
17628 fn perform_rename(
17629 &self,
17630 buffer: &Entity<Buffer>,
17631 position: text::Anchor,
17632 new_name: String,
17633 cx: &mut App,
17634 ) -> Option<Task<Result<ProjectTransaction>>>;
17635}
17636
17637pub trait CompletionProvider {
17638 fn completions(
17639 &self,
17640 buffer: &Entity<Buffer>,
17641 buffer_position: text::Anchor,
17642 trigger: CompletionContext,
17643 window: &mut Window,
17644 cx: &mut Context<Editor>,
17645 ) -> Task<Result<Option<Vec<Completion>>>>;
17646
17647 fn resolve_completions(
17648 &self,
17649 buffer: Entity<Buffer>,
17650 completion_indices: Vec<usize>,
17651 completions: Rc<RefCell<Box<[Completion]>>>,
17652 cx: &mut Context<Editor>,
17653 ) -> Task<Result<bool>>;
17654
17655 fn apply_additional_edits_for_completion(
17656 &self,
17657 _buffer: Entity<Buffer>,
17658 _completions: Rc<RefCell<Box<[Completion]>>>,
17659 _completion_index: usize,
17660 _push_to_history: bool,
17661 _cx: &mut Context<Editor>,
17662 ) -> Task<Result<Option<language::Transaction>>> {
17663 Task::ready(Ok(None))
17664 }
17665
17666 fn is_completion_trigger(
17667 &self,
17668 buffer: &Entity<Buffer>,
17669 position: language::Anchor,
17670 text: &str,
17671 trigger_in_words: bool,
17672 cx: &mut Context<Editor>,
17673 ) -> bool;
17674
17675 fn sort_completions(&self) -> bool {
17676 true
17677 }
17678}
17679
17680pub trait CodeActionProvider {
17681 fn id(&self) -> Arc<str>;
17682
17683 fn code_actions(
17684 &self,
17685 buffer: &Entity<Buffer>,
17686 range: Range<text::Anchor>,
17687 window: &mut Window,
17688 cx: &mut App,
17689 ) -> Task<Result<Vec<CodeAction>>>;
17690
17691 fn apply_code_action(
17692 &self,
17693 buffer_handle: Entity<Buffer>,
17694 action: CodeAction,
17695 excerpt_id: ExcerptId,
17696 push_to_history: bool,
17697 window: &mut Window,
17698 cx: &mut App,
17699 ) -> Task<Result<ProjectTransaction>>;
17700}
17701
17702impl CodeActionProvider for Entity<Project> {
17703 fn id(&self) -> Arc<str> {
17704 "project".into()
17705 }
17706
17707 fn code_actions(
17708 &self,
17709 buffer: &Entity<Buffer>,
17710 range: Range<text::Anchor>,
17711 _window: &mut Window,
17712 cx: &mut App,
17713 ) -> Task<Result<Vec<CodeAction>>> {
17714 self.update(cx, |project, cx| {
17715 let code_lens = project.code_lens(buffer, range.clone(), cx);
17716 let code_actions = project.code_actions(buffer, range, None, cx);
17717 cx.background_spawn(async move {
17718 let (code_lens, code_actions) = join(code_lens, code_actions).await;
17719 Ok(code_lens
17720 .context("code lens fetch")?
17721 .into_iter()
17722 .chain(code_actions.context("code action fetch")?)
17723 .collect())
17724 })
17725 })
17726 }
17727
17728 fn apply_code_action(
17729 &self,
17730 buffer_handle: Entity<Buffer>,
17731 action: CodeAction,
17732 _excerpt_id: ExcerptId,
17733 push_to_history: bool,
17734 _window: &mut Window,
17735 cx: &mut App,
17736 ) -> Task<Result<ProjectTransaction>> {
17737 self.update(cx, |project, cx| {
17738 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17739 })
17740 }
17741}
17742
17743fn snippet_completions(
17744 project: &Project,
17745 buffer: &Entity<Buffer>,
17746 buffer_position: text::Anchor,
17747 cx: &mut App,
17748) -> Task<Result<Vec<Completion>>> {
17749 let language = buffer.read(cx).language_at(buffer_position);
17750 let language_name = language.as_ref().map(|language| language.lsp_id());
17751 let snippet_store = project.snippets().read(cx);
17752 let snippets = snippet_store.snippets_for(language_name, cx);
17753
17754 if snippets.is_empty() {
17755 return Task::ready(Ok(vec![]));
17756 }
17757 let snapshot = buffer.read(cx).text_snapshot();
17758 let chars: String = snapshot
17759 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17760 .collect();
17761
17762 let scope = language.map(|language| language.default_scope());
17763 let executor = cx.background_executor().clone();
17764
17765 cx.background_spawn(async move {
17766 let classifier = CharClassifier::new(scope).for_completion(true);
17767 let mut last_word = chars
17768 .chars()
17769 .take_while(|c| classifier.is_word(*c))
17770 .collect::<String>();
17771 last_word = last_word.chars().rev().collect();
17772
17773 if last_word.is_empty() {
17774 return Ok(vec![]);
17775 }
17776
17777 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
17778 let to_lsp = |point: &text::Anchor| {
17779 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
17780 point_to_lsp(end)
17781 };
17782 let lsp_end = to_lsp(&buffer_position);
17783
17784 let candidates = snippets
17785 .iter()
17786 .enumerate()
17787 .flat_map(|(ix, snippet)| {
17788 snippet
17789 .prefix
17790 .iter()
17791 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
17792 })
17793 .collect::<Vec<StringMatchCandidate>>();
17794
17795 let mut matches = fuzzy::match_strings(
17796 &candidates,
17797 &last_word,
17798 last_word.chars().any(|c| c.is_uppercase()),
17799 100,
17800 &Default::default(),
17801 executor,
17802 )
17803 .await;
17804
17805 // Remove all candidates where the query's start does not match the start of any word in the candidate
17806 if let Some(query_start) = last_word.chars().next() {
17807 matches.retain(|string_match| {
17808 split_words(&string_match.string).any(|word| {
17809 // Check that the first codepoint of the word as lowercase matches the first
17810 // codepoint of the query as lowercase
17811 word.chars()
17812 .flat_map(|codepoint| codepoint.to_lowercase())
17813 .zip(query_start.to_lowercase())
17814 .all(|(word_cp, query_cp)| word_cp == query_cp)
17815 })
17816 });
17817 }
17818
17819 let matched_strings = matches
17820 .into_iter()
17821 .map(|m| m.string)
17822 .collect::<HashSet<_>>();
17823
17824 let result: Vec<Completion> = snippets
17825 .into_iter()
17826 .filter_map(|snippet| {
17827 let matching_prefix = snippet
17828 .prefix
17829 .iter()
17830 .find(|prefix| matched_strings.contains(*prefix))?;
17831 let start = as_offset - last_word.len();
17832 let start = snapshot.anchor_before(start);
17833 let range = start..buffer_position;
17834 let lsp_start = to_lsp(&start);
17835 let lsp_range = lsp::Range {
17836 start: lsp_start,
17837 end: lsp_end,
17838 };
17839 Some(Completion {
17840 old_range: range,
17841 new_text: snippet.body.clone(),
17842 source: CompletionSource::Lsp {
17843 server_id: LanguageServerId(usize::MAX),
17844 resolved: true,
17845 lsp_completion: Box::new(lsp::CompletionItem {
17846 label: snippet.prefix.first().unwrap().clone(),
17847 kind: Some(CompletionItemKind::SNIPPET),
17848 label_details: snippet.description.as_ref().map(|description| {
17849 lsp::CompletionItemLabelDetails {
17850 detail: Some(description.clone()),
17851 description: None,
17852 }
17853 }),
17854 insert_text_format: Some(InsertTextFormat::SNIPPET),
17855 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
17856 lsp::InsertReplaceEdit {
17857 new_text: snippet.body.clone(),
17858 insert: lsp_range,
17859 replace: lsp_range,
17860 },
17861 )),
17862 filter_text: Some(snippet.body.clone()),
17863 sort_text: Some(char::MAX.to_string()),
17864 ..lsp::CompletionItem::default()
17865 }),
17866 lsp_defaults: None,
17867 },
17868 label: CodeLabel {
17869 text: matching_prefix.clone(),
17870 runs: Vec::new(),
17871 filter_range: 0..matching_prefix.len(),
17872 },
17873 documentation: snippet
17874 .description
17875 .clone()
17876 .map(|description| CompletionDocumentation::SingleLine(description.into())),
17877 confirm: None,
17878 })
17879 })
17880 .collect();
17881
17882 Ok(result)
17883 })
17884}
17885
17886impl CompletionProvider for Entity<Project> {
17887 fn completions(
17888 &self,
17889 buffer: &Entity<Buffer>,
17890 buffer_position: text::Anchor,
17891 options: CompletionContext,
17892 _window: &mut Window,
17893 cx: &mut Context<Editor>,
17894 ) -> Task<Result<Option<Vec<Completion>>>> {
17895 self.update(cx, |project, cx| {
17896 let snippets = snippet_completions(project, buffer, buffer_position, cx);
17897 let project_completions = project.completions(buffer, buffer_position, options, cx);
17898 cx.background_spawn(async move {
17899 let snippets_completions = snippets.await?;
17900 match project_completions.await? {
17901 Some(mut completions) => {
17902 completions.extend(snippets_completions);
17903 Ok(Some(completions))
17904 }
17905 None => {
17906 if snippets_completions.is_empty() {
17907 Ok(None)
17908 } else {
17909 Ok(Some(snippets_completions))
17910 }
17911 }
17912 }
17913 })
17914 })
17915 }
17916
17917 fn resolve_completions(
17918 &self,
17919 buffer: Entity<Buffer>,
17920 completion_indices: Vec<usize>,
17921 completions: Rc<RefCell<Box<[Completion]>>>,
17922 cx: &mut Context<Editor>,
17923 ) -> Task<Result<bool>> {
17924 self.update(cx, |project, cx| {
17925 project.lsp_store().update(cx, |lsp_store, cx| {
17926 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
17927 })
17928 })
17929 }
17930
17931 fn apply_additional_edits_for_completion(
17932 &self,
17933 buffer: Entity<Buffer>,
17934 completions: Rc<RefCell<Box<[Completion]>>>,
17935 completion_index: usize,
17936 push_to_history: bool,
17937 cx: &mut Context<Editor>,
17938 ) -> Task<Result<Option<language::Transaction>>> {
17939 self.update(cx, |project, cx| {
17940 project.lsp_store().update(cx, |lsp_store, cx| {
17941 lsp_store.apply_additional_edits_for_completion(
17942 buffer,
17943 completions,
17944 completion_index,
17945 push_to_history,
17946 cx,
17947 )
17948 })
17949 })
17950 }
17951
17952 fn is_completion_trigger(
17953 &self,
17954 buffer: &Entity<Buffer>,
17955 position: language::Anchor,
17956 text: &str,
17957 trigger_in_words: bool,
17958 cx: &mut Context<Editor>,
17959 ) -> bool {
17960 let mut chars = text.chars();
17961 let char = if let Some(char) = chars.next() {
17962 char
17963 } else {
17964 return false;
17965 };
17966 if chars.next().is_some() {
17967 return false;
17968 }
17969
17970 let buffer = buffer.read(cx);
17971 let snapshot = buffer.snapshot();
17972 if !snapshot.settings_at(position, cx).show_completions_on_input {
17973 return false;
17974 }
17975 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17976 if trigger_in_words && classifier.is_word(char) {
17977 return true;
17978 }
17979
17980 buffer.completion_triggers().contains(text)
17981 }
17982}
17983
17984impl SemanticsProvider for Entity<Project> {
17985 fn hover(
17986 &self,
17987 buffer: &Entity<Buffer>,
17988 position: text::Anchor,
17989 cx: &mut App,
17990 ) -> Option<Task<Vec<project::Hover>>> {
17991 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17992 }
17993
17994 fn document_highlights(
17995 &self,
17996 buffer: &Entity<Buffer>,
17997 position: text::Anchor,
17998 cx: &mut App,
17999 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18000 Some(self.update(cx, |project, cx| {
18001 project.document_highlights(buffer, position, cx)
18002 }))
18003 }
18004
18005 fn definitions(
18006 &self,
18007 buffer: &Entity<Buffer>,
18008 position: text::Anchor,
18009 kind: GotoDefinitionKind,
18010 cx: &mut App,
18011 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18012 Some(self.update(cx, |project, cx| match kind {
18013 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18014 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18015 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18016 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18017 }))
18018 }
18019
18020 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18021 // TODO: make this work for remote projects
18022 self.update(cx, |this, cx| {
18023 buffer.update(cx, |buffer, cx| {
18024 this.any_language_server_supports_inlay_hints(buffer, cx)
18025 })
18026 })
18027 }
18028
18029 fn inlay_hints(
18030 &self,
18031 buffer_handle: Entity<Buffer>,
18032 range: Range<text::Anchor>,
18033 cx: &mut App,
18034 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18035 Some(self.update(cx, |project, cx| {
18036 project.inlay_hints(buffer_handle, range, cx)
18037 }))
18038 }
18039
18040 fn resolve_inlay_hint(
18041 &self,
18042 hint: InlayHint,
18043 buffer_handle: Entity<Buffer>,
18044 server_id: LanguageServerId,
18045 cx: &mut App,
18046 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18047 Some(self.update(cx, |project, cx| {
18048 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18049 }))
18050 }
18051
18052 fn range_for_rename(
18053 &self,
18054 buffer: &Entity<Buffer>,
18055 position: text::Anchor,
18056 cx: &mut App,
18057 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18058 Some(self.update(cx, |project, cx| {
18059 let buffer = buffer.clone();
18060 let task = project.prepare_rename(buffer.clone(), position, cx);
18061 cx.spawn(async move |_, cx| {
18062 Ok(match task.await? {
18063 PrepareRenameResponse::Success(range) => Some(range),
18064 PrepareRenameResponse::InvalidPosition => None,
18065 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18066 // Fallback on using TreeSitter info to determine identifier range
18067 buffer.update(cx, |buffer, _| {
18068 let snapshot = buffer.snapshot();
18069 let (range, kind) = snapshot.surrounding_word(position);
18070 if kind != Some(CharKind::Word) {
18071 return None;
18072 }
18073 Some(
18074 snapshot.anchor_before(range.start)
18075 ..snapshot.anchor_after(range.end),
18076 )
18077 })?
18078 }
18079 })
18080 })
18081 }))
18082 }
18083
18084 fn perform_rename(
18085 &self,
18086 buffer: &Entity<Buffer>,
18087 position: text::Anchor,
18088 new_name: String,
18089 cx: &mut App,
18090 ) -> Option<Task<Result<ProjectTransaction>>> {
18091 Some(self.update(cx, |project, cx| {
18092 project.perform_rename(buffer.clone(), position, new_name, cx)
18093 }))
18094 }
18095}
18096
18097fn inlay_hint_settings(
18098 location: Anchor,
18099 snapshot: &MultiBufferSnapshot,
18100 cx: &mut Context<Editor>,
18101) -> InlayHintSettings {
18102 let file = snapshot.file_at(location);
18103 let language = snapshot.language_at(location).map(|l| l.name());
18104 language_settings(language, file, cx).inlay_hints
18105}
18106
18107fn consume_contiguous_rows(
18108 contiguous_row_selections: &mut Vec<Selection<Point>>,
18109 selection: &Selection<Point>,
18110 display_map: &DisplaySnapshot,
18111 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18112) -> (MultiBufferRow, MultiBufferRow) {
18113 contiguous_row_selections.push(selection.clone());
18114 let start_row = MultiBufferRow(selection.start.row);
18115 let mut end_row = ending_row(selection, display_map);
18116
18117 while let Some(next_selection) = selections.peek() {
18118 if next_selection.start.row <= end_row.0 {
18119 end_row = ending_row(next_selection, display_map);
18120 contiguous_row_selections.push(selections.next().unwrap().clone());
18121 } else {
18122 break;
18123 }
18124 }
18125 (start_row, end_row)
18126}
18127
18128fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18129 if next_selection.end.column > 0 || next_selection.is_empty() {
18130 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18131 } else {
18132 MultiBufferRow(next_selection.end.row)
18133 }
18134}
18135
18136impl EditorSnapshot {
18137 pub fn remote_selections_in_range<'a>(
18138 &'a self,
18139 range: &'a Range<Anchor>,
18140 collaboration_hub: &dyn CollaborationHub,
18141 cx: &'a App,
18142 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18143 let participant_names = collaboration_hub.user_names(cx);
18144 let participant_indices = collaboration_hub.user_participant_indices(cx);
18145 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18146 let collaborators_by_replica_id = collaborators_by_peer_id
18147 .iter()
18148 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18149 .collect::<HashMap<_, _>>();
18150 self.buffer_snapshot
18151 .selections_in_range(range, false)
18152 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18153 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18154 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18155 let user_name = participant_names.get(&collaborator.user_id).cloned();
18156 Some(RemoteSelection {
18157 replica_id,
18158 selection,
18159 cursor_shape,
18160 line_mode,
18161 participant_index,
18162 peer_id: collaborator.peer_id,
18163 user_name,
18164 })
18165 })
18166 }
18167
18168 pub fn hunks_for_ranges(
18169 &self,
18170 ranges: impl IntoIterator<Item = Range<Point>>,
18171 ) -> Vec<MultiBufferDiffHunk> {
18172 let mut hunks = Vec::new();
18173 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18174 HashMap::default();
18175 for query_range in ranges {
18176 let query_rows =
18177 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18178 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18179 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18180 ) {
18181 // Include deleted hunks that are adjacent to the query range, because
18182 // otherwise they would be missed.
18183 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18184 if hunk.status().is_deleted() {
18185 intersects_range |= hunk.row_range.start == query_rows.end;
18186 intersects_range |= hunk.row_range.end == query_rows.start;
18187 }
18188 if intersects_range {
18189 if !processed_buffer_rows
18190 .entry(hunk.buffer_id)
18191 .or_default()
18192 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18193 {
18194 continue;
18195 }
18196 hunks.push(hunk);
18197 }
18198 }
18199 }
18200
18201 hunks
18202 }
18203
18204 fn display_diff_hunks_for_rows<'a>(
18205 &'a self,
18206 display_rows: Range<DisplayRow>,
18207 folded_buffers: &'a HashSet<BufferId>,
18208 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18209 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18210 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18211
18212 self.buffer_snapshot
18213 .diff_hunks_in_range(buffer_start..buffer_end)
18214 .filter_map(|hunk| {
18215 if folded_buffers.contains(&hunk.buffer_id) {
18216 return None;
18217 }
18218
18219 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18220 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18221
18222 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18223 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18224
18225 let display_hunk = if hunk_display_start.column() != 0 {
18226 DisplayDiffHunk::Folded {
18227 display_row: hunk_display_start.row(),
18228 }
18229 } else {
18230 let mut end_row = hunk_display_end.row();
18231 if hunk_display_end.column() > 0 {
18232 end_row.0 += 1;
18233 }
18234 let is_created_file = hunk.is_created_file();
18235 DisplayDiffHunk::Unfolded {
18236 status: hunk.status(),
18237 diff_base_byte_range: hunk.diff_base_byte_range,
18238 display_row_range: hunk_display_start.row()..end_row,
18239 multi_buffer_range: Anchor::range_in_buffer(
18240 hunk.excerpt_id,
18241 hunk.buffer_id,
18242 hunk.buffer_range,
18243 ),
18244 is_created_file,
18245 }
18246 };
18247
18248 Some(display_hunk)
18249 })
18250 }
18251
18252 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18253 self.display_snapshot.buffer_snapshot.language_at(position)
18254 }
18255
18256 pub fn is_focused(&self) -> bool {
18257 self.is_focused
18258 }
18259
18260 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18261 self.placeholder_text.as_ref()
18262 }
18263
18264 pub fn scroll_position(&self) -> gpui::Point<f32> {
18265 self.scroll_anchor.scroll_position(&self.display_snapshot)
18266 }
18267
18268 fn gutter_dimensions(
18269 &self,
18270 font_id: FontId,
18271 font_size: Pixels,
18272 max_line_number_width: Pixels,
18273 cx: &App,
18274 ) -> Option<GutterDimensions> {
18275 if !self.show_gutter {
18276 return None;
18277 }
18278
18279 let descent = cx.text_system().descent(font_id, font_size);
18280 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18281 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18282
18283 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18284 matches!(
18285 ProjectSettings::get_global(cx).git.git_gutter,
18286 Some(GitGutterSetting::TrackedFiles)
18287 )
18288 });
18289 let gutter_settings = EditorSettings::get_global(cx).gutter;
18290 let show_line_numbers = self
18291 .show_line_numbers
18292 .unwrap_or(gutter_settings.line_numbers);
18293 let line_gutter_width = if show_line_numbers {
18294 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18295 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18296 max_line_number_width.max(min_width_for_number_on_gutter)
18297 } else {
18298 0.0.into()
18299 };
18300
18301 let show_code_actions = self
18302 .show_code_actions
18303 .unwrap_or(gutter_settings.code_actions);
18304
18305 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18306 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18307
18308 let git_blame_entries_width =
18309 self.git_blame_gutter_max_author_length
18310 .map(|max_author_length| {
18311 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18312
18313 /// The number of characters to dedicate to gaps and margins.
18314 const SPACING_WIDTH: usize = 4;
18315
18316 let max_char_count = max_author_length
18317 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18318 + ::git::SHORT_SHA_LENGTH
18319 + MAX_RELATIVE_TIMESTAMP.len()
18320 + SPACING_WIDTH;
18321
18322 em_advance * max_char_count
18323 });
18324
18325 let is_singleton = self.buffer_snapshot.is_singleton();
18326
18327 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18328 left_padding += if !is_singleton {
18329 em_width * 4.0
18330 } else if show_code_actions || show_runnables || show_breakpoints {
18331 em_width * 3.0
18332 } else if show_git_gutter && show_line_numbers {
18333 em_width * 2.0
18334 } else if show_git_gutter || show_line_numbers {
18335 em_width
18336 } else {
18337 px(0.)
18338 };
18339
18340 let shows_folds = is_singleton && gutter_settings.folds;
18341
18342 let right_padding = if shows_folds && show_line_numbers {
18343 em_width * 4.0
18344 } else if shows_folds || (!is_singleton && show_line_numbers) {
18345 em_width * 3.0
18346 } else if show_line_numbers {
18347 em_width
18348 } else {
18349 px(0.)
18350 };
18351
18352 Some(GutterDimensions {
18353 left_padding,
18354 right_padding,
18355 width: line_gutter_width + left_padding + right_padding,
18356 margin: -descent,
18357 git_blame_entries_width,
18358 })
18359 }
18360
18361 pub fn render_crease_toggle(
18362 &self,
18363 buffer_row: MultiBufferRow,
18364 row_contains_cursor: bool,
18365 editor: Entity<Editor>,
18366 window: &mut Window,
18367 cx: &mut App,
18368 ) -> Option<AnyElement> {
18369 let folded = self.is_line_folded(buffer_row);
18370 let mut is_foldable = false;
18371
18372 if let Some(crease) = self
18373 .crease_snapshot
18374 .query_row(buffer_row, &self.buffer_snapshot)
18375 {
18376 is_foldable = true;
18377 match crease {
18378 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18379 if let Some(render_toggle) = render_toggle {
18380 let toggle_callback =
18381 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18382 if folded {
18383 editor.update(cx, |editor, cx| {
18384 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18385 });
18386 } else {
18387 editor.update(cx, |editor, cx| {
18388 editor.unfold_at(
18389 &crate::UnfoldAt { buffer_row },
18390 window,
18391 cx,
18392 )
18393 });
18394 }
18395 });
18396 return Some((render_toggle)(
18397 buffer_row,
18398 folded,
18399 toggle_callback,
18400 window,
18401 cx,
18402 ));
18403 }
18404 }
18405 }
18406 }
18407
18408 is_foldable |= self.starts_indent(buffer_row);
18409
18410 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18411 Some(
18412 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18413 .toggle_state(folded)
18414 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18415 if folded {
18416 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18417 } else {
18418 this.fold_at(&FoldAt { buffer_row }, window, cx);
18419 }
18420 }))
18421 .into_any_element(),
18422 )
18423 } else {
18424 None
18425 }
18426 }
18427
18428 pub fn render_crease_trailer(
18429 &self,
18430 buffer_row: MultiBufferRow,
18431 window: &mut Window,
18432 cx: &mut App,
18433 ) -> Option<AnyElement> {
18434 let folded = self.is_line_folded(buffer_row);
18435 if let Crease::Inline { render_trailer, .. } = self
18436 .crease_snapshot
18437 .query_row(buffer_row, &self.buffer_snapshot)?
18438 {
18439 let render_trailer = render_trailer.as_ref()?;
18440 Some(render_trailer(buffer_row, folded, window, cx))
18441 } else {
18442 None
18443 }
18444 }
18445}
18446
18447impl Deref for EditorSnapshot {
18448 type Target = DisplaySnapshot;
18449
18450 fn deref(&self) -> &Self::Target {
18451 &self.display_snapshot
18452 }
18453}
18454
18455#[derive(Clone, Debug, PartialEq, Eq)]
18456pub enum EditorEvent {
18457 InputIgnored {
18458 text: Arc<str>,
18459 },
18460 InputHandled {
18461 utf16_range_to_replace: Option<Range<isize>>,
18462 text: Arc<str>,
18463 },
18464 ExcerptsAdded {
18465 buffer: Entity<Buffer>,
18466 predecessor: ExcerptId,
18467 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18468 },
18469 ExcerptsRemoved {
18470 ids: Vec<ExcerptId>,
18471 },
18472 BufferFoldToggled {
18473 ids: Vec<ExcerptId>,
18474 folded: bool,
18475 },
18476 ExcerptsEdited {
18477 ids: Vec<ExcerptId>,
18478 },
18479 ExcerptsExpanded {
18480 ids: Vec<ExcerptId>,
18481 },
18482 BufferEdited,
18483 Edited {
18484 transaction_id: clock::Lamport,
18485 },
18486 Reparsed(BufferId),
18487 Focused,
18488 FocusedIn,
18489 Blurred,
18490 DirtyChanged,
18491 Saved,
18492 TitleChanged,
18493 DiffBaseChanged,
18494 SelectionsChanged {
18495 local: bool,
18496 },
18497 ScrollPositionChanged {
18498 local: bool,
18499 autoscroll: bool,
18500 },
18501 Closed,
18502 TransactionUndone {
18503 transaction_id: clock::Lamport,
18504 },
18505 TransactionBegun {
18506 transaction_id: clock::Lamport,
18507 },
18508 Reloaded,
18509 CursorShapeChanged,
18510}
18511
18512impl EventEmitter<EditorEvent> for Editor {}
18513
18514impl Focusable for Editor {
18515 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18516 self.focus_handle.clone()
18517 }
18518}
18519
18520impl Render for Editor {
18521 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18522 let settings = ThemeSettings::get_global(cx);
18523
18524 let mut text_style = match self.mode {
18525 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18526 color: cx.theme().colors().editor_foreground,
18527 font_family: settings.ui_font.family.clone(),
18528 font_features: settings.ui_font.features.clone(),
18529 font_fallbacks: settings.ui_font.fallbacks.clone(),
18530 font_size: rems(0.875).into(),
18531 font_weight: settings.ui_font.weight,
18532 line_height: relative(settings.buffer_line_height.value()),
18533 ..Default::default()
18534 },
18535 EditorMode::Full => TextStyle {
18536 color: cx.theme().colors().editor_foreground,
18537 font_family: settings.buffer_font.family.clone(),
18538 font_features: settings.buffer_font.features.clone(),
18539 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18540 font_size: settings.buffer_font_size(cx).into(),
18541 font_weight: settings.buffer_font.weight,
18542 line_height: relative(settings.buffer_line_height.value()),
18543 ..Default::default()
18544 },
18545 };
18546 if let Some(text_style_refinement) = &self.text_style_refinement {
18547 text_style.refine(text_style_refinement)
18548 }
18549
18550 let background = match self.mode {
18551 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18552 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18553 EditorMode::Full => cx.theme().colors().editor_background,
18554 };
18555
18556 EditorElement::new(
18557 &cx.entity(),
18558 EditorStyle {
18559 background,
18560 local_player: cx.theme().players().local(),
18561 text: text_style,
18562 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18563 syntax: cx.theme().syntax().clone(),
18564 status: cx.theme().status().clone(),
18565 inlay_hints_style: make_inlay_hints_style(cx),
18566 inline_completion_styles: make_suggestion_styles(cx),
18567 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18568 },
18569 )
18570 }
18571}
18572
18573impl EntityInputHandler for Editor {
18574 fn text_for_range(
18575 &mut self,
18576 range_utf16: Range<usize>,
18577 adjusted_range: &mut Option<Range<usize>>,
18578 _: &mut Window,
18579 cx: &mut Context<Self>,
18580 ) -> Option<String> {
18581 let snapshot = self.buffer.read(cx).read(cx);
18582 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18583 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18584 if (start.0..end.0) != range_utf16 {
18585 adjusted_range.replace(start.0..end.0);
18586 }
18587 Some(snapshot.text_for_range(start..end).collect())
18588 }
18589
18590 fn selected_text_range(
18591 &mut self,
18592 ignore_disabled_input: bool,
18593 _: &mut Window,
18594 cx: &mut Context<Self>,
18595 ) -> Option<UTF16Selection> {
18596 // Prevent the IME menu from appearing when holding down an alphabetic key
18597 // while input is disabled.
18598 if !ignore_disabled_input && !self.input_enabled {
18599 return None;
18600 }
18601
18602 let selection = self.selections.newest::<OffsetUtf16>(cx);
18603 let range = selection.range();
18604
18605 Some(UTF16Selection {
18606 range: range.start.0..range.end.0,
18607 reversed: selection.reversed,
18608 })
18609 }
18610
18611 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18612 let snapshot = self.buffer.read(cx).read(cx);
18613 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18614 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18615 }
18616
18617 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18618 self.clear_highlights::<InputComposition>(cx);
18619 self.ime_transaction.take();
18620 }
18621
18622 fn replace_text_in_range(
18623 &mut self,
18624 range_utf16: Option<Range<usize>>,
18625 text: &str,
18626 window: &mut Window,
18627 cx: &mut Context<Self>,
18628 ) {
18629 if !self.input_enabled {
18630 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18631 return;
18632 }
18633
18634 self.transact(window, cx, |this, window, cx| {
18635 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18636 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18637 Some(this.selection_replacement_ranges(range_utf16, cx))
18638 } else {
18639 this.marked_text_ranges(cx)
18640 };
18641
18642 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
18643 let newest_selection_id = this.selections.newest_anchor().id;
18644 this.selections
18645 .all::<OffsetUtf16>(cx)
18646 .iter()
18647 .zip(ranges_to_replace.iter())
18648 .find_map(|(selection, range)| {
18649 if selection.id == newest_selection_id {
18650 Some(
18651 (range.start.0 as isize - selection.head().0 as isize)
18652 ..(range.end.0 as isize - selection.head().0 as isize),
18653 )
18654 } else {
18655 None
18656 }
18657 })
18658 });
18659
18660 cx.emit(EditorEvent::InputHandled {
18661 utf16_range_to_replace: range_to_replace,
18662 text: text.into(),
18663 });
18664
18665 if let Some(new_selected_ranges) = new_selected_ranges {
18666 this.change_selections(None, window, cx, |selections| {
18667 selections.select_ranges(new_selected_ranges)
18668 });
18669 this.backspace(&Default::default(), window, cx);
18670 }
18671
18672 this.handle_input(text, window, cx);
18673 });
18674
18675 if let Some(transaction) = self.ime_transaction {
18676 self.buffer.update(cx, |buffer, cx| {
18677 buffer.group_until_transaction(transaction, cx);
18678 });
18679 }
18680
18681 self.unmark_text(window, cx);
18682 }
18683
18684 fn replace_and_mark_text_in_range(
18685 &mut self,
18686 range_utf16: Option<Range<usize>>,
18687 text: &str,
18688 new_selected_range_utf16: Option<Range<usize>>,
18689 window: &mut Window,
18690 cx: &mut Context<Self>,
18691 ) {
18692 if !self.input_enabled {
18693 return;
18694 }
18695
18696 let transaction = self.transact(window, cx, |this, window, cx| {
18697 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
18698 let snapshot = this.buffer.read(cx).read(cx);
18699 if let Some(relative_range_utf16) = range_utf16.as_ref() {
18700 for marked_range in &mut marked_ranges {
18701 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
18702 marked_range.start.0 += relative_range_utf16.start;
18703 marked_range.start =
18704 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
18705 marked_range.end =
18706 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
18707 }
18708 }
18709 Some(marked_ranges)
18710 } else if let Some(range_utf16) = range_utf16 {
18711 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18712 Some(this.selection_replacement_ranges(range_utf16, cx))
18713 } else {
18714 None
18715 };
18716
18717 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
18718 let newest_selection_id = this.selections.newest_anchor().id;
18719 this.selections
18720 .all::<OffsetUtf16>(cx)
18721 .iter()
18722 .zip(ranges_to_replace.iter())
18723 .find_map(|(selection, range)| {
18724 if selection.id == newest_selection_id {
18725 Some(
18726 (range.start.0 as isize - selection.head().0 as isize)
18727 ..(range.end.0 as isize - selection.head().0 as isize),
18728 )
18729 } else {
18730 None
18731 }
18732 })
18733 });
18734
18735 cx.emit(EditorEvent::InputHandled {
18736 utf16_range_to_replace: range_to_replace,
18737 text: text.into(),
18738 });
18739
18740 if let Some(ranges) = ranges_to_replace {
18741 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18742 }
18743
18744 let marked_ranges = {
18745 let snapshot = this.buffer.read(cx).read(cx);
18746 this.selections
18747 .disjoint_anchors()
18748 .iter()
18749 .map(|selection| {
18750 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18751 })
18752 .collect::<Vec<_>>()
18753 };
18754
18755 if text.is_empty() {
18756 this.unmark_text(window, cx);
18757 } else {
18758 this.highlight_text::<InputComposition>(
18759 marked_ranges.clone(),
18760 HighlightStyle {
18761 underline: Some(UnderlineStyle {
18762 thickness: px(1.),
18763 color: None,
18764 wavy: false,
18765 }),
18766 ..Default::default()
18767 },
18768 cx,
18769 );
18770 }
18771
18772 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
18773 let use_autoclose = this.use_autoclose;
18774 let use_auto_surround = this.use_auto_surround;
18775 this.set_use_autoclose(false);
18776 this.set_use_auto_surround(false);
18777 this.handle_input(text, window, cx);
18778 this.set_use_autoclose(use_autoclose);
18779 this.set_use_auto_surround(use_auto_surround);
18780
18781 if let Some(new_selected_range) = new_selected_range_utf16 {
18782 let snapshot = this.buffer.read(cx).read(cx);
18783 let new_selected_ranges = marked_ranges
18784 .into_iter()
18785 .map(|marked_range| {
18786 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
18787 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
18788 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
18789 snapshot.clip_offset_utf16(new_start, Bias::Left)
18790 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
18791 })
18792 .collect::<Vec<_>>();
18793
18794 drop(snapshot);
18795 this.change_selections(None, window, cx, |selections| {
18796 selections.select_ranges(new_selected_ranges)
18797 });
18798 }
18799 });
18800
18801 self.ime_transaction = self.ime_transaction.or(transaction);
18802 if let Some(transaction) = self.ime_transaction {
18803 self.buffer.update(cx, |buffer, cx| {
18804 buffer.group_until_transaction(transaction, cx);
18805 });
18806 }
18807
18808 if self.text_highlights::<InputComposition>(cx).is_none() {
18809 self.ime_transaction.take();
18810 }
18811 }
18812
18813 fn bounds_for_range(
18814 &mut self,
18815 range_utf16: Range<usize>,
18816 element_bounds: gpui::Bounds<Pixels>,
18817 window: &mut Window,
18818 cx: &mut Context<Self>,
18819 ) -> Option<gpui::Bounds<Pixels>> {
18820 let text_layout_details = self.text_layout_details(window);
18821 let gpui::Size {
18822 width: em_width,
18823 height: line_height,
18824 } = self.character_size(window);
18825
18826 let snapshot = self.snapshot(window, cx);
18827 let scroll_position = snapshot.scroll_position();
18828 let scroll_left = scroll_position.x * em_width;
18829
18830 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
18831 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
18832 + self.gutter_dimensions.width
18833 + self.gutter_dimensions.margin;
18834 let y = line_height * (start.row().as_f32() - scroll_position.y);
18835
18836 Some(Bounds {
18837 origin: element_bounds.origin + point(x, y),
18838 size: size(em_width, line_height),
18839 })
18840 }
18841
18842 fn character_index_for_point(
18843 &mut self,
18844 point: gpui::Point<Pixels>,
18845 _window: &mut Window,
18846 _cx: &mut Context<Self>,
18847 ) -> Option<usize> {
18848 let position_map = self.last_position_map.as_ref()?;
18849 if !position_map.text_hitbox.contains(&point) {
18850 return None;
18851 }
18852 let display_point = position_map.point_for_position(point).previous_valid;
18853 let anchor = position_map
18854 .snapshot
18855 .display_point_to_anchor(display_point, Bias::Left);
18856 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
18857 Some(utf16_offset.0)
18858 }
18859}
18860
18861trait SelectionExt {
18862 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
18863 fn spanned_rows(
18864 &self,
18865 include_end_if_at_line_start: bool,
18866 map: &DisplaySnapshot,
18867 ) -> Range<MultiBufferRow>;
18868}
18869
18870impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
18871 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
18872 let start = self
18873 .start
18874 .to_point(&map.buffer_snapshot)
18875 .to_display_point(map);
18876 let end = self
18877 .end
18878 .to_point(&map.buffer_snapshot)
18879 .to_display_point(map);
18880 if self.reversed {
18881 end..start
18882 } else {
18883 start..end
18884 }
18885 }
18886
18887 fn spanned_rows(
18888 &self,
18889 include_end_if_at_line_start: bool,
18890 map: &DisplaySnapshot,
18891 ) -> Range<MultiBufferRow> {
18892 let start = self.start.to_point(&map.buffer_snapshot);
18893 let mut end = self.end.to_point(&map.buffer_snapshot);
18894 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
18895 end.row -= 1;
18896 }
18897
18898 let buffer_start = map.prev_line_boundary(start).0;
18899 let buffer_end = map.next_line_boundary(end).0;
18900 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
18901 }
18902}
18903
18904impl<T: InvalidationRegion> InvalidationStack<T> {
18905 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
18906 where
18907 S: Clone + ToOffset,
18908 {
18909 while let Some(region) = self.last() {
18910 let all_selections_inside_invalidation_ranges =
18911 if selections.len() == region.ranges().len() {
18912 selections
18913 .iter()
18914 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
18915 .all(|(selection, invalidation_range)| {
18916 let head = selection.head().to_offset(buffer);
18917 invalidation_range.start <= head && invalidation_range.end >= head
18918 })
18919 } else {
18920 false
18921 };
18922
18923 if all_selections_inside_invalidation_ranges {
18924 break;
18925 } else {
18926 self.pop();
18927 }
18928 }
18929 }
18930}
18931
18932impl<T> Default for InvalidationStack<T> {
18933 fn default() -> Self {
18934 Self(Default::default())
18935 }
18936}
18937
18938impl<T> Deref for InvalidationStack<T> {
18939 type Target = Vec<T>;
18940
18941 fn deref(&self) -> &Self::Target {
18942 &self.0
18943 }
18944}
18945
18946impl<T> DerefMut for InvalidationStack<T> {
18947 fn deref_mut(&mut self) -> &mut Self::Target {
18948 &mut self.0
18949 }
18950}
18951
18952impl InvalidationRegion for SnippetState {
18953 fn ranges(&self) -> &[Range<Anchor>] {
18954 &self.ranges[self.active_index]
18955 }
18956}
18957
18958pub fn diagnostic_block_renderer(
18959 diagnostic: Diagnostic,
18960 max_message_rows: Option<u8>,
18961 allow_closing: bool,
18962) -> RenderBlock {
18963 let (text_without_backticks, code_ranges) =
18964 highlight_diagnostic_message(&diagnostic, max_message_rows);
18965
18966 Arc::new(move |cx: &mut BlockContext| {
18967 let group_id: SharedString = cx.block_id.to_string().into();
18968
18969 let mut text_style = cx.window.text_style().clone();
18970 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18971 let theme_settings = ThemeSettings::get_global(cx);
18972 text_style.font_family = theme_settings.buffer_font.family.clone();
18973 text_style.font_style = theme_settings.buffer_font.style;
18974 text_style.font_features = theme_settings.buffer_font.features.clone();
18975 text_style.font_weight = theme_settings.buffer_font.weight;
18976
18977 let multi_line_diagnostic = diagnostic.message.contains('\n');
18978
18979 let buttons = |diagnostic: &Diagnostic| {
18980 if multi_line_diagnostic {
18981 v_flex()
18982 } else {
18983 h_flex()
18984 }
18985 .when(allow_closing, |div| {
18986 div.children(diagnostic.is_primary.then(|| {
18987 IconButton::new("close-block", IconName::XCircle)
18988 .icon_color(Color::Muted)
18989 .size(ButtonSize::Compact)
18990 .style(ButtonStyle::Transparent)
18991 .visible_on_hover(group_id.clone())
18992 .on_click(move |_click, window, cx| {
18993 window.dispatch_action(Box::new(Cancel), cx)
18994 })
18995 .tooltip(|window, cx| {
18996 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18997 })
18998 }))
18999 })
19000 .child(
19001 IconButton::new("copy-block", IconName::Copy)
19002 .icon_color(Color::Muted)
19003 .size(ButtonSize::Compact)
19004 .style(ButtonStyle::Transparent)
19005 .visible_on_hover(group_id.clone())
19006 .on_click({
19007 let message = diagnostic.message.clone();
19008 move |_click, _, cx| {
19009 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19010 }
19011 })
19012 .tooltip(Tooltip::text("Copy diagnostic message")),
19013 )
19014 };
19015
19016 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19017 AvailableSpace::min_size(),
19018 cx.window,
19019 cx.app,
19020 );
19021
19022 h_flex()
19023 .id(cx.block_id)
19024 .group(group_id.clone())
19025 .relative()
19026 .size_full()
19027 .block_mouse_down()
19028 .pl(cx.gutter_dimensions.width)
19029 .w(cx.max_width - cx.gutter_dimensions.full_width())
19030 .child(
19031 div()
19032 .flex()
19033 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19034 .flex_shrink(),
19035 )
19036 .child(buttons(&diagnostic))
19037 .child(div().flex().flex_shrink_0().child(
19038 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19039 &text_style,
19040 code_ranges.iter().map(|range| {
19041 (
19042 range.clone(),
19043 HighlightStyle {
19044 font_weight: Some(FontWeight::BOLD),
19045 ..Default::default()
19046 },
19047 )
19048 }),
19049 ),
19050 ))
19051 .into_any_element()
19052 })
19053}
19054
19055fn inline_completion_edit_text(
19056 current_snapshot: &BufferSnapshot,
19057 edits: &[(Range<Anchor>, String)],
19058 edit_preview: &EditPreview,
19059 include_deletions: bool,
19060 cx: &App,
19061) -> HighlightedText {
19062 let edits = edits
19063 .iter()
19064 .map(|(anchor, text)| {
19065 (
19066 anchor.start.text_anchor..anchor.end.text_anchor,
19067 text.clone(),
19068 )
19069 })
19070 .collect::<Vec<_>>();
19071
19072 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19073}
19074
19075pub fn highlight_diagnostic_message(
19076 diagnostic: &Diagnostic,
19077 mut max_message_rows: Option<u8>,
19078) -> (SharedString, Vec<Range<usize>>) {
19079 let mut text_without_backticks = String::new();
19080 let mut code_ranges = Vec::new();
19081
19082 if let Some(source) = &diagnostic.source {
19083 text_without_backticks.push_str(source);
19084 code_ranges.push(0..source.len());
19085 text_without_backticks.push_str(": ");
19086 }
19087
19088 let mut prev_offset = 0;
19089 let mut in_code_block = false;
19090 let has_row_limit = max_message_rows.is_some();
19091 let mut newline_indices = diagnostic
19092 .message
19093 .match_indices('\n')
19094 .filter(|_| has_row_limit)
19095 .map(|(ix, _)| ix)
19096 .fuse()
19097 .peekable();
19098
19099 for (quote_ix, _) in diagnostic
19100 .message
19101 .match_indices('`')
19102 .chain([(diagnostic.message.len(), "")])
19103 {
19104 let mut first_newline_ix = None;
19105 let mut last_newline_ix = None;
19106 while let Some(newline_ix) = newline_indices.peek() {
19107 if *newline_ix < quote_ix {
19108 if first_newline_ix.is_none() {
19109 first_newline_ix = Some(*newline_ix);
19110 }
19111 last_newline_ix = Some(*newline_ix);
19112
19113 if let Some(rows_left) = &mut max_message_rows {
19114 if *rows_left == 0 {
19115 break;
19116 } else {
19117 *rows_left -= 1;
19118 }
19119 }
19120 let _ = newline_indices.next();
19121 } else {
19122 break;
19123 }
19124 }
19125 let prev_len = text_without_backticks.len();
19126 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19127 text_without_backticks.push_str(new_text);
19128 if in_code_block {
19129 code_ranges.push(prev_len..text_without_backticks.len());
19130 }
19131 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19132 in_code_block = !in_code_block;
19133 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19134 text_without_backticks.push_str("...");
19135 break;
19136 }
19137 }
19138
19139 (text_without_backticks.into(), code_ranges)
19140}
19141
19142fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19143 match severity {
19144 DiagnosticSeverity::ERROR => colors.error,
19145 DiagnosticSeverity::WARNING => colors.warning,
19146 DiagnosticSeverity::INFORMATION => colors.info,
19147 DiagnosticSeverity::HINT => colors.info,
19148 _ => colors.ignored,
19149 }
19150}
19151
19152pub fn styled_runs_for_code_label<'a>(
19153 label: &'a CodeLabel,
19154 syntax_theme: &'a theme::SyntaxTheme,
19155) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19156 let fade_out = HighlightStyle {
19157 fade_out: Some(0.35),
19158 ..Default::default()
19159 };
19160
19161 let mut prev_end = label.filter_range.end;
19162 label
19163 .runs
19164 .iter()
19165 .enumerate()
19166 .flat_map(move |(ix, (range, highlight_id))| {
19167 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19168 style
19169 } else {
19170 return Default::default();
19171 };
19172 let mut muted_style = style;
19173 muted_style.highlight(fade_out);
19174
19175 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19176 if range.start >= label.filter_range.end {
19177 if range.start > prev_end {
19178 runs.push((prev_end..range.start, fade_out));
19179 }
19180 runs.push((range.clone(), muted_style));
19181 } else if range.end <= label.filter_range.end {
19182 runs.push((range.clone(), style));
19183 } else {
19184 runs.push((range.start..label.filter_range.end, style));
19185 runs.push((label.filter_range.end..range.end, muted_style));
19186 }
19187 prev_end = cmp::max(prev_end, range.end);
19188
19189 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19190 runs.push((prev_end..label.text.len(), fade_out));
19191 }
19192
19193 runs
19194 })
19195}
19196
19197pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19198 let mut prev_index = 0;
19199 let mut prev_codepoint: Option<char> = None;
19200 text.char_indices()
19201 .chain([(text.len(), '\0')])
19202 .filter_map(move |(index, codepoint)| {
19203 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19204 let is_boundary = index == text.len()
19205 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19206 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19207 if is_boundary {
19208 let chunk = &text[prev_index..index];
19209 prev_index = index;
19210 Some(chunk)
19211 } else {
19212 None
19213 }
19214 })
19215}
19216
19217pub trait RangeToAnchorExt: Sized {
19218 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19219
19220 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19221 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19222 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19223 }
19224}
19225
19226impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19227 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19228 let start_offset = self.start.to_offset(snapshot);
19229 let end_offset = self.end.to_offset(snapshot);
19230 if start_offset == end_offset {
19231 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19232 } else {
19233 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19234 }
19235 }
19236}
19237
19238pub trait RowExt {
19239 fn as_f32(&self) -> f32;
19240
19241 fn next_row(&self) -> Self;
19242
19243 fn previous_row(&self) -> Self;
19244
19245 fn minus(&self, other: Self) -> u32;
19246}
19247
19248impl RowExt for DisplayRow {
19249 fn as_f32(&self) -> f32 {
19250 self.0 as f32
19251 }
19252
19253 fn next_row(&self) -> Self {
19254 Self(self.0 + 1)
19255 }
19256
19257 fn previous_row(&self) -> Self {
19258 Self(self.0.saturating_sub(1))
19259 }
19260
19261 fn minus(&self, other: Self) -> u32 {
19262 self.0 - other.0
19263 }
19264}
19265
19266impl RowExt for MultiBufferRow {
19267 fn as_f32(&self) -> f32 {
19268 self.0 as f32
19269 }
19270
19271 fn next_row(&self) -> Self {
19272 Self(self.0 + 1)
19273 }
19274
19275 fn previous_row(&self) -> Self {
19276 Self(self.0.saturating_sub(1))
19277 }
19278
19279 fn minus(&self, other: Self) -> u32 {
19280 self.0 - other.0
19281 }
19282}
19283
19284trait RowRangeExt {
19285 type Row;
19286
19287 fn len(&self) -> usize;
19288
19289 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19290}
19291
19292impl RowRangeExt for Range<MultiBufferRow> {
19293 type Row = MultiBufferRow;
19294
19295 fn len(&self) -> usize {
19296 (self.end.0 - self.start.0) as usize
19297 }
19298
19299 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19300 (self.start.0..self.end.0).map(MultiBufferRow)
19301 }
19302}
19303
19304impl RowRangeExt for Range<DisplayRow> {
19305 type Row = DisplayRow;
19306
19307 fn len(&self) -> usize {
19308 (self.end.0 - self.start.0) as usize
19309 }
19310
19311 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19312 (self.start.0..self.end.0).map(DisplayRow)
19313 }
19314}
19315
19316/// If select range has more than one line, we
19317/// just point the cursor to range.start.
19318fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19319 if range.start.row == range.end.row {
19320 range
19321 } else {
19322 range.start..range.start
19323 }
19324}
19325pub struct KillRing(ClipboardItem);
19326impl Global for KillRing {}
19327
19328const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19329
19330struct BreakpointPromptEditor {
19331 pub(crate) prompt: Entity<Editor>,
19332 editor: WeakEntity<Editor>,
19333 breakpoint_anchor: Anchor,
19334 kind: BreakpointKind,
19335 block_ids: HashSet<CustomBlockId>,
19336 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19337 _subscriptions: Vec<Subscription>,
19338}
19339
19340impl BreakpointPromptEditor {
19341 const MAX_LINES: u8 = 4;
19342
19343 fn new(
19344 editor: WeakEntity<Editor>,
19345 breakpoint_anchor: Anchor,
19346 kind: BreakpointKind,
19347 window: &mut Window,
19348 cx: &mut Context<Self>,
19349 ) -> Self {
19350 let buffer = cx.new(|cx| {
19351 Buffer::local(
19352 kind.log_message()
19353 .map(|msg| msg.to_string())
19354 .unwrap_or_default(),
19355 cx,
19356 )
19357 });
19358 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19359
19360 let prompt = cx.new(|cx| {
19361 let mut prompt = Editor::new(
19362 EditorMode::AutoHeight {
19363 max_lines: Self::MAX_LINES as usize,
19364 },
19365 buffer,
19366 None,
19367 window,
19368 cx,
19369 );
19370 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19371 prompt.set_show_cursor_when_unfocused(false, cx);
19372 prompt.set_placeholder_text(
19373 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19374 cx,
19375 );
19376
19377 prompt
19378 });
19379
19380 Self {
19381 prompt,
19382 editor,
19383 breakpoint_anchor,
19384 kind,
19385 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19386 block_ids: Default::default(),
19387 _subscriptions: vec![],
19388 }
19389 }
19390
19391 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19392 self.block_ids.extend(block_ids)
19393 }
19394
19395 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19396 if let Some(editor) = self.editor.upgrade() {
19397 let log_message = self
19398 .prompt
19399 .read(cx)
19400 .buffer
19401 .read(cx)
19402 .as_singleton()
19403 .expect("A multi buffer in breakpoint prompt isn't possible")
19404 .read(cx)
19405 .as_rope()
19406 .to_string();
19407
19408 editor.update(cx, |editor, cx| {
19409 editor.edit_breakpoint_at_anchor(
19410 self.breakpoint_anchor,
19411 self.kind.clone(),
19412 BreakpointEditAction::EditLogMessage(log_message.into()),
19413 cx,
19414 );
19415
19416 editor.remove_blocks(self.block_ids.clone(), None, cx);
19417 cx.focus_self(window);
19418 });
19419 }
19420 }
19421
19422 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19423 self.editor
19424 .update(cx, |editor, cx| {
19425 editor.remove_blocks(self.block_ids.clone(), None, cx);
19426 window.focus(&editor.focus_handle);
19427 })
19428 .log_err();
19429 }
19430
19431 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19432 let settings = ThemeSettings::get_global(cx);
19433 let text_style = TextStyle {
19434 color: if self.prompt.read(cx).read_only(cx) {
19435 cx.theme().colors().text_disabled
19436 } else {
19437 cx.theme().colors().text
19438 },
19439 font_family: settings.buffer_font.family.clone(),
19440 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19441 font_size: settings.buffer_font_size(cx).into(),
19442 font_weight: settings.buffer_font.weight,
19443 line_height: relative(settings.buffer_line_height.value()),
19444 ..Default::default()
19445 };
19446 EditorElement::new(
19447 &self.prompt,
19448 EditorStyle {
19449 background: cx.theme().colors().editor_background,
19450 local_player: cx.theme().players().local(),
19451 text: text_style,
19452 ..Default::default()
19453 },
19454 )
19455 }
19456}
19457
19458impl Render for BreakpointPromptEditor {
19459 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19460 let gutter_dimensions = *self.gutter_dimensions.lock();
19461 h_flex()
19462 .key_context("Editor")
19463 .bg(cx.theme().colors().editor_background)
19464 .border_y_1()
19465 .border_color(cx.theme().status().info_border)
19466 .size_full()
19467 .py(window.line_height() / 2.5)
19468 .on_action(cx.listener(Self::confirm))
19469 .on_action(cx.listener(Self::cancel))
19470 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19471 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19472 }
19473}
19474
19475impl Focusable for BreakpointPromptEditor {
19476 fn focus_handle(&self, cx: &App) -> FocusHandle {
19477 self.prompt.focus_handle(cx)
19478 }
19479}
19480
19481fn all_edits_insertions_or_deletions(
19482 edits: &Vec<(Range<Anchor>, String)>,
19483 snapshot: &MultiBufferSnapshot,
19484) -> bool {
19485 let mut all_insertions = true;
19486 let mut all_deletions = true;
19487
19488 for (range, new_text) in edits.iter() {
19489 let range_is_empty = range.to_offset(&snapshot).is_empty();
19490 let text_is_empty = new_text.is_empty();
19491
19492 if range_is_empty != text_is_empty {
19493 if range_is_empty {
19494 all_deletions = false;
19495 } else {
19496 all_insertions = false;
19497 }
19498 } else {
19499 return false;
19500 }
19501
19502 if !all_insertions && !all_deletions {
19503 return false;
19504 }
19505 }
19506 all_insertions || all_deletions
19507}
19508
19509struct MissingEditPredictionKeybindingTooltip;
19510
19511impl Render for MissingEditPredictionKeybindingTooltip {
19512 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19513 ui::tooltip_container(window, cx, |container, _, cx| {
19514 container
19515 .flex_shrink_0()
19516 .max_w_80()
19517 .min_h(rems_from_px(124.))
19518 .justify_between()
19519 .child(
19520 v_flex()
19521 .flex_1()
19522 .text_ui_sm(cx)
19523 .child(Label::new("Conflict with Accept Keybinding"))
19524 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19525 )
19526 .child(
19527 h_flex()
19528 .pb_1()
19529 .gap_1()
19530 .items_end()
19531 .w_full()
19532 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19533 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19534 }))
19535 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19536 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19537 })),
19538 )
19539 })
19540 }
19541}
19542
19543#[derive(Debug, Clone, Copy, PartialEq)]
19544pub struct LineHighlight {
19545 pub background: Background,
19546 pub border: Option<gpui::Hsla>,
19547}
19548
19549impl From<Hsla> for LineHighlight {
19550 fn from(hsla: Hsla) -> Self {
19551 Self {
19552 background: hsla.into(),
19553 border: None,
19554 }
19555 }
19556}
19557
19558impl From<Background> for LineHighlight {
19559 fn from(background: Background) -> Self {
19560 Self {
19561 background,
19562 border: None,
19563 }
19564 }
19565}