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};
63use editor_settings::GoToDefinitionFallback;
64pub use editor_settings::{
65 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
66};
67pub use editor_settings_controls::*;
68use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
69pub use element::{
70 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
71};
72use feature_flags::{Debugger, FeatureFlagAppExt};
73use futures::{
74 future::{self, join, Shared},
75 FutureExt,
76};
77use fuzzy::StringMatchCandidate;
78
79use ::git::Restore;
80use code_context_menus::{
81 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
82 CompletionsMenu, ContextMenuOrigin,
83};
84use git::blame::GitBlame;
85use gpui::{
86 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
87 AnimationExt, AnyElement, App, AppContext, AsyncWindowContext, AvailableSpace, Background,
88 Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity,
89 EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight,
90 Global, HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
91 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
92 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
93 WeakEntity, WeakFocusHandle, Window,
94};
95use highlight_matching_bracket::refresh_matching_bracket_highlights;
96use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
97use hover_popover::{hide_hover, HoverState};
98use indent_guides::ActiveIndentGuidesState;
99use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
100pub use inline_completion::Direction;
101use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
102pub use items::MAX_TAB_TITLE_LEN;
103use itertools::Itertools;
104use language::{
105 language_settings::{
106 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
107 WordsCompletionMode,
108 },
109 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
110 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
111 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
112 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
113};
114use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
115use linked_editing_ranges::refresh_linked_ranges;
116use mouse_context_menu::MouseContextMenu;
117use persistence::DB;
118use project::{
119 debugger::breakpoint_store::{
120 BreakpointEditAction, BreakpointState, BreakpointStore, BreakpointStoreEvent,
121 },
122 ProjectPath,
123};
124
125pub use proposed_changes_editor::{
126 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
127};
128use smallvec::smallvec;
129use std::{cell::OnceCell, iter::Peekable};
130use task::{ResolvedTask, TaskTemplate, TaskVariables};
131
132pub use lsp::CompletionContext;
133use lsp::{
134 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
135 InsertTextFormat, LanguageServerId, LanguageServerName,
136};
137
138use language::BufferSnapshot;
139use movement::TextLayoutDetails;
140pub use multi_buffer::{
141 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
142 ToOffset, ToPoint,
143};
144use multi_buffer::{
145 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
146 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
147};
148use parking_lot::Mutex;
149use project::{
150 debugger::breakpoint_store::{Breakpoint, BreakpointKind},
151 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
152 project_settings::{GitGutterSetting, ProjectSettings},
153 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
154 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
155 TaskSourceKind,
156};
157use rand::prelude::*;
158use rpc::{proto::*, ErrorExt};
159use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
160use selections_collection::{
161 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
162};
163use serde::{Deserialize, Serialize};
164use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
165use smallvec::SmallVec;
166use snippet::Snippet;
167use std::sync::Arc;
168use std::{
169 any::TypeId,
170 borrow::Cow,
171 cell::RefCell,
172 cmp::{self, Ordering, Reverse},
173 mem,
174 num::NonZeroU32,
175 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
176 path::{Path, PathBuf},
177 rc::Rc,
178 time::{Duration, Instant},
179};
180pub use sum_tree::Bias;
181use sum_tree::TreeMap;
182use text::{BufferId, OffsetUtf16, Rope};
183use theme::{
184 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
185 ThemeColors, ThemeSettings,
186};
187use ui::{
188 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
189 Tooltip,
190};
191use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
192use workspace::{
193 item::{ItemHandle, PreviewTabsSettings},
194 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
195 searchable::SearchEvent,
196 Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
197 RestoreOnStartupBehavior, SplitDirection, TabBarSettings, Toast, ViewId, Workspace,
198 WorkspaceId, WorkspaceSettings, SERIALIZATION_THROTTLE_TIME,
199};
200
201use crate::hover_links::{find_url, find_url_from_range};
202use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
203
204pub const FILE_HEADER_HEIGHT: u32 = 2;
205pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
206pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
207const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
208const MAX_LINE_LEN: usize = 1024;
209const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
210const MAX_SELECTION_HISTORY_LEN: usize = 1024;
211pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
212#[doc(hidden)]
213pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
214
215pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
216pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
217pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
218
219pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
220pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
221pub(crate) const MIN_LINE_NUMBER_DIGITS: u32 = 4;
222
223const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
224 alt: true,
225 shift: true,
226 control: false,
227 platform: false,
228 function: false,
229};
230
231#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
232pub enum InlayId {
233 InlineCompletion(usize),
234 Hint(usize),
235}
236
237impl InlayId {
238 fn id(&self) -> usize {
239 match self {
240 Self::InlineCompletion(id) => *id,
241 Self::Hint(id) => *id,
242 }
243 }
244}
245
246pub enum DebugCurrentRowHighlight {}
247enum DocumentHighlightRead {}
248enum DocumentHighlightWrite {}
249enum InputComposition {}
250enum SelectedTextHighlight {}
251
252#[derive(Debug, Copy, Clone, PartialEq, Eq)]
253pub enum Navigated {
254 Yes,
255 No,
256}
257
258impl Navigated {
259 pub fn from_bool(yes: bool) -> Navigated {
260 if yes {
261 Navigated::Yes
262 } else {
263 Navigated::No
264 }
265 }
266}
267
268#[derive(Debug, Clone, PartialEq, Eq)]
269enum DisplayDiffHunk {
270 Folded {
271 display_row: DisplayRow,
272 },
273 Unfolded {
274 is_created_file: bool,
275 diff_base_byte_range: Range<usize>,
276 display_row_range: Range<DisplayRow>,
277 multi_buffer_range: Range<Anchor>,
278 status: DiffHunkStatus,
279 },
280}
281
282pub fn init_settings(cx: &mut App) {
283 EditorSettings::register(cx);
284}
285
286pub fn init(cx: &mut App) {
287 init_settings(cx);
288
289 workspace::register_project_item::<Editor>(cx);
290 workspace::FollowableViewRegistry::register::<Editor>(cx);
291 workspace::register_serializable_item::<Editor>(cx);
292
293 cx.observe_new(
294 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
295 workspace.register_action(Editor::new_file);
296 workspace.register_action(Editor::new_file_vertical);
297 workspace.register_action(Editor::new_file_horizontal);
298 workspace.register_action(Editor::cancel_language_server_work);
299 },
300 )
301 .detach();
302
303 cx.on_action(move |_: &workspace::NewFile, cx| {
304 let app_state = workspace::AppState::global(cx);
305 if let Some(app_state) = app_state.upgrade() {
306 workspace::open_new(
307 Default::default(),
308 app_state,
309 cx,
310 |workspace, window, cx| {
311 Editor::new_file(workspace, &Default::default(), window, cx)
312 },
313 )
314 .detach();
315 }
316 });
317 cx.on_action(move |_: &workspace::NewWindow, cx| {
318 let app_state = workspace::AppState::global(cx);
319 if let Some(app_state) = app_state.upgrade() {
320 workspace::open_new(
321 Default::default(),
322 app_state,
323 cx,
324 |workspace, window, cx| {
325 cx.activate(true);
326 Editor::new_file(workspace, &Default::default(), window, cx)
327 },
328 )
329 .detach();
330 }
331 });
332}
333
334pub struct SearchWithinRange;
335
336trait InvalidationRegion {
337 fn ranges(&self) -> &[Range<Anchor>];
338}
339
340#[derive(Clone, Debug, PartialEq)]
341pub enum SelectPhase {
342 Begin {
343 position: DisplayPoint,
344 add: bool,
345 click_count: usize,
346 },
347 BeginColumnar {
348 position: DisplayPoint,
349 reset: bool,
350 goal_column: u32,
351 },
352 Extend {
353 position: DisplayPoint,
354 click_count: usize,
355 },
356 Update {
357 position: DisplayPoint,
358 goal_column: u32,
359 scroll_delta: gpui::Point<f32>,
360 },
361 End,
362}
363
364#[derive(Clone, Debug)]
365pub enum SelectMode {
366 Character,
367 Word(Range<Anchor>),
368 Line(Range<Anchor>),
369 All,
370}
371
372#[derive(Copy, Clone, PartialEq, Eq, Debug)]
373pub enum EditorMode {
374 SingleLine { auto_width: bool },
375 AutoHeight { max_lines: usize },
376 Full,
377}
378
379#[derive(Copy, Clone, Debug)]
380pub enum SoftWrap {
381 /// Prefer not to wrap at all.
382 ///
383 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
384 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
385 GitDiff,
386 /// Prefer a single line generally, unless an overly long line is encountered.
387 None,
388 /// Soft wrap lines that exceed the editor width.
389 EditorWidth,
390 /// Soft wrap lines at the preferred line length.
391 Column(u32),
392 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
393 Bounded(u32),
394}
395
396#[derive(Clone)]
397pub struct EditorStyle {
398 pub background: Hsla,
399 pub local_player: PlayerColor,
400 pub text: TextStyle,
401 pub scrollbar_width: Pixels,
402 pub syntax: Arc<SyntaxTheme>,
403 pub status: StatusColors,
404 pub inlay_hints_style: HighlightStyle,
405 pub inline_completion_styles: InlineCompletionStyles,
406 pub unnecessary_code_fade: f32,
407}
408
409impl Default for EditorStyle {
410 fn default() -> Self {
411 Self {
412 background: Hsla::default(),
413 local_player: PlayerColor::default(),
414 text: TextStyle::default(),
415 scrollbar_width: Pixels::default(),
416 syntax: Default::default(),
417 // HACK: Status colors don't have a real default.
418 // We should look into removing the status colors from the editor
419 // style and retrieve them directly from the theme.
420 status: StatusColors::dark(),
421 inlay_hints_style: HighlightStyle::default(),
422 inline_completion_styles: InlineCompletionStyles {
423 insertion: HighlightStyle::default(),
424 whitespace: HighlightStyle::default(),
425 },
426 unnecessary_code_fade: Default::default(),
427 }
428 }
429}
430
431pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
432 let show_background = language_settings::language_settings(None, None, cx)
433 .inlay_hints
434 .show_background;
435
436 HighlightStyle {
437 color: Some(cx.theme().status().hint),
438 background_color: show_background.then(|| cx.theme().status().hint_background),
439 ..HighlightStyle::default()
440 }
441}
442
443pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
444 InlineCompletionStyles {
445 insertion: HighlightStyle {
446 color: Some(cx.theme().status().predictive),
447 ..HighlightStyle::default()
448 },
449 whitespace: HighlightStyle {
450 background_color: Some(cx.theme().status().created_background),
451 ..HighlightStyle::default()
452 },
453 }
454}
455
456type CompletionId = usize;
457
458pub(crate) enum EditDisplayMode {
459 TabAccept,
460 DiffPopover,
461 Inline,
462}
463
464enum InlineCompletion {
465 Edit {
466 edits: Vec<(Range<Anchor>, String)>,
467 edit_preview: Option<EditPreview>,
468 display_mode: EditDisplayMode,
469 snapshot: BufferSnapshot,
470 },
471 Move {
472 target: Anchor,
473 snapshot: BufferSnapshot,
474 },
475}
476
477struct InlineCompletionState {
478 inlay_ids: Vec<InlayId>,
479 completion: InlineCompletion,
480 completion_id: Option<SharedString>,
481 invalidation_range: Range<Anchor>,
482}
483
484enum EditPredictionSettings {
485 Disabled,
486 Enabled {
487 show_in_menu: bool,
488 preview_requires_modifier: bool,
489 },
490}
491
492enum InlineCompletionHighlight {}
493
494#[derive(Debug, Clone)]
495struct InlineDiagnostic {
496 message: SharedString,
497 group_id: usize,
498 is_primary: bool,
499 start: Point,
500 severity: DiagnosticSeverity,
501}
502
503pub enum MenuInlineCompletionsPolicy {
504 Never,
505 ByProvider,
506}
507
508pub enum EditPredictionPreview {
509 /// Modifier is not pressed
510 Inactive { released_too_fast: bool },
511 /// Modifier pressed
512 Active {
513 since: Instant,
514 previous_scroll_position: Option<ScrollAnchor>,
515 },
516}
517
518impl EditPredictionPreview {
519 pub fn released_too_fast(&self) -> bool {
520 match self {
521 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
522 EditPredictionPreview::Active { .. } => false,
523 }
524 }
525
526 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
527 if let EditPredictionPreview::Active {
528 previous_scroll_position,
529 ..
530 } = self
531 {
532 *previous_scroll_position = scroll_position;
533 }
534 }
535}
536
537pub struct ContextMenuOptions {
538 pub min_entries_visible: usize,
539 pub max_entries_visible: usize,
540 pub placement: Option<ContextMenuPlacement>,
541}
542
543#[derive(Debug, Clone, PartialEq, Eq)]
544pub enum ContextMenuPlacement {
545 Above,
546 Below,
547}
548
549#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
550struct EditorActionId(usize);
551
552impl EditorActionId {
553 pub fn post_inc(&mut self) -> Self {
554 let answer = self.0;
555
556 *self = Self(answer + 1);
557
558 Self(answer)
559 }
560}
561
562// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
563// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
564
565type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
566type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
567
568#[derive(Default)]
569struct ScrollbarMarkerState {
570 scrollbar_size: Size<Pixels>,
571 dirty: bool,
572 markers: Arc<[PaintQuad]>,
573 pending_refresh: Option<Task<Result<()>>>,
574}
575
576impl ScrollbarMarkerState {
577 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
578 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
579 }
580}
581
582#[derive(Clone, Debug)]
583struct RunnableTasks {
584 templates: Vec<(TaskSourceKind, TaskTemplate)>,
585 offset: multi_buffer::Anchor,
586 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
587 column: u32,
588 // Values of all named captures, including those starting with '_'
589 extra_variables: HashMap<String, String>,
590 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
591 context_range: Range<BufferOffset>,
592}
593
594impl RunnableTasks {
595 fn resolve<'a>(
596 &'a self,
597 cx: &'a task::TaskContext,
598 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
599 self.templates.iter().filter_map(|(kind, template)| {
600 template
601 .resolve_task(&kind.to_id_base(), cx)
602 .map(|task| (kind.clone(), task))
603 })
604 }
605}
606
607#[derive(Clone)]
608struct ResolvedTasks {
609 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
610 position: Anchor,
611}
612
613#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
614struct BufferOffset(usize);
615
616// Addons allow storing per-editor state in other crates (e.g. Vim)
617pub trait Addon: 'static {
618 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
619
620 fn render_buffer_header_controls(
621 &self,
622 _: &ExcerptInfo,
623 _: &Window,
624 _: &App,
625 ) -> Option<AnyElement> {
626 None
627 }
628
629 fn to_any(&self) -> &dyn std::any::Any;
630}
631
632/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
633///
634/// See the [module level documentation](self) for more information.
635pub struct Editor {
636 focus_handle: FocusHandle,
637 last_focused_descendant: Option<WeakFocusHandle>,
638 /// The text buffer being edited
639 buffer: Entity<MultiBuffer>,
640 /// Map of how text in the buffer should be displayed.
641 /// Handles soft wraps, folds, fake inlay text insertions, etc.
642 pub display_map: Entity<DisplayMap>,
643 pub selections: SelectionsCollection,
644 pub scroll_manager: ScrollManager,
645 /// When inline assist editors are linked, they all render cursors because
646 /// typing enters text into each of them, even the ones that aren't focused.
647 pub(crate) show_cursor_when_unfocused: bool,
648 columnar_selection_tail: Option<Anchor>,
649 add_selections_state: Option<AddSelectionsState>,
650 select_next_state: Option<SelectNextState>,
651 select_prev_state: Option<SelectNextState>,
652 selection_history: SelectionHistory,
653 autoclose_regions: Vec<AutocloseRegion>,
654 snippet_stack: InvalidationStack<SnippetState>,
655 select_syntax_node_history: SelectSyntaxNodeHistory,
656 ime_transaction: Option<TransactionId>,
657 active_diagnostics: Option<ActiveDiagnosticGroup>,
658 show_inline_diagnostics: bool,
659 inline_diagnostics_update: Task<()>,
660 inline_diagnostics_enabled: bool,
661 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
662 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
663 hard_wrap: Option<usize>,
664
665 // TODO: make this a access method
666 pub project: Option<Entity<Project>>,
667 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
668 completion_provider: Option<Box<dyn CompletionProvider>>,
669 collaboration_hub: Option<Box<dyn CollaborationHub>>,
670 blink_manager: Entity<BlinkManager>,
671 show_cursor_names: bool,
672 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
673 pub show_local_selections: bool,
674 mode: EditorMode,
675 show_breadcrumbs: bool,
676 show_gutter: bool,
677 show_scrollbars: bool,
678 show_line_numbers: Option<bool>,
679 use_relative_line_numbers: Option<bool>,
680 show_git_diff_gutter: Option<bool>,
681 show_code_actions: Option<bool>,
682 show_runnables: Option<bool>,
683 show_breakpoints: Option<bool>,
684 show_wrap_guides: Option<bool>,
685 show_indent_guides: Option<bool>,
686 placeholder_text: Option<Arc<str>>,
687 highlight_order: usize,
688 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
689 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
690 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
691 scrollbar_marker_state: ScrollbarMarkerState,
692 active_indent_guides_state: ActiveIndentGuidesState,
693 nav_history: Option<ItemNavHistory>,
694 context_menu: RefCell<Option<CodeContextMenu>>,
695 context_menu_options: Option<ContextMenuOptions>,
696 mouse_context_menu: Option<MouseContextMenu>,
697 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
698 signature_help_state: SignatureHelpState,
699 auto_signature_help: Option<bool>,
700 find_all_references_task_sources: Vec<Anchor>,
701 next_completion_id: CompletionId,
702 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
703 code_actions_task: Option<Task<Result<()>>>,
704 selection_highlight_task: Option<Task<()>>,
705 document_highlights_task: Option<Task<()>>,
706 linked_editing_range_task: Option<Task<Option<()>>>,
707 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
708 pending_rename: Option<RenameState>,
709 searchable: bool,
710 cursor_shape: CursorShape,
711 current_line_highlight: Option<CurrentLineHighlight>,
712 collapse_matches: bool,
713 autoindent_mode: Option<AutoindentMode>,
714 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
715 input_enabled: bool,
716 use_modal_editing: bool,
717 read_only: bool,
718 leader_peer_id: Option<PeerId>,
719 remote_id: Option<ViewId>,
720 hover_state: HoverState,
721 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
722 gutter_hovered: bool,
723 hovered_link_state: Option<HoveredLinkState>,
724 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
725 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
726 active_inline_completion: Option<InlineCompletionState>,
727 /// Used to prevent flickering as the user types while the menu is open
728 stale_inline_completion_in_menu: Option<InlineCompletionState>,
729 edit_prediction_settings: EditPredictionSettings,
730 inline_completions_hidden_for_vim_mode: bool,
731 show_inline_completions_override: Option<bool>,
732 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
733 edit_prediction_preview: EditPredictionPreview,
734 edit_prediction_indent_conflict: bool,
735 edit_prediction_requires_modifier_in_indent_conflict: bool,
736 inlay_hint_cache: InlayHintCache,
737 next_inlay_id: usize,
738 _subscriptions: Vec<Subscription>,
739 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
740 gutter_dimensions: GutterDimensions,
741 style: Option<EditorStyle>,
742 text_style_refinement: Option<TextStyleRefinement>,
743 next_editor_action_id: EditorActionId,
744 editor_actions:
745 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
746 use_autoclose: bool,
747 use_auto_surround: bool,
748 auto_replace_emoji_shortcode: bool,
749 jsx_tag_auto_close_enabled_in_any_buffer: bool,
750 show_git_blame_gutter: bool,
751 show_git_blame_inline: bool,
752 show_git_blame_inline_delay_task: Option<Task<()>>,
753 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
754 git_blame_inline_enabled: bool,
755 serialize_dirty_buffers: bool,
756 show_selection_menu: Option<bool>,
757 blame: Option<Entity<GitBlame>>,
758 blame_subscription: Option<Subscription>,
759 custom_context_menu: Option<
760 Box<
761 dyn 'static
762 + Fn(
763 &mut Self,
764 DisplayPoint,
765 &mut Window,
766 &mut Context<Self>,
767 ) -> Option<Entity<ui::ContextMenu>>,
768 >,
769 >,
770 last_bounds: Option<Bounds<Pixels>>,
771 last_position_map: Option<Rc<PositionMap>>,
772 expect_bounds_change: Option<Bounds<Pixels>>,
773 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
774 tasks_update_task: Option<Task<()>>,
775 pub breakpoint_store: Option<Entity<BreakpointStore>>,
776 /// Allow's a user to create a breakpoint by selecting this indicator
777 /// It should be None while a user is not hovering over the gutter
778 /// Otherwise it represents the point that the breakpoint will be shown
779 pub gutter_breakpoint_indicator: Option<DisplayPoint>,
780 in_project_search: bool,
781 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
782 breadcrumb_header: Option<String>,
783 focused_block: Option<FocusedBlock>,
784 next_scroll_position: NextScrollCursorCenterTopBottom,
785 addons: HashMap<TypeId, Box<dyn Addon>>,
786 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
787 load_diff_task: Option<Shared<Task<()>>>,
788 selection_mark_mode: bool,
789 toggle_fold_multiple_buffers: Task<()>,
790 _scroll_cursor_center_top_bottom_task: Task<()>,
791 serialize_selections: Task<()>,
792 serialize_folds: Task<()>,
793 mouse_cursor_hidden: bool,
794 hide_mouse_while_typing: bool,
795}
796
797#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
798enum NextScrollCursorCenterTopBottom {
799 #[default]
800 Center,
801 Top,
802 Bottom,
803}
804
805impl NextScrollCursorCenterTopBottom {
806 fn next(&self) -> Self {
807 match self {
808 Self::Center => Self::Top,
809 Self::Top => Self::Bottom,
810 Self::Bottom => Self::Center,
811 }
812 }
813}
814
815#[derive(Clone)]
816pub struct EditorSnapshot {
817 pub mode: EditorMode,
818 show_gutter: bool,
819 show_line_numbers: Option<bool>,
820 show_git_diff_gutter: Option<bool>,
821 show_code_actions: Option<bool>,
822 show_runnables: Option<bool>,
823 show_breakpoints: Option<bool>,
824 git_blame_gutter_max_author_length: Option<usize>,
825 pub display_snapshot: DisplaySnapshot,
826 pub placeholder_text: Option<Arc<str>>,
827 is_focused: bool,
828 scroll_anchor: ScrollAnchor,
829 ongoing_scroll: OngoingScroll,
830 current_line_highlight: CurrentLineHighlight,
831 gutter_hovered: bool,
832}
833
834const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
835
836#[derive(Default, Debug, Clone, Copy)]
837pub struct GutterDimensions {
838 pub left_padding: Pixels,
839 pub right_padding: Pixels,
840 pub width: Pixels,
841 pub margin: Pixels,
842 pub git_blame_entries_width: Option<Pixels>,
843}
844
845impl GutterDimensions {
846 /// The full width of the space taken up by the gutter.
847 pub fn full_width(&self) -> Pixels {
848 self.margin + self.width
849 }
850
851 /// The width of the space reserved for the fold indicators,
852 /// use alongside 'justify_end' and `gutter_width` to
853 /// right align content with the line numbers
854 pub fn fold_area_width(&self) -> Pixels {
855 self.margin + self.right_padding
856 }
857}
858
859#[derive(Debug)]
860pub struct RemoteSelection {
861 pub replica_id: ReplicaId,
862 pub selection: Selection<Anchor>,
863 pub cursor_shape: CursorShape,
864 pub peer_id: PeerId,
865 pub line_mode: bool,
866 pub participant_index: Option<ParticipantIndex>,
867 pub user_name: Option<SharedString>,
868}
869
870#[derive(Clone, Debug)]
871struct SelectionHistoryEntry {
872 selections: Arc<[Selection<Anchor>]>,
873 select_next_state: Option<SelectNextState>,
874 select_prev_state: Option<SelectNextState>,
875 add_selections_state: Option<AddSelectionsState>,
876}
877
878enum SelectionHistoryMode {
879 Normal,
880 Undoing,
881 Redoing,
882}
883
884#[derive(Clone, PartialEq, Eq, Hash)]
885struct HoveredCursor {
886 replica_id: u16,
887 selection_id: usize,
888}
889
890impl Default for SelectionHistoryMode {
891 fn default() -> Self {
892 Self::Normal
893 }
894}
895
896#[derive(Default)]
897struct SelectionHistory {
898 #[allow(clippy::type_complexity)]
899 selections_by_transaction:
900 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
901 mode: SelectionHistoryMode,
902 undo_stack: VecDeque<SelectionHistoryEntry>,
903 redo_stack: VecDeque<SelectionHistoryEntry>,
904}
905
906impl SelectionHistory {
907 fn insert_transaction(
908 &mut self,
909 transaction_id: TransactionId,
910 selections: Arc<[Selection<Anchor>]>,
911 ) {
912 self.selections_by_transaction
913 .insert(transaction_id, (selections, None));
914 }
915
916 #[allow(clippy::type_complexity)]
917 fn transaction(
918 &self,
919 transaction_id: TransactionId,
920 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
921 self.selections_by_transaction.get(&transaction_id)
922 }
923
924 #[allow(clippy::type_complexity)]
925 fn transaction_mut(
926 &mut self,
927 transaction_id: TransactionId,
928 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
929 self.selections_by_transaction.get_mut(&transaction_id)
930 }
931
932 fn push(&mut self, entry: SelectionHistoryEntry) {
933 if !entry.selections.is_empty() {
934 match self.mode {
935 SelectionHistoryMode::Normal => {
936 self.push_undo(entry);
937 self.redo_stack.clear();
938 }
939 SelectionHistoryMode::Undoing => self.push_redo(entry),
940 SelectionHistoryMode::Redoing => self.push_undo(entry),
941 }
942 }
943 }
944
945 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
946 if self
947 .undo_stack
948 .back()
949 .map_or(true, |e| e.selections != entry.selections)
950 {
951 self.undo_stack.push_back(entry);
952 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
953 self.undo_stack.pop_front();
954 }
955 }
956 }
957
958 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
959 if self
960 .redo_stack
961 .back()
962 .map_or(true, |e| e.selections != entry.selections)
963 {
964 self.redo_stack.push_back(entry);
965 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
966 self.redo_stack.pop_front();
967 }
968 }
969 }
970}
971
972struct RowHighlight {
973 index: usize,
974 range: Range<Anchor>,
975 color: Hsla,
976 should_autoscroll: bool,
977}
978
979#[derive(Clone, Debug)]
980struct AddSelectionsState {
981 above: bool,
982 stack: Vec<usize>,
983}
984
985#[derive(Clone)]
986struct SelectNextState {
987 query: AhoCorasick,
988 wordwise: bool,
989 done: bool,
990}
991
992impl std::fmt::Debug for SelectNextState {
993 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994 f.debug_struct(std::any::type_name::<Self>())
995 .field("wordwise", &self.wordwise)
996 .field("done", &self.done)
997 .finish()
998 }
999}
1000
1001#[derive(Debug)]
1002struct AutocloseRegion {
1003 selection_id: usize,
1004 range: Range<Anchor>,
1005 pair: BracketPair,
1006}
1007
1008#[derive(Debug)]
1009struct SnippetState {
1010 ranges: Vec<Vec<Range<Anchor>>>,
1011 active_index: usize,
1012 choices: Vec<Option<Vec<String>>>,
1013}
1014
1015#[doc(hidden)]
1016pub struct RenameState {
1017 pub range: Range<Anchor>,
1018 pub old_name: Arc<str>,
1019 pub editor: Entity<Editor>,
1020 block_id: CustomBlockId,
1021}
1022
1023struct InvalidationStack<T>(Vec<T>);
1024
1025struct RegisteredInlineCompletionProvider {
1026 provider: Arc<dyn InlineCompletionProviderHandle>,
1027 _subscription: Subscription,
1028}
1029
1030#[derive(Debug, PartialEq, Eq)]
1031struct ActiveDiagnosticGroup {
1032 primary_range: Range<Anchor>,
1033 primary_message: String,
1034 group_id: usize,
1035 blocks: HashMap<CustomBlockId, Diagnostic>,
1036 is_valid: bool,
1037}
1038
1039#[derive(Serialize, Deserialize, Clone, Debug)]
1040pub struct ClipboardSelection {
1041 /// The number of bytes in this selection.
1042 pub len: usize,
1043 /// Whether this was a full-line selection.
1044 pub is_entire_line: bool,
1045 /// The indentation of the first line when this content was originally copied.
1046 pub first_line_indent: u32,
1047}
1048
1049// selections, scroll behavior, was newest selection reversed
1050type SelectSyntaxNodeHistoryState = (
1051 Box<[Selection<usize>]>,
1052 SelectSyntaxNodeScrollBehavior,
1053 bool,
1054);
1055
1056#[derive(Default)]
1057struct SelectSyntaxNodeHistory {
1058 stack: Vec<SelectSyntaxNodeHistoryState>,
1059 // disable temporarily to allow changing selections without losing the stack
1060 pub disable_clearing: bool,
1061}
1062
1063impl SelectSyntaxNodeHistory {
1064 pub fn try_clear(&mut self) {
1065 if !self.disable_clearing {
1066 self.stack.clear();
1067 }
1068 }
1069
1070 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1071 self.stack.push(selection);
1072 }
1073
1074 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1075 self.stack.pop()
1076 }
1077}
1078
1079enum SelectSyntaxNodeScrollBehavior {
1080 CursorTop,
1081 CenterSelection,
1082 CursorBottom,
1083}
1084
1085#[derive(Debug)]
1086pub(crate) struct NavigationData {
1087 cursor_anchor: Anchor,
1088 cursor_position: Point,
1089 scroll_anchor: ScrollAnchor,
1090 scroll_top_row: u32,
1091}
1092
1093#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1094pub enum GotoDefinitionKind {
1095 Symbol,
1096 Declaration,
1097 Type,
1098 Implementation,
1099}
1100
1101#[derive(Debug, Clone)]
1102enum InlayHintRefreshReason {
1103 ModifiersChanged(bool),
1104 Toggle(bool),
1105 SettingsChange(InlayHintSettings),
1106 NewLinesShown,
1107 BufferEdited(HashSet<Arc<Language>>),
1108 RefreshRequested,
1109 ExcerptsRemoved(Vec<ExcerptId>),
1110}
1111
1112impl InlayHintRefreshReason {
1113 fn description(&self) -> &'static str {
1114 match self {
1115 Self::ModifiersChanged(_) => "modifiers changed",
1116 Self::Toggle(_) => "toggle",
1117 Self::SettingsChange(_) => "settings change",
1118 Self::NewLinesShown => "new lines shown",
1119 Self::BufferEdited(_) => "buffer edited",
1120 Self::RefreshRequested => "refresh requested",
1121 Self::ExcerptsRemoved(_) => "excerpts removed",
1122 }
1123 }
1124}
1125
1126pub enum FormatTarget {
1127 Buffers,
1128 Ranges(Vec<Range<MultiBufferPoint>>),
1129}
1130
1131pub(crate) struct FocusedBlock {
1132 id: BlockId,
1133 focus_handle: WeakFocusHandle,
1134}
1135
1136#[derive(Clone)]
1137enum JumpData {
1138 MultiBufferRow {
1139 row: MultiBufferRow,
1140 line_offset_from_top: u32,
1141 },
1142 MultiBufferPoint {
1143 excerpt_id: ExcerptId,
1144 position: Point,
1145 anchor: text::Anchor,
1146 line_offset_from_top: u32,
1147 },
1148}
1149
1150pub enum MultibufferSelectionMode {
1151 First,
1152 All,
1153}
1154
1155#[derive(Clone, Copy, Debug, Default)]
1156pub struct RewrapOptions {
1157 pub override_language_settings: bool,
1158 pub preserve_existing_whitespace: bool,
1159}
1160
1161impl Editor {
1162 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1163 let buffer = cx.new(|cx| Buffer::local("", cx));
1164 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1165 Self::new(
1166 EditorMode::SingleLine { auto_width: false },
1167 buffer,
1168 None,
1169 window,
1170 cx,
1171 )
1172 }
1173
1174 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1175 let buffer = cx.new(|cx| Buffer::local("", cx));
1176 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1177 Self::new(EditorMode::Full, buffer, None, window, cx)
1178 }
1179
1180 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1181 let buffer = cx.new(|cx| Buffer::local("", cx));
1182 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1183 Self::new(
1184 EditorMode::SingleLine { auto_width: true },
1185 buffer,
1186 None,
1187 window,
1188 cx,
1189 )
1190 }
1191
1192 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1193 let buffer = cx.new(|cx| Buffer::local("", cx));
1194 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1195 Self::new(
1196 EditorMode::AutoHeight { max_lines },
1197 buffer,
1198 None,
1199 window,
1200 cx,
1201 )
1202 }
1203
1204 pub fn for_buffer(
1205 buffer: Entity<Buffer>,
1206 project: Option<Entity<Project>>,
1207 window: &mut Window,
1208 cx: &mut Context<Self>,
1209 ) -> Self {
1210 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1211 Self::new(EditorMode::Full, buffer, project, window, cx)
1212 }
1213
1214 pub fn for_multibuffer(
1215 buffer: Entity<MultiBuffer>,
1216 project: Option<Entity<Project>>,
1217 window: &mut Window,
1218 cx: &mut Context<Self>,
1219 ) -> Self {
1220 Self::new(EditorMode::Full, buffer, project, window, cx)
1221 }
1222
1223 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1224 let mut clone = Self::new(
1225 self.mode,
1226 self.buffer.clone(),
1227 self.project.clone(),
1228 window,
1229 cx,
1230 );
1231 self.display_map.update(cx, |display_map, cx| {
1232 let snapshot = display_map.snapshot(cx);
1233 clone.display_map.update(cx, |display_map, cx| {
1234 display_map.set_state(&snapshot, cx);
1235 });
1236 });
1237 clone.folds_did_change(cx);
1238 clone.selections.clone_state(&self.selections);
1239 clone.scroll_manager.clone_state(&self.scroll_manager);
1240 clone.searchable = self.searchable;
1241 clone
1242 }
1243
1244 pub fn new(
1245 mode: EditorMode,
1246 buffer: Entity<MultiBuffer>,
1247 project: Option<Entity<Project>>,
1248 window: &mut Window,
1249 cx: &mut Context<Self>,
1250 ) -> Self {
1251 let style = window.text_style();
1252 let font_size = style.font_size.to_pixels(window.rem_size());
1253 let editor = cx.entity().downgrade();
1254 let fold_placeholder = FoldPlaceholder {
1255 constrain_width: true,
1256 render: Arc::new(move |fold_id, fold_range, cx| {
1257 let editor = editor.clone();
1258 div()
1259 .id(fold_id)
1260 .bg(cx.theme().colors().ghost_element_background)
1261 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1262 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1263 .rounded_xs()
1264 .size_full()
1265 .cursor_pointer()
1266 .child("⋯")
1267 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1268 .on_click(move |_, _window, cx| {
1269 editor
1270 .update(cx, |editor, cx| {
1271 editor.unfold_ranges(
1272 &[fold_range.start..fold_range.end],
1273 true,
1274 false,
1275 cx,
1276 );
1277 cx.stop_propagation();
1278 })
1279 .ok();
1280 })
1281 .into_any()
1282 }),
1283 merge_adjacent: true,
1284 ..Default::default()
1285 };
1286 let display_map = cx.new(|cx| {
1287 DisplayMap::new(
1288 buffer.clone(),
1289 style.font(),
1290 font_size,
1291 None,
1292 FILE_HEADER_HEIGHT,
1293 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1294 fold_placeholder,
1295 cx,
1296 )
1297 });
1298
1299 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1300
1301 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1302
1303 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1304 .then(|| language_settings::SoftWrap::None);
1305
1306 let mut project_subscriptions = Vec::new();
1307 if mode == EditorMode::Full {
1308 if let Some(project) = project.as_ref() {
1309 project_subscriptions.push(cx.subscribe_in(
1310 project,
1311 window,
1312 |editor, _, event, window, cx| match event {
1313 project::Event::RefreshCodeLens => {
1314 // we always query lens with actions, without storing them, always refreshing them
1315 }
1316 project::Event::RefreshInlayHints => {
1317 editor
1318 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1319 }
1320 project::Event::SnippetEdit(id, snippet_edits) => {
1321 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1322 let focus_handle = editor.focus_handle(cx);
1323 if focus_handle.is_focused(window) {
1324 let snapshot = buffer.read(cx).snapshot();
1325 for (range, snippet) in snippet_edits {
1326 let editor_range =
1327 language::range_from_lsp(*range).to_offset(&snapshot);
1328 editor
1329 .insert_snippet(
1330 &[editor_range],
1331 snippet.clone(),
1332 window,
1333 cx,
1334 )
1335 .ok();
1336 }
1337 }
1338 }
1339 }
1340 _ => {}
1341 },
1342 ));
1343 if let Some(task_inventory) = project
1344 .read(cx)
1345 .task_store()
1346 .read(cx)
1347 .task_inventory()
1348 .cloned()
1349 {
1350 project_subscriptions.push(cx.observe_in(
1351 &task_inventory,
1352 window,
1353 |editor, _, window, cx| {
1354 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1355 },
1356 ));
1357 };
1358
1359 project_subscriptions.push(cx.subscribe_in(
1360 &project.read(cx).breakpoint_store(),
1361 window,
1362 |editor, _, event, window, cx| match event {
1363 BreakpointStoreEvent::ActiveDebugLineChanged => {
1364 editor.go_to_active_debug_line(window, cx);
1365 }
1366 _ => {}
1367 },
1368 ));
1369 }
1370 }
1371
1372 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1373
1374 let inlay_hint_settings =
1375 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1376 let focus_handle = cx.focus_handle();
1377 cx.on_focus(&focus_handle, window, Self::handle_focus)
1378 .detach();
1379 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1380 .detach();
1381 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1382 .detach();
1383 cx.on_blur(&focus_handle, window, Self::handle_blur)
1384 .detach();
1385
1386 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1387 Some(false)
1388 } else {
1389 None
1390 };
1391
1392 let breakpoint_store = match (mode, project.as_ref()) {
1393 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1394 _ => None,
1395 };
1396
1397 let mut code_action_providers = Vec::new();
1398 let mut load_uncommitted_diff = None;
1399 if let Some(project) = project.clone() {
1400 load_uncommitted_diff = Some(
1401 get_uncommitted_diff_for_buffer(
1402 &project,
1403 buffer.read(cx).all_buffers(),
1404 buffer.clone(),
1405 cx,
1406 )
1407 .shared(),
1408 );
1409 code_action_providers.push(Rc::new(project) as Rc<_>);
1410 }
1411
1412 let hide_mouse_while_typing = if !matches!(mode, EditorMode::SingleLine { .. }) {
1413 EditorSettings::get_global(cx)
1414 .hide_mouse_while_typing
1415 .unwrap_or(true)
1416 } else {
1417 false
1418 };
1419
1420 let mut this = Self {
1421 focus_handle,
1422 show_cursor_when_unfocused: false,
1423 last_focused_descendant: None,
1424 buffer: buffer.clone(),
1425 display_map: display_map.clone(),
1426 selections,
1427 scroll_manager: ScrollManager::new(cx),
1428 columnar_selection_tail: None,
1429 add_selections_state: None,
1430 select_next_state: None,
1431 select_prev_state: None,
1432 selection_history: Default::default(),
1433 autoclose_regions: Default::default(),
1434 snippet_stack: Default::default(),
1435 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
1436 ime_transaction: Default::default(),
1437 active_diagnostics: None,
1438 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1439 inline_diagnostics_update: Task::ready(()),
1440 inline_diagnostics: Vec::new(),
1441 soft_wrap_mode_override,
1442 hard_wrap: None,
1443 completion_provider: project.clone().map(|project| Box::new(project) as _),
1444 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1445 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1446 project,
1447 blink_manager: blink_manager.clone(),
1448 show_local_selections: true,
1449 show_scrollbars: true,
1450 mode,
1451 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1452 show_gutter: mode == EditorMode::Full,
1453 show_line_numbers: None,
1454 use_relative_line_numbers: None,
1455 show_git_diff_gutter: None,
1456 show_code_actions: None,
1457 show_runnables: None,
1458 show_breakpoints: None,
1459 show_wrap_guides: None,
1460 show_indent_guides,
1461 placeholder_text: None,
1462 highlight_order: 0,
1463 highlighted_rows: HashMap::default(),
1464 background_highlights: Default::default(),
1465 gutter_highlights: TreeMap::default(),
1466 scrollbar_marker_state: ScrollbarMarkerState::default(),
1467 active_indent_guides_state: ActiveIndentGuidesState::default(),
1468 nav_history: None,
1469 context_menu: RefCell::new(None),
1470 context_menu_options: None,
1471 mouse_context_menu: None,
1472 completion_tasks: Default::default(),
1473 signature_help_state: SignatureHelpState::default(),
1474 auto_signature_help: None,
1475 find_all_references_task_sources: Vec::new(),
1476 next_completion_id: 0,
1477 next_inlay_id: 0,
1478 code_action_providers,
1479 available_code_actions: Default::default(),
1480 code_actions_task: Default::default(),
1481 selection_highlight_task: Default::default(),
1482 document_highlights_task: Default::default(),
1483 linked_editing_range_task: Default::default(),
1484 pending_rename: Default::default(),
1485 searchable: true,
1486 cursor_shape: EditorSettings::get_global(cx)
1487 .cursor_shape
1488 .unwrap_or_default(),
1489 current_line_highlight: None,
1490 autoindent_mode: Some(AutoindentMode::EachLine),
1491 collapse_matches: false,
1492 workspace: None,
1493 input_enabled: true,
1494 use_modal_editing: mode == EditorMode::Full,
1495 read_only: false,
1496 use_autoclose: true,
1497 use_auto_surround: true,
1498 auto_replace_emoji_shortcode: false,
1499 jsx_tag_auto_close_enabled_in_any_buffer: false,
1500 leader_peer_id: None,
1501 remote_id: None,
1502 hover_state: Default::default(),
1503 pending_mouse_down: None,
1504 hovered_link_state: Default::default(),
1505 edit_prediction_provider: None,
1506 active_inline_completion: None,
1507 stale_inline_completion_in_menu: None,
1508 edit_prediction_preview: EditPredictionPreview::Inactive {
1509 released_too_fast: false,
1510 },
1511 inline_diagnostics_enabled: mode == EditorMode::Full,
1512 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1513
1514 gutter_hovered: false,
1515 pixel_position_of_newest_cursor: None,
1516 last_bounds: None,
1517 last_position_map: None,
1518 expect_bounds_change: None,
1519 gutter_dimensions: GutterDimensions::default(),
1520 style: None,
1521 show_cursor_names: false,
1522 hovered_cursors: Default::default(),
1523 next_editor_action_id: EditorActionId::default(),
1524 editor_actions: Rc::default(),
1525 inline_completions_hidden_for_vim_mode: false,
1526 show_inline_completions_override: None,
1527 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1528 edit_prediction_settings: EditPredictionSettings::Disabled,
1529 edit_prediction_indent_conflict: false,
1530 edit_prediction_requires_modifier_in_indent_conflict: true,
1531 custom_context_menu: None,
1532 show_git_blame_gutter: false,
1533 show_git_blame_inline: false,
1534 show_selection_menu: None,
1535 show_git_blame_inline_delay_task: None,
1536 git_blame_inline_tooltip: None,
1537 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1538 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1539 .session
1540 .restore_unsaved_buffers,
1541 blame: None,
1542 blame_subscription: None,
1543 tasks: Default::default(),
1544
1545 breakpoint_store,
1546 gutter_breakpoint_indicator: None,
1547 _subscriptions: vec![
1548 cx.observe(&buffer, Self::on_buffer_changed),
1549 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1550 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1551 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1552 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1553 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1554 cx.observe_window_activation(window, |editor, window, cx| {
1555 let active = window.is_window_active();
1556 editor.blink_manager.update(cx, |blink_manager, cx| {
1557 if active {
1558 blink_manager.enable(cx);
1559 } else {
1560 blink_manager.disable(cx);
1561 }
1562 });
1563 }),
1564 ],
1565 tasks_update_task: None,
1566 linked_edit_ranges: Default::default(),
1567 in_project_search: false,
1568 previous_search_ranges: None,
1569 breadcrumb_header: None,
1570 focused_block: None,
1571 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1572 addons: HashMap::default(),
1573 registered_buffers: HashMap::default(),
1574 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1575 selection_mark_mode: false,
1576 toggle_fold_multiple_buffers: Task::ready(()),
1577 serialize_selections: Task::ready(()),
1578 serialize_folds: Task::ready(()),
1579 text_style_refinement: None,
1580 load_diff_task: load_uncommitted_diff,
1581 mouse_cursor_hidden: false,
1582 hide_mouse_while_typing,
1583 };
1584 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1585 this._subscriptions
1586 .push(cx.observe(breakpoints, |_, _, cx| {
1587 cx.notify();
1588 }));
1589 }
1590 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1591 this._subscriptions.extend(project_subscriptions);
1592
1593 this.end_selection(window, cx);
1594 this.scroll_manager.show_scrollbars(window, cx);
1595 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1596
1597 if mode == EditorMode::Full {
1598 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1599 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1600
1601 if this.git_blame_inline_enabled {
1602 this.git_blame_inline_enabled = true;
1603 this.start_git_blame_inline(false, window, cx);
1604 }
1605
1606 this.go_to_active_debug_line(window, cx);
1607
1608 if let Some(buffer) = buffer.read(cx).as_singleton() {
1609 if let Some(project) = this.project.as_ref() {
1610 let handle = project.update(cx, |project, cx| {
1611 project.register_buffer_with_language_servers(&buffer, cx)
1612 });
1613 this.registered_buffers
1614 .insert(buffer.read(cx).remote_id(), handle);
1615 }
1616 }
1617 }
1618
1619 this.report_editor_event("Editor Opened", None, cx);
1620 this
1621 }
1622
1623 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1624 self.mouse_context_menu
1625 .as_ref()
1626 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1627 }
1628
1629 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1630 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1631 }
1632
1633 fn key_context_internal(
1634 &self,
1635 has_active_edit_prediction: bool,
1636 window: &Window,
1637 cx: &App,
1638 ) -> KeyContext {
1639 let mut key_context = KeyContext::new_with_defaults();
1640 key_context.add("Editor");
1641 let mode = match self.mode {
1642 EditorMode::SingleLine { .. } => "single_line",
1643 EditorMode::AutoHeight { .. } => "auto_height",
1644 EditorMode::Full => "full",
1645 };
1646
1647 if EditorSettings::jupyter_enabled(cx) {
1648 key_context.add("jupyter");
1649 }
1650
1651 key_context.set("mode", mode);
1652 if self.pending_rename.is_some() {
1653 key_context.add("renaming");
1654 }
1655
1656 match self.context_menu.borrow().as_ref() {
1657 Some(CodeContextMenu::Completions(_)) => {
1658 key_context.add("menu");
1659 key_context.add("showing_completions");
1660 }
1661 Some(CodeContextMenu::CodeActions(_)) => {
1662 key_context.add("menu");
1663 key_context.add("showing_code_actions")
1664 }
1665 None => {}
1666 }
1667
1668 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1669 if !self.focus_handle(cx).contains_focused(window, cx)
1670 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1671 {
1672 for addon in self.addons.values() {
1673 addon.extend_key_context(&mut key_context, cx)
1674 }
1675 }
1676
1677 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1678 if let Some(extension) = singleton_buffer
1679 .read(cx)
1680 .file()
1681 .and_then(|file| file.path().extension()?.to_str())
1682 {
1683 key_context.set("extension", extension.to_string());
1684 }
1685 } else {
1686 key_context.add("multibuffer");
1687 }
1688
1689 if has_active_edit_prediction {
1690 if self.edit_prediction_in_conflict() {
1691 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1692 } else {
1693 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1694 key_context.add("copilot_suggestion");
1695 }
1696 }
1697
1698 if self.selection_mark_mode {
1699 key_context.add("selection_mode");
1700 }
1701
1702 key_context
1703 }
1704
1705 pub fn edit_prediction_in_conflict(&self) -> bool {
1706 if !self.show_edit_predictions_in_menu() {
1707 return false;
1708 }
1709
1710 let showing_completions = self
1711 .context_menu
1712 .borrow()
1713 .as_ref()
1714 .map_or(false, |context| {
1715 matches!(context, CodeContextMenu::Completions(_))
1716 });
1717
1718 showing_completions
1719 || self.edit_prediction_requires_modifier()
1720 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1721 // bindings to insert tab characters.
1722 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1723 }
1724
1725 pub fn accept_edit_prediction_keybind(
1726 &self,
1727 window: &Window,
1728 cx: &App,
1729 ) -> AcceptEditPredictionBinding {
1730 let key_context = self.key_context_internal(true, window, cx);
1731 let in_conflict = self.edit_prediction_in_conflict();
1732
1733 AcceptEditPredictionBinding(
1734 window
1735 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1736 .into_iter()
1737 .filter(|binding| {
1738 !in_conflict
1739 || binding
1740 .keystrokes()
1741 .first()
1742 .map_or(false, |keystroke| keystroke.modifiers.modified())
1743 })
1744 .rev()
1745 .min_by_key(|binding| {
1746 binding
1747 .keystrokes()
1748 .first()
1749 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1750 }),
1751 )
1752 }
1753
1754 pub fn new_file(
1755 workspace: &mut Workspace,
1756 _: &workspace::NewFile,
1757 window: &mut Window,
1758 cx: &mut Context<Workspace>,
1759 ) {
1760 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1761 "Failed to create buffer",
1762 window,
1763 cx,
1764 |e, _, _| match e.error_code() {
1765 ErrorCode::RemoteUpgradeRequired => Some(format!(
1766 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1767 e.error_tag("required").unwrap_or("the latest version")
1768 )),
1769 _ => None,
1770 },
1771 );
1772 }
1773
1774 pub fn new_in_workspace(
1775 workspace: &mut Workspace,
1776 window: &mut Window,
1777 cx: &mut Context<Workspace>,
1778 ) -> Task<Result<Entity<Editor>>> {
1779 let project = workspace.project().clone();
1780 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1781
1782 cx.spawn_in(window, async move |workspace, cx| {
1783 let buffer = create.await?;
1784 workspace.update_in(cx, |workspace, window, cx| {
1785 let editor =
1786 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1787 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1788 editor
1789 })
1790 })
1791 }
1792
1793 fn new_file_vertical(
1794 workspace: &mut Workspace,
1795 _: &workspace::NewFileSplitVertical,
1796 window: &mut Window,
1797 cx: &mut Context<Workspace>,
1798 ) {
1799 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1800 }
1801
1802 fn new_file_horizontal(
1803 workspace: &mut Workspace,
1804 _: &workspace::NewFileSplitHorizontal,
1805 window: &mut Window,
1806 cx: &mut Context<Workspace>,
1807 ) {
1808 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1809 }
1810
1811 fn new_file_in_direction(
1812 workspace: &mut Workspace,
1813 direction: SplitDirection,
1814 window: &mut Window,
1815 cx: &mut Context<Workspace>,
1816 ) {
1817 let project = workspace.project().clone();
1818 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1819
1820 cx.spawn_in(window, async move |workspace, cx| {
1821 let buffer = create.await?;
1822 workspace.update_in(cx, move |workspace, window, cx| {
1823 workspace.split_item(
1824 direction,
1825 Box::new(
1826 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1827 ),
1828 window,
1829 cx,
1830 )
1831 })?;
1832 anyhow::Ok(())
1833 })
1834 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1835 match e.error_code() {
1836 ErrorCode::RemoteUpgradeRequired => Some(format!(
1837 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1838 e.error_tag("required").unwrap_or("the latest version")
1839 )),
1840 _ => None,
1841 }
1842 });
1843 }
1844
1845 pub fn leader_peer_id(&self) -> Option<PeerId> {
1846 self.leader_peer_id
1847 }
1848
1849 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1850 &self.buffer
1851 }
1852
1853 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1854 self.workspace.as_ref()?.0.upgrade()
1855 }
1856
1857 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1858 self.buffer().read(cx).title(cx)
1859 }
1860
1861 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1862 let git_blame_gutter_max_author_length = self
1863 .render_git_blame_gutter(cx)
1864 .then(|| {
1865 if let Some(blame) = self.blame.as_ref() {
1866 let max_author_length =
1867 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1868 Some(max_author_length)
1869 } else {
1870 None
1871 }
1872 })
1873 .flatten();
1874
1875 EditorSnapshot {
1876 mode: self.mode,
1877 show_gutter: self.show_gutter,
1878 show_line_numbers: self.show_line_numbers,
1879 show_git_diff_gutter: self.show_git_diff_gutter,
1880 show_code_actions: self.show_code_actions,
1881 show_runnables: self.show_runnables,
1882 show_breakpoints: self.show_breakpoints,
1883 git_blame_gutter_max_author_length,
1884 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1885 scroll_anchor: self.scroll_manager.anchor(),
1886 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1887 placeholder_text: self.placeholder_text.clone(),
1888 is_focused: self.focus_handle.is_focused(window),
1889 current_line_highlight: self
1890 .current_line_highlight
1891 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1892 gutter_hovered: self.gutter_hovered,
1893 }
1894 }
1895
1896 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1897 self.buffer.read(cx).language_at(point, cx)
1898 }
1899
1900 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1901 self.buffer.read(cx).read(cx).file_at(point).cloned()
1902 }
1903
1904 pub fn active_excerpt(
1905 &self,
1906 cx: &App,
1907 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1908 self.buffer
1909 .read(cx)
1910 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1911 }
1912
1913 pub fn mode(&self) -> EditorMode {
1914 self.mode
1915 }
1916
1917 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1918 self.collaboration_hub.as_deref()
1919 }
1920
1921 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1922 self.collaboration_hub = Some(hub);
1923 }
1924
1925 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1926 self.in_project_search = in_project_search;
1927 }
1928
1929 pub fn set_custom_context_menu(
1930 &mut self,
1931 f: impl 'static
1932 + Fn(
1933 &mut Self,
1934 DisplayPoint,
1935 &mut Window,
1936 &mut Context<Self>,
1937 ) -> Option<Entity<ui::ContextMenu>>,
1938 ) {
1939 self.custom_context_menu = Some(Box::new(f))
1940 }
1941
1942 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1943 self.completion_provider = provider;
1944 }
1945
1946 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1947 self.semantics_provider.clone()
1948 }
1949
1950 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1951 self.semantics_provider = provider;
1952 }
1953
1954 pub fn set_edit_prediction_provider<T>(
1955 &mut self,
1956 provider: Option<Entity<T>>,
1957 window: &mut Window,
1958 cx: &mut Context<Self>,
1959 ) where
1960 T: EditPredictionProvider,
1961 {
1962 self.edit_prediction_provider =
1963 provider.map(|provider| RegisteredInlineCompletionProvider {
1964 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1965 if this.focus_handle.is_focused(window) {
1966 this.update_visible_inline_completion(window, cx);
1967 }
1968 }),
1969 provider: Arc::new(provider),
1970 });
1971 self.update_edit_prediction_settings(cx);
1972 self.refresh_inline_completion(false, false, window, cx);
1973 }
1974
1975 pub fn placeholder_text(&self) -> Option<&str> {
1976 self.placeholder_text.as_deref()
1977 }
1978
1979 pub fn set_placeholder_text(
1980 &mut self,
1981 placeholder_text: impl Into<Arc<str>>,
1982 cx: &mut Context<Self>,
1983 ) {
1984 let placeholder_text = Some(placeholder_text.into());
1985 if self.placeholder_text != placeholder_text {
1986 self.placeholder_text = placeholder_text;
1987 cx.notify();
1988 }
1989 }
1990
1991 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1992 self.cursor_shape = cursor_shape;
1993
1994 // Disrupt blink for immediate user feedback that the cursor shape has changed
1995 self.blink_manager.update(cx, BlinkManager::show_cursor);
1996
1997 cx.notify();
1998 }
1999
2000 pub fn set_current_line_highlight(
2001 &mut self,
2002 current_line_highlight: Option<CurrentLineHighlight>,
2003 ) {
2004 self.current_line_highlight = current_line_highlight;
2005 }
2006
2007 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2008 self.collapse_matches = collapse_matches;
2009 }
2010
2011 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2012 let buffers = self.buffer.read(cx).all_buffers();
2013 let Some(project) = self.project.as_ref() else {
2014 return;
2015 };
2016 project.update(cx, |project, cx| {
2017 for buffer in buffers {
2018 self.registered_buffers
2019 .entry(buffer.read(cx).remote_id())
2020 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2021 }
2022 })
2023 }
2024
2025 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2026 if self.collapse_matches {
2027 return range.start..range.start;
2028 }
2029 range.clone()
2030 }
2031
2032 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2033 if self.display_map.read(cx).clip_at_line_ends != clip {
2034 self.display_map
2035 .update(cx, |map, _| map.clip_at_line_ends = clip);
2036 }
2037 }
2038
2039 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2040 self.input_enabled = input_enabled;
2041 }
2042
2043 pub fn set_inline_completions_hidden_for_vim_mode(
2044 &mut self,
2045 hidden: bool,
2046 window: &mut Window,
2047 cx: &mut Context<Self>,
2048 ) {
2049 if hidden != self.inline_completions_hidden_for_vim_mode {
2050 self.inline_completions_hidden_for_vim_mode = hidden;
2051 if hidden {
2052 self.update_visible_inline_completion(window, cx);
2053 } else {
2054 self.refresh_inline_completion(true, false, window, cx);
2055 }
2056 }
2057 }
2058
2059 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2060 self.menu_inline_completions_policy = value;
2061 }
2062
2063 pub fn set_autoindent(&mut self, autoindent: bool) {
2064 if autoindent {
2065 self.autoindent_mode = Some(AutoindentMode::EachLine);
2066 } else {
2067 self.autoindent_mode = None;
2068 }
2069 }
2070
2071 pub fn read_only(&self, cx: &App) -> bool {
2072 self.read_only || self.buffer.read(cx).read_only()
2073 }
2074
2075 pub fn set_read_only(&mut self, read_only: bool) {
2076 self.read_only = read_only;
2077 }
2078
2079 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2080 self.use_autoclose = autoclose;
2081 }
2082
2083 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2084 self.use_auto_surround = auto_surround;
2085 }
2086
2087 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2088 self.auto_replace_emoji_shortcode = auto_replace;
2089 }
2090
2091 pub fn toggle_edit_predictions(
2092 &mut self,
2093 _: &ToggleEditPrediction,
2094 window: &mut Window,
2095 cx: &mut Context<Self>,
2096 ) {
2097 if self.show_inline_completions_override.is_some() {
2098 self.set_show_edit_predictions(None, window, cx);
2099 } else {
2100 let show_edit_predictions = !self.edit_predictions_enabled();
2101 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2102 }
2103 }
2104
2105 pub fn set_show_edit_predictions(
2106 &mut self,
2107 show_edit_predictions: Option<bool>,
2108 window: &mut Window,
2109 cx: &mut Context<Self>,
2110 ) {
2111 self.show_inline_completions_override = show_edit_predictions;
2112 self.update_edit_prediction_settings(cx);
2113
2114 if let Some(false) = show_edit_predictions {
2115 self.discard_inline_completion(false, cx);
2116 } else {
2117 self.refresh_inline_completion(false, true, window, cx);
2118 }
2119 }
2120
2121 fn inline_completions_disabled_in_scope(
2122 &self,
2123 buffer: &Entity<Buffer>,
2124 buffer_position: language::Anchor,
2125 cx: &App,
2126 ) -> bool {
2127 let snapshot = buffer.read(cx).snapshot();
2128 let settings = snapshot.settings_at(buffer_position, cx);
2129
2130 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2131 return false;
2132 };
2133
2134 scope.override_name().map_or(false, |scope_name| {
2135 settings
2136 .edit_predictions_disabled_in
2137 .iter()
2138 .any(|s| s == scope_name)
2139 })
2140 }
2141
2142 pub fn set_use_modal_editing(&mut self, to: bool) {
2143 self.use_modal_editing = to;
2144 }
2145
2146 pub fn use_modal_editing(&self) -> bool {
2147 self.use_modal_editing
2148 }
2149
2150 fn selections_did_change(
2151 &mut self,
2152 local: bool,
2153 old_cursor_position: &Anchor,
2154 show_completions: bool,
2155 window: &mut Window,
2156 cx: &mut Context<Self>,
2157 ) {
2158 window.invalidate_character_coordinates();
2159
2160 // Copy selections to primary selection buffer
2161 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2162 if local {
2163 let selections = self.selections.all::<usize>(cx);
2164 let buffer_handle = self.buffer.read(cx).read(cx);
2165
2166 let mut text = String::new();
2167 for (index, selection) in selections.iter().enumerate() {
2168 let text_for_selection = buffer_handle
2169 .text_for_range(selection.start..selection.end)
2170 .collect::<String>();
2171
2172 text.push_str(&text_for_selection);
2173 if index != selections.len() - 1 {
2174 text.push('\n');
2175 }
2176 }
2177
2178 if !text.is_empty() {
2179 cx.write_to_primary(ClipboardItem::new_string(text));
2180 }
2181 }
2182
2183 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2184 self.buffer.update(cx, |buffer, cx| {
2185 buffer.set_active_selections(
2186 &self.selections.disjoint_anchors(),
2187 self.selections.line_mode,
2188 self.cursor_shape,
2189 cx,
2190 )
2191 });
2192 }
2193 let display_map = self
2194 .display_map
2195 .update(cx, |display_map, cx| display_map.snapshot(cx));
2196 let buffer = &display_map.buffer_snapshot;
2197 self.add_selections_state = None;
2198 self.select_next_state = None;
2199 self.select_prev_state = None;
2200 self.select_syntax_node_history.try_clear();
2201 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2202 self.snippet_stack
2203 .invalidate(&self.selections.disjoint_anchors(), buffer);
2204 self.take_rename(false, window, cx);
2205
2206 let new_cursor_position = self.selections.newest_anchor().head();
2207
2208 self.push_to_nav_history(
2209 *old_cursor_position,
2210 Some(new_cursor_position.to_point(buffer)),
2211 false,
2212 cx,
2213 );
2214
2215 if local {
2216 let new_cursor_position = self.selections.newest_anchor().head();
2217 let mut context_menu = self.context_menu.borrow_mut();
2218 let completion_menu = match context_menu.as_ref() {
2219 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2220 _ => {
2221 *context_menu = None;
2222 None
2223 }
2224 };
2225 if let Some(buffer_id) = new_cursor_position.buffer_id {
2226 if !self.registered_buffers.contains_key(&buffer_id) {
2227 if let Some(project) = self.project.as_ref() {
2228 project.update(cx, |project, cx| {
2229 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2230 return;
2231 };
2232 self.registered_buffers.insert(
2233 buffer_id,
2234 project.register_buffer_with_language_servers(&buffer, cx),
2235 );
2236 })
2237 }
2238 }
2239 }
2240
2241 if let Some(completion_menu) = completion_menu {
2242 let cursor_position = new_cursor_position.to_offset(buffer);
2243 let (word_range, kind) =
2244 buffer.surrounding_word(completion_menu.initial_position, true);
2245 if kind == Some(CharKind::Word)
2246 && word_range.to_inclusive().contains(&cursor_position)
2247 {
2248 let mut completion_menu = completion_menu.clone();
2249 drop(context_menu);
2250
2251 let query = Self::completion_query(buffer, cursor_position);
2252 cx.spawn(async move |this, cx| {
2253 completion_menu
2254 .filter(query.as_deref(), cx.background_executor().clone())
2255 .await;
2256
2257 this.update(cx, |this, cx| {
2258 let mut context_menu = this.context_menu.borrow_mut();
2259 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2260 else {
2261 return;
2262 };
2263
2264 if menu.id > completion_menu.id {
2265 return;
2266 }
2267
2268 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2269 drop(context_menu);
2270 cx.notify();
2271 })
2272 })
2273 .detach();
2274
2275 if show_completions {
2276 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2277 }
2278 } else {
2279 drop(context_menu);
2280 self.hide_context_menu(window, cx);
2281 }
2282 } else {
2283 drop(context_menu);
2284 }
2285
2286 hide_hover(self, cx);
2287
2288 if old_cursor_position.to_display_point(&display_map).row()
2289 != new_cursor_position.to_display_point(&display_map).row()
2290 {
2291 self.available_code_actions.take();
2292 }
2293 self.refresh_code_actions(window, cx);
2294 self.refresh_document_highlights(cx);
2295 self.refresh_selected_text_highlights(window, cx);
2296 refresh_matching_bracket_highlights(self, window, cx);
2297 self.update_visible_inline_completion(window, cx);
2298 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2299 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2300 if self.git_blame_inline_enabled {
2301 self.start_inline_blame_timer(window, cx);
2302 }
2303 }
2304
2305 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2306 cx.emit(EditorEvent::SelectionsChanged { local });
2307
2308 let selections = &self.selections.disjoint;
2309 if selections.len() == 1 {
2310 cx.emit(SearchEvent::ActiveMatchChanged)
2311 }
2312 if local
2313 && self.is_singleton(cx)
2314 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2315 {
2316 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2317 let background_executor = cx.background_executor().clone();
2318 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2319 let snapshot = self.buffer().read(cx).snapshot(cx);
2320 let selections = selections.clone();
2321 self.serialize_selections = cx.background_spawn(async move {
2322 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2323 let selections = selections
2324 .iter()
2325 .map(|selection| {
2326 (
2327 selection.start.to_offset(&snapshot),
2328 selection.end.to_offset(&snapshot),
2329 )
2330 })
2331 .collect();
2332
2333 DB.save_editor_selections(editor_id, workspace_id, selections)
2334 .await
2335 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2336 .log_err();
2337 });
2338 }
2339 }
2340
2341 cx.notify();
2342 }
2343
2344 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
2345 if !self.is_singleton(cx)
2346 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
2347 {
2348 return;
2349 }
2350
2351 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
2352 return;
2353 };
2354 let background_executor = cx.background_executor().clone();
2355 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2356 let snapshot = self.buffer().read(cx).snapshot(cx);
2357 let folds = self.display_map.update(cx, |display_map, cx| {
2358 display_map
2359 .snapshot(cx)
2360 .folds_in_range(0..snapshot.len())
2361 .map(|fold| {
2362 (
2363 fold.range.start.to_offset(&snapshot),
2364 fold.range.end.to_offset(&snapshot),
2365 )
2366 })
2367 .collect()
2368 });
2369 self.serialize_folds = cx.background_spawn(async move {
2370 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2371 DB.save_editor_folds(editor_id, workspace_id, folds)
2372 .await
2373 .with_context(|| format!("persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"))
2374 .log_err();
2375 });
2376 }
2377
2378 pub fn sync_selections(
2379 &mut self,
2380 other: Entity<Editor>,
2381 cx: &mut Context<Self>,
2382 ) -> gpui::Subscription {
2383 let other_selections = other.read(cx).selections.disjoint.to_vec();
2384 self.selections.change_with(cx, |selections| {
2385 selections.select_anchors(other_selections);
2386 });
2387
2388 let other_subscription =
2389 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2390 EditorEvent::SelectionsChanged { local: true } => {
2391 let other_selections = other.read(cx).selections.disjoint.to_vec();
2392 if other_selections.is_empty() {
2393 return;
2394 }
2395 this.selections.change_with(cx, |selections| {
2396 selections.select_anchors(other_selections);
2397 });
2398 }
2399 _ => {}
2400 });
2401
2402 let this_subscription =
2403 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2404 EditorEvent::SelectionsChanged { local: true } => {
2405 let these_selections = this.selections.disjoint.to_vec();
2406 if these_selections.is_empty() {
2407 return;
2408 }
2409 other.update(cx, |other_editor, cx| {
2410 other_editor.selections.change_with(cx, |selections| {
2411 selections.select_anchors(these_selections);
2412 })
2413 });
2414 }
2415 _ => {}
2416 });
2417
2418 Subscription::join(other_subscription, this_subscription)
2419 }
2420
2421 pub fn change_selections<R>(
2422 &mut self,
2423 autoscroll: Option<Autoscroll>,
2424 window: &mut Window,
2425 cx: &mut Context<Self>,
2426 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2427 ) -> R {
2428 self.change_selections_inner(autoscroll, true, window, cx, change)
2429 }
2430
2431 fn change_selections_inner<R>(
2432 &mut self,
2433 autoscroll: Option<Autoscroll>,
2434 request_completions: bool,
2435 window: &mut Window,
2436 cx: &mut Context<Self>,
2437 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2438 ) -> R {
2439 let old_cursor_position = self.selections.newest_anchor().head();
2440 self.push_to_selection_history();
2441
2442 let (changed, result) = self.selections.change_with(cx, change);
2443
2444 if changed {
2445 if let Some(autoscroll) = autoscroll {
2446 self.request_autoscroll(autoscroll, cx);
2447 }
2448 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2449
2450 if self.should_open_signature_help_automatically(
2451 &old_cursor_position,
2452 self.signature_help_state.backspace_pressed(),
2453 cx,
2454 ) {
2455 self.show_signature_help(&ShowSignatureHelp, window, cx);
2456 }
2457 self.signature_help_state.set_backspace_pressed(false);
2458 }
2459
2460 result
2461 }
2462
2463 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2464 where
2465 I: IntoIterator<Item = (Range<S>, T)>,
2466 S: ToOffset,
2467 T: Into<Arc<str>>,
2468 {
2469 if self.read_only(cx) {
2470 return;
2471 }
2472
2473 self.buffer
2474 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2475 }
2476
2477 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2478 where
2479 I: IntoIterator<Item = (Range<S>, T)>,
2480 S: ToOffset,
2481 T: Into<Arc<str>>,
2482 {
2483 if self.read_only(cx) {
2484 return;
2485 }
2486
2487 self.buffer.update(cx, |buffer, cx| {
2488 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2489 });
2490 }
2491
2492 pub fn edit_with_block_indent<I, S, T>(
2493 &mut self,
2494 edits: I,
2495 original_indent_columns: Vec<Option<u32>>,
2496 cx: &mut Context<Self>,
2497 ) where
2498 I: IntoIterator<Item = (Range<S>, T)>,
2499 S: ToOffset,
2500 T: Into<Arc<str>>,
2501 {
2502 if self.read_only(cx) {
2503 return;
2504 }
2505
2506 self.buffer.update(cx, |buffer, cx| {
2507 buffer.edit(
2508 edits,
2509 Some(AutoindentMode::Block {
2510 original_indent_columns,
2511 }),
2512 cx,
2513 )
2514 });
2515 }
2516
2517 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2518 self.hide_context_menu(window, cx);
2519
2520 match phase {
2521 SelectPhase::Begin {
2522 position,
2523 add,
2524 click_count,
2525 } => self.begin_selection(position, add, click_count, window, cx),
2526 SelectPhase::BeginColumnar {
2527 position,
2528 goal_column,
2529 reset,
2530 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2531 SelectPhase::Extend {
2532 position,
2533 click_count,
2534 } => self.extend_selection(position, click_count, window, cx),
2535 SelectPhase::Update {
2536 position,
2537 goal_column,
2538 scroll_delta,
2539 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2540 SelectPhase::End => self.end_selection(window, cx),
2541 }
2542 }
2543
2544 fn extend_selection(
2545 &mut self,
2546 position: DisplayPoint,
2547 click_count: usize,
2548 window: &mut Window,
2549 cx: &mut Context<Self>,
2550 ) {
2551 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2552 let tail = self.selections.newest::<usize>(cx).tail();
2553 self.begin_selection(position, false, click_count, window, cx);
2554
2555 let position = position.to_offset(&display_map, Bias::Left);
2556 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2557
2558 let mut pending_selection = self
2559 .selections
2560 .pending_anchor()
2561 .expect("extend_selection not called with pending selection");
2562 if position >= tail {
2563 pending_selection.start = tail_anchor;
2564 } else {
2565 pending_selection.end = tail_anchor;
2566 pending_selection.reversed = true;
2567 }
2568
2569 let mut pending_mode = self.selections.pending_mode().unwrap();
2570 match &mut pending_mode {
2571 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2572 _ => {}
2573 }
2574
2575 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2576 s.set_pending(pending_selection, pending_mode)
2577 });
2578 }
2579
2580 fn begin_selection(
2581 &mut self,
2582 position: DisplayPoint,
2583 add: bool,
2584 click_count: usize,
2585 window: &mut Window,
2586 cx: &mut Context<Self>,
2587 ) {
2588 if !self.focus_handle.is_focused(window) {
2589 self.last_focused_descendant = None;
2590 window.focus(&self.focus_handle);
2591 }
2592
2593 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2594 let buffer = &display_map.buffer_snapshot;
2595 let newest_selection = self.selections.newest_anchor().clone();
2596 let position = display_map.clip_point(position, Bias::Left);
2597
2598 let start;
2599 let end;
2600 let mode;
2601 let mut auto_scroll;
2602 match click_count {
2603 1 => {
2604 start = buffer.anchor_before(position.to_point(&display_map));
2605 end = start;
2606 mode = SelectMode::Character;
2607 auto_scroll = true;
2608 }
2609 2 => {
2610 let range = movement::surrounding_word(&display_map, position);
2611 start = buffer.anchor_before(range.start.to_point(&display_map));
2612 end = buffer.anchor_before(range.end.to_point(&display_map));
2613 mode = SelectMode::Word(start..end);
2614 auto_scroll = true;
2615 }
2616 3 => {
2617 let position = display_map
2618 .clip_point(position, Bias::Left)
2619 .to_point(&display_map);
2620 let line_start = display_map.prev_line_boundary(position).0;
2621 let next_line_start = buffer.clip_point(
2622 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2623 Bias::Left,
2624 );
2625 start = buffer.anchor_before(line_start);
2626 end = buffer.anchor_before(next_line_start);
2627 mode = SelectMode::Line(start..end);
2628 auto_scroll = true;
2629 }
2630 _ => {
2631 start = buffer.anchor_before(0);
2632 end = buffer.anchor_before(buffer.len());
2633 mode = SelectMode::All;
2634 auto_scroll = false;
2635 }
2636 }
2637 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2638
2639 let point_to_delete: Option<usize> = {
2640 let selected_points: Vec<Selection<Point>> =
2641 self.selections.disjoint_in_range(start..end, cx);
2642
2643 if !add || click_count > 1 {
2644 None
2645 } else if !selected_points.is_empty() {
2646 Some(selected_points[0].id)
2647 } else {
2648 let clicked_point_already_selected =
2649 self.selections.disjoint.iter().find(|selection| {
2650 selection.start.to_point(buffer) == start.to_point(buffer)
2651 || selection.end.to_point(buffer) == end.to_point(buffer)
2652 });
2653
2654 clicked_point_already_selected.map(|selection| selection.id)
2655 }
2656 };
2657
2658 let selections_count = self.selections.count();
2659
2660 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2661 if let Some(point_to_delete) = point_to_delete {
2662 s.delete(point_to_delete);
2663
2664 if selections_count == 1 {
2665 s.set_pending_anchor_range(start..end, mode);
2666 }
2667 } else {
2668 if !add {
2669 s.clear_disjoint();
2670 } else if click_count > 1 {
2671 s.delete(newest_selection.id)
2672 }
2673
2674 s.set_pending_anchor_range(start..end, mode);
2675 }
2676 });
2677 }
2678
2679 fn begin_columnar_selection(
2680 &mut self,
2681 position: DisplayPoint,
2682 goal_column: u32,
2683 reset: bool,
2684 window: &mut Window,
2685 cx: &mut Context<Self>,
2686 ) {
2687 if !self.focus_handle.is_focused(window) {
2688 self.last_focused_descendant = None;
2689 window.focus(&self.focus_handle);
2690 }
2691
2692 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2693
2694 if reset {
2695 let pointer_position = display_map
2696 .buffer_snapshot
2697 .anchor_before(position.to_point(&display_map));
2698
2699 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2700 s.clear_disjoint();
2701 s.set_pending_anchor_range(
2702 pointer_position..pointer_position,
2703 SelectMode::Character,
2704 );
2705 });
2706 }
2707
2708 let tail = self.selections.newest::<Point>(cx).tail();
2709 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2710
2711 if !reset {
2712 self.select_columns(
2713 tail.to_display_point(&display_map),
2714 position,
2715 goal_column,
2716 &display_map,
2717 window,
2718 cx,
2719 );
2720 }
2721 }
2722
2723 fn update_selection(
2724 &mut self,
2725 position: DisplayPoint,
2726 goal_column: u32,
2727 scroll_delta: gpui::Point<f32>,
2728 window: &mut Window,
2729 cx: &mut Context<Self>,
2730 ) {
2731 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2732
2733 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2734 let tail = tail.to_display_point(&display_map);
2735 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2736 } else if let Some(mut pending) = self.selections.pending_anchor() {
2737 let buffer = self.buffer.read(cx).snapshot(cx);
2738 let head;
2739 let tail;
2740 let mode = self.selections.pending_mode().unwrap();
2741 match &mode {
2742 SelectMode::Character => {
2743 head = position.to_point(&display_map);
2744 tail = pending.tail().to_point(&buffer);
2745 }
2746 SelectMode::Word(original_range) => {
2747 let original_display_range = original_range.start.to_display_point(&display_map)
2748 ..original_range.end.to_display_point(&display_map);
2749 let original_buffer_range = original_display_range.start.to_point(&display_map)
2750 ..original_display_range.end.to_point(&display_map);
2751 if movement::is_inside_word(&display_map, position)
2752 || original_display_range.contains(&position)
2753 {
2754 let word_range = movement::surrounding_word(&display_map, position);
2755 if word_range.start < original_display_range.start {
2756 head = word_range.start.to_point(&display_map);
2757 } else {
2758 head = word_range.end.to_point(&display_map);
2759 }
2760 } else {
2761 head = position.to_point(&display_map);
2762 }
2763
2764 if head <= original_buffer_range.start {
2765 tail = original_buffer_range.end;
2766 } else {
2767 tail = original_buffer_range.start;
2768 }
2769 }
2770 SelectMode::Line(original_range) => {
2771 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2772
2773 let position = display_map
2774 .clip_point(position, Bias::Left)
2775 .to_point(&display_map);
2776 let line_start = display_map.prev_line_boundary(position).0;
2777 let next_line_start = buffer.clip_point(
2778 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2779 Bias::Left,
2780 );
2781
2782 if line_start < original_range.start {
2783 head = line_start
2784 } else {
2785 head = next_line_start
2786 }
2787
2788 if head <= original_range.start {
2789 tail = original_range.end;
2790 } else {
2791 tail = original_range.start;
2792 }
2793 }
2794 SelectMode::All => {
2795 return;
2796 }
2797 };
2798
2799 if head < tail {
2800 pending.start = buffer.anchor_before(head);
2801 pending.end = buffer.anchor_before(tail);
2802 pending.reversed = true;
2803 } else {
2804 pending.start = buffer.anchor_before(tail);
2805 pending.end = buffer.anchor_before(head);
2806 pending.reversed = false;
2807 }
2808
2809 self.change_selections(None, window, cx, |s| {
2810 s.set_pending(pending, mode);
2811 });
2812 } else {
2813 log::error!("update_selection dispatched with no pending selection");
2814 return;
2815 }
2816
2817 self.apply_scroll_delta(scroll_delta, window, cx);
2818 cx.notify();
2819 }
2820
2821 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2822 self.columnar_selection_tail.take();
2823 if self.selections.pending_anchor().is_some() {
2824 let selections = self.selections.all::<usize>(cx);
2825 self.change_selections(None, window, cx, |s| {
2826 s.select(selections);
2827 s.clear_pending();
2828 });
2829 }
2830 }
2831
2832 fn select_columns(
2833 &mut self,
2834 tail: DisplayPoint,
2835 head: DisplayPoint,
2836 goal_column: u32,
2837 display_map: &DisplaySnapshot,
2838 window: &mut Window,
2839 cx: &mut Context<Self>,
2840 ) {
2841 let start_row = cmp::min(tail.row(), head.row());
2842 let end_row = cmp::max(tail.row(), head.row());
2843 let start_column = cmp::min(tail.column(), goal_column);
2844 let end_column = cmp::max(tail.column(), goal_column);
2845 let reversed = start_column < tail.column();
2846
2847 let selection_ranges = (start_row.0..=end_row.0)
2848 .map(DisplayRow)
2849 .filter_map(|row| {
2850 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2851 let start = display_map
2852 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2853 .to_point(display_map);
2854 let end = display_map
2855 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2856 .to_point(display_map);
2857 if reversed {
2858 Some(end..start)
2859 } else {
2860 Some(start..end)
2861 }
2862 } else {
2863 None
2864 }
2865 })
2866 .collect::<Vec<_>>();
2867
2868 self.change_selections(None, window, cx, |s| {
2869 s.select_ranges(selection_ranges);
2870 });
2871 cx.notify();
2872 }
2873
2874 pub fn has_pending_nonempty_selection(&self) -> bool {
2875 let pending_nonempty_selection = match self.selections.pending_anchor() {
2876 Some(Selection { start, end, .. }) => start != end,
2877 None => false,
2878 };
2879
2880 pending_nonempty_selection
2881 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2882 }
2883
2884 pub fn has_pending_selection(&self) -> bool {
2885 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2886 }
2887
2888 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2889 self.selection_mark_mode = false;
2890
2891 if self.clear_expanded_diff_hunks(cx) {
2892 cx.notify();
2893 return;
2894 }
2895 if self.dismiss_menus_and_popups(true, window, cx) {
2896 return;
2897 }
2898
2899 if self.mode == EditorMode::Full
2900 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2901 {
2902 return;
2903 }
2904
2905 cx.propagate();
2906 }
2907
2908 pub fn dismiss_menus_and_popups(
2909 &mut self,
2910 is_user_requested: bool,
2911 window: &mut Window,
2912 cx: &mut Context<Self>,
2913 ) -> bool {
2914 if self.take_rename(false, window, cx).is_some() {
2915 return true;
2916 }
2917
2918 if hide_hover(self, cx) {
2919 return true;
2920 }
2921
2922 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2923 return true;
2924 }
2925
2926 if self.hide_context_menu(window, cx).is_some() {
2927 return true;
2928 }
2929
2930 if self.mouse_context_menu.take().is_some() {
2931 return true;
2932 }
2933
2934 if is_user_requested && self.discard_inline_completion(true, cx) {
2935 return true;
2936 }
2937
2938 if self.snippet_stack.pop().is_some() {
2939 return true;
2940 }
2941
2942 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2943 self.dismiss_diagnostics(cx);
2944 return true;
2945 }
2946
2947 false
2948 }
2949
2950 fn linked_editing_ranges_for(
2951 &self,
2952 selection: Range<text::Anchor>,
2953 cx: &App,
2954 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2955 if self.linked_edit_ranges.is_empty() {
2956 return None;
2957 }
2958 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2959 selection.end.buffer_id.and_then(|end_buffer_id| {
2960 if selection.start.buffer_id != Some(end_buffer_id) {
2961 return None;
2962 }
2963 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2964 let snapshot = buffer.read(cx).snapshot();
2965 self.linked_edit_ranges
2966 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2967 .map(|ranges| (ranges, snapshot, buffer))
2968 })?;
2969 use text::ToOffset as TO;
2970 // find offset from the start of current range to current cursor position
2971 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2972
2973 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2974 let start_difference = start_offset - start_byte_offset;
2975 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2976 let end_difference = end_offset - start_byte_offset;
2977 // Current range has associated linked ranges.
2978 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2979 for range in linked_ranges.iter() {
2980 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2981 let end_offset = start_offset + end_difference;
2982 let start_offset = start_offset + start_difference;
2983 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2984 continue;
2985 }
2986 if self.selections.disjoint_anchor_ranges().any(|s| {
2987 if s.start.buffer_id != selection.start.buffer_id
2988 || s.end.buffer_id != selection.end.buffer_id
2989 {
2990 return false;
2991 }
2992 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2993 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2994 }) {
2995 continue;
2996 }
2997 let start = buffer_snapshot.anchor_after(start_offset);
2998 let end = buffer_snapshot.anchor_after(end_offset);
2999 linked_edits
3000 .entry(buffer.clone())
3001 .or_default()
3002 .push(start..end);
3003 }
3004 Some(linked_edits)
3005 }
3006
3007 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3008 let text: Arc<str> = text.into();
3009
3010 if self.read_only(cx) {
3011 return;
3012 }
3013
3014 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3015
3016 let selections = self.selections.all_adjusted(cx);
3017 let mut bracket_inserted = false;
3018 let mut edits = Vec::new();
3019 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3020 let mut new_selections = Vec::with_capacity(selections.len());
3021 let mut new_autoclose_regions = Vec::new();
3022 let snapshot = self.buffer.read(cx).read(cx);
3023
3024 for (selection, autoclose_region) in
3025 self.selections_with_autoclose_regions(selections, &snapshot)
3026 {
3027 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3028 // Determine if the inserted text matches the opening or closing
3029 // bracket of any of this language's bracket pairs.
3030 let mut bracket_pair = None;
3031 let mut is_bracket_pair_start = false;
3032 let mut is_bracket_pair_end = false;
3033 if !text.is_empty() {
3034 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3035 // and they are removing the character that triggered IME popup.
3036 for (pair, enabled) in scope.brackets() {
3037 if !pair.close && !pair.surround {
3038 continue;
3039 }
3040
3041 if enabled && pair.start.ends_with(text.as_ref()) {
3042 let prefix_len = pair.start.len() - text.len();
3043 let preceding_text_matches_prefix = prefix_len == 0
3044 || (selection.start.column >= (prefix_len as u32)
3045 && snapshot.contains_str_at(
3046 Point::new(
3047 selection.start.row,
3048 selection.start.column - (prefix_len as u32),
3049 ),
3050 &pair.start[..prefix_len],
3051 ));
3052 if preceding_text_matches_prefix {
3053 bracket_pair = Some(pair.clone());
3054 is_bracket_pair_start = true;
3055 break;
3056 }
3057 }
3058 if pair.end.as_str() == text.as_ref() {
3059 bracket_pair = Some(pair.clone());
3060 is_bracket_pair_end = true;
3061 break;
3062 }
3063 }
3064 }
3065
3066 if let Some(bracket_pair) = bracket_pair {
3067 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3068 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3069 let auto_surround =
3070 self.use_auto_surround && snapshot_settings.use_auto_surround;
3071 if selection.is_empty() {
3072 if is_bracket_pair_start {
3073 // If the inserted text is a suffix of an opening bracket and the
3074 // selection is preceded by the rest of the opening bracket, then
3075 // insert the closing bracket.
3076 let following_text_allows_autoclose = snapshot
3077 .chars_at(selection.start)
3078 .next()
3079 .map_or(true, |c| scope.should_autoclose_before(c));
3080
3081 let preceding_text_allows_autoclose = selection.start.column == 0
3082 || snapshot.reversed_chars_at(selection.start).next().map_or(
3083 true,
3084 |c| {
3085 bracket_pair.start != bracket_pair.end
3086 || !snapshot
3087 .char_classifier_at(selection.start)
3088 .is_word(c)
3089 },
3090 );
3091
3092 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3093 && bracket_pair.start.len() == 1
3094 {
3095 let target = bracket_pair.start.chars().next().unwrap();
3096 let current_line_count = snapshot
3097 .reversed_chars_at(selection.start)
3098 .take_while(|&c| c != '\n')
3099 .filter(|&c| c == target)
3100 .count();
3101 current_line_count % 2 == 1
3102 } else {
3103 false
3104 };
3105
3106 if autoclose
3107 && bracket_pair.close
3108 && following_text_allows_autoclose
3109 && preceding_text_allows_autoclose
3110 && !is_closing_quote
3111 {
3112 let anchor = snapshot.anchor_before(selection.end);
3113 new_selections.push((selection.map(|_| anchor), text.len()));
3114 new_autoclose_regions.push((
3115 anchor,
3116 text.len(),
3117 selection.id,
3118 bracket_pair.clone(),
3119 ));
3120 edits.push((
3121 selection.range(),
3122 format!("{}{}", text, bracket_pair.end).into(),
3123 ));
3124 bracket_inserted = true;
3125 continue;
3126 }
3127 }
3128
3129 if let Some(region) = autoclose_region {
3130 // If the selection is followed by an auto-inserted closing bracket,
3131 // then don't insert that closing bracket again; just move the selection
3132 // past the closing bracket.
3133 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3134 && text.as_ref() == region.pair.end.as_str();
3135 if should_skip {
3136 let anchor = snapshot.anchor_after(selection.end);
3137 new_selections
3138 .push((selection.map(|_| anchor), region.pair.end.len()));
3139 continue;
3140 }
3141 }
3142
3143 let always_treat_brackets_as_autoclosed = snapshot
3144 .language_settings_at(selection.start, cx)
3145 .always_treat_brackets_as_autoclosed;
3146 if always_treat_brackets_as_autoclosed
3147 && is_bracket_pair_end
3148 && snapshot.contains_str_at(selection.end, text.as_ref())
3149 {
3150 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3151 // and the inserted text is a closing bracket and the selection is followed
3152 // by the closing bracket then move the selection past the closing bracket.
3153 let anchor = snapshot.anchor_after(selection.end);
3154 new_selections.push((selection.map(|_| anchor), text.len()));
3155 continue;
3156 }
3157 }
3158 // If an opening bracket is 1 character long and is typed while
3159 // text is selected, then surround that text with the bracket pair.
3160 else if auto_surround
3161 && bracket_pair.surround
3162 && is_bracket_pair_start
3163 && bracket_pair.start.chars().count() == 1
3164 {
3165 edits.push((selection.start..selection.start, text.clone()));
3166 edits.push((
3167 selection.end..selection.end,
3168 bracket_pair.end.as_str().into(),
3169 ));
3170 bracket_inserted = true;
3171 new_selections.push((
3172 Selection {
3173 id: selection.id,
3174 start: snapshot.anchor_after(selection.start),
3175 end: snapshot.anchor_before(selection.end),
3176 reversed: selection.reversed,
3177 goal: selection.goal,
3178 },
3179 0,
3180 ));
3181 continue;
3182 }
3183 }
3184 }
3185
3186 if self.auto_replace_emoji_shortcode
3187 && selection.is_empty()
3188 && text.as_ref().ends_with(':')
3189 {
3190 if let Some(possible_emoji_short_code) =
3191 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3192 {
3193 if !possible_emoji_short_code.is_empty() {
3194 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3195 let emoji_shortcode_start = Point::new(
3196 selection.start.row,
3197 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3198 );
3199
3200 // Remove shortcode from buffer
3201 edits.push((
3202 emoji_shortcode_start..selection.start,
3203 "".to_string().into(),
3204 ));
3205 new_selections.push((
3206 Selection {
3207 id: selection.id,
3208 start: snapshot.anchor_after(emoji_shortcode_start),
3209 end: snapshot.anchor_before(selection.start),
3210 reversed: selection.reversed,
3211 goal: selection.goal,
3212 },
3213 0,
3214 ));
3215
3216 // Insert emoji
3217 let selection_start_anchor = snapshot.anchor_after(selection.start);
3218 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3219 edits.push((selection.start..selection.end, emoji.to_string().into()));
3220
3221 continue;
3222 }
3223 }
3224 }
3225 }
3226
3227 // If not handling any auto-close operation, then just replace the selected
3228 // text with the given input and move the selection to the end of the
3229 // newly inserted text.
3230 let anchor = snapshot.anchor_after(selection.end);
3231 if !self.linked_edit_ranges.is_empty() {
3232 let start_anchor = snapshot.anchor_before(selection.start);
3233
3234 let is_word_char = text.chars().next().map_or(true, |char| {
3235 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3236 classifier.is_word(char)
3237 });
3238
3239 if is_word_char {
3240 if let Some(ranges) = self
3241 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3242 {
3243 for (buffer, edits) in ranges {
3244 linked_edits
3245 .entry(buffer.clone())
3246 .or_default()
3247 .extend(edits.into_iter().map(|range| (range, text.clone())));
3248 }
3249 }
3250 }
3251 }
3252
3253 new_selections.push((selection.map(|_| anchor), 0));
3254 edits.push((selection.start..selection.end, text.clone()));
3255 }
3256
3257 drop(snapshot);
3258
3259 self.transact(window, cx, |this, window, cx| {
3260 let initial_buffer_versions =
3261 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3262
3263 this.buffer.update(cx, |buffer, cx| {
3264 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3265 });
3266 for (buffer, edits) in linked_edits {
3267 buffer.update(cx, |buffer, cx| {
3268 let snapshot = buffer.snapshot();
3269 let edits = edits
3270 .into_iter()
3271 .map(|(range, text)| {
3272 use text::ToPoint as TP;
3273 let end_point = TP::to_point(&range.end, &snapshot);
3274 let start_point = TP::to_point(&range.start, &snapshot);
3275 (start_point..end_point, text)
3276 })
3277 .sorted_by_key(|(range, _)| range.start);
3278 buffer.edit(edits, None, cx);
3279 })
3280 }
3281 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3282 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3283 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3284 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3285 .zip(new_selection_deltas)
3286 .map(|(selection, delta)| Selection {
3287 id: selection.id,
3288 start: selection.start + delta,
3289 end: selection.end + delta,
3290 reversed: selection.reversed,
3291 goal: SelectionGoal::None,
3292 })
3293 .collect::<Vec<_>>();
3294
3295 let mut i = 0;
3296 for (position, delta, selection_id, pair) in new_autoclose_regions {
3297 let position = position.to_offset(&map.buffer_snapshot) + delta;
3298 let start = map.buffer_snapshot.anchor_before(position);
3299 let end = map.buffer_snapshot.anchor_after(position);
3300 while let Some(existing_state) = this.autoclose_regions.get(i) {
3301 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3302 Ordering::Less => i += 1,
3303 Ordering::Greater => break,
3304 Ordering::Equal => {
3305 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3306 Ordering::Less => i += 1,
3307 Ordering::Equal => break,
3308 Ordering::Greater => break,
3309 }
3310 }
3311 }
3312 }
3313 this.autoclose_regions.insert(
3314 i,
3315 AutocloseRegion {
3316 selection_id,
3317 range: start..end,
3318 pair,
3319 },
3320 );
3321 }
3322
3323 let had_active_inline_completion = this.has_active_inline_completion();
3324 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3325 s.select(new_selections)
3326 });
3327
3328 if !bracket_inserted {
3329 if let Some(on_type_format_task) =
3330 this.trigger_on_type_formatting(text.to_string(), window, cx)
3331 {
3332 on_type_format_task.detach_and_log_err(cx);
3333 }
3334 }
3335
3336 let editor_settings = EditorSettings::get_global(cx);
3337 if bracket_inserted
3338 && (editor_settings.auto_signature_help
3339 || editor_settings.show_signature_help_after_edits)
3340 {
3341 this.show_signature_help(&ShowSignatureHelp, window, cx);
3342 }
3343
3344 let trigger_in_words =
3345 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3346 if this.hard_wrap.is_some() {
3347 let latest: Range<Point> = this.selections.newest(cx).range();
3348 if latest.is_empty()
3349 && this
3350 .buffer()
3351 .read(cx)
3352 .snapshot(cx)
3353 .line_len(MultiBufferRow(latest.start.row))
3354 == latest.start.column
3355 {
3356 this.rewrap_impl(
3357 RewrapOptions {
3358 override_language_settings: true,
3359 preserve_existing_whitespace: true,
3360 },
3361 cx,
3362 )
3363 }
3364 }
3365 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3366 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3367 this.refresh_inline_completion(true, false, window, cx);
3368 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3369 });
3370 }
3371
3372 fn find_possible_emoji_shortcode_at_position(
3373 snapshot: &MultiBufferSnapshot,
3374 position: Point,
3375 ) -> Option<String> {
3376 let mut chars = Vec::new();
3377 let mut found_colon = false;
3378 for char in snapshot.reversed_chars_at(position).take(100) {
3379 // Found a possible emoji shortcode in the middle of the buffer
3380 if found_colon {
3381 if char.is_whitespace() {
3382 chars.reverse();
3383 return Some(chars.iter().collect());
3384 }
3385 // If the previous character is not a whitespace, we are in the middle of a word
3386 // and we only want to complete the shortcode if the word is made up of other emojis
3387 let mut containing_word = String::new();
3388 for ch in snapshot
3389 .reversed_chars_at(position)
3390 .skip(chars.len() + 1)
3391 .take(100)
3392 {
3393 if ch.is_whitespace() {
3394 break;
3395 }
3396 containing_word.push(ch);
3397 }
3398 let containing_word = containing_word.chars().rev().collect::<String>();
3399 if util::word_consists_of_emojis(containing_word.as_str()) {
3400 chars.reverse();
3401 return Some(chars.iter().collect());
3402 }
3403 }
3404
3405 if char.is_whitespace() || !char.is_ascii() {
3406 return None;
3407 }
3408 if char == ':' {
3409 found_colon = true;
3410 } else {
3411 chars.push(char);
3412 }
3413 }
3414 // Found a possible emoji shortcode at the beginning of the buffer
3415 chars.reverse();
3416 Some(chars.iter().collect())
3417 }
3418
3419 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3420 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3421 self.transact(window, cx, |this, window, cx| {
3422 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3423 let selections = this.selections.all::<usize>(cx);
3424 let multi_buffer = this.buffer.read(cx);
3425 let buffer = multi_buffer.snapshot(cx);
3426 selections
3427 .iter()
3428 .map(|selection| {
3429 let start_point = selection.start.to_point(&buffer);
3430 let mut indent =
3431 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3432 indent.len = cmp::min(indent.len, start_point.column);
3433 let start = selection.start;
3434 let end = selection.end;
3435 let selection_is_empty = start == end;
3436 let language_scope = buffer.language_scope_at(start);
3437 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3438 &language_scope
3439 {
3440 let insert_extra_newline =
3441 insert_extra_newline_brackets(&buffer, start..end, language)
3442 || insert_extra_newline_tree_sitter(&buffer, start..end);
3443
3444 // Comment extension on newline is allowed only for cursor selections
3445 let comment_delimiter = maybe!({
3446 if !selection_is_empty {
3447 return None;
3448 }
3449
3450 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3451 return None;
3452 }
3453
3454 let delimiters = language.line_comment_prefixes();
3455 let max_len_of_delimiter =
3456 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3457 let (snapshot, range) =
3458 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3459
3460 let mut index_of_first_non_whitespace = 0;
3461 let comment_candidate = snapshot
3462 .chars_for_range(range)
3463 .skip_while(|c| {
3464 let should_skip = c.is_whitespace();
3465 if should_skip {
3466 index_of_first_non_whitespace += 1;
3467 }
3468 should_skip
3469 })
3470 .take(max_len_of_delimiter)
3471 .collect::<String>();
3472 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3473 comment_candidate.starts_with(comment_prefix.as_ref())
3474 })?;
3475 let cursor_is_placed_after_comment_marker =
3476 index_of_first_non_whitespace + comment_prefix.len()
3477 <= start_point.column as usize;
3478 if cursor_is_placed_after_comment_marker {
3479 Some(comment_prefix.clone())
3480 } else {
3481 None
3482 }
3483 });
3484 (comment_delimiter, insert_extra_newline)
3485 } else {
3486 (None, false)
3487 };
3488
3489 let capacity_for_delimiter = comment_delimiter
3490 .as_deref()
3491 .map(str::len)
3492 .unwrap_or_default();
3493 let mut new_text =
3494 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3495 new_text.push('\n');
3496 new_text.extend(indent.chars());
3497 if let Some(delimiter) = &comment_delimiter {
3498 new_text.push_str(delimiter);
3499 }
3500 if insert_extra_newline {
3501 new_text = new_text.repeat(2);
3502 }
3503
3504 let anchor = buffer.anchor_after(end);
3505 let new_selection = selection.map(|_| anchor);
3506 (
3507 (start..end, new_text),
3508 (insert_extra_newline, new_selection),
3509 )
3510 })
3511 .unzip()
3512 };
3513
3514 this.edit_with_autoindent(edits, cx);
3515 let buffer = this.buffer.read(cx).snapshot(cx);
3516 let new_selections = selection_fixup_info
3517 .into_iter()
3518 .map(|(extra_newline_inserted, new_selection)| {
3519 let mut cursor = new_selection.end.to_point(&buffer);
3520 if extra_newline_inserted {
3521 cursor.row -= 1;
3522 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3523 }
3524 new_selection.map(|_| cursor)
3525 })
3526 .collect();
3527
3528 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3529 s.select(new_selections)
3530 });
3531 this.refresh_inline_completion(true, false, window, cx);
3532 });
3533 }
3534
3535 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3536 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3537
3538 let buffer = self.buffer.read(cx);
3539 let snapshot = buffer.snapshot(cx);
3540
3541 let mut edits = Vec::new();
3542 let mut rows = Vec::new();
3543
3544 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3545 let cursor = selection.head();
3546 let row = cursor.row;
3547
3548 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3549
3550 let newline = "\n".to_string();
3551 edits.push((start_of_line..start_of_line, newline));
3552
3553 rows.push(row + rows_inserted as u32);
3554 }
3555
3556 self.transact(window, cx, |editor, window, cx| {
3557 editor.edit(edits, cx);
3558
3559 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3560 let mut index = 0;
3561 s.move_cursors_with(|map, _, _| {
3562 let row = rows[index];
3563 index += 1;
3564
3565 let point = Point::new(row, 0);
3566 let boundary = map.next_line_boundary(point).1;
3567 let clipped = map.clip_point(boundary, Bias::Left);
3568
3569 (clipped, SelectionGoal::None)
3570 });
3571 });
3572
3573 let mut indent_edits = Vec::new();
3574 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3575 for row in rows {
3576 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3577 for (row, indent) in indents {
3578 if indent.len == 0 {
3579 continue;
3580 }
3581
3582 let text = match indent.kind {
3583 IndentKind::Space => " ".repeat(indent.len as usize),
3584 IndentKind::Tab => "\t".repeat(indent.len as usize),
3585 };
3586 let point = Point::new(row.0, 0);
3587 indent_edits.push((point..point, text));
3588 }
3589 }
3590 editor.edit(indent_edits, cx);
3591 });
3592 }
3593
3594 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3595 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3596
3597 let buffer = self.buffer.read(cx);
3598 let snapshot = buffer.snapshot(cx);
3599
3600 let mut edits = Vec::new();
3601 let mut rows = Vec::new();
3602 let mut rows_inserted = 0;
3603
3604 for selection in self.selections.all_adjusted(cx) {
3605 let cursor = selection.head();
3606 let row = cursor.row;
3607
3608 let point = Point::new(row + 1, 0);
3609 let start_of_line = snapshot.clip_point(point, Bias::Left);
3610
3611 let newline = "\n".to_string();
3612 edits.push((start_of_line..start_of_line, newline));
3613
3614 rows_inserted += 1;
3615 rows.push(row + rows_inserted);
3616 }
3617
3618 self.transact(window, cx, |editor, window, cx| {
3619 editor.edit(edits, cx);
3620
3621 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3622 let mut index = 0;
3623 s.move_cursors_with(|map, _, _| {
3624 let row = rows[index];
3625 index += 1;
3626
3627 let point = Point::new(row, 0);
3628 let boundary = map.next_line_boundary(point).1;
3629 let clipped = map.clip_point(boundary, Bias::Left);
3630
3631 (clipped, SelectionGoal::None)
3632 });
3633 });
3634
3635 let mut indent_edits = Vec::new();
3636 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3637 for row in rows {
3638 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3639 for (row, indent) in indents {
3640 if indent.len == 0 {
3641 continue;
3642 }
3643
3644 let text = match indent.kind {
3645 IndentKind::Space => " ".repeat(indent.len as usize),
3646 IndentKind::Tab => "\t".repeat(indent.len as usize),
3647 };
3648 let point = Point::new(row.0, 0);
3649 indent_edits.push((point..point, text));
3650 }
3651 }
3652 editor.edit(indent_edits, cx);
3653 });
3654 }
3655
3656 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3657 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3658 original_indent_columns: Vec::new(),
3659 });
3660 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3661 }
3662
3663 fn insert_with_autoindent_mode(
3664 &mut self,
3665 text: &str,
3666 autoindent_mode: Option<AutoindentMode>,
3667 window: &mut Window,
3668 cx: &mut Context<Self>,
3669 ) {
3670 if self.read_only(cx) {
3671 return;
3672 }
3673
3674 let text: Arc<str> = text.into();
3675 self.transact(window, cx, |this, window, cx| {
3676 let old_selections = this.selections.all_adjusted(cx);
3677 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3678 let anchors = {
3679 let snapshot = buffer.read(cx);
3680 old_selections
3681 .iter()
3682 .map(|s| {
3683 let anchor = snapshot.anchor_after(s.head());
3684 s.map(|_| anchor)
3685 })
3686 .collect::<Vec<_>>()
3687 };
3688 buffer.edit(
3689 old_selections
3690 .iter()
3691 .map(|s| (s.start..s.end, text.clone())),
3692 autoindent_mode,
3693 cx,
3694 );
3695 anchors
3696 });
3697
3698 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3699 s.select_anchors(selection_anchors);
3700 });
3701
3702 cx.notify();
3703 });
3704 }
3705
3706 fn trigger_completion_on_input(
3707 &mut self,
3708 text: &str,
3709 trigger_in_words: bool,
3710 window: &mut Window,
3711 cx: &mut Context<Self>,
3712 ) {
3713 let ignore_completion_provider = self
3714 .context_menu
3715 .borrow()
3716 .as_ref()
3717 .map(|menu| match menu {
3718 CodeContextMenu::Completions(completions_menu) => {
3719 completions_menu.ignore_completion_provider
3720 }
3721 CodeContextMenu::CodeActions(_) => false,
3722 })
3723 .unwrap_or(false);
3724
3725 if ignore_completion_provider {
3726 self.show_word_completions(&ShowWordCompletions, window, cx);
3727 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3728 self.show_completions(
3729 &ShowCompletions {
3730 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3731 },
3732 window,
3733 cx,
3734 );
3735 } else {
3736 self.hide_context_menu(window, cx);
3737 }
3738 }
3739
3740 fn is_completion_trigger(
3741 &self,
3742 text: &str,
3743 trigger_in_words: bool,
3744 cx: &mut Context<Self>,
3745 ) -> bool {
3746 let position = self.selections.newest_anchor().head();
3747 let multibuffer = self.buffer.read(cx);
3748 let Some(buffer) = position
3749 .buffer_id
3750 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3751 else {
3752 return false;
3753 };
3754
3755 if let Some(completion_provider) = &self.completion_provider {
3756 completion_provider.is_completion_trigger(
3757 &buffer,
3758 position.text_anchor,
3759 text,
3760 trigger_in_words,
3761 cx,
3762 )
3763 } else {
3764 false
3765 }
3766 }
3767
3768 /// If any empty selections is touching the start of its innermost containing autoclose
3769 /// region, expand it to select the brackets.
3770 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3771 let selections = self.selections.all::<usize>(cx);
3772 let buffer = self.buffer.read(cx).read(cx);
3773 let new_selections = self
3774 .selections_with_autoclose_regions(selections, &buffer)
3775 .map(|(mut selection, region)| {
3776 if !selection.is_empty() {
3777 return selection;
3778 }
3779
3780 if let Some(region) = region {
3781 let mut range = region.range.to_offset(&buffer);
3782 if selection.start == range.start && range.start >= region.pair.start.len() {
3783 range.start -= region.pair.start.len();
3784 if buffer.contains_str_at(range.start, ®ion.pair.start)
3785 && buffer.contains_str_at(range.end, ®ion.pair.end)
3786 {
3787 range.end += region.pair.end.len();
3788 selection.start = range.start;
3789 selection.end = range.end;
3790
3791 return selection;
3792 }
3793 }
3794 }
3795
3796 let always_treat_brackets_as_autoclosed = buffer
3797 .language_settings_at(selection.start, cx)
3798 .always_treat_brackets_as_autoclosed;
3799
3800 if !always_treat_brackets_as_autoclosed {
3801 return selection;
3802 }
3803
3804 if let Some(scope) = buffer.language_scope_at(selection.start) {
3805 for (pair, enabled) in scope.brackets() {
3806 if !enabled || !pair.close {
3807 continue;
3808 }
3809
3810 if buffer.contains_str_at(selection.start, &pair.end) {
3811 let pair_start_len = pair.start.len();
3812 if buffer.contains_str_at(
3813 selection.start.saturating_sub(pair_start_len),
3814 &pair.start,
3815 ) {
3816 selection.start -= pair_start_len;
3817 selection.end += pair.end.len();
3818
3819 return selection;
3820 }
3821 }
3822 }
3823 }
3824
3825 selection
3826 })
3827 .collect();
3828
3829 drop(buffer);
3830 self.change_selections(None, window, cx, |selections| {
3831 selections.select(new_selections)
3832 });
3833 }
3834
3835 /// Iterate the given selections, and for each one, find the smallest surrounding
3836 /// autoclose region. This uses the ordering of the selections and the autoclose
3837 /// regions to avoid repeated comparisons.
3838 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3839 &'a self,
3840 selections: impl IntoIterator<Item = Selection<D>>,
3841 buffer: &'a MultiBufferSnapshot,
3842 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3843 let mut i = 0;
3844 let mut regions = self.autoclose_regions.as_slice();
3845 selections.into_iter().map(move |selection| {
3846 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3847
3848 let mut enclosing = None;
3849 while let Some(pair_state) = regions.get(i) {
3850 if pair_state.range.end.to_offset(buffer) < range.start {
3851 regions = ®ions[i + 1..];
3852 i = 0;
3853 } else if pair_state.range.start.to_offset(buffer) > range.end {
3854 break;
3855 } else {
3856 if pair_state.selection_id == selection.id {
3857 enclosing = Some(pair_state);
3858 }
3859 i += 1;
3860 }
3861 }
3862
3863 (selection, enclosing)
3864 })
3865 }
3866
3867 /// Remove any autoclose regions that no longer contain their selection.
3868 fn invalidate_autoclose_regions(
3869 &mut self,
3870 mut selections: &[Selection<Anchor>],
3871 buffer: &MultiBufferSnapshot,
3872 ) {
3873 self.autoclose_regions.retain(|state| {
3874 let mut i = 0;
3875 while let Some(selection) = selections.get(i) {
3876 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3877 selections = &selections[1..];
3878 continue;
3879 }
3880 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3881 break;
3882 }
3883 if selection.id == state.selection_id {
3884 return true;
3885 } else {
3886 i += 1;
3887 }
3888 }
3889 false
3890 });
3891 }
3892
3893 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3894 let offset = position.to_offset(buffer);
3895 let (word_range, kind) = buffer.surrounding_word(offset, true);
3896 if offset > word_range.start && kind == Some(CharKind::Word) {
3897 Some(
3898 buffer
3899 .text_for_range(word_range.start..offset)
3900 .collect::<String>(),
3901 )
3902 } else {
3903 None
3904 }
3905 }
3906
3907 pub fn toggle_inlay_hints(
3908 &mut self,
3909 _: &ToggleInlayHints,
3910 _: &mut Window,
3911 cx: &mut Context<Self>,
3912 ) {
3913 self.refresh_inlay_hints(
3914 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3915 cx,
3916 );
3917 }
3918
3919 pub fn inlay_hints_enabled(&self) -> bool {
3920 self.inlay_hint_cache.enabled
3921 }
3922
3923 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3924 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3925 return;
3926 }
3927
3928 let reason_description = reason.description();
3929 let ignore_debounce = matches!(
3930 reason,
3931 InlayHintRefreshReason::SettingsChange(_)
3932 | InlayHintRefreshReason::Toggle(_)
3933 | InlayHintRefreshReason::ExcerptsRemoved(_)
3934 | InlayHintRefreshReason::ModifiersChanged(_)
3935 );
3936 let (invalidate_cache, required_languages) = match reason {
3937 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3938 match self.inlay_hint_cache.modifiers_override(enabled) {
3939 Some(enabled) => {
3940 if enabled {
3941 (InvalidationStrategy::RefreshRequested, None)
3942 } else {
3943 self.splice_inlays(
3944 &self
3945 .visible_inlay_hints(cx)
3946 .iter()
3947 .map(|inlay| inlay.id)
3948 .collect::<Vec<InlayId>>(),
3949 Vec::new(),
3950 cx,
3951 );
3952 return;
3953 }
3954 }
3955 None => return,
3956 }
3957 }
3958 InlayHintRefreshReason::Toggle(enabled) => {
3959 if self.inlay_hint_cache.toggle(enabled) {
3960 if enabled {
3961 (InvalidationStrategy::RefreshRequested, None)
3962 } else {
3963 self.splice_inlays(
3964 &self
3965 .visible_inlay_hints(cx)
3966 .iter()
3967 .map(|inlay| inlay.id)
3968 .collect::<Vec<InlayId>>(),
3969 Vec::new(),
3970 cx,
3971 );
3972 return;
3973 }
3974 } else {
3975 return;
3976 }
3977 }
3978 InlayHintRefreshReason::SettingsChange(new_settings) => {
3979 match self.inlay_hint_cache.update_settings(
3980 &self.buffer,
3981 new_settings,
3982 self.visible_inlay_hints(cx),
3983 cx,
3984 ) {
3985 ControlFlow::Break(Some(InlaySplice {
3986 to_remove,
3987 to_insert,
3988 })) => {
3989 self.splice_inlays(&to_remove, to_insert, cx);
3990 return;
3991 }
3992 ControlFlow::Break(None) => return,
3993 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3994 }
3995 }
3996 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3997 if let Some(InlaySplice {
3998 to_remove,
3999 to_insert,
4000 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
4001 {
4002 self.splice_inlays(&to_remove, to_insert, cx);
4003 }
4004 return;
4005 }
4006 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4007 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4008 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4009 }
4010 InlayHintRefreshReason::RefreshRequested => {
4011 (InvalidationStrategy::RefreshRequested, None)
4012 }
4013 };
4014
4015 if let Some(InlaySplice {
4016 to_remove,
4017 to_insert,
4018 }) = self.inlay_hint_cache.spawn_hint_refresh(
4019 reason_description,
4020 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4021 invalidate_cache,
4022 ignore_debounce,
4023 cx,
4024 ) {
4025 self.splice_inlays(&to_remove, to_insert, cx);
4026 }
4027 }
4028
4029 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
4030 self.display_map
4031 .read(cx)
4032 .current_inlays()
4033 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4034 .cloned()
4035 .collect()
4036 }
4037
4038 pub fn excerpts_for_inlay_hints_query(
4039 &self,
4040 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4041 cx: &mut Context<Editor>,
4042 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
4043 let Some(project) = self.project.as_ref() else {
4044 return HashMap::default();
4045 };
4046 let project = project.read(cx);
4047 let multi_buffer = self.buffer().read(cx);
4048 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4049 let multi_buffer_visible_start = self
4050 .scroll_manager
4051 .anchor()
4052 .anchor
4053 .to_point(&multi_buffer_snapshot);
4054 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4055 multi_buffer_visible_start
4056 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4057 Bias::Left,
4058 );
4059 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4060 multi_buffer_snapshot
4061 .range_to_buffer_ranges(multi_buffer_visible_range)
4062 .into_iter()
4063 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4064 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
4065 let buffer_file = project::File::from_dyn(buffer.file())?;
4066 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4067 let worktree_entry = buffer_worktree
4068 .read(cx)
4069 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4070 if worktree_entry.is_ignored {
4071 return None;
4072 }
4073
4074 let language = buffer.language()?;
4075 if let Some(restrict_to_languages) = restrict_to_languages {
4076 if !restrict_to_languages.contains(language) {
4077 return None;
4078 }
4079 }
4080 Some((
4081 excerpt_id,
4082 (
4083 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4084 buffer.version().clone(),
4085 excerpt_visible_range,
4086 ),
4087 ))
4088 })
4089 .collect()
4090 }
4091
4092 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4093 TextLayoutDetails {
4094 text_system: window.text_system().clone(),
4095 editor_style: self.style.clone().unwrap(),
4096 rem_size: window.rem_size(),
4097 scroll_anchor: self.scroll_manager.anchor(),
4098 visible_rows: self.visible_line_count(),
4099 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4100 }
4101 }
4102
4103 pub fn splice_inlays(
4104 &self,
4105 to_remove: &[InlayId],
4106 to_insert: Vec<Inlay>,
4107 cx: &mut Context<Self>,
4108 ) {
4109 self.display_map.update(cx, |display_map, cx| {
4110 display_map.splice_inlays(to_remove, to_insert, cx)
4111 });
4112 cx.notify();
4113 }
4114
4115 fn trigger_on_type_formatting(
4116 &self,
4117 input: String,
4118 window: &mut Window,
4119 cx: &mut Context<Self>,
4120 ) -> Option<Task<Result<()>>> {
4121 if input.len() != 1 {
4122 return None;
4123 }
4124
4125 let project = self.project.as_ref()?;
4126 let position = self.selections.newest_anchor().head();
4127 let (buffer, buffer_position) = self
4128 .buffer
4129 .read(cx)
4130 .text_anchor_for_position(position, cx)?;
4131
4132 let settings = language_settings::language_settings(
4133 buffer
4134 .read(cx)
4135 .language_at(buffer_position)
4136 .map(|l| l.name()),
4137 buffer.read(cx).file(),
4138 cx,
4139 );
4140 if !settings.use_on_type_format {
4141 return None;
4142 }
4143
4144 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4145 // hence we do LSP request & edit on host side only — add formats to host's history.
4146 let push_to_lsp_host_history = true;
4147 // If this is not the host, append its history with new edits.
4148 let push_to_client_history = project.read(cx).is_via_collab();
4149
4150 let on_type_formatting = project.update(cx, |project, cx| {
4151 project.on_type_format(
4152 buffer.clone(),
4153 buffer_position,
4154 input,
4155 push_to_lsp_host_history,
4156 cx,
4157 )
4158 });
4159 Some(cx.spawn_in(window, async move |editor, cx| {
4160 if let Some(transaction) = on_type_formatting.await? {
4161 if push_to_client_history {
4162 buffer
4163 .update(cx, |buffer, _| {
4164 buffer.push_transaction(transaction, Instant::now());
4165 })
4166 .ok();
4167 }
4168 editor.update(cx, |editor, cx| {
4169 editor.refresh_document_highlights(cx);
4170 })?;
4171 }
4172 Ok(())
4173 }))
4174 }
4175
4176 pub fn show_word_completions(
4177 &mut self,
4178 _: &ShowWordCompletions,
4179 window: &mut Window,
4180 cx: &mut Context<Self>,
4181 ) {
4182 self.open_completions_menu(true, None, window, cx);
4183 }
4184
4185 pub fn show_completions(
4186 &mut self,
4187 options: &ShowCompletions,
4188 window: &mut Window,
4189 cx: &mut Context<Self>,
4190 ) {
4191 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4192 }
4193
4194 fn open_completions_menu(
4195 &mut self,
4196 ignore_completion_provider: bool,
4197 trigger: Option<&str>,
4198 window: &mut Window,
4199 cx: &mut Context<Self>,
4200 ) {
4201 if self.pending_rename.is_some() {
4202 return;
4203 }
4204 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4205 return;
4206 }
4207
4208 let position = self.selections.newest_anchor().head();
4209 if position.diff_base_anchor.is_some() {
4210 return;
4211 }
4212 let (buffer, buffer_position) =
4213 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4214 output
4215 } else {
4216 return;
4217 };
4218 let buffer_snapshot = buffer.read(cx).snapshot();
4219 let show_completion_documentation = buffer_snapshot
4220 .settings_at(buffer_position, cx)
4221 .show_completion_documentation;
4222
4223 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4224
4225 let trigger_kind = match trigger {
4226 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4227 CompletionTriggerKind::TRIGGER_CHARACTER
4228 }
4229 _ => CompletionTriggerKind::INVOKED,
4230 };
4231 let completion_context = CompletionContext {
4232 trigger_character: trigger.and_then(|trigger| {
4233 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4234 Some(String::from(trigger))
4235 } else {
4236 None
4237 }
4238 }),
4239 trigger_kind,
4240 };
4241
4242 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4243 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4244 let word_to_exclude = buffer_snapshot
4245 .text_for_range(old_range.clone())
4246 .collect::<String>();
4247 (
4248 buffer_snapshot.anchor_before(old_range.start)
4249 ..buffer_snapshot.anchor_after(old_range.end),
4250 Some(word_to_exclude),
4251 )
4252 } else {
4253 (buffer_position..buffer_position, None)
4254 };
4255
4256 let completion_settings = language_settings(
4257 buffer_snapshot
4258 .language_at(buffer_position)
4259 .map(|language| language.name()),
4260 buffer_snapshot.file(),
4261 cx,
4262 )
4263 .completions;
4264
4265 // The document can be large, so stay in reasonable bounds when searching for words,
4266 // otherwise completion pop-up might be slow to appear.
4267 const WORD_LOOKUP_ROWS: u32 = 5_000;
4268 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4269 let min_word_search = buffer_snapshot.clip_point(
4270 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4271 Bias::Left,
4272 );
4273 let max_word_search = buffer_snapshot.clip_point(
4274 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4275 Bias::Right,
4276 );
4277 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4278 ..buffer_snapshot.point_to_offset(max_word_search);
4279
4280 let provider = self
4281 .completion_provider
4282 .as_ref()
4283 .filter(|_| !ignore_completion_provider);
4284 let skip_digits = query
4285 .as_ref()
4286 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4287
4288 let (mut words, provided_completions) = match provider {
4289 Some(provider) => {
4290 let completions = provider.completions(
4291 position.excerpt_id,
4292 &buffer,
4293 buffer_position,
4294 completion_context,
4295 window,
4296 cx,
4297 );
4298
4299 let words = match completion_settings.words {
4300 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4301 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4302 .background_spawn(async move {
4303 buffer_snapshot.words_in_range(WordsQuery {
4304 fuzzy_contents: None,
4305 range: word_search_range,
4306 skip_digits,
4307 })
4308 }),
4309 };
4310
4311 (words, completions)
4312 }
4313 None => (
4314 cx.background_spawn(async move {
4315 buffer_snapshot.words_in_range(WordsQuery {
4316 fuzzy_contents: None,
4317 range: word_search_range,
4318 skip_digits,
4319 })
4320 }),
4321 Task::ready(Ok(None)),
4322 ),
4323 };
4324
4325 let sort_completions = provider
4326 .as_ref()
4327 .map_or(true, |provider| provider.sort_completions());
4328
4329 let filter_completions = provider
4330 .as_ref()
4331 .map_or(true, |provider| provider.filter_completions());
4332
4333 let id = post_inc(&mut self.next_completion_id);
4334 let task = cx.spawn_in(window, async move |editor, cx| {
4335 async move {
4336 editor.update(cx, |this, _| {
4337 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4338 })?;
4339
4340 let mut completions = Vec::new();
4341 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4342 completions.extend(provided_completions);
4343 if completion_settings.words == WordsCompletionMode::Fallback {
4344 words = Task::ready(HashMap::default());
4345 }
4346 }
4347
4348 let mut words = words.await;
4349 if let Some(word_to_exclude) = &word_to_exclude {
4350 words.remove(word_to_exclude);
4351 }
4352 for lsp_completion in &completions {
4353 words.remove(&lsp_completion.new_text);
4354 }
4355 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4356 old_range: old_range.clone(),
4357 new_text: word.clone(),
4358 label: CodeLabel::plain(word, None),
4359 icon_path: None,
4360 documentation: None,
4361 source: CompletionSource::BufferWord {
4362 word_range,
4363 resolved: false,
4364 },
4365 confirm: None,
4366 }));
4367
4368 let menu = if completions.is_empty() {
4369 None
4370 } else {
4371 let mut menu = CompletionsMenu::new(
4372 id,
4373 sort_completions,
4374 show_completion_documentation,
4375 ignore_completion_provider,
4376 position,
4377 buffer.clone(),
4378 completions.into(),
4379 );
4380
4381 menu.filter(
4382 if filter_completions {
4383 query.as_deref()
4384 } else {
4385 None
4386 },
4387 cx.background_executor().clone(),
4388 )
4389 .await;
4390
4391 menu.visible().then_some(menu)
4392 };
4393
4394 editor.update_in(cx, |editor, window, cx| {
4395 match editor.context_menu.borrow().as_ref() {
4396 None => {}
4397 Some(CodeContextMenu::Completions(prev_menu)) => {
4398 if prev_menu.id > id {
4399 return;
4400 }
4401 }
4402 _ => return,
4403 }
4404
4405 if editor.focus_handle.is_focused(window) && menu.is_some() {
4406 let mut menu = menu.unwrap();
4407 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4408
4409 *editor.context_menu.borrow_mut() =
4410 Some(CodeContextMenu::Completions(menu));
4411
4412 if editor.show_edit_predictions_in_menu() {
4413 editor.update_visible_inline_completion(window, cx);
4414 } else {
4415 editor.discard_inline_completion(false, cx);
4416 }
4417
4418 cx.notify();
4419 } else if editor.completion_tasks.len() <= 1 {
4420 // If there are no more completion tasks and the last menu was
4421 // empty, we should hide it.
4422 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4423 // If it was already hidden and we don't show inline
4424 // completions in the menu, we should also show the
4425 // inline-completion when available.
4426 if was_hidden && editor.show_edit_predictions_in_menu() {
4427 editor.update_visible_inline_completion(window, cx);
4428 }
4429 }
4430 })?;
4431
4432 anyhow::Ok(())
4433 }
4434 .log_err()
4435 .await
4436 });
4437
4438 self.completion_tasks.push((id, task));
4439 }
4440
4441 #[cfg(feature = "test-support")]
4442 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
4443 let menu = self.context_menu.borrow();
4444 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
4445 let completions = menu.completions.borrow();
4446 Some(completions.to_vec())
4447 } else {
4448 None
4449 }
4450 }
4451
4452 pub fn confirm_completion(
4453 &mut self,
4454 action: &ConfirmCompletion,
4455 window: &mut Window,
4456 cx: &mut Context<Self>,
4457 ) -> Option<Task<Result<()>>> {
4458 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4459 }
4460
4461 pub fn compose_completion(
4462 &mut self,
4463 action: &ComposeCompletion,
4464 window: &mut Window,
4465 cx: &mut Context<Self>,
4466 ) -> Option<Task<Result<()>>> {
4467 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4468 }
4469
4470 fn do_completion(
4471 &mut self,
4472 item_ix: Option<usize>,
4473 intent: CompletionIntent,
4474 window: &mut Window,
4475 cx: &mut Context<Editor>,
4476 ) -> Option<Task<Result<()>>> {
4477 use language::ToOffset as _;
4478
4479 let completions_menu =
4480 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4481 menu
4482 } else {
4483 return None;
4484 };
4485
4486 let candidate_id = {
4487 let entries = completions_menu.entries.borrow();
4488 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4489 if self.show_edit_predictions_in_menu() {
4490 self.discard_inline_completion(true, cx);
4491 }
4492 mat.candidate_id
4493 };
4494
4495 let buffer_handle = completions_menu.buffer;
4496 let completion = completions_menu
4497 .completions
4498 .borrow()
4499 .get(candidate_id)?
4500 .clone();
4501 cx.stop_propagation();
4502
4503 let snippet;
4504 let new_text;
4505 if completion.is_snippet() {
4506 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4507 new_text = snippet.as_ref().unwrap().text.clone();
4508 } else {
4509 snippet = None;
4510 new_text = completion.new_text.clone();
4511 };
4512 let selections = self.selections.all::<usize>(cx);
4513 let buffer = buffer_handle.read(cx);
4514 let old_range = completion.old_range.to_offset(buffer);
4515 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4516
4517 let newest_selection = self.selections.newest_anchor();
4518 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4519 return None;
4520 }
4521
4522 let lookbehind = newest_selection
4523 .start
4524 .text_anchor
4525 .to_offset(buffer)
4526 .saturating_sub(old_range.start);
4527 let lookahead = old_range
4528 .end
4529 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4530 let mut common_prefix_len = old_text
4531 .bytes()
4532 .zip(new_text.bytes())
4533 .take_while(|(a, b)| a == b)
4534 .count();
4535
4536 let snapshot = self.buffer.read(cx).snapshot(cx);
4537 let mut range_to_replace: Option<Range<isize>> = None;
4538 let mut ranges = Vec::new();
4539 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4540 for selection in &selections {
4541 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4542 let start = selection.start.saturating_sub(lookbehind);
4543 let end = selection.end + lookahead;
4544 if selection.id == newest_selection.id {
4545 range_to_replace = Some(
4546 ((start + common_prefix_len) as isize - selection.start as isize)
4547 ..(end as isize - selection.start as isize),
4548 );
4549 }
4550 ranges.push(start + common_prefix_len..end);
4551 } else {
4552 common_prefix_len = 0;
4553 ranges.clear();
4554 ranges.extend(selections.iter().map(|s| {
4555 if s.id == newest_selection.id {
4556 range_to_replace = Some(
4557 old_range.start.to_offset_utf16(&snapshot).0 as isize
4558 - selection.start as isize
4559 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4560 - selection.start as isize,
4561 );
4562 old_range.clone()
4563 } else {
4564 s.start..s.end
4565 }
4566 }));
4567 break;
4568 }
4569 if !self.linked_edit_ranges.is_empty() {
4570 let start_anchor = snapshot.anchor_before(selection.head());
4571 let end_anchor = snapshot.anchor_after(selection.tail());
4572 if let Some(ranges) = self
4573 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4574 {
4575 for (buffer, edits) in ranges {
4576 linked_edits.entry(buffer.clone()).or_default().extend(
4577 edits
4578 .into_iter()
4579 .map(|range| (range, new_text[common_prefix_len..].to_owned())),
4580 );
4581 }
4582 }
4583 }
4584 }
4585 let text = &new_text[common_prefix_len..];
4586
4587 cx.emit(EditorEvent::InputHandled {
4588 utf16_range_to_replace: range_to_replace,
4589 text: text.into(),
4590 });
4591
4592 self.transact(window, cx, |this, window, cx| {
4593 if let Some(mut snippet) = snippet {
4594 snippet.text = text.to_string();
4595 for tabstop in snippet
4596 .tabstops
4597 .iter_mut()
4598 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4599 {
4600 tabstop.start -= common_prefix_len as isize;
4601 tabstop.end -= common_prefix_len as isize;
4602 }
4603
4604 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4605 } else {
4606 this.buffer.update(cx, |buffer, cx| {
4607 let edits = ranges.iter().map(|range| (range.clone(), text));
4608 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4609 });
4610 }
4611 for (buffer, edits) in linked_edits {
4612 buffer.update(cx, |buffer, cx| {
4613 let snapshot = buffer.snapshot();
4614 let edits = edits
4615 .into_iter()
4616 .map(|(range, text)| {
4617 use text::ToPoint as TP;
4618 let end_point = TP::to_point(&range.end, &snapshot);
4619 let start_point = TP::to_point(&range.start, &snapshot);
4620 (start_point..end_point, text)
4621 })
4622 .sorted_by_key(|(range, _)| range.start);
4623 buffer.edit(edits, None, cx);
4624 })
4625 }
4626
4627 this.refresh_inline_completion(true, false, window, cx);
4628 });
4629
4630 let show_new_completions_on_confirm = completion
4631 .confirm
4632 .as_ref()
4633 .map_or(false, |confirm| confirm(intent, window, cx));
4634 if show_new_completions_on_confirm {
4635 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4636 }
4637
4638 let provider = self.completion_provider.as_ref()?;
4639 drop(completion);
4640 let apply_edits = provider.apply_additional_edits_for_completion(
4641 buffer_handle,
4642 completions_menu.completions.clone(),
4643 candidate_id,
4644 true,
4645 cx,
4646 );
4647
4648 let editor_settings = EditorSettings::get_global(cx);
4649 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4650 // After the code completion is finished, users often want to know what signatures are needed.
4651 // so we should automatically call signature_help
4652 self.show_signature_help(&ShowSignatureHelp, window, cx);
4653 }
4654
4655 Some(cx.foreground_executor().spawn(async move {
4656 apply_edits.await?;
4657 Ok(())
4658 }))
4659 }
4660
4661 pub fn toggle_code_actions(
4662 &mut self,
4663 action: &ToggleCodeActions,
4664 window: &mut Window,
4665 cx: &mut Context<Self>,
4666 ) {
4667 let mut context_menu = self.context_menu.borrow_mut();
4668 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4669 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4670 // Toggle if we're selecting the same one
4671 *context_menu = None;
4672 cx.notify();
4673 return;
4674 } else {
4675 // Otherwise, clear it and start a new one
4676 *context_menu = None;
4677 cx.notify();
4678 }
4679 }
4680 drop(context_menu);
4681 let snapshot = self.snapshot(window, cx);
4682 let deployed_from_indicator = action.deployed_from_indicator;
4683 let mut task = self.code_actions_task.take();
4684 let action = action.clone();
4685 cx.spawn_in(window, async move |editor, cx| {
4686 while let Some(prev_task) = task {
4687 prev_task.await.log_err();
4688 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4689 }
4690
4691 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4692 if editor.focus_handle.is_focused(window) {
4693 let multibuffer_point = action
4694 .deployed_from_indicator
4695 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4696 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4697 let (buffer, buffer_row) = snapshot
4698 .buffer_snapshot
4699 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4700 .and_then(|(buffer_snapshot, range)| {
4701 editor
4702 .buffer
4703 .read(cx)
4704 .buffer(buffer_snapshot.remote_id())
4705 .map(|buffer| (buffer, range.start.row))
4706 })?;
4707 let (_, code_actions) = editor
4708 .available_code_actions
4709 .clone()
4710 .and_then(|(location, code_actions)| {
4711 let snapshot = location.buffer.read(cx).snapshot();
4712 let point_range = location.range.to_point(&snapshot);
4713 let point_range = point_range.start.row..=point_range.end.row;
4714 if point_range.contains(&buffer_row) {
4715 Some((location, code_actions))
4716 } else {
4717 None
4718 }
4719 })
4720 .unzip();
4721 let buffer_id = buffer.read(cx).remote_id();
4722 let tasks = editor
4723 .tasks
4724 .get(&(buffer_id, buffer_row))
4725 .map(|t| Arc::new(t.to_owned()));
4726 if tasks.is_none() && code_actions.is_none() {
4727 return None;
4728 }
4729
4730 editor.completion_tasks.clear();
4731 editor.discard_inline_completion(false, cx);
4732 let task_context =
4733 tasks
4734 .as_ref()
4735 .zip(editor.project.clone())
4736 .map(|(tasks, project)| {
4737 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4738 });
4739
4740 Some(cx.spawn_in(window, async move |editor, cx| {
4741 let task_context = match task_context {
4742 Some(task_context) => task_context.await,
4743 None => None,
4744 };
4745 let resolved_tasks =
4746 tasks.zip(task_context).map(|(tasks, task_context)| {
4747 Rc::new(ResolvedTasks {
4748 templates: tasks.resolve(&task_context).collect(),
4749 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4750 multibuffer_point.row,
4751 tasks.column,
4752 )),
4753 })
4754 });
4755 let spawn_straight_away = resolved_tasks
4756 .as_ref()
4757 .map_or(false, |tasks| tasks.templates.len() == 1)
4758 && code_actions
4759 .as_ref()
4760 .map_or(true, |actions| actions.is_empty());
4761 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4762 *editor.context_menu.borrow_mut() =
4763 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4764 buffer,
4765 actions: CodeActionContents {
4766 tasks: resolved_tasks,
4767 actions: code_actions,
4768 },
4769 selected_item: Default::default(),
4770 scroll_handle: UniformListScrollHandle::default(),
4771 deployed_from_indicator,
4772 }));
4773 if spawn_straight_away {
4774 if let Some(task) = editor.confirm_code_action(
4775 &ConfirmCodeAction { item_ix: Some(0) },
4776 window,
4777 cx,
4778 ) {
4779 cx.notify();
4780 return task;
4781 }
4782 }
4783 cx.notify();
4784 Task::ready(Ok(()))
4785 }) {
4786 task.await
4787 } else {
4788 Ok(())
4789 }
4790 }))
4791 } else {
4792 Some(Task::ready(Ok(())))
4793 }
4794 })?;
4795 if let Some(task) = spawned_test_task {
4796 task.await?;
4797 }
4798
4799 Ok::<_, anyhow::Error>(())
4800 })
4801 .detach_and_log_err(cx);
4802 }
4803
4804 pub fn confirm_code_action(
4805 &mut self,
4806 action: &ConfirmCodeAction,
4807 window: &mut Window,
4808 cx: &mut Context<Self>,
4809 ) -> Option<Task<Result<()>>> {
4810 let actions_menu =
4811 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4812 menu
4813 } else {
4814 return None;
4815 };
4816 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4817 let action = actions_menu.actions.get(action_ix)?;
4818 let title = action.label();
4819 let buffer = actions_menu.buffer;
4820 let workspace = self.workspace()?;
4821
4822 match action {
4823 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4824 workspace.update(cx, |workspace, cx| {
4825 workspace::tasks::schedule_resolved_task(
4826 workspace,
4827 task_source_kind,
4828 resolved_task,
4829 false,
4830 cx,
4831 );
4832
4833 Some(Task::ready(Ok(())))
4834 })
4835 }
4836 CodeActionsItem::CodeAction {
4837 excerpt_id,
4838 action,
4839 provider,
4840 } => {
4841 let apply_code_action =
4842 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4843 let workspace = workspace.downgrade();
4844 Some(cx.spawn_in(window, async move |editor, cx| {
4845 let project_transaction = apply_code_action.await?;
4846 Self::open_project_transaction(
4847 &editor,
4848 workspace,
4849 project_transaction,
4850 title,
4851 cx,
4852 )
4853 .await
4854 }))
4855 }
4856 }
4857 }
4858
4859 pub async fn open_project_transaction(
4860 this: &WeakEntity<Editor>,
4861 workspace: WeakEntity<Workspace>,
4862 transaction: ProjectTransaction,
4863 title: String,
4864 cx: &mut AsyncWindowContext,
4865 ) -> Result<()> {
4866 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4867 cx.update(|_, cx| {
4868 entries.sort_unstable_by_key(|(buffer, _)| {
4869 buffer.read(cx).file().map(|f| f.path().clone())
4870 });
4871 })?;
4872
4873 // If the project transaction's edits are all contained within this editor, then
4874 // avoid opening a new editor to display them.
4875
4876 if let Some((buffer, transaction)) = entries.first() {
4877 if entries.len() == 1 {
4878 let excerpt = this.update(cx, |editor, cx| {
4879 editor
4880 .buffer()
4881 .read(cx)
4882 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4883 })?;
4884 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4885 if excerpted_buffer == *buffer {
4886 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4887 let excerpt_range = excerpt_range.to_offset(buffer);
4888 buffer
4889 .edited_ranges_for_transaction::<usize>(transaction)
4890 .all(|range| {
4891 excerpt_range.start <= range.start
4892 && excerpt_range.end >= range.end
4893 })
4894 })?;
4895
4896 if all_edits_within_excerpt {
4897 return Ok(());
4898 }
4899 }
4900 }
4901 }
4902 } else {
4903 return Ok(());
4904 }
4905
4906 let mut ranges_to_highlight = Vec::new();
4907 let excerpt_buffer = cx.new(|cx| {
4908 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4909 for (buffer_handle, transaction) in &entries {
4910 let buffer = buffer_handle.read(cx);
4911 ranges_to_highlight.extend(
4912 multibuffer.push_excerpts_with_context_lines(
4913 buffer_handle.clone(),
4914 buffer
4915 .edited_ranges_for_transaction::<usize>(transaction)
4916 .collect(),
4917 DEFAULT_MULTIBUFFER_CONTEXT,
4918 cx,
4919 ),
4920 );
4921 }
4922 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4923 multibuffer
4924 })?;
4925
4926 workspace.update_in(cx, |workspace, window, cx| {
4927 let project = workspace.project().clone();
4928 let editor =
4929 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4930 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4931 editor.update(cx, |editor, cx| {
4932 editor.highlight_background::<Self>(
4933 &ranges_to_highlight,
4934 |theme| theme.editor_highlighted_line_background,
4935 cx,
4936 );
4937 });
4938 })?;
4939
4940 Ok(())
4941 }
4942
4943 pub fn clear_code_action_providers(&mut self) {
4944 self.code_action_providers.clear();
4945 self.available_code_actions.take();
4946 }
4947
4948 pub fn add_code_action_provider(
4949 &mut self,
4950 provider: Rc<dyn CodeActionProvider>,
4951 window: &mut Window,
4952 cx: &mut Context<Self>,
4953 ) {
4954 if self
4955 .code_action_providers
4956 .iter()
4957 .any(|existing_provider| existing_provider.id() == provider.id())
4958 {
4959 return;
4960 }
4961
4962 self.code_action_providers.push(provider);
4963 self.refresh_code_actions(window, cx);
4964 }
4965
4966 pub fn remove_code_action_provider(
4967 &mut self,
4968 id: Arc<str>,
4969 window: &mut Window,
4970 cx: &mut Context<Self>,
4971 ) {
4972 self.code_action_providers
4973 .retain(|provider| provider.id() != id);
4974 self.refresh_code_actions(window, cx);
4975 }
4976
4977 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4978 let buffer = self.buffer.read(cx);
4979 let newest_selection = self.selections.newest_anchor().clone();
4980 if newest_selection.head().diff_base_anchor.is_some() {
4981 return None;
4982 }
4983 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4984 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4985 if start_buffer != end_buffer {
4986 return None;
4987 }
4988
4989 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4990 cx.background_executor()
4991 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4992 .await;
4993
4994 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4995 let providers = this.code_action_providers.clone();
4996 let tasks = this
4997 .code_action_providers
4998 .iter()
4999 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
5000 .collect::<Vec<_>>();
5001 (providers, tasks)
5002 })?;
5003
5004 let mut actions = Vec::new();
5005 for (provider, provider_actions) in
5006 providers.into_iter().zip(future::join_all(tasks).await)
5007 {
5008 if let Some(provider_actions) = provider_actions.log_err() {
5009 actions.extend(provider_actions.into_iter().map(|action| {
5010 AvailableCodeAction {
5011 excerpt_id: newest_selection.start.excerpt_id,
5012 action,
5013 provider: provider.clone(),
5014 }
5015 }));
5016 }
5017 }
5018
5019 this.update(cx, |this, cx| {
5020 this.available_code_actions = if actions.is_empty() {
5021 None
5022 } else {
5023 Some((
5024 Location {
5025 buffer: start_buffer,
5026 range: start..end,
5027 },
5028 actions.into(),
5029 ))
5030 };
5031 cx.notify();
5032 })
5033 }));
5034 None
5035 }
5036
5037 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5038 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5039 self.show_git_blame_inline = false;
5040
5041 self.show_git_blame_inline_delay_task =
5042 Some(cx.spawn_in(window, async move |this, cx| {
5043 cx.background_executor().timer(delay).await;
5044
5045 this.update(cx, |this, cx| {
5046 this.show_git_blame_inline = true;
5047 cx.notify();
5048 })
5049 .log_err();
5050 }));
5051 }
5052 }
5053
5054 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
5055 if self.pending_rename.is_some() {
5056 return None;
5057 }
5058
5059 let provider = self.semantics_provider.clone()?;
5060 let buffer = self.buffer.read(cx);
5061 let newest_selection = self.selections.newest_anchor().clone();
5062 let cursor_position = newest_selection.head();
5063 let (cursor_buffer, cursor_buffer_position) =
5064 buffer.text_anchor_for_position(cursor_position, cx)?;
5065 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5066 if cursor_buffer != tail_buffer {
5067 return None;
5068 }
5069 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
5070 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
5071 cx.background_executor()
5072 .timer(Duration::from_millis(debounce))
5073 .await;
5074
5075 let highlights = if let Some(highlights) = cx
5076 .update(|cx| {
5077 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5078 })
5079 .ok()
5080 .flatten()
5081 {
5082 highlights.await.log_err()
5083 } else {
5084 None
5085 };
5086
5087 if let Some(highlights) = highlights {
5088 this.update(cx, |this, cx| {
5089 if this.pending_rename.is_some() {
5090 return;
5091 }
5092
5093 let buffer_id = cursor_position.buffer_id;
5094 let buffer = this.buffer.read(cx);
5095 if !buffer
5096 .text_anchor_for_position(cursor_position, cx)
5097 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5098 {
5099 return;
5100 }
5101
5102 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5103 let mut write_ranges = Vec::new();
5104 let mut read_ranges = Vec::new();
5105 for highlight in highlights {
5106 for (excerpt_id, excerpt_range) in
5107 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5108 {
5109 let start = highlight
5110 .range
5111 .start
5112 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5113 let end = highlight
5114 .range
5115 .end
5116 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5117 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5118 continue;
5119 }
5120
5121 let range = Anchor {
5122 buffer_id,
5123 excerpt_id,
5124 text_anchor: start,
5125 diff_base_anchor: None,
5126 }..Anchor {
5127 buffer_id,
5128 excerpt_id,
5129 text_anchor: end,
5130 diff_base_anchor: None,
5131 };
5132 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5133 write_ranges.push(range);
5134 } else {
5135 read_ranges.push(range);
5136 }
5137 }
5138 }
5139
5140 this.highlight_background::<DocumentHighlightRead>(
5141 &read_ranges,
5142 |theme| theme.editor_document_highlight_read_background,
5143 cx,
5144 );
5145 this.highlight_background::<DocumentHighlightWrite>(
5146 &write_ranges,
5147 |theme| theme.editor_document_highlight_write_background,
5148 cx,
5149 );
5150 cx.notify();
5151 })
5152 .log_err();
5153 }
5154 }));
5155 None
5156 }
5157
5158 pub fn refresh_selected_text_highlights(
5159 &mut self,
5160 window: &mut Window,
5161 cx: &mut Context<Editor>,
5162 ) {
5163 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5164 return;
5165 }
5166 self.selection_highlight_task.take();
5167 if !EditorSettings::get_global(cx).selection_highlight {
5168 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5169 return;
5170 }
5171 if self.selections.count() != 1 || self.selections.line_mode {
5172 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5173 return;
5174 }
5175 let selection = self.selections.newest::<Point>(cx);
5176 if selection.is_empty() || selection.start.row != selection.end.row {
5177 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5178 return;
5179 }
5180 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5181 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5182 cx.background_executor()
5183 .timer(Duration::from_millis(debounce))
5184 .await;
5185 let Some(Some(matches_task)) = editor
5186 .update_in(cx, |editor, _, cx| {
5187 if editor.selections.count() != 1 || editor.selections.line_mode {
5188 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5189 return None;
5190 }
5191 let selection = editor.selections.newest::<Point>(cx);
5192 if selection.is_empty() || selection.start.row != selection.end.row {
5193 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5194 return None;
5195 }
5196 let buffer = editor.buffer().read(cx).snapshot(cx);
5197 let query = buffer.text_for_range(selection.range()).collect::<String>();
5198 if query.trim().is_empty() {
5199 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5200 return None;
5201 }
5202 Some(cx.background_spawn(async move {
5203 let mut ranges = Vec::new();
5204 let selection_anchors = selection.range().to_anchors(&buffer);
5205 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5206 for (search_buffer, search_range, excerpt_id) in
5207 buffer.range_to_buffer_ranges(range)
5208 {
5209 ranges.extend(
5210 project::search::SearchQuery::text(
5211 query.clone(),
5212 false,
5213 false,
5214 false,
5215 Default::default(),
5216 Default::default(),
5217 None,
5218 )
5219 .unwrap()
5220 .search(search_buffer, Some(search_range.clone()))
5221 .await
5222 .into_iter()
5223 .filter_map(
5224 |match_range| {
5225 let start = search_buffer.anchor_after(
5226 search_range.start + match_range.start,
5227 );
5228 let end = search_buffer.anchor_before(
5229 search_range.start + match_range.end,
5230 );
5231 let range = Anchor::range_in_buffer(
5232 excerpt_id,
5233 search_buffer.remote_id(),
5234 start..end,
5235 );
5236 (range != selection_anchors).then_some(range)
5237 },
5238 ),
5239 );
5240 }
5241 }
5242 ranges
5243 }))
5244 })
5245 .log_err()
5246 else {
5247 return;
5248 };
5249 let matches = matches_task.await;
5250 editor
5251 .update_in(cx, |editor, _, cx| {
5252 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5253 if !matches.is_empty() {
5254 editor.highlight_background::<SelectedTextHighlight>(
5255 &matches,
5256 |theme| theme.editor_document_highlight_bracket_background,
5257 cx,
5258 )
5259 }
5260 })
5261 .log_err();
5262 }));
5263 }
5264
5265 pub fn refresh_inline_completion(
5266 &mut self,
5267 debounce: bool,
5268 user_requested: bool,
5269 window: &mut Window,
5270 cx: &mut Context<Self>,
5271 ) -> Option<()> {
5272 let provider = self.edit_prediction_provider()?;
5273 let cursor = self.selections.newest_anchor().head();
5274 let (buffer, cursor_buffer_position) =
5275 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5276
5277 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5278 self.discard_inline_completion(false, cx);
5279 return None;
5280 }
5281
5282 if !user_requested
5283 && (!self.should_show_edit_predictions()
5284 || !self.is_focused(window)
5285 || buffer.read(cx).is_empty())
5286 {
5287 self.discard_inline_completion(false, cx);
5288 return None;
5289 }
5290
5291 self.update_visible_inline_completion(window, cx);
5292 provider.refresh(
5293 self.project.clone(),
5294 buffer,
5295 cursor_buffer_position,
5296 debounce,
5297 cx,
5298 );
5299 Some(())
5300 }
5301
5302 fn show_edit_predictions_in_menu(&self) -> bool {
5303 match self.edit_prediction_settings {
5304 EditPredictionSettings::Disabled => false,
5305 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5306 }
5307 }
5308
5309 pub fn edit_predictions_enabled(&self) -> bool {
5310 match self.edit_prediction_settings {
5311 EditPredictionSettings::Disabled => false,
5312 EditPredictionSettings::Enabled { .. } => true,
5313 }
5314 }
5315
5316 fn edit_prediction_requires_modifier(&self) -> bool {
5317 match self.edit_prediction_settings {
5318 EditPredictionSettings::Disabled => false,
5319 EditPredictionSettings::Enabled {
5320 preview_requires_modifier,
5321 ..
5322 } => preview_requires_modifier,
5323 }
5324 }
5325
5326 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5327 if self.edit_prediction_provider.is_none() {
5328 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5329 } else {
5330 let selection = self.selections.newest_anchor();
5331 let cursor = selection.head();
5332
5333 if let Some((buffer, cursor_buffer_position)) =
5334 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5335 {
5336 self.edit_prediction_settings =
5337 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5338 }
5339 }
5340 }
5341
5342 fn edit_prediction_settings_at_position(
5343 &self,
5344 buffer: &Entity<Buffer>,
5345 buffer_position: language::Anchor,
5346 cx: &App,
5347 ) -> EditPredictionSettings {
5348 if self.mode != EditorMode::Full
5349 || !self.show_inline_completions_override.unwrap_or(true)
5350 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5351 {
5352 return EditPredictionSettings::Disabled;
5353 }
5354
5355 let buffer = buffer.read(cx);
5356
5357 let file = buffer.file();
5358
5359 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5360 return EditPredictionSettings::Disabled;
5361 };
5362
5363 let by_provider = matches!(
5364 self.menu_inline_completions_policy,
5365 MenuInlineCompletionsPolicy::ByProvider
5366 );
5367
5368 let show_in_menu = by_provider
5369 && self
5370 .edit_prediction_provider
5371 .as_ref()
5372 .map_or(false, |provider| {
5373 provider.provider.show_completions_in_menu()
5374 });
5375
5376 let preview_requires_modifier =
5377 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5378
5379 EditPredictionSettings::Enabled {
5380 show_in_menu,
5381 preview_requires_modifier,
5382 }
5383 }
5384
5385 fn should_show_edit_predictions(&self) -> bool {
5386 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5387 }
5388
5389 pub fn edit_prediction_preview_is_active(&self) -> bool {
5390 matches!(
5391 self.edit_prediction_preview,
5392 EditPredictionPreview::Active { .. }
5393 )
5394 }
5395
5396 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5397 let cursor = self.selections.newest_anchor().head();
5398 if let Some((buffer, cursor_position)) =
5399 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5400 {
5401 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5402 } else {
5403 false
5404 }
5405 }
5406
5407 fn edit_predictions_enabled_in_buffer(
5408 &self,
5409 buffer: &Entity<Buffer>,
5410 buffer_position: language::Anchor,
5411 cx: &App,
5412 ) -> bool {
5413 maybe!({
5414 if self.read_only(cx) {
5415 return Some(false);
5416 }
5417 let provider = self.edit_prediction_provider()?;
5418 if !provider.is_enabled(&buffer, buffer_position, cx) {
5419 return Some(false);
5420 }
5421 let buffer = buffer.read(cx);
5422 let Some(file) = buffer.file() else {
5423 return Some(true);
5424 };
5425 let settings = all_language_settings(Some(file), cx);
5426 Some(settings.edit_predictions_enabled_for_file(file, cx))
5427 })
5428 .unwrap_or(false)
5429 }
5430
5431 fn cycle_inline_completion(
5432 &mut self,
5433 direction: Direction,
5434 window: &mut Window,
5435 cx: &mut Context<Self>,
5436 ) -> Option<()> {
5437 let provider = self.edit_prediction_provider()?;
5438 let cursor = self.selections.newest_anchor().head();
5439 let (buffer, cursor_buffer_position) =
5440 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5441 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5442 return None;
5443 }
5444
5445 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5446 self.update_visible_inline_completion(window, cx);
5447
5448 Some(())
5449 }
5450
5451 pub fn show_inline_completion(
5452 &mut self,
5453 _: &ShowEditPrediction,
5454 window: &mut Window,
5455 cx: &mut Context<Self>,
5456 ) {
5457 if !self.has_active_inline_completion() {
5458 self.refresh_inline_completion(false, true, window, cx);
5459 return;
5460 }
5461
5462 self.update_visible_inline_completion(window, cx);
5463 }
5464
5465 pub fn display_cursor_names(
5466 &mut self,
5467 _: &DisplayCursorNames,
5468 window: &mut Window,
5469 cx: &mut Context<Self>,
5470 ) {
5471 self.show_cursor_names(window, cx);
5472 }
5473
5474 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5475 self.show_cursor_names = true;
5476 cx.notify();
5477 cx.spawn_in(window, async move |this, cx| {
5478 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5479 this.update(cx, |this, cx| {
5480 this.show_cursor_names = false;
5481 cx.notify()
5482 })
5483 .ok()
5484 })
5485 .detach();
5486 }
5487
5488 pub fn next_edit_prediction(
5489 &mut self,
5490 _: &NextEditPrediction,
5491 window: &mut Window,
5492 cx: &mut Context<Self>,
5493 ) {
5494 if self.has_active_inline_completion() {
5495 self.cycle_inline_completion(Direction::Next, window, cx);
5496 } else {
5497 let is_copilot_disabled = self
5498 .refresh_inline_completion(false, true, window, cx)
5499 .is_none();
5500 if is_copilot_disabled {
5501 cx.propagate();
5502 }
5503 }
5504 }
5505
5506 pub fn previous_edit_prediction(
5507 &mut self,
5508 _: &PreviousEditPrediction,
5509 window: &mut Window,
5510 cx: &mut Context<Self>,
5511 ) {
5512 if self.has_active_inline_completion() {
5513 self.cycle_inline_completion(Direction::Prev, window, cx);
5514 } else {
5515 let is_copilot_disabled = self
5516 .refresh_inline_completion(false, true, window, cx)
5517 .is_none();
5518 if is_copilot_disabled {
5519 cx.propagate();
5520 }
5521 }
5522 }
5523
5524 pub fn accept_edit_prediction(
5525 &mut self,
5526 _: &AcceptEditPrediction,
5527 window: &mut Window,
5528 cx: &mut Context<Self>,
5529 ) {
5530 if self.show_edit_predictions_in_menu() {
5531 self.hide_context_menu(window, cx);
5532 }
5533
5534 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5535 return;
5536 };
5537
5538 self.report_inline_completion_event(
5539 active_inline_completion.completion_id.clone(),
5540 true,
5541 cx,
5542 );
5543
5544 match &active_inline_completion.completion {
5545 InlineCompletion::Move { target, .. } => {
5546 let target = *target;
5547
5548 if let Some(position_map) = &self.last_position_map {
5549 if position_map
5550 .visible_row_range
5551 .contains(&target.to_display_point(&position_map.snapshot).row())
5552 || !self.edit_prediction_requires_modifier()
5553 {
5554 self.unfold_ranges(&[target..target], true, false, cx);
5555 // Note that this is also done in vim's handler of the Tab action.
5556 self.change_selections(
5557 Some(Autoscroll::newest()),
5558 window,
5559 cx,
5560 |selections| {
5561 selections.select_anchor_ranges([target..target]);
5562 },
5563 );
5564 self.clear_row_highlights::<EditPredictionPreview>();
5565
5566 self.edit_prediction_preview
5567 .set_previous_scroll_position(None);
5568 } else {
5569 self.edit_prediction_preview
5570 .set_previous_scroll_position(Some(
5571 position_map.snapshot.scroll_anchor,
5572 ));
5573
5574 self.highlight_rows::<EditPredictionPreview>(
5575 target..target,
5576 cx.theme().colors().editor_highlighted_line_background,
5577 true,
5578 cx,
5579 );
5580 self.request_autoscroll(Autoscroll::fit(), cx);
5581 }
5582 }
5583 }
5584 InlineCompletion::Edit { edits, .. } => {
5585 if let Some(provider) = self.edit_prediction_provider() {
5586 provider.accept(cx);
5587 }
5588
5589 let snapshot = self.buffer.read(cx).snapshot(cx);
5590 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5591
5592 self.buffer.update(cx, |buffer, cx| {
5593 buffer.edit(edits.iter().cloned(), None, cx)
5594 });
5595
5596 self.change_selections(None, window, cx, |s| {
5597 s.select_anchor_ranges([last_edit_end..last_edit_end])
5598 });
5599
5600 self.update_visible_inline_completion(window, cx);
5601 if self.active_inline_completion.is_none() {
5602 self.refresh_inline_completion(true, true, window, cx);
5603 }
5604
5605 cx.notify();
5606 }
5607 }
5608
5609 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5610 }
5611
5612 pub fn accept_partial_inline_completion(
5613 &mut self,
5614 _: &AcceptPartialEditPrediction,
5615 window: &mut Window,
5616 cx: &mut Context<Self>,
5617 ) {
5618 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5619 return;
5620 };
5621 if self.selections.count() != 1 {
5622 return;
5623 }
5624
5625 self.report_inline_completion_event(
5626 active_inline_completion.completion_id.clone(),
5627 true,
5628 cx,
5629 );
5630
5631 match &active_inline_completion.completion {
5632 InlineCompletion::Move { target, .. } => {
5633 let target = *target;
5634 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5635 selections.select_anchor_ranges([target..target]);
5636 });
5637 }
5638 InlineCompletion::Edit { edits, .. } => {
5639 // Find an insertion that starts at the cursor position.
5640 let snapshot = self.buffer.read(cx).snapshot(cx);
5641 let cursor_offset = self.selections.newest::<usize>(cx).head();
5642 let insertion = edits.iter().find_map(|(range, text)| {
5643 let range = range.to_offset(&snapshot);
5644 if range.is_empty() && range.start == cursor_offset {
5645 Some(text)
5646 } else {
5647 None
5648 }
5649 });
5650
5651 if let Some(text) = insertion {
5652 let mut partial_completion = text
5653 .chars()
5654 .by_ref()
5655 .take_while(|c| c.is_alphabetic())
5656 .collect::<String>();
5657 if partial_completion.is_empty() {
5658 partial_completion = text
5659 .chars()
5660 .by_ref()
5661 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5662 .collect::<String>();
5663 }
5664
5665 cx.emit(EditorEvent::InputHandled {
5666 utf16_range_to_replace: None,
5667 text: partial_completion.clone().into(),
5668 });
5669
5670 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5671
5672 self.refresh_inline_completion(true, true, window, cx);
5673 cx.notify();
5674 } else {
5675 self.accept_edit_prediction(&Default::default(), window, cx);
5676 }
5677 }
5678 }
5679 }
5680
5681 fn discard_inline_completion(
5682 &mut self,
5683 should_report_inline_completion_event: bool,
5684 cx: &mut Context<Self>,
5685 ) -> bool {
5686 if should_report_inline_completion_event {
5687 let completion_id = self
5688 .active_inline_completion
5689 .as_ref()
5690 .and_then(|active_completion| active_completion.completion_id.clone());
5691
5692 self.report_inline_completion_event(completion_id, false, cx);
5693 }
5694
5695 if let Some(provider) = self.edit_prediction_provider() {
5696 provider.discard(cx);
5697 }
5698
5699 self.take_active_inline_completion(cx)
5700 }
5701
5702 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5703 let Some(provider) = self.edit_prediction_provider() else {
5704 return;
5705 };
5706
5707 let Some((_, buffer, _)) = self
5708 .buffer
5709 .read(cx)
5710 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5711 else {
5712 return;
5713 };
5714
5715 let extension = buffer
5716 .read(cx)
5717 .file()
5718 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5719
5720 let event_type = match accepted {
5721 true => "Edit Prediction Accepted",
5722 false => "Edit Prediction Discarded",
5723 };
5724 telemetry::event!(
5725 event_type,
5726 provider = provider.name(),
5727 prediction_id = id,
5728 suggestion_accepted = accepted,
5729 file_extension = extension,
5730 );
5731 }
5732
5733 pub fn has_active_inline_completion(&self) -> bool {
5734 self.active_inline_completion.is_some()
5735 }
5736
5737 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5738 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5739 return false;
5740 };
5741
5742 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5743 self.clear_highlights::<InlineCompletionHighlight>(cx);
5744 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5745 true
5746 }
5747
5748 /// Returns true when we're displaying the edit prediction popover below the cursor
5749 /// like we are not previewing and the LSP autocomplete menu is visible
5750 /// or we are in `when_holding_modifier` mode.
5751 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5752 if self.edit_prediction_preview_is_active()
5753 || !self.show_edit_predictions_in_menu()
5754 || !self.edit_predictions_enabled()
5755 {
5756 return false;
5757 }
5758
5759 if self.has_visible_completions_menu() {
5760 return true;
5761 }
5762
5763 has_completion && self.edit_prediction_requires_modifier()
5764 }
5765
5766 fn handle_modifiers_changed(
5767 &mut self,
5768 modifiers: Modifiers,
5769 position_map: &PositionMap,
5770 window: &mut Window,
5771 cx: &mut Context<Self>,
5772 ) {
5773 if self.show_edit_predictions_in_menu() {
5774 self.update_edit_prediction_preview(&modifiers, window, cx);
5775 }
5776
5777 self.update_selection_mode(&modifiers, position_map, window, cx);
5778
5779 let mouse_position = window.mouse_position();
5780 if !position_map.text_hitbox.is_hovered(window) {
5781 return;
5782 }
5783
5784 self.update_hovered_link(
5785 position_map.point_for_position(mouse_position),
5786 &position_map.snapshot,
5787 modifiers,
5788 window,
5789 cx,
5790 )
5791 }
5792
5793 fn update_selection_mode(
5794 &mut self,
5795 modifiers: &Modifiers,
5796 position_map: &PositionMap,
5797 window: &mut Window,
5798 cx: &mut Context<Self>,
5799 ) {
5800 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5801 return;
5802 }
5803
5804 let mouse_position = window.mouse_position();
5805 let point_for_position = position_map.point_for_position(mouse_position);
5806 let position = point_for_position.previous_valid;
5807
5808 self.select(
5809 SelectPhase::BeginColumnar {
5810 position,
5811 reset: false,
5812 goal_column: point_for_position.exact_unclipped.column(),
5813 },
5814 window,
5815 cx,
5816 );
5817 }
5818
5819 fn update_edit_prediction_preview(
5820 &mut self,
5821 modifiers: &Modifiers,
5822 window: &mut Window,
5823 cx: &mut Context<Self>,
5824 ) {
5825 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5826 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5827 return;
5828 };
5829
5830 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5831 if matches!(
5832 self.edit_prediction_preview,
5833 EditPredictionPreview::Inactive { .. }
5834 ) {
5835 self.edit_prediction_preview = EditPredictionPreview::Active {
5836 previous_scroll_position: None,
5837 since: Instant::now(),
5838 };
5839
5840 self.update_visible_inline_completion(window, cx);
5841 cx.notify();
5842 }
5843 } else if let EditPredictionPreview::Active {
5844 previous_scroll_position,
5845 since,
5846 } = self.edit_prediction_preview
5847 {
5848 if let (Some(previous_scroll_position), Some(position_map)) =
5849 (previous_scroll_position, self.last_position_map.as_ref())
5850 {
5851 self.set_scroll_position(
5852 previous_scroll_position
5853 .scroll_position(&position_map.snapshot.display_snapshot),
5854 window,
5855 cx,
5856 );
5857 }
5858
5859 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5860 released_too_fast: since.elapsed() < Duration::from_millis(200),
5861 };
5862 self.clear_row_highlights::<EditPredictionPreview>();
5863 self.update_visible_inline_completion(window, cx);
5864 cx.notify();
5865 }
5866 }
5867
5868 fn update_visible_inline_completion(
5869 &mut self,
5870 _window: &mut Window,
5871 cx: &mut Context<Self>,
5872 ) -> Option<()> {
5873 let selection = self.selections.newest_anchor();
5874 let cursor = selection.head();
5875 let multibuffer = self.buffer.read(cx).snapshot(cx);
5876 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5877 let excerpt_id = cursor.excerpt_id;
5878
5879 let show_in_menu = self.show_edit_predictions_in_menu();
5880 let completions_menu_has_precedence = !show_in_menu
5881 && (self.context_menu.borrow().is_some()
5882 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5883
5884 if completions_menu_has_precedence
5885 || !offset_selection.is_empty()
5886 || self
5887 .active_inline_completion
5888 .as_ref()
5889 .map_or(false, |completion| {
5890 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5891 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5892 !invalidation_range.contains(&offset_selection.head())
5893 })
5894 {
5895 self.discard_inline_completion(false, cx);
5896 return None;
5897 }
5898
5899 self.take_active_inline_completion(cx);
5900 let Some(provider) = self.edit_prediction_provider() else {
5901 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5902 return None;
5903 };
5904
5905 let (buffer, cursor_buffer_position) =
5906 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5907
5908 self.edit_prediction_settings =
5909 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5910
5911 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5912
5913 if self.edit_prediction_indent_conflict {
5914 let cursor_point = cursor.to_point(&multibuffer);
5915
5916 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5917
5918 if let Some((_, indent)) = indents.iter().next() {
5919 if indent.len == cursor_point.column {
5920 self.edit_prediction_indent_conflict = false;
5921 }
5922 }
5923 }
5924
5925 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5926 let edits = inline_completion
5927 .edits
5928 .into_iter()
5929 .flat_map(|(range, new_text)| {
5930 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5931 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5932 Some((start..end, new_text))
5933 })
5934 .collect::<Vec<_>>();
5935 if edits.is_empty() {
5936 return None;
5937 }
5938
5939 let first_edit_start = edits.first().unwrap().0.start;
5940 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5941 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5942
5943 let last_edit_end = edits.last().unwrap().0.end;
5944 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5945 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5946
5947 let cursor_row = cursor.to_point(&multibuffer).row;
5948
5949 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5950
5951 let mut inlay_ids = Vec::new();
5952 let invalidation_row_range;
5953 let move_invalidation_row_range = if cursor_row < edit_start_row {
5954 Some(cursor_row..edit_end_row)
5955 } else if cursor_row > edit_end_row {
5956 Some(edit_start_row..cursor_row)
5957 } else {
5958 None
5959 };
5960 let is_move =
5961 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5962 let completion = if is_move {
5963 invalidation_row_range =
5964 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5965 let target = first_edit_start;
5966 InlineCompletion::Move { target, snapshot }
5967 } else {
5968 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5969 && !self.inline_completions_hidden_for_vim_mode;
5970
5971 if show_completions_in_buffer {
5972 if edits
5973 .iter()
5974 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5975 {
5976 let mut inlays = Vec::new();
5977 for (range, new_text) in &edits {
5978 let inlay = Inlay::inline_completion(
5979 post_inc(&mut self.next_inlay_id),
5980 range.start,
5981 new_text.as_str(),
5982 );
5983 inlay_ids.push(inlay.id);
5984 inlays.push(inlay);
5985 }
5986
5987 self.splice_inlays(&[], inlays, cx);
5988 } else {
5989 let background_color = cx.theme().status().deleted_background;
5990 self.highlight_text::<InlineCompletionHighlight>(
5991 edits.iter().map(|(range, _)| range.clone()).collect(),
5992 HighlightStyle {
5993 background_color: Some(background_color),
5994 ..Default::default()
5995 },
5996 cx,
5997 );
5998 }
5999 }
6000
6001 invalidation_row_range = edit_start_row..edit_end_row;
6002
6003 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
6004 if provider.show_tab_accept_marker() {
6005 EditDisplayMode::TabAccept
6006 } else {
6007 EditDisplayMode::Inline
6008 }
6009 } else {
6010 EditDisplayMode::DiffPopover
6011 };
6012
6013 InlineCompletion::Edit {
6014 edits,
6015 edit_preview: inline_completion.edit_preview,
6016 display_mode,
6017 snapshot,
6018 }
6019 };
6020
6021 let invalidation_range = multibuffer
6022 .anchor_before(Point::new(invalidation_row_range.start, 0))
6023 ..multibuffer.anchor_after(Point::new(
6024 invalidation_row_range.end,
6025 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
6026 ));
6027
6028 self.stale_inline_completion_in_menu = None;
6029 self.active_inline_completion = Some(InlineCompletionState {
6030 inlay_ids,
6031 completion,
6032 completion_id: inline_completion.id,
6033 invalidation_range,
6034 });
6035
6036 cx.notify();
6037
6038 Some(())
6039 }
6040
6041 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
6042 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
6043 }
6044
6045 fn render_code_actions_indicator(
6046 &self,
6047 _style: &EditorStyle,
6048 row: DisplayRow,
6049 is_active: bool,
6050 breakpoint: Option<&(Anchor, Breakpoint)>,
6051 cx: &mut Context<Self>,
6052 ) -> Option<IconButton> {
6053 let color = Color::Muted;
6054 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6055
6056 if self.available_code_actions.is_some() {
6057 Some(
6058 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
6059 .shape(ui::IconButtonShape::Square)
6060 .icon_size(IconSize::XSmall)
6061 .icon_color(color)
6062 .toggle_state(is_active)
6063 .tooltip({
6064 let focus_handle = self.focus_handle.clone();
6065 move |window, cx| {
6066 Tooltip::for_action_in(
6067 "Toggle Code Actions",
6068 &ToggleCodeActions {
6069 deployed_from_indicator: None,
6070 },
6071 &focus_handle,
6072 window,
6073 cx,
6074 )
6075 }
6076 })
6077 .on_click(cx.listener(move |editor, _e, window, cx| {
6078 window.focus(&editor.focus_handle(cx));
6079 editor.toggle_code_actions(
6080 &ToggleCodeActions {
6081 deployed_from_indicator: Some(row),
6082 },
6083 window,
6084 cx,
6085 );
6086 }))
6087 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6088 editor.set_breakpoint_context_menu(
6089 row,
6090 position,
6091 event.down.position,
6092 window,
6093 cx,
6094 );
6095 })),
6096 )
6097 } else {
6098 None
6099 }
6100 }
6101
6102 fn clear_tasks(&mut self) {
6103 self.tasks.clear()
6104 }
6105
6106 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6107 if self.tasks.insert(key, value).is_some() {
6108 // This case should hopefully be rare, but just in case...
6109 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6110 }
6111 }
6112
6113 /// Get all display points of breakpoints that will be rendered within editor
6114 ///
6115 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6116 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6117 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6118 fn active_breakpoints(
6119 &mut self,
6120 range: Range<DisplayRow>,
6121 window: &mut Window,
6122 cx: &mut Context<Self>,
6123 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6124 let mut breakpoint_display_points = HashMap::default();
6125
6126 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6127 return breakpoint_display_points;
6128 };
6129
6130 let snapshot = self.snapshot(window, cx);
6131
6132 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6133 let Some(project) = self.project.as_ref() else {
6134 return breakpoint_display_points;
6135 };
6136
6137 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6138 let buffer_snapshot = buffer.read(cx).snapshot();
6139
6140 for breakpoint in
6141 breakpoint_store
6142 .read(cx)
6143 .breakpoints(&buffer, None, &buffer_snapshot, cx)
6144 {
6145 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6146 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6147 anchor.text_anchor = breakpoint.0;
6148
6149 breakpoint_display_points.insert(
6150 snapshot
6151 .point_to_display_point(
6152 MultiBufferPoint {
6153 row: point.row,
6154 column: point.column,
6155 },
6156 Bias::Left,
6157 )
6158 .row(),
6159 (anchor, breakpoint.1.clone()),
6160 );
6161 }
6162
6163 return breakpoint_display_points;
6164 }
6165
6166 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6167 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6168
6169 for (buffer_snapshot, range, excerpt_id) in
6170 multi_buffer_snapshot.range_to_buffer_ranges(range)
6171 {
6172 let Some(buffer) = project.read_with(cx, |this, cx| {
6173 this.buffer_for_id(buffer_snapshot.remote_id(), cx)
6174 }) else {
6175 continue;
6176 };
6177 let breakpoints = breakpoint_store.read(cx).breakpoints(
6178 &buffer,
6179 Some(
6180 buffer_snapshot.anchor_before(range.start)
6181 ..buffer_snapshot.anchor_after(range.end),
6182 ),
6183 buffer_snapshot,
6184 cx,
6185 );
6186 for (anchor, breakpoint) in breakpoints {
6187 let multi_buffer_anchor =
6188 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), *anchor);
6189 let position = multi_buffer_anchor
6190 .to_point(&multi_buffer_snapshot)
6191 .to_display_point(&snapshot);
6192
6193 breakpoint_display_points
6194 .insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
6195 }
6196 }
6197
6198 breakpoint_display_points
6199 }
6200
6201 fn breakpoint_context_menu(
6202 &self,
6203 anchor: Anchor,
6204 window: &mut Window,
6205 cx: &mut Context<Self>,
6206 ) -> Entity<ui::ContextMenu> {
6207 let weak_editor = cx.weak_entity();
6208 let focus_handle = self.focus_handle(cx);
6209
6210 let row = self
6211 .buffer
6212 .read(cx)
6213 .snapshot(cx)
6214 .summary_for_anchor::<Point>(&anchor)
6215 .row;
6216
6217 let breakpoint = self
6218 .breakpoint_at_row(row, window, cx)
6219 .map(|(_, bp)| Arc::from(bp));
6220
6221 let log_breakpoint_msg = if breakpoint
6222 .as_ref()
6223 .is_some_and(|bp| bp.kind.log_message().is_some())
6224 {
6225 "Edit Log Breakpoint"
6226 } else {
6227 "Set Log Breakpoint"
6228 };
6229
6230 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
6231 "Unset Breakpoint"
6232 } else {
6233 "Set Breakpoint"
6234 };
6235
6236 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.state {
6237 BreakpointState::Enabled => Some("Disable"),
6238 BreakpointState::Disabled => Some("Enable"),
6239 });
6240
6241 let breakpoint = breakpoint.unwrap_or_else(|| {
6242 Arc::new(Breakpoint {
6243 state: BreakpointState::Enabled,
6244 kind: BreakpointKind::Standard,
6245 })
6246 });
6247
6248 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6249 menu.on_blur_subscription(Subscription::new(|| {}))
6250 .context(focus_handle)
6251 .when_some(toggle_state_msg, |this, msg| {
6252 this.entry(msg, None, {
6253 let weak_editor = weak_editor.clone();
6254 let breakpoint = breakpoint.clone();
6255 move |_window, cx| {
6256 weak_editor
6257 .update(cx, |this, cx| {
6258 this.edit_breakpoint_at_anchor(
6259 anchor,
6260 breakpoint.as_ref().clone(),
6261 BreakpointEditAction::InvertState,
6262 cx,
6263 );
6264 })
6265 .log_err();
6266 }
6267 })
6268 })
6269 .entry(set_breakpoint_msg, None, {
6270 let weak_editor = weak_editor.clone();
6271 let breakpoint = breakpoint.clone();
6272 move |_window, cx| {
6273 weak_editor
6274 .update(cx, |this, cx| {
6275 this.edit_breakpoint_at_anchor(
6276 anchor,
6277 breakpoint.as_ref().clone(),
6278 BreakpointEditAction::Toggle,
6279 cx,
6280 );
6281 })
6282 .log_err();
6283 }
6284 })
6285 .entry(log_breakpoint_msg, None, move |window, cx| {
6286 weak_editor
6287 .update(cx, |this, cx| {
6288 this.add_edit_breakpoint_block(anchor, breakpoint.as_ref(), window, cx);
6289 })
6290 .log_err();
6291 })
6292 })
6293 }
6294
6295 fn render_breakpoint(
6296 &self,
6297 position: Anchor,
6298 row: DisplayRow,
6299 breakpoint: &Breakpoint,
6300 cx: &mut Context<Self>,
6301 ) -> IconButton {
6302 let (color, icon) = {
6303 let color = if self
6304 .gutter_breakpoint_indicator
6305 .is_some_and(|point| point.row() == row)
6306 {
6307 Color::Hint
6308 } else if breakpoint.is_disabled() {
6309 Color::Custom(Color::Debugger.color(cx).opacity(0.5))
6310 } else {
6311 Color::Debugger
6312 };
6313 let icon = match &breakpoint.kind {
6314 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6315 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6316 };
6317 (color, icon)
6318 };
6319
6320 let breakpoint = Arc::from(breakpoint.clone());
6321
6322 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6323 .icon_size(IconSize::XSmall)
6324 .size(ui::ButtonSize::None)
6325 .icon_color(color)
6326 .style(ButtonStyle::Transparent)
6327 .on_click(cx.listener({
6328 let breakpoint = breakpoint.clone();
6329
6330 move |editor, _e, window, cx| {
6331 window.focus(&editor.focus_handle(cx));
6332 editor.edit_breakpoint_at_anchor(
6333 position,
6334 breakpoint.as_ref().clone(),
6335 BreakpointEditAction::Toggle,
6336 cx,
6337 );
6338 }
6339 }))
6340 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6341 editor.set_breakpoint_context_menu(
6342 row,
6343 Some(position),
6344 event.down.position,
6345 window,
6346 cx,
6347 );
6348 }))
6349 }
6350
6351 fn build_tasks_context(
6352 project: &Entity<Project>,
6353 buffer: &Entity<Buffer>,
6354 buffer_row: u32,
6355 tasks: &Arc<RunnableTasks>,
6356 cx: &mut Context<Self>,
6357 ) -> Task<Option<task::TaskContext>> {
6358 let position = Point::new(buffer_row, tasks.column);
6359 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6360 let location = Location {
6361 buffer: buffer.clone(),
6362 range: range_start..range_start,
6363 };
6364 // Fill in the environmental variables from the tree-sitter captures
6365 let mut captured_task_variables = TaskVariables::default();
6366 for (capture_name, value) in tasks.extra_variables.clone() {
6367 captured_task_variables.insert(
6368 task::VariableName::Custom(capture_name.into()),
6369 value.clone(),
6370 );
6371 }
6372 project.update(cx, |project, cx| {
6373 project.task_store().update(cx, |task_store, cx| {
6374 task_store.task_context_for_location(captured_task_variables, location, cx)
6375 })
6376 })
6377 }
6378
6379 pub fn spawn_nearest_task(
6380 &mut self,
6381 action: &SpawnNearestTask,
6382 window: &mut Window,
6383 cx: &mut Context<Self>,
6384 ) {
6385 let Some((workspace, _)) = self.workspace.clone() else {
6386 return;
6387 };
6388 let Some(project) = self.project.clone() else {
6389 return;
6390 };
6391
6392 // Try to find a closest, enclosing node using tree-sitter that has a
6393 // task
6394 let Some((buffer, buffer_row, tasks)) = self
6395 .find_enclosing_node_task(cx)
6396 // Or find the task that's closest in row-distance.
6397 .or_else(|| self.find_closest_task(cx))
6398 else {
6399 return;
6400 };
6401
6402 let reveal_strategy = action.reveal;
6403 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6404 cx.spawn_in(window, async move |_, cx| {
6405 let context = task_context.await?;
6406 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6407
6408 let resolved = resolved_task.resolved.as_mut()?;
6409 resolved.reveal = reveal_strategy;
6410
6411 workspace
6412 .update(cx, |workspace, cx| {
6413 workspace::tasks::schedule_resolved_task(
6414 workspace,
6415 task_source_kind,
6416 resolved_task,
6417 false,
6418 cx,
6419 );
6420 })
6421 .ok()
6422 })
6423 .detach();
6424 }
6425
6426 fn find_closest_task(
6427 &mut self,
6428 cx: &mut Context<Self>,
6429 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6430 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6431
6432 let ((buffer_id, row), tasks) = self
6433 .tasks
6434 .iter()
6435 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6436
6437 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6438 let tasks = Arc::new(tasks.to_owned());
6439 Some((buffer, *row, tasks))
6440 }
6441
6442 fn find_enclosing_node_task(
6443 &mut self,
6444 cx: &mut Context<Self>,
6445 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6446 let snapshot = self.buffer.read(cx).snapshot(cx);
6447 let offset = self.selections.newest::<usize>(cx).head();
6448 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6449 let buffer_id = excerpt.buffer().remote_id();
6450
6451 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6452 let mut cursor = layer.node().walk();
6453
6454 while cursor.goto_first_child_for_byte(offset).is_some() {
6455 if cursor.node().end_byte() == offset {
6456 cursor.goto_next_sibling();
6457 }
6458 }
6459
6460 // Ascend to the smallest ancestor that contains the range and has a task.
6461 loop {
6462 let node = cursor.node();
6463 let node_range = node.byte_range();
6464 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6465
6466 // Check if this node contains our offset
6467 if node_range.start <= offset && node_range.end >= offset {
6468 // If it contains offset, check for task
6469 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6470 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6471 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6472 }
6473 }
6474
6475 if !cursor.goto_parent() {
6476 break;
6477 }
6478 }
6479 None
6480 }
6481
6482 fn render_run_indicator(
6483 &self,
6484 _style: &EditorStyle,
6485 is_active: bool,
6486 row: DisplayRow,
6487 breakpoint: Option<(Anchor, Breakpoint)>,
6488 cx: &mut Context<Self>,
6489 ) -> IconButton {
6490 let color = Color::Muted;
6491 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6492
6493 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6494 .shape(ui::IconButtonShape::Square)
6495 .icon_size(IconSize::XSmall)
6496 .icon_color(color)
6497 .toggle_state(is_active)
6498 .on_click(cx.listener(move |editor, _e, window, cx| {
6499 window.focus(&editor.focus_handle(cx));
6500 editor.toggle_code_actions(
6501 &ToggleCodeActions {
6502 deployed_from_indicator: Some(row),
6503 },
6504 window,
6505 cx,
6506 );
6507 }))
6508 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6509 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
6510 }))
6511 }
6512
6513 pub fn context_menu_visible(&self) -> bool {
6514 !self.edit_prediction_preview_is_active()
6515 && self
6516 .context_menu
6517 .borrow()
6518 .as_ref()
6519 .map_or(false, |menu| menu.visible())
6520 }
6521
6522 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6523 self.context_menu
6524 .borrow()
6525 .as_ref()
6526 .map(|menu| menu.origin())
6527 }
6528
6529 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
6530 self.context_menu_options = Some(options);
6531 }
6532
6533 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6534 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6535
6536 fn render_edit_prediction_popover(
6537 &mut self,
6538 text_bounds: &Bounds<Pixels>,
6539 content_origin: gpui::Point<Pixels>,
6540 editor_snapshot: &EditorSnapshot,
6541 visible_row_range: Range<DisplayRow>,
6542 scroll_top: f32,
6543 scroll_bottom: f32,
6544 line_layouts: &[LineWithInvisibles],
6545 line_height: Pixels,
6546 scroll_pixel_position: gpui::Point<Pixels>,
6547 newest_selection_head: Option<DisplayPoint>,
6548 editor_width: Pixels,
6549 style: &EditorStyle,
6550 window: &mut Window,
6551 cx: &mut App,
6552 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6553 let active_inline_completion = self.active_inline_completion.as_ref()?;
6554
6555 if self.edit_prediction_visible_in_cursor_popover(true) {
6556 return None;
6557 }
6558
6559 match &active_inline_completion.completion {
6560 InlineCompletion::Move { target, .. } => {
6561 let target_display_point = target.to_display_point(editor_snapshot);
6562
6563 if self.edit_prediction_requires_modifier() {
6564 if !self.edit_prediction_preview_is_active() {
6565 return None;
6566 }
6567
6568 self.render_edit_prediction_modifier_jump_popover(
6569 text_bounds,
6570 content_origin,
6571 visible_row_range,
6572 line_layouts,
6573 line_height,
6574 scroll_pixel_position,
6575 newest_selection_head,
6576 target_display_point,
6577 window,
6578 cx,
6579 )
6580 } else {
6581 self.render_edit_prediction_eager_jump_popover(
6582 text_bounds,
6583 content_origin,
6584 editor_snapshot,
6585 visible_row_range,
6586 scroll_top,
6587 scroll_bottom,
6588 line_height,
6589 scroll_pixel_position,
6590 target_display_point,
6591 editor_width,
6592 window,
6593 cx,
6594 )
6595 }
6596 }
6597 InlineCompletion::Edit {
6598 display_mode: EditDisplayMode::Inline,
6599 ..
6600 } => None,
6601 InlineCompletion::Edit {
6602 display_mode: EditDisplayMode::TabAccept,
6603 edits,
6604 ..
6605 } => {
6606 let range = &edits.first()?.0;
6607 let target_display_point = range.end.to_display_point(editor_snapshot);
6608
6609 self.render_edit_prediction_end_of_line_popover(
6610 "Accept",
6611 editor_snapshot,
6612 visible_row_range,
6613 target_display_point,
6614 line_height,
6615 scroll_pixel_position,
6616 content_origin,
6617 editor_width,
6618 window,
6619 cx,
6620 )
6621 }
6622 InlineCompletion::Edit {
6623 edits,
6624 edit_preview,
6625 display_mode: EditDisplayMode::DiffPopover,
6626 snapshot,
6627 } => self.render_edit_prediction_diff_popover(
6628 text_bounds,
6629 content_origin,
6630 editor_snapshot,
6631 visible_row_range,
6632 line_layouts,
6633 line_height,
6634 scroll_pixel_position,
6635 newest_selection_head,
6636 editor_width,
6637 style,
6638 edits,
6639 edit_preview,
6640 snapshot,
6641 window,
6642 cx,
6643 ),
6644 }
6645 }
6646
6647 fn render_edit_prediction_modifier_jump_popover(
6648 &mut self,
6649 text_bounds: &Bounds<Pixels>,
6650 content_origin: gpui::Point<Pixels>,
6651 visible_row_range: Range<DisplayRow>,
6652 line_layouts: &[LineWithInvisibles],
6653 line_height: Pixels,
6654 scroll_pixel_position: gpui::Point<Pixels>,
6655 newest_selection_head: Option<DisplayPoint>,
6656 target_display_point: DisplayPoint,
6657 window: &mut Window,
6658 cx: &mut App,
6659 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6660 let scrolled_content_origin =
6661 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6662
6663 const SCROLL_PADDING_Y: Pixels = px(12.);
6664
6665 if target_display_point.row() < visible_row_range.start {
6666 return self.render_edit_prediction_scroll_popover(
6667 |_| SCROLL_PADDING_Y,
6668 IconName::ArrowUp,
6669 visible_row_range,
6670 line_layouts,
6671 newest_selection_head,
6672 scrolled_content_origin,
6673 window,
6674 cx,
6675 );
6676 } else if target_display_point.row() >= visible_row_range.end {
6677 return self.render_edit_prediction_scroll_popover(
6678 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6679 IconName::ArrowDown,
6680 visible_row_range,
6681 line_layouts,
6682 newest_selection_head,
6683 scrolled_content_origin,
6684 window,
6685 cx,
6686 );
6687 }
6688
6689 const POLE_WIDTH: Pixels = px(2.);
6690
6691 let line_layout =
6692 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6693 let target_column = target_display_point.column() as usize;
6694
6695 let target_x = line_layout.x_for_index(target_column);
6696 let target_y =
6697 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6698
6699 let flag_on_right = target_x < text_bounds.size.width / 2.;
6700
6701 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6702 border_color.l += 0.001;
6703
6704 let mut element = v_flex()
6705 .items_end()
6706 .when(flag_on_right, |el| el.items_start())
6707 .child(if flag_on_right {
6708 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6709 .rounded_bl(px(0.))
6710 .rounded_tl(px(0.))
6711 .border_l_2()
6712 .border_color(border_color)
6713 } else {
6714 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6715 .rounded_br(px(0.))
6716 .rounded_tr(px(0.))
6717 .border_r_2()
6718 .border_color(border_color)
6719 })
6720 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6721 .into_any();
6722
6723 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6724
6725 let mut origin = scrolled_content_origin + point(target_x, target_y)
6726 - point(
6727 if flag_on_right {
6728 POLE_WIDTH
6729 } else {
6730 size.width - POLE_WIDTH
6731 },
6732 size.height - line_height,
6733 );
6734
6735 origin.x = origin.x.max(content_origin.x);
6736
6737 element.prepaint_at(origin, window, cx);
6738
6739 Some((element, origin))
6740 }
6741
6742 fn render_edit_prediction_scroll_popover(
6743 &mut self,
6744 to_y: impl Fn(Size<Pixels>) -> Pixels,
6745 scroll_icon: IconName,
6746 visible_row_range: Range<DisplayRow>,
6747 line_layouts: &[LineWithInvisibles],
6748 newest_selection_head: Option<DisplayPoint>,
6749 scrolled_content_origin: gpui::Point<Pixels>,
6750 window: &mut Window,
6751 cx: &mut App,
6752 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6753 let mut element = self
6754 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6755 .into_any();
6756
6757 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6758
6759 let cursor = newest_selection_head?;
6760 let cursor_row_layout =
6761 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6762 let cursor_column = cursor.column() as usize;
6763
6764 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6765
6766 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6767
6768 element.prepaint_at(origin, window, cx);
6769 Some((element, origin))
6770 }
6771
6772 fn render_edit_prediction_eager_jump_popover(
6773 &mut self,
6774 text_bounds: &Bounds<Pixels>,
6775 content_origin: gpui::Point<Pixels>,
6776 editor_snapshot: &EditorSnapshot,
6777 visible_row_range: Range<DisplayRow>,
6778 scroll_top: f32,
6779 scroll_bottom: f32,
6780 line_height: Pixels,
6781 scroll_pixel_position: gpui::Point<Pixels>,
6782 target_display_point: DisplayPoint,
6783 editor_width: Pixels,
6784 window: &mut Window,
6785 cx: &mut App,
6786 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6787 if target_display_point.row().as_f32() < scroll_top {
6788 let mut element = self
6789 .render_edit_prediction_line_popover(
6790 "Jump to Edit",
6791 Some(IconName::ArrowUp),
6792 window,
6793 cx,
6794 )?
6795 .into_any();
6796
6797 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6798 let offset = point(
6799 (text_bounds.size.width - size.width) / 2.,
6800 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6801 );
6802
6803 let origin = text_bounds.origin + offset;
6804 element.prepaint_at(origin, window, cx);
6805 Some((element, origin))
6806 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6807 let mut element = self
6808 .render_edit_prediction_line_popover(
6809 "Jump to Edit",
6810 Some(IconName::ArrowDown),
6811 window,
6812 cx,
6813 )?
6814 .into_any();
6815
6816 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6817 let offset = point(
6818 (text_bounds.size.width - size.width) / 2.,
6819 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6820 );
6821
6822 let origin = text_bounds.origin + offset;
6823 element.prepaint_at(origin, window, cx);
6824 Some((element, origin))
6825 } else {
6826 self.render_edit_prediction_end_of_line_popover(
6827 "Jump to Edit",
6828 editor_snapshot,
6829 visible_row_range,
6830 target_display_point,
6831 line_height,
6832 scroll_pixel_position,
6833 content_origin,
6834 editor_width,
6835 window,
6836 cx,
6837 )
6838 }
6839 }
6840
6841 fn render_edit_prediction_end_of_line_popover(
6842 self: &mut Editor,
6843 label: &'static str,
6844 editor_snapshot: &EditorSnapshot,
6845 visible_row_range: Range<DisplayRow>,
6846 target_display_point: DisplayPoint,
6847 line_height: Pixels,
6848 scroll_pixel_position: gpui::Point<Pixels>,
6849 content_origin: gpui::Point<Pixels>,
6850 editor_width: Pixels,
6851 window: &mut Window,
6852 cx: &mut App,
6853 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6854 let target_line_end = DisplayPoint::new(
6855 target_display_point.row(),
6856 editor_snapshot.line_len(target_display_point.row()),
6857 );
6858
6859 let mut element = self
6860 .render_edit_prediction_line_popover(label, None, window, cx)?
6861 .into_any();
6862
6863 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6864
6865 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6866
6867 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6868 let mut origin = start_point
6869 + line_origin
6870 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6871 origin.x = origin.x.max(content_origin.x);
6872
6873 let max_x = content_origin.x + editor_width - size.width;
6874
6875 if origin.x > max_x {
6876 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6877
6878 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6879 origin.y += offset;
6880 IconName::ArrowUp
6881 } else {
6882 origin.y -= offset;
6883 IconName::ArrowDown
6884 };
6885
6886 element = self
6887 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6888 .into_any();
6889
6890 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6891
6892 origin.x = content_origin.x + editor_width - size.width - px(2.);
6893 }
6894
6895 element.prepaint_at(origin, window, cx);
6896 Some((element, origin))
6897 }
6898
6899 fn render_edit_prediction_diff_popover(
6900 self: &Editor,
6901 text_bounds: &Bounds<Pixels>,
6902 content_origin: gpui::Point<Pixels>,
6903 editor_snapshot: &EditorSnapshot,
6904 visible_row_range: Range<DisplayRow>,
6905 line_layouts: &[LineWithInvisibles],
6906 line_height: Pixels,
6907 scroll_pixel_position: gpui::Point<Pixels>,
6908 newest_selection_head: Option<DisplayPoint>,
6909 editor_width: Pixels,
6910 style: &EditorStyle,
6911 edits: &Vec<(Range<Anchor>, String)>,
6912 edit_preview: &Option<language::EditPreview>,
6913 snapshot: &language::BufferSnapshot,
6914 window: &mut Window,
6915 cx: &mut App,
6916 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6917 let edit_start = edits
6918 .first()
6919 .unwrap()
6920 .0
6921 .start
6922 .to_display_point(editor_snapshot);
6923 let edit_end = edits
6924 .last()
6925 .unwrap()
6926 .0
6927 .end
6928 .to_display_point(editor_snapshot);
6929
6930 let is_visible = visible_row_range.contains(&edit_start.row())
6931 || visible_row_range.contains(&edit_end.row());
6932 if !is_visible {
6933 return None;
6934 }
6935
6936 let highlighted_edits =
6937 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6938
6939 let styled_text = highlighted_edits.to_styled_text(&style.text);
6940 let line_count = highlighted_edits.text.lines().count();
6941
6942 const BORDER_WIDTH: Pixels = px(1.);
6943
6944 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6945 let has_keybind = keybind.is_some();
6946
6947 let mut element = h_flex()
6948 .items_start()
6949 .child(
6950 h_flex()
6951 .bg(cx.theme().colors().editor_background)
6952 .border(BORDER_WIDTH)
6953 .shadow_sm()
6954 .border_color(cx.theme().colors().border)
6955 .rounded_l_lg()
6956 .when(line_count > 1, |el| el.rounded_br_lg())
6957 .pr_1()
6958 .child(styled_text),
6959 )
6960 .child(
6961 h_flex()
6962 .h(line_height + BORDER_WIDTH * 2.)
6963 .px_1p5()
6964 .gap_1()
6965 // Workaround: For some reason, there's a gap if we don't do this
6966 .ml(-BORDER_WIDTH)
6967 .shadow(smallvec![gpui::BoxShadow {
6968 color: gpui::black().opacity(0.05),
6969 offset: point(px(1.), px(1.)),
6970 blur_radius: px(2.),
6971 spread_radius: px(0.),
6972 }])
6973 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6974 .border(BORDER_WIDTH)
6975 .border_color(cx.theme().colors().border)
6976 .rounded_r_lg()
6977 .id("edit_prediction_diff_popover_keybind")
6978 .when(!has_keybind, |el| {
6979 let status_colors = cx.theme().status();
6980
6981 el.bg(status_colors.error_background)
6982 .border_color(status_colors.error.opacity(0.6))
6983 .child(Icon::new(IconName::Info).color(Color::Error))
6984 .cursor_default()
6985 .hoverable_tooltip(move |_window, cx| {
6986 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6987 })
6988 })
6989 .children(keybind),
6990 )
6991 .into_any();
6992
6993 let longest_row =
6994 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6995 let longest_line_width = if visible_row_range.contains(&longest_row) {
6996 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6997 } else {
6998 layout_line(
6999 longest_row,
7000 editor_snapshot,
7001 style,
7002 editor_width,
7003 |_| false,
7004 window,
7005 cx,
7006 )
7007 .width
7008 };
7009
7010 let viewport_bounds =
7011 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
7012 right: -EditorElement::SCROLLBAR_WIDTH,
7013 ..Default::default()
7014 });
7015
7016 let x_after_longest =
7017 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
7018 - scroll_pixel_position.x;
7019
7020 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
7021
7022 // Fully visible if it can be displayed within the window (allow overlapping other
7023 // panes). However, this is only allowed if the popover starts within text_bounds.
7024 let can_position_to_the_right = x_after_longest < text_bounds.right()
7025 && x_after_longest + element_bounds.width < viewport_bounds.right();
7026
7027 let mut origin = if can_position_to_the_right {
7028 point(
7029 x_after_longest,
7030 text_bounds.origin.y + edit_start.row().as_f32() * line_height
7031 - scroll_pixel_position.y,
7032 )
7033 } else {
7034 let cursor_row = newest_selection_head.map(|head| head.row());
7035 let above_edit = edit_start
7036 .row()
7037 .0
7038 .checked_sub(line_count as u32)
7039 .map(DisplayRow);
7040 let below_edit = Some(edit_end.row() + 1);
7041 let above_cursor =
7042 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
7043 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
7044
7045 // Place the edit popover adjacent to the edit if there is a location
7046 // available that is onscreen and does not obscure the cursor. Otherwise,
7047 // place it adjacent to the cursor.
7048 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
7049 .into_iter()
7050 .flatten()
7051 .find(|&start_row| {
7052 let end_row = start_row + line_count as u32;
7053 visible_row_range.contains(&start_row)
7054 && visible_row_range.contains(&end_row)
7055 && cursor_row.map_or(true, |cursor_row| {
7056 !((start_row..end_row).contains(&cursor_row))
7057 })
7058 })?;
7059
7060 content_origin
7061 + point(
7062 -scroll_pixel_position.x,
7063 row_target.as_f32() * line_height - scroll_pixel_position.y,
7064 )
7065 };
7066
7067 origin.x -= BORDER_WIDTH;
7068
7069 window.defer_draw(element, origin, 1);
7070
7071 // Do not return an element, since it will already be drawn due to defer_draw.
7072 None
7073 }
7074
7075 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
7076 px(30.)
7077 }
7078
7079 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
7080 if self.read_only(cx) {
7081 cx.theme().players().read_only()
7082 } else {
7083 self.style.as_ref().unwrap().local_player
7084 }
7085 }
7086
7087 fn render_edit_prediction_accept_keybind(
7088 &self,
7089 window: &mut Window,
7090 cx: &App,
7091 ) -> Option<AnyElement> {
7092 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
7093 let accept_keystroke = accept_binding.keystroke()?;
7094
7095 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7096
7097 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
7098 Color::Accent
7099 } else {
7100 Color::Muted
7101 };
7102
7103 h_flex()
7104 .px_0p5()
7105 .when(is_platform_style_mac, |parent| parent.gap_0p5())
7106 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7107 .text_size(TextSize::XSmall.rems(cx))
7108 .child(h_flex().children(ui::render_modifiers(
7109 &accept_keystroke.modifiers,
7110 PlatformStyle::platform(),
7111 Some(modifiers_color),
7112 Some(IconSize::XSmall.rems().into()),
7113 true,
7114 )))
7115 .when(is_platform_style_mac, |parent| {
7116 parent.child(accept_keystroke.key.clone())
7117 })
7118 .when(!is_platform_style_mac, |parent| {
7119 parent.child(
7120 Key::new(
7121 util::capitalize(&accept_keystroke.key),
7122 Some(Color::Default),
7123 )
7124 .size(Some(IconSize::XSmall.rems().into())),
7125 )
7126 })
7127 .into_any()
7128 .into()
7129 }
7130
7131 fn render_edit_prediction_line_popover(
7132 &self,
7133 label: impl Into<SharedString>,
7134 icon: Option<IconName>,
7135 window: &mut Window,
7136 cx: &App,
7137 ) -> Option<Stateful<Div>> {
7138 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7139
7140 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7141 let has_keybind = keybind.is_some();
7142
7143 let result = h_flex()
7144 .id("ep-line-popover")
7145 .py_0p5()
7146 .pl_1()
7147 .pr(padding_right)
7148 .gap_1()
7149 .rounded_md()
7150 .border_1()
7151 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7152 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7153 .shadow_sm()
7154 .when(!has_keybind, |el| {
7155 let status_colors = cx.theme().status();
7156
7157 el.bg(status_colors.error_background)
7158 .border_color(status_colors.error.opacity(0.6))
7159 .pl_2()
7160 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7161 .cursor_default()
7162 .hoverable_tooltip(move |_window, cx| {
7163 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7164 })
7165 })
7166 .children(keybind)
7167 .child(
7168 Label::new(label)
7169 .size(LabelSize::Small)
7170 .when(!has_keybind, |el| {
7171 el.color(cx.theme().status().error.into()).strikethrough()
7172 }),
7173 )
7174 .when(!has_keybind, |el| {
7175 el.child(
7176 h_flex().ml_1().child(
7177 Icon::new(IconName::Info)
7178 .size(IconSize::Small)
7179 .color(cx.theme().status().error.into()),
7180 ),
7181 )
7182 })
7183 .when_some(icon, |element, icon| {
7184 element.child(
7185 div()
7186 .mt(px(1.5))
7187 .child(Icon::new(icon).size(IconSize::Small)),
7188 )
7189 });
7190
7191 Some(result)
7192 }
7193
7194 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7195 let accent_color = cx.theme().colors().text_accent;
7196 let editor_bg_color = cx.theme().colors().editor_background;
7197 editor_bg_color.blend(accent_color.opacity(0.1))
7198 }
7199
7200 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7201 let accent_color = cx.theme().colors().text_accent;
7202 let editor_bg_color = cx.theme().colors().editor_background;
7203 editor_bg_color.blend(accent_color.opacity(0.6))
7204 }
7205
7206 fn render_edit_prediction_cursor_popover(
7207 &self,
7208 min_width: Pixels,
7209 max_width: Pixels,
7210 cursor_point: Point,
7211 style: &EditorStyle,
7212 accept_keystroke: Option<&gpui::Keystroke>,
7213 _window: &Window,
7214 cx: &mut Context<Editor>,
7215 ) -> Option<AnyElement> {
7216 let provider = self.edit_prediction_provider.as_ref()?;
7217
7218 if provider.provider.needs_terms_acceptance(cx) {
7219 return Some(
7220 h_flex()
7221 .min_w(min_width)
7222 .flex_1()
7223 .px_2()
7224 .py_1()
7225 .gap_3()
7226 .elevation_2(cx)
7227 .hover(|style| style.bg(cx.theme().colors().element_hover))
7228 .id("accept-terms")
7229 .cursor_pointer()
7230 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7231 .on_click(cx.listener(|this, _event, window, cx| {
7232 cx.stop_propagation();
7233 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7234 window.dispatch_action(
7235 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7236 cx,
7237 );
7238 }))
7239 .child(
7240 h_flex()
7241 .flex_1()
7242 .gap_2()
7243 .child(Icon::new(IconName::ZedPredict))
7244 .child(Label::new("Accept Terms of Service"))
7245 .child(div().w_full())
7246 .child(
7247 Icon::new(IconName::ArrowUpRight)
7248 .color(Color::Muted)
7249 .size(IconSize::Small),
7250 )
7251 .into_any_element(),
7252 )
7253 .into_any(),
7254 );
7255 }
7256
7257 let is_refreshing = provider.provider.is_refreshing(cx);
7258
7259 fn pending_completion_container() -> Div {
7260 h_flex()
7261 .h_full()
7262 .flex_1()
7263 .gap_2()
7264 .child(Icon::new(IconName::ZedPredict))
7265 }
7266
7267 let completion = match &self.active_inline_completion {
7268 Some(prediction) => {
7269 if !self.has_visible_completions_menu() {
7270 const RADIUS: Pixels = px(6.);
7271 const BORDER_WIDTH: Pixels = px(1.);
7272
7273 return Some(
7274 h_flex()
7275 .elevation_2(cx)
7276 .border(BORDER_WIDTH)
7277 .border_color(cx.theme().colors().border)
7278 .when(accept_keystroke.is_none(), |el| {
7279 el.border_color(cx.theme().status().error)
7280 })
7281 .rounded(RADIUS)
7282 .rounded_tl(px(0.))
7283 .overflow_hidden()
7284 .child(div().px_1p5().child(match &prediction.completion {
7285 InlineCompletion::Move { target, snapshot } => {
7286 use text::ToPoint as _;
7287 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7288 {
7289 Icon::new(IconName::ZedPredictDown)
7290 } else {
7291 Icon::new(IconName::ZedPredictUp)
7292 }
7293 }
7294 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7295 }))
7296 .child(
7297 h_flex()
7298 .gap_1()
7299 .py_1()
7300 .px_2()
7301 .rounded_r(RADIUS - BORDER_WIDTH)
7302 .border_l_1()
7303 .border_color(cx.theme().colors().border)
7304 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7305 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7306 el.child(
7307 Label::new("Hold")
7308 .size(LabelSize::Small)
7309 .when(accept_keystroke.is_none(), |el| {
7310 el.strikethrough()
7311 })
7312 .line_height_style(LineHeightStyle::UiLabel),
7313 )
7314 })
7315 .id("edit_prediction_cursor_popover_keybind")
7316 .when(accept_keystroke.is_none(), |el| {
7317 let status_colors = cx.theme().status();
7318
7319 el.bg(status_colors.error_background)
7320 .border_color(status_colors.error.opacity(0.6))
7321 .child(Icon::new(IconName::Info).color(Color::Error))
7322 .cursor_default()
7323 .hoverable_tooltip(move |_window, cx| {
7324 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7325 .into()
7326 })
7327 })
7328 .when_some(
7329 accept_keystroke.as_ref(),
7330 |el, accept_keystroke| {
7331 el.child(h_flex().children(ui::render_modifiers(
7332 &accept_keystroke.modifiers,
7333 PlatformStyle::platform(),
7334 Some(Color::Default),
7335 Some(IconSize::XSmall.rems().into()),
7336 false,
7337 )))
7338 },
7339 ),
7340 )
7341 .into_any(),
7342 );
7343 }
7344
7345 self.render_edit_prediction_cursor_popover_preview(
7346 prediction,
7347 cursor_point,
7348 style,
7349 cx,
7350 )?
7351 }
7352
7353 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7354 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7355 stale_completion,
7356 cursor_point,
7357 style,
7358 cx,
7359 )?,
7360
7361 None => {
7362 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7363 }
7364 },
7365
7366 None => pending_completion_container().child(Label::new("No Prediction")),
7367 };
7368
7369 let completion = if is_refreshing {
7370 completion
7371 .with_animation(
7372 "loading-completion",
7373 Animation::new(Duration::from_secs(2))
7374 .repeat()
7375 .with_easing(pulsating_between(0.4, 0.8)),
7376 |label, delta| label.opacity(delta),
7377 )
7378 .into_any_element()
7379 } else {
7380 completion.into_any_element()
7381 };
7382
7383 let has_completion = self.active_inline_completion.is_some();
7384
7385 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7386 Some(
7387 h_flex()
7388 .min_w(min_width)
7389 .max_w(max_width)
7390 .flex_1()
7391 .elevation_2(cx)
7392 .border_color(cx.theme().colors().border)
7393 .child(
7394 div()
7395 .flex_1()
7396 .py_1()
7397 .px_2()
7398 .overflow_hidden()
7399 .child(completion),
7400 )
7401 .when_some(accept_keystroke, |el, accept_keystroke| {
7402 if !accept_keystroke.modifiers.modified() {
7403 return el;
7404 }
7405
7406 el.child(
7407 h_flex()
7408 .h_full()
7409 .border_l_1()
7410 .rounded_r_lg()
7411 .border_color(cx.theme().colors().border)
7412 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7413 .gap_1()
7414 .py_1()
7415 .px_2()
7416 .child(
7417 h_flex()
7418 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7419 .when(is_platform_style_mac, |parent| parent.gap_1())
7420 .child(h_flex().children(ui::render_modifiers(
7421 &accept_keystroke.modifiers,
7422 PlatformStyle::platform(),
7423 Some(if !has_completion {
7424 Color::Muted
7425 } else {
7426 Color::Default
7427 }),
7428 None,
7429 false,
7430 ))),
7431 )
7432 .child(Label::new("Preview").into_any_element())
7433 .opacity(if has_completion { 1.0 } else { 0.4 }),
7434 )
7435 })
7436 .into_any(),
7437 )
7438 }
7439
7440 fn render_edit_prediction_cursor_popover_preview(
7441 &self,
7442 completion: &InlineCompletionState,
7443 cursor_point: Point,
7444 style: &EditorStyle,
7445 cx: &mut Context<Editor>,
7446 ) -> Option<Div> {
7447 use text::ToPoint as _;
7448
7449 fn render_relative_row_jump(
7450 prefix: impl Into<String>,
7451 current_row: u32,
7452 target_row: u32,
7453 ) -> Div {
7454 let (row_diff, arrow) = if target_row < current_row {
7455 (current_row - target_row, IconName::ArrowUp)
7456 } else {
7457 (target_row - current_row, IconName::ArrowDown)
7458 };
7459
7460 h_flex()
7461 .child(
7462 Label::new(format!("{}{}", prefix.into(), row_diff))
7463 .color(Color::Muted)
7464 .size(LabelSize::Small),
7465 )
7466 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7467 }
7468
7469 match &completion.completion {
7470 InlineCompletion::Move {
7471 target, snapshot, ..
7472 } => Some(
7473 h_flex()
7474 .px_2()
7475 .gap_2()
7476 .flex_1()
7477 .child(
7478 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7479 Icon::new(IconName::ZedPredictDown)
7480 } else {
7481 Icon::new(IconName::ZedPredictUp)
7482 },
7483 )
7484 .child(Label::new("Jump to Edit")),
7485 ),
7486
7487 InlineCompletion::Edit {
7488 edits,
7489 edit_preview,
7490 snapshot,
7491 display_mode: _,
7492 } => {
7493 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7494
7495 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7496 &snapshot,
7497 &edits,
7498 edit_preview.as_ref()?,
7499 true,
7500 cx,
7501 )
7502 .first_line_preview();
7503
7504 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7505 .with_default_highlights(&style.text, highlighted_edits.highlights);
7506
7507 let preview = h_flex()
7508 .gap_1()
7509 .min_w_16()
7510 .child(styled_text)
7511 .when(has_more_lines, |parent| parent.child("…"));
7512
7513 let left = if first_edit_row != cursor_point.row {
7514 render_relative_row_jump("", cursor_point.row, first_edit_row)
7515 .into_any_element()
7516 } else {
7517 Icon::new(IconName::ZedPredict).into_any_element()
7518 };
7519
7520 Some(
7521 h_flex()
7522 .h_full()
7523 .flex_1()
7524 .gap_2()
7525 .pr_1()
7526 .overflow_x_hidden()
7527 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7528 .child(left)
7529 .child(preview),
7530 )
7531 }
7532 }
7533 }
7534
7535 fn render_context_menu(
7536 &self,
7537 style: &EditorStyle,
7538 max_height_in_lines: u32,
7539 y_flipped: bool,
7540 window: &mut Window,
7541 cx: &mut Context<Editor>,
7542 ) -> Option<AnyElement> {
7543 let menu = self.context_menu.borrow();
7544 let menu = menu.as_ref()?;
7545 if !menu.visible() {
7546 return None;
7547 };
7548 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7549 }
7550
7551 fn render_context_menu_aside(
7552 &mut self,
7553 max_size: Size<Pixels>,
7554 window: &mut Window,
7555 cx: &mut Context<Editor>,
7556 ) -> Option<AnyElement> {
7557 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7558 if menu.visible() {
7559 menu.render_aside(self, max_size, window, cx)
7560 } else {
7561 None
7562 }
7563 })
7564 }
7565
7566 fn hide_context_menu(
7567 &mut self,
7568 window: &mut Window,
7569 cx: &mut Context<Self>,
7570 ) -> Option<CodeContextMenu> {
7571 cx.notify();
7572 self.completion_tasks.clear();
7573 let context_menu = self.context_menu.borrow_mut().take();
7574 self.stale_inline_completion_in_menu.take();
7575 self.update_visible_inline_completion(window, cx);
7576 context_menu
7577 }
7578
7579 fn show_snippet_choices(
7580 &mut self,
7581 choices: &Vec<String>,
7582 selection: Range<Anchor>,
7583 cx: &mut Context<Self>,
7584 ) {
7585 if selection.start.buffer_id.is_none() {
7586 return;
7587 }
7588 let buffer_id = selection.start.buffer_id.unwrap();
7589 let buffer = self.buffer().read(cx).buffer(buffer_id);
7590 let id = post_inc(&mut self.next_completion_id);
7591
7592 if let Some(buffer) = buffer {
7593 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7594 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7595 ));
7596 }
7597 }
7598
7599 pub fn insert_snippet(
7600 &mut self,
7601 insertion_ranges: &[Range<usize>],
7602 snippet: Snippet,
7603 window: &mut Window,
7604 cx: &mut Context<Self>,
7605 ) -> Result<()> {
7606 struct Tabstop<T> {
7607 is_end_tabstop: bool,
7608 ranges: Vec<Range<T>>,
7609 choices: Option<Vec<String>>,
7610 }
7611
7612 let tabstops = self.buffer.update(cx, |buffer, cx| {
7613 let snippet_text: Arc<str> = snippet.text.clone().into();
7614 let edits = insertion_ranges
7615 .iter()
7616 .cloned()
7617 .map(|range| (range, snippet_text.clone()));
7618 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
7619
7620 let snapshot = &*buffer.read(cx);
7621 let snippet = &snippet;
7622 snippet
7623 .tabstops
7624 .iter()
7625 .map(|tabstop| {
7626 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7627 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7628 });
7629 let mut tabstop_ranges = tabstop
7630 .ranges
7631 .iter()
7632 .flat_map(|tabstop_range| {
7633 let mut delta = 0_isize;
7634 insertion_ranges.iter().map(move |insertion_range| {
7635 let insertion_start = insertion_range.start as isize + delta;
7636 delta +=
7637 snippet.text.len() as isize - insertion_range.len() as isize;
7638
7639 let start = ((insertion_start + tabstop_range.start) as usize)
7640 .min(snapshot.len());
7641 let end = ((insertion_start + tabstop_range.end) as usize)
7642 .min(snapshot.len());
7643 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7644 })
7645 })
7646 .collect::<Vec<_>>();
7647 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7648
7649 Tabstop {
7650 is_end_tabstop,
7651 ranges: tabstop_ranges,
7652 choices: tabstop.choices.clone(),
7653 }
7654 })
7655 .collect::<Vec<_>>()
7656 });
7657 if let Some(tabstop) = tabstops.first() {
7658 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7659 s.select_ranges(tabstop.ranges.iter().cloned());
7660 });
7661
7662 if let Some(choices) = &tabstop.choices {
7663 if let Some(selection) = tabstop.ranges.first() {
7664 self.show_snippet_choices(choices, selection.clone(), cx)
7665 }
7666 }
7667
7668 // If we're already at the last tabstop and it's at the end of the snippet,
7669 // we're done, we don't need to keep the state around.
7670 if !tabstop.is_end_tabstop {
7671 let choices = tabstops
7672 .iter()
7673 .map(|tabstop| tabstop.choices.clone())
7674 .collect();
7675
7676 let ranges = tabstops
7677 .into_iter()
7678 .map(|tabstop| tabstop.ranges)
7679 .collect::<Vec<_>>();
7680
7681 self.snippet_stack.push(SnippetState {
7682 active_index: 0,
7683 ranges,
7684 choices,
7685 });
7686 }
7687
7688 // Check whether the just-entered snippet ends with an auto-closable bracket.
7689 if self.autoclose_regions.is_empty() {
7690 let snapshot = self.buffer.read(cx).snapshot(cx);
7691 for selection in &mut self.selections.all::<Point>(cx) {
7692 let selection_head = selection.head();
7693 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7694 continue;
7695 };
7696
7697 let mut bracket_pair = None;
7698 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7699 let prev_chars = snapshot
7700 .reversed_chars_at(selection_head)
7701 .collect::<String>();
7702 for (pair, enabled) in scope.brackets() {
7703 if enabled
7704 && pair.close
7705 && prev_chars.starts_with(pair.start.as_str())
7706 && next_chars.starts_with(pair.end.as_str())
7707 {
7708 bracket_pair = Some(pair.clone());
7709 break;
7710 }
7711 }
7712 if let Some(pair) = bracket_pair {
7713 let start = snapshot.anchor_after(selection_head);
7714 let end = snapshot.anchor_after(selection_head);
7715 self.autoclose_regions.push(AutocloseRegion {
7716 selection_id: selection.id,
7717 range: start..end,
7718 pair,
7719 });
7720 }
7721 }
7722 }
7723 }
7724 Ok(())
7725 }
7726
7727 pub fn move_to_next_snippet_tabstop(
7728 &mut self,
7729 window: &mut Window,
7730 cx: &mut Context<Self>,
7731 ) -> bool {
7732 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7733 }
7734
7735 pub fn move_to_prev_snippet_tabstop(
7736 &mut self,
7737 window: &mut Window,
7738 cx: &mut Context<Self>,
7739 ) -> bool {
7740 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7741 }
7742
7743 pub fn move_to_snippet_tabstop(
7744 &mut self,
7745 bias: Bias,
7746 window: &mut Window,
7747 cx: &mut Context<Self>,
7748 ) -> bool {
7749 if let Some(mut snippet) = self.snippet_stack.pop() {
7750 match bias {
7751 Bias::Left => {
7752 if snippet.active_index > 0 {
7753 snippet.active_index -= 1;
7754 } else {
7755 self.snippet_stack.push(snippet);
7756 return false;
7757 }
7758 }
7759 Bias::Right => {
7760 if snippet.active_index + 1 < snippet.ranges.len() {
7761 snippet.active_index += 1;
7762 } else {
7763 self.snippet_stack.push(snippet);
7764 return false;
7765 }
7766 }
7767 }
7768 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7769 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7770 s.select_anchor_ranges(current_ranges.iter().cloned())
7771 });
7772
7773 if let Some(choices) = &snippet.choices[snippet.active_index] {
7774 if let Some(selection) = current_ranges.first() {
7775 self.show_snippet_choices(&choices, selection.clone(), cx);
7776 }
7777 }
7778
7779 // If snippet state is not at the last tabstop, push it back on the stack
7780 if snippet.active_index + 1 < snippet.ranges.len() {
7781 self.snippet_stack.push(snippet);
7782 }
7783 return true;
7784 }
7785 }
7786
7787 false
7788 }
7789
7790 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7791 self.transact(window, cx, |this, window, cx| {
7792 this.select_all(&SelectAll, window, cx);
7793 this.insert("", window, cx);
7794 });
7795 }
7796
7797 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7798 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7799 self.transact(window, cx, |this, window, cx| {
7800 this.select_autoclose_pair(window, cx);
7801 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7802 if !this.linked_edit_ranges.is_empty() {
7803 let selections = this.selections.all::<MultiBufferPoint>(cx);
7804 let snapshot = this.buffer.read(cx).snapshot(cx);
7805
7806 for selection in selections.iter() {
7807 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7808 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7809 if selection_start.buffer_id != selection_end.buffer_id {
7810 continue;
7811 }
7812 if let Some(ranges) =
7813 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7814 {
7815 for (buffer, entries) in ranges {
7816 linked_ranges.entry(buffer).or_default().extend(entries);
7817 }
7818 }
7819 }
7820 }
7821
7822 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7823 if !this.selections.line_mode {
7824 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7825 for selection in &mut selections {
7826 if selection.is_empty() {
7827 let old_head = selection.head();
7828 let mut new_head =
7829 movement::left(&display_map, old_head.to_display_point(&display_map))
7830 .to_point(&display_map);
7831 if let Some((buffer, line_buffer_range)) = display_map
7832 .buffer_snapshot
7833 .buffer_line_for_row(MultiBufferRow(old_head.row))
7834 {
7835 let indent_size =
7836 buffer.indent_size_for_line(line_buffer_range.start.row);
7837 let indent_len = match indent_size.kind {
7838 IndentKind::Space => {
7839 buffer.settings_at(line_buffer_range.start, cx).tab_size
7840 }
7841 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7842 };
7843 if old_head.column <= indent_size.len && old_head.column > 0 {
7844 let indent_len = indent_len.get();
7845 new_head = cmp::min(
7846 new_head,
7847 MultiBufferPoint::new(
7848 old_head.row,
7849 ((old_head.column - 1) / indent_len) * indent_len,
7850 ),
7851 );
7852 }
7853 }
7854
7855 selection.set_head(new_head, SelectionGoal::None);
7856 }
7857 }
7858 }
7859
7860 this.signature_help_state.set_backspace_pressed(true);
7861 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7862 s.select(selections)
7863 });
7864 this.insert("", window, cx);
7865 let empty_str: Arc<str> = Arc::from("");
7866 for (buffer, edits) in linked_ranges {
7867 let snapshot = buffer.read(cx).snapshot();
7868 use text::ToPoint as TP;
7869
7870 let edits = edits
7871 .into_iter()
7872 .map(|range| {
7873 let end_point = TP::to_point(&range.end, &snapshot);
7874 let mut start_point = TP::to_point(&range.start, &snapshot);
7875
7876 if end_point == start_point {
7877 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7878 .saturating_sub(1);
7879 start_point =
7880 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7881 };
7882
7883 (start_point..end_point, empty_str.clone())
7884 })
7885 .sorted_by_key(|(range, _)| range.start)
7886 .collect::<Vec<_>>();
7887 buffer.update(cx, |this, cx| {
7888 this.edit(edits, None, cx);
7889 })
7890 }
7891 this.refresh_inline_completion(true, false, window, cx);
7892 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7893 });
7894 }
7895
7896 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7897 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7898 self.transact(window, cx, |this, window, cx| {
7899 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7900 let line_mode = s.line_mode;
7901 s.move_with(|map, selection| {
7902 if selection.is_empty() && !line_mode {
7903 let cursor = movement::right(map, selection.head());
7904 selection.end = cursor;
7905 selection.reversed = true;
7906 selection.goal = SelectionGoal::None;
7907 }
7908 })
7909 });
7910 this.insert("", window, cx);
7911 this.refresh_inline_completion(true, false, window, cx);
7912 });
7913 }
7914
7915 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7916 if self.move_to_prev_snippet_tabstop(window, cx) {
7917 return;
7918 }
7919 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7920 self.outdent(&Outdent, window, cx);
7921 }
7922
7923 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7924 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7925 return;
7926 }
7927 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7928 let mut selections = self.selections.all_adjusted(cx);
7929 let buffer = self.buffer.read(cx);
7930 let snapshot = buffer.snapshot(cx);
7931 let rows_iter = selections.iter().map(|s| s.head().row);
7932 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7933
7934 let mut edits = Vec::new();
7935 let mut prev_edited_row = 0;
7936 let mut row_delta = 0;
7937 for selection in &mut selections {
7938 if selection.start.row != prev_edited_row {
7939 row_delta = 0;
7940 }
7941 prev_edited_row = selection.end.row;
7942
7943 // If the selection is non-empty, then increase the indentation of the selected lines.
7944 if !selection.is_empty() {
7945 row_delta =
7946 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7947 continue;
7948 }
7949
7950 // If the selection is empty and the cursor is in the leading whitespace before the
7951 // suggested indentation, then auto-indent the line.
7952 let cursor = selection.head();
7953 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7954 if let Some(suggested_indent) =
7955 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7956 {
7957 if cursor.column < suggested_indent.len
7958 && cursor.column <= current_indent.len
7959 && current_indent.len <= suggested_indent.len
7960 {
7961 selection.start = Point::new(cursor.row, suggested_indent.len);
7962 selection.end = selection.start;
7963 if row_delta == 0 {
7964 edits.extend(Buffer::edit_for_indent_size_adjustment(
7965 cursor.row,
7966 current_indent,
7967 suggested_indent,
7968 ));
7969 row_delta = suggested_indent.len - current_indent.len;
7970 }
7971 continue;
7972 }
7973 }
7974
7975 // Otherwise, insert a hard or soft tab.
7976 let settings = buffer.language_settings_at(cursor, cx);
7977 let tab_size = if settings.hard_tabs {
7978 IndentSize::tab()
7979 } else {
7980 let tab_size = settings.tab_size.get();
7981 let char_column = snapshot
7982 .text_for_range(Point::new(cursor.row, 0)..cursor)
7983 .flat_map(str::chars)
7984 .count()
7985 + row_delta as usize;
7986 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7987 IndentSize::spaces(chars_to_next_tab_stop)
7988 };
7989 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7990 selection.end = selection.start;
7991 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7992 row_delta += tab_size.len;
7993 }
7994
7995 self.transact(window, cx, |this, window, cx| {
7996 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7997 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7998 s.select(selections)
7999 });
8000 this.refresh_inline_completion(true, false, window, cx);
8001 });
8002 }
8003
8004 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
8005 if self.read_only(cx) {
8006 return;
8007 }
8008 let mut selections = self.selections.all::<Point>(cx);
8009 let mut prev_edited_row = 0;
8010 let mut row_delta = 0;
8011 let mut edits = Vec::new();
8012 let buffer = self.buffer.read(cx);
8013 let snapshot = buffer.snapshot(cx);
8014 for selection in &mut selections {
8015 if selection.start.row != prev_edited_row {
8016 row_delta = 0;
8017 }
8018 prev_edited_row = selection.end.row;
8019
8020 row_delta =
8021 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
8022 }
8023
8024 self.transact(window, cx, |this, window, cx| {
8025 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
8026 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8027 s.select(selections)
8028 });
8029 });
8030 }
8031
8032 fn indent_selection(
8033 buffer: &MultiBuffer,
8034 snapshot: &MultiBufferSnapshot,
8035 selection: &mut Selection<Point>,
8036 edits: &mut Vec<(Range<Point>, String)>,
8037 delta_for_start_row: u32,
8038 cx: &App,
8039 ) -> u32 {
8040 let settings = buffer.language_settings_at(selection.start, cx);
8041 let tab_size = settings.tab_size.get();
8042 let indent_kind = if settings.hard_tabs {
8043 IndentKind::Tab
8044 } else {
8045 IndentKind::Space
8046 };
8047 let mut start_row = selection.start.row;
8048 let mut end_row = selection.end.row + 1;
8049
8050 // If a selection ends at the beginning of a line, don't indent
8051 // that last line.
8052 if selection.end.column == 0 && selection.end.row > selection.start.row {
8053 end_row -= 1;
8054 }
8055
8056 // Avoid re-indenting a row that has already been indented by a
8057 // previous selection, but still update this selection's column
8058 // to reflect that indentation.
8059 if delta_for_start_row > 0 {
8060 start_row += 1;
8061 selection.start.column += delta_for_start_row;
8062 if selection.end.row == selection.start.row {
8063 selection.end.column += delta_for_start_row;
8064 }
8065 }
8066
8067 let mut delta_for_end_row = 0;
8068 let has_multiple_rows = start_row + 1 != end_row;
8069 for row in start_row..end_row {
8070 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
8071 let indent_delta = match (current_indent.kind, indent_kind) {
8072 (IndentKind::Space, IndentKind::Space) => {
8073 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
8074 IndentSize::spaces(columns_to_next_tab_stop)
8075 }
8076 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
8077 (_, IndentKind::Tab) => IndentSize::tab(),
8078 };
8079
8080 let start = if has_multiple_rows || current_indent.len < selection.start.column {
8081 0
8082 } else {
8083 selection.start.column
8084 };
8085 let row_start = Point::new(row, start);
8086 edits.push((
8087 row_start..row_start,
8088 indent_delta.chars().collect::<String>(),
8089 ));
8090
8091 // Update this selection's endpoints to reflect the indentation.
8092 if row == selection.start.row {
8093 selection.start.column += indent_delta.len;
8094 }
8095 if row == selection.end.row {
8096 selection.end.column += indent_delta.len;
8097 delta_for_end_row = indent_delta.len;
8098 }
8099 }
8100
8101 if selection.start.row == selection.end.row {
8102 delta_for_start_row + delta_for_end_row
8103 } else {
8104 delta_for_end_row
8105 }
8106 }
8107
8108 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
8109 if self.read_only(cx) {
8110 return;
8111 }
8112 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8113 let selections = self.selections.all::<Point>(cx);
8114 let mut deletion_ranges = Vec::new();
8115 let mut last_outdent = None;
8116 {
8117 let buffer = self.buffer.read(cx);
8118 let snapshot = buffer.snapshot(cx);
8119 for selection in &selections {
8120 let settings = buffer.language_settings_at(selection.start, cx);
8121 let tab_size = settings.tab_size.get();
8122 let mut rows = selection.spanned_rows(false, &display_map);
8123
8124 // Avoid re-outdenting a row that has already been outdented by a
8125 // previous selection.
8126 if let Some(last_row) = last_outdent {
8127 if last_row == rows.start {
8128 rows.start = rows.start.next_row();
8129 }
8130 }
8131 let has_multiple_rows = rows.len() > 1;
8132 for row in rows.iter_rows() {
8133 let indent_size = snapshot.indent_size_for_line(row);
8134 if indent_size.len > 0 {
8135 let deletion_len = match indent_size.kind {
8136 IndentKind::Space => {
8137 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8138 if columns_to_prev_tab_stop == 0 {
8139 tab_size
8140 } else {
8141 columns_to_prev_tab_stop
8142 }
8143 }
8144 IndentKind::Tab => 1,
8145 };
8146 let start = if has_multiple_rows
8147 || deletion_len > selection.start.column
8148 || indent_size.len < selection.start.column
8149 {
8150 0
8151 } else {
8152 selection.start.column - deletion_len
8153 };
8154 deletion_ranges.push(
8155 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8156 );
8157 last_outdent = Some(row);
8158 }
8159 }
8160 }
8161 }
8162
8163 self.transact(window, cx, |this, window, cx| {
8164 this.buffer.update(cx, |buffer, cx| {
8165 let empty_str: Arc<str> = Arc::default();
8166 buffer.edit(
8167 deletion_ranges
8168 .into_iter()
8169 .map(|range| (range, empty_str.clone())),
8170 None,
8171 cx,
8172 );
8173 });
8174 let selections = this.selections.all::<usize>(cx);
8175 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8176 s.select(selections)
8177 });
8178 });
8179 }
8180
8181 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8182 if self.read_only(cx) {
8183 return;
8184 }
8185 let selections = self
8186 .selections
8187 .all::<usize>(cx)
8188 .into_iter()
8189 .map(|s| s.range());
8190
8191 self.transact(window, cx, |this, window, cx| {
8192 this.buffer.update(cx, |buffer, cx| {
8193 buffer.autoindent_ranges(selections, cx);
8194 });
8195 let selections = this.selections.all::<usize>(cx);
8196 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8197 s.select(selections)
8198 });
8199 });
8200 }
8201
8202 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8203 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8204 let selections = self.selections.all::<Point>(cx);
8205
8206 let mut new_cursors = Vec::new();
8207 let mut edit_ranges = Vec::new();
8208 let mut selections = selections.iter().peekable();
8209 while let Some(selection) = selections.next() {
8210 let mut rows = selection.spanned_rows(false, &display_map);
8211 let goal_display_column = selection.head().to_display_point(&display_map).column();
8212
8213 // Accumulate contiguous regions of rows that we want to delete.
8214 while let Some(next_selection) = selections.peek() {
8215 let next_rows = next_selection.spanned_rows(false, &display_map);
8216 if next_rows.start <= rows.end {
8217 rows.end = next_rows.end;
8218 selections.next().unwrap();
8219 } else {
8220 break;
8221 }
8222 }
8223
8224 let buffer = &display_map.buffer_snapshot;
8225 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8226 let edit_end;
8227 let cursor_buffer_row;
8228 if buffer.max_point().row >= rows.end.0 {
8229 // If there's a line after the range, delete the \n from the end of the row range
8230 // and position the cursor on the next line.
8231 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8232 cursor_buffer_row = rows.end;
8233 } else {
8234 // If there isn't a line after the range, delete the \n from the line before the
8235 // start of the row range and position the cursor there.
8236 edit_start = edit_start.saturating_sub(1);
8237 edit_end = buffer.len();
8238 cursor_buffer_row = rows.start.previous_row();
8239 }
8240
8241 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8242 *cursor.column_mut() =
8243 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8244
8245 new_cursors.push((
8246 selection.id,
8247 buffer.anchor_after(cursor.to_point(&display_map)),
8248 ));
8249 edit_ranges.push(edit_start..edit_end);
8250 }
8251
8252 self.transact(window, cx, |this, window, cx| {
8253 let buffer = this.buffer.update(cx, |buffer, cx| {
8254 let empty_str: Arc<str> = Arc::default();
8255 buffer.edit(
8256 edit_ranges
8257 .into_iter()
8258 .map(|range| (range, empty_str.clone())),
8259 None,
8260 cx,
8261 );
8262 buffer.snapshot(cx)
8263 });
8264 let new_selections = new_cursors
8265 .into_iter()
8266 .map(|(id, cursor)| {
8267 let cursor = cursor.to_point(&buffer);
8268 Selection {
8269 id,
8270 start: cursor,
8271 end: cursor,
8272 reversed: false,
8273 goal: SelectionGoal::None,
8274 }
8275 })
8276 .collect();
8277
8278 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8279 s.select(new_selections);
8280 });
8281 });
8282 }
8283
8284 pub fn join_lines_impl(
8285 &mut self,
8286 insert_whitespace: bool,
8287 window: &mut Window,
8288 cx: &mut Context<Self>,
8289 ) {
8290 if self.read_only(cx) {
8291 return;
8292 }
8293 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8294 for selection in self.selections.all::<Point>(cx) {
8295 let start = MultiBufferRow(selection.start.row);
8296 // Treat single line selections as if they include the next line. Otherwise this action
8297 // would do nothing for single line selections individual cursors.
8298 let end = if selection.start.row == selection.end.row {
8299 MultiBufferRow(selection.start.row + 1)
8300 } else {
8301 MultiBufferRow(selection.end.row)
8302 };
8303
8304 if let Some(last_row_range) = row_ranges.last_mut() {
8305 if start <= last_row_range.end {
8306 last_row_range.end = end;
8307 continue;
8308 }
8309 }
8310 row_ranges.push(start..end);
8311 }
8312
8313 let snapshot = self.buffer.read(cx).snapshot(cx);
8314 let mut cursor_positions = Vec::new();
8315 for row_range in &row_ranges {
8316 let anchor = snapshot.anchor_before(Point::new(
8317 row_range.end.previous_row().0,
8318 snapshot.line_len(row_range.end.previous_row()),
8319 ));
8320 cursor_positions.push(anchor..anchor);
8321 }
8322
8323 self.transact(window, cx, |this, window, cx| {
8324 for row_range in row_ranges.into_iter().rev() {
8325 for row in row_range.iter_rows().rev() {
8326 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8327 let next_line_row = row.next_row();
8328 let indent = snapshot.indent_size_for_line(next_line_row);
8329 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8330
8331 let replace =
8332 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8333 " "
8334 } else {
8335 ""
8336 };
8337
8338 this.buffer.update(cx, |buffer, cx| {
8339 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8340 });
8341 }
8342 }
8343
8344 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8345 s.select_anchor_ranges(cursor_positions)
8346 });
8347 });
8348 }
8349
8350 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8351 self.join_lines_impl(true, window, cx);
8352 }
8353
8354 pub fn sort_lines_case_sensitive(
8355 &mut self,
8356 _: &SortLinesCaseSensitive,
8357 window: &mut Window,
8358 cx: &mut Context<Self>,
8359 ) {
8360 self.manipulate_lines(window, cx, |lines| lines.sort())
8361 }
8362
8363 pub fn sort_lines_case_insensitive(
8364 &mut self,
8365 _: &SortLinesCaseInsensitive,
8366 window: &mut Window,
8367 cx: &mut Context<Self>,
8368 ) {
8369 self.manipulate_lines(window, cx, |lines| {
8370 lines.sort_by_key(|line| line.to_lowercase())
8371 })
8372 }
8373
8374 pub fn unique_lines_case_insensitive(
8375 &mut self,
8376 _: &UniqueLinesCaseInsensitive,
8377 window: &mut Window,
8378 cx: &mut Context<Self>,
8379 ) {
8380 self.manipulate_lines(window, cx, |lines| {
8381 let mut seen = HashSet::default();
8382 lines.retain(|line| seen.insert(line.to_lowercase()));
8383 })
8384 }
8385
8386 pub fn unique_lines_case_sensitive(
8387 &mut self,
8388 _: &UniqueLinesCaseSensitive,
8389 window: &mut Window,
8390 cx: &mut Context<Self>,
8391 ) {
8392 self.manipulate_lines(window, cx, |lines| {
8393 let mut seen = HashSet::default();
8394 lines.retain(|line| seen.insert(*line));
8395 })
8396 }
8397
8398 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8399 let Some(project) = self.project.clone() else {
8400 return;
8401 };
8402 self.reload(project, window, cx)
8403 .detach_and_notify_err(window, cx);
8404 }
8405
8406 pub fn restore_file(
8407 &mut self,
8408 _: &::git::RestoreFile,
8409 window: &mut Window,
8410 cx: &mut Context<Self>,
8411 ) {
8412 let mut buffer_ids = HashSet::default();
8413 let snapshot = self.buffer().read(cx).snapshot(cx);
8414 for selection in self.selections.all::<usize>(cx) {
8415 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8416 }
8417
8418 let buffer = self.buffer().read(cx);
8419 let ranges = buffer_ids
8420 .into_iter()
8421 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8422 .collect::<Vec<_>>();
8423
8424 self.restore_hunks_in_ranges(ranges, window, cx);
8425 }
8426
8427 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8428 let selections = self
8429 .selections
8430 .all(cx)
8431 .into_iter()
8432 .map(|s| s.range())
8433 .collect();
8434 self.restore_hunks_in_ranges(selections, window, cx);
8435 }
8436
8437 fn restore_hunks_in_ranges(
8438 &mut self,
8439 ranges: Vec<Range<Point>>,
8440 window: &mut Window,
8441 cx: &mut Context<Editor>,
8442 ) {
8443 let mut revert_changes = HashMap::default();
8444 let chunk_by = self
8445 .snapshot(window, cx)
8446 .hunks_for_ranges(ranges)
8447 .into_iter()
8448 .chunk_by(|hunk| hunk.buffer_id);
8449 for (buffer_id, hunks) in &chunk_by {
8450 let hunks = hunks.collect::<Vec<_>>();
8451 for hunk in &hunks {
8452 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8453 }
8454 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8455 }
8456 drop(chunk_by);
8457 if !revert_changes.is_empty() {
8458 self.transact(window, cx, |editor, window, cx| {
8459 editor.restore(revert_changes, window, cx);
8460 });
8461 }
8462 }
8463
8464 pub fn open_active_item_in_terminal(
8465 &mut self,
8466 _: &OpenInTerminal,
8467 window: &mut Window,
8468 cx: &mut Context<Self>,
8469 ) {
8470 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8471 let project_path = buffer.read(cx).project_path(cx)?;
8472 let project = self.project.as_ref()?.read(cx);
8473 let entry = project.entry_for_path(&project_path, cx)?;
8474 let parent = match &entry.canonical_path {
8475 Some(canonical_path) => canonical_path.to_path_buf(),
8476 None => project.absolute_path(&project_path, cx)?,
8477 }
8478 .parent()?
8479 .to_path_buf();
8480 Some(parent)
8481 }) {
8482 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8483 }
8484 }
8485
8486 fn set_breakpoint_context_menu(
8487 &mut self,
8488 display_row: DisplayRow,
8489 position: Option<Anchor>,
8490 clicked_point: gpui::Point<Pixels>,
8491 window: &mut Window,
8492 cx: &mut Context<Self>,
8493 ) {
8494 if !cx.has_flag::<Debugger>() {
8495 return;
8496 }
8497 let source = self
8498 .buffer
8499 .read(cx)
8500 .snapshot(cx)
8501 .anchor_before(Point::new(display_row.0, 0u32));
8502
8503 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
8504
8505 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8506 self,
8507 source,
8508 clicked_point,
8509 context_menu,
8510 window,
8511 cx,
8512 );
8513 }
8514
8515 fn add_edit_breakpoint_block(
8516 &mut self,
8517 anchor: Anchor,
8518 breakpoint: &Breakpoint,
8519 window: &mut Window,
8520 cx: &mut Context<Self>,
8521 ) {
8522 let weak_editor = cx.weak_entity();
8523 let bp_prompt = cx.new(|cx| {
8524 BreakpointPromptEditor::new(weak_editor, anchor, breakpoint.clone(), window, cx)
8525 });
8526
8527 let height = bp_prompt.update(cx, |this, cx| {
8528 this.prompt
8529 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8530 });
8531 let cloned_prompt = bp_prompt.clone();
8532 let blocks = vec![BlockProperties {
8533 style: BlockStyle::Sticky,
8534 placement: BlockPlacement::Above(anchor),
8535 height,
8536 render: Arc::new(move |cx| {
8537 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8538 cloned_prompt.clone().into_any_element()
8539 }),
8540 priority: 0,
8541 }];
8542
8543 let focus_handle = bp_prompt.focus_handle(cx);
8544 window.focus(&focus_handle);
8545
8546 let block_ids = self.insert_blocks(blocks, None, cx);
8547 bp_prompt.update(cx, |prompt, _| {
8548 prompt.add_block_ids(block_ids);
8549 });
8550 }
8551
8552 fn breakpoint_at_cursor_head(
8553 &self,
8554 window: &mut Window,
8555 cx: &mut Context<Self>,
8556 ) -> Option<(Anchor, Breakpoint)> {
8557 let cursor_position: Point = self.selections.newest(cx).head();
8558 self.breakpoint_at_row(cursor_position.row, window, cx)
8559 }
8560
8561 pub(crate) fn breakpoint_at_row(
8562 &self,
8563 row: u32,
8564 window: &mut Window,
8565 cx: &mut Context<Self>,
8566 ) -> Option<(Anchor, Breakpoint)> {
8567 let snapshot = self.snapshot(window, cx);
8568 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
8569
8570 let project = self.project.clone()?;
8571
8572 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
8573 snapshot
8574 .buffer_snapshot
8575 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
8576 })?;
8577
8578 let enclosing_excerpt = breakpoint_position.excerpt_id;
8579 let buffer = project.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8580 let buffer_snapshot = buffer.read(cx).snapshot();
8581
8582 let row = buffer_snapshot
8583 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
8584 .row;
8585
8586 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
8587 let anchor_end = snapshot
8588 .buffer_snapshot
8589 .anchor_before(Point::new(row, line_len));
8590
8591 let bp = self
8592 .breakpoint_store
8593 .as_ref()?
8594 .read_with(cx, |breakpoint_store, cx| {
8595 breakpoint_store
8596 .breakpoints(
8597 &buffer,
8598 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
8599 &buffer_snapshot,
8600 cx,
8601 )
8602 .next()
8603 .and_then(|(anchor, bp)| {
8604 let breakpoint_row = buffer_snapshot
8605 .summary_for_anchor::<text::PointUtf16>(anchor)
8606 .row;
8607
8608 if breakpoint_row == row {
8609 snapshot
8610 .buffer_snapshot
8611 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8612 .map(|anchor| (anchor, bp.clone()))
8613 } else {
8614 None
8615 }
8616 })
8617 });
8618 bp
8619 }
8620
8621 pub fn edit_log_breakpoint(
8622 &mut self,
8623 _: &EditLogBreakpoint,
8624 window: &mut Window,
8625 cx: &mut Context<Self>,
8626 ) {
8627 let (anchor, bp) = self
8628 .breakpoint_at_cursor_head(window, cx)
8629 .unwrap_or_else(|| {
8630 let cursor_position: Point = self.selections.newest(cx).head();
8631
8632 let breakpoint_position = self
8633 .snapshot(window, cx)
8634 .display_snapshot
8635 .buffer_snapshot
8636 .anchor_before(Point::new(cursor_position.row, 0));
8637
8638 (
8639 breakpoint_position,
8640 Breakpoint {
8641 kind: BreakpointKind::Standard,
8642 state: BreakpointState::Enabled,
8643 },
8644 )
8645 });
8646
8647 self.add_edit_breakpoint_block(anchor, &bp, window, cx);
8648 }
8649
8650 pub fn enable_breakpoint(
8651 &mut self,
8652 _: &crate::actions::EnableBreakpoint,
8653 window: &mut Window,
8654 cx: &mut Context<Self>,
8655 ) {
8656 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8657 if breakpoint.is_disabled() {
8658 self.edit_breakpoint_at_anchor(
8659 anchor,
8660 breakpoint,
8661 BreakpointEditAction::InvertState,
8662 cx,
8663 );
8664 }
8665 }
8666 }
8667
8668 pub fn disable_breakpoint(
8669 &mut self,
8670 _: &crate::actions::DisableBreakpoint,
8671 window: &mut Window,
8672 cx: &mut Context<Self>,
8673 ) {
8674 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8675 if breakpoint.is_enabled() {
8676 self.edit_breakpoint_at_anchor(
8677 anchor,
8678 breakpoint,
8679 BreakpointEditAction::InvertState,
8680 cx,
8681 );
8682 }
8683 }
8684 }
8685
8686 pub fn toggle_breakpoint(
8687 &mut self,
8688 _: &crate::actions::ToggleBreakpoint,
8689 window: &mut Window,
8690 cx: &mut Context<Self>,
8691 ) {
8692 let edit_action = BreakpointEditAction::Toggle;
8693
8694 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8695 self.edit_breakpoint_at_anchor(anchor, breakpoint, edit_action, cx);
8696 } else {
8697 let cursor_position: Point = self.selections.newest(cx).head();
8698
8699 let breakpoint_position = self
8700 .snapshot(window, cx)
8701 .display_snapshot
8702 .buffer_snapshot
8703 .anchor_before(Point::new(cursor_position.row, 0));
8704
8705 self.edit_breakpoint_at_anchor(
8706 breakpoint_position,
8707 Breakpoint::new_standard(),
8708 edit_action,
8709 cx,
8710 );
8711 }
8712 }
8713
8714 pub fn edit_breakpoint_at_anchor(
8715 &mut self,
8716 breakpoint_position: Anchor,
8717 breakpoint: Breakpoint,
8718 edit_action: BreakpointEditAction,
8719 cx: &mut Context<Self>,
8720 ) {
8721 let Some(breakpoint_store) = &self.breakpoint_store else {
8722 return;
8723 };
8724
8725 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8726 if breakpoint_position == Anchor::min() {
8727 self.buffer()
8728 .read(cx)
8729 .excerpt_buffer_ids()
8730 .into_iter()
8731 .next()
8732 } else {
8733 None
8734 }
8735 }) else {
8736 return;
8737 };
8738
8739 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8740 return;
8741 };
8742
8743 breakpoint_store.update(cx, |breakpoint_store, cx| {
8744 breakpoint_store.toggle_breakpoint(
8745 buffer,
8746 (breakpoint_position.text_anchor, breakpoint),
8747 edit_action,
8748 cx,
8749 );
8750 });
8751
8752 cx.notify();
8753 }
8754
8755 #[cfg(any(test, feature = "test-support"))]
8756 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8757 self.breakpoint_store.clone()
8758 }
8759
8760 pub fn prepare_restore_change(
8761 &self,
8762 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8763 hunk: &MultiBufferDiffHunk,
8764 cx: &mut App,
8765 ) -> Option<()> {
8766 if hunk.is_created_file() {
8767 return None;
8768 }
8769 let buffer = self.buffer.read(cx);
8770 let diff = buffer.diff_for(hunk.buffer_id)?;
8771 let buffer = buffer.buffer(hunk.buffer_id)?;
8772 let buffer = buffer.read(cx);
8773 let original_text = diff
8774 .read(cx)
8775 .base_text()
8776 .as_rope()
8777 .slice(hunk.diff_base_byte_range.clone());
8778 let buffer_snapshot = buffer.snapshot();
8779 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8780 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8781 probe
8782 .0
8783 .start
8784 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8785 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8786 }) {
8787 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8788 Some(())
8789 } else {
8790 None
8791 }
8792 }
8793
8794 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8795 self.manipulate_lines(window, cx, |lines| lines.reverse())
8796 }
8797
8798 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8799 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8800 }
8801
8802 fn manipulate_lines<Fn>(
8803 &mut self,
8804 window: &mut Window,
8805 cx: &mut Context<Self>,
8806 mut callback: Fn,
8807 ) where
8808 Fn: FnMut(&mut Vec<&str>),
8809 {
8810 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8811 let buffer = self.buffer.read(cx).snapshot(cx);
8812
8813 let mut edits = Vec::new();
8814
8815 let selections = self.selections.all::<Point>(cx);
8816 let mut selections = selections.iter().peekable();
8817 let mut contiguous_row_selections = Vec::new();
8818 let mut new_selections = Vec::new();
8819 let mut added_lines = 0;
8820 let mut removed_lines = 0;
8821
8822 while let Some(selection) = selections.next() {
8823 let (start_row, end_row) = consume_contiguous_rows(
8824 &mut contiguous_row_selections,
8825 selection,
8826 &display_map,
8827 &mut selections,
8828 );
8829
8830 let start_point = Point::new(start_row.0, 0);
8831 let end_point = Point::new(
8832 end_row.previous_row().0,
8833 buffer.line_len(end_row.previous_row()),
8834 );
8835 let text = buffer
8836 .text_for_range(start_point..end_point)
8837 .collect::<String>();
8838
8839 let mut lines = text.split('\n').collect_vec();
8840
8841 let lines_before = lines.len();
8842 callback(&mut lines);
8843 let lines_after = lines.len();
8844
8845 edits.push((start_point..end_point, lines.join("\n")));
8846
8847 // Selections must change based on added and removed line count
8848 let start_row =
8849 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8850 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8851 new_selections.push(Selection {
8852 id: selection.id,
8853 start: start_row,
8854 end: end_row,
8855 goal: SelectionGoal::None,
8856 reversed: selection.reversed,
8857 });
8858
8859 if lines_after > lines_before {
8860 added_lines += lines_after - lines_before;
8861 } else if lines_before > lines_after {
8862 removed_lines += lines_before - lines_after;
8863 }
8864 }
8865
8866 self.transact(window, cx, |this, window, cx| {
8867 let buffer = this.buffer.update(cx, |buffer, cx| {
8868 buffer.edit(edits, None, cx);
8869 buffer.snapshot(cx)
8870 });
8871
8872 // Recalculate offsets on newly edited buffer
8873 let new_selections = new_selections
8874 .iter()
8875 .map(|s| {
8876 let start_point = Point::new(s.start.0, 0);
8877 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8878 Selection {
8879 id: s.id,
8880 start: buffer.point_to_offset(start_point),
8881 end: buffer.point_to_offset(end_point),
8882 goal: s.goal,
8883 reversed: s.reversed,
8884 }
8885 })
8886 .collect();
8887
8888 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8889 s.select(new_selections);
8890 });
8891
8892 this.request_autoscroll(Autoscroll::fit(), cx);
8893 });
8894 }
8895
8896 pub fn convert_to_upper_case(
8897 &mut self,
8898 _: &ConvertToUpperCase,
8899 window: &mut Window,
8900 cx: &mut Context<Self>,
8901 ) {
8902 self.manipulate_text(window, cx, |text| text.to_uppercase())
8903 }
8904
8905 pub fn convert_to_lower_case(
8906 &mut self,
8907 _: &ConvertToLowerCase,
8908 window: &mut Window,
8909 cx: &mut Context<Self>,
8910 ) {
8911 self.manipulate_text(window, cx, |text| text.to_lowercase())
8912 }
8913
8914 pub fn convert_to_title_case(
8915 &mut self,
8916 _: &ConvertToTitleCase,
8917 window: &mut Window,
8918 cx: &mut Context<Self>,
8919 ) {
8920 self.manipulate_text(window, cx, |text| {
8921 text.split('\n')
8922 .map(|line| line.to_case(Case::Title))
8923 .join("\n")
8924 })
8925 }
8926
8927 pub fn convert_to_snake_case(
8928 &mut self,
8929 _: &ConvertToSnakeCase,
8930 window: &mut Window,
8931 cx: &mut Context<Self>,
8932 ) {
8933 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8934 }
8935
8936 pub fn convert_to_kebab_case(
8937 &mut self,
8938 _: &ConvertToKebabCase,
8939 window: &mut Window,
8940 cx: &mut Context<Self>,
8941 ) {
8942 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8943 }
8944
8945 pub fn convert_to_upper_camel_case(
8946 &mut self,
8947 _: &ConvertToUpperCamelCase,
8948 window: &mut Window,
8949 cx: &mut Context<Self>,
8950 ) {
8951 self.manipulate_text(window, cx, |text| {
8952 text.split('\n')
8953 .map(|line| line.to_case(Case::UpperCamel))
8954 .join("\n")
8955 })
8956 }
8957
8958 pub fn convert_to_lower_camel_case(
8959 &mut self,
8960 _: &ConvertToLowerCamelCase,
8961 window: &mut Window,
8962 cx: &mut Context<Self>,
8963 ) {
8964 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8965 }
8966
8967 pub fn convert_to_opposite_case(
8968 &mut self,
8969 _: &ConvertToOppositeCase,
8970 window: &mut Window,
8971 cx: &mut Context<Self>,
8972 ) {
8973 self.manipulate_text(window, cx, |text| {
8974 text.chars()
8975 .fold(String::with_capacity(text.len()), |mut t, c| {
8976 if c.is_uppercase() {
8977 t.extend(c.to_lowercase());
8978 } else {
8979 t.extend(c.to_uppercase());
8980 }
8981 t
8982 })
8983 })
8984 }
8985
8986 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8987 where
8988 Fn: FnMut(&str) -> String,
8989 {
8990 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8991 let buffer = self.buffer.read(cx).snapshot(cx);
8992
8993 let mut new_selections = Vec::new();
8994 let mut edits = Vec::new();
8995 let mut selection_adjustment = 0i32;
8996
8997 for selection in self.selections.all::<usize>(cx) {
8998 let selection_is_empty = selection.is_empty();
8999
9000 let (start, end) = if selection_is_empty {
9001 let word_range = movement::surrounding_word(
9002 &display_map,
9003 selection.start.to_display_point(&display_map),
9004 );
9005 let start = word_range.start.to_offset(&display_map, Bias::Left);
9006 let end = word_range.end.to_offset(&display_map, Bias::Left);
9007 (start, end)
9008 } else {
9009 (selection.start, selection.end)
9010 };
9011
9012 let text = buffer.text_for_range(start..end).collect::<String>();
9013 let old_length = text.len() as i32;
9014 let text = callback(&text);
9015
9016 new_selections.push(Selection {
9017 start: (start as i32 - selection_adjustment) as usize,
9018 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
9019 goal: SelectionGoal::None,
9020 ..selection
9021 });
9022
9023 selection_adjustment += old_length - text.len() as i32;
9024
9025 edits.push((start..end, text));
9026 }
9027
9028 self.transact(window, cx, |this, window, cx| {
9029 this.buffer.update(cx, |buffer, cx| {
9030 buffer.edit(edits, None, cx);
9031 });
9032
9033 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9034 s.select(new_selections);
9035 });
9036
9037 this.request_autoscroll(Autoscroll::fit(), cx);
9038 });
9039 }
9040
9041 pub fn duplicate(
9042 &mut self,
9043 upwards: bool,
9044 whole_lines: bool,
9045 window: &mut Window,
9046 cx: &mut Context<Self>,
9047 ) {
9048 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9049 let buffer = &display_map.buffer_snapshot;
9050 let selections = self.selections.all::<Point>(cx);
9051
9052 let mut edits = Vec::new();
9053 let mut selections_iter = selections.iter().peekable();
9054 while let Some(selection) = selections_iter.next() {
9055 let mut rows = selection.spanned_rows(false, &display_map);
9056 // duplicate line-wise
9057 if whole_lines || selection.start == selection.end {
9058 // Avoid duplicating the same lines twice.
9059 while let Some(next_selection) = selections_iter.peek() {
9060 let next_rows = next_selection.spanned_rows(false, &display_map);
9061 if next_rows.start < rows.end {
9062 rows.end = next_rows.end;
9063 selections_iter.next().unwrap();
9064 } else {
9065 break;
9066 }
9067 }
9068
9069 // Copy the text from the selected row region and splice it either at the start
9070 // or end of the region.
9071 let start = Point::new(rows.start.0, 0);
9072 let end = Point::new(
9073 rows.end.previous_row().0,
9074 buffer.line_len(rows.end.previous_row()),
9075 );
9076 let text = buffer
9077 .text_for_range(start..end)
9078 .chain(Some("\n"))
9079 .collect::<String>();
9080 let insert_location = if upwards {
9081 Point::new(rows.end.0, 0)
9082 } else {
9083 start
9084 };
9085 edits.push((insert_location..insert_location, text));
9086 } else {
9087 // duplicate character-wise
9088 let start = selection.start;
9089 let end = selection.end;
9090 let text = buffer.text_for_range(start..end).collect::<String>();
9091 edits.push((selection.end..selection.end, text));
9092 }
9093 }
9094
9095 self.transact(window, cx, |this, _, cx| {
9096 this.buffer.update(cx, |buffer, cx| {
9097 buffer.edit(edits, None, cx);
9098 });
9099
9100 this.request_autoscroll(Autoscroll::fit(), cx);
9101 });
9102 }
9103
9104 pub fn duplicate_line_up(
9105 &mut self,
9106 _: &DuplicateLineUp,
9107 window: &mut Window,
9108 cx: &mut Context<Self>,
9109 ) {
9110 self.duplicate(true, true, window, cx);
9111 }
9112
9113 pub fn duplicate_line_down(
9114 &mut self,
9115 _: &DuplicateLineDown,
9116 window: &mut Window,
9117 cx: &mut Context<Self>,
9118 ) {
9119 self.duplicate(false, true, window, cx);
9120 }
9121
9122 pub fn duplicate_selection(
9123 &mut self,
9124 _: &DuplicateSelection,
9125 window: &mut Window,
9126 cx: &mut Context<Self>,
9127 ) {
9128 self.duplicate(false, false, window, cx);
9129 }
9130
9131 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
9132 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9133 let buffer = self.buffer.read(cx).snapshot(cx);
9134
9135 let mut edits = Vec::new();
9136 let mut unfold_ranges = Vec::new();
9137 let mut refold_creases = Vec::new();
9138
9139 let selections = self.selections.all::<Point>(cx);
9140 let mut selections = selections.iter().peekable();
9141 let mut contiguous_row_selections = Vec::new();
9142 let mut new_selections = Vec::new();
9143
9144 while let Some(selection) = selections.next() {
9145 // Find all the selections that span a contiguous row range
9146 let (start_row, end_row) = consume_contiguous_rows(
9147 &mut contiguous_row_selections,
9148 selection,
9149 &display_map,
9150 &mut selections,
9151 );
9152
9153 // Move the text spanned by the row range to be before the line preceding the row range
9154 if start_row.0 > 0 {
9155 let range_to_move = Point::new(
9156 start_row.previous_row().0,
9157 buffer.line_len(start_row.previous_row()),
9158 )
9159 ..Point::new(
9160 end_row.previous_row().0,
9161 buffer.line_len(end_row.previous_row()),
9162 );
9163 let insertion_point = display_map
9164 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9165 .0;
9166
9167 // Don't move lines across excerpts
9168 if buffer
9169 .excerpt_containing(insertion_point..range_to_move.end)
9170 .is_some()
9171 {
9172 let text = buffer
9173 .text_for_range(range_to_move.clone())
9174 .flat_map(|s| s.chars())
9175 .skip(1)
9176 .chain(['\n'])
9177 .collect::<String>();
9178
9179 edits.push((
9180 buffer.anchor_after(range_to_move.start)
9181 ..buffer.anchor_before(range_to_move.end),
9182 String::new(),
9183 ));
9184 let insertion_anchor = buffer.anchor_after(insertion_point);
9185 edits.push((insertion_anchor..insertion_anchor, text));
9186
9187 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9188
9189 // Move selections up
9190 new_selections.extend(contiguous_row_selections.drain(..).map(
9191 |mut selection| {
9192 selection.start.row -= row_delta;
9193 selection.end.row -= row_delta;
9194 selection
9195 },
9196 ));
9197
9198 // Move folds up
9199 unfold_ranges.push(range_to_move.clone());
9200 for fold in display_map.folds_in_range(
9201 buffer.anchor_before(range_to_move.start)
9202 ..buffer.anchor_after(range_to_move.end),
9203 ) {
9204 let mut start = fold.range.start.to_point(&buffer);
9205 let mut end = fold.range.end.to_point(&buffer);
9206 start.row -= row_delta;
9207 end.row -= row_delta;
9208 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9209 }
9210 }
9211 }
9212
9213 // If we didn't move line(s), preserve the existing selections
9214 new_selections.append(&mut contiguous_row_selections);
9215 }
9216
9217 self.transact(window, cx, |this, window, cx| {
9218 this.unfold_ranges(&unfold_ranges, true, true, cx);
9219 this.buffer.update(cx, |buffer, cx| {
9220 for (range, text) in edits {
9221 buffer.edit([(range, text)], None, cx);
9222 }
9223 });
9224 this.fold_creases(refold_creases, true, window, cx);
9225 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9226 s.select(new_selections);
9227 })
9228 });
9229 }
9230
9231 pub fn move_line_down(
9232 &mut self,
9233 _: &MoveLineDown,
9234 window: &mut Window,
9235 cx: &mut Context<Self>,
9236 ) {
9237 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9238 let buffer = self.buffer.read(cx).snapshot(cx);
9239
9240 let mut edits = Vec::new();
9241 let mut unfold_ranges = Vec::new();
9242 let mut refold_creases = Vec::new();
9243
9244 let selections = self.selections.all::<Point>(cx);
9245 let mut selections = selections.iter().peekable();
9246 let mut contiguous_row_selections = Vec::new();
9247 let mut new_selections = Vec::new();
9248
9249 while let Some(selection) = selections.next() {
9250 // Find all the selections that span a contiguous row range
9251 let (start_row, end_row) = consume_contiguous_rows(
9252 &mut contiguous_row_selections,
9253 selection,
9254 &display_map,
9255 &mut selections,
9256 );
9257
9258 // Move the text spanned by the row range to be after the last line of the row range
9259 if end_row.0 <= buffer.max_point().row {
9260 let range_to_move =
9261 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9262 let insertion_point = display_map
9263 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9264 .0;
9265
9266 // Don't move lines across excerpt boundaries
9267 if buffer
9268 .excerpt_containing(range_to_move.start..insertion_point)
9269 .is_some()
9270 {
9271 let mut text = String::from("\n");
9272 text.extend(buffer.text_for_range(range_to_move.clone()));
9273 text.pop(); // Drop trailing newline
9274 edits.push((
9275 buffer.anchor_after(range_to_move.start)
9276 ..buffer.anchor_before(range_to_move.end),
9277 String::new(),
9278 ));
9279 let insertion_anchor = buffer.anchor_after(insertion_point);
9280 edits.push((insertion_anchor..insertion_anchor, text));
9281
9282 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9283
9284 // Move selections down
9285 new_selections.extend(contiguous_row_selections.drain(..).map(
9286 |mut selection| {
9287 selection.start.row += row_delta;
9288 selection.end.row += row_delta;
9289 selection
9290 },
9291 ));
9292
9293 // Move folds down
9294 unfold_ranges.push(range_to_move.clone());
9295 for fold in display_map.folds_in_range(
9296 buffer.anchor_before(range_to_move.start)
9297 ..buffer.anchor_after(range_to_move.end),
9298 ) {
9299 let mut start = fold.range.start.to_point(&buffer);
9300 let mut end = fold.range.end.to_point(&buffer);
9301 start.row += row_delta;
9302 end.row += row_delta;
9303 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9304 }
9305 }
9306 }
9307
9308 // If we didn't move line(s), preserve the existing selections
9309 new_selections.append(&mut contiguous_row_selections);
9310 }
9311
9312 self.transact(window, cx, |this, window, cx| {
9313 this.unfold_ranges(&unfold_ranges, true, true, cx);
9314 this.buffer.update(cx, |buffer, cx| {
9315 for (range, text) in edits {
9316 buffer.edit([(range, text)], None, cx);
9317 }
9318 });
9319 this.fold_creases(refold_creases, true, window, cx);
9320 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9321 s.select(new_selections)
9322 });
9323 });
9324 }
9325
9326 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9327 let text_layout_details = &self.text_layout_details(window);
9328 self.transact(window, cx, |this, window, cx| {
9329 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9330 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9331 let line_mode = s.line_mode;
9332 s.move_with(|display_map, selection| {
9333 if !selection.is_empty() || line_mode {
9334 return;
9335 }
9336
9337 let mut head = selection.head();
9338 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9339 if head.column() == display_map.line_len(head.row()) {
9340 transpose_offset = display_map
9341 .buffer_snapshot
9342 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9343 }
9344
9345 if transpose_offset == 0 {
9346 return;
9347 }
9348
9349 *head.column_mut() += 1;
9350 head = display_map.clip_point(head, Bias::Right);
9351 let goal = SelectionGoal::HorizontalPosition(
9352 display_map
9353 .x_for_display_point(head, text_layout_details)
9354 .into(),
9355 );
9356 selection.collapse_to(head, goal);
9357
9358 let transpose_start = display_map
9359 .buffer_snapshot
9360 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9361 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9362 let transpose_end = display_map
9363 .buffer_snapshot
9364 .clip_offset(transpose_offset + 1, Bias::Right);
9365 if let Some(ch) =
9366 display_map.buffer_snapshot.chars_at(transpose_start).next()
9367 {
9368 edits.push((transpose_start..transpose_offset, String::new()));
9369 edits.push((transpose_end..transpose_end, ch.to_string()));
9370 }
9371 }
9372 });
9373 edits
9374 });
9375 this.buffer
9376 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9377 let selections = this.selections.all::<usize>(cx);
9378 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9379 s.select(selections);
9380 });
9381 });
9382 }
9383
9384 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9385 self.rewrap_impl(RewrapOptions::default(), cx)
9386 }
9387
9388 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9389 let buffer = self.buffer.read(cx).snapshot(cx);
9390 let selections = self.selections.all::<Point>(cx);
9391 let mut selections = selections.iter().peekable();
9392
9393 let mut edits = Vec::new();
9394 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9395
9396 while let Some(selection) = selections.next() {
9397 let mut start_row = selection.start.row;
9398 let mut end_row = selection.end.row;
9399
9400 // Skip selections that overlap with a range that has already been rewrapped.
9401 let selection_range = start_row..end_row;
9402 if rewrapped_row_ranges
9403 .iter()
9404 .any(|range| range.overlaps(&selection_range))
9405 {
9406 continue;
9407 }
9408
9409 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9410
9411 // Since not all lines in the selection may be at the same indent
9412 // level, choose the indent size that is the most common between all
9413 // of the lines.
9414 //
9415 // If there is a tie, we use the deepest indent.
9416 let (indent_size, indent_end) = {
9417 let mut indent_size_occurrences = HashMap::default();
9418 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9419
9420 for row in start_row..=end_row {
9421 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9422 rows_by_indent_size.entry(indent).or_default().push(row);
9423 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9424 }
9425
9426 let indent_size = indent_size_occurrences
9427 .into_iter()
9428 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9429 .map(|(indent, _)| indent)
9430 .unwrap_or_default();
9431 let row = rows_by_indent_size[&indent_size][0];
9432 let indent_end = Point::new(row, indent_size.len);
9433
9434 (indent_size, indent_end)
9435 };
9436
9437 let mut line_prefix = indent_size.chars().collect::<String>();
9438
9439 let mut inside_comment = false;
9440 if let Some(comment_prefix) =
9441 buffer
9442 .language_scope_at(selection.head())
9443 .and_then(|language| {
9444 language
9445 .line_comment_prefixes()
9446 .iter()
9447 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9448 .cloned()
9449 })
9450 {
9451 line_prefix.push_str(&comment_prefix);
9452 inside_comment = true;
9453 }
9454
9455 let language_settings = buffer.language_settings_at(selection.head(), cx);
9456 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9457 RewrapBehavior::InComments => inside_comment,
9458 RewrapBehavior::InSelections => !selection.is_empty(),
9459 RewrapBehavior::Anywhere => true,
9460 };
9461
9462 let should_rewrap = options.override_language_settings
9463 || allow_rewrap_based_on_language
9464 || self.hard_wrap.is_some();
9465 if !should_rewrap {
9466 continue;
9467 }
9468
9469 if selection.is_empty() {
9470 'expand_upwards: while start_row > 0 {
9471 let prev_row = start_row - 1;
9472 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9473 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9474 {
9475 start_row = prev_row;
9476 } else {
9477 break 'expand_upwards;
9478 }
9479 }
9480
9481 'expand_downwards: while end_row < buffer.max_point().row {
9482 let next_row = end_row + 1;
9483 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9484 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9485 {
9486 end_row = next_row;
9487 } else {
9488 break 'expand_downwards;
9489 }
9490 }
9491 }
9492
9493 let start = Point::new(start_row, 0);
9494 let start_offset = start.to_offset(&buffer);
9495 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9496 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9497 let Some(lines_without_prefixes) = selection_text
9498 .lines()
9499 .map(|line| {
9500 line.strip_prefix(&line_prefix)
9501 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9502 .ok_or_else(|| {
9503 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9504 })
9505 })
9506 .collect::<Result<Vec<_>, _>>()
9507 .log_err()
9508 else {
9509 continue;
9510 };
9511
9512 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9513 buffer
9514 .language_settings_at(Point::new(start_row, 0), cx)
9515 .preferred_line_length as usize
9516 });
9517 let wrapped_text = wrap_with_prefix(
9518 line_prefix,
9519 lines_without_prefixes.join("\n"),
9520 wrap_column,
9521 tab_size,
9522 options.preserve_existing_whitespace,
9523 );
9524
9525 // TODO: should always use char-based diff while still supporting cursor behavior that
9526 // matches vim.
9527 let mut diff_options = DiffOptions::default();
9528 if options.override_language_settings {
9529 diff_options.max_word_diff_len = 0;
9530 diff_options.max_word_diff_line_count = 0;
9531 } else {
9532 diff_options.max_word_diff_len = usize::MAX;
9533 diff_options.max_word_diff_line_count = usize::MAX;
9534 }
9535
9536 for (old_range, new_text) in
9537 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9538 {
9539 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9540 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9541 edits.push((edit_start..edit_end, new_text));
9542 }
9543
9544 rewrapped_row_ranges.push(start_row..=end_row);
9545 }
9546
9547 self.buffer
9548 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9549 }
9550
9551 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9552 let mut text = String::new();
9553 let buffer = self.buffer.read(cx).snapshot(cx);
9554 let mut selections = self.selections.all::<Point>(cx);
9555 let mut clipboard_selections = Vec::with_capacity(selections.len());
9556 {
9557 let max_point = buffer.max_point();
9558 let mut is_first = true;
9559 for selection in &mut selections {
9560 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9561 if is_entire_line {
9562 selection.start = Point::new(selection.start.row, 0);
9563 if !selection.is_empty() && selection.end.column == 0 {
9564 selection.end = cmp::min(max_point, selection.end);
9565 } else {
9566 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9567 }
9568 selection.goal = SelectionGoal::None;
9569 }
9570 if is_first {
9571 is_first = false;
9572 } else {
9573 text += "\n";
9574 }
9575 let mut len = 0;
9576 for chunk in buffer.text_for_range(selection.start..selection.end) {
9577 text.push_str(chunk);
9578 len += chunk.len();
9579 }
9580 clipboard_selections.push(ClipboardSelection {
9581 len,
9582 is_entire_line,
9583 first_line_indent: buffer
9584 .indent_size_for_line(MultiBufferRow(selection.start.row))
9585 .len,
9586 });
9587 }
9588 }
9589
9590 self.transact(window, cx, |this, window, cx| {
9591 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9592 s.select(selections);
9593 });
9594 this.insert("", window, cx);
9595 });
9596 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9597 }
9598
9599 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9600 let item = self.cut_common(window, cx);
9601 cx.write_to_clipboard(item);
9602 }
9603
9604 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9605 self.change_selections(None, window, cx, |s| {
9606 s.move_with(|snapshot, sel| {
9607 if sel.is_empty() {
9608 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9609 }
9610 });
9611 });
9612 let item = self.cut_common(window, cx);
9613 cx.set_global(KillRing(item))
9614 }
9615
9616 pub fn kill_ring_yank(
9617 &mut self,
9618 _: &KillRingYank,
9619 window: &mut Window,
9620 cx: &mut Context<Self>,
9621 ) {
9622 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9623 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9624 (kill_ring.text().to_string(), kill_ring.metadata_json())
9625 } else {
9626 return;
9627 }
9628 } else {
9629 return;
9630 };
9631 self.do_paste(&text, metadata, false, window, cx);
9632 }
9633
9634 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9635 self.do_copy(true, cx);
9636 }
9637
9638 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9639 self.do_copy(false, cx);
9640 }
9641
9642 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9643 let selections = self.selections.all::<Point>(cx);
9644 let buffer = self.buffer.read(cx).read(cx);
9645 let mut text = String::new();
9646
9647 let mut clipboard_selections = Vec::with_capacity(selections.len());
9648 {
9649 let max_point = buffer.max_point();
9650 let mut is_first = true;
9651 for selection in &selections {
9652 let mut start = selection.start;
9653 let mut end = selection.end;
9654 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9655 if is_entire_line {
9656 start = Point::new(start.row, 0);
9657 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9658 }
9659
9660 let mut trimmed_selections = Vec::new();
9661 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9662 let row = MultiBufferRow(start.row);
9663 let first_indent = buffer.indent_size_for_line(row);
9664 if first_indent.len == 0 || start.column > first_indent.len {
9665 trimmed_selections.push(start..end);
9666 } else {
9667 trimmed_selections.push(
9668 Point::new(row.0, first_indent.len)
9669 ..Point::new(row.0, buffer.line_len(row)),
9670 );
9671 for row in start.row + 1..=end.row {
9672 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9673 if row_indent_size.len >= first_indent.len {
9674 trimmed_selections.push(
9675 Point::new(row, first_indent.len)
9676 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9677 );
9678 } else {
9679 trimmed_selections.clear();
9680 trimmed_selections.push(start..end);
9681 break;
9682 }
9683 }
9684 }
9685 } else {
9686 trimmed_selections.push(start..end);
9687 }
9688
9689 for trimmed_range in trimmed_selections {
9690 if is_first {
9691 is_first = false;
9692 } else {
9693 text += "\n";
9694 }
9695 let mut len = 0;
9696 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9697 text.push_str(chunk);
9698 len += chunk.len();
9699 }
9700 clipboard_selections.push(ClipboardSelection {
9701 len,
9702 is_entire_line,
9703 first_line_indent: buffer
9704 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9705 .len,
9706 });
9707 }
9708 }
9709 }
9710
9711 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9712 text,
9713 clipboard_selections,
9714 ));
9715 }
9716
9717 pub fn do_paste(
9718 &mut self,
9719 text: &String,
9720 clipboard_selections: Option<Vec<ClipboardSelection>>,
9721 handle_entire_lines: bool,
9722 window: &mut Window,
9723 cx: &mut Context<Self>,
9724 ) {
9725 if self.read_only(cx) {
9726 return;
9727 }
9728
9729 let clipboard_text = Cow::Borrowed(text);
9730
9731 self.transact(window, cx, |this, window, cx| {
9732 if let Some(mut clipboard_selections) = clipboard_selections {
9733 let old_selections = this.selections.all::<usize>(cx);
9734 let all_selections_were_entire_line =
9735 clipboard_selections.iter().all(|s| s.is_entire_line);
9736 let first_selection_indent_column =
9737 clipboard_selections.first().map(|s| s.first_line_indent);
9738 if clipboard_selections.len() != old_selections.len() {
9739 clipboard_selections.drain(..);
9740 }
9741 let cursor_offset = this.selections.last::<usize>(cx).head();
9742 let mut auto_indent_on_paste = true;
9743
9744 this.buffer.update(cx, |buffer, cx| {
9745 let snapshot = buffer.read(cx);
9746 auto_indent_on_paste = snapshot
9747 .language_settings_at(cursor_offset, cx)
9748 .auto_indent_on_paste;
9749
9750 let mut start_offset = 0;
9751 let mut edits = Vec::new();
9752 let mut original_indent_columns = Vec::new();
9753 for (ix, selection) in old_selections.iter().enumerate() {
9754 let to_insert;
9755 let entire_line;
9756 let original_indent_column;
9757 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9758 let end_offset = start_offset + clipboard_selection.len;
9759 to_insert = &clipboard_text[start_offset..end_offset];
9760 entire_line = clipboard_selection.is_entire_line;
9761 start_offset = end_offset + 1;
9762 original_indent_column = Some(clipboard_selection.first_line_indent);
9763 } else {
9764 to_insert = clipboard_text.as_str();
9765 entire_line = all_selections_were_entire_line;
9766 original_indent_column = first_selection_indent_column
9767 }
9768
9769 // If the corresponding selection was empty when this slice of the
9770 // clipboard text was written, then the entire line containing the
9771 // selection was copied. If this selection is also currently empty,
9772 // then paste the line before the current line of the buffer.
9773 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9774 let column = selection.start.to_point(&snapshot).column as usize;
9775 let line_start = selection.start - column;
9776 line_start..line_start
9777 } else {
9778 selection.range()
9779 };
9780
9781 edits.push((range, to_insert));
9782 original_indent_columns.push(original_indent_column);
9783 }
9784 drop(snapshot);
9785
9786 buffer.edit(
9787 edits,
9788 if auto_indent_on_paste {
9789 Some(AutoindentMode::Block {
9790 original_indent_columns,
9791 })
9792 } else {
9793 None
9794 },
9795 cx,
9796 );
9797 });
9798
9799 let selections = this.selections.all::<usize>(cx);
9800 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9801 s.select(selections)
9802 });
9803 } else {
9804 this.insert(&clipboard_text, window, cx);
9805 }
9806 });
9807 }
9808
9809 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9810 if let Some(item) = cx.read_from_clipboard() {
9811 let entries = item.entries();
9812
9813 match entries.first() {
9814 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9815 // of all the pasted entries.
9816 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9817 .do_paste(
9818 clipboard_string.text(),
9819 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9820 true,
9821 window,
9822 cx,
9823 ),
9824 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9825 }
9826 }
9827 }
9828
9829 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9830 if self.read_only(cx) {
9831 return;
9832 }
9833
9834 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9835 if let Some((selections, _)) =
9836 self.selection_history.transaction(transaction_id).cloned()
9837 {
9838 self.change_selections(None, window, cx, |s| {
9839 s.select_anchors(selections.to_vec());
9840 });
9841 } else {
9842 log::error!(
9843 "No entry in selection_history found for undo. \
9844 This may correspond to a bug where undo does not update the selection. \
9845 If this is occurring, please add details to \
9846 https://github.com/zed-industries/zed/issues/22692"
9847 );
9848 }
9849 self.request_autoscroll(Autoscroll::fit(), cx);
9850 self.unmark_text(window, cx);
9851 self.refresh_inline_completion(true, false, window, cx);
9852 cx.emit(EditorEvent::Edited { transaction_id });
9853 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9854 }
9855 }
9856
9857 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9858 if self.read_only(cx) {
9859 return;
9860 }
9861
9862 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9863 if let Some((_, Some(selections))) =
9864 self.selection_history.transaction(transaction_id).cloned()
9865 {
9866 self.change_selections(None, window, cx, |s| {
9867 s.select_anchors(selections.to_vec());
9868 });
9869 } else {
9870 log::error!(
9871 "No entry in selection_history found for redo. \
9872 This may correspond to a bug where undo does not update the selection. \
9873 If this is occurring, please add details to \
9874 https://github.com/zed-industries/zed/issues/22692"
9875 );
9876 }
9877 self.request_autoscroll(Autoscroll::fit(), cx);
9878 self.unmark_text(window, cx);
9879 self.refresh_inline_completion(true, false, window, cx);
9880 cx.emit(EditorEvent::Edited { transaction_id });
9881 }
9882 }
9883
9884 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9885 self.buffer
9886 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9887 }
9888
9889 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9890 self.buffer
9891 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9892 }
9893
9894 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9895 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9896 let line_mode = s.line_mode;
9897 s.move_with(|map, selection| {
9898 let cursor = if selection.is_empty() && !line_mode {
9899 movement::left(map, selection.start)
9900 } else {
9901 selection.start
9902 };
9903 selection.collapse_to(cursor, SelectionGoal::None);
9904 });
9905 })
9906 }
9907
9908 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9909 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9910 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9911 })
9912 }
9913
9914 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9915 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9916 let line_mode = s.line_mode;
9917 s.move_with(|map, selection| {
9918 let cursor = if selection.is_empty() && !line_mode {
9919 movement::right(map, selection.end)
9920 } else {
9921 selection.end
9922 };
9923 selection.collapse_to(cursor, SelectionGoal::None)
9924 });
9925 })
9926 }
9927
9928 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9929 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9930 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9931 })
9932 }
9933
9934 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9935 if self.take_rename(true, window, cx).is_some() {
9936 return;
9937 }
9938
9939 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9940 cx.propagate();
9941 return;
9942 }
9943
9944 let text_layout_details = &self.text_layout_details(window);
9945 let selection_count = self.selections.count();
9946 let first_selection = self.selections.first_anchor();
9947
9948 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9949 let line_mode = s.line_mode;
9950 s.move_with(|map, selection| {
9951 if !selection.is_empty() && !line_mode {
9952 selection.goal = SelectionGoal::None;
9953 }
9954 let (cursor, goal) = movement::up(
9955 map,
9956 selection.start,
9957 selection.goal,
9958 false,
9959 text_layout_details,
9960 );
9961 selection.collapse_to(cursor, goal);
9962 });
9963 });
9964
9965 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9966 {
9967 cx.propagate();
9968 }
9969 }
9970
9971 pub fn move_up_by_lines(
9972 &mut self,
9973 action: &MoveUpByLines,
9974 window: &mut Window,
9975 cx: &mut Context<Self>,
9976 ) {
9977 if self.take_rename(true, window, cx).is_some() {
9978 return;
9979 }
9980
9981 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9982 cx.propagate();
9983 return;
9984 }
9985
9986 let text_layout_details = &self.text_layout_details(window);
9987
9988 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9989 let line_mode = s.line_mode;
9990 s.move_with(|map, selection| {
9991 if !selection.is_empty() && !line_mode {
9992 selection.goal = SelectionGoal::None;
9993 }
9994 let (cursor, goal) = movement::up_by_rows(
9995 map,
9996 selection.start,
9997 action.lines,
9998 selection.goal,
9999 false,
10000 text_layout_details,
10001 );
10002 selection.collapse_to(cursor, goal);
10003 });
10004 })
10005 }
10006
10007 pub fn move_down_by_lines(
10008 &mut self,
10009 action: &MoveDownByLines,
10010 window: &mut Window,
10011 cx: &mut Context<Self>,
10012 ) {
10013 if self.take_rename(true, window, cx).is_some() {
10014 return;
10015 }
10016
10017 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10018 cx.propagate();
10019 return;
10020 }
10021
10022 let text_layout_details = &self.text_layout_details(window);
10023
10024 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10025 let line_mode = s.line_mode;
10026 s.move_with(|map, selection| {
10027 if !selection.is_empty() && !line_mode {
10028 selection.goal = SelectionGoal::None;
10029 }
10030 let (cursor, goal) = movement::down_by_rows(
10031 map,
10032 selection.start,
10033 action.lines,
10034 selection.goal,
10035 false,
10036 text_layout_details,
10037 );
10038 selection.collapse_to(cursor, goal);
10039 });
10040 })
10041 }
10042
10043 pub fn select_down_by_lines(
10044 &mut self,
10045 action: &SelectDownByLines,
10046 window: &mut Window,
10047 cx: &mut Context<Self>,
10048 ) {
10049 let text_layout_details = &self.text_layout_details(window);
10050 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10051 s.move_heads_with(|map, head, goal| {
10052 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
10053 })
10054 })
10055 }
10056
10057 pub fn select_up_by_lines(
10058 &mut self,
10059 action: &SelectUpByLines,
10060 window: &mut Window,
10061 cx: &mut Context<Self>,
10062 ) {
10063 let text_layout_details = &self.text_layout_details(window);
10064 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10065 s.move_heads_with(|map, head, goal| {
10066 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
10067 })
10068 })
10069 }
10070
10071 pub fn select_page_up(
10072 &mut self,
10073 _: &SelectPageUp,
10074 window: &mut Window,
10075 cx: &mut Context<Self>,
10076 ) {
10077 let Some(row_count) = self.visible_row_count() else {
10078 return;
10079 };
10080
10081 let text_layout_details = &self.text_layout_details(window);
10082
10083 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10084 s.move_heads_with(|map, head, goal| {
10085 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
10086 })
10087 })
10088 }
10089
10090 pub fn move_page_up(
10091 &mut self,
10092 action: &MovePageUp,
10093 window: &mut Window,
10094 cx: &mut Context<Self>,
10095 ) {
10096 if self.take_rename(true, window, cx).is_some() {
10097 return;
10098 }
10099
10100 if self
10101 .context_menu
10102 .borrow_mut()
10103 .as_mut()
10104 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
10105 .unwrap_or(false)
10106 {
10107 return;
10108 }
10109
10110 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10111 cx.propagate();
10112 return;
10113 }
10114
10115 let Some(row_count) = self.visible_row_count() else {
10116 return;
10117 };
10118
10119 let autoscroll = if action.center_cursor {
10120 Autoscroll::center()
10121 } else {
10122 Autoscroll::fit()
10123 };
10124
10125 let text_layout_details = &self.text_layout_details(window);
10126
10127 self.change_selections(Some(autoscroll), window, cx, |s| {
10128 let line_mode = s.line_mode;
10129 s.move_with(|map, selection| {
10130 if !selection.is_empty() && !line_mode {
10131 selection.goal = SelectionGoal::None;
10132 }
10133 let (cursor, goal) = movement::up_by_rows(
10134 map,
10135 selection.end,
10136 row_count,
10137 selection.goal,
10138 false,
10139 text_layout_details,
10140 );
10141 selection.collapse_to(cursor, goal);
10142 });
10143 });
10144 }
10145
10146 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
10147 let text_layout_details = &self.text_layout_details(window);
10148 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10149 s.move_heads_with(|map, head, goal| {
10150 movement::up(map, head, goal, false, text_layout_details)
10151 })
10152 })
10153 }
10154
10155 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
10156 self.take_rename(true, window, cx);
10157
10158 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10159 cx.propagate();
10160 return;
10161 }
10162
10163 let text_layout_details = &self.text_layout_details(window);
10164 let selection_count = self.selections.count();
10165 let first_selection = self.selections.first_anchor();
10166
10167 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10168 let line_mode = s.line_mode;
10169 s.move_with(|map, selection| {
10170 if !selection.is_empty() && !line_mode {
10171 selection.goal = SelectionGoal::None;
10172 }
10173 let (cursor, goal) = movement::down(
10174 map,
10175 selection.end,
10176 selection.goal,
10177 false,
10178 text_layout_details,
10179 );
10180 selection.collapse_to(cursor, goal);
10181 });
10182 });
10183
10184 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10185 {
10186 cx.propagate();
10187 }
10188 }
10189
10190 pub fn select_page_down(
10191 &mut self,
10192 _: &SelectPageDown,
10193 window: &mut Window,
10194 cx: &mut Context<Self>,
10195 ) {
10196 let Some(row_count) = self.visible_row_count() else {
10197 return;
10198 };
10199
10200 let text_layout_details = &self.text_layout_details(window);
10201
10202 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10203 s.move_heads_with(|map, head, goal| {
10204 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10205 })
10206 })
10207 }
10208
10209 pub fn move_page_down(
10210 &mut self,
10211 action: &MovePageDown,
10212 window: &mut Window,
10213 cx: &mut Context<Self>,
10214 ) {
10215 if self.take_rename(true, window, cx).is_some() {
10216 return;
10217 }
10218
10219 if self
10220 .context_menu
10221 .borrow_mut()
10222 .as_mut()
10223 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10224 .unwrap_or(false)
10225 {
10226 return;
10227 }
10228
10229 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10230 cx.propagate();
10231 return;
10232 }
10233
10234 let Some(row_count) = self.visible_row_count() else {
10235 return;
10236 };
10237
10238 let autoscroll = if action.center_cursor {
10239 Autoscroll::center()
10240 } else {
10241 Autoscroll::fit()
10242 };
10243
10244 let text_layout_details = &self.text_layout_details(window);
10245 self.change_selections(Some(autoscroll), window, cx, |s| {
10246 let line_mode = s.line_mode;
10247 s.move_with(|map, selection| {
10248 if !selection.is_empty() && !line_mode {
10249 selection.goal = SelectionGoal::None;
10250 }
10251 let (cursor, goal) = movement::down_by_rows(
10252 map,
10253 selection.end,
10254 row_count,
10255 selection.goal,
10256 false,
10257 text_layout_details,
10258 );
10259 selection.collapse_to(cursor, goal);
10260 });
10261 });
10262 }
10263
10264 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10265 let text_layout_details = &self.text_layout_details(window);
10266 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10267 s.move_heads_with(|map, head, goal| {
10268 movement::down(map, head, goal, false, text_layout_details)
10269 })
10270 });
10271 }
10272
10273 pub fn context_menu_first(
10274 &mut self,
10275 _: &ContextMenuFirst,
10276 _window: &mut Window,
10277 cx: &mut Context<Self>,
10278 ) {
10279 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10280 context_menu.select_first(self.completion_provider.as_deref(), cx);
10281 }
10282 }
10283
10284 pub fn context_menu_prev(
10285 &mut self,
10286 _: &ContextMenuPrevious,
10287 _window: &mut Window,
10288 cx: &mut Context<Self>,
10289 ) {
10290 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10291 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10292 }
10293 }
10294
10295 pub fn context_menu_next(
10296 &mut self,
10297 _: &ContextMenuNext,
10298 _window: &mut Window,
10299 cx: &mut Context<Self>,
10300 ) {
10301 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10302 context_menu.select_next(self.completion_provider.as_deref(), cx);
10303 }
10304 }
10305
10306 pub fn context_menu_last(
10307 &mut self,
10308 _: &ContextMenuLast,
10309 _window: &mut Window,
10310 cx: &mut Context<Self>,
10311 ) {
10312 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10313 context_menu.select_last(self.completion_provider.as_deref(), cx);
10314 }
10315 }
10316
10317 pub fn move_to_previous_word_start(
10318 &mut self,
10319 _: &MoveToPreviousWordStart,
10320 window: &mut Window,
10321 cx: &mut Context<Self>,
10322 ) {
10323 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10324 s.move_cursors_with(|map, head, _| {
10325 (
10326 movement::previous_word_start(map, head),
10327 SelectionGoal::None,
10328 )
10329 });
10330 })
10331 }
10332
10333 pub fn move_to_previous_subword_start(
10334 &mut self,
10335 _: &MoveToPreviousSubwordStart,
10336 window: &mut Window,
10337 cx: &mut Context<Self>,
10338 ) {
10339 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10340 s.move_cursors_with(|map, head, _| {
10341 (
10342 movement::previous_subword_start(map, head),
10343 SelectionGoal::None,
10344 )
10345 });
10346 })
10347 }
10348
10349 pub fn select_to_previous_word_start(
10350 &mut self,
10351 _: &SelectToPreviousWordStart,
10352 window: &mut Window,
10353 cx: &mut Context<Self>,
10354 ) {
10355 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10356 s.move_heads_with(|map, head, _| {
10357 (
10358 movement::previous_word_start(map, head),
10359 SelectionGoal::None,
10360 )
10361 });
10362 })
10363 }
10364
10365 pub fn select_to_previous_subword_start(
10366 &mut self,
10367 _: &SelectToPreviousSubwordStart,
10368 window: &mut Window,
10369 cx: &mut Context<Self>,
10370 ) {
10371 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10372 s.move_heads_with(|map, head, _| {
10373 (
10374 movement::previous_subword_start(map, head),
10375 SelectionGoal::None,
10376 )
10377 });
10378 })
10379 }
10380
10381 pub fn delete_to_previous_word_start(
10382 &mut self,
10383 action: &DeleteToPreviousWordStart,
10384 window: &mut Window,
10385 cx: &mut Context<Self>,
10386 ) {
10387 self.transact(window, cx, |this, window, cx| {
10388 this.select_autoclose_pair(window, cx);
10389 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10390 let line_mode = s.line_mode;
10391 s.move_with(|map, selection| {
10392 if selection.is_empty() && !line_mode {
10393 let cursor = if action.ignore_newlines {
10394 movement::previous_word_start(map, selection.head())
10395 } else {
10396 movement::previous_word_start_or_newline(map, selection.head())
10397 };
10398 selection.set_head(cursor, SelectionGoal::None);
10399 }
10400 });
10401 });
10402 this.insert("", window, cx);
10403 });
10404 }
10405
10406 pub fn delete_to_previous_subword_start(
10407 &mut self,
10408 _: &DeleteToPreviousSubwordStart,
10409 window: &mut Window,
10410 cx: &mut Context<Self>,
10411 ) {
10412 self.transact(window, cx, |this, window, cx| {
10413 this.select_autoclose_pair(window, cx);
10414 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10415 let line_mode = s.line_mode;
10416 s.move_with(|map, selection| {
10417 if selection.is_empty() && !line_mode {
10418 let cursor = movement::previous_subword_start(map, selection.head());
10419 selection.set_head(cursor, SelectionGoal::None);
10420 }
10421 });
10422 });
10423 this.insert("", window, cx);
10424 });
10425 }
10426
10427 pub fn move_to_next_word_end(
10428 &mut self,
10429 _: &MoveToNextWordEnd,
10430 window: &mut Window,
10431 cx: &mut Context<Self>,
10432 ) {
10433 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10434 s.move_cursors_with(|map, head, _| {
10435 (movement::next_word_end(map, head), SelectionGoal::None)
10436 });
10437 })
10438 }
10439
10440 pub fn move_to_next_subword_end(
10441 &mut self,
10442 _: &MoveToNextSubwordEnd,
10443 window: &mut Window,
10444 cx: &mut Context<Self>,
10445 ) {
10446 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10447 s.move_cursors_with(|map, head, _| {
10448 (movement::next_subword_end(map, head), SelectionGoal::None)
10449 });
10450 })
10451 }
10452
10453 pub fn select_to_next_word_end(
10454 &mut self,
10455 _: &SelectToNextWordEnd,
10456 window: &mut Window,
10457 cx: &mut Context<Self>,
10458 ) {
10459 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10460 s.move_heads_with(|map, head, _| {
10461 (movement::next_word_end(map, head), SelectionGoal::None)
10462 });
10463 })
10464 }
10465
10466 pub fn select_to_next_subword_end(
10467 &mut self,
10468 _: &SelectToNextSubwordEnd,
10469 window: &mut Window,
10470 cx: &mut Context<Self>,
10471 ) {
10472 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10473 s.move_heads_with(|map, head, _| {
10474 (movement::next_subword_end(map, head), SelectionGoal::None)
10475 });
10476 })
10477 }
10478
10479 pub fn delete_to_next_word_end(
10480 &mut self,
10481 action: &DeleteToNextWordEnd,
10482 window: &mut Window,
10483 cx: &mut Context<Self>,
10484 ) {
10485 self.transact(window, cx, |this, window, cx| {
10486 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10487 let line_mode = s.line_mode;
10488 s.move_with(|map, selection| {
10489 if selection.is_empty() && !line_mode {
10490 let cursor = if action.ignore_newlines {
10491 movement::next_word_end(map, selection.head())
10492 } else {
10493 movement::next_word_end_or_newline(map, selection.head())
10494 };
10495 selection.set_head(cursor, SelectionGoal::None);
10496 }
10497 });
10498 });
10499 this.insert("", window, cx);
10500 });
10501 }
10502
10503 pub fn delete_to_next_subword_end(
10504 &mut self,
10505 _: &DeleteToNextSubwordEnd,
10506 window: &mut Window,
10507 cx: &mut Context<Self>,
10508 ) {
10509 self.transact(window, cx, |this, window, cx| {
10510 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10511 s.move_with(|map, selection| {
10512 if selection.is_empty() {
10513 let cursor = movement::next_subword_end(map, selection.head());
10514 selection.set_head(cursor, SelectionGoal::None);
10515 }
10516 });
10517 });
10518 this.insert("", window, cx);
10519 });
10520 }
10521
10522 pub fn move_to_beginning_of_line(
10523 &mut self,
10524 action: &MoveToBeginningOfLine,
10525 window: &mut Window,
10526 cx: &mut Context<Self>,
10527 ) {
10528 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10529 s.move_cursors_with(|map, head, _| {
10530 (
10531 movement::indented_line_beginning(
10532 map,
10533 head,
10534 action.stop_at_soft_wraps,
10535 action.stop_at_indent,
10536 ),
10537 SelectionGoal::None,
10538 )
10539 });
10540 })
10541 }
10542
10543 pub fn select_to_beginning_of_line(
10544 &mut self,
10545 action: &SelectToBeginningOfLine,
10546 window: &mut Window,
10547 cx: &mut Context<Self>,
10548 ) {
10549 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10550 s.move_heads_with(|map, head, _| {
10551 (
10552 movement::indented_line_beginning(
10553 map,
10554 head,
10555 action.stop_at_soft_wraps,
10556 action.stop_at_indent,
10557 ),
10558 SelectionGoal::None,
10559 )
10560 });
10561 });
10562 }
10563
10564 pub fn delete_to_beginning_of_line(
10565 &mut self,
10566 action: &DeleteToBeginningOfLine,
10567 window: &mut Window,
10568 cx: &mut Context<Self>,
10569 ) {
10570 self.transact(window, cx, |this, window, cx| {
10571 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10572 s.move_with(|_, selection| {
10573 selection.reversed = true;
10574 });
10575 });
10576
10577 this.select_to_beginning_of_line(
10578 &SelectToBeginningOfLine {
10579 stop_at_soft_wraps: false,
10580 stop_at_indent: action.stop_at_indent,
10581 },
10582 window,
10583 cx,
10584 );
10585 this.backspace(&Backspace, window, cx);
10586 });
10587 }
10588
10589 pub fn move_to_end_of_line(
10590 &mut self,
10591 action: &MoveToEndOfLine,
10592 window: &mut Window,
10593 cx: &mut Context<Self>,
10594 ) {
10595 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10596 s.move_cursors_with(|map, head, _| {
10597 (
10598 movement::line_end(map, head, action.stop_at_soft_wraps),
10599 SelectionGoal::None,
10600 )
10601 });
10602 })
10603 }
10604
10605 pub fn select_to_end_of_line(
10606 &mut self,
10607 action: &SelectToEndOfLine,
10608 window: &mut Window,
10609 cx: &mut Context<Self>,
10610 ) {
10611 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10612 s.move_heads_with(|map, head, _| {
10613 (
10614 movement::line_end(map, head, action.stop_at_soft_wraps),
10615 SelectionGoal::None,
10616 )
10617 });
10618 })
10619 }
10620
10621 pub fn delete_to_end_of_line(
10622 &mut self,
10623 _: &DeleteToEndOfLine,
10624 window: &mut Window,
10625 cx: &mut Context<Self>,
10626 ) {
10627 self.transact(window, cx, |this, window, cx| {
10628 this.select_to_end_of_line(
10629 &SelectToEndOfLine {
10630 stop_at_soft_wraps: false,
10631 },
10632 window,
10633 cx,
10634 );
10635 this.delete(&Delete, window, cx);
10636 });
10637 }
10638
10639 pub fn cut_to_end_of_line(
10640 &mut self,
10641 _: &CutToEndOfLine,
10642 window: &mut Window,
10643 cx: &mut Context<Self>,
10644 ) {
10645 self.transact(window, cx, |this, window, cx| {
10646 this.select_to_end_of_line(
10647 &SelectToEndOfLine {
10648 stop_at_soft_wraps: false,
10649 },
10650 window,
10651 cx,
10652 );
10653 this.cut(&Cut, window, cx);
10654 });
10655 }
10656
10657 pub fn move_to_start_of_paragraph(
10658 &mut self,
10659 _: &MoveToStartOfParagraph,
10660 window: &mut Window,
10661 cx: &mut Context<Self>,
10662 ) {
10663 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10664 cx.propagate();
10665 return;
10666 }
10667
10668 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10669 s.move_with(|map, selection| {
10670 selection.collapse_to(
10671 movement::start_of_paragraph(map, selection.head(), 1),
10672 SelectionGoal::None,
10673 )
10674 });
10675 })
10676 }
10677
10678 pub fn move_to_end_of_paragraph(
10679 &mut self,
10680 _: &MoveToEndOfParagraph,
10681 window: &mut Window,
10682 cx: &mut Context<Self>,
10683 ) {
10684 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10685 cx.propagate();
10686 return;
10687 }
10688
10689 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10690 s.move_with(|map, selection| {
10691 selection.collapse_to(
10692 movement::end_of_paragraph(map, selection.head(), 1),
10693 SelectionGoal::None,
10694 )
10695 });
10696 })
10697 }
10698
10699 pub fn select_to_start_of_paragraph(
10700 &mut self,
10701 _: &SelectToStartOfParagraph,
10702 window: &mut Window,
10703 cx: &mut Context<Self>,
10704 ) {
10705 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10706 cx.propagate();
10707 return;
10708 }
10709
10710 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10711 s.move_heads_with(|map, head, _| {
10712 (
10713 movement::start_of_paragraph(map, head, 1),
10714 SelectionGoal::None,
10715 )
10716 });
10717 })
10718 }
10719
10720 pub fn select_to_end_of_paragraph(
10721 &mut self,
10722 _: &SelectToEndOfParagraph,
10723 window: &mut Window,
10724 cx: &mut Context<Self>,
10725 ) {
10726 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10727 cx.propagate();
10728 return;
10729 }
10730
10731 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10732 s.move_heads_with(|map, head, _| {
10733 (
10734 movement::end_of_paragraph(map, head, 1),
10735 SelectionGoal::None,
10736 )
10737 });
10738 })
10739 }
10740
10741 pub fn move_to_start_of_excerpt(
10742 &mut self,
10743 _: &MoveToStartOfExcerpt,
10744 window: &mut Window,
10745 cx: &mut Context<Self>,
10746 ) {
10747 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10748 cx.propagate();
10749 return;
10750 }
10751
10752 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10753 s.move_with(|map, selection| {
10754 selection.collapse_to(
10755 movement::start_of_excerpt(
10756 map,
10757 selection.head(),
10758 workspace::searchable::Direction::Prev,
10759 ),
10760 SelectionGoal::None,
10761 )
10762 });
10763 })
10764 }
10765
10766 pub fn move_to_start_of_next_excerpt(
10767 &mut self,
10768 _: &MoveToStartOfNextExcerpt,
10769 window: &mut Window,
10770 cx: &mut Context<Self>,
10771 ) {
10772 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10773 cx.propagate();
10774 return;
10775 }
10776
10777 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10778 s.move_with(|map, selection| {
10779 selection.collapse_to(
10780 movement::start_of_excerpt(
10781 map,
10782 selection.head(),
10783 workspace::searchable::Direction::Next,
10784 ),
10785 SelectionGoal::None,
10786 )
10787 });
10788 })
10789 }
10790
10791 pub fn move_to_end_of_excerpt(
10792 &mut self,
10793 _: &MoveToEndOfExcerpt,
10794 window: &mut Window,
10795 cx: &mut Context<Self>,
10796 ) {
10797 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10798 cx.propagate();
10799 return;
10800 }
10801
10802 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10803 s.move_with(|map, selection| {
10804 selection.collapse_to(
10805 movement::end_of_excerpt(
10806 map,
10807 selection.head(),
10808 workspace::searchable::Direction::Next,
10809 ),
10810 SelectionGoal::None,
10811 )
10812 });
10813 })
10814 }
10815
10816 pub fn move_to_end_of_previous_excerpt(
10817 &mut self,
10818 _: &MoveToEndOfPreviousExcerpt,
10819 window: &mut Window,
10820 cx: &mut Context<Self>,
10821 ) {
10822 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10823 cx.propagate();
10824 return;
10825 }
10826
10827 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10828 s.move_with(|map, selection| {
10829 selection.collapse_to(
10830 movement::end_of_excerpt(
10831 map,
10832 selection.head(),
10833 workspace::searchable::Direction::Prev,
10834 ),
10835 SelectionGoal::None,
10836 )
10837 });
10838 })
10839 }
10840
10841 pub fn select_to_start_of_excerpt(
10842 &mut self,
10843 _: &SelectToStartOfExcerpt,
10844 window: &mut Window,
10845 cx: &mut Context<Self>,
10846 ) {
10847 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10848 cx.propagate();
10849 return;
10850 }
10851
10852 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10853 s.move_heads_with(|map, head, _| {
10854 (
10855 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10856 SelectionGoal::None,
10857 )
10858 });
10859 })
10860 }
10861
10862 pub fn select_to_start_of_next_excerpt(
10863 &mut self,
10864 _: &SelectToStartOfNextExcerpt,
10865 window: &mut Window,
10866 cx: &mut Context<Self>,
10867 ) {
10868 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10869 cx.propagate();
10870 return;
10871 }
10872
10873 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10874 s.move_heads_with(|map, head, _| {
10875 (
10876 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10877 SelectionGoal::None,
10878 )
10879 });
10880 })
10881 }
10882
10883 pub fn select_to_end_of_excerpt(
10884 &mut self,
10885 _: &SelectToEndOfExcerpt,
10886 window: &mut Window,
10887 cx: &mut Context<Self>,
10888 ) {
10889 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10890 cx.propagate();
10891 return;
10892 }
10893
10894 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10895 s.move_heads_with(|map, head, _| {
10896 (
10897 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10898 SelectionGoal::None,
10899 )
10900 });
10901 })
10902 }
10903
10904 pub fn select_to_end_of_previous_excerpt(
10905 &mut self,
10906 _: &SelectToEndOfPreviousExcerpt,
10907 window: &mut Window,
10908 cx: &mut Context<Self>,
10909 ) {
10910 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10911 cx.propagate();
10912 return;
10913 }
10914
10915 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10916 s.move_heads_with(|map, head, _| {
10917 (
10918 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10919 SelectionGoal::None,
10920 )
10921 });
10922 })
10923 }
10924
10925 pub fn move_to_beginning(
10926 &mut self,
10927 _: &MoveToBeginning,
10928 window: &mut Window,
10929 cx: &mut Context<Self>,
10930 ) {
10931 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10932 cx.propagate();
10933 return;
10934 }
10935
10936 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10937 s.select_ranges(vec![0..0]);
10938 });
10939 }
10940
10941 pub fn select_to_beginning(
10942 &mut self,
10943 _: &SelectToBeginning,
10944 window: &mut Window,
10945 cx: &mut Context<Self>,
10946 ) {
10947 let mut selection = self.selections.last::<Point>(cx);
10948 selection.set_head(Point::zero(), SelectionGoal::None);
10949
10950 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10951 s.select(vec![selection]);
10952 });
10953 }
10954
10955 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10956 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10957 cx.propagate();
10958 return;
10959 }
10960
10961 let cursor = self.buffer.read(cx).read(cx).len();
10962 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10963 s.select_ranges(vec![cursor..cursor])
10964 });
10965 }
10966
10967 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10968 self.nav_history = nav_history;
10969 }
10970
10971 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10972 self.nav_history.as_ref()
10973 }
10974
10975 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10976 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10977 }
10978
10979 fn push_to_nav_history(
10980 &mut self,
10981 cursor_anchor: Anchor,
10982 new_position: Option<Point>,
10983 is_deactivate: bool,
10984 cx: &mut Context<Self>,
10985 ) {
10986 if let Some(nav_history) = self.nav_history.as_mut() {
10987 let buffer = self.buffer.read(cx).read(cx);
10988 let cursor_position = cursor_anchor.to_point(&buffer);
10989 let scroll_state = self.scroll_manager.anchor();
10990 let scroll_top_row = scroll_state.top_row(&buffer);
10991 drop(buffer);
10992
10993 if let Some(new_position) = new_position {
10994 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10995 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10996 return;
10997 }
10998 }
10999
11000 nav_history.push(
11001 Some(NavigationData {
11002 cursor_anchor,
11003 cursor_position,
11004 scroll_anchor: scroll_state,
11005 scroll_top_row,
11006 }),
11007 cx,
11008 );
11009 cx.emit(EditorEvent::PushedToNavHistory {
11010 anchor: cursor_anchor,
11011 is_deactivate,
11012 })
11013 }
11014 }
11015
11016 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
11017 let buffer = self.buffer.read(cx).snapshot(cx);
11018 let mut selection = self.selections.first::<usize>(cx);
11019 selection.set_head(buffer.len(), SelectionGoal::None);
11020 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11021 s.select(vec![selection]);
11022 });
11023 }
11024
11025 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
11026 let end = self.buffer.read(cx).read(cx).len();
11027 self.change_selections(None, window, cx, |s| {
11028 s.select_ranges(vec![0..end]);
11029 });
11030 }
11031
11032 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
11033 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11034 let mut selections = self.selections.all::<Point>(cx);
11035 let max_point = display_map.buffer_snapshot.max_point();
11036 for selection in &mut selections {
11037 let rows = selection.spanned_rows(true, &display_map);
11038 selection.start = Point::new(rows.start.0, 0);
11039 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
11040 selection.reversed = false;
11041 }
11042 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11043 s.select(selections);
11044 });
11045 }
11046
11047 pub fn split_selection_into_lines(
11048 &mut self,
11049 _: &SplitSelectionIntoLines,
11050 window: &mut Window,
11051 cx: &mut Context<Self>,
11052 ) {
11053 let selections = self
11054 .selections
11055 .all::<Point>(cx)
11056 .into_iter()
11057 .map(|selection| selection.start..selection.end)
11058 .collect::<Vec<_>>();
11059 self.unfold_ranges(&selections, true, true, cx);
11060
11061 let mut new_selection_ranges = Vec::new();
11062 {
11063 let buffer = self.buffer.read(cx).read(cx);
11064 for selection in selections {
11065 for row in selection.start.row..selection.end.row {
11066 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11067 new_selection_ranges.push(cursor..cursor);
11068 }
11069
11070 let is_multiline_selection = selection.start.row != selection.end.row;
11071 // Don't insert last one if it's a multi-line selection ending at the start of a line,
11072 // so this action feels more ergonomic when paired with other selection operations
11073 let should_skip_last = is_multiline_selection && selection.end.column == 0;
11074 if !should_skip_last {
11075 new_selection_ranges.push(selection.end..selection.end);
11076 }
11077 }
11078 }
11079 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11080 s.select_ranges(new_selection_ranges);
11081 });
11082 }
11083
11084 pub fn add_selection_above(
11085 &mut self,
11086 _: &AddSelectionAbove,
11087 window: &mut Window,
11088 cx: &mut Context<Self>,
11089 ) {
11090 self.add_selection(true, window, cx);
11091 }
11092
11093 pub fn add_selection_below(
11094 &mut self,
11095 _: &AddSelectionBelow,
11096 window: &mut Window,
11097 cx: &mut Context<Self>,
11098 ) {
11099 self.add_selection(false, window, cx);
11100 }
11101
11102 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
11103 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11104 let mut selections = self.selections.all::<Point>(cx);
11105 let text_layout_details = self.text_layout_details(window);
11106 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
11107 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
11108 let range = oldest_selection.display_range(&display_map).sorted();
11109
11110 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
11111 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
11112 let positions = start_x.min(end_x)..start_x.max(end_x);
11113
11114 selections.clear();
11115 let mut stack = Vec::new();
11116 for row in range.start.row().0..=range.end.row().0 {
11117 if let Some(selection) = self.selections.build_columnar_selection(
11118 &display_map,
11119 DisplayRow(row),
11120 &positions,
11121 oldest_selection.reversed,
11122 &text_layout_details,
11123 ) {
11124 stack.push(selection.id);
11125 selections.push(selection);
11126 }
11127 }
11128
11129 if above {
11130 stack.reverse();
11131 }
11132
11133 AddSelectionsState { above, stack }
11134 });
11135
11136 let last_added_selection = *state.stack.last().unwrap();
11137 let mut new_selections = Vec::new();
11138 if above == state.above {
11139 let end_row = if above {
11140 DisplayRow(0)
11141 } else {
11142 display_map.max_point().row()
11143 };
11144
11145 'outer: for selection in selections {
11146 if selection.id == last_added_selection {
11147 let range = selection.display_range(&display_map).sorted();
11148 debug_assert_eq!(range.start.row(), range.end.row());
11149 let mut row = range.start.row();
11150 let positions =
11151 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
11152 px(start)..px(end)
11153 } else {
11154 let start_x =
11155 display_map.x_for_display_point(range.start, &text_layout_details);
11156 let end_x =
11157 display_map.x_for_display_point(range.end, &text_layout_details);
11158 start_x.min(end_x)..start_x.max(end_x)
11159 };
11160
11161 while row != end_row {
11162 if above {
11163 row.0 -= 1;
11164 } else {
11165 row.0 += 1;
11166 }
11167
11168 if let Some(new_selection) = self.selections.build_columnar_selection(
11169 &display_map,
11170 row,
11171 &positions,
11172 selection.reversed,
11173 &text_layout_details,
11174 ) {
11175 state.stack.push(new_selection.id);
11176 if above {
11177 new_selections.push(new_selection);
11178 new_selections.push(selection);
11179 } else {
11180 new_selections.push(selection);
11181 new_selections.push(new_selection);
11182 }
11183
11184 continue 'outer;
11185 }
11186 }
11187 }
11188
11189 new_selections.push(selection);
11190 }
11191 } else {
11192 new_selections = selections;
11193 new_selections.retain(|s| s.id != last_added_selection);
11194 state.stack.pop();
11195 }
11196
11197 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11198 s.select(new_selections);
11199 });
11200 if state.stack.len() > 1 {
11201 self.add_selections_state = Some(state);
11202 }
11203 }
11204
11205 pub fn select_next_match_internal(
11206 &mut self,
11207 display_map: &DisplaySnapshot,
11208 replace_newest: bool,
11209 autoscroll: Option<Autoscroll>,
11210 window: &mut Window,
11211 cx: &mut Context<Self>,
11212 ) -> Result<()> {
11213 fn select_next_match_ranges(
11214 this: &mut Editor,
11215 range: Range<usize>,
11216 replace_newest: bool,
11217 auto_scroll: Option<Autoscroll>,
11218 window: &mut Window,
11219 cx: &mut Context<Editor>,
11220 ) {
11221 this.unfold_ranges(&[range.clone()], false, true, cx);
11222 this.change_selections(auto_scroll, window, cx, |s| {
11223 if replace_newest {
11224 s.delete(s.newest_anchor().id);
11225 }
11226 s.insert_range(range.clone());
11227 });
11228 }
11229
11230 let buffer = &display_map.buffer_snapshot;
11231 let mut selections = self.selections.all::<usize>(cx);
11232 if let Some(mut select_next_state) = self.select_next_state.take() {
11233 let query = &select_next_state.query;
11234 if !select_next_state.done {
11235 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11236 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11237 let mut next_selected_range = None;
11238
11239 let bytes_after_last_selection =
11240 buffer.bytes_in_range(last_selection.end..buffer.len());
11241 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11242 let query_matches = query
11243 .stream_find_iter(bytes_after_last_selection)
11244 .map(|result| (last_selection.end, result))
11245 .chain(
11246 query
11247 .stream_find_iter(bytes_before_first_selection)
11248 .map(|result| (0, result)),
11249 );
11250
11251 for (start_offset, query_match) in query_matches {
11252 let query_match = query_match.unwrap(); // can only fail due to I/O
11253 let offset_range =
11254 start_offset + query_match.start()..start_offset + query_match.end();
11255 let display_range = offset_range.start.to_display_point(display_map)
11256 ..offset_range.end.to_display_point(display_map);
11257
11258 if !select_next_state.wordwise
11259 || (!movement::is_inside_word(display_map, display_range.start)
11260 && !movement::is_inside_word(display_map, display_range.end))
11261 {
11262 // TODO: This is n^2, because we might check all the selections
11263 if !selections
11264 .iter()
11265 .any(|selection| selection.range().overlaps(&offset_range))
11266 {
11267 next_selected_range = Some(offset_range);
11268 break;
11269 }
11270 }
11271 }
11272
11273 if let Some(next_selected_range) = next_selected_range {
11274 select_next_match_ranges(
11275 self,
11276 next_selected_range,
11277 replace_newest,
11278 autoscroll,
11279 window,
11280 cx,
11281 );
11282 } else {
11283 select_next_state.done = true;
11284 }
11285 }
11286
11287 self.select_next_state = Some(select_next_state);
11288 } else {
11289 let mut only_carets = true;
11290 let mut same_text_selected = true;
11291 let mut selected_text = None;
11292
11293 let mut selections_iter = selections.iter().peekable();
11294 while let Some(selection) = selections_iter.next() {
11295 if selection.start != selection.end {
11296 only_carets = false;
11297 }
11298
11299 if same_text_selected {
11300 if selected_text.is_none() {
11301 selected_text =
11302 Some(buffer.text_for_range(selection.range()).collect::<String>());
11303 }
11304
11305 if let Some(next_selection) = selections_iter.peek() {
11306 if next_selection.range().len() == selection.range().len() {
11307 let next_selected_text = buffer
11308 .text_for_range(next_selection.range())
11309 .collect::<String>();
11310 if Some(next_selected_text) != selected_text {
11311 same_text_selected = false;
11312 selected_text = None;
11313 }
11314 } else {
11315 same_text_selected = false;
11316 selected_text = None;
11317 }
11318 }
11319 }
11320 }
11321
11322 if only_carets {
11323 for selection in &mut selections {
11324 let word_range = movement::surrounding_word(
11325 display_map,
11326 selection.start.to_display_point(display_map),
11327 );
11328 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11329 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11330 selection.goal = SelectionGoal::None;
11331 selection.reversed = false;
11332 select_next_match_ranges(
11333 self,
11334 selection.start..selection.end,
11335 replace_newest,
11336 autoscroll,
11337 window,
11338 cx,
11339 );
11340 }
11341
11342 if selections.len() == 1 {
11343 let selection = selections
11344 .last()
11345 .expect("ensured that there's only one selection");
11346 let query = buffer
11347 .text_for_range(selection.start..selection.end)
11348 .collect::<String>();
11349 let is_empty = query.is_empty();
11350 let select_state = SelectNextState {
11351 query: AhoCorasick::new(&[query])?,
11352 wordwise: true,
11353 done: is_empty,
11354 };
11355 self.select_next_state = Some(select_state);
11356 } else {
11357 self.select_next_state = None;
11358 }
11359 } else if let Some(selected_text) = selected_text {
11360 self.select_next_state = Some(SelectNextState {
11361 query: AhoCorasick::new(&[selected_text])?,
11362 wordwise: false,
11363 done: false,
11364 });
11365 self.select_next_match_internal(
11366 display_map,
11367 replace_newest,
11368 autoscroll,
11369 window,
11370 cx,
11371 )?;
11372 }
11373 }
11374 Ok(())
11375 }
11376
11377 pub fn select_all_matches(
11378 &mut self,
11379 _action: &SelectAllMatches,
11380 window: &mut Window,
11381 cx: &mut Context<Self>,
11382 ) -> Result<()> {
11383 self.push_to_selection_history();
11384 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11385
11386 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11387 let Some(select_next_state) = self.select_next_state.as_mut() else {
11388 return Ok(());
11389 };
11390 if select_next_state.done {
11391 return Ok(());
11392 }
11393
11394 let mut new_selections = self.selections.all::<usize>(cx);
11395
11396 let buffer = &display_map.buffer_snapshot;
11397 let query_matches = select_next_state
11398 .query
11399 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11400
11401 for query_match in query_matches {
11402 let query_match = query_match.unwrap(); // can only fail due to I/O
11403 let offset_range = query_match.start()..query_match.end();
11404 let display_range = offset_range.start.to_display_point(&display_map)
11405 ..offset_range.end.to_display_point(&display_map);
11406
11407 if !select_next_state.wordwise
11408 || (!movement::is_inside_word(&display_map, display_range.start)
11409 && !movement::is_inside_word(&display_map, display_range.end))
11410 {
11411 self.selections.change_with(cx, |selections| {
11412 new_selections.push(Selection {
11413 id: selections.new_selection_id(),
11414 start: offset_range.start,
11415 end: offset_range.end,
11416 reversed: false,
11417 goal: SelectionGoal::None,
11418 });
11419 });
11420 }
11421 }
11422
11423 new_selections.sort_by_key(|selection| selection.start);
11424 let mut ix = 0;
11425 while ix + 1 < new_selections.len() {
11426 let current_selection = &new_selections[ix];
11427 let next_selection = &new_selections[ix + 1];
11428 if current_selection.range().overlaps(&next_selection.range()) {
11429 if current_selection.id < next_selection.id {
11430 new_selections.remove(ix + 1);
11431 } else {
11432 new_selections.remove(ix);
11433 }
11434 } else {
11435 ix += 1;
11436 }
11437 }
11438
11439 let reversed = self.selections.oldest::<usize>(cx).reversed;
11440
11441 for selection in new_selections.iter_mut() {
11442 selection.reversed = reversed;
11443 }
11444
11445 select_next_state.done = true;
11446 self.unfold_ranges(
11447 &new_selections
11448 .iter()
11449 .map(|selection| selection.range())
11450 .collect::<Vec<_>>(),
11451 false,
11452 false,
11453 cx,
11454 );
11455 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11456 selections.select(new_selections)
11457 });
11458
11459 Ok(())
11460 }
11461
11462 pub fn select_next(
11463 &mut self,
11464 action: &SelectNext,
11465 window: &mut Window,
11466 cx: &mut Context<Self>,
11467 ) -> Result<()> {
11468 self.push_to_selection_history();
11469 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11470 self.select_next_match_internal(
11471 &display_map,
11472 action.replace_newest,
11473 Some(Autoscroll::newest()),
11474 window,
11475 cx,
11476 )?;
11477 Ok(())
11478 }
11479
11480 pub fn select_previous(
11481 &mut self,
11482 action: &SelectPrevious,
11483 window: &mut Window,
11484 cx: &mut Context<Self>,
11485 ) -> Result<()> {
11486 self.push_to_selection_history();
11487 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11488 let buffer = &display_map.buffer_snapshot;
11489 let mut selections = self.selections.all::<usize>(cx);
11490 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11491 let query = &select_prev_state.query;
11492 if !select_prev_state.done {
11493 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11494 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11495 let mut next_selected_range = None;
11496 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11497 let bytes_before_last_selection =
11498 buffer.reversed_bytes_in_range(0..last_selection.start);
11499 let bytes_after_first_selection =
11500 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11501 let query_matches = query
11502 .stream_find_iter(bytes_before_last_selection)
11503 .map(|result| (last_selection.start, result))
11504 .chain(
11505 query
11506 .stream_find_iter(bytes_after_first_selection)
11507 .map(|result| (buffer.len(), result)),
11508 );
11509 for (end_offset, query_match) in query_matches {
11510 let query_match = query_match.unwrap(); // can only fail due to I/O
11511 let offset_range =
11512 end_offset - query_match.end()..end_offset - query_match.start();
11513 let display_range = offset_range.start.to_display_point(&display_map)
11514 ..offset_range.end.to_display_point(&display_map);
11515
11516 if !select_prev_state.wordwise
11517 || (!movement::is_inside_word(&display_map, display_range.start)
11518 && !movement::is_inside_word(&display_map, display_range.end))
11519 {
11520 next_selected_range = Some(offset_range);
11521 break;
11522 }
11523 }
11524
11525 if let Some(next_selected_range) = next_selected_range {
11526 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11527 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11528 if action.replace_newest {
11529 s.delete(s.newest_anchor().id);
11530 }
11531 s.insert_range(next_selected_range);
11532 });
11533 } else {
11534 select_prev_state.done = true;
11535 }
11536 }
11537
11538 self.select_prev_state = Some(select_prev_state);
11539 } else {
11540 let mut only_carets = true;
11541 let mut same_text_selected = true;
11542 let mut selected_text = None;
11543
11544 let mut selections_iter = selections.iter().peekable();
11545 while let Some(selection) = selections_iter.next() {
11546 if selection.start != selection.end {
11547 only_carets = false;
11548 }
11549
11550 if same_text_selected {
11551 if selected_text.is_none() {
11552 selected_text =
11553 Some(buffer.text_for_range(selection.range()).collect::<String>());
11554 }
11555
11556 if let Some(next_selection) = selections_iter.peek() {
11557 if next_selection.range().len() == selection.range().len() {
11558 let next_selected_text = buffer
11559 .text_for_range(next_selection.range())
11560 .collect::<String>();
11561 if Some(next_selected_text) != selected_text {
11562 same_text_selected = false;
11563 selected_text = None;
11564 }
11565 } else {
11566 same_text_selected = false;
11567 selected_text = None;
11568 }
11569 }
11570 }
11571 }
11572
11573 if only_carets {
11574 for selection in &mut selections {
11575 let word_range = movement::surrounding_word(
11576 &display_map,
11577 selection.start.to_display_point(&display_map),
11578 );
11579 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11580 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11581 selection.goal = SelectionGoal::None;
11582 selection.reversed = false;
11583 }
11584 if selections.len() == 1 {
11585 let selection = selections
11586 .last()
11587 .expect("ensured that there's only one selection");
11588 let query = buffer
11589 .text_for_range(selection.start..selection.end)
11590 .collect::<String>();
11591 let is_empty = query.is_empty();
11592 let select_state = SelectNextState {
11593 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11594 wordwise: true,
11595 done: is_empty,
11596 };
11597 self.select_prev_state = Some(select_state);
11598 } else {
11599 self.select_prev_state = None;
11600 }
11601
11602 self.unfold_ranges(
11603 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11604 false,
11605 true,
11606 cx,
11607 );
11608 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11609 s.select(selections);
11610 });
11611 } else if let Some(selected_text) = selected_text {
11612 self.select_prev_state = Some(SelectNextState {
11613 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11614 wordwise: false,
11615 done: false,
11616 });
11617 self.select_previous(action, window, cx)?;
11618 }
11619 }
11620 Ok(())
11621 }
11622
11623 pub fn toggle_comments(
11624 &mut self,
11625 action: &ToggleComments,
11626 window: &mut Window,
11627 cx: &mut Context<Self>,
11628 ) {
11629 if self.read_only(cx) {
11630 return;
11631 }
11632 let text_layout_details = &self.text_layout_details(window);
11633 self.transact(window, cx, |this, window, cx| {
11634 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11635 let mut edits = Vec::new();
11636 let mut selection_edit_ranges = Vec::new();
11637 let mut last_toggled_row = None;
11638 let snapshot = this.buffer.read(cx).read(cx);
11639 let empty_str: Arc<str> = Arc::default();
11640 let mut suffixes_inserted = Vec::new();
11641 let ignore_indent = action.ignore_indent;
11642
11643 fn comment_prefix_range(
11644 snapshot: &MultiBufferSnapshot,
11645 row: MultiBufferRow,
11646 comment_prefix: &str,
11647 comment_prefix_whitespace: &str,
11648 ignore_indent: bool,
11649 ) -> Range<Point> {
11650 let indent_size = if ignore_indent {
11651 0
11652 } else {
11653 snapshot.indent_size_for_line(row).len
11654 };
11655
11656 let start = Point::new(row.0, indent_size);
11657
11658 let mut line_bytes = snapshot
11659 .bytes_in_range(start..snapshot.max_point())
11660 .flatten()
11661 .copied();
11662
11663 // If this line currently begins with the line comment prefix, then record
11664 // the range containing the prefix.
11665 if line_bytes
11666 .by_ref()
11667 .take(comment_prefix.len())
11668 .eq(comment_prefix.bytes())
11669 {
11670 // Include any whitespace that matches the comment prefix.
11671 let matching_whitespace_len = line_bytes
11672 .zip(comment_prefix_whitespace.bytes())
11673 .take_while(|(a, b)| a == b)
11674 .count() as u32;
11675 let end = Point::new(
11676 start.row,
11677 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11678 );
11679 start..end
11680 } else {
11681 start..start
11682 }
11683 }
11684
11685 fn comment_suffix_range(
11686 snapshot: &MultiBufferSnapshot,
11687 row: MultiBufferRow,
11688 comment_suffix: &str,
11689 comment_suffix_has_leading_space: bool,
11690 ) -> Range<Point> {
11691 let end = Point::new(row.0, snapshot.line_len(row));
11692 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11693
11694 let mut line_end_bytes = snapshot
11695 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11696 .flatten()
11697 .copied();
11698
11699 let leading_space_len = if suffix_start_column > 0
11700 && line_end_bytes.next() == Some(b' ')
11701 && comment_suffix_has_leading_space
11702 {
11703 1
11704 } else {
11705 0
11706 };
11707
11708 // If this line currently begins with the line comment prefix, then record
11709 // the range containing the prefix.
11710 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11711 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11712 start..end
11713 } else {
11714 end..end
11715 }
11716 }
11717
11718 // TODO: Handle selections that cross excerpts
11719 for selection in &mut selections {
11720 let start_column = snapshot
11721 .indent_size_for_line(MultiBufferRow(selection.start.row))
11722 .len;
11723 let language = if let Some(language) =
11724 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11725 {
11726 language
11727 } else {
11728 continue;
11729 };
11730
11731 selection_edit_ranges.clear();
11732
11733 // If multiple selections contain a given row, avoid processing that
11734 // row more than once.
11735 let mut start_row = MultiBufferRow(selection.start.row);
11736 if last_toggled_row == Some(start_row) {
11737 start_row = start_row.next_row();
11738 }
11739 let end_row =
11740 if selection.end.row > selection.start.row && selection.end.column == 0 {
11741 MultiBufferRow(selection.end.row - 1)
11742 } else {
11743 MultiBufferRow(selection.end.row)
11744 };
11745 last_toggled_row = Some(end_row);
11746
11747 if start_row > end_row {
11748 continue;
11749 }
11750
11751 // If the language has line comments, toggle those.
11752 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11753
11754 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11755 if ignore_indent {
11756 full_comment_prefixes = full_comment_prefixes
11757 .into_iter()
11758 .map(|s| Arc::from(s.trim_end()))
11759 .collect();
11760 }
11761
11762 if !full_comment_prefixes.is_empty() {
11763 let first_prefix = full_comment_prefixes
11764 .first()
11765 .expect("prefixes is non-empty");
11766 let prefix_trimmed_lengths = full_comment_prefixes
11767 .iter()
11768 .map(|p| p.trim_end_matches(' ').len())
11769 .collect::<SmallVec<[usize; 4]>>();
11770
11771 let mut all_selection_lines_are_comments = true;
11772
11773 for row in start_row.0..=end_row.0 {
11774 let row = MultiBufferRow(row);
11775 if start_row < end_row && snapshot.is_line_blank(row) {
11776 continue;
11777 }
11778
11779 let prefix_range = full_comment_prefixes
11780 .iter()
11781 .zip(prefix_trimmed_lengths.iter().copied())
11782 .map(|(prefix, trimmed_prefix_len)| {
11783 comment_prefix_range(
11784 snapshot.deref(),
11785 row,
11786 &prefix[..trimmed_prefix_len],
11787 &prefix[trimmed_prefix_len..],
11788 ignore_indent,
11789 )
11790 })
11791 .max_by_key(|range| range.end.column - range.start.column)
11792 .expect("prefixes is non-empty");
11793
11794 if prefix_range.is_empty() {
11795 all_selection_lines_are_comments = false;
11796 }
11797
11798 selection_edit_ranges.push(prefix_range);
11799 }
11800
11801 if all_selection_lines_are_comments {
11802 edits.extend(
11803 selection_edit_ranges
11804 .iter()
11805 .cloned()
11806 .map(|range| (range, empty_str.clone())),
11807 );
11808 } else {
11809 let min_column = selection_edit_ranges
11810 .iter()
11811 .map(|range| range.start.column)
11812 .min()
11813 .unwrap_or(0);
11814 edits.extend(selection_edit_ranges.iter().map(|range| {
11815 let position = Point::new(range.start.row, min_column);
11816 (position..position, first_prefix.clone())
11817 }));
11818 }
11819 } else if let Some((full_comment_prefix, comment_suffix)) =
11820 language.block_comment_delimiters()
11821 {
11822 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11823 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11824 let prefix_range = comment_prefix_range(
11825 snapshot.deref(),
11826 start_row,
11827 comment_prefix,
11828 comment_prefix_whitespace,
11829 ignore_indent,
11830 );
11831 let suffix_range = comment_suffix_range(
11832 snapshot.deref(),
11833 end_row,
11834 comment_suffix.trim_start_matches(' '),
11835 comment_suffix.starts_with(' '),
11836 );
11837
11838 if prefix_range.is_empty() || suffix_range.is_empty() {
11839 edits.push((
11840 prefix_range.start..prefix_range.start,
11841 full_comment_prefix.clone(),
11842 ));
11843 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11844 suffixes_inserted.push((end_row, comment_suffix.len()));
11845 } else {
11846 edits.push((prefix_range, empty_str.clone()));
11847 edits.push((suffix_range, empty_str.clone()));
11848 }
11849 } else {
11850 continue;
11851 }
11852 }
11853
11854 drop(snapshot);
11855 this.buffer.update(cx, |buffer, cx| {
11856 buffer.edit(edits, None, cx);
11857 });
11858
11859 // Adjust selections so that they end before any comment suffixes that
11860 // were inserted.
11861 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11862 let mut selections = this.selections.all::<Point>(cx);
11863 let snapshot = this.buffer.read(cx).read(cx);
11864 for selection in &mut selections {
11865 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11866 match row.cmp(&MultiBufferRow(selection.end.row)) {
11867 Ordering::Less => {
11868 suffixes_inserted.next();
11869 continue;
11870 }
11871 Ordering::Greater => break,
11872 Ordering::Equal => {
11873 if selection.end.column == snapshot.line_len(row) {
11874 if selection.is_empty() {
11875 selection.start.column -= suffix_len as u32;
11876 }
11877 selection.end.column -= suffix_len as u32;
11878 }
11879 break;
11880 }
11881 }
11882 }
11883 }
11884
11885 drop(snapshot);
11886 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11887 s.select(selections)
11888 });
11889
11890 let selections = this.selections.all::<Point>(cx);
11891 let selections_on_single_row = selections.windows(2).all(|selections| {
11892 selections[0].start.row == selections[1].start.row
11893 && selections[0].end.row == selections[1].end.row
11894 && selections[0].start.row == selections[0].end.row
11895 });
11896 let selections_selecting = selections
11897 .iter()
11898 .any(|selection| selection.start != selection.end);
11899 let advance_downwards = action.advance_downwards
11900 && selections_on_single_row
11901 && !selections_selecting
11902 && !matches!(this.mode, EditorMode::SingleLine { .. });
11903
11904 if advance_downwards {
11905 let snapshot = this.buffer.read(cx).snapshot(cx);
11906
11907 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11908 s.move_cursors_with(|display_snapshot, display_point, _| {
11909 let mut point = display_point.to_point(display_snapshot);
11910 point.row += 1;
11911 point = snapshot.clip_point(point, Bias::Left);
11912 let display_point = point.to_display_point(display_snapshot);
11913 let goal = SelectionGoal::HorizontalPosition(
11914 display_snapshot
11915 .x_for_display_point(display_point, text_layout_details)
11916 .into(),
11917 );
11918 (display_point, goal)
11919 })
11920 });
11921 }
11922 });
11923 }
11924
11925 pub fn select_enclosing_symbol(
11926 &mut self,
11927 _: &SelectEnclosingSymbol,
11928 window: &mut Window,
11929 cx: &mut Context<Self>,
11930 ) {
11931 let buffer = self.buffer.read(cx).snapshot(cx);
11932 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11933
11934 fn update_selection(
11935 selection: &Selection<usize>,
11936 buffer_snap: &MultiBufferSnapshot,
11937 ) -> Option<Selection<usize>> {
11938 let cursor = selection.head();
11939 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11940 for symbol in symbols.iter().rev() {
11941 let start = symbol.range.start.to_offset(buffer_snap);
11942 let end = symbol.range.end.to_offset(buffer_snap);
11943 let new_range = start..end;
11944 if start < selection.start || end > selection.end {
11945 return Some(Selection {
11946 id: selection.id,
11947 start: new_range.start,
11948 end: new_range.end,
11949 goal: SelectionGoal::None,
11950 reversed: selection.reversed,
11951 });
11952 }
11953 }
11954 None
11955 }
11956
11957 let mut selected_larger_symbol = false;
11958 let new_selections = old_selections
11959 .iter()
11960 .map(|selection| match update_selection(selection, &buffer) {
11961 Some(new_selection) => {
11962 if new_selection.range() != selection.range() {
11963 selected_larger_symbol = true;
11964 }
11965 new_selection
11966 }
11967 None => selection.clone(),
11968 })
11969 .collect::<Vec<_>>();
11970
11971 if selected_larger_symbol {
11972 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11973 s.select(new_selections);
11974 });
11975 }
11976 }
11977
11978 pub fn select_larger_syntax_node(
11979 &mut self,
11980 _: &SelectLargerSyntaxNode,
11981 window: &mut Window,
11982 cx: &mut Context<Self>,
11983 ) {
11984 let Some(visible_row_count) = self.visible_row_count() else {
11985 return;
11986 };
11987 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
11988 if old_selections.is_empty() {
11989 return;
11990 }
11991
11992 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11993 let buffer = self.buffer.read(cx).snapshot(cx);
11994
11995 let mut selected_larger_node = false;
11996 let mut new_selections = old_selections
11997 .iter()
11998 .map(|selection| {
11999 let old_range = selection.start..selection.end;
12000 let mut new_range = old_range.clone();
12001 let mut new_node = None;
12002 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
12003 {
12004 new_node = Some(node);
12005 new_range = match containing_range {
12006 MultiOrSingleBufferOffsetRange::Single(_) => break,
12007 MultiOrSingleBufferOffsetRange::Multi(range) => range,
12008 };
12009 if !display_map.intersects_fold(new_range.start)
12010 && !display_map.intersects_fold(new_range.end)
12011 {
12012 break;
12013 }
12014 }
12015
12016 if let Some(node) = new_node {
12017 // Log the ancestor, to support using this action as a way to explore TreeSitter
12018 // nodes. Parent and grandparent are also logged because this operation will not
12019 // visit nodes that have the same range as their parent.
12020 log::info!("Node: {node:?}");
12021 let parent = node.parent();
12022 log::info!("Parent: {parent:?}");
12023 let grandparent = parent.and_then(|x| x.parent());
12024 log::info!("Grandparent: {grandparent:?}");
12025 }
12026
12027 selected_larger_node |= new_range != old_range;
12028 Selection {
12029 id: selection.id,
12030 start: new_range.start,
12031 end: new_range.end,
12032 goal: SelectionGoal::None,
12033 reversed: selection.reversed,
12034 }
12035 })
12036 .collect::<Vec<_>>();
12037
12038 if !selected_larger_node {
12039 return; // don't put this call in the history
12040 }
12041
12042 // scroll based on transformation done to the last selection created by the user
12043 let (last_old, last_new) = old_selections
12044 .last()
12045 .zip(new_selections.last().cloned())
12046 .expect("old_selections isn't empty");
12047
12048 // revert selection
12049 let is_selection_reversed = {
12050 let should_newest_selection_be_reversed = last_old.start != last_new.start;
12051 new_selections.last_mut().expect("checked above").reversed =
12052 should_newest_selection_be_reversed;
12053 should_newest_selection_be_reversed
12054 };
12055
12056 if selected_larger_node {
12057 self.select_syntax_node_history.disable_clearing = true;
12058 self.change_selections(None, window, cx, |s| {
12059 s.select(new_selections.clone());
12060 });
12061 self.select_syntax_node_history.disable_clearing = false;
12062 }
12063
12064 let start_row = last_new.start.to_display_point(&display_map).row().0;
12065 let end_row = last_new.end.to_display_point(&display_map).row().0;
12066 let selection_height = end_row - start_row + 1;
12067 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
12068
12069 // if fits on screen (considering margin), keep it in the middle, else, scroll to selection head
12070 let scroll_behavior = if visible_row_count >= selection_height + scroll_margin_rows * 2 {
12071 let middle_row = (end_row + start_row) / 2;
12072 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12073 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12074 SelectSyntaxNodeScrollBehavior::CenterSelection
12075 } else if is_selection_reversed {
12076 self.scroll_cursor_top(&Default::default(), window, cx);
12077 SelectSyntaxNodeScrollBehavior::CursorTop
12078 } else {
12079 self.scroll_cursor_bottom(&Default::default(), window, cx);
12080 SelectSyntaxNodeScrollBehavior::CursorBottom
12081 };
12082
12083 self.select_syntax_node_history.push((
12084 old_selections,
12085 scroll_behavior,
12086 is_selection_reversed,
12087 ));
12088 }
12089
12090 pub fn select_smaller_syntax_node(
12091 &mut self,
12092 _: &SelectSmallerSyntaxNode,
12093 window: &mut Window,
12094 cx: &mut Context<Self>,
12095 ) {
12096 let Some(visible_row_count) = self.visible_row_count() else {
12097 return;
12098 };
12099
12100 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
12101 self.select_syntax_node_history.pop()
12102 {
12103 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12104
12105 if let Some(selection) = selections.last_mut() {
12106 selection.reversed = is_selection_reversed;
12107 }
12108
12109 self.select_syntax_node_history.disable_clearing = true;
12110 self.change_selections(None, window, cx, |s| {
12111 s.select(selections.to_vec());
12112 });
12113 self.select_syntax_node_history.disable_clearing = false;
12114
12115 let newest = self.selections.newest::<usize>(cx);
12116 let start_row = newest.start.to_display_point(&display_map).row().0;
12117 let end_row = newest.end.to_display_point(&display_map).row().0;
12118
12119 match scroll_behavior {
12120 SelectSyntaxNodeScrollBehavior::CursorTop => {
12121 self.scroll_cursor_top(&Default::default(), window, cx);
12122 }
12123 SelectSyntaxNodeScrollBehavior::CenterSelection => {
12124 let middle_row = (end_row + start_row) / 2;
12125 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12126 // centralize the selection, not the cursor
12127 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12128 }
12129 SelectSyntaxNodeScrollBehavior::CursorBottom => {
12130 self.scroll_cursor_bottom(&Default::default(), window, cx);
12131 }
12132 }
12133 }
12134 }
12135
12136 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
12137 if !EditorSettings::get_global(cx).gutter.runnables {
12138 self.clear_tasks();
12139 return Task::ready(());
12140 }
12141 let project = self.project.as_ref().map(Entity::downgrade);
12142 cx.spawn_in(window, async move |this, cx| {
12143 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
12144 let Some(project) = project.and_then(|p| p.upgrade()) else {
12145 return;
12146 };
12147 let Ok(display_snapshot) = this.update(cx, |this, cx| {
12148 this.display_map.update(cx, |map, cx| map.snapshot(cx))
12149 }) else {
12150 return;
12151 };
12152
12153 let hide_runnables = project
12154 .update(cx, |project, cx| {
12155 // Do not display any test indicators in non-dev server remote projects.
12156 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
12157 })
12158 .unwrap_or(true);
12159 if hide_runnables {
12160 return;
12161 }
12162 let new_rows =
12163 cx.background_spawn({
12164 let snapshot = display_snapshot.clone();
12165 async move {
12166 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
12167 }
12168 })
12169 .await;
12170
12171 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
12172 this.update(cx, |this, _| {
12173 this.clear_tasks();
12174 for (key, value) in rows {
12175 this.insert_tasks(key, value);
12176 }
12177 })
12178 .ok();
12179 })
12180 }
12181 fn fetch_runnable_ranges(
12182 snapshot: &DisplaySnapshot,
12183 range: Range<Anchor>,
12184 ) -> Vec<language::RunnableRange> {
12185 snapshot.buffer_snapshot.runnable_ranges(range).collect()
12186 }
12187
12188 fn runnable_rows(
12189 project: Entity<Project>,
12190 snapshot: DisplaySnapshot,
12191 runnable_ranges: Vec<RunnableRange>,
12192 mut cx: AsyncWindowContext,
12193 ) -> Vec<((BufferId, u32), RunnableTasks)> {
12194 runnable_ranges
12195 .into_iter()
12196 .filter_map(|mut runnable| {
12197 let tasks = cx
12198 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
12199 .ok()?;
12200 if tasks.is_empty() {
12201 return None;
12202 }
12203
12204 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
12205
12206 let row = snapshot
12207 .buffer_snapshot
12208 .buffer_line_for_row(MultiBufferRow(point.row))?
12209 .1
12210 .start
12211 .row;
12212
12213 let context_range =
12214 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
12215 Some((
12216 (runnable.buffer_id, row),
12217 RunnableTasks {
12218 templates: tasks,
12219 offset: snapshot
12220 .buffer_snapshot
12221 .anchor_before(runnable.run_range.start),
12222 context_range,
12223 column: point.column,
12224 extra_variables: runnable.extra_captures,
12225 },
12226 ))
12227 })
12228 .collect()
12229 }
12230
12231 fn templates_with_tags(
12232 project: &Entity<Project>,
12233 runnable: &mut Runnable,
12234 cx: &mut App,
12235 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
12236 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
12237 let (worktree_id, file) = project
12238 .buffer_for_id(runnable.buffer, cx)
12239 .and_then(|buffer| buffer.read(cx).file())
12240 .map(|file| (file.worktree_id(cx), file.clone()))
12241 .unzip();
12242
12243 (
12244 project.task_store().read(cx).task_inventory().cloned(),
12245 worktree_id,
12246 file,
12247 )
12248 });
12249
12250 let tags = mem::take(&mut runnable.tags);
12251 let mut tags: Vec<_> = tags
12252 .into_iter()
12253 .flat_map(|tag| {
12254 let tag = tag.0.clone();
12255 inventory
12256 .as_ref()
12257 .into_iter()
12258 .flat_map(|inventory| {
12259 inventory.read(cx).list_tasks(
12260 file.clone(),
12261 Some(runnable.language.clone()),
12262 worktree_id,
12263 cx,
12264 )
12265 })
12266 .filter(move |(_, template)| {
12267 template.tags.iter().any(|source_tag| source_tag == &tag)
12268 })
12269 })
12270 .sorted_by_key(|(kind, _)| kind.to_owned())
12271 .collect();
12272 if let Some((leading_tag_source, _)) = tags.first() {
12273 // Strongest source wins; if we have worktree tag binding, prefer that to
12274 // global and language bindings;
12275 // if we have a global binding, prefer that to language binding.
12276 let first_mismatch = tags
12277 .iter()
12278 .position(|(tag_source, _)| tag_source != leading_tag_source);
12279 if let Some(index) = first_mismatch {
12280 tags.truncate(index);
12281 }
12282 }
12283
12284 tags
12285 }
12286
12287 pub fn move_to_enclosing_bracket(
12288 &mut self,
12289 _: &MoveToEnclosingBracket,
12290 window: &mut Window,
12291 cx: &mut Context<Self>,
12292 ) {
12293 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12294 s.move_offsets_with(|snapshot, selection| {
12295 let Some(enclosing_bracket_ranges) =
12296 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12297 else {
12298 return;
12299 };
12300
12301 let mut best_length = usize::MAX;
12302 let mut best_inside = false;
12303 let mut best_in_bracket_range = false;
12304 let mut best_destination = None;
12305 for (open, close) in enclosing_bracket_ranges {
12306 let close = close.to_inclusive();
12307 let length = close.end() - open.start;
12308 let inside = selection.start >= open.end && selection.end <= *close.start();
12309 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12310 || close.contains(&selection.head());
12311
12312 // If best is next to a bracket and current isn't, skip
12313 if !in_bracket_range && best_in_bracket_range {
12314 continue;
12315 }
12316
12317 // Prefer smaller lengths unless best is inside and current isn't
12318 if length > best_length && (best_inside || !inside) {
12319 continue;
12320 }
12321
12322 best_length = length;
12323 best_inside = inside;
12324 best_in_bracket_range = in_bracket_range;
12325 best_destination = Some(
12326 if close.contains(&selection.start) && close.contains(&selection.end) {
12327 if inside {
12328 open.end
12329 } else {
12330 open.start
12331 }
12332 } else if inside {
12333 *close.start()
12334 } else {
12335 *close.end()
12336 },
12337 );
12338 }
12339
12340 if let Some(destination) = best_destination {
12341 selection.collapse_to(destination, SelectionGoal::None);
12342 }
12343 })
12344 });
12345 }
12346
12347 pub fn undo_selection(
12348 &mut self,
12349 _: &UndoSelection,
12350 window: &mut Window,
12351 cx: &mut Context<Self>,
12352 ) {
12353 self.end_selection(window, cx);
12354 self.selection_history.mode = SelectionHistoryMode::Undoing;
12355 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12356 self.change_selections(None, window, cx, |s| {
12357 s.select_anchors(entry.selections.to_vec())
12358 });
12359 self.select_next_state = entry.select_next_state;
12360 self.select_prev_state = entry.select_prev_state;
12361 self.add_selections_state = entry.add_selections_state;
12362 self.request_autoscroll(Autoscroll::newest(), cx);
12363 }
12364 self.selection_history.mode = SelectionHistoryMode::Normal;
12365 }
12366
12367 pub fn redo_selection(
12368 &mut self,
12369 _: &RedoSelection,
12370 window: &mut Window,
12371 cx: &mut Context<Self>,
12372 ) {
12373 self.end_selection(window, cx);
12374 self.selection_history.mode = SelectionHistoryMode::Redoing;
12375 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12376 self.change_selections(None, window, cx, |s| {
12377 s.select_anchors(entry.selections.to_vec())
12378 });
12379 self.select_next_state = entry.select_next_state;
12380 self.select_prev_state = entry.select_prev_state;
12381 self.add_selections_state = entry.add_selections_state;
12382 self.request_autoscroll(Autoscroll::newest(), cx);
12383 }
12384 self.selection_history.mode = SelectionHistoryMode::Normal;
12385 }
12386
12387 pub fn expand_excerpts(
12388 &mut self,
12389 action: &ExpandExcerpts,
12390 _: &mut Window,
12391 cx: &mut Context<Self>,
12392 ) {
12393 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12394 }
12395
12396 pub fn expand_excerpts_down(
12397 &mut self,
12398 action: &ExpandExcerptsDown,
12399 _: &mut Window,
12400 cx: &mut Context<Self>,
12401 ) {
12402 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12403 }
12404
12405 pub fn expand_excerpts_up(
12406 &mut self,
12407 action: &ExpandExcerptsUp,
12408 _: &mut Window,
12409 cx: &mut Context<Self>,
12410 ) {
12411 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12412 }
12413
12414 pub fn expand_excerpts_for_direction(
12415 &mut self,
12416 lines: u32,
12417 direction: ExpandExcerptDirection,
12418
12419 cx: &mut Context<Self>,
12420 ) {
12421 let selections = self.selections.disjoint_anchors();
12422
12423 let lines = if lines == 0 {
12424 EditorSettings::get_global(cx).expand_excerpt_lines
12425 } else {
12426 lines
12427 };
12428
12429 self.buffer.update(cx, |buffer, cx| {
12430 let snapshot = buffer.snapshot(cx);
12431 let mut excerpt_ids = selections
12432 .iter()
12433 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12434 .collect::<Vec<_>>();
12435 excerpt_ids.sort();
12436 excerpt_ids.dedup();
12437 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12438 })
12439 }
12440
12441 pub fn expand_excerpt(
12442 &mut self,
12443 excerpt: ExcerptId,
12444 direction: ExpandExcerptDirection,
12445 window: &mut Window,
12446 cx: &mut Context<Self>,
12447 ) {
12448 let current_scroll_position = self.scroll_position(cx);
12449 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12450 self.buffer.update(cx, |buffer, cx| {
12451 buffer.expand_excerpts([excerpt], lines, direction, cx)
12452 });
12453 if direction == ExpandExcerptDirection::Down {
12454 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12455 self.set_scroll_position(new_scroll_position, window, cx);
12456 }
12457 }
12458
12459 pub fn go_to_singleton_buffer_point(
12460 &mut self,
12461 point: Point,
12462 window: &mut Window,
12463 cx: &mut Context<Self>,
12464 ) {
12465 self.go_to_singleton_buffer_range(point..point, window, cx);
12466 }
12467
12468 pub fn go_to_singleton_buffer_range(
12469 &mut self,
12470 range: Range<Point>,
12471 window: &mut Window,
12472 cx: &mut Context<Self>,
12473 ) {
12474 let multibuffer = self.buffer().read(cx);
12475 let Some(buffer) = multibuffer.as_singleton() else {
12476 return;
12477 };
12478 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12479 return;
12480 };
12481 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12482 return;
12483 };
12484 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12485 s.select_anchor_ranges([start..end])
12486 });
12487 }
12488
12489 fn go_to_diagnostic(
12490 &mut self,
12491 _: &GoToDiagnostic,
12492 window: &mut Window,
12493 cx: &mut Context<Self>,
12494 ) {
12495 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12496 }
12497
12498 fn go_to_prev_diagnostic(
12499 &mut self,
12500 _: &GoToPreviousDiagnostic,
12501 window: &mut Window,
12502 cx: &mut Context<Self>,
12503 ) {
12504 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12505 }
12506
12507 pub fn go_to_diagnostic_impl(
12508 &mut self,
12509 direction: Direction,
12510 window: &mut Window,
12511 cx: &mut Context<Self>,
12512 ) {
12513 let buffer = self.buffer.read(cx).snapshot(cx);
12514 let selection = self.selections.newest::<usize>(cx);
12515
12516 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12517 if direction == Direction::Next {
12518 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12519 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12520 return;
12521 };
12522 self.activate_diagnostics(
12523 buffer_id,
12524 popover.local_diagnostic.diagnostic.group_id,
12525 window,
12526 cx,
12527 );
12528 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12529 let primary_range_start = active_diagnostics.primary_range.start;
12530 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12531 let mut new_selection = s.newest_anchor().clone();
12532 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12533 s.select_anchors(vec![new_selection.clone()]);
12534 });
12535 self.refresh_inline_completion(false, true, window, cx);
12536 }
12537 return;
12538 }
12539 }
12540
12541 let active_group_id = self
12542 .active_diagnostics
12543 .as_ref()
12544 .map(|active_group| active_group.group_id);
12545 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12546 active_diagnostics
12547 .primary_range
12548 .to_offset(&buffer)
12549 .to_inclusive()
12550 });
12551 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12552 if active_primary_range.contains(&selection.head()) {
12553 *active_primary_range.start()
12554 } else {
12555 selection.head()
12556 }
12557 } else {
12558 selection.head()
12559 };
12560
12561 let snapshot = self.snapshot(window, cx);
12562 let primary_diagnostics_before = buffer
12563 .diagnostics_in_range::<usize>(0..search_start)
12564 .filter(|entry| entry.diagnostic.is_primary)
12565 .filter(|entry| entry.range.start != entry.range.end)
12566 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12567 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12568 .collect::<Vec<_>>();
12569 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12570 primary_diagnostics_before
12571 .iter()
12572 .position(|entry| entry.diagnostic.group_id == active_group_id)
12573 });
12574
12575 let primary_diagnostics_after = buffer
12576 .diagnostics_in_range::<usize>(search_start..buffer.len())
12577 .filter(|entry| entry.diagnostic.is_primary)
12578 .filter(|entry| entry.range.start != entry.range.end)
12579 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12580 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12581 .collect::<Vec<_>>();
12582 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12583 primary_diagnostics_after
12584 .iter()
12585 .enumerate()
12586 .rev()
12587 .find_map(|(i, entry)| {
12588 if entry.diagnostic.group_id == active_group_id {
12589 Some(i)
12590 } else {
12591 None
12592 }
12593 })
12594 });
12595
12596 let next_primary_diagnostic = match direction {
12597 Direction::Prev => primary_diagnostics_before
12598 .iter()
12599 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12600 .rev()
12601 .next(),
12602 Direction::Next => primary_diagnostics_after
12603 .iter()
12604 .skip(
12605 last_same_group_diagnostic_after
12606 .map(|index| index + 1)
12607 .unwrap_or(0),
12608 )
12609 .next(),
12610 };
12611
12612 // Cycle around to the start of the buffer, potentially moving back to the start of
12613 // the currently active diagnostic.
12614 let cycle_around = || match direction {
12615 Direction::Prev => primary_diagnostics_after
12616 .iter()
12617 .rev()
12618 .chain(primary_diagnostics_before.iter().rev())
12619 .next(),
12620 Direction::Next => primary_diagnostics_before
12621 .iter()
12622 .chain(primary_diagnostics_after.iter())
12623 .next(),
12624 };
12625
12626 if let Some((primary_range, group_id)) = next_primary_diagnostic
12627 .or_else(cycle_around)
12628 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12629 {
12630 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12631 return;
12632 };
12633 self.activate_diagnostics(buffer_id, group_id, window, cx);
12634 if self.active_diagnostics.is_some() {
12635 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12636 s.select(vec![Selection {
12637 id: selection.id,
12638 start: primary_range.start,
12639 end: primary_range.start,
12640 reversed: false,
12641 goal: SelectionGoal::None,
12642 }]);
12643 });
12644 self.refresh_inline_completion(false, true, window, cx);
12645 }
12646 }
12647 }
12648
12649 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12650 let snapshot = self.snapshot(window, cx);
12651 let selection = self.selections.newest::<Point>(cx);
12652 self.go_to_hunk_before_or_after_position(
12653 &snapshot,
12654 selection.head(),
12655 Direction::Next,
12656 window,
12657 cx,
12658 );
12659 }
12660
12661 fn go_to_hunk_before_or_after_position(
12662 &mut self,
12663 snapshot: &EditorSnapshot,
12664 position: Point,
12665 direction: Direction,
12666 window: &mut Window,
12667 cx: &mut Context<Editor>,
12668 ) {
12669 let row = if direction == Direction::Next {
12670 self.hunk_after_position(snapshot, position)
12671 .map(|hunk| hunk.row_range.start)
12672 } else {
12673 self.hunk_before_position(snapshot, position)
12674 };
12675
12676 if let Some(row) = row {
12677 let destination = Point::new(row.0, 0);
12678 let autoscroll = Autoscroll::center();
12679
12680 self.unfold_ranges(&[destination..destination], false, false, cx);
12681 self.change_selections(Some(autoscroll), window, cx, |s| {
12682 s.select_ranges([destination..destination]);
12683 });
12684 }
12685 }
12686
12687 fn hunk_after_position(
12688 &mut self,
12689 snapshot: &EditorSnapshot,
12690 position: Point,
12691 ) -> Option<MultiBufferDiffHunk> {
12692 snapshot
12693 .buffer_snapshot
12694 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12695 .find(|hunk| hunk.row_range.start.0 > position.row)
12696 .or_else(|| {
12697 snapshot
12698 .buffer_snapshot
12699 .diff_hunks_in_range(Point::zero()..position)
12700 .find(|hunk| hunk.row_range.end.0 < position.row)
12701 })
12702 }
12703
12704 fn go_to_prev_hunk(
12705 &mut self,
12706 _: &GoToPreviousHunk,
12707 window: &mut Window,
12708 cx: &mut Context<Self>,
12709 ) {
12710 let snapshot = self.snapshot(window, cx);
12711 let selection = self.selections.newest::<Point>(cx);
12712 self.go_to_hunk_before_or_after_position(
12713 &snapshot,
12714 selection.head(),
12715 Direction::Prev,
12716 window,
12717 cx,
12718 );
12719 }
12720
12721 fn hunk_before_position(
12722 &mut self,
12723 snapshot: &EditorSnapshot,
12724 position: Point,
12725 ) -> Option<MultiBufferRow> {
12726 snapshot
12727 .buffer_snapshot
12728 .diff_hunk_before(position)
12729 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12730 }
12731
12732 fn go_to_line<T: 'static>(
12733 &mut self,
12734 position: Anchor,
12735 highlight_color: Option<Hsla>,
12736 window: &mut Window,
12737 cx: &mut Context<Self>,
12738 ) {
12739 let snapshot = self.snapshot(window, cx).display_snapshot;
12740 let position = position.to_point(&snapshot.buffer_snapshot);
12741 let start = snapshot
12742 .buffer_snapshot
12743 .clip_point(Point::new(position.row, 0), Bias::Left);
12744 let end = start + Point::new(1, 0);
12745 let start = snapshot.buffer_snapshot.anchor_before(start);
12746 let end = snapshot.buffer_snapshot.anchor_before(end);
12747
12748 self.highlight_rows::<T>(
12749 start..end,
12750 highlight_color
12751 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12752 false,
12753 cx,
12754 );
12755 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
12756 }
12757
12758 pub fn go_to_definition(
12759 &mut self,
12760 _: &GoToDefinition,
12761 window: &mut Window,
12762 cx: &mut Context<Self>,
12763 ) -> Task<Result<Navigated>> {
12764 let definition =
12765 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12766 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
12767 cx.spawn_in(window, async move |editor, cx| {
12768 if definition.await? == Navigated::Yes {
12769 return Ok(Navigated::Yes);
12770 }
12771 match fallback_strategy {
12772 GoToDefinitionFallback::None => Ok(Navigated::No),
12773 GoToDefinitionFallback::FindAllReferences => {
12774 match editor.update_in(cx, |editor, window, cx| {
12775 editor.find_all_references(&FindAllReferences, window, cx)
12776 })? {
12777 Some(references) => references.await,
12778 None => Ok(Navigated::No),
12779 }
12780 }
12781 }
12782 })
12783 }
12784
12785 pub fn go_to_declaration(
12786 &mut self,
12787 _: &GoToDeclaration,
12788 window: &mut Window,
12789 cx: &mut Context<Self>,
12790 ) -> Task<Result<Navigated>> {
12791 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12792 }
12793
12794 pub fn go_to_declaration_split(
12795 &mut self,
12796 _: &GoToDeclaration,
12797 window: &mut Window,
12798 cx: &mut Context<Self>,
12799 ) -> Task<Result<Navigated>> {
12800 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12801 }
12802
12803 pub fn go_to_implementation(
12804 &mut self,
12805 _: &GoToImplementation,
12806 window: &mut Window,
12807 cx: &mut Context<Self>,
12808 ) -> Task<Result<Navigated>> {
12809 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12810 }
12811
12812 pub fn go_to_implementation_split(
12813 &mut self,
12814 _: &GoToImplementationSplit,
12815 window: &mut Window,
12816 cx: &mut Context<Self>,
12817 ) -> Task<Result<Navigated>> {
12818 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12819 }
12820
12821 pub fn go_to_type_definition(
12822 &mut self,
12823 _: &GoToTypeDefinition,
12824 window: &mut Window,
12825 cx: &mut Context<Self>,
12826 ) -> Task<Result<Navigated>> {
12827 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12828 }
12829
12830 pub fn go_to_definition_split(
12831 &mut self,
12832 _: &GoToDefinitionSplit,
12833 window: &mut Window,
12834 cx: &mut Context<Self>,
12835 ) -> Task<Result<Navigated>> {
12836 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12837 }
12838
12839 pub fn go_to_type_definition_split(
12840 &mut self,
12841 _: &GoToTypeDefinitionSplit,
12842 window: &mut Window,
12843 cx: &mut Context<Self>,
12844 ) -> Task<Result<Navigated>> {
12845 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12846 }
12847
12848 fn go_to_definition_of_kind(
12849 &mut self,
12850 kind: GotoDefinitionKind,
12851 split: bool,
12852 window: &mut Window,
12853 cx: &mut Context<Self>,
12854 ) -> Task<Result<Navigated>> {
12855 let Some(provider) = self.semantics_provider.clone() else {
12856 return Task::ready(Ok(Navigated::No));
12857 };
12858 let head = self.selections.newest::<usize>(cx).head();
12859 let buffer = self.buffer.read(cx);
12860 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12861 text_anchor
12862 } else {
12863 return Task::ready(Ok(Navigated::No));
12864 };
12865
12866 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12867 return Task::ready(Ok(Navigated::No));
12868 };
12869
12870 cx.spawn_in(window, async move |editor, cx| {
12871 let definitions = definitions.await?;
12872 let navigated = editor
12873 .update_in(cx, |editor, window, cx| {
12874 editor.navigate_to_hover_links(
12875 Some(kind),
12876 definitions
12877 .into_iter()
12878 .filter(|location| {
12879 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12880 })
12881 .map(HoverLink::Text)
12882 .collect::<Vec<_>>(),
12883 split,
12884 window,
12885 cx,
12886 )
12887 })?
12888 .await?;
12889 anyhow::Ok(navigated)
12890 })
12891 }
12892
12893 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12894 let selection = self.selections.newest_anchor();
12895 let head = selection.head();
12896 let tail = selection.tail();
12897
12898 let Some((buffer, start_position)) =
12899 self.buffer.read(cx).text_anchor_for_position(head, cx)
12900 else {
12901 return;
12902 };
12903
12904 let end_position = if head != tail {
12905 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12906 return;
12907 };
12908 Some(pos)
12909 } else {
12910 None
12911 };
12912
12913 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12914 let url = if let Some(end_pos) = end_position {
12915 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12916 } else {
12917 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12918 };
12919
12920 if let Some(url) = url {
12921 editor.update(cx, |_, cx| {
12922 cx.open_url(&url);
12923 })
12924 } else {
12925 Ok(())
12926 }
12927 });
12928
12929 url_finder.detach();
12930 }
12931
12932 pub fn open_selected_filename(
12933 &mut self,
12934 _: &OpenSelectedFilename,
12935 window: &mut Window,
12936 cx: &mut Context<Self>,
12937 ) {
12938 let Some(workspace) = self.workspace() else {
12939 return;
12940 };
12941
12942 let position = self.selections.newest_anchor().head();
12943
12944 let Some((buffer, buffer_position)) =
12945 self.buffer.read(cx).text_anchor_for_position(position, cx)
12946 else {
12947 return;
12948 };
12949
12950 let project = self.project.clone();
12951
12952 cx.spawn_in(window, async move |_, cx| {
12953 let result = find_file(&buffer, project, buffer_position, cx).await;
12954
12955 if let Some((_, path)) = result {
12956 workspace
12957 .update_in(cx, |workspace, window, cx| {
12958 workspace.open_resolved_path(path, window, cx)
12959 })?
12960 .await?;
12961 }
12962 anyhow::Ok(())
12963 })
12964 .detach();
12965 }
12966
12967 pub(crate) fn navigate_to_hover_links(
12968 &mut self,
12969 kind: Option<GotoDefinitionKind>,
12970 mut definitions: Vec<HoverLink>,
12971 split: bool,
12972 window: &mut Window,
12973 cx: &mut Context<Editor>,
12974 ) -> Task<Result<Navigated>> {
12975 // If there is one definition, just open it directly
12976 if definitions.len() == 1 {
12977 let definition = definitions.pop().unwrap();
12978
12979 enum TargetTaskResult {
12980 Location(Option<Location>),
12981 AlreadyNavigated,
12982 }
12983
12984 let target_task = match definition {
12985 HoverLink::Text(link) => {
12986 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12987 }
12988 HoverLink::InlayHint(lsp_location, server_id) => {
12989 let computation =
12990 self.compute_target_location(lsp_location, server_id, window, cx);
12991 cx.background_spawn(async move {
12992 let location = computation.await?;
12993 Ok(TargetTaskResult::Location(location))
12994 })
12995 }
12996 HoverLink::Url(url) => {
12997 cx.open_url(&url);
12998 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12999 }
13000 HoverLink::File(path) => {
13001 if let Some(workspace) = self.workspace() {
13002 cx.spawn_in(window, async move |_, cx| {
13003 workspace
13004 .update_in(cx, |workspace, window, cx| {
13005 workspace.open_resolved_path(path, window, cx)
13006 })?
13007 .await
13008 .map(|_| TargetTaskResult::AlreadyNavigated)
13009 })
13010 } else {
13011 Task::ready(Ok(TargetTaskResult::Location(None)))
13012 }
13013 }
13014 };
13015 cx.spawn_in(window, async move |editor, cx| {
13016 let target = match target_task.await.context("target resolution task")? {
13017 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
13018 TargetTaskResult::Location(None) => return Ok(Navigated::No),
13019 TargetTaskResult::Location(Some(target)) => target,
13020 };
13021
13022 editor.update_in(cx, |editor, window, cx| {
13023 let Some(workspace) = editor.workspace() else {
13024 return Navigated::No;
13025 };
13026 let pane = workspace.read(cx).active_pane().clone();
13027
13028 let range = target.range.to_point(target.buffer.read(cx));
13029 let range = editor.range_for_match(&range);
13030 let range = collapse_multiline_range(range);
13031
13032 if !split
13033 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
13034 {
13035 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
13036 } else {
13037 window.defer(cx, move |window, cx| {
13038 let target_editor: Entity<Self> =
13039 workspace.update(cx, |workspace, cx| {
13040 let pane = if split {
13041 workspace.adjacent_pane(window, cx)
13042 } else {
13043 workspace.active_pane().clone()
13044 };
13045
13046 workspace.open_project_item(
13047 pane,
13048 target.buffer.clone(),
13049 true,
13050 true,
13051 window,
13052 cx,
13053 )
13054 });
13055 target_editor.update(cx, |target_editor, cx| {
13056 // When selecting a definition in a different buffer, disable the nav history
13057 // to avoid creating a history entry at the previous cursor location.
13058 pane.update(cx, |pane, _| pane.disable_history());
13059 target_editor.go_to_singleton_buffer_range(range, window, cx);
13060 pane.update(cx, |pane, _| pane.enable_history());
13061 });
13062 });
13063 }
13064 Navigated::Yes
13065 })
13066 })
13067 } else if !definitions.is_empty() {
13068 cx.spawn_in(window, async move |editor, cx| {
13069 let (title, location_tasks, workspace) = editor
13070 .update_in(cx, |editor, window, cx| {
13071 let tab_kind = match kind {
13072 Some(GotoDefinitionKind::Implementation) => "Implementations",
13073 _ => "Definitions",
13074 };
13075 let title = definitions
13076 .iter()
13077 .find_map(|definition| match definition {
13078 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
13079 let buffer = origin.buffer.read(cx);
13080 format!(
13081 "{} for {}",
13082 tab_kind,
13083 buffer
13084 .text_for_range(origin.range.clone())
13085 .collect::<String>()
13086 )
13087 }),
13088 HoverLink::InlayHint(_, _) => None,
13089 HoverLink::Url(_) => None,
13090 HoverLink::File(_) => None,
13091 })
13092 .unwrap_or(tab_kind.to_string());
13093 let location_tasks = definitions
13094 .into_iter()
13095 .map(|definition| match definition {
13096 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
13097 HoverLink::InlayHint(lsp_location, server_id) => editor
13098 .compute_target_location(lsp_location, server_id, window, cx),
13099 HoverLink::Url(_) => Task::ready(Ok(None)),
13100 HoverLink::File(_) => Task::ready(Ok(None)),
13101 })
13102 .collect::<Vec<_>>();
13103 (title, location_tasks, editor.workspace().clone())
13104 })
13105 .context("location tasks preparation")?;
13106
13107 let locations = future::join_all(location_tasks)
13108 .await
13109 .into_iter()
13110 .filter_map(|location| location.transpose())
13111 .collect::<Result<_>>()
13112 .context("location tasks")?;
13113
13114 let Some(workspace) = workspace else {
13115 return Ok(Navigated::No);
13116 };
13117 let opened = workspace
13118 .update_in(cx, |workspace, window, cx| {
13119 Self::open_locations_in_multibuffer(
13120 workspace,
13121 locations,
13122 title,
13123 split,
13124 MultibufferSelectionMode::First,
13125 window,
13126 cx,
13127 )
13128 })
13129 .ok();
13130
13131 anyhow::Ok(Navigated::from_bool(opened.is_some()))
13132 })
13133 } else {
13134 Task::ready(Ok(Navigated::No))
13135 }
13136 }
13137
13138 fn compute_target_location(
13139 &self,
13140 lsp_location: lsp::Location,
13141 server_id: LanguageServerId,
13142 window: &mut Window,
13143 cx: &mut Context<Self>,
13144 ) -> Task<anyhow::Result<Option<Location>>> {
13145 let Some(project) = self.project.clone() else {
13146 return Task::ready(Ok(None));
13147 };
13148
13149 cx.spawn_in(window, async move |editor, cx| {
13150 let location_task = editor.update(cx, |_, cx| {
13151 project.update(cx, |project, cx| {
13152 let language_server_name = project
13153 .language_server_statuses(cx)
13154 .find(|(id, _)| server_id == *id)
13155 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
13156 language_server_name.map(|language_server_name| {
13157 project.open_local_buffer_via_lsp(
13158 lsp_location.uri.clone(),
13159 server_id,
13160 language_server_name,
13161 cx,
13162 )
13163 })
13164 })
13165 })?;
13166 let location = match location_task {
13167 Some(task) => Some({
13168 let target_buffer_handle = task.await.context("open local buffer")?;
13169 let range = target_buffer_handle.update(cx, |target_buffer, _| {
13170 let target_start = target_buffer
13171 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
13172 let target_end = target_buffer
13173 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
13174 target_buffer.anchor_after(target_start)
13175 ..target_buffer.anchor_before(target_end)
13176 })?;
13177 Location {
13178 buffer: target_buffer_handle,
13179 range,
13180 }
13181 }),
13182 None => None,
13183 };
13184 Ok(location)
13185 })
13186 }
13187
13188 pub fn find_all_references(
13189 &mut self,
13190 _: &FindAllReferences,
13191 window: &mut Window,
13192 cx: &mut Context<Self>,
13193 ) -> Option<Task<Result<Navigated>>> {
13194 let selection = self.selections.newest::<usize>(cx);
13195 let multi_buffer = self.buffer.read(cx);
13196 let head = selection.head();
13197
13198 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13199 let head_anchor = multi_buffer_snapshot.anchor_at(
13200 head,
13201 if head < selection.tail() {
13202 Bias::Right
13203 } else {
13204 Bias::Left
13205 },
13206 );
13207
13208 match self
13209 .find_all_references_task_sources
13210 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13211 {
13212 Ok(_) => {
13213 log::info!(
13214 "Ignoring repeated FindAllReferences invocation with the position of already running task"
13215 );
13216 return None;
13217 }
13218 Err(i) => {
13219 self.find_all_references_task_sources.insert(i, head_anchor);
13220 }
13221 }
13222
13223 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
13224 let workspace = self.workspace()?;
13225 let project = workspace.read(cx).project().clone();
13226 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
13227 Some(cx.spawn_in(window, async move |editor, cx| {
13228 let _cleanup = cx.on_drop(&editor, move |editor, _| {
13229 if let Ok(i) = editor
13230 .find_all_references_task_sources
13231 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13232 {
13233 editor.find_all_references_task_sources.remove(i);
13234 }
13235 });
13236
13237 let locations = references.await?;
13238 if locations.is_empty() {
13239 return anyhow::Ok(Navigated::No);
13240 }
13241
13242 workspace.update_in(cx, |workspace, window, cx| {
13243 let title = locations
13244 .first()
13245 .as_ref()
13246 .map(|location| {
13247 let buffer = location.buffer.read(cx);
13248 format!(
13249 "References to `{}`",
13250 buffer
13251 .text_for_range(location.range.clone())
13252 .collect::<String>()
13253 )
13254 })
13255 .unwrap();
13256 Self::open_locations_in_multibuffer(
13257 workspace,
13258 locations,
13259 title,
13260 false,
13261 MultibufferSelectionMode::First,
13262 window,
13263 cx,
13264 );
13265 Navigated::Yes
13266 })
13267 }))
13268 }
13269
13270 /// Opens a multibuffer with the given project locations in it
13271 pub fn open_locations_in_multibuffer(
13272 workspace: &mut Workspace,
13273 mut locations: Vec<Location>,
13274 title: String,
13275 split: bool,
13276 multibuffer_selection_mode: MultibufferSelectionMode,
13277 window: &mut Window,
13278 cx: &mut Context<Workspace>,
13279 ) {
13280 // If there are multiple definitions, open them in a multibuffer
13281 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13282 let mut locations = locations.into_iter().peekable();
13283 let mut ranges = Vec::new();
13284 let capability = workspace.project().read(cx).capability();
13285
13286 let excerpt_buffer = cx.new(|cx| {
13287 let mut multibuffer = MultiBuffer::new(capability);
13288 while let Some(location) = locations.next() {
13289 let buffer = location.buffer.read(cx);
13290 let mut ranges_for_buffer = Vec::new();
13291 let range = location.range.to_offset(buffer);
13292 ranges_for_buffer.push(range.clone());
13293
13294 while let Some(next_location) = locations.peek() {
13295 if next_location.buffer == location.buffer {
13296 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13297 locations.next();
13298 } else {
13299 break;
13300 }
13301 }
13302
13303 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13304 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13305 location.buffer.clone(),
13306 ranges_for_buffer,
13307 DEFAULT_MULTIBUFFER_CONTEXT,
13308 cx,
13309 ))
13310 }
13311
13312 multibuffer.with_title(title)
13313 });
13314
13315 let editor = cx.new(|cx| {
13316 Editor::for_multibuffer(
13317 excerpt_buffer,
13318 Some(workspace.project().clone()),
13319 window,
13320 cx,
13321 )
13322 });
13323 editor.update(cx, |editor, cx| {
13324 match multibuffer_selection_mode {
13325 MultibufferSelectionMode::First => {
13326 if let Some(first_range) = ranges.first() {
13327 editor.change_selections(None, window, cx, |selections| {
13328 selections.clear_disjoint();
13329 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13330 });
13331 }
13332 editor.highlight_background::<Self>(
13333 &ranges,
13334 |theme| theme.editor_highlighted_line_background,
13335 cx,
13336 );
13337 }
13338 MultibufferSelectionMode::All => {
13339 editor.change_selections(None, window, cx, |selections| {
13340 selections.clear_disjoint();
13341 selections.select_anchor_ranges(ranges);
13342 });
13343 }
13344 }
13345 editor.register_buffers_with_language_servers(cx);
13346 });
13347
13348 let item = Box::new(editor);
13349 let item_id = item.item_id();
13350
13351 if split {
13352 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13353 } else {
13354 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13355 let (preview_item_id, preview_item_idx) =
13356 workspace.active_pane().update(cx, |pane, _| {
13357 (pane.preview_item_id(), pane.preview_item_idx())
13358 });
13359
13360 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13361
13362 if let Some(preview_item_id) = preview_item_id {
13363 workspace.active_pane().update(cx, |pane, cx| {
13364 pane.remove_item(preview_item_id, false, false, window, cx);
13365 });
13366 }
13367 } else {
13368 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13369 }
13370 }
13371 workspace.active_pane().update(cx, |pane, cx| {
13372 pane.set_preview_item_id(Some(item_id), cx);
13373 });
13374 }
13375
13376 pub fn rename(
13377 &mut self,
13378 _: &Rename,
13379 window: &mut Window,
13380 cx: &mut Context<Self>,
13381 ) -> Option<Task<Result<()>>> {
13382 use language::ToOffset as _;
13383
13384 let provider = self.semantics_provider.clone()?;
13385 let selection = self.selections.newest_anchor().clone();
13386 let (cursor_buffer, cursor_buffer_position) = self
13387 .buffer
13388 .read(cx)
13389 .text_anchor_for_position(selection.head(), cx)?;
13390 let (tail_buffer, cursor_buffer_position_end) = self
13391 .buffer
13392 .read(cx)
13393 .text_anchor_for_position(selection.tail(), cx)?;
13394 if tail_buffer != cursor_buffer {
13395 return None;
13396 }
13397
13398 let snapshot = cursor_buffer.read(cx).snapshot();
13399 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13400 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13401 let prepare_rename = provider
13402 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13403 .unwrap_or_else(|| Task::ready(Ok(None)));
13404 drop(snapshot);
13405
13406 Some(cx.spawn_in(window, async move |this, cx| {
13407 let rename_range = if let Some(range) = prepare_rename.await? {
13408 Some(range)
13409 } else {
13410 this.update(cx, |this, cx| {
13411 let buffer = this.buffer.read(cx).snapshot(cx);
13412 let mut buffer_highlights = this
13413 .document_highlights_for_position(selection.head(), &buffer)
13414 .filter(|highlight| {
13415 highlight.start.excerpt_id == selection.head().excerpt_id
13416 && highlight.end.excerpt_id == selection.head().excerpt_id
13417 });
13418 buffer_highlights
13419 .next()
13420 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13421 })?
13422 };
13423 if let Some(rename_range) = rename_range {
13424 this.update_in(cx, |this, window, cx| {
13425 let snapshot = cursor_buffer.read(cx).snapshot();
13426 let rename_buffer_range = rename_range.to_offset(&snapshot);
13427 let cursor_offset_in_rename_range =
13428 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13429 let cursor_offset_in_rename_range_end =
13430 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13431
13432 this.take_rename(false, window, cx);
13433 let buffer = this.buffer.read(cx).read(cx);
13434 let cursor_offset = selection.head().to_offset(&buffer);
13435 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13436 let rename_end = rename_start + rename_buffer_range.len();
13437 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13438 let mut old_highlight_id = None;
13439 let old_name: Arc<str> = buffer
13440 .chunks(rename_start..rename_end, true)
13441 .map(|chunk| {
13442 if old_highlight_id.is_none() {
13443 old_highlight_id = chunk.syntax_highlight_id;
13444 }
13445 chunk.text
13446 })
13447 .collect::<String>()
13448 .into();
13449
13450 drop(buffer);
13451
13452 // Position the selection in the rename editor so that it matches the current selection.
13453 this.show_local_selections = false;
13454 let rename_editor = cx.new(|cx| {
13455 let mut editor = Editor::single_line(window, cx);
13456 editor.buffer.update(cx, |buffer, cx| {
13457 buffer.edit([(0..0, old_name.clone())], None, cx)
13458 });
13459 let rename_selection_range = match cursor_offset_in_rename_range
13460 .cmp(&cursor_offset_in_rename_range_end)
13461 {
13462 Ordering::Equal => {
13463 editor.select_all(&SelectAll, window, cx);
13464 return editor;
13465 }
13466 Ordering::Less => {
13467 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13468 }
13469 Ordering::Greater => {
13470 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13471 }
13472 };
13473 if rename_selection_range.end > old_name.len() {
13474 editor.select_all(&SelectAll, window, cx);
13475 } else {
13476 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13477 s.select_ranges([rename_selection_range]);
13478 });
13479 }
13480 editor
13481 });
13482 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13483 if e == &EditorEvent::Focused {
13484 cx.emit(EditorEvent::FocusedIn)
13485 }
13486 })
13487 .detach();
13488
13489 let write_highlights =
13490 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13491 let read_highlights =
13492 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13493 let ranges = write_highlights
13494 .iter()
13495 .flat_map(|(_, ranges)| ranges.iter())
13496 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13497 .cloned()
13498 .collect();
13499
13500 this.highlight_text::<Rename>(
13501 ranges,
13502 HighlightStyle {
13503 fade_out: Some(0.6),
13504 ..Default::default()
13505 },
13506 cx,
13507 );
13508 let rename_focus_handle = rename_editor.focus_handle(cx);
13509 window.focus(&rename_focus_handle);
13510 let block_id = this.insert_blocks(
13511 [BlockProperties {
13512 style: BlockStyle::Flex,
13513 placement: BlockPlacement::Below(range.start),
13514 height: 1,
13515 render: Arc::new({
13516 let rename_editor = rename_editor.clone();
13517 move |cx: &mut BlockContext| {
13518 let mut text_style = cx.editor_style.text.clone();
13519 if let Some(highlight_style) = old_highlight_id
13520 .and_then(|h| h.style(&cx.editor_style.syntax))
13521 {
13522 text_style = text_style.highlight(highlight_style);
13523 }
13524 div()
13525 .block_mouse_down()
13526 .pl(cx.anchor_x)
13527 .child(EditorElement::new(
13528 &rename_editor,
13529 EditorStyle {
13530 background: cx.theme().system().transparent,
13531 local_player: cx.editor_style.local_player,
13532 text: text_style,
13533 scrollbar_width: cx.editor_style.scrollbar_width,
13534 syntax: cx.editor_style.syntax.clone(),
13535 status: cx.editor_style.status.clone(),
13536 inlay_hints_style: HighlightStyle {
13537 font_weight: Some(FontWeight::BOLD),
13538 ..make_inlay_hints_style(cx.app)
13539 },
13540 inline_completion_styles: make_suggestion_styles(
13541 cx.app,
13542 ),
13543 ..EditorStyle::default()
13544 },
13545 ))
13546 .into_any_element()
13547 }
13548 }),
13549 priority: 0,
13550 }],
13551 Some(Autoscroll::fit()),
13552 cx,
13553 )[0];
13554 this.pending_rename = Some(RenameState {
13555 range,
13556 old_name,
13557 editor: rename_editor,
13558 block_id,
13559 });
13560 })?;
13561 }
13562
13563 Ok(())
13564 }))
13565 }
13566
13567 pub fn confirm_rename(
13568 &mut self,
13569 _: &ConfirmRename,
13570 window: &mut Window,
13571 cx: &mut Context<Self>,
13572 ) -> Option<Task<Result<()>>> {
13573 let rename = self.take_rename(false, window, cx)?;
13574 let workspace = self.workspace()?.downgrade();
13575 let (buffer, start) = self
13576 .buffer
13577 .read(cx)
13578 .text_anchor_for_position(rename.range.start, cx)?;
13579 let (end_buffer, _) = self
13580 .buffer
13581 .read(cx)
13582 .text_anchor_for_position(rename.range.end, cx)?;
13583 if buffer != end_buffer {
13584 return None;
13585 }
13586
13587 let old_name = rename.old_name;
13588 let new_name = rename.editor.read(cx).text(cx);
13589
13590 let rename = self.semantics_provider.as_ref()?.perform_rename(
13591 &buffer,
13592 start,
13593 new_name.clone(),
13594 cx,
13595 )?;
13596
13597 Some(cx.spawn_in(window, async move |editor, cx| {
13598 let project_transaction = rename.await?;
13599 Self::open_project_transaction(
13600 &editor,
13601 workspace,
13602 project_transaction,
13603 format!("Rename: {} → {}", old_name, new_name),
13604 cx,
13605 )
13606 .await?;
13607
13608 editor.update(cx, |editor, cx| {
13609 editor.refresh_document_highlights(cx);
13610 })?;
13611 Ok(())
13612 }))
13613 }
13614
13615 fn take_rename(
13616 &mut self,
13617 moving_cursor: bool,
13618 window: &mut Window,
13619 cx: &mut Context<Self>,
13620 ) -> Option<RenameState> {
13621 let rename = self.pending_rename.take()?;
13622 if rename.editor.focus_handle(cx).is_focused(window) {
13623 window.focus(&self.focus_handle);
13624 }
13625
13626 self.remove_blocks(
13627 [rename.block_id].into_iter().collect(),
13628 Some(Autoscroll::fit()),
13629 cx,
13630 );
13631 self.clear_highlights::<Rename>(cx);
13632 self.show_local_selections = true;
13633
13634 if moving_cursor {
13635 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13636 editor.selections.newest::<usize>(cx).head()
13637 });
13638
13639 // Update the selection to match the position of the selection inside
13640 // the rename editor.
13641 let snapshot = self.buffer.read(cx).read(cx);
13642 let rename_range = rename.range.to_offset(&snapshot);
13643 let cursor_in_editor = snapshot
13644 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13645 .min(rename_range.end);
13646 drop(snapshot);
13647
13648 self.change_selections(None, window, cx, |s| {
13649 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13650 });
13651 } else {
13652 self.refresh_document_highlights(cx);
13653 }
13654
13655 Some(rename)
13656 }
13657
13658 pub fn pending_rename(&self) -> Option<&RenameState> {
13659 self.pending_rename.as_ref()
13660 }
13661
13662 fn format(
13663 &mut self,
13664 _: &Format,
13665 window: &mut Window,
13666 cx: &mut Context<Self>,
13667 ) -> Option<Task<Result<()>>> {
13668 let project = match &self.project {
13669 Some(project) => project.clone(),
13670 None => return None,
13671 };
13672
13673 Some(self.perform_format(
13674 project,
13675 FormatTrigger::Manual,
13676 FormatTarget::Buffers,
13677 window,
13678 cx,
13679 ))
13680 }
13681
13682 fn format_selections(
13683 &mut self,
13684 _: &FormatSelections,
13685 window: &mut Window,
13686 cx: &mut Context<Self>,
13687 ) -> Option<Task<Result<()>>> {
13688 let project = match &self.project {
13689 Some(project) => project.clone(),
13690 None => return None,
13691 };
13692
13693 let ranges = self
13694 .selections
13695 .all_adjusted(cx)
13696 .into_iter()
13697 .map(|selection| selection.range())
13698 .collect_vec();
13699
13700 Some(self.perform_format(
13701 project,
13702 FormatTrigger::Manual,
13703 FormatTarget::Ranges(ranges),
13704 window,
13705 cx,
13706 ))
13707 }
13708
13709 fn perform_format(
13710 &mut self,
13711 project: Entity<Project>,
13712 trigger: FormatTrigger,
13713 target: FormatTarget,
13714 window: &mut Window,
13715 cx: &mut Context<Self>,
13716 ) -> Task<Result<()>> {
13717 let buffer = self.buffer.clone();
13718 let (buffers, target) = match target {
13719 FormatTarget::Buffers => {
13720 let mut buffers = buffer.read(cx).all_buffers();
13721 if trigger == FormatTrigger::Save {
13722 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13723 }
13724 (buffers, LspFormatTarget::Buffers)
13725 }
13726 FormatTarget::Ranges(selection_ranges) => {
13727 let multi_buffer = buffer.read(cx);
13728 let snapshot = multi_buffer.read(cx);
13729 let mut buffers = HashSet::default();
13730 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13731 BTreeMap::new();
13732 for selection_range in selection_ranges {
13733 for (buffer, buffer_range, _) in
13734 snapshot.range_to_buffer_ranges(selection_range)
13735 {
13736 let buffer_id = buffer.remote_id();
13737 let start = buffer.anchor_before(buffer_range.start);
13738 let end = buffer.anchor_after(buffer_range.end);
13739 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13740 buffer_id_to_ranges
13741 .entry(buffer_id)
13742 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13743 .or_insert_with(|| vec![start..end]);
13744 }
13745 }
13746 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13747 }
13748 };
13749
13750 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13751 let format = project.update(cx, |project, cx| {
13752 project.format(buffers, target, true, trigger, cx)
13753 });
13754
13755 cx.spawn_in(window, async move |_, cx| {
13756 let transaction = futures::select_biased! {
13757 transaction = format.log_err().fuse() => transaction,
13758 () = timeout => {
13759 log::warn!("timed out waiting for formatting");
13760 None
13761 }
13762 };
13763
13764 buffer
13765 .update(cx, |buffer, cx| {
13766 if let Some(transaction) = transaction {
13767 if !buffer.is_singleton() {
13768 buffer.push_transaction(&transaction.0, cx);
13769 }
13770 }
13771 cx.notify();
13772 })
13773 .ok();
13774
13775 Ok(())
13776 })
13777 }
13778
13779 fn organize_imports(
13780 &mut self,
13781 _: &OrganizeImports,
13782 window: &mut Window,
13783 cx: &mut Context<Self>,
13784 ) -> Option<Task<Result<()>>> {
13785 let project = match &self.project {
13786 Some(project) => project.clone(),
13787 None => return None,
13788 };
13789 Some(self.perform_code_action_kind(
13790 project,
13791 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13792 window,
13793 cx,
13794 ))
13795 }
13796
13797 fn perform_code_action_kind(
13798 &mut self,
13799 project: Entity<Project>,
13800 kind: CodeActionKind,
13801 window: &mut Window,
13802 cx: &mut Context<Self>,
13803 ) -> Task<Result<()>> {
13804 let buffer = self.buffer.clone();
13805 let buffers = buffer.read(cx).all_buffers();
13806 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13807 let apply_action = project.update(cx, |project, cx| {
13808 project.apply_code_action_kind(buffers, kind, true, cx)
13809 });
13810 cx.spawn_in(window, async move |_, cx| {
13811 let transaction = futures::select_biased! {
13812 () = timeout => {
13813 log::warn!("timed out waiting for executing code action");
13814 None
13815 }
13816 transaction = apply_action.log_err().fuse() => transaction,
13817 };
13818 buffer
13819 .update(cx, |buffer, cx| {
13820 // check if we need this
13821 if let Some(transaction) = transaction {
13822 if !buffer.is_singleton() {
13823 buffer.push_transaction(&transaction.0, cx);
13824 }
13825 }
13826 cx.notify();
13827 })
13828 .ok();
13829 Ok(())
13830 })
13831 }
13832
13833 fn restart_language_server(
13834 &mut self,
13835 _: &RestartLanguageServer,
13836 _: &mut Window,
13837 cx: &mut Context<Self>,
13838 ) {
13839 if let Some(project) = self.project.clone() {
13840 self.buffer.update(cx, |multi_buffer, cx| {
13841 project.update(cx, |project, cx| {
13842 project.restart_language_servers_for_buffers(
13843 multi_buffer.all_buffers().into_iter().collect(),
13844 cx,
13845 );
13846 });
13847 })
13848 }
13849 }
13850
13851 fn cancel_language_server_work(
13852 workspace: &mut Workspace,
13853 _: &actions::CancelLanguageServerWork,
13854 _: &mut Window,
13855 cx: &mut Context<Workspace>,
13856 ) {
13857 let project = workspace.project();
13858 let buffers = workspace
13859 .active_item(cx)
13860 .and_then(|item| item.act_as::<Editor>(cx))
13861 .map_or(HashSet::default(), |editor| {
13862 editor.read(cx).buffer.read(cx).all_buffers()
13863 });
13864 project.update(cx, |project, cx| {
13865 project.cancel_language_server_work_for_buffers(buffers, cx);
13866 });
13867 }
13868
13869 fn show_character_palette(
13870 &mut self,
13871 _: &ShowCharacterPalette,
13872 window: &mut Window,
13873 _: &mut Context<Self>,
13874 ) {
13875 window.show_character_palette();
13876 }
13877
13878 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13879 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13880 let buffer = self.buffer.read(cx).snapshot(cx);
13881 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13882 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13883 let is_valid = buffer
13884 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13885 .any(|entry| {
13886 entry.diagnostic.is_primary
13887 && !entry.range.is_empty()
13888 && entry.range.start == primary_range_start
13889 && entry.diagnostic.message == active_diagnostics.primary_message
13890 });
13891
13892 if is_valid != active_diagnostics.is_valid {
13893 active_diagnostics.is_valid = is_valid;
13894 if is_valid {
13895 let mut new_styles = HashMap::default();
13896 for (block_id, diagnostic) in &active_diagnostics.blocks {
13897 new_styles.insert(
13898 *block_id,
13899 diagnostic_block_renderer(diagnostic.clone(), None, true),
13900 );
13901 }
13902 self.display_map.update(cx, |display_map, _cx| {
13903 display_map.replace_blocks(new_styles);
13904 });
13905 } else {
13906 self.dismiss_diagnostics(cx);
13907 }
13908 }
13909 }
13910 }
13911
13912 fn activate_diagnostics(
13913 &mut self,
13914 buffer_id: BufferId,
13915 group_id: usize,
13916 window: &mut Window,
13917 cx: &mut Context<Self>,
13918 ) {
13919 self.dismiss_diagnostics(cx);
13920 let snapshot = self.snapshot(window, cx);
13921 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13922 let buffer = self.buffer.read(cx).snapshot(cx);
13923
13924 let mut primary_range = None;
13925 let mut primary_message = None;
13926 let diagnostic_group = buffer
13927 .diagnostic_group(buffer_id, group_id)
13928 .filter_map(|entry| {
13929 let start = entry.range.start;
13930 let end = entry.range.end;
13931 if snapshot.is_line_folded(MultiBufferRow(start.row))
13932 && (start.row == end.row
13933 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13934 {
13935 return None;
13936 }
13937 if entry.diagnostic.is_primary {
13938 primary_range = Some(entry.range.clone());
13939 primary_message = Some(entry.diagnostic.message.clone());
13940 }
13941 Some(entry)
13942 })
13943 .collect::<Vec<_>>();
13944 let primary_range = primary_range?;
13945 let primary_message = primary_message?;
13946
13947 let blocks = display_map
13948 .insert_blocks(
13949 diagnostic_group.iter().map(|entry| {
13950 let diagnostic = entry.diagnostic.clone();
13951 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13952 BlockProperties {
13953 style: BlockStyle::Fixed,
13954 placement: BlockPlacement::Below(
13955 buffer.anchor_after(entry.range.start),
13956 ),
13957 height: message_height,
13958 render: diagnostic_block_renderer(diagnostic, None, true),
13959 priority: 0,
13960 }
13961 }),
13962 cx,
13963 )
13964 .into_iter()
13965 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13966 .collect();
13967
13968 Some(ActiveDiagnosticGroup {
13969 primary_range: buffer.anchor_before(primary_range.start)
13970 ..buffer.anchor_after(primary_range.end),
13971 primary_message,
13972 group_id,
13973 blocks,
13974 is_valid: true,
13975 })
13976 });
13977 }
13978
13979 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13980 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13981 self.display_map.update(cx, |display_map, cx| {
13982 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13983 });
13984 cx.notify();
13985 }
13986 }
13987
13988 /// Disable inline diagnostics rendering for this editor.
13989 pub fn disable_inline_diagnostics(&mut self) {
13990 self.inline_diagnostics_enabled = false;
13991 self.inline_diagnostics_update = Task::ready(());
13992 self.inline_diagnostics.clear();
13993 }
13994
13995 pub fn inline_diagnostics_enabled(&self) -> bool {
13996 self.inline_diagnostics_enabled
13997 }
13998
13999 pub fn show_inline_diagnostics(&self) -> bool {
14000 self.show_inline_diagnostics
14001 }
14002
14003 pub fn toggle_inline_diagnostics(
14004 &mut self,
14005 _: &ToggleInlineDiagnostics,
14006 window: &mut Window,
14007 cx: &mut Context<'_, Editor>,
14008 ) {
14009 self.show_inline_diagnostics = !self.show_inline_diagnostics;
14010 self.refresh_inline_diagnostics(false, window, cx);
14011 }
14012
14013 fn refresh_inline_diagnostics(
14014 &mut self,
14015 debounce: bool,
14016 window: &mut Window,
14017 cx: &mut Context<Self>,
14018 ) {
14019 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
14020 self.inline_diagnostics_update = Task::ready(());
14021 self.inline_diagnostics.clear();
14022 return;
14023 }
14024
14025 let debounce_ms = ProjectSettings::get_global(cx)
14026 .diagnostics
14027 .inline
14028 .update_debounce_ms;
14029 let debounce = if debounce && debounce_ms > 0 {
14030 Some(Duration::from_millis(debounce_ms))
14031 } else {
14032 None
14033 };
14034 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
14035 if let Some(debounce) = debounce {
14036 cx.background_executor().timer(debounce).await;
14037 }
14038 let Some(snapshot) = editor
14039 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
14040 .ok()
14041 else {
14042 return;
14043 };
14044
14045 let new_inline_diagnostics = cx
14046 .background_spawn(async move {
14047 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
14048 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
14049 let message = diagnostic_entry
14050 .diagnostic
14051 .message
14052 .split_once('\n')
14053 .map(|(line, _)| line)
14054 .map(SharedString::new)
14055 .unwrap_or_else(|| {
14056 SharedString::from(diagnostic_entry.diagnostic.message)
14057 });
14058 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
14059 let (Ok(i) | Err(i)) = inline_diagnostics
14060 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
14061 inline_diagnostics.insert(
14062 i,
14063 (
14064 start_anchor,
14065 InlineDiagnostic {
14066 message,
14067 group_id: diagnostic_entry.diagnostic.group_id,
14068 start: diagnostic_entry.range.start.to_point(&snapshot),
14069 is_primary: diagnostic_entry.diagnostic.is_primary,
14070 severity: diagnostic_entry.diagnostic.severity,
14071 },
14072 ),
14073 );
14074 }
14075 inline_diagnostics
14076 })
14077 .await;
14078
14079 editor
14080 .update(cx, |editor, cx| {
14081 editor.inline_diagnostics = new_inline_diagnostics;
14082 cx.notify();
14083 })
14084 .ok();
14085 });
14086 }
14087
14088 pub fn set_selections_from_remote(
14089 &mut self,
14090 selections: Vec<Selection<Anchor>>,
14091 pending_selection: Option<Selection<Anchor>>,
14092 window: &mut Window,
14093 cx: &mut Context<Self>,
14094 ) {
14095 let old_cursor_position = self.selections.newest_anchor().head();
14096 self.selections.change_with(cx, |s| {
14097 s.select_anchors(selections);
14098 if let Some(pending_selection) = pending_selection {
14099 s.set_pending(pending_selection, SelectMode::Character);
14100 } else {
14101 s.clear_pending();
14102 }
14103 });
14104 self.selections_did_change(false, &old_cursor_position, true, window, cx);
14105 }
14106
14107 fn push_to_selection_history(&mut self) {
14108 self.selection_history.push(SelectionHistoryEntry {
14109 selections: self.selections.disjoint_anchors(),
14110 select_next_state: self.select_next_state.clone(),
14111 select_prev_state: self.select_prev_state.clone(),
14112 add_selections_state: self.add_selections_state.clone(),
14113 });
14114 }
14115
14116 pub fn transact(
14117 &mut self,
14118 window: &mut Window,
14119 cx: &mut Context<Self>,
14120 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
14121 ) -> Option<TransactionId> {
14122 self.start_transaction_at(Instant::now(), window, cx);
14123 update(self, window, cx);
14124 self.end_transaction_at(Instant::now(), cx)
14125 }
14126
14127 pub fn start_transaction_at(
14128 &mut self,
14129 now: Instant,
14130 window: &mut Window,
14131 cx: &mut Context<Self>,
14132 ) {
14133 self.end_selection(window, cx);
14134 if let Some(tx_id) = self
14135 .buffer
14136 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
14137 {
14138 self.selection_history
14139 .insert_transaction(tx_id, self.selections.disjoint_anchors());
14140 cx.emit(EditorEvent::TransactionBegun {
14141 transaction_id: tx_id,
14142 })
14143 }
14144 }
14145
14146 pub fn end_transaction_at(
14147 &mut self,
14148 now: Instant,
14149 cx: &mut Context<Self>,
14150 ) -> Option<TransactionId> {
14151 if let Some(transaction_id) = self
14152 .buffer
14153 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
14154 {
14155 if let Some((_, end_selections)) =
14156 self.selection_history.transaction_mut(transaction_id)
14157 {
14158 *end_selections = Some(self.selections.disjoint_anchors());
14159 } else {
14160 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
14161 }
14162
14163 cx.emit(EditorEvent::Edited { transaction_id });
14164 Some(transaction_id)
14165 } else {
14166 None
14167 }
14168 }
14169
14170 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
14171 if self.selection_mark_mode {
14172 self.change_selections(None, window, cx, |s| {
14173 s.move_with(|_, sel| {
14174 sel.collapse_to(sel.head(), SelectionGoal::None);
14175 });
14176 })
14177 }
14178 self.selection_mark_mode = true;
14179 cx.notify();
14180 }
14181
14182 pub fn swap_selection_ends(
14183 &mut self,
14184 _: &actions::SwapSelectionEnds,
14185 window: &mut Window,
14186 cx: &mut Context<Self>,
14187 ) {
14188 self.change_selections(None, window, cx, |s| {
14189 s.move_with(|_, sel| {
14190 if sel.start != sel.end {
14191 sel.reversed = !sel.reversed
14192 }
14193 });
14194 });
14195 self.request_autoscroll(Autoscroll::newest(), cx);
14196 cx.notify();
14197 }
14198
14199 pub fn toggle_fold(
14200 &mut self,
14201 _: &actions::ToggleFold,
14202 window: &mut Window,
14203 cx: &mut Context<Self>,
14204 ) {
14205 if self.is_singleton(cx) {
14206 let selection = self.selections.newest::<Point>(cx);
14207
14208 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14209 let range = if selection.is_empty() {
14210 let point = selection.head().to_display_point(&display_map);
14211 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14212 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14213 .to_point(&display_map);
14214 start..end
14215 } else {
14216 selection.range()
14217 };
14218 if display_map.folds_in_range(range).next().is_some() {
14219 self.unfold_lines(&Default::default(), window, cx)
14220 } else {
14221 self.fold(&Default::default(), window, cx)
14222 }
14223 } else {
14224 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14225 let buffer_ids: HashSet<_> = self
14226 .selections
14227 .disjoint_anchor_ranges()
14228 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14229 .collect();
14230
14231 let should_unfold = buffer_ids
14232 .iter()
14233 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
14234
14235 for buffer_id in buffer_ids {
14236 if should_unfold {
14237 self.unfold_buffer(buffer_id, cx);
14238 } else {
14239 self.fold_buffer(buffer_id, cx);
14240 }
14241 }
14242 }
14243 }
14244
14245 pub fn toggle_fold_recursive(
14246 &mut self,
14247 _: &actions::ToggleFoldRecursive,
14248 window: &mut Window,
14249 cx: &mut Context<Self>,
14250 ) {
14251 let selection = self.selections.newest::<Point>(cx);
14252
14253 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14254 let range = if selection.is_empty() {
14255 let point = selection.head().to_display_point(&display_map);
14256 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14257 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14258 .to_point(&display_map);
14259 start..end
14260 } else {
14261 selection.range()
14262 };
14263 if display_map.folds_in_range(range).next().is_some() {
14264 self.unfold_recursive(&Default::default(), window, cx)
14265 } else {
14266 self.fold_recursive(&Default::default(), window, cx)
14267 }
14268 }
14269
14270 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14271 if self.is_singleton(cx) {
14272 let mut to_fold = Vec::new();
14273 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14274 let selections = self.selections.all_adjusted(cx);
14275
14276 for selection in selections {
14277 let range = selection.range().sorted();
14278 let buffer_start_row = range.start.row;
14279
14280 if range.start.row != range.end.row {
14281 let mut found = false;
14282 let mut row = range.start.row;
14283 while row <= range.end.row {
14284 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14285 {
14286 found = true;
14287 row = crease.range().end.row + 1;
14288 to_fold.push(crease);
14289 } else {
14290 row += 1
14291 }
14292 }
14293 if found {
14294 continue;
14295 }
14296 }
14297
14298 for row in (0..=range.start.row).rev() {
14299 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14300 if crease.range().end.row >= buffer_start_row {
14301 to_fold.push(crease);
14302 if row <= range.start.row {
14303 break;
14304 }
14305 }
14306 }
14307 }
14308 }
14309
14310 self.fold_creases(to_fold, true, window, cx);
14311 } else {
14312 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14313 let buffer_ids = self
14314 .selections
14315 .disjoint_anchor_ranges()
14316 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14317 .collect::<HashSet<_>>();
14318 for buffer_id in buffer_ids {
14319 self.fold_buffer(buffer_id, cx);
14320 }
14321 }
14322 }
14323
14324 fn fold_at_level(
14325 &mut self,
14326 fold_at: &FoldAtLevel,
14327 window: &mut Window,
14328 cx: &mut Context<Self>,
14329 ) {
14330 if !self.buffer.read(cx).is_singleton() {
14331 return;
14332 }
14333
14334 let fold_at_level = fold_at.0;
14335 let snapshot = self.buffer.read(cx).snapshot(cx);
14336 let mut to_fold = Vec::new();
14337 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14338
14339 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14340 while start_row < end_row {
14341 match self
14342 .snapshot(window, cx)
14343 .crease_for_buffer_row(MultiBufferRow(start_row))
14344 {
14345 Some(crease) => {
14346 let nested_start_row = crease.range().start.row + 1;
14347 let nested_end_row = crease.range().end.row;
14348
14349 if current_level < fold_at_level {
14350 stack.push((nested_start_row, nested_end_row, current_level + 1));
14351 } else if current_level == fold_at_level {
14352 to_fold.push(crease);
14353 }
14354
14355 start_row = nested_end_row + 1;
14356 }
14357 None => start_row += 1,
14358 }
14359 }
14360 }
14361
14362 self.fold_creases(to_fold, true, window, cx);
14363 }
14364
14365 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14366 if self.buffer.read(cx).is_singleton() {
14367 let mut fold_ranges = Vec::new();
14368 let snapshot = self.buffer.read(cx).snapshot(cx);
14369
14370 for row in 0..snapshot.max_row().0 {
14371 if let Some(foldable_range) = self
14372 .snapshot(window, cx)
14373 .crease_for_buffer_row(MultiBufferRow(row))
14374 {
14375 fold_ranges.push(foldable_range);
14376 }
14377 }
14378
14379 self.fold_creases(fold_ranges, true, window, cx);
14380 } else {
14381 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14382 editor
14383 .update_in(cx, |editor, _, cx| {
14384 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14385 editor.fold_buffer(buffer_id, cx);
14386 }
14387 })
14388 .ok();
14389 });
14390 }
14391 }
14392
14393 pub fn fold_function_bodies(
14394 &mut self,
14395 _: &actions::FoldFunctionBodies,
14396 window: &mut Window,
14397 cx: &mut Context<Self>,
14398 ) {
14399 let snapshot = self.buffer.read(cx).snapshot(cx);
14400
14401 let ranges = snapshot
14402 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14403 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14404 .collect::<Vec<_>>();
14405
14406 let creases = ranges
14407 .into_iter()
14408 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14409 .collect();
14410
14411 self.fold_creases(creases, true, window, cx);
14412 }
14413
14414 pub fn fold_recursive(
14415 &mut self,
14416 _: &actions::FoldRecursive,
14417 window: &mut Window,
14418 cx: &mut Context<Self>,
14419 ) {
14420 let mut to_fold = Vec::new();
14421 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14422 let selections = self.selections.all_adjusted(cx);
14423
14424 for selection in selections {
14425 let range = selection.range().sorted();
14426 let buffer_start_row = range.start.row;
14427
14428 if range.start.row != range.end.row {
14429 let mut found = false;
14430 for row in range.start.row..=range.end.row {
14431 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14432 found = true;
14433 to_fold.push(crease);
14434 }
14435 }
14436 if found {
14437 continue;
14438 }
14439 }
14440
14441 for row in (0..=range.start.row).rev() {
14442 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14443 if crease.range().end.row >= buffer_start_row {
14444 to_fold.push(crease);
14445 } else {
14446 break;
14447 }
14448 }
14449 }
14450 }
14451
14452 self.fold_creases(to_fold, true, window, cx);
14453 }
14454
14455 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14456 let buffer_row = fold_at.buffer_row;
14457 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14458
14459 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14460 let autoscroll = self
14461 .selections
14462 .all::<Point>(cx)
14463 .iter()
14464 .any(|selection| crease.range().overlaps(&selection.range()));
14465
14466 self.fold_creases(vec![crease], autoscroll, window, cx);
14467 }
14468 }
14469
14470 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14471 if self.is_singleton(cx) {
14472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14473 let buffer = &display_map.buffer_snapshot;
14474 let selections = self.selections.all::<Point>(cx);
14475 let ranges = selections
14476 .iter()
14477 .map(|s| {
14478 let range = s.display_range(&display_map).sorted();
14479 let mut start = range.start.to_point(&display_map);
14480 let mut end = range.end.to_point(&display_map);
14481 start.column = 0;
14482 end.column = buffer.line_len(MultiBufferRow(end.row));
14483 start..end
14484 })
14485 .collect::<Vec<_>>();
14486
14487 self.unfold_ranges(&ranges, true, true, cx);
14488 } else {
14489 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14490 let buffer_ids = self
14491 .selections
14492 .disjoint_anchor_ranges()
14493 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14494 .collect::<HashSet<_>>();
14495 for buffer_id in buffer_ids {
14496 self.unfold_buffer(buffer_id, cx);
14497 }
14498 }
14499 }
14500
14501 pub fn unfold_recursive(
14502 &mut self,
14503 _: &UnfoldRecursive,
14504 _window: &mut Window,
14505 cx: &mut Context<Self>,
14506 ) {
14507 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14508 let selections = self.selections.all::<Point>(cx);
14509 let ranges = selections
14510 .iter()
14511 .map(|s| {
14512 let mut range = s.display_range(&display_map).sorted();
14513 *range.start.column_mut() = 0;
14514 *range.end.column_mut() = display_map.line_len(range.end.row());
14515 let start = range.start.to_point(&display_map);
14516 let end = range.end.to_point(&display_map);
14517 start..end
14518 })
14519 .collect::<Vec<_>>();
14520
14521 self.unfold_ranges(&ranges, true, true, cx);
14522 }
14523
14524 pub fn unfold_at(
14525 &mut self,
14526 unfold_at: &UnfoldAt,
14527 _window: &mut Window,
14528 cx: &mut Context<Self>,
14529 ) {
14530 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14531
14532 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14533 ..Point::new(
14534 unfold_at.buffer_row.0,
14535 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14536 );
14537
14538 let autoscroll = self
14539 .selections
14540 .all::<Point>(cx)
14541 .iter()
14542 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14543
14544 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14545 }
14546
14547 pub fn unfold_all(
14548 &mut self,
14549 _: &actions::UnfoldAll,
14550 _window: &mut Window,
14551 cx: &mut Context<Self>,
14552 ) {
14553 if self.buffer.read(cx).is_singleton() {
14554 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14555 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14556 } else {
14557 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14558 editor
14559 .update(cx, |editor, cx| {
14560 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14561 editor.unfold_buffer(buffer_id, cx);
14562 }
14563 })
14564 .ok();
14565 });
14566 }
14567 }
14568
14569 pub fn fold_selected_ranges(
14570 &mut self,
14571 _: &FoldSelectedRanges,
14572 window: &mut Window,
14573 cx: &mut Context<Self>,
14574 ) {
14575 let selections = self.selections.all::<Point>(cx);
14576 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14577 let line_mode = self.selections.line_mode;
14578 let ranges = selections
14579 .into_iter()
14580 .map(|s| {
14581 if line_mode {
14582 let start = Point::new(s.start.row, 0);
14583 let end = Point::new(
14584 s.end.row,
14585 display_map
14586 .buffer_snapshot
14587 .line_len(MultiBufferRow(s.end.row)),
14588 );
14589 Crease::simple(start..end, display_map.fold_placeholder.clone())
14590 } else {
14591 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14592 }
14593 })
14594 .collect::<Vec<_>>();
14595 self.fold_creases(ranges, true, window, cx);
14596 }
14597
14598 pub fn fold_ranges<T: ToOffset + Clone>(
14599 &mut self,
14600 ranges: Vec<Range<T>>,
14601 auto_scroll: bool,
14602 window: &mut Window,
14603 cx: &mut Context<Self>,
14604 ) {
14605 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14606 let ranges = ranges
14607 .into_iter()
14608 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14609 .collect::<Vec<_>>();
14610 self.fold_creases(ranges, auto_scroll, window, cx);
14611 }
14612
14613 pub fn fold_creases<T: ToOffset + Clone>(
14614 &mut self,
14615 creases: Vec<Crease<T>>,
14616 auto_scroll: bool,
14617 window: &mut Window,
14618 cx: &mut Context<Self>,
14619 ) {
14620 if creases.is_empty() {
14621 return;
14622 }
14623
14624 let mut buffers_affected = HashSet::default();
14625 let multi_buffer = self.buffer().read(cx);
14626 for crease in &creases {
14627 if let Some((_, buffer, _)) =
14628 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14629 {
14630 buffers_affected.insert(buffer.read(cx).remote_id());
14631 };
14632 }
14633
14634 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14635
14636 if auto_scroll {
14637 self.request_autoscroll(Autoscroll::fit(), cx);
14638 }
14639
14640 cx.notify();
14641
14642 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14643 // Clear diagnostics block when folding a range that contains it.
14644 let snapshot = self.snapshot(window, cx);
14645 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14646 drop(snapshot);
14647 self.active_diagnostics = Some(active_diagnostics);
14648 self.dismiss_diagnostics(cx);
14649 } else {
14650 self.active_diagnostics = Some(active_diagnostics);
14651 }
14652 }
14653
14654 self.scrollbar_marker_state.dirty = true;
14655 self.folds_did_change(cx);
14656 }
14657
14658 /// Removes any folds whose ranges intersect any of the given ranges.
14659 pub fn unfold_ranges<T: ToOffset + Clone>(
14660 &mut self,
14661 ranges: &[Range<T>],
14662 inclusive: bool,
14663 auto_scroll: bool,
14664 cx: &mut Context<Self>,
14665 ) {
14666 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14667 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14668 });
14669 self.folds_did_change(cx);
14670 }
14671
14672 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14673 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14674 return;
14675 }
14676 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14677 self.display_map.update(cx, |display_map, cx| {
14678 display_map.fold_buffers([buffer_id], cx)
14679 });
14680 cx.emit(EditorEvent::BufferFoldToggled {
14681 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14682 folded: true,
14683 });
14684 cx.notify();
14685 }
14686
14687 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14688 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14689 return;
14690 }
14691 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14692 self.display_map.update(cx, |display_map, cx| {
14693 display_map.unfold_buffers([buffer_id], cx);
14694 });
14695 cx.emit(EditorEvent::BufferFoldToggled {
14696 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14697 folded: false,
14698 });
14699 cx.notify();
14700 }
14701
14702 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14703 self.display_map.read(cx).is_buffer_folded(buffer)
14704 }
14705
14706 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14707 self.display_map.read(cx).folded_buffers()
14708 }
14709
14710 /// Removes any folds with the given ranges.
14711 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14712 &mut self,
14713 ranges: &[Range<T>],
14714 type_id: TypeId,
14715 auto_scroll: bool,
14716 cx: &mut Context<Self>,
14717 ) {
14718 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14719 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14720 });
14721 self.folds_did_change(cx);
14722 }
14723
14724 fn remove_folds_with<T: ToOffset + Clone>(
14725 &mut self,
14726 ranges: &[Range<T>],
14727 auto_scroll: bool,
14728 cx: &mut Context<Self>,
14729 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14730 ) {
14731 if ranges.is_empty() {
14732 return;
14733 }
14734
14735 let mut buffers_affected = HashSet::default();
14736 let multi_buffer = self.buffer().read(cx);
14737 for range in ranges {
14738 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14739 buffers_affected.insert(buffer.read(cx).remote_id());
14740 };
14741 }
14742
14743 self.display_map.update(cx, update);
14744
14745 if auto_scroll {
14746 self.request_autoscroll(Autoscroll::fit(), cx);
14747 }
14748
14749 cx.notify();
14750 self.scrollbar_marker_state.dirty = true;
14751 self.active_indent_guides_state.dirty = true;
14752 }
14753
14754 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14755 self.display_map.read(cx).fold_placeholder.clone()
14756 }
14757
14758 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14759 self.buffer.update(cx, |buffer, cx| {
14760 buffer.set_all_diff_hunks_expanded(cx);
14761 });
14762 }
14763
14764 pub fn expand_all_diff_hunks(
14765 &mut self,
14766 _: &ExpandAllDiffHunks,
14767 _window: &mut Window,
14768 cx: &mut Context<Self>,
14769 ) {
14770 self.buffer.update(cx, |buffer, cx| {
14771 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14772 });
14773 }
14774
14775 pub fn toggle_selected_diff_hunks(
14776 &mut self,
14777 _: &ToggleSelectedDiffHunks,
14778 _window: &mut Window,
14779 cx: &mut Context<Self>,
14780 ) {
14781 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14782 self.toggle_diff_hunks_in_ranges(ranges, cx);
14783 }
14784
14785 pub fn diff_hunks_in_ranges<'a>(
14786 &'a self,
14787 ranges: &'a [Range<Anchor>],
14788 buffer: &'a MultiBufferSnapshot,
14789 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14790 ranges.iter().flat_map(move |range| {
14791 let end_excerpt_id = range.end.excerpt_id;
14792 let range = range.to_point(buffer);
14793 let mut peek_end = range.end;
14794 if range.end.row < buffer.max_row().0 {
14795 peek_end = Point::new(range.end.row + 1, 0);
14796 }
14797 buffer
14798 .diff_hunks_in_range(range.start..peek_end)
14799 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14800 })
14801 }
14802
14803 pub fn has_stageable_diff_hunks_in_ranges(
14804 &self,
14805 ranges: &[Range<Anchor>],
14806 snapshot: &MultiBufferSnapshot,
14807 ) -> bool {
14808 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14809 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14810 }
14811
14812 pub fn toggle_staged_selected_diff_hunks(
14813 &mut self,
14814 _: &::git::ToggleStaged,
14815 _: &mut Window,
14816 cx: &mut Context<Self>,
14817 ) {
14818 let snapshot = self.buffer.read(cx).snapshot(cx);
14819 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14820 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14821 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14822 }
14823
14824 pub fn stage_and_next(
14825 &mut self,
14826 _: &::git::StageAndNext,
14827 window: &mut Window,
14828 cx: &mut Context<Self>,
14829 ) {
14830 self.do_stage_or_unstage_and_next(true, window, cx);
14831 }
14832
14833 pub fn unstage_and_next(
14834 &mut self,
14835 _: &::git::UnstageAndNext,
14836 window: &mut Window,
14837 cx: &mut Context<Self>,
14838 ) {
14839 self.do_stage_or_unstage_and_next(false, window, cx);
14840 }
14841
14842 pub fn stage_or_unstage_diff_hunks(
14843 &mut self,
14844 stage: bool,
14845 ranges: Vec<Range<Anchor>>,
14846 cx: &mut Context<Self>,
14847 ) {
14848 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14849 cx.spawn(async move |this, cx| {
14850 task.await?;
14851 this.update(cx, |this, cx| {
14852 let snapshot = this.buffer.read(cx).snapshot(cx);
14853 let chunk_by = this
14854 .diff_hunks_in_ranges(&ranges, &snapshot)
14855 .chunk_by(|hunk| hunk.buffer_id);
14856 for (buffer_id, hunks) in &chunk_by {
14857 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14858 }
14859 })
14860 })
14861 .detach_and_log_err(cx);
14862 }
14863
14864 fn save_buffers_for_ranges_if_needed(
14865 &mut self,
14866 ranges: &[Range<Anchor>],
14867 cx: &mut Context<'_, Editor>,
14868 ) -> Task<Result<()>> {
14869 let multibuffer = self.buffer.read(cx);
14870 let snapshot = multibuffer.read(cx);
14871 let buffer_ids: HashSet<_> = ranges
14872 .iter()
14873 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14874 .collect();
14875 drop(snapshot);
14876
14877 let mut buffers = HashSet::default();
14878 for buffer_id in buffer_ids {
14879 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14880 let buffer = buffer_entity.read(cx);
14881 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14882 {
14883 buffers.insert(buffer_entity);
14884 }
14885 }
14886 }
14887
14888 if let Some(project) = &self.project {
14889 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14890 } else {
14891 Task::ready(Ok(()))
14892 }
14893 }
14894
14895 fn do_stage_or_unstage_and_next(
14896 &mut self,
14897 stage: bool,
14898 window: &mut Window,
14899 cx: &mut Context<Self>,
14900 ) {
14901 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14902
14903 if ranges.iter().any(|range| range.start != range.end) {
14904 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14905 return;
14906 }
14907
14908 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14909 let snapshot = self.snapshot(window, cx);
14910 let position = self.selections.newest::<Point>(cx).head();
14911 let mut row = snapshot
14912 .buffer_snapshot
14913 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14914 .find(|hunk| hunk.row_range.start.0 > position.row)
14915 .map(|hunk| hunk.row_range.start);
14916
14917 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14918 // Outside of the project diff editor, wrap around to the beginning.
14919 if !all_diff_hunks_expanded {
14920 row = row.or_else(|| {
14921 snapshot
14922 .buffer_snapshot
14923 .diff_hunks_in_range(Point::zero()..position)
14924 .find(|hunk| hunk.row_range.end.0 < position.row)
14925 .map(|hunk| hunk.row_range.start)
14926 });
14927 }
14928
14929 if let Some(row) = row {
14930 let destination = Point::new(row.0, 0);
14931 let autoscroll = Autoscroll::center();
14932
14933 self.unfold_ranges(&[destination..destination], false, false, cx);
14934 self.change_selections(Some(autoscroll), window, cx, |s| {
14935 s.select_ranges([destination..destination]);
14936 });
14937 }
14938 }
14939
14940 fn do_stage_or_unstage(
14941 &self,
14942 stage: bool,
14943 buffer_id: BufferId,
14944 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14945 cx: &mut App,
14946 ) -> Option<()> {
14947 let project = self.project.as_ref()?;
14948 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14949 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14950 let buffer_snapshot = buffer.read(cx).snapshot();
14951 let file_exists = buffer_snapshot
14952 .file()
14953 .is_some_and(|file| file.disk_state().exists());
14954 diff.update(cx, |diff, cx| {
14955 diff.stage_or_unstage_hunks(
14956 stage,
14957 &hunks
14958 .map(|hunk| buffer_diff::DiffHunk {
14959 buffer_range: hunk.buffer_range,
14960 diff_base_byte_range: hunk.diff_base_byte_range,
14961 secondary_status: hunk.secondary_status,
14962 range: Point::zero()..Point::zero(), // unused
14963 })
14964 .collect::<Vec<_>>(),
14965 &buffer_snapshot,
14966 file_exists,
14967 cx,
14968 )
14969 });
14970 None
14971 }
14972
14973 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14974 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14975 self.buffer
14976 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14977 }
14978
14979 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14980 self.buffer.update(cx, |buffer, cx| {
14981 let ranges = vec![Anchor::min()..Anchor::max()];
14982 if !buffer.all_diff_hunks_expanded()
14983 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14984 {
14985 buffer.collapse_diff_hunks(ranges, cx);
14986 true
14987 } else {
14988 false
14989 }
14990 })
14991 }
14992
14993 fn toggle_diff_hunks_in_ranges(
14994 &mut self,
14995 ranges: Vec<Range<Anchor>>,
14996 cx: &mut Context<'_, Editor>,
14997 ) {
14998 self.buffer.update(cx, |buffer, cx| {
14999 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
15000 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
15001 })
15002 }
15003
15004 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
15005 self.buffer.update(cx, |buffer, cx| {
15006 let snapshot = buffer.snapshot(cx);
15007 let excerpt_id = range.end.excerpt_id;
15008 let point_range = range.to_point(&snapshot);
15009 let expand = !buffer.single_hunk_is_expanded(range, cx);
15010 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
15011 })
15012 }
15013
15014 pub(crate) fn apply_all_diff_hunks(
15015 &mut self,
15016 _: &ApplyAllDiffHunks,
15017 window: &mut Window,
15018 cx: &mut Context<Self>,
15019 ) {
15020 let buffers = self.buffer.read(cx).all_buffers();
15021 for branch_buffer in buffers {
15022 branch_buffer.update(cx, |branch_buffer, cx| {
15023 branch_buffer.merge_into_base(Vec::new(), cx);
15024 });
15025 }
15026
15027 if let Some(project) = self.project.clone() {
15028 self.save(true, project, window, cx).detach_and_log_err(cx);
15029 }
15030 }
15031
15032 pub(crate) fn apply_selected_diff_hunks(
15033 &mut self,
15034 _: &ApplyDiffHunk,
15035 window: &mut Window,
15036 cx: &mut Context<Self>,
15037 ) {
15038 let snapshot = self.snapshot(window, cx);
15039 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
15040 let mut ranges_by_buffer = HashMap::default();
15041 self.transact(window, cx, |editor, _window, cx| {
15042 for hunk in hunks {
15043 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
15044 ranges_by_buffer
15045 .entry(buffer.clone())
15046 .or_insert_with(Vec::new)
15047 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
15048 }
15049 }
15050
15051 for (buffer, ranges) in ranges_by_buffer {
15052 buffer.update(cx, |buffer, cx| {
15053 buffer.merge_into_base(ranges, cx);
15054 });
15055 }
15056 });
15057
15058 if let Some(project) = self.project.clone() {
15059 self.save(true, project, window, cx).detach_and_log_err(cx);
15060 }
15061 }
15062
15063 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
15064 if hovered != self.gutter_hovered {
15065 self.gutter_hovered = hovered;
15066 cx.notify();
15067 }
15068 }
15069
15070 pub fn insert_blocks(
15071 &mut self,
15072 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
15073 autoscroll: Option<Autoscroll>,
15074 cx: &mut Context<Self>,
15075 ) -> Vec<CustomBlockId> {
15076 let blocks = self
15077 .display_map
15078 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
15079 if let Some(autoscroll) = autoscroll {
15080 self.request_autoscroll(autoscroll, cx);
15081 }
15082 cx.notify();
15083 blocks
15084 }
15085
15086 pub fn resize_blocks(
15087 &mut self,
15088 heights: HashMap<CustomBlockId, u32>,
15089 autoscroll: Option<Autoscroll>,
15090 cx: &mut Context<Self>,
15091 ) {
15092 self.display_map
15093 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
15094 if let Some(autoscroll) = autoscroll {
15095 self.request_autoscroll(autoscroll, cx);
15096 }
15097 cx.notify();
15098 }
15099
15100 pub fn replace_blocks(
15101 &mut self,
15102 renderers: HashMap<CustomBlockId, RenderBlock>,
15103 autoscroll: Option<Autoscroll>,
15104 cx: &mut Context<Self>,
15105 ) {
15106 self.display_map
15107 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
15108 if let Some(autoscroll) = autoscroll {
15109 self.request_autoscroll(autoscroll, cx);
15110 }
15111 cx.notify();
15112 }
15113
15114 pub fn remove_blocks(
15115 &mut self,
15116 block_ids: HashSet<CustomBlockId>,
15117 autoscroll: Option<Autoscroll>,
15118 cx: &mut Context<Self>,
15119 ) {
15120 self.display_map.update(cx, |display_map, cx| {
15121 display_map.remove_blocks(block_ids, cx)
15122 });
15123 if let Some(autoscroll) = autoscroll {
15124 self.request_autoscroll(autoscroll, cx);
15125 }
15126 cx.notify();
15127 }
15128
15129 pub fn row_for_block(
15130 &self,
15131 block_id: CustomBlockId,
15132 cx: &mut Context<Self>,
15133 ) -> Option<DisplayRow> {
15134 self.display_map
15135 .update(cx, |map, cx| map.row_for_block(block_id, cx))
15136 }
15137
15138 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
15139 self.focused_block = Some(focused_block);
15140 }
15141
15142 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
15143 self.focused_block.take()
15144 }
15145
15146 pub fn insert_creases(
15147 &mut self,
15148 creases: impl IntoIterator<Item = Crease<Anchor>>,
15149 cx: &mut Context<Self>,
15150 ) -> Vec<CreaseId> {
15151 self.display_map
15152 .update(cx, |map, cx| map.insert_creases(creases, cx))
15153 }
15154
15155 pub fn remove_creases(
15156 &mut self,
15157 ids: impl IntoIterator<Item = CreaseId>,
15158 cx: &mut Context<Self>,
15159 ) {
15160 self.display_map
15161 .update(cx, |map, cx| map.remove_creases(ids, cx));
15162 }
15163
15164 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
15165 self.display_map
15166 .update(cx, |map, cx| map.snapshot(cx))
15167 .longest_row()
15168 }
15169
15170 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
15171 self.display_map
15172 .update(cx, |map, cx| map.snapshot(cx))
15173 .max_point()
15174 }
15175
15176 pub fn text(&self, cx: &App) -> String {
15177 self.buffer.read(cx).read(cx).text()
15178 }
15179
15180 pub fn is_empty(&self, cx: &App) -> bool {
15181 self.buffer.read(cx).read(cx).is_empty()
15182 }
15183
15184 pub fn text_option(&self, cx: &App) -> Option<String> {
15185 let text = self.text(cx);
15186 let text = text.trim();
15187
15188 if text.is_empty() {
15189 return None;
15190 }
15191
15192 Some(text.to_string())
15193 }
15194
15195 pub fn set_text(
15196 &mut self,
15197 text: impl Into<Arc<str>>,
15198 window: &mut Window,
15199 cx: &mut Context<Self>,
15200 ) {
15201 self.transact(window, cx, |this, _, cx| {
15202 this.buffer
15203 .read(cx)
15204 .as_singleton()
15205 .expect("you can only call set_text on editors for singleton buffers")
15206 .update(cx, |buffer, cx| buffer.set_text(text, cx));
15207 });
15208 }
15209
15210 pub fn display_text(&self, cx: &mut App) -> String {
15211 self.display_map
15212 .update(cx, |map, cx| map.snapshot(cx))
15213 .text()
15214 }
15215
15216 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
15217 let mut wrap_guides = smallvec::smallvec![];
15218
15219 if self.show_wrap_guides == Some(false) {
15220 return wrap_guides;
15221 }
15222
15223 let settings = self.buffer.read(cx).language_settings(cx);
15224 if settings.show_wrap_guides {
15225 match self.soft_wrap_mode(cx) {
15226 SoftWrap::Column(soft_wrap) => {
15227 wrap_guides.push((soft_wrap as usize, true));
15228 }
15229 SoftWrap::Bounded(soft_wrap) => {
15230 wrap_guides.push((soft_wrap as usize, true));
15231 }
15232 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
15233 }
15234 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
15235 }
15236
15237 wrap_guides
15238 }
15239
15240 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
15241 let settings = self.buffer.read(cx).language_settings(cx);
15242 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
15243 match mode {
15244 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
15245 SoftWrap::None
15246 }
15247 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15248 language_settings::SoftWrap::PreferredLineLength => {
15249 SoftWrap::Column(settings.preferred_line_length)
15250 }
15251 language_settings::SoftWrap::Bounded => {
15252 SoftWrap::Bounded(settings.preferred_line_length)
15253 }
15254 }
15255 }
15256
15257 pub fn set_soft_wrap_mode(
15258 &mut self,
15259 mode: language_settings::SoftWrap,
15260
15261 cx: &mut Context<Self>,
15262 ) {
15263 self.soft_wrap_mode_override = Some(mode);
15264 cx.notify();
15265 }
15266
15267 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15268 self.hard_wrap = hard_wrap;
15269 cx.notify();
15270 }
15271
15272 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15273 self.text_style_refinement = Some(style);
15274 }
15275
15276 /// called by the Element so we know what style we were most recently rendered with.
15277 pub(crate) fn set_style(
15278 &mut self,
15279 style: EditorStyle,
15280 window: &mut Window,
15281 cx: &mut Context<Self>,
15282 ) {
15283 let rem_size = window.rem_size();
15284 self.display_map.update(cx, |map, cx| {
15285 map.set_font(
15286 style.text.font(),
15287 style.text.font_size.to_pixels(rem_size),
15288 cx,
15289 )
15290 });
15291 self.style = Some(style);
15292 }
15293
15294 pub fn style(&self) -> Option<&EditorStyle> {
15295 self.style.as_ref()
15296 }
15297
15298 // Called by the element. This method is not designed to be called outside of the editor
15299 // element's layout code because it does not notify when rewrapping is computed synchronously.
15300 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15301 self.display_map
15302 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15303 }
15304
15305 pub fn set_soft_wrap(&mut self) {
15306 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15307 }
15308
15309 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15310 if self.soft_wrap_mode_override.is_some() {
15311 self.soft_wrap_mode_override.take();
15312 } else {
15313 let soft_wrap = match self.soft_wrap_mode(cx) {
15314 SoftWrap::GitDiff => return,
15315 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15316 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15317 language_settings::SoftWrap::None
15318 }
15319 };
15320 self.soft_wrap_mode_override = Some(soft_wrap);
15321 }
15322 cx.notify();
15323 }
15324
15325 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15326 let Some(workspace) = self.workspace() else {
15327 return;
15328 };
15329 let fs = workspace.read(cx).app_state().fs.clone();
15330 let current_show = TabBarSettings::get_global(cx).show;
15331 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15332 setting.show = Some(!current_show);
15333 });
15334 }
15335
15336 pub fn toggle_indent_guides(
15337 &mut self,
15338 _: &ToggleIndentGuides,
15339 _: &mut Window,
15340 cx: &mut Context<Self>,
15341 ) {
15342 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15343 self.buffer
15344 .read(cx)
15345 .language_settings(cx)
15346 .indent_guides
15347 .enabled
15348 });
15349 self.show_indent_guides = Some(!currently_enabled);
15350 cx.notify();
15351 }
15352
15353 fn should_show_indent_guides(&self) -> Option<bool> {
15354 self.show_indent_guides
15355 }
15356
15357 pub fn toggle_line_numbers(
15358 &mut self,
15359 _: &ToggleLineNumbers,
15360 _: &mut Window,
15361 cx: &mut Context<Self>,
15362 ) {
15363 let mut editor_settings = EditorSettings::get_global(cx).clone();
15364 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15365 EditorSettings::override_global(editor_settings, cx);
15366 }
15367
15368 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15369 if let Some(show_line_numbers) = self.show_line_numbers {
15370 return show_line_numbers;
15371 }
15372 EditorSettings::get_global(cx).gutter.line_numbers
15373 }
15374
15375 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15376 self.use_relative_line_numbers
15377 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15378 }
15379
15380 pub fn toggle_relative_line_numbers(
15381 &mut self,
15382 _: &ToggleRelativeLineNumbers,
15383 _: &mut Window,
15384 cx: &mut Context<Self>,
15385 ) {
15386 let is_relative = self.should_use_relative_line_numbers(cx);
15387 self.set_relative_line_number(Some(!is_relative), cx)
15388 }
15389
15390 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15391 self.use_relative_line_numbers = is_relative;
15392 cx.notify();
15393 }
15394
15395 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15396 self.show_gutter = show_gutter;
15397 cx.notify();
15398 }
15399
15400 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15401 self.show_scrollbars = show_scrollbars;
15402 cx.notify();
15403 }
15404
15405 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15406 self.show_line_numbers = Some(show_line_numbers);
15407 cx.notify();
15408 }
15409
15410 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15411 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15412 cx.notify();
15413 }
15414
15415 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15416 self.show_code_actions = Some(show_code_actions);
15417 cx.notify();
15418 }
15419
15420 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15421 self.show_runnables = Some(show_runnables);
15422 cx.notify();
15423 }
15424
15425 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15426 self.show_breakpoints = Some(show_breakpoints);
15427 cx.notify();
15428 }
15429
15430 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15431 if self.display_map.read(cx).masked != masked {
15432 self.display_map.update(cx, |map, _| map.masked = masked);
15433 }
15434 cx.notify()
15435 }
15436
15437 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15438 self.show_wrap_guides = Some(show_wrap_guides);
15439 cx.notify();
15440 }
15441
15442 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15443 self.show_indent_guides = Some(show_indent_guides);
15444 cx.notify();
15445 }
15446
15447 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15448 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15449 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15450 if let Some(dir) = file.abs_path(cx).parent() {
15451 return Some(dir.to_owned());
15452 }
15453 }
15454
15455 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15456 return Some(project_path.path.to_path_buf());
15457 }
15458 }
15459
15460 None
15461 }
15462
15463 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15464 self.active_excerpt(cx)?
15465 .1
15466 .read(cx)
15467 .file()
15468 .and_then(|f| f.as_local())
15469 }
15470
15471 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15472 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15473 let buffer = buffer.read(cx);
15474 if let Some(project_path) = buffer.project_path(cx) {
15475 let project = self.project.as_ref()?.read(cx);
15476 project.absolute_path(&project_path, cx)
15477 } else {
15478 buffer
15479 .file()
15480 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15481 }
15482 })
15483 }
15484
15485 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15486 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15487 let project_path = buffer.read(cx).project_path(cx)?;
15488 let project = self.project.as_ref()?.read(cx);
15489 let entry = project.entry_for_path(&project_path, cx)?;
15490 let path = entry.path.to_path_buf();
15491 Some(path)
15492 })
15493 }
15494
15495 pub fn reveal_in_finder(
15496 &mut self,
15497 _: &RevealInFileManager,
15498 _window: &mut Window,
15499 cx: &mut Context<Self>,
15500 ) {
15501 if let Some(target) = self.target_file(cx) {
15502 cx.reveal_path(&target.abs_path(cx));
15503 }
15504 }
15505
15506 pub fn copy_path(
15507 &mut self,
15508 _: &zed_actions::workspace::CopyPath,
15509 _window: &mut Window,
15510 cx: &mut Context<Self>,
15511 ) {
15512 if let Some(path) = self.target_file_abs_path(cx) {
15513 if let Some(path) = path.to_str() {
15514 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15515 }
15516 }
15517 }
15518
15519 pub fn copy_relative_path(
15520 &mut self,
15521 _: &zed_actions::workspace::CopyRelativePath,
15522 _window: &mut Window,
15523 cx: &mut Context<Self>,
15524 ) {
15525 if let Some(path) = self.target_file_path(cx) {
15526 if let Some(path) = path.to_str() {
15527 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15528 }
15529 }
15530 }
15531
15532 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15533 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15534 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15535 } else {
15536 None
15537 }
15538 }
15539
15540 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15541 let _ = maybe!({
15542 let breakpoint_store = self.breakpoint_store.as_ref()?;
15543
15544 let Some((_, _, active_position)) =
15545 breakpoint_store.read(cx).active_position().cloned()
15546 else {
15547 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15548 return None;
15549 };
15550
15551 let snapshot = self
15552 .project
15553 .as_ref()?
15554 .read(cx)
15555 .buffer_for_id(active_position.buffer_id?, cx)?
15556 .read(cx)
15557 .snapshot();
15558
15559 for (id, ExcerptRange { context, .. }) in self
15560 .buffer
15561 .read(cx)
15562 .excerpts_for_buffer(active_position.buffer_id?, cx)
15563 {
15564 if context.start.cmp(&active_position, &snapshot).is_ge()
15565 || context.end.cmp(&active_position, &snapshot).is_lt()
15566 {
15567 continue;
15568 }
15569 let snapshot = self.buffer.read(cx).snapshot(cx);
15570 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15571
15572 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15573 self.go_to_line::<DebugCurrentRowHighlight>(
15574 multibuffer_anchor,
15575 Some(cx.theme().colors().editor_debugger_active_line_background),
15576 window,
15577 cx,
15578 );
15579
15580 cx.notify();
15581 }
15582
15583 Some(())
15584 });
15585 }
15586
15587 pub fn copy_file_name_without_extension(
15588 &mut self,
15589 _: &CopyFileNameWithoutExtension,
15590 _: &mut Window,
15591 cx: &mut Context<Self>,
15592 ) {
15593 if let Some(file) = self.target_file(cx) {
15594 if let Some(file_stem) = file.path().file_stem() {
15595 if let Some(name) = file_stem.to_str() {
15596 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15597 }
15598 }
15599 }
15600 }
15601
15602 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15603 if let Some(file) = self.target_file(cx) {
15604 if let Some(file_name) = file.path().file_name() {
15605 if let Some(name) = file_name.to_str() {
15606 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15607 }
15608 }
15609 }
15610 }
15611
15612 pub fn toggle_git_blame(
15613 &mut self,
15614 _: &::git::Blame,
15615 window: &mut Window,
15616 cx: &mut Context<Self>,
15617 ) {
15618 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15619
15620 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15621 self.start_git_blame(true, window, cx);
15622 }
15623
15624 cx.notify();
15625 }
15626
15627 pub fn toggle_git_blame_inline(
15628 &mut self,
15629 _: &ToggleGitBlameInline,
15630 window: &mut Window,
15631 cx: &mut Context<Self>,
15632 ) {
15633 self.toggle_git_blame_inline_internal(true, window, cx);
15634 cx.notify();
15635 }
15636
15637 pub fn git_blame_inline_enabled(&self) -> bool {
15638 self.git_blame_inline_enabled
15639 }
15640
15641 pub fn toggle_selection_menu(
15642 &mut self,
15643 _: &ToggleSelectionMenu,
15644 _: &mut Window,
15645 cx: &mut Context<Self>,
15646 ) {
15647 self.show_selection_menu = self
15648 .show_selection_menu
15649 .map(|show_selections_menu| !show_selections_menu)
15650 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15651
15652 cx.notify();
15653 }
15654
15655 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15656 self.show_selection_menu
15657 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15658 }
15659
15660 fn start_git_blame(
15661 &mut self,
15662 user_triggered: bool,
15663 window: &mut Window,
15664 cx: &mut Context<Self>,
15665 ) {
15666 if let Some(project) = self.project.as_ref() {
15667 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15668 return;
15669 };
15670
15671 if buffer.read(cx).file().is_none() {
15672 return;
15673 }
15674
15675 let focused = self.focus_handle(cx).contains_focused(window, cx);
15676
15677 let project = project.clone();
15678 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15679 self.blame_subscription =
15680 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15681 self.blame = Some(blame);
15682 }
15683 }
15684
15685 fn toggle_git_blame_inline_internal(
15686 &mut self,
15687 user_triggered: bool,
15688 window: &mut Window,
15689 cx: &mut Context<Self>,
15690 ) {
15691 if self.git_blame_inline_enabled {
15692 self.git_blame_inline_enabled = false;
15693 self.show_git_blame_inline = false;
15694 self.show_git_blame_inline_delay_task.take();
15695 } else {
15696 self.git_blame_inline_enabled = true;
15697 self.start_git_blame_inline(user_triggered, window, cx);
15698 }
15699
15700 cx.notify();
15701 }
15702
15703 fn start_git_blame_inline(
15704 &mut self,
15705 user_triggered: bool,
15706 window: &mut Window,
15707 cx: &mut Context<Self>,
15708 ) {
15709 self.start_git_blame(user_triggered, window, cx);
15710
15711 if ProjectSettings::get_global(cx)
15712 .git
15713 .inline_blame_delay()
15714 .is_some()
15715 {
15716 self.start_inline_blame_timer(window, cx);
15717 } else {
15718 self.show_git_blame_inline = true
15719 }
15720 }
15721
15722 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15723 self.blame.as_ref()
15724 }
15725
15726 pub fn show_git_blame_gutter(&self) -> bool {
15727 self.show_git_blame_gutter
15728 }
15729
15730 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15731 self.show_git_blame_gutter && self.has_blame_entries(cx)
15732 }
15733
15734 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15735 self.show_git_blame_inline
15736 && (self.focus_handle.is_focused(window)
15737 || self
15738 .git_blame_inline_tooltip
15739 .as_ref()
15740 .and_then(|t| t.upgrade())
15741 .is_some())
15742 && !self.newest_selection_head_on_empty_line(cx)
15743 && self.has_blame_entries(cx)
15744 }
15745
15746 fn has_blame_entries(&self, cx: &App) -> bool {
15747 self.blame()
15748 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15749 }
15750
15751 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15752 let cursor_anchor = self.selections.newest_anchor().head();
15753
15754 let snapshot = self.buffer.read(cx).snapshot(cx);
15755 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15756
15757 snapshot.line_len(buffer_row) == 0
15758 }
15759
15760 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15761 let buffer_and_selection = maybe!({
15762 let selection = self.selections.newest::<Point>(cx);
15763 let selection_range = selection.range();
15764
15765 let multi_buffer = self.buffer().read(cx);
15766 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15767 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15768
15769 let (buffer, range, _) = if selection.reversed {
15770 buffer_ranges.first()
15771 } else {
15772 buffer_ranges.last()
15773 }?;
15774
15775 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15776 ..text::ToPoint::to_point(&range.end, &buffer).row;
15777 Some((
15778 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15779 selection,
15780 ))
15781 });
15782
15783 let Some((buffer, selection)) = buffer_and_selection else {
15784 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15785 };
15786
15787 let Some(project) = self.project.as_ref() else {
15788 return Task::ready(Err(anyhow!("editor does not have project")));
15789 };
15790
15791 project.update(cx, |project, cx| {
15792 project.get_permalink_to_line(&buffer, selection, cx)
15793 })
15794 }
15795
15796 pub fn copy_permalink_to_line(
15797 &mut self,
15798 _: &CopyPermalinkToLine,
15799 window: &mut Window,
15800 cx: &mut Context<Self>,
15801 ) {
15802 let permalink_task = self.get_permalink_to_line(cx);
15803 let workspace = self.workspace();
15804
15805 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15806 Ok(permalink) => {
15807 cx.update(|_, cx| {
15808 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15809 })
15810 .ok();
15811 }
15812 Err(err) => {
15813 let message = format!("Failed to copy permalink: {err}");
15814
15815 Err::<(), anyhow::Error>(err).log_err();
15816
15817 if let Some(workspace) = workspace {
15818 workspace
15819 .update_in(cx, |workspace, _, cx| {
15820 struct CopyPermalinkToLine;
15821
15822 workspace.show_toast(
15823 Toast::new(
15824 NotificationId::unique::<CopyPermalinkToLine>(),
15825 message,
15826 ),
15827 cx,
15828 )
15829 })
15830 .ok();
15831 }
15832 }
15833 })
15834 .detach();
15835 }
15836
15837 pub fn copy_file_location(
15838 &mut self,
15839 _: &CopyFileLocation,
15840 _: &mut Window,
15841 cx: &mut Context<Self>,
15842 ) {
15843 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15844 if let Some(file) = self.target_file(cx) {
15845 if let Some(path) = file.path().to_str() {
15846 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15847 }
15848 }
15849 }
15850
15851 pub fn open_permalink_to_line(
15852 &mut self,
15853 _: &OpenPermalinkToLine,
15854 window: &mut Window,
15855 cx: &mut Context<Self>,
15856 ) {
15857 let permalink_task = self.get_permalink_to_line(cx);
15858 let workspace = self.workspace();
15859
15860 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15861 Ok(permalink) => {
15862 cx.update(|_, cx| {
15863 cx.open_url(permalink.as_ref());
15864 })
15865 .ok();
15866 }
15867 Err(err) => {
15868 let message = format!("Failed to open permalink: {err}");
15869
15870 Err::<(), anyhow::Error>(err).log_err();
15871
15872 if let Some(workspace) = workspace {
15873 workspace
15874 .update(cx, |workspace, cx| {
15875 struct OpenPermalinkToLine;
15876
15877 workspace.show_toast(
15878 Toast::new(
15879 NotificationId::unique::<OpenPermalinkToLine>(),
15880 message,
15881 ),
15882 cx,
15883 )
15884 })
15885 .ok();
15886 }
15887 }
15888 })
15889 .detach();
15890 }
15891
15892 pub fn insert_uuid_v4(
15893 &mut self,
15894 _: &InsertUuidV4,
15895 window: &mut Window,
15896 cx: &mut Context<Self>,
15897 ) {
15898 self.insert_uuid(UuidVersion::V4, window, cx);
15899 }
15900
15901 pub fn insert_uuid_v7(
15902 &mut self,
15903 _: &InsertUuidV7,
15904 window: &mut Window,
15905 cx: &mut Context<Self>,
15906 ) {
15907 self.insert_uuid(UuidVersion::V7, window, cx);
15908 }
15909
15910 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15911 self.transact(window, cx, |this, window, cx| {
15912 let edits = this
15913 .selections
15914 .all::<Point>(cx)
15915 .into_iter()
15916 .map(|selection| {
15917 let uuid = match version {
15918 UuidVersion::V4 => uuid::Uuid::new_v4(),
15919 UuidVersion::V7 => uuid::Uuid::now_v7(),
15920 };
15921
15922 (selection.range(), uuid.to_string())
15923 });
15924 this.edit(edits, cx);
15925 this.refresh_inline_completion(true, false, window, cx);
15926 });
15927 }
15928
15929 pub fn open_selections_in_multibuffer(
15930 &mut self,
15931 _: &OpenSelectionsInMultibuffer,
15932 window: &mut Window,
15933 cx: &mut Context<Self>,
15934 ) {
15935 let multibuffer = self.buffer.read(cx);
15936
15937 let Some(buffer) = multibuffer.as_singleton() else {
15938 return;
15939 };
15940
15941 let Some(workspace) = self.workspace() else {
15942 return;
15943 };
15944
15945 let locations = self
15946 .selections
15947 .disjoint_anchors()
15948 .iter()
15949 .map(|range| Location {
15950 buffer: buffer.clone(),
15951 range: range.start.text_anchor..range.end.text_anchor,
15952 })
15953 .collect::<Vec<_>>();
15954
15955 let title = multibuffer.title(cx).to_string();
15956
15957 cx.spawn_in(window, async move |_, cx| {
15958 workspace.update_in(cx, |workspace, window, cx| {
15959 Self::open_locations_in_multibuffer(
15960 workspace,
15961 locations,
15962 format!("Selections for '{title}'"),
15963 false,
15964 MultibufferSelectionMode::All,
15965 window,
15966 cx,
15967 );
15968 })
15969 })
15970 .detach();
15971 }
15972
15973 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15974 /// last highlight added will be used.
15975 ///
15976 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15977 pub fn highlight_rows<T: 'static>(
15978 &mut self,
15979 range: Range<Anchor>,
15980 color: Hsla,
15981 should_autoscroll: bool,
15982 cx: &mut Context<Self>,
15983 ) {
15984 let snapshot = self.buffer().read(cx).snapshot(cx);
15985 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15986 let ix = row_highlights.binary_search_by(|highlight| {
15987 Ordering::Equal
15988 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15989 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15990 });
15991
15992 if let Err(mut ix) = ix {
15993 let index = post_inc(&mut self.highlight_order);
15994
15995 // If this range intersects with the preceding highlight, then merge it with
15996 // the preceding highlight. Otherwise insert a new highlight.
15997 let mut merged = false;
15998 if ix > 0 {
15999 let prev_highlight = &mut row_highlights[ix - 1];
16000 if prev_highlight
16001 .range
16002 .end
16003 .cmp(&range.start, &snapshot)
16004 .is_ge()
16005 {
16006 ix -= 1;
16007 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
16008 prev_highlight.range.end = range.end;
16009 }
16010 merged = true;
16011 prev_highlight.index = index;
16012 prev_highlight.color = color;
16013 prev_highlight.should_autoscroll = should_autoscroll;
16014 }
16015 }
16016
16017 if !merged {
16018 row_highlights.insert(
16019 ix,
16020 RowHighlight {
16021 range: range.clone(),
16022 index,
16023 color,
16024 should_autoscroll,
16025 },
16026 );
16027 }
16028
16029 // If any of the following highlights intersect with this one, merge them.
16030 while let Some(next_highlight) = row_highlights.get(ix + 1) {
16031 let highlight = &row_highlights[ix];
16032 if next_highlight
16033 .range
16034 .start
16035 .cmp(&highlight.range.end, &snapshot)
16036 .is_le()
16037 {
16038 if next_highlight
16039 .range
16040 .end
16041 .cmp(&highlight.range.end, &snapshot)
16042 .is_gt()
16043 {
16044 row_highlights[ix].range.end = next_highlight.range.end;
16045 }
16046 row_highlights.remove(ix + 1);
16047 } else {
16048 break;
16049 }
16050 }
16051 }
16052 }
16053
16054 /// Remove any highlighted row ranges of the given type that intersect the
16055 /// given ranges.
16056 pub fn remove_highlighted_rows<T: 'static>(
16057 &mut self,
16058 ranges_to_remove: Vec<Range<Anchor>>,
16059 cx: &mut Context<Self>,
16060 ) {
16061 let snapshot = self.buffer().read(cx).snapshot(cx);
16062 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
16063 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
16064 row_highlights.retain(|highlight| {
16065 while let Some(range_to_remove) = ranges_to_remove.peek() {
16066 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
16067 Ordering::Less | Ordering::Equal => {
16068 ranges_to_remove.next();
16069 }
16070 Ordering::Greater => {
16071 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
16072 Ordering::Less | Ordering::Equal => {
16073 return false;
16074 }
16075 Ordering::Greater => break,
16076 }
16077 }
16078 }
16079 }
16080
16081 true
16082 })
16083 }
16084
16085 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
16086 pub fn clear_row_highlights<T: 'static>(&mut self) {
16087 self.highlighted_rows.remove(&TypeId::of::<T>());
16088 }
16089
16090 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
16091 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
16092 self.highlighted_rows
16093 .get(&TypeId::of::<T>())
16094 .map_or(&[] as &[_], |vec| vec.as_slice())
16095 .iter()
16096 .map(|highlight| (highlight.range.clone(), highlight.color))
16097 }
16098
16099 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
16100 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
16101 /// Allows to ignore certain kinds of highlights.
16102 pub fn highlighted_display_rows(
16103 &self,
16104 window: &mut Window,
16105 cx: &mut App,
16106 ) -> BTreeMap<DisplayRow, LineHighlight> {
16107 let snapshot = self.snapshot(window, cx);
16108 let mut used_highlight_orders = HashMap::default();
16109 self.highlighted_rows
16110 .iter()
16111 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
16112 .fold(
16113 BTreeMap::<DisplayRow, LineHighlight>::new(),
16114 |mut unique_rows, highlight| {
16115 let start = highlight.range.start.to_display_point(&snapshot);
16116 let end = highlight.range.end.to_display_point(&snapshot);
16117 let start_row = start.row().0;
16118 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
16119 && end.column() == 0
16120 {
16121 end.row().0.saturating_sub(1)
16122 } else {
16123 end.row().0
16124 };
16125 for row in start_row..=end_row {
16126 let used_index =
16127 used_highlight_orders.entry(row).or_insert(highlight.index);
16128 if highlight.index >= *used_index {
16129 *used_index = highlight.index;
16130 unique_rows.insert(DisplayRow(row), highlight.color.into());
16131 }
16132 }
16133 unique_rows
16134 },
16135 )
16136 }
16137
16138 pub fn highlighted_display_row_for_autoscroll(
16139 &self,
16140 snapshot: &DisplaySnapshot,
16141 ) -> Option<DisplayRow> {
16142 self.highlighted_rows
16143 .values()
16144 .flat_map(|highlighted_rows| highlighted_rows.iter())
16145 .filter_map(|highlight| {
16146 if highlight.should_autoscroll {
16147 Some(highlight.range.start.to_display_point(snapshot).row())
16148 } else {
16149 None
16150 }
16151 })
16152 .min()
16153 }
16154
16155 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
16156 self.highlight_background::<SearchWithinRange>(
16157 ranges,
16158 |colors| colors.editor_document_highlight_read_background,
16159 cx,
16160 )
16161 }
16162
16163 pub fn set_breadcrumb_header(&mut self, new_header: String) {
16164 self.breadcrumb_header = Some(new_header);
16165 }
16166
16167 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
16168 self.clear_background_highlights::<SearchWithinRange>(cx);
16169 }
16170
16171 pub fn highlight_background<T: 'static>(
16172 &mut self,
16173 ranges: &[Range<Anchor>],
16174 color_fetcher: fn(&ThemeColors) -> Hsla,
16175 cx: &mut Context<Self>,
16176 ) {
16177 self.background_highlights
16178 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16179 self.scrollbar_marker_state.dirty = true;
16180 cx.notify();
16181 }
16182
16183 pub fn clear_background_highlights<T: 'static>(
16184 &mut self,
16185 cx: &mut Context<Self>,
16186 ) -> Option<BackgroundHighlight> {
16187 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
16188 if !text_highlights.1.is_empty() {
16189 self.scrollbar_marker_state.dirty = true;
16190 cx.notify();
16191 }
16192 Some(text_highlights)
16193 }
16194
16195 pub fn highlight_gutter<T: 'static>(
16196 &mut self,
16197 ranges: &[Range<Anchor>],
16198 color_fetcher: fn(&App) -> Hsla,
16199 cx: &mut Context<Self>,
16200 ) {
16201 self.gutter_highlights
16202 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16203 cx.notify();
16204 }
16205
16206 pub fn clear_gutter_highlights<T: 'static>(
16207 &mut self,
16208 cx: &mut Context<Self>,
16209 ) -> Option<GutterHighlight> {
16210 cx.notify();
16211 self.gutter_highlights.remove(&TypeId::of::<T>())
16212 }
16213
16214 #[cfg(feature = "test-support")]
16215 pub fn all_text_background_highlights(
16216 &self,
16217 window: &mut Window,
16218 cx: &mut Context<Self>,
16219 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16220 let snapshot = self.snapshot(window, cx);
16221 let buffer = &snapshot.buffer_snapshot;
16222 let start = buffer.anchor_before(0);
16223 let end = buffer.anchor_after(buffer.len());
16224 let theme = cx.theme().colors();
16225 self.background_highlights_in_range(start..end, &snapshot, theme)
16226 }
16227
16228 #[cfg(feature = "test-support")]
16229 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
16230 let snapshot = self.buffer().read(cx).snapshot(cx);
16231
16232 let highlights = self
16233 .background_highlights
16234 .get(&TypeId::of::<items::BufferSearchHighlights>());
16235
16236 if let Some((_color, ranges)) = highlights {
16237 ranges
16238 .iter()
16239 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
16240 .collect_vec()
16241 } else {
16242 vec![]
16243 }
16244 }
16245
16246 fn document_highlights_for_position<'a>(
16247 &'a self,
16248 position: Anchor,
16249 buffer: &'a MultiBufferSnapshot,
16250 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16251 let read_highlights = self
16252 .background_highlights
16253 .get(&TypeId::of::<DocumentHighlightRead>())
16254 .map(|h| &h.1);
16255 let write_highlights = self
16256 .background_highlights
16257 .get(&TypeId::of::<DocumentHighlightWrite>())
16258 .map(|h| &h.1);
16259 let left_position = position.bias_left(buffer);
16260 let right_position = position.bias_right(buffer);
16261 read_highlights
16262 .into_iter()
16263 .chain(write_highlights)
16264 .flat_map(move |ranges| {
16265 let start_ix = match ranges.binary_search_by(|probe| {
16266 let cmp = probe.end.cmp(&left_position, buffer);
16267 if cmp.is_ge() {
16268 Ordering::Greater
16269 } else {
16270 Ordering::Less
16271 }
16272 }) {
16273 Ok(i) | Err(i) => i,
16274 };
16275
16276 ranges[start_ix..]
16277 .iter()
16278 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16279 })
16280 }
16281
16282 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16283 self.background_highlights
16284 .get(&TypeId::of::<T>())
16285 .map_or(false, |(_, highlights)| !highlights.is_empty())
16286 }
16287
16288 pub fn background_highlights_in_range(
16289 &self,
16290 search_range: Range<Anchor>,
16291 display_snapshot: &DisplaySnapshot,
16292 theme: &ThemeColors,
16293 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16294 let mut results = Vec::new();
16295 for (color_fetcher, ranges) in self.background_highlights.values() {
16296 let color = color_fetcher(theme);
16297 let start_ix = match ranges.binary_search_by(|probe| {
16298 let cmp = probe
16299 .end
16300 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16301 if cmp.is_gt() {
16302 Ordering::Greater
16303 } else {
16304 Ordering::Less
16305 }
16306 }) {
16307 Ok(i) | Err(i) => i,
16308 };
16309 for range in &ranges[start_ix..] {
16310 if range
16311 .start
16312 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16313 .is_ge()
16314 {
16315 break;
16316 }
16317
16318 let start = range.start.to_display_point(display_snapshot);
16319 let end = range.end.to_display_point(display_snapshot);
16320 results.push((start..end, color))
16321 }
16322 }
16323 results
16324 }
16325
16326 pub fn background_highlight_row_ranges<T: 'static>(
16327 &self,
16328 search_range: Range<Anchor>,
16329 display_snapshot: &DisplaySnapshot,
16330 count: usize,
16331 ) -> Vec<RangeInclusive<DisplayPoint>> {
16332 let mut results = Vec::new();
16333 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16334 return vec![];
16335 };
16336
16337 let start_ix = match ranges.binary_search_by(|probe| {
16338 let cmp = probe
16339 .end
16340 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16341 if cmp.is_gt() {
16342 Ordering::Greater
16343 } else {
16344 Ordering::Less
16345 }
16346 }) {
16347 Ok(i) | Err(i) => i,
16348 };
16349 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16350 if let (Some(start_display), Some(end_display)) = (start, end) {
16351 results.push(
16352 start_display.to_display_point(display_snapshot)
16353 ..=end_display.to_display_point(display_snapshot),
16354 );
16355 }
16356 };
16357 let mut start_row: Option<Point> = None;
16358 let mut end_row: Option<Point> = None;
16359 if ranges.len() > count {
16360 return Vec::new();
16361 }
16362 for range in &ranges[start_ix..] {
16363 if range
16364 .start
16365 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16366 .is_ge()
16367 {
16368 break;
16369 }
16370 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16371 if let Some(current_row) = &end_row {
16372 if end.row == current_row.row {
16373 continue;
16374 }
16375 }
16376 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16377 if start_row.is_none() {
16378 assert_eq!(end_row, None);
16379 start_row = Some(start);
16380 end_row = Some(end);
16381 continue;
16382 }
16383 if let Some(current_end) = end_row.as_mut() {
16384 if start.row > current_end.row + 1 {
16385 push_region(start_row, end_row);
16386 start_row = Some(start);
16387 end_row = Some(end);
16388 } else {
16389 // Merge two hunks.
16390 *current_end = end;
16391 }
16392 } else {
16393 unreachable!();
16394 }
16395 }
16396 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16397 push_region(start_row, end_row);
16398 results
16399 }
16400
16401 pub fn gutter_highlights_in_range(
16402 &self,
16403 search_range: Range<Anchor>,
16404 display_snapshot: &DisplaySnapshot,
16405 cx: &App,
16406 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16407 let mut results = Vec::new();
16408 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16409 let color = color_fetcher(cx);
16410 let start_ix = match ranges.binary_search_by(|probe| {
16411 let cmp = probe
16412 .end
16413 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16414 if cmp.is_gt() {
16415 Ordering::Greater
16416 } else {
16417 Ordering::Less
16418 }
16419 }) {
16420 Ok(i) | Err(i) => i,
16421 };
16422 for range in &ranges[start_ix..] {
16423 if range
16424 .start
16425 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16426 .is_ge()
16427 {
16428 break;
16429 }
16430
16431 let start = range.start.to_display_point(display_snapshot);
16432 let end = range.end.to_display_point(display_snapshot);
16433 results.push((start..end, color))
16434 }
16435 }
16436 results
16437 }
16438
16439 /// Get the text ranges corresponding to the redaction query
16440 pub fn redacted_ranges(
16441 &self,
16442 search_range: Range<Anchor>,
16443 display_snapshot: &DisplaySnapshot,
16444 cx: &App,
16445 ) -> Vec<Range<DisplayPoint>> {
16446 display_snapshot
16447 .buffer_snapshot
16448 .redacted_ranges(search_range, |file| {
16449 if let Some(file) = file {
16450 file.is_private()
16451 && EditorSettings::get(
16452 Some(SettingsLocation {
16453 worktree_id: file.worktree_id(cx),
16454 path: file.path().as_ref(),
16455 }),
16456 cx,
16457 )
16458 .redact_private_values
16459 } else {
16460 false
16461 }
16462 })
16463 .map(|range| {
16464 range.start.to_display_point(display_snapshot)
16465 ..range.end.to_display_point(display_snapshot)
16466 })
16467 .collect()
16468 }
16469
16470 pub fn highlight_text<T: 'static>(
16471 &mut self,
16472 ranges: Vec<Range<Anchor>>,
16473 style: HighlightStyle,
16474 cx: &mut Context<Self>,
16475 ) {
16476 self.display_map.update(cx, |map, _| {
16477 map.highlight_text(TypeId::of::<T>(), ranges, style)
16478 });
16479 cx.notify();
16480 }
16481
16482 pub(crate) fn highlight_inlays<T: 'static>(
16483 &mut self,
16484 highlights: Vec<InlayHighlight>,
16485 style: HighlightStyle,
16486 cx: &mut Context<Self>,
16487 ) {
16488 self.display_map.update(cx, |map, _| {
16489 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16490 });
16491 cx.notify();
16492 }
16493
16494 pub fn text_highlights<'a, T: 'static>(
16495 &'a self,
16496 cx: &'a App,
16497 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16498 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16499 }
16500
16501 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16502 let cleared = self
16503 .display_map
16504 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16505 if cleared {
16506 cx.notify();
16507 }
16508 }
16509
16510 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16511 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16512 && self.focus_handle.is_focused(window)
16513 }
16514
16515 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16516 self.show_cursor_when_unfocused = is_enabled;
16517 cx.notify();
16518 }
16519
16520 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16521 cx.notify();
16522 }
16523
16524 fn on_buffer_event(
16525 &mut self,
16526 multibuffer: &Entity<MultiBuffer>,
16527 event: &multi_buffer::Event,
16528 window: &mut Window,
16529 cx: &mut Context<Self>,
16530 ) {
16531 match event {
16532 multi_buffer::Event::Edited {
16533 singleton_buffer_edited,
16534 edited_buffer: buffer_edited,
16535 } => {
16536 self.scrollbar_marker_state.dirty = true;
16537 self.active_indent_guides_state.dirty = true;
16538 self.refresh_active_diagnostics(cx);
16539 self.refresh_code_actions(window, cx);
16540 if self.has_active_inline_completion() {
16541 self.update_visible_inline_completion(window, cx);
16542 }
16543 if let Some(buffer) = buffer_edited {
16544 let buffer_id = buffer.read(cx).remote_id();
16545 if !self.registered_buffers.contains_key(&buffer_id) {
16546 if let Some(project) = self.project.as_ref() {
16547 project.update(cx, |project, cx| {
16548 self.registered_buffers.insert(
16549 buffer_id,
16550 project.register_buffer_with_language_servers(&buffer, cx),
16551 );
16552 })
16553 }
16554 }
16555 }
16556 cx.emit(EditorEvent::BufferEdited);
16557 cx.emit(SearchEvent::MatchesInvalidated);
16558 if *singleton_buffer_edited {
16559 if let Some(project) = &self.project {
16560 #[allow(clippy::mutable_key_type)]
16561 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16562 multibuffer
16563 .all_buffers()
16564 .into_iter()
16565 .filter_map(|buffer| {
16566 buffer.update(cx, |buffer, cx| {
16567 let language = buffer.language()?;
16568 let should_discard = project.update(cx, |project, cx| {
16569 project.is_local()
16570 && !project.has_language_servers_for(buffer, cx)
16571 });
16572 should_discard.not().then_some(language.clone())
16573 })
16574 })
16575 .collect::<HashSet<_>>()
16576 });
16577 if !languages_affected.is_empty() {
16578 self.refresh_inlay_hints(
16579 InlayHintRefreshReason::BufferEdited(languages_affected),
16580 cx,
16581 );
16582 }
16583 }
16584 }
16585
16586 let Some(project) = &self.project else { return };
16587 let (telemetry, is_via_ssh) = {
16588 let project = project.read(cx);
16589 let telemetry = project.client().telemetry().clone();
16590 let is_via_ssh = project.is_via_ssh();
16591 (telemetry, is_via_ssh)
16592 };
16593 refresh_linked_ranges(self, window, cx);
16594 telemetry.log_edit_event("editor", is_via_ssh);
16595 }
16596 multi_buffer::Event::ExcerptsAdded {
16597 buffer,
16598 predecessor,
16599 excerpts,
16600 } => {
16601 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16602 let buffer_id = buffer.read(cx).remote_id();
16603 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16604 if let Some(project) = &self.project {
16605 get_uncommitted_diff_for_buffer(
16606 project,
16607 [buffer.clone()],
16608 self.buffer.clone(),
16609 cx,
16610 )
16611 .detach();
16612 }
16613 }
16614 cx.emit(EditorEvent::ExcerptsAdded {
16615 buffer: buffer.clone(),
16616 predecessor: *predecessor,
16617 excerpts: excerpts.clone(),
16618 });
16619 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16620 }
16621 multi_buffer::Event::ExcerptsRemoved { ids } => {
16622 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16623 let buffer = self.buffer.read(cx);
16624 self.registered_buffers
16625 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16626 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16627 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16628 }
16629 multi_buffer::Event::ExcerptsEdited {
16630 excerpt_ids,
16631 buffer_ids,
16632 } => {
16633 self.display_map.update(cx, |map, cx| {
16634 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16635 });
16636 cx.emit(EditorEvent::ExcerptsEdited {
16637 ids: excerpt_ids.clone(),
16638 })
16639 }
16640 multi_buffer::Event::ExcerptsExpanded { ids } => {
16641 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16642 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16643 }
16644 multi_buffer::Event::Reparsed(buffer_id) => {
16645 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16646 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16647
16648 cx.emit(EditorEvent::Reparsed(*buffer_id));
16649 }
16650 multi_buffer::Event::DiffHunksToggled => {
16651 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16652 }
16653 multi_buffer::Event::LanguageChanged(buffer_id) => {
16654 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16655 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16656 cx.emit(EditorEvent::Reparsed(*buffer_id));
16657 cx.notify();
16658 }
16659 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16660 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16661 multi_buffer::Event::FileHandleChanged
16662 | multi_buffer::Event::Reloaded
16663 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16664 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16665 multi_buffer::Event::DiagnosticsUpdated => {
16666 self.refresh_active_diagnostics(cx);
16667 self.refresh_inline_diagnostics(true, window, cx);
16668 self.scrollbar_marker_state.dirty = true;
16669 cx.notify();
16670 }
16671 _ => {}
16672 };
16673 }
16674
16675 fn on_display_map_changed(
16676 &mut self,
16677 _: Entity<DisplayMap>,
16678 _: &mut Window,
16679 cx: &mut Context<Self>,
16680 ) {
16681 cx.notify();
16682 }
16683
16684 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16685 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16686 self.update_edit_prediction_settings(cx);
16687 self.refresh_inline_completion(true, false, window, cx);
16688 self.refresh_inlay_hints(
16689 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16690 self.selections.newest_anchor().head(),
16691 &self.buffer.read(cx).snapshot(cx),
16692 cx,
16693 )),
16694 cx,
16695 );
16696
16697 let old_cursor_shape = self.cursor_shape;
16698
16699 {
16700 let editor_settings = EditorSettings::get_global(cx);
16701 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16702 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16703 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16704 self.hide_mouse_while_typing = if !matches!(self.mode, EditorMode::SingleLine { .. }) {
16705 editor_settings.hide_mouse_while_typing.unwrap_or(true)
16706 } else {
16707 false
16708 };
16709
16710 if !self.hide_mouse_while_typing {
16711 self.mouse_cursor_hidden = false;
16712 }
16713 }
16714
16715 if old_cursor_shape != self.cursor_shape {
16716 cx.emit(EditorEvent::CursorShapeChanged);
16717 }
16718
16719 let project_settings = ProjectSettings::get_global(cx);
16720 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16721
16722 if self.mode == EditorMode::Full {
16723 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16724 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16725 if self.show_inline_diagnostics != show_inline_diagnostics {
16726 self.show_inline_diagnostics = show_inline_diagnostics;
16727 self.refresh_inline_diagnostics(false, window, cx);
16728 }
16729
16730 if self.git_blame_inline_enabled != inline_blame_enabled {
16731 self.toggle_git_blame_inline_internal(false, window, cx);
16732 }
16733 }
16734
16735 cx.notify();
16736 }
16737
16738 pub fn set_searchable(&mut self, searchable: bool) {
16739 self.searchable = searchable;
16740 }
16741
16742 pub fn searchable(&self) -> bool {
16743 self.searchable
16744 }
16745
16746 fn open_proposed_changes_editor(
16747 &mut self,
16748 _: &OpenProposedChangesEditor,
16749 window: &mut Window,
16750 cx: &mut Context<Self>,
16751 ) {
16752 let Some(workspace) = self.workspace() else {
16753 cx.propagate();
16754 return;
16755 };
16756
16757 let selections = self.selections.all::<usize>(cx);
16758 let multi_buffer = self.buffer.read(cx);
16759 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16760 let mut new_selections_by_buffer = HashMap::default();
16761 for selection in selections {
16762 for (buffer, range, _) in
16763 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16764 {
16765 let mut range = range.to_point(buffer);
16766 range.start.column = 0;
16767 range.end.column = buffer.line_len(range.end.row);
16768 new_selections_by_buffer
16769 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16770 .or_insert(Vec::new())
16771 .push(range)
16772 }
16773 }
16774
16775 let proposed_changes_buffers = new_selections_by_buffer
16776 .into_iter()
16777 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16778 .collect::<Vec<_>>();
16779 let proposed_changes_editor = cx.new(|cx| {
16780 ProposedChangesEditor::new(
16781 "Proposed changes",
16782 proposed_changes_buffers,
16783 self.project.clone(),
16784 window,
16785 cx,
16786 )
16787 });
16788
16789 window.defer(cx, move |window, cx| {
16790 workspace.update(cx, |workspace, cx| {
16791 workspace.active_pane().update(cx, |pane, cx| {
16792 pane.add_item(
16793 Box::new(proposed_changes_editor),
16794 true,
16795 true,
16796 None,
16797 window,
16798 cx,
16799 );
16800 });
16801 });
16802 });
16803 }
16804
16805 pub fn open_excerpts_in_split(
16806 &mut self,
16807 _: &OpenExcerptsSplit,
16808 window: &mut Window,
16809 cx: &mut Context<Self>,
16810 ) {
16811 self.open_excerpts_common(None, true, window, cx)
16812 }
16813
16814 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16815 self.open_excerpts_common(None, false, window, cx)
16816 }
16817
16818 fn open_excerpts_common(
16819 &mut self,
16820 jump_data: Option<JumpData>,
16821 split: bool,
16822 window: &mut Window,
16823 cx: &mut Context<Self>,
16824 ) {
16825 let Some(workspace) = self.workspace() else {
16826 cx.propagate();
16827 return;
16828 };
16829
16830 if self.buffer.read(cx).is_singleton() {
16831 cx.propagate();
16832 return;
16833 }
16834
16835 let mut new_selections_by_buffer = HashMap::default();
16836 match &jump_data {
16837 Some(JumpData::MultiBufferPoint {
16838 excerpt_id,
16839 position,
16840 anchor,
16841 line_offset_from_top,
16842 }) => {
16843 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16844 if let Some(buffer) = multi_buffer_snapshot
16845 .buffer_id_for_excerpt(*excerpt_id)
16846 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16847 {
16848 let buffer_snapshot = buffer.read(cx).snapshot();
16849 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16850 language::ToPoint::to_point(anchor, &buffer_snapshot)
16851 } else {
16852 buffer_snapshot.clip_point(*position, Bias::Left)
16853 };
16854 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16855 new_selections_by_buffer.insert(
16856 buffer,
16857 (
16858 vec![jump_to_offset..jump_to_offset],
16859 Some(*line_offset_from_top),
16860 ),
16861 );
16862 }
16863 }
16864 Some(JumpData::MultiBufferRow {
16865 row,
16866 line_offset_from_top,
16867 }) => {
16868 let point = MultiBufferPoint::new(row.0, 0);
16869 if let Some((buffer, buffer_point, _)) =
16870 self.buffer.read(cx).point_to_buffer_point(point, cx)
16871 {
16872 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16873 new_selections_by_buffer
16874 .entry(buffer)
16875 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16876 .0
16877 .push(buffer_offset..buffer_offset)
16878 }
16879 }
16880 None => {
16881 let selections = self.selections.all::<usize>(cx);
16882 let multi_buffer = self.buffer.read(cx);
16883 for selection in selections {
16884 for (snapshot, range, _, anchor) in multi_buffer
16885 .snapshot(cx)
16886 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16887 {
16888 if let Some(anchor) = anchor {
16889 // selection is in a deleted hunk
16890 let Some(buffer_id) = anchor.buffer_id else {
16891 continue;
16892 };
16893 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16894 continue;
16895 };
16896 let offset = text::ToOffset::to_offset(
16897 &anchor.text_anchor,
16898 &buffer_handle.read(cx).snapshot(),
16899 );
16900 let range = offset..offset;
16901 new_selections_by_buffer
16902 .entry(buffer_handle)
16903 .or_insert((Vec::new(), None))
16904 .0
16905 .push(range)
16906 } else {
16907 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16908 else {
16909 continue;
16910 };
16911 new_selections_by_buffer
16912 .entry(buffer_handle)
16913 .or_insert((Vec::new(), None))
16914 .0
16915 .push(range)
16916 }
16917 }
16918 }
16919 }
16920 }
16921
16922 if new_selections_by_buffer.is_empty() {
16923 return;
16924 }
16925
16926 // We defer the pane interaction because we ourselves are a workspace item
16927 // and activating a new item causes the pane to call a method on us reentrantly,
16928 // which panics if we're on the stack.
16929 window.defer(cx, move |window, cx| {
16930 workspace.update(cx, |workspace, cx| {
16931 let pane = if split {
16932 workspace.adjacent_pane(window, cx)
16933 } else {
16934 workspace.active_pane().clone()
16935 };
16936
16937 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16938 let editor = buffer
16939 .read(cx)
16940 .file()
16941 .is_none()
16942 .then(|| {
16943 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16944 // so `workspace.open_project_item` will never find them, always opening a new editor.
16945 // Instead, we try to activate the existing editor in the pane first.
16946 let (editor, pane_item_index) =
16947 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16948 let editor = item.downcast::<Editor>()?;
16949 let singleton_buffer =
16950 editor.read(cx).buffer().read(cx).as_singleton()?;
16951 if singleton_buffer == buffer {
16952 Some((editor, i))
16953 } else {
16954 None
16955 }
16956 })?;
16957 pane.update(cx, |pane, cx| {
16958 pane.activate_item(pane_item_index, true, true, window, cx)
16959 });
16960 Some(editor)
16961 })
16962 .flatten()
16963 .unwrap_or_else(|| {
16964 workspace.open_project_item::<Self>(
16965 pane.clone(),
16966 buffer,
16967 true,
16968 true,
16969 window,
16970 cx,
16971 )
16972 });
16973
16974 editor.update(cx, |editor, cx| {
16975 let autoscroll = match scroll_offset {
16976 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16977 None => Autoscroll::newest(),
16978 };
16979 let nav_history = editor.nav_history.take();
16980 editor.change_selections(Some(autoscroll), window, cx, |s| {
16981 s.select_ranges(ranges);
16982 });
16983 editor.nav_history = nav_history;
16984 });
16985 }
16986 })
16987 });
16988 }
16989
16990 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16991 let snapshot = self.buffer.read(cx).read(cx);
16992 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16993 Some(
16994 ranges
16995 .iter()
16996 .map(move |range| {
16997 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16998 })
16999 .collect(),
17000 )
17001 }
17002
17003 fn selection_replacement_ranges(
17004 &self,
17005 range: Range<OffsetUtf16>,
17006 cx: &mut App,
17007 ) -> Vec<Range<OffsetUtf16>> {
17008 let selections = self.selections.all::<OffsetUtf16>(cx);
17009 let newest_selection = selections
17010 .iter()
17011 .max_by_key(|selection| selection.id)
17012 .unwrap();
17013 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
17014 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
17015 let snapshot = self.buffer.read(cx).read(cx);
17016 selections
17017 .into_iter()
17018 .map(|mut selection| {
17019 selection.start.0 =
17020 (selection.start.0 as isize).saturating_add(start_delta) as usize;
17021 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
17022 snapshot.clip_offset_utf16(selection.start, Bias::Left)
17023 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
17024 })
17025 .collect()
17026 }
17027
17028 fn report_editor_event(
17029 &self,
17030 event_type: &'static str,
17031 file_extension: Option<String>,
17032 cx: &App,
17033 ) {
17034 if cfg!(any(test, feature = "test-support")) {
17035 return;
17036 }
17037
17038 let Some(project) = &self.project else { return };
17039
17040 // If None, we are in a file without an extension
17041 let file = self
17042 .buffer
17043 .read(cx)
17044 .as_singleton()
17045 .and_then(|b| b.read(cx).file());
17046 let file_extension = file_extension.or(file
17047 .as_ref()
17048 .and_then(|file| Path::new(file.file_name(cx)).extension())
17049 .and_then(|e| e.to_str())
17050 .map(|a| a.to_string()));
17051
17052 let vim_mode = cx
17053 .global::<SettingsStore>()
17054 .raw_user_settings()
17055 .get("vim_mode")
17056 == Some(&serde_json::Value::Bool(true));
17057
17058 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
17059 let copilot_enabled = edit_predictions_provider
17060 == language::language_settings::EditPredictionProvider::Copilot;
17061 let copilot_enabled_for_language = self
17062 .buffer
17063 .read(cx)
17064 .language_settings(cx)
17065 .show_edit_predictions;
17066
17067 let project = project.read(cx);
17068 telemetry::event!(
17069 event_type,
17070 file_extension,
17071 vim_mode,
17072 copilot_enabled,
17073 copilot_enabled_for_language,
17074 edit_predictions_provider,
17075 is_via_ssh = project.is_via_ssh(),
17076 );
17077 }
17078
17079 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
17080 /// with each line being an array of {text, highlight} objects.
17081 fn copy_highlight_json(
17082 &mut self,
17083 _: &CopyHighlightJson,
17084 window: &mut Window,
17085 cx: &mut Context<Self>,
17086 ) {
17087 #[derive(Serialize)]
17088 struct Chunk<'a> {
17089 text: String,
17090 highlight: Option<&'a str>,
17091 }
17092
17093 let snapshot = self.buffer.read(cx).snapshot(cx);
17094 let range = self
17095 .selected_text_range(false, window, cx)
17096 .and_then(|selection| {
17097 if selection.range.is_empty() {
17098 None
17099 } else {
17100 Some(selection.range)
17101 }
17102 })
17103 .unwrap_or_else(|| 0..snapshot.len());
17104
17105 let chunks = snapshot.chunks(range, true);
17106 let mut lines = Vec::new();
17107 let mut line: VecDeque<Chunk> = VecDeque::new();
17108
17109 let Some(style) = self.style.as_ref() else {
17110 return;
17111 };
17112
17113 for chunk in chunks {
17114 let highlight = chunk
17115 .syntax_highlight_id
17116 .and_then(|id| id.name(&style.syntax));
17117 let mut chunk_lines = chunk.text.split('\n').peekable();
17118 while let Some(text) = chunk_lines.next() {
17119 let mut merged_with_last_token = false;
17120 if let Some(last_token) = line.back_mut() {
17121 if last_token.highlight == highlight {
17122 last_token.text.push_str(text);
17123 merged_with_last_token = true;
17124 }
17125 }
17126
17127 if !merged_with_last_token {
17128 line.push_back(Chunk {
17129 text: text.into(),
17130 highlight,
17131 });
17132 }
17133
17134 if chunk_lines.peek().is_some() {
17135 if line.len() > 1 && line.front().unwrap().text.is_empty() {
17136 line.pop_front();
17137 }
17138 if line.len() > 1 && line.back().unwrap().text.is_empty() {
17139 line.pop_back();
17140 }
17141
17142 lines.push(mem::take(&mut line));
17143 }
17144 }
17145 }
17146
17147 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
17148 return;
17149 };
17150 cx.write_to_clipboard(ClipboardItem::new_string(lines));
17151 }
17152
17153 pub fn open_context_menu(
17154 &mut self,
17155 _: &OpenContextMenu,
17156 window: &mut Window,
17157 cx: &mut Context<Self>,
17158 ) {
17159 self.request_autoscroll(Autoscroll::newest(), cx);
17160 let position = self.selections.newest_display(cx).start;
17161 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
17162 }
17163
17164 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
17165 &self.inlay_hint_cache
17166 }
17167
17168 pub fn replay_insert_event(
17169 &mut self,
17170 text: &str,
17171 relative_utf16_range: Option<Range<isize>>,
17172 window: &mut Window,
17173 cx: &mut Context<Self>,
17174 ) {
17175 if !self.input_enabled {
17176 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17177 return;
17178 }
17179 if let Some(relative_utf16_range) = relative_utf16_range {
17180 let selections = self.selections.all::<OffsetUtf16>(cx);
17181 self.change_selections(None, window, cx, |s| {
17182 let new_ranges = selections.into_iter().map(|range| {
17183 let start = OffsetUtf16(
17184 range
17185 .head()
17186 .0
17187 .saturating_add_signed(relative_utf16_range.start),
17188 );
17189 let end = OffsetUtf16(
17190 range
17191 .head()
17192 .0
17193 .saturating_add_signed(relative_utf16_range.end),
17194 );
17195 start..end
17196 });
17197 s.select_ranges(new_ranges);
17198 });
17199 }
17200
17201 self.handle_input(text, window, cx);
17202 }
17203
17204 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
17205 let Some(provider) = self.semantics_provider.as_ref() else {
17206 return false;
17207 };
17208
17209 let mut supports = false;
17210 self.buffer().update(cx, |this, cx| {
17211 this.for_each_buffer(|buffer| {
17212 supports |= provider.supports_inlay_hints(buffer, cx);
17213 });
17214 });
17215
17216 supports
17217 }
17218
17219 pub fn is_focused(&self, window: &Window) -> bool {
17220 self.focus_handle.is_focused(window)
17221 }
17222
17223 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17224 cx.emit(EditorEvent::Focused);
17225
17226 if let Some(descendant) = self
17227 .last_focused_descendant
17228 .take()
17229 .and_then(|descendant| descendant.upgrade())
17230 {
17231 window.focus(&descendant);
17232 } else {
17233 if let Some(blame) = self.blame.as_ref() {
17234 blame.update(cx, GitBlame::focus)
17235 }
17236
17237 self.blink_manager.update(cx, BlinkManager::enable);
17238 self.show_cursor_names(window, cx);
17239 self.buffer.update(cx, |buffer, cx| {
17240 buffer.finalize_last_transaction(cx);
17241 if self.leader_peer_id.is_none() {
17242 buffer.set_active_selections(
17243 &self.selections.disjoint_anchors(),
17244 self.selections.line_mode,
17245 self.cursor_shape,
17246 cx,
17247 );
17248 }
17249 });
17250 }
17251 }
17252
17253 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17254 cx.emit(EditorEvent::FocusedIn)
17255 }
17256
17257 fn handle_focus_out(
17258 &mut self,
17259 event: FocusOutEvent,
17260 _window: &mut Window,
17261 cx: &mut Context<Self>,
17262 ) {
17263 if event.blurred != self.focus_handle {
17264 self.last_focused_descendant = Some(event.blurred);
17265 }
17266 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17267 }
17268
17269 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17270 self.blink_manager.update(cx, BlinkManager::disable);
17271 self.buffer
17272 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17273
17274 if let Some(blame) = self.blame.as_ref() {
17275 blame.update(cx, GitBlame::blur)
17276 }
17277 if !self.hover_state.focused(window, cx) {
17278 hide_hover(self, cx);
17279 }
17280 if !self
17281 .context_menu
17282 .borrow()
17283 .as_ref()
17284 .is_some_and(|context_menu| context_menu.focused(window, cx))
17285 {
17286 self.hide_context_menu(window, cx);
17287 }
17288 self.discard_inline_completion(false, cx);
17289 cx.emit(EditorEvent::Blurred);
17290 cx.notify();
17291 }
17292
17293 pub fn register_action<A: Action>(
17294 &mut self,
17295 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17296 ) -> Subscription {
17297 let id = self.next_editor_action_id.post_inc();
17298 let listener = Arc::new(listener);
17299 self.editor_actions.borrow_mut().insert(
17300 id,
17301 Box::new(move |window, _| {
17302 let listener = listener.clone();
17303 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17304 let action = action.downcast_ref().unwrap();
17305 if phase == DispatchPhase::Bubble {
17306 listener(action, window, cx)
17307 }
17308 })
17309 }),
17310 );
17311
17312 let editor_actions = self.editor_actions.clone();
17313 Subscription::new(move || {
17314 editor_actions.borrow_mut().remove(&id);
17315 })
17316 }
17317
17318 pub fn file_header_size(&self) -> u32 {
17319 FILE_HEADER_HEIGHT
17320 }
17321
17322 pub fn restore(
17323 &mut self,
17324 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17325 window: &mut Window,
17326 cx: &mut Context<Self>,
17327 ) {
17328 let workspace = self.workspace();
17329 let project = self.project.as_ref();
17330 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17331 let mut tasks = Vec::new();
17332 for (buffer_id, changes) in revert_changes {
17333 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17334 buffer.update(cx, |buffer, cx| {
17335 buffer.edit(
17336 changes
17337 .into_iter()
17338 .map(|(range, text)| (range, text.to_string())),
17339 None,
17340 cx,
17341 );
17342 });
17343
17344 if let Some(project) =
17345 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17346 {
17347 project.update(cx, |project, cx| {
17348 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17349 })
17350 }
17351 }
17352 }
17353 tasks
17354 });
17355 cx.spawn_in(window, async move |_, cx| {
17356 for (buffer, task) in save_tasks {
17357 let result = task.await;
17358 if result.is_err() {
17359 let Some(path) = buffer
17360 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17361 .ok()
17362 else {
17363 continue;
17364 };
17365 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17366 let Some(task) = cx
17367 .update_window_entity(&workspace, |workspace, window, cx| {
17368 workspace
17369 .open_path_preview(path, None, false, false, false, window, cx)
17370 })
17371 .ok()
17372 else {
17373 continue;
17374 };
17375 task.await.log_err();
17376 }
17377 }
17378 }
17379 })
17380 .detach();
17381 self.change_selections(None, window, cx, |selections| selections.refresh());
17382 }
17383
17384 pub fn to_pixel_point(
17385 &self,
17386 source: multi_buffer::Anchor,
17387 editor_snapshot: &EditorSnapshot,
17388 window: &mut Window,
17389 ) -> Option<gpui::Point<Pixels>> {
17390 let source_point = source.to_display_point(editor_snapshot);
17391 self.display_to_pixel_point(source_point, editor_snapshot, window)
17392 }
17393
17394 pub fn display_to_pixel_point(
17395 &self,
17396 source: DisplayPoint,
17397 editor_snapshot: &EditorSnapshot,
17398 window: &mut Window,
17399 ) -> Option<gpui::Point<Pixels>> {
17400 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17401 let text_layout_details = self.text_layout_details(window);
17402 let scroll_top = text_layout_details
17403 .scroll_anchor
17404 .scroll_position(editor_snapshot)
17405 .y;
17406
17407 if source.row().as_f32() < scroll_top.floor() {
17408 return None;
17409 }
17410 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17411 let source_y = line_height * (source.row().as_f32() - scroll_top);
17412 Some(gpui::Point::new(source_x, source_y))
17413 }
17414
17415 pub fn has_visible_completions_menu(&self) -> bool {
17416 !self.edit_prediction_preview_is_active()
17417 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17418 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17419 })
17420 }
17421
17422 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17423 self.addons
17424 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17425 }
17426
17427 pub fn unregister_addon<T: Addon>(&mut self) {
17428 self.addons.remove(&std::any::TypeId::of::<T>());
17429 }
17430
17431 pub fn addon<T: Addon>(&self) -> Option<&T> {
17432 let type_id = std::any::TypeId::of::<T>();
17433 self.addons
17434 .get(&type_id)
17435 .and_then(|item| item.to_any().downcast_ref::<T>())
17436 }
17437
17438 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17439 let text_layout_details = self.text_layout_details(window);
17440 let style = &text_layout_details.editor_style;
17441 let font_id = window.text_system().resolve_font(&style.text.font());
17442 let font_size = style.text.font_size.to_pixels(window.rem_size());
17443 let line_height = style.text.line_height_in_pixels(window.rem_size());
17444 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17445
17446 gpui::Size::new(em_width, line_height)
17447 }
17448
17449 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17450 self.load_diff_task.clone()
17451 }
17452
17453 fn read_metadata_from_db(
17454 &mut self,
17455 item_id: u64,
17456 workspace_id: WorkspaceId,
17457 window: &mut Window,
17458 cx: &mut Context<Editor>,
17459 ) {
17460 if self.is_singleton(cx)
17461 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17462 {
17463 let buffer_snapshot = OnceCell::new();
17464
17465 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17466 if !selections.is_empty() {
17467 let snapshot =
17468 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17469 self.change_selections(None, window, cx, |s| {
17470 s.select_ranges(selections.into_iter().map(|(start, end)| {
17471 snapshot.clip_offset(start, Bias::Left)
17472 ..snapshot.clip_offset(end, Bias::Right)
17473 }));
17474 });
17475 }
17476 };
17477
17478 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17479 if !folds.is_empty() {
17480 let snapshot =
17481 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17482 self.fold_ranges(
17483 folds
17484 .into_iter()
17485 .map(|(start, end)| {
17486 snapshot.clip_offset(start, Bias::Left)
17487 ..snapshot.clip_offset(end, Bias::Right)
17488 })
17489 .collect(),
17490 false,
17491 window,
17492 cx,
17493 );
17494 }
17495 }
17496 }
17497
17498 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17499 }
17500}
17501
17502fn insert_extra_newline_brackets(
17503 buffer: &MultiBufferSnapshot,
17504 range: Range<usize>,
17505 language: &language::LanguageScope,
17506) -> bool {
17507 let leading_whitespace_len = buffer
17508 .reversed_chars_at(range.start)
17509 .take_while(|c| c.is_whitespace() && *c != '\n')
17510 .map(|c| c.len_utf8())
17511 .sum::<usize>();
17512 let trailing_whitespace_len = buffer
17513 .chars_at(range.end)
17514 .take_while(|c| c.is_whitespace() && *c != '\n')
17515 .map(|c| c.len_utf8())
17516 .sum::<usize>();
17517 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17518
17519 language.brackets().any(|(pair, enabled)| {
17520 let pair_start = pair.start.trim_end();
17521 let pair_end = pair.end.trim_start();
17522
17523 enabled
17524 && pair.newline
17525 && buffer.contains_str_at(range.end, pair_end)
17526 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17527 })
17528}
17529
17530fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17531 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17532 [(buffer, range, _)] => (*buffer, range.clone()),
17533 _ => return false,
17534 };
17535 let pair = {
17536 let mut result: Option<BracketMatch> = None;
17537
17538 for pair in buffer
17539 .all_bracket_ranges(range.clone())
17540 .filter(move |pair| {
17541 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17542 })
17543 {
17544 let len = pair.close_range.end - pair.open_range.start;
17545
17546 if let Some(existing) = &result {
17547 let existing_len = existing.close_range.end - existing.open_range.start;
17548 if len > existing_len {
17549 continue;
17550 }
17551 }
17552
17553 result = Some(pair);
17554 }
17555
17556 result
17557 };
17558 let Some(pair) = pair else {
17559 return false;
17560 };
17561 pair.newline_only
17562 && buffer
17563 .chars_for_range(pair.open_range.end..range.start)
17564 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17565 .all(|c| c.is_whitespace() && c != '\n')
17566}
17567
17568fn get_uncommitted_diff_for_buffer(
17569 project: &Entity<Project>,
17570 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17571 buffer: Entity<MultiBuffer>,
17572 cx: &mut App,
17573) -> Task<()> {
17574 let mut tasks = Vec::new();
17575 project.update(cx, |project, cx| {
17576 for buffer in buffers {
17577 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17578 }
17579 });
17580 cx.spawn(async move |cx| {
17581 let diffs = future::join_all(tasks).await;
17582 buffer
17583 .update(cx, |buffer, cx| {
17584 for diff in diffs.into_iter().flatten() {
17585 buffer.add_diff(diff, cx);
17586 }
17587 })
17588 .ok();
17589 })
17590}
17591
17592fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17593 let tab_size = tab_size.get() as usize;
17594 let mut width = offset;
17595
17596 for ch in text.chars() {
17597 width += if ch == '\t' {
17598 tab_size - (width % tab_size)
17599 } else {
17600 1
17601 };
17602 }
17603
17604 width - offset
17605}
17606
17607#[cfg(test)]
17608mod tests {
17609 use super::*;
17610
17611 #[test]
17612 fn test_string_size_with_expanded_tabs() {
17613 let nz = |val| NonZeroU32::new(val).unwrap();
17614 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17615 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17616 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17617 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17618 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17619 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17620 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17621 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17622 }
17623}
17624
17625/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17626struct WordBreakingTokenizer<'a> {
17627 input: &'a str,
17628}
17629
17630impl<'a> WordBreakingTokenizer<'a> {
17631 fn new(input: &'a str) -> Self {
17632 Self { input }
17633 }
17634}
17635
17636fn is_char_ideographic(ch: char) -> bool {
17637 use unicode_script::Script::*;
17638 use unicode_script::UnicodeScript;
17639 matches!(ch.script(), Han | Tangut | Yi)
17640}
17641
17642fn is_grapheme_ideographic(text: &str) -> bool {
17643 text.chars().any(is_char_ideographic)
17644}
17645
17646fn is_grapheme_whitespace(text: &str) -> bool {
17647 text.chars().any(|x| x.is_whitespace())
17648}
17649
17650fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17651 text.chars().next().map_or(false, |ch| {
17652 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17653 })
17654}
17655
17656#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17657enum WordBreakToken<'a> {
17658 Word { token: &'a str, grapheme_len: usize },
17659 InlineWhitespace { token: &'a str, grapheme_len: usize },
17660 Newline,
17661}
17662
17663impl<'a> Iterator for WordBreakingTokenizer<'a> {
17664 /// Yields a span, the count of graphemes in the token, and whether it was
17665 /// whitespace. Note that it also breaks at word boundaries.
17666 type Item = WordBreakToken<'a>;
17667
17668 fn next(&mut self) -> Option<Self::Item> {
17669 use unicode_segmentation::UnicodeSegmentation;
17670 if self.input.is_empty() {
17671 return None;
17672 }
17673
17674 let mut iter = self.input.graphemes(true).peekable();
17675 let mut offset = 0;
17676 let mut grapheme_len = 0;
17677 if let Some(first_grapheme) = iter.next() {
17678 let is_newline = first_grapheme == "\n";
17679 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17680 offset += first_grapheme.len();
17681 grapheme_len += 1;
17682 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17683 if let Some(grapheme) = iter.peek().copied() {
17684 if should_stay_with_preceding_ideograph(grapheme) {
17685 offset += grapheme.len();
17686 grapheme_len += 1;
17687 }
17688 }
17689 } else {
17690 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17691 let mut next_word_bound = words.peek().copied();
17692 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17693 next_word_bound = words.next();
17694 }
17695 while let Some(grapheme) = iter.peek().copied() {
17696 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17697 break;
17698 };
17699 if is_grapheme_whitespace(grapheme) != is_whitespace
17700 || (grapheme == "\n") != is_newline
17701 {
17702 break;
17703 };
17704 offset += grapheme.len();
17705 grapheme_len += 1;
17706 iter.next();
17707 }
17708 }
17709 let token = &self.input[..offset];
17710 self.input = &self.input[offset..];
17711 if token == "\n" {
17712 Some(WordBreakToken::Newline)
17713 } else if is_whitespace {
17714 Some(WordBreakToken::InlineWhitespace {
17715 token,
17716 grapheme_len,
17717 })
17718 } else {
17719 Some(WordBreakToken::Word {
17720 token,
17721 grapheme_len,
17722 })
17723 }
17724 } else {
17725 None
17726 }
17727 }
17728}
17729
17730#[test]
17731fn test_word_breaking_tokenizer() {
17732 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17733 ("", &[]),
17734 (" ", &[whitespace(" ", 2)]),
17735 ("Ʒ", &[word("Ʒ", 1)]),
17736 ("Ǽ", &[word("Ǽ", 1)]),
17737 ("⋑", &[word("⋑", 1)]),
17738 ("⋑⋑", &[word("⋑⋑", 2)]),
17739 (
17740 "原理,进而",
17741 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17742 ),
17743 (
17744 "hello world",
17745 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17746 ),
17747 (
17748 "hello, world",
17749 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17750 ),
17751 (
17752 " hello world",
17753 &[
17754 whitespace(" ", 2),
17755 word("hello", 5),
17756 whitespace(" ", 1),
17757 word("world", 5),
17758 ],
17759 ),
17760 (
17761 "这是什么 \n 钢笔",
17762 &[
17763 word("这", 1),
17764 word("是", 1),
17765 word("什", 1),
17766 word("么", 1),
17767 whitespace(" ", 1),
17768 newline(),
17769 whitespace(" ", 1),
17770 word("钢", 1),
17771 word("笔", 1),
17772 ],
17773 ),
17774 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17775 ];
17776
17777 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17778 WordBreakToken::Word {
17779 token,
17780 grapheme_len,
17781 }
17782 }
17783
17784 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17785 WordBreakToken::InlineWhitespace {
17786 token,
17787 grapheme_len,
17788 }
17789 }
17790
17791 fn newline() -> WordBreakToken<'static> {
17792 WordBreakToken::Newline
17793 }
17794
17795 for (input, result) in tests {
17796 assert_eq!(
17797 WordBreakingTokenizer::new(input)
17798 .collect::<Vec<_>>()
17799 .as_slice(),
17800 *result,
17801 );
17802 }
17803}
17804
17805fn wrap_with_prefix(
17806 line_prefix: String,
17807 unwrapped_text: String,
17808 wrap_column: usize,
17809 tab_size: NonZeroU32,
17810 preserve_existing_whitespace: bool,
17811) -> String {
17812 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17813 let mut wrapped_text = String::new();
17814 let mut current_line = line_prefix.clone();
17815
17816 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17817 let mut current_line_len = line_prefix_len;
17818 let mut in_whitespace = false;
17819 for token in tokenizer {
17820 let have_preceding_whitespace = in_whitespace;
17821 match token {
17822 WordBreakToken::Word {
17823 token,
17824 grapheme_len,
17825 } => {
17826 in_whitespace = false;
17827 if current_line_len + grapheme_len > wrap_column
17828 && current_line_len != line_prefix_len
17829 {
17830 wrapped_text.push_str(current_line.trim_end());
17831 wrapped_text.push('\n');
17832 current_line.truncate(line_prefix.len());
17833 current_line_len = line_prefix_len;
17834 }
17835 current_line.push_str(token);
17836 current_line_len += grapheme_len;
17837 }
17838 WordBreakToken::InlineWhitespace {
17839 mut token,
17840 mut grapheme_len,
17841 } => {
17842 in_whitespace = true;
17843 if have_preceding_whitespace && !preserve_existing_whitespace {
17844 continue;
17845 }
17846 if !preserve_existing_whitespace {
17847 token = " ";
17848 grapheme_len = 1;
17849 }
17850 if current_line_len + grapheme_len > wrap_column {
17851 wrapped_text.push_str(current_line.trim_end());
17852 wrapped_text.push('\n');
17853 current_line.truncate(line_prefix.len());
17854 current_line_len = line_prefix_len;
17855 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17856 current_line.push_str(token);
17857 current_line_len += grapheme_len;
17858 }
17859 }
17860 WordBreakToken::Newline => {
17861 in_whitespace = true;
17862 if preserve_existing_whitespace {
17863 wrapped_text.push_str(current_line.trim_end());
17864 wrapped_text.push('\n');
17865 current_line.truncate(line_prefix.len());
17866 current_line_len = line_prefix_len;
17867 } else if have_preceding_whitespace {
17868 continue;
17869 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17870 {
17871 wrapped_text.push_str(current_line.trim_end());
17872 wrapped_text.push('\n');
17873 current_line.truncate(line_prefix.len());
17874 current_line_len = line_prefix_len;
17875 } else if current_line_len != line_prefix_len {
17876 current_line.push(' ');
17877 current_line_len += 1;
17878 }
17879 }
17880 }
17881 }
17882
17883 if !current_line.is_empty() {
17884 wrapped_text.push_str(¤t_line);
17885 }
17886 wrapped_text
17887}
17888
17889#[test]
17890fn test_wrap_with_prefix() {
17891 assert_eq!(
17892 wrap_with_prefix(
17893 "# ".to_string(),
17894 "abcdefg".to_string(),
17895 4,
17896 NonZeroU32::new(4).unwrap(),
17897 false,
17898 ),
17899 "# abcdefg"
17900 );
17901 assert_eq!(
17902 wrap_with_prefix(
17903 "".to_string(),
17904 "\thello world".to_string(),
17905 8,
17906 NonZeroU32::new(4).unwrap(),
17907 false,
17908 ),
17909 "hello\nworld"
17910 );
17911 assert_eq!(
17912 wrap_with_prefix(
17913 "// ".to_string(),
17914 "xx \nyy zz aa bb cc".to_string(),
17915 12,
17916 NonZeroU32::new(4).unwrap(),
17917 false,
17918 ),
17919 "// xx yy zz\n// aa bb cc"
17920 );
17921 assert_eq!(
17922 wrap_with_prefix(
17923 String::new(),
17924 "这是什么 \n 钢笔".to_string(),
17925 3,
17926 NonZeroU32::new(4).unwrap(),
17927 false,
17928 ),
17929 "这是什\n么 钢\n笔"
17930 );
17931}
17932
17933pub trait CollaborationHub {
17934 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17935 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17936 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17937}
17938
17939impl CollaborationHub for Entity<Project> {
17940 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17941 self.read(cx).collaborators()
17942 }
17943
17944 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17945 self.read(cx).user_store().read(cx).participant_indices()
17946 }
17947
17948 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17949 let this = self.read(cx);
17950 let user_ids = this.collaborators().values().map(|c| c.user_id);
17951 this.user_store().read_with(cx, |user_store, cx| {
17952 user_store.participant_names(user_ids, cx)
17953 })
17954 }
17955}
17956
17957pub trait SemanticsProvider {
17958 fn hover(
17959 &self,
17960 buffer: &Entity<Buffer>,
17961 position: text::Anchor,
17962 cx: &mut App,
17963 ) -> Option<Task<Vec<project::Hover>>>;
17964
17965 fn inlay_hints(
17966 &self,
17967 buffer_handle: Entity<Buffer>,
17968 range: Range<text::Anchor>,
17969 cx: &mut App,
17970 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17971
17972 fn resolve_inlay_hint(
17973 &self,
17974 hint: InlayHint,
17975 buffer_handle: Entity<Buffer>,
17976 server_id: LanguageServerId,
17977 cx: &mut App,
17978 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17979
17980 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17981
17982 fn document_highlights(
17983 &self,
17984 buffer: &Entity<Buffer>,
17985 position: text::Anchor,
17986 cx: &mut App,
17987 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17988
17989 fn definitions(
17990 &self,
17991 buffer: &Entity<Buffer>,
17992 position: text::Anchor,
17993 kind: GotoDefinitionKind,
17994 cx: &mut App,
17995 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17996
17997 fn range_for_rename(
17998 &self,
17999 buffer: &Entity<Buffer>,
18000 position: text::Anchor,
18001 cx: &mut App,
18002 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
18003
18004 fn perform_rename(
18005 &self,
18006 buffer: &Entity<Buffer>,
18007 position: text::Anchor,
18008 new_name: String,
18009 cx: &mut App,
18010 ) -> Option<Task<Result<ProjectTransaction>>>;
18011}
18012
18013pub trait CompletionProvider {
18014 fn completions(
18015 &self,
18016 excerpt_id: ExcerptId,
18017 buffer: &Entity<Buffer>,
18018 buffer_position: text::Anchor,
18019 trigger: CompletionContext,
18020 window: &mut Window,
18021 cx: &mut Context<Editor>,
18022 ) -> Task<Result<Option<Vec<Completion>>>>;
18023
18024 fn resolve_completions(
18025 &self,
18026 buffer: Entity<Buffer>,
18027 completion_indices: Vec<usize>,
18028 completions: Rc<RefCell<Box<[Completion]>>>,
18029 cx: &mut Context<Editor>,
18030 ) -> Task<Result<bool>>;
18031
18032 fn apply_additional_edits_for_completion(
18033 &self,
18034 _buffer: Entity<Buffer>,
18035 _completions: Rc<RefCell<Box<[Completion]>>>,
18036 _completion_index: usize,
18037 _push_to_history: bool,
18038 _cx: &mut Context<Editor>,
18039 ) -> Task<Result<Option<language::Transaction>>> {
18040 Task::ready(Ok(None))
18041 }
18042
18043 fn is_completion_trigger(
18044 &self,
18045 buffer: &Entity<Buffer>,
18046 position: language::Anchor,
18047 text: &str,
18048 trigger_in_words: bool,
18049 cx: &mut Context<Editor>,
18050 ) -> bool;
18051
18052 fn sort_completions(&self) -> bool {
18053 true
18054 }
18055
18056 fn filter_completions(&self) -> bool {
18057 true
18058 }
18059}
18060
18061pub trait CodeActionProvider {
18062 fn id(&self) -> Arc<str>;
18063
18064 fn code_actions(
18065 &self,
18066 buffer: &Entity<Buffer>,
18067 range: Range<text::Anchor>,
18068 window: &mut Window,
18069 cx: &mut App,
18070 ) -> Task<Result<Vec<CodeAction>>>;
18071
18072 fn apply_code_action(
18073 &self,
18074 buffer_handle: Entity<Buffer>,
18075 action: CodeAction,
18076 excerpt_id: ExcerptId,
18077 push_to_history: bool,
18078 window: &mut Window,
18079 cx: &mut App,
18080 ) -> Task<Result<ProjectTransaction>>;
18081}
18082
18083impl CodeActionProvider for Entity<Project> {
18084 fn id(&self) -> Arc<str> {
18085 "project".into()
18086 }
18087
18088 fn code_actions(
18089 &self,
18090 buffer: &Entity<Buffer>,
18091 range: Range<text::Anchor>,
18092 _window: &mut Window,
18093 cx: &mut App,
18094 ) -> Task<Result<Vec<CodeAction>>> {
18095 self.update(cx, |project, cx| {
18096 let code_lens = project.code_lens(buffer, range.clone(), cx);
18097 let code_actions = project.code_actions(buffer, range, None, cx);
18098 cx.background_spawn(async move {
18099 let (code_lens, code_actions) = join(code_lens, code_actions).await;
18100 Ok(code_lens
18101 .context("code lens fetch")?
18102 .into_iter()
18103 .chain(code_actions.context("code action fetch")?)
18104 .collect())
18105 })
18106 })
18107 }
18108
18109 fn apply_code_action(
18110 &self,
18111 buffer_handle: Entity<Buffer>,
18112 action: CodeAction,
18113 _excerpt_id: ExcerptId,
18114 push_to_history: bool,
18115 _window: &mut Window,
18116 cx: &mut App,
18117 ) -> Task<Result<ProjectTransaction>> {
18118 self.update(cx, |project, cx| {
18119 project.apply_code_action(buffer_handle, action, push_to_history, cx)
18120 })
18121 }
18122}
18123
18124fn snippet_completions(
18125 project: &Project,
18126 buffer: &Entity<Buffer>,
18127 buffer_position: text::Anchor,
18128 cx: &mut App,
18129) -> Task<Result<Vec<Completion>>> {
18130 let language = buffer.read(cx).language_at(buffer_position);
18131 let language_name = language.as_ref().map(|language| language.lsp_id());
18132 let snippet_store = project.snippets().read(cx);
18133 let snippets = snippet_store.snippets_for(language_name, cx);
18134
18135 if snippets.is_empty() {
18136 return Task::ready(Ok(vec![]));
18137 }
18138 let snapshot = buffer.read(cx).text_snapshot();
18139 let chars: String = snapshot
18140 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
18141 .collect();
18142
18143 let scope = language.map(|language| language.default_scope());
18144 let executor = cx.background_executor().clone();
18145
18146 cx.background_spawn(async move {
18147 let classifier = CharClassifier::new(scope).for_completion(true);
18148 let mut last_word = chars
18149 .chars()
18150 .take_while(|c| classifier.is_word(*c))
18151 .collect::<String>();
18152 last_word = last_word.chars().rev().collect();
18153
18154 if last_word.is_empty() {
18155 return Ok(vec![]);
18156 }
18157
18158 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
18159 let to_lsp = |point: &text::Anchor| {
18160 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
18161 point_to_lsp(end)
18162 };
18163 let lsp_end = to_lsp(&buffer_position);
18164
18165 let candidates = snippets
18166 .iter()
18167 .enumerate()
18168 .flat_map(|(ix, snippet)| {
18169 snippet
18170 .prefix
18171 .iter()
18172 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
18173 })
18174 .collect::<Vec<StringMatchCandidate>>();
18175
18176 let mut matches = fuzzy::match_strings(
18177 &candidates,
18178 &last_word,
18179 last_word.chars().any(|c| c.is_uppercase()),
18180 100,
18181 &Default::default(),
18182 executor,
18183 )
18184 .await;
18185
18186 // Remove all candidates where the query's start does not match the start of any word in the candidate
18187 if let Some(query_start) = last_word.chars().next() {
18188 matches.retain(|string_match| {
18189 split_words(&string_match.string).any(|word| {
18190 // Check that the first codepoint of the word as lowercase matches the first
18191 // codepoint of the query as lowercase
18192 word.chars()
18193 .flat_map(|codepoint| codepoint.to_lowercase())
18194 .zip(query_start.to_lowercase())
18195 .all(|(word_cp, query_cp)| word_cp == query_cp)
18196 })
18197 });
18198 }
18199
18200 let matched_strings = matches
18201 .into_iter()
18202 .map(|m| m.string)
18203 .collect::<HashSet<_>>();
18204
18205 let result: Vec<Completion> = snippets
18206 .into_iter()
18207 .filter_map(|snippet| {
18208 let matching_prefix = snippet
18209 .prefix
18210 .iter()
18211 .find(|prefix| matched_strings.contains(*prefix))?;
18212 let start = as_offset - last_word.len();
18213 let start = snapshot.anchor_before(start);
18214 let range = start..buffer_position;
18215 let lsp_start = to_lsp(&start);
18216 let lsp_range = lsp::Range {
18217 start: lsp_start,
18218 end: lsp_end,
18219 };
18220 Some(Completion {
18221 old_range: range,
18222 new_text: snippet.body.clone(),
18223 source: CompletionSource::Lsp {
18224 server_id: LanguageServerId(usize::MAX),
18225 resolved: true,
18226 lsp_completion: Box::new(lsp::CompletionItem {
18227 label: snippet.prefix.first().unwrap().clone(),
18228 kind: Some(CompletionItemKind::SNIPPET),
18229 label_details: snippet.description.as_ref().map(|description| {
18230 lsp::CompletionItemLabelDetails {
18231 detail: Some(description.clone()),
18232 description: None,
18233 }
18234 }),
18235 insert_text_format: Some(InsertTextFormat::SNIPPET),
18236 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18237 lsp::InsertReplaceEdit {
18238 new_text: snippet.body.clone(),
18239 insert: lsp_range,
18240 replace: lsp_range,
18241 },
18242 )),
18243 filter_text: Some(snippet.body.clone()),
18244 sort_text: Some(char::MAX.to_string()),
18245 ..lsp::CompletionItem::default()
18246 }),
18247 lsp_defaults: None,
18248 },
18249 label: CodeLabel {
18250 text: matching_prefix.clone(),
18251 runs: Vec::new(),
18252 filter_range: 0..matching_prefix.len(),
18253 },
18254 icon_path: None,
18255 documentation: snippet
18256 .description
18257 .clone()
18258 .map(|description| CompletionDocumentation::SingleLine(description.into())),
18259 confirm: None,
18260 })
18261 })
18262 .collect();
18263
18264 Ok(result)
18265 })
18266}
18267
18268impl CompletionProvider for Entity<Project> {
18269 fn completions(
18270 &self,
18271 _excerpt_id: ExcerptId,
18272 buffer: &Entity<Buffer>,
18273 buffer_position: text::Anchor,
18274 options: CompletionContext,
18275 _window: &mut Window,
18276 cx: &mut Context<Editor>,
18277 ) -> Task<Result<Option<Vec<Completion>>>> {
18278 self.update(cx, |project, cx| {
18279 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18280 let project_completions = project.completions(buffer, buffer_position, options, cx);
18281 cx.background_spawn(async move {
18282 let snippets_completions = snippets.await?;
18283 match project_completions.await? {
18284 Some(mut completions) => {
18285 completions.extend(snippets_completions);
18286 Ok(Some(completions))
18287 }
18288 None => {
18289 if snippets_completions.is_empty() {
18290 Ok(None)
18291 } else {
18292 Ok(Some(snippets_completions))
18293 }
18294 }
18295 }
18296 })
18297 })
18298 }
18299
18300 fn resolve_completions(
18301 &self,
18302 buffer: Entity<Buffer>,
18303 completion_indices: Vec<usize>,
18304 completions: Rc<RefCell<Box<[Completion]>>>,
18305 cx: &mut Context<Editor>,
18306 ) -> Task<Result<bool>> {
18307 self.update(cx, |project, cx| {
18308 project.lsp_store().update(cx, |lsp_store, cx| {
18309 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18310 })
18311 })
18312 }
18313
18314 fn apply_additional_edits_for_completion(
18315 &self,
18316 buffer: Entity<Buffer>,
18317 completions: Rc<RefCell<Box<[Completion]>>>,
18318 completion_index: usize,
18319 push_to_history: bool,
18320 cx: &mut Context<Editor>,
18321 ) -> Task<Result<Option<language::Transaction>>> {
18322 self.update(cx, |project, cx| {
18323 project.lsp_store().update(cx, |lsp_store, cx| {
18324 lsp_store.apply_additional_edits_for_completion(
18325 buffer,
18326 completions,
18327 completion_index,
18328 push_to_history,
18329 cx,
18330 )
18331 })
18332 })
18333 }
18334
18335 fn is_completion_trigger(
18336 &self,
18337 buffer: &Entity<Buffer>,
18338 position: language::Anchor,
18339 text: &str,
18340 trigger_in_words: bool,
18341 cx: &mut Context<Editor>,
18342 ) -> bool {
18343 let mut chars = text.chars();
18344 let char = if let Some(char) = chars.next() {
18345 char
18346 } else {
18347 return false;
18348 };
18349 if chars.next().is_some() {
18350 return false;
18351 }
18352
18353 let buffer = buffer.read(cx);
18354 let snapshot = buffer.snapshot();
18355 if !snapshot.settings_at(position, cx).show_completions_on_input {
18356 return false;
18357 }
18358 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18359 if trigger_in_words && classifier.is_word(char) {
18360 return true;
18361 }
18362
18363 buffer.completion_triggers().contains(text)
18364 }
18365}
18366
18367impl SemanticsProvider for Entity<Project> {
18368 fn hover(
18369 &self,
18370 buffer: &Entity<Buffer>,
18371 position: text::Anchor,
18372 cx: &mut App,
18373 ) -> Option<Task<Vec<project::Hover>>> {
18374 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18375 }
18376
18377 fn document_highlights(
18378 &self,
18379 buffer: &Entity<Buffer>,
18380 position: text::Anchor,
18381 cx: &mut App,
18382 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18383 Some(self.update(cx, |project, cx| {
18384 project.document_highlights(buffer, position, cx)
18385 }))
18386 }
18387
18388 fn definitions(
18389 &self,
18390 buffer: &Entity<Buffer>,
18391 position: text::Anchor,
18392 kind: GotoDefinitionKind,
18393 cx: &mut App,
18394 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18395 Some(self.update(cx, |project, cx| match kind {
18396 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18397 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18398 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18399 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18400 }))
18401 }
18402
18403 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18404 // TODO: make this work for remote projects
18405 self.update(cx, |this, cx| {
18406 buffer.update(cx, |buffer, cx| {
18407 this.any_language_server_supports_inlay_hints(buffer, cx)
18408 })
18409 })
18410 }
18411
18412 fn inlay_hints(
18413 &self,
18414 buffer_handle: Entity<Buffer>,
18415 range: Range<text::Anchor>,
18416 cx: &mut App,
18417 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18418 Some(self.update(cx, |project, cx| {
18419 project.inlay_hints(buffer_handle, range, cx)
18420 }))
18421 }
18422
18423 fn resolve_inlay_hint(
18424 &self,
18425 hint: InlayHint,
18426 buffer_handle: Entity<Buffer>,
18427 server_id: LanguageServerId,
18428 cx: &mut App,
18429 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18430 Some(self.update(cx, |project, cx| {
18431 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18432 }))
18433 }
18434
18435 fn range_for_rename(
18436 &self,
18437 buffer: &Entity<Buffer>,
18438 position: text::Anchor,
18439 cx: &mut App,
18440 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18441 Some(self.update(cx, |project, cx| {
18442 let buffer = buffer.clone();
18443 let task = project.prepare_rename(buffer.clone(), position, cx);
18444 cx.spawn(async move |_, cx| {
18445 Ok(match task.await? {
18446 PrepareRenameResponse::Success(range) => Some(range),
18447 PrepareRenameResponse::InvalidPosition => None,
18448 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18449 // Fallback on using TreeSitter info to determine identifier range
18450 buffer.update(cx, |buffer, _| {
18451 let snapshot = buffer.snapshot();
18452 let (range, kind) = snapshot.surrounding_word(position);
18453 if kind != Some(CharKind::Word) {
18454 return None;
18455 }
18456 Some(
18457 snapshot.anchor_before(range.start)
18458 ..snapshot.anchor_after(range.end),
18459 )
18460 })?
18461 }
18462 })
18463 })
18464 }))
18465 }
18466
18467 fn perform_rename(
18468 &self,
18469 buffer: &Entity<Buffer>,
18470 position: text::Anchor,
18471 new_name: String,
18472 cx: &mut App,
18473 ) -> Option<Task<Result<ProjectTransaction>>> {
18474 Some(self.update(cx, |project, cx| {
18475 project.perform_rename(buffer.clone(), position, new_name, cx)
18476 }))
18477 }
18478}
18479
18480fn inlay_hint_settings(
18481 location: Anchor,
18482 snapshot: &MultiBufferSnapshot,
18483 cx: &mut Context<Editor>,
18484) -> InlayHintSettings {
18485 let file = snapshot.file_at(location);
18486 let language = snapshot.language_at(location).map(|l| l.name());
18487 language_settings(language, file, cx).inlay_hints
18488}
18489
18490fn consume_contiguous_rows(
18491 contiguous_row_selections: &mut Vec<Selection<Point>>,
18492 selection: &Selection<Point>,
18493 display_map: &DisplaySnapshot,
18494 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18495) -> (MultiBufferRow, MultiBufferRow) {
18496 contiguous_row_selections.push(selection.clone());
18497 let start_row = MultiBufferRow(selection.start.row);
18498 let mut end_row = ending_row(selection, display_map);
18499
18500 while let Some(next_selection) = selections.peek() {
18501 if next_selection.start.row <= end_row.0 {
18502 end_row = ending_row(next_selection, display_map);
18503 contiguous_row_selections.push(selections.next().unwrap().clone());
18504 } else {
18505 break;
18506 }
18507 }
18508 (start_row, end_row)
18509}
18510
18511fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18512 if next_selection.end.column > 0 || next_selection.is_empty() {
18513 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18514 } else {
18515 MultiBufferRow(next_selection.end.row)
18516 }
18517}
18518
18519impl EditorSnapshot {
18520 pub fn remote_selections_in_range<'a>(
18521 &'a self,
18522 range: &'a Range<Anchor>,
18523 collaboration_hub: &dyn CollaborationHub,
18524 cx: &'a App,
18525 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18526 let participant_names = collaboration_hub.user_names(cx);
18527 let participant_indices = collaboration_hub.user_participant_indices(cx);
18528 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18529 let collaborators_by_replica_id = collaborators_by_peer_id
18530 .iter()
18531 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18532 .collect::<HashMap<_, _>>();
18533 self.buffer_snapshot
18534 .selections_in_range(range, false)
18535 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18536 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18537 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18538 let user_name = participant_names.get(&collaborator.user_id).cloned();
18539 Some(RemoteSelection {
18540 replica_id,
18541 selection,
18542 cursor_shape,
18543 line_mode,
18544 participant_index,
18545 peer_id: collaborator.peer_id,
18546 user_name,
18547 })
18548 })
18549 }
18550
18551 pub fn hunks_for_ranges(
18552 &self,
18553 ranges: impl IntoIterator<Item = Range<Point>>,
18554 ) -> Vec<MultiBufferDiffHunk> {
18555 let mut hunks = Vec::new();
18556 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18557 HashMap::default();
18558 for query_range in ranges {
18559 let query_rows =
18560 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18561 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18562 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18563 ) {
18564 // Include deleted hunks that are adjacent to the query range, because
18565 // otherwise they would be missed.
18566 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18567 if hunk.status().is_deleted() {
18568 intersects_range |= hunk.row_range.start == query_rows.end;
18569 intersects_range |= hunk.row_range.end == query_rows.start;
18570 }
18571 if intersects_range {
18572 if !processed_buffer_rows
18573 .entry(hunk.buffer_id)
18574 .or_default()
18575 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18576 {
18577 continue;
18578 }
18579 hunks.push(hunk);
18580 }
18581 }
18582 }
18583
18584 hunks
18585 }
18586
18587 fn display_diff_hunks_for_rows<'a>(
18588 &'a self,
18589 display_rows: Range<DisplayRow>,
18590 folded_buffers: &'a HashSet<BufferId>,
18591 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18592 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18593 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18594
18595 self.buffer_snapshot
18596 .diff_hunks_in_range(buffer_start..buffer_end)
18597 .filter_map(|hunk| {
18598 if folded_buffers.contains(&hunk.buffer_id) {
18599 return None;
18600 }
18601
18602 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18603 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18604
18605 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18606 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18607
18608 let display_hunk = if hunk_display_start.column() != 0 {
18609 DisplayDiffHunk::Folded {
18610 display_row: hunk_display_start.row(),
18611 }
18612 } else {
18613 let mut end_row = hunk_display_end.row();
18614 if hunk_display_end.column() > 0 {
18615 end_row.0 += 1;
18616 }
18617 let is_created_file = hunk.is_created_file();
18618 DisplayDiffHunk::Unfolded {
18619 status: hunk.status(),
18620 diff_base_byte_range: hunk.diff_base_byte_range,
18621 display_row_range: hunk_display_start.row()..end_row,
18622 multi_buffer_range: Anchor::range_in_buffer(
18623 hunk.excerpt_id,
18624 hunk.buffer_id,
18625 hunk.buffer_range,
18626 ),
18627 is_created_file,
18628 }
18629 };
18630
18631 Some(display_hunk)
18632 })
18633 }
18634
18635 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18636 self.display_snapshot.buffer_snapshot.language_at(position)
18637 }
18638
18639 pub fn is_focused(&self) -> bool {
18640 self.is_focused
18641 }
18642
18643 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18644 self.placeholder_text.as_ref()
18645 }
18646
18647 pub fn scroll_position(&self) -> gpui::Point<f32> {
18648 self.scroll_anchor.scroll_position(&self.display_snapshot)
18649 }
18650
18651 fn gutter_dimensions(
18652 &self,
18653 font_id: FontId,
18654 font_size: Pixels,
18655 max_line_number_width: Pixels,
18656 cx: &App,
18657 ) -> Option<GutterDimensions> {
18658 if !self.show_gutter {
18659 return None;
18660 }
18661
18662 let descent = cx.text_system().descent(font_id, font_size);
18663 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18664 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18665
18666 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18667 matches!(
18668 ProjectSettings::get_global(cx).git.git_gutter,
18669 Some(GitGutterSetting::TrackedFiles)
18670 )
18671 });
18672 let gutter_settings = EditorSettings::get_global(cx).gutter;
18673 let show_line_numbers = self
18674 .show_line_numbers
18675 .unwrap_or(gutter_settings.line_numbers);
18676 let line_gutter_width = if show_line_numbers {
18677 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18678 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18679 max_line_number_width.max(min_width_for_number_on_gutter)
18680 } else {
18681 0.0.into()
18682 };
18683
18684 let show_code_actions = self
18685 .show_code_actions
18686 .unwrap_or(gutter_settings.code_actions);
18687
18688 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18689 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18690
18691 let git_blame_entries_width =
18692 self.git_blame_gutter_max_author_length
18693 .map(|max_author_length| {
18694 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18695
18696 /// The number of characters to dedicate to gaps and margins.
18697 const SPACING_WIDTH: usize = 4;
18698
18699 let max_char_count = max_author_length
18700 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18701 + ::git::SHORT_SHA_LENGTH
18702 + MAX_RELATIVE_TIMESTAMP.len()
18703 + SPACING_WIDTH;
18704
18705 em_advance * max_char_count
18706 });
18707
18708 let is_singleton = self.buffer_snapshot.is_singleton();
18709
18710 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18711 left_padding += if !is_singleton {
18712 em_width * 4.0
18713 } else if show_code_actions || show_runnables || show_breakpoints {
18714 em_width * 3.0
18715 } else if show_git_gutter && show_line_numbers {
18716 em_width * 2.0
18717 } else if show_git_gutter || show_line_numbers {
18718 em_width
18719 } else {
18720 px(0.)
18721 };
18722
18723 let shows_folds = is_singleton && gutter_settings.folds;
18724
18725 let right_padding = if shows_folds && show_line_numbers {
18726 em_width * 4.0
18727 } else if shows_folds || (!is_singleton && show_line_numbers) {
18728 em_width * 3.0
18729 } else if show_line_numbers {
18730 em_width
18731 } else {
18732 px(0.)
18733 };
18734
18735 Some(GutterDimensions {
18736 left_padding,
18737 right_padding,
18738 width: line_gutter_width + left_padding + right_padding,
18739 margin: -descent,
18740 git_blame_entries_width,
18741 })
18742 }
18743
18744 pub fn render_crease_toggle(
18745 &self,
18746 buffer_row: MultiBufferRow,
18747 row_contains_cursor: bool,
18748 editor: Entity<Editor>,
18749 window: &mut Window,
18750 cx: &mut App,
18751 ) -> Option<AnyElement> {
18752 let folded = self.is_line_folded(buffer_row);
18753 let mut is_foldable = false;
18754
18755 if let Some(crease) = self
18756 .crease_snapshot
18757 .query_row(buffer_row, &self.buffer_snapshot)
18758 {
18759 is_foldable = true;
18760 match crease {
18761 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18762 if let Some(render_toggle) = render_toggle {
18763 let toggle_callback =
18764 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18765 if folded {
18766 editor.update(cx, |editor, cx| {
18767 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18768 });
18769 } else {
18770 editor.update(cx, |editor, cx| {
18771 editor.unfold_at(
18772 &crate::UnfoldAt { buffer_row },
18773 window,
18774 cx,
18775 )
18776 });
18777 }
18778 });
18779 return Some((render_toggle)(
18780 buffer_row,
18781 folded,
18782 toggle_callback,
18783 window,
18784 cx,
18785 ));
18786 }
18787 }
18788 }
18789 }
18790
18791 is_foldable |= self.starts_indent(buffer_row);
18792
18793 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18794 Some(
18795 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18796 .toggle_state(folded)
18797 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18798 if folded {
18799 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18800 } else {
18801 this.fold_at(&FoldAt { buffer_row }, window, cx);
18802 }
18803 }))
18804 .into_any_element(),
18805 )
18806 } else {
18807 None
18808 }
18809 }
18810
18811 pub fn render_crease_trailer(
18812 &self,
18813 buffer_row: MultiBufferRow,
18814 window: &mut Window,
18815 cx: &mut App,
18816 ) -> Option<AnyElement> {
18817 let folded = self.is_line_folded(buffer_row);
18818 if let Crease::Inline { render_trailer, .. } = self
18819 .crease_snapshot
18820 .query_row(buffer_row, &self.buffer_snapshot)?
18821 {
18822 let render_trailer = render_trailer.as_ref()?;
18823 Some(render_trailer(buffer_row, folded, window, cx))
18824 } else {
18825 None
18826 }
18827 }
18828}
18829
18830impl Deref for EditorSnapshot {
18831 type Target = DisplaySnapshot;
18832
18833 fn deref(&self) -> &Self::Target {
18834 &self.display_snapshot
18835 }
18836}
18837
18838#[derive(Clone, Debug, PartialEq, Eq)]
18839pub enum EditorEvent {
18840 InputIgnored {
18841 text: Arc<str>,
18842 },
18843 InputHandled {
18844 utf16_range_to_replace: Option<Range<isize>>,
18845 text: Arc<str>,
18846 },
18847 ExcerptsAdded {
18848 buffer: Entity<Buffer>,
18849 predecessor: ExcerptId,
18850 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18851 },
18852 ExcerptsRemoved {
18853 ids: Vec<ExcerptId>,
18854 },
18855 BufferFoldToggled {
18856 ids: Vec<ExcerptId>,
18857 folded: bool,
18858 },
18859 ExcerptsEdited {
18860 ids: Vec<ExcerptId>,
18861 },
18862 ExcerptsExpanded {
18863 ids: Vec<ExcerptId>,
18864 },
18865 BufferEdited,
18866 Edited {
18867 transaction_id: clock::Lamport,
18868 },
18869 Reparsed(BufferId),
18870 Focused,
18871 FocusedIn,
18872 Blurred,
18873 DirtyChanged,
18874 Saved,
18875 TitleChanged,
18876 DiffBaseChanged,
18877 SelectionsChanged {
18878 local: bool,
18879 },
18880 ScrollPositionChanged {
18881 local: bool,
18882 autoscroll: bool,
18883 },
18884 Closed,
18885 TransactionUndone {
18886 transaction_id: clock::Lamport,
18887 },
18888 TransactionBegun {
18889 transaction_id: clock::Lamport,
18890 },
18891 Reloaded,
18892 CursorShapeChanged,
18893 PushedToNavHistory {
18894 anchor: Anchor,
18895 is_deactivate: bool,
18896 },
18897}
18898
18899impl EventEmitter<EditorEvent> for Editor {}
18900
18901impl Focusable for Editor {
18902 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18903 self.focus_handle.clone()
18904 }
18905}
18906
18907impl Render for Editor {
18908 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18909 let settings = ThemeSettings::get_global(cx);
18910
18911 let mut text_style = match self.mode {
18912 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18913 color: cx.theme().colors().editor_foreground,
18914 font_family: settings.ui_font.family.clone(),
18915 font_features: settings.ui_font.features.clone(),
18916 font_fallbacks: settings.ui_font.fallbacks.clone(),
18917 font_size: rems(0.875).into(),
18918 font_weight: settings.ui_font.weight,
18919 line_height: relative(settings.buffer_line_height.value()),
18920 ..Default::default()
18921 },
18922 EditorMode::Full => TextStyle {
18923 color: cx.theme().colors().editor_foreground,
18924 font_family: settings.buffer_font.family.clone(),
18925 font_features: settings.buffer_font.features.clone(),
18926 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18927 font_size: settings.buffer_font_size(cx).into(),
18928 font_weight: settings.buffer_font.weight,
18929 line_height: relative(settings.buffer_line_height.value()),
18930 ..Default::default()
18931 },
18932 };
18933 if let Some(text_style_refinement) = &self.text_style_refinement {
18934 text_style.refine(text_style_refinement)
18935 }
18936
18937 let background = match self.mode {
18938 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18939 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18940 EditorMode::Full => cx.theme().colors().editor_background,
18941 };
18942
18943 EditorElement::new(
18944 &cx.entity(),
18945 EditorStyle {
18946 background,
18947 local_player: cx.theme().players().local(),
18948 text: text_style,
18949 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18950 syntax: cx.theme().syntax().clone(),
18951 status: cx.theme().status().clone(),
18952 inlay_hints_style: make_inlay_hints_style(cx),
18953 inline_completion_styles: make_suggestion_styles(cx),
18954 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18955 },
18956 )
18957 }
18958}
18959
18960impl EntityInputHandler for Editor {
18961 fn text_for_range(
18962 &mut self,
18963 range_utf16: Range<usize>,
18964 adjusted_range: &mut Option<Range<usize>>,
18965 _: &mut Window,
18966 cx: &mut Context<Self>,
18967 ) -> Option<String> {
18968 let snapshot = self.buffer.read(cx).read(cx);
18969 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18970 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18971 if (start.0..end.0) != range_utf16 {
18972 adjusted_range.replace(start.0..end.0);
18973 }
18974 Some(snapshot.text_for_range(start..end).collect())
18975 }
18976
18977 fn selected_text_range(
18978 &mut self,
18979 ignore_disabled_input: bool,
18980 _: &mut Window,
18981 cx: &mut Context<Self>,
18982 ) -> Option<UTF16Selection> {
18983 // Prevent the IME menu from appearing when holding down an alphabetic key
18984 // while input is disabled.
18985 if !ignore_disabled_input && !self.input_enabled {
18986 return None;
18987 }
18988
18989 let selection = self.selections.newest::<OffsetUtf16>(cx);
18990 let range = selection.range();
18991
18992 Some(UTF16Selection {
18993 range: range.start.0..range.end.0,
18994 reversed: selection.reversed,
18995 })
18996 }
18997
18998 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18999 let snapshot = self.buffer.read(cx).read(cx);
19000 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
19001 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
19002 }
19003
19004 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
19005 self.clear_highlights::<InputComposition>(cx);
19006 self.ime_transaction.take();
19007 }
19008
19009 fn replace_text_in_range(
19010 &mut self,
19011 range_utf16: Option<Range<usize>>,
19012 text: &str,
19013 window: &mut Window,
19014 cx: &mut Context<Self>,
19015 ) {
19016 if !self.input_enabled {
19017 cx.emit(EditorEvent::InputIgnored { text: text.into() });
19018 return;
19019 }
19020
19021 self.transact(window, cx, |this, window, cx| {
19022 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
19023 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19024 Some(this.selection_replacement_ranges(range_utf16, cx))
19025 } else {
19026 this.marked_text_ranges(cx)
19027 };
19028
19029 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
19030 let newest_selection_id = this.selections.newest_anchor().id;
19031 this.selections
19032 .all::<OffsetUtf16>(cx)
19033 .iter()
19034 .zip(ranges_to_replace.iter())
19035 .find_map(|(selection, range)| {
19036 if selection.id == newest_selection_id {
19037 Some(
19038 (range.start.0 as isize - selection.head().0 as isize)
19039 ..(range.end.0 as isize - selection.head().0 as isize),
19040 )
19041 } else {
19042 None
19043 }
19044 })
19045 });
19046
19047 cx.emit(EditorEvent::InputHandled {
19048 utf16_range_to_replace: range_to_replace,
19049 text: text.into(),
19050 });
19051
19052 if let Some(new_selected_ranges) = new_selected_ranges {
19053 this.change_selections(None, window, cx, |selections| {
19054 selections.select_ranges(new_selected_ranges)
19055 });
19056 this.backspace(&Default::default(), window, cx);
19057 }
19058
19059 this.handle_input(text, window, cx);
19060 });
19061
19062 if let Some(transaction) = self.ime_transaction {
19063 self.buffer.update(cx, |buffer, cx| {
19064 buffer.group_until_transaction(transaction, cx);
19065 });
19066 }
19067
19068 self.unmark_text(window, cx);
19069 }
19070
19071 fn replace_and_mark_text_in_range(
19072 &mut self,
19073 range_utf16: Option<Range<usize>>,
19074 text: &str,
19075 new_selected_range_utf16: Option<Range<usize>>,
19076 window: &mut Window,
19077 cx: &mut Context<Self>,
19078 ) {
19079 if !self.input_enabled {
19080 return;
19081 }
19082
19083 let transaction = self.transact(window, cx, |this, window, cx| {
19084 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
19085 let snapshot = this.buffer.read(cx).read(cx);
19086 if let Some(relative_range_utf16) = range_utf16.as_ref() {
19087 for marked_range in &mut marked_ranges {
19088 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
19089 marked_range.start.0 += relative_range_utf16.start;
19090 marked_range.start =
19091 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
19092 marked_range.end =
19093 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
19094 }
19095 }
19096 Some(marked_ranges)
19097 } else if let Some(range_utf16) = range_utf16 {
19098 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19099 Some(this.selection_replacement_ranges(range_utf16, cx))
19100 } else {
19101 None
19102 };
19103
19104 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
19105 let newest_selection_id = this.selections.newest_anchor().id;
19106 this.selections
19107 .all::<OffsetUtf16>(cx)
19108 .iter()
19109 .zip(ranges_to_replace.iter())
19110 .find_map(|(selection, range)| {
19111 if selection.id == newest_selection_id {
19112 Some(
19113 (range.start.0 as isize - selection.head().0 as isize)
19114 ..(range.end.0 as isize - selection.head().0 as isize),
19115 )
19116 } else {
19117 None
19118 }
19119 })
19120 });
19121
19122 cx.emit(EditorEvent::InputHandled {
19123 utf16_range_to_replace: range_to_replace,
19124 text: text.into(),
19125 });
19126
19127 if let Some(ranges) = ranges_to_replace {
19128 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
19129 }
19130
19131 let marked_ranges = {
19132 let snapshot = this.buffer.read(cx).read(cx);
19133 this.selections
19134 .disjoint_anchors()
19135 .iter()
19136 .map(|selection| {
19137 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
19138 })
19139 .collect::<Vec<_>>()
19140 };
19141
19142 if text.is_empty() {
19143 this.unmark_text(window, cx);
19144 } else {
19145 this.highlight_text::<InputComposition>(
19146 marked_ranges.clone(),
19147 HighlightStyle {
19148 underline: Some(UnderlineStyle {
19149 thickness: px(1.),
19150 color: None,
19151 wavy: false,
19152 }),
19153 ..Default::default()
19154 },
19155 cx,
19156 );
19157 }
19158
19159 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
19160 let use_autoclose = this.use_autoclose;
19161 let use_auto_surround = this.use_auto_surround;
19162 this.set_use_autoclose(false);
19163 this.set_use_auto_surround(false);
19164 this.handle_input(text, window, cx);
19165 this.set_use_autoclose(use_autoclose);
19166 this.set_use_auto_surround(use_auto_surround);
19167
19168 if let Some(new_selected_range) = new_selected_range_utf16 {
19169 let snapshot = this.buffer.read(cx).read(cx);
19170 let new_selected_ranges = marked_ranges
19171 .into_iter()
19172 .map(|marked_range| {
19173 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
19174 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
19175 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
19176 snapshot.clip_offset_utf16(new_start, Bias::Left)
19177 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
19178 })
19179 .collect::<Vec<_>>();
19180
19181 drop(snapshot);
19182 this.change_selections(None, window, cx, |selections| {
19183 selections.select_ranges(new_selected_ranges)
19184 });
19185 }
19186 });
19187
19188 self.ime_transaction = self.ime_transaction.or(transaction);
19189 if let Some(transaction) = self.ime_transaction {
19190 self.buffer.update(cx, |buffer, cx| {
19191 buffer.group_until_transaction(transaction, cx);
19192 });
19193 }
19194
19195 if self.text_highlights::<InputComposition>(cx).is_none() {
19196 self.ime_transaction.take();
19197 }
19198 }
19199
19200 fn bounds_for_range(
19201 &mut self,
19202 range_utf16: Range<usize>,
19203 element_bounds: gpui::Bounds<Pixels>,
19204 window: &mut Window,
19205 cx: &mut Context<Self>,
19206 ) -> Option<gpui::Bounds<Pixels>> {
19207 let text_layout_details = self.text_layout_details(window);
19208 let gpui::Size {
19209 width: em_width,
19210 height: line_height,
19211 } = self.character_size(window);
19212
19213 let snapshot = self.snapshot(window, cx);
19214 let scroll_position = snapshot.scroll_position();
19215 let scroll_left = scroll_position.x * em_width;
19216
19217 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
19218 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
19219 + self.gutter_dimensions.width
19220 + self.gutter_dimensions.margin;
19221 let y = line_height * (start.row().as_f32() - scroll_position.y);
19222
19223 Some(Bounds {
19224 origin: element_bounds.origin + point(x, y),
19225 size: size(em_width, line_height),
19226 })
19227 }
19228
19229 fn character_index_for_point(
19230 &mut self,
19231 point: gpui::Point<Pixels>,
19232 _window: &mut Window,
19233 _cx: &mut Context<Self>,
19234 ) -> Option<usize> {
19235 let position_map = self.last_position_map.as_ref()?;
19236 if !position_map.text_hitbox.contains(&point) {
19237 return None;
19238 }
19239 let display_point = position_map.point_for_position(point).previous_valid;
19240 let anchor = position_map
19241 .snapshot
19242 .display_point_to_anchor(display_point, Bias::Left);
19243 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
19244 Some(utf16_offset.0)
19245 }
19246}
19247
19248trait SelectionExt {
19249 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
19250 fn spanned_rows(
19251 &self,
19252 include_end_if_at_line_start: bool,
19253 map: &DisplaySnapshot,
19254 ) -> Range<MultiBufferRow>;
19255}
19256
19257impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
19258 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
19259 let start = self
19260 .start
19261 .to_point(&map.buffer_snapshot)
19262 .to_display_point(map);
19263 let end = self
19264 .end
19265 .to_point(&map.buffer_snapshot)
19266 .to_display_point(map);
19267 if self.reversed {
19268 end..start
19269 } else {
19270 start..end
19271 }
19272 }
19273
19274 fn spanned_rows(
19275 &self,
19276 include_end_if_at_line_start: bool,
19277 map: &DisplaySnapshot,
19278 ) -> Range<MultiBufferRow> {
19279 let start = self.start.to_point(&map.buffer_snapshot);
19280 let mut end = self.end.to_point(&map.buffer_snapshot);
19281 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19282 end.row -= 1;
19283 }
19284
19285 let buffer_start = map.prev_line_boundary(start).0;
19286 let buffer_end = map.next_line_boundary(end).0;
19287 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19288 }
19289}
19290
19291impl<T: InvalidationRegion> InvalidationStack<T> {
19292 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19293 where
19294 S: Clone + ToOffset,
19295 {
19296 while let Some(region) = self.last() {
19297 let all_selections_inside_invalidation_ranges =
19298 if selections.len() == region.ranges().len() {
19299 selections
19300 .iter()
19301 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19302 .all(|(selection, invalidation_range)| {
19303 let head = selection.head().to_offset(buffer);
19304 invalidation_range.start <= head && invalidation_range.end >= head
19305 })
19306 } else {
19307 false
19308 };
19309
19310 if all_selections_inside_invalidation_ranges {
19311 break;
19312 } else {
19313 self.pop();
19314 }
19315 }
19316 }
19317}
19318
19319impl<T> Default for InvalidationStack<T> {
19320 fn default() -> Self {
19321 Self(Default::default())
19322 }
19323}
19324
19325impl<T> Deref for InvalidationStack<T> {
19326 type Target = Vec<T>;
19327
19328 fn deref(&self) -> &Self::Target {
19329 &self.0
19330 }
19331}
19332
19333impl<T> DerefMut for InvalidationStack<T> {
19334 fn deref_mut(&mut self) -> &mut Self::Target {
19335 &mut self.0
19336 }
19337}
19338
19339impl InvalidationRegion for SnippetState {
19340 fn ranges(&self) -> &[Range<Anchor>] {
19341 &self.ranges[self.active_index]
19342 }
19343}
19344
19345pub fn diagnostic_block_renderer(
19346 diagnostic: Diagnostic,
19347 max_message_rows: Option<u8>,
19348 allow_closing: bool,
19349) -> RenderBlock {
19350 let (text_without_backticks, code_ranges) =
19351 highlight_diagnostic_message(&diagnostic, max_message_rows);
19352
19353 Arc::new(move |cx: &mut BlockContext| {
19354 let group_id: SharedString = cx.block_id.to_string().into();
19355
19356 let mut text_style = cx.window.text_style().clone();
19357 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19358 let theme_settings = ThemeSettings::get_global(cx);
19359 text_style.font_family = theme_settings.buffer_font.family.clone();
19360 text_style.font_style = theme_settings.buffer_font.style;
19361 text_style.font_features = theme_settings.buffer_font.features.clone();
19362 text_style.font_weight = theme_settings.buffer_font.weight;
19363
19364 let multi_line_diagnostic = diagnostic.message.contains('\n');
19365
19366 let buttons = |diagnostic: &Diagnostic| {
19367 if multi_line_diagnostic {
19368 v_flex()
19369 } else {
19370 h_flex()
19371 }
19372 .when(allow_closing, |div| {
19373 div.children(diagnostic.is_primary.then(|| {
19374 IconButton::new("close-block", IconName::XCircle)
19375 .icon_color(Color::Muted)
19376 .size(ButtonSize::Compact)
19377 .style(ButtonStyle::Transparent)
19378 .visible_on_hover(group_id.clone())
19379 .on_click(move |_click, window, cx| {
19380 window.dispatch_action(Box::new(Cancel), cx)
19381 })
19382 .tooltip(|window, cx| {
19383 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19384 })
19385 }))
19386 })
19387 .child(
19388 IconButton::new("copy-block", IconName::Copy)
19389 .icon_color(Color::Muted)
19390 .size(ButtonSize::Compact)
19391 .style(ButtonStyle::Transparent)
19392 .visible_on_hover(group_id.clone())
19393 .on_click({
19394 let message = diagnostic.message.clone();
19395 move |_click, _, cx| {
19396 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19397 }
19398 })
19399 .tooltip(Tooltip::text("Copy diagnostic message")),
19400 )
19401 };
19402
19403 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19404 AvailableSpace::min_size(),
19405 cx.window,
19406 cx.app,
19407 );
19408
19409 h_flex()
19410 .id(cx.block_id)
19411 .group(group_id.clone())
19412 .relative()
19413 .size_full()
19414 .block_mouse_down()
19415 .pl(cx.gutter_dimensions.width)
19416 .w(cx.max_width - cx.gutter_dimensions.full_width())
19417 .child(
19418 div()
19419 .flex()
19420 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19421 .flex_shrink(),
19422 )
19423 .child(buttons(&diagnostic))
19424 .child(div().flex().flex_shrink_0().child(
19425 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19426 &text_style,
19427 code_ranges.iter().map(|range| {
19428 (
19429 range.clone(),
19430 HighlightStyle {
19431 font_weight: Some(FontWeight::BOLD),
19432 ..Default::default()
19433 },
19434 )
19435 }),
19436 ),
19437 ))
19438 .into_any_element()
19439 })
19440}
19441
19442fn inline_completion_edit_text(
19443 current_snapshot: &BufferSnapshot,
19444 edits: &[(Range<Anchor>, String)],
19445 edit_preview: &EditPreview,
19446 include_deletions: bool,
19447 cx: &App,
19448) -> HighlightedText {
19449 let edits = edits
19450 .iter()
19451 .map(|(anchor, text)| {
19452 (
19453 anchor.start.text_anchor..anchor.end.text_anchor,
19454 text.clone(),
19455 )
19456 })
19457 .collect::<Vec<_>>();
19458
19459 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19460}
19461
19462pub fn highlight_diagnostic_message(
19463 diagnostic: &Diagnostic,
19464 mut max_message_rows: Option<u8>,
19465) -> (SharedString, Vec<Range<usize>>) {
19466 let mut text_without_backticks = String::new();
19467 let mut code_ranges = Vec::new();
19468
19469 if let Some(source) = &diagnostic.source {
19470 text_without_backticks.push_str(source);
19471 code_ranges.push(0..source.len());
19472 text_without_backticks.push_str(": ");
19473 }
19474
19475 let mut prev_offset = 0;
19476 let mut in_code_block = false;
19477 let has_row_limit = max_message_rows.is_some();
19478 let mut newline_indices = diagnostic
19479 .message
19480 .match_indices('\n')
19481 .filter(|_| has_row_limit)
19482 .map(|(ix, _)| ix)
19483 .fuse()
19484 .peekable();
19485
19486 for (quote_ix, _) in diagnostic
19487 .message
19488 .match_indices('`')
19489 .chain([(diagnostic.message.len(), "")])
19490 {
19491 let mut first_newline_ix = None;
19492 let mut last_newline_ix = None;
19493 while let Some(newline_ix) = newline_indices.peek() {
19494 if *newline_ix < quote_ix {
19495 if first_newline_ix.is_none() {
19496 first_newline_ix = Some(*newline_ix);
19497 }
19498 last_newline_ix = Some(*newline_ix);
19499
19500 if let Some(rows_left) = &mut max_message_rows {
19501 if *rows_left == 0 {
19502 break;
19503 } else {
19504 *rows_left -= 1;
19505 }
19506 }
19507 let _ = newline_indices.next();
19508 } else {
19509 break;
19510 }
19511 }
19512 let prev_len = text_without_backticks.len();
19513 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19514 text_without_backticks.push_str(new_text);
19515 if in_code_block {
19516 code_ranges.push(prev_len..text_without_backticks.len());
19517 }
19518 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19519 in_code_block = !in_code_block;
19520 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19521 text_without_backticks.push_str("...");
19522 break;
19523 }
19524 }
19525
19526 (text_without_backticks.into(), code_ranges)
19527}
19528
19529fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19530 match severity {
19531 DiagnosticSeverity::ERROR => colors.error,
19532 DiagnosticSeverity::WARNING => colors.warning,
19533 DiagnosticSeverity::INFORMATION => colors.info,
19534 DiagnosticSeverity::HINT => colors.info,
19535 _ => colors.ignored,
19536 }
19537}
19538
19539pub fn styled_runs_for_code_label<'a>(
19540 label: &'a CodeLabel,
19541 syntax_theme: &'a theme::SyntaxTheme,
19542) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19543 let fade_out = HighlightStyle {
19544 fade_out: Some(0.35),
19545 ..Default::default()
19546 };
19547
19548 let mut prev_end = label.filter_range.end;
19549 label
19550 .runs
19551 .iter()
19552 .enumerate()
19553 .flat_map(move |(ix, (range, highlight_id))| {
19554 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19555 style
19556 } else {
19557 return Default::default();
19558 };
19559 let mut muted_style = style;
19560 muted_style.highlight(fade_out);
19561
19562 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19563 if range.start >= label.filter_range.end {
19564 if range.start > prev_end {
19565 runs.push((prev_end..range.start, fade_out));
19566 }
19567 runs.push((range.clone(), muted_style));
19568 } else if range.end <= label.filter_range.end {
19569 runs.push((range.clone(), style));
19570 } else {
19571 runs.push((range.start..label.filter_range.end, style));
19572 runs.push((label.filter_range.end..range.end, muted_style));
19573 }
19574 prev_end = cmp::max(prev_end, range.end);
19575
19576 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19577 runs.push((prev_end..label.text.len(), fade_out));
19578 }
19579
19580 runs
19581 })
19582}
19583
19584pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19585 let mut prev_index = 0;
19586 let mut prev_codepoint: Option<char> = None;
19587 text.char_indices()
19588 .chain([(text.len(), '\0')])
19589 .filter_map(move |(index, codepoint)| {
19590 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19591 let is_boundary = index == text.len()
19592 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19593 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19594 if is_boundary {
19595 let chunk = &text[prev_index..index];
19596 prev_index = index;
19597 Some(chunk)
19598 } else {
19599 None
19600 }
19601 })
19602}
19603
19604pub trait RangeToAnchorExt: Sized {
19605 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19606
19607 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19608 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19609 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19610 }
19611}
19612
19613impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19614 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19615 let start_offset = self.start.to_offset(snapshot);
19616 let end_offset = self.end.to_offset(snapshot);
19617 if start_offset == end_offset {
19618 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19619 } else {
19620 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19621 }
19622 }
19623}
19624
19625pub trait RowExt {
19626 fn as_f32(&self) -> f32;
19627
19628 fn next_row(&self) -> Self;
19629
19630 fn previous_row(&self) -> Self;
19631
19632 fn minus(&self, other: Self) -> u32;
19633}
19634
19635impl RowExt for DisplayRow {
19636 fn as_f32(&self) -> f32 {
19637 self.0 as f32
19638 }
19639
19640 fn next_row(&self) -> Self {
19641 Self(self.0 + 1)
19642 }
19643
19644 fn previous_row(&self) -> Self {
19645 Self(self.0.saturating_sub(1))
19646 }
19647
19648 fn minus(&self, other: Self) -> u32 {
19649 self.0 - other.0
19650 }
19651}
19652
19653impl RowExt for MultiBufferRow {
19654 fn as_f32(&self) -> f32 {
19655 self.0 as f32
19656 }
19657
19658 fn next_row(&self) -> Self {
19659 Self(self.0 + 1)
19660 }
19661
19662 fn previous_row(&self) -> Self {
19663 Self(self.0.saturating_sub(1))
19664 }
19665
19666 fn minus(&self, other: Self) -> u32 {
19667 self.0 - other.0
19668 }
19669}
19670
19671trait RowRangeExt {
19672 type Row;
19673
19674 fn len(&self) -> usize;
19675
19676 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19677}
19678
19679impl RowRangeExt for Range<MultiBufferRow> {
19680 type Row = MultiBufferRow;
19681
19682 fn len(&self) -> usize {
19683 (self.end.0 - self.start.0) as usize
19684 }
19685
19686 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19687 (self.start.0..self.end.0).map(MultiBufferRow)
19688 }
19689}
19690
19691impl RowRangeExt for Range<DisplayRow> {
19692 type Row = DisplayRow;
19693
19694 fn len(&self) -> usize {
19695 (self.end.0 - self.start.0) as usize
19696 }
19697
19698 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19699 (self.start.0..self.end.0).map(DisplayRow)
19700 }
19701}
19702
19703/// If select range has more than one line, we
19704/// just point the cursor to range.start.
19705fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19706 if range.start.row == range.end.row {
19707 range
19708 } else {
19709 range.start..range.start
19710 }
19711}
19712pub struct KillRing(ClipboardItem);
19713impl Global for KillRing {}
19714
19715const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19716
19717struct BreakpointPromptEditor {
19718 pub(crate) prompt: Entity<Editor>,
19719 editor: WeakEntity<Editor>,
19720 breakpoint_anchor: Anchor,
19721 breakpoint: Breakpoint,
19722 block_ids: HashSet<CustomBlockId>,
19723 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19724 _subscriptions: Vec<Subscription>,
19725}
19726
19727impl BreakpointPromptEditor {
19728 const MAX_LINES: u8 = 4;
19729
19730 fn new(
19731 editor: WeakEntity<Editor>,
19732 breakpoint_anchor: Anchor,
19733 breakpoint: Breakpoint,
19734 window: &mut Window,
19735 cx: &mut Context<Self>,
19736 ) -> Self {
19737 let buffer = cx.new(|cx| {
19738 Buffer::local(
19739 breakpoint
19740 .kind
19741 .log_message()
19742 .map(|msg| msg.to_string())
19743 .unwrap_or_default(),
19744 cx,
19745 )
19746 });
19747 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19748
19749 let prompt = cx.new(|cx| {
19750 let mut prompt = Editor::new(
19751 EditorMode::AutoHeight {
19752 max_lines: Self::MAX_LINES as usize,
19753 },
19754 buffer,
19755 None,
19756 window,
19757 cx,
19758 );
19759 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19760 prompt.set_show_cursor_when_unfocused(false, cx);
19761 prompt.set_placeholder_text(
19762 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19763 cx,
19764 );
19765
19766 prompt
19767 });
19768
19769 Self {
19770 prompt,
19771 editor,
19772 breakpoint_anchor,
19773 breakpoint,
19774 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19775 block_ids: Default::default(),
19776 _subscriptions: vec![],
19777 }
19778 }
19779
19780 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19781 self.block_ids.extend(block_ids)
19782 }
19783
19784 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19785 if let Some(editor) = self.editor.upgrade() {
19786 let log_message = self
19787 .prompt
19788 .read(cx)
19789 .buffer
19790 .read(cx)
19791 .as_singleton()
19792 .expect("A multi buffer in breakpoint prompt isn't possible")
19793 .read(cx)
19794 .as_rope()
19795 .to_string();
19796
19797 editor.update(cx, |editor, cx| {
19798 editor.edit_breakpoint_at_anchor(
19799 self.breakpoint_anchor,
19800 self.breakpoint.clone(),
19801 BreakpointEditAction::EditLogMessage(log_message.into()),
19802 cx,
19803 );
19804
19805 editor.remove_blocks(self.block_ids.clone(), None, cx);
19806 cx.focus_self(window);
19807 });
19808 }
19809 }
19810
19811 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19812 self.editor
19813 .update(cx, |editor, cx| {
19814 editor.remove_blocks(self.block_ids.clone(), None, cx);
19815 window.focus(&editor.focus_handle);
19816 })
19817 .log_err();
19818 }
19819
19820 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19821 let settings = ThemeSettings::get_global(cx);
19822 let text_style = TextStyle {
19823 color: if self.prompt.read(cx).read_only(cx) {
19824 cx.theme().colors().text_disabled
19825 } else {
19826 cx.theme().colors().text
19827 },
19828 font_family: settings.buffer_font.family.clone(),
19829 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19830 font_size: settings.buffer_font_size(cx).into(),
19831 font_weight: settings.buffer_font.weight,
19832 line_height: relative(settings.buffer_line_height.value()),
19833 ..Default::default()
19834 };
19835 EditorElement::new(
19836 &self.prompt,
19837 EditorStyle {
19838 background: cx.theme().colors().editor_background,
19839 local_player: cx.theme().players().local(),
19840 text: text_style,
19841 ..Default::default()
19842 },
19843 )
19844 }
19845}
19846
19847impl Render for BreakpointPromptEditor {
19848 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19849 let gutter_dimensions = *self.gutter_dimensions.lock();
19850 h_flex()
19851 .key_context("Editor")
19852 .bg(cx.theme().colors().editor_background)
19853 .border_y_1()
19854 .border_color(cx.theme().status().info_border)
19855 .size_full()
19856 .py(window.line_height() / 2.5)
19857 .on_action(cx.listener(Self::confirm))
19858 .on_action(cx.listener(Self::cancel))
19859 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19860 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19861 }
19862}
19863
19864impl Focusable for BreakpointPromptEditor {
19865 fn focus_handle(&self, cx: &App) -> FocusHandle {
19866 self.prompt.focus_handle(cx)
19867 }
19868}
19869
19870fn all_edits_insertions_or_deletions(
19871 edits: &Vec<(Range<Anchor>, String)>,
19872 snapshot: &MultiBufferSnapshot,
19873) -> bool {
19874 let mut all_insertions = true;
19875 let mut all_deletions = true;
19876
19877 for (range, new_text) in edits.iter() {
19878 let range_is_empty = range.to_offset(&snapshot).is_empty();
19879 let text_is_empty = new_text.is_empty();
19880
19881 if range_is_empty != text_is_empty {
19882 if range_is_empty {
19883 all_deletions = false;
19884 } else {
19885 all_insertions = false;
19886 }
19887 } else {
19888 return false;
19889 }
19890
19891 if !all_insertions && !all_deletions {
19892 return false;
19893 }
19894 }
19895 all_insertions || all_deletions
19896}
19897
19898struct MissingEditPredictionKeybindingTooltip;
19899
19900impl Render for MissingEditPredictionKeybindingTooltip {
19901 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19902 ui::tooltip_container(window, cx, |container, _, cx| {
19903 container
19904 .flex_shrink_0()
19905 .max_w_80()
19906 .min_h(rems_from_px(124.))
19907 .justify_between()
19908 .child(
19909 v_flex()
19910 .flex_1()
19911 .text_ui_sm(cx)
19912 .child(Label::new("Conflict with Accept Keybinding"))
19913 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19914 )
19915 .child(
19916 h_flex()
19917 .pb_1()
19918 .gap_1()
19919 .items_end()
19920 .w_full()
19921 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19922 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19923 }))
19924 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19925 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19926 })),
19927 )
19928 })
19929 }
19930}
19931
19932#[derive(Debug, Clone, Copy, PartialEq)]
19933pub struct LineHighlight {
19934 pub background: Background,
19935 pub border: Option<gpui::Hsla>,
19936}
19937
19938impl From<Hsla> for LineHighlight {
19939 fn from(hsla: Hsla) -> Self {
19940 Self {
19941 background: hsla.into(),
19942 border: None,
19943 }
19944 }
19945}
19946
19947impl From<Background> for LineHighlight {
19948 fn from(background: Background) -> Self {
19949 Self {
19950 background,
19951 border: None,
19952 }
19953 }
19954}