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 id = post_inc(&mut self.next_completion_id);
4330 let task = cx.spawn_in(window, async move |editor, cx| {
4331 async move {
4332 editor.update(cx, |this, _| {
4333 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4334 })?;
4335
4336 let mut completions = Vec::new();
4337 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4338 completions.extend(provided_completions);
4339 if completion_settings.words == WordsCompletionMode::Fallback {
4340 words = Task::ready(HashMap::default());
4341 }
4342 }
4343
4344 let mut words = words.await;
4345 if let Some(word_to_exclude) = &word_to_exclude {
4346 words.remove(word_to_exclude);
4347 }
4348 for lsp_completion in &completions {
4349 words.remove(&lsp_completion.new_text);
4350 }
4351 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4352 old_range: old_range.clone(),
4353 new_text: word.clone(),
4354 label: CodeLabel::plain(word, None),
4355 icon_path: None,
4356 documentation: None,
4357 source: CompletionSource::BufferWord {
4358 word_range,
4359 resolved: false,
4360 },
4361 confirm: None,
4362 }));
4363
4364 let menu = if completions.is_empty() {
4365 None
4366 } else {
4367 let mut menu = CompletionsMenu::new(
4368 id,
4369 sort_completions,
4370 show_completion_documentation,
4371 ignore_completion_provider,
4372 position,
4373 buffer.clone(),
4374 completions.into(),
4375 );
4376
4377 menu.filter(query.as_deref(), cx.background_executor().clone())
4378 .await;
4379
4380 menu.visible().then_some(menu)
4381 };
4382
4383 editor.update_in(cx, |editor, window, cx| {
4384 match editor.context_menu.borrow().as_ref() {
4385 None => {}
4386 Some(CodeContextMenu::Completions(prev_menu)) => {
4387 if prev_menu.id > id {
4388 return;
4389 }
4390 }
4391 _ => return,
4392 }
4393
4394 if editor.focus_handle.is_focused(window) && menu.is_some() {
4395 let mut menu = menu.unwrap();
4396 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4397
4398 *editor.context_menu.borrow_mut() =
4399 Some(CodeContextMenu::Completions(menu));
4400
4401 if editor.show_edit_predictions_in_menu() {
4402 editor.update_visible_inline_completion(window, cx);
4403 } else {
4404 editor.discard_inline_completion(false, cx);
4405 }
4406
4407 cx.notify();
4408 } else if editor.completion_tasks.len() <= 1 {
4409 // If there are no more completion tasks and the last menu was
4410 // empty, we should hide it.
4411 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4412 // If it was already hidden and we don't show inline
4413 // completions in the menu, we should also show the
4414 // inline-completion when available.
4415 if was_hidden && editor.show_edit_predictions_in_menu() {
4416 editor.update_visible_inline_completion(window, cx);
4417 }
4418 }
4419 })?;
4420
4421 anyhow::Ok(())
4422 }
4423 .log_err()
4424 .await
4425 });
4426
4427 self.completion_tasks.push((id, task));
4428 }
4429
4430 #[cfg(feature = "test-support")]
4431 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
4432 let menu = self.context_menu.borrow();
4433 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
4434 let completions = menu.completions.borrow();
4435 Some(completions.to_vec())
4436 } else {
4437 None
4438 }
4439 }
4440
4441 pub fn confirm_completion(
4442 &mut self,
4443 action: &ConfirmCompletion,
4444 window: &mut Window,
4445 cx: &mut Context<Self>,
4446 ) -> Option<Task<Result<()>>> {
4447 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4448 }
4449
4450 pub fn compose_completion(
4451 &mut self,
4452 action: &ComposeCompletion,
4453 window: &mut Window,
4454 cx: &mut Context<Self>,
4455 ) -> Option<Task<Result<()>>> {
4456 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4457 }
4458
4459 fn do_completion(
4460 &mut self,
4461 item_ix: Option<usize>,
4462 intent: CompletionIntent,
4463 window: &mut Window,
4464 cx: &mut Context<Editor>,
4465 ) -> Option<Task<Result<()>>> {
4466 use language::ToOffset as _;
4467
4468 let completions_menu =
4469 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4470 menu
4471 } else {
4472 return None;
4473 };
4474
4475 let candidate_id = {
4476 let entries = completions_menu.entries.borrow();
4477 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4478 if self.show_edit_predictions_in_menu() {
4479 self.discard_inline_completion(true, cx);
4480 }
4481 mat.candidate_id
4482 };
4483
4484 let buffer_handle = completions_menu.buffer;
4485 let completion = completions_menu
4486 .completions
4487 .borrow()
4488 .get(candidate_id)?
4489 .clone();
4490 cx.stop_propagation();
4491
4492 let snippet;
4493 let new_text;
4494 if completion.is_snippet() {
4495 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4496 new_text = snippet.as_ref().unwrap().text.clone();
4497 } else {
4498 snippet = None;
4499 new_text = completion.new_text.clone();
4500 };
4501 let selections = self.selections.all::<usize>(cx);
4502 let buffer = buffer_handle.read(cx);
4503 let old_range = completion.old_range.to_offset(buffer);
4504 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4505
4506 let newest_selection = self.selections.newest_anchor();
4507 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4508 return None;
4509 }
4510
4511 let lookbehind = newest_selection
4512 .start
4513 .text_anchor
4514 .to_offset(buffer)
4515 .saturating_sub(old_range.start);
4516 let lookahead = old_range
4517 .end
4518 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4519 let mut common_prefix_len = old_text
4520 .bytes()
4521 .zip(new_text.bytes())
4522 .take_while(|(a, b)| a == b)
4523 .count();
4524
4525 let snapshot = self.buffer.read(cx).snapshot(cx);
4526 let mut range_to_replace: Option<Range<isize>> = None;
4527 let mut ranges = Vec::new();
4528 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4529 for selection in &selections {
4530 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4531 let start = selection.start.saturating_sub(lookbehind);
4532 let end = selection.end + lookahead;
4533 if selection.id == newest_selection.id {
4534 range_to_replace = Some(
4535 ((start + common_prefix_len) as isize - selection.start as isize)
4536 ..(end as isize - selection.start as isize),
4537 );
4538 }
4539 ranges.push(start + common_prefix_len..end);
4540 } else {
4541 common_prefix_len = 0;
4542 ranges.clear();
4543 ranges.extend(selections.iter().map(|s| {
4544 if s.id == newest_selection.id {
4545 range_to_replace = Some(
4546 old_range.start.to_offset_utf16(&snapshot).0 as isize
4547 - selection.start as isize
4548 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4549 - selection.start as isize,
4550 );
4551 old_range.clone()
4552 } else {
4553 s.start..s.end
4554 }
4555 }));
4556 break;
4557 }
4558 if !self.linked_edit_ranges.is_empty() {
4559 let start_anchor = snapshot.anchor_before(selection.head());
4560 let end_anchor = snapshot.anchor_after(selection.tail());
4561 if let Some(ranges) = self
4562 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4563 {
4564 for (buffer, edits) in ranges {
4565 linked_edits.entry(buffer.clone()).or_default().extend(
4566 edits
4567 .into_iter()
4568 .map(|range| (range, new_text[common_prefix_len..].to_owned())),
4569 );
4570 }
4571 }
4572 }
4573 }
4574 let text = &new_text[common_prefix_len..];
4575
4576 cx.emit(EditorEvent::InputHandled {
4577 utf16_range_to_replace: range_to_replace,
4578 text: text.into(),
4579 });
4580
4581 self.transact(window, cx, |this, window, cx| {
4582 if let Some(mut snippet) = snippet {
4583 snippet.text = text.to_string();
4584 for tabstop in snippet
4585 .tabstops
4586 .iter_mut()
4587 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4588 {
4589 tabstop.start -= common_prefix_len as isize;
4590 tabstop.end -= common_prefix_len as isize;
4591 }
4592
4593 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4594 } else {
4595 this.buffer.update(cx, |buffer, cx| {
4596 let edits = ranges.iter().map(|range| (range.clone(), text));
4597 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4598 });
4599 }
4600 for (buffer, edits) in linked_edits {
4601 buffer.update(cx, |buffer, cx| {
4602 let snapshot = buffer.snapshot();
4603 let edits = edits
4604 .into_iter()
4605 .map(|(range, text)| {
4606 use text::ToPoint as TP;
4607 let end_point = TP::to_point(&range.end, &snapshot);
4608 let start_point = TP::to_point(&range.start, &snapshot);
4609 (start_point..end_point, text)
4610 })
4611 .sorted_by_key(|(range, _)| range.start);
4612 buffer.edit(edits, None, cx);
4613 })
4614 }
4615
4616 this.refresh_inline_completion(true, false, window, cx);
4617 });
4618
4619 let show_new_completions_on_confirm = completion
4620 .confirm
4621 .as_ref()
4622 .map_or(false, |confirm| confirm(intent, window, cx));
4623 if show_new_completions_on_confirm {
4624 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4625 }
4626
4627 let provider = self.completion_provider.as_ref()?;
4628 drop(completion);
4629 let apply_edits = provider.apply_additional_edits_for_completion(
4630 buffer_handle,
4631 completions_menu.completions.clone(),
4632 candidate_id,
4633 true,
4634 cx,
4635 );
4636
4637 let editor_settings = EditorSettings::get_global(cx);
4638 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4639 // After the code completion is finished, users often want to know what signatures are needed.
4640 // so we should automatically call signature_help
4641 self.show_signature_help(&ShowSignatureHelp, window, cx);
4642 }
4643
4644 Some(cx.foreground_executor().spawn(async move {
4645 apply_edits.await?;
4646 Ok(())
4647 }))
4648 }
4649
4650 pub fn toggle_code_actions(
4651 &mut self,
4652 action: &ToggleCodeActions,
4653 window: &mut Window,
4654 cx: &mut Context<Self>,
4655 ) {
4656 let mut context_menu = self.context_menu.borrow_mut();
4657 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4658 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4659 // Toggle if we're selecting the same one
4660 *context_menu = None;
4661 cx.notify();
4662 return;
4663 } else {
4664 // Otherwise, clear it and start a new one
4665 *context_menu = None;
4666 cx.notify();
4667 }
4668 }
4669 drop(context_menu);
4670 let snapshot = self.snapshot(window, cx);
4671 let deployed_from_indicator = action.deployed_from_indicator;
4672 let mut task = self.code_actions_task.take();
4673 let action = action.clone();
4674 cx.spawn_in(window, async move |editor, cx| {
4675 while let Some(prev_task) = task {
4676 prev_task.await.log_err();
4677 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4678 }
4679
4680 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4681 if editor.focus_handle.is_focused(window) {
4682 let multibuffer_point = action
4683 .deployed_from_indicator
4684 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4685 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4686 let (buffer, buffer_row) = snapshot
4687 .buffer_snapshot
4688 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4689 .and_then(|(buffer_snapshot, range)| {
4690 editor
4691 .buffer
4692 .read(cx)
4693 .buffer(buffer_snapshot.remote_id())
4694 .map(|buffer| (buffer, range.start.row))
4695 })?;
4696 let (_, code_actions) = editor
4697 .available_code_actions
4698 .clone()
4699 .and_then(|(location, code_actions)| {
4700 let snapshot = location.buffer.read(cx).snapshot();
4701 let point_range = location.range.to_point(&snapshot);
4702 let point_range = point_range.start.row..=point_range.end.row;
4703 if point_range.contains(&buffer_row) {
4704 Some((location, code_actions))
4705 } else {
4706 None
4707 }
4708 })
4709 .unzip();
4710 let buffer_id = buffer.read(cx).remote_id();
4711 let tasks = editor
4712 .tasks
4713 .get(&(buffer_id, buffer_row))
4714 .map(|t| Arc::new(t.to_owned()));
4715 if tasks.is_none() && code_actions.is_none() {
4716 return None;
4717 }
4718
4719 editor.completion_tasks.clear();
4720 editor.discard_inline_completion(false, cx);
4721 let task_context =
4722 tasks
4723 .as_ref()
4724 .zip(editor.project.clone())
4725 .map(|(tasks, project)| {
4726 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4727 });
4728
4729 Some(cx.spawn_in(window, async move |editor, cx| {
4730 let task_context = match task_context {
4731 Some(task_context) => task_context.await,
4732 None => None,
4733 };
4734 let resolved_tasks =
4735 tasks.zip(task_context).map(|(tasks, task_context)| {
4736 Rc::new(ResolvedTasks {
4737 templates: tasks.resolve(&task_context).collect(),
4738 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4739 multibuffer_point.row,
4740 tasks.column,
4741 )),
4742 })
4743 });
4744 let spawn_straight_away = resolved_tasks
4745 .as_ref()
4746 .map_or(false, |tasks| tasks.templates.len() == 1)
4747 && code_actions
4748 .as_ref()
4749 .map_or(true, |actions| actions.is_empty());
4750 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4751 *editor.context_menu.borrow_mut() =
4752 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4753 buffer,
4754 actions: CodeActionContents {
4755 tasks: resolved_tasks,
4756 actions: code_actions,
4757 },
4758 selected_item: Default::default(),
4759 scroll_handle: UniformListScrollHandle::default(),
4760 deployed_from_indicator,
4761 }));
4762 if spawn_straight_away {
4763 if let Some(task) = editor.confirm_code_action(
4764 &ConfirmCodeAction { item_ix: Some(0) },
4765 window,
4766 cx,
4767 ) {
4768 cx.notify();
4769 return task;
4770 }
4771 }
4772 cx.notify();
4773 Task::ready(Ok(()))
4774 }) {
4775 task.await
4776 } else {
4777 Ok(())
4778 }
4779 }))
4780 } else {
4781 Some(Task::ready(Ok(())))
4782 }
4783 })?;
4784 if let Some(task) = spawned_test_task {
4785 task.await?;
4786 }
4787
4788 Ok::<_, anyhow::Error>(())
4789 })
4790 .detach_and_log_err(cx);
4791 }
4792
4793 pub fn confirm_code_action(
4794 &mut self,
4795 action: &ConfirmCodeAction,
4796 window: &mut Window,
4797 cx: &mut Context<Self>,
4798 ) -> Option<Task<Result<()>>> {
4799 let actions_menu =
4800 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4801 menu
4802 } else {
4803 return None;
4804 };
4805 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4806 let action = actions_menu.actions.get(action_ix)?;
4807 let title = action.label();
4808 let buffer = actions_menu.buffer;
4809 let workspace = self.workspace()?;
4810
4811 match action {
4812 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4813 workspace.update(cx, |workspace, cx| {
4814 workspace::tasks::schedule_resolved_task(
4815 workspace,
4816 task_source_kind,
4817 resolved_task,
4818 false,
4819 cx,
4820 );
4821
4822 Some(Task::ready(Ok(())))
4823 })
4824 }
4825 CodeActionsItem::CodeAction {
4826 excerpt_id,
4827 action,
4828 provider,
4829 } => {
4830 let apply_code_action =
4831 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4832 let workspace = workspace.downgrade();
4833 Some(cx.spawn_in(window, async move |editor, cx| {
4834 let project_transaction = apply_code_action.await?;
4835 Self::open_project_transaction(
4836 &editor,
4837 workspace,
4838 project_transaction,
4839 title,
4840 cx,
4841 )
4842 .await
4843 }))
4844 }
4845 }
4846 }
4847
4848 pub async fn open_project_transaction(
4849 this: &WeakEntity<Editor>,
4850 workspace: WeakEntity<Workspace>,
4851 transaction: ProjectTransaction,
4852 title: String,
4853 cx: &mut AsyncWindowContext,
4854 ) -> Result<()> {
4855 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4856 cx.update(|_, cx| {
4857 entries.sort_unstable_by_key(|(buffer, _)| {
4858 buffer.read(cx).file().map(|f| f.path().clone())
4859 });
4860 })?;
4861
4862 // If the project transaction's edits are all contained within this editor, then
4863 // avoid opening a new editor to display them.
4864
4865 if let Some((buffer, transaction)) = entries.first() {
4866 if entries.len() == 1 {
4867 let excerpt = this.update(cx, |editor, cx| {
4868 editor
4869 .buffer()
4870 .read(cx)
4871 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4872 })?;
4873 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4874 if excerpted_buffer == *buffer {
4875 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4876 let excerpt_range = excerpt_range.to_offset(buffer);
4877 buffer
4878 .edited_ranges_for_transaction::<usize>(transaction)
4879 .all(|range| {
4880 excerpt_range.start <= range.start
4881 && excerpt_range.end >= range.end
4882 })
4883 })?;
4884
4885 if all_edits_within_excerpt {
4886 return Ok(());
4887 }
4888 }
4889 }
4890 }
4891 } else {
4892 return Ok(());
4893 }
4894
4895 let mut ranges_to_highlight = Vec::new();
4896 let excerpt_buffer = cx.new(|cx| {
4897 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4898 for (buffer_handle, transaction) in &entries {
4899 let buffer = buffer_handle.read(cx);
4900 ranges_to_highlight.extend(
4901 multibuffer.push_excerpts_with_context_lines(
4902 buffer_handle.clone(),
4903 buffer
4904 .edited_ranges_for_transaction::<usize>(transaction)
4905 .collect(),
4906 DEFAULT_MULTIBUFFER_CONTEXT,
4907 cx,
4908 ),
4909 );
4910 }
4911 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4912 multibuffer
4913 })?;
4914
4915 workspace.update_in(cx, |workspace, window, cx| {
4916 let project = workspace.project().clone();
4917 let editor =
4918 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4919 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4920 editor.update(cx, |editor, cx| {
4921 editor.highlight_background::<Self>(
4922 &ranges_to_highlight,
4923 |theme| theme.editor_highlighted_line_background,
4924 cx,
4925 );
4926 });
4927 })?;
4928
4929 Ok(())
4930 }
4931
4932 pub fn clear_code_action_providers(&mut self) {
4933 self.code_action_providers.clear();
4934 self.available_code_actions.take();
4935 }
4936
4937 pub fn add_code_action_provider(
4938 &mut self,
4939 provider: Rc<dyn CodeActionProvider>,
4940 window: &mut Window,
4941 cx: &mut Context<Self>,
4942 ) {
4943 if self
4944 .code_action_providers
4945 .iter()
4946 .any(|existing_provider| existing_provider.id() == provider.id())
4947 {
4948 return;
4949 }
4950
4951 self.code_action_providers.push(provider);
4952 self.refresh_code_actions(window, cx);
4953 }
4954
4955 pub fn remove_code_action_provider(
4956 &mut self,
4957 id: Arc<str>,
4958 window: &mut Window,
4959 cx: &mut Context<Self>,
4960 ) {
4961 self.code_action_providers
4962 .retain(|provider| provider.id() != id);
4963 self.refresh_code_actions(window, cx);
4964 }
4965
4966 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4967 let buffer = self.buffer.read(cx);
4968 let newest_selection = self.selections.newest_anchor().clone();
4969 if newest_selection.head().diff_base_anchor.is_some() {
4970 return None;
4971 }
4972 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4973 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4974 if start_buffer != end_buffer {
4975 return None;
4976 }
4977
4978 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4979 cx.background_executor()
4980 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4981 .await;
4982
4983 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4984 let providers = this.code_action_providers.clone();
4985 let tasks = this
4986 .code_action_providers
4987 .iter()
4988 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4989 .collect::<Vec<_>>();
4990 (providers, tasks)
4991 })?;
4992
4993 let mut actions = Vec::new();
4994 for (provider, provider_actions) in
4995 providers.into_iter().zip(future::join_all(tasks).await)
4996 {
4997 if let Some(provider_actions) = provider_actions.log_err() {
4998 actions.extend(provider_actions.into_iter().map(|action| {
4999 AvailableCodeAction {
5000 excerpt_id: newest_selection.start.excerpt_id,
5001 action,
5002 provider: provider.clone(),
5003 }
5004 }));
5005 }
5006 }
5007
5008 this.update(cx, |this, cx| {
5009 this.available_code_actions = if actions.is_empty() {
5010 None
5011 } else {
5012 Some((
5013 Location {
5014 buffer: start_buffer,
5015 range: start..end,
5016 },
5017 actions.into(),
5018 ))
5019 };
5020 cx.notify();
5021 })
5022 }));
5023 None
5024 }
5025
5026 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5027 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5028 self.show_git_blame_inline = false;
5029
5030 self.show_git_blame_inline_delay_task =
5031 Some(cx.spawn_in(window, async move |this, cx| {
5032 cx.background_executor().timer(delay).await;
5033
5034 this.update(cx, |this, cx| {
5035 this.show_git_blame_inline = true;
5036 cx.notify();
5037 })
5038 .log_err();
5039 }));
5040 }
5041 }
5042
5043 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
5044 if self.pending_rename.is_some() {
5045 return None;
5046 }
5047
5048 let provider = self.semantics_provider.clone()?;
5049 let buffer = self.buffer.read(cx);
5050 let newest_selection = self.selections.newest_anchor().clone();
5051 let cursor_position = newest_selection.head();
5052 let (cursor_buffer, cursor_buffer_position) =
5053 buffer.text_anchor_for_position(cursor_position, cx)?;
5054 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5055 if cursor_buffer != tail_buffer {
5056 return None;
5057 }
5058 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
5059 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
5060 cx.background_executor()
5061 .timer(Duration::from_millis(debounce))
5062 .await;
5063
5064 let highlights = if let Some(highlights) = cx
5065 .update(|cx| {
5066 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5067 })
5068 .ok()
5069 .flatten()
5070 {
5071 highlights.await.log_err()
5072 } else {
5073 None
5074 };
5075
5076 if let Some(highlights) = highlights {
5077 this.update(cx, |this, cx| {
5078 if this.pending_rename.is_some() {
5079 return;
5080 }
5081
5082 let buffer_id = cursor_position.buffer_id;
5083 let buffer = this.buffer.read(cx);
5084 if !buffer
5085 .text_anchor_for_position(cursor_position, cx)
5086 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5087 {
5088 return;
5089 }
5090
5091 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5092 let mut write_ranges = Vec::new();
5093 let mut read_ranges = Vec::new();
5094 for highlight in highlights {
5095 for (excerpt_id, excerpt_range) in
5096 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5097 {
5098 let start = highlight
5099 .range
5100 .start
5101 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5102 let end = highlight
5103 .range
5104 .end
5105 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5106 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5107 continue;
5108 }
5109
5110 let range = Anchor {
5111 buffer_id,
5112 excerpt_id,
5113 text_anchor: start,
5114 diff_base_anchor: None,
5115 }..Anchor {
5116 buffer_id,
5117 excerpt_id,
5118 text_anchor: end,
5119 diff_base_anchor: None,
5120 };
5121 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5122 write_ranges.push(range);
5123 } else {
5124 read_ranges.push(range);
5125 }
5126 }
5127 }
5128
5129 this.highlight_background::<DocumentHighlightRead>(
5130 &read_ranges,
5131 |theme| theme.editor_document_highlight_read_background,
5132 cx,
5133 );
5134 this.highlight_background::<DocumentHighlightWrite>(
5135 &write_ranges,
5136 |theme| theme.editor_document_highlight_write_background,
5137 cx,
5138 );
5139 cx.notify();
5140 })
5141 .log_err();
5142 }
5143 }));
5144 None
5145 }
5146
5147 pub fn refresh_selected_text_highlights(
5148 &mut self,
5149 window: &mut Window,
5150 cx: &mut Context<Editor>,
5151 ) {
5152 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5153 return;
5154 }
5155 self.selection_highlight_task.take();
5156 if !EditorSettings::get_global(cx).selection_highlight {
5157 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5158 return;
5159 }
5160 if self.selections.count() != 1 || self.selections.line_mode {
5161 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5162 return;
5163 }
5164 let selection = self.selections.newest::<Point>(cx);
5165 if selection.is_empty() || selection.start.row != selection.end.row {
5166 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5167 return;
5168 }
5169 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5170 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5171 cx.background_executor()
5172 .timer(Duration::from_millis(debounce))
5173 .await;
5174 let Some(Some(matches_task)) = editor
5175 .update_in(cx, |editor, _, cx| {
5176 if editor.selections.count() != 1 || editor.selections.line_mode {
5177 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5178 return None;
5179 }
5180 let selection = editor.selections.newest::<Point>(cx);
5181 if selection.is_empty() || selection.start.row != selection.end.row {
5182 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5183 return None;
5184 }
5185 let buffer = editor.buffer().read(cx).snapshot(cx);
5186 let query = buffer.text_for_range(selection.range()).collect::<String>();
5187 if query.trim().is_empty() {
5188 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5189 return None;
5190 }
5191 Some(cx.background_spawn(async move {
5192 let mut ranges = Vec::new();
5193 let selection_anchors = selection.range().to_anchors(&buffer);
5194 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5195 for (search_buffer, search_range, excerpt_id) in
5196 buffer.range_to_buffer_ranges(range)
5197 {
5198 ranges.extend(
5199 project::search::SearchQuery::text(
5200 query.clone(),
5201 false,
5202 false,
5203 false,
5204 Default::default(),
5205 Default::default(),
5206 None,
5207 )
5208 .unwrap()
5209 .search(search_buffer, Some(search_range.clone()))
5210 .await
5211 .into_iter()
5212 .filter_map(
5213 |match_range| {
5214 let start = search_buffer.anchor_after(
5215 search_range.start + match_range.start,
5216 );
5217 let end = search_buffer.anchor_before(
5218 search_range.start + match_range.end,
5219 );
5220 let range = Anchor::range_in_buffer(
5221 excerpt_id,
5222 search_buffer.remote_id(),
5223 start..end,
5224 );
5225 (range != selection_anchors).then_some(range)
5226 },
5227 ),
5228 );
5229 }
5230 }
5231 ranges
5232 }))
5233 })
5234 .log_err()
5235 else {
5236 return;
5237 };
5238 let matches = matches_task.await;
5239 editor
5240 .update_in(cx, |editor, _, cx| {
5241 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5242 if !matches.is_empty() {
5243 editor.highlight_background::<SelectedTextHighlight>(
5244 &matches,
5245 |theme| theme.editor_document_highlight_bracket_background,
5246 cx,
5247 )
5248 }
5249 })
5250 .log_err();
5251 }));
5252 }
5253
5254 pub fn refresh_inline_completion(
5255 &mut self,
5256 debounce: bool,
5257 user_requested: bool,
5258 window: &mut Window,
5259 cx: &mut Context<Self>,
5260 ) -> Option<()> {
5261 let provider = self.edit_prediction_provider()?;
5262 let cursor = self.selections.newest_anchor().head();
5263 let (buffer, cursor_buffer_position) =
5264 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5265
5266 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5267 self.discard_inline_completion(false, cx);
5268 return None;
5269 }
5270
5271 if !user_requested
5272 && (!self.should_show_edit_predictions()
5273 || !self.is_focused(window)
5274 || buffer.read(cx).is_empty())
5275 {
5276 self.discard_inline_completion(false, cx);
5277 return None;
5278 }
5279
5280 self.update_visible_inline_completion(window, cx);
5281 provider.refresh(
5282 self.project.clone(),
5283 buffer,
5284 cursor_buffer_position,
5285 debounce,
5286 cx,
5287 );
5288 Some(())
5289 }
5290
5291 fn show_edit_predictions_in_menu(&self) -> bool {
5292 match self.edit_prediction_settings {
5293 EditPredictionSettings::Disabled => false,
5294 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5295 }
5296 }
5297
5298 pub fn edit_predictions_enabled(&self) -> bool {
5299 match self.edit_prediction_settings {
5300 EditPredictionSettings::Disabled => false,
5301 EditPredictionSettings::Enabled { .. } => true,
5302 }
5303 }
5304
5305 fn edit_prediction_requires_modifier(&self) -> bool {
5306 match self.edit_prediction_settings {
5307 EditPredictionSettings::Disabled => false,
5308 EditPredictionSettings::Enabled {
5309 preview_requires_modifier,
5310 ..
5311 } => preview_requires_modifier,
5312 }
5313 }
5314
5315 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5316 if self.edit_prediction_provider.is_none() {
5317 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5318 } else {
5319 let selection = self.selections.newest_anchor();
5320 let cursor = selection.head();
5321
5322 if let Some((buffer, cursor_buffer_position)) =
5323 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5324 {
5325 self.edit_prediction_settings =
5326 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5327 }
5328 }
5329 }
5330
5331 fn edit_prediction_settings_at_position(
5332 &self,
5333 buffer: &Entity<Buffer>,
5334 buffer_position: language::Anchor,
5335 cx: &App,
5336 ) -> EditPredictionSettings {
5337 if self.mode != EditorMode::Full
5338 || !self.show_inline_completions_override.unwrap_or(true)
5339 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5340 {
5341 return EditPredictionSettings::Disabled;
5342 }
5343
5344 let buffer = buffer.read(cx);
5345
5346 let file = buffer.file();
5347
5348 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5349 return EditPredictionSettings::Disabled;
5350 };
5351
5352 let by_provider = matches!(
5353 self.menu_inline_completions_policy,
5354 MenuInlineCompletionsPolicy::ByProvider
5355 );
5356
5357 let show_in_menu = by_provider
5358 && self
5359 .edit_prediction_provider
5360 .as_ref()
5361 .map_or(false, |provider| {
5362 provider.provider.show_completions_in_menu()
5363 });
5364
5365 let preview_requires_modifier =
5366 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5367
5368 EditPredictionSettings::Enabled {
5369 show_in_menu,
5370 preview_requires_modifier,
5371 }
5372 }
5373
5374 fn should_show_edit_predictions(&self) -> bool {
5375 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5376 }
5377
5378 pub fn edit_prediction_preview_is_active(&self) -> bool {
5379 matches!(
5380 self.edit_prediction_preview,
5381 EditPredictionPreview::Active { .. }
5382 )
5383 }
5384
5385 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5386 let cursor = self.selections.newest_anchor().head();
5387 if let Some((buffer, cursor_position)) =
5388 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5389 {
5390 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5391 } else {
5392 false
5393 }
5394 }
5395
5396 fn edit_predictions_enabled_in_buffer(
5397 &self,
5398 buffer: &Entity<Buffer>,
5399 buffer_position: language::Anchor,
5400 cx: &App,
5401 ) -> bool {
5402 maybe!({
5403 if self.read_only(cx) {
5404 return Some(false);
5405 }
5406 let provider = self.edit_prediction_provider()?;
5407 if !provider.is_enabled(&buffer, buffer_position, cx) {
5408 return Some(false);
5409 }
5410 let buffer = buffer.read(cx);
5411 let Some(file) = buffer.file() else {
5412 return Some(true);
5413 };
5414 let settings = all_language_settings(Some(file), cx);
5415 Some(settings.edit_predictions_enabled_for_file(file, cx))
5416 })
5417 .unwrap_or(false)
5418 }
5419
5420 fn cycle_inline_completion(
5421 &mut self,
5422 direction: Direction,
5423 window: &mut Window,
5424 cx: &mut Context<Self>,
5425 ) -> Option<()> {
5426 let provider = self.edit_prediction_provider()?;
5427 let cursor = self.selections.newest_anchor().head();
5428 let (buffer, cursor_buffer_position) =
5429 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5430 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5431 return None;
5432 }
5433
5434 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5435 self.update_visible_inline_completion(window, cx);
5436
5437 Some(())
5438 }
5439
5440 pub fn show_inline_completion(
5441 &mut self,
5442 _: &ShowEditPrediction,
5443 window: &mut Window,
5444 cx: &mut Context<Self>,
5445 ) {
5446 if !self.has_active_inline_completion() {
5447 self.refresh_inline_completion(false, true, window, cx);
5448 return;
5449 }
5450
5451 self.update_visible_inline_completion(window, cx);
5452 }
5453
5454 pub fn display_cursor_names(
5455 &mut self,
5456 _: &DisplayCursorNames,
5457 window: &mut Window,
5458 cx: &mut Context<Self>,
5459 ) {
5460 self.show_cursor_names(window, cx);
5461 }
5462
5463 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5464 self.show_cursor_names = true;
5465 cx.notify();
5466 cx.spawn_in(window, async move |this, cx| {
5467 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5468 this.update(cx, |this, cx| {
5469 this.show_cursor_names = false;
5470 cx.notify()
5471 })
5472 .ok()
5473 })
5474 .detach();
5475 }
5476
5477 pub fn next_edit_prediction(
5478 &mut self,
5479 _: &NextEditPrediction,
5480 window: &mut Window,
5481 cx: &mut Context<Self>,
5482 ) {
5483 if self.has_active_inline_completion() {
5484 self.cycle_inline_completion(Direction::Next, window, cx);
5485 } else {
5486 let is_copilot_disabled = self
5487 .refresh_inline_completion(false, true, window, cx)
5488 .is_none();
5489 if is_copilot_disabled {
5490 cx.propagate();
5491 }
5492 }
5493 }
5494
5495 pub fn previous_edit_prediction(
5496 &mut self,
5497 _: &PreviousEditPrediction,
5498 window: &mut Window,
5499 cx: &mut Context<Self>,
5500 ) {
5501 if self.has_active_inline_completion() {
5502 self.cycle_inline_completion(Direction::Prev, window, cx);
5503 } else {
5504 let is_copilot_disabled = self
5505 .refresh_inline_completion(false, true, window, cx)
5506 .is_none();
5507 if is_copilot_disabled {
5508 cx.propagate();
5509 }
5510 }
5511 }
5512
5513 pub fn accept_edit_prediction(
5514 &mut self,
5515 _: &AcceptEditPrediction,
5516 window: &mut Window,
5517 cx: &mut Context<Self>,
5518 ) {
5519 if self.show_edit_predictions_in_menu() {
5520 self.hide_context_menu(window, cx);
5521 }
5522
5523 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5524 return;
5525 };
5526
5527 self.report_inline_completion_event(
5528 active_inline_completion.completion_id.clone(),
5529 true,
5530 cx,
5531 );
5532
5533 match &active_inline_completion.completion {
5534 InlineCompletion::Move { target, .. } => {
5535 let target = *target;
5536
5537 if let Some(position_map) = &self.last_position_map {
5538 if position_map
5539 .visible_row_range
5540 .contains(&target.to_display_point(&position_map.snapshot).row())
5541 || !self.edit_prediction_requires_modifier()
5542 {
5543 self.unfold_ranges(&[target..target], true, false, cx);
5544 // Note that this is also done in vim's handler of the Tab action.
5545 self.change_selections(
5546 Some(Autoscroll::newest()),
5547 window,
5548 cx,
5549 |selections| {
5550 selections.select_anchor_ranges([target..target]);
5551 },
5552 );
5553 self.clear_row_highlights::<EditPredictionPreview>();
5554
5555 self.edit_prediction_preview
5556 .set_previous_scroll_position(None);
5557 } else {
5558 self.edit_prediction_preview
5559 .set_previous_scroll_position(Some(
5560 position_map.snapshot.scroll_anchor,
5561 ));
5562
5563 self.highlight_rows::<EditPredictionPreview>(
5564 target..target,
5565 cx.theme().colors().editor_highlighted_line_background,
5566 true,
5567 cx,
5568 );
5569 self.request_autoscroll(Autoscroll::fit(), cx);
5570 }
5571 }
5572 }
5573 InlineCompletion::Edit { edits, .. } => {
5574 if let Some(provider) = self.edit_prediction_provider() {
5575 provider.accept(cx);
5576 }
5577
5578 let snapshot = self.buffer.read(cx).snapshot(cx);
5579 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5580
5581 self.buffer.update(cx, |buffer, cx| {
5582 buffer.edit(edits.iter().cloned(), None, cx)
5583 });
5584
5585 self.change_selections(None, window, cx, |s| {
5586 s.select_anchor_ranges([last_edit_end..last_edit_end])
5587 });
5588
5589 self.update_visible_inline_completion(window, cx);
5590 if self.active_inline_completion.is_none() {
5591 self.refresh_inline_completion(true, true, window, cx);
5592 }
5593
5594 cx.notify();
5595 }
5596 }
5597
5598 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5599 }
5600
5601 pub fn accept_partial_inline_completion(
5602 &mut self,
5603 _: &AcceptPartialEditPrediction,
5604 window: &mut Window,
5605 cx: &mut Context<Self>,
5606 ) {
5607 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5608 return;
5609 };
5610 if self.selections.count() != 1 {
5611 return;
5612 }
5613
5614 self.report_inline_completion_event(
5615 active_inline_completion.completion_id.clone(),
5616 true,
5617 cx,
5618 );
5619
5620 match &active_inline_completion.completion {
5621 InlineCompletion::Move { target, .. } => {
5622 let target = *target;
5623 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5624 selections.select_anchor_ranges([target..target]);
5625 });
5626 }
5627 InlineCompletion::Edit { edits, .. } => {
5628 // Find an insertion that starts at the cursor position.
5629 let snapshot = self.buffer.read(cx).snapshot(cx);
5630 let cursor_offset = self.selections.newest::<usize>(cx).head();
5631 let insertion = edits.iter().find_map(|(range, text)| {
5632 let range = range.to_offset(&snapshot);
5633 if range.is_empty() && range.start == cursor_offset {
5634 Some(text)
5635 } else {
5636 None
5637 }
5638 });
5639
5640 if let Some(text) = insertion {
5641 let mut partial_completion = text
5642 .chars()
5643 .by_ref()
5644 .take_while(|c| c.is_alphabetic())
5645 .collect::<String>();
5646 if partial_completion.is_empty() {
5647 partial_completion = text
5648 .chars()
5649 .by_ref()
5650 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5651 .collect::<String>();
5652 }
5653
5654 cx.emit(EditorEvent::InputHandled {
5655 utf16_range_to_replace: None,
5656 text: partial_completion.clone().into(),
5657 });
5658
5659 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5660
5661 self.refresh_inline_completion(true, true, window, cx);
5662 cx.notify();
5663 } else {
5664 self.accept_edit_prediction(&Default::default(), window, cx);
5665 }
5666 }
5667 }
5668 }
5669
5670 fn discard_inline_completion(
5671 &mut self,
5672 should_report_inline_completion_event: bool,
5673 cx: &mut Context<Self>,
5674 ) -> bool {
5675 if should_report_inline_completion_event {
5676 let completion_id = self
5677 .active_inline_completion
5678 .as_ref()
5679 .and_then(|active_completion| active_completion.completion_id.clone());
5680
5681 self.report_inline_completion_event(completion_id, false, cx);
5682 }
5683
5684 if let Some(provider) = self.edit_prediction_provider() {
5685 provider.discard(cx);
5686 }
5687
5688 self.take_active_inline_completion(cx)
5689 }
5690
5691 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5692 let Some(provider) = self.edit_prediction_provider() else {
5693 return;
5694 };
5695
5696 let Some((_, buffer, _)) = self
5697 .buffer
5698 .read(cx)
5699 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5700 else {
5701 return;
5702 };
5703
5704 let extension = buffer
5705 .read(cx)
5706 .file()
5707 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5708
5709 let event_type = match accepted {
5710 true => "Edit Prediction Accepted",
5711 false => "Edit Prediction Discarded",
5712 };
5713 telemetry::event!(
5714 event_type,
5715 provider = provider.name(),
5716 prediction_id = id,
5717 suggestion_accepted = accepted,
5718 file_extension = extension,
5719 );
5720 }
5721
5722 pub fn has_active_inline_completion(&self) -> bool {
5723 self.active_inline_completion.is_some()
5724 }
5725
5726 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5727 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5728 return false;
5729 };
5730
5731 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5732 self.clear_highlights::<InlineCompletionHighlight>(cx);
5733 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5734 true
5735 }
5736
5737 /// Returns true when we're displaying the edit prediction popover below the cursor
5738 /// like we are not previewing and the LSP autocomplete menu is visible
5739 /// or we are in `when_holding_modifier` mode.
5740 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5741 if self.edit_prediction_preview_is_active()
5742 || !self.show_edit_predictions_in_menu()
5743 || !self.edit_predictions_enabled()
5744 {
5745 return false;
5746 }
5747
5748 if self.has_visible_completions_menu() {
5749 return true;
5750 }
5751
5752 has_completion && self.edit_prediction_requires_modifier()
5753 }
5754
5755 fn handle_modifiers_changed(
5756 &mut self,
5757 modifiers: Modifiers,
5758 position_map: &PositionMap,
5759 window: &mut Window,
5760 cx: &mut Context<Self>,
5761 ) {
5762 if self.show_edit_predictions_in_menu() {
5763 self.update_edit_prediction_preview(&modifiers, window, cx);
5764 }
5765
5766 self.update_selection_mode(&modifiers, position_map, window, cx);
5767
5768 let mouse_position = window.mouse_position();
5769 if !position_map.text_hitbox.is_hovered(window) {
5770 return;
5771 }
5772
5773 self.update_hovered_link(
5774 position_map.point_for_position(mouse_position),
5775 &position_map.snapshot,
5776 modifiers,
5777 window,
5778 cx,
5779 )
5780 }
5781
5782 fn update_selection_mode(
5783 &mut self,
5784 modifiers: &Modifiers,
5785 position_map: &PositionMap,
5786 window: &mut Window,
5787 cx: &mut Context<Self>,
5788 ) {
5789 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5790 return;
5791 }
5792
5793 let mouse_position = window.mouse_position();
5794 let point_for_position = position_map.point_for_position(mouse_position);
5795 let position = point_for_position.previous_valid;
5796
5797 self.select(
5798 SelectPhase::BeginColumnar {
5799 position,
5800 reset: false,
5801 goal_column: point_for_position.exact_unclipped.column(),
5802 },
5803 window,
5804 cx,
5805 );
5806 }
5807
5808 fn update_edit_prediction_preview(
5809 &mut self,
5810 modifiers: &Modifiers,
5811 window: &mut Window,
5812 cx: &mut Context<Self>,
5813 ) {
5814 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5815 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5816 return;
5817 };
5818
5819 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5820 if matches!(
5821 self.edit_prediction_preview,
5822 EditPredictionPreview::Inactive { .. }
5823 ) {
5824 self.edit_prediction_preview = EditPredictionPreview::Active {
5825 previous_scroll_position: None,
5826 since: Instant::now(),
5827 };
5828
5829 self.update_visible_inline_completion(window, cx);
5830 cx.notify();
5831 }
5832 } else if let EditPredictionPreview::Active {
5833 previous_scroll_position,
5834 since,
5835 } = self.edit_prediction_preview
5836 {
5837 if let (Some(previous_scroll_position), Some(position_map)) =
5838 (previous_scroll_position, self.last_position_map.as_ref())
5839 {
5840 self.set_scroll_position(
5841 previous_scroll_position
5842 .scroll_position(&position_map.snapshot.display_snapshot),
5843 window,
5844 cx,
5845 );
5846 }
5847
5848 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5849 released_too_fast: since.elapsed() < Duration::from_millis(200),
5850 };
5851 self.clear_row_highlights::<EditPredictionPreview>();
5852 self.update_visible_inline_completion(window, cx);
5853 cx.notify();
5854 }
5855 }
5856
5857 fn update_visible_inline_completion(
5858 &mut self,
5859 _window: &mut Window,
5860 cx: &mut Context<Self>,
5861 ) -> Option<()> {
5862 let selection = self.selections.newest_anchor();
5863 let cursor = selection.head();
5864 let multibuffer = self.buffer.read(cx).snapshot(cx);
5865 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5866 let excerpt_id = cursor.excerpt_id;
5867
5868 let show_in_menu = self.show_edit_predictions_in_menu();
5869 let completions_menu_has_precedence = !show_in_menu
5870 && (self.context_menu.borrow().is_some()
5871 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5872
5873 if completions_menu_has_precedence
5874 || !offset_selection.is_empty()
5875 || self
5876 .active_inline_completion
5877 .as_ref()
5878 .map_or(false, |completion| {
5879 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5880 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5881 !invalidation_range.contains(&offset_selection.head())
5882 })
5883 {
5884 self.discard_inline_completion(false, cx);
5885 return None;
5886 }
5887
5888 self.take_active_inline_completion(cx);
5889 let Some(provider) = self.edit_prediction_provider() else {
5890 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5891 return None;
5892 };
5893
5894 let (buffer, cursor_buffer_position) =
5895 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5896
5897 self.edit_prediction_settings =
5898 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5899
5900 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5901
5902 if self.edit_prediction_indent_conflict {
5903 let cursor_point = cursor.to_point(&multibuffer);
5904
5905 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5906
5907 if let Some((_, indent)) = indents.iter().next() {
5908 if indent.len == cursor_point.column {
5909 self.edit_prediction_indent_conflict = false;
5910 }
5911 }
5912 }
5913
5914 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5915 let edits = inline_completion
5916 .edits
5917 .into_iter()
5918 .flat_map(|(range, new_text)| {
5919 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5920 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5921 Some((start..end, new_text))
5922 })
5923 .collect::<Vec<_>>();
5924 if edits.is_empty() {
5925 return None;
5926 }
5927
5928 let first_edit_start = edits.first().unwrap().0.start;
5929 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5930 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5931
5932 let last_edit_end = edits.last().unwrap().0.end;
5933 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5934 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5935
5936 let cursor_row = cursor.to_point(&multibuffer).row;
5937
5938 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5939
5940 let mut inlay_ids = Vec::new();
5941 let invalidation_row_range;
5942 let move_invalidation_row_range = if cursor_row < edit_start_row {
5943 Some(cursor_row..edit_end_row)
5944 } else if cursor_row > edit_end_row {
5945 Some(edit_start_row..cursor_row)
5946 } else {
5947 None
5948 };
5949 let is_move =
5950 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5951 let completion = if is_move {
5952 invalidation_row_range =
5953 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5954 let target = first_edit_start;
5955 InlineCompletion::Move { target, snapshot }
5956 } else {
5957 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5958 && !self.inline_completions_hidden_for_vim_mode;
5959
5960 if show_completions_in_buffer {
5961 if edits
5962 .iter()
5963 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5964 {
5965 let mut inlays = Vec::new();
5966 for (range, new_text) in &edits {
5967 let inlay = Inlay::inline_completion(
5968 post_inc(&mut self.next_inlay_id),
5969 range.start,
5970 new_text.as_str(),
5971 );
5972 inlay_ids.push(inlay.id);
5973 inlays.push(inlay);
5974 }
5975
5976 self.splice_inlays(&[], inlays, cx);
5977 } else {
5978 let background_color = cx.theme().status().deleted_background;
5979 self.highlight_text::<InlineCompletionHighlight>(
5980 edits.iter().map(|(range, _)| range.clone()).collect(),
5981 HighlightStyle {
5982 background_color: Some(background_color),
5983 ..Default::default()
5984 },
5985 cx,
5986 );
5987 }
5988 }
5989
5990 invalidation_row_range = edit_start_row..edit_end_row;
5991
5992 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5993 if provider.show_tab_accept_marker() {
5994 EditDisplayMode::TabAccept
5995 } else {
5996 EditDisplayMode::Inline
5997 }
5998 } else {
5999 EditDisplayMode::DiffPopover
6000 };
6001
6002 InlineCompletion::Edit {
6003 edits,
6004 edit_preview: inline_completion.edit_preview,
6005 display_mode,
6006 snapshot,
6007 }
6008 };
6009
6010 let invalidation_range = multibuffer
6011 .anchor_before(Point::new(invalidation_row_range.start, 0))
6012 ..multibuffer.anchor_after(Point::new(
6013 invalidation_row_range.end,
6014 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
6015 ));
6016
6017 self.stale_inline_completion_in_menu = None;
6018 self.active_inline_completion = Some(InlineCompletionState {
6019 inlay_ids,
6020 completion,
6021 completion_id: inline_completion.id,
6022 invalidation_range,
6023 });
6024
6025 cx.notify();
6026
6027 Some(())
6028 }
6029
6030 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
6031 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
6032 }
6033
6034 fn render_code_actions_indicator(
6035 &self,
6036 _style: &EditorStyle,
6037 row: DisplayRow,
6038 is_active: bool,
6039 breakpoint: Option<&(Anchor, Breakpoint)>,
6040 cx: &mut Context<Self>,
6041 ) -> Option<IconButton> {
6042 let color = Color::Muted;
6043 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6044
6045 if self.available_code_actions.is_some() {
6046 Some(
6047 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
6048 .shape(ui::IconButtonShape::Square)
6049 .icon_size(IconSize::XSmall)
6050 .icon_color(color)
6051 .toggle_state(is_active)
6052 .tooltip({
6053 let focus_handle = self.focus_handle.clone();
6054 move |window, cx| {
6055 Tooltip::for_action_in(
6056 "Toggle Code Actions",
6057 &ToggleCodeActions {
6058 deployed_from_indicator: None,
6059 },
6060 &focus_handle,
6061 window,
6062 cx,
6063 )
6064 }
6065 })
6066 .on_click(cx.listener(move |editor, _e, window, cx| {
6067 window.focus(&editor.focus_handle(cx));
6068 editor.toggle_code_actions(
6069 &ToggleCodeActions {
6070 deployed_from_indicator: Some(row),
6071 },
6072 window,
6073 cx,
6074 );
6075 }))
6076 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6077 editor.set_breakpoint_context_menu(
6078 row,
6079 position,
6080 event.down.position,
6081 window,
6082 cx,
6083 );
6084 })),
6085 )
6086 } else {
6087 None
6088 }
6089 }
6090
6091 fn clear_tasks(&mut self) {
6092 self.tasks.clear()
6093 }
6094
6095 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6096 if self.tasks.insert(key, value).is_some() {
6097 // This case should hopefully be rare, but just in case...
6098 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6099 }
6100 }
6101
6102 /// Get all display points of breakpoints that will be rendered within editor
6103 ///
6104 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6105 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6106 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6107 fn active_breakpoints(
6108 &mut self,
6109 range: Range<DisplayRow>,
6110 window: &mut Window,
6111 cx: &mut Context<Self>,
6112 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6113 let mut breakpoint_display_points = HashMap::default();
6114
6115 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6116 return breakpoint_display_points;
6117 };
6118
6119 let snapshot = self.snapshot(window, cx);
6120
6121 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6122 let Some(project) = self.project.as_ref() else {
6123 return breakpoint_display_points;
6124 };
6125
6126 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6127 let buffer_snapshot = buffer.read(cx).snapshot();
6128
6129 for breakpoint in
6130 breakpoint_store
6131 .read(cx)
6132 .breakpoints(&buffer, None, &buffer_snapshot, cx)
6133 {
6134 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6135 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6136 anchor.text_anchor = breakpoint.0;
6137
6138 breakpoint_display_points.insert(
6139 snapshot
6140 .point_to_display_point(
6141 MultiBufferPoint {
6142 row: point.row,
6143 column: point.column,
6144 },
6145 Bias::Left,
6146 )
6147 .row(),
6148 (anchor, breakpoint.1.clone()),
6149 );
6150 }
6151
6152 return breakpoint_display_points;
6153 }
6154
6155 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6156 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6157
6158 for (buffer_snapshot, range, excerpt_id) in
6159 multi_buffer_snapshot.range_to_buffer_ranges(range)
6160 {
6161 let Some(buffer) = project.read_with(cx, |this, cx| {
6162 this.buffer_for_id(buffer_snapshot.remote_id(), cx)
6163 }) else {
6164 continue;
6165 };
6166 let breakpoints = breakpoint_store.read(cx).breakpoints(
6167 &buffer,
6168 Some(
6169 buffer_snapshot.anchor_before(range.start)
6170 ..buffer_snapshot.anchor_after(range.end),
6171 ),
6172 buffer_snapshot,
6173 cx,
6174 );
6175 for (anchor, breakpoint) in breakpoints {
6176 let multi_buffer_anchor =
6177 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), *anchor);
6178 let position = multi_buffer_anchor
6179 .to_point(&multi_buffer_snapshot)
6180 .to_display_point(&snapshot);
6181
6182 breakpoint_display_points
6183 .insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
6184 }
6185 }
6186
6187 breakpoint_display_points
6188 }
6189
6190 fn breakpoint_context_menu(
6191 &self,
6192 anchor: Anchor,
6193 window: &mut Window,
6194 cx: &mut Context<Self>,
6195 ) -> Entity<ui::ContextMenu> {
6196 let weak_editor = cx.weak_entity();
6197 let focus_handle = self.focus_handle(cx);
6198
6199 let row = self
6200 .buffer
6201 .read(cx)
6202 .snapshot(cx)
6203 .summary_for_anchor::<Point>(&anchor)
6204 .row;
6205
6206 let breakpoint = self
6207 .breakpoint_at_row(row, window, cx)
6208 .map(|(_, bp)| Arc::from(bp));
6209
6210 let log_breakpoint_msg = if breakpoint
6211 .as_ref()
6212 .is_some_and(|bp| bp.kind.log_message().is_some())
6213 {
6214 "Edit Log Breakpoint"
6215 } else {
6216 "Set Log Breakpoint"
6217 };
6218
6219 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
6220 "Unset Breakpoint"
6221 } else {
6222 "Set Breakpoint"
6223 };
6224
6225 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.state {
6226 BreakpointState::Enabled => Some("Disable"),
6227 BreakpointState::Disabled => Some("Enable"),
6228 });
6229
6230 let breakpoint = breakpoint.unwrap_or_else(|| {
6231 Arc::new(Breakpoint {
6232 state: BreakpointState::Enabled,
6233 kind: BreakpointKind::Standard,
6234 })
6235 });
6236
6237 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6238 menu.on_blur_subscription(Subscription::new(|| {}))
6239 .context(focus_handle)
6240 .when_some(toggle_state_msg, |this, msg| {
6241 this.entry(msg, None, {
6242 let weak_editor = weak_editor.clone();
6243 let breakpoint = breakpoint.clone();
6244 move |_window, cx| {
6245 weak_editor
6246 .update(cx, |this, cx| {
6247 this.edit_breakpoint_at_anchor(
6248 anchor,
6249 breakpoint.as_ref().clone(),
6250 BreakpointEditAction::InvertState,
6251 cx,
6252 );
6253 })
6254 .log_err();
6255 }
6256 })
6257 })
6258 .entry(set_breakpoint_msg, None, {
6259 let weak_editor = weak_editor.clone();
6260 let breakpoint = breakpoint.clone();
6261 move |_window, cx| {
6262 weak_editor
6263 .update(cx, |this, cx| {
6264 this.edit_breakpoint_at_anchor(
6265 anchor,
6266 breakpoint.as_ref().clone(),
6267 BreakpointEditAction::Toggle,
6268 cx,
6269 );
6270 })
6271 .log_err();
6272 }
6273 })
6274 .entry(log_breakpoint_msg, None, move |window, cx| {
6275 weak_editor
6276 .update(cx, |this, cx| {
6277 this.add_edit_breakpoint_block(anchor, breakpoint.as_ref(), window, cx);
6278 })
6279 .log_err();
6280 })
6281 })
6282 }
6283
6284 fn render_breakpoint(
6285 &self,
6286 position: Anchor,
6287 row: DisplayRow,
6288 breakpoint: &Breakpoint,
6289 cx: &mut Context<Self>,
6290 ) -> IconButton {
6291 let (color, icon) = {
6292 let color = if self
6293 .gutter_breakpoint_indicator
6294 .is_some_and(|point| point.row() == row)
6295 {
6296 Color::Hint
6297 } else if breakpoint.is_disabled() {
6298 Color::Custom(Color::Debugger.color(cx).opacity(0.5))
6299 } else {
6300 Color::Debugger
6301 };
6302 let icon = match &breakpoint.kind {
6303 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6304 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6305 };
6306 (color, icon)
6307 };
6308
6309 let breakpoint = Arc::from(breakpoint.clone());
6310
6311 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6312 .icon_size(IconSize::XSmall)
6313 .size(ui::ButtonSize::None)
6314 .icon_color(color)
6315 .style(ButtonStyle::Transparent)
6316 .on_click(cx.listener({
6317 let breakpoint = breakpoint.clone();
6318
6319 move |editor, _e, window, cx| {
6320 window.focus(&editor.focus_handle(cx));
6321 editor.edit_breakpoint_at_anchor(
6322 position,
6323 breakpoint.as_ref().clone(),
6324 BreakpointEditAction::Toggle,
6325 cx,
6326 );
6327 }
6328 }))
6329 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6330 editor.set_breakpoint_context_menu(
6331 row,
6332 Some(position),
6333 event.down.position,
6334 window,
6335 cx,
6336 );
6337 }))
6338 }
6339
6340 fn build_tasks_context(
6341 project: &Entity<Project>,
6342 buffer: &Entity<Buffer>,
6343 buffer_row: u32,
6344 tasks: &Arc<RunnableTasks>,
6345 cx: &mut Context<Self>,
6346 ) -> Task<Option<task::TaskContext>> {
6347 let position = Point::new(buffer_row, tasks.column);
6348 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6349 let location = Location {
6350 buffer: buffer.clone(),
6351 range: range_start..range_start,
6352 };
6353 // Fill in the environmental variables from the tree-sitter captures
6354 let mut captured_task_variables = TaskVariables::default();
6355 for (capture_name, value) in tasks.extra_variables.clone() {
6356 captured_task_variables.insert(
6357 task::VariableName::Custom(capture_name.into()),
6358 value.clone(),
6359 );
6360 }
6361 project.update(cx, |project, cx| {
6362 project.task_store().update(cx, |task_store, cx| {
6363 task_store.task_context_for_location(captured_task_variables, location, cx)
6364 })
6365 })
6366 }
6367
6368 pub fn spawn_nearest_task(
6369 &mut self,
6370 action: &SpawnNearestTask,
6371 window: &mut Window,
6372 cx: &mut Context<Self>,
6373 ) {
6374 let Some((workspace, _)) = self.workspace.clone() else {
6375 return;
6376 };
6377 let Some(project) = self.project.clone() else {
6378 return;
6379 };
6380
6381 // Try to find a closest, enclosing node using tree-sitter that has a
6382 // task
6383 let Some((buffer, buffer_row, tasks)) = self
6384 .find_enclosing_node_task(cx)
6385 // Or find the task that's closest in row-distance.
6386 .or_else(|| self.find_closest_task(cx))
6387 else {
6388 return;
6389 };
6390
6391 let reveal_strategy = action.reveal;
6392 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6393 cx.spawn_in(window, async move |_, cx| {
6394 let context = task_context.await?;
6395 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6396
6397 let resolved = resolved_task.resolved.as_mut()?;
6398 resolved.reveal = reveal_strategy;
6399
6400 workspace
6401 .update(cx, |workspace, cx| {
6402 workspace::tasks::schedule_resolved_task(
6403 workspace,
6404 task_source_kind,
6405 resolved_task,
6406 false,
6407 cx,
6408 );
6409 })
6410 .ok()
6411 })
6412 .detach();
6413 }
6414
6415 fn find_closest_task(
6416 &mut self,
6417 cx: &mut Context<Self>,
6418 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6419 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6420
6421 let ((buffer_id, row), tasks) = self
6422 .tasks
6423 .iter()
6424 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6425
6426 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6427 let tasks = Arc::new(tasks.to_owned());
6428 Some((buffer, *row, tasks))
6429 }
6430
6431 fn find_enclosing_node_task(
6432 &mut self,
6433 cx: &mut Context<Self>,
6434 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6435 let snapshot = self.buffer.read(cx).snapshot(cx);
6436 let offset = self.selections.newest::<usize>(cx).head();
6437 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6438 let buffer_id = excerpt.buffer().remote_id();
6439
6440 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6441 let mut cursor = layer.node().walk();
6442
6443 while cursor.goto_first_child_for_byte(offset).is_some() {
6444 if cursor.node().end_byte() == offset {
6445 cursor.goto_next_sibling();
6446 }
6447 }
6448
6449 // Ascend to the smallest ancestor that contains the range and has a task.
6450 loop {
6451 let node = cursor.node();
6452 let node_range = node.byte_range();
6453 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6454
6455 // Check if this node contains our offset
6456 if node_range.start <= offset && node_range.end >= offset {
6457 // If it contains offset, check for task
6458 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6459 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6460 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6461 }
6462 }
6463
6464 if !cursor.goto_parent() {
6465 break;
6466 }
6467 }
6468 None
6469 }
6470
6471 fn render_run_indicator(
6472 &self,
6473 _style: &EditorStyle,
6474 is_active: bool,
6475 row: DisplayRow,
6476 breakpoint: Option<(Anchor, Breakpoint)>,
6477 cx: &mut Context<Self>,
6478 ) -> IconButton {
6479 let color = Color::Muted;
6480 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6481
6482 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6483 .shape(ui::IconButtonShape::Square)
6484 .icon_size(IconSize::XSmall)
6485 .icon_color(color)
6486 .toggle_state(is_active)
6487 .on_click(cx.listener(move |editor, _e, window, cx| {
6488 window.focus(&editor.focus_handle(cx));
6489 editor.toggle_code_actions(
6490 &ToggleCodeActions {
6491 deployed_from_indicator: Some(row),
6492 },
6493 window,
6494 cx,
6495 );
6496 }))
6497 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6498 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
6499 }))
6500 }
6501
6502 pub fn context_menu_visible(&self) -> bool {
6503 !self.edit_prediction_preview_is_active()
6504 && self
6505 .context_menu
6506 .borrow()
6507 .as_ref()
6508 .map_or(false, |menu| menu.visible())
6509 }
6510
6511 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6512 self.context_menu
6513 .borrow()
6514 .as_ref()
6515 .map(|menu| menu.origin())
6516 }
6517
6518 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
6519 self.context_menu_options = Some(options);
6520 }
6521
6522 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6523 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6524
6525 fn render_edit_prediction_popover(
6526 &mut self,
6527 text_bounds: &Bounds<Pixels>,
6528 content_origin: gpui::Point<Pixels>,
6529 editor_snapshot: &EditorSnapshot,
6530 visible_row_range: Range<DisplayRow>,
6531 scroll_top: f32,
6532 scroll_bottom: f32,
6533 line_layouts: &[LineWithInvisibles],
6534 line_height: Pixels,
6535 scroll_pixel_position: gpui::Point<Pixels>,
6536 newest_selection_head: Option<DisplayPoint>,
6537 editor_width: Pixels,
6538 style: &EditorStyle,
6539 window: &mut Window,
6540 cx: &mut App,
6541 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6542 let active_inline_completion = self.active_inline_completion.as_ref()?;
6543
6544 if self.edit_prediction_visible_in_cursor_popover(true) {
6545 return None;
6546 }
6547
6548 match &active_inline_completion.completion {
6549 InlineCompletion::Move { target, .. } => {
6550 let target_display_point = target.to_display_point(editor_snapshot);
6551
6552 if self.edit_prediction_requires_modifier() {
6553 if !self.edit_prediction_preview_is_active() {
6554 return None;
6555 }
6556
6557 self.render_edit_prediction_modifier_jump_popover(
6558 text_bounds,
6559 content_origin,
6560 visible_row_range,
6561 line_layouts,
6562 line_height,
6563 scroll_pixel_position,
6564 newest_selection_head,
6565 target_display_point,
6566 window,
6567 cx,
6568 )
6569 } else {
6570 self.render_edit_prediction_eager_jump_popover(
6571 text_bounds,
6572 content_origin,
6573 editor_snapshot,
6574 visible_row_range,
6575 scroll_top,
6576 scroll_bottom,
6577 line_height,
6578 scroll_pixel_position,
6579 target_display_point,
6580 editor_width,
6581 window,
6582 cx,
6583 )
6584 }
6585 }
6586 InlineCompletion::Edit {
6587 display_mode: EditDisplayMode::Inline,
6588 ..
6589 } => None,
6590 InlineCompletion::Edit {
6591 display_mode: EditDisplayMode::TabAccept,
6592 edits,
6593 ..
6594 } => {
6595 let range = &edits.first()?.0;
6596 let target_display_point = range.end.to_display_point(editor_snapshot);
6597
6598 self.render_edit_prediction_end_of_line_popover(
6599 "Accept",
6600 editor_snapshot,
6601 visible_row_range,
6602 target_display_point,
6603 line_height,
6604 scroll_pixel_position,
6605 content_origin,
6606 editor_width,
6607 window,
6608 cx,
6609 )
6610 }
6611 InlineCompletion::Edit {
6612 edits,
6613 edit_preview,
6614 display_mode: EditDisplayMode::DiffPopover,
6615 snapshot,
6616 } => self.render_edit_prediction_diff_popover(
6617 text_bounds,
6618 content_origin,
6619 editor_snapshot,
6620 visible_row_range,
6621 line_layouts,
6622 line_height,
6623 scroll_pixel_position,
6624 newest_selection_head,
6625 editor_width,
6626 style,
6627 edits,
6628 edit_preview,
6629 snapshot,
6630 window,
6631 cx,
6632 ),
6633 }
6634 }
6635
6636 fn render_edit_prediction_modifier_jump_popover(
6637 &mut self,
6638 text_bounds: &Bounds<Pixels>,
6639 content_origin: gpui::Point<Pixels>,
6640 visible_row_range: Range<DisplayRow>,
6641 line_layouts: &[LineWithInvisibles],
6642 line_height: Pixels,
6643 scroll_pixel_position: gpui::Point<Pixels>,
6644 newest_selection_head: Option<DisplayPoint>,
6645 target_display_point: DisplayPoint,
6646 window: &mut Window,
6647 cx: &mut App,
6648 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6649 let scrolled_content_origin =
6650 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6651
6652 const SCROLL_PADDING_Y: Pixels = px(12.);
6653
6654 if target_display_point.row() < visible_row_range.start {
6655 return self.render_edit_prediction_scroll_popover(
6656 |_| SCROLL_PADDING_Y,
6657 IconName::ArrowUp,
6658 visible_row_range,
6659 line_layouts,
6660 newest_selection_head,
6661 scrolled_content_origin,
6662 window,
6663 cx,
6664 );
6665 } else if target_display_point.row() >= visible_row_range.end {
6666 return self.render_edit_prediction_scroll_popover(
6667 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6668 IconName::ArrowDown,
6669 visible_row_range,
6670 line_layouts,
6671 newest_selection_head,
6672 scrolled_content_origin,
6673 window,
6674 cx,
6675 );
6676 }
6677
6678 const POLE_WIDTH: Pixels = px(2.);
6679
6680 let line_layout =
6681 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6682 let target_column = target_display_point.column() as usize;
6683
6684 let target_x = line_layout.x_for_index(target_column);
6685 let target_y =
6686 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6687
6688 let flag_on_right = target_x < text_bounds.size.width / 2.;
6689
6690 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6691 border_color.l += 0.001;
6692
6693 let mut element = v_flex()
6694 .items_end()
6695 .when(flag_on_right, |el| el.items_start())
6696 .child(if flag_on_right {
6697 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6698 .rounded_bl(px(0.))
6699 .rounded_tl(px(0.))
6700 .border_l_2()
6701 .border_color(border_color)
6702 } else {
6703 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6704 .rounded_br(px(0.))
6705 .rounded_tr(px(0.))
6706 .border_r_2()
6707 .border_color(border_color)
6708 })
6709 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6710 .into_any();
6711
6712 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6713
6714 let mut origin = scrolled_content_origin + point(target_x, target_y)
6715 - point(
6716 if flag_on_right {
6717 POLE_WIDTH
6718 } else {
6719 size.width - POLE_WIDTH
6720 },
6721 size.height - line_height,
6722 );
6723
6724 origin.x = origin.x.max(content_origin.x);
6725
6726 element.prepaint_at(origin, window, cx);
6727
6728 Some((element, origin))
6729 }
6730
6731 fn render_edit_prediction_scroll_popover(
6732 &mut self,
6733 to_y: impl Fn(Size<Pixels>) -> Pixels,
6734 scroll_icon: IconName,
6735 visible_row_range: Range<DisplayRow>,
6736 line_layouts: &[LineWithInvisibles],
6737 newest_selection_head: Option<DisplayPoint>,
6738 scrolled_content_origin: gpui::Point<Pixels>,
6739 window: &mut Window,
6740 cx: &mut App,
6741 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6742 let mut element = self
6743 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6744 .into_any();
6745
6746 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6747
6748 let cursor = newest_selection_head?;
6749 let cursor_row_layout =
6750 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6751 let cursor_column = cursor.column() as usize;
6752
6753 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6754
6755 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6756
6757 element.prepaint_at(origin, window, cx);
6758 Some((element, origin))
6759 }
6760
6761 fn render_edit_prediction_eager_jump_popover(
6762 &mut self,
6763 text_bounds: &Bounds<Pixels>,
6764 content_origin: gpui::Point<Pixels>,
6765 editor_snapshot: &EditorSnapshot,
6766 visible_row_range: Range<DisplayRow>,
6767 scroll_top: f32,
6768 scroll_bottom: f32,
6769 line_height: Pixels,
6770 scroll_pixel_position: gpui::Point<Pixels>,
6771 target_display_point: DisplayPoint,
6772 editor_width: Pixels,
6773 window: &mut Window,
6774 cx: &mut App,
6775 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6776 if target_display_point.row().as_f32() < scroll_top {
6777 let mut element = self
6778 .render_edit_prediction_line_popover(
6779 "Jump to Edit",
6780 Some(IconName::ArrowUp),
6781 window,
6782 cx,
6783 )?
6784 .into_any();
6785
6786 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6787 let offset = point(
6788 (text_bounds.size.width - size.width) / 2.,
6789 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6790 );
6791
6792 let origin = text_bounds.origin + offset;
6793 element.prepaint_at(origin, window, cx);
6794 Some((element, origin))
6795 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6796 let mut element = self
6797 .render_edit_prediction_line_popover(
6798 "Jump to Edit",
6799 Some(IconName::ArrowDown),
6800 window,
6801 cx,
6802 )?
6803 .into_any();
6804
6805 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6806 let offset = point(
6807 (text_bounds.size.width - size.width) / 2.,
6808 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6809 );
6810
6811 let origin = text_bounds.origin + offset;
6812 element.prepaint_at(origin, window, cx);
6813 Some((element, origin))
6814 } else {
6815 self.render_edit_prediction_end_of_line_popover(
6816 "Jump to Edit",
6817 editor_snapshot,
6818 visible_row_range,
6819 target_display_point,
6820 line_height,
6821 scroll_pixel_position,
6822 content_origin,
6823 editor_width,
6824 window,
6825 cx,
6826 )
6827 }
6828 }
6829
6830 fn render_edit_prediction_end_of_line_popover(
6831 self: &mut Editor,
6832 label: &'static str,
6833 editor_snapshot: &EditorSnapshot,
6834 visible_row_range: Range<DisplayRow>,
6835 target_display_point: DisplayPoint,
6836 line_height: Pixels,
6837 scroll_pixel_position: gpui::Point<Pixels>,
6838 content_origin: gpui::Point<Pixels>,
6839 editor_width: Pixels,
6840 window: &mut Window,
6841 cx: &mut App,
6842 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6843 let target_line_end = DisplayPoint::new(
6844 target_display_point.row(),
6845 editor_snapshot.line_len(target_display_point.row()),
6846 );
6847
6848 let mut element = self
6849 .render_edit_prediction_line_popover(label, None, window, cx)?
6850 .into_any();
6851
6852 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6853
6854 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6855
6856 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6857 let mut origin = start_point
6858 + line_origin
6859 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6860 origin.x = origin.x.max(content_origin.x);
6861
6862 let max_x = content_origin.x + editor_width - size.width;
6863
6864 if origin.x > max_x {
6865 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6866
6867 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6868 origin.y += offset;
6869 IconName::ArrowUp
6870 } else {
6871 origin.y -= offset;
6872 IconName::ArrowDown
6873 };
6874
6875 element = self
6876 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6877 .into_any();
6878
6879 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6880
6881 origin.x = content_origin.x + editor_width - size.width - px(2.);
6882 }
6883
6884 element.prepaint_at(origin, window, cx);
6885 Some((element, origin))
6886 }
6887
6888 fn render_edit_prediction_diff_popover(
6889 self: &Editor,
6890 text_bounds: &Bounds<Pixels>,
6891 content_origin: gpui::Point<Pixels>,
6892 editor_snapshot: &EditorSnapshot,
6893 visible_row_range: Range<DisplayRow>,
6894 line_layouts: &[LineWithInvisibles],
6895 line_height: Pixels,
6896 scroll_pixel_position: gpui::Point<Pixels>,
6897 newest_selection_head: Option<DisplayPoint>,
6898 editor_width: Pixels,
6899 style: &EditorStyle,
6900 edits: &Vec<(Range<Anchor>, String)>,
6901 edit_preview: &Option<language::EditPreview>,
6902 snapshot: &language::BufferSnapshot,
6903 window: &mut Window,
6904 cx: &mut App,
6905 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6906 let edit_start = edits
6907 .first()
6908 .unwrap()
6909 .0
6910 .start
6911 .to_display_point(editor_snapshot);
6912 let edit_end = edits
6913 .last()
6914 .unwrap()
6915 .0
6916 .end
6917 .to_display_point(editor_snapshot);
6918
6919 let is_visible = visible_row_range.contains(&edit_start.row())
6920 || visible_row_range.contains(&edit_end.row());
6921 if !is_visible {
6922 return None;
6923 }
6924
6925 let highlighted_edits =
6926 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6927
6928 let styled_text = highlighted_edits.to_styled_text(&style.text);
6929 let line_count = highlighted_edits.text.lines().count();
6930
6931 const BORDER_WIDTH: Pixels = px(1.);
6932
6933 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6934 let has_keybind = keybind.is_some();
6935
6936 let mut element = h_flex()
6937 .items_start()
6938 .child(
6939 h_flex()
6940 .bg(cx.theme().colors().editor_background)
6941 .border(BORDER_WIDTH)
6942 .shadow_sm()
6943 .border_color(cx.theme().colors().border)
6944 .rounded_l_lg()
6945 .when(line_count > 1, |el| el.rounded_br_lg())
6946 .pr_1()
6947 .child(styled_text),
6948 )
6949 .child(
6950 h_flex()
6951 .h(line_height + BORDER_WIDTH * 2.)
6952 .px_1p5()
6953 .gap_1()
6954 // Workaround: For some reason, there's a gap if we don't do this
6955 .ml(-BORDER_WIDTH)
6956 .shadow(smallvec![gpui::BoxShadow {
6957 color: gpui::black().opacity(0.05),
6958 offset: point(px(1.), px(1.)),
6959 blur_radius: px(2.),
6960 spread_radius: px(0.),
6961 }])
6962 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6963 .border(BORDER_WIDTH)
6964 .border_color(cx.theme().colors().border)
6965 .rounded_r_lg()
6966 .id("edit_prediction_diff_popover_keybind")
6967 .when(!has_keybind, |el| {
6968 let status_colors = cx.theme().status();
6969
6970 el.bg(status_colors.error_background)
6971 .border_color(status_colors.error.opacity(0.6))
6972 .child(Icon::new(IconName::Info).color(Color::Error))
6973 .cursor_default()
6974 .hoverable_tooltip(move |_window, cx| {
6975 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6976 })
6977 })
6978 .children(keybind),
6979 )
6980 .into_any();
6981
6982 let longest_row =
6983 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6984 let longest_line_width = if visible_row_range.contains(&longest_row) {
6985 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6986 } else {
6987 layout_line(
6988 longest_row,
6989 editor_snapshot,
6990 style,
6991 editor_width,
6992 |_| false,
6993 window,
6994 cx,
6995 )
6996 .width
6997 };
6998
6999 let viewport_bounds =
7000 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
7001 right: -EditorElement::SCROLLBAR_WIDTH,
7002 ..Default::default()
7003 });
7004
7005 let x_after_longest =
7006 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
7007 - scroll_pixel_position.x;
7008
7009 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
7010
7011 // Fully visible if it can be displayed within the window (allow overlapping other
7012 // panes). However, this is only allowed if the popover starts within text_bounds.
7013 let can_position_to_the_right = x_after_longest < text_bounds.right()
7014 && x_after_longest + element_bounds.width < viewport_bounds.right();
7015
7016 let mut origin = if can_position_to_the_right {
7017 point(
7018 x_after_longest,
7019 text_bounds.origin.y + edit_start.row().as_f32() * line_height
7020 - scroll_pixel_position.y,
7021 )
7022 } else {
7023 let cursor_row = newest_selection_head.map(|head| head.row());
7024 let above_edit = edit_start
7025 .row()
7026 .0
7027 .checked_sub(line_count as u32)
7028 .map(DisplayRow);
7029 let below_edit = Some(edit_end.row() + 1);
7030 let above_cursor =
7031 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
7032 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
7033
7034 // Place the edit popover adjacent to the edit if there is a location
7035 // available that is onscreen and does not obscure the cursor. Otherwise,
7036 // place it adjacent to the cursor.
7037 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
7038 .into_iter()
7039 .flatten()
7040 .find(|&start_row| {
7041 let end_row = start_row + line_count as u32;
7042 visible_row_range.contains(&start_row)
7043 && visible_row_range.contains(&end_row)
7044 && cursor_row.map_or(true, |cursor_row| {
7045 !((start_row..end_row).contains(&cursor_row))
7046 })
7047 })?;
7048
7049 content_origin
7050 + point(
7051 -scroll_pixel_position.x,
7052 row_target.as_f32() * line_height - scroll_pixel_position.y,
7053 )
7054 };
7055
7056 origin.x -= BORDER_WIDTH;
7057
7058 window.defer_draw(element, origin, 1);
7059
7060 // Do not return an element, since it will already be drawn due to defer_draw.
7061 None
7062 }
7063
7064 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
7065 px(30.)
7066 }
7067
7068 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
7069 if self.read_only(cx) {
7070 cx.theme().players().read_only()
7071 } else {
7072 self.style.as_ref().unwrap().local_player
7073 }
7074 }
7075
7076 fn render_edit_prediction_accept_keybind(
7077 &self,
7078 window: &mut Window,
7079 cx: &App,
7080 ) -> Option<AnyElement> {
7081 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
7082 let accept_keystroke = accept_binding.keystroke()?;
7083
7084 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7085
7086 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
7087 Color::Accent
7088 } else {
7089 Color::Muted
7090 };
7091
7092 h_flex()
7093 .px_0p5()
7094 .when(is_platform_style_mac, |parent| parent.gap_0p5())
7095 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7096 .text_size(TextSize::XSmall.rems(cx))
7097 .child(h_flex().children(ui::render_modifiers(
7098 &accept_keystroke.modifiers,
7099 PlatformStyle::platform(),
7100 Some(modifiers_color),
7101 Some(IconSize::XSmall.rems().into()),
7102 true,
7103 )))
7104 .when(is_platform_style_mac, |parent| {
7105 parent.child(accept_keystroke.key.clone())
7106 })
7107 .when(!is_platform_style_mac, |parent| {
7108 parent.child(
7109 Key::new(
7110 util::capitalize(&accept_keystroke.key),
7111 Some(Color::Default),
7112 )
7113 .size(Some(IconSize::XSmall.rems().into())),
7114 )
7115 })
7116 .into_any()
7117 .into()
7118 }
7119
7120 fn render_edit_prediction_line_popover(
7121 &self,
7122 label: impl Into<SharedString>,
7123 icon: Option<IconName>,
7124 window: &mut Window,
7125 cx: &App,
7126 ) -> Option<Stateful<Div>> {
7127 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7128
7129 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7130 let has_keybind = keybind.is_some();
7131
7132 let result = h_flex()
7133 .id("ep-line-popover")
7134 .py_0p5()
7135 .pl_1()
7136 .pr(padding_right)
7137 .gap_1()
7138 .rounded_md()
7139 .border_1()
7140 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7141 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7142 .shadow_sm()
7143 .when(!has_keybind, |el| {
7144 let status_colors = cx.theme().status();
7145
7146 el.bg(status_colors.error_background)
7147 .border_color(status_colors.error.opacity(0.6))
7148 .pl_2()
7149 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7150 .cursor_default()
7151 .hoverable_tooltip(move |_window, cx| {
7152 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7153 })
7154 })
7155 .children(keybind)
7156 .child(
7157 Label::new(label)
7158 .size(LabelSize::Small)
7159 .when(!has_keybind, |el| {
7160 el.color(cx.theme().status().error.into()).strikethrough()
7161 }),
7162 )
7163 .when(!has_keybind, |el| {
7164 el.child(
7165 h_flex().ml_1().child(
7166 Icon::new(IconName::Info)
7167 .size(IconSize::Small)
7168 .color(cx.theme().status().error.into()),
7169 ),
7170 )
7171 })
7172 .when_some(icon, |element, icon| {
7173 element.child(
7174 div()
7175 .mt(px(1.5))
7176 .child(Icon::new(icon).size(IconSize::Small)),
7177 )
7178 });
7179
7180 Some(result)
7181 }
7182
7183 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7184 let accent_color = cx.theme().colors().text_accent;
7185 let editor_bg_color = cx.theme().colors().editor_background;
7186 editor_bg_color.blend(accent_color.opacity(0.1))
7187 }
7188
7189 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7190 let accent_color = cx.theme().colors().text_accent;
7191 let editor_bg_color = cx.theme().colors().editor_background;
7192 editor_bg_color.blend(accent_color.opacity(0.6))
7193 }
7194
7195 fn render_edit_prediction_cursor_popover(
7196 &self,
7197 min_width: Pixels,
7198 max_width: Pixels,
7199 cursor_point: Point,
7200 style: &EditorStyle,
7201 accept_keystroke: Option<&gpui::Keystroke>,
7202 _window: &Window,
7203 cx: &mut Context<Editor>,
7204 ) -> Option<AnyElement> {
7205 let provider = self.edit_prediction_provider.as_ref()?;
7206
7207 if provider.provider.needs_terms_acceptance(cx) {
7208 return Some(
7209 h_flex()
7210 .min_w(min_width)
7211 .flex_1()
7212 .px_2()
7213 .py_1()
7214 .gap_3()
7215 .elevation_2(cx)
7216 .hover(|style| style.bg(cx.theme().colors().element_hover))
7217 .id("accept-terms")
7218 .cursor_pointer()
7219 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7220 .on_click(cx.listener(|this, _event, window, cx| {
7221 cx.stop_propagation();
7222 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7223 window.dispatch_action(
7224 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7225 cx,
7226 );
7227 }))
7228 .child(
7229 h_flex()
7230 .flex_1()
7231 .gap_2()
7232 .child(Icon::new(IconName::ZedPredict))
7233 .child(Label::new("Accept Terms of Service"))
7234 .child(div().w_full())
7235 .child(
7236 Icon::new(IconName::ArrowUpRight)
7237 .color(Color::Muted)
7238 .size(IconSize::Small),
7239 )
7240 .into_any_element(),
7241 )
7242 .into_any(),
7243 );
7244 }
7245
7246 let is_refreshing = provider.provider.is_refreshing(cx);
7247
7248 fn pending_completion_container() -> Div {
7249 h_flex()
7250 .h_full()
7251 .flex_1()
7252 .gap_2()
7253 .child(Icon::new(IconName::ZedPredict))
7254 }
7255
7256 let completion = match &self.active_inline_completion {
7257 Some(prediction) => {
7258 if !self.has_visible_completions_menu() {
7259 const RADIUS: Pixels = px(6.);
7260 const BORDER_WIDTH: Pixels = px(1.);
7261
7262 return Some(
7263 h_flex()
7264 .elevation_2(cx)
7265 .border(BORDER_WIDTH)
7266 .border_color(cx.theme().colors().border)
7267 .when(accept_keystroke.is_none(), |el| {
7268 el.border_color(cx.theme().status().error)
7269 })
7270 .rounded(RADIUS)
7271 .rounded_tl(px(0.))
7272 .overflow_hidden()
7273 .child(div().px_1p5().child(match &prediction.completion {
7274 InlineCompletion::Move { target, snapshot } => {
7275 use text::ToPoint as _;
7276 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7277 {
7278 Icon::new(IconName::ZedPredictDown)
7279 } else {
7280 Icon::new(IconName::ZedPredictUp)
7281 }
7282 }
7283 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7284 }))
7285 .child(
7286 h_flex()
7287 .gap_1()
7288 .py_1()
7289 .px_2()
7290 .rounded_r(RADIUS - BORDER_WIDTH)
7291 .border_l_1()
7292 .border_color(cx.theme().colors().border)
7293 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7294 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7295 el.child(
7296 Label::new("Hold")
7297 .size(LabelSize::Small)
7298 .when(accept_keystroke.is_none(), |el| {
7299 el.strikethrough()
7300 })
7301 .line_height_style(LineHeightStyle::UiLabel),
7302 )
7303 })
7304 .id("edit_prediction_cursor_popover_keybind")
7305 .when(accept_keystroke.is_none(), |el| {
7306 let status_colors = cx.theme().status();
7307
7308 el.bg(status_colors.error_background)
7309 .border_color(status_colors.error.opacity(0.6))
7310 .child(Icon::new(IconName::Info).color(Color::Error))
7311 .cursor_default()
7312 .hoverable_tooltip(move |_window, cx| {
7313 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7314 .into()
7315 })
7316 })
7317 .when_some(
7318 accept_keystroke.as_ref(),
7319 |el, accept_keystroke| {
7320 el.child(h_flex().children(ui::render_modifiers(
7321 &accept_keystroke.modifiers,
7322 PlatformStyle::platform(),
7323 Some(Color::Default),
7324 Some(IconSize::XSmall.rems().into()),
7325 false,
7326 )))
7327 },
7328 ),
7329 )
7330 .into_any(),
7331 );
7332 }
7333
7334 self.render_edit_prediction_cursor_popover_preview(
7335 prediction,
7336 cursor_point,
7337 style,
7338 cx,
7339 )?
7340 }
7341
7342 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7343 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7344 stale_completion,
7345 cursor_point,
7346 style,
7347 cx,
7348 )?,
7349
7350 None => {
7351 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7352 }
7353 },
7354
7355 None => pending_completion_container().child(Label::new("No Prediction")),
7356 };
7357
7358 let completion = if is_refreshing {
7359 completion
7360 .with_animation(
7361 "loading-completion",
7362 Animation::new(Duration::from_secs(2))
7363 .repeat()
7364 .with_easing(pulsating_between(0.4, 0.8)),
7365 |label, delta| label.opacity(delta),
7366 )
7367 .into_any_element()
7368 } else {
7369 completion.into_any_element()
7370 };
7371
7372 let has_completion = self.active_inline_completion.is_some();
7373
7374 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7375 Some(
7376 h_flex()
7377 .min_w(min_width)
7378 .max_w(max_width)
7379 .flex_1()
7380 .elevation_2(cx)
7381 .border_color(cx.theme().colors().border)
7382 .child(
7383 div()
7384 .flex_1()
7385 .py_1()
7386 .px_2()
7387 .overflow_hidden()
7388 .child(completion),
7389 )
7390 .when_some(accept_keystroke, |el, accept_keystroke| {
7391 if !accept_keystroke.modifiers.modified() {
7392 return el;
7393 }
7394
7395 el.child(
7396 h_flex()
7397 .h_full()
7398 .border_l_1()
7399 .rounded_r_lg()
7400 .border_color(cx.theme().colors().border)
7401 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7402 .gap_1()
7403 .py_1()
7404 .px_2()
7405 .child(
7406 h_flex()
7407 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7408 .when(is_platform_style_mac, |parent| parent.gap_1())
7409 .child(h_flex().children(ui::render_modifiers(
7410 &accept_keystroke.modifiers,
7411 PlatformStyle::platform(),
7412 Some(if !has_completion {
7413 Color::Muted
7414 } else {
7415 Color::Default
7416 }),
7417 None,
7418 false,
7419 ))),
7420 )
7421 .child(Label::new("Preview").into_any_element())
7422 .opacity(if has_completion { 1.0 } else { 0.4 }),
7423 )
7424 })
7425 .into_any(),
7426 )
7427 }
7428
7429 fn render_edit_prediction_cursor_popover_preview(
7430 &self,
7431 completion: &InlineCompletionState,
7432 cursor_point: Point,
7433 style: &EditorStyle,
7434 cx: &mut Context<Editor>,
7435 ) -> Option<Div> {
7436 use text::ToPoint as _;
7437
7438 fn render_relative_row_jump(
7439 prefix: impl Into<String>,
7440 current_row: u32,
7441 target_row: u32,
7442 ) -> Div {
7443 let (row_diff, arrow) = if target_row < current_row {
7444 (current_row - target_row, IconName::ArrowUp)
7445 } else {
7446 (target_row - current_row, IconName::ArrowDown)
7447 };
7448
7449 h_flex()
7450 .child(
7451 Label::new(format!("{}{}", prefix.into(), row_diff))
7452 .color(Color::Muted)
7453 .size(LabelSize::Small),
7454 )
7455 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7456 }
7457
7458 match &completion.completion {
7459 InlineCompletion::Move {
7460 target, snapshot, ..
7461 } => Some(
7462 h_flex()
7463 .px_2()
7464 .gap_2()
7465 .flex_1()
7466 .child(
7467 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7468 Icon::new(IconName::ZedPredictDown)
7469 } else {
7470 Icon::new(IconName::ZedPredictUp)
7471 },
7472 )
7473 .child(Label::new("Jump to Edit")),
7474 ),
7475
7476 InlineCompletion::Edit {
7477 edits,
7478 edit_preview,
7479 snapshot,
7480 display_mode: _,
7481 } => {
7482 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7483
7484 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7485 &snapshot,
7486 &edits,
7487 edit_preview.as_ref()?,
7488 true,
7489 cx,
7490 )
7491 .first_line_preview();
7492
7493 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7494 .with_default_highlights(&style.text, highlighted_edits.highlights);
7495
7496 let preview = h_flex()
7497 .gap_1()
7498 .min_w_16()
7499 .child(styled_text)
7500 .when(has_more_lines, |parent| parent.child("…"));
7501
7502 let left = if first_edit_row != cursor_point.row {
7503 render_relative_row_jump("", cursor_point.row, first_edit_row)
7504 .into_any_element()
7505 } else {
7506 Icon::new(IconName::ZedPredict).into_any_element()
7507 };
7508
7509 Some(
7510 h_flex()
7511 .h_full()
7512 .flex_1()
7513 .gap_2()
7514 .pr_1()
7515 .overflow_x_hidden()
7516 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7517 .child(left)
7518 .child(preview),
7519 )
7520 }
7521 }
7522 }
7523
7524 fn render_context_menu(
7525 &self,
7526 style: &EditorStyle,
7527 max_height_in_lines: u32,
7528 y_flipped: bool,
7529 window: &mut Window,
7530 cx: &mut Context<Editor>,
7531 ) -> Option<AnyElement> {
7532 let menu = self.context_menu.borrow();
7533 let menu = menu.as_ref()?;
7534 if !menu.visible() {
7535 return None;
7536 };
7537 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7538 }
7539
7540 fn render_context_menu_aside(
7541 &mut self,
7542 max_size: Size<Pixels>,
7543 window: &mut Window,
7544 cx: &mut Context<Editor>,
7545 ) -> Option<AnyElement> {
7546 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7547 if menu.visible() {
7548 menu.render_aside(self, max_size, window, cx)
7549 } else {
7550 None
7551 }
7552 })
7553 }
7554
7555 fn hide_context_menu(
7556 &mut self,
7557 window: &mut Window,
7558 cx: &mut Context<Self>,
7559 ) -> Option<CodeContextMenu> {
7560 cx.notify();
7561 self.completion_tasks.clear();
7562 let context_menu = self.context_menu.borrow_mut().take();
7563 self.stale_inline_completion_in_menu.take();
7564 self.update_visible_inline_completion(window, cx);
7565 context_menu
7566 }
7567
7568 fn show_snippet_choices(
7569 &mut self,
7570 choices: &Vec<String>,
7571 selection: Range<Anchor>,
7572 cx: &mut Context<Self>,
7573 ) {
7574 if selection.start.buffer_id.is_none() {
7575 return;
7576 }
7577 let buffer_id = selection.start.buffer_id.unwrap();
7578 let buffer = self.buffer().read(cx).buffer(buffer_id);
7579 let id = post_inc(&mut self.next_completion_id);
7580
7581 if let Some(buffer) = buffer {
7582 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7583 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7584 ));
7585 }
7586 }
7587
7588 pub fn insert_snippet(
7589 &mut self,
7590 insertion_ranges: &[Range<usize>],
7591 snippet: Snippet,
7592 window: &mut Window,
7593 cx: &mut Context<Self>,
7594 ) -> Result<()> {
7595 struct Tabstop<T> {
7596 is_end_tabstop: bool,
7597 ranges: Vec<Range<T>>,
7598 choices: Option<Vec<String>>,
7599 }
7600
7601 let tabstops = self.buffer.update(cx, |buffer, cx| {
7602 let snippet_text: Arc<str> = snippet.text.clone().into();
7603 let edits = insertion_ranges
7604 .iter()
7605 .cloned()
7606 .map(|range| (range, snippet_text.clone()));
7607 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
7608
7609 let snapshot = &*buffer.read(cx);
7610 let snippet = &snippet;
7611 snippet
7612 .tabstops
7613 .iter()
7614 .map(|tabstop| {
7615 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7616 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7617 });
7618 let mut tabstop_ranges = tabstop
7619 .ranges
7620 .iter()
7621 .flat_map(|tabstop_range| {
7622 let mut delta = 0_isize;
7623 insertion_ranges.iter().map(move |insertion_range| {
7624 let insertion_start = insertion_range.start as isize + delta;
7625 delta +=
7626 snippet.text.len() as isize - insertion_range.len() as isize;
7627
7628 let start = ((insertion_start + tabstop_range.start) as usize)
7629 .min(snapshot.len());
7630 let end = ((insertion_start + tabstop_range.end) as usize)
7631 .min(snapshot.len());
7632 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7633 })
7634 })
7635 .collect::<Vec<_>>();
7636 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7637
7638 Tabstop {
7639 is_end_tabstop,
7640 ranges: tabstop_ranges,
7641 choices: tabstop.choices.clone(),
7642 }
7643 })
7644 .collect::<Vec<_>>()
7645 });
7646 if let Some(tabstop) = tabstops.first() {
7647 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7648 s.select_ranges(tabstop.ranges.iter().cloned());
7649 });
7650
7651 if let Some(choices) = &tabstop.choices {
7652 if let Some(selection) = tabstop.ranges.first() {
7653 self.show_snippet_choices(choices, selection.clone(), cx)
7654 }
7655 }
7656
7657 // If we're already at the last tabstop and it's at the end of the snippet,
7658 // we're done, we don't need to keep the state around.
7659 if !tabstop.is_end_tabstop {
7660 let choices = tabstops
7661 .iter()
7662 .map(|tabstop| tabstop.choices.clone())
7663 .collect();
7664
7665 let ranges = tabstops
7666 .into_iter()
7667 .map(|tabstop| tabstop.ranges)
7668 .collect::<Vec<_>>();
7669
7670 self.snippet_stack.push(SnippetState {
7671 active_index: 0,
7672 ranges,
7673 choices,
7674 });
7675 }
7676
7677 // Check whether the just-entered snippet ends with an auto-closable bracket.
7678 if self.autoclose_regions.is_empty() {
7679 let snapshot = self.buffer.read(cx).snapshot(cx);
7680 for selection in &mut self.selections.all::<Point>(cx) {
7681 let selection_head = selection.head();
7682 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7683 continue;
7684 };
7685
7686 let mut bracket_pair = None;
7687 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7688 let prev_chars = snapshot
7689 .reversed_chars_at(selection_head)
7690 .collect::<String>();
7691 for (pair, enabled) in scope.brackets() {
7692 if enabled
7693 && pair.close
7694 && prev_chars.starts_with(pair.start.as_str())
7695 && next_chars.starts_with(pair.end.as_str())
7696 {
7697 bracket_pair = Some(pair.clone());
7698 break;
7699 }
7700 }
7701 if let Some(pair) = bracket_pair {
7702 let start = snapshot.anchor_after(selection_head);
7703 let end = snapshot.anchor_after(selection_head);
7704 self.autoclose_regions.push(AutocloseRegion {
7705 selection_id: selection.id,
7706 range: start..end,
7707 pair,
7708 });
7709 }
7710 }
7711 }
7712 }
7713 Ok(())
7714 }
7715
7716 pub fn move_to_next_snippet_tabstop(
7717 &mut self,
7718 window: &mut Window,
7719 cx: &mut Context<Self>,
7720 ) -> bool {
7721 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7722 }
7723
7724 pub fn move_to_prev_snippet_tabstop(
7725 &mut self,
7726 window: &mut Window,
7727 cx: &mut Context<Self>,
7728 ) -> bool {
7729 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7730 }
7731
7732 pub fn move_to_snippet_tabstop(
7733 &mut self,
7734 bias: Bias,
7735 window: &mut Window,
7736 cx: &mut Context<Self>,
7737 ) -> bool {
7738 if let Some(mut snippet) = self.snippet_stack.pop() {
7739 match bias {
7740 Bias::Left => {
7741 if snippet.active_index > 0 {
7742 snippet.active_index -= 1;
7743 } else {
7744 self.snippet_stack.push(snippet);
7745 return false;
7746 }
7747 }
7748 Bias::Right => {
7749 if snippet.active_index + 1 < snippet.ranges.len() {
7750 snippet.active_index += 1;
7751 } else {
7752 self.snippet_stack.push(snippet);
7753 return false;
7754 }
7755 }
7756 }
7757 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7758 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7759 s.select_anchor_ranges(current_ranges.iter().cloned())
7760 });
7761
7762 if let Some(choices) = &snippet.choices[snippet.active_index] {
7763 if let Some(selection) = current_ranges.first() {
7764 self.show_snippet_choices(&choices, selection.clone(), cx);
7765 }
7766 }
7767
7768 // If snippet state is not at the last tabstop, push it back on the stack
7769 if snippet.active_index + 1 < snippet.ranges.len() {
7770 self.snippet_stack.push(snippet);
7771 }
7772 return true;
7773 }
7774 }
7775
7776 false
7777 }
7778
7779 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7780 self.transact(window, cx, |this, window, cx| {
7781 this.select_all(&SelectAll, window, cx);
7782 this.insert("", window, cx);
7783 });
7784 }
7785
7786 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7787 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7788 self.transact(window, cx, |this, window, cx| {
7789 this.select_autoclose_pair(window, cx);
7790 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7791 if !this.linked_edit_ranges.is_empty() {
7792 let selections = this.selections.all::<MultiBufferPoint>(cx);
7793 let snapshot = this.buffer.read(cx).snapshot(cx);
7794
7795 for selection in selections.iter() {
7796 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7797 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7798 if selection_start.buffer_id != selection_end.buffer_id {
7799 continue;
7800 }
7801 if let Some(ranges) =
7802 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7803 {
7804 for (buffer, entries) in ranges {
7805 linked_ranges.entry(buffer).or_default().extend(entries);
7806 }
7807 }
7808 }
7809 }
7810
7811 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7812 if !this.selections.line_mode {
7813 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7814 for selection in &mut selections {
7815 if selection.is_empty() {
7816 let old_head = selection.head();
7817 let mut new_head =
7818 movement::left(&display_map, old_head.to_display_point(&display_map))
7819 .to_point(&display_map);
7820 if let Some((buffer, line_buffer_range)) = display_map
7821 .buffer_snapshot
7822 .buffer_line_for_row(MultiBufferRow(old_head.row))
7823 {
7824 let indent_size =
7825 buffer.indent_size_for_line(line_buffer_range.start.row);
7826 let indent_len = match indent_size.kind {
7827 IndentKind::Space => {
7828 buffer.settings_at(line_buffer_range.start, cx).tab_size
7829 }
7830 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7831 };
7832 if old_head.column <= indent_size.len && old_head.column > 0 {
7833 let indent_len = indent_len.get();
7834 new_head = cmp::min(
7835 new_head,
7836 MultiBufferPoint::new(
7837 old_head.row,
7838 ((old_head.column - 1) / indent_len) * indent_len,
7839 ),
7840 );
7841 }
7842 }
7843
7844 selection.set_head(new_head, SelectionGoal::None);
7845 }
7846 }
7847 }
7848
7849 this.signature_help_state.set_backspace_pressed(true);
7850 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7851 s.select(selections)
7852 });
7853 this.insert("", window, cx);
7854 let empty_str: Arc<str> = Arc::from("");
7855 for (buffer, edits) in linked_ranges {
7856 let snapshot = buffer.read(cx).snapshot();
7857 use text::ToPoint as TP;
7858
7859 let edits = edits
7860 .into_iter()
7861 .map(|range| {
7862 let end_point = TP::to_point(&range.end, &snapshot);
7863 let mut start_point = TP::to_point(&range.start, &snapshot);
7864
7865 if end_point == start_point {
7866 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7867 .saturating_sub(1);
7868 start_point =
7869 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7870 };
7871
7872 (start_point..end_point, empty_str.clone())
7873 })
7874 .sorted_by_key(|(range, _)| range.start)
7875 .collect::<Vec<_>>();
7876 buffer.update(cx, |this, cx| {
7877 this.edit(edits, None, cx);
7878 })
7879 }
7880 this.refresh_inline_completion(true, false, window, cx);
7881 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7882 });
7883 }
7884
7885 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7886 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7887 self.transact(window, cx, |this, window, cx| {
7888 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7889 let line_mode = s.line_mode;
7890 s.move_with(|map, selection| {
7891 if selection.is_empty() && !line_mode {
7892 let cursor = movement::right(map, selection.head());
7893 selection.end = cursor;
7894 selection.reversed = true;
7895 selection.goal = SelectionGoal::None;
7896 }
7897 })
7898 });
7899 this.insert("", window, cx);
7900 this.refresh_inline_completion(true, false, window, cx);
7901 });
7902 }
7903
7904 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7905 if self.move_to_prev_snippet_tabstop(window, cx) {
7906 return;
7907 }
7908 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7909 self.outdent(&Outdent, window, cx);
7910 }
7911
7912 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7913 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7914 return;
7915 }
7916 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7917 let mut selections = self.selections.all_adjusted(cx);
7918 let buffer = self.buffer.read(cx);
7919 let snapshot = buffer.snapshot(cx);
7920 let rows_iter = selections.iter().map(|s| s.head().row);
7921 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7922
7923 let mut edits = Vec::new();
7924 let mut prev_edited_row = 0;
7925 let mut row_delta = 0;
7926 for selection in &mut selections {
7927 if selection.start.row != prev_edited_row {
7928 row_delta = 0;
7929 }
7930 prev_edited_row = selection.end.row;
7931
7932 // If the selection is non-empty, then increase the indentation of the selected lines.
7933 if !selection.is_empty() {
7934 row_delta =
7935 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7936 continue;
7937 }
7938
7939 // If the selection is empty and the cursor is in the leading whitespace before the
7940 // suggested indentation, then auto-indent the line.
7941 let cursor = selection.head();
7942 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7943 if let Some(suggested_indent) =
7944 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7945 {
7946 if cursor.column < suggested_indent.len
7947 && cursor.column <= current_indent.len
7948 && current_indent.len <= suggested_indent.len
7949 {
7950 selection.start = Point::new(cursor.row, suggested_indent.len);
7951 selection.end = selection.start;
7952 if row_delta == 0 {
7953 edits.extend(Buffer::edit_for_indent_size_adjustment(
7954 cursor.row,
7955 current_indent,
7956 suggested_indent,
7957 ));
7958 row_delta = suggested_indent.len - current_indent.len;
7959 }
7960 continue;
7961 }
7962 }
7963
7964 // Otherwise, insert a hard or soft tab.
7965 let settings = buffer.language_settings_at(cursor, cx);
7966 let tab_size = if settings.hard_tabs {
7967 IndentSize::tab()
7968 } else {
7969 let tab_size = settings.tab_size.get();
7970 let char_column = snapshot
7971 .text_for_range(Point::new(cursor.row, 0)..cursor)
7972 .flat_map(str::chars)
7973 .count()
7974 + row_delta as usize;
7975 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7976 IndentSize::spaces(chars_to_next_tab_stop)
7977 };
7978 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7979 selection.end = selection.start;
7980 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7981 row_delta += tab_size.len;
7982 }
7983
7984 self.transact(window, cx, |this, window, cx| {
7985 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7986 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7987 s.select(selections)
7988 });
7989 this.refresh_inline_completion(true, false, window, cx);
7990 });
7991 }
7992
7993 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7994 if self.read_only(cx) {
7995 return;
7996 }
7997 let mut selections = self.selections.all::<Point>(cx);
7998 let mut prev_edited_row = 0;
7999 let mut row_delta = 0;
8000 let mut edits = Vec::new();
8001 let buffer = self.buffer.read(cx);
8002 let snapshot = buffer.snapshot(cx);
8003 for selection in &mut selections {
8004 if selection.start.row != prev_edited_row {
8005 row_delta = 0;
8006 }
8007 prev_edited_row = selection.end.row;
8008
8009 row_delta =
8010 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
8011 }
8012
8013 self.transact(window, cx, |this, window, cx| {
8014 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
8015 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8016 s.select(selections)
8017 });
8018 });
8019 }
8020
8021 fn indent_selection(
8022 buffer: &MultiBuffer,
8023 snapshot: &MultiBufferSnapshot,
8024 selection: &mut Selection<Point>,
8025 edits: &mut Vec<(Range<Point>, String)>,
8026 delta_for_start_row: u32,
8027 cx: &App,
8028 ) -> u32 {
8029 let settings = buffer.language_settings_at(selection.start, cx);
8030 let tab_size = settings.tab_size.get();
8031 let indent_kind = if settings.hard_tabs {
8032 IndentKind::Tab
8033 } else {
8034 IndentKind::Space
8035 };
8036 let mut start_row = selection.start.row;
8037 let mut end_row = selection.end.row + 1;
8038
8039 // If a selection ends at the beginning of a line, don't indent
8040 // that last line.
8041 if selection.end.column == 0 && selection.end.row > selection.start.row {
8042 end_row -= 1;
8043 }
8044
8045 // Avoid re-indenting a row that has already been indented by a
8046 // previous selection, but still update this selection's column
8047 // to reflect that indentation.
8048 if delta_for_start_row > 0 {
8049 start_row += 1;
8050 selection.start.column += delta_for_start_row;
8051 if selection.end.row == selection.start.row {
8052 selection.end.column += delta_for_start_row;
8053 }
8054 }
8055
8056 let mut delta_for_end_row = 0;
8057 let has_multiple_rows = start_row + 1 != end_row;
8058 for row in start_row..end_row {
8059 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
8060 let indent_delta = match (current_indent.kind, indent_kind) {
8061 (IndentKind::Space, IndentKind::Space) => {
8062 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
8063 IndentSize::spaces(columns_to_next_tab_stop)
8064 }
8065 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
8066 (_, IndentKind::Tab) => IndentSize::tab(),
8067 };
8068
8069 let start = if has_multiple_rows || current_indent.len < selection.start.column {
8070 0
8071 } else {
8072 selection.start.column
8073 };
8074 let row_start = Point::new(row, start);
8075 edits.push((
8076 row_start..row_start,
8077 indent_delta.chars().collect::<String>(),
8078 ));
8079
8080 // Update this selection's endpoints to reflect the indentation.
8081 if row == selection.start.row {
8082 selection.start.column += indent_delta.len;
8083 }
8084 if row == selection.end.row {
8085 selection.end.column += indent_delta.len;
8086 delta_for_end_row = indent_delta.len;
8087 }
8088 }
8089
8090 if selection.start.row == selection.end.row {
8091 delta_for_start_row + delta_for_end_row
8092 } else {
8093 delta_for_end_row
8094 }
8095 }
8096
8097 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
8098 if self.read_only(cx) {
8099 return;
8100 }
8101 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8102 let selections = self.selections.all::<Point>(cx);
8103 let mut deletion_ranges = Vec::new();
8104 let mut last_outdent = None;
8105 {
8106 let buffer = self.buffer.read(cx);
8107 let snapshot = buffer.snapshot(cx);
8108 for selection in &selections {
8109 let settings = buffer.language_settings_at(selection.start, cx);
8110 let tab_size = settings.tab_size.get();
8111 let mut rows = selection.spanned_rows(false, &display_map);
8112
8113 // Avoid re-outdenting a row that has already been outdented by a
8114 // previous selection.
8115 if let Some(last_row) = last_outdent {
8116 if last_row == rows.start {
8117 rows.start = rows.start.next_row();
8118 }
8119 }
8120 let has_multiple_rows = rows.len() > 1;
8121 for row in rows.iter_rows() {
8122 let indent_size = snapshot.indent_size_for_line(row);
8123 if indent_size.len > 0 {
8124 let deletion_len = match indent_size.kind {
8125 IndentKind::Space => {
8126 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8127 if columns_to_prev_tab_stop == 0 {
8128 tab_size
8129 } else {
8130 columns_to_prev_tab_stop
8131 }
8132 }
8133 IndentKind::Tab => 1,
8134 };
8135 let start = if has_multiple_rows
8136 || deletion_len > selection.start.column
8137 || indent_size.len < selection.start.column
8138 {
8139 0
8140 } else {
8141 selection.start.column - deletion_len
8142 };
8143 deletion_ranges.push(
8144 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8145 );
8146 last_outdent = Some(row);
8147 }
8148 }
8149 }
8150 }
8151
8152 self.transact(window, cx, |this, window, cx| {
8153 this.buffer.update(cx, |buffer, cx| {
8154 let empty_str: Arc<str> = Arc::default();
8155 buffer.edit(
8156 deletion_ranges
8157 .into_iter()
8158 .map(|range| (range, empty_str.clone())),
8159 None,
8160 cx,
8161 );
8162 });
8163 let selections = this.selections.all::<usize>(cx);
8164 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8165 s.select(selections)
8166 });
8167 });
8168 }
8169
8170 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8171 if self.read_only(cx) {
8172 return;
8173 }
8174 let selections = self
8175 .selections
8176 .all::<usize>(cx)
8177 .into_iter()
8178 .map(|s| s.range());
8179
8180 self.transact(window, cx, |this, window, cx| {
8181 this.buffer.update(cx, |buffer, cx| {
8182 buffer.autoindent_ranges(selections, cx);
8183 });
8184 let selections = this.selections.all::<usize>(cx);
8185 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8186 s.select(selections)
8187 });
8188 });
8189 }
8190
8191 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8192 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8193 let selections = self.selections.all::<Point>(cx);
8194
8195 let mut new_cursors = Vec::new();
8196 let mut edit_ranges = Vec::new();
8197 let mut selections = selections.iter().peekable();
8198 while let Some(selection) = selections.next() {
8199 let mut rows = selection.spanned_rows(false, &display_map);
8200 let goal_display_column = selection.head().to_display_point(&display_map).column();
8201
8202 // Accumulate contiguous regions of rows that we want to delete.
8203 while let Some(next_selection) = selections.peek() {
8204 let next_rows = next_selection.spanned_rows(false, &display_map);
8205 if next_rows.start <= rows.end {
8206 rows.end = next_rows.end;
8207 selections.next().unwrap();
8208 } else {
8209 break;
8210 }
8211 }
8212
8213 let buffer = &display_map.buffer_snapshot;
8214 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8215 let edit_end;
8216 let cursor_buffer_row;
8217 if buffer.max_point().row >= rows.end.0 {
8218 // If there's a line after the range, delete the \n from the end of the row range
8219 // and position the cursor on the next line.
8220 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8221 cursor_buffer_row = rows.end;
8222 } else {
8223 // If there isn't a line after the range, delete the \n from the line before the
8224 // start of the row range and position the cursor there.
8225 edit_start = edit_start.saturating_sub(1);
8226 edit_end = buffer.len();
8227 cursor_buffer_row = rows.start.previous_row();
8228 }
8229
8230 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8231 *cursor.column_mut() =
8232 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8233
8234 new_cursors.push((
8235 selection.id,
8236 buffer.anchor_after(cursor.to_point(&display_map)),
8237 ));
8238 edit_ranges.push(edit_start..edit_end);
8239 }
8240
8241 self.transact(window, cx, |this, window, cx| {
8242 let buffer = this.buffer.update(cx, |buffer, cx| {
8243 let empty_str: Arc<str> = Arc::default();
8244 buffer.edit(
8245 edit_ranges
8246 .into_iter()
8247 .map(|range| (range, empty_str.clone())),
8248 None,
8249 cx,
8250 );
8251 buffer.snapshot(cx)
8252 });
8253 let new_selections = new_cursors
8254 .into_iter()
8255 .map(|(id, cursor)| {
8256 let cursor = cursor.to_point(&buffer);
8257 Selection {
8258 id,
8259 start: cursor,
8260 end: cursor,
8261 reversed: false,
8262 goal: SelectionGoal::None,
8263 }
8264 })
8265 .collect();
8266
8267 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8268 s.select(new_selections);
8269 });
8270 });
8271 }
8272
8273 pub fn join_lines_impl(
8274 &mut self,
8275 insert_whitespace: bool,
8276 window: &mut Window,
8277 cx: &mut Context<Self>,
8278 ) {
8279 if self.read_only(cx) {
8280 return;
8281 }
8282 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8283 for selection in self.selections.all::<Point>(cx) {
8284 let start = MultiBufferRow(selection.start.row);
8285 // Treat single line selections as if they include the next line. Otherwise this action
8286 // would do nothing for single line selections individual cursors.
8287 let end = if selection.start.row == selection.end.row {
8288 MultiBufferRow(selection.start.row + 1)
8289 } else {
8290 MultiBufferRow(selection.end.row)
8291 };
8292
8293 if let Some(last_row_range) = row_ranges.last_mut() {
8294 if start <= last_row_range.end {
8295 last_row_range.end = end;
8296 continue;
8297 }
8298 }
8299 row_ranges.push(start..end);
8300 }
8301
8302 let snapshot = self.buffer.read(cx).snapshot(cx);
8303 let mut cursor_positions = Vec::new();
8304 for row_range in &row_ranges {
8305 let anchor = snapshot.anchor_before(Point::new(
8306 row_range.end.previous_row().0,
8307 snapshot.line_len(row_range.end.previous_row()),
8308 ));
8309 cursor_positions.push(anchor..anchor);
8310 }
8311
8312 self.transact(window, cx, |this, window, cx| {
8313 for row_range in row_ranges.into_iter().rev() {
8314 for row in row_range.iter_rows().rev() {
8315 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8316 let next_line_row = row.next_row();
8317 let indent = snapshot.indent_size_for_line(next_line_row);
8318 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8319
8320 let replace =
8321 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8322 " "
8323 } else {
8324 ""
8325 };
8326
8327 this.buffer.update(cx, |buffer, cx| {
8328 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8329 });
8330 }
8331 }
8332
8333 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8334 s.select_anchor_ranges(cursor_positions)
8335 });
8336 });
8337 }
8338
8339 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8340 self.join_lines_impl(true, window, cx);
8341 }
8342
8343 pub fn sort_lines_case_sensitive(
8344 &mut self,
8345 _: &SortLinesCaseSensitive,
8346 window: &mut Window,
8347 cx: &mut Context<Self>,
8348 ) {
8349 self.manipulate_lines(window, cx, |lines| lines.sort())
8350 }
8351
8352 pub fn sort_lines_case_insensitive(
8353 &mut self,
8354 _: &SortLinesCaseInsensitive,
8355 window: &mut Window,
8356 cx: &mut Context<Self>,
8357 ) {
8358 self.manipulate_lines(window, cx, |lines| {
8359 lines.sort_by_key(|line| line.to_lowercase())
8360 })
8361 }
8362
8363 pub fn unique_lines_case_insensitive(
8364 &mut self,
8365 _: &UniqueLinesCaseInsensitive,
8366 window: &mut Window,
8367 cx: &mut Context<Self>,
8368 ) {
8369 self.manipulate_lines(window, cx, |lines| {
8370 let mut seen = HashSet::default();
8371 lines.retain(|line| seen.insert(line.to_lowercase()));
8372 })
8373 }
8374
8375 pub fn unique_lines_case_sensitive(
8376 &mut self,
8377 _: &UniqueLinesCaseSensitive,
8378 window: &mut Window,
8379 cx: &mut Context<Self>,
8380 ) {
8381 self.manipulate_lines(window, cx, |lines| {
8382 let mut seen = HashSet::default();
8383 lines.retain(|line| seen.insert(*line));
8384 })
8385 }
8386
8387 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8388 let Some(project) = self.project.clone() else {
8389 return;
8390 };
8391 self.reload(project, window, cx)
8392 .detach_and_notify_err(window, cx);
8393 }
8394
8395 pub fn restore_file(
8396 &mut self,
8397 _: &::git::RestoreFile,
8398 window: &mut Window,
8399 cx: &mut Context<Self>,
8400 ) {
8401 let mut buffer_ids = HashSet::default();
8402 let snapshot = self.buffer().read(cx).snapshot(cx);
8403 for selection in self.selections.all::<usize>(cx) {
8404 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8405 }
8406
8407 let buffer = self.buffer().read(cx);
8408 let ranges = buffer_ids
8409 .into_iter()
8410 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8411 .collect::<Vec<_>>();
8412
8413 self.restore_hunks_in_ranges(ranges, window, cx);
8414 }
8415
8416 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8417 let selections = self
8418 .selections
8419 .all(cx)
8420 .into_iter()
8421 .map(|s| s.range())
8422 .collect();
8423 self.restore_hunks_in_ranges(selections, window, cx);
8424 }
8425
8426 fn restore_hunks_in_ranges(
8427 &mut self,
8428 ranges: Vec<Range<Point>>,
8429 window: &mut Window,
8430 cx: &mut Context<Editor>,
8431 ) {
8432 let mut revert_changes = HashMap::default();
8433 let chunk_by = self
8434 .snapshot(window, cx)
8435 .hunks_for_ranges(ranges)
8436 .into_iter()
8437 .chunk_by(|hunk| hunk.buffer_id);
8438 for (buffer_id, hunks) in &chunk_by {
8439 let hunks = hunks.collect::<Vec<_>>();
8440 for hunk in &hunks {
8441 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8442 }
8443 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8444 }
8445 drop(chunk_by);
8446 if !revert_changes.is_empty() {
8447 self.transact(window, cx, |editor, window, cx| {
8448 editor.restore(revert_changes, window, cx);
8449 });
8450 }
8451 }
8452
8453 pub fn open_active_item_in_terminal(
8454 &mut self,
8455 _: &OpenInTerminal,
8456 window: &mut Window,
8457 cx: &mut Context<Self>,
8458 ) {
8459 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8460 let project_path = buffer.read(cx).project_path(cx)?;
8461 let project = self.project.as_ref()?.read(cx);
8462 let entry = project.entry_for_path(&project_path, cx)?;
8463 let parent = match &entry.canonical_path {
8464 Some(canonical_path) => canonical_path.to_path_buf(),
8465 None => project.absolute_path(&project_path, cx)?,
8466 }
8467 .parent()?
8468 .to_path_buf();
8469 Some(parent)
8470 }) {
8471 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8472 }
8473 }
8474
8475 fn set_breakpoint_context_menu(
8476 &mut self,
8477 display_row: DisplayRow,
8478 position: Option<Anchor>,
8479 clicked_point: gpui::Point<Pixels>,
8480 window: &mut Window,
8481 cx: &mut Context<Self>,
8482 ) {
8483 if !cx.has_flag::<Debugger>() {
8484 return;
8485 }
8486 let source = self
8487 .buffer
8488 .read(cx)
8489 .snapshot(cx)
8490 .anchor_before(Point::new(display_row.0, 0u32));
8491
8492 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
8493
8494 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8495 self,
8496 source,
8497 clicked_point,
8498 context_menu,
8499 window,
8500 cx,
8501 );
8502 }
8503
8504 fn add_edit_breakpoint_block(
8505 &mut self,
8506 anchor: Anchor,
8507 breakpoint: &Breakpoint,
8508 window: &mut Window,
8509 cx: &mut Context<Self>,
8510 ) {
8511 let weak_editor = cx.weak_entity();
8512 let bp_prompt = cx.new(|cx| {
8513 BreakpointPromptEditor::new(weak_editor, anchor, breakpoint.clone(), window, cx)
8514 });
8515
8516 let height = bp_prompt.update(cx, |this, cx| {
8517 this.prompt
8518 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8519 });
8520 let cloned_prompt = bp_prompt.clone();
8521 let blocks = vec![BlockProperties {
8522 style: BlockStyle::Sticky,
8523 placement: BlockPlacement::Above(anchor),
8524 height,
8525 render: Arc::new(move |cx| {
8526 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8527 cloned_prompt.clone().into_any_element()
8528 }),
8529 priority: 0,
8530 }];
8531
8532 let focus_handle = bp_prompt.focus_handle(cx);
8533 window.focus(&focus_handle);
8534
8535 let block_ids = self.insert_blocks(blocks, None, cx);
8536 bp_prompt.update(cx, |prompt, _| {
8537 prompt.add_block_ids(block_ids);
8538 });
8539 }
8540
8541 fn breakpoint_at_cursor_head(
8542 &self,
8543 window: &mut Window,
8544 cx: &mut Context<Self>,
8545 ) -> Option<(Anchor, Breakpoint)> {
8546 let cursor_position: Point = self.selections.newest(cx).head();
8547 self.breakpoint_at_row(cursor_position.row, window, cx)
8548 }
8549
8550 pub(crate) fn breakpoint_at_row(
8551 &self,
8552 row: u32,
8553 window: &mut Window,
8554 cx: &mut Context<Self>,
8555 ) -> Option<(Anchor, Breakpoint)> {
8556 let snapshot = self.snapshot(window, cx);
8557 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
8558
8559 let project = self.project.clone()?;
8560
8561 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
8562 snapshot
8563 .buffer_snapshot
8564 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
8565 })?;
8566
8567 let enclosing_excerpt = breakpoint_position.excerpt_id;
8568 let buffer = project.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8569 let buffer_snapshot = buffer.read(cx).snapshot();
8570
8571 let row = buffer_snapshot
8572 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
8573 .row;
8574
8575 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
8576 let anchor_end = snapshot
8577 .buffer_snapshot
8578 .anchor_before(Point::new(row, line_len));
8579
8580 let bp = self
8581 .breakpoint_store
8582 .as_ref()?
8583 .read_with(cx, |breakpoint_store, cx| {
8584 breakpoint_store
8585 .breakpoints(
8586 &buffer,
8587 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
8588 &buffer_snapshot,
8589 cx,
8590 )
8591 .next()
8592 .and_then(|(anchor, bp)| {
8593 let breakpoint_row = buffer_snapshot
8594 .summary_for_anchor::<text::PointUtf16>(anchor)
8595 .row;
8596
8597 if breakpoint_row == row {
8598 snapshot
8599 .buffer_snapshot
8600 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8601 .map(|anchor| (anchor, bp.clone()))
8602 } else {
8603 None
8604 }
8605 })
8606 });
8607 bp
8608 }
8609
8610 pub fn edit_log_breakpoint(
8611 &mut self,
8612 _: &EditLogBreakpoint,
8613 window: &mut Window,
8614 cx: &mut Context<Self>,
8615 ) {
8616 let (anchor, bp) = self
8617 .breakpoint_at_cursor_head(window, cx)
8618 .unwrap_or_else(|| {
8619 let cursor_position: Point = self.selections.newest(cx).head();
8620
8621 let breakpoint_position = self
8622 .snapshot(window, cx)
8623 .display_snapshot
8624 .buffer_snapshot
8625 .anchor_before(Point::new(cursor_position.row, 0));
8626
8627 (
8628 breakpoint_position,
8629 Breakpoint {
8630 kind: BreakpointKind::Standard,
8631 state: BreakpointState::Enabled,
8632 },
8633 )
8634 });
8635
8636 self.add_edit_breakpoint_block(anchor, &bp, window, cx);
8637 }
8638
8639 pub fn enable_breakpoint(
8640 &mut self,
8641 _: &crate::actions::EnableBreakpoint,
8642 window: &mut Window,
8643 cx: &mut Context<Self>,
8644 ) {
8645 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8646 if breakpoint.is_disabled() {
8647 self.edit_breakpoint_at_anchor(
8648 anchor,
8649 breakpoint,
8650 BreakpointEditAction::InvertState,
8651 cx,
8652 );
8653 }
8654 }
8655 }
8656
8657 pub fn disable_breakpoint(
8658 &mut self,
8659 _: &crate::actions::DisableBreakpoint,
8660 window: &mut Window,
8661 cx: &mut Context<Self>,
8662 ) {
8663 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8664 if breakpoint.is_enabled() {
8665 self.edit_breakpoint_at_anchor(
8666 anchor,
8667 breakpoint,
8668 BreakpointEditAction::InvertState,
8669 cx,
8670 );
8671 }
8672 }
8673 }
8674
8675 pub fn toggle_breakpoint(
8676 &mut self,
8677 _: &crate::actions::ToggleBreakpoint,
8678 window: &mut Window,
8679 cx: &mut Context<Self>,
8680 ) {
8681 let edit_action = BreakpointEditAction::Toggle;
8682
8683 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8684 self.edit_breakpoint_at_anchor(anchor, breakpoint, edit_action, cx);
8685 } else {
8686 let cursor_position: Point = self.selections.newest(cx).head();
8687
8688 let breakpoint_position = self
8689 .snapshot(window, cx)
8690 .display_snapshot
8691 .buffer_snapshot
8692 .anchor_before(Point::new(cursor_position.row, 0));
8693
8694 self.edit_breakpoint_at_anchor(
8695 breakpoint_position,
8696 Breakpoint::new_standard(),
8697 edit_action,
8698 cx,
8699 );
8700 }
8701 }
8702
8703 pub fn edit_breakpoint_at_anchor(
8704 &mut self,
8705 breakpoint_position: Anchor,
8706 breakpoint: Breakpoint,
8707 edit_action: BreakpointEditAction,
8708 cx: &mut Context<Self>,
8709 ) {
8710 let Some(breakpoint_store) = &self.breakpoint_store else {
8711 return;
8712 };
8713
8714 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8715 if breakpoint_position == Anchor::min() {
8716 self.buffer()
8717 .read(cx)
8718 .excerpt_buffer_ids()
8719 .into_iter()
8720 .next()
8721 } else {
8722 None
8723 }
8724 }) else {
8725 return;
8726 };
8727
8728 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8729 return;
8730 };
8731
8732 breakpoint_store.update(cx, |breakpoint_store, cx| {
8733 breakpoint_store.toggle_breakpoint(
8734 buffer,
8735 (breakpoint_position.text_anchor, breakpoint),
8736 edit_action,
8737 cx,
8738 );
8739 });
8740
8741 cx.notify();
8742 }
8743
8744 #[cfg(any(test, feature = "test-support"))]
8745 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8746 self.breakpoint_store.clone()
8747 }
8748
8749 pub fn prepare_restore_change(
8750 &self,
8751 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8752 hunk: &MultiBufferDiffHunk,
8753 cx: &mut App,
8754 ) -> Option<()> {
8755 if hunk.is_created_file() {
8756 return None;
8757 }
8758 let buffer = self.buffer.read(cx);
8759 let diff = buffer.diff_for(hunk.buffer_id)?;
8760 let buffer = buffer.buffer(hunk.buffer_id)?;
8761 let buffer = buffer.read(cx);
8762 let original_text = diff
8763 .read(cx)
8764 .base_text()
8765 .as_rope()
8766 .slice(hunk.diff_base_byte_range.clone());
8767 let buffer_snapshot = buffer.snapshot();
8768 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8769 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8770 probe
8771 .0
8772 .start
8773 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8774 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8775 }) {
8776 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8777 Some(())
8778 } else {
8779 None
8780 }
8781 }
8782
8783 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8784 self.manipulate_lines(window, cx, |lines| lines.reverse())
8785 }
8786
8787 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8788 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8789 }
8790
8791 fn manipulate_lines<Fn>(
8792 &mut self,
8793 window: &mut Window,
8794 cx: &mut Context<Self>,
8795 mut callback: Fn,
8796 ) where
8797 Fn: FnMut(&mut Vec<&str>),
8798 {
8799 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8800 let buffer = self.buffer.read(cx).snapshot(cx);
8801
8802 let mut edits = Vec::new();
8803
8804 let selections = self.selections.all::<Point>(cx);
8805 let mut selections = selections.iter().peekable();
8806 let mut contiguous_row_selections = Vec::new();
8807 let mut new_selections = Vec::new();
8808 let mut added_lines = 0;
8809 let mut removed_lines = 0;
8810
8811 while let Some(selection) = selections.next() {
8812 let (start_row, end_row) = consume_contiguous_rows(
8813 &mut contiguous_row_selections,
8814 selection,
8815 &display_map,
8816 &mut selections,
8817 );
8818
8819 let start_point = Point::new(start_row.0, 0);
8820 let end_point = Point::new(
8821 end_row.previous_row().0,
8822 buffer.line_len(end_row.previous_row()),
8823 );
8824 let text = buffer
8825 .text_for_range(start_point..end_point)
8826 .collect::<String>();
8827
8828 let mut lines = text.split('\n').collect_vec();
8829
8830 let lines_before = lines.len();
8831 callback(&mut lines);
8832 let lines_after = lines.len();
8833
8834 edits.push((start_point..end_point, lines.join("\n")));
8835
8836 // Selections must change based on added and removed line count
8837 let start_row =
8838 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8839 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8840 new_selections.push(Selection {
8841 id: selection.id,
8842 start: start_row,
8843 end: end_row,
8844 goal: SelectionGoal::None,
8845 reversed: selection.reversed,
8846 });
8847
8848 if lines_after > lines_before {
8849 added_lines += lines_after - lines_before;
8850 } else if lines_before > lines_after {
8851 removed_lines += lines_before - lines_after;
8852 }
8853 }
8854
8855 self.transact(window, cx, |this, window, cx| {
8856 let buffer = this.buffer.update(cx, |buffer, cx| {
8857 buffer.edit(edits, None, cx);
8858 buffer.snapshot(cx)
8859 });
8860
8861 // Recalculate offsets on newly edited buffer
8862 let new_selections = new_selections
8863 .iter()
8864 .map(|s| {
8865 let start_point = Point::new(s.start.0, 0);
8866 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8867 Selection {
8868 id: s.id,
8869 start: buffer.point_to_offset(start_point),
8870 end: buffer.point_to_offset(end_point),
8871 goal: s.goal,
8872 reversed: s.reversed,
8873 }
8874 })
8875 .collect();
8876
8877 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8878 s.select(new_selections);
8879 });
8880
8881 this.request_autoscroll(Autoscroll::fit(), cx);
8882 });
8883 }
8884
8885 pub fn convert_to_upper_case(
8886 &mut self,
8887 _: &ConvertToUpperCase,
8888 window: &mut Window,
8889 cx: &mut Context<Self>,
8890 ) {
8891 self.manipulate_text(window, cx, |text| text.to_uppercase())
8892 }
8893
8894 pub fn convert_to_lower_case(
8895 &mut self,
8896 _: &ConvertToLowerCase,
8897 window: &mut Window,
8898 cx: &mut Context<Self>,
8899 ) {
8900 self.manipulate_text(window, cx, |text| text.to_lowercase())
8901 }
8902
8903 pub fn convert_to_title_case(
8904 &mut self,
8905 _: &ConvertToTitleCase,
8906 window: &mut Window,
8907 cx: &mut Context<Self>,
8908 ) {
8909 self.manipulate_text(window, cx, |text| {
8910 text.split('\n')
8911 .map(|line| line.to_case(Case::Title))
8912 .join("\n")
8913 })
8914 }
8915
8916 pub fn convert_to_snake_case(
8917 &mut self,
8918 _: &ConvertToSnakeCase,
8919 window: &mut Window,
8920 cx: &mut Context<Self>,
8921 ) {
8922 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8923 }
8924
8925 pub fn convert_to_kebab_case(
8926 &mut self,
8927 _: &ConvertToKebabCase,
8928 window: &mut Window,
8929 cx: &mut Context<Self>,
8930 ) {
8931 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8932 }
8933
8934 pub fn convert_to_upper_camel_case(
8935 &mut self,
8936 _: &ConvertToUpperCamelCase,
8937 window: &mut Window,
8938 cx: &mut Context<Self>,
8939 ) {
8940 self.manipulate_text(window, cx, |text| {
8941 text.split('\n')
8942 .map(|line| line.to_case(Case::UpperCamel))
8943 .join("\n")
8944 })
8945 }
8946
8947 pub fn convert_to_lower_camel_case(
8948 &mut self,
8949 _: &ConvertToLowerCamelCase,
8950 window: &mut Window,
8951 cx: &mut Context<Self>,
8952 ) {
8953 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8954 }
8955
8956 pub fn convert_to_opposite_case(
8957 &mut self,
8958 _: &ConvertToOppositeCase,
8959 window: &mut Window,
8960 cx: &mut Context<Self>,
8961 ) {
8962 self.manipulate_text(window, cx, |text| {
8963 text.chars()
8964 .fold(String::with_capacity(text.len()), |mut t, c| {
8965 if c.is_uppercase() {
8966 t.extend(c.to_lowercase());
8967 } else {
8968 t.extend(c.to_uppercase());
8969 }
8970 t
8971 })
8972 })
8973 }
8974
8975 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8976 where
8977 Fn: FnMut(&str) -> String,
8978 {
8979 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8980 let buffer = self.buffer.read(cx).snapshot(cx);
8981
8982 let mut new_selections = Vec::new();
8983 let mut edits = Vec::new();
8984 let mut selection_adjustment = 0i32;
8985
8986 for selection in self.selections.all::<usize>(cx) {
8987 let selection_is_empty = selection.is_empty();
8988
8989 let (start, end) = if selection_is_empty {
8990 let word_range = movement::surrounding_word(
8991 &display_map,
8992 selection.start.to_display_point(&display_map),
8993 );
8994 let start = word_range.start.to_offset(&display_map, Bias::Left);
8995 let end = word_range.end.to_offset(&display_map, Bias::Left);
8996 (start, end)
8997 } else {
8998 (selection.start, selection.end)
8999 };
9000
9001 let text = buffer.text_for_range(start..end).collect::<String>();
9002 let old_length = text.len() as i32;
9003 let text = callback(&text);
9004
9005 new_selections.push(Selection {
9006 start: (start as i32 - selection_adjustment) as usize,
9007 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
9008 goal: SelectionGoal::None,
9009 ..selection
9010 });
9011
9012 selection_adjustment += old_length - text.len() as i32;
9013
9014 edits.push((start..end, text));
9015 }
9016
9017 self.transact(window, cx, |this, window, cx| {
9018 this.buffer.update(cx, |buffer, cx| {
9019 buffer.edit(edits, None, cx);
9020 });
9021
9022 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9023 s.select(new_selections);
9024 });
9025
9026 this.request_autoscroll(Autoscroll::fit(), cx);
9027 });
9028 }
9029
9030 pub fn duplicate(
9031 &mut self,
9032 upwards: bool,
9033 whole_lines: bool,
9034 window: &mut Window,
9035 cx: &mut Context<Self>,
9036 ) {
9037 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9038 let buffer = &display_map.buffer_snapshot;
9039 let selections = self.selections.all::<Point>(cx);
9040
9041 let mut edits = Vec::new();
9042 let mut selections_iter = selections.iter().peekable();
9043 while let Some(selection) = selections_iter.next() {
9044 let mut rows = selection.spanned_rows(false, &display_map);
9045 // duplicate line-wise
9046 if whole_lines || selection.start == selection.end {
9047 // Avoid duplicating the same lines twice.
9048 while let Some(next_selection) = selections_iter.peek() {
9049 let next_rows = next_selection.spanned_rows(false, &display_map);
9050 if next_rows.start < rows.end {
9051 rows.end = next_rows.end;
9052 selections_iter.next().unwrap();
9053 } else {
9054 break;
9055 }
9056 }
9057
9058 // Copy the text from the selected row region and splice it either at the start
9059 // or end of the region.
9060 let start = Point::new(rows.start.0, 0);
9061 let end = Point::new(
9062 rows.end.previous_row().0,
9063 buffer.line_len(rows.end.previous_row()),
9064 );
9065 let text = buffer
9066 .text_for_range(start..end)
9067 .chain(Some("\n"))
9068 .collect::<String>();
9069 let insert_location = if upwards {
9070 Point::new(rows.end.0, 0)
9071 } else {
9072 start
9073 };
9074 edits.push((insert_location..insert_location, text));
9075 } else {
9076 // duplicate character-wise
9077 let start = selection.start;
9078 let end = selection.end;
9079 let text = buffer.text_for_range(start..end).collect::<String>();
9080 edits.push((selection.end..selection.end, text));
9081 }
9082 }
9083
9084 self.transact(window, cx, |this, _, cx| {
9085 this.buffer.update(cx, |buffer, cx| {
9086 buffer.edit(edits, None, cx);
9087 });
9088
9089 this.request_autoscroll(Autoscroll::fit(), cx);
9090 });
9091 }
9092
9093 pub fn duplicate_line_up(
9094 &mut self,
9095 _: &DuplicateLineUp,
9096 window: &mut Window,
9097 cx: &mut Context<Self>,
9098 ) {
9099 self.duplicate(true, true, window, cx);
9100 }
9101
9102 pub fn duplicate_line_down(
9103 &mut self,
9104 _: &DuplicateLineDown,
9105 window: &mut Window,
9106 cx: &mut Context<Self>,
9107 ) {
9108 self.duplicate(false, true, window, cx);
9109 }
9110
9111 pub fn duplicate_selection(
9112 &mut self,
9113 _: &DuplicateSelection,
9114 window: &mut Window,
9115 cx: &mut Context<Self>,
9116 ) {
9117 self.duplicate(false, false, window, cx);
9118 }
9119
9120 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
9121 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9122 let buffer = self.buffer.read(cx).snapshot(cx);
9123
9124 let mut edits = Vec::new();
9125 let mut unfold_ranges = Vec::new();
9126 let mut refold_creases = Vec::new();
9127
9128 let selections = self.selections.all::<Point>(cx);
9129 let mut selections = selections.iter().peekable();
9130 let mut contiguous_row_selections = Vec::new();
9131 let mut new_selections = Vec::new();
9132
9133 while let Some(selection) = selections.next() {
9134 // Find all the selections that span a contiguous row range
9135 let (start_row, end_row) = consume_contiguous_rows(
9136 &mut contiguous_row_selections,
9137 selection,
9138 &display_map,
9139 &mut selections,
9140 );
9141
9142 // Move the text spanned by the row range to be before the line preceding the row range
9143 if start_row.0 > 0 {
9144 let range_to_move = Point::new(
9145 start_row.previous_row().0,
9146 buffer.line_len(start_row.previous_row()),
9147 )
9148 ..Point::new(
9149 end_row.previous_row().0,
9150 buffer.line_len(end_row.previous_row()),
9151 );
9152 let insertion_point = display_map
9153 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9154 .0;
9155
9156 // Don't move lines across excerpts
9157 if buffer
9158 .excerpt_containing(insertion_point..range_to_move.end)
9159 .is_some()
9160 {
9161 let text = buffer
9162 .text_for_range(range_to_move.clone())
9163 .flat_map(|s| s.chars())
9164 .skip(1)
9165 .chain(['\n'])
9166 .collect::<String>();
9167
9168 edits.push((
9169 buffer.anchor_after(range_to_move.start)
9170 ..buffer.anchor_before(range_to_move.end),
9171 String::new(),
9172 ));
9173 let insertion_anchor = buffer.anchor_after(insertion_point);
9174 edits.push((insertion_anchor..insertion_anchor, text));
9175
9176 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9177
9178 // Move selections up
9179 new_selections.extend(contiguous_row_selections.drain(..).map(
9180 |mut selection| {
9181 selection.start.row -= row_delta;
9182 selection.end.row -= row_delta;
9183 selection
9184 },
9185 ));
9186
9187 // Move folds up
9188 unfold_ranges.push(range_to_move.clone());
9189 for fold in display_map.folds_in_range(
9190 buffer.anchor_before(range_to_move.start)
9191 ..buffer.anchor_after(range_to_move.end),
9192 ) {
9193 let mut start = fold.range.start.to_point(&buffer);
9194 let mut end = fold.range.end.to_point(&buffer);
9195 start.row -= row_delta;
9196 end.row -= row_delta;
9197 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9198 }
9199 }
9200 }
9201
9202 // If we didn't move line(s), preserve the existing selections
9203 new_selections.append(&mut contiguous_row_selections);
9204 }
9205
9206 self.transact(window, cx, |this, window, cx| {
9207 this.unfold_ranges(&unfold_ranges, true, true, cx);
9208 this.buffer.update(cx, |buffer, cx| {
9209 for (range, text) in edits {
9210 buffer.edit([(range, text)], None, cx);
9211 }
9212 });
9213 this.fold_creases(refold_creases, true, window, cx);
9214 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9215 s.select(new_selections);
9216 })
9217 });
9218 }
9219
9220 pub fn move_line_down(
9221 &mut self,
9222 _: &MoveLineDown,
9223 window: &mut Window,
9224 cx: &mut Context<Self>,
9225 ) {
9226 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9227 let buffer = self.buffer.read(cx).snapshot(cx);
9228
9229 let mut edits = Vec::new();
9230 let mut unfold_ranges = Vec::new();
9231 let mut refold_creases = Vec::new();
9232
9233 let selections = self.selections.all::<Point>(cx);
9234 let mut selections = selections.iter().peekable();
9235 let mut contiguous_row_selections = Vec::new();
9236 let mut new_selections = Vec::new();
9237
9238 while let Some(selection) = selections.next() {
9239 // Find all the selections that span a contiguous row range
9240 let (start_row, end_row) = consume_contiguous_rows(
9241 &mut contiguous_row_selections,
9242 selection,
9243 &display_map,
9244 &mut selections,
9245 );
9246
9247 // Move the text spanned by the row range to be after the last line of the row range
9248 if end_row.0 <= buffer.max_point().row {
9249 let range_to_move =
9250 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9251 let insertion_point = display_map
9252 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9253 .0;
9254
9255 // Don't move lines across excerpt boundaries
9256 if buffer
9257 .excerpt_containing(range_to_move.start..insertion_point)
9258 .is_some()
9259 {
9260 let mut text = String::from("\n");
9261 text.extend(buffer.text_for_range(range_to_move.clone()));
9262 text.pop(); // Drop trailing newline
9263 edits.push((
9264 buffer.anchor_after(range_to_move.start)
9265 ..buffer.anchor_before(range_to_move.end),
9266 String::new(),
9267 ));
9268 let insertion_anchor = buffer.anchor_after(insertion_point);
9269 edits.push((insertion_anchor..insertion_anchor, text));
9270
9271 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9272
9273 // Move selections down
9274 new_selections.extend(contiguous_row_selections.drain(..).map(
9275 |mut selection| {
9276 selection.start.row += row_delta;
9277 selection.end.row += row_delta;
9278 selection
9279 },
9280 ));
9281
9282 // Move folds down
9283 unfold_ranges.push(range_to_move.clone());
9284 for fold in display_map.folds_in_range(
9285 buffer.anchor_before(range_to_move.start)
9286 ..buffer.anchor_after(range_to_move.end),
9287 ) {
9288 let mut start = fold.range.start.to_point(&buffer);
9289 let mut end = fold.range.end.to_point(&buffer);
9290 start.row += row_delta;
9291 end.row += row_delta;
9292 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9293 }
9294 }
9295 }
9296
9297 // If we didn't move line(s), preserve the existing selections
9298 new_selections.append(&mut contiguous_row_selections);
9299 }
9300
9301 self.transact(window, cx, |this, window, cx| {
9302 this.unfold_ranges(&unfold_ranges, true, true, cx);
9303 this.buffer.update(cx, |buffer, cx| {
9304 for (range, text) in edits {
9305 buffer.edit([(range, text)], None, cx);
9306 }
9307 });
9308 this.fold_creases(refold_creases, true, window, cx);
9309 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9310 s.select(new_selections)
9311 });
9312 });
9313 }
9314
9315 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9316 let text_layout_details = &self.text_layout_details(window);
9317 self.transact(window, cx, |this, window, cx| {
9318 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9319 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9320 let line_mode = s.line_mode;
9321 s.move_with(|display_map, selection| {
9322 if !selection.is_empty() || line_mode {
9323 return;
9324 }
9325
9326 let mut head = selection.head();
9327 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9328 if head.column() == display_map.line_len(head.row()) {
9329 transpose_offset = display_map
9330 .buffer_snapshot
9331 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9332 }
9333
9334 if transpose_offset == 0 {
9335 return;
9336 }
9337
9338 *head.column_mut() += 1;
9339 head = display_map.clip_point(head, Bias::Right);
9340 let goal = SelectionGoal::HorizontalPosition(
9341 display_map
9342 .x_for_display_point(head, text_layout_details)
9343 .into(),
9344 );
9345 selection.collapse_to(head, goal);
9346
9347 let transpose_start = display_map
9348 .buffer_snapshot
9349 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9350 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9351 let transpose_end = display_map
9352 .buffer_snapshot
9353 .clip_offset(transpose_offset + 1, Bias::Right);
9354 if let Some(ch) =
9355 display_map.buffer_snapshot.chars_at(transpose_start).next()
9356 {
9357 edits.push((transpose_start..transpose_offset, String::new()));
9358 edits.push((transpose_end..transpose_end, ch.to_string()));
9359 }
9360 }
9361 });
9362 edits
9363 });
9364 this.buffer
9365 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9366 let selections = this.selections.all::<usize>(cx);
9367 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9368 s.select(selections);
9369 });
9370 });
9371 }
9372
9373 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9374 self.rewrap_impl(RewrapOptions::default(), cx)
9375 }
9376
9377 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9378 let buffer = self.buffer.read(cx).snapshot(cx);
9379 let selections = self.selections.all::<Point>(cx);
9380 let mut selections = selections.iter().peekable();
9381
9382 let mut edits = Vec::new();
9383 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9384
9385 while let Some(selection) = selections.next() {
9386 let mut start_row = selection.start.row;
9387 let mut end_row = selection.end.row;
9388
9389 // Skip selections that overlap with a range that has already been rewrapped.
9390 let selection_range = start_row..end_row;
9391 if rewrapped_row_ranges
9392 .iter()
9393 .any(|range| range.overlaps(&selection_range))
9394 {
9395 continue;
9396 }
9397
9398 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9399
9400 // Since not all lines in the selection may be at the same indent
9401 // level, choose the indent size that is the most common between all
9402 // of the lines.
9403 //
9404 // If there is a tie, we use the deepest indent.
9405 let (indent_size, indent_end) = {
9406 let mut indent_size_occurrences = HashMap::default();
9407 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9408
9409 for row in start_row..=end_row {
9410 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9411 rows_by_indent_size.entry(indent).or_default().push(row);
9412 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9413 }
9414
9415 let indent_size = indent_size_occurrences
9416 .into_iter()
9417 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9418 .map(|(indent, _)| indent)
9419 .unwrap_or_default();
9420 let row = rows_by_indent_size[&indent_size][0];
9421 let indent_end = Point::new(row, indent_size.len);
9422
9423 (indent_size, indent_end)
9424 };
9425
9426 let mut line_prefix = indent_size.chars().collect::<String>();
9427
9428 let mut inside_comment = false;
9429 if let Some(comment_prefix) =
9430 buffer
9431 .language_scope_at(selection.head())
9432 .and_then(|language| {
9433 language
9434 .line_comment_prefixes()
9435 .iter()
9436 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9437 .cloned()
9438 })
9439 {
9440 line_prefix.push_str(&comment_prefix);
9441 inside_comment = true;
9442 }
9443
9444 let language_settings = buffer.language_settings_at(selection.head(), cx);
9445 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9446 RewrapBehavior::InComments => inside_comment,
9447 RewrapBehavior::InSelections => !selection.is_empty(),
9448 RewrapBehavior::Anywhere => true,
9449 };
9450
9451 let should_rewrap = options.override_language_settings
9452 || allow_rewrap_based_on_language
9453 || self.hard_wrap.is_some();
9454 if !should_rewrap {
9455 continue;
9456 }
9457
9458 if selection.is_empty() {
9459 'expand_upwards: while start_row > 0 {
9460 let prev_row = start_row - 1;
9461 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9462 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9463 {
9464 start_row = prev_row;
9465 } else {
9466 break 'expand_upwards;
9467 }
9468 }
9469
9470 'expand_downwards: while end_row < buffer.max_point().row {
9471 let next_row = end_row + 1;
9472 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9473 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9474 {
9475 end_row = next_row;
9476 } else {
9477 break 'expand_downwards;
9478 }
9479 }
9480 }
9481
9482 let start = Point::new(start_row, 0);
9483 let start_offset = start.to_offset(&buffer);
9484 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9485 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9486 let Some(lines_without_prefixes) = selection_text
9487 .lines()
9488 .map(|line| {
9489 line.strip_prefix(&line_prefix)
9490 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9491 .ok_or_else(|| {
9492 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9493 })
9494 })
9495 .collect::<Result<Vec<_>, _>>()
9496 .log_err()
9497 else {
9498 continue;
9499 };
9500
9501 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9502 buffer
9503 .language_settings_at(Point::new(start_row, 0), cx)
9504 .preferred_line_length as usize
9505 });
9506 let wrapped_text = wrap_with_prefix(
9507 line_prefix,
9508 lines_without_prefixes.join("\n"),
9509 wrap_column,
9510 tab_size,
9511 options.preserve_existing_whitespace,
9512 );
9513
9514 // TODO: should always use char-based diff while still supporting cursor behavior that
9515 // matches vim.
9516 let mut diff_options = DiffOptions::default();
9517 if options.override_language_settings {
9518 diff_options.max_word_diff_len = 0;
9519 diff_options.max_word_diff_line_count = 0;
9520 } else {
9521 diff_options.max_word_diff_len = usize::MAX;
9522 diff_options.max_word_diff_line_count = usize::MAX;
9523 }
9524
9525 for (old_range, new_text) in
9526 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9527 {
9528 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9529 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9530 edits.push((edit_start..edit_end, new_text));
9531 }
9532
9533 rewrapped_row_ranges.push(start_row..=end_row);
9534 }
9535
9536 self.buffer
9537 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9538 }
9539
9540 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9541 let mut text = String::new();
9542 let buffer = self.buffer.read(cx).snapshot(cx);
9543 let mut selections = self.selections.all::<Point>(cx);
9544 let mut clipboard_selections = Vec::with_capacity(selections.len());
9545 {
9546 let max_point = buffer.max_point();
9547 let mut is_first = true;
9548 for selection in &mut selections {
9549 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9550 if is_entire_line {
9551 selection.start = Point::new(selection.start.row, 0);
9552 if !selection.is_empty() && selection.end.column == 0 {
9553 selection.end = cmp::min(max_point, selection.end);
9554 } else {
9555 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9556 }
9557 selection.goal = SelectionGoal::None;
9558 }
9559 if is_first {
9560 is_first = false;
9561 } else {
9562 text += "\n";
9563 }
9564 let mut len = 0;
9565 for chunk in buffer.text_for_range(selection.start..selection.end) {
9566 text.push_str(chunk);
9567 len += chunk.len();
9568 }
9569 clipboard_selections.push(ClipboardSelection {
9570 len,
9571 is_entire_line,
9572 first_line_indent: buffer
9573 .indent_size_for_line(MultiBufferRow(selection.start.row))
9574 .len,
9575 });
9576 }
9577 }
9578
9579 self.transact(window, cx, |this, window, cx| {
9580 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9581 s.select(selections);
9582 });
9583 this.insert("", window, cx);
9584 });
9585 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9586 }
9587
9588 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9589 let item = self.cut_common(window, cx);
9590 cx.write_to_clipboard(item);
9591 }
9592
9593 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9594 self.change_selections(None, window, cx, |s| {
9595 s.move_with(|snapshot, sel| {
9596 if sel.is_empty() {
9597 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9598 }
9599 });
9600 });
9601 let item = self.cut_common(window, cx);
9602 cx.set_global(KillRing(item))
9603 }
9604
9605 pub fn kill_ring_yank(
9606 &mut self,
9607 _: &KillRingYank,
9608 window: &mut Window,
9609 cx: &mut Context<Self>,
9610 ) {
9611 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9612 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9613 (kill_ring.text().to_string(), kill_ring.metadata_json())
9614 } else {
9615 return;
9616 }
9617 } else {
9618 return;
9619 };
9620 self.do_paste(&text, metadata, false, window, cx);
9621 }
9622
9623 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9624 self.do_copy(true, cx);
9625 }
9626
9627 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9628 self.do_copy(false, cx);
9629 }
9630
9631 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9632 let selections = self.selections.all::<Point>(cx);
9633 let buffer = self.buffer.read(cx).read(cx);
9634 let mut text = String::new();
9635
9636 let mut clipboard_selections = Vec::with_capacity(selections.len());
9637 {
9638 let max_point = buffer.max_point();
9639 let mut is_first = true;
9640 for selection in &selections {
9641 let mut start = selection.start;
9642 let mut end = selection.end;
9643 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9644 if is_entire_line {
9645 start = Point::new(start.row, 0);
9646 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9647 }
9648
9649 let mut trimmed_selections = Vec::new();
9650 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9651 let row = MultiBufferRow(start.row);
9652 let first_indent = buffer.indent_size_for_line(row);
9653 if first_indent.len == 0 || start.column > first_indent.len {
9654 trimmed_selections.push(start..end);
9655 } else {
9656 trimmed_selections.push(
9657 Point::new(row.0, first_indent.len)
9658 ..Point::new(row.0, buffer.line_len(row)),
9659 );
9660 for row in start.row + 1..=end.row {
9661 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9662 if row_indent_size.len >= first_indent.len {
9663 trimmed_selections.push(
9664 Point::new(row, first_indent.len)
9665 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9666 );
9667 } else {
9668 trimmed_selections.clear();
9669 trimmed_selections.push(start..end);
9670 break;
9671 }
9672 }
9673 }
9674 } else {
9675 trimmed_selections.push(start..end);
9676 }
9677
9678 for trimmed_range in trimmed_selections {
9679 if is_first {
9680 is_first = false;
9681 } else {
9682 text += "\n";
9683 }
9684 let mut len = 0;
9685 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9686 text.push_str(chunk);
9687 len += chunk.len();
9688 }
9689 clipboard_selections.push(ClipboardSelection {
9690 len,
9691 is_entire_line,
9692 first_line_indent: buffer
9693 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9694 .len,
9695 });
9696 }
9697 }
9698 }
9699
9700 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9701 text,
9702 clipboard_selections,
9703 ));
9704 }
9705
9706 pub fn do_paste(
9707 &mut self,
9708 text: &String,
9709 clipboard_selections: Option<Vec<ClipboardSelection>>,
9710 handle_entire_lines: bool,
9711 window: &mut Window,
9712 cx: &mut Context<Self>,
9713 ) {
9714 if self.read_only(cx) {
9715 return;
9716 }
9717
9718 let clipboard_text = Cow::Borrowed(text);
9719
9720 self.transact(window, cx, |this, window, cx| {
9721 if let Some(mut clipboard_selections) = clipboard_selections {
9722 let old_selections = this.selections.all::<usize>(cx);
9723 let all_selections_were_entire_line =
9724 clipboard_selections.iter().all(|s| s.is_entire_line);
9725 let first_selection_indent_column =
9726 clipboard_selections.first().map(|s| s.first_line_indent);
9727 if clipboard_selections.len() != old_selections.len() {
9728 clipboard_selections.drain(..);
9729 }
9730 let cursor_offset = this.selections.last::<usize>(cx).head();
9731 let mut auto_indent_on_paste = true;
9732
9733 this.buffer.update(cx, |buffer, cx| {
9734 let snapshot = buffer.read(cx);
9735 auto_indent_on_paste = snapshot
9736 .language_settings_at(cursor_offset, cx)
9737 .auto_indent_on_paste;
9738
9739 let mut start_offset = 0;
9740 let mut edits = Vec::new();
9741 let mut original_indent_columns = Vec::new();
9742 for (ix, selection) in old_selections.iter().enumerate() {
9743 let to_insert;
9744 let entire_line;
9745 let original_indent_column;
9746 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9747 let end_offset = start_offset + clipboard_selection.len;
9748 to_insert = &clipboard_text[start_offset..end_offset];
9749 entire_line = clipboard_selection.is_entire_line;
9750 start_offset = end_offset + 1;
9751 original_indent_column = Some(clipboard_selection.first_line_indent);
9752 } else {
9753 to_insert = clipboard_text.as_str();
9754 entire_line = all_selections_were_entire_line;
9755 original_indent_column = first_selection_indent_column
9756 }
9757
9758 // If the corresponding selection was empty when this slice of the
9759 // clipboard text was written, then the entire line containing the
9760 // selection was copied. If this selection is also currently empty,
9761 // then paste the line before the current line of the buffer.
9762 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9763 let column = selection.start.to_point(&snapshot).column as usize;
9764 let line_start = selection.start - column;
9765 line_start..line_start
9766 } else {
9767 selection.range()
9768 };
9769
9770 edits.push((range, to_insert));
9771 original_indent_columns.push(original_indent_column);
9772 }
9773 drop(snapshot);
9774
9775 buffer.edit(
9776 edits,
9777 if auto_indent_on_paste {
9778 Some(AutoindentMode::Block {
9779 original_indent_columns,
9780 })
9781 } else {
9782 None
9783 },
9784 cx,
9785 );
9786 });
9787
9788 let selections = this.selections.all::<usize>(cx);
9789 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9790 s.select(selections)
9791 });
9792 } else {
9793 this.insert(&clipboard_text, window, cx);
9794 }
9795 });
9796 }
9797
9798 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9799 if let Some(item) = cx.read_from_clipboard() {
9800 let entries = item.entries();
9801
9802 match entries.first() {
9803 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9804 // of all the pasted entries.
9805 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9806 .do_paste(
9807 clipboard_string.text(),
9808 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9809 true,
9810 window,
9811 cx,
9812 ),
9813 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9814 }
9815 }
9816 }
9817
9818 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9819 if self.read_only(cx) {
9820 return;
9821 }
9822
9823 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9824 if let Some((selections, _)) =
9825 self.selection_history.transaction(transaction_id).cloned()
9826 {
9827 self.change_selections(None, window, cx, |s| {
9828 s.select_anchors(selections.to_vec());
9829 });
9830 } else {
9831 log::error!(
9832 "No entry in selection_history found for undo. \
9833 This may correspond to a bug where undo does not update the selection. \
9834 If this is occurring, please add details to \
9835 https://github.com/zed-industries/zed/issues/22692"
9836 );
9837 }
9838 self.request_autoscroll(Autoscroll::fit(), cx);
9839 self.unmark_text(window, cx);
9840 self.refresh_inline_completion(true, false, window, cx);
9841 cx.emit(EditorEvent::Edited { transaction_id });
9842 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9843 }
9844 }
9845
9846 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9847 if self.read_only(cx) {
9848 return;
9849 }
9850
9851 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9852 if let Some((_, Some(selections))) =
9853 self.selection_history.transaction(transaction_id).cloned()
9854 {
9855 self.change_selections(None, window, cx, |s| {
9856 s.select_anchors(selections.to_vec());
9857 });
9858 } else {
9859 log::error!(
9860 "No entry in selection_history found for redo. \
9861 This may correspond to a bug where undo does not update the selection. \
9862 If this is occurring, please add details to \
9863 https://github.com/zed-industries/zed/issues/22692"
9864 );
9865 }
9866 self.request_autoscroll(Autoscroll::fit(), cx);
9867 self.unmark_text(window, cx);
9868 self.refresh_inline_completion(true, false, window, cx);
9869 cx.emit(EditorEvent::Edited { transaction_id });
9870 }
9871 }
9872
9873 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9874 self.buffer
9875 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9876 }
9877
9878 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9879 self.buffer
9880 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9881 }
9882
9883 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9884 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9885 let line_mode = s.line_mode;
9886 s.move_with(|map, selection| {
9887 let cursor = if selection.is_empty() && !line_mode {
9888 movement::left(map, selection.start)
9889 } else {
9890 selection.start
9891 };
9892 selection.collapse_to(cursor, SelectionGoal::None);
9893 });
9894 })
9895 }
9896
9897 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9898 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9899 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9900 })
9901 }
9902
9903 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9904 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9905 let line_mode = s.line_mode;
9906 s.move_with(|map, selection| {
9907 let cursor = if selection.is_empty() && !line_mode {
9908 movement::right(map, selection.end)
9909 } else {
9910 selection.end
9911 };
9912 selection.collapse_to(cursor, SelectionGoal::None)
9913 });
9914 })
9915 }
9916
9917 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9918 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9919 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9920 })
9921 }
9922
9923 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9924 if self.take_rename(true, window, cx).is_some() {
9925 return;
9926 }
9927
9928 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9929 cx.propagate();
9930 return;
9931 }
9932
9933 let text_layout_details = &self.text_layout_details(window);
9934 let selection_count = self.selections.count();
9935 let first_selection = self.selections.first_anchor();
9936
9937 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9938 let line_mode = s.line_mode;
9939 s.move_with(|map, selection| {
9940 if !selection.is_empty() && !line_mode {
9941 selection.goal = SelectionGoal::None;
9942 }
9943 let (cursor, goal) = movement::up(
9944 map,
9945 selection.start,
9946 selection.goal,
9947 false,
9948 text_layout_details,
9949 );
9950 selection.collapse_to(cursor, goal);
9951 });
9952 });
9953
9954 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9955 {
9956 cx.propagate();
9957 }
9958 }
9959
9960 pub fn move_up_by_lines(
9961 &mut self,
9962 action: &MoveUpByLines,
9963 window: &mut Window,
9964 cx: &mut Context<Self>,
9965 ) {
9966 if self.take_rename(true, window, cx).is_some() {
9967 return;
9968 }
9969
9970 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9971 cx.propagate();
9972 return;
9973 }
9974
9975 let text_layout_details = &self.text_layout_details(window);
9976
9977 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9978 let line_mode = s.line_mode;
9979 s.move_with(|map, selection| {
9980 if !selection.is_empty() && !line_mode {
9981 selection.goal = SelectionGoal::None;
9982 }
9983 let (cursor, goal) = movement::up_by_rows(
9984 map,
9985 selection.start,
9986 action.lines,
9987 selection.goal,
9988 false,
9989 text_layout_details,
9990 );
9991 selection.collapse_to(cursor, goal);
9992 });
9993 })
9994 }
9995
9996 pub fn move_down_by_lines(
9997 &mut self,
9998 action: &MoveDownByLines,
9999 window: &mut Window,
10000 cx: &mut Context<Self>,
10001 ) {
10002 if self.take_rename(true, window, cx).is_some() {
10003 return;
10004 }
10005
10006 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10007 cx.propagate();
10008 return;
10009 }
10010
10011 let text_layout_details = &self.text_layout_details(window);
10012
10013 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10014 let line_mode = s.line_mode;
10015 s.move_with(|map, selection| {
10016 if !selection.is_empty() && !line_mode {
10017 selection.goal = SelectionGoal::None;
10018 }
10019 let (cursor, goal) = movement::down_by_rows(
10020 map,
10021 selection.start,
10022 action.lines,
10023 selection.goal,
10024 false,
10025 text_layout_details,
10026 );
10027 selection.collapse_to(cursor, goal);
10028 });
10029 })
10030 }
10031
10032 pub fn select_down_by_lines(
10033 &mut self,
10034 action: &SelectDownByLines,
10035 window: &mut Window,
10036 cx: &mut Context<Self>,
10037 ) {
10038 let text_layout_details = &self.text_layout_details(window);
10039 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10040 s.move_heads_with(|map, head, goal| {
10041 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
10042 })
10043 })
10044 }
10045
10046 pub fn select_up_by_lines(
10047 &mut self,
10048 action: &SelectUpByLines,
10049 window: &mut Window,
10050 cx: &mut Context<Self>,
10051 ) {
10052 let text_layout_details = &self.text_layout_details(window);
10053 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10054 s.move_heads_with(|map, head, goal| {
10055 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
10056 })
10057 })
10058 }
10059
10060 pub fn select_page_up(
10061 &mut self,
10062 _: &SelectPageUp,
10063 window: &mut Window,
10064 cx: &mut Context<Self>,
10065 ) {
10066 let Some(row_count) = self.visible_row_count() else {
10067 return;
10068 };
10069
10070 let text_layout_details = &self.text_layout_details(window);
10071
10072 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10073 s.move_heads_with(|map, head, goal| {
10074 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
10075 })
10076 })
10077 }
10078
10079 pub fn move_page_up(
10080 &mut self,
10081 action: &MovePageUp,
10082 window: &mut Window,
10083 cx: &mut Context<Self>,
10084 ) {
10085 if self.take_rename(true, window, cx).is_some() {
10086 return;
10087 }
10088
10089 if self
10090 .context_menu
10091 .borrow_mut()
10092 .as_mut()
10093 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
10094 .unwrap_or(false)
10095 {
10096 return;
10097 }
10098
10099 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10100 cx.propagate();
10101 return;
10102 }
10103
10104 let Some(row_count) = self.visible_row_count() else {
10105 return;
10106 };
10107
10108 let autoscroll = if action.center_cursor {
10109 Autoscroll::center()
10110 } else {
10111 Autoscroll::fit()
10112 };
10113
10114 let text_layout_details = &self.text_layout_details(window);
10115
10116 self.change_selections(Some(autoscroll), window, cx, |s| {
10117 let line_mode = s.line_mode;
10118 s.move_with(|map, selection| {
10119 if !selection.is_empty() && !line_mode {
10120 selection.goal = SelectionGoal::None;
10121 }
10122 let (cursor, goal) = movement::up_by_rows(
10123 map,
10124 selection.end,
10125 row_count,
10126 selection.goal,
10127 false,
10128 text_layout_details,
10129 );
10130 selection.collapse_to(cursor, goal);
10131 });
10132 });
10133 }
10134
10135 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
10136 let text_layout_details = &self.text_layout_details(window);
10137 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10138 s.move_heads_with(|map, head, goal| {
10139 movement::up(map, head, goal, false, text_layout_details)
10140 })
10141 })
10142 }
10143
10144 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
10145 self.take_rename(true, window, cx);
10146
10147 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10148 cx.propagate();
10149 return;
10150 }
10151
10152 let text_layout_details = &self.text_layout_details(window);
10153 let selection_count = self.selections.count();
10154 let first_selection = self.selections.first_anchor();
10155
10156 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10157 let line_mode = s.line_mode;
10158 s.move_with(|map, selection| {
10159 if !selection.is_empty() && !line_mode {
10160 selection.goal = SelectionGoal::None;
10161 }
10162 let (cursor, goal) = movement::down(
10163 map,
10164 selection.end,
10165 selection.goal,
10166 false,
10167 text_layout_details,
10168 );
10169 selection.collapse_to(cursor, goal);
10170 });
10171 });
10172
10173 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10174 {
10175 cx.propagate();
10176 }
10177 }
10178
10179 pub fn select_page_down(
10180 &mut self,
10181 _: &SelectPageDown,
10182 window: &mut Window,
10183 cx: &mut Context<Self>,
10184 ) {
10185 let Some(row_count) = self.visible_row_count() else {
10186 return;
10187 };
10188
10189 let text_layout_details = &self.text_layout_details(window);
10190
10191 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10192 s.move_heads_with(|map, head, goal| {
10193 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10194 })
10195 })
10196 }
10197
10198 pub fn move_page_down(
10199 &mut self,
10200 action: &MovePageDown,
10201 window: &mut Window,
10202 cx: &mut Context<Self>,
10203 ) {
10204 if self.take_rename(true, window, cx).is_some() {
10205 return;
10206 }
10207
10208 if self
10209 .context_menu
10210 .borrow_mut()
10211 .as_mut()
10212 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10213 .unwrap_or(false)
10214 {
10215 return;
10216 }
10217
10218 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10219 cx.propagate();
10220 return;
10221 }
10222
10223 let Some(row_count) = self.visible_row_count() else {
10224 return;
10225 };
10226
10227 let autoscroll = if action.center_cursor {
10228 Autoscroll::center()
10229 } else {
10230 Autoscroll::fit()
10231 };
10232
10233 let text_layout_details = &self.text_layout_details(window);
10234 self.change_selections(Some(autoscroll), window, cx, |s| {
10235 let line_mode = s.line_mode;
10236 s.move_with(|map, selection| {
10237 if !selection.is_empty() && !line_mode {
10238 selection.goal = SelectionGoal::None;
10239 }
10240 let (cursor, goal) = movement::down_by_rows(
10241 map,
10242 selection.end,
10243 row_count,
10244 selection.goal,
10245 false,
10246 text_layout_details,
10247 );
10248 selection.collapse_to(cursor, goal);
10249 });
10250 });
10251 }
10252
10253 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10254 let text_layout_details = &self.text_layout_details(window);
10255 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10256 s.move_heads_with(|map, head, goal| {
10257 movement::down(map, head, goal, false, text_layout_details)
10258 })
10259 });
10260 }
10261
10262 pub fn context_menu_first(
10263 &mut self,
10264 _: &ContextMenuFirst,
10265 _window: &mut Window,
10266 cx: &mut Context<Self>,
10267 ) {
10268 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10269 context_menu.select_first(self.completion_provider.as_deref(), cx);
10270 }
10271 }
10272
10273 pub fn context_menu_prev(
10274 &mut self,
10275 _: &ContextMenuPrevious,
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_prev(self.completion_provider.as_deref(), cx);
10281 }
10282 }
10283
10284 pub fn context_menu_next(
10285 &mut self,
10286 _: &ContextMenuNext,
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_next(self.completion_provider.as_deref(), cx);
10292 }
10293 }
10294
10295 pub fn context_menu_last(
10296 &mut self,
10297 _: &ContextMenuLast,
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_last(self.completion_provider.as_deref(), cx);
10303 }
10304 }
10305
10306 pub fn move_to_previous_word_start(
10307 &mut self,
10308 _: &MoveToPreviousWordStart,
10309 window: &mut Window,
10310 cx: &mut Context<Self>,
10311 ) {
10312 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10313 s.move_cursors_with(|map, head, _| {
10314 (
10315 movement::previous_word_start(map, head),
10316 SelectionGoal::None,
10317 )
10318 });
10319 })
10320 }
10321
10322 pub fn move_to_previous_subword_start(
10323 &mut self,
10324 _: &MoveToPreviousSubwordStart,
10325 window: &mut Window,
10326 cx: &mut Context<Self>,
10327 ) {
10328 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10329 s.move_cursors_with(|map, head, _| {
10330 (
10331 movement::previous_subword_start(map, head),
10332 SelectionGoal::None,
10333 )
10334 });
10335 })
10336 }
10337
10338 pub fn select_to_previous_word_start(
10339 &mut self,
10340 _: &SelectToPreviousWordStart,
10341 window: &mut Window,
10342 cx: &mut Context<Self>,
10343 ) {
10344 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10345 s.move_heads_with(|map, head, _| {
10346 (
10347 movement::previous_word_start(map, head),
10348 SelectionGoal::None,
10349 )
10350 });
10351 })
10352 }
10353
10354 pub fn select_to_previous_subword_start(
10355 &mut self,
10356 _: &SelectToPreviousSubwordStart,
10357 window: &mut Window,
10358 cx: &mut Context<Self>,
10359 ) {
10360 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10361 s.move_heads_with(|map, head, _| {
10362 (
10363 movement::previous_subword_start(map, head),
10364 SelectionGoal::None,
10365 )
10366 });
10367 })
10368 }
10369
10370 pub fn delete_to_previous_word_start(
10371 &mut self,
10372 action: &DeleteToPreviousWordStart,
10373 window: &mut Window,
10374 cx: &mut Context<Self>,
10375 ) {
10376 self.transact(window, cx, |this, window, cx| {
10377 this.select_autoclose_pair(window, cx);
10378 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10379 let line_mode = s.line_mode;
10380 s.move_with(|map, selection| {
10381 if selection.is_empty() && !line_mode {
10382 let cursor = if action.ignore_newlines {
10383 movement::previous_word_start(map, selection.head())
10384 } else {
10385 movement::previous_word_start_or_newline(map, selection.head())
10386 };
10387 selection.set_head(cursor, SelectionGoal::None);
10388 }
10389 });
10390 });
10391 this.insert("", window, cx);
10392 });
10393 }
10394
10395 pub fn delete_to_previous_subword_start(
10396 &mut self,
10397 _: &DeleteToPreviousSubwordStart,
10398 window: &mut Window,
10399 cx: &mut Context<Self>,
10400 ) {
10401 self.transact(window, cx, |this, window, cx| {
10402 this.select_autoclose_pair(window, cx);
10403 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10404 let line_mode = s.line_mode;
10405 s.move_with(|map, selection| {
10406 if selection.is_empty() && !line_mode {
10407 let cursor = movement::previous_subword_start(map, selection.head());
10408 selection.set_head(cursor, SelectionGoal::None);
10409 }
10410 });
10411 });
10412 this.insert("", window, cx);
10413 });
10414 }
10415
10416 pub fn move_to_next_word_end(
10417 &mut self,
10418 _: &MoveToNextWordEnd,
10419 window: &mut Window,
10420 cx: &mut Context<Self>,
10421 ) {
10422 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10423 s.move_cursors_with(|map, head, _| {
10424 (movement::next_word_end(map, head), SelectionGoal::None)
10425 });
10426 })
10427 }
10428
10429 pub fn move_to_next_subword_end(
10430 &mut self,
10431 _: &MoveToNextSubwordEnd,
10432 window: &mut Window,
10433 cx: &mut Context<Self>,
10434 ) {
10435 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10436 s.move_cursors_with(|map, head, _| {
10437 (movement::next_subword_end(map, head), SelectionGoal::None)
10438 });
10439 })
10440 }
10441
10442 pub fn select_to_next_word_end(
10443 &mut self,
10444 _: &SelectToNextWordEnd,
10445 window: &mut Window,
10446 cx: &mut Context<Self>,
10447 ) {
10448 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10449 s.move_heads_with(|map, head, _| {
10450 (movement::next_word_end(map, head), SelectionGoal::None)
10451 });
10452 })
10453 }
10454
10455 pub fn select_to_next_subword_end(
10456 &mut self,
10457 _: &SelectToNextSubwordEnd,
10458 window: &mut Window,
10459 cx: &mut Context<Self>,
10460 ) {
10461 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10462 s.move_heads_with(|map, head, _| {
10463 (movement::next_subword_end(map, head), SelectionGoal::None)
10464 });
10465 })
10466 }
10467
10468 pub fn delete_to_next_word_end(
10469 &mut self,
10470 action: &DeleteToNextWordEnd,
10471 window: &mut Window,
10472 cx: &mut Context<Self>,
10473 ) {
10474 self.transact(window, cx, |this, window, cx| {
10475 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10476 let line_mode = s.line_mode;
10477 s.move_with(|map, selection| {
10478 if selection.is_empty() && !line_mode {
10479 let cursor = if action.ignore_newlines {
10480 movement::next_word_end(map, selection.head())
10481 } else {
10482 movement::next_word_end_or_newline(map, selection.head())
10483 };
10484 selection.set_head(cursor, SelectionGoal::None);
10485 }
10486 });
10487 });
10488 this.insert("", window, cx);
10489 });
10490 }
10491
10492 pub fn delete_to_next_subword_end(
10493 &mut self,
10494 _: &DeleteToNextSubwordEnd,
10495 window: &mut Window,
10496 cx: &mut Context<Self>,
10497 ) {
10498 self.transact(window, cx, |this, window, cx| {
10499 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10500 s.move_with(|map, selection| {
10501 if selection.is_empty() {
10502 let cursor = movement::next_subword_end(map, selection.head());
10503 selection.set_head(cursor, SelectionGoal::None);
10504 }
10505 });
10506 });
10507 this.insert("", window, cx);
10508 });
10509 }
10510
10511 pub fn move_to_beginning_of_line(
10512 &mut self,
10513 action: &MoveToBeginningOfLine,
10514 window: &mut Window,
10515 cx: &mut Context<Self>,
10516 ) {
10517 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10518 s.move_cursors_with(|map, head, _| {
10519 (
10520 movement::indented_line_beginning(
10521 map,
10522 head,
10523 action.stop_at_soft_wraps,
10524 action.stop_at_indent,
10525 ),
10526 SelectionGoal::None,
10527 )
10528 });
10529 })
10530 }
10531
10532 pub fn select_to_beginning_of_line(
10533 &mut self,
10534 action: &SelectToBeginningOfLine,
10535 window: &mut Window,
10536 cx: &mut Context<Self>,
10537 ) {
10538 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10539 s.move_heads_with(|map, head, _| {
10540 (
10541 movement::indented_line_beginning(
10542 map,
10543 head,
10544 action.stop_at_soft_wraps,
10545 action.stop_at_indent,
10546 ),
10547 SelectionGoal::None,
10548 )
10549 });
10550 });
10551 }
10552
10553 pub fn delete_to_beginning_of_line(
10554 &mut self,
10555 action: &DeleteToBeginningOfLine,
10556 window: &mut Window,
10557 cx: &mut Context<Self>,
10558 ) {
10559 self.transact(window, cx, |this, window, cx| {
10560 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10561 s.move_with(|_, selection| {
10562 selection.reversed = true;
10563 });
10564 });
10565
10566 this.select_to_beginning_of_line(
10567 &SelectToBeginningOfLine {
10568 stop_at_soft_wraps: false,
10569 stop_at_indent: action.stop_at_indent,
10570 },
10571 window,
10572 cx,
10573 );
10574 this.backspace(&Backspace, window, cx);
10575 });
10576 }
10577
10578 pub fn move_to_end_of_line(
10579 &mut self,
10580 action: &MoveToEndOfLine,
10581 window: &mut Window,
10582 cx: &mut Context<Self>,
10583 ) {
10584 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10585 s.move_cursors_with(|map, head, _| {
10586 (
10587 movement::line_end(map, head, action.stop_at_soft_wraps),
10588 SelectionGoal::None,
10589 )
10590 });
10591 })
10592 }
10593
10594 pub fn select_to_end_of_line(
10595 &mut self,
10596 action: &SelectToEndOfLine,
10597 window: &mut Window,
10598 cx: &mut Context<Self>,
10599 ) {
10600 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10601 s.move_heads_with(|map, head, _| {
10602 (
10603 movement::line_end(map, head, action.stop_at_soft_wraps),
10604 SelectionGoal::None,
10605 )
10606 });
10607 })
10608 }
10609
10610 pub fn delete_to_end_of_line(
10611 &mut self,
10612 _: &DeleteToEndOfLine,
10613 window: &mut Window,
10614 cx: &mut Context<Self>,
10615 ) {
10616 self.transact(window, cx, |this, window, cx| {
10617 this.select_to_end_of_line(
10618 &SelectToEndOfLine {
10619 stop_at_soft_wraps: false,
10620 },
10621 window,
10622 cx,
10623 );
10624 this.delete(&Delete, window, cx);
10625 });
10626 }
10627
10628 pub fn cut_to_end_of_line(
10629 &mut self,
10630 _: &CutToEndOfLine,
10631 window: &mut Window,
10632 cx: &mut Context<Self>,
10633 ) {
10634 self.transact(window, cx, |this, window, cx| {
10635 this.select_to_end_of_line(
10636 &SelectToEndOfLine {
10637 stop_at_soft_wraps: false,
10638 },
10639 window,
10640 cx,
10641 );
10642 this.cut(&Cut, window, cx);
10643 });
10644 }
10645
10646 pub fn move_to_start_of_paragraph(
10647 &mut self,
10648 _: &MoveToStartOfParagraph,
10649 window: &mut Window,
10650 cx: &mut Context<Self>,
10651 ) {
10652 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10653 cx.propagate();
10654 return;
10655 }
10656
10657 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10658 s.move_with(|map, selection| {
10659 selection.collapse_to(
10660 movement::start_of_paragraph(map, selection.head(), 1),
10661 SelectionGoal::None,
10662 )
10663 });
10664 })
10665 }
10666
10667 pub fn move_to_end_of_paragraph(
10668 &mut self,
10669 _: &MoveToEndOfParagraph,
10670 window: &mut Window,
10671 cx: &mut Context<Self>,
10672 ) {
10673 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10674 cx.propagate();
10675 return;
10676 }
10677
10678 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10679 s.move_with(|map, selection| {
10680 selection.collapse_to(
10681 movement::end_of_paragraph(map, selection.head(), 1),
10682 SelectionGoal::None,
10683 )
10684 });
10685 })
10686 }
10687
10688 pub fn select_to_start_of_paragraph(
10689 &mut self,
10690 _: &SelectToStartOfParagraph,
10691 window: &mut Window,
10692 cx: &mut Context<Self>,
10693 ) {
10694 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10695 cx.propagate();
10696 return;
10697 }
10698
10699 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10700 s.move_heads_with(|map, head, _| {
10701 (
10702 movement::start_of_paragraph(map, head, 1),
10703 SelectionGoal::None,
10704 )
10705 });
10706 })
10707 }
10708
10709 pub fn select_to_end_of_paragraph(
10710 &mut self,
10711 _: &SelectToEndOfParagraph,
10712 window: &mut Window,
10713 cx: &mut Context<Self>,
10714 ) {
10715 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10716 cx.propagate();
10717 return;
10718 }
10719
10720 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10721 s.move_heads_with(|map, head, _| {
10722 (
10723 movement::end_of_paragraph(map, head, 1),
10724 SelectionGoal::None,
10725 )
10726 });
10727 })
10728 }
10729
10730 pub fn move_to_start_of_excerpt(
10731 &mut self,
10732 _: &MoveToStartOfExcerpt,
10733 window: &mut Window,
10734 cx: &mut Context<Self>,
10735 ) {
10736 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10737 cx.propagate();
10738 return;
10739 }
10740
10741 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10742 s.move_with(|map, selection| {
10743 selection.collapse_to(
10744 movement::start_of_excerpt(
10745 map,
10746 selection.head(),
10747 workspace::searchable::Direction::Prev,
10748 ),
10749 SelectionGoal::None,
10750 )
10751 });
10752 })
10753 }
10754
10755 pub fn move_to_start_of_next_excerpt(
10756 &mut self,
10757 _: &MoveToStartOfNextExcerpt,
10758 window: &mut Window,
10759 cx: &mut Context<Self>,
10760 ) {
10761 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10762 cx.propagate();
10763 return;
10764 }
10765
10766 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10767 s.move_with(|map, selection| {
10768 selection.collapse_to(
10769 movement::start_of_excerpt(
10770 map,
10771 selection.head(),
10772 workspace::searchable::Direction::Next,
10773 ),
10774 SelectionGoal::None,
10775 )
10776 });
10777 })
10778 }
10779
10780 pub fn move_to_end_of_excerpt(
10781 &mut self,
10782 _: &MoveToEndOfExcerpt,
10783 window: &mut Window,
10784 cx: &mut Context<Self>,
10785 ) {
10786 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10787 cx.propagate();
10788 return;
10789 }
10790
10791 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10792 s.move_with(|map, selection| {
10793 selection.collapse_to(
10794 movement::end_of_excerpt(
10795 map,
10796 selection.head(),
10797 workspace::searchable::Direction::Next,
10798 ),
10799 SelectionGoal::None,
10800 )
10801 });
10802 })
10803 }
10804
10805 pub fn move_to_end_of_previous_excerpt(
10806 &mut self,
10807 _: &MoveToEndOfPreviousExcerpt,
10808 window: &mut Window,
10809 cx: &mut Context<Self>,
10810 ) {
10811 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10812 cx.propagate();
10813 return;
10814 }
10815
10816 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10817 s.move_with(|map, selection| {
10818 selection.collapse_to(
10819 movement::end_of_excerpt(
10820 map,
10821 selection.head(),
10822 workspace::searchable::Direction::Prev,
10823 ),
10824 SelectionGoal::None,
10825 )
10826 });
10827 })
10828 }
10829
10830 pub fn select_to_start_of_excerpt(
10831 &mut self,
10832 _: &SelectToStartOfExcerpt,
10833 window: &mut Window,
10834 cx: &mut Context<Self>,
10835 ) {
10836 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10837 cx.propagate();
10838 return;
10839 }
10840
10841 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10842 s.move_heads_with(|map, head, _| {
10843 (
10844 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10845 SelectionGoal::None,
10846 )
10847 });
10848 })
10849 }
10850
10851 pub fn select_to_start_of_next_excerpt(
10852 &mut self,
10853 _: &SelectToStartOfNextExcerpt,
10854 window: &mut Window,
10855 cx: &mut Context<Self>,
10856 ) {
10857 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10858 cx.propagate();
10859 return;
10860 }
10861
10862 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10863 s.move_heads_with(|map, head, _| {
10864 (
10865 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10866 SelectionGoal::None,
10867 )
10868 });
10869 })
10870 }
10871
10872 pub fn select_to_end_of_excerpt(
10873 &mut self,
10874 _: &SelectToEndOfExcerpt,
10875 window: &mut Window,
10876 cx: &mut Context<Self>,
10877 ) {
10878 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10879 cx.propagate();
10880 return;
10881 }
10882
10883 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10884 s.move_heads_with(|map, head, _| {
10885 (
10886 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10887 SelectionGoal::None,
10888 )
10889 });
10890 })
10891 }
10892
10893 pub fn select_to_end_of_previous_excerpt(
10894 &mut self,
10895 _: &SelectToEndOfPreviousExcerpt,
10896 window: &mut Window,
10897 cx: &mut Context<Self>,
10898 ) {
10899 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10900 cx.propagate();
10901 return;
10902 }
10903
10904 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10905 s.move_heads_with(|map, head, _| {
10906 (
10907 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10908 SelectionGoal::None,
10909 )
10910 });
10911 })
10912 }
10913
10914 pub fn move_to_beginning(
10915 &mut self,
10916 _: &MoveToBeginning,
10917 window: &mut Window,
10918 cx: &mut Context<Self>,
10919 ) {
10920 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10921 cx.propagate();
10922 return;
10923 }
10924
10925 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10926 s.select_ranges(vec![0..0]);
10927 });
10928 }
10929
10930 pub fn select_to_beginning(
10931 &mut self,
10932 _: &SelectToBeginning,
10933 window: &mut Window,
10934 cx: &mut Context<Self>,
10935 ) {
10936 let mut selection = self.selections.last::<Point>(cx);
10937 selection.set_head(Point::zero(), SelectionGoal::None);
10938
10939 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10940 s.select(vec![selection]);
10941 });
10942 }
10943
10944 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10945 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10946 cx.propagate();
10947 return;
10948 }
10949
10950 let cursor = self.buffer.read(cx).read(cx).len();
10951 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10952 s.select_ranges(vec![cursor..cursor])
10953 });
10954 }
10955
10956 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10957 self.nav_history = nav_history;
10958 }
10959
10960 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10961 self.nav_history.as_ref()
10962 }
10963
10964 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10965 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10966 }
10967
10968 fn push_to_nav_history(
10969 &mut self,
10970 cursor_anchor: Anchor,
10971 new_position: Option<Point>,
10972 is_deactivate: bool,
10973 cx: &mut Context<Self>,
10974 ) {
10975 if let Some(nav_history) = self.nav_history.as_mut() {
10976 let buffer = self.buffer.read(cx).read(cx);
10977 let cursor_position = cursor_anchor.to_point(&buffer);
10978 let scroll_state = self.scroll_manager.anchor();
10979 let scroll_top_row = scroll_state.top_row(&buffer);
10980 drop(buffer);
10981
10982 if let Some(new_position) = new_position {
10983 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10984 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10985 return;
10986 }
10987 }
10988
10989 nav_history.push(
10990 Some(NavigationData {
10991 cursor_anchor,
10992 cursor_position,
10993 scroll_anchor: scroll_state,
10994 scroll_top_row,
10995 }),
10996 cx,
10997 );
10998 cx.emit(EditorEvent::PushedToNavHistory {
10999 anchor: cursor_anchor,
11000 is_deactivate,
11001 })
11002 }
11003 }
11004
11005 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
11006 let buffer = self.buffer.read(cx).snapshot(cx);
11007 let mut selection = self.selections.first::<usize>(cx);
11008 selection.set_head(buffer.len(), SelectionGoal::None);
11009 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11010 s.select(vec![selection]);
11011 });
11012 }
11013
11014 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
11015 let end = self.buffer.read(cx).read(cx).len();
11016 self.change_selections(None, window, cx, |s| {
11017 s.select_ranges(vec![0..end]);
11018 });
11019 }
11020
11021 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
11022 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11023 let mut selections = self.selections.all::<Point>(cx);
11024 let max_point = display_map.buffer_snapshot.max_point();
11025 for selection in &mut selections {
11026 let rows = selection.spanned_rows(true, &display_map);
11027 selection.start = Point::new(rows.start.0, 0);
11028 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
11029 selection.reversed = false;
11030 }
11031 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11032 s.select(selections);
11033 });
11034 }
11035
11036 pub fn split_selection_into_lines(
11037 &mut self,
11038 _: &SplitSelectionIntoLines,
11039 window: &mut Window,
11040 cx: &mut Context<Self>,
11041 ) {
11042 let selections = self
11043 .selections
11044 .all::<Point>(cx)
11045 .into_iter()
11046 .map(|selection| selection.start..selection.end)
11047 .collect::<Vec<_>>();
11048 self.unfold_ranges(&selections, true, true, cx);
11049
11050 let mut new_selection_ranges = Vec::new();
11051 {
11052 let buffer = self.buffer.read(cx).read(cx);
11053 for selection in selections {
11054 for row in selection.start.row..selection.end.row {
11055 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11056 new_selection_ranges.push(cursor..cursor);
11057 }
11058
11059 let is_multiline_selection = selection.start.row != selection.end.row;
11060 // Don't insert last one if it's a multi-line selection ending at the start of a line,
11061 // so this action feels more ergonomic when paired with other selection operations
11062 let should_skip_last = is_multiline_selection && selection.end.column == 0;
11063 if !should_skip_last {
11064 new_selection_ranges.push(selection.end..selection.end);
11065 }
11066 }
11067 }
11068 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11069 s.select_ranges(new_selection_ranges);
11070 });
11071 }
11072
11073 pub fn add_selection_above(
11074 &mut self,
11075 _: &AddSelectionAbove,
11076 window: &mut Window,
11077 cx: &mut Context<Self>,
11078 ) {
11079 self.add_selection(true, window, cx);
11080 }
11081
11082 pub fn add_selection_below(
11083 &mut self,
11084 _: &AddSelectionBelow,
11085 window: &mut Window,
11086 cx: &mut Context<Self>,
11087 ) {
11088 self.add_selection(false, window, cx);
11089 }
11090
11091 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
11092 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11093 let mut selections = self.selections.all::<Point>(cx);
11094 let text_layout_details = self.text_layout_details(window);
11095 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
11096 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
11097 let range = oldest_selection.display_range(&display_map).sorted();
11098
11099 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
11100 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
11101 let positions = start_x.min(end_x)..start_x.max(end_x);
11102
11103 selections.clear();
11104 let mut stack = Vec::new();
11105 for row in range.start.row().0..=range.end.row().0 {
11106 if let Some(selection) = self.selections.build_columnar_selection(
11107 &display_map,
11108 DisplayRow(row),
11109 &positions,
11110 oldest_selection.reversed,
11111 &text_layout_details,
11112 ) {
11113 stack.push(selection.id);
11114 selections.push(selection);
11115 }
11116 }
11117
11118 if above {
11119 stack.reverse();
11120 }
11121
11122 AddSelectionsState { above, stack }
11123 });
11124
11125 let last_added_selection = *state.stack.last().unwrap();
11126 let mut new_selections = Vec::new();
11127 if above == state.above {
11128 let end_row = if above {
11129 DisplayRow(0)
11130 } else {
11131 display_map.max_point().row()
11132 };
11133
11134 'outer: for selection in selections {
11135 if selection.id == last_added_selection {
11136 let range = selection.display_range(&display_map).sorted();
11137 debug_assert_eq!(range.start.row(), range.end.row());
11138 let mut row = range.start.row();
11139 let positions =
11140 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
11141 px(start)..px(end)
11142 } else {
11143 let start_x =
11144 display_map.x_for_display_point(range.start, &text_layout_details);
11145 let end_x =
11146 display_map.x_for_display_point(range.end, &text_layout_details);
11147 start_x.min(end_x)..start_x.max(end_x)
11148 };
11149
11150 while row != end_row {
11151 if above {
11152 row.0 -= 1;
11153 } else {
11154 row.0 += 1;
11155 }
11156
11157 if let Some(new_selection) = self.selections.build_columnar_selection(
11158 &display_map,
11159 row,
11160 &positions,
11161 selection.reversed,
11162 &text_layout_details,
11163 ) {
11164 state.stack.push(new_selection.id);
11165 if above {
11166 new_selections.push(new_selection);
11167 new_selections.push(selection);
11168 } else {
11169 new_selections.push(selection);
11170 new_selections.push(new_selection);
11171 }
11172
11173 continue 'outer;
11174 }
11175 }
11176 }
11177
11178 new_selections.push(selection);
11179 }
11180 } else {
11181 new_selections = selections;
11182 new_selections.retain(|s| s.id != last_added_selection);
11183 state.stack.pop();
11184 }
11185
11186 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11187 s.select(new_selections);
11188 });
11189 if state.stack.len() > 1 {
11190 self.add_selections_state = Some(state);
11191 }
11192 }
11193
11194 pub fn select_next_match_internal(
11195 &mut self,
11196 display_map: &DisplaySnapshot,
11197 replace_newest: bool,
11198 autoscroll: Option<Autoscroll>,
11199 window: &mut Window,
11200 cx: &mut Context<Self>,
11201 ) -> Result<()> {
11202 fn select_next_match_ranges(
11203 this: &mut Editor,
11204 range: Range<usize>,
11205 replace_newest: bool,
11206 auto_scroll: Option<Autoscroll>,
11207 window: &mut Window,
11208 cx: &mut Context<Editor>,
11209 ) {
11210 this.unfold_ranges(&[range.clone()], false, true, cx);
11211 this.change_selections(auto_scroll, window, cx, |s| {
11212 if replace_newest {
11213 s.delete(s.newest_anchor().id);
11214 }
11215 s.insert_range(range.clone());
11216 });
11217 }
11218
11219 let buffer = &display_map.buffer_snapshot;
11220 let mut selections = self.selections.all::<usize>(cx);
11221 if let Some(mut select_next_state) = self.select_next_state.take() {
11222 let query = &select_next_state.query;
11223 if !select_next_state.done {
11224 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11225 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11226 let mut next_selected_range = None;
11227
11228 let bytes_after_last_selection =
11229 buffer.bytes_in_range(last_selection.end..buffer.len());
11230 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11231 let query_matches = query
11232 .stream_find_iter(bytes_after_last_selection)
11233 .map(|result| (last_selection.end, result))
11234 .chain(
11235 query
11236 .stream_find_iter(bytes_before_first_selection)
11237 .map(|result| (0, result)),
11238 );
11239
11240 for (start_offset, query_match) in query_matches {
11241 let query_match = query_match.unwrap(); // can only fail due to I/O
11242 let offset_range =
11243 start_offset + query_match.start()..start_offset + query_match.end();
11244 let display_range = offset_range.start.to_display_point(display_map)
11245 ..offset_range.end.to_display_point(display_map);
11246
11247 if !select_next_state.wordwise
11248 || (!movement::is_inside_word(display_map, display_range.start)
11249 && !movement::is_inside_word(display_map, display_range.end))
11250 {
11251 // TODO: This is n^2, because we might check all the selections
11252 if !selections
11253 .iter()
11254 .any(|selection| selection.range().overlaps(&offset_range))
11255 {
11256 next_selected_range = Some(offset_range);
11257 break;
11258 }
11259 }
11260 }
11261
11262 if let Some(next_selected_range) = next_selected_range {
11263 select_next_match_ranges(
11264 self,
11265 next_selected_range,
11266 replace_newest,
11267 autoscroll,
11268 window,
11269 cx,
11270 );
11271 } else {
11272 select_next_state.done = true;
11273 }
11274 }
11275
11276 self.select_next_state = Some(select_next_state);
11277 } else {
11278 let mut only_carets = true;
11279 let mut same_text_selected = true;
11280 let mut selected_text = None;
11281
11282 let mut selections_iter = selections.iter().peekable();
11283 while let Some(selection) = selections_iter.next() {
11284 if selection.start != selection.end {
11285 only_carets = false;
11286 }
11287
11288 if same_text_selected {
11289 if selected_text.is_none() {
11290 selected_text =
11291 Some(buffer.text_for_range(selection.range()).collect::<String>());
11292 }
11293
11294 if let Some(next_selection) = selections_iter.peek() {
11295 if next_selection.range().len() == selection.range().len() {
11296 let next_selected_text = buffer
11297 .text_for_range(next_selection.range())
11298 .collect::<String>();
11299 if Some(next_selected_text) != selected_text {
11300 same_text_selected = false;
11301 selected_text = None;
11302 }
11303 } else {
11304 same_text_selected = false;
11305 selected_text = None;
11306 }
11307 }
11308 }
11309 }
11310
11311 if only_carets {
11312 for selection in &mut selections {
11313 let word_range = movement::surrounding_word(
11314 display_map,
11315 selection.start.to_display_point(display_map),
11316 );
11317 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11318 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11319 selection.goal = SelectionGoal::None;
11320 selection.reversed = false;
11321 select_next_match_ranges(
11322 self,
11323 selection.start..selection.end,
11324 replace_newest,
11325 autoscroll,
11326 window,
11327 cx,
11328 );
11329 }
11330
11331 if selections.len() == 1 {
11332 let selection = selections
11333 .last()
11334 .expect("ensured that there's only one selection");
11335 let query = buffer
11336 .text_for_range(selection.start..selection.end)
11337 .collect::<String>();
11338 let is_empty = query.is_empty();
11339 let select_state = SelectNextState {
11340 query: AhoCorasick::new(&[query])?,
11341 wordwise: true,
11342 done: is_empty,
11343 };
11344 self.select_next_state = Some(select_state);
11345 } else {
11346 self.select_next_state = None;
11347 }
11348 } else if let Some(selected_text) = selected_text {
11349 self.select_next_state = Some(SelectNextState {
11350 query: AhoCorasick::new(&[selected_text])?,
11351 wordwise: false,
11352 done: false,
11353 });
11354 self.select_next_match_internal(
11355 display_map,
11356 replace_newest,
11357 autoscroll,
11358 window,
11359 cx,
11360 )?;
11361 }
11362 }
11363 Ok(())
11364 }
11365
11366 pub fn select_all_matches(
11367 &mut self,
11368 _action: &SelectAllMatches,
11369 window: &mut Window,
11370 cx: &mut Context<Self>,
11371 ) -> Result<()> {
11372 self.push_to_selection_history();
11373 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11374
11375 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11376 let Some(select_next_state) = self.select_next_state.as_mut() else {
11377 return Ok(());
11378 };
11379 if select_next_state.done {
11380 return Ok(());
11381 }
11382
11383 let mut new_selections = self.selections.all::<usize>(cx);
11384
11385 let buffer = &display_map.buffer_snapshot;
11386 let query_matches = select_next_state
11387 .query
11388 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11389
11390 for query_match in query_matches {
11391 let query_match = query_match.unwrap(); // can only fail due to I/O
11392 let offset_range = query_match.start()..query_match.end();
11393 let display_range = offset_range.start.to_display_point(&display_map)
11394 ..offset_range.end.to_display_point(&display_map);
11395
11396 if !select_next_state.wordwise
11397 || (!movement::is_inside_word(&display_map, display_range.start)
11398 && !movement::is_inside_word(&display_map, display_range.end))
11399 {
11400 self.selections.change_with(cx, |selections| {
11401 new_selections.push(Selection {
11402 id: selections.new_selection_id(),
11403 start: offset_range.start,
11404 end: offset_range.end,
11405 reversed: false,
11406 goal: SelectionGoal::None,
11407 });
11408 });
11409 }
11410 }
11411
11412 new_selections.sort_by_key(|selection| selection.start);
11413 let mut ix = 0;
11414 while ix + 1 < new_selections.len() {
11415 let current_selection = &new_selections[ix];
11416 let next_selection = &new_selections[ix + 1];
11417 if current_selection.range().overlaps(&next_selection.range()) {
11418 if current_selection.id < next_selection.id {
11419 new_selections.remove(ix + 1);
11420 } else {
11421 new_selections.remove(ix);
11422 }
11423 } else {
11424 ix += 1;
11425 }
11426 }
11427
11428 let reversed = self.selections.oldest::<usize>(cx).reversed;
11429
11430 for selection in new_selections.iter_mut() {
11431 selection.reversed = reversed;
11432 }
11433
11434 select_next_state.done = true;
11435 self.unfold_ranges(
11436 &new_selections
11437 .iter()
11438 .map(|selection| selection.range())
11439 .collect::<Vec<_>>(),
11440 false,
11441 false,
11442 cx,
11443 );
11444 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11445 selections.select(new_selections)
11446 });
11447
11448 Ok(())
11449 }
11450
11451 pub fn select_next(
11452 &mut self,
11453 action: &SelectNext,
11454 window: &mut Window,
11455 cx: &mut Context<Self>,
11456 ) -> Result<()> {
11457 self.push_to_selection_history();
11458 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11459 self.select_next_match_internal(
11460 &display_map,
11461 action.replace_newest,
11462 Some(Autoscroll::newest()),
11463 window,
11464 cx,
11465 )?;
11466 Ok(())
11467 }
11468
11469 pub fn select_previous(
11470 &mut self,
11471 action: &SelectPrevious,
11472 window: &mut Window,
11473 cx: &mut Context<Self>,
11474 ) -> Result<()> {
11475 self.push_to_selection_history();
11476 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11477 let buffer = &display_map.buffer_snapshot;
11478 let mut selections = self.selections.all::<usize>(cx);
11479 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11480 let query = &select_prev_state.query;
11481 if !select_prev_state.done {
11482 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11483 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11484 let mut next_selected_range = None;
11485 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11486 let bytes_before_last_selection =
11487 buffer.reversed_bytes_in_range(0..last_selection.start);
11488 let bytes_after_first_selection =
11489 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11490 let query_matches = query
11491 .stream_find_iter(bytes_before_last_selection)
11492 .map(|result| (last_selection.start, result))
11493 .chain(
11494 query
11495 .stream_find_iter(bytes_after_first_selection)
11496 .map(|result| (buffer.len(), result)),
11497 );
11498 for (end_offset, query_match) in query_matches {
11499 let query_match = query_match.unwrap(); // can only fail due to I/O
11500 let offset_range =
11501 end_offset - query_match.end()..end_offset - query_match.start();
11502 let display_range = offset_range.start.to_display_point(&display_map)
11503 ..offset_range.end.to_display_point(&display_map);
11504
11505 if !select_prev_state.wordwise
11506 || (!movement::is_inside_word(&display_map, display_range.start)
11507 && !movement::is_inside_word(&display_map, display_range.end))
11508 {
11509 next_selected_range = Some(offset_range);
11510 break;
11511 }
11512 }
11513
11514 if let Some(next_selected_range) = next_selected_range {
11515 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11516 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11517 if action.replace_newest {
11518 s.delete(s.newest_anchor().id);
11519 }
11520 s.insert_range(next_selected_range);
11521 });
11522 } else {
11523 select_prev_state.done = true;
11524 }
11525 }
11526
11527 self.select_prev_state = Some(select_prev_state);
11528 } else {
11529 let mut only_carets = true;
11530 let mut same_text_selected = true;
11531 let mut selected_text = None;
11532
11533 let mut selections_iter = selections.iter().peekable();
11534 while let Some(selection) = selections_iter.next() {
11535 if selection.start != selection.end {
11536 only_carets = false;
11537 }
11538
11539 if same_text_selected {
11540 if selected_text.is_none() {
11541 selected_text =
11542 Some(buffer.text_for_range(selection.range()).collect::<String>());
11543 }
11544
11545 if let Some(next_selection) = selections_iter.peek() {
11546 if next_selection.range().len() == selection.range().len() {
11547 let next_selected_text = buffer
11548 .text_for_range(next_selection.range())
11549 .collect::<String>();
11550 if Some(next_selected_text) != selected_text {
11551 same_text_selected = false;
11552 selected_text = None;
11553 }
11554 } else {
11555 same_text_selected = false;
11556 selected_text = None;
11557 }
11558 }
11559 }
11560 }
11561
11562 if only_carets {
11563 for selection in &mut selections {
11564 let word_range = movement::surrounding_word(
11565 &display_map,
11566 selection.start.to_display_point(&display_map),
11567 );
11568 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11569 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11570 selection.goal = SelectionGoal::None;
11571 selection.reversed = false;
11572 }
11573 if selections.len() == 1 {
11574 let selection = selections
11575 .last()
11576 .expect("ensured that there's only one selection");
11577 let query = buffer
11578 .text_for_range(selection.start..selection.end)
11579 .collect::<String>();
11580 let is_empty = query.is_empty();
11581 let select_state = SelectNextState {
11582 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11583 wordwise: true,
11584 done: is_empty,
11585 };
11586 self.select_prev_state = Some(select_state);
11587 } else {
11588 self.select_prev_state = None;
11589 }
11590
11591 self.unfold_ranges(
11592 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11593 false,
11594 true,
11595 cx,
11596 );
11597 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11598 s.select(selections);
11599 });
11600 } else if let Some(selected_text) = selected_text {
11601 self.select_prev_state = Some(SelectNextState {
11602 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11603 wordwise: false,
11604 done: false,
11605 });
11606 self.select_previous(action, window, cx)?;
11607 }
11608 }
11609 Ok(())
11610 }
11611
11612 pub fn toggle_comments(
11613 &mut self,
11614 action: &ToggleComments,
11615 window: &mut Window,
11616 cx: &mut Context<Self>,
11617 ) {
11618 if self.read_only(cx) {
11619 return;
11620 }
11621 let text_layout_details = &self.text_layout_details(window);
11622 self.transact(window, cx, |this, window, cx| {
11623 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11624 let mut edits = Vec::new();
11625 let mut selection_edit_ranges = Vec::new();
11626 let mut last_toggled_row = None;
11627 let snapshot = this.buffer.read(cx).read(cx);
11628 let empty_str: Arc<str> = Arc::default();
11629 let mut suffixes_inserted = Vec::new();
11630 let ignore_indent = action.ignore_indent;
11631
11632 fn comment_prefix_range(
11633 snapshot: &MultiBufferSnapshot,
11634 row: MultiBufferRow,
11635 comment_prefix: &str,
11636 comment_prefix_whitespace: &str,
11637 ignore_indent: bool,
11638 ) -> Range<Point> {
11639 let indent_size = if ignore_indent {
11640 0
11641 } else {
11642 snapshot.indent_size_for_line(row).len
11643 };
11644
11645 let start = Point::new(row.0, indent_size);
11646
11647 let mut line_bytes = snapshot
11648 .bytes_in_range(start..snapshot.max_point())
11649 .flatten()
11650 .copied();
11651
11652 // If this line currently begins with the line comment prefix, then record
11653 // the range containing the prefix.
11654 if line_bytes
11655 .by_ref()
11656 .take(comment_prefix.len())
11657 .eq(comment_prefix.bytes())
11658 {
11659 // Include any whitespace that matches the comment prefix.
11660 let matching_whitespace_len = line_bytes
11661 .zip(comment_prefix_whitespace.bytes())
11662 .take_while(|(a, b)| a == b)
11663 .count() as u32;
11664 let end = Point::new(
11665 start.row,
11666 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11667 );
11668 start..end
11669 } else {
11670 start..start
11671 }
11672 }
11673
11674 fn comment_suffix_range(
11675 snapshot: &MultiBufferSnapshot,
11676 row: MultiBufferRow,
11677 comment_suffix: &str,
11678 comment_suffix_has_leading_space: bool,
11679 ) -> Range<Point> {
11680 let end = Point::new(row.0, snapshot.line_len(row));
11681 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11682
11683 let mut line_end_bytes = snapshot
11684 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11685 .flatten()
11686 .copied();
11687
11688 let leading_space_len = if suffix_start_column > 0
11689 && line_end_bytes.next() == Some(b' ')
11690 && comment_suffix_has_leading_space
11691 {
11692 1
11693 } else {
11694 0
11695 };
11696
11697 // If this line currently begins with the line comment prefix, then record
11698 // the range containing the prefix.
11699 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11700 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11701 start..end
11702 } else {
11703 end..end
11704 }
11705 }
11706
11707 // TODO: Handle selections that cross excerpts
11708 for selection in &mut selections {
11709 let start_column = snapshot
11710 .indent_size_for_line(MultiBufferRow(selection.start.row))
11711 .len;
11712 let language = if let Some(language) =
11713 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11714 {
11715 language
11716 } else {
11717 continue;
11718 };
11719
11720 selection_edit_ranges.clear();
11721
11722 // If multiple selections contain a given row, avoid processing that
11723 // row more than once.
11724 let mut start_row = MultiBufferRow(selection.start.row);
11725 if last_toggled_row == Some(start_row) {
11726 start_row = start_row.next_row();
11727 }
11728 let end_row =
11729 if selection.end.row > selection.start.row && selection.end.column == 0 {
11730 MultiBufferRow(selection.end.row - 1)
11731 } else {
11732 MultiBufferRow(selection.end.row)
11733 };
11734 last_toggled_row = Some(end_row);
11735
11736 if start_row > end_row {
11737 continue;
11738 }
11739
11740 // If the language has line comments, toggle those.
11741 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11742
11743 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11744 if ignore_indent {
11745 full_comment_prefixes = full_comment_prefixes
11746 .into_iter()
11747 .map(|s| Arc::from(s.trim_end()))
11748 .collect();
11749 }
11750
11751 if !full_comment_prefixes.is_empty() {
11752 let first_prefix = full_comment_prefixes
11753 .first()
11754 .expect("prefixes is non-empty");
11755 let prefix_trimmed_lengths = full_comment_prefixes
11756 .iter()
11757 .map(|p| p.trim_end_matches(' ').len())
11758 .collect::<SmallVec<[usize; 4]>>();
11759
11760 let mut all_selection_lines_are_comments = true;
11761
11762 for row in start_row.0..=end_row.0 {
11763 let row = MultiBufferRow(row);
11764 if start_row < end_row && snapshot.is_line_blank(row) {
11765 continue;
11766 }
11767
11768 let prefix_range = full_comment_prefixes
11769 .iter()
11770 .zip(prefix_trimmed_lengths.iter().copied())
11771 .map(|(prefix, trimmed_prefix_len)| {
11772 comment_prefix_range(
11773 snapshot.deref(),
11774 row,
11775 &prefix[..trimmed_prefix_len],
11776 &prefix[trimmed_prefix_len..],
11777 ignore_indent,
11778 )
11779 })
11780 .max_by_key(|range| range.end.column - range.start.column)
11781 .expect("prefixes is non-empty");
11782
11783 if prefix_range.is_empty() {
11784 all_selection_lines_are_comments = false;
11785 }
11786
11787 selection_edit_ranges.push(prefix_range);
11788 }
11789
11790 if all_selection_lines_are_comments {
11791 edits.extend(
11792 selection_edit_ranges
11793 .iter()
11794 .cloned()
11795 .map(|range| (range, empty_str.clone())),
11796 );
11797 } else {
11798 let min_column = selection_edit_ranges
11799 .iter()
11800 .map(|range| range.start.column)
11801 .min()
11802 .unwrap_or(0);
11803 edits.extend(selection_edit_ranges.iter().map(|range| {
11804 let position = Point::new(range.start.row, min_column);
11805 (position..position, first_prefix.clone())
11806 }));
11807 }
11808 } else if let Some((full_comment_prefix, comment_suffix)) =
11809 language.block_comment_delimiters()
11810 {
11811 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11812 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11813 let prefix_range = comment_prefix_range(
11814 snapshot.deref(),
11815 start_row,
11816 comment_prefix,
11817 comment_prefix_whitespace,
11818 ignore_indent,
11819 );
11820 let suffix_range = comment_suffix_range(
11821 snapshot.deref(),
11822 end_row,
11823 comment_suffix.trim_start_matches(' '),
11824 comment_suffix.starts_with(' '),
11825 );
11826
11827 if prefix_range.is_empty() || suffix_range.is_empty() {
11828 edits.push((
11829 prefix_range.start..prefix_range.start,
11830 full_comment_prefix.clone(),
11831 ));
11832 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11833 suffixes_inserted.push((end_row, comment_suffix.len()));
11834 } else {
11835 edits.push((prefix_range, empty_str.clone()));
11836 edits.push((suffix_range, empty_str.clone()));
11837 }
11838 } else {
11839 continue;
11840 }
11841 }
11842
11843 drop(snapshot);
11844 this.buffer.update(cx, |buffer, cx| {
11845 buffer.edit(edits, None, cx);
11846 });
11847
11848 // Adjust selections so that they end before any comment suffixes that
11849 // were inserted.
11850 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11851 let mut selections = this.selections.all::<Point>(cx);
11852 let snapshot = this.buffer.read(cx).read(cx);
11853 for selection in &mut selections {
11854 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11855 match row.cmp(&MultiBufferRow(selection.end.row)) {
11856 Ordering::Less => {
11857 suffixes_inserted.next();
11858 continue;
11859 }
11860 Ordering::Greater => break,
11861 Ordering::Equal => {
11862 if selection.end.column == snapshot.line_len(row) {
11863 if selection.is_empty() {
11864 selection.start.column -= suffix_len as u32;
11865 }
11866 selection.end.column -= suffix_len as u32;
11867 }
11868 break;
11869 }
11870 }
11871 }
11872 }
11873
11874 drop(snapshot);
11875 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11876 s.select(selections)
11877 });
11878
11879 let selections = this.selections.all::<Point>(cx);
11880 let selections_on_single_row = selections.windows(2).all(|selections| {
11881 selections[0].start.row == selections[1].start.row
11882 && selections[0].end.row == selections[1].end.row
11883 && selections[0].start.row == selections[0].end.row
11884 });
11885 let selections_selecting = selections
11886 .iter()
11887 .any(|selection| selection.start != selection.end);
11888 let advance_downwards = action.advance_downwards
11889 && selections_on_single_row
11890 && !selections_selecting
11891 && !matches!(this.mode, EditorMode::SingleLine { .. });
11892
11893 if advance_downwards {
11894 let snapshot = this.buffer.read(cx).snapshot(cx);
11895
11896 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11897 s.move_cursors_with(|display_snapshot, display_point, _| {
11898 let mut point = display_point.to_point(display_snapshot);
11899 point.row += 1;
11900 point = snapshot.clip_point(point, Bias::Left);
11901 let display_point = point.to_display_point(display_snapshot);
11902 let goal = SelectionGoal::HorizontalPosition(
11903 display_snapshot
11904 .x_for_display_point(display_point, text_layout_details)
11905 .into(),
11906 );
11907 (display_point, goal)
11908 })
11909 });
11910 }
11911 });
11912 }
11913
11914 pub fn select_enclosing_symbol(
11915 &mut self,
11916 _: &SelectEnclosingSymbol,
11917 window: &mut Window,
11918 cx: &mut Context<Self>,
11919 ) {
11920 let buffer = self.buffer.read(cx).snapshot(cx);
11921 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11922
11923 fn update_selection(
11924 selection: &Selection<usize>,
11925 buffer_snap: &MultiBufferSnapshot,
11926 ) -> Option<Selection<usize>> {
11927 let cursor = selection.head();
11928 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11929 for symbol in symbols.iter().rev() {
11930 let start = symbol.range.start.to_offset(buffer_snap);
11931 let end = symbol.range.end.to_offset(buffer_snap);
11932 let new_range = start..end;
11933 if start < selection.start || end > selection.end {
11934 return Some(Selection {
11935 id: selection.id,
11936 start: new_range.start,
11937 end: new_range.end,
11938 goal: SelectionGoal::None,
11939 reversed: selection.reversed,
11940 });
11941 }
11942 }
11943 None
11944 }
11945
11946 let mut selected_larger_symbol = false;
11947 let new_selections = old_selections
11948 .iter()
11949 .map(|selection| match update_selection(selection, &buffer) {
11950 Some(new_selection) => {
11951 if new_selection.range() != selection.range() {
11952 selected_larger_symbol = true;
11953 }
11954 new_selection
11955 }
11956 None => selection.clone(),
11957 })
11958 .collect::<Vec<_>>();
11959
11960 if selected_larger_symbol {
11961 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11962 s.select(new_selections);
11963 });
11964 }
11965 }
11966
11967 pub fn select_larger_syntax_node(
11968 &mut self,
11969 _: &SelectLargerSyntaxNode,
11970 window: &mut Window,
11971 cx: &mut Context<Self>,
11972 ) {
11973 let Some(visible_row_count) = self.visible_row_count() else {
11974 return;
11975 };
11976 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
11977 if old_selections.is_empty() {
11978 return;
11979 }
11980
11981 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11982 let buffer = self.buffer.read(cx).snapshot(cx);
11983
11984 let mut selected_larger_node = false;
11985 let mut new_selections = old_selections
11986 .iter()
11987 .map(|selection| {
11988 let old_range = selection.start..selection.end;
11989 let mut new_range = old_range.clone();
11990 let mut new_node = None;
11991 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11992 {
11993 new_node = Some(node);
11994 new_range = match containing_range {
11995 MultiOrSingleBufferOffsetRange::Single(_) => break,
11996 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11997 };
11998 if !display_map.intersects_fold(new_range.start)
11999 && !display_map.intersects_fold(new_range.end)
12000 {
12001 break;
12002 }
12003 }
12004
12005 if let Some(node) = new_node {
12006 // Log the ancestor, to support using this action as a way to explore TreeSitter
12007 // nodes. Parent and grandparent are also logged because this operation will not
12008 // visit nodes that have the same range as their parent.
12009 log::info!("Node: {node:?}");
12010 let parent = node.parent();
12011 log::info!("Parent: {parent:?}");
12012 let grandparent = parent.and_then(|x| x.parent());
12013 log::info!("Grandparent: {grandparent:?}");
12014 }
12015
12016 selected_larger_node |= new_range != old_range;
12017 Selection {
12018 id: selection.id,
12019 start: new_range.start,
12020 end: new_range.end,
12021 goal: SelectionGoal::None,
12022 reversed: selection.reversed,
12023 }
12024 })
12025 .collect::<Vec<_>>();
12026
12027 if !selected_larger_node {
12028 return; // don't put this call in the history
12029 }
12030
12031 // scroll based on transformation done to the last selection created by the user
12032 let (last_old, last_new) = old_selections
12033 .last()
12034 .zip(new_selections.last().cloned())
12035 .expect("old_selections isn't empty");
12036
12037 // revert selection
12038 let is_selection_reversed = {
12039 let should_newest_selection_be_reversed = last_old.start != last_new.start;
12040 new_selections.last_mut().expect("checked above").reversed =
12041 should_newest_selection_be_reversed;
12042 should_newest_selection_be_reversed
12043 };
12044
12045 if selected_larger_node {
12046 self.select_syntax_node_history.disable_clearing = true;
12047 self.change_selections(None, window, cx, |s| {
12048 s.select(new_selections.clone());
12049 });
12050 self.select_syntax_node_history.disable_clearing = false;
12051 }
12052
12053 let start_row = last_new.start.to_display_point(&display_map).row().0;
12054 let end_row = last_new.end.to_display_point(&display_map).row().0;
12055 let selection_height = end_row - start_row + 1;
12056 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
12057
12058 // if fits on screen (considering margin), keep it in the middle, else, scroll to selection head
12059 let scroll_behavior = if visible_row_count >= selection_height + scroll_margin_rows * 2 {
12060 let middle_row = (end_row + start_row) / 2;
12061 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12062 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12063 SelectSyntaxNodeScrollBehavior::CenterSelection
12064 } else if is_selection_reversed {
12065 self.scroll_cursor_top(&Default::default(), window, cx);
12066 SelectSyntaxNodeScrollBehavior::CursorTop
12067 } else {
12068 self.scroll_cursor_bottom(&Default::default(), window, cx);
12069 SelectSyntaxNodeScrollBehavior::CursorBottom
12070 };
12071
12072 self.select_syntax_node_history.push((
12073 old_selections,
12074 scroll_behavior,
12075 is_selection_reversed,
12076 ));
12077 }
12078
12079 pub fn select_smaller_syntax_node(
12080 &mut self,
12081 _: &SelectSmallerSyntaxNode,
12082 window: &mut Window,
12083 cx: &mut Context<Self>,
12084 ) {
12085 let Some(visible_row_count) = self.visible_row_count() else {
12086 return;
12087 };
12088
12089 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
12090 self.select_syntax_node_history.pop()
12091 {
12092 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12093
12094 if let Some(selection) = selections.last_mut() {
12095 selection.reversed = is_selection_reversed;
12096 }
12097
12098 self.select_syntax_node_history.disable_clearing = true;
12099 self.change_selections(None, window, cx, |s| {
12100 s.select(selections.to_vec());
12101 });
12102 self.select_syntax_node_history.disable_clearing = false;
12103
12104 let newest = self.selections.newest::<usize>(cx);
12105 let start_row = newest.start.to_display_point(&display_map).row().0;
12106 let end_row = newest.end.to_display_point(&display_map).row().0;
12107
12108 match scroll_behavior {
12109 SelectSyntaxNodeScrollBehavior::CursorTop => {
12110 self.scroll_cursor_top(&Default::default(), window, cx);
12111 }
12112 SelectSyntaxNodeScrollBehavior::CenterSelection => {
12113 let middle_row = (end_row + start_row) / 2;
12114 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12115 // centralize the selection, not the cursor
12116 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12117 }
12118 SelectSyntaxNodeScrollBehavior::CursorBottom => {
12119 self.scroll_cursor_bottom(&Default::default(), window, cx);
12120 }
12121 }
12122 }
12123 }
12124
12125 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
12126 if !EditorSettings::get_global(cx).gutter.runnables {
12127 self.clear_tasks();
12128 return Task::ready(());
12129 }
12130 let project = self.project.as_ref().map(Entity::downgrade);
12131 cx.spawn_in(window, async move |this, cx| {
12132 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
12133 let Some(project) = project.and_then(|p| p.upgrade()) else {
12134 return;
12135 };
12136 let Ok(display_snapshot) = this.update(cx, |this, cx| {
12137 this.display_map.update(cx, |map, cx| map.snapshot(cx))
12138 }) else {
12139 return;
12140 };
12141
12142 let hide_runnables = project
12143 .update(cx, |project, cx| {
12144 // Do not display any test indicators in non-dev server remote projects.
12145 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
12146 })
12147 .unwrap_or(true);
12148 if hide_runnables {
12149 return;
12150 }
12151 let new_rows =
12152 cx.background_spawn({
12153 let snapshot = display_snapshot.clone();
12154 async move {
12155 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
12156 }
12157 })
12158 .await;
12159
12160 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
12161 this.update(cx, |this, _| {
12162 this.clear_tasks();
12163 for (key, value) in rows {
12164 this.insert_tasks(key, value);
12165 }
12166 })
12167 .ok();
12168 })
12169 }
12170 fn fetch_runnable_ranges(
12171 snapshot: &DisplaySnapshot,
12172 range: Range<Anchor>,
12173 ) -> Vec<language::RunnableRange> {
12174 snapshot.buffer_snapshot.runnable_ranges(range).collect()
12175 }
12176
12177 fn runnable_rows(
12178 project: Entity<Project>,
12179 snapshot: DisplaySnapshot,
12180 runnable_ranges: Vec<RunnableRange>,
12181 mut cx: AsyncWindowContext,
12182 ) -> Vec<((BufferId, u32), RunnableTasks)> {
12183 runnable_ranges
12184 .into_iter()
12185 .filter_map(|mut runnable| {
12186 let tasks = cx
12187 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
12188 .ok()?;
12189 if tasks.is_empty() {
12190 return None;
12191 }
12192
12193 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
12194
12195 let row = snapshot
12196 .buffer_snapshot
12197 .buffer_line_for_row(MultiBufferRow(point.row))?
12198 .1
12199 .start
12200 .row;
12201
12202 let context_range =
12203 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
12204 Some((
12205 (runnable.buffer_id, row),
12206 RunnableTasks {
12207 templates: tasks,
12208 offset: snapshot
12209 .buffer_snapshot
12210 .anchor_before(runnable.run_range.start),
12211 context_range,
12212 column: point.column,
12213 extra_variables: runnable.extra_captures,
12214 },
12215 ))
12216 })
12217 .collect()
12218 }
12219
12220 fn templates_with_tags(
12221 project: &Entity<Project>,
12222 runnable: &mut Runnable,
12223 cx: &mut App,
12224 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
12225 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
12226 let (worktree_id, file) = project
12227 .buffer_for_id(runnable.buffer, cx)
12228 .and_then(|buffer| buffer.read(cx).file())
12229 .map(|file| (file.worktree_id(cx), file.clone()))
12230 .unzip();
12231
12232 (
12233 project.task_store().read(cx).task_inventory().cloned(),
12234 worktree_id,
12235 file,
12236 )
12237 });
12238
12239 let tags = mem::take(&mut runnable.tags);
12240 let mut tags: Vec<_> = tags
12241 .into_iter()
12242 .flat_map(|tag| {
12243 let tag = tag.0.clone();
12244 inventory
12245 .as_ref()
12246 .into_iter()
12247 .flat_map(|inventory| {
12248 inventory.read(cx).list_tasks(
12249 file.clone(),
12250 Some(runnable.language.clone()),
12251 worktree_id,
12252 cx,
12253 )
12254 })
12255 .filter(move |(_, template)| {
12256 template.tags.iter().any(|source_tag| source_tag == &tag)
12257 })
12258 })
12259 .sorted_by_key(|(kind, _)| kind.to_owned())
12260 .collect();
12261 if let Some((leading_tag_source, _)) = tags.first() {
12262 // Strongest source wins; if we have worktree tag binding, prefer that to
12263 // global and language bindings;
12264 // if we have a global binding, prefer that to language binding.
12265 let first_mismatch = tags
12266 .iter()
12267 .position(|(tag_source, _)| tag_source != leading_tag_source);
12268 if let Some(index) = first_mismatch {
12269 tags.truncate(index);
12270 }
12271 }
12272
12273 tags
12274 }
12275
12276 pub fn move_to_enclosing_bracket(
12277 &mut self,
12278 _: &MoveToEnclosingBracket,
12279 window: &mut Window,
12280 cx: &mut Context<Self>,
12281 ) {
12282 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12283 s.move_offsets_with(|snapshot, selection| {
12284 let Some(enclosing_bracket_ranges) =
12285 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12286 else {
12287 return;
12288 };
12289
12290 let mut best_length = usize::MAX;
12291 let mut best_inside = false;
12292 let mut best_in_bracket_range = false;
12293 let mut best_destination = None;
12294 for (open, close) in enclosing_bracket_ranges {
12295 let close = close.to_inclusive();
12296 let length = close.end() - open.start;
12297 let inside = selection.start >= open.end && selection.end <= *close.start();
12298 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12299 || close.contains(&selection.head());
12300
12301 // If best is next to a bracket and current isn't, skip
12302 if !in_bracket_range && best_in_bracket_range {
12303 continue;
12304 }
12305
12306 // Prefer smaller lengths unless best is inside and current isn't
12307 if length > best_length && (best_inside || !inside) {
12308 continue;
12309 }
12310
12311 best_length = length;
12312 best_inside = inside;
12313 best_in_bracket_range = in_bracket_range;
12314 best_destination = Some(
12315 if close.contains(&selection.start) && close.contains(&selection.end) {
12316 if inside {
12317 open.end
12318 } else {
12319 open.start
12320 }
12321 } else if inside {
12322 *close.start()
12323 } else {
12324 *close.end()
12325 },
12326 );
12327 }
12328
12329 if let Some(destination) = best_destination {
12330 selection.collapse_to(destination, SelectionGoal::None);
12331 }
12332 })
12333 });
12334 }
12335
12336 pub fn undo_selection(
12337 &mut self,
12338 _: &UndoSelection,
12339 window: &mut Window,
12340 cx: &mut Context<Self>,
12341 ) {
12342 self.end_selection(window, cx);
12343 self.selection_history.mode = SelectionHistoryMode::Undoing;
12344 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12345 self.change_selections(None, window, cx, |s| {
12346 s.select_anchors(entry.selections.to_vec())
12347 });
12348 self.select_next_state = entry.select_next_state;
12349 self.select_prev_state = entry.select_prev_state;
12350 self.add_selections_state = entry.add_selections_state;
12351 self.request_autoscroll(Autoscroll::newest(), cx);
12352 }
12353 self.selection_history.mode = SelectionHistoryMode::Normal;
12354 }
12355
12356 pub fn redo_selection(
12357 &mut self,
12358 _: &RedoSelection,
12359 window: &mut Window,
12360 cx: &mut Context<Self>,
12361 ) {
12362 self.end_selection(window, cx);
12363 self.selection_history.mode = SelectionHistoryMode::Redoing;
12364 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12365 self.change_selections(None, window, cx, |s| {
12366 s.select_anchors(entry.selections.to_vec())
12367 });
12368 self.select_next_state = entry.select_next_state;
12369 self.select_prev_state = entry.select_prev_state;
12370 self.add_selections_state = entry.add_selections_state;
12371 self.request_autoscroll(Autoscroll::newest(), cx);
12372 }
12373 self.selection_history.mode = SelectionHistoryMode::Normal;
12374 }
12375
12376 pub fn expand_excerpts(
12377 &mut self,
12378 action: &ExpandExcerpts,
12379 _: &mut Window,
12380 cx: &mut Context<Self>,
12381 ) {
12382 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12383 }
12384
12385 pub fn expand_excerpts_down(
12386 &mut self,
12387 action: &ExpandExcerptsDown,
12388 _: &mut Window,
12389 cx: &mut Context<Self>,
12390 ) {
12391 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12392 }
12393
12394 pub fn expand_excerpts_up(
12395 &mut self,
12396 action: &ExpandExcerptsUp,
12397 _: &mut Window,
12398 cx: &mut Context<Self>,
12399 ) {
12400 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12401 }
12402
12403 pub fn expand_excerpts_for_direction(
12404 &mut self,
12405 lines: u32,
12406 direction: ExpandExcerptDirection,
12407
12408 cx: &mut Context<Self>,
12409 ) {
12410 let selections = self.selections.disjoint_anchors();
12411
12412 let lines = if lines == 0 {
12413 EditorSettings::get_global(cx).expand_excerpt_lines
12414 } else {
12415 lines
12416 };
12417
12418 self.buffer.update(cx, |buffer, cx| {
12419 let snapshot = buffer.snapshot(cx);
12420 let mut excerpt_ids = selections
12421 .iter()
12422 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12423 .collect::<Vec<_>>();
12424 excerpt_ids.sort();
12425 excerpt_ids.dedup();
12426 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12427 })
12428 }
12429
12430 pub fn expand_excerpt(
12431 &mut self,
12432 excerpt: ExcerptId,
12433 direction: ExpandExcerptDirection,
12434 window: &mut Window,
12435 cx: &mut Context<Self>,
12436 ) {
12437 let current_scroll_position = self.scroll_position(cx);
12438 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12439 self.buffer.update(cx, |buffer, cx| {
12440 buffer.expand_excerpts([excerpt], lines, direction, cx)
12441 });
12442 if direction == ExpandExcerptDirection::Down {
12443 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12444 self.set_scroll_position(new_scroll_position, window, cx);
12445 }
12446 }
12447
12448 pub fn go_to_singleton_buffer_point(
12449 &mut self,
12450 point: Point,
12451 window: &mut Window,
12452 cx: &mut Context<Self>,
12453 ) {
12454 self.go_to_singleton_buffer_range(point..point, window, cx);
12455 }
12456
12457 pub fn go_to_singleton_buffer_range(
12458 &mut self,
12459 range: Range<Point>,
12460 window: &mut Window,
12461 cx: &mut Context<Self>,
12462 ) {
12463 let multibuffer = self.buffer().read(cx);
12464 let Some(buffer) = multibuffer.as_singleton() else {
12465 return;
12466 };
12467 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12468 return;
12469 };
12470 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12471 return;
12472 };
12473 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12474 s.select_anchor_ranges([start..end])
12475 });
12476 }
12477
12478 fn go_to_diagnostic(
12479 &mut self,
12480 _: &GoToDiagnostic,
12481 window: &mut Window,
12482 cx: &mut Context<Self>,
12483 ) {
12484 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12485 }
12486
12487 fn go_to_prev_diagnostic(
12488 &mut self,
12489 _: &GoToPreviousDiagnostic,
12490 window: &mut Window,
12491 cx: &mut Context<Self>,
12492 ) {
12493 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12494 }
12495
12496 pub fn go_to_diagnostic_impl(
12497 &mut self,
12498 direction: Direction,
12499 window: &mut Window,
12500 cx: &mut Context<Self>,
12501 ) {
12502 let buffer = self.buffer.read(cx).snapshot(cx);
12503 let selection = self.selections.newest::<usize>(cx);
12504
12505 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12506 if direction == Direction::Next {
12507 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12508 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12509 return;
12510 };
12511 self.activate_diagnostics(
12512 buffer_id,
12513 popover.local_diagnostic.diagnostic.group_id,
12514 window,
12515 cx,
12516 );
12517 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12518 let primary_range_start = active_diagnostics.primary_range.start;
12519 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12520 let mut new_selection = s.newest_anchor().clone();
12521 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12522 s.select_anchors(vec![new_selection.clone()]);
12523 });
12524 self.refresh_inline_completion(false, true, window, cx);
12525 }
12526 return;
12527 }
12528 }
12529
12530 let active_group_id = self
12531 .active_diagnostics
12532 .as_ref()
12533 .map(|active_group| active_group.group_id);
12534 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12535 active_diagnostics
12536 .primary_range
12537 .to_offset(&buffer)
12538 .to_inclusive()
12539 });
12540 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12541 if active_primary_range.contains(&selection.head()) {
12542 *active_primary_range.start()
12543 } else {
12544 selection.head()
12545 }
12546 } else {
12547 selection.head()
12548 };
12549
12550 let snapshot = self.snapshot(window, cx);
12551 let primary_diagnostics_before = buffer
12552 .diagnostics_in_range::<usize>(0..search_start)
12553 .filter(|entry| entry.diagnostic.is_primary)
12554 .filter(|entry| entry.range.start != entry.range.end)
12555 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12556 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12557 .collect::<Vec<_>>();
12558 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12559 primary_diagnostics_before
12560 .iter()
12561 .position(|entry| entry.diagnostic.group_id == active_group_id)
12562 });
12563
12564 let primary_diagnostics_after = buffer
12565 .diagnostics_in_range::<usize>(search_start..buffer.len())
12566 .filter(|entry| entry.diagnostic.is_primary)
12567 .filter(|entry| entry.range.start != entry.range.end)
12568 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12569 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12570 .collect::<Vec<_>>();
12571 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12572 primary_diagnostics_after
12573 .iter()
12574 .enumerate()
12575 .rev()
12576 .find_map(|(i, entry)| {
12577 if entry.diagnostic.group_id == active_group_id {
12578 Some(i)
12579 } else {
12580 None
12581 }
12582 })
12583 });
12584
12585 let next_primary_diagnostic = match direction {
12586 Direction::Prev => primary_diagnostics_before
12587 .iter()
12588 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12589 .rev()
12590 .next(),
12591 Direction::Next => primary_diagnostics_after
12592 .iter()
12593 .skip(
12594 last_same_group_diagnostic_after
12595 .map(|index| index + 1)
12596 .unwrap_or(0),
12597 )
12598 .next(),
12599 };
12600
12601 // Cycle around to the start of the buffer, potentially moving back to the start of
12602 // the currently active diagnostic.
12603 let cycle_around = || match direction {
12604 Direction::Prev => primary_diagnostics_after
12605 .iter()
12606 .rev()
12607 .chain(primary_diagnostics_before.iter().rev())
12608 .next(),
12609 Direction::Next => primary_diagnostics_before
12610 .iter()
12611 .chain(primary_diagnostics_after.iter())
12612 .next(),
12613 };
12614
12615 if let Some((primary_range, group_id)) = next_primary_diagnostic
12616 .or_else(cycle_around)
12617 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12618 {
12619 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12620 return;
12621 };
12622 self.activate_diagnostics(buffer_id, group_id, window, cx);
12623 if self.active_diagnostics.is_some() {
12624 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12625 s.select(vec![Selection {
12626 id: selection.id,
12627 start: primary_range.start,
12628 end: primary_range.start,
12629 reversed: false,
12630 goal: SelectionGoal::None,
12631 }]);
12632 });
12633 self.refresh_inline_completion(false, true, window, cx);
12634 }
12635 }
12636 }
12637
12638 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12639 let snapshot = self.snapshot(window, cx);
12640 let selection = self.selections.newest::<Point>(cx);
12641 self.go_to_hunk_before_or_after_position(
12642 &snapshot,
12643 selection.head(),
12644 Direction::Next,
12645 window,
12646 cx,
12647 );
12648 }
12649
12650 fn go_to_hunk_before_or_after_position(
12651 &mut self,
12652 snapshot: &EditorSnapshot,
12653 position: Point,
12654 direction: Direction,
12655 window: &mut Window,
12656 cx: &mut Context<Editor>,
12657 ) {
12658 let row = if direction == Direction::Next {
12659 self.hunk_after_position(snapshot, position)
12660 .map(|hunk| hunk.row_range.start)
12661 } else {
12662 self.hunk_before_position(snapshot, position)
12663 };
12664
12665 if let Some(row) = row {
12666 let destination = Point::new(row.0, 0);
12667 let autoscroll = Autoscroll::center();
12668
12669 self.unfold_ranges(&[destination..destination], false, false, cx);
12670 self.change_selections(Some(autoscroll), window, cx, |s| {
12671 s.select_ranges([destination..destination]);
12672 });
12673 }
12674 }
12675
12676 fn hunk_after_position(
12677 &mut self,
12678 snapshot: &EditorSnapshot,
12679 position: Point,
12680 ) -> Option<MultiBufferDiffHunk> {
12681 snapshot
12682 .buffer_snapshot
12683 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12684 .find(|hunk| hunk.row_range.start.0 > position.row)
12685 .or_else(|| {
12686 snapshot
12687 .buffer_snapshot
12688 .diff_hunks_in_range(Point::zero()..position)
12689 .find(|hunk| hunk.row_range.end.0 < position.row)
12690 })
12691 }
12692
12693 fn go_to_prev_hunk(
12694 &mut self,
12695 _: &GoToPreviousHunk,
12696 window: &mut Window,
12697 cx: &mut Context<Self>,
12698 ) {
12699 let snapshot = self.snapshot(window, cx);
12700 let selection = self.selections.newest::<Point>(cx);
12701 self.go_to_hunk_before_or_after_position(
12702 &snapshot,
12703 selection.head(),
12704 Direction::Prev,
12705 window,
12706 cx,
12707 );
12708 }
12709
12710 fn hunk_before_position(
12711 &mut self,
12712 snapshot: &EditorSnapshot,
12713 position: Point,
12714 ) -> Option<MultiBufferRow> {
12715 snapshot
12716 .buffer_snapshot
12717 .diff_hunk_before(position)
12718 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12719 }
12720
12721 fn go_to_line<T: 'static>(
12722 &mut self,
12723 position: Anchor,
12724 highlight_color: Option<Hsla>,
12725 window: &mut Window,
12726 cx: &mut Context<Self>,
12727 ) {
12728 let snapshot = self.snapshot(window, cx).display_snapshot;
12729 let position = position.to_point(&snapshot.buffer_snapshot);
12730 let start = snapshot
12731 .buffer_snapshot
12732 .clip_point(Point::new(position.row, 0), Bias::Left);
12733 let end = start + Point::new(1, 0);
12734 let start = snapshot.buffer_snapshot.anchor_before(start);
12735 let end = snapshot.buffer_snapshot.anchor_before(end);
12736
12737 self.highlight_rows::<T>(
12738 start..end,
12739 highlight_color
12740 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12741 false,
12742 cx,
12743 );
12744 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
12745 }
12746
12747 pub fn go_to_definition(
12748 &mut self,
12749 _: &GoToDefinition,
12750 window: &mut Window,
12751 cx: &mut Context<Self>,
12752 ) -> Task<Result<Navigated>> {
12753 let definition =
12754 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12755 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
12756 cx.spawn_in(window, async move |editor, cx| {
12757 if definition.await? == Navigated::Yes {
12758 return Ok(Navigated::Yes);
12759 }
12760 match fallback_strategy {
12761 GoToDefinitionFallback::None => Ok(Navigated::No),
12762 GoToDefinitionFallback::FindAllReferences => {
12763 match editor.update_in(cx, |editor, window, cx| {
12764 editor.find_all_references(&FindAllReferences, window, cx)
12765 })? {
12766 Some(references) => references.await,
12767 None => Ok(Navigated::No),
12768 }
12769 }
12770 }
12771 })
12772 }
12773
12774 pub fn go_to_declaration(
12775 &mut self,
12776 _: &GoToDeclaration,
12777 window: &mut Window,
12778 cx: &mut Context<Self>,
12779 ) -> Task<Result<Navigated>> {
12780 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12781 }
12782
12783 pub fn go_to_declaration_split(
12784 &mut self,
12785 _: &GoToDeclaration,
12786 window: &mut Window,
12787 cx: &mut Context<Self>,
12788 ) -> Task<Result<Navigated>> {
12789 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12790 }
12791
12792 pub fn go_to_implementation(
12793 &mut self,
12794 _: &GoToImplementation,
12795 window: &mut Window,
12796 cx: &mut Context<Self>,
12797 ) -> Task<Result<Navigated>> {
12798 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12799 }
12800
12801 pub fn go_to_implementation_split(
12802 &mut self,
12803 _: &GoToImplementationSplit,
12804 window: &mut Window,
12805 cx: &mut Context<Self>,
12806 ) -> Task<Result<Navigated>> {
12807 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12808 }
12809
12810 pub fn go_to_type_definition(
12811 &mut self,
12812 _: &GoToTypeDefinition,
12813 window: &mut Window,
12814 cx: &mut Context<Self>,
12815 ) -> Task<Result<Navigated>> {
12816 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12817 }
12818
12819 pub fn go_to_definition_split(
12820 &mut self,
12821 _: &GoToDefinitionSplit,
12822 window: &mut Window,
12823 cx: &mut Context<Self>,
12824 ) -> Task<Result<Navigated>> {
12825 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12826 }
12827
12828 pub fn go_to_type_definition_split(
12829 &mut self,
12830 _: &GoToTypeDefinitionSplit,
12831 window: &mut Window,
12832 cx: &mut Context<Self>,
12833 ) -> Task<Result<Navigated>> {
12834 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12835 }
12836
12837 fn go_to_definition_of_kind(
12838 &mut self,
12839 kind: GotoDefinitionKind,
12840 split: bool,
12841 window: &mut Window,
12842 cx: &mut Context<Self>,
12843 ) -> Task<Result<Navigated>> {
12844 let Some(provider) = self.semantics_provider.clone() else {
12845 return Task::ready(Ok(Navigated::No));
12846 };
12847 let head = self.selections.newest::<usize>(cx).head();
12848 let buffer = self.buffer.read(cx);
12849 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12850 text_anchor
12851 } else {
12852 return Task::ready(Ok(Navigated::No));
12853 };
12854
12855 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12856 return Task::ready(Ok(Navigated::No));
12857 };
12858
12859 cx.spawn_in(window, async move |editor, cx| {
12860 let definitions = definitions.await?;
12861 let navigated = editor
12862 .update_in(cx, |editor, window, cx| {
12863 editor.navigate_to_hover_links(
12864 Some(kind),
12865 definitions
12866 .into_iter()
12867 .filter(|location| {
12868 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12869 })
12870 .map(HoverLink::Text)
12871 .collect::<Vec<_>>(),
12872 split,
12873 window,
12874 cx,
12875 )
12876 })?
12877 .await?;
12878 anyhow::Ok(navigated)
12879 })
12880 }
12881
12882 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12883 let selection = self.selections.newest_anchor();
12884 let head = selection.head();
12885 let tail = selection.tail();
12886
12887 let Some((buffer, start_position)) =
12888 self.buffer.read(cx).text_anchor_for_position(head, cx)
12889 else {
12890 return;
12891 };
12892
12893 let end_position = if head != tail {
12894 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12895 return;
12896 };
12897 Some(pos)
12898 } else {
12899 None
12900 };
12901
12902 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12903 let url = if let Some(end_pos) = end_position {
12904 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12905 } else {
12906 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12907 };
12908
12909 if let Some(url) = url {
12910 editor.update(cx, |_, cx| {
12911 cx.open_url(&url);
12912 })
12913 } else {
12914 Ok(())
12915 }
12916 });
12917
12918 url_finder.detach();
12919 }
12920
12921 pub fn open_selected_filename(
12922 &mut self,
12923 _: &OpenSelectedFilename,
12924 window: &mut Window,
12925 cx: &mut Context<Self>,
12926 ) {
12927 let Some(workspace) = self.workspace() else {
12928 return;
12929 };
12930
12931 let position = self.selections.newest_anchor().head();
12932
12933 let Some((buffer, buffer_position)) =
12934 self.buffer.read(cx).text_anchor_for_position(position, cx)
12935 else {
12936 return;
12937 };
12938
12939 let project = self.project.clone();
12940
12941 cx.spawn_in(window, async move |_, cx| {
12942 let result = find_file(&buffer, project, buffer_position, cx).await;
12943
12944 if let Some((_, path)) = result {
12945 workspace
12946 .update_in(cx, |workspace, window, cx| {
12947 workspace.open_resolved_path(path, window, cx)
12948 })?
12949 .await?;
12950 }
12951 anyhow::Ok(())
12952 })
12953 .detach();
12954 }
12955
12956 pub(crate) fn navigate_to_hover_links(
12957 &mut self,
12958 kind: Option<GotoDefinitionKind>,
12959 mut definitions: Vec<HoverLink>,
12960 split: bool,
12961 window: &mut Window,
12962 cx: &mut Context<Editor>,
12963 ) -> Task<Result<Navigated>> {
12964 // If there is one definition, just open it directly
12965 if definitions.len() == 1 {
12966 let definition = definitions.pop().unwrap();
12967
12968 enum TargetTaskResult {
12969 Location(Option<Location>),
12970 AlreadyNavigated,
12971 }
12972
12973 let target_task = match definition {
12974 HoverLink::Text(link) => {
12975 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12976 }
12977 HoverLink::InlayHint(lsp_location, server_id) => {
12978 let computation =
12979 self.compute_target_location(lsp_location, server_id, window, cx);
12980 cx.background_spawn(async move {
12981 let location = computation.await?;
12982 Ok(TargetTaskResult::Location(location))
12983 })
12984 }
12985 HoverLink::Url(url) => {
12986 cx.open_url(&url);
12987 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12988 }
12989 HoverLink::File(path) => {
12990 if let Some(workspace) = self.workspace() {
12991 cx.spawn_in(window, async move |_, cx| {
12992 workspace
12993 .update_in(cx, |workspace, window, cx| {
12994 workspace.open_resolved_path(path, window, cx)
12995 })?
12996 .await
12997 .map(|_| TargetTaskResult::AlreadyNavigated)
12998 })
12999 } else {
13000 Task::ready(Ok(TargetTaskResult::Location(None)))
13001 }
13002 }
13003 };
13004 cx.spawn_in(window, async move |editor, cx| {
13005 let target = match target_task.await.context("target resolution task")? {
13006 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
13007 TargetTaskResult::Location(None) => return Ok(Navigated::No),
13008 TargetTaskResult::Location(Some(target)) => target,
13009 };
13010
13011 editor.update_in(cx, |editor, window, cx| {
13012 let Some(workspace) = editor.workspace() else {
13013 return Navigated::No;
13014 };
13015 let pane = workspace.read(cx).active_pane().clone();
13016
13017 let range = target.range.to_point(target.buffer.read(cx));
13018 let range = editor.range_for_match(&range);
13019 let range = collapse_multiline_range(range);
13020
13021 if !split
13022 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
13023 {
13024 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
13025 } else {
13026 window.defer(cx, move |window, cx| {
13027 let target_editor: Entity<Self> =
13028 workspace.update(cx, |workspace, cx| {
13029 let pane = if split {
13030 workspace.adjacent_pane(window, cx)
13031 } else {
13032 workspace.active_pane().clone()
13033 };
13034
13035 workspace.open_project_item(
13036 pane,
13037 target.buffer.clone(),
13038 true,
13039 true,
13040 window,
13041 cx,
13042 )
13043 });
13044 target_editor.update(cx, |target_editor, cx| {
13045 // When selecting a definition in a different buffer, disable the nav history
13046 // to avoid creating a history entry at the previous cursor location.
13047 pane.update(cx, |pane, _| pane.disable_history());
13048 target_editor.go_to_singleton_buffer_range(range, window, cx);
13049 pane.update(cx, |pane, _| pane.enable_history());
13050 });
13051 });
13052 }
13053 Navigated::Yes
13054 })
13055 })
13056 } else if !definitions.is_empty() {
13057 cx.spawn_in(window, async move |editor, cx| {
13058 let (title, location_tasks, workspace) = editor
13059 .update_in(cx, |editor, window, cx| {
13060 let tab_kind = match kind {
13061 Some(GotoDefinitionKind::Implementation) => "Implementations",
13062 _ => "Definitions",
13063 };
13064 let title = definitions
13065 .iter()
13066 .find_map(|definition| match definition {
13067 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
13068 let buffer = origin.buffer.read(cx);
13069 format!(
13070 "{} for {}",
13071 tab_kind,
13072 buffer
13073 .text_for_range(origin.range.clone())
13074 .collect::<String>()
13075 )
13076 }),
13077 HoverLink::InlayHint(_, _) => None,
13078 HoverLink::Url(_) => None,
13079 HoverLink::File(_) => None,
13080 })
13081 .unwrap_or(tab_kind.to_string());
13082 let location_tasks = definitions
13083 .into_iter()
13084 .map(|definition| match definition {
13085 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
13086 HoverLink::InlayHint(lsp_location, server_id) => editor
13087 .compute_target_location(lsp_location, server_id, window, cx),
13088 HoverLink::Url(_) => Task::ready(Ok(None)),
13089 HoverLink::File(_) => Task::ready(Ok(None)),
13090 })
13091 .collect::<Vec<_>>();
13092 (title, location_tasks, editor.workspace().clone())
13093 })
13094 .context("location tasks preparation")?;
13095
13096 let locations = future::join_all(location_tasks)
13097 .await
13098 .into_iter()
13099 .filter_map(|location| location.transpose())
13100 .collect::<Result<_>>()
13101 .context("location tasks")?;
13102
13103 let Some(workspace) = workspace else {
13104 return Ok(Navigated::No);
13105 };
13106 let opened = workspace
13107 .update_in(cx, |workspace, window, cx| {
13108 Self::open_locations_in_multibuffer(
13109 workspace,
13110 locations,
13111 title,
13112 split,
13113 MultibufferSelectionMode::First,
13114 window,
13115 cx,
13116 )
13117 })
13118 .ok();
13119
13120 anyhow::Ok(Navigated::from_bool(opened.is_some()))
13121 })
13122 } else {
13123 Task::ready(Ok(Navigated::No))
13124 }
13125 }
13126
13127 fn compute_target_location(
13128 &self,
13129 lsp_location: lsp::Location,
13130 server_id: LanguageServerId,
13131 window: &mut Window,
13132 cx: &mut Context<Self>,
13133 ) -> Task<anyhow::Result<Option<Location>>> {
13134 let Some(project) = self.project.clone() else {
13135 return Task::ready(Ok(None));
13136 };
13137
13138 cx.spawn_in(window, async move |editor, cx| {
13139 let location_task = editor.update(cx, |_, cx| {
13140 project.update(cx, |project, cx| {
13141 let language_server_name = project
13142 .language_server_statuses(cx)
13143 .find(|(id, _)| server_id == *id)
13144 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
13145 language_server_name.map(|language_server_name| {
13146 project.open_local_buffer_via_lsp(
13147 lsp_location.uri.clone(),
13148 server_id,
13149 language_server_name,
13150 cx,
13151 )
13152 })
13153 })
13154 })?;
13155 let location = match location_task {
13156 Some(task) => Some({
13157 let target_buffer_handle = task.await.context("open local buffer")?;
13158 let range = target_buffer_handle.update(cx, |target_buffer, _| {
13159 let target_start = target_buffer
13160 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
13161 let target_end = target_buffer
13162 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
13163 target_buffer.anchor_after(target_start)
13164 ..target_buffer.anchor_before(target_end)
13165 })?;
13166 Location {
13167 buffer: target_buffer_handle,
13168 range,
13169 }
13170 }),
13171 None => None,
13172 };
13173 Ok(location)
13174 })
13175 }
13176
13177 pub fn find_all_references(
13178 &mut self,
13179 _: &FindAllReferences,
13180 window: &mut Window,
13181 cx: &mut Context<Self>,
13182 ) -> Option<Task<Result<Navigated>>> {
13183 let selection = self.selections.newest::<usize>(cx);
13184 let multi_buffer = self.buffer.read(cx);
13185 let head = selection.head();
13186
13187 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13188 let head_anchor = multi_buffer_snapshot.anchor_at(
13189 head,
13190 if head < selection.tail() {
13191 Bias::Right
13192 } else {
13193 Bias::Left
13194 },
13195 );
13196
13197 match self
13198 .find_all_references_task_sources
13199 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13200 {
13201 Ok(_) => {
13202 log::info!(
13203 "Ignoring repeated FindAllReferences invocation with the position of already running task"
13204 );
13205 return None;
13206 }
13207 Err(i) => {
13208 self.find_all_references_task_sources.insert(i, head_anchor);
13209 }
13210 }
13211
13212 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
13213 let workspace = self.workspace()?;
13214 let project = workspace.read(cx).project().clone();
13215 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
13216 Some(cx.spawn_in(window, async move |editor, cx| {
13217 let _cleanup = cx.on_drop(&editor, move |editor, _| {
13218 if let Ok(i) = editor
13219 .find_all_references_task_sources
13220 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13221 {
13222 editor.find_all_references_task_sources.remove(i);
13223 }
13224 });
13225
13226 let locations = references.await?;
13227 if locations.is_empty() {
13228 return anyhow::Ok(Navigated::No);
13229 }
13230
13231 workspace.update_in(cx, |workspace, window, cx| {
13232 let title = locations
13233 .first()
13234 .as_ref()
13235 .map(|location| {
13236 let buffer = location.buffer.read(cx);
13237 format!(
13238 "References to `{}`",
13239 buffer
13240 .text_for_range(location.range.clone())
13241 .collect::<String>()
13242 )
13243 })
13244 .unwrap();
13245 Self::open_locations_in_multibuffer(
13246 workspace,
13247 locations,
13248 title,
13249 false,
13250 MultibufferSelectionMode::First,
13251 window,
13252 cx,
13253 );
13254 Navigated::Yes
13255 })
13256 }))
13257 }
13258
13259 /// Opens a multibuffer with the given project locations in it
13260 pub fn open_locations_in_multibuffer(
13261 workspace: &mut Workspace,
13262 mut locations: Vec<Location>,
13263 title: String,
13264 split: bool,
13265 multibuffer_selection_mode: MultibufferSelectionMode,
13266 window: &mut Window,
13267 cx: &mut Context<Workspace>,
13268 ) {
13269 // If there are multiple definitions, open them in a multibuffer
13270 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13271 let mut locations = locations.into_iter().peekable();
13272 let mut ranges = Vec::new();
13273 let capability = workspace.project().read(cx).capability();
13274
13275 let excerpt_buffer = cx.new(|cx| {
13276 let mut multibuffer = MultiBuffer::new(capability);
13277 while let Some(location) = locations.next() {
13278 let buffer = location.buffer.read(cx);
13279 let mut ranges_for_buffer = Vec::new();
13280 let range = location.range.to_offset(buffer);
13281 ranges_for_buffer.push(range.clone());
13282
13283 while let Some(next_location) = locations.peek() {
13284 if next_location.buffer == location.buffer {
13285 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13286 locations.next();
13287 } else {
13288 break;
13289 }
13290 }
13291
13292 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13293 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13294 location.buffer.clone(),
13295 ranges_for_buffer,
13296 DEFAULT_MULTIBUFFER_CONTEXT,
13297 cx,
13298 ))
13299 }
13300
13301 multibuffer.with_title(title)
13302 });
13303
13304 let editor = cx.new(|cx| {
13305 Editor::for_multibuffer(
13306 excerpt_buffer,
13307 Some(workspace.project().clone()),
13308 window,
13309 cx,
13310 )
13311 });
13312 editor.update(cx, |editor, cx| {
13313 match multibuffer_selection_mode {
13314 MultibufferSelectionMode::First => {
13315 if let Some(first_range) = ranges.first() {
13316 editor.change_selections(None, window, cx, |selections| {
13317 selections.clear_disjoint();
13318 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13319 });
13320 }
13321 editor.highlight_background::<Self>(
13322 &ranges,
13323 |theme| theme.editor_highlighted_line_background,
13324 cx,
13325 );
13326 }
13327 MultibufferSelectionMode::All => {
13328 editor.change_selections(None, window, cx, |selections| {
13329 selections.clear_disjoint();
13330 selections.select_anchor_ranges(ranges);
13331 });
13332 }
13333 }
13334 editor.register_buffers_with_language_servers(cx);
13335 });
13336
13337 let item = Box::new(editor);
13338 let item_id = item.item_id();
13339
13340 if split {
13341 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13342 } else {
13343 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13344 let (preview_item_id, preview_item_idx) =
13345 workspace.active_pane().update(cx, |pane, _| {
13346 (pane.preview_item_id(), pane.preview_item_idx())
13347 });
13348
13349 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13350
13351 if let Some(preview_item_id) = preview_item_id {
13352 workspace.active_pane().update(cx, |pane, cx| {
13353 pane.remove_item(preview_item_id, false, false, window, cx);
13354 });
13355 }
13356 } else {
13357 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13358 }
13359 }
13360 workspace.active_pane().update(cx, |pane, cx| {
13361 pane.set_preview_item_id(Some(item_id), cx);
13362 });
13363 }
13364
13365 pub fn rename(
13366 &mut self,
13367 _: &Rename,
13368 window: &mut Window,
13369 cx: &mut Context<Self>,
13370 ) -> Option<Task<Result<()>>> {
13371 use language::ToOffset as _;
13372
13373 let provider = self.semantics_provider.clone()?;
13374 let selection = self.selections.newest_anchor().clone();
13375 let (cursor_buffer, cursor_buffer_position) = self
13376 .buffer
13377 .read(cx)
13378 .text_anchor_for_position(selection.head(), cx)?;
13379 let (tail_buffer, cursor_buffer_position_end) = self
13380 .buffer
13381 .read(cx)
13382 .text_anchor_for_position(selection.tail(), cx)?;
13383 if tail_buffer != cursor_buffer {
13384 return None;
13385 }
13386
13387 let snapshot = cursor_buffer.read(cx).snapshot();
13388 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13389 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13390 let prepare_rename = provider
13391 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13392 .unwrap_or_else(|| Task::ready(Ok(None)));
13393 drop(snapshot);
13394
13395 Some(cx.spawn_in(window, async move |this, cx| {
13396 let rename_range = if let Some(range) = prepare_rename.await? {
13397 Some(range)
13398 } else {
13399 this.update(cx, |this, cx| {
13400 let buffer = this.buffer.read(cx).snapshot(cx);
13401 let mut buffer_highlights = this
13402 .document_highlights_for_position(selection.head(), &buffer)
13403 .filter(|highlight| {
13404 highlight.start.excerpt_id == selection.head().excerpt_id
13405 && highlight.end.excerpt_id == selection.head().excerpt_id
13406 });
13407 buffer_highlights
13408 .next()
13409 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13410 })?
13411 };
13412 if let Some(rename_range) = rename_range {
13413 this.update_in(cx, |this, window, cx| {
13414 let snapshot = cursor_buffer.read(cx).snapshot();
13415 let rename_buffer_range = rename_range.to_offset(&snapshot);
13416 let cursor_offset_in_rename_range =
13417 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13418 let cursor_offset_in_rename_range_end =
13419 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13420
13421 this.take_rename(false, window, cx);
13422 let buffer = this.buffer.read(cx).read(cx);
13423 let cursor_offset = selection.head().to_offset(&buffer);
13424 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13425 let rename_end = rename_start + rename_buffer_range.len();
13426 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13427 let mut old_highlight_id = None;
13428 let old_name: Arc<str> = buffer
13429 .chunks(rename_start..rename_end, true)
13430 .map(|chunk| {
13431 if old_highlight_id.is_none() {
13432 old_highlight_id = chunk.syntax_highlight_id;
13433 }
13434 chunk.text
13435 })
13436 .collect::<String>()
13437 .into();
13438
13439 drop(buffer);
13440
13441 // Position the selection in the rename editor so that it matches the current selection.
13442 this.show_local_selections = false;
13443 let rename_editor = cx.new(|cx| {
13444 let mut editor = Editor::single_line(window, cx);
13445 editor.buffer.update(cx, |buffer, cx| {
13446 buffer.edit([(0..0, old_name.clone())], None, cx)
13447 });
13448 let rename_selection_range = match cursor_offset_in_rename_range
13449 .cmp(&cursor_offset_in_rename_range_end)
13450 {
13451 Ordering::Equal => {
13452 editor.select_all(&SelectAll, window, cx);
13453 return editor;
13454 }
13455 Ordering::Less => {
13456 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13457 }
13458 Ordering::Greater => {
13459 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13460 }
13461 };
13462 if rename_selection_range.end > old_name.len() {
13463 editor.select_all(&SelectAll, window, cx);
13464 } else {
13465 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13466 s.select_ranges([rename_selection_range]);
13467 });
13468 }
13469 editor
13470 });
13471 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13472 if e == &EditorEvent::Focused {
13473 cx.emit(EditorEvent::FocusedIn)
13474 }
13475 })
13476 .detach();
13477
13478 let write_highlights =
13479 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13480 let read_highlights =
13481 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13482 let ranges = write_highlights
13483 .iter()
13484 .flat_map(|(_, ranges)| ranges.iter())
13485 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13486 .cloned()
13487 .collect();
13488
13489 this.highlight_text::<Rename>(
13490 ranges,
13491 HighlightStyle {
13492 fade_out: Some(0.6),
13493 ..Default::default()
13494 },
13495 cx,
13496 );
13497 let rename_focus_handle = rename_editor.focus_handle(cx);
13498 window.focus(&rename_focus_handle);
13499 let block_id = this.insert_blocks(
13500 [BlockProperties {
13501 style: BlockStyle::Flex,
13502 placement: BlockPlacement::Below(range.start),
13503 height: 1,
13504 render: Arc::new({
13505 let rename_editor = rename_editor.clone();
13506 move |cx: &mut BlockContext| {
13507 let mut text_style = cx.editor_style.text.clone();
13508 if let Some(highlight_style) = old_highlight_id
13509 .and_then(|h| h.style(&cx.editor_style.syntax))
13510 {
13511 text_style = text_style.highlight(highlight_style);
13512 }
13513 div()
13514 .block_mouse_down()
13515 .pl(cx.anchor_x)
13516 .child(EditorElement::new(
13517 &rename_editor,
13518 EditorStyle {
13519 background: cx.theme().system().transparent,
13520 local_player: cx.editor_style.local_player,
13521 text: text_style,
13522 scrollbar_width: cx.editor_style.scrollbar_width,
13523 syntax: cx.editor_style.syntax.clone(),
13524 status: cx.editor_style.status.clone(),
13525 inlay_hints_style: HighlightStyle {
13526 font_weight: Some(FontWeight::BOLD),
13527 ..make_inlay_hints_style(cx.app)
13528 },
13529 inline_completion_styles: make_suggestion_styles(
13530 cx.app,
13531 ),
13532 ..EditorStyle::default()
13533 },
13534 ))
13535 .into_any_element()
13536 }
13537 }),
13538 priority: 0,
13539 }],
13540 Some(Autoscroll::fit()),
13541 cx,
13542 )[0];
13543 this.pending_rename = Some(RenameState {
13544 range,
13545 old_name,
13546 editor: rename_editor,
13547 block_id,
13548 });
13549 })?;
13550 }
13551
13552 Ok(())
13553 }))
13554 }
13555
13556 pub fn confirm_rename(
13557 &mut self,
13558 _: &ConfirmRename,
13559 window: &mut Window,
13560 cx: &mut Context<Self>,
13561 ) -> Option<Task<Result<()>>> {
13562 let rename = self.take_rename(false, window, cx)?;
13563 let workspace = self.workspace()?.downgrade();
13564 let (buffer, start) = self
13565 .buffer
13566 .read(cx)
13567 .text_anchor_for_position(rename.range.start, cx)?;
13568 let (end_buffer, _) = self
13569 .buffer
13570 .read(cx)
13571 .text_anchor_for_position(rename.range.end, cx)?;
13572 if buffer != end_buffer {
13573 return None;
13574 }
13575
13576 let old_name = rename.old_name;
13577 let new_name = rename.editor.read(cx).text(cx);
13578
13579 let rename = self.semantics_provider.as_ref()?.perform_rename(
13580 &buffer,
13581 start,
13582 new_name.clone(),
13583 cx,
13584 )?;
13585
13586 Some(cx.spawn_in(window, async move |editor, cx| {
13587 let project_transaction = rename.await?;
13588 Self::open_project_transaction(
13589 &editor,
13590 workspace,
13591 project_transaction,
13592 format!("Rename: {} → {}", old_name, new_name),
13593 cx,
13594 )
13595 .await?;
13596
13597 editor.update(cx, |editor, cx| {
13598 editor.refresh_document_highlights(cx);
13599 })?;
13600 Ok(())
13601 }))
13602 }
13603
13604 fn take_rename(
13605 &mut self,
13606 moving_cursor: bool,
13607 window: &mut Window,
13608 cx: &mut Context<Self>,
13609 ) -> Option<RenameState> {
13610 let rename = self.pending_rename.take()?;
13611 if rename.editor.focus_handle(cx).is_focused(window) {
13612 window.focus(&self.focus_handle);
13613 }
13614
13615 self.remove_blocks(
13616 [rename.block_id].into_iter().collect(),
13617 Some(Autoscroll::fit()),
13618 cx,
13619 );
13620 self.clear_highlights::<Rename>(cx);
13621 self.show_local_selections = true;
13622
13623 if moving_cursor {
13624 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13625 editor.selections.newest::<usize>(cx).head()
13626 });
13627
13628 // Update the selection to match the position of the selection inside
13629 // the rename editor.
13630 let snapshot = self.buffer.read(cx).read(cx);
13631 let rename_range = rename.range.to_offset(&snapshot);
13632 let cursor_in_editor = snapshot
13633 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13634 .min(rename_range.end);
13635 drop(snapshot);
13636
13637 self.change_selections(None, window, cx, |s| {
13638 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13639 });
13640 } else {
13641 self.refresh_document_highlights(cx);
13642 }
13643
13644 Some(rename)
13645 }
13646
13647 pub fn pending_rename(&self) -> Option<&RenameState> {
13648 self.pending_rename.as_ref()
13649 }
13650
13651 fn format(
13652 &mut self,
13653 _: &Format,
13654 window: &mut Window,
13655 cx: &mut Context<Self>,
13656 ) -> Option<Task<Result<()>>> {
13657 let project = match &self.project {
13658 Some(project) => project.clone(),
13659 None => return None,
13660 };
13661
13662 Some(self.perform_format(
13663 project,
13664 FormatTrigger::Manual,
13665 FormatTarget::Buffers,
13666 window,
13667 cx,
13668 ))
13669 }
13670
13671 fn format_selections(
13672 &mut self,
13673 _: &FormatSelections,
13674 window: &mut Window,
13675 cx: &mut Context<Self>,
13676 ) -> Option<Task<Result<()>>> {
13677 let project = match &self.project {
13678 Some(project) => project.clone(),
13679 None => return None,
13680 };
13681
13682 let ranges = self
13683 .selections
13684 .all_adjusted(cx)
13685 .into_iter()
13686 .map(|selection| selection.range())
13687 .collect_vec();
13688
13689 Some(self.perform_format(
13690 project,
13691 FormatTrigger::Manual,
13692 FormatTarget::Ranges(ranges),
13693 window,
13694 cx,
13695 ))
13696 }
13697
13698 fn perform_format(
13699 &mut self,
13700 project: Entity<Project>,
13701 trigger: FormatTrigger,
13702 target: FormatTarget,
13703 window: &mut Window,
13704 cx: &mut Context<Self>,
13705 ) -> Task<Result<()>> {
13706 let buffer = self.buffer.clone();
13707 let (buffers, target) = match target {
13708 FormatTarget::Buffers => {
13709 let mut buffers = buffer.read(cx).all_buffers();
13710 if trigger == FormatTrigger::Save {
13711 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13712 }
13713 (buffers, LspFormatTarget::Buffers)
13714 }
13715 FormatTarget::Ranges(selection_ranges) => {
13716 let multi_buffer = buffer.read(cx);
13717 let snapshot = multi_buffer.read(cx);
13718 let mut buffers = HashSet::default();
13719 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13720 BTreeMap::new();
13721 for selection_range in selection_ranges {
13722 for (buffer, buffer_range, _) in
13723 snapshot.range_to_buffer_ranges(selection_range)
13724 {
13725 let buffer_id = buffer.remote_id();
13726 let start = buffer.anchor_before(buffer_range.start);
13727 let end = buffer.anchor_after(buffer_range.end);
13728 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13729 buffer_id_to_ranges
13730 .entry(buffer_id)
13731 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13732 .or_insert_with(|| vec![start..end]);
13733 }
13734 }
13735 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13736 }
13737 };
13738
13739 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13740 let format = project.update(cx, |project, cx| {
13741 project.format(buffers, target, true, trigger, cx)
13742 });
13743
13744 cx.spawn_in(window, async move |_, cx| {
13745 let transaction = futures::select_biased! {
13746 transaction = format.log_err().fuse() => transaction,
13747 () = timeout => {
13748 log::warn!("timed out waiting for formatting");
13749 None
13750 }
13751 };
13752
13753 buffer
13754 .update(cx, |buffer, cx| {
13755 if let Some(transaction) = transaction {
13756 if !buffer.is_singleton() {
13757 buffer.push_transaction(&transaction.0, cx);
13758 }
13759 }
13760 cx.notify();
13761 })
13762 .ok();
13763
13764 Ok(())
13765 })
13766 }
13767
13768 fn organize_imports(
13769 &mut self,
13770 _: &OrganizeImports,
13771 window: &mut Window,
13772 cx: &mut Context<Self>,
13773 ) -> Option<Task<Result<()>>> {
13774 let project = match &self.project {
13775 Some(project) => project.clone(),
13776 None => return None,
13777 };
13778 Some(self.perform_code_action_kind(
13779 project,
13780 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13781 window,
13782 cx,
13783 ))
13784 }
13785
13786 fn perform_code_action_kind(
13787 &mut self,
13788 project: Entity<Project>,
13789 kind: CodeActionKind,
13790 window: &mut Window,
13791 cx: &mut Context<Self>,
13792 ) -> Task<Result<()>> {
13793 let buffer = self.buffer.clone();
13794 let buffers = buffer.read(cx).all_buffers();
13795 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13796 let apply_action = project.update(cx, |project, cx| {
13797 project.apply_code_action_kind(buffers, kind, true, cx)
13798 });
13799 cx.spawn_in(window, async move |_, cx| {
13800 let transaction = futures::select_biased! {
13801 () = timeout => {
13802 log::warn!("timed out waiting for executing code action");
13803 None
13804 }
13805 transaction = apply_action.log_err().fuse() => transaction,
13806 };
13807 buffer
13808 .update(cx, |buffer, cx| {
13809 // check if we need this
13810 if let Some(transaction) = transaction {
13811 if !buffer.is_singleton() {
13812 buffer.push_transaction(&transaction.0, cx);
13813 }
13814 }
13815 cx.notify();
13816 })
13817 .ok();
13818 Ok(())
13819 })
13820 }
13821
13822 fn restart_language_server(
13823 &mut self,
13824 _: &RestartLanguageServer,
13825 _: &mut Window,
13826 cx: &mut Context<Self>,
13827 ) {
13828 if let Some(project) = self.project.clone() {
13829 self.buffer.update(cx, |multi_buffer, cx| {
13830 project.update(cx, |project, cx| {
13831 project.restart_language_servers_for_buffers(
13832 multi_buffer.all_buffers().into_iter().collect(),
13833 cx,
13834 );
13835 });
13836 })
13837 }
13838 }
13839
13840 fn cancel_language_server_work(
13841 workspace: &mut Workspace,
13842 _: &actions::CancelLanguageServerWork,
13843 _: &mut Window,
13844 cx: &mut Context<Workspace>,
13845 ) {
13846 let project = workspace.project();
13847 let buffers = workspace
13848 .active_item(cx)
13849 .and_then(|item| item.act_as::<Editor>(cx))
13850 .map_or(HashSet::default(), |editor| {
13851 editor.read(cx).buffer.read(cx).all_buffers()
13852 });
13853 project.update(cx, |project, cx| {
13854 project.cancel_language_server_work_for_buffers(buffers, cx);
13855 });
13856 }
13857
13858 fn show_character_palette(
13859 &mut self,
13860 _: &ShowCharacterPalette,
13861 window: &mut Window,
13862 _: &mut Context<Self>,
13863 ) {
13864 window.show_character_palette();
13865 }
13866
13867 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13868 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13869 let buffer = self.buffer.read(cx).snapshot(cx);
13870 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13871 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13872 let is_valid = buffer
13873 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13874 .any(|entry| {
13875 entry.diagnostic.is_primary
13876 && !entry.range.is_empty()
13877 && entry.range.start == primary_range_start
13878 && entry.diagnostic.message == active_diagnostics.primary_message
13879 });
13880
13881 if is_valid != active_diagnostics.is_valid {
13882 active_diagnostics.is_valid = is_valid;
13883 if is_valid {
13884 let mut new_styles = HashMap::default();
13885 for (block_id, diagnostic) in &active_diagnostics.blocks {
13886 new_styles.insert(
13887 *block_id,
13888 diagnostic_block_renderer(diagnostic.clone(), None, true),
13889 );
13890 }
13891 self.display_map.update(cx, |display_map, _cx| {
13892 display_map.replace_blocks(new_styles);
13893 });
13894 } else {
13895 self.dismiss_diagnostics(cx);
13896 }
13897 }
13898 }
13899 }
13900
13901 fn activate_diagnostics(
13902 &mut self,
13903 buffer_id: BufferId,
13904 group_id: usize,
13905 window: &mut Window,
13906 cx: &mut Context<Self>,
13907 ) {
13908 self.dismiss_diagnostics(cx);
13909 let snapshot = self.snapshot(window, cx);
13910 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13911 let buffer = self.buffer.read(cx).snapshot(cx);
13912
13913 let mut primary_range = None;
13914 let mut primary_message = None;
13915 let diagnostic_group = buffer
13916 .diagnostic_group(buffer_id, group_id)
13917 .filter_map(|entry| {
13918 let start = entry.range.start;
13919 let end = entry.range.end;
13920 if snapshot.is_line_folded(MultiBufferRow(start.row))
13921 && (start.row == end.row
13922 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13923 {
13924 return None;
13925 }
13926 if entry.diagnostic.is_primary {
13927 primary_range = Some(entry.range.clone());
13928 primary_message = Some(entry.diagnostic.message.clone());
13929 }
13930 Some(entry)
13931 })
13932 .collect::<Vec<_>>();
13933 let primary_range = primary_range?;
13934 let primary_message = primary_message?;
13935
13936 let blocks = display_map
13937 .insert_blocks(
13938 diagnostic_group.iter().map(|entry| {
13939 let diagnostic = entry.diagnostic.clone();
13940 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13941 BlockProperties {
13942 style: BlockStyle::Fixed,
13943 placement: BlockPlacement::Below(
13944 buffer.anchor_after(entry.range.start),
13945 ),
13946 height: message_height,
13947 render: diagnostic_block_renderer(diagnostic, None, true),
13948 priority: 0,
13949 }
13950 }),
13951 cx,
13952 )
13953 .into_iter()
13954 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13955 .collect();
13956
13957 Some(ActiveDiagnosticGroup {
13958 primary_range: buffer.anchor_before(primary_range.start)
13959 ..buffer.anchor_after(primary_range.end),
13960 primary_message,
13961 group_id,
13962 blocks,
13963 is_valid: true,
13964 })
13965 });
13966 }
13967
13968 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13969 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13970 self.display_map.update(cx, |display_map, cx| {
13971 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13972 });
13973 cx.notify();
13974 }
13975 }
13976
13977 /// Disable inline diagnostics rendering for this editor.
13978 pub fn disable_inline_diagnostics(&mut self) {
13979 self.inline_diagnostics_enabled = false;
13980 self.inline_diagnostics_update = Task::ready(());
13981 self.inline_diagnostics.clear();
13982 }
13983
13984 pub fn inline_diagnostics_enabled(&self) -> bool {
13985 self.inline_diagnostics_enabled
13986 }
13987
13988 pub fn show_inline_diagnostics(&self) -> bool {
13989 self.show_inline_diagnostics
13990 }
13991
13992 pub fn toggle_inline_diagnostics(
13993 &mut self,
13994 _: &ToggleInlineDiagnostics,
13995 window: &mut Window,
13996 cx: &mut Context<'_, Editor>,
13997 ) {
13998 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13999 self.refresh_inline_diagnostics(false, window, cx);
14000 }
14001
14002 fn refresh_inline_diagnostics(
14003 &mut self,
14004 debounce: bool,
14005 window: &mut Window,
14006 cx: &mut Context<Self>,
14007 ) {
14008 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
14009 self.inline_diagnostics_update = Task::ready(());
14010 self.inline_diagnostics.clear();
14011 return;
14012 }
14013
14014 let debounce_ms = ProjectSettings::get_global(cx)
14015 .diagnostics
14016 .inline
14017 .update_debounce_ms;
14018 let debounce = if debounce && debounce_ms > 0 {
14019 Some(Duration::from_millis(debounce_ms))
14020 } else {
14021 None
14022 };
14023 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
14024 if let Some(debounce) = debounce {
14025 cx.background_executor().timer(debounce).await;
14026 }
14027 let Some(snapshot) = editor
14028 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
14029 .ok()
14030 else {
14031 return;
14032 };
14033
14034 let new_inline_diagnostics = cx
14035 .background_spawn(async move {
14036 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
14037 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
14038 let message = diagnostic_entry
14039 .diagnostic
14040 .message
14041 .split_once('\n')
14042 .map(|(line, _)| line)
14043 .map(SharedString::new)
14044 .unwrap_or_else(|| {
14045 SharedString::from(diagnostic_entry.diagnostic.message)
14046 });
14047 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
14048 let (Ok(i) | Err(i)) = inline_diagnostics
14049 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
14050 inline_diagnostics.insert(
14051 i,
14052 (
14053 start_anchor,
14054 InlineDiagnostic {
14055 message,
14056 group_id: diagnostic_entry.diagnostic.group_id,
14057 start: diagnostic_entry.range.start.to_point(&snapshot),
14058 is_primary: diagnostic_entry.diagnostic.is_primary,
14059 severity: diagnostic_entry.diagnostic.severity,
14060 },
14061 ),
14062 );
14063 }
14064 inline_diagnostics
14065 })
14066 .await;
14067
14068 editor
14069 .update(cx, |editor, cx| {
14070 editor.inline_diagnostics = new_inline_diagnostics;
14071 cx.notify();
14072 })
14073 .ok();
14074 });
14075 }
14076
14077 pub fn set_selections_from_remote(
14078 &mut self,
14079 selections: Vec<Selection<Anchor>>,
14080 pending_selection: Option<Selection<Anchor>>,
14081 window: &mut Window,
14082 cx: &mut Context<Self>,
14083 ) {
14084 let old_cursor_position = self.selections.newest_anchor().head();
14085 self.selections.change_with(cx, |s| {
14086 s.select_anchors(selections);
14087 if let Some(pending_selection) = pending_selection {
14088 s.set_pending(pending_selection, SelectMode::Character);
14089 } else {
14090 s.clear_pending();
14091 }
14092 });
14093 self.selections_did_change(false, &old_cursor_position, true, window, cx);
14094 }
14095
14096 fn push_to_selection_history(&mut self) {
14097 self.selection_history.push(SelectionHistoryEntry {
14098 selections: self.selections.disjoint_anchors(),
14099 select_next_state: self.select_next_state.clone(),
14100 select_prev_state: self.select_prev_state.clone(),
14101 add_selections_state: self.add_selections_state.clone(),
14102 });
14103 }
14104
14105 pub fn transact(
14106 &mut self,
14107 window: &mut Window,
14108 cx: &mut Context<Self>,
14109 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
14110 ) -> Option<TransactionId> {
14111 self.start_transaction_at(Instant::now(), window, cx);
14112 update(self, window, cx);
14113 self.end_transaction_at(Instant::now(), cx)
14114 }
14115
14116 pub fn start_transaction_at(
14117 &mut self,
14118 now: Instant,
14119 window: &mut Window,
14120 cx: &mut Context<Self>,
14121 ) {
14122 self.end_selection(window, cx);
14123 if let Some(tx_id) = self
14124 .buffer
14125 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
14126 {
14127 self.selection_history
14128 .insert_transaction(tx_id, self.selections.disjoint_anchors());
14129 cx.emit(EditorEvent::TransactionBegun {
14130 transaction_id: tx_id,
14131 })
14132 }
14133 }
14134
14135 pub fn end_transaction_at(
14136 &mut self,
14137 now: Instant,
14138 cx: &mut Context<Self>,
14139 ) -> Option<TransactionId> {
14140 if let Some(transaction_id) = self
14141 .buffer
14142 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
14143 {
14144 if let Some((_, end_selections)) =
14145 self.selection_history.transaction_mut(transaction_id)
14146 {
14147 *end_selections = Some(self.selections.disjoint_anchors());
14148 } else {
14149 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
14150 }
14151
14152 cx.emit(EditorEvent::Edited { transaction_id });
14153 Some(transaction_id)
14154 } else {
14155 None
14156 }
14157 }
14158
14159 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
14160 if self.selection_mark_mode {
14161 self.change_selections(None, window, cx, |s| {
14162 s.move_with(|_, sel| {
14163 sel.collapse_to(sel.head(), SelectionGoal::None);
14164 });
14165 })
14166 }
14167 self.selection_mark_mode = true;
14168 cx.notify();
14169 }
14170
14171 pub fn swap_selection_ends(
14172 &mut self,
14173 _: &actions::SwapSelectionEnds,
14174 window: &mut Window,
14175 cx: &mut Context<Self>,
14176 ) {
14177 self.change_selections(None, window, cx, |s| {
14178 s.move_with(|_, sel| {
14179 if sel.start != sel.end {
14180 sel.reversed = !sel.reversed
14181 }
14182 });
14183 });
14184 self.request_autoscroll(Autoscroll::newest(), cx);
14185 cx.notify();
14186 }
14187
14188 pub fn toggle_fold(
14189 &mut self,
14190 _: &actions::ToggleFold,
14191 window: &mut Window,
14192 cx: &mut Context<Self>,
14193 ) {
14194 if self.is_singleton(cx) {
14195 let selection = self.selections.newest::<Point>(cx);
14196
14197 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14198 let range = if selection.is_empty() {
14199 let point = selection.head().to_display_point(&display_map);
14200 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14201 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14202 .to_point(&display_map);
14203 start..end
14204 } else {
14205 selection.range()
14206 };
14207 if display_map.folds_in_range(range).next().is_some() {
14208 self.unfold_lines(&Default::default(), window, cx)
14209 } else {
14210 self.fold(&Default::default(), window, cx)
14211 }
14212 } else {
14213 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14214 let buffer_ids: HashSet<_> = self
14215 .selections
14216 .disjoint_anchor_ranges()
14217 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14218 .collect();
14219
14220 let should_unfold = buffer_ids
14221 .iter()
14222 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
14223
14224 for buffer_id in buffer_ids {
14225 if should_unfold {
14226 self.unfold_buffer(buffer_id, cx);
14227 } else {
14228 self.fold_buffer(buffer_id, cx);
14229 }
14230 }
14231 }
14232 }
14233
14234 pub fn toggle_fold_recursive(
14235 &mut self,
14236 _: &actions::ToggleFoldRecursive,
14237 window: &mut Window,
14238 cx: &mut Context<Self>,
14239 ) {
14240 let selection = self.selections.newest::<Point>(cx);
14241
14242 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14243 let range = if selection.is_empty() {
14244 let point = selection.head().to_display_point(&display_map);
14245 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14246 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14247 .to_point(&display_map);
14248 start..end
14249 } else {
14250 selection.range()
14251 };
14252 if display_map.folds_in_range(range).next().is_some() {
14253 self.unfold_recursive(&Default::default(), window, cx)
14254 } else {
14255 self.fold_recursive(&Default::default(), window, cx)
14256 }
14257 }
14258
14259 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14260 if self.is_singleton(cx) {
14261 let mut to_fold = Vec::new();
14262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14263 let selections = self.selections.all_adjusted(cx);
14264
14265 for selection in selections {
14266 let range = selection.range().sorted();
14267 let buffer_start_row = range.start.row;
14268
14269 if range.start.row != range.end.row {
14270 let mut found = false;
14271 let mut row = range.start.row;
14272 while row <= range.end.row {
14273 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14274 {
14275 found = true;
14276 row = crease.range().end.row + 1;
14277 to_fold.push(crease);
14278 } else {
14279 row += 1
14280 }
14281 }
14282 if found {
14283 continue;
14284 }
14285 }
14286
14287 for row in (0..=range.start.row).rev() {
14288 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14289 if crease.range().end.row >= buffer_start_row {
14290 to_fold.push(crease);
14291 if row <= range.start.row {
14292 break;
14293 }
14294 }
14295 }
14296 }
14297 }
14298
14299 self.fold_creases(to_fold, true, window, cx);
14300 } else {
14301 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14302 let buffer_ids = self
14303 .selections
14304 .disjoint_anchor_ranges()
14305 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14306 .collect::<HashSet<_>>();
14307 for buffer_id in buffer_ids {
14308 self.fold_buffer(buffer_id, cx);
14309 }
14310 }
14311 }
14312
14313 fn fold_at_level(
14314 &mut self,
14315 fold_at: &FoldAtLevel,
14316 window: &mut Window,
14317 cx: &mut Context<Self>,
14318 ) {
14319 if !self.buffer.read(cx).is_singleton() {
14320 return;
14321 }
14322
14323 let fold_at_level = fold_at.0;
14324 let snapshot = self.buffer.read(cx).snapshot(cx);
14325 let mut to_fold = Vec::new();
14326 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14327
14328 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14329 while start_row < end_row {
14330 match self
14331 .snapshot(window, cx)
14332 .crease_for_buffer_row(MultiBufferRow(start_row))
14333 {
14334 Some(crease) => {
14335 let nested_start_row = crease.range().start.row + 1;
14336 let nested_end_row = crease.range().end.row;
14337
14338 if current_level < fold_at_level {
14339 stack.push((nested_start_row, nested_end_row, current_level + 1));
14340 } else if current_level == fold_at_level {
14341 to_fold.push(crease);
14342 }
14343
14344 start_row = nested_end_row + 1;
14345 }
14346 None => start_row += 1,
14347 }
14348 }
14349 }
14350
14351 self.fold_creases(to_fold, true, window, cx);
14352 }
14353
14354 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14355 if self.buffer.read(cx).is_singleton() {
14356 let mut fold_ranges = Vec::new();
14357 let snapshot = self.buffer.read(cx).snapshot(cx);
14358
14359 for row in 0..snapshot.max_row().0 {
14360 if let Some(foldable_range) = self
14361 .snapshot(window, cx)
14362 .crease_for_buffer_row(MultiBufferRow(row))
14363 {
14364 fold_ranges.push(foldable_range);
14365 }
14366 }
14367
14368 self.fold_creases(fold_ranges, true, window, cx);
14369 } else {
14370 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14371 editor
14372 .update_in(cx, |editor, _, cx| {
14373 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14374 editor.fold_buffer(buffer_id, cx);
14375 }
14376 })
14377 .ok();
14378 });
14379 }
14380 }
14381
14382 pub fn fold_function_bodies(
14383 &mut self,
14384 _: &actions::FoldFunctionBodies,
14385 window: &mut Window,
14386 cx: &mut Context<Self>,
14387 ) {
14388 let snapshot = self.buffer.read(cx).snapshot(cx);
14389
14390 let ranges = snapshot
14391 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14392 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14393 .collect::<Vec<_>>();
14394
14395 let creases = ranges
14396 .into_iter()
14397 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14398 .collect();
14399
14400 self.fold_creases(creases, true, window, cx);
14401 }
14402
14403 pub fn fold_recursive(
14404 &mut self,
14405 _: &actions::FoldRecursive,
14406 window: &mut Window,
14407 cx: &mut Context<Self>,
14408 ) {
14409 let mut to_fold = Vec::new();
14410 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14411 let selections = self.selections.all_adjusted(cx);
14412
14413 for selection in selections {
14414 let range = selection.range().sorted();
14415 let buffer_start_row = range.start.row;
14416
14417 if range.start.row != range.end.row {
14418 let mut found = false;
14419 for row in range.start.row..=range.end.row {
14420 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14421 found = true;
14422 to_fold.push(crease);
14423 }
14424 }
14425 if found {
14426 continue;
14427 }
14428 }
14429
14430 for row in (0..=range.start.row).rev() {
14431 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14432 if crease.range().end.row >= buffer_start_row {
14433 to_fold.push(crease);
14434 } else {
14435 break;
14436 }
14437 }
14438 }
14439 }
14440
14441 self.fold_creases(to_fold, true, window, cx);
14442 }
14443
14444 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14445 let buffer_row = fold_at.buffer_row;
14446 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14447
14448 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14449 let autoscroll = self
14450 .selections
14451 .all::<Point>(cx)
14452 .iter()
14453 .any(|selection| crease.range().overlaps(&selection.range()));
14454
14455 self.fold_creases(vec![crease], autoscroll, window, cx);
14456 }
14457 }
14458
14459 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14460 if self.is_singleton(cx) {
14461 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14462 let buffer = &display_map.buffer_snapshot;
14463 let selections = self.selections.all::<Point>(cx);
14464 let ranges = selections
14465 .iter()
14466 .map(|s| {
14467 let range = s.display_range(&display_map).sorted();
14468 let mut start = range.start.to_point(&display_map);
14469 let mut end = range.end.to_point(&display_map);
14470 start.column = 0;
14471 end.column = buffer.line_len(MultiBufferRow(end.row));
14472 start..end
14473 })
14474 .collect::<Vec<_>>();
14475
14476 self.unfold_ranges(&ranges, true, true, cx);
14477 } else {
14478 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14479 let buffer_ids = self
14480 .selections
14481 .disjoint_anchor_ranges()
14482 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14483 .collect::<HashSet<_>>();
14484 for buffer_id in buffer_ids {
14485 self.unfold_buffer(buffer_id, cx);
14486 }
14487 }
14488 }
14489
14490 pub fn unfold_recursive(
14491 &mut self,
14492 _: &UnfoldRecursive,
14493 _window: &mut Window,
14494 cx: &mut Context<Self>,
14495 ) {
14496 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14497 let selections = self.selections.all::<Point>(cx);
14498 let ranges = selections
14499 .iter()
14500 .map(|s| {
14501 let mut range = s.display_range(&display_map).sorted();
14502 *range.start.column_mut() = 0;
14503 *range.end.column_mut() = display_map.line_len(range.end.row());
14504 let start = range.start.to_point(&display_map);
14505 let end = range.end.to_point(&display_map);
14506 start..end
14507 })
14508 .collect::<Vec<_>>();
14509
14510 self.unfold_ranges(&ranges, true, true, cx);
14511 }
14512
14513 pub fn unfold_at(
14514 &mut self,
14515 unfold_at: &UnfoldAt,
14516 _window: &mut Window,
14517 cx: &mut Context<Self>,
14518 ) {
14519 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14520
14521 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14522 ..Point::new(
14523 unfold_at.buffer_row.0,
14524 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14525 );
14526
14527 let autoscroll = self
14528 .selections
14529 .all::<Point>(cx)
14530 .iter()
14531 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14532
14533 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14534 }
14535
14536 pub fn unfold_all(
14537 &mut self,
14538 _: &actions::UnfoldAll,
14539 _window: &mut Window,
14540 cx: &mut Context<Self>,
14541 ) {
14542 if self.buffer.read(cx).is_singleton() {
14543 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14544 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14545 } else {
14546 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14547 editor
14548 .update(cx, |editor, cx| {
14549 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14550 editor.unfold_buffer(buffer_id, cx);
14551 }
14552 })
14553 .ok();
14554 });
14555 }
14556 }
14557
14558 pub fn fold_selected_ranges(
14559 &mut self,
14560 _: &FoldSelectedRanges,
14561 window: &mut Window,
14562 cx: &mut Context<Self>,
14563 ) {
14564 let selections = self.selections.all::<Point>(cx);
14565 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14566 let line_mode = self.selections.line_mode;
14567 let ranges = selections
14568 .into_iter()
14569 .map(|s| {
14570 if line_mode {
14571 let start = Point::new(s.start.row, 0);
14572 let end = Point::new(
14573 s.end.row,
14574 display_map
14575 .buffer_snapshot
14576 .line_len(MultiBufferRow(s.end.row)),
14577 );
14578 Crease::simple(start..end, display_map.fold_placeholder.clone())
14579 } else {
14580 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14581 }
14582 })
14583 .collect::<Vec<_>>();
14584 self.fold_creases(ranges, true, window, cx);
14585 }
14586
14587 pub fn fold_ranges<T: ToOffset + Clone>(
14588 &mut self,
14589 ranges: Vec<Range<T>>,
14590 auto_scroll: bool,
14591 window: &mut Window,
14592 cx: &mut Context<Self>,
14593 ) {
14594 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14595 let ranges = ranges
14596 .into_iter()
14597 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14598 .collect::<Vec<_>>();
14599 self.fold_creases(ranges, auto_scroll, window, cx);
14600 }
14601
14602 pub fn fold_creases<T: ToOffset + Clone>(
14603 &mut self,
14604 creases: Vec<Crease<T>>,
14605 auto_scroll: bool,
14606 window: &mut Window,
14607 cx: &mut Context<Self>,
14608 ) {
14609 if creases.is_empty() {
14610 return;
14611 }
14612
14613 let mut buffers_affected = HashSet::default();
14614 let multi_buffer = self.buffer().read(cx);
14615 for crease in &creases {
14616 if let Some((_, buffer, _)) =
14617 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14618 {
14619 buffers_affected.insert(buffer.read(cx).remote_id());
14620 };
14621 }
14622
14623 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14624
14625 if auto_scroll {
14626 self.request_autoscroll(Autoscroll::fit(), cx);
14627 }
14628
14629 cx.notify();
14630
14631 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14632 // Clear diagnostics block when folding a range that contains it.
14633 let snapshot = self.snapshot(window, cx);
14634 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14635 drop(snapshot);
14636 self.active_diagnostics = Some(active_diagnostics);
14637 self.dismiss_diagnostics(cx);
14638 } else {
14639 self.active_diagnostics = Some(active_diagnostics);
14640 }
14641 }
14642
14643 self.scrollbar_marker_state.dirty = true;
14644 self.folds_did_change(cx);
14645 }
14646
14647 /// Removes any folds whose ranges intersect any of the given ranges.
14648 pub fn unfold_ranges<T: ToOffset + Clone>(
14649 &mut self,
14650 ranges: &[Range<T>],
14651 inclusive: bool,
14652 auto_scroll: bool,
14653 cx: &mut Context<Self>,
14654 ) {
14655 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14656 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14657 });
14658 self.folds_did_change(cx);
14659 }
14660
14661 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14662 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14663 return;
14664 }
14665 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14666 self.display_map.update(cx, |display_map, cx| {
14667 display_map.fold_buffers([buffer_id], cx)
14668 });
14669 cx.emit(EditorEvent::BufferFoldToggled {
14670 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14671 folded: true,
14672 });
14673 cx.notify();
14674 }
14675
14676 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14677 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14678 return;
14679 }
14680 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14681 self.display_map.update(cx, |display_map, cx| {
14682 display_map.unfold_buffers([buffer_id], cx);
14683 });
14684 cx.emit(EditorEvent::BufferFoldToggled {
14685 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14686 folded: false,
14687 });
14688 cx.notify();
14689 }
14690
14691 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14692 self.display_map.read(cx).is_buffer_folded(buffer)
14693 }
14694
14695 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14696 self.display_map.read(cx).folded_buffers()
14697 }
14698
14699 /// Removes any folds with the given ranges.
14700 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14701 &mut self,
14702 ranges: &[Range<T>],
14703 type_id: TypeId,
14704 auto_scroll: bool,
14705 cx: &mut Context<Self>,
14706 ) {
14707 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14708 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14709 });
14710 self.folds_did_change(cx);
14711 }
14712
14713 fn remove_folds_with<T: ToOffset + Clone>(
14714 &mut self,
14715 ranges: &[Range<T>],
14716 auto_scroll: bool,
14717 cx: &mut Context<Self>,
14718 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14719 ) {
14720 if ranges.is_empty() {
14721 return;
14722 }
14723
14724 let mut buffers_affected = HashSet::default();
14725 let multi_buffer = self.buffer().read(cx);
14726 for range in ranges {
14727 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14728 buffers_affected.insert(buffer.read(cx).remote_id());
14729 };
14730 }
14731
14732 self.display_map.update(cx, update);
14733
14734 if auto_scroll {
14735 self.request_autoscroll(Autoscroll::fit(), cx);
14736 }
14737
14738 cx.notify();
14739 self.scrollbar_marker_state.dirty = true;
14740 self.active_indent_guides_state.dirty = true;
14741 }
14742
14743 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14744 self.display_map.read(cx).fold_placeholder.clone()
14745 }
14746
14747 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14748 self.buffer.update(cx, |buffer, cx| {
14749 buffer.set_all_diff_hunks_expanded(cx);
14750 });
14751 }
14752
14753 pub fn expand_all_diff_hunks(
14754 &mut self,
14755 _: &ExpandAllDiffHunks,
14756 _window: &mut Window,
14757 cx: &mut Context<Self>,
14758 ) {
14759 self.buffer.update(cx, |buffer, cx| {
14760 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14761 });
14762 }
14763
14764 pub fn toggle_selected_diff_hunks(
14765 &mut self,
14766 _: &ToggleSelectedDiffHunks,
14767 _window: &mut Window,
14768 cx: &mut Context<Self>,
14769 ) {
14770 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14771 self.toggle_diff_hunks_in_ranges(ranges, cx);
14772 }
14773
14774 pub fn diff_hunks_in_ranges<'a>(
14775 &'a self,
14776 ranges: &'a [Range<Anchor>],
14777 buffer: &'a MultiBufferSnapshot,
14778 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14779 ranges.iter().flat_map(move |range| {
14780 let end_excerpt_id = range.end.excerpt_id;
14781 let range = range.to_point(buffer);
14782 let mut peek_end = range.end;
14783 if range.end.row < buffer.max_row().0 {
14784 peek_end = Point::new(range.end.row + 1, 0);
14785 }
14786 buffer
14787 .diff_hunks_in_range(range.start..peek_end)
14788 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14789 })
14790 }
14791
14792 pub fn has_stageable_diff_hunks_in_ranges(
14793 &self,
14794 ranges: &[Range<Anchor>],
14795 snapshot: &MultiBufferSnapshot,
14796 ) -> bool {
14797 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14798 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14799 }
14800
14801 pub fn toggle_staged_selected_diff_hunks(
14802 &mut self,
14803 _: &::git::ToggleStaged,
14804 _: &mut Window,
14805 cx: &mut Context<Self>,
14806 ) {
14807 let snapshot = self.buffer.read(cx).snapshot(cx);
14808 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14809 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14810 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14811 }
14812
14813 pub fn stage_and_next(
14814 &mut self,
14815 _: &::git::StageAndNext,
14816 window: &mut Window,
14817 cx: &mut Context<Self>,
14818 ) {
14819 self.do_stage_or_unstage_and_next(true, window, cx);
14820 }
14821
14822 pub fn unstage_and_next(
14823 &mut self,
14824 _: &::git::UnstageAndNext,
14825 window: &mut Window,
14826 cx: &mut Context<Self>,
14827 ) {
14828 self.do_stage_or_unstage_and_next(false, window, cx);
14829 }
14830
14831 pub fn stage_or_unstage_diff_hunks(
14832 &mut self,
14833 stage: bool,
14834 ranges: Vec<Range<Anchor>>,
14835 cx: &mut Context<Self>,
14836 ) {
14837 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14838 cx.spawn(async move |this, cx| {
14839 task.await?;
14840 this.update(cx, |this, cx| {
14841 let snapshot = this.buffer.read(cx).snapshot(cx);
14842 let chunk_by = this
14843 .diff_hunks_in_ranges(&ranges, &snapshot)
14844 .chunk_by(|hunk| hunk.buffer_id);
14845 for (buffer_id, hunks) in &chunk_by {
14846 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14847 }
14848 })
14849 })
14850 .detach_and_log_err(cx);
14851 }
14852
14853 fn save_buffers_for_ranges_if_needed(
14854 &mut self,
14855 ranges: &[Range<Anchor>],
14856 cx: &mut Context<'_, Editor>,
14857 ) -> Task<Result<()>> {
14858 let multibuffer = self.buffer.read(cx);
14859 let snapshot = multibuffer.read(cx);
14860 let buffer_ids: HashSet<_> = ranges
14861 .iter()
14862 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14863 .collect();
14864 drop(snapshot);
14865
14866 let mut buffers = HashSet::default();
14867 for buffer_id in buffer_ids {
14868 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14869 let buffer = buffer_entity.read(cx);
14870 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14871 {
14872 buffers.insert(buffer_entity);
14873 }
14874 }
14875 }
14876
14877 if let Some(project) = &self.project {
14878 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14879 } else {
14880 Task::ready(Ok(()))
14881 }
14882 }
14883
14884 fn do_stage_or_unstage_and_next(
14885 &mut self,
14886 stage: bool,
14887 window: &mut Window,
14888 cx: &mut Context<Self>,
14889 ) {
14890 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14891
14892 if ranges.iter().any(|range| range.start != range.end) {
14893 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14894 return;
14895 }
14896
14897 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14898 let snapshot = self.snapshot(window, cx);
14899 let position = self.selections.newest::<Point>(cx).head();
14900 let mut row = snapshot
14901 .buffer_snapshot
14902 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14903 .find(|hunk| hunk.row_range.start.0 > position.row)
14904 .map(|hunk| hunk.row_range.start);
14905
14906 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14907 // Outside of the project diff editor, wrap around to the beginning.
14908 if !all_diff_hunks_expanded {
14909 row = row.or_else(|| {
14910 snapshot
14911 .buffer_snapshot
14912 .diff_hunks_in_range(Point::zero()..position)
14913 .find(|hunk| hunk.row_range.end.0 < position.row)
14914 .map(|hunk| hunk.row_range.start)
14915 });
14916 }
14917
14918 if let Some(row) = row {
14919 let destination = Point::new(row.0, 0);
14920 let autoscroll = Autoscroll::center();
14921
14922 self.unfold_ranges(&[destination..destination], false, false, cx);
14923 self.change_selections(Some(autoscroll), window, cx, |s| {
14924 s.select_ranges([destination..destination]);
14925 });
14926 }
14927 }
14928
14929 fn do_stage_or_unstage(
14930 &self,
14931 stage: bool,
14932 buffer_id: BufferId,
14933 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14934 cx: &mut App,
14935 ) -> Option<()> {
14936 let project = self.project.as_ref()?;
14937 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14938 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14939 let buffer_snapshot = buffer.read(cx).snapshot();
14940 let file_exists = buffer_snapshot
14941 .file()
14942 .is_some_and(|file| file.disk_state().exists());
14943 diff.update(cx, |diff, cx| {
14944 diff.stage_or_unstage_hunks(
14945 stage,
14946 &hunks
14947 .map(|hunk| buffer_diff::DiffHunk {
14948 buffer_range: hunk.buffer_range,
14949 diff_base_byte_range: hunk.diff_base_byte_range,
14950 secondary_status: hunk.secondary_status,
14951 range: Point::zero()..Point::zero(), // unused
14952 })
14953 .collect::<Vec<_>>(),
14954 &buffer_snapshot,
14955 file_exists,
14956 cx,
14957 )
14958 });
14959 None
14960 }
14961
14962 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14963 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14964 self.buffer
14965 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14966 }
14967
14968 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14969 self.buffer.update(cx, |buffer, cx| {
14970 let ranges = vec![Anchor::min()..Anchor::max()];
14971 if !buffer.all_diff_hunks_expanded()
14972 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14973 {
14974 buffer.collapse_diff_hunks(ranges, cx);
14975 true
14976 } else {
14977 false
14978 }
14979 })
14980 }
14981
14982 fn toggle_diff_hunks_in_ranges(
14983 &mut self,
14984 ranges: Vec<Range<Anchor>>,
14985 cx: &mut Context<'_, Editor>,
14986 ) {
14987 self.buffer.update(cx, |buffer, cx| {
14988 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14989 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14990 })
14991 }
14992
14993 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14994 self.buffer.update(cx, |buffer, cx| {
14995 let snapshot = buffer.snapshot(cx);
14996 let excerpt_id = range.end.excerpt_id;
14997 let point_range = range.to_point(&snapshot);
14998 let expand = !buffer.single_hunk_is_expanded(range, cx);
14999 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
15000 })
15001 }
15002
15003 pub(crate) fn apply_all_diff_hunks(
15004 &mut self,
15005 _: &ApplyAllDiffHunks,
15006 window: &mut Window,
15007 cx: &mut Context<Self>,
15008 ) {
15009 let buffers = self.buffer.read(cx).all_buffers();
15010 for branch_buffer in buffers {
15011 branch_buffer.update(cx, |branch_buffer, cx| {
15012 branch_buffer.merge_into_base(Vec::new(), cx);
15013 });
15014 }
15015
15016 if let Some(project) = self.project.clone() {
15017 self.save(true, project, window, cx).detach_and_log_err(cx);
15018 }
15019 }
15020
15021 pub(crate) fn apply_selected_diff_hunks(
15022 &mut self,
15023 _: &ApplyDiffHunk,
15024 window: &mut Window,
15025 cx: &mut Context<Self>,
15026 ) {
15027 let snapshot = self.snapshot(window, cx);
15028 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
15029 let mut ranges_by_buffer = HashMap::default();
15030 self.transact(window, cx, |editor, _window, cx| {
15031 for hunk in hunks {
15032 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
15033 ranges_by_buffer
15034 .entry(buffer.clone())
15035 .or_insert_with(Vec::new)
15036 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
15037 }
15038 }
15039
15040 for (buffer, ranges) in ranges_by_buffer {
15041 buffer.update(cx, |buffer, cx| {
15042 buffer.merge_into_base(ranges, cx);
15043 });
15044 }
15045 });
15046
15047 if let Some(project) = self.project.clone() {
15048 self.save(true, project, window, cx).detach_and_log_err(cx);
15049 }
15050 }
15051
15052 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
15053 if hovered != self.gutter_hovered {
15054 self.gutter_hovered = hovered;
15055 cx.notify();
15056 }
15057 }
15058
15059 pub fn insert_blocks(
15060 &mut self,
15061 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
15062 autoscroll: Option<Autoscroll>,
15063 cx: &mut Context<Self>,
15064 ) -> Vec<CustomBlockId> {
15065 let blocks = self
15066 .display_map
15067 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
15068 if let Some(autoscroll) = autoscroll {
15069 self.request_autoscroll(autoscroll, cx);
15070 }
15071 cx.notify();
15072 blocks
15073 }
15074
15075 pub fn resize_blocks(
15076 &mut self,
15077 heights: HashMap<CustomBlockId, u32>,
15078 autoscroll: Option<Autoscroll>,
15079 cx: &mut Context<Self>,
15080 ) {
15081 self.display_map
15082 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
15083 if let Some(autoscroll) = autoscroll {
15084 self.request_autoscroll(autoscroll, cx);
15085 }
15086 cx.notify();
15087 }
15088
15089 pub fn replace_blocks(
15090 &mut self,
15091 renderers: HashMap<CustomBlockId, RenderBlock>,
15092 autoscroll: Option<Autoscroll>,
15093 cx: &mut Context<Self>,
15094 ) {
15095 self.display_map
15096 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
15097 if let Some(autoscroll) = autoscroll {
15098 self.request_autoscroll(autoscroll, cx);
15099 }
15100 cx.notify();
15101 }
15102
15103 pub fn remove_blocks(
15104 &mut self,
15105 block_ids: HashSet<CustomBlockId>,
15106 autoscroll: Option<Autoscroll>,
15107 cx: &mut Context<Self>,
15108 ) {
15109 self.display_map.update(cx, |display_map, cx| {
15110 display_map.remove_blocks(block_ids, cx)
15111 });
15112 if let Some(autoscroll) = autoscroll {
15113 self.request_autoscroll(autoscroll, cx);
15114 }
15115 cx.notify();
15116 }
15117
15118 pub fn row_for_block(
15119 &self,
15120 block_id: CustomBlockId,
15121 cx: &mut Context<Self>,
15122 ) -> Option<DisplayRow> {
15123 self.display_map
15124 .update(cx, |map, cx| map.row_for_block(block_id, cx))
15125 }
15126
15127 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
15128 self.focused_block = Some(focused_block);
15129 }
15130
15131 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
15132 self.focused_block.take()
15133 }
15134
15135 pub fn insert_creases(
15136 &mut self,
15137 creases: impl IntoIterator<Item = Crease<Anchor>>,
15138 cx: &mut Context<Self>,
15139 ) -> Vec<CreaseId> {
15140 self.display_map
15141 .update(cx, |map, cx| map.insert_creases(creases, cx))
15142 }
15143
15144 pub fn remove_creases(
15145 &mut self,
15146 ids: impl IntoIterator<Item = CreaseId>,
15147 cx: &mut Context<Self>,
15148 ) {
15149 self.display_map
15150 .update(cx, |map, cx| map.remove_creases(ids, cx));
15151 }
15152
15153 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
15154 self.display_map
15155 .update(cx, |map, cx| map.snapshot(cx))
15156 .longest_row()
15157 }
15158
15159 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
15160 self.display_map
15161 .update(cx, |map, cx| map.snapshot(cx))
15162 .max_point()
15163 }
15164
15165 pub fn text(&self, cx: &App) -> String {
15166 self.buffer.read(cx).read(cx).text()
15167 }
15168
15169 pub fn is_empty(&self, cx: &App) -> bool {
15170 self.buffer.read(cx).read(cx).is_empty()
15171 }
15172
15173 pub fn text_option(&self, cx: &App) -> Option<String> {
15174 let text = self.text(cx);
15175 let text = text.trim();
15176
15177 if text.is_empty() {
15178 return None;
15179 }
15180
15181 Some(text.to_string())
15182 }
15183
15184 pub fn set_text(
15185 &mut self,
15186 text: impl Into<Arc<str>>,
15187 window: &mut Window,
15188 cx: &mut Context<Self>,
15189 ) {
15190 self.transact(window, cx, |this, _, cx| {
15191 this.buffer
15192 .read(cx)
15193 .as_singleton()
15194 .expect("you can only call set_text on editors for singleton buffers")
15195 .update(cx, |buffer, cx| buffer.set_text(text, cx));
15196 });
15197 }
15198
15199 pub fn display_text(&self, cx: &mut App) -> String {
15200 self.display_map
15201 .update(cx, |map, cx| map.snapshot(cx))
15202 .text()
15203 }
15204
15205 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
15206 let mut wrap_guides = smallvec::smallvec![];
15207
15208 if self.show_wrap_guides == Some(false) {
15209 return wrap_guides;
15210 }
15211
15212 let settings = self.buffer.read(cx).language_settings(cx);
15213 if settings.show_wrap_guides {
15214 match self.soft_wrap_mode(cx) {
15215 SoftWrap::Column(soft_wrap) => {
15216 wrap_guides.push((soft_wrap as usize, true));
15217 }
15218 SoftWrap::Bounded(soft_wrap) => {
15219 wrap_guides.push((soft_wrap as usize, true));
15220 }
15221 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
15222 }
15223 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
15224 }
15225
15226 wrap_guides
15227 }
15228
15229 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
15230 let settings = self.buffer.read(cx).language_settings(cx);
15231 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
15232 match mode {
15233 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
15234 SoftWrap::None
15235 }
15236 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15237 language_settings::SoftWrap::PreferredLineLength => {
15238 SoftWrap::Column(settings.preferred_line_length)
15239 }
15240 language_settings::SoftWrap::Bounded => {
15241 SoftWrap::Bounded(settings.preferred_line_length)
15242 }
15243 }
15244 }
15245
15246 pub fn set_soft_wrap_mode(
15247 &mut self,
15248 mode: language_settings::SoftWrap,
15249
15250 cx: &mut Context<Self>,
15251 ) {
15252 self.soft_wrap_mode_override = Some(mode);
15253 cx.notify();
15254 }
15255
15256 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15257 self.hard_wrap = hard_wrap;
15258 cx.notify();
15259 }
15260
15261 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15262 self.text_style_refinement = Some(style);
15263 }
15264
15265 /// called by the Element so we know what style we were most recently rendered with.
15266 pub(crate) fn set_style(
15267 &mut self,
15268 style: EditorStyle,
15269 window: &mut Window,
15270 cx: &mut Context<Self>,
15271 ) {
15272 let rem_size = window.rem_size();
15273 self.display_map.update(cx, |map, cx| {
15274 map.set_font(
15275 style.text.font(),
15276 style.text.font_size.to_pixels(rem_size),
15277 cx,
15278 )
15279 });
15280 self.style = Some(style);
15281 }
15282
15283 pub fn style(&self) -> Option<&EditorStyle> {
15284 self.style.as_ref()
15285 }
15286
15287 // Called by the element. This method is not designed to be called outside of the editor
15288 // element's layout code because it does not notify when rewrapping is computed synchronously.
15289 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15290 self.display_map
15291 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15292 }
15293
15294 pub fn set_soft_wrap(&mut self) {
15295 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15296 }
15297
15298 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15299 if self.soft_wrap_mode_override.is_some() {
15300 self.soft_wrap_mode_override.take();
15301 } else {
15302 let soft_wrap = match self.soft_wrap_mode(cx) {
15303 SoftWrap::GitDiff => return,
15304 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15305 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15306 language_settings::SoftWrap::None
15307 }
15308 };
15309 self.soft_wrap_mode_override = Some(soft_wrap);
15310 }
15311 cx.notify();
15312 }
15313
15314 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15315 let Some(workspace) = self.workspace() else {
15316 return;
15317 };
15318 let fs = workspace.read(cx).app_state().fs.clone();
15319 let current_show = TabBarSettings::get_global(cx).show;
15320 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15321 setting.show = Some(!current_show);
15322 });
15323 }
15324
15325 pub fn toggle_indent_guides(
15326 &mut self,
15327 _: &ToggleIndentGuides,
15328 _: &mut Window,
15329 cx: &mut Context<Self>,
15330 ) {
15331 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15332 self.buffer
15333 .read(cx)
15334 .language_settings(cx)
15335 .indent_guides
15336 .enabled
15337 });
15338 self.show_indent_guides = Some(!currently_enabled);
15339 cx.notify();
15340 }
15341
15342 fn should_show_indent_guides(&self) -> Option<bool> {
15343 self.show_indent_guides
15344 }
15345
15346 pub fn toggle_line_numbers(
15347 &mut self,
15348 _: &ToggleLineNumbers,
15349 _: &mut Window,
15350 cx: &mut Context<Self>,
15351 ) {
15352 let mut editor_settings = EditorSettings::get_global(cx).clone();
15353 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15354 EditorSettings::override_global(editor_settings, cx);
15355 }
15356
15357 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15358 if let Some(show_line_numbers) = self.show_line_numbers {
15359 return show_line_numbers;
15360 }
15361 EditorSettings::get_global(cx).gutter.line_numbers
15362 }
15363
15364 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15365 self.use_relative_line_numbers
15366 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15367 }
15368
15369 pub fn toggle_relative_line_numbers(
15370 &mut self,
15371 _: &ToggleRelativeLineNumbers,
15372 _: &mut Window,
15373 cx: &mut Context<Self>,
15374 ) {
15375 let is_relative = self.should_use_relative_line_numbers(cx);
15376 self.set_relative_line_number(Some(!is_relative), cx)
15377 }
15378
15379 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15380 self.use_relative_line_numbers = is_relative;
15381 cx.notify();
15382 }
15383
15384 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15385 self.show_gutter = show_gutter;
15386 cx.notify();
15387 }
15388
15389 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15390 self.show_scrollbars = show_scrollbars;
15391 cx.notify();
15392 }
15393
15394 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15395 self.show_line_numbers = Some(show_line_numbers);
15396 cx.notify();
15397 }
15398
15399 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15400 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15401 cx.notify();
15402 }
15403
15404 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15405 self.show_code_actions = Some(show_code_actions);
15406 cx.notify();
15407 }
15408
15409 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15410 self.show_runnables = Some(show_runnables);
15411 cx.notify();
15412 }
15413
15414 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15415 self.show_breakpoints = Some(show_breakpoints);
15416 cx.notify();
15417 }
15418
15419 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15420 if self.display_map.read(cx).masked != masked {
15421 self.display_map.update(cx, |map, _| map.masked = masked);
15422 }
15423 cx.notify()
15424 }
15425
15426 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15427 self.show_wrap_guides = Some(show_wrap_guides);
15428 cx.notify();
15429 }
15430
15431 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15432 self.show_indent_guides = Some(show_indent_guides);
15433 cx.notify();
15434 }
15435
15436 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15437 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15438 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15439 if let Some(dir) = file.abs_path(cx).parent() {
15440 return Some(dir.to_owned());
15441 }
15442 }
15443
15444 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15445 return Some(project_path.path.to_path_buf());
15446 }
15447 }
15448
15449 None
15450 }
15451
15452 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15453 self.active_excerpt(cx)?
15454 .1
15455 .read(cx)
15456 .file()
15457 .and_then(|f| f.as_local())
15458 }
15459
15460 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15461 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15462 let buffer = buffer.read(cx);
15463 if let Some(project_path) = buffer.project_path(cx) {
15464 let project = self.project.as_ref()?.read(cx);
15465 project.absolute_path(&project_path, cx)
15466 } else {
15467 buffer
15468 .file()
15469 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15470 }
15471 })
15472 }
15473
15474 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15475 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15476 let project_path = buffer.read(cx).project_path(cx)?;
15477 let project = self.project.as_ref()?.read(cx);
15478 let entry = project.entry_for_path(&project_path, cx)?;
15479 let path = entry.path.to_path_buf();
15480 Some(path)
15481 })
15482 }
15483
15484 pub fn reveal_in_finder(
15485 &mut self,
15486 _: &RevealInFileManager,
15487 _window: &mut Window,
15488 cx: &mut Context<Self>,
15489 ) {
15490 if let Some(target) = self.target_file(cx) {
15491 cx.reveal_path(&target.abs_path(cx));
15492 }
15493 }
15494
15495 pub fn copy_path(
15496 &mut self,
15497 _: &zed_actions::workspace::CopyPath,
15498 _window: &mut Window,
15499 cx: &mut Context<Self>,
15500 ) {
15501 if let Some(path) = self.target_file_abs_path(cx) {
15502 if let Some(path) = path.to_str() {
15503 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15504 }
15505 }
15506 }
15507
15508 pub fn copy_relative_path(
15509 &mut self,
15510 _: &zed_actions::workspace::CopyRelativePath,
15511 _window: &mut Window,
15512 cx: &mut Context<Self>,
15513 ) {
15514 if let Some(path) = self.target_file_path(cx) {
15515 if let Some(path) = path.to_str() {
15516 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15517 }
15518 }
15519 }
15520
15521 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15522 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15523 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15524 } else {
15525 None
15526 }
15527 }
15528
15529 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15530 let _ = maybe!({
15531 let breakpoint_store = self.breakpoint_store.as_ref()?;
15532
15533 let Some((_, _, active_position)) =
15534 breakpoint_store.read(cx).active_position().cloned()
15535 else {
15536 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15537 return None;
15538 };
15539
15540 let snapshot = self
15541 .project
15542 .as_ref()?
15543 .read(cx)
15544 .buffer_for_id(active_position.buffer_id?, cx)?
15545 .read(cx)
15546 .snapshot();
15547
15548 for (id, ExcerptRange { context, .. }) in self
15549 .buffer
15550 .read(cx)
15551 .excerpts_for_buffer(active_position.buffer_id?, cx)
15552 {
15553 if context.start.cmp(&active_position, &snapshot).is_ge()
15554 || context.end.cmp(&active_position, &snapshot).is_lt()
15555 {
15556 continue;
15557 }
15558 let snapshot = self.buffer.read(cx).snapshot(cx);
15559 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15560
15561 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15562 self.go_to_line::<DebugCurrentRowHighlight>(
15563 multibuffer_anchor,
15564 Some(cx.theme().colors().editor_debugger_active_line_background),
15565 window,
15566 cx,
15567 );
15568
15569 cx.notify();
15570 }
15571
15572 Some(())
15573 });
15574 }
15575
15576 pub fn copy_file_name_without_extension(
15577 &mut self,
15578 _: &CopyFileNameWithoutExtension,
15579 _: &mut Window,
15580 cx: &mut Context<Self>,
15581 ) {
15582 if let Some(file) = self.target_file(cx) {
15583 if let Some(file_stem) = file.path().file_stem() {
15584 if let Some(name) = file_stem.to_str() {
15585 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15586 }
15587 }
15588 }
15589 }
15590
15591 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15592 if let Some(file) = self.target_file(cx) {
15593 if let Some(file_name) = file.path().file_name() {
15594 if let Some(name) = file_name.to_str() {
15595 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15596 }
15597 }
15598 }
15599 }
15600
15601 pub fn toggle_git_blame(
15602 &mut self,
15603 _: &::git::Blame,
15604 window: &mut Window,
15605 cx: &mut Context<Self>,
15606 ) {
15607 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15608
15609 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15610 self.start_git_blame(true, window, cx);
15611 }
15612
15613 cx.notify();
15614 }
15615
15616 pub fn toggle_git_blame_inline(
15617 &mut self,
15618 _: &ToggleGitBlameInline,
15619 window: &mut Window,
15620 cx: &mut Context<Self>,
15621 ) {
15622 self.toggle_git_blame_inline_internal(true, window, cx);
15623 cx.notify();
15624 }
15625
15626 pub fn git_blame_inline_enabled(&self) -> bool {
15627 self.git_blame_inline_enabled
15628 }
15629
15630 pub fn toggle_selection_menu(
15631 &mut self,
15632 _: &ToggleSelectionMenu,
15633 _: &mut Window,
15634 cx: &mut Context<Self>,
15635 ) {
15636 self.show_selection_menu = self
15637 .show_selection_menu
15638 .map(|show_selections_menu| !show_selections_menu)
15639 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15640
15641 cx.notify();
15642 }
15643
15644 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15645 self.show_selection_menu
15646 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15647 }
15648
15649 fn start_git_blame(
15650 &mut self,
15651 user_triggered: bool,
15652 window: &mut Window,
15653 cx: &mut Context<Self>,
15654 ) {
15655 if let Some(project) = self.project.as_ref() {
15656 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15657 return;
15658 };
15659
15660 if buffer.read(cx).file().is_none() {
15661 return;
15662 }
15663
15664 let focused = self.focus_handle(cx).contains_focused(window, cx);
15665
15666 let project = project.clone();
15667 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15668 self.blame_subscription =
15669 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15670 self.blame = Some(blame);
15671 }
15672 }
15673
15674 fn toggle_git_blame_inline_internal(
15675 &mut self,
15676 user_triggered: bool,
15677 window: &mut Window,
15678 cx: &mut Context<Self>,
15679 ) {
15680 if self.git_blame_inline_enabled {
15681 self.git_blame_inline_enabled = false;
15682 self.show_git_blame_inline = false;
15683 self.show_git_blame_inline_delay_task.take();
15684 } else {
15685 self.git_blame_inline_enabled = true;
15686 self.start_git_blame_inline(user_triggered, window, cx);
15687 }
15688
15689 cx.notify();
15690 }
15691
15692 fn start_git_blame_inline(
15693 &mut self,
15694 user_triggered: bool,
15695 window: &mut Window,
15696 cx: &mut Context<Self>,
15697 ) {
15698 self.start_git_blame(user_triggered, window, cx);
15699
15700 if ProjectSettings::get_global(cx)
15701 .git
15702 .inline_blame_delay()
15703 .is_some()
15704 {
15705 self.start_inline_blame_timer(window, cx);
15706 } else {
15707 self.show_git_blame_inline = true
15708 }
15709 }
15710
15711 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15712 self.blame.as_ref()
15713 }
15714
15715 pub fn show_git_blame_gutter(&self) -> bool {
15716 self.show_git_blame_gutter
15717 }
15718
15719 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15720 self.show_git_blame_gutter && self.has_blame_entries(cx)
15721 }
15722
15723 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15724 self.show_git_blame_inline
15725 && (self.focus_handle.is_focused(window)
15726 || self
15727 .git_blame_inline_tooltip
15728 .as_ref()
15729 .and_then(|t| t.upgrade())
15730 .is_some())
15731 && !self.newest_selection_head_on_empty_line(cx)
15732 && self.has_blame_entries(cx)
15733 }
15734
15735 fn has_blame_entries(&self, cx: &App) -> bool {
15736 self.blame()
15737 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15738 }
15739
15740 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15741 let cursor_anchor = self.selections.newest_anchor().head();
15742
15743 let snapshot = self.buffer.read(cx).snapshot(cx);
15744 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15745
15746 snapshot.line_len(buffer_row) == 0
15747 }
15748
15749 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15750 let buffer_and_selection = maybe!({
15751 let selection = self.selections.newest::<Point>(cx);
15752 let selection_range = selection.range();
15753
15754 let multi_buffer = self.buffer().read(cx);
15755 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15756 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15757
15758 let (buffer, range, _) = if selection.reversed {
15759 buffer_ranges.first()
15760 } else {
15761 buffer_ranges.last()
15762 }?;
15763
15764 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15765 ..text::ToPoint::to_point(&range.end, &buffer).row;
15766 Some((
15767 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15768 selection,
15769 ))
15770 });
15771
15772 let Some((buffer, selection)) = buffer_and_selection else {
15773 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15774 };
15775
15776 let Some(project) = self.project.as_ref() else {
15777 return Task::ready(Err(anyhow!("editor does not have project")));
15778 };
15779
15780 project.update(cx, |project, cx| {
15781 project.get_permalink_to_line(&buffer, selection, cx)
15782 })
15783 }
15784
15785 pub fn copy_permalink_to_line(
15786 &mut self,
15787 _: &CopyPermalinkToLine,
15788 window: &mut Window,
15789 cx: &mut Context<Self>,
15790 ) {
15791 let permalink_task = self.get_permalink_to_line(cx);
15792 let workspace = self.workspace();
15793
15794 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15795 Ok(permalink) => {
15796 cx.update(|_, cx| {
15797 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15798 })
15799 .ok();
15800 }
15801 Err(err) => {
15802 let message = format!("Failed to copy permalink: {err}");
15803
15804 Err::<(), anyhow::Error>(err).log_err();
15805
15806 if let Some(workspace) = workspace {
15807 workspace
15808 .update_in(cx, |workspace, _, cx| {
15809 struct CopyPermalinkToLine;
15810
15811 workspace.show_toast(
15812 Toast::new(
15813 NotificationId::unique::<CopyPermalinkToLine>(),
15814 message,
15815 ),
15816 cx,
15817 )
15818 })
15819 .ok();
15820 }
15821 }
15822 })
15823 .detach();
15824 }
15825
15826 pub fn copy_file_location(
15827 &mut self,
15828 _: &CopyFileLocation,
15829 _: &mut Window,
15830 cx: &mut Context<Self>,
15831 ) {
15832 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15833 if let Some(file) = self.target_file(cx) {
15834 if let Some(path) = file.path().to_str() {
15835 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15836 }
15837 }
15838 }
15839
15840 pub fn open_permalink_to_line(
15841 &mut self,
15842 _: &OpenPermalinkToLine,
15843 window: &mut Window,
15844 cx: &mut Context<Self>,
15845 ) {
15846 let permalink_task = self.get_permalink_to_line(cx);
15847 let workspace = self.workspace();
15848
15849 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15850 Ok(permalink) => {
15851 cx.update(|_, cx| {
15852 cx.open_url(permalink.as_ref());
15853 })
15854 .ok();
15855 }
15856 Err(err) => {
15857 let message = format!("Failed to open permalink: {err}");
15858
15859 Err::<(), anyhow::Error>(err).log_err();
15860
15861 if let Some(workspace) = workspace {
15862 workspace
15863 .update(cx, |workspace, cx| {
15864 struct OpenPermalinkToLine;
15865
15866 workspace.show_toast(
15867 Toast::new(
15868 NotificationId::unique::<OpenPermalinkToLine>(),
15869 message,
15870 ),
15871 cx,
15872 )
15873 })
15874 .ok();
15875 }
15876 }
15877 })
15878 .detach();
15879 }
15880
15881 pub fn insert_uuid_v4(
15882 &mut self,
15883 _: &InsertUuidV4,
15884 window: &mut Window,
15885 cx: &mut Context<Self>,
15886 ) {
15887 self.insert_uuid(UuidVersion::V4, window, cx);
15888 }
15889
15890 pub fn insert_uuid_v7(
15891 &mut self,
15892 _: &InsertUuidV7,
15893 window: &mut Window,
15894 cx: &mut Context<Self>,
15895 ) {
15896 self.insert_uuid(UuidVersion::V7, window, cx);
15897 }
15898
15899 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15900 self.transact(window, cx, |this, window, cx| {
15901 let edits = this
15902 .selections
15903 .all::<Point>(cx)
15904 .into_iter()
15905 .map(|selection| {
15906 let uuid = match version {
15907 UuidVersion::V4 => uuid::Uuid::new_v4(),
15908 UuidVersion::V7 => uuid::Uuid::now_v7(),
15909 };
15910
15911 (selection.range(), uuid.to_string())
15912 });
15913 this.edit(edits, cx);
15914 this.refresh_inline_completion(true, false, window, cx);
15915 });
15916 }
15917
15918 pub fn open_selections_in_multibuffer(
15919 &mut self,
15920 _: &OpenSelectionsInMultibuffer,
15921 window: &mut Window,
15922 cx: &mut Context<Self>,
15923 ) {
15924 let multibuffer = self.buffer.read(cx);
15925
15926 let Some(buffer) = multibuffer.as_singleton() else {
15927 return;
15928 };
15929
15930 let Some(workspace) = self.workspace() else {
15931 return;
15932 };
15933
15934 let locations = self
15935 .selections
15936 .disjoint_anchors()
15937 .iter()
15938 .map(|range| Location {
15939 buffer: buffer.clone(),
15940 range: range.start.text_anchor..range.end.text_anchor,
15941 })
15942 .collect::<Vec<_>>();
15943
15944 let title = multibuffer.title(cx).to_string();
15945
15946 cx.spawn_in(window, async move |_, cx| {
15947 workspace.update_in(cx, |workspace, window, cx| {
15948 Self::open_locations_in_multibuffer(
15949 workspace,
15950 locations,
15951 format!("Selections for '{title}'"),
15952 false,
15953 MultibufferSelectionMode::All,
15954 window,
15955 cx,
15956 );
15957 })
15958 })
15959 .detach();
15960 }
15961
15962 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15963 /// last highlight added will be used.
15964 ///
15965 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15966 pub fn highlight_rows<T: 'static>(
15967 &mut self,
15968 range: Range<Anchor>,
15969 color: Hsla,
15970 should_autoscroll: bool,
15971 cx: &mut Context<Self>,
15972 ) {
15973 let snapshot = self.buffer().read(cx).snapshot(cx);
15974 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15975 let ix = row_highlights.binary_search_by(|highlight| {
15976 Ordering::Equal
15977 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15978 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15979 });
15980
15981 if let Err(mut ix) = ix {
15982 let index = post_inc(&mut self.highlight_order);
15983
15984 // If this range intersects with the preceding highlight, then merge it with
15985 // the preceding highlight. Otherwise insert a new highlight.
15986 let mut merged = false;
15987 if ix > 0 {
15988 let prev_highlight = &mut row_highlights[ix - 1];
15989 if prev_highlight
15990 .range
15991 .end
15992 .cmp(&range.start, &snapshot)
15993 .is_ge()
15994 {
15995 ix -= 1;
15996 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15997 prev_highlight.range.end = range.end;
15998 }
15999 merged = true;
16000 prev_highlight.index = index;
16001 prev_highlight.color = color;
16002 prev_highlight.should_autoscroll = should_autoscroll;
16003 }
16004 }
16005
16006 if !merged {
16007 row_highlights.insert(
16008 ix,
16009 RowHighlight {
16010 range: range.clone(),
16011 index,
16012 color,
16013 should_autoscroll,
16014 },
16015 );
16016 }
16017
16018 // If any of the following highlights intersect with this one, merge them.
16019 while let Some(next_highlight) = row_highlights.get(ix + 1) {
16020 let highlight = &row_highlights[ix];
16021 if next_highlight
16022 .range
16023 .start
16024 .cmp(&highlight.range.end, &snapshot)
16025 .is_le()
16026 {
16027 if next_highlight
16028 .range
16029 .end
16030 .cmp(&highlight.range.end, &snapshot)
16031 .is_gt()
16032 {
16033 row_highlights[ix].range.end = next_highlight.range.end;
16034 }
16035 row_highlights.remove(ix + 1);
16036 } else {
16037 break;
16038 }
16039 }
16040 }
16041 }
16042
16043 /// Remove any highlighted row ranges of the given type that intersect the
16044 /// given ranges.
16045 pub fn remove_highlighted_rows<T: 'static>(
16046 &mut self,
16047 ranges_to_remove: Vec<Range<Anchor>>,
16048 cx: &mut Context<Self>,
16049 ) {
16050 let snapshot = self.buffer().read(cx).snapshot(cx);
16051 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
16052 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
16053 row_highlights.retain(|highlight| {
16054 while let Some(range_to_remove) = ranges_to_remove.peek() {
16055 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
16056 Ordering::Less | Ordering::Equal => {
16057 ranges_to_remove.next();
16058 }
16059 Ordering::Greater => {
16060 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
16061 Ordering::Less | Ordering::Equal => {
16062 return false;
16063 }
16064 Ordering::Greater => break,
16065 }
16066 }
16067 }
16068 }
16069
16070 true
16071 })
16072 }
16073
16074 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
16075 pub fn clear_row_highlights<T: 'static>(&mut self) {
16076 self.highlighted_rows.remove(&TypeId::of::<T>());
16077 }
16078
16079 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
16080 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
16081 self.highlighted_rows
16082 .get(&TypeId::of::<T>())
16083 .map_or(&[] as &[_], |vec| vec.as_slice())
16084 .iter()
16085 .map(|highlight| (highlight.range.clone(), highlight.color))
16086 }
16087
16088 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
16089 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
16090 /// Allows to ignore certain kinds of highlights.
16091 pub fn highlighted_display_rows(
16092 &self,
16093 window: &mut Window,
16094 cx: &mut App,
16095 ) -> BTreeMap<DisplayRow, LineHighlight> {
16096 let snapshot = self.snapshot(window, cx);
16097 let mut used_highlight_orders = HashMap::default();
16098 self.highlighted_rows
16099 .iter()
16100 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
16101 .fold(
16102 BTreeMap::<DisplayRow, LineHighlight>::new(),
16103 |mut unique_rows, highlight| {
16104 let start = highlight.range.start.to_display_point(&snapshot);
16105 let end = highlight.range.end.to_display_point(&snapshot);
16106 let start_row = start.row().0;
16107 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
16108 && end.column() == 0
16109 {
16110 end.row().0.saturating_sub(1)
16111 } else {
16112 end.row().0
16113 };
16114 for row in start_row..=end_row {
16115 let used_index =
16116 used_highlight_orders.entry(row).or_insert(highlight.index);
16117 if highlight.index >= *used_index {
16118 *used_index = highlight.index;
16119 unique_rows.insert(DisplayRow(row), highlight.color.into());
16120 }
16121 }
16122 unique_rows
16123 },
16124 )
16125 }
16126
16127 pub fn highlighted_display_row_for_autoscroll(
16128 &self,
16129 snapshot: &DisplaySnapshot,
16130 ) -> Option<DisplayRow> {
16131 self.highlighted_rows
16132 .values()
16133 .flat_map(|highlighted_rows| highlighted_rows.iter())
16134 .filter_map(|highlight| {
16135 if highlight.should_autoscroll {
16136 Some(highlight.range.start.to_display_point(snapshot).row())
16137 } else {
16138 None
16139 }
16140 })
16141 .min()
16142 }
16143
16144 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
16145 self.highlight_background::<SearchWithinRange>(
16146 ranges,
16147 |colors| colors.editor_document_highlight_read_background,
16148 cx,
16149 )
16150 }
16151
16152 pub fn set_breadcrumb_header(&mut self, new_header: String) {
16153 self.breadcrumb_header = Some(new_header);
16154 }
16155
16156 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
16157 self.clear_background_highlights::<SearchWithinRange>(cx);
16158 }
16159
16160 pub fn highlight_background<T: 'static>(
16161 &mut self,
16162 ranges: &[Range<Anchor>],
16163 color_fetcher: fn(&ThemeColors) -> Hsla,
16164 cx: &mut Context<Self>,
16165 ) {
16166 self.background_highlights
16167 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16168 self.scrollbar_marker_state.dirty = true;
16169 cx.notify();
16170 }
16171
16172 pub fn clear_background_highlights<T: 'static>(
16173 &mut self,
16174 cx: &mut Context<Self>,
16175 ) -> Option<BackgroundHighlight> {
16176 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
16177 if !text_highlights.1.is_empty() {
16178 self.scrollbar_marker_state.dirty = true;
16179 cx.notify();
16180 }
16181 Some(text_highlights)
16182 }
16183
16184 pub fn highlight_gutter<T: 'static>(
16185 &mut self,
16186 ranges: &[Range<Anchor>],
16187 color_fetcher: fn(&App) -> Hsla,
16188 cx: &mut Context<Self>,
16189 ) {
16190 self.gutter_highlights
16191 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16192 cx.notify();
16193 }
16194
16195 pub fn clear_gutter_highlights<T: 'static>(
16196 &mut self,
16197 cx: &mut Context<Self>,
16198 ) -> Option<GutterHighlight> {
16199 cx.notify();
16200 self.gutter_highlights.remove(&TypeId::of::<T>())
16201 }
16202
16203 #[cfg(feature = "test-support")]
16204 pub fn all_text_background_highlights(
16205 &self,
16206 window: &mut Window,
16207 cx: &mut Context<Self>,
16208 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16209 let snapshot = self.snapshot(window, cx);
16210 let buffer = &snapshot.buffer_snapshot;
16211 let start = buffer.anchor_before(0);
16212 let end = buffer.anchor_after(buffer.len());
16213 let theme = cx.theme().colors();
16214 self.background_highlights_in_range(start..end, &snapshot, theme)
16215 }
16216
16217 #[cfg(feature = "test-support")]
16218 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
16219 let snapshot = self.buffer().read(cx).snapshot(cx);
16220
16221 let highlights = self
16222 .background_highlights
16223 .get(&TypeId::of::<items::BufferSearchHighlights>());
16224
16225 if let Some((_color, ranges)) = highlights {
16226 ranges
16227 .iter()
16228 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
16229 .collect_vec()
16230 } else {
16231 vec![]
16232 }
16233 }
16234
16235 fn document_highlights_for_position<'a>(
16236 &'a self,
16237 position: Anchor,
16238 buffer: &'a MultiBufferSnapshot,
16239 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16240 let read_highlights = self
16241 .background_highlights
16242 .get(&TypeId::of::<DocumentHighlightRead>())
16243 .map(|h| &h.1);
16244 let write_highlights = self
16245 .background_highlights
16246 .get(&TypeId::of::<DocumentHighlightWrite>())
16247 .map(|h| &h.1);
16248 let left_position = position.bias_left(buffer);
16249 let right_position = position.bias_right(buffer);
16250 read_highlights
16251 .into_iter()
16252 .chain(write_highlights)
16253 .flat_map(move |ranges| {
16254 let start_ix = match ranges.binary_search_by(|probe| {
16255 let cmp = probe.end.cmp(&left_position, buffer);
16256 if cmp.is_ge() {
16257 Ordering::Greater
16258 } else {
16259 Ordering::Less
16260 }
16261 }) {
16262 Ok(i) | Err(i) => i,
16263 };
16264
16265 ranges[start_ix..]
16266 .iter()
16267 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16268 })
16269 }
16270
16271 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16272 self.background_highlights
16273 .get(&TypeId::of::<T>())
16274 .map_or(false, |(_, highlights)| !highlights.is_empty())
16275 }
16276
16277 pub fn background_highlights_in_range(
16278 &self,
16279 search_range: Range<Anchor>,
16280 display_snapshot: &DisplaySnapshot,
16281 theme: &ThemeColors,
16282 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16283 let mut results = Vec::new();
16284 for (color_fetcher, ranges) in self.background_highlights.values() {
16285 let color = color_fetcher(theme);
16286 let start_ix = match ranges.binary_search_by(|probe| {
16287 let cmp = probe
16288 .end
16289 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16290 if cmp.is_gt() {
16291 Ordering::Greater
16292 } else {
16293 Ordering::Less
16294 }
16295 }) {
16296 Ok(i) | Err(i) => i,
16297 };
16298 for range in &ranges[start_ix..] {
16299 if range
16300 .start
16301 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16302 .is_ge()
16303 {
16304 break;
16305 }
16306
16307 let start = range.start.to_display_point(display_snapshot);
16308 let end = range.end.to_display_point(display_snapshot);
16309 results.push((start..end, color))
16310 }
16311 }
16312 results
16313 }
16314
16315 pub fn background_highlight_row_ranges<T: 'static>(
16316 &self,
16317 search_range: Range<Anchor>,
16318 display_snapshot: &DisplaySnapshot,
16319 count: usize,
16320 ) -> Vec<RangeInclusive<DisplayPoint>> {
16321 let mut results = Vec::new();
16322 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16323 return vec![];
16324 };
16325
16326 let start_ix = match ranges.binary_search_by(|probe| {
16327 let cmp = probe
16328 .end
16329 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16330 if cmp.is_gt() {
16331 Ordering::Greater
16332 } else {
16333 Ordering::Less
16334 }
16335 }) {
16336 Ok(i) | Err(i) => i,
16337 };
16338 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16339 if let (Some(start_display), Some(end_display)) = (start, end) {
16340 results.push(
16341 start_display.to_display_point(display_snapshot)
16342 ..=end_display.to_display_point(display_snapshot),
16343 );
16344 }
16345 };
16346 let mut start_row: Option<Point> = None;
16347 let mut end_row: Option<Point> = None;
16348 if ranges.len() > count {
16349 return Vec::new();
16350 }
16351 for range in &ranges[start_ix..] {
16352 if range
16353 .start
16354 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16355 .is_ge()
16356 {
16357 break;
16358 }
16359 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16360 if let Some(current_row) = &end_row {
16361 if end.row == current_row.row {
16362 continue;
16363 }
16364 }
16365 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16366 if start_row.is_none() {
16367 assert_eq!(end_row, None);
16368 start_row = Some(start);
16369 end_row = Some(end);
16370 continue;
16371 }
16372 if let Some(current_end) = end_row.as_mut() {
16373 if start.row > current_end.row + 1 {
16374 push_region(start_row, end_row);
16375 start_row = Some(start);
16376 end_row = Some(end);
16377 } else {
16378 // Merge two hunks.
16379 *current_end = end;
16380 }
16381 } else {
16382 unreachable!();
16383 }
16384 }
16385 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16386 push_region(start_row, end_row);
16387 results
16388 }
16389
16390 pub fn gutter_highlights_in_range(
16391 &self,
16392 search_range: Range<Anchor>,
16393 display_snapshot: &DisplaySnapshot,
16394 cx: &App,
16395 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16396 let mut results = Vec::new();
16397 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16398 let color = color_fetcher(cx);
16399 let start_ix = match ranges.binary_search_by(|probe| {
16400 let cmp = probe
16401 .end
16402 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16403 if cmp.is_gt() {
16404 Ordering::Greater
16405 } else {
16406 Ordering::Less
16407 }
16408 }) {
16409 Ok(i) | Err(i) => i,
16410 };
16411 for range in &ranges[start_ix..] {
16412 if range
16413 .start
16414 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16415 .is_ge()
16416 {
16417 break;
16418 }
16419
16420 let start = range.start.to_display_point(display_snapshot);
16421 let end = range.end.to_display_point(display_snapshot);
16422 results.push((start..end, color))
16423 }
16424 }
16425 results
16426 }
16427
16428 /// Get the text ranges corresponding to the redaction query
16429 pub fn redacted_ranges(
16430 &self,
16431 search_range: Range<Anchor>,
16432 display_snapshot: &DisplaySnapshot,
16433 cx: &App,
16434 ) -> Vec<Range<DisplayPoint>> {
16435 display_snapshot
16436 .buffer_snapshot
16437 .redacted_ranges(search_range, |file| {
16438 if let Some(file) = file {
16439 file.is_private()
16440 && EditorSettings::get(
16441 Some(SettingsLocation {
16442 worktree_id: file.worktree_id(cx),
16443 path: file.path().as_ref(),
16444 }),
16445 cx,
16446 )
16447 .redact_private_values
16448 } else {
16449 false
16450 }
16451 })
16452 .map(|range| {
16453 range.start.to_display_point(display_snapshot)
16454 ..range.end.to_display_point(display_snapshot)
16455 })
16456 .collect()
16457 }
16458
16459 pub fn highlight_text<T: 'static>(
16460 &mut self,
16461 ranges: Vec<Range<Anchor>>,
16462 style: HighlightStyle,
16463 cx: &mut Context<Self>,
16464 ) {
16465 self.display_map.update(cx, |map, _| {
16466 map.highlight_text(TypeId::of::<T>(), ranges, style)
16467 });
16468 cx.notify();
16469 }
16470
16471 pub(crate) fn highlight_inlays<T: 'static>(
16472 &mut self,
16473 highlights: Vec<InlayHighlight>,
16474 style: HighlightStyle,
16475 cx: &mut Context<Self>,
16476 ) {
16477 self.display_map.update(cx, |map, _| {
16478 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16479 });
16480 cx.notify();
16481 }
16482
16483 pub fn text_highlights<'a, T: 'static>(
16484 &'a self,
16485 cx: &'a App,
16486 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16487 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16488 }
16489
16490 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16491 let cleared = self
16492 .display_map
16493 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16494 if cleared {
16495 cx.notify();
16496 }
16497 }
16498
16499 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16500 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16501 && self.focus_handle.is_focused(window)
16502 }
16503
16504 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16505 self.show_cursor_when_unfocused = is_enabled;
16506 cx.notify();
16507 }
16508
16509 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16510 cx.notify();
16511 }
16512
16513 fn on_buffer_event(
16514 &mut self,
16515 multibuffer: &Entity<MultiBuffer>,
16516 event: &multi_buffer::Event,
16517 window: &mut Window,
16518 cx: &mut Context<Self>,
16519 ) {
16520 match event {
16521 multi_buffer::Event::Edited {
16522 singleton_buffer_edited,
16523 edited_buffer: buffer_edited,
16524 } => {
16525 self.scrollbar_marker_state.dirty = true;
16526 self.active_indent_guides_state.dirty = true;
16527 self.refresh_active_diagnostics(cx);
16528 self.refresh_code_actions(window, cx);
16529 if self.has_active_inline_completion() {
16530 self.update_visible_inline_completion(window, cx);
16531 }
16532 if let Some(buffer) = buffer_edited {
16533 let buffer_id = buffer.read(cx).remote_id();
16534 if !self.registered_buffers.contains_key(&buffer_id) {
16535 if let Some(project) = self.project.as_ref() {
16536 project.update(cx, |project, cx| {
16537 self.registered_buffers.insert(
16538 buffer_id,
16539 project.register_buffer_with_language_servers(&buffer, cx),
16540 );
16541 })
16542 }
16543 }
16544 }
16545 cx.emit(EditorEvent::BufferEdited);
16546 cx.emit(SearchEvent::MatchesInvalidated);
16547 if *singleton_buffer_edited {
16548 if let Some(project) = &self.project {
16549 #[allow(clippy::mutable_key_type)]
16550 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16551 multibuffer
16552 .all_buffers()
16553 .into_iter()
16554 .filter_map(|buffer| {
16555 buffer.update(cx, |buffer, cx| {
16556 let language = buffer.language()?;
16557 let should_discard = project.update(cx, |project, cx| {
16558 project.is_local()
16559 && !project.has_language_servers_for(buffer, cx)
16560 });
16561 should_discard.not().then_some(language.clone())
16562 })
16563 })
16564 .collect::<HashSet<_>>()
16565 });
16566 if !languages_affected.is_empty() {
16567 self.refresh_inlay_hints(
16568 InlayHintRefreshReason::BufferEdited(languages_affected),
16569 cx,
16570 );
16571 }
16572 }
16573 }
16574
16575 let Some(project) = &self.project else { return };
16576 let (telemetry, is_via_ssh) = {
16577 let project = project.read(cx);
16578 let telemetry = project.client().telemetry().clone();
16579 let is_via_ssh = project.is_via_ssh();
16580 (telemetry, is_via_ssh)
16581 };
16582 refresh_linked_ranges(self, window, cx);
16583 telemetry.log_edit_event("editor", is_via_ssh);
16584 }
16585 multi_buffer::Event::ExcerptsAdded {
16586 buffer,
16587 predecessor,
16588 excerpts,
16589 } => {
16590 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16591 let buffer_id = buffer.read(cx).remote_id();
16592 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16593 if let Some(project) = &self.project {
16594 get_uncommitted_diff_for_buffer(
16595 project,
16596 [buffer.clone()],
16597 self.buffer.clone(),
16598 cx,
16599 )
16600 .detach();
16601 }
16602 }
16603 cx.emit(EditorEvent::ExcerptsAdded {
16604 buffer: buffer.clone(),
16605 predecessor: *predecessor,
16606 excerpts: excerpts.clone(),
16607 });
16608 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16609 }
16610 multi_buffer::Event::ExcerptsRemoved { ids } => {
16611 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16612 let buffer = self.buffer.read(cx);
16613 self.registered_buffers
16614 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16615 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16616 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16617 }
16618 multi_buffer::Event::ExcerptsEdited {
16619 excerpt_ids,
16620 buffer_ids,
16621 } => {
16622 self.display_map.update(cx, |map, cx| {
16623 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16624 });
16625 cx.emit(EditorEvent::ExcerptsEdited {
16626 ids: excerpt_ids.clone(),
16627 })
16628 }
16629 multi_buffer::Event::ExcerptsExpanded { ids } => {
16630 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16631 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16632 }
16633 multi_buffer::Event::Reparsed(buffer_id) => {
16634 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16635 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16636
16637 cx.emit(EditorEvent::Reparsed(*buffer_id));
16638 }
16639 multi_buffer::Event::DiffHunksToggled => {
16640 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16641 }
16642 multi_buffer::Event::LanguageChanged(buffer_id) => {
16643 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16644 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16645 cx.emit(EditorEvent::Reparsed(*buffer_id));
16646 cx.notify();
16647 }
16648 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16649 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16650 multi_buffer::Event::FileHandleChanged
16651 | multi_buffer::Event::Reloaded
16652 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16653 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16654 multi_buffer::Event::DiagnosticsUpdated => {
16655 self.refresh_active_diagnostics(cx);
16656 self.refresh_inline_diagnostics(true, window, cx);
16657 self.scrollbar_marker_state.dirty = true;
16658 cx.notify();
16659 }
16660 _ => {}
16661 };
16662 }
16663
16664 fn on_display_map_changed(
16665 &mut self,
16666 _: Entity<DisplayMap>,
16667 _: &mut Window,
16668 cx: &mut Context<Self>,
16669 ) {
16670 cx.notify();
16671 }
16672
16673 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16674 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16675 self.update_edit_prediction_settings(cx);
16676 self.refresh_inline_completion(true, false, window, cx);
16677 self.refresh_inlay_hints(
16678 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16679 self.selections.newest_anchor().head(),
16680 &self.buffer.read(cx).snapshot(cx),
16681 cx,
16682 )),
16683 cx,
16684 );
16685
16686 let old_cursor_shape = self.cursor_shape;
16687
16688 {
16689 let editor_settings = EditorSettings::get_global(cx);
16690 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16691 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16692 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16693 self.hide_mouse_while_typing = if !matches!(self.mode, EditorMode::SingleLine { .. }) {
16694 editor_settings.hide_mouse_while_typing.unwrap_or(true)
16695 } else {
16696 false
16697 };
16698
16699 if !self.hide_mouse_while_typing {
16700 self.mouse_cursor_hidden = false;
16701 }
16702 }
16703
16704 if old_cursor_shape != self.cursor_shape {
16705 cx.emit(EditorEvent::CursorShapeChanged);
16706 }
16707
16708 let project_settings = ProjectSettings::get_global(cx);
16709 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16710
16711 if self.mode == EditorMode::Full {
16712 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16713 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16714 if self.show_inline_diagnostics != show_inline_diagnostics {
16715 self.show_inline_diagnostics = show_inline_diagnostics;
16716 self.refresh_inline_diagnostics(false, window, cx);
16717 }
16718
16719 if self.git_blame_inline_enabled != inline_blame_enabled {
16720 self.toggle_git_blame_inline_internal(false, window, cx);
16721 }
16722 }
16723
16724 cx.notify();
16725 }
16726
16727 pub fn set_searchable(&mut self, searchable: bool) {
16728 self.searchable = searchable;
16729 }
16730
16731 pub fn searchable(&self) -> bool {
16732 self.searchable
16733 }
16734
16735 fn open_proposed_changes_editor(
16736 &mut self,
16737 _: &OpenProposedChangesEditor,
16738 window: &mut Window,
16739 cx: &mut Context<Self>,
16740 ) {
16741 let Some(workspace) = self.workspace() else {
16742 cx.propagate();
16743 return;
16744 };
16745
16746 let selections = self.selections.all::<usize>(cx);
16747 let multi_buffer = self.buffer.read(cx);
16748 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16749 let mut new_selections_by_buffer = HashMap::default();
16750 for selection in selections {
16751 for (buffer, range, _) in
16752 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16753 {
16754 let mut range = range.to_point(buffer);
16755 range.start.column = 0;
16756 range.end.column = buffer.line_len(range.end.row);
16757 new_selections_by_buffer
16758 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16759 .or_insert(Vec::new())
16760 .push(range)
16761 }
16762 }
16763
16764 let proposed_changes_buffers = new_selections_by_buffer
16765 .into_iter()
16766 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16767 .collect::<Vec<_>>();
16768 let proposed_changes_editor = cx.new(|cx| {
16769 ProposedChangesEditor::new(
16770 "Proposed changes",
16771 proposed_changes_buffers,
16772 self.project.clone(),
16773 window,
16774 cx,
16775 )
16776 });
16777
16778 window.defer(cx, move |window, cx| {
16779 workspace.update(cx, |workspace, cx| {
16780 workspace.active_pane().update(cx, |pane, cx| {
16781 pane.add_item(
16782 Box::new(proposed_changes_editor),
16783 true,
16784 true,
16785 None,
16786 window,
16787 cx,
16788 );
16789 });
16790 });
16791 });
16792 }
16793
16794 pub fn open_excerpts_in_split(
16795 &mut self,
16796 _: &OpenExcerptsSplit,
16797 window: &mut Window,
16798 cx: &mut Context<Self>,
16799 ) {
16800 self.open_excerpts_common(None, true, window, cx)
16801 }
16802
16803 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16804 self.open_excerpts_common(None, false, window, cx)
16805 }
16806
16807 fn open_excerpts_common(
16808 &mut self,
16809 jump_data: Option<JumpData>,
16810 split: bool,
16811 window: &mut Window,
16812 cx: &mut Context<Self>,
16813 ) {
16814 let Some(workspace) = self.workspace() else {
16815 cx.propagate();
16816 return;
16817 };
16818
16819 if self.buffer.read(cx).is_singleton() {
16820 cx.propagate();
16821 return;
16822 }
16823
16824 let mut new_selections_by_buffer = HashMap::default();
16825 match &jump_data {
16826 Some(JumpData::MultiBufferPoint {
16827 excerpt_id,
16828 position,
16829 anchor,
16830 line_offset_from_top,
16831 }) => {
16832 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16833 if let Some(buffer) = multi_buffer_snapshot
16834 .buffer_id_for_excerpt(*excerpt_id)
16835 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16836 {
16837 let buffer_snapshot = buffer.read(cx).snapshot();
16838 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16839 language::ToPoint::to_point(anchor, &buffer_snapshot)
16840 } else {
16841 buffer_snapshot.clip_point(*position, Bias::Left)
16842 };
16843 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16844 new_selections_by_buffer.insert(
16845 buffer,
16846 (
16847 vec![jump_to_offset..jump_to_offset],
16848 Some(*line_offset_from_top),
16849 ),
16850 );
16851 }
16852 }
16853 Some(JumpData::MultiBufferRow {
16854 row,
16855 line_offset_from_top,
16856 }) => {
16857 let point = MultiBufferPoint::new(row.0, 0);
16858 if let Some((buffer, buffer_point, _)) =
16859 self.buffer.read(cx).point_to_buffer_point(point, cx)
16860 {
16861 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16862 new_selections_by_buffer
16863 .entry(buffer)
16864 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16865 .0
16866 .push(buffer_offset..buffer_offset)
16867 }
16868 }
16869 None => {
16870 let selections = self.selections.all::<usize>(cx);
16871 let multi_buffer = self.buffer.read(cx);
16872 for selection in selections {
16873 for (snapshot, range, _, anchor) in multi_buffer
16874 .snapshot(cx)
16875 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16876 {
16877 if let Some(anchor) = anchor {
16878 // selection is in a deleted hunk
16879 let Some(buffer_id) = anchor.buffer_id else {
16880 continue;
16881 };
16882 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16883 continue;
16884 };
16885 let offset = text::ToOffset::to_offset(
16886 &anchor.text_anchor,
16887 &buffer_handle.read(cx).snapshot(),
16888 );
16889 let range = offset..offset;
16890 new_selections_by_buffer
16891 .entry(buffer_handle)
16892 .or_insert((Vec::new(), None))
16893 .0
16894 .push(range)
16895 } else {
16896 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16897 else {
16898 continue;
16899 };
16900 new_selections_by_buffer
16901 .entry(buffer_handle)
16902 .or_insert((Vec::new(), None))
16903 .0
16904 .push(range)
16905 }
16906 }
16907 }
16908 }
16909 }
16910
16911 if new_selections_by_buffer.is_empty() {
16912 return;
16913 }
16914
16915 // We defer the pane interaction because we ourselves are a workspace item
16916 // and activating a new item causes the pane to call a method on us reentrantly,
16917 // which panics if we're on the stack.
16918 window.defer(cx, move |window, cx| {
16919 workspace.update(cx, |workspace, cx| {
16920 let pane = if split {
16921 workspace.adjacent_pane(window, cx)
16922 } else {
16923 workspace.active_pane().clone()
16924 };
16925
16926 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16927 let editor = buffer
16928 .read(cx)
16929 .file()
16930 .is_none()
16931 .then(|| {
16932 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16933 // so `workspace.open_project_item` will never find them, always opening a new editor.
16934 // Instead, we try to activate the existing editor in the pane first.
16935 let (editor, pane_item_index) =
16936 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16937 let editor = item.downcast::<Editor>()?;
16938 let singleton_buffer =
16939 editor.read(cx).buffer().read(cx).as_singleton()?;
16940 if singleton_buffer == buffer {
16941 Some((editor, i))
16942 } else {
16943 None
16944 }
16945 })?;
16946 pane.update(cx, |pane, cx| {
16947 pane.activate_item(pane_item_index, true, true, window, cx)
16948 });
16949 Some(editor)
16950 })
16951 .flatten()
16952 .unwrap_or_else(|| {
16953 workspace.open_project_item::<Self>(
16954 pane.clone(),
16955 buffer,
16956 true,
16957 true,
16958 window,
16959 cx,
16960 )
16961 });
16962
16963 editor.update(cx, |editor, cx| {
16964 let autoscroll = match scroll_offset {
16965 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16966 None => Autoscroll::newest(),
16967 };
16968 let nav_history = editor.nav_history.take();
16969 editor.change_selections(Some(autoscroll), window, cx, |s| {
16970 s.select_ranges(ranges);
16971 });
16972 editor.nav_history = nav_history;
16973 });
16974 }
16975 })
16976 });
16977 }
16978
16979 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16980 let snapshot = self.buffer.read(cx).read(cx);
16981 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16982 Some(
16983 ranges
16984 .iter()
16985 .map(move |range| {
16986 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16987 })
16988 .collect(),
16989 )
16990 }
16991
16992 fn selection_replacement_ranges(
16993 &self,
16994 range: Range<OffsetUtf16>,
16995 cx: &mut App,
16996 ) -> Vec<Range<OffsetUtf16>> {
16997 let selections = self.selections.all::<OffsetUtf16>(cx);
16998 let newest_selection = selections
16999 .iter()
17000 .max_by_key(|selection| selection.id)
17001 .unwrap();
17002 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
17003 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
17004 let snapshot = self.buffer.read(cx).read(cx);
17005 selections
17006 .into_iter()
17007 .map(|mut selection| {
17008 selection.start.0 =
17009 (selection.start.0 as isize).saturating_add(start_delta) as usize;
17010 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
17011 snapshot.clip_offset_utf16(selection.start, Bias::Left)
17012 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
17013 })
17014 .collect()
17015 }
17016
17017 fn report_editor_event(
17018 &self,
17019 event_type: &'static str,
17020 file_extension: Option<String>,
17021 cx: &App,
17022 ) {
17023 if cfg!(any(test, feature = "test-support")) {
17024 return;
17025 }
17026
17027 let Some(project) = &self.project else { return };
17028
17029 // If None, we are in a file without an extension
17030 let file = self
17031 .buffer
17032 .read(cx)
17033 .as_singleton()
17034 .and_then(|b| b.read(cx).file());
17035 let file_extension = file_extension.or(file
17036 .as_ref()
17037 .and_then(|file| Path::new(file.file_name(cx)).extension())
17038 .and_then(|e| e.to_str())
17039 .map(|a| a.to_string()));
17040
17041 let vim_mode = cx
17042 .global::<SettingsStore>()
17043 .raw_user_settings()
17044 .get("vim_mode")
17045 == Some(&serde_json::Value::Bool(true));
17046
17047 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
17048 let copilot_enabled = edit_predictions_provider
17049 == language::language_settings::EditPredictionProvider::Copilot;
17050 let copilot_enabled_for_language = self
17051 .buffer
17052 .read(cx)
17053 .language_settings(cx)
17054 .show_edit_predictions;
17055
17056 let project = project.read(cx);
17057 telemetry::event!(
17058 event_type,
17059 file_extension,
17060 vim_mode,
17061 copilot_enabled,
17062 copilot_enabled_for_language,
17063 edit_predictions_provider,
17064 is_via_ssh = project.is_via_ssh(),
17065 );
17066 }
17067
17068 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
17069 /// with each line being an array of {text, highlight} objects.
17070 fn copy_highlight_json(
17071 &mut self,
17072 _: &CopyHighlightJson,
17073 window: &mut Window,
17074 cx: &mut Context<Self>,
17075 ) {
17076 #[derive(Serialize)]
17077 struct Chunk<'a> {
17078 text: String,
17079 highlight: Option<&'a str>,
17080 }
17081
17082 let snapshot = self.buffer.read(cx).snapshot(cx);
17083 let range = self
17084 .selected_text_range(false, window, cx)
17085 .and_then(|selection| {
17086 if selection.range.is_empty() {
17087 None
17088 } else {
17089 Some(selection.range)
17090 }
17091 })
17092 .unwrap_or_else(|| 0..snapshot.len());
17093
17094 let chunks = snapshot.chunks(range, true);
17095 let mut lines = Vec::new();
17096 let mut line: VecDeque<Chunk> = VecDeque::new();
17097
17098 let Some(style) = self.style.as_ref() else {
17099 return;
17100 };
17101
17102 for chunk in chunks {
17103 let highlight = chunk
17104 .syntax_highlight_id
17105 .and_then(|id| id.name(&style.syntax));
17106 let mut chunk_lines = chunk.text.split('\n').peekable();
17107 while let Some(text) = chunk_lines.next() {
17108 let mut merged_with_last_token = false;
17109 if let Some(last_token) = line.back_mut() {
17110 if last_token.highlight == highlight {
17111 last_token.text.push_str(text);
17112 merged_with_last_token = true;
17113 }
17114 }
17115
17116 if !merged_with_last_token {
17117 line.push_back(Chunk {
17118 text: text.into(),
17119 highlight,
17120 });
17121 }
17122
17123 if chunk_lines.peek().is_some() {
17124 if line.len() > 1 && line.front().unwrap().text.is_empty() {
17125 line.pop_front();
17126 }
17127 if line.len() > 1 && line.back().unwrap().text.is_empty() {
17128 line.pop_back();
17129 }
17130
17131 lines.push(mem::take(&mut line));
17132 }
17133 }
17134 }
17135
17136 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
17137 return;
17138 };
17139 cx.write_to_clipboard(ClipboardItem::new_string(lines));
17140 }
17141
17142 pub fn open_context_menu(
17143 &mut self,
17144 _: &OpenContextMenu,
17145 window: &mut Window,
17146 cx: &mut Context<Self>,
17147 ) {
17148 self.request_autoscroll(Autoscroll::newest(), cx);
17149 let position = self.selections.newest_display(cx).start;
17150 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
17151 }
17152
17153 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
17154 &self.inlay_hint_cache
17155 }
17156
17157 pub fn replay_insert_event(
17158 &mut self,
17159 text: &str,
17160 relative_utf16_range: Option<Range<isize>>,
17161 window: &mut Window,
17162 cx: &mut Context<Self>,
17163 ) {
17164 if !self.input_enabled {
17165 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17166 return;
17167 }
17168 if let Some(relative_utf16_range) = relative_utf16_range {
17169 let selections = self.selections.all::<OffsetUtf16>(cx);
17170 self.change_selections(None, window, cx, |s| {
17171 let new_ranges = selections.into_iter().map(|range| {
17172 let start = OffsetUtf16(
17173 range
17174 .head()
17175 .0
17176 .saturating_add_signed(relative_utf16_range.start),
17177 );
17178 let end = OffsetUtf16(
17179 range
17180 .head()
17181 .0
17182 .saturating_add_signed(relative_utf16_range.end),
17183 );
17184 start..end
17185 });
17186 s.select_ranges(new_ranges);
17187 });
17188 }
17189
17190 self.handle_input(text, window, cx);
17191 }
17192
17193 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
17194 let Some(provider) = self.semantics_provider.as_ref() else {
17195 return false;
17196 };
17197
17198 let mut supports = false;
17199 self.buffer().update(cx, |this, cx| {
17200 this.for_each_buffer(|buffer| {
17201 supports |= provider.supports_inlay_hints(buffer, cx);
17202 });
17203 });
17204
17205 supports
17206 }
17207
17208 pub fn is_focused(&self, window: &Window) -> bool {
17209 self.focus_handle.is_focused(window)
17210 }
17211
17212 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17213 cx.emit(EditorEvent::Focused);
17214
17215 if let Some(descendant) = self
17216 .last_focused_descendant
17217 .take()
17218 .and_then(|descendant| descendant.upgrade())
17219 {
17220 window.focus(&descendant);
17221 } else {
17222 if let Some(blame) = self.blame.as_ref() {
17223 blame.update(cx, GitBlame::focus)
17224 }
17225
17226 self.blink_manager.update(cx, BlinkManager::enable);
17227 self.show_cursor_names(window, cx);
17228 self.buffer.update(cx, |buffer, cx| {
17229 buffer.finalize_last_transaction(cx);
17230 if self.leader_peer_id.is_none() {
17231 buffer.set_active_selections(
17232 &self.selections.disjoint_anchors(),
17233 self.selections.line_mode,
17234 self.cursor_shape,
17235 cx,
17236 );
17237 }
17238 });
17239 }
17240 }
17241
17242 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17243 cx.emit(EditorEvent::FocusedIn)
17244 }
17245
17246 fn handle_focus_out(
17247 &mut self,
17248 event: FocusOutEvent,
17249 _window: &mut Window,
17250 cx: &mut Context<Self>,
17251 ) {
17252 if event.blurred != self.focus_handle {
17253 self.last_focused_descendant = Some(event.blurred);
17254 }
17255 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17256 }
17257
17258 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17259 self.blink_manager.update(cx, BlinkManager::disable);
17260 self.buffer
17261 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17262
17263 if let Some(blame) = self.blame.as_ref() {
17264 blame.update(cx, GitBlame::blur)
17265 }
17266 if !self.hover_state.focused(window, cx) {
17267 hide_hover(self, cx);
17268 }
17269 if !self
17270 .context_menu
17271 .borrow()
17272 .as_ref()
17273 .is_some_and(|context_menu| context_menu.focused(window, cx))
17274 {
17275 self.hide_context_menu(window, cx);
17276 }
17277 self.discard_inline_completion(false, cx);
17278 cx.emit(EditorEvent::Blurred);
17279 cx.notify();
17280 }
17281
17282 pub fn register_action<A: Action>(
17283 &mut self,
17284 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17285 ) -> Subscription {
17286 let id = self.next_editor_action_id.post_inc();
17287 let listener = Arc::new(listener);
17288 self.editor_actions.borrow_mut().insert(
17289 id,
17290 Box::new(move |window, _| {
17291 let listener = listener.clone();
17292 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17293 let action = action.downcast_ref().unwrap();
17294 if phase == DispatchPhase::Bubble {
17295 listener(action, window, cx)
17296 }
17297 })
17298 }),
17299 );
17300
17301 let editor_actions = self.editor_actions.clone();
17302 Subscription::new(move || {
17303 editor_actions.borrow_mut().remove(&id);
17304 })
17305 }
17306
17307 pub fn file_header_size(&self) -> u32 {
17308 FILE_HEADER_HEIGHT
17309 }
17310
17311 pub fn restore(
17312 &mut self,
17313 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17314 window: &mut Window,
17315 cx: &mut Context<Self>,
17316 ) {
17317 let workspace = self.workspace();
17318 let project = self.project.as_ref();
17319 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17320 let mut tasks = Vec::new();
17321 for (buffer_id, changes) in revert_changes {
17322 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17323 buffer.update(cx, |buffer, cx| {
17324 buffer.edit(
17325 changes
17326 .into_iter()
17327 .map(|(range, text)| (range, text.to_string())),
17328 None,
17329 cx,
17330 );
17331 });
17332
17333 if let Some(project) =
17334 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17335 {
17336 project.update(cx, |project, cx| {
17337 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17338 })
17339 }
17340 }
17341 }
17342 tasks
17343 });
17344 cx.spawn_in(window, async move |_, cx| {
17345 for (buffer, task) in save_tasks {
17346 let result = task.await;
17347 if result.is_err() {
17348 let Some(path) = buffer
17349 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17350 .ok()
17351 else {
17352 continue;
17353 };
17354 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17355 let Some(task) = cx
17356 .update_window_entity(&workspace, |workspace, window, cx| {
17357 workspace
17358 .open_path_preview(path, None, false, false, false, window, cx)
17359 })
17360 .ok()
17361 else {
17362 continue;
17363 };
17364 task.await.log_err();
17365 }
17366 }
17367 }
17368 })
17369 .detach();
17370 self.change_selections(None, window, cx, |selections| selections.refresh());
17371 }
17372
17373 pub fn to_pixel_point(
17374 &self,
17375 source: multi_buffer::Anchor,
17376 editor_snapshot: &EditorSnapshot,
17377 window: &mut Window,
17378 ) -> Option<gpui::Point<Pixels>> {
17379 let source_point = source.to_display_point(editor_snapshot);
17380 self.display_to_pixel_point(source_point, editor_snapshot, window)
17381 }
17382
17383 pub fn display_to_pixel_point(
17384 &self,
17385 source: DisplayPoint,
17386 editor_snapshot: &EditorSnapshot,
17387 window: &mut Window,
17388 ) -> Option<gpui::Point<Pixels>> {
17389 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17390 let text_layout_details = self.text_layout_details(window);
17391 let scroll_top = text_layout_details
17392 .scroll_anchor
17393 .scroll_position(editor_snapshot)
17394 .y;
17395
17396 if source.row().as_f32() < scroll_top.floor() {
17397 return None;
17398 }
17399 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17400 let source_y = line_height * (source.row().as_f32() - scroll_top);
17401 Some(gpui::Point::new(source_x, source_y))
17402 }
17403
17404 pub fn has_visible_completions_menu(&self) -> bool {
17405 !self.edit_prediction_preview_is_active()
17406 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17407 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17408 })
17409 }
17410
17411 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17412 self.addons
17413 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17414 }
17415
17416 pub fn unregister_addon<T: Addon>(&mut self) {
17417 self.addons.remove(&std::any::TypeId::of::<T>());
17418 }
17419
17420 pub fn addon<T: Addon>(&self) -> Option<&T> {
17421 let type_id = std::any::TypeId::of::<T>();
17422 self.addons
17423 .get(&type_id)
17424 .and_then(|item| item.to_any().downcast_ref::<T>())
17425 }
17426
17427 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17428 let text_layout_details = self.text_layout_details(window);
17429 let style = &text_layout_details.editor_style;
17430 let font_id = window.text_system().resolve_font(&style.text.font());
17431 let font_size = style.text.font_size.to_pixels(window.rem_size());
17432 let line_height = style.text.line_height_in_pixels(window.rem_size());
17433 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17434
17435 gpui::Size::new(em_width, line_height)
17436 }
17437
17438 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17439 self.load_diff_task.clone()
17440 }
17441
17442 fn read_metadata_from_db(
17443 &mut self,
17444 item_id: u64,
17445 workspace_id: WorkspaceId,
17446 window: &mut Window,
17447 cx: &mut Context<Editor>,
17448 ) {
17449 if self.is_singleton(cx)
17450 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17451 {
17452 let buffer_snapshot = OnceCell::new();
17453
17454 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17455 if !selections.is_empty() {
17456 let snapshot =
17457 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17458 self.change_selections(None, window, cx, |s| {
17459 s.select_ranges(selections.into_iter().map(|(start, end)| {
17460 snapshot.clip_offset(start, Bias::Left)
17461 ..snapshot.clip_offset(end, Bias::Right)
17462 }));
17463 });
17464 }
17465 };
17466
17467 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17468 if !folds.is_empty() {
17469 let snapshot =
17470 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17471 self.fold_ranges(
17472 folds
17473 .into_iter()
17474 .map(|(start, end)| {
17475 snapshot.clip_offset(start, Bias::Left)
17476 ..snapshot.clip_offset(end, Bias::Right)
17477 })
17478 .collect(),
17479 false,
17480 window,
17481 cx,
17482 );
17483 }
17484 }
17485 }
17486
17487 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17488 }
17489}
17490
17491fn insert_extra_newline_brackets(
17492 buffer: &MultiBufferSnapshot,
17493 range: Range<usize>,
17494 language: &language::LanguageScope,
17495) -> bool {
17496 let leading_whitespace_len = buffer
17497 .reversed_chars_at(range.start)
17498 .take_while(|c| c.is_whitespace() && *c != '\n')
17499 .map(|c| c.len_utf8())
17500 .sum::<usize>();
17501 let trailing_whitespace_len = buffer
17502 .chars_at(range.end)
17503 .take_while(|c| c.is_whitespace() && *c != '\n')
17504 .map(|c| c.len_utf8())
17505 .sum::<usize>();
17506 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17507
17508 language.brackets().any(|(pair, enabled)| {
17509 let pair_start = pair.start.trim_end();
17510 let pair_end = pair.end.trim_start();
17511
17512 enabled
17513 && pair.newline
17514 && buffer.contains_str_at(range.end, pair_end)
17515 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17516 })
17517}
17518
17519fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17520 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17521 [(buffer, range, _)] => (*buffer, range.clone()),
17522 _ => return false,
17523 };
17524 let pair = {
17525 let mut result: Option<BracketMatch> = None;
17526
17527 for pair in buffer
17528 .all_bracket_ranges(range.clone())
17529 .filter(move |pair| {
17530 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17531 })
17532 {
17533 let len = pair.close_range.end - pair.open_range.start;
17534
17535 if let Some(existing) = &result {
17536 let existing_len = existing.close_range.end - existing.open_range.start;
17537 if len > existing_len {
17538 continue;
17539 }
17540 }
17541
17542 result = Some(pair);
17543 }
17544
17545 result
17546 };
17547 let Some(pair) = pair else {
17548 return false;
17549 };
17550 pair.newline_only
17551 && buffer
17552 .chars_for_range(pair.open_range.end..range.start)
17553 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17554 .all(|c| c.is_whitespace() && c != '\n')
17555}
17556
17557fn get_uncommitted_diff_for_buffer(
17558 project: &Entity<Project>,
17559 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17560 buffer: Entity<MultiBuffer>,
17561 cx: &mut App,
17562) -> Task<()> {
17563 let mut tasks = Vec::new();
17564 project.update(cx, |project, cx| {
17565 for buffer in buffers {
17566 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17567 }
17568 });
17569 cx.spawn(async move |cx| {
17570 let diffs = future::join_all(tasks).await;
17571 buffer
17572 .update(cx, |buffer, cx| {
17573 for diff in diffs.into_iter().flatten() {
17574 buffer.add_diff(diff, cx);
17575 }
17576 })
17577 .ok();
17578 })
17579}
17580
17581fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17582 let tab_size = tab_size.get() as usize;
17583 let mut width = offset;
17584
17585 for ch in text.chars() {
17586 width += if ch == '\t' {
17587 tab_size - (width % tab_size)
17588 } else {
17589 1
17590 };
17591 }
17592
17593 width - offset
17594}
17595
17596#[cfg(test)]
17597mod tests {
17598 use super::*;
17599
17600 #[test]
17601 fn test_string_size_with_expanded_tabs() {
17602 let nz = |val| NonZeroU32::new(val).unwrap();
17603 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17604 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17605 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17606 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17607 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17608 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17609 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17610 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17611 }
17612}
17613
17614/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17615struct WordBreakingTokenizer<'a> {
17616 input: &'a str,
17617}
17618
17619impl<'a> WordBreakingTokenizer<'a> {
17620 fn new(input: &'a str) -> Self {
17621 Self { input }
17622 }
17623}
17624
17625fn is_char_ideographic(ch: char) -> bool {
17626 use unicode_script::Script::*;
17627 use unicode_script::UnicodeScript;
17628 matches!(ch.script(), Han | Tangut | Yi)
17629}
17630
17631fn is_grapheme_ideographic(text: &str) -> bool {
17632 text.chars().any(is_char_ideographic)
17633}
17634
17635fn is_grapheme_whitespace(text: &str) -> bool {
17636 text.chars().any(|x| x.is_whitespace())
17637}
17638
17639fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17640 text.chars().next().map_or(false, |ch| {
17641 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17642 })
17643}
17644
17645#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17646enum WordBreakToken<'a> {
17647 Word { token: &'a str, grapheme_len: usize },
17648 InlineWhitespace { token: &'a str, grapheme_len: usize },
17649 Newline,
17650}
17651
17652impl<'a> Iterator for WordBreakingTokenizer<'a> {
17653 /// Yields a span, the count of graphemes in the token, and whether it was
17654 /// whitespace. Note that it also breaks at word boundaries.
17655 type Item = WordBreakToken<'a>;
17656
17657 fn next(&mut self) -> Option<Self::Item> {
17658 use unicode_segmentation::UnicodeSegmentation;
17659 if self.input.is_empty() {
17660 return None;
17661 }
17662
17663 let mut iter = self.input.graphemes(true).peekable();
17664 let mut offset = 0;
17665 let mut grapheme_len = 0;
17666 if let Some(first_grapheme) = iter.next() {
17667 let is_newline = first_grapheme == "\n";
17668 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17669 offset += first_grapheme.len();
17670 grapheme_len += 1;
17671 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17672 if let Some(grapheme) = iter.peek().copied() {
17673 if should_stay_with_preceding_ideograph(grapheme) {
17674 offset += grapheme.len();
17675 grapheme_len += 1;
17676 }
17677 }
17678 } else {
17679 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17680 let mut next_word_bound = words.peek().copied();
17681 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17682 next_word_bound = words.next();
17683 }
17684 while let Some(grapheme) = iter.peek().copied() {
17685 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17686 break;
17687 };
17688 if is_grapheme_whitespace(grapheme) != is_whitespace
17689 || (grapheme == "\n") != is_newline
17690 {
17691 break;
17692 };
17693 offset += grapheme.len();
17694 grapheme_len += 1;
17695 iter.next();
17696 }
17697 }
17698 let token = &self.input[..offset];
17699 self.input = &self.input[offset..];
17700 if token == "\n" {
17701 Some(WordBreakToken::Newline)
17702 } else if is_whitespace {
17703 Some(WordBreakToken::InlineWhitespace {
17704 token,
17705 grapheme_len,
17706 })
17707 } else {
17708 Some(WordBreakToken::Word {
17709 token,
17710 grapheme_len,
17711 })
17712 }
17713 } else {
17714 None
17715 }
17716 }
17717}
17718
17719#[test]
17720fn test_word_breaking_tokenizer() {
17721 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17722 ("", &[]),
17723 (" ", &[whitespace(" ", 2)]),
17724 ("Ʒ", &[word("Ʒ", 1)]),
17725 ("Ǽ", &[word("Ǽ", 1)]),
17726 ("⋑", &[word("⋑", 1)]),
17727 ("⋑⋑", &[word("⋑⋑", 2)]),
17728 (
17729 "原理,进而",
17730 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17731 ),
17732 (
17733 "hello world",
17734 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17735 ),
17736 (
17737 "hello, world",
17738 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17739 ),
17740 (
17741 " hello world",
17742 &[
17743 whitespace(" ", 2),
17744 word("hello", 5),
17745 whitespace(" ", 1),
17746 word("world", 5),
17747 ],
17748 ),
17749 (
17750 "这是什么 \n 钢笔",
17751 &[
17752 word("这", 1),
17753 word("是", 1),
17754 word("什", 1),
17755 word("么", 1),
17756 whitespace(" ", 1),
17757 newline(),
17758 whitespace(" ", 1),
17759 word("钢", 1),
17760 word("笔", 1),
17761 ],
17762 ),
17763 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17764 ];
17765
17766 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17767 WordBreakToken::Word {
17768 token,
17769 grapheme_len,
17770 }
17771 }
17772
17773 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17774 WordBreakToken::InlineWhitespace {
17775 token,
17776 grapheme_len,
17777 }
17778 }
17779
17780 fn newline() -> WordBreakToken<'static> {
17781 WordBreakToken::Newline
17782 }
17783
17784 for (input, result) in tests {
17785 assert_eq!(
17786 WordBreakingTokenizer::new(input)
17787 .collect::<Vec<_>>()
17788 .as_slice(),
17789 *result,
17790 );
17791 }
17792}
17793
17794fn wrap_with_prefix(
17795 line_prefix: String,
17796 unwrapped_text: String,
17797 wrap_column: usize,
17798 tab_size: NonZeroU32,
17799 preserve_existing_whitespace: bool,
17800) -> String {
17801 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17802 let mut wrapped_text = String::new();
17803 let mut current_line = line_prefix.clone();
17804
17805 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17806 let mut current_line_len = line_prefix_len;
17807 let mut in_whitespace = false;
17808 for token in tokenizer {
17809 let have_preceding_whitespace = in_whitespace;
17810 match token {
17811 WordBreakToken::Word {
17812 token,
17813 grapheme_len,
17814 } => {
17815 in_whitespace = false;
17816 if current_line_len + grapheme_len > wrap_column
17817 && current_line_len != line_prefix_len
17818 {
17819 wrapped_text.push_str(current_line.trim_end());
17820 wrapped_text.push('\n');
17821 current_line.truncate(line_prefix.len());
17822 current_line_len = line_prefix_len;
17823 }
17824 current_line.push_str(token);
17825 current_line_len += grapheme_len;
17826 }
17827 WordBreakToken::InlineWhitespace {
17828 mut token,
17829 mut grapheme_len,
17830 } => {
17831 in_whitespace = true;
17832 if have_preceding_whitespace && !preserve_existing_whitespace {
17833 continue;
17834 }
17835 if !preserve_existing_whitespace {
17836 token = " ";
17837 grapheme_len = 1;
17838 }
17839 if current_line_len + grapheme_len > wrap_column {
17840 wrapped_text.push_str(current_line.trim_end());
17841 wrapped_text.push('\n');
17842 current_line.truncate(line_prefix.len());
17843 current_line_len = line_prefix_len;
17844 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17845 current_line.push_str(token);
17846 current_line_len += grapheme_len;
17847 }
17848 }
17849 WordBreakToken::Newline => {
17850 in_whitespace = true;
17851 if preserve_existing_whitespace {
17852 wrapped_text.push_str(current_line.trim_end());
17853 wrapped_text.push('\n');
17854 current_line.truncate(line_prefix.len());
17855 current_line_len = line_prefix_len;
17856 } else if have_preceding_whitespace {
17857 continue;
17858 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17859 {
17860 wrapped_text.push_str(current_line.trim_end());
17861 wrapped_text.push('\n');
17862 current_line.truncate(line_prefix.len());
17863 current_line_len = line_prefix_len;
17864 } else if current_line_len != line_prefix_len {
17865 current_line.push(' ');
17866 current_line_len += 1;
17867 }
17868 }
17869 }
17870 }
17871
17872 if !current_line.is_empty() {
17873 wrapped_text.push_str(¤t_line);
17874 }
17875 wrapped_text
17876}
17877
17878#[test]
17879fn test_wrap_with_prefix() {
17880 assert_eq!(
17881 wrap_with_prefix(
17882 "# ".to_string(),
17883 "abcdefg".to_string(),
17884 4,
17885 NonZeroU32::new(4).unwrap(),
17886 false,
17887 ),
17888 "# abcdefg"
17889 );
17890 assert_eq!(
17891 wrap_with_prefix(
17892 "".to_string(),
17893 "\thello world".to_string(),
17894 8,
17895 NonZeroU32::new(4).unwrap(),
17896 false,
17897 ),
17898 "hello\nworld"
17899 );
17900 assert_eq!(
17901 wrap_with_prefix(
17902 "// ".to_string(),
17903 "xx \nyy zz aa bb cc".to_string(),
17904 12,
17905 NonZeroU32::new(4).unwrap(),
17906 false,
17907 ),
17908 "// xx yy zz\n// aa bb cc"
17909 );
17910 assert_eq!(
17911 wrap_with_prefix(
17912 String::new(),
17913 "这是什么 \n 钢笔".to_string(),
17914 3,
17915 NonZeroU32::new(4).unwrap(),
17916 false,
17917 ),
17918 "这是什\n么 钢\n笔"
17919 );
17920}
17921
17922pub trait CollaborationHub {
17923 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17924 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17925 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17926}
17927
17928impl CollaborationHub for Entity<Project> {
17929 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17930 self.read(cx).collaborators()
17931 }
17932
17933 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17934 self.read(cx).user_store().read(cx).participant_indices()
17935 }
17936
17937 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17938 let this = self.read(cx);
17939 let user_ids = this.collaborators().values().map(|c| c.user_id);
17940 this.user_store().read_with(cx, |user_store, cx| {
17941 user_store.participant_names(user_ids, cx)
17942 })
17943 }
17944}
17945
17946pub trait SemanticsProvider {
17947 fn hover(
17948 &self,
17949 buffer: &Entity<Buffer>,
17950 position: text::Anchor,
17951 cx: &mut App,
17952 ) -> Option<Task<Vec<project::Hover>>>;
17953
17954 fn inlay_hints(
17955 &self,
17956 buffer_handle: Entity<Buffer>,
17957 range: Range<text::Anchor>,
17958 cx: &mut App,
17959 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17960
17961 fn resolve_inlay_hint(
17962 &self,
17963 hint: InlayHint,
17964 buffer_handle: Entity<Buffer>,
17965 server_id: LanguageServerId,
17966 cx: &mut App,
17967 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17968
17969 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17970
17971 fn document_highlights(
17972 &self,
17973 buffer: &Entity<Buffer>,
17974 position: text::Anchor,
17975 cx: &mut App,
17976 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17977
17978 fn definitions(
17979 &self,
17980 buffer: &Entity<Buffer>,
17981 position: text::Anchor,
17982 kind: GotoDefinitionKind,
17983 cx: &mut App,
17984 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17985
17986 fn range_for_rename(
17987 &self,
17988 buffer: &Entity<Buffer>,
17989 position: text::Anchor,
17990 cx: &mut App,
17991 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17992
17993 fn perform_rename(
17994 &self,
17995 buffer: &Entity<Buffer>,
17996 position: text::Anchor,
17997 new_name: String,
17998 cx: &mut App,
17999 ) -> Option<Task<Result<ProjectTransaction>>>;
18000}
18001
18002pub trait CompletionProvider {
18003 fn completions(
18004 &self,
18005 excerpt_id: ExcerptId,
18006 buffer: &Entity<Buffer>,
18007 buffer_position: text::Anchor,
18008 trigger: CompletionContext,
18009 window: &mut Window,
18010 cx: &mut Context<Editor>,
18011 ) -> Task<Result<Option<Vec<Completion>>>>;
18012
18013 fn resolve_completions(
18014 &self,
18015 buffer: Entity<Buffer>,
18016 completion_indices: Vec<usize>,
18017 completions: Rc<RefCell<Box<[Completion]>>>,
18018 cx: &mut Context<Editor>,
18019 ) -> Task<Result<bool>>;
18020
18021 fn apply_additional_edits_for_completion(
18022 &self,
18023 _buffer: Entity<Buffer>,
18024 _completions: Rc<RefCell<Box<[Completion]>>>,
18025 _completion_index: usize,
18026 _push_to_history: bool,
18027 _cx: &mut Context<Editor>,
18028 ) -> Task<Result<Option<language::Transaction>>> {
18029 Task::ready(Ok(None))
18030 }
18031
18032 fn is_completion_trigger(
18033 &self,
18034 buffer: &Entity<Buffer>,
18035 position: language::Anchor,
18036 text: &str,
18037 trigger_in_words: bool,
18038 cx: &mut Context<Editor>,
18039 ) -> bool;
18040
18041 fn sort_completions(&self) -> bool {
18042 true
18043 }
18044}
18045
18046pub trait CodeActionProvider {
18047 fn id(&self) -> Arc<str>;
18048
18049 fn code_actions(
18050 &self,
18051 buffer: &Entity<Buffer>,
18052 range: Range<text::Anchor>,
18053 window: &mut Window,
18054 cx: &mut App,
18055 ) -> Task<Result<Vec<CodeAction>>>;
18056
18057 fn apply_code_action(
18058 &self,
18059 buffer_handle: Entity<Buffer>,
18060 action: CodeAction,
18061 excerpt_id: ExcerptId,
18062 push_to_history: bool,
18063 window: &mut Window,
18064 cx: &mut App,
18065 ) -> Task<Result<ProjectTransaction>>;
18066}
18067
18068impl CodeActionProvider for Entity<Project> {
18069 fn id(&self) -> Arc<str> {
18070 "project".into()
18071 }
18072
18073 fn code_actions(
18074 &self,
18075 buffer: &Entity<Buffer>,
18076 range: Range<text::Anchor>,
18077 _window: &mut Window,
18078 cx: &mut App,
18079 ) -> Task<Result<Vec<CodeAction>>> {
18080 self.update(cx, |project, cx| {
18081 let code_lens = project.code_lens(buffer, range.clone(), cx);
18082 let code_actions = project.code_actions(buffer, range, None, cx);
18083 cx.background_spawn(async move {
18084 let (code_lens, code_actions) = join(code_lens, code_actions).await;
18085 Ok(code_lens
18086 .context("code lens fetch")?
18087 .into_iter()
18088 .chain(code_actions.context("code action fetch")?)
18089 .collect())
18090 })
18091 })
18092 }
18093
18094 fn apply_code_action(
18095 &self,
18096 buffer_handle: Entity<Buffer>,
18097 action: CodeAction,
18098 _excerpt_id: ExcerptId,
18099 push_to_history: bool,
18100 _window: &mut Window,
18101 cx: &mut App,
18102 ) -> Task<Result<ProjectTransaction>> {
18103 self.update(cx, |project, cx| {
18104 project.apply_code_action(buffer_handle, action, push_to_history, cx)
18105 })
18106 }
18107}
18108
18109fn snippet_completions(
18110 project: &Project,
18111 buffer: &Entity<Buffer>,
18112 buffer_position: text::Anchor,
18113 cx: &mut App,
18114) -> Task<Result<Vec<Completion>>> {
18115 let language = buffer.read(cx).language_at(buffer_position);
18116 let language_name = language.as_ref().map(|language| language.lsp_id());
18117 let snippet_store = project.snippets().read(cx);
18118 let snippets = snippet_store.snippets_for(language_name, cx);
18119
18120 if snippets.is_empty() {
18121 return Task::ready(Ok(vec![]));
18122 }
18123 let snapshot = buffer.read(cx).text_snapshot();
18124 let chars: String = snapshot
18125 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
18126 .collect();
18127
18128 let scope = language.map(|language| language.default_scope());
18129 let executor = cx.background_executor().clone();
18130
18131 cx.background_spawn(async move {
18132 let classifier = CharClassifier::new(scope).for_completion(true);
18133 let mut last_word = chars
18134 .chars()
18135 .take_while(|c| classifier.is_word(*c))
18136 .collect::<String>();
18137 last_word = last_word.chars().rev().collect();
18138
18139 if last_word.is_empty() {
18140 return Ok(vec![]);
18141 }
18142
18143 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
18144 let to_lsp = |point: &text::Anchor| {
18145 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
18146 point_to_lsp(end)
18147 };
18148 let lsp_end = to_lsp(&buffer_position);
18149
18150 let candidates = snippets
18151 .iter()
18152 .enumerate()
18153 .flat_map(|(ix, snippet)| {
18154 snippet
18155 .prefix
18156 .iter()
18157 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
18158 })
18159 .collect::<Vec<StringMatchCandidate>>();
18160
18161 let mut matches = fuzzy::match_strings(
18162 &candidates,
18163 &last_word,
18164 last_word.chars().any(|c| c.is_uppercase()),
18165 100,
18166 &Default::default(),
18167 executor,
18168 )
18169 .await;
18170
18171 // Remove all candidates where the query's start does not match the start of any word in the candidate
18172 if let Some(query_start) = last_word.chars().next() {
18173 matches.retain(|string_match| {
18174 split_words(&string_match.string).any(|word| {
18175 // Check that the first codepoint of the word as lowercase matches the first
18176 // codepoint of the query as lowercase
18177 word.chars()
18178 .flat_map(|codepoint| codepoint.to_lowercase())
18179 .zip(query_start.to_lowercase())
18180 .all(|(word_cp, query_cp)| word_cp == query_cp)
18181 })
18182 });
18183 }
18184
18185 let matched_strings = matches
18186 .into_iter()
18187 .map(|m| m.string)
18188 .collect::<HashSet<_>>();
18189
18190 let result: Vec<Completion> = snippets
18191 .into_iter()
18192 .filter_map(|snippet| {
18193 let matching_prefix = snippet
18194 .prefix
18195 .iter()
18196 .find(|prefix| matched_strings.contains(*prefix))?;
18197 let start = as_offset - last_word.len();
18198 let start = snapshot.anchor_before(start);
18199 let range = start..buffer_position;
18200 let lsp_start = to_lsp(&start);
18201 let lsp_range = lsp::Range {
18202 start: lsp_start,
18203 end: lsp_end,
18204 };
18205 Some(Completion {
18206 old_range: range,
18207 new_text: snippet.body.clone(),
18208 source: CompletionSource::Lsp {
18209 server_id: LanguageServerId(usize::MAX),
18210 resolved: true,
18211 lsp_completion: Box::new(lsp::CompletionItem {
18212 label: snippet.prefix.first().unwrap().clone(),
18213 kind: Some(CompletionItemKind::SNIPPET),
18214 label_details: snippet.description.as_ref().map(|description| {
18215 lsp::CompletionItemLabelDetails {
18216 detail: Some(description.clone()),
18217 description: None,
18218 }
18219 }),
18220 insert_text_format: Some(InsertTextFormat::SNIPPET),
18221 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18222 lsp::InsertReplaceEdit {
18223 new_text: snippet.body.clone(),
18224 insert: lsp_range,
18225 replace: lsp_range,
18226 },
18227 )),
18228 filter_text: Some(snippet.body.clone()),
18229 sort_text: Some(char::MAX.to_string()),
18230 ..lsp::CompletionItem::default()
18231 }),
18232 lsp_defaults: None,
18233 },
18234 label: CodeLabel {
18235 text: matching_prefix.clone(),
18236 runs: Vec::new(),
18237 filter_range: 0..matching_prefix.len(),
18238 },
18239 icon_path: None,
18240 documentation: snippet
18241 .description
18242 .clone()
18243 .map(|description| CompletionDocumentation::SingleLine(description.into())),
18244 confirm: None,
18245 })
18246 })
18247 .collect();
18248
18249 Ok(result)
18250 })
18251}
18252
18253impl CompletionProvider for Entity<Project> {
18254 fn completions(
18255 &self,
18256 _excerpt_id: ExcerptId,
18257 buffer: &Entity<Buffer>,
18258 buffer_position: text::Anchor,
18259 options: CompletionContext,
18260 _window: &mut Window,
18261 cx: &mut Context<Editor>,
18262 ) -> Task<Result<Option<Vec<Completion>>>> {
18263 self.update(cx, |project, cx| {
18264 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18265 let project_completions = project.completions(buffer, buffer_position, options, cx);
18266 cx.background_spawn(async move {
18267 let snippets_completions = snippets.await?;
18268 match project_completions.await? {
18269 Some(mut completions) => {
18270 completions.extend(snippets_completions);
18271 Ok(Some(completions))
18272 }
18273 None => {
18274 if snippets_completions.is_empty() {
18275 Ok(None)
18276 } else {
18277 Ok(Some(snippets_completions))
18278 }
18279 }
18280 }
18281 })
18282 })
18283 }
18284
18285 fn resolve_completions(
18286 &self,
18287 buffer: Entity<Buffer>,
18288 completion_indices: Vec<usize>,
18289 completions: Rc<RefCell<Box<[Completion]>>>,
18290 cx: &mut Context<Editor>,
18291 ) -> Task<Result<bool>> {
18292 self.update(cx, |project, cx| {
18293 project.lsp_store().update(cx, |lsp_store, cx| {
18294 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18295 })
18296 })
18297 }
18298
18299 fn apply_additional_edits_for_completion(
18300 &self,
18301 buffer: Entity<Buffer>,
18302 completions: Rc<RefCell<Box<[Completion]>>>,
18303 completion_index: usize,
18304 push_to_history: bool,
18305 cx: &mut Context<Editor>,
18306 ) -> Task<Result<Option<language::Transaction>>> {
18307 self.update(cx, |project, cx| {
18308 project.lsp_store().update(cx, |lsp_store, cx| {
18309 lsp_store.apply_additional_edits_for_completion(
18310 buffer,
18311 completions,
18312 completion_index,
18313 push_to_history,
18314 cx,
18315 )
18316 })
18317 })
18318 }
18319
18320 fn is_completion_trigger(
18321 &self,
18322 buffer: &Entity<Buffer>,
18323 position: language::Anchor,
18324 text: &str,
18325 trigger_in_words: bool,
18326 cx: &mut Context<Editor>,
18327 ) -> bool {
18328 let mut chars = text.chars();
18329 let char = if let Some(char) = chars.next() {
18330 char
18331 } else {
18332 return false;
18333 };
18334 if chars.next().is_some() {
18335 return false;
18336 }
18337
18338 let buffer = buffer.read(cx);
18339 let snapshot = buffer.snapshot();
18340 if !snapshot.settings_at(position, cx).show_completions_on_input {
18341 return false;
18342 }
18343 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18344 if trigger_in_words && classifier.is_word(char) {
18345 return true;
18346 }
18347
18348 buffer.completion_triggers().contains(text)
18349 }
18350}
18351
18352impl SemanticsProvider for Entity<Project> {
18353 fn hover(
18354 &self,
18355 buffer: &Entity<Buffer>,
18356 position: text::Anchor,
18357 cx: &mut App,
18358 ) -> Option<Task<Vec<project::Hover>>> {
18359 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18360 }
18361
18362 fn document_highlights(
18363 &self,
18364 buffer: &Entity<Buffer>,
18365 position: text::Anchor,
18366 cx: &mut App,
18367 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18368 Some(self.update(cx, |project, cx| {
18369 project.document_highlights(buffer, position, cx)
18370 }))
18371 }
18372
18373 fn definitions(
18374 &self,
18375 buffer: &Entity<Buffer>,
18376 position: text::Anchor,
18377 kind: GotoDefinitionKind,
18378 cx: &mut App,
18379 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18380 Some(self.update(cx, |project, cx| match kind {
18381 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18382 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18383 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18384 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18385 }))
18386 }
18387
18388 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18389 // TODO: make this work for remote projects
18390 self.update(cx, |this, cx| {
18391 buffer.update(cx, |buffer, cx| {
18392 this.any_language_server_supports_inlay_hints(buffer, cx)
18393 })
18394 })
18395 }
18396
18397 fn inlay_hints(
18398 &self,
18399 buffer_handle: Entity<Buffer>,
18400 range: Range<text::Anchor>,
18401 cx: &mut App,
18402 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18403 Some(self.update(cx, |project, cx| {
18404 project.inlay_hints(buffer_handle, range, cx)
18405 }))
18406 }
18407
18408 fn resolve_inlay_hint(
18409 &self,
18410 hint: InlayHint,
18411 buffer_handle: Entity<Buffer>,
18412 server_id: LanguageServerId,
18413 cx: &mut App,
18414 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18415 Some(self.update(cx, |project, cx| {
18416 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18417 }))
18418 }
18419
18420 fn range_for_rename(
18421 &self,
18422 buffer: &Entity<Buffer>,
18423 position: text::Anchor,
18424 cx: &mut App,
18425 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18426 Some(self.update(cx, |project, cx| {
18427 let buffer = buffer.clone();
18428 let task = project.prepare_rename(buffer.clone(), position, cx);
18429 cx.spawn(async move |_, cx| {
18430 Ok(match task.await? {
18431 PrepareRenameResponse::Success(range) => Some(range),
18432 PrepareRenameResponse::InvalidPosition => None,
18433 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18434 // Fallback on using TreeSitter info to determine identifier range
18435 buffer.update(cx, |buffer, _| {
18436 let snapshot = buffer.snapshot();
18437 let (range, kind) = snapshot.surrounding_word(position);
18438 if kind != Some(CharKind::Word) {
18439 return None;
18440 }
18441 Some(
18442 snapshot.anchor_before(range.start)
18443 ..snapshot.anchor_after(range.end),
18444 )
18445 })?
18446 }
18447 })
18448 })
18449 }))
18450 }
18451
18452 fn perform_rename(
18453 &self,
18454 buffer: &Entity<Buffer>,
18455 position: text::Anchor,
18456 new_name: String,
18457 cx: &mut App,
18458 ) -> Option<Task<Result<ProjectTransaction>>> {
18459 Some(self.update(cx, |project, cx| {
18460 project.perform_rename(buffer.clone(), position, new_name, cx)
18461 }))
18462 }
18463}
18464
18465fn inlay_hint_settings(
18466 location: Anchor,
18467 snapshot: &MultiBufferSnapshot,
18468 cx: &mut Context<Editor>,
18469) -> InlayHintSettings {
18470 let file = snapshot.file_at(location);
18471 let language = snapshot.language_at(location).map(|l| l.name());
18472 language_settings(language, file, cx).inlay_hints
18473}
18474
18475fn consume_contiguous_rows(
18476 contiguous_row_selections: &mut Vec<Selection<Point>>,
18477 selection: &Selection<Point>,
18478 display_map: &DisplaySnapshot,
18479 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18480) -> (MultiBufferRow, MultiBufferRow) {
18481 contiguous_row_selections.push(selection.clone());
18482 let start_row = MultiBufferRow(selection.start.row);
18483 let mut end_row = ending_row(selection, display_map);
18484
18485 while let Some(next_selection) = selections.peek() {
18486 if next_selection.start.row <= end_row.0 {
18487 end_row = ending_row(next_selection, display_map);
18488 contiguous_row_selections.push(selections.next().unwrap().clone());
18489 } else {
18490 break;
18491 }
18492 }
18493 (start_row, end_row)
18494}
18495
18496fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18497 if next_selection.end.column > 0 || next_selection.is_empty() {
18498 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18499 } else {
18500 MultiBufferRow(next_selection.end.row)
18501 }
18502}
18503
18504impl EditorSnapshot {
18505 pub fn remote_selections_in_range<'a>(
18506 &'a self,
18507 range: &'a Range<Anchor>,
18508 collaboration_hub: &dyn CollaborationHub,
18509 cx: &'a App,
18510 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18511 let participant_names = collaboration_hub.user_names(cx);
18512 let participant_indices = collaboration_hub.user_participant_indices(cx);
18513 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18514 let collaborators_by_replica_id = collaborators_by_peer_id
18515 .iter()
18516 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18517 .collect::<HashMap<_, _>>();
18518 self.buffer_snapshot
18519 .selections_in_range(range, false)
18520 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18521 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18522 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18523 let user_name = participant_names.get(&collaborator.user_id).cloned();
18524 Some(RemoteSelection {
18525 replica_id,
18526 selection,
18527 cursor_shape,
18528 line_mode,
18529 participant_index,
18530 peer_id: collaborator.peer_id,
18531 user_name,
18532 })
18533 })
18534 }
18535
18536 pub fn hunks_for_ranges(
18537 &self,
18538 ranges: impl IntoIterator<Item = Range<Point>>,
18539 ) -> Vec<MultiBufferDiffHunk> {
18540 let mut hunks = Vec::new();
18541 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18542 HashMap::default();
18543 for query_range in ranges {
18544 let query_rows =
18545 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18546 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18547 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18548 ) {
18549 // Include deleted hunks that are adjacent to the query range, because
18550 // otherwise they would be missed.
18551 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18552 if hunk.status().is_deleted() {
18553 intersects_range |= hunk.row_range.start == query_rows.end;
18554 intersects_range |= hunk.row_range.end == query_rows.start;
18555 }
18556 if intersects_range {
18557 if !processed_buffer_rows
18558 .entry(hunk.buffer_id)
18559 .or_default()
18560 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18561 {
18562 continue;
18563 }
18564 hunks.push(hunk);
18565 }
18566 }
18567 }
18568
18569 hunks
18570 }
18571
18572 fn display_diff_hunks_for_rows<'a>(
18573 &'a self,
18574 display_rows: Range<DisplayRow>,
18575 folded_buffers: &'a HashSet<BufferId>,
18576 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18577 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18578 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18579
18580 self.buffer_snapshot
18581 .diff_hunks_in_range(buffer_start..buffer_end)
18582 .filter_map(|hunk| {
18583 if folded_buffers.contains(&hunk.buffer_id) {
18584 return None;
18585 }
18586
18587 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18588 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18589
18590 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18591 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18592
18593 let display_hunk = if hunk_display_start.column() != 0 {
18594 DisplayDiffHunk::Folded {
18595 display_row: hunk_display_start.row(),
18596 }
18597 } else {
18598 let mut end_row = hunk_display_end.row();
18599 if hunk_display_end.column() > 0 {
18600 end_row.0 += 1;
18601 }
18602 let is_created_file = hunk.is_created_file();
18603 DisplayDiffHunk::Unfolded {
18604 status: hunk.status(),
18605 diff_base_byte_range: hunk.diff_base_byte_range,
18606 display_row_range: hunk_display_start.row()..end_row,
18607 multi_buffer_range: Anchor::range_in_buffer(
18608 hunk.excerpt_id,
18609 hunk.buffer_id,
18610 hunk.buffer_range,
18611 ),
18612 is_created_file,
18613 }
18614 };
18615
18616 Some(display_hunk)
18617 })
18618 }
18619
18620 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18621 self.display_snapshot.buffer_snapshot.language_at(position)
18622 }
18623
18624 pub fn is_focused(&self) -> bool {
18625 self.is_focused
18626 }
18627
18628 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18629 self.placeholder_text.as_ref()
18630 }
18631
18632 pub fn scroll_position(&self) -> gpui::Point<f32> {
18633 self.scroll_anchor.scroll_position(&self.display_snapshot)
18634 }
18635
18636 fn gutter_dimensions(
18637 &self,
18638 font_id: FontId,
18639 font_size: Pixels,
18640 max_line_number_width: Pixels,
18641 cx: &App,
18642 ) -> Option<GutterDimensions> {
18643 if !self.show_gutter {
18644 return None;
18645 }
18646
18647 let descent = cx.text_system().descent(font_id, font_size);
18648 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18649 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18650
18651 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18652 matches!(
18653 ProjectSettings::get_global(cx).git.git_gutter,
18654 Some(GitGutterSetting::TrackedFiles)
18655 )
18656 });
18657 let gutter_settings = EditorSettings::get_global(cx).gutter;
18658 let show_line_numbers = self
18659 .show_line_numbers
18660 .unwrap_or(gutter_settings.line_numbers);
18661 let line_gutter_width = if show_line_numbers {
18662 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18663 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18664 max_line_number_width.max(min_width_for_number_on_gutter)
18665 } else {
18666 0.0.into()
18667 };
18668
18669 let show_code_actions = self
18670 .show_code_actions
18671 .unwrap_or(gutter_settings.code_actions);
18672
18673 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18674 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18675
18676 let git_blame_entries_width =
18677 self.git_blame_gutter_max_author_length
18678 .map(|max_author_length| {
18679 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18680
18681 /// The number of characters to dedicate to gaps and margins.
18682 const SPACING_WIDTH: usize = 4;
18683
18684 let max_char_count = max_author_length
18685 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18686 + ::git::SHORT_SHA_LENGTH
18687 + MAX_RELATIVE_TIMESTAMP.len()
18688 + SPACING_WIDTH;
18689
18690 em_advance * max_char_count
18691 });
18692
18693 let is_singleton = self.buffer_snapshot.is_singleton();
18694
18695 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18696 left_padding += if !is_singleton {
18697 em_width * 4.0
18698 } else if show_code_actions || show_runnables || show_breakpoints {
18699 em_width * 3.0
18700 } else if show_git_gutter && show_line_numbers {
18701 em_width * 2.0
18702 } else if show_git_gutter || show_line_numbers {
18703 em_width
18704 } else {
18705 px(0.)
18706 };
18707
18708 let shows_folds = is_singleton && gutter_settings.folds;
18709
18710 let right_padding = if shows_folds && show_line_numbers {
18711 em_width * 4.0
18712 } else if shows_folds || (!is_singleton && show_line_numbers) {
18713 em_width * 3.0
18714 } else if show_line_numbers {
18715 em_width
18716 } else {
18717 px(0.)
18718 };
18719
18720 Some(GutterDimensions {
18721 left_padding,
18722 right_padding,
18723 width: line_gutter_width + left_padding + right_padding,
18724 margin: -descent,
18725 git_blame_entries_width,
18726 })
18727 }
18728
18729 pub fn render_crease_toggle(
18730 &self,
18731 buffer_row: MultiBufferRow,
18732 row_contains_cursor: bool,
18733 editor: Entity<Editor>,
18734 window: &mut Window,
18735 cx: &mut App,
18736 ) -> Option<AnyElement> {
18737 let folded = self.is_line_folded(buffer_row);
18738 let mut is_foldable = false;
18739
18740 if let Some(crease) = self
18741 .crease_snapshot
18742 .query_row(buffer_row, &self.buffer_snapshot)
18743 {
18744 is_foldable = true;
18745 match crease {
18746 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18747 if let Some(render_toggle) = render_toggle {
18748 let toggle_callback =
18749 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18750 if folded {
18751 editor.update(cx, |editor, cx| {
18752 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18753 });
18754 } else {
18755 editor.update(cx, |editor, cx| {
18756 editor.unfold_at(
18757 &crate::UnfoldAt { buffer_row },
18758 window,
18759 cx,
18760 )
18761 });
18762 }
18763 });
18764 return Some((render_toggle)(
18765 buffer_row,
18766 folded,
18767 toggle_callback,
18768 window,
18769 cx,
18770 ));
18771 }
18772 }
18773 }
18774 }
18775
18776 is_foldable |= self.starts_indent(buffer_row);
18777
18778 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18779 Some(
18780 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18781 .toggle_state(folded)
18782 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18783 if folded {
18784 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18785 } else {
18786 this.fold_at(&FoldAt { buffer_row }, window, cx);
18787 }
18788 }))
18789 .into_any_element(),
18790 )
18791 } else {
18792 None
18793 }
18794 }
18795
18796 pub fn render_crease_trailer(
18797 &self,
18798 buffer_row: MultiBufferRow,
18799 window: &mut Window,
18800 cx: &mut App,
18801 ) -> Option<AnyElement> {
18802 let folded = self.is_line_folded(buffer_row);
18803 if let Crease::Inline { render_trailer, .. } = self
18804 .crease_snapshot
18805 .query_row(buffer_row, &self.buffer_snapshot)?
18806 {
18807 let render_trailer = render_trailer.as_ref()?;
18808 Some(render_trailer(buffer_row, folded, window, cx))
18809 } else {
18810 None
18811 }
18812 }
18813}
18814
18815impl Deref for EditorSnapshot {
18816 type Target = DisplaySnapshot;
18817
18818 fn deref(&self) -> &Self::Target {
18819 &self.display_snapshot
18820 }
18821}
18822
18823#[derive(Clone, Debug, PartialEq, Eq)]
18824pub enum EditorEvent {
18825 InputIgnored {
18826 text: Arc<str>,
18827 },
18828 InputHandled {
18829 utf16_range_to_replace: Option<Range<isize>>,
18830 text: Arc<str>,
18831 },
18832 ExcerptsAdded {
18833 buffer: Entity<Buffer>,
18834 predecessor: ExcerptId,
18835 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18836 },
18837 ExcerptsRemoved {
18838 ids: Vec<ExcerptId>,
18839 },
18840 BufferFoldToggled {
18841 ids: Vec<ExcerptId>,
18842 folded: bool,
18843 },
18844 ExcerptsEdited {
18845 ids: Vec<ExcerptId>,
18846 },
18847 ExcerptsExpanded {
18848 ids: Vec<ExcerptId>,
18849 },
18850 BufferEdited,
18851 Edited {
18852 transaction_id: clock::Lamport,
18853 },
18854 Reparsed(BufferId),
18855 Focused,
18856 FocusedIn,
18857 Blurred,
18858 DirtyChanged,
18859 Saved,
18860 TitleChanged,
18861 DiffBaseChanged,
18862 SelectionsChanged {
18863 local: bool,
18864 },
18865 ScrollPositionChanged {
18866 local: bool,
18867 autoscroll: bool,
18868 },
18869 Closed,
18870 TransactionUndone {
18871 transaction_id: clock::Lamport,
18872 },
18873 TransactionBegun {
18874 transaction_id: clock::Lamport,
18875 },
18876 Reloaded,
18877 CursorShapeChanged,
18878 PushedToNavHistory {
18879 anchor: Anchor,
18880 is_deactivate: bool,
18881 },
18882}
18883
18884impl EventEmitter<EditorEvent> for Editor {}
18885
18886impl Focusable for Editor {
18887 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18888 self.focus_handle.clone()
18889 }
18890}
18891
18892impl Render for Editor {
18893 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18894 let settings = ThemeSettings::get_global(cx);
18895
18896 let mut text_style = match self.mode {
18897 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18898 color: cx.theme().colors().editor_foreground,
18899 font_family: settings.ui_font.family.clone(),
18900 font_features: settings.ui_font.features.clone(),
18901 font_fallbacks: settings.ui_font.fallbacks.clone(),
18902 font_size: rems(0.875).into(),
18903 font_weight: settings.ui_font.weight,
18904 line_height: relative(settings.buffer_line_height.value()),
18905 ..Default::default()
18906 },
18907 EditorMode::Full => TextStyle {
18908 color: cx.theme().colors().editor_foreground,
18909 font_family: settings.buffer_font.family.clone(),
18910 font_features: settings.buffer_font.features.clone(),
18911 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18912 font_size: settings.buffer_font_size(cx).into(),
18913 font_weight: settings.buffer_font.weight,
18914 line_height: relative(settings.buffer_line_height.value()),
18915 ..Default::default()
18916 },
18917 };
18918 if let Some(text_style_refinement) = &self.text_style_refinement {
18919 text_style.refine(text_style_refinement)
18920 }
18921
18922 let background = match self.mode {
18923 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18924 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18925 EditorMode::Full => cx.theme().colors().editor_background,
18926 };
18927
18928 EditorElement::new(
18929 &cx.entity(),
18930 EditorStyle {
18931 background,
18932 local_player: cx.theme().players().local(),
18933 text: text_style,
18934 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18935 syntax: cx.theme().syntax().clone(),
18936 status: cx.theme().status().clone(),
18937 inlay_hints_style: make_inlay_hints_style(cx),
18938 inline_completion_styles: make_suggestion_styles(cx),
18939 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18940 },
18941 )
18942 }
18943}
18944
18945impl EntityInputHandler for Editor {
18946 fn text_for_range(
18947 &mut self,
18948 range_utf16: Range<usize>,
18949 adjusted_range: &mut Option<Range<usize>>,
18950 _: &mut Window,
18951 cx: &mut Context<Self>,
18952 ) -> Option<String> {
18953 let snapshot = self.buffer.read(cx).read(cx);
18954 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18955 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18956 if (start.0..end.0) != range_utf16 {
18957 adjusted_range.replace(start.0..end.0);
18958 }
18959 Some(snapshot.text_for_range(start..end).collect())
18960 }
18961
18962 fn selected_text_range(
18963 &mut self,
18964 ignore_disabled_input: bool,
18965 _: &mut Window,
18966 cx: &mut Context<Self>,
18967 ) -> Option<UTF16Selection> {
18968 // Prevent the IME menu from appearing when holding down an alphabetic key
18969 // while input is disabled.
18970 if !ignore_disabled_input && !self.input_enabled {
18971 return None;
18972 }
18973
18974 let selection = self.selections.newest::<OffsetUtf16>(cx);
18975 let range = selection.range();
18976
18977 Some(UTF16Selection {
18978 range: range.start.0..range.end.0,
18979 reversed: selection.reversed,
18980 })
18981 }
18982
18983 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18984 let snapshot = self.buffer.read(cx).read(cx);
18985 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18986 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18987 }
18988
18989 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18990 self.clear_highlights::<InputComposition>(cx);
18991 self.ime_transaction.take();
18992 }
18993
18994 fn replace_text_in_range(
18995 &mut self,
18996 range_utf16: Option<Range<usize>>,
18997 text: &str,
18998 window: &mut Window,
18999 cx: &mut Context<Self>,
19000 ) {
19001 if !self.input_enabled {
19002 cx.emit(EditorEvent::InputIgnored { text: text.into() });
19003 return;
19004 }
19005
19006 self.transact(window, cx, |this, window, cx| {
19007 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
19008 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19009 Some(this.selection_replacement_ranges(range_utf16, cx))
19010 } else {
19011 this.marked_text_ranges(cx)
19012 };
19013
19014 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
19015 let newest_selection_id = this.selections.newest_anchor().id;
19016 this.selections
19017 .all::<OffsetUtf16>(cx)
19018 .iter()
19019 .zip(ranges_to_replace.iter())
19020 .find_map(|(selection, range)| {
19021 if selection.id == newest_selection_id {
19022 Some(
19023 (range.start.0 as isize - selection.head().0 as isize)
19024 ..(range.end.0 as isize - selection.head().0 as isize),
19025 )
19026 } else {
19027 None
19028 }
19029 })
19030 });
19031
19032 cx.emit(EditorEvent::InputHandled {
19033 utf16_range_to_replace: range_to_replace,
19034 text: text.into(),
19035 });
19036
19037 if let Some(new_selected_ranges) = new_selected_ranges {
19038 this.change_selections(None, window, cx, |selections| {
19039 selections.select_ranges(new_selected_ranges)
19040 });
19041 this.backspace(&Default::default(), window, cx);
19042 }
19043
19044 this.handle_input(text, window, cx);
19045 });
19046
19047 if let Some(transaction) = self.ime_transaction {
19048 self.buffer.update(cx, |buffer, cx| {
19049 buffer.group_until_transaction(transaction, cx);
19050 });
19051 }
19052
19053 self.unmark_text(window, cx);
19054 }
19055
19056 fn replace_and_mark_text_in_range(
19057 &mut self,
19058 range_utf16: Option<Range<usize>>,
19059 text: &str,
19060 new_selected_range_utf16: Option<Range<usize>>,
19061 window: &mut Window,
19062 cx: &mut Context<Self>,
19063 ) {
19064 if !self.input_enabled {
19065 return;
19066 }
19067
19068 let transaction = self.transact(window, cx, |this, window, cx| {
19069 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
19070 let snapshot = this.buffer.read(cx).read(cx);
19071 if let Some(relative_range_utf16) = range_utf16.as_ref() {
19072 for marked_range in &mut marked_ranges {
19073 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
19074 marked_range.start.0 += relative_range_utf16.start;
19075 marked_range.start =
19076 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
19077 marked_range.end =
19078 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
19079 }
19080 }
19081 Some(marked_ranges)
19082 } else if let Some(range_utf16) = range_utf16 {
19083 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19084 Some(this.selection_replacement_ranges(range_utf16, cx))
19085 } else {
19086 None
19087 };
19088
19089 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
19090 let newest_selection_id = this.selections.newest_anchor().id;
19091 this.selections
19092 .all::<OffsetUtf16>(cx)
19093 .iter()
19094 .zip(ranges_to_replace.iter())
19095 .find_map(|(selection, range)| {
19096 if selection.id == newest_selection_id {
19097 Some(
19098 (range.start.0 as isize - selection.head().0 as isize)
19099 ..(range.end.0 as isize - selection.head().0 as isize),
19100 )
19101 } else {
19102 None
19103 }
19104 })
19105 });
19106
19107 cx.emit(EditorEvent::InputHandled {
19108 utf16_range_to_replace: range_to_replace,
19109 text: text.into(),
19110 });
19111
19112 if let Some(ranges) = ranges_to_replace {
19113 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
19114 }
19115
19116 let marked_ranges = {
19117 let snapshot = this.buffer.read(cx).read(cx);
19118 this.selections
19119 .disjoint_anchors()
19120 .iter()
19121 .map(|selection| {
19122 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
19123 })
19124 .collect::<Vec<_>>()
19125 };
19126
19127 if text.is_empty() {
19128 this.unmark_text(window, cx);
19129 } else {
19130 this.highlight_text::<InputComposition>(
19131 marked_ranges.clone(),
19132 HighlightStyle {
19133 underline: Some(UnderlineStyle {
19134 thickness: px(1.),
19135 color: None,
19136 wavy: false,
19137 }),
19138 ..Default::default()
19139 },
19140 cx,
19141 );
19142 }
19143
19144 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
19145 let use_autoclose = this.use_autoclose;
19146 let use_auto_surround = this.use_auto_surround;
19147 this.set_use_autoclose(false);
19148 this.set_use_auto_surround(false);
19149 this.handle_input(text, window, cx);
19150 this.set_use_autoclose(use_autoclose);
19151 this.set_use_auto_surround(use_auto_surround);
19152
19153 if let Some(new_selected_range) = new_selected_range_utf16 {
19154 let snapshot = this.buffer.read(cx).read(cx);
19155 let new_selected_ranges = marked_ranges
19156 .into_iter()
19157 .map(|marked_range| {
19158 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
19159 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
19160 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
19161 snapshot.clip_offset_utf16(new_start, Bias::Left)
19162 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
19163 })
19164 .collect::<Vec<_>>();
19165
19166 drop(snapshot);
19167 this.change_selections(None, window, cx, |selections| {
19168 selections.select_ranges(new_selected_ranges)
19169 });
19170 }
19171 });
19172
19173 self.ime_transaction = self.ime_transaction.or(transaction);
19174 if let Some(transaction) = self.ime_transaction {
19175 self.buffer.update(cx, |buffer, cx| {
19176 buffer.group_until_transaction(transaction, cx);
19177 });
19178 }
19179
19180 if self.text_highlights::<InputComposition>(cx).is_none() {
19181 self.ime_transaction.take();
19182 }
19183 }
19184
19185 fn bounds_for_range(
19186 &mut self,
19187 range_utf16: Range<usize>,
19188 element_bounds: gpui::Bounds<Pixels>,
19189 window: &mut Window,
19190 cx: &mut Context<Self>,
19191 ) -> Option<gpui::Bounds<Pixels>> {
19192 let text_layout_details = self.text_layout_details(window);
19193 let gpui::Size {
19194 width: em_width,
19195 height: line_height,
19196 } = self.character_size(window);
19197
19198 let snapshot = self.snapshot(window, cx);
19199 let scroll_position = snapshot.scroll_position();
19200 let scroll_left = scroll_position.x * em_width;
19201
19202 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
19203 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
19204 + self.gutter_dimensions.width
19205 + self.gutter_dimensions.margin;
19206 let y = line_height * (start.row().as_f32() - scroll_position.y);
19207
19208 Some(Bounds {
19209 origin: element_bounds.origin + point(x, y),
19210 size: size(em_width, line_height),
19211 })
19212 }
19213
19214 fn character_index_for_point(
19215 &mut self,
19216 point: gpui::Point<Pixels>,
19217 _window: &mut Window,
19218 _cx: &mut Context<Self>,
19219 ) -> Option<usize> {
19220 let position_map = self.last_position_map.as_ref()?;
19221 if !position_map.text_hitbox.contains(&point) {
19222 return None;
19223 }
19224 let display_point = position_map.point_for_position(point).previous_valid;
19225 let anchor = position_map
19226 .snapshot
19227 .display_point_to_anchor(display_point, Bias::Left);
19228 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
19229 Some(utf16_offset.0)
19230 }
19231}
19232
19233trait SelectionExt {
19234 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
19235 fn spanned_rows(
19236 &self,
19237 include_end_if_at_line_start: bool,
19238 map: &DisplaySnapshot,
19239 ) -> Range<MultiBufferRow>;
19240}
19241
19242impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
19243 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
19244 let start = self
19245 .start
19246 .to_point(&map.buffer_snapshot)
19247 .to_display_point(map);
19248 let end = self
19249 .end
19250 .to_point(&map.buffer_snapshot)
19251 .to_display_point(map);
19252 if self.reversed {
19253 end..start
19254 } else {
19255 start..end
19256 }
19257 }
19258
19259 fn spanned_rows(
19260 &self,
19261 include_end_if_at_line_start: bool,
19262 map: &DisplaySnapshot,
19263 ) -> Range<MultiBufferRow> {
19264 let start = self.start.to_point(&map.buffer_snapshot);
19265 let mut end = self.end.to_point(&map.buffer_snapshot);
19266 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19267 end.row -= 1;
19268 }
19269
19270 let buffer_start = map.prev_line_boundary(start).0;
19271 let buffer_end = map.next_line_boundary(end).0;
19272 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19273 }
19274}
19275
19276impl<T: InvalidationRegion> InvalidationStack<T> {
19277 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19278 where
19279 S: Clone + ToOffset,
19280 {
19281 while let Some(region) = self.last() {
19282 let all_selections_inside_invalidation_ranges =
19283 if selections.len() == region.ranges().len() {
19284 selections
19285 .iter()
19286 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19287 .all(|(selection, invalidation_range)| {
19288 let head = selection.head().to_offset(buffer);
19289 invalidation_range.start <= head && invalidation_range.end >= head
19290 })
19291 } else {
19292 false
19293 };
19294
19295 if all_selections_inside_invalidation_ranges {
19296 break;
19297 } else {
19298 self.pop();
19299 }
19300 }
19301 }
19302}
19303
19304impl<T> Default for InvalidationStack<T> {
19305 fn default() -> Self {
19306 Self(Default::default())
19307 }
19308}
19309
19310impl<T> Deref for InvalidationStack<T> {
19311 type Target = Vec<T>;
19312
19313 fn deref(&self) -> &Self::Target {
19314 &self.0
19315 }
19316}
19317
19318impl<T> DerefMut for InvalidationStack<T> {
19319 fn deref_mut(&mut self) -> &mut Self::Target {
19320 &mut self.0
19321 }
19322}
19323
19324impl InvalidationRegion for SnippetState {
19325 fn ranges(&self) -> &[Range<Anchor>] {
19326 &self.ranges[self.active_index]
19327 }
19328}
19329
19330pub fn diagnostic_block_renderer(
19331 diagnostic: Diagnostic,
19332 max_message_rows: Option<u8>,
19333 allow_closing: bool,
19334) -> RenderBlock {
19335 let (text_without_backticks, code_ranges) =
19336 highlight_diagnostic_message(&diagnostic, max_message_rows);
19337
19338 Arc::new(move |cx: &mut BlockContext| {
19339 let group_id: SharedString = cx.block_id.to_string().into();
19340
19341 let mut text_style = cx.window.text_style().clone();
19342 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19343 let theme_settings = ThemeSettings::get_global(cx);
19344 text_style.font_family = theme_settings.buffer_font.family.clone();
19345 text_style.font_style = theme_settings.buffer_font.style;
19346 text_style.font_features = theme_settings.buffer_font.features.clone();
19347 text_style.font_weight = theme_settings.buffer_font.weight;
19348
19349 let multi_line_diagnostic = diagnostic.message.contains('\n');
19350
19351 let buttons = |diagnostic: &Diagnostic| {
19352 if multi_line_diagnostic {
19353 v_flex()
19354 } else {
19355 h_flex()
19356 }
19357 .when(allow_closing, |div| {
19358 div.children(diagnostic.is_primary.then(|| {
19359 IconButton::new("close-block", IconName::XCircle)
19360 .icon_color(Color::Muted)
19361 .size(ButtonSize::Compact)
19362 .style(ButtonStyle::Transparent)
19363 .visible_on_hover(group_id.clone())
19364 .on_click(move |_click, window, cx| {
19365 window.dispatch_action(Box::new(Cancel), cx)
19366 })
19367 .tooltip(|window, cx| {
19368 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19369 })
19370 }))
19371 })
19372 .child(
19373 IconButton::new("copy-block", IconName::Copy)
19374 .icon_color(Color::Muted)
19375 .size(ButtonSize::Compact)
19376 .style(ButtonStyle::Transparent)
19377 .visible_on_hover(group_id.clone())
19378 .on_click({
19379 let message = diagnostic.message.clone();
19380 move |_click, _, cx| {
19381 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19382 }
19383 })
19384 .tooltip(Tooltip::text("Copy diagnostic message")),
19385 )
19386 };
19387
19388 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19389 AvailableSpace::min_size(),
19390 cx.window,
19391 cx.app,
19392 );
19393
19394 h_flex()
19395 .id(cx.block_id)
19396 .group(group_id.clone())
19397 .relative()
19398 .size_full()
19399 .block_mouse_down()
19400 .pl(cx.gutter_dimensions.width)
19401 .w(cx.max_width - cx.gutter_dimensions.full_width())
19402 .child(
19403 div()
19404 .flex()
19405 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19406 .flex_shrink(),
19407 )
19408 .child(buttons(&diagnostic))
19409 .child(div().flex().flex_shrink_0().child(
19410 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19411 &text_style,
19412 code_ranges.iter().map(|range| {
19413 (
19414 range.clone(),
19415 HighlightStyle {
19416 font_weight: Some(FontWeight::BOLD),
19417 ..Default::default()
19418 },
19419 )
19420 }),
19421 ),
19422 ))
19423 .into_any_element()
19424 })
19425}
19426
19427fn inline_completion_edit_text(
19428 current_snapshot: &BufferSnapshot,
19429 edits: &[(Range<Anchor>, String)],
19430 edit_preview: &EditPreview,
19431 include_deletions: bool,
19432 cx: &App,
19433) -> HighlightedText {
19434 let edits = edits
19435 .iter()
19436 .map(|(anchor, text)| {
19437 (
19438 anchor.start.text_anchor..anchor.end.text_anchor,
19439 text.clone(),
19440 )
19441 })
19442 .collect::<Vec<_>>();
19443
19444 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19445}
19446
19447pub fn highlight_diagnostic_message(
19448 diagnostic: &Diagnostic,
19449 mut max_message_rows: Option<u8>,
19450) -> (SharedString, Vec<Range<usize>>) {
19451 let mut text_without_backticks = String::new();
19452 let mut code_ranges = Vec::new();
19453
19454 if let Some(source) = &diagnostic.source {
19455 text_without_backticks.push_str(source);
19456 code_ranges.push(0..source.len());
19457 text_without_backticks.push_str(": ");
19458 }
19459
19460 let mut prev_offset = 0;
19461 let mut in_code_block = false;
19462 let has_row_limit = max_message_rows.is_some();
19463 let mut newline_indices = diagnostic
19464 .message
19465 .match_indices('\n')
19466 .filter(|_| has_row_limit)
19467 .map(|(ix, _)| ix)
19468 .fuse()
19469 .peekable();
19470
19471 for (quote_ix, _) in diagnostic
19472 .message
19473 .match_indices('`')
19474 .chain([(diagnostic.message.len(), "")])
19475 {
19476 let mut first_newline_ix = None;
19477 let mut last_newline_ix = None;
19478 while let Some(newline_ix) = newline_indices.peek() {
19479 if *newline_ix < quote_ix {
19480 if first_newline_ix.is_none() {
19481 first_newline_ix = Some(*newline_ix);
19482 }
19483 last_newline_ix = Some(*newline_ix);
19484
19485 if let Some(rows_left) = &mut max_message_rows {
19486 if *rows_left == 0 {
19487 break;
19488 } else {
19489 *rows_left -= 1;
19490 }
19491 }
19492 let _ = newline_indices.next();
19493 } else {
19494 break;
19495 }
19496 }
19497 let prev_len = text_without_backticks.len();
19498 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19499 text_without_backticks.push_str(new_text);
19500 if in_code_block {
19501 code_ranges.push(prev_len..text_without_backticks.len());
19502 }
19503 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19504 in_code_block = !in_code_block;
19505 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19506 text_without_backticks.push_str("...");
19507 break;
19508 }
19509 }
19510
19511 (text_without_backticks.into(), code_ranges)
19512}
19513
19514fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19515 match severity {
19516 DiagnosticSeverity::ERROR => colors.error,
19517 DiagnosticSeverity::WARNING => colors.warning,
19518 DiagnosticSeverity::INFORMATION => colors.info,
19519 DiagnosticSeverity::HINT => colors.info,
19520 _ => colors.ignored,
19521 }
19522}
19523
19524pub fn styled_runs_for_code_label<'a>(
19525 label: &'a CodeLabel,
19526 syntax_theme: &'a theme::SyntaxTheme,
19527) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19528 let fade_out = HighlightStyle {
19529 fade_out: Some(0.35),
19530 ..Default::default()
19531 };
19532
19533 let mut prev_end = label.filter_range.end;
19534 label
19535 .runs
19536 .iter()
19537 .enumerate()
19538 .flat_map(move |(ix, (range, highlight_id))| {
19539 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19540 style
19541 } else {
19542 return Default::default();
19543 };
19544 let mut muted_style = style;
19545 muted_style.highlight(fade_out);
19546
19547 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19548 if range.start >= label.filter_range.end {
19549 if range.start > prev_end {
19550 runs.push((prev_end..range.start, fade_out));
19551 }
19552 runs.push((range.clone(), muted_style));
19553 } else if range.end <= label.filter_range.end {
19554 runs.push((range.clone(), style));
19555 } else {
19556 runs.push((range.start..label.filter_range.end, style));
19557 runs.push((label.filter_range.end..range.end, muted_style));
19558 }
19559 prev_end = cmp::max(prev_end, range.end);
19560
19561 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19562 runs.push((prev_end..label.text.len(), fade_out));
19563 }
19564
19565 runs
19566 })
19567}
19568
19569pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19570 let mut prev_index = 0;
19571 let mut prev_codepoint: Option<char> = None;
19572 text.char_indices()
19573 .chain([(text.len(), '\0')])
19574 .filter_map(move |(index, codepoint)| {
19575 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19576 let is_boundary = index == text.len()
19577 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19578 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19579 if is_boundary {
19580 let chunk = &text[prev_index..index];
19581 prev_index = index;
19582 Some(chunk)
19583 } else {
19584 None
19585 }
19586 })
19587}
19588
19589pub trait RangeToAnchorExt: Sized {
19590 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19591
19592 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19593 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19594 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19595 }
19596}
19597
19598impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19599 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19600 let start_offset = self.start.to_offset(snapshot);
19601 let end_offset = self.end.to_offset(snapshot);
19602 if start_offset == end_offset {
19603 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19604 } else {
19605 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19606 }
19607 }
19608}
19609
19610pub trait RowExt {
19611 fn as_f32(&self) -> f32;
19612
19613 fn next_row(&self) -> Self;
19614
19615 fn previous_row(&self) -> Self;
19616
19617 fn minus(&self, other: Self) -> u32;
19618}
19619
19620impl RowExt for DisplayRow {
19621 fn as_f32(&self) -> f32 {
19622 self.0 as f32
19623 }
19624
19625 fn next_row(&self) -> Self {
19626 Self(self.0 + 1)
19627 }
19628
19629 fn previous_row(&self) -> Self {
19630 Self(self.0.saturating_sub(1))
19631 }
19632
19633 fn minus(&self, other: Self) -> u32 {
19634 self.0 - other.0
19635 }
19636}
19637
19638impl RowExt for MultiBufferRow {
19639 fn as_f32(&self) -> f32 {
19640 self.0 as f32
19641 }
19642
19643 fn next_row(&self) -> Self {
19644 Self(self.0 + 1)
19645 }
19646
19647 fn previous_row(&self) -> Self {
19648 Self(self.0.saturating_sub(1))
19649 }
19650
19651 fn minus(&self, other: Self) -> u32 {
19652 self.0 - other.0
19653 }
19654}
19655
19656trait RowRangeExt {
19657 type Row;
19658
19659 fn len(&self) -> usize;
19660
19661 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19662}
19663
19664impl RowRangeExt for Range<MultiBufferRow> {
19665 type Row = MultiBufferRow;
19666
19667 fn len(&self) -> usize {
19668 (self.end.0 - self.start.0) as usize
19669 }
19670
19671 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19672 (self.start.0..self.end.0).map(MultiBufferRow)
19673 }
19674}
19675
19676impl RowRangeExt for Range<DisplayRow> {
19677 type Row = DisplayRow;
19678
19679 fn len(&self) -> usize {
19680 (self.end.0 - self.start.0) as usize
19681 }
19682
19683 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19684 (self.start.0..self.end.0).map(DisplayRow)
19685 }
19686}
19687
19688/// If select range has more than one line, we
19689/// just point the cursor to range.start.
19690fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19691 if range.start.row == range.end.row {
19692 range
19693 } else {
19694 range.start..range.start
19695 }
19696}
19697pub struct KillRing(ClipboardItem);
19698impl Global for KillRing {}
19699
19700const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19701
19702struct BreakpointPromptEditor {
19703 pub(crate) prompt: Entity<Editor>,
19704 editor: WeakEntity<Editor>,
19705 breakpoint_anchor: Anchor,
19706 breakpoint: Breakpoint,
19707 block_ids: HashSet<CustomBlockId>,
19708 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19709 _subscriptions: Vec<Subscription>,
19710}
19711
19712impl BreakpointPromptEditor {
19713 const MAX_LINES: u8 = 4;
19714
19715 fn new(
19716 editor: WeakEntity<Editor>,
19717 breakpoint_anchor: Anchor,
19718 breakpoint: Breakpoint,
19719 window: &mut Window,
19720 cx: &mut Context<Self>,
19721 ) -> Self {
19722 let buffer = cx.new(|cx| {
19723 Buffer::local(
19724 breakpoint
19725 .kind
19726 .log_message()
19727 .map(|msg| msg.to_string())
19728 .unwrap_or_default(),
19729 cx,
19730 )
19731 });
19732 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19733
19734 let prompt = cx.new(|cx| {
19735 let mut prompt = Editor::new(
19736 EditorMode::AutoHeight {
19737 max_lines: Self::MAX_LINES as usize,
19738 },
19739 buffer,
19740 None,
19741 window,
19742 cx,
19743 );
19744 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19745 prompt.set_show_cursor_when_unfocused(false, cx);
19746 prompt.set_placeholder_text(
19747 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19748 cx,
19749 );
19750
19751 prompt
19752 });
19753
19754 Self {
19755 prompt,
19756 editor,
19757 breakpoint_anchor,
19758 breakpoint,
19759 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19760 block_ids: Default::default(),
19761 _subscriptions: vec![],
19762 }
19763 }
19764
19765 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19766 self.block_ids.extend(block_ids)
19767 }
19768
19769 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19770 if let Some(editor) = self.editor.upgrade() {
19771 let log_message = self
19772 .prompt
19773 .read(cx)
19774 .buffer
19775 .read(cx)
19776 .as_singleton()
19777 .expect("A multi buffer in breakpoint prompt isn't possible")
19778 .read(cx)
19779 .as_rope()
19780 .to_string();
19781
19782 editor.update(cx, |editor, cx| {
19783 editor.edit_breakpoint_at_anchor(
19784 self.breakpoint_anchor,
19785 self.breakpoint.clone(),
19786 BreakpointEditAction::EditLogMessage(log_message.into()),
19787 cx,
19788 );
19789
19790 editor.remove_blocks(self.block_ids.clone(), None, cx);
19791 cx.focus_self(window);
19792 });
19793 }
19794 }
19795
19796 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19797 self.editor
19798 .update(cx, |editor, cx| {
19799 editor.remove_blocks(self.block_ids.clone(), None, cx);
19800 window.focus(&editor.focus_handle);
19801 })
19802 .log_err();
19803 }
19804
19805 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19806 let settings = ThemeSettings::get_global(cx);
19807 let text_style = TextStyle {
19808 color: if self.prompt.read(cx).read_only(cx) {
19809 cx.theme().colors().text_disabled
19810 } else {
19811 cx.theme().colors().text
19812 },
19813 font_family: settings.buffer_font.family.clone(),
19814 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19815 font_size: settings.buffer_font_size(cx).into(),
19816 font_weight: settings.buffer_font.weight,
19817 line_height: relative(settings.buffer_line_height.value()),
19818 ..Default::default()
19819 };
19820 EditorElement::new(
19821 &self.prompt,
19822 EditorStyle {
19823 background: cx.theme().colors().editor_background,
19824 local_player: cx.theme().players().local(),
19825 text: text_style,
19826 ..Default::default()
19827 },
19828 )
19829 }
19830}
19831
19832impl Render for BreakpointPromptEditor {
19833 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19834 let gutter_dimensions = *self.gutter_dimensions.lock();
19835 h_flex()
19836 .key_context("Editor")
19837 .bg(cx.theme().colors().editor_background)
19838 .border_y_1()
19839 .border_color(cx.theme().status().info_border)
19840 .size_full()
19841 .py(window.line_height() / 2.5)
19842 .on_action(cx.listener(Self::confirm))
19843 .on_action(cx.listener(Self::cancel))
19844 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19845 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19846 }
19847}
19848
19849impl Focusable for BreakpointPromptEditor {
19850 fn focus_handle(&self, cx: &App) -> FocusHandle {
19851 self.prompt.focus_handle(cx)
19852 }
19853}
19854
19855fn all_edits_insertions_or_deletions(
19856 edits: &Vec<(Range<Anchor>, String)>,
19857 snapshot: &MultiBufferSnapshot,
19858) -> bool {
19859 let mut all_insertions = true;
19860 let mut all_deletions = true;
19861
19862 for (range, new_text) in edits.iter() {
19863 let range_is_empty = range.to_offset(&snapshot).is_empty();
19864 let text_is_empty = new_text.is_empty();
19865
19866 if range_is_empty != text_is_empty {
19867 if range_is_empty {
19868 all_deletions = false;
19869 } else {
19870 all_insertions = false;
19871 }
19872 } else {
19873 return false;
19874 }
19875
19876 if !all_insertions && !all_deletions {
19877 return false;
19878 }
19879 }
19880 all_insertions || all_deletions
19881}
19882
19883struct MissingEditPredictionKeybindingTooltip;
19884
19885impl Render for MissingEditPredictionKeybindingTooltip {
19886 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19887 ui::tooltip_container(window, cx, |container, _, cx| {
19888 container
19889 .flex_shrink_0()
19890 .max_w_80()
19891 .min_h(rems_from_px(124.))
19892 .justify_between()
19893 .child(
19894 v_flex()
19895 .flex_1()
19896 .text_ui_sm(cx)
19897 .child(Label::new("Conflict with Accept Keybinding"))
19898 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19899 )
19900 .child(
19901 h_flex()
19902 .pb_1()
19903 .gap_1()
19904 .items_end()
19905 .w_full()
19906 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19907 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19908 }))
19909 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19910 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19911 })),
19912 )
19913 })
19914 }
19915}
19916
19917#[derive(Debug, Clone, Copy, PartialEq)]
19918pub struct LineHighlight {
19919 pub background: Background,
19920 pub border: Option<gpui::Hsla>,
19921}
19922
19923impl From<Hsla> for LineHighlight {
19924 fn from(hsla: Hsla) -> Self {
19925 Self {
19926 background: hsla.into(),
19927 border: None,
19928 }
19929 }
19930}
19931
19932impl From<Background> for LineHighlight {
19933 fn from(background: Background) -> Self {
19934 Self {
19935 background,
19936 border: None,
19937 }
19938 }
19939}