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}
794
795#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
796enum NextScrollCursorCenterTopBottom {
797 #[default]
798 Center,
799 Top,
800 Bottom,
801}
802
803impl NextScrollCursorCenterTopBottom {
804 fn next(&self) -> Self {
805 match self {
806 Self::Center => Self::Top,
807 Self::Top => Self::Bottom,
808 Self::Bottom => Self::Center,
809 }
810 }
811}
812
813#[derive(Clone)]
814pub struct EditorSnapshot {
815 pub mode: EditorMode,
816 show_gutter: bool,
817 show_line_numbers: Option<bool>,
818 show_git_diff_gutter: Option<bool>,
819 show_code_actions: Option<bool>,
820 show_runnables: Option<bool>,
821 show_breakpoints: Option<bool>,
822 git_blame_gutter_max_author_length: Option<usize>,
823 pub display_snapshot: DisplaySnapshot,
824 pub placeholder_text: Option<Arc<str>>,
825 is_focused: bool,
826 scroll_anchor: ScrollAnchor,
827 ongoing_scroll: OngoingScroll,
828 current_line_highlight: CurrentLineHighlight,
829 gutter_hovered: bool,
830}
831
832const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
833
834#[derive(Default, Debug, Clone, Copy)]
835pub struct GutterDimensions {
836 pub left_padding: Pixels,
837 pub right_padding: Pixels,
838 pub width: Pixels,
839 pub margin: Pixels,
840 pub git_blame_entries_width: Option<Pixels>,
841}
842
843impl GutterDimensions {
844 /// The full width of the space taken up by the gutter.
845 pub fn full_width(&self) -> Pixels {
846 self.margin + self.width
847 }
848
849 /// The width of the space reserved for the fold indicators,
850 /// use alongside 'justify_end' and `gutter_width` to
851 /// right align content with the line numbers
852 pub fn fold_area_width(&self) -> Pixels {
853 self.margin + self.right_padding
854 }
855}
856
857#[derive(Debug)]
858pub struct RemoteSelection {
859 pub replica_id: ReplicaId,
860 pub selection: Selection<Anchor>,
861 pub cursor_shape: CursorShape,
862 pub peer_id: PeerId,
863 pub line_mode: bool,
864 pub participant_index: Option<ParticipantIndex>,
865 pub user_name: Option<SharedString>,
866}
867
868#[derive(Clone, Debug)]
869struct SelectionHistoryEntry {
870 selections: Arc<[Selection<Anchor>]>,
871 select_next_state: Option<SelectNextState>,
872 select_prev_state: Option<SelectNextState>,
873 add_selections_state: Option<AddSelectionsState>,
874}
875
876enum SelectionHistoryMode {
877 Normal,
878 Undoing,
879 Redoing,
880}
881
882#[derive(Clone, PartialEq, Eq, Hash)]
883struct HoveredCursor {
884 replica_id: u16,
885 selection_id: usize,
886}
887
888impl Default for SelectionHistoryMode {
889 fn default() -> Self {
890 Self::Normal
891 }
892}
893
894#[derive(Default)]
895struct SelectionHistory {
896 #[allow(clippy::type_complexity)]
897 selections_by_transaction:
898 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
899 mode: SelectionHistoryMode,
900 undo_stack: VecDeque<SelectionHistoryEntry>,
901 redo_stack: VecDeque<SelectionHistoryEntry>,
902}
903
904impl SelectionHistory {
905 fn insert_transaction(
906 &mut self,
907 transaction_id: TransactionId,
908 selections: Arc<[Selection<Anchor>]>,
909 ) {
910 self.selections_by_transaction
911 .insert(transaction_id, (selections, None));
912 }
913
914 #[allow(clippy::type_complexity)]
915 fn transaction(
916 &self,
917 transaction_id: TransactionId,
918 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
919 self.selections_by_transaction.get(&transaction_id)
920 }
921
922 #[allow(clippy::type_complexity)]
923 fn transaction_mut(
924 &mut self,
925 transaction_id: TransactionId,
926 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
927 self.selections_by_transaction.get_mut(&transaction_id)
928 }
929
930 fn push(&mut self, entry: SelectionHistoryEntry) {
931 if !entry.selections.is_empty() {
932 match self.mode {
933 SelectionHistoryMode::Normal => {
934 self.push_undo(entry);
935 self.redo_stack.clear();
936 }
937 SelectionHistoryMode::Undoing => self.push_redo(entry),
938 SelectionHistoryMode::Redoing => self.push_undo(entry),
939 }
940 }
941 }
942
943 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
944 if self
945 .undo_stack
946 .back()
947 .map_or(true, |e| e.selections != entry.selections)
948 {
949 self.undo_stack.push_back(entry);
950 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
951 self.undo_stack.pop_front();
952 }
953 }
954 }
955
956 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
957 if self
958 .redo_stack
959 .back()
960 .map_or(true, |e| e.selections != entry.selections)
961 {
962 self.redo_stack.push_back(entry);
963 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
964 self.redo_stack.pop_front();
965 }
966 }
967 }
968}
969
970struct RowHighlight {
971 index: usize,
972 range: Range<Anchor>,
973 color: Hsla,
974 should_autoscroll: bool,
975}
976
977#[derive(Clone, Debug)]
978struct AddSelectionsState {
979 above: bool,
980 stack: Vec<usize>,
981}
982
983#[derive(Clone)]
984struct SelectNextState {
985 query: AhoCorasick,
986 wordwise: bool,
987 done: bool,
988}
989
990impl std::fmt::Debug for SelectNextState {
991 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
992 f.debug_struct(std::any::type_name::<Self>())
993 .field("wordwise", &self.wordwise)
994 .field("done", &self.done)
995 .finish()
996 }
997}
998
999#[derive(Debug)]
1000struct AutocloseRegion {
1001 selection_id: usize,
1002 range: Range<Anchor>,
1003 pair: BracketPair,
1004}
1005
1006#[derive(Debug)]
1007struct SnippetState {
1008 ranges: Vec<Vec<Range<Anchor>>>,
1009 active_index: usize,
1010 choices: Vec<Option<Vec<String>>>,
1011}
1012
1013#[doc(hidden)]
1014pub struct RenameState {
1015 pub range: Range<Anchor>,
1016 pub old_name: Arc<str>,
1017 pub editor: Entity<Editor>,
1018 block_id: CustomBlockId,
1019}
1020
1021struct InvalidationStack<T>(Vec<T>);
1022
1023struct RegisteredInlineCompletionProvider {
1024 provider: Arc<dyn InlineCompletionProviderHandle>,
1025 _subscription: Subscription,
1026}
1027
1028#[derive(Debug, PartialEq, Eq)]
1029struct ActiveDiagnosticGroup {
1030 primary_range: Range<Anchor>,
1031 primary_message: String,
1032 group_id: usize,
1033 blocks: HashMap<CustomBlockId, Diagnostic>,
1034 is_valid: bool,
1035}
1036
1037#[derive(Serialize, Deserialize, Clone, Debug)]
1038pub struct ClipboardSelection {
1039 /// The number of bytes in this selection.
1040 pub len: usize,
1041 /// Whether this was a full-line selection.
1042 pub is_entire_line: bool,
1043 /// The indentation of the first line when this content was originally copied.
1044 pub first_line_indent: u32,
1045}
1046
1047// selections, scroll behavior, was newest selection reversed
1048type SelectSyntaxNodeHistoryState = (
1049 Box<[Selection<usize>]>,
1050 SelectSyntaxNodeScrollBehavior,
1051 bool,
1052);
1053
1054#[derive(Default)]
1055struct SelectSyntaxNodeHistory {
1056 stack: Vec<SelectSyntaxNodeHistoryState>,
1057 // disable temporarily to allow changing selections without losing the stack
1058 pub disable_clearing: bool,
1059}
1060
1061impl SelectSyntaxNodeHistory {
1062 pub fn try_clear(&mut self) {
1063 if !self.disable_clearing {
1064 self.stack.clear();
1065 }
1066 }
1067
1068 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1069 self.stack.push(selection);
1070 }
1071
1072 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1073 self.stack.pop()
1074 }
1075}
1076
1077enum SelectSyntaxNodeScrollBehavior {
1078 CursorTop,
1079 CenterSelection,
1080 CursorBottom,
1081}
1082
1083#[derive(Debug)]
1084pub(crate) struct NavigationData {
1085 cursor_anchor: Anchor,
1086 cursor_position: Point,
1087 scroll_anchor: ScrollAnchor,
1088 scroll_top_row: u32,
1089}
1090
1091#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1092pub enum GotoDefinitionKind {
1093 Symbol,
1094 Declaration,
1095 Type,
1096 Implementation,
1097}
1098
1099#[derive(Debug, Clone)]
1100enum InlayHintRefreshReason {
1101 ModifiersChanged(bool),
1102 Toggle(bool),
1103 SettingsChange(InlayHintSettings),
1104 NewLinesShown,
1105 BufferEdited(HashSet<Arc<Language>>),
1106 RefreshRequested,
1107 ExcerptsRemoved(Vec<ExcerptId>),
1108}
1109
1110impl InlayHintRefreshReason {
1111 fn description(&self) -> &'static str {
1112 match self {
1113 Self::ModifiersChanged(_) => "modifiers changed",
1114 Self::Toggle(_) => "toggle",
1115 Self::SettingsChange(_) => "settings change",
1116 Self::NewLinesShown => "new lines shown",
1117 Self::BufferEdited(_) => "buffer edited",
1118 Self::RefreshRequested => "refresh requested",
1119 Self::ExcerptsRemoved(_) => "excerpts removed",
1120 }
1121 }
1122}
1123
1124pub enum FormatTarget {
1125 Buffers,
1126 Ranges(Vec<Range<MultiBufferPoint>>),
1127}
1128
1129pub(crate) struct FocusedBlock {
1130 id: BlockId,
1131 focus_handle: WeakFocusHandle,
1132}
1133
1134#[derive(Clone)]
1135enum JumpData {
1136 MultiBufferRow {
1137 row: MultiBufferRow,
1138 line_offset_from_top: u32,
1139 },
1140 MultiBufferPoint {
1141 excerpt_id: ExcerptId,
1142 position: Point,
1143 anchor: text::Anchor,
1144 line_offset_from_top: u32,
1145 },
1146}
1147
1148pub enum MultibufferSelectionMode {
1149 First,
1150 All,
1151}
1152
1153#[derive(Clone, Copy, Debug, Default)]
1154pub struct RewrapOptions {
1155 pub override_language_settings: bool,
1156 pub preserve_existing_whitespace: bool,
1157}
1158
1159impl Editor {
1160 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1161 let buffer = cx.new(|cx| Buffer::local("", cx));
1162 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1163 Self::new(
1164 EditorMode::SingleLine { auto_width: false },
1165 buffer,
1166 None,
1167 window,
1168 cx,
1169 )
1170 }
1171
1172 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1173 let buffer = cx.new(|cx| Buffer::local("", cx));
1174 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1175 Self::new(EditorMode::Full, buffer, None, window, cx)
1176 }
1177
1178 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1179 let buffer = cx.new(|cx| Buffer::local("", cx));
1180 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1181 Self::new(
1182 EditorMode::SingleLine { auto_width: true },
1183 buffer,
1184 None,
1185 window,
1186 cx,
1187 )
1188 }
1189
1190 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1191 let buffer = cx.new(|cx| Buffer::local("", cx));
1192 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1193 Self::new(
1194 EditorMode::AutoHeight { max_lines },
1195 buffer,
1196 None,
1197 window,
1198 cx,
1199 )
1200 }
1201
1202 pub fn for_buffer(
1203 buffer: Entity<Buffer>,
1204 project: Option<Entity<Project>>,
1205 window: &mut Window,
1206 cx: &mut Context<Self>,
1207 ) -> Self {
1208 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1209 Self::new(EditorMode::Full, buffer, project, window, cx)
1210 }
1211
1212 pub fn for_multibuffer(
1213 buffer: Entity<MultiBuffer>,
1214 project: Option<Entity<Project>>,
1215 window: &mut Window,
1216 cx: &mut Context<Self>,
1217 ) -> Self {
1218 Self::new(EditorMode::Full, buffer, project, window, cx)
1219 }
1220
1221 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1222 let mut clone = Self::new(
1223 self.mode,
1224 self.buffer.clone(),
1225 self.project.clone(),
1226 window,
1227 cx,
1228 );
1229 self.display_map.update(cx, |display_map, cx| {
1230 let snapshot = display_map.snapshot(cx);
1231 clone.display_map.update(cx, |display_map, cx| {
1232 display_map.set_state(&snapshot, cx);
1233 });
1234 });
1235 clone.folds_did_change(cx);
1236 clone.selections.clone_state(&self.selections);
1237 clone.scroll_manager.clone_state(&self.scroll_manager);
1238 clone.searchable = self.searchable;
1239 clone
1240 }
1241
1242 pub fn new(
1243 mode: EditorMode,
1244 buffer: Entity<MultiBuffer>,
1245 project: Option<Entity<Project>>,
1246 window: &mut Window,
1247 cx: &mut Context<Self>,
1248 ) -> Self {
1249 let style = window.text_style();
1250 let font_size = style.font_size.to_pixels(window.rem_size());
1251 let editor = cx.entity().downgrade();
1252 let fold_placeholder = FoldPlaceholder {
1253 constrain_width: true,
1254 render: Arc::new(move |fold_id, fold_range, cx| {
1255 let editor = editor.clone();
1256 div()
1257 .id(fold_id)
1258 .bg(cx.theme().colors().ghost_element_background)
1259 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1260 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1261 .rounded_xs()
1262 .size_full()
1263 .cursor_pointer()
1264 .child("⋯")
1265 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1266 .on_click(move |_, _window, cx| {
1267 editor
1268 .update(cx, |editor, cx| {
1269 editor.unfold_ranges(
1270 &[fold_range.start..fold_range.end],
1271 true,
1272 false,
1273 cx,
1274 );
1275 cx.stop_propagation();
1276 })
1277 .ok();
1278 })
1279 .into_any()
1280 }),
1281 merge_adjacent: true,
1282 ..Default::default()
1283 };
1284 let display_map = cx.new(|cx| {
1285 DisplayMap::new(
1286 buffer.clone(),
1287 style.font(),
1288 font_size,
1289 None,
1290 FILE_HEADER_HEIGHT,
1291 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1292 fold_placeholder,
1293 cx,
1294 )
1295 });
1296
1297 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1298
1299 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1300
1301 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1302 .then(|| language_settings::SoftWrap::None);
1303
1304 let mut project_subscriptions = Vec::new();
1305 if mode == EditorMode::Full {
1306 if let Some(project) = project.as_ref() {
1307 project_subscriptions.push(cx.subscribe_in(
1308 project,
1309 window,
1310 |editor, _, event, window, cx| match event {
1311 project::Event::RefreshCodeLens => {
1312 // we always query lens with actions, without storing them, always refreshing them
1313 }
1314 project::Event::RefreshInlayHints => {
1315 editor
1316 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1317 }
1318 project::Event::SnippetEdit(id, snippet_edits) => {
1319 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1320 let focus_handle = editor.focus_handle(cx);
1321 if focus_handle.is_focused(window) {
1322 let snapshot = buffer.read(cx).snapshot();
1323 for (range, snippet) in snippet_edits {
1324 let editor_range =
1325 language::range_from_lsp(*range).to_offset(&snapshot);
1326 editor
1327 .insert_snippet(
1328 &[editor_range],
1329 snippet.clone(),
1330 window,
1331 cx,
1332 )
1333 .ok();
1334 }
1335 }
1336 }
1337 }
1338 _ => {}
1339 },
1340 ));
1341 if let Some(task_inventory) = project
1342 .read(cx)
1343 .task_store()
1344 .read(cx)
1345 .task_inventory()
1346 .cloned()
1347 {
1348 project_subscriptions.push(cx.observe_in(
1349 &task_inventory,
1350 window,
1351 |editor, _, window, cx| {
1352 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1353 },
1354 ));
1355 };
1356
1357 project_subscriptions.push(cx.subscribe_in(
1358 &project.read(cx).breakpoint_store(),
1359 window,
1360 |editor, _, event, window, cx| match event {
1361 BreakpointStoreEvent::ActiveDebugLineChanged => {
1362 editor.go_to_active_debug_line(window, cx);
1363 }
1364 _ => {}
1365 },
1366 ));
1367 }
1368 }
1369
1370 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1371
1372 let inlay_hint_settings =
1373 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1374 let focus_handle = cx.focus_handle();
1375 cx.on_focus(&focus_handle, window, Self::handle_focus)
1376 .detach();
1377 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1378 .detach();
1379 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1380 .detach();
1381 cx.on_blur(&focus_handle, window, Self::handle_blur)
1382 .detach();
1383
1384 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1385 Some(false)
1386 } else {
1387 None
1388 };
1389
1390 let breakpoint_store = match (mode, project.as_ref()) {
1391 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1392 _ => None,
1393 };
1394
1395 let mut code_action_providers = Vec::new();
1396 let mut load_uncommitted_diff = None;
1397 if let Some(project) = project.clone() {
1398 load_uncommitted_diff = Some(
1399 get_uncommitted_diff_for_buffer(
1400 &project,
1401 buffer.read(cx).all_buffers(),
1402 buffer.clone(),
1403 cx,
1404 )
1405 .shared(),
1406 );
1407 code_action_providers.push(Rc::new(project) as Rc<_>);
1408 }
1409
1410 let mut this = Self {
1411 focus_handle,
1412 show_cursor_when_unfocused: false,
1413 last_focused_descendant: None,
1414 buffer: buffer.clone(),
1415 display_map: display_map.clone(),
1416 selections,
1417 scroll_manager: ScrollManager::new(cx),
1418 columnar_selection_tail: None,
1419 add_selections_state: None,
1420 select_next_state: None,
1421 select_prev_state: None,
1422 selection_history: Default::default(),
1423 autoclose_regions: Default::default(),
1424 snippet_stack: Default::default(),
1425 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
1426 ime_transaction: Default::default(),
1427 active_diagnostics: None,
1428 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1429 inline_diagnostics_update: Task::ready(()),
1430 inline_diagnostics: Vec::new(),
1431 soft_wrap_mode_override,
1432 hard_wrap: None,
1433 completion_provider: project.clone().map(|project| Box::new(project) as _),
1434 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1435 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1436 project,
1437 blink_manager: blink_manager.clone(),
1438 show_local_selections: true,
1439 show_scrollbars: true,
1440 mode,
1441 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1442 show_gutter: mode == EditorMode::Full,
1443 show_line_numbers: None,
1444 use_relative_line_numbers: None,
1445 show_git_diff_gutter: None,
1446 show_code_actions: None,
1447 show_runnables: None,
1448 show_breakpoints: None,
1449 show_wrap_guides: None,
1450 show_indent_guides,
1451 placeholder_text: None,
1452 highlight_order: 0,
1453 highlighted_rows: HashMap::default(),
1454 background_highlights: Default::default(),
1455 gutter_highlights: TreeMap::default(),
1456 scrollbar_marker_state: ScrollbarMarkerState::default(),
1457 active_indent_guides_state: ActiveIndentGuidesState::default(),
1458 nav_history: None,
1459 context_menu: RefCell::new(None),
1460 context_menu_options: None,
1461 mouse_context_menu: None,
1462 completion_tasks: Default::default(),
1463 signature_help_state: SignatureHelpState::default(),
1464 auto_signature_help: None,
1465 find_all_references_task_sources: Vec::new(),
1466 next_completion_id: 0,
1467 next_inlay_id: 0,
1468 code_action_providers,
1469 available_code_actions: Default::default(),
1470 code_actions_task: Default::default(),
1471 selection_highlight_task: Default::default(),
1472 document_highlights_task: Default::default(),
1473 linked_editing_range_task: Default::default(),
1474 pending_rename: Default::default(),
1475 searchable: true,
1476 cursor_shape: EditorSettings::get_global(cx)
1477 .cursor_shape
1478 .unwrap_or_default(),
1479 current_line_highlight: None,
1480 autoindent_mode: Some(AutoindentMode::EachLine),
1481 collapse_matches: false,
1482 workspace: None,
1483 input_enabled: true,
1484 use_modal_editing: mode == EditorMode::Full,
1485 read_only: false,
1486 use_autoclose: true,
1487 use_auto_surround: true,
1488 auto_replace_emoji_shortcode: false,
1489 jsx_tag_auto_close_enabled_in_any_buffer: false,
1490 leader_peer_id: None,
1491 remote_id: None,
1492 hover_state: Default::default(),
1493 pending_mouse_down: None,
1494 hovered_link_state: Default::default(),
1495 edit_prediction_provider: None,
1496 active_inline_completion: None,
1497 stale_inline_completion_in_menu: None,
1498 edit_prediction_preview: EditPredictionPreview::Inactive {
1499 released_too_fast: false,
1500 },
1501 inline_diagnostics_enabled: mode == EditorMode::Full,
1502 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1503
1504 gutter_hovered: false,
1505 pixel_position_of_newest_cursor: None,
1506 last_bounds: None,
1507 last_position_map: None,
1508 expect_bounds_change: None,
1509 gutter_dimensions: GutterDimensions::default(),
1510 style: None,
1511 show_cursor_names: false,
1512 hovered_cursors: Default::default(),
1513 next_editor_action_id: EditorActionId::default(),
1514 editor_actions: Rc::default(),
1515 inline_completions_hidden_for_vim_mode: false,
1516 show_inline_completions_override: None,
1517 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1518 edit_prediction_settings: EditPredictionSettings::Disabled,
1519 edit_prediction_indent_conflict: false,
1520 edit_prediction_requires_modifier_in_indent_conflict: true,
1521 custom_context_menu: None,
1522 show_git_blame_gutter: false,
1523 show_git_blame_inline: false,
1524 show_selection_menu: None,
1525 show_git_blame_inline_delay_task: None,
1526 git_blame_inline_tooltip: None,
1527 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1528 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1529 .session
1530 .restore_unsaved_buffers,
1531 blame: None,
1532 blame_subscription: None,
1533 tasks: Default::default(),
1534
1535 breakpoint_store,
1536 gutter_breakpoint_indicator: None,
1537 _subscriptions: vec![
1538 cx.observe(&buffer, Self::on_buffer_changed),
1539 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1540 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1541 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1542 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1543 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1544 cx.observe_window_activation(window, |editor, window, cx| {
1545 let active = window.is_window_active();
1546 editor.blink_manager.update(cx, |blink_manager, cx| {
1547 if active {
1548 blink_manager.enable(cx);
1549 } else {
1550 blink_manager.disable(cx);
1551 }
1552 });
1553 }),
1554 ],
1555 tasks_update_task: None,
1556 linked_edit_ranges: Default::default(),
1557 in_project_search: false,
1558 previous_search_ranges: None,
1559 breadcrumb_header: None,
1560 focused_block: None,
1561 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1562 addons: HashMap::default(),
1563 registered_buffers: HashMap::default(),
1564 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1565 selection_mark_mode: false,
1566 toggle_fold_multiple_buffers: Task::ready(()),
1567 serialize_selections: Task::ready(()),
1568 serialize_folds: Task::ready(()),
1569 text_style_refinement: None,
1570 load_diff_task: load_uncommitted_diff,
1571 };
1572 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1573 this._subscriptions
1574 .push(cx.observe(breakpoints, |_, _, cx| {
1575 cx.notify();
1576 }));
1577 }
1578 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1579 this._subscriptions.extend(project_subscriptions);
1580
1581 this.end_selection(window, cx);
1582 this.scroll_manager.show_scrollbars(window, cx);
1583 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1584
1585 if mode == EditorMode::Full {
1586 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1587 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1588
1589 if this.git_blame_inline_enabled {
1590 this.git_blame_inline_enabled = true;
1591 this.start_git_blame_inline(false, window, cx);
1592 }
1593
1594 this.go_to_active_debug_line(window, cx);
1595
1596 if let Some(buffer) = buffer.read(cx).as_singleton() {
1597 if let Some(project) = this.project.as_ref() {
1598 let handle = project.update(cx, |project, cx| {
1599 project.register_buffer_with_language_servers(&buffer, cx)
1600 });
1601 this.registered_buffers
1602 .insert(buffer.read(cx).remote_id(), handle);
1603 }
1604 }
1605 }
1606
1607 this.report_editor_event("Editor Opened", None, cx);
1608 this
1609 }
1610
1611 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1612 self.mouse_context_menu
1613 .as_ref()
1614 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1615 }
1616
1617 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1618 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1619 }
1620
1621 fn key_context_internal(
1622 &self,
1623 has_active_edit_prediction: bool,
1624 window: &Window,
1625 cx: &App,
1626 ) -> KeyContext {
1627 let mut key_context = KeyContext::new_with_defaults();
1628 key_context.add("Editor");
1629 let mode = match self.mode {
1630 EditorMode::SingleLine { .. } => "single_line",
1631 EditorMode::AutoHeight { .. } => "auto_height",
1632 EditorMode::Full => "full",
1633 };
1634
1635 if EditorSettings::jupyter_enabled(cx) {
1636 key_context.add("jupyter");
1637 }
1638
1639 key_context.set("mode", mode);
1640 if self.pending_rename.is_some() {
1641 key_context.add("renaming");
1642 }
1643
1644 match self.context_menu.borrow().as_ref() {
1645 Some(CodeContextMenu::Completions(_)) => {
1646 key_context.add("menu");
1647 key_context.add("showing_completions");
1648 }
1649 Some(CodeContextMenu::CodeActions(_)) => {
1650 key_context.add("menu");
1651 key_context.add("showing_code_actions")
1652 }
1653 None => {}
1654 }
1655
1656 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1657 if !self.focus_handle(cx).contains_focused(window, cx)
1658 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1659 {
1660 for addon in self.addons.values() {
1661 addon.extend_key_context(&mut key_context, cx)
1662 }
1663 }
1664
1665 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1666 if let Some(extension) = singleton_buffer
1667 .read(cx)
1668 .file()
1669 .and_then(|file| file.path().extension()?.to_str())
1670 {
1671 key_context.set("extension", extension.to_string());
1672 }
1673 } else {
1674 key_context.add("multibuffer");
1675 }
1676
1677 if has_active_edit_prediction {
1678 if self.edit_prediction_in_conflict() {
1679 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1680 } else {
1681 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1682 key_context.add("copilot_suggestion");
1683 }
1684 }
1685
1686 if self.selection_mark_mode {
1687 key_context.add("selection_mode");
1688 }
1689
1690 key_context
1691 }
1692
1693 pub fn edit_prediction_in_conflict(&self) -> bool {
1694 if !self.show_edit_predictions_in_menu() {
1695 return false;
1696 }
1697
1698 let showing_completions = self
1699 .context_menu
1700 .borrow()
1701 .as_ref()
1702 .map_or(false, |context| {
1703 matches!(context, CodeContextMenu::Completions(_))
1704 });
1705
1706 showing_completions
1707 || self.edit_prediction_requires_modifier()
1708 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1709 // bindings to insert tab characters.
1710 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1711 }
1712
1713 pub fn accept_edit_prediction_keybind(
1714 &self,
1715 window: &Window,
1716 cx: &App,
1717 ) -> AcceptEditPredictionBinding {
1718 let key_context = self.key_context_internal(true, window, cx);
1719 let in_conflict = self.edit_prediction_in_conflict();
1720
1721 AcceptEditPredictionBinding(
1722 window
1723 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1724 .into_iter()
1725 .filter(|binding| {
1726 !in_conflict
1727 || binding
1728 .keystrokes()
1729 .first()
1730 .map_or(false, |keystroke| keystroke.modifiers.modified())
1731 })
1732 .rev()
1733 .min_by_key(|binding| {
1734 binding
1735 .keystrokes()
1736 .first()
1737 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1738 }),
1739 )
1740 }
1741
1742 pub fn new_file(
1743 workspace: &mut Workspace,
1744 _: &workspace::NewFile,
1745 window: &mut Window,
1746 cx: &mut Context<Workspace>,
1747 ) {
1748 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1749 "Failed to create buffer",
1750 window,
1751 cx,
1752 |e, _, _| match e.error_code() {
1753 ErrorCode::RemoteUpgradeRequired => Some(format!(
1754 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1755 e.error_tag("required").unwrap_or("the latest version")
1756 )),
1757 _ => None,
1758 },
1759 );
1760 }
1761
1762 pub fn new_in_workspace(
1763 workspace: &mut Workspace,
1764 window: &mut Window,
1765 cx: &mut Context<Workspace>,
1766 ) -> Task<Result<Entity<Editor>>> {
1767 let project = workspace.project().clone();
1768 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1769
1770 cx.spawn_in(window, async move |workspace, cx| {
1771 let buffer = create.await?;
1772 workspace.update_in(cx, |workspace, window, cx| {
1773 let editor =
1774 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1775 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1776 editor
1777 })
1778 })
1779 }
1780
1781 fn new_file_vertical(
1782 workspace: &mut Workspace,
1783 _: &workspace::NewFileSplitVertical,
1784 window: &mut Window,
1785 cx: &mut Context<Workspace>,
1786 ) {
1787 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1788 }
1789
1790 fn new_file_horizontal(
1791 workspace: &mut Workspace,
1792 _: &workspace::NewFileSplitHorizontal,
1793 window: &mut Window,
1794 cx: &mut Context<Workspace>,
1795 ) {
1796 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1797 }
1798
1799 fn new_file_in_direction(
1800 workspace: &mut Workspace,
1801 direction: SplitDirection,
1802 window: &mut Window,
1803 cx: &mut Context<Workspace>,
1804 ) {
1805 let project = workspace.project().clone();
1806 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1807
1808 cx.spawn_in(window, async move |workspace, cx| {
1809 let buffer = create.await?;
1810 workspace.update_in(cx, move |workspace, window, cx| {
1811 workspace.split_item(
1812 direction,
1813 Box::new(
1814 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1815 ),
1816 window,
1817 cx,
1818 )
1819 })?;
1820 anyhow::Ok(())
1821 })
1822 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1823 match e.error_code() {
1824 ErrorCode::RemoteUpgradeRequired => Some(format!(
1825 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1826 e.error_tag("required").unwrap_or("the latest version")
1827 )),
1828 _ => None,
1829 }
1830 });
1831 }
1832
1833 pub fn leader_peer_id(&self) -> Option<PeerId> {
1834 self.leader_peer_id
1835 }
1836
1837 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1838 &self.buffer
1839 }
1840
1841 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1842 self.workspace.as_ref()?.0.upgrade()
1843 }
1844
1845 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1846 self.buffer().read(cx).title(cx)
1847 }
1848
1849 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1850 let git_blame_gutter_max_author_length = self
1851 .render_git_blame_gutter(cx)
1852 .then(|| {
1853 if let Some(blame) = self.blame.as_ref() {
1854 let max_author_length =
1855 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1856 Some(max_author_length)
1857 } else {
1858 None
1859 }
1860 })
1861 .flatten();
1862
1863 EditorSnapshot {
1864 mode: self.mode,
1865 show_gutter: self.show_gutter,
1866 show_line_numbers: self.show_line_numbers,
1867 show_git_diff_gutter: self.show_git_diff_gutter,
1868 show_code_actions: self.show_code_actions,
1869 show_runnables: self.show_runnables,
1870 show_breakpoints: self.show_breakpoints,
1871 git_blame_gutter_max_author_length,
1872 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1873 scroll_anchor: self.scroll_manager.anchor(),
1874 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1875 placeholder_text: self.placeholder_text.clone(),
1876 is_focused: self.focus_handle.is_focused(window),
1877 current_line_highlight: self
1878 .current_line_highlight
1879 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1880 gutter_hovered: self.gutter_hovered,
1881 }
1882 }
1883
1884 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1885 self.buffer.read(cx).language_at(point, cx)
1886 }
1887
1888 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1889 self.buffer.read(cx).read(cx).file_at(point).cloned()
1890 }
1891
1892 pub fn active_excerpt(
1893 &self,
1894 cx: &App,
1895 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1896 self.buffer
1897 .read(cx)
1898 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1899 }
1900
1901 pub fn mode(&self) -> EditorMode {
1902 self.mode
1903 }
1904
1905 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1906 self.collaboration_hub.as_deref()
1907 }
1908
1909 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1910 self.collaboration_hub = Some(hub);
1911 }
1912
1913 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1914 self.in_project_search = in_project_search;
1915 }
1916
1917 pub fn set_custom_context_menu(
1918 &mut self,
1919 f: impl 'static
1920 + Fn(
1921 &mut Self,
1922 DisplayPoint,
1923 &mut Window,
1924 &mut Context<Self>,
1925 ) -> Option<Entity<ui::ContextMenu>>,
1926 ) {
1927 self.custom_context_menu = Some(Box::new(f))
1928 }
1929
1930 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1931 self.completion_provider = provider;
1932 }
1933
1934 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1935 self.semantics_provider.clone()
1936 }
1937
1938 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1939 self.semantics_provider = provider;
1940 }
1941
1942 pub fn set_edit_prediction_provider<T>(
1943 &mut self,
1944 provider: Option<Entity<T>>,
1945 window: &mut Window,
1946 cx: &mut Context<Self>,
1947 ) where
1948 T: EditPredictionProvider,
1949 {
1950 self.edit_prediction_provider =
1951 provider.map(|provider| RegisteredInlineCompletionProvider {
1952 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1953 if this.focus_handle.is_focused(window) {
1954 this.update_visible_inline_completion(window, cx);
1955 }
1956 }),
1957 provider: Arc::new(provider),
1958 });
1959 self.update_edit_prediction_settings(cx);
1960 self.refresh_inline_completion(false, false, window, cx);
1961 }
1962
1963 pub fn placeholder_text(&self) -> Option<&str> {
1964 self.placeholder_text.as_deref()
1965 }
1966
1967 pub fn set_placeholder_text(
1968 &mut self,
1969 placeholder_text: impl Into<Arc<str>>,
1970 cx: &mut Context<Self>,
1971 ) {
1972 let placeholder_text = Some(placeholder_text.into());
1973 if self.placeholder_text != placeholder_text {
1974 self.placeholder_text = placeholder_text;
1975 cx.notify();
1976 }
1977 }
1978
1979 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1980 self.cursor_shape = cursor_shape;
1981
1982 // Disrupt blink for immediate user feedback that the cursor shape has changed
1983 self.blink_manager.update(cx, BlinkManager::show_cursor);
1984
1985 cx.notify();
1986 }
1987
1988 pub fn set_current_line_highlight(
1989 &mut self,
1990 current_line_highlight: Option<CurrentLineHighlight>,
1991 ) {
1992 self.current_line_highlight = current_line_highlight;
1993 }
1994
1995 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1996 self.collapse_matches = collapse_matches;
1997 }
1998
1999 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2000 let buffers = self.buffer.read(cx).all_buffers();
2001 let Some(project) = self.project.as_ref() else {
2002 return;
2003 };
2004 project.update(cx, |project, cx| {
2005 for buffer in buffers {
2006 self.registered_buffers
2007 .entry(buffer.read(cx).remote_id())
2008 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2009 }
2010 })
2011 }
2012
2013 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2014 if self.collapse_matches {
2015 return range.start..range.start;
2016 }
2017 range.clone()
2018 }
2019
2020 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2021 if self.display_map.read(cx).clip_at_line_ends != clip {
2022 self.display_map
2023 .update(cx, |map, _| map.clip_at_line_ends = clip);
2024 }
2025 }
2026
2027 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2028 self.input_enabled = input_enabled;
2029 }
2030
2031 pub fn set_inline_completions_hidden_for_vim_mode(
2032 &mut self,
2033 hidden: bool,
2034 window: &mut Window,
2035 cx: &mut Context<Self>,
2036 ) {
2037 if hidden != self.inline_completions_hidden_for_vim_mode {
2038 self.inline_completions_hidden_for_vim_mode = hidden;
2039 if hidden {
2040 self.update_visible_inline_completion(window, cx);
2041 } else {
2042 self.refresh_inline_completion(true, false, window, cx);
2043 }
2044 }
2045 }
2046
2047 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2048 self.menu_inline_completions_policy = value;
2049 }
2050
2051 pub fn set_autoindent(&mut self, autoindent: bool) {
2052 if autoindent {
2053 self.autoindent_mode = Some(AutoindentMode::EachLine);
2054 } else {
2055 self.autoindent_mode = None;
2056 }
2057 }
2058
2059 pub fn read_only(&self, cx: &App) -> bool {
2060 self.read_only || self.buffer.read(cx).read_only()
2061 }
2062
2063 pub fn set_read_only(&mut self, read_only: bool) {
2064 self.read_only = read_only;
2065 }
2066
2067 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2068 self.use_autoclose = autoclose;
2069 }
2070
2071 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2072 self.use_auto_surround = auto_surround;
2073 }
2074
2075 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2076 self.auto_replace_emoji_shortcode = auto_replace;
2077 }
2078
2079 pub fn toggle_edit_predictions(
2080 &mut self,
2081 _: &ToggleEditPrediction,
2082 window: &mut Window,
2083 cx: &mut Context<Self>,
2084 ) {
2085 if self.show_inline_completions_override.is_some() {
2086 self.set_show_edit_predictions(None, window, cx);
2087 } else {
2088 let show_edit_predictions = !self.edit_predictions_enabled();
2089 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2090 }
2091 }
2092
2093 pub fn set_show_edit_predictions(
2094 &mut self,
2095 show_edit_predictions: Option<bool>,
2096 window: &mut Window,
2097 cx: &mut Context<Self>,
2098 ) {
2099 self.show_inline_completions_override = show_edit_predictions;
2100 self.update_edit_prediction_settings(cx);
2101
2102 if let Some(false) = show_edit_predictions {
2103 self.discard_inline_completion(false, cx);
2104 } else {
2105 self.refresh_inline_completion(false, true, window, cx);
2106 }
2107 }
2108
2109 fn inline_completions_disabled_in_scope(
2110 &self,
2111 buffer: &Entity<Buffer>,
2112 buffer_position: language::Anchor,
2113 cx: &App,
2114 ) -> bool {
2115 let snapshot = buffer.read(cx).snapshot();
2116 let settings = snapshot.settings_at(buffer_position, cx);
2117
2118 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2119 return false;
2120 };
2121
2122 scope.override_name().map_or(false, |scope_name| {
2123 settings
2124 .edit_predictions_disabled_in
2125 .iter()
2126 .any(|s| s == scope_name)
2127 })
2128 }
2129
2130 pub fn set_use_modal_editing(&mut self, to: bool) {
2131 self.use_modal_editing = to;
2132 }
2133
2134 pub fn use_modal_editing(&self) -> bool {
2135 self.use_modal_editing
2136 }
2137
2138 fn selections_did_change(
2139 &mut self,
2140 local: bool,
2141 old_cursor_position: &Anchor,
2142 show_completions: bool,
2143 window: &mut Window,
2144 cx: &mut Context<Self>,
2145 ) {
2146 window.invalidate_character_coordinates();
2147
2148 // Copy selections to primary selection buffer
2149 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2150 if local {
2151 let selections = self.selections.all::<usize>(cx);
2152 let buffer_handle = self.buffer.read(cx).read(cx);
2153
2154 let mut text = String::new();
2155 for (index, selection) in selections.iter().enumerate() {
2156 let text_for_selection = buffer_handle
2157 .text_for_range(selection.start..selection.end)
2158 .collect::<String>();
2159
2160 text.push_str(&text_for_selection);
2161 if index != selections.len() - 1 {
2162 text.push('\n');
2163 }
2164 }
2165
2166 if !text.is_empty() {
2167 cx.write_to_primary(ClipboardItem::new_string(text));
2168 }
2169 }
2170
2171 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2172 self.buffer.update(cx, |buffer, cx| {
2173 buffer.set_active_selections(
2174 &self.selections.disjoint_anchors(),
2175 self.selections.line_mode,
2176 self.cursor_shape,
2177 cx,
2178 )
2179 });
2180 }
2181 let display_map = self
2182 .display_map
2183 .update(cx, |display_map, cx| display_map.snapshot(cx));
2184 let buffer = &display_map.buffer_snapshot;
2185 self.add_selections_state = None;
2186 self.select_next_state = None;
2187 self.select_prev_state = None;
2188 self.select_syntax_node_history.try_clear();
2189 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2190 self.snippet_stack
2191 .invalidate(&self.selections.disjoint_anchors(), buffer);
2192 self.take_rename(false, window, cx);
2193
2194 let new_cursor_position = self.selections.newest_anchor().head();
2195
2196 self.push_to_nav_history(
2197 *old_cursor_position,
2198 Some(new_cursor_position.to_point(buffer)),
2199 false,
2200 cx,
2201 );
2202
2203 if local {
2204 let new_cursor_position = self.selections.newest_anchor().head();
2205 let mut context_menu = self.context_menu.borrow_mut();
2206 let completion_menu = match context_menu.as_ref() {
2207 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2208 _ => {
2209 *context_menu = None;
2210 None
2211 }
2212 };
2213 if let Some(buffer_id) = new_cursor_position.buffer_id {
2214 if !self.registered_buffers.contains_key(&buffer_id) {
2215 if let Some(project) = self.project.as_ref() {
2216 project.update(cx, |project, cx| {
2217 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2218 return;
2219 };
2220 self.registered_buffers.insert(
2221 buffer_id,
2222 project.register_buffer_with_language_servers(&buffer, cx),
2223 );
2224 })
2225 }
2226 }
2227 }
2228
2229 if let Some(completion_menu) = completion_menu {
2230 let cursor_position = new_cursor_position.to_offset(buffer);
2231 let (word_range, kind) =
2232 buffer.surrounding_word(completion_menu.initial_position, true);
2233 if kind == Some(CharKind::Word)
2234 && word_range.to_inclusive().contains(&cursor_position)
2235 {
2236 let mut completion_menu = completion_menu.clone();
2237 drop(context_menu);
2238
2239 let query = Self::completion_query(buffer, cursor_position);
2240 cx.spawn(async move |this, cx| {
2241 completion_menu
2242 .filter(query.as_deref(), cx.background_executor().clone())
2243 .await;
2244
2245 this.update(cx, |this, cx| {
2246 let mut context_menu = this.context_menu.borrow_mut();
2247 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2248 else {
2249 return;
2250 };
2251
2252 if menu.id > completion_menu.id {
2253 return;
2254 }
2255
2256 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2257 drop(context_menu);
2258 cx.notify();
2259 })
2260 })
2261 .detach();
2262
2263 if show_completions {
2264 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2265 }
2266 } else {
2267 drop(context_menu);
2268 self.hide_context_menu(window, cx);
2269 }
2270 } else {
2271 drop(context_menu);
2272 }
2273
2274 hide_hover(self, cx);
2275
2276 if old_cursor_position.to_display_point(&display_map).row()
2277 != new_cursor_position.to_display_point(&display_map).row()
2278 {
2279 self.available_code_actions.take();
2280 }
2281 self.refresh_code_actions(window, cx);
2282 self.refresh_document_highlights(cx);
2283 self.refresh_selected_text_highlights(window, cx);
2284 refresh_matching_bracket_highlights(self, window, cx);
2285 self.update_visible_inline_completion(window, cx);
2286 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2287 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2288 if self.git_blame_inline_enabled {
2289 self.start_inline_blame_timer(window, cx);
2290 }
2291 }
2292
2293 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2294 cx.emit(EditorEvent::SelectionsChanged { local });
2295
2296 let selections = &self.selections.disjoint;
2297 if selections.len() == 1 {
2298 cx.emit(SearchEvent::ActiveMatchChanged)
2299 }
2300 if local
2301 && self.is_singleton(cx)
2302 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2303 {
2304 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2305 let background_executor = cx.background_executor().clone();
2306 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2307 let snapshot = self.buffer().read(cx).snapshot(cx);
2308 let selections = selections.clone();
2309 self.serialize_selections = cx.background_spawn(async move {
2310 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2311 let selections = selections
2312 .iter()
2313 .map(|selection| {
2314 (
2315 selection.start.to_offset(&snapshot),
2316 selection.end.to_offset(&snapshot),
2317 )
2318 })
2319 .collect();
2320
2321 DB.save_editor_selections(editor_id, workspace_id, selections)
2322 .await
2323 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2324 .log_err();
2325 });
2326 }
2327 }
2328
2329 cx.notify();
2330 }
2331
2332 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
2333 if !self.is_singleton(cx)
2334 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
2335 {
2336 return;
2337 }
2338
2339 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
2340 return;
2341 };
2342 let background_executor = cx.background_executor().clone();
2343 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2344 let snapshot = self.buffer().read(cx).snapshot(cx);
2345 let folds = self.display_map.update(cx, |display_map, cx| {
2346 display_map
2347 .snapshot(cx)
2348 .folds_in_range(0..snapshot.len())
2349 .map(|fold| {
2350 (
2351 fold.range.start.to_offset(&snapshot),
2352 fold.range.end.to_offset(&snapshot),
2353 )
2354 })
2355 .collect()
2356 });
2357 self.serialize_folds = cx.background_spawn(async move {
2358 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2359 DB.save_editor_folds(editor_id, workspace_id, folds)
2360 .await
2361 .with_context(|| format!("persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"))
2362 .log_err();
2363 });
2364 }
2365
2366 pub fn sync_selections(
2367 &mut self,
2368 other: Entity<Editor>,
2369 cx: &mut Context<Self>,
2370 ) -> gpui::Subscription {
2371 let other_selections = other.read(cx).selections.disjoint.to_vec();
2372 self.selections.change_with(cx, |selections| {
2373 selections.select_anchors(other_selections);
2374 });
2375
2376 let other_subscription =
2377 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2378 EditorEvent::SelectionsChanged { local: true } => {
2379 let other_selections = other.read(cx).selections.disjoint.to_vec();
2380 if other_selections.is_empty() {
2381 return;
2382 }
2383 this.selections.change_with(cx, |selections| {
2384 selections.select_anchors(other_selections);
2385 });
2386 }
2387 _ => {}
2388 });
2389
2390 let this_subscription =
2391 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2392 EditorEvent::SelectionsChanged { local: true } => {
2393 let these_selections = this.selections.disjoint.to_vec();
2394 if these_selections.is_empty() {
2395 return;
2396 }
2397 other.update(cx, |other_editor, cx| {
2398 other_editor.selections.change_with(cx, |selections| {
2399 selections.select_anchors(these_selections);
2400 })
2401 });
2402 }
2403 _ => {}
2404 });
2405
2406 Subscription::join(other_subscription, this_subscription)
2407 }
2408
2409 pub fn change_selections<R>(
2410 &mut self,
2411 autoscroll: Option<Autoscroll>,
2412 window: &mut Window,
2413 cx: &mut Context<Self>,
2414 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2415 ) -> R {
2416 self.change_selections_inner(autoscroll, true, window, cx, change)
2417 }
2418
2419 fn change_selections_inner<R>(
2420 &mut self,
2421 autoscroll: Option<Autoscroll>,
2422 request_completions: bool,
2423 window: &mut Window,
2424 cx: &mut Context<Self>,
2425 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2426 ) -> R {
2427 let old_cursor_position = self.selections.newest_anchor().head();
2428 self.push_to_selection_history();
2429
2430 let (changed, result) = self.selections.change_with(cx, change);
2431
2432 if changed {
2433 if let Some(autoscroll) = autoscroll {
2434 self.request_autoscroll(autoscroll, cx);
2435 }
2436 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2437
2438 if self.should_open_signature_help_automatically(
2439 &old_cursor_position,
2440 self.signature_help_state.backspace_pressed(),
2441 cx,
2442 ) {
2443 self.show_signature_help(&ShowSignatureHelp, window, cx);
2444 }
2445 self.signature_help_state.set_backspace_pressed(false);
2446 }
2447
2448 result
2449 }
2450
2451 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2452 where
2453 I: IntoIterator<Item = (Range<S>, T)>,
2454 S: ToOffset,
2455 T: Into<Arc<str>>,
2456 {
2457 if self.read_only(cx) {
2458 return;
2459 }
2460
2461 self.buffer
2462 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2463 }
2464
2465 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2466 where
2467 I: IntoIterator<Item = (Range<S>, T)>,
2468 S: ToOffset,
2469 T: Into<Arc<str>>,
2470 {
2471 if self.read_only(cx) {
2472 return;
2473 }
2474
2475 self.buffer.update(cx, |buffer, cx| {
2476 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2477 });
2478 }
2479
2480 pub fn edit_with_block_indent<I, S, T>(
2481 &mut self,
2482 edits: I,
2483 original_indent_columns: Vec<Option<u32>>,
2484 cx: &mut Context<Self>,
2485 ) where
2486 I: IntoIterator<Item = (Range<S>, T)>,
2487 S: ToOffset,
2488 T: Into<Arc<str>>,
2489 {
2490 if self.read_only(cx) {
2491 return;
2492 }
2493
2494 self.buffer.update(cx, |buffer, cx| {
2495 buffer.edit(
2496 edits,
2497 Some(AutoindentMode::Block {
2498 original_indent_columns,
2499 }),
2500 cx,
2501 )
2502 });
2503 }
2504
2505 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2506 self.hide_context_menu(window, cx);
2507
2508 match phase {
2509 SelectPhase::Begin {
2510 position,
2511 add,
2512 click_count,
2513 } => self.begin_selection(position, add, click_count, window, cx),
2514 SelectPhase::BeginColumnar {
2515 position,
2516 goal_column,
2517 reset,
2518 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2519 SelectPhase::Extend {
2520 position,
2521 click_count,
2522 } => self.extend_selection(position, click_count, window, cx),
2523 SelectPhase::Update {
2524 position,
2525 goal_column,
2526 scroll_delta,
2527 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2528 SelectPhase::End => self.end_selection(window, cx),
2529 }
2530 }
2531
2532 fn extend_selection(
2533 &mut self,
2534 position: DisplayPoint,
2535 click_count: usize,
2536 window: &mut Window,
2537 cx: &mut Context<Self>,
2538 ) {
2539 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2540 let tail = self.selections.newest::<usize>(cx).tail();
2541 self.begin_selection(position, false, click_count, window, cx);
2542
2543 let position = position.to_offset(&display_map, Bias::Left);
2544 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2545
2546 let mut pending_selection = self
2547 .selections
2548 .pending_anchor()
2549 .expect("extend_selection not called with pending selection");
2550 if position >= tail {
2551 pending_selection.start = tail_anchor;
2552 } else {
2553 pending_selection.end = tail_anchor;
2554 pending_selection.reversed = true;
2555 }
2556
2557 let mut pending_mode = self.selections.pending_mode().unwrap();
2558 match &mut pending_mode {
2559 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2560 _ => {}
2561 }
2562
2563 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2564 s.set_pending(pending_selection, pending_mode)
2565 });
2566 }
2567
2568 fn begin_selection(
2569 &mut self,
2570 position: DisplayPoint,
2571 add: bool,
2572 click_count: usize,
2573 window: &mut Window,
2574 cx: &mut Context<Self>,
2575 ) {
2576 if !self.focus_handle.is_focused(window) {
2577 self.last_focused_descendant = None;
2578 window.focus(&self.focus_handle);
2579 }
2580
2581 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2582 let buffer = &display_map.buffer_snapshot;
2583 let newest_selection = self.selections.newest_anchor().clone();
2584 let position = display_map.clip_point(position, Bias::Left);
2585
2586 let start;
2587 let end;
2588 let mode;
2589 let mut auto_scroll;
2590 match click_count {
2591 1 => {
2592 start = buffer.anchor_before(position.to_point(&display_map));
2593 end = start;
2594 mode = SelectMode::Character;
2595 auto_scroll = true;
2596 }
2597 2 => {
2598 let range = movement::surrounding_word(&display_map, position);
2599 start = buffer.anchor_before(range.start.to_point(&display_map));
2600 end = buffer.anchor_before(range.end.to_point(&display_map));
2601 mode = SelectMode::Word(start..end);
2602 auto_scroll = true;
2603 }
2604 3 => {
2605 let position = display_map
2606 .clip_point(position, Bias::Left)
2607 .to_point(&display_map);
2608 let line_start = display_map.prev_line_boundary(position).0;
2609 let next_line_start = buffer.clip_point(
2610 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2611 Bias::Left,
2612 );
2613 start = buffer.anchor_before(line_start);
2614 end = buffer.anchor_before(next_line_start);
2615 mode = SelectMode::Line(start..end);
2616 auto_scroll = true;
2617 }
2618 _ => {
2619 start = buffer.anchor_before(0);
2620 end = buffer.anchor_before(buffer.len());
2621 mode = SelectMode::All;
2622 auto_scroll = false;
2623 }
2624 }
2625 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2626
2627 let point_to_delete: Option<usize> = {
2628 let selected_points: Vec<Selection<Point>> =
2629 self.selections.disjoint_in_range(start..end, cx);
2630
2631 if !add || click_count > 1 {
2632 None
2633 } else if !selected_points.is_empty() {
2634 Some(selected_points[0].id)
2635 } else {
2636 let clicked_point_already_selected =
2637 self.selections.disjoint.iter().find(|selection| {
2638 selection.start.to_point(buffer) == start.to_point(buffer)
2639 || selection.end.to_point(buffer) == end.to_point(buffer)
2640 });
2641
2642 clicked_point_already_selected.map(|selection| selection.id)
2643 }
2644 };
2645
2646 let selections_count = self.selections.count();
2647
2648 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2649 if let Some(point_to_delete) = point_to_delete {
2650 s.delete(point_to_delete);
2651
2652 if selections_count == 1 {
2653 s.set_pending_anchor_range(start..end, mode);
2654 }
2655 } else {
2656 if !add {
2657 s.clear_disjoint();
2658 } else if click_count > 1 {
2659 s.delete(newest_selection.id)
2660 }
2661
2662 s.set_pending_anchor_range(start..end, mode);
2663 }
2664 });
2665 }
2666
2667 fn begin_columnar_selection(
2668 &mut self,
2669 position: DisplayPoint,
2670 goal_column: u32,
2671 reset: bool,
2672 window: &mut Window,
2673 cx: &mut Context<Self>,
2674 ) {
2675 if !self.focus_handle.is_focused(window) {
2676 self.last_focused_descendant = None;
2677 window.focus(&self.focus_handle);
2678 }
2679
2680 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2681
2682 if reset {
2683 let pointer_position = display_map
2684 .buffer_snapshot
2685 .anchor_before(position.to_point(&display_map));
2686
2687 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2688 s.clear_disjoint();
2689 s.set_pending_anchor_range(
2690 pointer_position..pointer_position,
2691 SelectMode::Character,
2692 );
2693 });
2694 }
2695
2696 let tail = self.selections.newest::<Point>(cx).tail();
2697 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2698
2699 if !reset {
2700 self.select_columns(
2701 tail.to_display_point(&display_map),
2702 position,
2703 goal_column,
2704 &display_map,
2705 window,
2706 cx,
2707 );
2708 }
2709 }
2710
2711 fn update_selection(
2712 &mut self,
2713 position: DisplayPoint,
2714 goal_column: u32,
2715 scroll_delta: gpui::Point<f32>,
2716 window: &mut Window,
2717 cx: &mut Context<Self>,
2718 ) {
2719 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2720
2721 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2722 let tail = tail.to_display_point(&display_map);
2723 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2724 } else if let Some(mut pending) = self.selections.pending_anchor() {
2725 let buffer = self.buffer.read(cx).snapshot(cx);
2726 let head;
2727 let tail;
2728 let mode = self.selections.pending_mode().unwrap();
2729 match &mode {
2730 SelectMode::Character => {
2731 head = position.to_point(&display_map);
2732 tail = pending.tail().to_point(&buffer);
2733 }
2734 SelectMode::Word(original_range) => {
2735 let original_display_range = original_range.start.to_display_point(&display_map)
2736 ..original_range.end.to_display_point(&display_map);
2737 let original_buffer_range = original_display_range.start.to_point(&display_map)
2738 ..original_display_range.end.to_point(&display_map);
2739 if movement::is_inside_word(&display_map, position)
2740 || original_display_range.contains(&position)
2741 {
2742 let word_range = movement::surrounding_word(&display_map, position);
2743 if word_range.start < original_display_range.start {
2744 head = word_range.start.to_point(&display_map);
2745 } else {
2746 head = word_range.end.to_point(&display_map);
2747 }
2748 } else {
2749 head = position.to_point(&display_map);
2750 }
2751
2752 if head <= original_buffer_range.start {
2753 tail = original_buffer_range.end;
2754 } else {
2755 tail = original_buffer_range.start;
2756 }
2757 }
2758 SelectMode::Line(original_range) => {
2759 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2760
2761 let position = display_map
2762 .clip_point(position, Bias::Left)
2763 .to_point(&display_map);
2764 let line_start = display_map.prev_line_boundary(position).0;
2765 let next_line_start = buffer.clip_point(
2766 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2767 Bias::Left,
2768 );
2769
2770 if line_start < original_range.start {
2771 head = line_start
2772 } else {
2773 head = next_line_start
2774 }
2775
2776 if head <= original_range.start {
2777 tail = original_range.end;
2778 } else {
2779 tail = original_range.start;
2780 }
2781 }
2782 SelectMode::All => {
2783 return;
2784 }
2785 };
2786
2787 if head < tail {
2788 pending.start = buffer.anchor_before(head);
2789 pending.end = buffer.anchor_before(tail);
2790 pending.reversed = true;
2791 } else {
2792 pending.start = buffer.anchor_before(tail);
2793 pending.end = buffer.anchor_before(head);
2794 pending.reversed = false;
2795 }
2796
2797 self.change_selections(None, window, cx, |s| {
2798 s.set_pending(pending, mode);
2799 });
2800 } else {
2801 log::error!("update_selection dispatched with no pending selection");
2802 return;
2803 }
2804
2805 self.apply_scroll_delta(scroll_delta, window, cx);
2806 cx.notify();
2807 }
2808
2809 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2810 self.columnar_selection_tail.take();
2811 if self.selections.pending_anchor().is_some() {
2812 let selections = self.selections.all::<usize>(cx);
2813 self.change_selections(None, window, cx, |s| {
2814 s.select(selections);
2815 s.clear_pending();
2816 });
2817 }
2818 }
2819
2820 fn select_columns(
2821 &mut self,
2822 tail: DisplayPoint,
2823 head: DisplayPoint,
2824 goal_column: u32,
2825 display_map: &DisplaySnapshot,
2826 window: &mut Window,
2827 cx: &mut Context<Self>,
2828 ) {
2829 let start_row = cmp::min(tail.row(), head.row());
2830 let end_row = cmp::max(tail.row(), head.row());
2831 let start_column = cmp::min(tail.column(), goal_column);
2832 let end_column = cmp::max(tail.column(), goal_column);
2833 let reversed = start_column < tail.column();
2834
2835 let selection_ranges = (start_row.0..=end_row.0)
2836 .map(DisplayRow)
2837 .filter_map(|row| {
2838 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2839 let start = display_map
2840 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2841 .to_point(display_map);
2842 let end = display_map
2843 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2844 .to_point(display_map);
2845 if reversed {
2846 Some(end..start)
2847 } else {
2848 Some(start..end)
2849 }
2850 } else {
2851 None
2852 }
2853 })
2854 .collect::<Vec<_>>();
2855
2856 self.change_selections(None, window, cx, |s| {
2857 s.select_ranges(selection_ranges);
2858 });
2859 cx.notify();
2860 }
2861
2862 pub fn has_pending_nonempty_selection(&self) -> bool {
2863 let pending_nonempty_selection = match self.selections.pending_anchor() {
2864 Some(Selection { start, end, .. }) => start != end,
2865 None => false,
2866 };
2867
2868 pending_nonempty_selection
2869 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2870 }
2871
2872 pub fn has_pending_selection(&self) -> bool {
2873 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2874 }
2875
2876 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2877 self.selection_mark_mode = false;
2878
2879 if self.clear_expanded_diff_hunks(cx) {
2880 cx.notify();
2881 return;
2882 }
2883 if self.dismiss_menus_and_popups(true, window, cx) {
2884 return;
2885 }
2886
2887 if self.mode == EditorMode::Full
2888 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2889 {
2890 return;
2891 }
2892
2893 cx.propagate();
2894 }
2895
2896 pub fn dismiss_menus_and_popups(
2897 &mut self,
2898 is_user_requested: bool,
2899 window: &mut Window,
2900 cx: &mut Context<Self>,
2901 ) -> bool {
2902 if self.take_rename(false, window, cx).is_some() {
2903 return true;
2904 }
2905
2906 if hide_hover(self, cx) {
2907 return true;
2908 }
2909
2910 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2911 return true;
2912 }
2913
2914 if self.hide_context_menu(window, cx).is_some() {
2915 return true;
2916 }
2917
2918 if self.mouse_context_menu.take().is_some() {
2919 return true;
2920 }
2921
2922 if is_user_requested && self.discard_inline_completion(true, cx) {
2923 return true;
2924 }
2925
2926 if self.snippet_stack.pop().is_some() {
2927 return true;
2928 }
2929
2930 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2931 self.dismiss_diagnostics(cx);
2932 return true;
2933 }
2934
2935 false
2936 }
2937
2938 fn linked_editing_ranges_for(
2939 &self,
2940 selection: Range<text::Anchor>,
2941 cx: &App,
2942 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2943 if self.linked_edit_ranges.is_empty() {
2944 return None;
2945 }
2946 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2947 selection.end.buffer_id.and_then(|end_buffer_id| {
2948 if selection.start.buffer_id != Some(end_buffer_id) {
2949 return None;
2950 }
2951 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2952 let snapshot = buffer.read(cx).snapshot();
2953 self.linked_edit_ranges
2954 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2955 .map(|ranges| (ranges, snapshot, buffer))
2956 })?;
2957 use text::ToOffset as TO;
2958 // find offset from the start of current range to current cursor position
2959 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2960
2961 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2962 let start_difference = start_offset - start_byte_offset;
2963 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2964 let end_difference = end_offset - start_byte_offset;
2965 // Current range has associated linked ranges.
2966 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2967 for range in linked_ranges.iter() {
2968 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2969 let end_offset = start_offset + end_difference;
2970 let start_offset = start_offset + start_difference;
2971 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2972 continue;
2973 }
2974 if self.selections.disjoint_anchor_ranges().any(|s| {
2975 if s.start.buffer_id != selection.start.buffer_id
2976 || s.end.buffer_id != selection.end.buffer_id
2977 {
2978 return false;
2979 }
2980 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2981 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2982 }) {
2983 continue;
2984 }
2985 let start = buffer_snapshot.anchor_after(start_offset);
2986 let end = buffer_snapshot.anchor_after(end_offset);
2987 linked_edits
2988 .entry(buffer.clone())
2989 .or_default()
2990 .push(start..end);
2991 }
2992 Some(linked_edits)
2993 }
2994
2995 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2996 let text: Arc<str> = text.into();
2997
2998 if self.read_only(cx) {
2999 return;
3000 }
3001
3002 let selections = self.selections.all_adjusted(cx);
3003 let mut bracket_inserted = false;
3004 let mut edits = Vec::new();
3005 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3006 let mut new_selections = Vec::with_capacity(selections.len());
3007 let mut new_autoclose_regions = Vec::new();
3008 let snapshot = self.buffer.read(cx).read(cx);
3009
3010 for (selection, autoclose_region) in
3011 self.selections_with_autoclose_regions(selections, &snapshot)
3012 {
3013 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3014 // Determine if the inserted text matches the opening or closing
3015 // bracket of any of this language's bracket pairs.
3016 let mut bracket_pair = None;
3017 let mut is_bracket_pair_start = false;
3018 let mut is_bracket_pair_end = false;
3019 if !text.is_empty() {
3020 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3021 // and they are removing the character that triggered IME popup.
3022 for (pair, enabled) in scope.brackets() {
3023 if !pair.close && !pair.surround {
3024 continue;
3025 }
3026
3027 if enabled && pair.start.ends_with(text.as_ref()) {
3028 let prefix_len = pair.start.len() - text.len();
3029 let preceding_text_matches_prefix = prefix_len == 0
3030 || (selection.start.column >= (prefix_len as u32)
3031 && snapshot.contains_str_at(
3032 Point::new(
3033 selection.start.row,
3034 selection.start.column - (prefix_len as u32),
3035 ),
3036 &pair.start[..prefix_len],
3037 ));
3038 if preceding_text_matches_prefix {
3039 bracket_pair = Some(pair.clone());
3040 is_bracket_pair_start = true;
3041 break;
3042 }
3043 }
3044 if pair.end.as_str() == text.as_ref() {
3045 bracket_pair = Some(pair.clone());
3046 is_bracket_pair_end = true;
3047 break;
3048 }
3049 }
3050 }
3051
3052 if let Some(bracket_pair) = bracket_pair {
3053 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3054 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3055 let auto_surround =
3056 self.use_auto_surround && snapshot_settings.use_auto_surround;
3057 if selection.is_empty() {
3058 if is_bracket_pair_start {
3059 // If the inserted text is a suffix of an opening bracket and the
3060 // selection is preceded by the rest of the opening bracket, then
3061 // insert the closing bracket.
3062 let following_text_allows_autoclose = snapshot
3063 .chars_at(selection.start)
3064 .next()
3065 .map_or(true, |c| scope.should_autoclose_before(c));
3066
3067 let preceding_text_allows_autoclose = selection.start.column == 0
3068 || snapshot.reversed_chars_at(selection.start).next().map_or(
3069 true,
3070 |c| {
3071 bracket_pair.start != bracket_pair.end
3072 || !snapshot
3073 .char_classifier_at(selection.start)
3074 .is_word(c)
3075 },
3076 );
3077
3078 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3079 && bracket_pair.start.len() == 1
3080 {
3081 let target = bracket_pair.start.chars().next().unwrap();
3082 let current_line_count = snapshot
3083 .reversed_chars_at(selection.start)
3084 .take_while(|&c| c != '\n')
3085 .filter(|&c| c == target)
3086 .count();
3087 current_line_count % 2 == 1
3088 } else {
3089 false
3090 };
3091
3092 if autoclose
3093 && bracket_pair.close
3094 && following_text_allows_autoclose
3095 && preceding_text_allows_autoclose
3096 && !is_closing_quote
3097 {
3098 let anchor = snapshot.anchor_before(selection.end);
3099 new_selections.push((selection.map(|_| anchor), text.len()));
3100 new_autoclose_regions.push((
3101 anchor,
3102 text.len(),
3103 selection.id,
3104 bracket_pair.clone(),
3105 ));
3106 edits.push((
3107 selection.range(),
3108 format!("{}{}", text, bracket_pair.end).into(),
3109 ));
3110 bracket_inserted = true;
3111 continue;
3112 }
3113 }
3114
3115 if let Some(region) = autoclose_region {
3116 // If the selection is followed by an auto-inserted closing bracket,
3117 // then don't insert that closing bracket again; just move the selection
3118 // past the closing bracket.
3119 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3120 && text.as_ref() == region.pair.end.as_str();
3121 if should_skip {
3122 let anchor = snapshot.anchor_after(selection.end);
3123 new_selections
3124 .push((selection.map(|_| anchor), region.pair.end.len()));
3125 continue;
3126 }
3127 }
3128
3129 let always_treat_brackets_as_autoclosed = snapshot
3130 .language_settings_at(selection.start, cx)
3131 .always_treat_brackets_as_autoclosed;
3132 if always_treat_brackets_as_autoclosed
3133 && is_bracket_pair_end
3134 && snapshot.contains_str_at(selection.end, text.as_ref())
3135 {
3136 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3137 // and the inserted text is a closing bracket and the selection is followed
3138 // by the closing bracket then move the selection past the closing bracket.
3139 let anchor = snapshot.anchor_after(selection.end);
3140 new_selections.push((selection.map(|_| anchor), text.len()));
3141 continue;
3142 }
3143 }
3144 // If an opening bracket is 1 character long and is typed while
3145 // text is selected, then surround that text with the bracket pair.
3146 else if auto_surround
3147 && bracket_pair.surround
3148 && is_bracket_pair_start
3149 && bracket_pair.start.chars().count() == 1
3150 {
3151 edits.push((selection.start..selection.start, text.clone()));
3152 edits.push((
3153 selection.end..selection.end,
3154 bracket_pair.end.as_str().into(),
3155 ));
3156 bracket_inserted = true;
3157 new_selections.push((
3158 Selection {
3159 id: selection.id,
3160 start: snapshot.anchor_after(selection.start),
3161 end: snapshot.anchor_before(selection.end),
3162 reversed: selection.reversed,
3163 goal: selection.goal,
3164 },
3165 0,
3166 ));
3167 continue;
3168 }
3169 }
3170 }
3171
3172 if self.auto_replace_emoji_shortcode
3173 && selection.is_empty()
3174 && text.as_ref().ends_with(':')
3175 {
3176 if let Some(possible_emoji_short_code) =
3177 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3178 {
3179 if !possible_emoji_short_code.is_empty() {
3180 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3181 let emoji_shortcode_start = Point::new(
3182 selection.start.row,
3183 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3184 );
3185
3186 // Remove shortcode from buffer
3187 edits.push((
3188 emoji_shortcode_start..selection.start,
3189 "".to_string().into(),
3190 ));
3191 new_selections.push((
3192 Selection {
3193 id: selection.id,
3194 start: snapshot.anchor_after(emoji_shortcode_start),
3195 end: snapshot.anchor_before(selection.start),
3196 reversed: selection.reversed,
3197 goal: selection.goal,
3198 },
3199 0,
3200 ));
3201
3202 // Insert emoji
3203 let selection_start_anchor = snapshot.anchor_after(selection.start);
3204 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3205 edits.push((selection.start..selection.end, emoji.to_string().into()));
3206
3207 continue;
3208 }
3209 }
3210 }
3211 }
3212
3213 // If not handling any auto-close operation, then just replace the selected
3214 // text with the given input and move the selection to the end of the
3215 // newly inserted text.
3216 let anchor = snapshot.anchor_after(selection.end);
3217 if !self.linked_edit_ranges.is_empty() {
3218 let start_anchor = snapshot.anchor_before(selection.start);
3219
3220 let is_word_char = text.chars().next().map_or(true, |char| {
3221 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3222 classifier.is_word(char)
3223 });
3224
3225 if is_word_char {
3226 if let Some(ranges) = self
3227 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3228 {
3229 for (buffer, edits) in ranges {
3230 linked_edits
3231 .entry(buffer.clone())
3232 .or_default()
3233 .extend(edits.into_iter().map(|range| (range, text.clone())));
3234 }
3235 }
3236 }
3237 }
3238
3239 new_selections.push((selection.map(|_| anchor), 0));
3240 edits.push((selection.start..selection.end, text.clone()));
3241 }
3242
3243 drop(snapshot);
3244
3245 self.transact(window, cx, |this, window, cx| {
3246 let initial_buffer_versions =
3247 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3248
3249 this.buffer.update(cx, |buffer, cx| {
3250 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3251 });
3252 for (buffer, edits) in linked_edits {
3253 buffer.update(cx, |buffer, cx| {
3254 let snapshot = buffer.snapshot();
3255 let edits = edits
3256 .into_iter()
3257 .map(|(range, text)| {
3258 use text::ToPoint as TP;
3259 let end_point = TP::to_point(&range.end, &snapshot);
3260 let start_point = TP::to_point(&range.start, &snapshot);
3261 (start_point..end_point, text)
3262 })
3263 .sorted_by_key(|(range, _)| range.start);
3264 buffer.edit(edits, None, cx);
3265 })
3266 }
3267 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3268 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3269 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3270 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3271 .zip(new_selection_deltas)
3272 .map(|(selection, delta)| Selection {
3273 id: selection.id,
3274 start: selection.start + delta,
3275 end: selection.end + delta,
3276 reversed: selection.reversed,
3277 goal: SelectionGoal::None,
3278 })
3279 .collect::<Vec<_>>();
3280
3281 let mut i = 0;
3282 for (position, delta, selection_id, pair) in new_autoclose_regions {
3283 let position = position.to_offset(&map.buffer_snapshot) + delta;
3284 let start = map.buffer_snapshot.anchor_before(position);
3285 let end = map.buffer_snapshot.anchor_after(position);
3286 while let Some(existing_state) = this.autoclose_regions.get(i) {
3287 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3288 Ordering::Less => i += 1,
3289 Ordering::Greater => break,
3290 Ordering::Equal => {
3291 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3292 Ordering::Less => i += 1,
3293 Ordering::Equal => break,
3294 Ordering::Greater => break,
3295 }
3296 }
3297 }
3298 }
3299 this.autoclose_regions.insert(
3300 i,
3301 AutocloseRegion {
3302 selection_id,
3303 range: start..end,
3304 pair,
3305 },
3306 );
3307 }
3308
3309 let had_active_inline_completion = this.has_active_inline_completion();
3310 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3311 s.select(new_selections)
3312 });
3313
3314 if !bracket_inserted {
3315 if let Some(on_type_format_task) =
3316 this.trigger_on_type_formatting(text.to_string(), window, cx)
3317 {
3318 on_type_format_task.detach_and_log_err(cx);
3319 }
3320 }
3321
3322 let editor_settings = EditorSettings::get_global(cx);
3323 if bracket_inserted
3324 && (editor_settings.auto_signature_help
3325 || editor_settings.show_signature_help_after_edits)
3326 {
3327 this.show_signature_help(&ShowSignatureHelp, window, cx);
3328 }
3329
3330 let trigger_in_words =
3331 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3332 if this.hard_wrap.is_some() {
3333 let latest: Range<Point> = this.selections.newest(cx).range();
3334 if latest.is_empty()
3335 && this
3336 .buffer()
3337 .read(cx)
3338 .snapshot(cx)
3339 .line_len(MultiBufferRow(latest.start.row))
3340 == latest.start.column
3341 {
3342 this.rewrap_impl(
3343 RewrapOptions {
3344 override_language_settings: true,
3345 preserve_existing_whitespace: true,
3346 },
3347 cx,
3348 )
3349 }
3350 }
3351 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3352 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3353 this.refresh_inline_completion(true, false, window, cx);
3354 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3355 });
3356 }
3357
3358 fn find_possible_emoji_shortcode_at_position(
3359 snapshot: &MultiBufferSnapshot,
3360 position: Point,
3361 ) -> Option<String> {
3362 let mut chars = Vec::new();
3363 let mut found_colon = false;
3364 for char in snapshot.reversed_chars_at(position).take(100) {
3365 // Found a possible emoji shortcode in the middle of the buffer
3366 if found_colon {
3367 if char.is_whitespace() {
3368 chars.reverse();
3369 return Some(chars.iter().collect());
3370 }
3371 // If the previous character is not a whitespace, we are in the middle of a word
3372 // and we only want to complete the shortcode if the word is made up of other emojis
3373 let mut containing_word = String::new();
3374 for ch in snapshot
3375 .reversed_chars_at(position)
3376 .skip(chars.len() + 1)
3377 .take(100)
3378 {
3379 if ch.is_whitespace() {
3380 break;
3381 }
3382 containing_word.push(ch);
3383 }
3384 let containing_word = containing_word.chars().rev().collect::<String>();
3385 if util::word_consists_of_emojis(containing_word.as_str()) {
3386 chars.reverse();
3387 return Some(chars.iter().collect());
3388 }
3389 }
3390
3391 if char.is_whitespace() || !char.is_ascii() {
3392 return None;
3393 }
3394 if char == ':' {
3395 found_colon = true;
3396 } else {
3397 chars.push(char);
3398 }
3399 }
3400 // Found a possible emoji shortcode at the beginning of the buffer
3401 chars.reverse();
3402 Some(chars.iter().collect())
3403 }
3404
3405 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3406 self.transact(window, cx, |this, window, cx| {
3407 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3408 let selections = this.selections.all::<usize>(cx);
3409 let multi_buffer = this.buffer.read(cx);
3410 let buffer = multi_buffer.snapshot(cx);
3411 selections
3412 .iter()
3413 .map(|selection| {
3414 let start_point = selection.start.to_point(&buffer);
3415 let mut indent =
3416 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3417 indent.len = cmp::min(indent.len, start_point.column);
3418 let start = selection.start;
3419 let end = selection.end;
3420 let selection_is_empty = start == end;
3421 let language_scope = buffer.language_scope_at(start);
3422 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3423 &language_scope
3424 {
3425 let insert_extra_newline =
3426 insert_extra_newline_brackets(&buffer, start..end, language)
3427 || insert_extra_newline_tree_sitter(&buffer, start..end);
3428
3429 // Comment extension on newline is allowed only for cursor selections
3430 let comment_delimiter = maybe!({
3431 if !selection_is_empty {
3432 return None;
3433 }
3434
3435 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3436 return None;
3437 }
3438
3439 let delimiters = language.line_comment_prefixes();
3440 let max_len_of_delimiter =
3441 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3442 let (snapshot, range) =
3443 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3444
3445 let mut index_of_first_non_whitespace = 0;
3446 let comment_candidate = snapshot
3447 .chars_for_range(range)
3448 .skip_while(|c| {
3449 let should_skip = c.is_whitespace();
3450 if should_skip {
3451 index_of_first_non_whitespace += 1;
3452 }
3453 should_skip
3454 })
3455 .take(max_len_of_delimiter)
3456 .collect::<String>();
3457 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3458 comment_candidate.starts_with(comment_prefix.as_ref())
3459 })?;
3460 let cursor_is_placed_after_comment_marker =
3461 index_of_first_non_whitespace + comment_prefix.len()
3462 <= start_point.column as usize;
3463 if cursor_is_placed_after_comment_marker {
3464 Some(comment_prefix.clone())
3465 } else {
3466 None
3467 }
3468 });
3469 (comment_delimiter, insert_extra_newline)
3470 } else {
3471 (None, false)
3472 };
3473
3474 let capacity_for_delimiter = comment_delimiter
3475 .as_deref()
3476 .map(str::len)
3477 .unwrap_or_default();
3478 let mut new_text =
3479 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3480 new_text.push('\n');
3481 new_text.extend(indent.chars());
3482 if let Some(delimiter) = &comment_delimiter {
3483 new_text.push_str(delimiter);
3484 }
3485 if insert_extra_newline {
3486 new_text = new_text.repeat(2);
3487 }
3488
3489 let anchor = buffer.anchor_after(end);
3490 let new_selection = selection.map(|_| anchor);
3491 (
3492 (start..end, new_text),
3493 (insert_extra_newline, new_selection),
3494 )
3495 })
3496 .unzip()
3497 };
3498
3499 this.edit_with_autoindent(edits, cx);
3500 let buffer = this.buffer.read(cx).snapshot(cx);
3501 let new_selections = selection_fixup_info
3502 .into_iter()
3503 .map(|(extra_newline_inserted, new_selection)| {
3504 let mut cursor = new_selection.end.to_point(&buffer);
3505 if extra_newline_inserted {
3506 cursor.row -= 1;
3507 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3508 }
3509 new_selection.map(|_| cursor)
3510 })
3511 .collect();
3512
3513 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3514 s.select(new_selections)
3515 });
3516 this.refresh_inline_completion(true, false, window, cx);
3517 });
3518 }
3519
3520 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3521 let buffer = self.buffer.read(cx);
3522 let snapshot = buffer.snapshot(cx);
3523
3524 let mut edits = Vec::new();
3525 let mut rows = Vec::new();
3526
3527 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3528 let cursor = selection.head();
3529 let row = cursor.row;
3530
3531 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3532
3533 let newline = "\n".to_string();
3534 edits.push((start_of_line..start_of_line, newline));
3535
3536 rows.push(row + rows_inserted as u32);
3537 }
3538
3539 self.transact(window, cx, |editor, window, cx| {
3540 editor.edit(edits, cx);
3541
3542 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3543 let mut index = 0;
3544 s.move_cursors_with(|map, _, _| {
3545 let row = rows[index];
3546 index += 1;
3547
3548 let point = Point::new(row, 0);
3549 let boundary = map.next_line_boundary(point).1;
3550 let clipped = map.clip_point(boundary, Bias::Left);
3551
3552 (clipped, SelectionGoal::None)
3553 });
3554 });
3555
3556 let mut indent_edits = Vec::new();
3557 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3558 for row in rows {
3559 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3560 for (row, indent) in indents {
3561 if indent.len == 0 {
3562 continue;
3563 }
3564
3565 let text = match indent.kind {
3566 IndentKind::Space => " ".repeat(indent.len as usize),
3567 IndentKind::Tab => "\t".repeat(indent.len as usize),
3568 };
3569 let point = Point::new(row.0, 0);
3570 indent_edits.push((point..point, text));
3571 }
3572 }
3573 editor.edit(indent_edits, cx);
3574 });
3575 }
3576
3577 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3578 let buffer = self.buffer.read(cx);
3579 let snapshot = buffer.snapshot(cx);
3580
3581 let mut edits = Vec::new();
3582 let mut rows = Vec::new();
3583 let mut rows_inserted = 0;
3584
3585 for selection in self.selections.all_adjusted(cx) {
3586 let cursor = selection.head();
3587 let row = cursor.row;
3588
3589 let point = Point::new(row + 1, 0);
3590 let start_of_line = snapshot.clip_point(point, Bias::Left);
3591
3592 let newline = "\n".to_string();
3593 edits.push((start_of_line..start_of_line, newline));
3594
3595 rows_inserted += 1;
3596 rows.push(row + rows_inserted);
3597 }
3598
3599 self.transact(window, cx, |editor, window, cx| {
3600 editor.edit(edits, cx);
3601
3602 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3603 let mut index = 0;
3604 s.move_cursors_with(|map, _, _| {
3605 let row = rows[index];
3606 index += 1;
3607
3608 let point = Point::new(row, 0);
3609 let boundary = map.next_line_boundary(point).1;
3610 let clipped = map.clip_point(boundary, Bias::Left);
3611
3612 (clipped, SelectionGoal::None)
3613 });
3614 });
3615
3616 let mut indent_edits = Vec::new();
3617 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3618 for row in rows {
3619 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3620 for (row, indent) in indents {
3621 if indent.len == 0 {
3622 continue;
3623 }
3624
3625 let text = match indent.kind {
3626 IndentKind::Space => " ".repeat(indent.len as usize),
3627 IndentKind::Tab => "\t".repeat(indent.len as usize),
3628 };
3629 let point = Point::new(row.0, 0);
3630 indent_edits.push((point..point, text));
3631 }
3632 }
3633 editor.edit(indent_edits, cx);
3634 });
3635 }
3636
3637 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3638 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3639 original_indent_columns: Vec::new(),
3640 });
3641 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3642 }
3643
3644 fn insert_with_autoindent_mode(
3645 &mut self,
3646 text: &str,
3647 autoindent_mode: Option<AutoindentMode>,
3648 window: &mut Window,
3649 cx: &mut Context<Self>,
3650 ) {
3651 if self.read_only(cx) {
3652 return;
3653 }
3654
3655 let text: Arc<str> = text.into();
3656 self.transact(window, cx, |this, window, cx| {
3657 let old_selections = this.selections.all_adjusted(cx);
3658 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3659 let anchors = {
3660 let snapshot = buffer.read(cx);
3661 old_selections
3662 .iter()
3663 .map(|s| {
3664 let anchor = snapshot.anchor_after(s.head());
3665 s.map(|_| anchor)
3666 })
3667 .collect::<Vec<_>>()
3668 };
3669 buffer.edit(
3670 old_selections
3671 .iter()
3672 .map(|s| (s.start..s.end, text.clone())),
3673 autoindent_mode,
3674 cx,
3675 );
3676 anchors
3677 });
3678
3679 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3680 s.select_anchors(selection_anchors);
3681 });
3682
3683 cx.notify();
3684 });
3685 }
3686
3687 fn trigger_completion_on_input(
3688 &mut self,
3689 text: &str,
3690 trigger_in_words: bool,
3691 window: &mut Window,
3692 cx: &mut Context<Self>,
3693 ) {
3694 let ignore_completion_provider = self
3695 .context_menu
3696 .borrow()
3697 .as_ref()
3698 .map(|menu| match menu {
3699 CodeContextMenu::Completions(completions_menu) => {
3700 completions_menu.ignore_completion_provider
3701 }
3702 CodeContextMenu::CodeActions(_) => false,
3703 })
3704 .unwrap_or(false);
3705
3706 if ignore_completion_provider {
3707 self.show_word_completions(&ShowWordCompletions, window, cx);
3708 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3709 self.show_completions(
3710 &ShowCompletions {
3711 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3712 },
3713 window,
3714 cx,
3715 );
3716 } else {
3717 self.hide_context_menu(window, cx);
3718 }
3719 }
3720
3721 fn is_completion_trigger(
3722 &self,
3723 text: &str,
3724 trigger_in_words: bool,
3725 cx: &mut Context<Self>,
3726 ) -> bool {
3727 let position = self.selections.newest_anchor().head();
3728 let multibuffer = self.buffer.read(cx);
3729 let Some(buffer) = position
3730 .buffer_id
3731 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3732 else {
3733 return false;
3734 };
3735
3736 if let Some(completion_provider) = &self.completion_provider {
3737 completion_provider.is_completion_trigger(
3738 &buffer,
3739 position.text_anchor,
3740 text,
3741 trigger_in_words,
3742 cx,
3743 )
3744 } else {
3745 false
3746 }
3747 }
3748
3749 /// If any empty selections is touching the start of its innermost containing autoclose
3750 /// region, expand it to select the brackets.
3751 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3752 let selections = self.selections.all::<usize>(cx);
3753 let buffer = self.buffer.read(cx).read(cx);
3754 let new_selections = self
3755 .selections_with_autoclose_regions(selections, &buffer)
3756 .map(|(mut selection, region)| {
3757 if !selection.is_empty() {
3758 return selection;
3759 }
3760
3761 if let Some(region) = region {
3762 let mut range = region.range.to_offset(&buffer);
3763 if selection.start == range.start && range.start >= region.pair.start.len() {
3764 range.start -= region.pair.start.len();
3765 if buffer.contains_str_at(range.start, ®ion.pair.start)
3766 && buffer.contains_str_at(range.end, ®ion.pair.end)
3767 {
3768 range.end += region.pair.end.len();
3769 selection.start = range.start;
3770 selection.end = range.end;
3771
3772 return selection;
3773 }
3774 }
3775 }
3776
3777 let always_treat_brackets_as_autoclosed = buffer
3778 .language_settings_at(selection.start, cx)
3779 .always_treat_brackets_as_autoclosed;
3780
3781 if !always_treat_brackets_as_autoclosed {
3782 return selection;
3783 }
3784
3785 if let Some(scope) = buffer.language_scope_at(selection.start) {
3786 for (pair, enabled) in scope.brackets() {
3787 if !enabled || !pair.close {
3788 continue;
3789 }
3790
3791 if buffer.contains_str_at(selection.start, &pair.end) {
3792 let pair_start_len = pair.start.len();
3793 if buffer.contains_str_at(
3794 selection.start.saturating_sub(pair_start_len),
3795 &pair.start,
3796 ) {
3797 selection.start -= pair_start_len;
3798 selection.end += pair.end.len();
3799
3800 return selection;
3801 }
3802 }
3803 }
3804 }
3805
3806 selection
3807 })
3808 .collect();
3809
3810 drop(buffer);
3811 self.change_selections(None, window, cx, |selections| {
3812 selections.select(new_selections)
3813 });
3814 }
3815
3816 /// Iterate the given selections, and for each one, find the smallest surrounding
3817 /// autoclose region. This uses the ordering of the selections and the autoclose
3818 /// regions to avoid repeated comparisons.
3819 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3820 &'a self,
3821 selections: impl IntoIterator<Item = Selection<D>>,
3822 buffer: &'a MultiBufferSnapshot,
3823 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3824 let mut i = 0;
3825 let mut regions = self.autoclose_regions.as_slice();
3826 selections.into_iter().map(move |selection| {
3827 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3828
3829 let mut enclosing = None;
3830 while let Some(pair_state) = regions.get(i) {
3831 if pair_state.range.end.to_offset(buffer) < range.start {
3832 regions = ®ions[i + 1..];
3833 i = 0;
3834 } else if pair_state.range.start.to_offset(buffer) > range.end {
3835 break;
3836 } else {
3837 if pair_state.selection_id == selection.id {
3838 enclosing = Some(pair_state);
3839 }
3840 i += 1;
3841 }
3842 }
3843
3844 (selection, enclosing)
3845 })
3846 }
3847
3848 /// Remove any autoclose regions that no longer contain their selection.
3849 fn invalidate_autoclose_regions(
3850 &mut self,
3851 mut selections: &[Selection<Anchor>],
3852 buffer: &MultiBufferSnapshot,
3853 ) {
3854 self.autoclose_regions.retain(|state| {
3855 let mut i = 0;
3856 while let Some(selection) = selections.get(i) {
3857 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3858 selections = &selections[1..];
3859 continue;
3860 }
3861 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3862 break;
3863 }
3864 if selection.id == state.selection_id {
3865 return true;
3866 } else {
3867 i += 1;
3868 }
3869 }
3870 false
3871 });
3872 }
3873
3874 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3875 let offset = position.to_offset(buffer);
3876 let (word_range, kind) = buffer.surrounding_word(offset, true);
3877 if offset > word_range.start && kind == Some(CharKind::Word) {
3878 Some(
3879 buffer
3880 .text_for_range(word_range.start..offset)
3881 .collect::<String>(),
3882 )
3883 } else {
3884 None
3885 }
3886 }
3887
3888 pub fn toggle_inlay_hints(
3889 &mut self,
3890 _: &ToggleInlayHints,
3891 _: &mut Window,
3892 cx: &mut Context<Self>,
3893 ) {
3894 self.refresh_inlay_hints(
3895 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3896 cx,
3897 );
3898 }
3899
3900 pub fn inlay_hints_enabled(&self) -> bool {
3901 self.inlay_hint_cache.enabled
3902 }
3903
3904 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3905 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3906 return;
3907 }
3908
3909 let reason_description = reason.description();
3910 let ignore_debounce = matches!(
3911 reason,
3912 InlayHintRefreshReason::SettingsChange(_)
3913 | InlayHintRefreshReason::Toggle(_)
3914 | InlayHintRefreshReason::ExcerptsRemoved(_)
3915 | InlayHintRefreshReason::ModifiersChanged(_)
3916 );
3917 let (invalidate_cache, required_languages) = match reason {
3918 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3919 match self.inlay_hint_cache.modifiers_override(enabled) {
3920 Some(enabled) => {
3921 if enabled {
3922 (InvalidationStrategy::RefreshRequested, None)
3923 } else {
3924 self.splice_inlays(
3925 &self
3926 .visible_inlay_hints(cx)
3927 .iter()
3928 .map(|inlay| inlay.id)
3929 .collect::<Vec<InlayId>>(),
3930 Vec::new(),
3931 cx,
3932 );
3933 return;
3934 }
3935 }
3936 None => return,
3937 }
3938 }
3939 InlayHintRefreshReason::Toggle(enabled) => {
3940 if self.inlay_hint_cache.toggle(enabled) {
3941 if enabled {
3942 (InvalidationStrategy::RefreshRequested, None)
3943 } else {
3944 self.splice_inlays(
3945 &self
3946 .visible_inlay_hints(cx)
3947 .iter()
3948 .map(|inlay| inlay.id)
3949 .collect::<Vec<InlayId>>(),
3950 Vec::new(),
3951 cx,
3952 );
3953 return;
3954 }
3955 } else {
3956 return;
3957 }
3958 }
3959 InlayHintRefreshReason::SettingsChange(new_settings) => {
3960 match self.inlay_hint_cache.update_settings(
3961 &self.buffer,
3962 new_settings,
3963 self.visible_inlay_hints(cx),
3964 cx,
3965 ) {
3966 ControlFlow::Break(Some(InlaySplice {
3967 to_remove,
3968 to_insert,
3969 })) => {
3970 self.splice_inlays(&to_remove, to_insert, cx);
3971 return;
3972 }
3973 ControlFlow::Break(None) => return,
3974 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3975 }
3976 }
3977 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3978 if let Some(InlaySplice {
3979 to_remove,
3980 to_insert,
3981 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3982 {
3983 self.splice_inlays(&to_remove, to_insert, cx);
3984 }
3985 return;
3986 }
3987 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3988 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3989 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3990 }
3991 InlayHintRefreshReason::RefreshRequested => {
3992 (InvalidationStrategy::RefreshRequested, None)
3993 }
3994 };
3995
3996 if let Some(InlaySplice {
3997 to_remove,
3998 to_insert,
3999 }) = self.inlay_hint_cache.spawn_hint_refresh(
4000 reason_description,
4001 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4002 invalidate_cache,
4003 ignore_debounce,
4004 cx,
4005 ) {
4006 self.splice_inlays(&to_remove, to_insert, cx);
4007 }
4008 }
4009
4010 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
4011 self.display_map
4012 .read(cx)
4013 .current_inlays()
4014 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4015 .cloned()
4016 .collect()
4017 }
4018
4019 pub fn excerpts_for_inlay_hints_query(
4020 &self,
4021 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4022 cx: &mut Context<Editor>,
4023 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
4024 let Some(project) = self.project.as_ref() else {
4025 return HashMap::default();
4026 };
4027 let project = project.read(cx);
4028 let multi_buffer = self.buffer().read(cx);
4029 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4030 let multi_buffer_visible_start = self
4031 .scroll_manager
4032 .anchor()
4033 .anchor
4034 .to_point(&multi_buffer_snapshot);
4035 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4036 multi_buffer_visible_start
4037 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4038 Bias::Left,
4039 );
4040 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4041 multi_buffer_snapshot
4042 .range_to_buffer_ranges(multi_buffer_visible_range)
4043 .into_iter()
4044 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4045 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
4046 let buffer_file = project::File::from_dyn(buffer.file())?;
4047 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4048 let worktree_entry = buffer_worktree
4049 .read(cx)
4050 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4051 if worktree_entry.is_ignored {
4052 return None;
4053 }
4054
4055 let language = buffer.language()?;
4056 if let Some(restrict_to_languages) = restrict_to_languages {
4057 if !restrict_to_languages.contains(language) {
4058 return None;
4059 }
4060 }
4061 Some((
4062 excerpt_id,
4063 (
4064 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4065 buffer.version().clone(),
4066 excerpt_visible_range,
4067 ),
4068 ))
4069 })
4070 .collect()
4071 }
4072
4073 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4074 TextLayoutDetails {
4075 text_system: window.text_system().clone(),
4076 editor_style: self.style.clone().unwrap(),
4077 rem_size: window.rem_size(),
4078 scroll_anchor: self.scroll_manager.anchor(),
4079 visible_rows: self.visible_line_count(),
4080 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4081 }
4082 }
4083
4084 pub fn splice_inlays(
4085 &self,
4086 to_remove: &[InlayId],
4087 to_insert: Vec<Inlay>,
4088 cx: &mut Context<Self>,
4089 ) {
4090 self.display_map.update(cx, |display_map, cx| {
4091 display_map.splice_inlays(to_remove, to_insert, cx)
4092 });
4093 cx.notify();
4094 }
4095
4096 fn trigger_on_type_formatting(
4097 &self,
4098 input: String,
4099 window: &mut Window,
4100 cx: &mut Context<Self>,
4101 ) -> Option<Task<Result<()>>> {
4102 if input.len() != 1 {
4103 return None;
4104 }
4105
4106 let project = self.project.as_ref()?;
4107 let position = self.selections.newest_anchor().head();
4108 let (buffer, buffer_position) = self
4109 .buffer
4110 .read(cx)
4111 .text_anchor_for_position(position, cx)?;
4112
4113 let settings = language_settings::language_settings(
4114 buffer
4115 .read(cx)
4116 .language_at(buffer_position)
4117 .map(|l| l.name()),
4118 buffer.read(cx).file(),
4119 cx,
4120 );
4121 if !settings.use_on_type_format {
4122 return None;
4123 }
4124
4125 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4126 // hence we do LSP request & edit on host side only — add formats to host's history.
4127 let push_to_lsp_host_history = true;
4128 // If this is not the host, append its history with new edits.
4129 let push_to_client_history = project.read(cx).is_via_collab();
4130
4131 let on_type_formatting = project.update(cx, |project, cx| {
4132 project.on_type_format(
4133 buffer.clone(),
4134 buffer_position,
4135 input,
4136 push_to_lsp_host_history,
4137 cx,
4138 )
4139 });
4140 Some(cx.spawn_in(window, async move |editor, cx| {
4141 if let Some(transaction) = on_type_formatting.await? {
4142 if push_to_client_history {
4143 buffer
4144 .update(cx, |buffer, _| {
4145 buffer.push_transaction(transaction, Instant::now());
4146 })
4147 .ok();
4148 }
4149 editor.update(cx, |editor, cx| {
4150 editor.refresh_document_highlights(cx);
4151 })?;
4152 }
4153 Ok(())
4154 }))
4155 }
4156
4157 pub fn show_word_completions(
4158 &mut self,
4159 _: &ShowWordCompletions,
4160 window: &mut Window,
4161 cx: &mut Context<Self>,
4162 ) {
4163 self.open_completions_menu(true, None, window, cx);
4164 }
4165
4166 pub fn show_completions(
4167 &mut self,
4168 options: &ShowCompletions,
4169 window: &mut Window,
4170 cx: &mut Context<Self>,
4171 ) {
4172 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4173 }
4174
4175 fn open_completions_menu(
4176 &mut self,
4177 ignore_completion_provider: bool,
4178 trigger: Option<&str>,
4179 window: &mut Window,
4180 cx: &mut Context<Self>,
4181 ) {
4182 if self.pending_rename.is_some() {
4183 return;
4184 }
4185 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4186 return;
4187 }
4188
4189 let position = self.selections.newest_anchor().head();
4190 if position.diff_base_anchor.is_some() {
4191 return;
4192 }
4193 let (buffer, buffer_position) =
4194 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4195 output
4196 } else {
4197 return;
4198 };
4199 let buffer_snapshot = buffer.read(cx).snapshot();
4200 let show_completion_documentation = buffer_snapshot
4201 .settings_at(buffer_position, cx)
4202 .show_completion_documentation;
4203
4204 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4205
4206 let trigger_kind = match trigger {
4207 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4208 CompletionTriggerKind::TRIGGER_CHARACTER
4209 }
4210 _ => CompletionTriggerKind::INVOKED,
4211 };
4212 let completion_context = CompletionContext {
4213 trigger_character: trigger.and_then(|trigger| {
4214 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4215 Some(String::from(trigger))
4216 } else {
4217 None
4218 }
4219 }),
4220 trigger_kind,
4221 };
4222
4223 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4224 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4225 let word_to_exclude = buffer_snapshot
4226 .text_for_range(old_range.clone())
4227 .collect::<String>();
4228 (
4229 buffer_snapshot.anchor_before(old_range.start)
4230 ..buffer_snapshot.anchor_after(old_range.end),
4231 Some(word_to_exclude),
4232 )
4233 } else {
4234 (buffer_position..buffer_position, None)
4235 };
4236
4237 let completion_settings = language_settings(
4238 buffer_snapshot
4239 .language_at(buffer_position)
4240 .map(|language| language.name()),
4241 buffer_snapshot.file(),
4242 cx,
4243 )
4244 .completions;
4245
4246 // The document can be large, so stay in reasonable bounds when searching for words,
4247 // otherwise completion pop-up might be slow to appear.
4248 const WORD_LOOKUP_ROWS: u32 = 5_000;
4249 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4250 let min_word_search = buffer_snapshot.clip_point(
4251 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4252 Bias::Left,
4253 );
4254 let max_word_search = buffer_snapshot.clip_point(
4255 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4256 Bias::Right,
4257 );
4258 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4259 ..buffer_snapshot.point_to_offset(max_word_search);
4260
4261 let provider = self
4262 .completion_provider
4263 .as_ref()
4264 .filter(|_| !ignore_completion_provider);
4265 let skip_digits = query
4266 .as_ref()
4267 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4268
4269 let (mut words, provided_completions) = match provider {
4270 Some(provider) => {
4271 let completions = provider.completions(
4272 position.excerpt_id,
4273 &buffer,
4274 buffer_position,
4275 completion_context,
4276 window,
4277 cx,
4278 );
4279
4280 let words = match completion_settings.words {
4281 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4282 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4283 .background_spawn(async move {
4284 buffer_snapshot.words_in_range(WordsQuery {
4285 fuzzy_contents: None,
4286 range: word_search_range,
4287 skip_digits,
4288 })
4289 }),
4290 };
4291
4292 (words, completions)
4293 }
4294 None => (
4295 cx.background_spawn(async move {
4296 buffer_snapshot.words_in_range(WordsQuery {
4297 fuzzy_contents: None,
4298 range: word_search_range,
4299 skip_digits,
4300 })
4301 }),
4302 Task::ready(Ok(None)),
4303 ),
4304 };
4305
4306 let sort_completions = provider
4307 .as_ref()
4308 .map_or(true, |provider| provider.sort_completions());
4309
4310 let id = post_inc(&mut self.next_completion_id);
4311 let task = cx.spawn_in(window, async move |editor, cx| {
4312 async move {
4313 editor.update(cx, |this, _| {
4314 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4315 })?;
4316
4317 let mut completions = Vec::new();
4318 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4319 completions.extend(provided_completions);
4320 if completion_settings.words == WordsCompletionMode::Fallback {
4321 words = Task::ready(HashMap::default());
4322 }
4323 }
4324
4325 let mut words = words.await;
4326 if let Some(word_to_exclude) = &word_to_exclude {
4327 words.remove(word_to_exclude);
4328 }
4329 for lsp_completion in &completions {
4330 words.remove(&lsp_completion.new_text);
4331 }
4332 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4333 old_range: old_range.clone(),
4334 new_text: word.clone(),
4335 label: CodeLabel::plain(word, None),
4336 icon_path: None,
4337 documentation: None,
4338 source: CompletionSource::BufferWord {
4339 word_range,
4340 resolved: false,
4341 },
4342 confirm: None,
4343 }));
4344
4345 let menu = if completions.is_empty() {
4346 None
4347 } else {
4348 let mut menu = CompletionsMenu::new(
4349 id,
4350 sort_completions,
4351 show_completion_documentation,
4352 ignore_completion_provider,
4353 position,
4354 buffer.clone(),
4355 completions.into(),
4356 );
4357
4358 menu.filter(query.as_deref(), cx.background_executor().clone())
4359 .await;
4360
4361 menu.visible().then_some(menu)
4362 };
4363
4364 editor.update_in(cx, |editor, window, cx| {
4365 match editor.context_menu.borrow().as_ref() {
4366 None => {}
4367 Some(CodeContextMenu::Completions(prev_menu)) => {
4368 if prev_menu.id > id {
4369 return;
4370 }
4371 }
4372 _ => return,
4373 }
4374
4375 if editor.focus_handle.is_focused(window) && menu.is_some() {
4376 let mut menu = menu.unwrap();
4377 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4378
4379 *editor.context_menu.borrow_mut() =
4380 Some(CodeContextMenu::Completions(menu));
4381
4382 if editor.show_edit_predictions_in_menu() {
4383 editor.update_visible_inline_completion(window, cx);
4384 } else {
4385 editor.discard_inline_completion(false, cx);
4386 }
4387
4388 cx.notify();
4389 } else if editor.completion_tasks.len() <= 1 {
4390 // If there are no more completion tasks and the last menu was
4391 // empty, we should hide it.
4392 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4393 // If it was already hidden and we don't show inline
4394 // completions in the menu, we should also show the
4395 // inline-completion when available.
4396 if was_hidden && editor.show_edit_predictions_in_menu() {
4397 editor.update_visible_inline_completion(window, cx);
4398 }
4399 }
4400 })?;
4401
4402 anyhow::Ok(())
4403 }
4404 .log_err()
4405 .await
4406 });
4407
4408 self.completion_tasks.push((id, task));
4409 }
4410
4411 #[cfg(feature = "test-support")]
4412 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
4413 let menu = self.context_menu.borrow();
4414 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
4415 let completions = menu.completions.borrow();
4416 Some(completions.to_vec())
4417 } else {
4418 None
4419 }
4420 }
4421
4422 pub fn confirm_completion(
4423 &mut self,
4424 action: &ConfirmCompletion,
4425 window: &mut Window,
4426 cx: &mut Context<Self>,
4427 ) -> Option<Task<Result<()>>> {
4428 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4429 }
4430
4431 pub fn compose_completion(
4432 &mut self,
4433 action: &ComposeCompletion,
4434 window: &mut Window,
4435 cx: &mut Context<Self>,
4436 ) -> Option<Task<Result<()>>> {
4437 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4438 }
4439
4440 fn do_completion(
4441 &mut self,
4442 item_ix: Option<usize>,
4443 intent: CompletionIntent,
4444 window: &mut Window,
4445 cx: &mut Context<Editor>,
4446 ) -> Option<Task<Result<()>>> {
4447 use language::ToOffset as _;
4448
4449 let completions_menu =
4450 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4451 menu
4452 } else {
4453 return None;
4454 };
4455
4456 let candidate_id = {
4457 let entries = completions_menu.entries.borrow();
4458 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4459 if self.show_edit_predictions_in_menu() {
4460 self.discard_inline_completion(true, cx);
4461 }
4462 mat.candidate_id
4463 };
4464
4465 let buffer_handle = completions_menu.buffer;
4466 let completion = completions_menu
4467 .completions
4468 .borrow()
4469 .get(candidate_id)?
4470 .clone();
4471 cx.stop_propagation();
4472
4473 let snippet;
4474 let new_text;
4475 if completion.is_snippet() {
4476 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4477 new_text = snippet.as_ref().unwrap().text.clone();
4478 } else {
4479 snippet = None;
4480 new_text = completion.new_text.clone();
4481 };
4482 let selections = self.selections.all::<usize>(cx);
4483 let buffer = buffer_handle.read(cx);
4484 let old_range = completion.old_range.to_offset(buffer);
4485 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4486
4487 let newest_selection = self.selections.newest_anchor();
4488 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4489 return None;
4490 }
4491
4492 let lookbehind = newest_selection
4493 .start
4494 .text_anchor
4495 .to_offset(buffer)
4496 .saturating_sub(old_range.start);
4497 let lookahead = old_range
4498 .end
4499 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4500 let mut common_prefix_len = old_text
4501 .bytes()
4502 .zip(new_text.bytes())
4503 .take_while(|(a, b)| a == b)
4504 .count();
4505
4506 let snapshot = self.buffer.read(cx).snapshot(cx);
4507 let mut range_to_replace: Option<Range<isize>> = None;
4508 let mut ranges = Vec::new();
4509 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4510 for selection in &selections {
4511 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4512 let start = selection.start.saturating_sub(lookbehind);
4513 let end = selection.end + lookahead;
4514 if selection.id == newest_selection.id {
4515 range_to_replace = Some(
4516 ((start + common_prefix_len) as isize - selection.start as isize)
4517 ..(end as isize - selection.start as isize),
4518 );
4519 }
4520 ranges.push(start + common_prefix_len..end);
4521 } else {
4522 common_prefix_len = 0;
4523 ranges.clear();
4524 ranges.extend(selections.iter().map(|s| {
4525 if s.id == newest_selection.id {
4526 range_to_replace = Some(
4527 old_range.start.to_offset_utf16(&snapshot).0 as isize
4528 - selection.start as isize
4529 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4530 - selection.start as isize,
4531 );
4532 old_range.clone()
4533 } else {
4534 s.start..s.end
4535 }
4536 }));
4537 break;
4538 }
4539 if !self.linked_edit_ranges.is_empty() {
4540 let start_anchor = snapshot.anchor_before(selection.head());
4541 let end_anchor = snapshot.anchor_after(selection.tail());
4542 if let Some(ranges) = self
4543 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4544 {
4545 for (buffer, edits) in ranges {
4546 linked_edits.entry(buffer.clone()).or_default().extend(
4547 edits
4548 .into_iter()
4549 .map(|range| (range, new_text[common_prefix_len..].to_owned())),
4550 );
4551 }
4552 }
4553 }
4554 }
4555 let text = &new_text[common_prefix_len..];
4556
4557 cx.emit(EditorEvent::InputHandled {
4558 utf16_range_to_replace: range_to_replace,
4559 text: text.into(),
4560 });
4561
4562 self.transact(window, cx, |this, window, cx| {
4563 if let Some(mut snippet) = snippet {
4564 snippet.text = text.to_string();
4565 for tabstop in snippet
4566 .tabstops
4567 .iter_mut()
4568 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4569 {
4570 tabstop.start -= common_prefix_len as isize;
4571 tabstop.end -= common_prefix_len as isize;
4572 }
4573
4574 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4575 } else {
4576 this.buffer.update(cx, |buffer, cx| {
4577 let edits = ranges.iter().map(|range| (range.clone(), text));
4578 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4579 });
4580 }
4581 for (buffer, edits) in linked_edits {
4582 buffer.update(cx, |buffer, cx| {
4583 let snapshot = buffer.snapshot();
4584 let edits = edits
4585 .into_iter()
4586 .map(|(range, text)| {
4587 use text::ToPoint as TP;
4588 let end_point = TP::to_point(&range.end, &snapshot);
4589 let start_point = TP::to_point(&range.start, &snapshot);
4590 (start_point..end_point, text)
4591 })
4592 .sorted_by_key(|(range, _)| range.start);
4593 buffer.edit(edits, None, cx);
4594 })
4595 }
4596
4597 this.refresh_inline_completion(true, false, window, cx);
4598 });
4599
4600 let show_new_completions_on_confirm = completion
4601 .confirm
4602 .as_ref()
4603 .map_or(false, |confirm| confirm(intent, window, cx));
4604 if show_new_completions_on_confirm {
4605 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4606 }
4607
4608 let provider = self.completion_provider.as_ref()?;
4609 drop(completion);
4610 let apply_edits = provider.apply_additional_edits_for_completion(
4611 buffer_handle,
4612 completions_menu.completions.clone(),
4613 candidate_id,
4614 true,
4615 cx,
4616 );
4617
4618 let editor_settings = EditorSettings::get_global(cx);
4619 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4620 // After the code completion is finished, users often want to know what signatures are needed.
4621 // so we should automatically call signature_help
4622 self.show_signature_help(&ShowSignatureHelp, window, cx);
4623 }
4624
4625 Some(cx.foreground_executor().spawn(async move {
4626 apply_edits.await?;
4627 Ok(())
4628 }))
4629 }
4630
4631 pub fn toggle_code_actions(
4632 &mut self,
4633 action: &ToggleCodeActions,
4634 window: &mut Window,
4635 cx: &mut Context<Self>,
4636 ) {
4637 let mut context_menu = self.context_menu.borrow_mut();
4638 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4639 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4640 // Toggle if we're selecting the same one
4641 *context_menu = None;
4642 cx.notify();
4643 return;
4644 } else {
4645 // Otherwise, clear it and start a new one
4646 *context_menu = None;
4647 cx.notify();
4648 }
4649 }
4650 drop(context_menu);
4651 let snapshot = self.snapshot(window, cx);
4652 let deployed_from_indicator = action.deployed_from_indicator;
4653 let mut task = self.code_actions_task.take();
4654 let action = action.clone();
4655 cx.spawn_in(window, async move |editor, cx| {
4656 while let Some(prev_task) = task {
4657 prev_task.await.log_err();
4658 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4659 }
4660
4661 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4662 if editor.focus_handle.is_focused(window) {
4663 let multibuffer_point = action
4664 .deployed_from_indicator
4665 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4666 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4667 let (buffer, buffer_row) = snapshot
4668 .buffer_snapshot
4669 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4670 .and_then(|(buffer_snapshot, range)| {
4671 editor
4672 .buffer
4673 .read(cx)
4674 .buffer(buffer_snapshot.remote_id())
4675 .map(|buffer| (buffer, range.start.row))
4676 })?;
4677 let (_, code_actions) = editor
4678 .available_code_actions
4679 .clone()
4680 .and_then(|(location, code_actions)| {
4681 let snapshot = location.buffer.read(cx).snapshot();
4682 let point_range = location.range.to_point(&snapshot);
4683 let point_range = point_range.start.row..=point_range.end.row;
4684 if point_range.contains(&buffer_row) {
4685 Some((location, code_actions))
4686 } else {
4687 None
4688 }
4689 })
4690 .unzip();
4691 let buffer_id = buffer.read(cx).remote_id();
4692 let tasks = editor
4693 .tasks
4694 .get(&(buffer_id, buffer_row))
4695 .map(|t| Arc::new(t.to_owned()));
4696 if tasks.is_none() && code_actions.is_none() {
4697 return None;
4698 }
4699
4700 editor.completion_tasks.clear();
4701 editor.discard_inline_completion(false, cx);
4702 let task_context =
4703 tasks
4704 .as_ref()
4705 .zip(editor.project.clone())
4706 .map(|(tasks, project)| {
4707 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4708 });
4709
4710 Some(cx.spawn_in(window, async move |editor, cx| {
4711 let task_context = match task_context {
4712 Some(task_context) => task_context.await,
4713 None => None,
4714 };
4715 let resolved_tasks =
4716 tasks.zip(task_context).map(|(tasks, task_context)| {
4717 Rc::new(ResolvedTasks {
4718 templates: tasks.resolve(&task_context).collect(),
4719 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4720 multibuffer_point.row,
4721 tasks.column,
4722 )),
4723 })
4724 });
4725 let spawn_straight_away = resolved_tasks
4726 .as_ref()
4727 .map_or(false, |tasks| tasks.templates.len() == 1)
4728 && code_actions
4729 .as_ref()
4730 .map_or(true, |actions| actions.is_empty());
4731 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4732 *editor.context_menu.borrow_mut() =
4733 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4734 buffer,
4735 actions: CodeActionContents {
4736 tasks: resolved_tasks,
4737 actions: code_actions,
4738 },
4739 selected_item: Default::default(),
4740 scroll_handle: UniformListScrollHandle::default(),
4741 deployed_from_indicator,
4742 }));
4743 if spawn_straight_away {
4744 if let Some(task) = editor.confirm_code_action(
4745 &ConfirmCodeAction { item_ix: Some(0) },
4746 window,
4747 cx,
4748 ) {
4749 cx.notify();
4750 return task;
4751 }
4752 }
4753 cx.notify();
4754 Task::ready(Ok(()))
4755 }) {
4756 task.await
4757 } else {
4758 Ok(())
4759 }
4760 }))
4761 } else {
4762 Some(Task::ready(Ok(())))
4763 }
4764 })?;
4765 if let Some(task) = spawned_test_task {
4766 task.await?;
4767 }
4768
4769 Ok::<_, anyhow::Error>(())
4770 })
4771 .detach_and_log_err(cx);
4772 }
4773
4774 pub fn confirm_code_action(
4775 &mut self,
4776 action: &ConfirmCodeAction,
4777 window: &mut Window,
4778 cx: &mut Context<Self>,
4779 ) -> Option<Task<Result<()>>> {
4780 let actions_menu =
4781 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4782 menu
4783 } else {
4784 return None;
4785 };
4786 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4787 let action = actions_menu.actions.get(action_ix)?;
4788 let title = action.label();
4789 let buffer = actions_menu.buffer;
4790 let workspace = self.workspace()?;
4791
4792 match action {
4793 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4794 workspace.update(cx, |workspace, cx| {
4795 workspace::tasks::schedule_resolved_task(
4796 workspace,
4797 task_source_kind,
4798 resolved_task,
4799 false,
4800 cx,
4801 );
4802
4803 Some(Task::ready(Ok(())))
4804 })
4805 }
4806 CodeActionsItem::CodeAction {
4807 excerpt_id,
4808 action,
4809 provider,
4810 } => {
4811 let apply_code_action =
4812 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4813 let workspace = workspace.downgrade();
4814 Some(cx.spawn_in(window, async move |editor, cx| {
4815 let project_transaction = apply_code_action.await?;
4816 Self::open_project_transaction(
4817 &editor,
4818 workspace,
4819 project_transaction,
4820 title,
4821 cx,
4822 )
4823 .await
4824 }))
4825 }
4826 }
4827 }
4828
4829 pub async fn open_project_transaction(
4830 this: &WeakEntity<Editor>,
4831 workspace: WeakEntity<Workspace>,
4832 transaction: ProjectTransaction,
4833 title: String,
4834 cx: &mut AsyncWindowContext,
4835 ) -> Result<()> {
4836 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4837 cx.update(|_, cx| {
4838 entries.sort_unstable_by_key(|(buffer, _)| {
4839 buffer.read(cx).file().map(|f| f.path().clone())
4840 });
4841 })?;
4842
4843 // If the project transaction's edits are all contained within this editor, then
4844 // avoid opening a new editor to display them.
4845
4846 if let Some((buffer, transaction)) = entries.first() {
4847 if entries.len() == 1 {
4848 let excerpt = this.update(cx, |editor, cx| {
4849 editor
4850 .buffer()
4851 .read(cx)
4852 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4853 })?;
4854 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4855 if excerpted_buffer == *buffer {
4856 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4857 let excerpt_range = excerpt_range.to_offset(buffer);
4858 buffer
4859 .edited_ranges_for_transaction::<usize>(transaction)
4860 .all(|range| {
4861 excerpt_range.start <= range.start
4862 && excerpt_range.end >= range.end
4863 })
4864 })?;
4865
4866 if all_edits_within_excerpt {
4867 return Ok(());
4868 }
4869 }
4870 }
4871 }
4872 } else {
4873 return Ok(());
4874 }
4875
4876 let mut ranges_to_highlight = Vec::new();
4877 let excerpt_buffer = cx.new(|cx| {
4878 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4879 for (buffer_handle, transaction) in &entries {
4880 let buffer = buffer_handle.read(cx);
4881 ranges_to_highlight.extend(
4882 multibuffer.push_excerpts_with_context_lines(
4883 buffer_handle.clone(),
4884 buffer
4885 .edited_ranges_for_transaction::<usize>(transaction)
4886 .collect(),
4887 DEFAULT_MULTIBUFFER_CONTEXT,
4888 cx,
4889 ),
4890 );
4891 }
4892 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4893 multibuffer
4894 })?;
4895
4896 workspace.update_in(cx, |workspace, window, cx| {
4897 let project = workspace.project().clone();
4898 let editor =
4899 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4900 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4901 editor.update(cx, |editor, cx| {
4902 editor.highlight_background::<Self>(
4903 &ranges_to_highlight,
4904 |theme| theme.editor_highlighted_line_background,
4905 cx,
4906 );
4907 });
4908 })?;
4909
4910 Ok(())
4911 }
4912
4913 pub fn clear_code_action_providers(&mut self) {
4914 self.code_action_providers.clear();
4915 self.available_code_actions.take();
4916 }
4917
4918 pub fn add_code_action_provider(
4919 &mut self,
4920 provider: Rc<dyn CodeActionProvider>,
4921 window: &mut Window,
4922 cx: &mut Context<Self>,
4923 ) {
4924 if self
4925 .code_action_providers
4926 .iter()
4927 .any(|existing_provider| existing_provider.id() == provider.id())
4928 {
4929 return;
4930 }
4931
4932 self.code_action_providers.push(provider);
4933 self.refresh_code_actions(window, cx);
4934 }
4935
4936 pub fn remove_code_action_provider(
4937 &mut self,
4938 id: Arc<str>,
4939 window: &mut Window,
4940 cx: &mut Context<Self>,
4941 ) {
4942 self.code_action_providers
4943 .retain(|provider| provider.id() != id);
4944 self.refresh_code_actions(window, cx);
4945 }
4946
4947 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4948 let buffer = self.buffer.read(cx);
4949 let newest_selection = self.selections.newest_anchor().clone();
4950 if newest_selection.head().diff_base_anchor.is_some() {
4951 return None;
4952 }
4953 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4954 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4955 if start_buffer != end_buffer {
4956 return None;
4957 }
4958
4959 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4960 cx.background_executor()
4961 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4962 .await;
4963
4964 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4965 let providers = this.code_action_providers.clone();
4966 let tasks = this
4967 .code_action_providers
4968 .iter()
4969 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4970 .collect::<Vec<_>>();
4971 (providers, tasks)
4972 })?;
4973
4974 let mut actions = Vec::new();
4975 for (provider, provider_actions) in
4976 providers.into_iter().zip(future::join_all(tasks).await)
4977 {
4978 if let Some(provider_actions) = provider_actions.log_err() {
4979 actions.extend(provider_actions.into_iter().map(|action| {
4980 AvailableCodeAction {
4981 excerpt_id: newest_selection.start.excerpt_id,
4982 action,
4983 provider: provider.clone(),
4984 }
4985 }));
4986 }
4987 }
4988
4989 this.update(cx, |this, cx| {
4990 this.available_code_actions = if actions.is_empty() {
4991 None
4992 } else {
4993 Some((
4994 Location {
4995 buffer: start_buffer,
4996 range: start..end,
4997 },
4998 actions.into(),
4999 ))
5000 };
5001 cx.notify();
5002 })
5003 }));
5004 None
5005 }
5006
5007 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5008 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5009 self.show_git_blame_inline = false;
5010
5011 self.show_git_blame_inline_delay_task =
5012 Some(cx.spawn_in(window, async move |this, cx| {
5013 cx.background_executor().timer(delay).await;
5014
5015 this.update(cx, |this, cx| {
5016 this.show_git_blame_inline = true;
5017 cx.notify();
5018 })
5019 .log_err();
5020 }));
5021 }
5022 }
5023
5024 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
5025 if self.pending_rename.is_some() {
5026 return None;
5027 }
5028
5029 let provider = self.semantics_provider.clone()?;
5030 let buffer = self.buffer.read(cx);
5031 let newest_selection = self.selections.newest_anchor().clone();
5032 let cursor_position = newest_selection.head();
5033 let (cursor_buffer, cursor_buffer_position) =
5034 buffer.text_anchor_for_position(cursor_position, cx)?;
5035 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5036 if cursor_buffer != tail_buffer {
5037 return None;
5038 }
5039 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
5040 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
5041 cx.background_executor()
5042 .timer(Duration::from_millis(debounce))
5043 .await;
5044
5045 let highlights = if let Some(highlights) = cx
5046 .update(|cx| {
5047 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5048 })
5049 .ok()
5050 .flatten()
5051 {
5052 highlights.await.log_err()
5053 } else {
5054 None
5055 };
5056
5057 if let Some(highlights) = highlights {
5058 this.update(cx, |this, cx| {
5059 if this.pending_rename.is_some() {
5060 return;
5061 }
5062
5063 let buffer_id = cursor_position.buffer_id;
5064 let buffer = this.buffer.read(cx);
5065 if !buffer
5066 .text_anchor_for_position(cursor_position, cx)
5067 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5068 {
5069 return;
5070 }
5071
5072 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5073 let mut write_ranges = Vec::new();
5074 let mut read_ranges = Vec::new();
5075 for highlight in highlights {
5076 for (excerpt_id, excerpt_range) in
5077 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5078 {
5079 let start = highlight
5080 .range
5081 .start
5082 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5083 let end = highlight
5084 .range
5085 .end
5086 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5087 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5088 continue;
5089 }
5090
5091 let range = Anchor {
5092 buffer_id,
5093 excerpt_id,
5094 text_anchor: start,
5095 diff_base_anchor: None,
5096 }..Anchor {
5097 buffer_id,
5098 excerpt_id,
5099 text_anchor: end,
5100 diff_base_anchor: None,
5101 };
5102 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5103 write_ranges.push(range);
5104 } else {
5105 read_ranges.push(range);
5106 }
5107 }
5108 }
5109
5110 this.highlight_background::<DocumentHighlightRead>(
5111 &read_ranges,
5112 |theme| theme.editor_document_highlight_read_background,
5113 cx,
5114 );
5115 this.highlight_background::<DocumentHighlightWrite>(
5116 &write_ranges,
5117 |theme| theme.editor_document_highlight_write_background,
5118 cx,
5119 );
5120 cx.notify();
5121 })
5122 .log_err();
5123 }
5124 }));
5125 None
5126 }
5127
5128 pub fn refresh_selected_text_highlights(
5129 &mut self,
5130 window: &mut Window,
5131 cx: &mut Context<Editor>,
5132 ) {
5133 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5134 return;
5135 }
5136 self.selection_highlight_task.take();
5137 if !EditorSettings::get_global(cx).selection_highlight {
5138 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5139 return;
5140 }
5141 if self.selections.count() != 1 || self.selections.line_mode {
5142 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5143 return;
5144 }
5145 let selection = self.selections.newest::<Point>(cx);
5146 if selection.is_empty() || selection.start.row != selection.end.row {
5147 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5148 return;
5149 }
5150 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5151 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5152 cx.background_executor()
5153 .timer(Duration::from_millis(debounce))
5154 .await;
5155 let Some(Some(matches_task)) = editor
5156 .update_in(cx, |editor, _, cx| {
5157 if editor.selections.count() != 1 || editor.selections.line_mode {
5158 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5159 return None;
5160 }
5161 let selection = editor.selections.newest::<Point>(cx);
5162 if selection.is_empty() || selection.start.row != selection.end.row {
5163 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5164 return None;
5165 }
5166 let buffer = editor.buffer().read(cx).snapshot(cx);
5167 let query = buffer.text_for_range(selection.range()).collect::<String>();
5168 if query.trim().is_empty() {
5169 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5170 return None;
5171 }
5172 Some(cx.background_spawn(async move {
5173 let mut ranges = Vec::new();
5174 let selection_anchors = selection.range().to_anchors(&buffer);
5175 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5176 for (search_buffer, search_range, excerpt_id) in
5177 buffer.range_to_buffer_ranges(range)
5178 {
5179 ranges.extend(
5180 project::search::SearchQuery::text(
5181 query.clone(),
5182 false,
5183 false,
5184 false,
5185 Default::default(),
5186 Default::default(),
5187 None,
5188 )
5189 .unwrap()
5190 .search(search_buffer, Some(search_range.clone()))
5191 .await
5192 .into_iter()
5193 .filter_map(
5194 |match_range| {
5195 let start = search_buffer.anchor_after(
5196 search_range.start + match_range.start,
5197 );
5198 let end = search_buffer.anchor_before(
5199 search_range.start + match_range.end,
5200 );
5201 let range = Anchor::range_in_buffer(
5202 excerpt_id,
5203 search_buffer.remote_id(),
5204 start..end,
5205 );
5206 (range != selection_anchors).then_some(range)
5207 },
5208 ),
5209 );
5210 }
5211 }
5212 ranges
5213 }))
5214 })
5215 .log_err()
5216 else {
5217 return;
5218 };
5219 let matches = matches_task.await;
5220 editor
5221 .update_in(cx, |editor, _, cx| {
5222 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5223 if !matches.is_empty() {
5224 editor.highlight_background::<SelectedTextHighlight>(
5225 &matches,
5226 |theme| theme.editor_document_highlight_bracket_background,
5227 cx,
5228 )
5229 }
5230 })
5231 .log_err();
5232 }));
5233 }
5234
5235 pub fn refresh_inline_completion(
5236 &mut self,
5237 debounce: bool,
5238 user_requested: bool,
5239 window: &mut Window,
5240 cx: &mut Context<Self>,
5241 ) -> Option<()> {
5242 let provider = self.edit_prediction_provider()?;
5243 let cursor = self.selections.newest_anchor().head();
5244 let (buffer, cursor_buffer_position) =
5245 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5246
5247 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5248 self.discard_inline_completion(false, cx);
5249 return None;
5250 }
5251
5252 if !user_requested
5253 && (!self.should_show_edit_predictions()
5254 || !self.is_focused(window)
5255 || buffer.read(cx).is_empty())
5256 {
5257 self.discard_inline_completion(false, cx);
5258 return None;
5259 }
5260
5261 self.update_visible_inline_completion(window, cx);
5262 provider.refresh(
5263 self.project.clone(),
5264 buffer,
5265 cursor_buffer_position,
5266 debounce,
5267 cx,
5268 );
5269 Some(())
5270 }
5271
5272 fn show_edit_predictions_in_menu(&self) -> bool {
5273 match self.edit_prediction_settings {
5274 EditPredictionSettings::Disabled => false,
5275 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5276 }
5277 }
5278
5279 pub fn edit_predictions_enabled(&self) -> bool {
5280 match self.edit_prediction_settings {
5281 EditPredictionSettings::Disabled => false,
5282 EditPredictionSettings::Enabled { .. } => true,
5283 }
5284 }
5285
5286 fn edit_prediction_requires_modifier(&self) -> bool {
5287 match self.edit_prediction_settings {
5288 EditPredictionSettings::Disabled => false,
5289 EditPredictionSettings::Enabled {
5290 preview_requires_modifier,
5291 ..
5292 } => preview_requires_modifier,
5293 }
5294 }
5295
5296 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5297 if self.edit_prediction_provider.is_none() {
5298 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5299 } else {
5300 let selection = self.selections.newest_anchor();
5301 let cursor = selection.head();
5302
5303 if let Some((buffer, cursor_buffer_position)) =
5304 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5305 {
5306 self.edit_prediction_settings =
5307 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5308 }
5309 }
5310 }
5311
5312 fn edit_prediction_settings_at_position(
5313 &self,
5314 buffer: &Entity<Buffer>,
5315 buffer_position: language::Anchor,
5316 cx: &App,
5317 ) -> EditPredictionSettings {
5318 if self.mode != EditorMode::Full
5319 || !self.show_inline_completions_override.unwrap_or(true)
5320 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5321 {
5322 return EditPredictionSettings::Disabled;
5323 }
5324
5325 let buffer = buffer.read(cx);
5326
5327 let file = buffer.file();
5328
5329 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5330 return EditPredictionSettings::Disabled;
5331 };
5332
5333 let by_provider = matches!(
5334 self.menu_inline_completions_policy,
5335 MenuInlineCompletionsPolicy::ByProvider
5336 );
5337
5338 let show_in_menu = by_provider
5339 && self
5340 .edit_prediction_provider
5341 .as_ref()
5342 .map_or(false, |provider| {
5343 provider.provider.show_completions_in_menu()
5344 });
5345
5346 let preview_requires_modifier =
5347 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5348
5349 EditPredictionSettings::Enabled {
5350 show_in_menu,
5351 preview_requires_modifier,
5352 }
5353 }
5354
5355 fn should_show_edit_predictions(&self) -> bool {
5356 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5357 }
5358
5359 pub fn edit_prediction_preview_is_active(&self) -> bool {
5360 matches!(
5361 self.edit_prediction_preview,
5362 EditPredictionPreview::Active { .. }
5363 )
5364 }
5365
5366 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5367 let cursor = self.selections.newest_anchor().head();
5368 if let Some((buffer, cursor_position)) =
5369 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5370 {
5371 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5372 } else {
5373 false
5374 }
5375 }
5376
5377 fn edit_predictions_enabled_in_buffer(
5378 &self,
5379 buffer: &Entity<Buffer>,
5380 buffer_position: language::Anchor,
5381 cx: &App,
5382 ) -> bool {
5383 maybe!({
5384 if self.read_only(cx) {
5385 return Some(false);
5386 }
5387 let provider = self.edit_prediction_provider()?;
5388 if !provider.is_enabled(&buffer, buffer_position, cx) {
5389 return Some(false);
5390 }
5391 let buffer = buffer.read(cx);
5392 let Some(file) = buffer.file() else {
5393 return Some(true);
5394 };
5395 let settings = all_language_settings(Some(file), cx);
5396 Some(settings.edit_predictions_enabled_for_file(file, cx))
5397 })
5398 .unwrap_or(false)
5399 }
5400
5401 fn cycle_inline_completion(
5402 &mut self,
5403 direction: Direction,
5404 window: &mut Window,
5405 cx: &mut Context<Self>,
5406 ) -> Option<()> {
5407 let provider = self.edit_prediction_provider()?;
5408 let cursor = self.selections.newest_anchor().head();
5409 let (buffer, cursor_buffer_position) =
5410 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5411 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5412 return None;
5413 }
5414
5415 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5416 self.update_visible_inline_completion(window, cx);
5417
5418 Some(())
5419 }
5420
5421 pub fn show_inline_completion(
5422 &mut self,
5423 _: &ShowEditPrediction,
5424 window: &mut Window,
5425 cx: &mut Context<Self>,
5426 ) {
5427 if !self.has_active_inline_completion() {
5428 self.refresh_inline_completion(false, true, window, cx);
5429 return;
5430 }
5431
5432 self.update_visible_inline_completion(window, cx);
5433 }
5434
5435 pub fn display_cursor_names(
5436 &mut self,
5437 _: &DisplayCursorNames,
5438 window: &mut Window,
5439 cx: &mut Context<Self>,
5440 ) {
5441 self.show_cursor_names(window, cx);
5442 }
5443
5444 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5445 self.show_cursor_names = true;
5446 cx.notify();
5447 cx.spawn_in(window, async move |this, cx| {
5448 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5449 this.update(cx, |this, cx| {
5450 this.show_cursor_names = false;
5451 cx.notify()
5452 })
5453 .ok()
5454 })
5455 .detach();
5456 }
5457
5458 pub fn next_edit_prediction(
5459 &mut self,
5460 _: &NextEditPrediction,
5461 window: &mut Window,
5462 cx: &mut Context<Self>,
5463 ) {
5464 if self.has_active_inline_completion() {
5465 self.cycle_inline_completion(Direction::Next, window, cx);
5466 } else {
5467 let is_copilot_disabled = self
5468 .refresh_inline_completion(false, true, window, cx)
5469 .is_none();
5470 if is_copilot_disabled {
5471 cx.propagate();
5472 }
5473 }
5474 }
5475
5476 pub fn previous_edit_prediction(
5477 &mut self,
5478 _: &PreviousEditPrediction,
5479 window: &mut Window,
5480 cx: &mut Context<Self>,
5481 ) {
5482 if self.has_active_inline_completion() {
5483 self.cycle_inline_completion(Direction::Prev, window, cx);
5484 } else {
5485 let is_copilot_disabled = self
5486 .refresh_inline_completion(false, true, window, cx)
5487 .is_none();
5488 if is_copilot_disabled {
5489 cx.propagate();
5490 }
5491 }
5492 }
5493
5494 pub fn accept_edit_prediction(
5495 &mut self,
5496 _: &AcceptEditPrediction,
5497 window: &mut Window,
5498 cx: &mut Context<Self>,
5499 ) {
5500 if self.show_edit_predictions_in_menu() {
5501 self.hide_context_menu(window, cx);
5502 }
5503
5504 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5505 return;
5506 };
5507
5508 self.report_inline_completion_event(
5509 active_inline_completion.completion_id.clone(),
5510 true,
5511 cx,
5512 );
5513
5514 match &active_inline_completion.completion {
5515 InlineCompletion::Move { target, .. } => {
5516 let target = *target;
5517
5518 if let Some(position_map) = &self.last_position_map {
5519 if position_map
5520 .visible_row_range
5521 .contains(&target.to_display_point(&position_map.snapshot).row())
5522 || !self.edit_prediction_requires_modifier()
5523 {
5524 self.unfold_ranges(&[target..target], true, false, cx);
5525 // Note that this is also done in vim's handler of the Tab action.
5526 self.change_selections(
5527 Some(Autoscroll::newest()),
5528 window,
5529 cx,
5530 |selections| {
5531 selections.select_anchor_ranges([target..target]);
5532 },
5533 );
5534 self.clear_row_highlights::<EditPredictionPreview>();
5535
5536 self.edit_prediction_preview
5537 .set_previous_scroll_position(None);
5538 } else {
5539 self.edit_prediction_preview
5540 .set_previous_scroll_position(Some(
5541 position_map.snapshot.scroll_anchor,
5542 ));
5543
5544 self.highlight_rows::<EditPredictionPreview>(
5545 target..target,
5546 cx.theme().colors().editor_highlighted_line_background,
5547 true,
5548 cx,
5549 );
5550 self.request_autoscroll(Autoscroll::fit(), cx);
5551 }
5552 }
5553 }
5554 InlineCompletion::Edit { edits, .. } => {
5555 if let Some(provider) = self.edit_prediction_provider() {
5556 provider.accept(cx);
5557 }
5558
5559 let snapshot = self.buffer.read(cx).snapshot(cx);
5560 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5561
5562 self.buffer.update(cx, |buffer, cx| {
5563 buffer.edit(edits.iter().cloned(), None, cx)
5564 });
5565
5566 self.change_selections(None, window, cx, |s| {
5567 s.select_anchor_ranges([last_edit_end..last_edit_end])
5568 });
5569
5570 self.update_visible_inline_completion(window, cx);
5571 if self.active_inline_completion.is_none() {
5572 self.refresh_inline_completion(true, true, window, cx);
5573 }
5574
5575 cx.notify();
5576 }
5577 }
5578
5579 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5580 }
5581
5582 pub fn accept_partial_inline_completion(
5583 &mut self,
5584 _: &AcceptPartialEditPrediction,
5585 window: &mut Window,
5586 cx: &mut Context<Self>,
5587 ) {
5588 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5589 return;
5590 };
5591 if self.selections.count() != 1 {
5592 return;
5593 }
5594
5595 self.report_inline_completion_event(
5596 active_inline_completion.completion_id.clone(),
5597 true,
5598 cx,
5599 );
5600
5601 match &active_inline_completion.completion {
5602 InlineCompletion::Move { target, .. } => {
5603 let target = *target;
5604 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5605 selections.select_anchor_ranges([target..target]);
5606 });
5607 }
5608 InlineCompletion::Edit { edits, .. } => {
5609 // Find an insertion that starts at the cursor position.
5610 let snapshot = self.buffer.read(cx).snapshot(cx);
5611 let cursor_offset = self.selections.newest::<usize>(cx).head();
5612 let insertion = edits.iter().find_map(|(range, text)| {
5613 let range = range.to_offset(&snapshot);
5614 if range.is_empty() && range.start == cursor_offset {
5615 Some(text)
5616 } else {
5617 None
5618 }
5619 });
5620
5621 if let Some(text) = insertion {
5622 let mut partial_completion = text
5623 .chars()
5624 .by_ref()
5625 .take_while(|c| c.is_alphabetic())
5626 .collect::<String>();
5627 if partial_completion.is_empty() {
5628 partial_completion = text
5629 .chars()
5630 .by_ref()
5631 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5632 .collect::<String>();
5633 }
5634
5635 cx.emit(EditorEvent::InputHandled {
5636 utf16_range_to_replace: None,
5637 text: partial_completion.clone().into(),
5638 });
5639
5640 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5641
5642 self.refresh_inline_completion(true, true, window, cx);
5643 cx.notify();
5644 } else {
5645 self.accept_edit_prediction(&Default::default(), window, cx);
5646 }
5647 }
5648 }
5649 }
5650
5651 fn discard_inline_completion(
5652 &mut self,
5653 should_report_inline_completion_event: bool,
5654 cx: &mut Context<Self>,
5655 ) -> bool {
5656 if should_report_inline_completion_event {
5657 let completion_id = self
5658 .active_inline_completion
5659 .as_ref()
5660 .and_then(|active_completion| active_completion.completion_id.clone());
5661
5662 self.report_inline_completion_event(completion_id, false, cx);
5663 }
5664
5665 if let Some(provider) = self.edit_prediction_provider() {
5666 provider.discard(cx);
5667 }
5668
5669 self.take_active_inline_completion(cx)
5670 }
5671
5672 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5673 let Some(provider) = self.edit_prediction_provider() else {
5674 return;
5675 };
5676
5677 let Some((_, buffer, _)) = self
5678 .buffer
5679 .read(cx)
5680 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5681 else {
5682 return;
5683 };
5684
5685 let extension = buffer
5686 .read(cx)
5687 .file()
5688 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5689
5690 let event_type = match accepted {
5691 true => "Edit Prediction Accepted",
5692 false => "Edit Prediction Discarded",
5693 };
5694 telemetry::event!(
5695 event_type,
5696 provider = provider.name(),
5697 prediction_id = id,
5698 suggestion_accepted = accepted,
5699 file_extension = extension,
5700 );
5701 }
5702
5703 pub fn has_active_inline_completion(&self) -> bool {
5704 self.active_inline_completion.is_some()
5705 }
5706
5707 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5708 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5709 return false;
5710 };
5711
5712 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5713 self.clear_highlights::<InlineCompletionHighlight>(cx);
5714 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5715 true
5716 }
5717
5718 /// Returns true when we're displaying the edit prediction popover below the cursor
5719 /// like we are not previewing and the LSP autocomplete menu is visible
5720 /// or we are in `when_holding_modifier` mode.
5721 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5722 if self.edit_prediction_preview_is_active()
5723 || !self.show_edit_predictions_in_menu()
5724 || !self.edit_predictions_enabled()
5725 {
5726 return false;
5727 }
5728
5729 if self.has_visible_completions_menu() {
5730 return true;
5731 }
5732
5733 has_completion && self.edit_prediction_requires_modifier()
5734 }
5735
5736 fn handle_modifiers_changed(
5737 &mut self,
5738 modifiers: Modifiers,
5739 position_map: &PositionMap,
5740 window: &mut Window,
5741 cx: &mut Context<Self>,
5742 ) {
5743 if self.show_edit_predictions_in_menu() {
5744 self.update_edit_prediction_preview(&modifiers, window, cx);
5745 }
5746
5747 self.update_selection_mode(&modifiers, position_map, window, cx);
5748
5749 let mouse_position = window.mouse_position();
5750 if !position_map.text_hitbox.is_hovered(window) {
5751 return;
5752 }
5753
5754 self.update_hovered_link(
5755 position_map.point_for_position(mouse_position),
5756 &position_map.snapshot,
5757 modifiers,
5758 window,
5759 cx,
5760 )
5761 }
5762
5763 fn update_selection_mode(
5764 &mut self,
5765 modifiers: &Modifiers,
5766 position_map: &PositionMap,
5767 window: &mut Window,
5768 cx: &mut Context<Self>,
5769 ) {
5770 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5771 return;
5772 }
5773
5774 let mouse_position = window.mouse_position();
5775 let point_for_position = position_map.point_for_position(mouse_position);
5776 let position = point_for_position.previous_valid;
5777
5778 self.select(
5779 SelectPhase::BeginColumnar {
5780 position,
5781 reset: false,
5782 goal_column: point_for_position.exact_unclipped.column(),
5783 },
5784 window,
5785 cx,
5786 );
5787 }
5788
5789 fn update_edit_prediction_preview(
5790 &mut self,
5791 modifiers: &Modifiers,
5792 window: &mut Window,
5793 cx: &mut Context<Self>,
5794 ) {
5795 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5796 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5797 return;
5798 };
5799
5800 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5801 if matches!(
5802 self.edit_prediction_preview,
5803 EditPredictionPreview::Inactive { .. }
5804 ) {
5805 self.edit_prediction_preview = EditPredictionPreview::Active {
5806 previous_scroll_position: None,
5807 since: Instant::now(),
5808 };
5809
5810 self.update_visible_inline_completion(window, cx);
5811 cx.notify();
5812 }
5813 } else if let EditPredictionPreview::Active {
5814 previous_scroll_position,
5815 since,
5816 } = self.edit_prediction_preview
5817 {
5818 if let (Some(previous_scroll_position), Some(position_map)) =
5819 (previous_scroll_position, self.last_position_map.as_ref())
5820 {
5821 self.set_scroll_position(
5822 previous_scroll_position
5823 .scroll_position(&position_map.snapshot.display_snapshot),
5824 window,
5825 cx,
5826 );
5827 }
5828
5829 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5830 released_too_fast: since.elapsed() < Duration::from_millis(200),
5831 };
5832 self.clear_row_highlights::<EditPredictionPreview>();
5833 self.update_visible_inline_completion(window, cx);
5834 cx.notify();
5835 }
5836 }
5837
5838 fn update_visible_inline_completion(
5839 &mut self,
5840 _window: &mut Window,
5841 cx: &mut Context<Self>,
5842 ) -> Option<()> {
5843 let selection = self.selections.newest_anchor();
5844 let cursor = selection.head();
5845 let multibuffer = self.buffer.read(cx).snapshot(cx);
5846 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5847 let excerpt_id = cursor.excerpt_id;
5848
5849 let show_in_menu = self.show_edit_predictions_in_menu();
5850 let completions_menu_has_precedence = !show_in_menu
5851 && (self.context_menu.borrow().is_some()
5852 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5853
5854 if completions_menu_has_precedence
5855 || !offset_selection.is_empty()
5856 || self
5857 .active_inline_completion
5858 .as_ref()
5859 .map_or(false, |completion| {
5860 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5861 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5862 !invalidation_range.contains(&offset_selection.head())
5863 })
5864 {
5865 self.discard_inline_completion(false, cx);
5866 return None;
5867 }
5868
5869 self.take_active_inline_completion(cx);
5870 let Some(provider) = self.edit_prediction_provider() else {
5871 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5872 return None;
5873 };
5874
5875 let (buffer, cursor_buffer_position) =
5876 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5877
5878 self.edit_prediction_settings =
5879 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5880
5881 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5882
5883 if self.edit_prediction_indent_conflict {
5884 let cursor_point = cursor.to_point(&multibuffer);
5885
5886 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5887
5888 if let Some((_, indent)) = indents.iter().next() {
5889 if indent.len == cursor_point.column {
5890 self.edit_prediction_indent_conflict = false;
5891 }
5892 }
5893 }
5894
5895 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5896 let edits = inline_completion
5897 .edits
5898 .into_iter()
5899 .flat_map(|(range, new_text)| {
5900 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5901 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5902 Some((start..end, new_text))
5903 })
5904 .collect::<Vec<_>>();
5905 if edits.is_empty() {
5906 return None;
5907 }
5908
5909 let first_edit_start = edits.first().unwrap().0.start;
5910 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5911 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5912
5913 let last_edit_end = edits.last().unwrap().0.end;
5914 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5915 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5916
5917 let cursor_row = cursor.to_point(&multibuffer).row;
5918
5919 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5920
5921 let mut inlay_ids = Vec::new();
5922 let invalidation_row_range;
5923 let move_invalidation_row_range = if cursor_row < edit_start_row {
5924 Some(cursor_row..edit_end_row)
5925 } else if cursor_row > edit_end_row {
5926 Some(edit_start_row..cursor_row)
5927 } else {
5928 None
5929 };
5930 let is_move =
5931 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5932 let completion = if is_move {
5933 invalidation_row_range =
5934 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5935 let target = first_edit_start;
5936 InlineCompletion::Move { target, snapshot }
5937 } else {
5938 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5939 && !self.inline_completions_hidden_for_vim_mode;
5940
5941 if show_completions_in_buffer {
5942 if edits
5943 .iter()
5944 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5945 {
5946 let mut inlays = Vec::new();
5947 for (range, new_text) in &edits {
5948 let inlay = Inlay::inline_completion(
5949 post_inc(&mut self.next_inlay_id),
5950 range.start,
5951 new_text.as_str(),
5952 );
5953 inlay_ids.push(inlay.id);
5954 inlays.push(inlay);
5955 }
5956
5957 self.splice_inlays(&[], inlays, cx);
5958 } else {
5959 let background_color = cx.theme().status().deleted_background;
5960 self.highlight_text::<InlineCompletionHighlight>(
5961 edits.iter().map(|(range, _)| range.clone()).collect(),
5962 HighlightStyle {
5963 background_color: Some(background_color),
5964 ..Default::default()
5965 },
5966 cx,
5967 );
5968 }
5969 }
5970
5971 invalidation_row_range = edit_start_row..edit_end_row;
5972
5973 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5974 if provider.show_tab_accept_marker() {
5975 EditDisplayMode::TabAccept
5976 } else {
5977 EditDisplayMode::Inline
5978 }
5979 } else {
5980 EditDisplayMode::DiffPopover
5981 };
5982
5983 InlineCompletion::Edit {
5984 edits,
5985 edit_preview: inline_completion.edit_preview,
5986 display_mode,
5987 snapshot,
5988 }
5989 };
5990
5991 let invalidation_range = multibuffer
5992 .anchor_before(Point::new(invalidation_row_range.start, 0))
5993 ..multibuffer.anchor_after(Point::new(
5994 invalidation_row_range.end,
5995 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5996 ));
5997
5998 self.stale_inline_completion_in_menu = None;
5999 self.active_inline_completion = Some(InlineCompletionState {
6000 inlay_ids,
6001 completion,
6002 completion_id: inline_completion.id,
6003 invalidation_range,
6004 });
6005
6006 cx.notify();
6007
6008 Some(())
6009 }
6010
6011 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
6012 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
6013 }
6014
6015 fn render_code_actions_indicator(
6016 &self,
6017 _style: &EditorStyle,
6018 row: DisplayRow,
6019 is_active: bool,
6020 breakpoint: Option<&(Anchor, Breakpoint)>,
6021 cx: &mut Context<Self>,
6022 ) -> Option<IconButton> {
6023 let color = Color::Muted;
6024 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6025
6026 if self.available_code_actions.is_some() {
6027 Some(
6028 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
6029 .shape(ui::IconButtonShape::Square)
6030 .icon_size(IconSize::XSmall)
6031 .icon_color(color)
6032 .toggle_state(is_active)
6033 .tooltip({
6034 let focus_handle = self.focus_handle.clone();
6035 move |window, cx| {
6036 Tooltip::for_action_in(
6037 "Toggle Code Actions",
6038 &ToggleCodeActions {
6039 deployed_from_indicator: None,
6040 },
6041 &focus_handle,
6042 window,
6043 cx,
6044 )
6045 }
6046 })
6047 .on_click(cx.listener(move |editor, _e, window, cx| {
6048 window.focus(&editor.focus_handle(cx));
6049 editor.toggle_code_actions(
6050 &ToggleCodeActions {
6051 deployed_from_indicator: Some(row),
6052 },
6053 window,
6054 cx,
6055 );
6056 }))
6057 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6058 editor.set_breakpoint_context_menu(
6059 row,
6060 position,
6061 event.down.position,
6062 window,
6063 cx,
6064 );
6065 })),
6066 )
6067 } else {
6068 None
6069 }
6070 }
6071
6072 fn clear_tasks(&mut self) {
6073 self.tasks.clear()
6074 }
6075
6076 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6077 if self.tasks.insert(key, value).is_some() {
6078 // This case should hopefully be rare, but just in case...
6079 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6080 }
6081 }
6082
6083 /// Get all display points of breakpoints that will be rendered within editor
6084 ///
6085 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6086 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6087 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6088 fn active_breakpoints(
6089 &mut self,
6090 range: Range<DisplayRow>,
6091 window: &mut Window,
6092 cx: &mut Context<Self>,
6093 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6094 let mut breakpoint_display_points = HashMap::default();
6095
6096 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6097 return breakpoint_display_points;
6098 };
6099
6100 let snapshot = self.snapshot(window, cx);
6101
6102 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6103 let Some(project) = self.project.as_ref() else {
6104 return breakpoint_display_points;
6105 };
6106
6107 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6108 let buffer_snapshot = buffer.read(cx).snapshot();
6109
6110 for breakpoint in
6111 breakpoint_store
6112 .read(cx)
6113 .breakpoints(&buffer, None, &buffer_snapshot, cx)
6114 {
6115 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6116 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6117 anchor.text_anchor = breakpoint.0;
6118
6119 breakpoint_display_points.insert(
6120 snapshot
6121 .point_to_display_point(
6122 MultiBufferPoint {
6123 row: point.row,
6124 column: point.column,
6125 },
6126 Bias::Left,
6127 )
6128 .row(),
6129 (anchor, breakpoint.1.clone()),
6130 );
6131 }
6132
6133 return breakpoint_display_points;
6134 }
6135
6136 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6137 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6138
6139 for (buffer_snapshot, range, excerpt_id) in
6140 multi_buffer_snapshot.range_to_buffer_ranges(range)
6141 {
6142 let Some(buffer) = project.read_with(cx, |this, cx| {
6143 this.buffer_for_id(buffer_snapshot.remote_id(), cx)
6144 }) else {
6145 continue;
6146 };
6147 let breakpoints = breakpoint_store.read(cx).breakpoints(
6148 &buffer,
6149 Some(
6150 buffer_snapshot.anchor_before(range.start)
6151 ..buffer_snapshot.anchor_after(range.end),
6152 ),
6153 buffer_snapshot,
6154 cx,
6155 );
6156 for (anchor, breakpoint) in breakpoints {
6157 let multi_buffer_anchor =
6158 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), *anchor);
6159 let position = multi_buffer_anchor
6160 .to_point(&multi_buffer_snapshot)
6161 .to_display_point(&snapshot);
6162
6163 breakpoint_display_points
6164 .insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
6165 }
6166 }
6167
6168 breakpoint_display_points
6169 }
6170
6171 fn breakpoint_context_menu(
6172 &self,
6173 anchor: Anchor,
6174 window: &mut Window,
6175 cx: &mut Context<Self>,
6176 ) -> Entity<ui::ContextMenu> {
6177 let weak_editor = cx.weak_entity();
6178 let focus_handle = self.focus_handle(cx);
6179
6180 let row = self
6181 .buffer
6182 .read(cx)
6183 .snapshot(cx)
6184 .summary_for_anchor::<Point>(&anchor)
6185 .row;
6186
6187 let breakpoint = self
6188 .breakpoint_at_row(row, window, cx)
6189 .map(|(_, bp)| Arc::from(bp));
6190
6191 let log_breakpoint_msg = if breakpoint
6192 .as_ref()
6193 .is_some_and(|bp| bp.kind.log_message().is_some())
6194 {
6195 "Edit Log Breakpoint"
6196 } else {
6197 "Set Log Breakpoint"
6198 };
6199
6200 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
6201 "Unset Breakpoint"
6202 } else {
6203 "Set Breakpoint"
6204 };
6205
6206 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.state {
6207 BreakpointState::Enabled => Some("Disable"),
6208 BreakpointState::Disabled => Some("Enable"),
6209 });
6210
6211 let breakpoint = breakpoint.unwrap_or_else(|| {
6212 Arc::new(Breakpoint {
6213 state: BreakpointState::Enabled,
6214 kind: BreakpointKind::Standard,
6215 })
6216 });
6217
6218 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6219 menu.on_blur_subscription(Subscription::new(|| {}))
6220 .context(focus_handle)
6221 .when_some(toggle_state_msg, |this, msg| {
6222 this.entry(msg, None, {
6223 let weak_editor = weak_editor.clone();
6224 let breakpoint = breakpoint.clone();
6225 move |_window, cx| {
6226 weak_editor
6227 .update(cx, |this, cx| {
6228 this.edit_breakpoint_at_anchor(
6229 anchor,
6230 breakpoint.as_ref().clone(),
6231 BreakpointEditAction::InvertState,
6232 cx,
6233 );
6234 })
6235 .log_err();
6236 }
6237 })
6238 })
6239 .entry(set_breakpoint_msg, None, {
6240 let weak_editor = weak_editor.clone();
6241 let breakpoint = breakpoint.clone();
6242 move |_window, cx| {
6243 weak_editor
6244 .update(cx, |this, cx| {
6245 this.edit_breakpoint_at_anchor(
6246 anchor,
6247 breakpoint.as_ref().clone(),
6248 BreakpointEditAction::Toggle,
6249 cx,
6250 );
6251 })
6252 .log_err();
6253 }
6254 })
6255 .entry(log_breakpoint_msg, None, move |window, cx| {
6256 weak_editor
6257 .update(cx, |this, cx| {
6258 this.add_edit_breakpoint_block(anchor, breakpoint.as_ref(), window, cx);
6259 })
6260 .log_err();
6261 })
6262 })
6263 }
6264
6265 fn render_breakpoint(
6266 &self,
6267 position: Anchor,
6268 row: DisplayRow,
6269 breakpoint: &Breakpoint,
6270 cx: &mut Context<Self>,
6271 ) -> IconButton {
6272 let (color, icon) = {
6273 let color = if self
6274 .gutter_breakpoint_indicator
6275 .is_some_and(|point| point.row() == row)
6276 {
6277 Color::Hint
6278 } else if breakpoint.is_disabled() {
6279 Color::Custom(Color::Debugger.color(cx).opacity(0.5))
6280 } else {
6281 Color::Debugger
6282 };
6283 let icon = match &breakpoint.kind {
6284 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6285 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6286 };
6287 (color, icon)
6288 };
6289
6290 let breakpoint = Arc::from(breakpoint.clone());
6291
6292 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6293 .icon_size(IconSize::XSmall)
6294 .size(ui::ButtonSize::None)
6295 .icon_color(color)
6296 .style(ButtonStyle::Transparent)
6297 .on_click(cx.listener({
6298 let breakpoint = breakpoint.clone();
6299
6300 move |editor, _e, window, cx| {
6301 window.focus(&editor.focus_handle(cx));
6302 editor.edit_breakpoint_at_anchor(
6303 position,
6304 breakpoint.as_ref().clone(),
6305 BreakpointEditAction::Toggle,
6306 cx,
6307 );
6308 }
6309 }))
6310 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6311 editor.set_breakpoint_context_menu(
6312 row,
6313 Some(position),
6314 event.down.position,
6315 window,
6316 cx,
6317 );
6318 }))
6319 }
6320
6321 fn build_tasks_context(
6322 project: &Entity<Project>,
6323 buffer: &Entity<Buffer>,
6324 buffer_row: u32,
6325 tasks: &Arc<RunnableTasks>,
6326 cx: &mut Context<Self>,
6327 ) -> Task<Option<task::TaskContext>> {
6328 let position = Point::new(buffer_row, tasks.column);
6329 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6330 let location = Location {
6331 buffer: buffer.clone(),
6332 range: range_start..range_start,
6333 };
6334 // Fill in the environmental variables from the tree-sitter captures
6335 let mut captured_task_variables = TaskVariables::default();
6336 for (capture_name, value) in tasks.extra_variables.clone() {
6337 captured_task_variables.insert(
6338 task::VariableName::Custom(capture_name.into()),
6339 value.clone(),
6340 );
6341 }
6342 project.update(cx, |project, cx| {
6343 project.task_store().update(cx, |task_store, cx| {
6344 task_store.task_context_for_location(captured_task_variables, location, cx)
6345 })
6346 })
6347 }
6348
6349 pub fn spawn_nearest_task(
6350 &mut self,
6351 action: &SpawnNearestTask,
6352 window: &mut Window,
6353 cx: &mut Context<Self>,
6354 ) {
6355 let Some((workspace, _)) = self.workspace.clone() else {
6356 return;
6357 };
6358 let Some(project) = self.project.clone() else {
6359 return;
6360 };
6361
6362 // Try to find a closest, enclosing node using tree-sitter that has a
6363 // task
6364 let Some((buffer, buffer_row, tasks)) = self
6365 .find_enclosing_node_task(cx)
6366 // Or find the task that's closest in row-distance.
6367 .or_else(|| self.find_closest_task(cx))
6368 else {
6369 return;
6370 };
6371
6372 let reveal_strategy = action.reveal;
6373 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6374 cx.spawn_in(window, async move |_, cx| {
6375 let context = task_context.await?;
6376 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6377
6378 let resolved = resolved_task.resolved.as_mut()?;
6379 resolved.reveal = reveal_strategy;
6380
6381 workspace
6382 .update(cx, |workspace, cx| {
6383 workspace::tasks::schedule_resolved_task(
6384 workspace,
6385 task_source_kind,
6386 resolved_task,
6387 false,
6388 cx,
6389 );
6390 })
6391 .ok()
6392 })
6393 .detach();
6394 }
6395
6396 fn find_closest_task(
6397 &mut self,
6398 cx: &mut Context<Self>,
6399 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6400 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6401
6402 let ((buffer_id, row), tasks) = self
6403 .tasks
6404 .iter()
6405 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6406
6407 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6408 let tasks = Arc::new(tasks.to_owned());
6409 Some((buffer, *row, tasks))
6410 }
6411
6412 fn find_enclosing_node_task(
6413 &mut self,
6414 cx: &mut Context<Self>,
6415 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6416 let snapshot = self.buffer.read(cx).snapshot(cx);
6417 let offset = self.selections.newest::<usize>(cx).head();
6418 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6419 let buffer_id = excerpt.buffer().remote_id();
6420
6421 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6422 let mut cursor = layer.node().walk();
6423
6424 while cursor.goto_first_child_for_byte(offset).is_some() {
6425 if cursor.node().end_byte() == offset {
6426 cursor.goto_next_sibling();
6427 }
6428 }
6429
6430 // Ascend to the smallest ancestor that contains the range and has a task.
6431 loop {
6432 let node = cursor.node();
6433 let node_range = node.byte_range();
6434 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6435
6436 // Check if this node contains our offset
6437 if node_range.start <= offset && node_range.end >= offset {
6438 // If it contains offset, check for task
6439 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6440 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6441 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6442 }
6443 }
6444
6445 if !cursor.goto_parent() {
6446 break;
6447 }
6448 }
6449 None
6450 }
6451
6452 fn render_run_indicator(
6453 &self,
6454 _style: &EditorStyle,
6455 is_active: bool,
6456 row: DisplayRow,
6457 breakpoint: Option<(Anchor, Breakpoint)>,
6458 cx: &mut Context<Self>,
6459 ) -> IconButton {
6460 let color = Color::Muted;
6461 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6462
6463 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6464 .shape(ui::IconButtonShape::Square)
6465 .icon_size(IconSize::XSmall)
6466 .icon_color(color)
6467 .toggle_state(is_active)
6468 .on_click(cx.listener(move |editor, _e, window, cx| {
6469 window.focus(&editor.focus_handle(cx));
6470 editor.toggle_code_actions(
6471 &ToggleCodeActions {
6472 deployed_from_indicator: Some(row),
6473 },
6474 window,
6475 cx,
6476 );
6477 }))
6478 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6479 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
6480 }))
6481 }
6482
6483 pub fn context_menu_visible(&self) -> bool {
6484 !self.edit_prediction_preview_is_active()
6485 && self
6486 .context_menu
6487 .borrow()
6488 .as_ref()
6489 .map_or(false, |menu| menu.visible())
6490 }
6491
6492 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6493 self.context_menu
6494 .borrow()
6495 .as_ref()
6496 .map(|menu| menu.origin())
6497 }
6498
6499 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
6500 self.context_menu_options = Some(options);
6501 }
6502
6503 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6504 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6505
6506 fn render_edit_prediction_popover(
6507 &mut self,
6508 text_bounds: &Bounds<Pixels>,
6509 content_origin: gpui::Point<Pixels>,
6510 editor_snapshot: &EditorSnapshot,
6511 visible_row_range: Range<DisplayRow>,
6512 scroll_top: f32,
6513 scroll_bottom: f32,
6514 line_layouts: &[LineWithInvisibles],
6515 line_height: Pixels,
6516 scroll_pixel_position: gpui::Point<Pixels>,
6517 newest_selection_head: Option<DisplayPoint>,
6518 editor_width: Pixels,
6519 style: &EditorStyle,
6520 window: &mut Window,
6521 cx: &mut App,
6522 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6523 let active_inline_completion = self.active_inline_completion.as_ref()?;
6524
6525 if self.edit_prediction_visible_in_cursor_popover(true) {
6526 return None;
6527 }
6528
6529 match &active_inline_completion.completion {
6530 InlineCompletion::Move { target, .. } => {
6531 let target_display_point = target.to_display_point(editor_snapshot);
6532
6533 if self.edit_prediction_requires_modifier() {
6534 if !self.edit_prediction_preview_is_active() {
6535 return None;
6536 }
6537
6538 self.render_edit_prediction_modifier_jump_popover(
6539 text_bounds,
6540 content_origin,
6541 visible_row_range,
6542 line_layouts,
6543 line_height,
6544 scroll_pixel_position,
6545 newest_selection_head,
6546 target_display_point,
6547 window,
6548 cx,
6549 )
6550 } else {
6551 self.render_edit_prediction_eager_jump_popover(
6552 text_bounds,
6553 content_origin,
6554 editor_snapshot,
6555 visible_row_range,
6556 scroll_top,
6557 scroll_bottom,
6558 line_height,
6559 scroll_pixel_position,
6560 target_display_point,
6561 editor_width,
6562 window,
6563 cx,
6564 )
6565 }
6566 }
6567 InlineCompletion::Edit {
6568 display_mode: EditDisplayMode::Inline,
6569 ..
6570 } => None,
6571 InlineCompletion::Edit {
6572 display_mode: EditDisplayMode::TabAccept,
6573 edits,
6574 ..
6575 } => {
6576 let range = &edits.first()?.0;
6577 let target_display_point = range.end.to_display_point(editor_snapshot);
6578
6579 self.render_edit_prediction_end_of_line_popover(
6580 "Accept",
6581 editor_snapshot,
6582 visible_row_range,
6583 target_display_point,
6584 line_height,
6585 scroll_pixel_position,
6586 content_origin,
6587 editor_width,
6588 window,
6589 cx,
6590 )
6591 }
6592 InlineCompletion::Edit {
6593 edits,
6594 edit_preview,
6595 display_mode: EditDisplayMode::DiffPopover,
6596 snapshot,
6597 } => self.render_edit_prediction_diff_popover(
6598 text_bounds,
6599 content_origin,
6600 editor_snapshot,
6601 visible_row_range,
6602 line_layouts,
6603 line_height,
6604 scroll_pixel_position,
6605 newest_selection_head,
6606 editor_width,
6607 style,
6608 edits,
6609 edit_preview,
6610 snapshot,
6611 window,
6612 cx,
6613 ),
6614 }
6615 }
6616
6617 fn render_edit_prediction_modifier_jump_popover(
6618 &mut self,
6619 text_bounds: &Bounds<Pixels>,
6620 content_origin: gpui::Point<Pixels>,
6621 visible_row_range: Range<DisplayRow>,
6622 line_layouts: &[LineWithInvisibles],
6623 line_height: Pixels,
6624 scroll_pixel_position: gpui::Point<Pixels>,
6625 newest_selection_head: Option<DisplayPoint>,
6626 target_display_point: DisplayPoint,
6627 window: &mut Window,
6628 cx: &mut App,
6629 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6630 let scrolled_content_origin =
6631 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6632
6633 const SCROLL_PADDING_Y: Pixels = px(12.);
6634
6635 if target_display_point.row() < visible_row_range.start {
6636 return self.render_edit_prediction_scroll_popover(
6637 |_| SCROLL_PADDING_Y,
6638 IconName::ArrowUp,
6639 visible_row_range,
6640 line_layouts,
6641 newest_selection_head,
6642 scrolled_content_origin,
6643 window,
6644 cx,
6645 );
6646 } else if target_display_point.row() >= visible_row_range.end {
6647 return self.render_edit_prediction_scroll_popover(
6648 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6649 IconName::ArrowDown,
6650 visible_row_range,
6651 line_layouts,
6652 newest_selection_head,
6653 scrolled_content_origin,
6654 window,
6655 cx,
6656 );
6657 }
6658
6659 const POLE_WIDTH: Pixels = px(2.);
6660
6661 let line_layout =
6662 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6663 let target_column = target_display_point.column() as usize;
6664
6665 let target_x = line_layout.x_for_index(target_column);
6666 let target_y =
6667 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6668
6669 let flag_on_right = target_x < text_bounds.size.width / 2.;
6670
6671 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6672 border_color.l += 0.001;
6673
6674 let mut element = v_flex()
6675 .items_end()
6676 .when(flag_on_right, |el| el.items_start())
6677 .child(if flag_on_right {
6678 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6679 .rounded_bl(px(0.))
6680 .rounded_tl(px(0.))
6681 .border_l_2()
6682 .border_color(border_color)
6683 } else {
6684 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6685 .rounded_br(px(0.))
6686 .rounded_tr(px(0.))
6687 .border_r_2()
6688 .border_color(border_color)
6689 })
6690 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6691 .into_any();
6692
6693 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6694
6695 let mut origin = scrolled_content_origin + point(target_x, target_y)
6696 - point(
6697 if flag_on_right {
6698 POLE_WIDTH
6699 } else {
6700 size.width - POLE_WIDTH
6701 },
6702 size.height - line_height,
6703 );
6704
6705 origin.x = origin.x.max(content_origin.x);
6706
6707 element.prepaint_at(origin, window, cx);
6708
6709 Some((element, origin))
6710 }
6711
6712 fn render_edit_prediction_scroll_popover(
6713 &mut self,
6714 to_y: impl Fn(Size<Pixels>) -> Pixels,
6715 scroll_icon: IconName,
6716 visible_row_range: Range<DisplayRow>,
6717 line_layouts: &[LineWithInvisibles],
6718 newest_selection_head: Option<DisplayPoint>,
6719 scrolled_content_origin: gpui::Point<Pixels>,
6720 window: &mut Window,
6721 cx: &mut App,
6722 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6723 let mut element = self
6724 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6725 .into_any();
6726
6727 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6728
6729 let cursor = newest_selection_head?;
6730 let cursor_row_layout =
6731 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6732 let cursor_column = cursor.column() as usize;
6733
6734 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6735
6736 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6737
6738 element.prepaint_at(origin, window, cx);
6739 Some((element, origin))
6740 }
6741
6742 fn render_edit_prediction_eager_jump_popover(
6743 &mut self,
6744 text_bounds: &Bounds<Pixels>,
6745 content_origin: gpui::Point<Pixels>,
6746 editor_snapshot: &EditorSnapshot,
6747 visible_row_range: Range<DisplayRow>,
6748 scroll_top: f32,
6749 scroll_bottom: f32,
6750 line_height: Pixels,
6751 scroll_pixel_position: gpui::Point<Pixels>,
6752 target_display_point: DisplayPoint,
6753 editor_width: Pixels,
6754 window: &mut Window,
6755 cx: &mut App,
6756 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6757 if target_display_point.row().as_f32() < scroll_top {
6758 let mut element = self
6759 .render_edit_prediction_line_popover(
6760 "Jump to Edit",
6761 Some(IconName::ArrowUp),
6762 window,
6763 cx,
6764 )?
6765 .into_any();
6766
6767 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6768 let offset = point(
6769 (text_bounds.size.width - size.width) / 2.,
6770 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6771 );
6772
6773 let origin = text_bounds.origin + offset;
6774 element.prepaint_at(origin, window, cx);
6775 Some((element, origin))
6776 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6777 let mut element = self
6778 .render_edit_prediction_line_popover(
6779 "Jump to Edit",
6780 Some(IconName::ArrowDown),
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 text_bounds.size.height - size.height - 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 {
6796 self.render_edit_prediction_end_of_line_popover(
6797 "Jump to Edit",
6798 editor_snapshot,
6799 visible_row_range,
6800 target_display_point,
6801 line_height,
6802 scroll_pixel_position,
6803 content_origin,
6804 editor_width,
6805 window,
6806 cx,
6807 )
6808 }
6809 }
6810
6811 fn render_edit_prediction_end_of_line_popover(
6812 self: &mut Editor,
6813 label: &'static str,
6814 editor_snapshot: &EditorSnapshot,
6815 visible_row_range: Range<DisplayRow>,
6816 target_display_point: DisplayPoint,
6817 line_height: Pixels,
6818 scroll_pixel_position: gpui::Point<Pixels>,
6819 content_origin: gpui::Point<Pixels>,
6820 editor_width: Pixels,
6821 window: &mut Window,
6822 cx: &mut App,
6823 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6824 let target_line_end = DisplayPoint::new(
6825 target_display_point.row(),
6826 editor_snapshot.line_len(target_display_point.row()),
6827 );
6828
6829 let mut element = self
6830 .render_edit_prediction_line_popover(label, None, window, cx)?
6831 .into_any();
6832
6833 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6834
6835 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6836
6837 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6838 let mut origin = start_point
6839 + line_origin
6840 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6841 origin.x = origin.x.max(content_origin.x);
6842
6843 let max_x = content_origin.x + editor_width - size.width;
6844
6845 if origin.x > max_x {
6846 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6847
6848 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6849 origin.y += offset;
6850 IconName::ArrowUp
6851 } else {
6852 origin.y -= offset;
6853 IconName::ArrowDown
6854 };
6855
6856 element = self
6857 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6858 .into_any();
6859
6860 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6861
6862 origin.x = content_origin.x + editor_width - size.width - px(2.);
6863 }
6864
6865 element.prepaint_at(origin, window, cx);
6866 Some((element, origin))
6867 }
6868
6869 fn render_edit_prediction_diff_popover(
6870 self: &Editor,
6871 text_bounds: &Bounds<Pixels>,
6872 content_origin: gpui::Point<Pixels>,
6873 editor_snapshot: &EditorSnapshot,
6874 visible_row_range: Range<DisplayRow>,
6875 line_layouts: &[LineWithInvisibles],
6876 line_height: Pixels,
6877 scroll_pixel_position: gpui::Point<Pixels>,
6878 newest_selection_head: Option<DisplayPoint>,
6879 editor_width: Pixels,
6880 style: &EditorStyle,
6881 edits: &Vec<(Range<Anchor>, String)>,
6882 edit_preview: &Option<language::EditPreview>,
6883 snapshot: &language::BufferSnapshot,
6884 window: &mut Window,
6885 cx: &mut App,
6886 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6887 let edit_start = edits
6888 .first()
6889 .unwrap()
6890 .0
6891 .start
6892 .to_display_point(editor_snapshot);
6893 let edit_end = edits
6894 .last()
6895 .unwrap()
6896 .0
6897 .end
6898 .to_display_point(editor_snapshot);
6899
6900 let is_visible = visible_row_range.contains(&edit_start.row())
6901 || visible_row_range.contains(&edit_end.row());
6902 if !is_visible {
6903 return None;
6904 }
6905
6906 let highlighted_edits =
6907 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6908
6909 let styled_text = highlighted_edits.to_styled_text(&style.text);
6910 let line_count = highlighted_edits.text.lines().count();
6911
6912 const BORDER_WIDTH: Pixels = px(1.);
6913
6914 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6915 let has_keybind = keybind.is_some();
6916
6917 let mut element = h_flex()
6918 .items_start()
6919 .child(
6920 h_flex()
6921 .bg(cx.theme().colors().editor_background)
6922 .border(BORDER_WIDTH)
6923 .shadow_sm()
6924 .border_color(cx.theme().colors().border)
6925 .rounded_l_lg()
6926 .when(line_count > 1, |el| el.rounded_br_lg())
6927 .pr_1()
6928 .child(styled_text),
6929 )
6930 .child(
6931 h_flex()
6932 .h(line_height + BORDER_WIDTH * 2.)
6933 .px_1p5()
6934 .gap_1()
6935 // Workaround: For some reason, there's a gap if we don't do this
6936 .ml(-BORDER_WIDTH)
6937 .shadow(smallvec![gpui::BoxShadow {
6938 color: gpui::black().opacity(0.05),
6939 offset: point(px(1.), px(1.)),
6940 blur_radius: px(2.),
6941 spread_radius: px(0.),
6942 }])
6943 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6944 .border(BORDER_WIDTH)
6945 .border_color(cx.theme().colors().border)
6946 .rounded_r_lg()
6947 .id("edit_prediction_diff_popover_keybind")
6948 .when(!has_keybind, |el| {
6949 let status_colors = cx.theme().status();
6950
6951 el.bg(status_colors.error_background)
6952 .border_color(status_colors.error.opacity(0.6))
6953 .child(Icon::new(IconName::Info).color(Color::Error))
6954 .cursor_default()
6955 .hoverable_tooltip(move |_window, cx| {
6956 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6957 })
6958 })
6959 .children(keybind),
6960 )
6961 .into_any();
6962
6963 let longest_row =
6964 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6965 let longest_line_width = if visible_row_range.contains(&longest_row) {
6966 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6967 } else {
6968 layout_line(
6969 longest_row,
6970 editor_snapshot,
6971 style,
6972 editor_width,
6973 |_| false,
6974 window,
6975 cx,
6976 )
6977 .width
6978 };
6979
6980 let viewport_bounds =
6981 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6982 right: -EditorElement::SCROLLBAR_WIDTH,
6983 ..Default::default()
6984 });
6985
6986 let x_after_longest =
6987 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6988 - scroll_pixel_position.x;
6989
6990 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6991
6992 // Fully visible if it can be displayed within the window (allow overlapping other
6993 // panes). However, this is only allowed if the popover starts within text_bounds.
6994 let can_position_to_the_right = x_after_longest < text_bounds.right()
6995 && x_after_longest + element_bounds.width < viewport_bounds.right();
6996
6997 let mut origin = if can_position_to_the_right {
6998 point(
6999 x_after_longest,
7000 text_bounds.origin.y + edit_start.row().as_f32() * line_height
7001 - scroll_pixel_position.y,
7002 )
7003 } else {
7004 let cursor_row = newest_selection_head.map(|head| head.row());
7005 let above_edit = edit_start
7006 .row()
7007 .0
7008 .checked_sub(line_count as u32)
7009 .map(DisplayRow);
7010 let below_edit = Some(edit_end.row() + 1);
7011 let above_cursor =
7012 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
7013 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
7014
7015 // Place the edit popover adjacent to the edit if there is a location
7016 // available that is onscreen and does not obscure the cursor. Otherwise,
7017 // place it adjacent to the cursor.
7018 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
7019 .into_iter()
7020 .flatten()
7021 .find(|&start_row| {
7022 let end_row = start_row + line_count as u32;
7023 visible_row_range.contains(&start_row)
7024 && visible_row_range.contains(&end_row)
7025 && cursor_row.map_or(true, |cursor_row| {
7026 !((start_row..end_row).contains(&cursor_row))
7027 })
7028 })?;
7029
7030 content_origin
7031 + point(
7032 -scroll_pixel_position.x,
7033 row_target.as_f32() * line_height - scroll_pixel_position.y,
7034 )
7035 };
7036
7037 origin.x -= BORDER_WIDTH;
7038
7039 window.defer_draw(element, origin, 1);
7040
7041 // Do not return an element, since it will already be drawn due to defer_draw.
7042 None
7043 }
7044
7045 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
7046 px(30.)
7047 }
7048
7049 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
7050 if self.read_only(cx) {
7051 cx.theme().players().read_only()
7052 } else {
7053 self.style.as_ref().unwrap().local_player
7054 }
7055 }
7056
7057 fn render_edit_prediction_accept_keybind(
7058 &self,
7059 window: &mut Window,
7060 cx: &App,
7061 ) -> Option<AnyElement> {
7062 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
7063 let accept_keystroke = accept_binding.keystroke()?;
7064
7065 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7066
7067 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
7068 Color::Accent
7069 } else {
7070 Color::Muted
7071 };
7072
7073 h_flex()
7074 .px_0p5()
7075 .when(is_platform_style_mac, |parent| parent.gap_0p5())
7076 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7077 .text_size(TextSize::XSmall.rems(cx))
7078 .child(h_flex().children(ui::render_modifiers(
7079 &accept_keystroke.modifiers,
7080 PlatformStyle::platform(),
7081 Some(modifiers_color),
7082 Some(IconSize::XSmall.rems().into()),
7083 true,
7084 )))
7085 .when(is_platform_style_mac, |parent| {
7086 parent.child(accept_keystroke.key.clone())
7087 })
7088 .when(!is_platform_style_mac, |parent| {
7089 parent.child(
7090 Key::new(
7091 util::capitalize(&accept_keystroke.key),
7092 Some(Color::Default),
7093 )
7094 .size(Some(IconSize::XSmall.rems().into())),
7095 )
7096 })
7097 .into_any()
7098 .into()
7099 }
7100
7101 fn render_edit_prediction_line_popover(
7102 &self,
7103 label: impl Into<SharedString>,
7104 icon: Option<IconName>,
7105 window: &mut Window,
7106 cx: &App,
7107 ) -> Option<Stateful<Div>> {
7108 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7109
7110 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7111 let has_keybind = keybind.is_some();
7112
7113 let result = h_flex()
7114 .id("ep-line-popover")
7115 .py_0p5()
7116 .pl_1()
7117 .pr(padding_right)
7118 .gap_1()
7119 .rounded_md()
7120 .border_1()
7121 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7122 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7123 .shadow_sm()
7124 .when(!has_keybind, |el| {
7125 let status_colors = cx.theme().status();
7126
7127 el.bg(status_colors.error_background)
7128 .border_color(status_colors.error.opacity(0.6))
7129 .pl_2()
7130 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7131 .cursor_default()
7132 .hoverable_tooltip(move |_window, cx| {
7133 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7134 })
7135 })
7136 .children(keybind)
7137 .child(
7138 Label::new(label)
7139 .size(LabelSize::Small)
7140 .when(!has_keybind, |el| {
7141 el.color(cx.theme().status().error.into()).strikethrough()
7142 }),
7143 )
7144 .when(!has_keybind, |el| {
7145 el.child(
7146 h_flex().ml_1().child(
7147 Icon::new(IconName::Info)
7148 .size(IconSize::Small)
7149 .color(cx.theme().status().error.into()),
7150 ),
7151 )
7152 })
7153 .when_some(icon, |element, icon| {
7154 element.child(
7155 div()
7156 .mt(px(1.5))
7157 .child(Icon::new(icon).size(IconSize::Small)),
7158 )
7159 });
7160
7161 Some(result)
7162 }
7163
7164 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7165 let accent_color = cx.theme().colors().text_accent;
7166 let editor_bg_color = cx.theme().colors().editor_background;
7167 editor_bg_color.blend(accent_color.opacity(0.1))
7168 }
7169
7170 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7171 let accent_color = cx.theme().colors().text_accent;
7172 let editor_bg_color = cx.theme().colors().editor_background;
7173 editor_bg_color.blend(accent_color.opacity(0.6))
7174 }
7175
7176 fn render_edit_prediction_cursor_popover(
7177 &self,
7178 min_width: Pixels,
7179 max_width: Pixels,
7180 cursor_point: Point,
7181 style: &EditorStyle,
7182 accept_keystroke: Option<&gpui::Keystroke>,
7183 _window: &Window,
7184 cx: &mut Context<Editor>,
7185 ) -> Option<AnyElement> {
7186 let provider = self.edit_prediction_provider.as_ref()?;
7187
7188 if provider.provider.needs_terms_acceptance(cx) {
7189 return Some(
7190 h_flex()
7191 .min_w(min_width)
7192 .flex_1()
7193 .px_2()
7194 .py_1()
7195 .gap_3()
7196 .elevation_2(cx)
7197 .hover(|style| style.bg(cx.theme().colors().element_hover))
7198 .id("accept-terms")
7199 .cursor_pointer()
7200 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7201 .on_click(cx.listener(|this, _event, window, cx| {
7202 cx.stop_propagation();
7203 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7204 window.dispatch_action(
7205 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7206 cx,
7207 );
7208 }))
7209 .child(
7210 h_flex()
7211 .flex_1()
7212 .gap_2()
7213 .child(Icon::new(IconName::ZedPredict))
7214 .child(Label::new("Accept Terms of Service"))
7215 .child(div().w_full())
7216 .child(
7217 Icon::new(IconName::ArrowUpRight)
7218 .color(Color::Muted)
7219 .size(IconSize::Small),
7220 )
7221 .into_any_element(),
7222 )
7223 .into_any(),
7224 );
7225 }
7226
7227 let is_refreshing = provider.provider.is_refreshing(cx);
7228
7229 fn pending_completion_container() -> Div {
7230 h_flex()
7231 .h_full()
7232 .flex_1()
7233 .gap_2()
7234 .child(Icon::new(IconName::ZedPredict))
7235 }
7236
7237 let completion = match &self.active_inline_completion {
7238 Some(prediction) => {
7239 if !self.has_visible_completions_menu() {
7240 const RADIUS: Pixels = px(6.);
7241 const BORDER_WIDTH: Pixels = px(1.);
7242
7243 return Some(
7244 h_flex()
7245 .elevation_2(cx)
7246 .border(BORDER_WIDTH)
7247 .border_color(cx.theme().colors().border)
7248 .when(accept_keystroke.is_none(), |el| {
7249 el.border_color(cx.theme().status().error)
7250 })
7251 .rounded(RADIUS)
7252 .rounded_tl(px(0.))
7253 .overflow_hidden()
7254 .child(div().px_1p5().child(match &prediction.completion {
7255 InlineCompletion::Move { target, snapshot } => {
7256 use text::ToPoint as _;
7257 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7258 {
7259 Icon::new(IconName::ZedPredictDown)
7260 } else {
7261 Icon::new(IconName::ZedPredictUp)
7262 }
7263 }
7264 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7265 }))
7266 .child(
7267 h_flex()
7268 .gap_1()
7269 .py_1()
7270 .px_2()
7271 .rounded_r(RADIUS - BORDER_WIDTH)
7272 .border_l_1()
7273 .border_color(cx.theme().colors().border)
7274 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7275 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7276 el.child(
7277 Label::new("Hold")
7278 .size(LabelSize::Small)
7279 .when(accept_keystroke.is_none(), |el| {
7280 el.strikethrough()
7281 })
7282 .line_height_style(LineHeightStyle::UiLabel),
7283 )
7284 })
7285 .id("edit_prediction_cursor_popover_keybind")
7286 .when(accept_keystroke.is_none(), |el| {
7287 let status_colors = cx.theme().status();
7288
7289 el.bg(status_colors.error_background)
7290 .border_color(status_colors.error.opacity(0.6))
7291 .child(Icon::new(IconName::Info).color(Color::Error))
7292 .cursor_default()
7293 .hoverable_tooltip(move |_window, cx| {
7294 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7295 .into()
7296 })
7297 })
7298 .when_some(
7299 accept_keystroke.as_ref(),
7300 |el, accept_keystroke| {
7301 el.child(h_flex().children(ui::render_modifiers(
7302 &accept_keystroke.modifiers,
7303 PlatformStyle::platform(),
7304 Some(Color::Default),
7305 Some(IconSize::XSmall.rems().into()),
7306 false,
7307 )))
7308 },
7309 ),
7310 )
7311 .into_any(),
7312 );
7313 }
7314
7315 self.render_edit_prediction_cursor_popover_preview(
7316 prediction,
7317 cursor_point,
7318 style,
7319 cx,
7320 )?
7321 }
7322
7323 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7324 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7325 stale_completion,
7326 cursor_point,
7327 style,
7328 cx,
7329 )?,
7330
7331 None => {
7332 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7333 }
7334 },
7335
7336 None => pending_completion_container().child(Label::new("No Prediction")),
7337 };
7338
7339 let completion = if is_refreshing {
7340 completion
7341 .with_animation(
7342 "loading-completion",
7343 Animation::new(Duration::from_secs(2))
7344 .repeat()
7345 .with_easing(pulsating_between(0.4, 0.8)),
7346 |label, delta| label.opacity(delta),
7347 )
7348 .into_any_element()
7349 } else {
7350 completion.into_any_element()
7351 };
7352
7353 let has_completion = self.active_inline_completion.is_some();
7354
7355 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7356 Some(
7357 h_flex()
7358 .min_w(min_width)
7359 .max_w(max_width)
7360 .flex_1()
7361 .elevation_2(cx)
7362 .border_color(cx.theme().colors().border)
7363 .child(
7364 div()
7365 .flex_1()
7366 .py_1()
7367 .px_2()
7368 .overflow_hidden()
7369 .child(completion),
7370 )
7371 .when_some(accept_keystroke, |el, accept_keystroke| {
7372 if !accept_keystroke.modifiers.modified() {
7373 return el;
7374 }
7375
7376 el.child(
7377 h_flex()
7378 .h_full()
7379 .border_l_1()
7380 .rounded_r_lg()
7381 .border_color(cx.theme().colors().border)
7382 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7383 .gap_1()
7384 .py_1()
7385 .px_2()
7386 .child(
7387 h_flex()
7388 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7389 .when(is_platform_style_mac, |parent| parent.gap_1())
7390 .child(h_flex().children(ui::render_modifiers(
7391 &accept_keystroke.modifiers,
7392 PlatformStyle::platform(),
7393 Some(if !has_completion {
7394 Color::Muted
7395 } else {
7396 Color::Default
7397 }),
7398 None,
7399 false,
7400 ))),
7401 )
7402 .child(Label::new("Preview").into_any_element())
7403 .opacity(if has_completion { 1.0 } else { 0.4 }),
7404 )
7405 })
7406 .into_any(),
7407 )
7408 }
7409
7410 fn render_edit_prediction_cursor_popover_preview(
7411 &self,
7412 completion: &InlineCompletionState,
7413 cursor_point: Point,
7414 style: &EditorStyle,
7415 cx: &mut Context<Editor>,
7416 ) -> Option<Div> {
7417 use text::ToPoint as _;
7418
7419 fn render_relative_row_jump(
7420 prefix: impl Into<String>,
7421 current_row: u32,
7422 target_row: u32,
7423 ) -> Div {
7424 let (row_diff, arrow) = if target_row < current_row {
7425 (current_row - target_row, IconName::ArrowUp)
7426 } else {
7427 (target_row - current_row, IconName::ArrowDown)
7428 };
7429
7430 h_flex()
7431 .child(
7432 Label::new(format!("{}{}", prefix.into(), row_diff))
7433 .color(Color::Muted)
7434 .size(LabelSize::Small),
7435 )
7436 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7437 }
7438
7439 match &completion.completion {
7440 InlineCompletion::Move {
7441 target, snapshot, ..
7442 } => Some(
7443 h_flex()
7444 .px_2()
7445 .gap_2()
7446 .flex_1()
7447 .child(
7448 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7449 Icon::new(IconName::ZedPredictDown)
7450 } else {
7451 Icon::new(IconName::ZedPredictUp)
7452 },
7453 )
7454 .child(Label::new("Jump to Edit")),
7455 ),
7456
7457 InlineCompletion::Edit {
7458 edits,
7459 edit_preview,
7460 snapshot,
7461 display_mode: _,
7462 } => {
7463 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7464
7465 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7466 &snapshot,
7467 &edits,
7468 edit_preview.as_ref()?,
7469 true,
7470 cx,
7471 )
7472 .first_line_preview();
7473
7474 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7475 .with_default_highlights(&style.text, highlighted_edits.highlights);
7476
7477 let preview = h_flex()
7478 .gap_1()
7479 .min_w_16()
7480 .child(styled_text)
7481 .when(has_more_lines, |parent| parent.child("…"));
7482
7483 let left = if first_edit_row != cursor_point.row {
7484 render_relative_row_jump("", cursor_point.row, first_edit_row)
7485 .into_any_element()
7486 } else {
7487 Icon::new(IconName::ZedPredict).into_any_element()
7488 };
7489
7490 Some(
7491 h_flex()
7492 .h_full()
7493 .flex_1()
7494 .gap_2()
7495 .pr_1()
7496 .overflow_x_hidden()
7497 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7498 .child(left)
7499 .child(preview),
7500 )
7501 }
7502 }
7503 }
7504
7505 fn render_context_menu(
7506 &self,
7507 style: &EditorStyle,
7508 max_height_in_lines: u32,
7509 y_flipped: bool,
7510 window: &mut Window,
7511 cx: &mut Context<Editor>,
7512 ) -> Option<AnyElement> {
7513 let menu = self.context_menu.borrow();
7514 let menu = menu.as_ref()?;
7515 if !menu.visible() {
7516 return None;
7517 };
7518 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7519 }
7520
7521 fn render_context_menu_aside(
7522 &mut self,
7523 max_size: Size<Pixels>,
7524 window: &mut Window,
7525 cx: &mut Context<Editor>,
7526 ) -> Option<AnyElement> {
7527 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7528 if menu.visible() {
7529 menu.render_aside(self, max_size, window, cx)
7530 } else {
7531 None
7532 }
7533 })
7534 }
7535
7536 fn hide_context_menu(
7537 &mut self,
7538 window: &mut Window,
7539 cx: &mut Context<Self>,
7540 ) -> Option<CodeContextMenu> {
7541 cx.notify();
7542 self.completion_tasks.clear();
7543 let context_menu = self.context_menu.borrow_mut().take();
7544 self.stale_inline_completion_in_menu.take();
7545 self.update_visible_inline_completion(window, cx);
7546 context_menu
7547 }
7548
7549 fn show_snippet_choices(
7550 &mut self,
7551 choices: &Vec<String>,
7552 selection: Range<Anchor>,
7553 cx: &mut Context<Self>,
7554 ) {
7555 if selection.start.buffer_id.is_none() {
7556 return;
7557 }
7558 let buffer_id = selection.start.buffer_id.unwrap();
7559 let buffer = self.buffer().read(cx).buffer(buffer_id);
7560 let id = post_inc(&mut self.next_completion_id);
7561
7562 if let Some(buffer) = buffer {
7563 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7564 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7565 ));
7566 }
7567 }
7568
7569 pub fn insert_snippet(
7570 &mut self,
7571 insertion_ranges: &[Range<usize>],
7572 snippet: Snippet,
7573 window: &mut Window,
7574 cx: &mut Context<Self>,
7575 ) -> Result<()> {
7576 struct Tabstop<T> {
7577 is_end_tabstop: bool,
7578 ranges: Vec<Range<T>>,
7579 choices: Option<Vec<String>>,
7580 }
7581
7582 let tabstops = self.buffer.update(cx, |buffer, cx| {
7583 let snippet_text: Arc<str> = snippet.text.clone().into();
7584 let edits = insertion_ranges
7585 .iter()
7586 .cloned()
7587 .map(|range| (range, snippet_text.clone()));
7588 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
7589
7590 let snapshot = &*buffer.read(cx);
7591 let snippet = &snippet;
7592 snippet
7593 .tabstops
7594 .iter()
7595 .map(|tabstop| {
7596 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7597 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7598 });
7599 let mut tabstop_ranges = tabstop
7600 .ranges
7601 .iter()
7602 .flat_map(|tabstop_range| {
7603 let mut delta = 0_isize;
7604 insertion_ranges.iter().map(move |insertion_range| {
7605 let insertion_start = insertion_range.start as isize + delta;
7606 delta +=
7607 snippet.text.len() as isize - insertion_range.len() as isize;
7608
7609 let start = ((insertion_start + tabstop_range.start) as usize)
7610 .min(snapshot.len());
7611 let end = ((insertion_start + tabstop_range.end) as usize)
7612 .min(snapshot.len());
7613 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7614 })
7615 })
7616 .collect::<Vec<_>>();
7617 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7618
7619 Tabstop {
7620 is_end_tabstop,
7621 ranges: tabstop_ranges,
7622 choices: tabstop.choices.clone(),
7623 }
7624 })
7625 .collect::<Vec<_>>()
7626 });
7627 if let Some(tabstop) = tabstops.first() {
7628 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7629 s.select_ranges(tabstop.ranges.iter().cloned());
7630 });
7631
7632 if let Some(choices) = &tabstop.choices {
7633 if let Some(selection) = tabstop.ranges.first() {
7634 self.show_snippet_choices(choices, selection.clone(), cx)
7635 }
7636 }
7637
7638 // If we're already at the last tabstop and it's at the end of the snippet,
7639 // we're done, we don't need to keep the state around.
7640 if !tabstop.is_end_tabstop {
7641 let choices = tabstops
7642 .iter()
7643 .map(|tabstop| tabstop.choices.clone())
7644 .collect();
7645
7646 let ranges = tabstops
7647 .into_iter()
7648 .map(|tabstop| tabstop.ranges)
7649 .collect::<Vec<_>>();
7650
7651 self.snippet_stack.push(SnippetState {
7652 active_index: 0,
7653 ranges,
7654 choices,
7655 });
7656 }
7657
7658 // Check whether the just-entered snippet ends with an auto-closable bracket.
7659 if self.autoclose_regions.is_empty() {
7660 let snapshot = self.buffer.read(cx).snapshot(cx);
7661 for selection in &mut self.selections.all::<Point>(cx) {
7662 let selection_head = selection.head();
7663 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7664 continue;
7665 };
7666
7667 let mut bracket_pair = None;
7668 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7669 let prev_chars = snapshot
7670 .reversed_chars_at(selection_head)
7671 .collect::<String>();
7672 for (pair, enabled) in scope.brackets() {
7673 if enabled
7674 && pair.close
7675 && prev_chars.starts_with(pair.start.as_str())
7676 && next_chars.starts_with(pair.end.as_str())
7677 {
7678 bracket_pair = Some(pair.clone());
7679 break;
7680 }
7681 }
7682 if let Some(pair) = bracket_pair {
7683 let start = snapshot.anchor_after(selection_head);
7684 let end = snapshot.anchor_after(selection_head);
7685 self.autoclose_regions.push(AutocloseRegion {
7686 selection_id: selection.id,
7687 range: start..end,
7688 pair,
7689 });
7690 }
7691 }
7692 }
7693 }
7694 Ok(())
7695 }
7696
7697 pub fn move_to_next_snippet_tabstop(
7698 &mut self,
7699 window: &mut Window,
7700 cx: &mut Context<Self>,
7701 ) -> bool {
7702 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7703 }
7704
7705 pub fn move_to_prev_snippet_tabstop(
7706 &mut self,
7707 window: &mut Window,
7708 cx: &mut Context<Self>,
7709 ) -> bool {
7710 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7711 }
7712
7713 pub fn move_to_snippet_tabstop(
7714 &mut self,
7715 bias: Bias,
7716 window: &mut Window,
7717 cx: &mut Context<Self>,
7718 ) -> bool {
7719 if let Some(mut snippet) = self.snippet_stack.pop() {
7720 match bias {
7721 Bias::Left => {
7722 if snippet.active_index > 0 {
7723 snippet.active_index -= 1;
7724 } else {
7725 self.snippet_stack.push(snippet);
7726 return false;
7727 }
7728 }
7729 Bias::Right => {
7730 if snippet.active_index + 1 < snippet.ranges.len() {
7731 snippet.active_index += 1;
7732 } else {
7733 self.snippet_stack.push(snippet);
7734 return false;
7735 }
7736 }
7737 }
7738 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7739 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7740 s.select_anchor_ranges(current_ranges.iter().cloned())
7741 });
7742
7743 if let Some(choices) = &snippet.choices[snippet.active_index] {
7744 if let Some(selection) = current_ranges.first() {
7745 self.show_snippet_choices(&choices, selection.clone(), cx);
7746 }
7747 }
7748
7749 // If snippet state is not at the last tabstop, push it back on the stack
7750 if snippet.active_index + 1 < snippet.ranges.len() {
7751 self.snippet_stack.push(snippet);
7752 }
7753 return true;
7754 }
7755 }
7756
7757 false
7758 }
7759
7760 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7761 self.transact(window, cx, |this, window, cx| {
7762 this.select_all(&SelectAll, window, cx);
7763 this.insert("", window, cx);
7764 });
7765 }
7766
7767 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7768 self.transact(window, cx, |this, window, cx| {
7769 this.select_autoclose_pair(window, cx);
7770 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7771 if !this.linked_edit_ranges.is_empty() {
7772 let selections = this.selections.all::<MultiBufferPoint>(cx);
7773 let snapshot = this.buffer.read(cx).snapshot(cx);
7774
7775 for selection in selections.iter() {
7776 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7777 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7778 if selection_start.buffer_id != selection_end.buffer_id {
7779 continue;
7780 }
7781 if let Some(ranges) =
7782 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7783 {
7784 for (buffer, entries) in ranges {
7785 linked_ranges.entry(buffer).or_default().extend(entries);
7786 }
7787 }
7788 }
7789 }
7790
7791 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7792 if !this.selections.line_mode {
7793 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7794 for selection in &mut selections {
7795 if selection.is_empty() {
7796 let old_head = selection.head();
7797 let mut new_head =
7798 movement::left(&display_map, old_head.to_display_point(&display_map))
7799 .to_point(&display_map);
7800 if let Some((buffer, line_buffer_range)) = display_map
7801 .buffer_snapshot
7802 .buffer_line_for_row(MultiBufferRow(old_head.row))
7803 {
7804 let indent_size =
7805 buffer.indent_size_for_line(line_buffer_range.start.row);
7806 let indent_len = match indent_size.kind {
7807 IndentKind::Space => {
7808 buffer.settings_at(line_buffer_range.start, cx).tab_size
7809 }
7810 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7811 };
7812 if old_head.column <= indent_size.len && old_head.column > 0 {
7813 let indent_len = indent_len.get();
7814 new_head = cmp::min(
7815 new_head,
7816 MultiBufferPoint::new(
7817 old_head.row,
7818 ((old_head.column - 1) / indent_len) * indent_len,
7819 ),
7820 );
7821 }
7822 }
7823
7824 selection.set_head(new_head, SelectionGoal::None);
7825 }
7826 }
7827 }
7828
7829 this.signature_help_state.set_backspace_pressed(true);
7830 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7831 s.select(selections)
7832 });
7833 this.insert("", window, cx);
7834 let empty_str: Arc<str> = Arc::from("");
7835 for (buffer, edits) in linked_ranges {
7836 let snapshot = buffer.read(cx).snapshot();
7837 use text::ToPoint as TP;
7838
7839 let edits = edits
7840 .into_iter()
7841 .map(|range| {
7842 let end_point = TP::to_point(&range.end, &snapshot);
7843 let mut start_point = TP::to_point(&range.start, &snapshot);
7844
7845 if end_point == start_point {
7846 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7847 .saturating_sub(1);
7848 start_point =
7849 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7850 };
7851
7852 (start_point..end_point, empty_str.clone())
7853 })
7854 .sorted_by_key(|(range, _)| range.start)
7855 .collect::<Vec<_>>();
7856 buffer.update(cx, |this, cx| {
7857 this.edit(edits, None, cx);
7858 })
7859 }
7860 this.refresh_inline_completion(true, false, window, cx);
7861 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7862 });
7863 }
7864
7865 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7866 self.transact(window, cx, |this, window, cx| {
7867 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7868 let line_mode = s.line_mode;
7869 s.move_with(|map, selection| {
7870 if selection.is_empty() && !line_mode {
7871 let cursor = movement::right(map, selection.head());
7872 selection.end = cursor;
7873 selection.reversed = true;
7874 selection.goal = SelectionGoal::None;
7875 }
7876 })
7877 });
7878 this.insert("", window, cx);
7879 this.refresh_inline_completion(true, false, window, cx);
7880 });
7881 }
7882
7883 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7884 if self.move_to_prev_snippet_tabstop(window, cx) {
7885 return;
7886 }
7887
7888 self.outdent(&Outdent, window, cx);
7889 }
7890
7891 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7892 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7893 return;
7894 }
7895
7896 let mut selections = self.selections.all_adjusted(cx);
7897 let buffer = self.buffer.read(cx);
7898 let snapshot = buffer.snapshot(cx);
7899 let rows_iter = selections.iter().map(|s| s.head().row);
7900 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7901
7902 let mut edits = Vec::new();
7903 let mut prev_edited_row = 0;
7904 let mut row_delta = 0;
7905 for selection in &mut selections {
7906 if selection.start.row != prev_edited_row {
7907 row_delta = 0;
7908 }
7909 prev_edited_row = selection.end.row;
7910
7911 // If the selection is non-empty, then increase the indentation of the selected lines.
7912 if !selection.is_empty() {
7913 row_delta =
7914 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7915 continue;
7916 }
7917
7918 // If the selection is empty and the cursor is in the leading whitespace before the
7919 // suggested indentation, then auto-indent the line.
7920 let cursor = selection.head();
7921 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7922 if let Some(suggested_indent) =
7923 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7924 {
7925 if cursor.column < suggested_indent.len
7926 && cursor.column <= current_indent.len
7927 && current_indent.len <= suggested_indent.len
7928 {
7929 selection.start = Point::new(cursor.row, suggested_indent.len);
7930 selection.end = selection.start;
7931 if row_delta == 0 {
7932 edits.extend(Buffer::edit_for_indent_size_adjustment(
7933 cursor.row,
7934 current_indent,
7935 suggested_indent,
7936 ));
7937 row_delta = suggested_indent.len - current_indent.len;
7938 }
7939 continue;
7940 }
7941 }
7942
7943 // Otherwise, insert a hard or soft tab.
7944 let settings = buffer.language_settings_at(cursor, cx);
7945 let tab_size = if settings.hard_tabs {
7946 IndentSize::tab()
7947 } else {
7948 let tab_size = settings.tab_size.get();
7949 let char_column = snapshot
7950 .text_for_range(Point::new(cursor.row, 0)..cursor)
7951 .flat_map(str::chars)
7952 .count()
7953 + row_delta as usize;
7954 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7955 IndentSize::spaces(chars_to_next_tab_stop)
7956 };
7957 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7958 selection.end = selection.start;
7959 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7960 row_delta += tab_size.len;
7961 }
7962
7963 self.transact(window, cx, |this, window, cx| {
7964 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7965 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7966 s.select(selections)
7967 });
7968 this.refresh_inline_completion(true, false, window, cx);
7969 });
7970 }
7971
7972 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7973 if self.read_only(cx) {
7974 return;
7975 }
7976 let mut selections = self.selections.all::<Point>(cx);
7977 let mut prev_edited_row = 0;
7978 let mut row_delta = 0;
7979 let mut edits = Vec::new();
7980 let buffer = self.buffer.read(cx);
7981 let snapshot = buffer.snapshot(cx);
7982 for selection in &mut selections {
7983 if selection.start.row != prev_edited_row {
7984 row_delta = 0;
7985 }
7986 prev_edited_row = selection.end.row;
7987
7988 row_delta =
7989 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7990 }
7991
7992 self.transact(window, cx, |this, window, cx| {
7993 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7994 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7995 s.select(selections)
7996 });
7997 });
7998 }
7999
8000 fn indent_selection(
8001 buffer: &MultiBuffer,
8002 snapshot: &MultiBufferSnapshot,
8003 selection: &mut Selection<Point>,
8004 edits: &mut Vec<(Range<Point>, String)>,
8005 delta_for_start_row: u32,
8006 cx: &App,
8007 ) -> u32 {
8008 let settings = buffer.language_settings_at(selection.start, cx);
8009 let tab_size = settings.tab_size.get();
8010 let indent_kind = if settings.hard_tabs {
8011 IndentKind::Tab
8012 } else {
8013 IndentKind::Space
8014 };
8015 let mut start_row = selection.start.row;
8016 let mut end_row = selection.end.row + 1;
8017
8018 // If a selection ends at the beginning of a line, don't indent
8019 // that last line.
8020 if selection.end.column == 0 && selection.end.row > selection.start.row {
8021 end_row -= 1;
8022 }
8023
8024 // Avoid re-indenting a row that has already been indented by a
8025 // previous selection, but still update this selection's column
8026 // to reflect that indentation.
8027 if delta_for_start_row > 0 {
8028 start_row += 1;
8029 selection.start.column += delta_for_start_row;
8030 if selection.end.row == selection.start.row {
8031 selection.end.column += delta_for_start_row;
8032 }
8033 }
8034
8035 let mut delta_for_end_row = 0;
8036 let has_multiple_rows = start_row + 1 != end_row;
8037 for row in start_row..end_row {
8038 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
8039 let indent_delta = match (current_indent.kind, indent_kind) {
8040 (IndentKind::Space, IndentKind::Space) => {
8041 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
8042 IndentSize::spaces(columns_to_next_tab_stop)
8043 }
8044 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
8045 (_, IndentKind::Tab) => IndentSize::tab(),
8046 };
8047
8048 let start = if has_multiple_rows || current_indent.len < selection.start.column {
8049 0
8050 } else {
8051 selection.start.column
8052 };
8053 let row_start = Point::new(row, start);
8054 edits.push((
8055 row_start..row_start,
8056 indent_delta.chars().collect::<String>(),
8057 ));
8058
8059 // Update this selection's endpoints to reflect the indentation.
8060 if row == selection.start.row {
8061 selection.start.column += indent_delta.len;
8062 }
8063 if row == selection.end.row {
8064 selection.end.column += indent_delta.len;
8065 delta_for_end_row = indent_delta.len;
8066 }
8067 }
8068
8069 if selection.start.row == selection.end.row {
8070 delta_for_start_row + delta_for_end_row
8071 } else {
8072 delta_for_end_row
8073 }
8074 }
8075
8076 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
8077 if self.read_only(cx) {
8078 return;
8079 }
8080 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8081 let selections = self.selections.all::<Point>(cx);
8082 let mut deletion_ranges = Vec::new();
8083 let mut last_outdent = None;
8084 {
8085 let buffer = self.buffer.read(cx);
8086 let snapshot = buffer.snapshot(cx);
8087 for selection in &selections {
8088 let settings = buffer.language_settings_at(selection.start, cx);
8089 let tab_size = settings.tab_size.get();
8090 let mut rows = selection.spanned_rows(false, &display_map);
8091
8092 // Avoid re-outdenting a row that has already been outdented by a
8093 // previous selection.
8094 if let Some(last_row) = last_outdent {
8095 if last_row == rows.start {
8096 rows.start = rows.start.next_row();
8097 }
8098 }
8099 let has_multiple_rows = rows.len() > 1;
8100 for row in rows.iter_rows() {
8101 let indent_size = snapshot.indent_size_for_line(row);
8102 if indent_size.len > 0 {
8103 let deletion_len = match indent_size.kind {
8104 IndentKind::Space => {
8105 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8106 if columns_to_prev_tab_stop == 0 {
8107 tab_size
8108 } else {
8109 columns_to_prev_tab_stop
8110 }
8111 }
8112 IndentKind::Tab => 1,
8113 };
8114 let start = if has_multiple_rows
8115 || deletion_len > selection.start.column
8116 || indent_size.len < selection.start.column
8117 {
8118 0
8119 } else {
8120 selection.start.column - deletion_len
8121 };
8122 deletion_ranges.push(
8123 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8124 );
8125 last_outdent = Some(row);
8126 }
8127 }
8128 }
8129 }
8130
8131 self.transact(window, cx, |this, window, cx| {
8132 this.buffer.update(cx, |buffer, cx| {
8133 let empty_str: Arc<str> = Arc::default();
8134 buffer.edit(
8135 deletion_ranges
8136 .into_iter()
8137 .map(|range| (range, empty_str.clone())),
8138 None,
8139 cx,
8140 );
8141 });
8142 let selections = this.selections.all::<usize>(cx);
8143 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8144 s.select(selections)
8145 });
8146 });
8147 }
8148
8149 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8150 if self.read_only(cx) {
8151 return;
8152 }
8153 let selections = self
8154 .selections
8155 .all::<usize>(cx)
8156 .into_iter()
8157 .map(|s| s.range());
8158
8159 self.transact(window, cx, |this, window, cx| {
8160 this.buffer.update(cx, |buffer, cx| {
8161 buffer.autoindent_ranges(selections, cx);
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 delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8171 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8172 let selections = self.selections.all::<Point>(cx);
8173
8174 let mut new_cursors = Vec::new();
8175 let mut edit_ranges = Vec::new();
8176 let mut selections = selections.iter().peekable();
8177 while let Some(selection) = selections.next() {
8178 let mut rows = selection.spanned_rows(false, &display_map);
8179 let goal_display_column = selection.head().to_display_point(&display_map).column();
8180
8181 // Accumulate contiguous regions of rows that we want to delete.
8182 while let Some(next_selection) = selections.peek() {
8183 let next_rows = next_selection.spanned_rows(false, &display_map);
8184 if next_rows.start <= rows.end {
8185 rows.end = next_rows.end;
8186 selections.next().unwrap();
8187 } else {
8188 break;
8189 }
8190 }
8191
8192 let buffer = &display_map.buffer_snapshot;
8193 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8194 let edit_end;
8195 let cursor_buffer_row;
8196 if buffer.max_point().row >= rows.end.0 {
8197 // If there's a line after the range, delete the \n from the end of the row range
8198 // and position the cursor on the next line.
8199 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8200 cursor_buffer_row = rows.end;
8201 } else {
8202 // If there isn't a line after the range, delete the \n from the line before the
8203 // start of the row range and position the cursor there.
8204 edit_start = edit_start.saturating_sub(1);
8205 edit_end = buffer.len();
8206 cursor_buffer_row = rows.start.previous_row();
8207 }
8208
8209 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8210 *cursor.column_mut() =
8211 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8212
8213 new_cursors.push((
8214 selection.id,
8215 buffer.anchor_after(cursor.to_point(&display_map)),
8216 ));
8217 edit_ranges.push(edit_start..edit_end);
8218 }
8219
8220 self.transact(window, cx, |this, window, cx| {
8221 let buffer = this.buffer.update(cx, |buffer, cx| {
8222 let empty_str: Arc<str> = Arc::default();
8223 buffer.edit(
8224 edit_ranges
8225 .into_iter()
8226 .map(|range| (range, empty_str.clone())),
8227 None,
8228 cx,
8229 );
8230 buffer.snapshot(cx)
8231 });
8232 let new_selections = new_cursors
8233 .into_iter()
8234 .map(|(id, cursor)| {
8235 let cursor = cursor.to_point(&buffer);
8236 Selection {
8237 id,
8238 start: cursor,
8239 end: cursor,
8240 reversed: false,
8241 goal: SelectionGoal::None,
8242 }
8243 })
8244 .collect();
8245
8246 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8247 s.select(new_selections);
8248 });
8249 });
8250 }
8251
8252 pub fn join_lines_impl(
8253 &mut self,
8254 insert_whitespace: bool,
8255 window: &mut Window,
8256 cx: &mut Context<Self>,
8257 ) {
8258 if self.read_only(cx) {
8259 return;
8260 }
8261 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8262 for selection in self.selections.all::<Point>(cx) {
8263 let start = MultiBufferRow(selection.start.row);
8264 // Treat single line selections as if they include the next line. Otherwise this action
8265 // would do nothing for single line selections individual cursors.
8266 let end = if selection.start.row == selection.end.row {
8267 MultiBufferRow(selection.start.row + 1)
8268 } else {
8269 MultiBufferRow(selection.end.row)
8270 };
8271
8272 if let Some(last_row_range) = row_ranges.last_mut() {
8273 if start <= last_row_range.end {
8274 last_row_range.end = end;
8275 continue;
8276 }
8277 }
8278 row_ranges.push(start..end);
8279 }
8280
8281 let snapshot = self.buffer.read(cx).snapshot(cx);
8282 let mut cursor_positions = Vec::new();
8283 for row_range in &row_ranges {
8284 let anchor = snapshot.anchor_before(Point::new(
8285 row_range.end.previous_row().0,
8286 snapshot.line_len(row_range.end.previous_row()),
8287 ));
8288 cursor_positions.push(anchor..anchor);
8289 }
8290
8291 self.transact(window, cx, |this, window, cx| {
8292 for row_range in row_ranges.into_iter().rev() {
8293 for row in row_range.iter_rows().rev() {
8294 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8295 let next_line_row = row.next_row();
8296 let indent = snapshot.indent_size_for_line(next_line_row);
8297 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8298
8299 let replace =
8300 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8301 " "
8302 } else {
8303 ""
8304 };
8305
8306 this.buffer.update(cx, |buffer, cx| {
8307 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8308 });
8309 }
8310 }
8311
8312 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8313 s.select_anchor_ranges(cursor_positions)
8314 });
8315 });
8316 }
8317
8318 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8319 self.join_lines_impl(true, window, cx);
8320 }
8321
8322 pub fn sort_lines_case_sensitive(
8323 &mut self,
8324 _: &SortLinesCaseSensitive,
8325 window: &mut Window,
8326 cx: &mut Context<Self>,
8327 ) {
8328 self.manipulate_lines(window, cx, |lines| lines.sort())
8329 }
8330
8331 pub fn sort_lines_case_insensitive(
8332 &mut self,
8333 _: &SortLinesCaseInsensitive,
8334 window: &mut Window,
8335 cx: &mut Context<Self>,
8336 ) {
8337 self.manipulate_lines(window, cx, |lines| {
8338 lines.sort_by_key(|line| line.to_lowercase())
8339 })
8340 }
8341
8342 pub fn unique_lines_case_insensitive(
8343 &mut self,
8344 _: &UniqueLinesCaseInsensitive,
8345 window: &mut Window,
8346 cx: &mut Context<Self>,
8347 ) {
8348 self.manipulate_lines(window, cx, |lines| {
8349 let mut seen = HashSet::default();
8350 lines.retain(|line| seen.insert(line.to_lowercase()));
8351 })
8352 }
8353
8354 pub fn unique_lines_case_sensitive(
8355 &mut self,
8356 _: &UniqueLinesCaseSensitive,
8357 window: &mut Window,
8358 cx: &mut Context<Self>,
8359 ) {
8360 self.manipulate_lines(window, cx, |lines| {
8361 let mut seen = HashSet::default();
8362 lines.retain(|line| seen.insert(*line));
8363 })
8364 }
8365
8366 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8367 let Some(project) = self.project.clone() else {
8368 return;
8369 };
8370 self.reload(project, window, cx)
8371 .detach_and_notify_err(window, cx);
8372 }
8373
8374 pub fn restore_file(
8375 &mut self,
8376 _: &::git::RestoreFile,
8377 window: &mut Window,
8378 cx: &mut Context<Self>,
8379 ) {
8380 let mut buffer_ids = HashSet::default();
8381 let snapshot = self.buffer().read(cx).snapshot(cx);
8382 for selection in self.selections.all::<usize>(cx) {
8383 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8384 }
8385
8386 let buffer = self.buffer().read(cx);
8387 let ranges = buffer_ids
8388 .into_iter()
8389 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8390 .collect::<Vec<_>>();
8391
8392 self.restore_hunks_in_ranges(ranges, window, cx);
8393 }
8394
8395 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8396 let selections = self
8397 .selections
8398 .all(cx)
8399 .into_iter()
8400 .map(|s| s.range())
8401 .collect();
8402 self.restore_hunks_in_ranges(selections, window, cx);
8403 }
8404
8405 fn restore_hunks_in_ranges(
8406 &mut self,
8407 ranges: Vec<Range<Point>>,
8408 window: &mut Window,
8409 cx: &mut Context<Editor>,
8410 ) {
8411 let mut revert_changes = HashMap::default();
8412 let chunk_by = self
8413 .snapshot(window, cx)
8414 .hunks_for_ranges(ranges)
8415 .into_iter()
8416 .chunk_by(|hunk| hunk.buffer_id);
8417 for (buffer_id, hunks) in &chunk_by {
8418 let hunks = hunks.collect::<Vec<_>>();
8419 for hunk in &hunks {
8420 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8421 }
8422 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8423 }
8424 drop(chunk_by);
8425 if !revert_changes.is_empty() {
8426 self.transact(window, cx, |editor, window, cx| {
8427 editor.restore(revert_changes, window, cx);
8428 });
8429 }
8430 }
8431
8432 pub fn open_active_item_in_terminal(
8433 &mut self,
8434 _: &OpenInTerminal,
8435 window: &mut Window,
8436 cx: &mut Context<Self>,
8437 ) {
8438 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8439 let project_path = buffer.read(cx).project_path(cx)?;
8440 let project = self.project.as_ref()?.read(cx);
8441 let entry = project.entry_for_path(&project_path, cx)?;
8442 let parent = match &entry.canonical_path {
8443 Some(canonical_path) => canonical_path.to_path_buf(),
8444 None => project.absolute_path(&project_path, cx)?,
8445 }
8446 .parent()?
8447 .to_path_buf();
8448 Some(parent)
8449 }) {
8450 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8451 }
8452 }
8453
8454 fn set_breakpoint_context_menu(
8455 &mut self,
8456 display_row: DisplayRow,
8457 position: Option<Anchor>,
8458 clicked_point: gpui::Point<Pixels>,
8459 window: &mut Window,
8460 cx: &mut Context<Self>,
8461 ) {
8462 if !cx.has_flag::<Debugger>() {
8463 return;
8464 }
8465 let source = self
8466 .buffer
8467 .read(cx)
8468 .snapshot(cx)
8469 .anchor_before(Point::new(display_row.0, 0u32));
8470
8471 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
8472
8473 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8474 self,
8475 source,
8476 clicked_point,
8477 context_menu,
8478 window,
8479 cx,
8480 );
8481 }
8482
8483 fn add_edit_breakpoint_block(
8484 &mut self,
8485 anchor: Anchor,
8486 breakpoint: &Breakpoint,
8487 window: &mut Window,
8488 cx: &mut Context<Self>,
8489 ) {
8490 let weak_editor = cx.weak_entity();
8491 let bp_prompt = cx.new(|cx| {
8492 BreakpointPromptEditor::new(weak_editor, anchor, breakpoint.clone(), window, cx)
8493 });
8494
8495 let height = bp_prompt.update(cx, |this, cx| {
8496 this.prompt
8497 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8498 });
8499 let cloned_prompt = bp_prompt.clone();
8500 let blocks = vec![BlockProperties {
8501 style: BlockStyle::Sticky,
8502 placement: BlockPlacement::Above(anchor),
8503 height,
8504 render: Arc::new(move |cx| {
8505 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8506 cloned_prompt.clone().into_any_element()
8507 }),
8508 priority: 0,
8509 }];
8510
8511 let focus_handle = bp_prompt.focus_handle(cx);
8512 window.focus(&focus_handle);
8513
8514 let block_ids = self.insert_blocks(blocks, None, cx);
8515 bp_prompt.update(cx, |prompt, _| {
8516 prompt.add_block_ids(block_ids);
8517 });
8518 }
8519
8520 fn breakpoint_at_cursor_head(
8521 &self,
8522 window: &mut Window,
8523 cx: &mut Context<Self>,
8524 ) -> Option<(Anchor, Breakpoint)> {
8525 let cursor_position: Point = self.selections.newest(cx).head();
8526 self.breakpoint_at_row(cursor_position.row, window, cx)
8527 }
8528
8529 pub(crate) fn breakpoint_at_row(
8530 &self,
8531 row: u32,
8532 window: &mut Window,
8533 cx: &mut Context<Self>,
8534 ) -> Option<(Anchor, Breakpoint)> {
8535 let snapshot = self.snapshot(window, cx);
8536 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
8537
8538 let project = self.project.clone()?;
8539
8540 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
8541 snapshot
8542 .buffer_snapshot
8543 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
8544 })?;
8545
8546 let enclosing_excerpt = breakpoint_position.excerpt_id;
8547 let buffer = project.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8548 let buffer_snapshot = buffer.read(cx).snapshot();
8549
8550 let row = buffer_snapshot
8551 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
8552 .row;
8553
8554 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
8555 let anchor_end = snapshot
8556 .buffer_snapshot
8557 .anchor_before(Point::new(row, line_len));
8558
8559 let bp = self
8560 .breakpoint_store
8561 .as_ref()?
8562 .read_with(cx, |breakpoint_store, cx| {
8563 breakpoint_store
8564 .breakpoints(
8565 &buffer,
8566 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
8567 &buffer_snapshot,
8568 cx,
8569 )
8570 .next()
8571 .and_then(|(anchor, bp)| {
8572 let breakpoint_row = buffer_snapshot
8573 .summary_for_anchor::<text::PointUtf16>(anchor)
8574 .row;
8575
8576 if breakpoint_row == row {
8577 snapshot
8578 .buffer_snapshot
8579 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8580 .map(|anchor| (anchor, bp.clone()))
8581 } else {
8582 None
8583 }
8584 })
8585 });
8586 bp
8587 }
8588
8589 pub fn edit_log_breakpoint(
8590 &mut self,
8591 _: &EditLogBreakpoint,
8592 window: &mut Window,
8593 cx: &mut Context<Self>,
8594 ) {
8595 let (anchor, bp) = self
8596 .breakpoint_at_cursor_head(window, cx)
8597 .unwrap_or_else(|| {
8598 let cursor_position: Point = self.selections.newest(cx).head();
8599
8600 let breakpoint_position = self
8601 .snapshot(window, cx)
8602 .display_snapshot
8603 .buffer_snapshot
8604 .anchor_before(Point::new(cursor_position.row, 0));
8605
8606 (
8607 breakpoint_position,
8608 Breakpoint {
8609 kind: BreakpointKind::Standard,
8610 state: BreakpointState::Enabled,
8611 },
8612 )
8613 });
8614
8615 self.add_edit_breakpoint_block(anchor, &bp, window, cx);
8616 }
8617
8618 pub fn enable_breakpoint(
8619 &mut self,
8620 _: &crate::actions::EnableBreakpoint,
8621 window: &mut Window,
8622 cx: &mut Context<Self>,
8623 ) {
8624 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8625 if breakpoint.is_disabled() {
8626 self.edit_breakpoint_at_anchor(
8627 anchor,
8628 breakpoint,
8629 BreakpointEditAction::InvertState,
8630 cx,
8631 );
8632 }
8633 }
8634 }
8635
8636 pub fn disable_breakpoint(
8637 &mut self,
8638 _: &crate::actions::DisableBreakpoint,
8639 window: &mut Window,
8640 cx: &mut Context<Self>,
8641 ) {
8642 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8643 if breakpoint.is_enabled() {
8644 self.edit_breakpoint_at_anchor(
8645 anchor,
8646 breakpoint,
8647 BreakpointEditAction::InvertState,
8648 cx,
8649 );
8650 }
8651 }
8652 }
8653
8654 pub fn toggle_breakpoint(
8655 &mut self,
8656 _: &crate::actions::ToggleBreakpoint,
8657 window: &mut Window,
8658 cx: &mut Context<Self>,
8659 ) {
8660 let edit_action = BreakpointEditAction::Toggle;
8661
8662 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8663 self.edit_breakpoint_at_anchor(anchor, breakpoint, edit_action, cx);
8664 } else {
8665 let cursor_position: Point = self.selections.newest(cx).head();
8666
8667 let breakpoint_position = self
8668 .snapshot(window, cx)
8669 .display_snapshot
8670 .buffer_snapshot
8671 .anchor_before(Point::new(cursor_position.row, 0));
8672
8673 self.edit_breakpoint_at_anchor(
8674 breakpoint_position,
8675 Breakpoint::new_standard(),
8676 edit_action,
8677 cx,
8678 );
8679 }
8680 }
8681
8682 pub fn edit_breakpoint_at_anchor(
8683 &mut self,
8684 breakpoint_position: Anchor,
8685 breakpoint: Breakpoint,
8686 edit_action: BreakpointEditAction,
8687 cx: &mut Context<Self>,
8688 ) {
8689 let Some(breakpoint_store) = &self.breakpoint_store else {
8690 return;
8691 };
8692
8693 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8694 if breakpoint_position == Anchor::min() {
8695 self.buffer()
8696 .read(cx)
8697 .excerpt_buffer_ids()
8698 .into_iter()
8699 .next()
8700 } else {
8701 None
8702 }
8703 }) else {
8704 return;
8705 };
8706
8707 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8708 return;
8709 };
8710
8711 breakpoint_store.update(cx, |breakpoint_store, cx| {
8712 breakpoint_store.toggle_breakpoint(
8713 buffer,
8714 (breakpoint_position.text_anchor, breakpoint),
8715 edit_action,
8716 cx,
8717 );
8718 });
8719
8720 cx.notify();
8721 }
8722
8723 #[cfg(any(test, feature = "test-support"))]
8724 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8725 self.breakpoint_store.clone()
8726 }
8727
8728 pub fn prepare_restore_change(
8729 &self,
8730 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8731 hunk: &MultiBufferDiffHunk,
8732 cx: &mut App,
8733 ) -> Option<()> {
8734 if hunk.is_created_file() {
8735 return None;
8736 }
8737 let buffer = self.buffer.read(cx);
8738 let diff = buffer.diff_for(hunk.buffer_id)?;
8739 let buffer = buffer.buffer(hunk.buffer_id)?;
8740 let buffer = buffer.read(cx);
8741 let original_text = diff
8742 .read(cx)
8743 .base_text()
8744 .as_rope()
8745 .slice(hunk.diff_base_byte_range.clone());
8746 let buffer_snapshot = buffer.snapshot();
8747 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8748 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8749 probe
8750 .0
8751 .start
8752 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8753 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8754 }) {
8755 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8756 Some(())
8757 } else {
8758 None
8759 }
8760 }
8761
8762 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8763 self.manipulate_lines(window, cx, |lines| lines.reverse())
8764 }
8765
8766 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8767 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8768 }
8769
8770 fn manipulate_lines<Fn>(
8771 &mut self,
8772 window: &mut Window,
8773 cx: &mut Context<Self>,
8774 mut callback: Fn,
8775 ) where
8776 Fn: FnMut(&mut Vec<&str>),
8777 {
8778 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8779 let buffer = self.buffer.read(cx).snapshot(cx);
8780
8781 let mut edits = Vec::new();
8782
8783 let selections = self.selections.all::<Point>(cx);
8784 let mut selections = selections.iter().peekable();
8785 let mut contiguous_row_selections = Vec::new();
8786 let mut new_selections = Vec::new();
8787 let mut added_lines = 0;
8788 let mut removed_lines = 0;
8789
8790 while let Some(selection) = selections.next() {
8791 let (start_row, end_row) = consume_contiguous_rows(
8792 &mut contiguous_row_selections,
8793 selection,
8794 &display_map,
8795 &mut selections,
8796 );
8797
8798 let start_point = Point::new(start_row.0, 0);
8799 let end_point = Point::new(
8800 end_row.previous_row().0,
8801 buffer.line_len(end_row.previous_row()),
8802 );
8803 let text = buffer
8804 .text_for_range(start_point..end_point)
8805 .collect::<String>();
8806
8807 let mut lines = text.split('\n').collect_vec();
8808
8809 let lines_before = lines.len();
8810 callback(&mut lines);
8811 let lines_after = lines.len();
8812
8813 edits.push((start_point..end_point, lines.join("\n")));
8814
8815 // Selections must change based on added and removed line count
8816 let start_row =
8817 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8818 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8819 new_selections.push(Selection {
8820 id: selection.id,
8821 start: start_row,
8822 end: end_row,
8823 goal: SelectionGoal::None,
8824 reversed: selection.reversed,
8825 });
8826
8827 if lines_after > lines_before {
8828 added_lines += lines_after - lines_before;
8829 } else if lines_before > lines_after {
8830 removed_lines += lines_before - lines_after;
8831 }
8832 }
8833
8834 self.transact(window, cx, |this, window, cx| {
8835 let buffer = this.buffer.update(cx, |buffer, cx| {
8836 buffer.edit(edits, None, cx);
8837 buffer.snapshot(cx)
8838 });
8839
8840 // Recalculate offsets on newly edited buffer
8841 let new_selections = new_selections
8842 .iter()
8843 .map(|s| {
8844 let start_point = Point::new(s.start.0, 0);
8845 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8846 Selection {
8847 id: s.id,
8848 start: buffer.point_to_offset(start_point),
8849 end: buffer.point_to_offset(end_point),
8850 goal: s.goal,
8851 reversed: s.reversed,
8852 }
8853 })
8854 .collect();
8855
8856 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8857 s.select(new_selections);
8858 });
8859
8860 this.request_autoscroll(Autoscroll::fit(), cx);
8861 });
8862 }
8863
8864 pub fn convert_to_upper_case(
8865 &mut self,
8866 _: &ConvertToUpperCase,
8867 window: &mut Window,
8868 cx: &mut Context<Self>,
8869 ) {
8870 self.manipulate_text(window, cx, |text| text.to_uppercase())
8871 }
8872
8873 pub fn convert_to_lower_case(
8874 &mut self,
8875 _: &ConvertToLowerCase,
8876 window: &mut Window,
8877 cx: &mut Context<Self>,
8878 ) {
8879 self.manipulate_text(window, cx, |text| text.to_lowercase())
8880 }
8881
8882 pub fn convert_to_title_case(
8883 &mut self,
8884 _: &ConvertToTitleCase,
8885 window: &mut Window,
8886 cx: &mut Context<Self>,
8887 ) {
8888 self.manipulate_text(window, cx, |text| {
8889 text.split('\n')
8890 .map(|line| line.to_case(Case::Title))
8891 .join("\n")
8892 })
8893 }
8894
8895 pub fn convert_to_snake_case(
8896 &mut self,
8897 _: &ConvertToSnakeCase,
8898 window: &mut Window,
8899 cx: &mut Context<Self>,
8900 ) {
8901 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8902 }
8903
8904 pub fn convert_to_kebab_case(
8905 &mut self,
8906 _: &ConvertToKebabCase,
8907 window: &mut Window,
8908 cx: &mut Context<Self>,
8909 ) {
8910 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8911 }
8912
8913 pub fn convert_to_upper_camel_case(
8914 &mut self,
8915 _: &ConvertToUpperCamelCase,
8916 window: &mut Window,
8917 cx: &mut Context<Self>,
8918 ) {
8919 self.manipulate_text(window, cx, |text| {
8920 text.split('\n')
8921 .map(|line| line.to_case(Case::UpperCamel))
8922 .join("\n")
8923 })
8924 }
8925
8926 pub fn convert_to_lower_camel_case(
8927 &mut self,
8928 _: &ConvertToLowerCamelCase,
8929 window: &mut Window,
8930 cx: &mut Context<Self>,
8931 ) {
8932 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8933 }
8934
8935 pub fn convert_to_opposite_case(
8936 &mut self,
8937 _: &ConvertToOppositeCase,
8938 window: &mut Window,
8939 cx: &mut Context<Self>,
8940 ) {
8941 self.manipulate_text(window, cx, |text| {
8942 text.chars()
8943 .fold(String::with_capacity(text.len()), |mut t, c| {
8944 if c.is_uppercase() {
8945 t.extend(c.to_lowercase());
8946 } else {
8947 t.extend(c.to_uppercase());
8948 }
8949 t
8950 })
8951 })
8952 }
8953
8954 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8955 where
8956 Fn: FnMut(&str) -> String,
8957 {
8958 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8959 let buffer = self.buffer.read(cx).snapshot(cx);
8960
8961 let mut new_selections = Vec::new();
8962 let mut edits = Vec::new();
8963 let mut selection_adjustment = 0i32;
8964
8965 for selection in self.selections.all::<usize>(cx) {
8966 let selection_is_empty = selection.is_empty();
8967
8968 let (start, end) = if selection_is_empty {
8969 let word_range = movement::surrounding_word(
8970 &display_map,
8971 selection.start.to_display_point(&display_map),
8972 );
8973 let start = word_range.start.to_offset(&display_map, Bias::Left);
8974 let end = word_range.end.to_offset(&display_map, Bias::Left);
8975 (start, end)
8976 } else {
8977 (selection.start, selection.end)
8978 };
8979
8980 let text = buffer.text_for_range(start..end).collect::<String>();
8981 let old_length = text.len() as i32;
8982 let text = callback(&text);
8983
8984 new_selections.push(Selection {
8985 start: (start as i32 - selection_adjustment) as usize,
8986 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8987 goal: SelectionGoal::None,
8988 ..selection
8989 });
8990
8991 selection_adjustment += old_length - text.len() as i32;
8992
8993 edits.push((start..end, text));
8994 }
8995
8996 self.transact(window, cx, |this, window, cx| {
8997 this.buffer.update(cx, |buffer, cx| {
8998 buffer.edit(edits, None, cx);
8999 });
9000
9001 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9002 s.select(new_selections);
9003 });
9004
9005 this.request_autoscroll(Autoscroll::fit(), cx);
9006 });
9007 }
9008
9009 pub fn duplicate(
9010 &mut self,
9011 upwards: bool,
9012 whole_lines: bool,
9013 window: &mut Window,
9014 cx: &mut Context<Self>,
9015 ) {
9016 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9017 let buffer = &display_map.buffer_snapshot;
9018 let selections = self.selections.all::<Point>(cx);
9019
9020 let mut edits = Vec::new();
9021 let mut selections_iter = selections.iter().peekable();
9022 while let Some(selection) = selections_iter.next() {
9023 let mut rows = selection.spanned_rows(false, &display_map);
9024 // duplicate line-wise
9025 if whole_lines || selection.start == selection.end {
9026 // Avoid duplicating the same lines twice.
9027 while let Some(next_selection) = selections_iter.peek() {
9028 let next_rows = next_selection.spanned_rows(false, &display_map);
9029 if next_rows.start < rows.end {
9030 rows.end = next_rows.end;
9031 selections_iter.next().unwrap();
9032 } else {
9033 break;
9034 }
9035 }
9036
9037 // Copy the text from the selected row region and splice it either at the start
9038 // or end of the region.
9039 let start = Point::new(rows.start.0, 0);
9040 let end = Point::new(
9041 rows.end.previous_row().0,
9042 buffer.line_len(rows.end.previous_row()),
9043 );
9044 let text = buffer
9045 .text_for_range(start..end)
9046 .chain(Some("\n"))
9047 .collect::<String>();
9048 let insert_location = if upwards {
9049 Point::new(rows.end.0, 0)
9050 } else {
9051 start
9052 };
9053 edits.push((insert_location..insert_location, text));
9054 } else {
9055 // duplicate character-wise
9056 let start = selection.start;
9057 let end = selection.end;
9058 let text = buffer.text_for_range(start..end).collect::<String>();
9059 edits.push((selection.end..selection.end, text));
9060 }
9061 }
9062
9063 self.transact(window, cx, |this, _, cx| {
9064 this.buffer.update(cx, |buffer, cx| {
9065 buffer.edit(edits, None, cx);
9066 });
9067
9068 this.request_autoscroll(Autoscroll::fit(), cx);
9069 });
9070 }
9071
9072 pub fn duplicate_line_up(
9073 &mut self,
9074 _: &DuplicateLineUp,
9075 window: &mut Window,
9076 cx: &mut Context<Self>,
9077 ) {
9078 self.duplicate(true, true, window, cx);
9079 }
9080
9081 pub fn duplicate_line_down(
9082 &mut self,
9083 _: &DuplicateLineDown,
9084 window: &mut Window,
9085 cx: &mut Context<Self>,
9086 ) {
9087 self.duplicate(false, true, window, cx);
9088 }
9089
9090 pub fn duplicate_selection(
9091 &mut self,
9092 _: &DuplicateSelection,
9093 window: &mut Window,
9094 cx: &mut Context<Self>,
9095 ) {
9096 self.duplicate(false, false, window, cx);
9097 }
9098
9099 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
9100 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9101 let buffer = self.buffer.read(cx).snapshot(cx);
9102
9103 let mut edits = Vec::new();
9104 let mut unfold_ranges = Vec::new();
9105 let mut refold_creases = Vec::new();
9106
9107 let selections = self.selections.all::<Point>(cx);
9108 let mut selections = selections.iter().peekable();
9109 let mut contiguous_row_selections = Vec::new();
9110 let mut new_selections = Vec::new();
9111
9112 while let Some(selection) = selections.next() {
9113 // Find all the selections that span a contiguous row range
9114 let (start_row, end_row) = consume_contiguous_rows(
9115 &mut contiguous_row_selections,
9116 selection,
9117 &display_map,
9118 &mut selections,
9119 );
9120
9121 // Move the text spanned by the row range to be before the line preceding the row range
9122 if start_row.0 > 0 {
9123 let range_to_move = Point::new(
9124 start_row.previous_row().0,
9125 buffer.line_len(start_row.previous_row()),
9126 )
9127 ..Point::new(
9128 end_row.previous_row().0,
9129 buffer.line_len(end_row.previous_row()),
9130 );
9131 let insertion_point = display_map
9132 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9133 .0;
9134
9135 // Don't move lines across excerpts
9136 if buffer
9137 .excerpt_containing(insertion_point..range_to_move.end)
9138 .is_some()
9139 {
9140 let text = buffer
9141 .text_for_range(range_to_move.clone())
9142 .flat_map(|s| s.chars())
9143 .skip(1)
9144 .chain(['\n'])
9145 .collect::<String>();
9146
9147 edits.push((
9148 buffer.anchor_after(range_to_move.start)
9149 ..buffer.anchor_before(range_to_move.end),
9150 String::new(),
9151 ));
9152 let insertion_anchor = buffer.anchor_after(insertion_point);
9153 edits.push((insertion_anchor..insertion_anchor, text));
9154
9155 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9156
9157 // Move selections up
9158 new_selections.extend(contiguous_row_selections.drain(..).map(
9159 |mut selection| {
9160 selection.start.row -= row_delta;
9161 selection.end.row -= row_delta;
9162 selection
9163 },
9164 ));
9165
9166 // Move folds up
9167 unfold_ranges.push(range_to_move.clone());
9168 for fold in display_map.folds_in_range(
9169 buffer.anchor_before(range_to_move.start)
9170 ..buffer.anchor_after(range_to_move.end),
9171 ) {
9172 let mut start = fold.range.start.to_point(&buffer);
9173 let mut end = fold.range.end.to_point(&buffer);
9174 start.row -= row_delta;
9175 end.row -= row_delta;
9176 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9177 }
9178 }
9179 }
9180
9181 // If we didn't move line(s), preserve the existing selections
9182 new_selections.append(&mut contiguous_row_selections);
9183 }
9184
9185 self.transact(window, cx, |this, window, cx| {
9186 this.unfold_ranges(&unfold_ranges, true, true, cx);
9187 this.buffer.update(cx, |buffer, cx| {
9188 for (range, text) in edits {
9189 buffer.edit([(range, text)], None, cx);
9190 }
9191 });
9192 this.fold_creases(refold_creases, true, window, cx);
9193 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9194 s.select(new_selections);
9195 })
9196 });
9197 }
9198
9199 pub fn move_line_down(
9200 &mut self,
9201 _: &MoveLineDown,
9202 window: &mut Window,
9203 cx: &mut Context<Self>,
9204 ) {
9205 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9206 let buffer = self.buffer.read(cx).snapshot(cx);
9207
9208 let mut edits = Vec::new();
9209 let mut unfold_ranges = Vec::new();
9210 let mut refold_creases = Vec::new();
9211
9212 let selections = self.selections.all::<Point>(cx);
9213 let mut selections = selections.iter().peekable();
9214 let mut contiguous_row_selections = Vec::new();
9215 let mut new_selections = Vec::new();
9216
9217 while let Some(selection) = selections.next() {
9218 // Find all the selections that span a contiguous row range
9219 let (start_row, end_row) = consume_contiguous_rows(
9220 &mut contiguous_row_selections,
9221 selection,
9222 &display_map,
9223 &mut selections,
9224 );
9225
9226 // Move the text spanned by the row range to be after the last line of the row range
9227 if end_row.0 <= buffer.max_point().row {
9228 let range_to_move =
9229 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9230 let insertion_point = display_map
9231 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9232 .0;
9233
9234 // Don't move lines across excerpt boundaries
9235 if buffer
9236 .excerpt_containing(range_to_move.start..insertion_point)
9237 .is_some()
9238 {
9239 let mut text = String::from("\n");
9240 text.extend(buffer.text_for_range(range_to_move.clone()));
9241 text.pop(); // Drop trailing newline
9242 edits.push((
9243 buffer.anchor_after(range_to_move.start)
9244 ..buffer.anchor_before(range_to_move.end),
9245 String::new(),
9246 ));
9247 let insertion_anchor = buffer.anchor_after(insertion_point);
9248 edits.push((insertion_anchor..insertion_anchor, text));
9249
9250 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9251
9252 // Move selections down
9253 new_selections.extend(contiguous_row_selections.drain(..).map(
9254 |mut selection| {
9255 selection.start.row += row_delta;
9256 selection.end.row += row_delta;
9257 selection
9258 },
9259 ));
9260
9261 // Move folds down
9262 unfold_ranges.push(range_to_move.clone());
9263 for fold in display_map.folds_in_range(
9264 buffer.anchor_before(range_to_move.start)
9265 ..buffer.anchor_after(range_to_move.end),
9266 ) {
9267 let mut start = fold.range.start.to_point(&buffer);
9268 let mut end = fold.range.end.to_point(&buffer);
9269 start.row += row_delta;
9270 end.row += row_delta;
9271 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9272 }
9273 }
9274 }
9275
9276 // If we didn't move line(s), preserve the existing selections
9277 new_selections.append(&mut contiguous_row_selections);
9278 }
9279
9280 self.transact(window, cx, |this, window, cx| {
9281 this.unfold_ranges(&unfold_ranges, true, true, cx);
9282 this.buffer.update(cx, |buffer, cx| {
9283 for (range, text) in edits {
9284 buffer.edit([(range, text)], None, cx);
9285 }
9286 });
9287 this.fold_creases(refold_creases, true, window, cx);
9288 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9289 s.select(new_selections)
9290 });
9291 });
9292 }
9293
9294 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9295 let text_layout_details = &self.text_layout_details(window);
9296 self.transact(window, cx, |this, window, cx| {
9297 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9298 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9299 let line_mode = s.line_mode;
9300 s.move_with(|display_map, selection| {
9301 if !selection.is_empty() || line_mode {
9302 return;
9303 }
9304
9305 let mut head = selection.head();
9306 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9307 if head.column() == display_map.line_len(head.row()) {
9308 transpose_offset = display_map
9309 .buffer_snapshot
9310 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9311 }
9312
9313 if transpose_offset == 0 {
9314 return;
9315 }
9316
9317 *head.column_mut() += 1;
9318 head = display_map.clip_point(head, Bias::Right);
9319 let goal = SelectionGoal::HorizontalPosition(
9320 display_map
9321 .x_for_display_point(head, text_layout_details)
9322 .into(),
9323 );
9324 selection.collapse_to(head, goal);
9325
9326 let transpose_start = display_map
9327 .buffer_snapshot
9328 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9329 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9330 let transpose_end = display_map
9331 .buffer_snapshot
9332 .clip_offset(transpose_offset + 1, Bias::Right);
9333 if let Some(ch) =
9334 display_map.buffer_snapshot.chars_at(transpose_start).next()
9335 {
9336 edits.push((transpose_start..transpose_offset, String::new()));
9337 edits.push((transpose_end..transpose_end, ch.to_string()));
9338 }
9339 }
9340 });
9341 edits
9342 });
9343 this.buffer
9344 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9345 let selections = this.selections.all::<usize>(cx);
9346 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9347 s.select(selections);
9348 });
9349 });
9350 }
9351
9352 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9353 self.rewrap_impl(RewrapOptions::default(), cx)
9354 }
9355
9356 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9357 let buffer = self.buffer.read(cx).snapshot(cx);
9358 let selections = self.selections.all::<Point>(cx);
9359 let mut selections = selections.iter().peekable();
9360
9361 let mut edits = Vec::new();
9362 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9363
9364 while let Some(selection) = selections.next() {
9365 let mut start_row = selection.start.row;
9366 let mut end_row = selection.end.row;
9367
9368 // Skip selections that overlap with a range that has already been rewrapped.
9369 let selection_range = start_row..end_row;
9370 if rewrapped_row_ranges
9371 .iter()
9372 .any(|range| range.overlaps(&selection_range))
9373 {
9374 continue;
9375 }
9376
9377 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9378
9379 // Since not all lines in the selection may be at the same indent
9380 // level, choose the indent size that is the most common between all
9381 // of the lines.
9382 //
9383 // If there is a tie, we use the deepest indent.
9384 let (indent_size, indent_end) = {
9385 let mut indent_size_occurrences = HashMap::default();
9386 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9387
9388 for row in start_row..=end_row {
9389 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9390 rows_by_indent_size.entry(indent).or_default().push(row);
9391 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9392 }
9393
9394 let indent_size = indent_size_occurrences
9395 .into_iter()
9396 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9397 .map(|(indent, _)| indent)
9398 .unwrap_or_default();
9399 let row = rows_by_indent_size[&indent_size][0];
9400 let indent_end = Point::new(row, indent_size.len);
9401
9402 (indent_size, indent_end)
9403 };
9404
9405 let mut line_prefix = indent_size.chars().collect::<String>();
9406
9407 let mut inside_comment = false;
9408 if let Some(comment_prefix) =
9409 buffer
9410 .language_scope_at(selection.head())
9411 .and_then(|language| {
9412 language
9413 .line_comment_prefixes()
9414 .iter()
9415 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9416 .cloned()
9417 })
9418 {
9419 line_prefix.push_str(&comment_prefix);
9420 inside_comment = true;
9421 }
9422
9423 let language_settings = buffer.language_settings_at(selection.head(), cx);
9424 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9425 RewrapBehavior::InComments => inside_comment,
9426 RewrapBehavior::InSelections => !selection.is_empty(),
9427 RewrapBehavior::Anywhere => true,
9428 };
9429
9430 let should_rewrap = options.override_language_settings
9431 || allow_rewrap_based_on_language
9432 || self.hard_wrap.is_some();
9433 if !should_rewrap {
9434 continue;
9435 }
9436
9437 if selection.is_empty() {
9438 'expand_upwards: while start_row > 0 {
9439 let prev_row = start_row - 1;
9440 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9441 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9442 {
9443 start_row = prev_row;
9444 } else {
9445 break 'expand_upwards;
9446 }
9447 }
9448
9449 'expand_downwards: while end_row < buffer.max_point().row {
9450 let next_row = end_row + 1;
9451 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9452 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9453 {
9454 end_row = next_row;
9455 } else {
9456 break 'expand_downwards;
9457 }
9458 }
9459 }
9460
9461 let start = Point::new(start_row, 0);
9462 let start_offset = start.to_offset(&buffer);
9463 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9464 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9465 let Some(lines_without_prefixes) = selection_text
9466 .lines()
9467 .map(|line| {
9468 line.strip_prefix(&line_prefix)
9469 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9470 .ok_or_else(|| {
9471 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9472 })
9473 })
9474 .collect::<Result<Vec<_>, _>>()
9475 .log_err()
9476 else {
9477 continue;
9478 };
9479
9480 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9481 buffer
9482 .language_settings_at(Point::new(start_row, 0), cx)
9483 .preferred_line_length as usize
9484 });
9485 let wrapped_text = wrap_with_prefix(
9486 line_prefix,
9487 lines_without_prefixes.join("\n"),
9488 wrap_column,
9489 tab_size,
9490 options.preserve_existing_whitespace,
9491 );
9492
9493 // TODO: should always use char-based diff while still supporting cursor behavior that
9494 // matches vim.
9495 let mut diff_options = DiffOptions::default();
9496 if options.override_language_settings {
9497 diff_options.max_word_diff_len = 0;
9498 diff_options.max_word_diff_line_count = 0;
9499 } else {
9500 diff_options.max_word_diff_len = usize::MAX;
9501 diff_options.max_word_diff_line_count = usize::MAX;
9502 }
9503
9504 for (old_range, new_text) in
9505 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9506 {
9507 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9508 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9509 edits.push((edit_start..edit_end, new_text));
9510 }
9511
9512 rewrapped_row_ranges.push(start_row..=end_row);
9513 }
9514
9515 self.buffer
9516 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9517 }
9518
9519 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9520 let mut text = String::new();
9521 let buffer = self.buffer.read(cx).snapshot(cx);
9522 let mut selections = self.selections.all::<Point>(cx);
9523 let mut clipboard_selections = Vec::with_capacity(selections.len());
9524 {
9525 let max_point = buffer.max_point();
9526 let mut is_first = true;
9527 for selection in &mut selections {
9528 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9529 if is_entire_line {
9530 selection.start = Point::new(selection.start.row, 0);
9531 if !selection.is_empty() && selection.end.column == 0 {
9532 selection.end = cmp::min(max_point, selection.end);
9533 } else {
9534 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9535 }
9536 selection.goal = SelectionGoal::None;
9537 }
9538 if is_first {
9539 is_first = false;
9540 } else {
9541 text += "\n";
9542 }
9543 let mut len = 0;
9544 for chunk in buffer.text_for_range(selection.start..selection.end) {
9545 text.push_str(chunk);
9546 len += chunk.len();
9547 }
9548 clipboard_selections.push(ClipboardSelection {
9549 len,
9550 is_entire_line,
9551 first_line_indent: buffer
9552 .indent_size_for_line(MultiBufferRow(selection.start.row))
9553 .len,
9554 });
9555 }
9556 }
9557
9558 self.transact(window, cx, |this, window, cx| {
9559 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9560 s.select(selections);
9561 });
9562 this.insert("", window, cx);
9563 });
9564 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9565 }
9566
9567 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9568 let item = self.cut_common(window, cx);
9569 cx.write_to_clipboard(item);
9570 }
9571
9572 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9573 self.change_selections(None, window, cx, |s| {
9574 s.move_with(|snapshot, sel| {
9575 if sel.is_empty() {
9576 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9577 }
9578 });
9579 });
9580 let item = self.cut_common(window, cx);
9581 cx.set_global(KillRing(item))
9582 }
9583
9584 pub fn kill_ring_yank(
9585 &mut self,
9586 _: &KillRingYank,
9587 window: &mut Window,
9588 cx: &mut Context<Self>,
9589 ) {
9590 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9591 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9592 (kill_ring.text().to_string(), kill_ring.metadata_json())
9593 } else {
9594 return;
9595 }
9596 } else {
9597 return;
9598 };
9599 self.do_paste(&text, metadata, false, window, cx);
9600 }
9601
9602 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9603 self.do_copy(true, cx);
9604 }
9605
9606 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9607 self.do_copy(false, cx);
9608 }
9609
9610 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9611 let selections = self.selections.all::<Point>(cx);
9612 let buffer = self.buffer.read(cx).read(cx);
9613 let mut text = String::new();
9614
9615 let mut clipboard_selections = Vec::with_capacity(selections.len());
9616 {
9617 let max_point = buffer.max_point();
9618 let mut is_first = true;
9619 for selection in &selections {
9620 let mut start = selection.start;
9621 let mut end = selection.end;
9622 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9623 if is_entire_line {
9624 start = Point::new(start.row, 0);
9625 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9626 }
9627
9628 let mut trimmed_selections = Vec::new();
9629 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9630 let row = MultiBufferRow(start.row);
9631 let first_indent = buffer.indent_size_for_line(row);
9632 if first_indent.len == 0 || start.column > first_indent.len {
9633 trimmed_selections.push(start..end);
9634 } else {
9635 trimmed_selections.push(
9636 Point::new(row.0, first_indent.len)
9637 ..Point::new(row.0, buffer.line_len(row)),
9638 );
9639 for row in start.row + 1..=end.row {
9640 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9641 if row_indent_size.len >= first_indent.len {
9642 trimmed_selections.push(
9643 Point::new(row, first_indent.len)
9644 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9645 );
9646 } else {
9647 trimmed_selections.clear();
9648 trimmed_selections.push(start..end);
9649 break;
9650 }
9651 }
9652 }
9653 } else {
9654 trimmed_selections.push(start..end);
9655 }
9656
9657 for trimmed_range in trimmed_selections {
9658 if is_first {
9659 is_first = false;
9660 } else {
9661 text += "\n";
9662 }
9663 let mut len = 0;
9664 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9665 text.push_str(chunk);
9666 len += chunk.len();
9667 }
9668 clipboard_selections.push(ClipboardSelection {
9669 len,
9670 is_entire_line,
9671 first_line_indent: buffer
9672 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9673 .len,
9674 });
9675 }
9676 }
9677 }
9678
9679 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9680 text,
9681 clipboard_selections,
9682 ));
9683 }
9684
9685 pub fn do_paste(
9686 &mut self,
9687 text: &String,
9688 clipboard_selections: Option<Vec<ClipboardSelection>>,
9689 handle_entire_lines: bool,
9690 window: &mut Window,
9691 cx: &mut Context<Self>,
9692 ) {
9693 if self.read_only(cx) {
9694 return;
9695 }
9696
9697 let clipboard_text = Cow::Borrowed(text);
9698
9699 self.transact(window, cx, |this, window, cx| {
9700 if let Some(mut clipboard_selections) = clipboard_selections {
9701 let old_selections = this.selections.all::<usize>(cx);
9702 let all_selections_were_entire_line =
9703 clipboard_selections.iter().all(|s| s.is_entire_line);
9704 let first_selection_indent_column =
9705 clipboard_selections.first().map(|s| s.first_line_indent);
9706 if clipboard_selections.len() != old_selections.len() {
9707 clipboard_selections.drain(..);
9708 }
9709 let cursor_offset = this.selections.last::<usize>(cx).head();
9710 let mut auto_indent_on_paste = true;
9711
9712 this.buffer.update(cx, |buffer, cx| {
9713 let snapshot = buffer.read(cx);
9714 auto_indent_on_paste = snapshot
9715 .language_settings_at(cursor_offset, cx)
9716 .auto_indent_on_paste;
9717
9718 let mut start_offset = 0;
9719 let mut edits = Vec::new();
9720 let mut original_indent_columns = Vec::new();
9721 for (ix, selection) in old_selections.iter().enumerate() {
9722 let to_insert;
9723 let entire_line;
9724 let original_indent_column;
9725 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9726 let end_offset = start_offset + clipboard_selection.len;
9727 to_insert = &clipboard_text[start_offset..end_offset];
9728 entire_line = clipboard_selection.is_entire_line;
9729 start_offset = end_offset + 1;
9730 original_indent_column = Some(clipboard_selection.first_line_indent);
9731 } else {
9732 to_insert = clipboard_text.as_str();
9733 entire_line = all_selections_were_entire_line;
9734 original_indent_column = first_selection_indent_column
9735 }
9736
9737 // If the corresponding selection was empty when this slice of the
9738 // clipboard text was written, then the entire line containing the
9739 // selection was copied. If this selection is also currently empty,
9740 // then paste the line before the current line of the buffer.
9741 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9742 let column = selection.start.to_point(&snapshot).column as usize;
9743 let line_start = selection.start - column;
9744 line_start..line_start
9745 } else {
9746 selection.range()
9747 };
9748
9749 edits.push((range, to_insert));
9750 original_indent_columns.push(original_indent_column);
9751 }
9752 drop(snapshot);
9753
9754 buffer.edit(
9755 edits,
9756 if auto_indent_on_paste {
9757 Some(AutoindentMode::Block {
9758 original_indent_columns,
9759 })
9760 } else {
9761 None
9762 },
9763 cx,
9764 );
9765 });
9766
9767 let selections = this.selections.all::<usize>(cx);
9768 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9769 s.select(selections)
9770 });
9771 } else {
9772 this.insert(&clipboard_text, window, cx);
9773 }
9774 });
9775 }
9776
9777 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9778 if let Some(item) = cx.read_from_clipboard() {
9779 let entries = item.entries();
9780
9781 match entries.first() {
9782 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9783 // of all the pasted entries.
9784 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9785 .do_paste(
9786 clipboard_string.text(),
9787 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9788 true,
9789 window,
9790 cx,
9791 ),
9792 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9793 }
9794 }
9795 }
9796
9797 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9798 if self.read_only(cx) {
9799 return;
9800 }
9801
9802 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9803 if let Some((selections, _)) =
9804 self.selection_history.transaction(transaction_id).cloned()
9805 {
9806 self.change_selections(None, window, cx, |s| {
9807 s.select_anchors(selections.to_vec());
9808 });
9809 } else {
9810 log::error!(
9811 "No entry in selection_history found for undo. \
9812 This may correspond to a bug where undo does not update the selection. \
9813 If this is occurring, please add details to \
9814 https://github.com/zed-industries/zed/issues/22692"
9815 );
9816 }
9817 self.request_autoscroll(Autoscroll::fit(), cx);
9818 self.unmark_text(window, cx);
9819 self.refresh_inline_completion(true, false, window, cx);
9820 cx.emit(EditorEvent::Edited { transaction_id });
9821 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9822 }
9823 }
9824
9825 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9826 if self.read_only(cx) {
9827 return;
9828 }
9829
9830 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9831 if let Some((_, Some(selections))) =
9832 self.selection_history.transaction(transaction_id).cloned()
9833 {
9834 self.change_selections(None, window, cx, |s| {
9835 s.select_anchors(selections.to_vec());
9836 });
9837 } else {
9838 log::error!(
9839 "No entry in selection_history found for redo. \
9840 This may correspond to a bug where undo does not update the selection. \
9841 If this is occurring, please add details to \
9842 https://github.com/zed-industries/zed/issues/22692"
9843 );
9844 }
9845 self.request_autoscroll(Autoscroll::fit(), cx);
9846 self.unmark_text(window, cx);
9847 self.refresh_inline_completion(true, false, window, cx);
9848 cx.emit(EditorEvent::Edited { transaction_id });
9849 }
9850 }
9851
9852 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9853 self.buffer
9854 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9855 }
9856
9857 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9858 self.buffer
9859 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9860 }
9861
9862 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9863 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9864 let line_mode = s.line_mode;
9865 s.move_with(|map, selection| {
9866 let cursor = if selection.is_empty() && !line_mode {
9867 movement::left(map, selection.start)
9868 } else {
9869 selection.start
9870 };
9871 selection.collapse_to(cursor, SelectionGoal::None);
9872 });
9873 })
9874 }
9875
9876 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9877 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9878 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9879 })
9880 }
9881
9882 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9883 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9884 let line_mode = s.line_mode;
9885 s.move_with(|map, selection| {
9886 let cursor = if selection.is_empty() && !line_mode {
9887 movement::right(map, selection.end)
9888 } else {
9889 selection.end
9890 };
9891 selection.collapse_to(cursor, SelectionGoal::None)
9892 });
9893 })
9894 }
9895
9896 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9897 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9898 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9899 })
9900 }
9901
9902 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9903 if self.take_rename(true, window, cx).is_some() {
9904 return;
9905 }
9906
9907 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9908 cx.propagate();
9909 return;
9910 }
9911
9912 let text_layout_details = &self.text_layout_details(window);
9913 let selection_count = self.selections.count();
9914 let first_selection = self.selections.first_anchor();
9915
9916 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9917 let line_mode = s.line_mode;
9918 s.move_with(|map, selection| {
9919 if !selection.is_empty() && !line_mode {
9920 selection.goal = SelectionGoal::None;
9921 }
9922 let (cursor, goal) = movement::up(
9923 map,
9924 selection.start,
9925 selection.goal,
9926 false,
9927 text_layout_details,
9928 );
9929 selection.collapse_to(cursor, goal);
9930 });
9931 });
9932
9933 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9934 {
9935 cx.propagate();
9936 }
9937 }
9938
9939 pub fn move_up_by_lines(
9940 &mut self,
9941 action: &MoveUpByLines,
9942 window: &mut Window,
9943 cx: &mut Context<Self>,
9944 ) {
9945 if self.take_rename(true, window, cx).is_some() {
9946 return;
9947 }
9948
9949 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9950 cx.propagate();
9951 return;
9952 }
9953
9954 let text_layout_details = &self.text_layout_details(window);
9955
9956 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9957 let line_mode = s.line_mode;
9958 s.move_with(|map, selection| {
9959 if !selection.is_empty() && !line_mode {
9960 selection.goal = SelectionGoal::None;
9961 }
9962 let (cursor, goal) = movement::up_by_rows(
9963 map,
9964 selection.start,
9965 action.lines,
9966 selection.goal,
9967 false,
9968 text_layout_details,
9969 );
9970 selection.collapse_to(cursor, goal);
9971 });
9972 })
9973 }
9974
9975 pub fn move_down_by_lines(
9976 &mut self,
9977 action: &MoveDownByLines,
9978 window: &mut Window,
9979 cx: &mut Context<Self>,
9980 ) {
9981 if self.take_rename(true, window, cx).is_some() {
9982 return;
9983 }
9984
9985 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9986 cx.propagate();
9987 return;
9988 }
9989
9990 let text_layout_details = &self.text_layout_details(window);
9991
9992 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9993 let line_mode = s.line_mode;
9994 s.move_with(|map, selection| {
9995 if !selection.is_empty() && !line_mode {
9996 selection.goal = SelectionGoal::None;
9997 }
9998 let (cursor, goal) = movement::down_by_rows(
9999 map,
10000 selection.start,
10001 action.lines,
10002 selection.goal,
10003 false,
10004 text_layout_details,
10005 );
10006 selection.collapse_to(cursor, goal);
10007 });
10008 })
10009 }
10010
10011 pub fn select_down_by_lines(
10012 &mut self,
10013 action: &SelectDownByLines,
10014 window: &mut Window,
10015 cx: &mut Context<Self>,
10016 ) {
10017 let text_layout_details = &self.text_layout_details(window);
10018 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10019 s.move_heads_with(|map, head, goal| {
10020 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
10021 })
10022 })
10023 }
10024
10025 pub fn select_up_by_lines(
10026 &mut self,
10027 action: &SelectUpByLines,
10028 window: &mut Window,
10029 cx: &mut Context<Self>,
10030 ) {
10031 let text_layout_details = &self.text_layout_details(window);
10032 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10033 s.move_heads_with(|map, head, goal| {
10034 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
10035 })
10036 })
10037 }
10038
10039 pub fn select_page_up(
10040 &mut self,
10041 _: &SelectPageUp,
10042 window: &mut Window,
10043 cx: &mut Context<Self>,
10044 ) {
10045 let Some(row_count) = self.visible_row_count() else {
10046 return;
10047 };
10048
10049 let text_layout_details = &self.text_layout_details(window);
10050
10051 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10052 s.move_heads_with(|map, head, goal| {
10053 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
10054 })
10055 })
10056 }
10057
10058 pub fn move_page_up(
10059 &mut self,
10060 action: &MovePageUp,
10061 window: &mut Window,
10062 cx: &mut Context<Self>,
10063 ) {
10064 if self.take_rename(true, window, cx).is_some() {
10065 return;
10066 }
10067
10068 if self
10069 .context_menu
10070 .borrow_mut()
10071 .as_mut()
10072 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
10073 .unwrap_or(false)
10074 {
10075 return;
10076 }
10077
10078 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10079 cx.propagate();
10080 return;
10081 }
10082
10083 let Some(row_count) = self.visible_row_count() else {
10084 return;
10085 };
10086
10087 let autoscroll = if action.center_cursor {
10088 Autoscroll::center()
10089 } else {
10090 Autoscroll::fit()
10091 };
10092
10093 let text_layout_details = &self.text_layout_details(window);
10094
10095 self.change_selections(Some(autoscroll), window, cx, |s| {
10096 let line_mode = s.line_mode;
10097 s.move_with(|map, selection| {
10098 if !selection.is_empty() && !line_mode {
10099 selection.goal = SelectionGoal::None;
10100 }
10101 let (cursor, goal) = movement::up_by_rows(
10102 map,
10103 selection.end,
10104 row_count,
10105 selection.goal,
10106 false,
10107 text_layout_details,
10108 );
10109 selection.collapse_to(cursor, goal);
10110 });
10111 });
10112 }
10113
10114 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
10115 let text_layout_details = &self.text_layout_details(window);
10116 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10117 s.move_heads_with(|map, head, goal| {
10118 movement::up(map, head, goal, false, text_layout_details)
10119 })
10120 })
10121 }
10122
10123 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
10124 self.take_rename(true, window, cx);
10125
10126 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10127 cx.propagate();
10128 return;
10129 }
10130
10131 let text_layout_details = &self.text_layout_details(window);
10132 let selection_count = self.selections.count();
10133 let first_selection = self.selections.first_anchor();
10134
10135 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10136 let line_mode = s.line_mode;
10137 s.move_with(|map, selection| {
10138 if !selection.is_empty() && !line_mode {
10139 selection.goal = SelectionGoal::None;
10140 }
10141 let (cursor, goal) = movement::down(
10142 map,
10143 selection.end,
10144 selection.goal,
10145 false,
10146 text_layout_details,
10147 );
10148 selection.collapse_to(cursor, goal);
10149 });
10150 });
10151
10152 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10153 {
10154 cx.propagate();
10155 }
10156 }
10157
10158 pub fn select_page_down(
10159 &mut self,
10160 _: &SelectPageDown,
10161 window: &mut Window,
10162 cx: &mut Context<Self>,
10163 ) {
10164 let Some(row_count) = self.visible_row_count() else {
10165 return;
10166 };
10167
10168 let text_layout_details = &self.text_layout_details(window);
10169
10170 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10171 s.move_heads_with(|map, head, goal| {
10172 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10173 })
10174 })
10175 }
10176
10177 pub fn move_page_down(
10178 &mut self,
10179 action: &MovePageDown,
10180 window: &mut Window,
10181 cx: &mut Context<Self>,
10182 ) {
10183 if self.take_rename(true, window, cx).is_some() {
10184 return;
10185 }
10186
10187 if self
10188 .context_menu
10189 .borrow_mut()
10190 .as_mut()
10191 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10192 .unwrap_or(false)
10193 {
10194 return;
10195 }
10196
10197 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10198 cx.propagate();
10199 return;
10200 }
10201
10202 let Some(row_count) = self.visible_row_count() else {
10203 return;
10204 };
10205
10206 let autoscroll = if action.center_cursor {
10207 Autoscroll::center()
10208 } else {
10209 Autoscroll::fit()
10210 };
10211
10212 let text_layout_details = &self.text_layout_details(window);
10213 self.change_selections(Some(autoscroll), window, cx, |s| {
10214 let line_mode = s.line_mode;
10215 s.move_with(|map, selection| {
10216 if !selection.is_empty() && !line_mode {
10217 selection.goal = SelectionGoal::None;
10218 }
10219 let (cursor, goal) = movement::down_by_rows(
10220 map,
10221 selection.end,
10222 row_count,
10223 selection.goal,
10224 false,
10225 text_layout_details,
10226 );
10227 selection.collapse_to(cursor, goal);
10228 });
10229 });
10230 }
10231
10232 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10233 let text_layout_details = &self.text_layout_details(window);
10234 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10235 s.move_heads_with(|map, head, goal| {
10236 movement::down(map, head, goal, false, text_layout_details)
10237 })
10238 });
10239 }
10240
10241 pub fn context_menu_first(
10242 &mut self,
10243 _: &ContextMenuFirst,
10244 _window: &mut Window,
10245 cx: &mut Context<Self>,
10246 ) {
10247 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10248 context_menu.select_first(self.completion_provider.as_deref(), cx);
10249 }
10250 }
10251
10252 pub fn context_menu_prev(
10253 &mut self,
10254 _: &ContextMenuPrevious,
10255 _window: &mut Window,
10256 cx: &mut Context<Self>,
10257 ) {
10258 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10259 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10260 }
10261 }
10262
10263 pub fn context_menu_next(
10264 &mut self,
10265 _: &ContextMenuNext,
10266 _window: &mut Window,
10267 cx: &mut Context<Self>,
10268 ) {
10269 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10270 context_menu.select_next(self.completion_provider.as_deref(), cx);
10271 }
10272 }
10273
10274 pub fn context_menu_last(
10275 &mut self,
10276 _: &ContextMenuLast,
10277 _window: &mut Window,
10278 cx: &mut Context<Self>,
10279 ) {
10280 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10281 context_menu.select_last(self.completion_provider.as_deref(), cx);
10282 }
10283 }
10284
10285 pub fn move_to_previous_word_start(
10286 &mut self,
10287 _: &MoveToPreviousWordStart,
10288 window: &mut Window,
10289 cx: &mut Context<Self>,
10290 ) {
10291 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10292 s.move_cursors_with(|map, head, _| {
10293 (
10294 movement::previous_word_start(map, head),
10295 SelectionGoal::None,
10296 )
10297 });
10298 })
10299 }
10300
10301 pub fn move_to_previous_subword_start(
10302 &mut self,
10303 _: &MoveToPreviousSubwordStart,
10304 window: &mut Window,
10305 cx: &mut Context<Self>,
10306 ) {
10307 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10308 s.move_cursors_with(|map, head, _| {
10309 (
10310 movement::previous_subword_start(map, head),
10311 SelectionGoal::None,
10312 )
10313 });
10314 })
10315 }
10316
10317 pub fn select_to_previous_word_start(
10318 &mut self,
10319 _: &SelectToPreviousWordStart,
10320 window: &mut Window,
10321 cx: &mut Context<Self>,
10322 ) {
10323 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10324 s.move_heads_with(|map, head, _| {
10325 (
10326 movement::previous_word_start(map, head),
10327 SelectionGoal::None,
10328 )
10329 });
10330 })
10331 }
10332
10333 pub fn select_to_previous_subword_start(
10334 &mut self,
10335 _: &SelectToPreviousSubwordStart,
10336 window: &mut Window,
10337 cx: &mut Context<Self>,
10338 ) {
10339 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10340 s.move_heads_with(|map, head, _| {
10341 (
10342 movement::previous_subword_start(map, head),
10343 SelectionGoal::None,
10344 )
10345 });
10346 })
10347 }
10348
10349 pub fn delete_to_previous_word_start(
10350 &mut self,
10351 action: &DeleteToPreviousWordStart,
10352 window: &mut Window,
10353 cx: &mut Context<Self>,
10354 ) {
10355 self.transact(window, cx, |this, window, cx| {
10356 this.select_autoclose_pair(window, cx);
10357 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10358 let line_mode = s.line_mode;
10359 s.move_with(|map, selection| {
10360 if selection.is_empty() && !line_mode {
10361 let cursor = if action.ignore_newlines {
10362 movement::previous_word_start(map, selection.head())
10363 } else {
10364 movement::previous_word_start_or_newline(map, selection.head())
10365 };
10366 selection.set_head(cursor, SelectionGoal::None);
10367 }
10368 });
10369 });
10370 this.insert("", window, cx);
10371 });
10372 }
10373
10374 pub fn delete_to_previous_subword_start(
10375 &mut self,
10376 _: &DeleteToPreviousSubwordStart,
10377 window: &mut Window,
10378 cx: &mut Context<Self>,
10379 ) {
10380 self.transact(window, cx, |this, window, cx| {
10381 this.select_autoclose_pair(window, cx);
10382 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10383 let line_mode = s.line_mode;
10384 s.move_with(|map, selection| {
10385 if selection.is_empty() && !line_mode {
10386 let cursor = movement::previous_subword_start(map, selection.head());
10387 selection.set_head(cursor, SelectionGoal::None);
10388 }
10389 });
10390 });
10391 this.insert("", window, cx);
10392 });
10393 }
10394
10395 pub fn move_to_next_word_end(
10396 &mut self,
10397 _: &MoveToNextWordEnd,
10398 window: &mut Window,
10399 cx: &mut Context<Self>,
10400 ) {
10401 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10402 s.move_cursors_with(|map, head, _| {
10403 (movement::next_word_end(map, head), SelectionGoal::None)
10404 });
10405 })
10406 }
10407
10408 pub fn move_to_next_subword_end(
10409 &mut self,
10410 _: &MoveToNextSubwordEnd,
10411 window: &mut Window,
10412 cx: &mut Context<Self>,
10413 ) {
10414 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10415 s.move_cursors_with(|map, head, _| {
10416 (movement::next_subword_end(map, head), SelectionGoal::None)
10417 });
10418 })
10419 }
10420
10421 pub fn select_to_next_word_end(
10422 &mut self,
10423 _: &SelectToNextWordEnd,
10424 window: &mut Window,
10425 cx: &mut Context<Self>,
10426 ) {
10427 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10428 s.move_heads_with(|map, head, _| {
10429 (movement::next_word_end(map, head), SelectionGoal::None)
10430 });
10431 })
10432 }
10433
10434 pub fn select_to_next_subword_end(
10435 &mut self,
10436 _: &SelectToNextSubwordEnd,
10437 window: &mut Window,
10438 cx: &mut Context<Self>,
10439 ) {
10440 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10441 s.move_heads_with(|map, head, _| {
10442 (movement::next_subword_end(map, head), SelectionGoal::None)
10443 });
10444 })
10445 }
10446
10447 pub fn delete_to_next_word_end(
10448 &mut self,
10449 action: &DeleteToNextWordEnd,
10450 window: &mut Window,
10451 cx: &mut Context<Self>,
10452 ) {
10453 self.transact(window, cx, |this, window, cx| {
10454 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10455 let line_mode = s.line_mode;
10456 s.move_with(|map, selection| {
10457 if selection.is_empty() && !line_mode {
10458 let cursor = if action.ignore_newlines {
10459 movement::next_word_end(map, selection.head())
10460 } else {
10461 movement::next_word_end_or_newline(map, selection.head())
10462 };
10463 selection.set_head(cursor, SelectionGoal::None);
10464 }
10465 });
10466 });
10467 this.insert("", window, cx);
10468 });
10469 }
10470
10471 pub fn delete_to_next_subword_end(
10472 &mut self,
10473 _: &DeleteToNextSubwordEnd,
10474 window: &mut Window,
10475 cx: &mut Context<Self>,
10476 ) {
10477 self.transact(window, cx, |this, window, cx| {
10478 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10479 s.move_with(|map, selection| {
10480 if selection.is_empty() {
10481 let cursor = movement::next_subword_end(map, selection.head());
10482 selection.set_head(cursor, SelectionGoal::None);
10483 }
10484 });
10485 });
10486 this.insert("", window, cx);
10487 });
10488 }
10489
10490 pub fn move_to_beginning_of_line(
10491 &mut self,
10492 action: &MoveToBeginningOfLine,
10493 window: &mut Window,
10494 cx: &mut Context<Self>,
10495 ) {
10496 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10497 s.move_cursors_with(|map, head, _| {
10498 (
10499 movement::indented_line_beginning(
10500 map,
10501 head,
10502 action.stop_at_soft_wraps,
10503 action.stop_at_indent,
10504 ),
10505 SelectionGoal::None,
10506 )
10507 });
10508 })
10509 }
10510
10511 pub fn select_to_beginning_of_line(
10512 &mut self,
10513 action: &SelectToBeginningOfLine,
10514 window: &mut Window,
10515 cx: &mut Context<Self>,
10516 ) {
10517 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10518 s.move_heads_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 delete_to_beginning_of_line(
10533 &mut self,
10534 action: &DeleteToBeginningOfLine,
10535 window: &mut Window,
10536 cx: &mut Context<Self>,
10537 ) {
10538 self.transact(window, cx, |this, window, cx| {
10539 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10540 s.move_with(|_, selection| {
10541 selection.reversed = true;
10542 });
10543 });
10544
10545 this.select_to_beginning_of_line(
10546 &SelectToBeginningOfLine {
10547 stop_at_soft_wraps: false,
10548 stop_at_indent: action.stop_at_indent,
10549 },
10550 window,
10551 cx,
10552 );
10553 this.backspace(&Backspace, window, cx);
10554 });
10555 }
10556
10557 pub fn move_to_end_of_line(
10558 &mut self,
10559 action: &MoveToEndOfLine,
10560 window: &mut Window,
10561 cx: &mut Context<Self>,
10562 ) {
10563 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10564 s.move_cursors_with(|map, head, _| {
10565 (
10566 movement::line_end(map, head, action.stop_at_soft_wraps),
10567 SelectionGoal::None,
10568 )
10569 });
10570 })
10571 }
10572
10573 pub fn select_to_end_of_line(
10574 &mut self,
10575 action: &SelectToEndOfLine,
10576 window: &mut Window,
10577 cx: &mut Context<Self>,
10578 ) {
10579 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10580 s.move_heads_with(|map, head, _| {
10581 (
10582 movement::line_end(map, head, action.stop_at_soft_wraps),
10583 SelectionGoal::None,
10584 )
10585 });
10586 })
10587 }
10588
10589 pub fn delete_to_end_of_line(
10590 &mut self,
10591 _: &DeleteToEndOfLine,
10592 window: &mut Window,
10593 cx: &mut Context<Self>,
10594 ) {
10595 self.transact(window, cx, |this, window, cx| {
10596 this.select_to_end_of_line(
10597 &SelectToEndOfLine {
10598 stop_at_soft_wraps: false,
10599 },
10600 window,
10601 cx,
10602 );
10603 this.delete(&Delete, window, cx);
10604 });
10605 }
10606
10607 pub fn cut_to_end_of_line(
10608 &mut self,
10609 _: &CutToEndOfLine,
10610 window: &mut Window,
10611 cx: &mut Context<Self>,
10612 ) {
10613 self.transact(window, cx, |this, window, cx| {
10614 this.select_to_end_of_line(
10615 &SelectToEndOfLine {
10616 stop_at_soft_wraps: false,
10617 },
10618 window,
10619 cx,
10620 );
10621 this.cut(&Cut, window, cx);
10622 });
10623 }
10624
10625 pub fn move_to_start_of_paragraph(
10626 &mut self,
10627 _: &MoveToStartOfParagraph,
10628 window: &mut Window,
10629 cx: &mut Context<Self>,
10630 ) {
10631 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10632 cx.propagate();
10633 return;
10634 }
10635
10636 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10637 s.move_with(|map, selection| {
10638 selection.collapse_to(
10639 movement::start_of_paragraph(map, selection.head(), 1),
10640 SelectionGoal::None,
10641 )
10642 });
10643 })
10644 }
10645
10646 pub fn move_to_end_of_paragraph(
10647 &mut self,
10648 _: &MoveToEndOfParagraph,
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::end_of_paragraph(map, selection.head(), 1),
10661 SelectionGoal::None,
10662 )
10663 });
10664 })
10665 }
10666
10667 pub fn select_to_start_of_paragraph(
10668 &mut self,
10669 _: &SelectToStartOfParagraph,
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_heads_with(|map, head, _| {
10680 (
10681 movement::start_of_paragraph(map, head, 1),
10682 SelectionGoal::None,
10683 )
10684 });
10685 })
10686 }
10687
10688 pub fn select_to_end_of_paragraph(
10689 &mut self,
10690 _: &SelectToEndOfParagraph,
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::end_of_paragraph(map, head, 1),
10703 SelectionGoal::None,
10704 )
10705 });
10706 })
10707 }
10708
10709 pub fn move_to_start_of_excerpt(
10710 &mut self,
10711 _: &MoveToStartOfExcerpt,
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_with(|map, selection| {
10722 selection.collapse_to(
10723 movement::start_of_excerpt(
10724 map,
10725 selection.head(),
10726 workspace::searchable::Direction::Prev,
10727 ),
10728 SelectionGoal::None,
10729 )
10730 });
10731 })
10732 }
10733
10734 pub fn move_to_start_of_next_excerpt(
10735 &mut self,
10736 _: &MoveToStartOfNextExcerpt,
10737 window: &mut Window,
10738 cx: &mut Context<Self>,
10739 ) {
10740 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10741 cx.propagate();
10742 return;
10743 }
10744
10745 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10746 s.move_with(|map, selection| {
10747 selection.collapse_to(
10748 movement::start_of_excerpt(
10749 map,
10750 selection.head(),
10751 workspace::searchable::Direction::Next,
10752 ),
10753 SelectionGoal::None,
10754 )
10755 });
10756 })
10757 }
10758
10759 pub fn move_to_end_of_excerpt(
10760 &mut self,
10761 _: &MoveToEndOfExcerpt,
10762 window: &mut Window,
10763 cx: &mut Context<Self>,
10764 ) {
10765 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10766 cx.propagate();
10767 return;
10768 }
10769
10770 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10771 s.move_with(|map, selection| {
10772 selection.collapse_to(
10773 movement::end_of_excerpt(
10774 map,
10775 selection.head(),
10776 workspace::searchable::Direction::Next,
10777 ),
10778 SelectionGoal::None,
10779 )
10780 });
10781 })
10782 }
10783
10784 pub fn move_to_end_of_previous_excerpt(
10785 &mut self,
10786 _: &MoveToEndOfPreviousExcerpt,
10787 window: &mut Window,
10788 cx: &mut Context<Self>,
10789 ) {
10790 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10791 cx.propagate();
10792 return;
10793 }
10794
10795 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10796 s.move_with(|map, selection| {
10797 selection.collapse_to(
10798 movement::end_of_excerpt(
10799 map,
10800 selection.head(),
10801 workspace::searchable::Direction::Prev,
10802 ),
10803 SelectionGoal::None,
10804 )
10805 });
10806 })
10807 }
10808
10809 pub fn select_to_start_of_excerpt(
10810 &mut self,
10811 _: &SelectToStartOfExcerpt,
10812 window: &mut Window,
10813 cx: &mut Context<Self>,
10814 ) {
10815 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10816 cx.propagate();
10817 return;
10818 }
10819
10820 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10821 s.move_heads_with(|map, head, _| {
10822 (
10823 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10824 SelectionGoal::None,
10825 )
10826 });
10827 })
10828 }
10829
10830 pub fn select_to_start_of_next_excerpt(
10831 &mut self,
10832 _: &SelectToStartOfNextExcerpt,
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::Next),
10845 SelectionGoal::None,
10846 )
10847 });
10848 })
10849 }
10850
10851 pub fn select_to_end_of_excerpt(
10852 &mut self,
10853 _: &SelectToEndOfExcerpt,
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::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10866 SelectionGoal::None,
10867 )
10868 });
10869 })
10870 }
10871
10872 pub fn select_to_end_of_previous_excerpt(
10873 &mut self,
10874 _: &SelectToEndOfPreviousExcerpt,
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::Prev),
10887 SelectionGoal::None,
10888 )
10889 });
10890 })
10891 }
10892
10893 pub fn move_to_beginning(
10894 &mut self,
10895 _: &MoveToBeginning,
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.select_ranges(vec![0..0]);
10906 });
10907 }
10908
10909 pub fn select_to_beginning(
10910 &mut self,
10911 _: &SelectToBeginning,
10912 window: &mut Window,
10913 cx: &mut Context<Self>,
10914 ) {
10915 let mut selection = self.selections.last::<Point>(cx);
10916 selection.set_head(Point::zero(), SelectionGoal::None);
10917
10918 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10919 s.select(vec![selection]);
10920 });
10921 }
10922
10923 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10924 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10925 cx.propagate();
10926 return;
10927 }
10928
10929 let cursor = self.buffer.read(cx).read(cx).len();
10930 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10931 s.select_ranges(vec![cursor..cursor])
10932 });
10933 }
10934
10935 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10936 self.nav_history = nav_history;
10937 }
10938
10939 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10940 self.nav_history.as_ref()
10941 }
10942
10943 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10944 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10945 }
10946
10947 fn push_to_nav_history(
10948 &mut self,
10949 cursor_anchor: Anchor,
10950 new_position: Option<Point>,
10951 is_deactivate: bool,
10952 cx: &mut Context<Self>,
10953 ) {
10954 if let Some(nav_history) = self.nav_history.as_mut() {
10955 let buffer = self.buffer.read(cx).read(cx);
10956 let cursor_position = cursor_anchor.to_point(&buffer);
10957 let scroll_state = self.scroll_manager.anchor();
10958 let scroll_top_row = scroll_state.top_row(&buffer);
10959 drop(buffer);
10960
10961 if let Some(new_position) = new_position {
10962 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10963 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10964 return;
10965 }
10966 }
10967
10968 nav_history.push(
10969 Some(NavigationData {
10970 cursor_anchor,
10971 cursor_position,
10972 scroll_anchor: scroll_state,
10973 scroll_top_row,
10974 }),
10975 cx,
10976 );
10977 cx.emit(EditorEvent::PushedToNavHistory {
10978 anchor: cursor_anchor,
10979 is_deactivate,
10980 })
10981 }
10982 }
10983
10984 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10985 let buffer = self.buffer.read(cx).snapshot(cx);
10986 let mut selection = self.selections.first::<usize>(cx);
10987 selection.set_head(buffer.len(), SelectionGoal::None);
10988 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10989 s.select(vec![selection]);
10990 });
10991 }
10992
10993 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10994 let end = self.buffer.read(cx).read(cx).len();
10995 self.change_selections(None, window, cx, |s| {
10996 s.select_ranges(vec![0..end]);
10997 });
10998 }
10999
11000 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
11001 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11002 let mut selections = self.selections.all::<Point>(cx);
11003 let max_point = display_map.buffer_snapshot.max_point();
11004 for selection in &mut selections {
11005 let rows = selection.spanned_rows(true, &display_map);
11006 selection.start = Point::new(rows.start.0, 0);
11007 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
11008 selection.reversed = false;
11009 }
11010 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11011 s.select(selections);
11012 });
11013 }
11014
11015 pub fn split_selection_into_lines(
11016 &mut self,
11017 _: &SplitSelectionIntoLines,
11018 window: &mut Window,
11019 cx: &mut Context<Self>,
11020 ) {
11021 let selections = self
11022 .selections
11023 .all::<Point>(cx)
11024 .into_iter()
11025 .map(|selection| selection.start..selection.end)
11026 .collect::<Vec<_>>();
11027 self.unfold_ranges(&selections, true, true, cx);
11028
11029 let mut new_selection_ranges = Vec::new();
11030 {
11031 let buffer = self.buffer.read(cx).read(cx);
11032 for selection in selections {
11033 for row in selection.start.row..selection.end.row {
11034 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11035 new_selection_ranges.push(cursor..cursor);
11036 }
11037
11038 let is_multiline_selection = selection.start.row != selection.end.row;
11039 // Don't insert last one if it's a multi-line selection ending at the start of a line,
11040 // so this action feels more ergonomic when paired with other selection operations
11041 let should_skip_last = is_multiline_selection && selection.end.column == 0;
11042 if !should_skip_last {
11043 new_selection_ranges.push(selection.end..selection.end);
11044 }
11045 }
11046 }
11047 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11048 s.select_ranges(new_selection_ranges);
11049 });
11050 }
11051
11052 pub fn add_selection_above(
11053 &mut self,
11054 _: &AddSelectionAbove,
11055 window: &mut Window,
11056 cx: &mut Context<Self>,
11057 ) {
11058 self.add_selection(true, window, cx);
11059 }
11060
11061 pub fn add_selection_below(
11062 &mut self,
11063 _: &AddSelectionBelow,
11064 window: &mut Window,
11065 cx: &mut Context<Self>,
11066 ) {
11067 self.add_selection(false, window, cx);
11068 }
11069
11070 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
11071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11072 let mut selections = self.selections.all::<Point>(cx);
11073 let text_layout_details = self.text_layout_details(window);
11074 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
11075 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
11076 let range = oldest_selection.display_range(&display_map).sorted();
11077
11078 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
11079 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
11080 let positions = start_x.min(end_x)..start_x.max(end_x);
11081
11082 selections.clear();
11083 let mut stack = Vec::new();
11084 for row in range.start.row().0..=range.end.row().0 {
11085 if let Some(selection) = self.selections.build_columnar_selection(
11086 &display_map,
11087 DisplayRow(row),
11088 &positions,
11089 oldest_selection.reversed,
11090 &text_layout_details,
11091 ) {
11092 stack.push(selection.id);
11093 selections.push(selection);
11094 }
11095 }
11096
11097 if above {
11098 stack.reverse();
11099 }
11100
11101 AddSelectionsState { above, stack }
11102 });
11103
11104 let last_added_selection = *state.stack.last().unwrap();
11105 let mut new_selections = Vec::new();
11106 if above == state.above {
11107 let end_row = if above {
11108 DisplayRow(0)
11109 } else {
11110 display_map.max_point().row()
11111 };
11112
11113 'outer: for selection in selections {
11114 if selection.id == last_added_selection {
11115 let range = selection.display_range(&display_map).sorted();
11116 debug_assert_eq!(range.start.row(), range.end.row());
11117 let mut row = range.start.row();
11118 let positions =
11119 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
11120 px(start)..px(end)
11121 } else {
11122 let start_x =
11123 display_map.x_for_display_point(range.start, &text_layout_details);
11124 let end_x =
11125 display_map.x_for_display_point(range.end, &text_layout_details);
11126 start_x.min(end_x)..start_x.max(end_x)
11127 };
11128
11129 while row != end_row {
11130 if above {
11131 row.0 -= 1;
11132 } else {
11133 row.0 += 1;
11134 }
11135
11136 if let Some(new_selection) = self.selections.build_columnar_selection(
11137 &display_map,
11138 row,
11139 &positions,
11140 selection.reversed,
11141 &text_layout_details,
11142 ) {
11143 state.stack.push(new_selection.id);
11144 if above {
11145 new_selections.push(new_selection);
11146 new_selections.push(selection);
11147 } else {
11148 new_selections.push(selection);
11149 new_selections.push(new_selection);
11150 }
11151
11152 continue 'outer;
11153 }
11154 }
11155 }
11156
11157 new_selections.push(selection);
11158 }
11159 } else {
11160 new_selections = selections;
11161 new_selections.retain(|s| s.id != last_added_selection);
11162 state.stack.pop();
11163 }
11164
11165 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11166 s.select(new_selections);
11167 });
11168 if state.stack.len() > 1 {
11169 self.add_selections_state = Some(state);
11170 }
11171 }
11172
11173 pub fn select_next_match_internal(
11174 &mut self,
11175 display_map: &DisplaySnapshot,
11176 replace_newest: bool,
11177 autoscroll: Option<Autoscroll>,
11178 window: &mut Window,
11179 cx: &mut Context<Self>,
11180 ) -> Result<()> {
11181 fn select_next_match_ranges(
11182 this: &mut Editor,
11183 range: Range<usize>,
11184 replace_newest: bool,
11185 auto_scroll: Option<Autoscroll>,
11186 window: &mut Window,
11187 cx: &mut Context<Editor>,
11188 ) {
11189 this.unfold_ranges(&[range.clone()], false, true, cx);
11190 this.change_selections(auto_scroll, window, cx, |s| {
11191 if replace_newest {
11192 s.delete(s.newest_anchor().id);
11193 }
11194 s.insert_range(range.clone());
11195 });
11196 }
11197
11198 let buffer = &display_map.buffer_snapshot;
11199 let mut selections = self.selections.all::<usize>(cx);
11200 if let Some(mut select_next_state) = self.select_next_state.take() {
11201 let query = &select_next_state.query;
11202 if !select_next_state.done {
11203 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11204 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11205 let mut next_selected_range = None;
11206
11207 let bytes_after_last_selection =
11208 buffer.bytes_in_range(last_selection.end..buffer.len());
11209 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11210 let query_matches = query
11211 .stream_find_iter(bytes_after_last_selection)
11212 .map(|result| (last_selection.end, result))
11213 .chain(
11214 query
11215 .stream_find_iter(bytes_before_first_selection)
11216 .map(|result| (0, result)),
11217 );
11218
11219 for (start_offset, query_match) in query_matches {
11220 let query_match = query_match.unwrap(); // can only fail due to I/O
11221 let offset_range =
11222 start_offset + query_match.start()..start_offset + query_match.end();
11223 let display_range = offset_range.start.to_display_point(display_map)
11224 ..offset_range.end.to_display_point(display_map);
11225
11226 if !select_next_state.wordwise
11227 || (!movement::is_inside_word(display_map, display_range.start)
11228 && !movement::is_inside_word(display_map, display_range.end))
11229 {
11230 // TODO: This is n^2, because we might check all the selections
11231 if !selections
11232 .iter()
11233 .any(|selection| selection.range().overlaps(&offset_range))
11234 {
11235 next_selected_range = Some(offset_range);
11236 break;
11237 }
11238 }
11239 }
11240
11241 if let Some(next_selected_range) = next_selected_range {
11242 select_next_match_ranges(
11243 self,
11244 next_selected_range,
11245 replace_newest,
11246 autoscroll,
11247 window,
11248 cx,
11249 );
11250 } else {
11251 select_next_state.done = true;
11252 }
11253 }
11254
11255 self.select_next_state = Some(select_next_state);
11256 } else {
11257 let mut only_carets = true;
11258 let mut same_text_selected = true;
11259 let mut selected_text = None;
11260
11261 let mut selections_iter = selections.iter().peekable();
11262 while let Some(selection) = selections_iter.next() {
11263 if selection.start != selection.end {
11264 only_carets = false;
11265 }
11266
11267 if same_text_selected {
11268 if selected_text.is_none() {
11269 selected_text =
11270 Some(buffer.text_for_range(selection.range()).collect::<String>());
11271 }
11272
11273 if let Some(next_selection) = selections_iter.peek() {
11274 if next_selection.range().len() == selection.range().len() {
11275 let next_selected_text = buffer
11276 .text_for_range(next_selection.range())
11277 .collect::<String>();
11278 if Some(next_selected_text) != selected_text {
11279 same_text_selected = false;
11280 selected_text = None;
11281 }
11282 } else {
11283 same_text_selected = false;
11284 selected_text = None;
11285 }
11286 }
11287 }
11288 }
11289
11290 if only_carets {
11291 for selection in &mut selections {
11292 let word_range = movement::surrounding_word(
11293 display_map,
11294 selection.start.to_display_point(display_map),
11295 );
11296 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11297 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11298 selection.goal = SelectionGoal::None;
11299 selection.reversed = false;
11300 select_next_match_ranges(
11301 self,
11302 selection.start..selection.end,
11303 replace_newest,
11304 autoscroll,
11305 window,
11306 cx,
11307 );
11308 }
11309
11310 if selections.len() == 1 {
11311 let selection = selections
11312 .last()
11313 .expect("ensured that there's only one selection");
11314 let query = buffer
11315 .text_for_range(selection.start..selection.end)
11316 .collect::<String>();
11317 let is_empty = query.is_empty();
11318 let select_state = SelectNextState {
11319 query: AhoCorasick::new(&[query])?,
11320 wordwise: true,
11321 done: is_empty,
11322 };
11323 self.select_next_state = Some(select_state);
11324 } else {
11325 self.select_next_state = None;
11326 }
11327 } else if let Some(selected_text) = selected_text {
11328 self.select_next_state = Some(SelectNextState {
11329 query: AhoCorasick::new(&[selected_text])?,
11330 wordwise: false,
11331 done: false,
11332 });
11333 self.select_next_match_internal(
11334 display_map,
11335 replace_newest,
11336 autoscroll,
11337 window,
11338 cx,
11339 )?;
11340 }
11341 }
11342 Ok(())
11343 }
11344
11345 pub fn select_all_matches(
11346 &mut self,
11347 _action: &SelectAllMatches,
11348 window: &mut Window,
11349 cx: &mut Context<Self>,
11350 ) -> Result<()> {
11351 self.push_to_selection_history();
11352 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11353
11354 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11355 let Some(select_next_state) = self.select_next_state.as_mut() else {
11356 return Ok(());
11357 };
11358 if select_next_state.done {
11359 return Ok(());
11360 }
11361
11362 let mut new_selections = self.selections.all::<usize>(cx);
11363
11364 let buffer = &display_map.buffer_snapshot;
11365 let query_matches = select_next_state
11366 .query
11367 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11368
11369 for query_match in query_matches {
11370 let query_match = query_match.unwrap(); // can only fail due to I/O
11371 let offset_range = query_match.start()..query_match.end();
11372 let display_range = offset_range.start.to_display_point(&display_map)
11373 ..offset_range.end.to_display_point(&display_map);
11374
11375 if !select_next_state.wordwise
11376 || (!movement::is_inside_word(&display_map, display_range.start)
11377 && !movement::is_inside_word(&display_map, display_range.end))
11378 {
11379 self.selections.change_with(cx, |selections| {
11380 new_selections.push(Selection {
11381 id: selections.new_selection_id(),
11382 start: offset_range.start,
11383 end: offset_range.end,
11384 reversed: false,
11385 goal: SelectionGoal::None,
11386 });
11387 });
11388 }
11389 }
11390
11391 new_selections.sort_by_key(|selection| selection.start);
11392 let mut ix = 0;
11393 while ix + 1 < new_selections.len() {
11394 let current_selection = &new_selections[ix];
11395 let next_selection = &new_selections[ix + 1];
11396 if current_selection.range().overlaps(&next_selection.range()) {
11397 if current_selection.id < next_selection.id {
11398 new_selections.remove(ix + 1);
11399 } else {
11400 new_selections.remove(ix);
11401 }
11402 } else {
11403 ix += 1;
11404 }
11405 }
11406
11407 let reversed = self.selections.oldest::<usize>(cx).reversed;
11408
11409 for selection in new_selections.iter_mut() {
11410 selection.reversed = reversed;
11411 }
11412
11413 select_next_state.done = true;
11414 self.unfold_ranges(
11415 &new_selections
11416 .iter()
11417 .map(|selection| selection.range())
11418 .collect::<Vec<_>>(),
11419 false,
11420 false,
11421 cx,
11422 );
11423 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11424 selections.select(new_selections)
11425 });
11426
11427 Ok(())
11428 }
11429
11430 pub fn select_next(
11431 &mut self,
11432 action: &SelectNext,
11433 window: &mut Window,
11434 cx: &mut Context<Self>,
11435 ) -> Result<()> {
11436 self.push_to_selection_history();
11437 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11438 self.select_next_match_internal(
11439 &display_map,
11440 action.replace_newest,
11441 Some(Autoscroll::newest()),
11442 window,
11443 cx,
11444 )?;
11445 Ok(())
11446 }
11447
11448 pub fn select_previous(
11449 &mut self,
11450 action: &SelectPrevious,
11451 window: &mut Window,
11452 cx: &mut Context<Self>,
11453 ) -> Result<()> {
11454 self.push_to_selection_history();
11455 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11456 let buffer = &display_map.buffer_snapshot;
11457 let mut selections = self.selections.all::<usize>(cx);
11458 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11459 let query = &select_prev_state.query;
11460 if !select_prev_state.done {
11461 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11462 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11463 let mut next_selected_range = None;
11464 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11465 let bytes_before_last_selection =
11466 buffer.reversed_bytes_in_range(0..last_selection.start);
11467 let bytes_after_first_selection =
11468 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11469 let query_matches = query
11470 .stream_find_iter(bytes_before_last_selection)
11471 .map(|result| (last_selection.start, result))
11472 .chain(
11473 query
11474 .stream_find_iter(bytes_after_first_selection)
11475 .map(|result| (buffer.len(), result)),
11476 );
11477 for (end_offset, query_match) in query_matches {
11478 let query_match = query_match.unwrap(); // can only fail due to I/O
11479 let offset_range =
11480 end_offset - query_match.end()..end_offset - query_match.start();
11481 let display_range = offset_range.start.to_display_point(&display_map)
11482 ..offset_range.end.to_display_point(&display_map);
11483
11484 if !select_prev_state.wordwise
11485 || (!movement::is_inside_word(&display_map, display_range.start)
11486 && !movement::is_inside_word(&display_map, display_range.end))
11487 {
11488 next_selected_range = Some(offset_range);
11489 break;
11490 }
11491 }
11492
11493 if let Some(next_selected_range) = next_selected_range {
11494 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11495 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11496 if action.replace_newest {
11497 s.delete(s.newest_anchor().id);
11498 }
11499 s.insert_range(next_selected_range);
11500 });
11501 } else {
11502 select_prev_state.done = true;
11503 }
11504 }
11505
11506 self.select_prev_state = Some(select_prev_state);
11507 } else {
11508 let mut only_carets = true;
11509 let mut same_text_selected = true;
11510 let mut selected_text = None;
11511
11512 let mut selections_iter = selections.iter().peekable();
11513 while let Some(selection) = selections_iter.next() {
11514 if selection.start != selection.end {
11515 only_carets = false;
11516 }
11517
11518 if same_text_selected {
11519 if selected_text.is_none() {
11520 selected_text =
11521 Some(buffer.text_for_range(selection.range()).collect::<String>());
11522 }
11523
11524 if let Some(next_selection) = selections_iter.peek() {
11525 if next_selection.range().len() == selection.range().len() {
11526 let next_selected_text = buffer
11527 .text_for_range(next_selection.range())
11528 .collect::<String>();
11529 if Some(next_selected_text) != selected_text {
11530 same_text_selected = false;
11531 selected_text = None;
11532 }
11533 } else {
11534 same_text_selected = false;
11535 selected_text = None;
11536 }
11537 }
11538 }
11539 }
11540
11541 if only_carets {
11542 for selection in &mut selections {
11543 let word_range = movement::surrounding_word(
11544 &display_map,
11545 selection.start.to_display_point(&display_map),
11546 );
11547 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11548 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11549 selection.goal = SelectionGoal::None;
11550 selection.reversed = false;
11551 }
11552 if selections.len() == 1 {
11553 let selection = selections
11554 .last()
11555 .expect("ensured that there's only one selection");
11556 let query = buffer
11557 .text_for_range(selection.start..selection.end)
11558 .collect::<String>();
11559 let is_empty = query.is_empty();
11560 let select_state = SelectNextState {
11561 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11562 wordwise: true,
11563 done: is_empty,
11564 };
11565 self.select_prev_state = Some(select_state);
11566 } else {
11567 self.select_prev_state = None;
11568 }
11569
11570 self.unfold_ranges(
11571 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11572 false,
11573 true,
11574 cx,
11575 );
11576 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11577 s.select(selections);
11578 });
11579 } else if let Some(selected_text) = selected_text {
11580 self.select_prev_state = Some(SelectNextState {
11581 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11582 wordwise: false,
11583 done: false,
11584 });
11585 self.select_previous(action, window, cx)?;
11586 }
11587 }
11588 Ok(())
11589 }
11590
11591 pub fn toggle_comments(
11592 &mut self,
11593 action: &ToggleComments,
11594 window: &mut Window,
11595 cx: &mut Context<Self>,
11596 ) {
11597 if self.read_only(cx) {
11598 return;
11599 }
11600 let text_layout_details = &self.text_layout_details(window);
11601 self.transact(window, cx, |this, window, cx| {
11602 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11603 let mut edits = Vec::new();
11604 let mut selection_edit_ranges = Vec::new();
11605 let mut last_toggled_row = None;
11606 let snapshot = this.buffer.read(cx).read(cx);
11607 let empty_str: Arc<str> = Arc::default();
11608 let mut suffixes_inserted = Vec::new();
11609 let ignore_indent = action.ignore_indent;
11610
11611 fn comment_prefix_range(
11612 snapshot: &MultiBufferSnapshot,
11613 row: MultiBufferRow,
11614 comment_prefix: &str,
11615 comment_prefix_whitespace: &str,
11616 ignore_indent: bool,
11617 ) -> Range<Point> {
11618 let indent_size = if ignore_indent {
11619 0
11620 } else {
11621 snapshot.indent_size_for_line(row).len
11622 };
11623
11624 let start = Point::new(row.0, indent_size);
11625
11626 let mut line_bytes = snapshot
11627 .bytes_in_range(start..snapshot.max_point())
11628 .flatten()
11629 .copied();
11630
11631 // If this line currently begins with the line comment prefix, then record
11632 // the range containing the prefix.
11633 if line_bytes
11634 .by_ref()
11635 .take(comment_prefix.len())
11636 .eq(comment_prefix.bytes())
11637 {
11638 // Include any whitespace that matches the comment prefix.
11639 let matching_whitespace_len = line_bytes
11640 .zip(comment_prefix_whitespace.bytes())
11641 .take_while(|(a, b)| a == b)
11642 .count() as u32;
11643 let end = Point::new(
11644 start.row,
11645 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11646 );
11647 start..end
11648 } else {
11649 start..start
11650 }
11651 }
11652
11653 fn comment_suffix_range(
11654 snapshot: &MultiBufferSnapshot,
11655 row: MultiBufferRow,
11656 comment_suffix: &str,
11657 comment_suffix_has_leading_space: bool,
11658 ) -> Range<Point> {
11659 let end = Point::new(row.0, snapshot.line_len(row));
11660 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11661
11662 let mut line_end_bytes = snapshot
11663 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11664 .flatten()
11665 .copied();
11666
11667 let leading_space_len = if suffix_start_column > 0
11668 && line_end_bytes.next() == Some(b' ')
11669 && comment_suffix_has_leading_space
11670 {
11671 1
11672 } else {
11673 0
11674 };
11675
11676 // If this line currently begins with the line comment prefix, then record
11677 // the range containing the prefix.
11678 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11679 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11680 start..end
11681 } else {
11682 end..end
11683 }
11684 }
11685
11686 // TODO: Handle selections that cross excerpts
11687 for selection in &mut selections {
11688 let start_column = snapshot
11689 .indent_size_for_line(MultiBufferRow(selection.start.row))
11690 .len;
11691 let language = if let Some(language) =
11692 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11693 {
11694 language
11695 } else {
11696 continue;
11697 };
11698
11699 selection_edit_ranges.clear();
11700
11701 // If multiple selections contain a given row, avoid processing that
11702 // row more than once.
11703 let mut start_row = MultiBufferRow(selection.start.row);
11704 if last_toggled_row == Some(start_row) {
11705 start_row = start_row.next_row();
11706 }
11707 let end_row =
11708 if selection.end.row > selection.start.row && selection.end.column == 0 {
11709 MultiBufferRow(selection.end.row - 1)
11710 } else {
11711 MultiBufferRow(selection.end.row)
11712 };
11713 last_toggled_row = Some(end_row);
11714
11715 if start_row > end_row {
11716 continue;
11717 }
11718
11719 // If the language has line comments, toggle those.
11720 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11721
11722 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11723 if ignore_indent {
11724 full_comment_prefixes = full_comment_prefixes
11725 .into_iter()
11726 .map(|s| Arc::from(s.trim_end()))
11727 .collect();
11728 }
11729
11730 if !full_comment_prefixes.is_empty() {
11731 let first_prefix = full_comment_prefixes
11732 .first()
11733 .expect("prefixes is non-empty");
11734 let prefix_trimmed_lengths = full_comment_prefixes
11735 .iter()
11736 .map(|p| p.trim_end_matches(' ').len())
11737 .collect::<SmallVec<[usize; 4]>>();
11738
11739 let mut all_selection_lines_are_comments = true;
11740
11741 for row in start_row.0..=end_row.0 {
11742 let row = MultiBufferRow(row);
11743 if start_row < end_row && snapshot.is_line_blank(row) {
11744 continue;
11745 }
11746
11747 let prefix_range = full_comment_prefixes
11748 .iter()
11749 .zip(prefix_trimmed_lengths.iter().copied())
11750 .map(|(prefix, trimmed_prefix_len)| {
11751 comment_prefix_range(
11752 snapshot.deref(),
11753 row,
11754 &prefix[..trimmed_prefix_len],
11755 &prefix[trimmed_prefix_len..],
11756 ignore_indent,
11757 )
11758 })
11759 .max_by_key(|range| range.end.column - range.start.column)
11760 .expect("prefixes is non-empty");
11761
11762 if prefix_range.is_empty() {
11763 all_selection_lines_are_comments = false;
11764 }
11765
11766 selection_edit_ranges.push(prefix_range);
11767 }
11768
11769 if all_selection_lines_are_comments {
11770 edits.extend(
11771 selection_edit_ranges
11772 .iter()
11773 .cloned()
11774 .map(|range| (range, empty_str.clone())),
11775 );
11776 } else {
11777 let min_column = selection_edit_ranges
11778 .iter()
11779 .map(|range| range.start.column)
11780 .min()
11781 .unwrap_or(0);
11782 edits.extend(selection_edit_ranges.iter().map(|range| {
11783 let position = Point::new(range.start.row, min_column);
11784 (position..position, first_prefix.clone())
11785 }));
11786 }
11787 } else if let Some((full_comment_prefix, comment_suffix)) =
11788 language.block_comment_delimiters()
11789 {
11790 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11791 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11792 let prefix_range = comment_prefix_range(
11793 snapshot.deref(),
11794 start_row,
11795 comment_prefix,
11796 comment_prefix_whitespace,
11797 ignore_indent,
11798 );
11799 let suffix_range = comment_suffix_range(
11800 snapshot.deref(),
11801 end_row,
11802 comment_suffix.trim_start_matches(' '),
11803 comment_suffix.starts_with(' '),
11804 );
11805
11806 if prefix_range.is_empty() || suffix_range.is_empty() {
11807 edits.push((
11808 prefix_range.start..prefix_range.start,
11809 full_comment_prefix.clone(),
11810 ));
11811 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11812 suffixes_inserted.push((end_row, comment_suffix.len()));
11813 } else {
11814 edits.push((prefix_range, empty_str.clone()));
11815 edits.push((suffix_range, empty_str.clone()));
11816 }
11817 } else {
11818 continue;
11819 }
11820 }
11821
11822 drop(snapshot);
11823 this.buffer.update(cx, |buffer, cx| {
11824 buffer.edit(edits, None, cx);
11825 });
11826
11827 // Adjust selections so that they end before any comment suffixes that
11828 // were inserted.
11829 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11830 let mut selections = this.selections.all::<Point>(cx);
11831 let snapshot = this.buffer.read(cx).read(cx);
11832 for selection in &mut selections {
11833 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11834 match row.cmp(&MultiBufferRow(selection.end.row)) {
11835 Ordering::Less => {
11836 suffixes_inserted.next();
11837 continue;
11838 }
11839 Ordering::Greater => break,
11840 Ordering::Equal => {
11841 if selection.end.column == snapshot.line_len(row) {
11842 if selection.is_empty() {
11843 selection.start.column -= suffix_len as u32;
11844 }
11845 selection.end.column -= suffix_len as u32;
11846 }
11847 break;
11848 }
11849 }
11850 }
11851 }
11852
11853 drop(snapshot);
11854 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11855 s.select(selections)
11856 });
11857
11858 let selections = this.selections.all::<Point>(cx);
11859 let selections_on_single_row = selections.windows(2).all(|selections| {
11860 selections[0].start.row == selections[1].start.row
11861 && selections[0].end.row == selections[1].end.row
11862 && selections[0].start.row == selections[0].end.row
11863 });
11864 let selections_selecting = selections
11865 .iter()
11866 .any(|selection| selection.start != selection.end);
11867 let advance_downwards = action.advance_downwards
11868 && selections_on_single_row
11869 && !selections_selecting
11870 && !matches!(this.mode, EditorMode::SingleLine { .. });
11871
11872 if advance_downwards {
11873 let snapshot = this.buffer.read(cx).snapshot(cx);
11874
11875 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11876 s.move_cursors_with(|display_snapshot, display_point, _| {
11877 let mut point = display_point.to_point(display_snapshot);
11878 point.row += 1;
11879 point = snapshot.clip_point(point, Bias::Left);
11880 let display_point = point.to_display_point(display_snapshot);
11881 let goal = SelectionGoal::HorizontalPosition(
11882 display_snapshot
11883 .x_for_display_point(display_point, text_layout_details)
11884 .into(),
11885 );
11886 (display_point, goal)
11887 })
11888 });
11889 }
11890 });
11891 }
11892
11893 pub fn select_enclosing_symbol(
11894 &mut self,
11895 _: &SelectEnclosingSymbol,
11896 window: &mut Window,
11897 cx: &mut Context<Self>,
11898 ) {
11899 let buffer = self.buffer.read(cx).snapshot(cx);
11900 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11901
11902 fn update_selection(
11903 selection: &Selection<usize>,
11904 buffer_snap: &MultiBufferSnapshot,
11905 ) -> Option<Selection<usize>> {
11906 let cursor = selection.head();
11907 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11908 for symbol in symbols.iter().rev() {
11909 let start = symbol.range.start.to_offset(buffer_snap);
11910 let end = symbol.range.end.to_offset(buffer_snap);
11911 let new_range = start..end;
11912 if start < selection.start || end > selection.end {
11913 return Some(Selection {
11914 id: selection.id,
11915 start: new_range.start,
11916 end: new_range.end,
11917 goal: SelectionGoal::None,
11918 reversed: selection.reversed,
11919 });
11920 }
11921 }
11922 None
11923 }
11924
11925 let mut selected_larger_symbol = false;
11926 let new_selections = old_selections
11927 .iter()
11928 .map(|selection| match update_selection(selection, &buffer) {
11929 Some(new_selection) => {
11930 if new_selection.range() != selection.range() {
11931 selected_larger_symbol = true;
11932 }
11933 new_selection
11934 }
11935 None => selection.clone(),
11936 })
11937 .collect::<Vec<_>>();
11938
11939 if selected_larger_symbol {
11940 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11941 s.select(new_selections);
11942 });
11943 }
11944 }
11945
11946 pub fn select_larger_syntax_node(
11947 &mut self,
11948 _: &SelectLargerSyntaxNode,
11949 window: &mut Window,
11950 cx: &mut Context<Self>,
11951 ) {
11952 let Some(visible_row_count) = self.visible_row_count() else {
11953 return;
11954 };
11955 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
11956 if old_selections.is_empty() {
11957 return;
11958 }
11959
11960 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11961 let buffer = self.buffer.read(cx).snapshot(cx);
11962
11963 let mut selected_larger_node = false;
11964 let mut new_selections = old_selections
11965 .iter()
11966 .map(|selection| {
11967 let old_range = selection.start..selection.end;
11968 let mut new_range = old_range.clone();
11969 let mut new_node = None;
11970 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11971 {
11972 new_node = Some(node);
11973 new_range = match containing_range {
11974 MultiOrSingleBufferOffsetRange::Single(_) => break,
11975 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11976 };
11977 if !display_map.intersects_fold(new_range.start)
11978 && !display_map.intersects_fold(new_range.end)
11979 {
11980 break;
11981 }
11982 }
11983
11984 if let Some(node) = new_node {
11985 // Log the ancestor, to support using this action as a way to explore TreeSitter
11986 // nodes. Parent and grandparent are also logged because this operation will not
11987 // visit nodes that have the same range as their parent.
11988 log::info!("Node: {node:?}");
11989 let parent = node.parent();
11990 log::info!("Parent: {parent:?}");
11991 let grandparent = parent.and_then(|x| x.parent());
11992 log::info!("Grandparent: {grandparent:?}");
11993 }
11994
11995 selected_larger_node |= new_range != old_range;
11996 Selection {
11997 id: selection.id,
11998 start: new_range.start,
11999 end: new_range.end,
12000 goal: SelectionGoal::None,
12001 reversed: selection.reversed,
12002 }
12003 })
12004 .collect::<Vec<_>>();
12005
12006 if !selected_larger_node {
12007 return; // don't put this call in the history
12008 }
12009
12010 // scroll based on transformation done to the last selection created by the user
12011 let (last_old, last_new) = old_selections
12012 .last()
12013 .zip(new_selections.last().cloned())
12014 .expect("old_selections isn't empty");
12015
12016 // revert selection
12017 let is_selection_reversed = {
12018 let should_newest_selection_be_reversed = last_old.start != last_new.start;
12019 new_selections.last_mut().expect("checked above").reversed =
12020 should_newest_selection_be_reversed;
12021 should_newest_selection_be_reversed
12022 };
12023
12024 if selected_larger_node {
12025 self.select_syntax_node_history.disable_clearing = true;
12026 self.change_selections(None, window, cx, |s| {
12027 s.select(new_selections.clone());
12028 });
12029 self.select_syntax_node_history.disable_clearing = false;
12030 }
12031
12032 let start_row = last_new.start.to_display_point(&display_map).row().0;
12033 let end_row = last_new.end.to_display_point(&display_map).row().0;
12034 let selection_height = end_row - start_row + 1;
12035 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
12036
12037 // if fits on screen (considering margin), keep it in the middle, else, scroll to selection head
12038 let scroll_behavior = if visible_row_count >= selection_height + scroll_margin_rows * 2 {
12039 let middle_row = (end_row + start_row) / 2;
12040 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12041 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12042 SelectSyntaxNodeScrollBehavior::CenterSelection
12043 } else if is_selection_reversed {
12044 self.scroll_cursor_top(&Default::default(), window, cx);
12045 SelectSyntaxNodeScrollBehavior::CursorTop
12046 } else {
12047 self.scroll_cursor_bottom(&Default::default(), window, cx);
12048 SelectSyntaxNodeScrollBehavior::CursorBottom
12049 };
12050
12051 self.select_syntax_node_history.push((
12052 old_selections,
12053 scroll_behavior,
12054 is_selection_reversed,
12055 ));
12056 }
12057
12058 pub fn select_smaller_syntax_node(
12059 &mut self,
12060 _: &SelectSmallerSyntaxNode,
12061 window: &mut Window,
12062 cx: &mut Context<Self>,
12063 ) {
12064 let Some(visible_row_count) = self.visible_row_count() else {
12065 return;
12066 };
12067
12068 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
12069 self.select_syntax_node_history.pop()
12070 {
12071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12072
12073 if let Some(selection) = selections.last_mut() {
12074 selection.reversed = is_selection_reversed;
12075 }
12076
12077 self.select_syntax_node_history.disable_clearing = true;
12078 self.change_selections(None, window, cx, |s| {
12079 s.select(selections.to_vec());
12080 });
12081 self.select_syntax_node_history.disable_clearing = false;
12082
12083 let newest = self.selections.newest::<usize>(cx);
12084 let start_row = newest.start.to_display_point(&display_map).row().0;
12085 let end_row = newest.end.to_display_point(&display_map).row().0;
12086
12087 match scroll_behavior {
12088 SelectSyntaxNodeScrollBehavior::CursorTop => {
12089 self.scroll_cursor_top(&Default::default(), window, cx);
12090 }
12091 SelectSyntaxNodeScrollBehavior::CenterSelection => {
12092 let middle_row = (end_row + start_row) / 2;
12093 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12094 // centralize the selection, not the cursor
12095 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12096 }
12097 SelectSyntaxNodeScrollBehavior::CursorBottom => {
12098 self.scroll_cursor_bottom(&Default::default(), window, cx);
12099 }
12100 }
12101 }
12102 }
12103
12104 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
12105 if !EditorSettings::get_global(cx).gutter.runnables {
12106 self.clear_tasks();
12107 return Task::ready(());
12108 }
12109 let project = self.project.as_ref().map(Entity::downgrade);
12110 cx.spawn_in(window, async move |this, cx| {
12111 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
12112 let Some(project) = project.and_then(|p| p.upgrade()) else {
12113 return;
12114 };
12115 let Ok(display_snapshot) = this.update(cx, |this, cx| {
12116 this.display_map.update(cx, |map, cx| map.snapshot(cx))
12117 }) else {
12118 return;
12119 };
12120
12121 let hide_runnables = project
12122 .update(cx, |project, cx| {
12123 // Do not display any test indicators in non-dev server remote projects.
12124 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
12125 })
12126 .unwrap_or(true);
12127 if hide_runnables {
12128 return;
12129 }
12130 let new_rows =
12131 cx.background_spawn({
12132 let snapshot = display_snapshot.clone();
12133 async move {
12134 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
12135 }
12136 })
12137 .await;
12138
12139 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
12140 this.update(cx, |this, _| {
12141 this.clear_tasks();
12142 for (key, value) in rows {
12143 this.insert_tasks(key, value);
12144 }
12145 })
12146 .ok();
12147 })
12148 }
12149 fn fetch_runnable_ranges(
12150 snapshot: &DisplaySnapshot,
12151 range: Range<Anchor>,
12152 ) -> Vec<language::RunnableRange> {
12153 snapshot.buffer_snapshot.runnable_ranges(range).collect()
12154 }
12155
12156 fn runnable_rows(
12157 project: Entity<Project>,
12158 snapshot: DisplaySnapshot,
12159 runnable_ranges: Vec<RunnableRange>,
12160 mut cx: AsyncWindowContext,
12161 ) -> Vec<((BufferId, u32), RunnableTasks)> {
12162 runnable_ranges
12163 .into_iter()
12164 .filter_map(|mut runnable| {
12165 let tasks = cx
12166 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
12167 .ok()?;
12168 if tasks.is_empty() {
12169 return None;
12170 }
12171
12172 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
12173
12174 let row = snapshot
12175 .buffer_snapshot
12176 .buffer_line_for_row(MultiBufferRow(point.row))?
12177 .1
12178 .start
12179 .row;
12180
12181 let context_range =
12182 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
12183 Some((
12184 (runnable.buffer_id, row),
12185 RunnableTasks {
12186 templates: tasks,
12187 offset: snapshot
12188 .buffer_snapshot
12189 .anchor_before(runnable.run_range.start),
12190 context_range,
12191 column: point.column,
12192 extra_variables: runnable.extra_captures,
12193 },
12194 ))
12195 })
12196 .collect()
12197 }
12198
12199 fn templates_with_tags(
12200 project: &Entity<Project>,
12201 runnable: &mut Runnable,
12202 cx: &mut App,
12203 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
12204 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
12205 let (worktree_id, file) = project
12206 .buffer_for_id(runnable.buffer, cx)
12207 .and_then(|buffer| buffer.read(cx).file())
12208 .map(|file| (file.worktree_id(cx), file.clone()))
12209 .unzip();
12210
12211 (
12212 project.task_store().read(cx).task_inventory().cloned(),
12213 worktree_id,
12214 file,
12215 )
12216 });
12217
12218 let tags = mem::take(&mut runnable.tags);
12219 let mut tags: Vec<_> = tags
12220 .into_iter()
12221 .flat_map(|tag| {
12222 let tag = tag.0.clone();
12223 inventory
12224 .as_ref()
12225 .into_iter()
12226 .flat_map(|inventory| {
12227 inventory.read(cx).list_tasks(
12228 file.clone(),
12229 Some(runnable.language.clone()),
12230 worktree_id,
12231 cx,
12232 )
12233 })
12234 .filter(move |(_, template)| {
12235 template.tags.iter().any(|source_tag| source_tag == &tag)
12236 })
12237 })
12238 .sorted_by_key(|(kind, _)| kind.to_owned())
12239 .collect();
12240 if let Some((leading_tag_source, _)) = tags.first() {
12241 // Strongest source wins; if we have worktree tag binding, prefer that to
12242 // global and language bindings;
12243 // if we have a global binding, prefer that to language binding.
12244 let first_mismatch = tags
12245 .iter()
12246 .position(|(tag_source, _)| tag_source != leading_tag_source);
12247 if let Some(index) = first_mismatch {
12248 tags.truncate(index);
12249 }
12250 }
12251
12252 tags
12253 }
12254
12255 pub fn move_to_enclosing_bracket(
12256 &mut self,
12257 _: &MoveToEnclosingBracket,
12258 window: &mut Window,
12259 cx: &mut Context<Self>,
12260 ) {
12261 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12262 s.move_offsets_with(|snapshot, selection| {
12263 let Some(enclosing_bracket_ranges) =
12264 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12265 else {
12266 return;
12267 };
12268
12269 let mut best_length = usize::MAX;
12270 let mut best_inside = false;
12271 let mut best_in_bracket_range = false;
12272 let mut best_destination = None;
12273 for (open, close) in enclosing_bracket_ranges {
12274 let close = close.to_inclusive();
12275 let length = close.end() - open.start;
12276 let inside = selection.start >= open.end && selection.end <= *close.start();
12277 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12278 || close.contains(&selection.head());
12279
12280 // If best is next to a bracket and current isn't, skip
12281 if !in_bracket_range && best_in_bracket_range {
12282 continue;
12283 }
12284
12285 // Prefer smaller lengths unless best is inside and current isn't
12286 if length > best_length && (best_inside || !inside) {
12287 continue;
12288 }
12289
12290 best_length = length;
12291 best_inside = inside;
12292 best_in_bracket_range = in_bracket_range;
12293 best_destination = Some(
12294 if close.contains(&selection.start) && close.contains(&selection.end) {
12295 if inside {
12296 open.end
12297 } else {
12298 open.start
12299 }
12300 } else if inside {
12301 *close.start()
12302 } else {
12303 *close.end()
12304 },
12305 );
12306 }
12307
12308 if let Some(destination) = best_destination {
12309 selection.collapse_to(destination, SelectionGoal::None);
12310 }
12311 })
12312 });
12313 }
12314
12315 pub fn undo_selection(
12316 &mut self,
12317 _: &UndoSelection,
12318 window: &mut Window,
12319 cx: &mut Context<Self>,
12320 ) {
12321 self.end_selection(window, cx);
12322 self.selection_history.mode = SelectionHistoryMode::Undoing;
12323 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12324 self.change_selections(None, window, cx, |s| {
12325 s.select_anchors(entry.selections.to_vec())
12326 });
12327 self.select_next_state = entry.select_next_state;
12328 self.select_prev_state = entry.select_prev_state;
12329 self.add_selections_state = entry.add_selections_state;
12330 self.request_autoscroll(Autoscroll::newest(), cx);
12331 }
12332 self.selection_history.mode = SelectionHistoryMode::Normal;
12333 }
12334
12335 pub fn redo_selection(
12336 &mut self,
12337 _: &RedoSelection,
12338 window: &mut Window,
12339 cx: &mut Context<Self>,
12340 ) {
12341 self.end_selection(window, cx);
12342 self.selection_history.mode = SelectionHistoryMode::Redoing;
12343 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12344 self.change_selections(None, window, cx, |s| {
12345 s.select_anchors(entry.selections.to_vec())
12346 });
12347 self.select_next_state = entry.select_next_state;
12348 self.select_prev_state = entry.select_prev_state;
12349 self.add_selections_state = entry.add_selections_state;
12350 self.request_autoscroll(Autoscroll::newest(), cx);
12351 }
12352 self.selection_history.mode = SelectionHistoryMode::Normal;
12353 }
12354
12355 pub fn expand_excerpts(
12356 &mut self,
12357 action: &ExpandExcerpts,
12358 _: &mut Window,
12359 cx: &mut Context<Self>,
12360 ) {
12361 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12362 }
12363
12364 pub fn expand_excerpts_down(
12365 &mut self,
12366 action: &ExpandExcerptsDown,
12367 _: &mut Window,
12368 cx: &mut Context<Self>,
12369 ) {
12370 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12371 }
12372
12373 pub fn expand_excerpts_up(
12374 &mut self,
12375 action: &ExpandExcerptsUp,
12376 _: &mut Window,
12377 cx: &mut Context<Self>,
12378 ) {
12379 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12380 }
12381
12382 pub fn expand_excerpts_for_direction(
12383 &mut self,
12384 lines: u32,
12385 direction: ExpandExcerptDirection,
12386
12387 cx: &mut Context<Self>,
12388 ) {
12389 let selections = self.selections.disjoint_anchors();
12390
12391 let lines = if lines == 0 {
12392 EditorSettings::get_global(cx).expand_excerpt_lines
12393 } else {
12394 lines
12395 };
12396
12397 self.buffer.update(cx, |buffer, cx| {
12398 let snapshot = buffer.snapshot(cx);
12399 let mut excerpt_ids = selections
12400 .iter()
12401 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12402 .collect::<Vec<_>>();
12403 excerpt_ids.sort();
12404 excerpt_ids.dedup();
12405 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12406 })
12407 }
12408
12409 pub fn expand_excerpt(
12410 &mut self,
12411 excerpt: ExcerptId,
12412 direction: ExpandExcerptDirection,
12413 window: &mut Window,
12414 cx: &mut Context<Self>,
12415 ) {
12416 let current_scroll_position = self.scroll_position(cx);
12417 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12418 self.buffer.update(cx, |buffer, cx| {
12419 buffer.expand_excerpts([excerpt], lines, direction, cx)
12420 });
12421 if direction == ExpandExcerptDirection::Down {
12422 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12423 self.set_scroll_position(new_scroll_position, window, cx);
12424 }
12425 }
12426
12427 pub fn go_to_singleton_buffer_point(
12428 &mut self,
12429 point: Point,
12430 window: &mut Window,
12431 cx: &mut Context<Self>,
12432 ) {
12433 self.go_to_singleton_buffer_range(point..point, window, cx);
12434 }
12435
12436 pub fn go_to_singleton_buffer_range(
12437 &mut self,
12438 range: Range<Point>,
12439 window: &mut Window,
12440 cx: &mut Context<Self>,
12441 ) {
12442 let multibuffer = self.buffer().read(cx);
12443 let Some(buffer) = multibuffer.as_singleton() else {
12444 return;
12445 };
12446 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12447 return;
12448 };
12449 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12450 return;
12451 };
12452 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12453 s.select_anchor_ranges([start..end])
12454 });
12455 }
12456
12457 fn go_to_diagnostic(
12458 &mut self,
12459 _: &GoToDiagnostic,
12460 window: &mut Window,
12461 cx: &mut Context<Self>,
12462 ) {
12463 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12464 }
12465
12466 fn go_to_prev_diagnostic(
12467 &mut self,
12468 _: &GoToPreviousDiagnostic,
12469 window: &mut Window,
12470 cx: &mut Context<Self>,
12471 ) {
12472 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12473 }
12474
12475 pub fn go_to_diagnostic_impl(
12476 &mut self,
12477 direction: Direction,
12478 window: &mut Window,
12479 cx: &mut Context<Self>,
12480 ) {
12481 let buffer = self.buffer.read(cx).snapshot(cx);
12482 let selection = self.selections.newest::<usize>(cx);
12483
12484 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12485 if direction == Direction::Next {
12486 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12487 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12488 return;
12489 };
12490 self.activate_diagnostics(
12491 buffer_id,
12492 popover.local_diagnostic.diagnostic.group_id,
12493 window,
12494 cx,
12495 );
12496 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12497 let primary_range_start = active_diagnostics.primary_range.start;
12498 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12499 let mut new_selection = s.newest_anchor().clone();
12500 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12501 s.select_anchors(vec![new_selection.clone()]);
12502 });
12503 self.refresh_inline_completion(false, true, window, cx);
12504 }
12505 return;
12506 }
12507 }
12508
12509 let active_group_id = self
12510 .active_diagnostics
12511 .as_ref()
12512 .map(|active_group| active_group.group_id);
12513 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12514 active_diagnostics
12515 .primary_range
12516 .to_offset(&buffer)
12517 .to_inclusive()
12518 });
12519 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12520 if active_primary_range.contains(&selection.head()) {
12521 *active_primary_range.start()
12522 } else {
12523 selection.head()
12524 }
12525 } else {
12526 selection.head()
12527 };
12528
12529 let snapshot = self.snapshot(window, cx);
12530 let primary_diagnostics_before = buffer
12531 .diagnostics_in_range::<usize>(0..search_start)
12532 .filter(|entry| entry.diagnostic.is_primary)
12533 .filter(|entry| entry.range.start != entry.range.end)
12534 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12535 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12536 .collect::<Vec<_>>();
12537 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12538 primary_diagnostics_before
12539 .iter()
12540 .position(|entry| entry.diagnostic.group_id == active_group_id)
12541 });
12542
12543 let primary_diagnostics_after = buffer
12544 .diagnostics_in_range::<usize>(search_start..buffer.len())
12545 .filter(|entry| entry.diagnostic.is_primary)
12546 .filter(|entry| entry.range.start != entry.range.end)
12547 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12548 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12549 .collect::<Vec<_>>();
12550 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12551 primary_diagnostics_after
12552 .iter()
12553 .enumerate()
12554 .rev()
12555 .find_map(|(i, entry)| {
12556 if entry.diagnostic.group_id == active_group_id {
12557 Some(i)
12558 } else {
12559 None
12560 }
12561 })
12562 });
12563
12564 let next_primary_diagnostic = match direction {
12565 Direction::Prev => primary_diagnostics_before
12566 .iter()
12567 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12568 .rev()
12569 .next(),
12570 Direction::Next => primary_diagnostics_after
12571 .iter()
12572 .skip(
12573 last_same_group_diagnostic_after
12574 .map(|index| index + 1)
12575 .unwrap_or(0),
12576 )
12577 .next(),
12578 };
12579
12580 // Cycle around to the start of the buffer, potentially moving back to the start of
12581 // the currently active diagnostic.
12582 let cycle_around = || match direction {
12583 Direction::Prev => primary_diagnostics_after
12584 .iter()
12585 .rev()
12586 .chain(primary_diagnostics_before.iter().rev())
12587 .next(),
12588 Direction::Next => primary_diagnostics_before
12589 .iter()
12590 .chain(primary_diagnostics_after.iter())
12591 .next(),
12592 };
12593
12594 if let Some((primary_range, group_id)) = next_primary_diagnostic
12595 .or_else(cycle_around)
12596 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12597 {
12598 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12599 return;
12600 };
12601 self.activate_diagnostics(buffer_id, group_id, window, cx);
12602 if self.active_diagnostics.is_some() {
12603 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12604 s.select(vec![Selection {
12605 id: selection.id,
12606 start: primary_range.start,
12607 end: primary_range.start,
12608 reversed: false,
12609 goal: SelectionGoal::None,
12610 }]);
12611 });
12612 self.refresh_inline_completion(false, true, window, cx);
12613 }
12614 }
12615 }
12616
12617 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12618 let snapshot = self.snapshot(window, cx);
12619 let selection = self.selections.newest::<Point>(cx);
12620 self.go_to_hunk_before_or_after_position(
12621 &snapshot,
12622 selection.head(),
12623 Direction::Next,
12624 window,
12625 cx,
12626 );
12627 }
12628
12629 fn go_to_hunk_before_or_after_position(
12630 &mut self,
12631 snapshot: &EditorSnapshot,
12632 position: Point,
12633 direction: Direction,
12634 window: &mut Window,
12635 cx: &mut Context<Editor>,
12636 ) {
12637 let row = if direction == Direction::Next {
12638 self.hunk_after_position(snapshot, position)
12639 .map(|hunk| hunk.row_range.start)
12640 } else {
12641 self.hunk_before_position(snapshot, position)
12642 };
12643
12644 if let Some(row) = row {
12645 let destination = Point::new(row.0, 0);
12646 let autoscroll = Autoscroll::center();
12647
12648 self.unfold_ranges(&[destination..destination], false, false, cx);
12649 self.change_selections(Some(autoscroll), window, cx, |s| {
12650 s.select_ranges([destination..destination]);
12651 });
12652 }
12653 }
12654
12655 fn hunk_after_position(
12656 &mut self,
12657 snapshot: &EditorSnapshot,
12658 position: Point,
12659 ) -> Option<MultiBufferDiffHunk> {
12660 snapshot
12661 .buffer_snapshot
12662 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12663 .find(|hunk| hunk.row_range.start.0 > position.row)
12664 .or_else(|| {
12665 snapshot
12666 .buffer_snapshot
12667 .diff_hunks_in_range(Point::zero()..position)
12668 .find(|hunk| hunk.row_range.end.0 < position.row)
12669 })
12670 }
12671
12672 fn go_to_prev_hunk(
12673 &mut self,
12674 _: &GoToPreviousHunk,
12675 window: &mut Window,
12676 cx: &mut Context<Self>,
12677 ) {
12678 let snapshot = self.snapshot(window, cx);
12679 let selection = self.selections.newest::<Point>(cx);
12680 self.go_to_hunk_before_or_after_position(
12681 &snapshot,
12682 selection.head(),
12683 Direction::Prev,
12684 window,
12685 cx,
12686 );
12687 }
12688
12689 fn hunk_before_position(
12690 &mut self,
12691 snapshot: &EditorSnapshot,
12692 position: Point,
12693 ) -> Option<MultiBufferRow> {
12694 snapshot
12695 .buffer_snapshot
12696 .diff_hunk_before(position)
12697 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12698 }
12699
12700 fn go_to_line<T: 'static>(
12701 &mut self,
12702 position: Anchor,
12703 highlight_color: Option<Hsla>,
12704 window: &mut Window,
12705 cx: &mut Context<Self>,
12706 ) {
12707 let snapshot = self.snapshot(window, cx).display_snapshot;
12708 let position = position.to_point(&snapshot.buffer_snapshot);
12709 let start = snapshot
12710 .buffer_snapshot
12711 .clip_point(Point::new(position.row, 0), Bias::Left);
12712 let end = start + Point::new(1, 0);
12713 let start = snapshot.buffer_snapshot.anchor_before(start);
12714 let end = snapshot.buffer_snapshot.anchor_before(end);
12715
12716 self.highlight_rows::<T>(
12717 start..end,
12718 highlight_color
12719 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12720 false,
12721 cx,
12722 );
12723 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
12724 }
12725
12726 pub fn go_to_definition(
12727 &mut self,
12728 _: &GoToDefinition,
12729 window: &mut Window,
12730 cx: &mut Context<Self>,
12731 ) -> Task<Result<Navigated>> {
12732 let definition =
12733 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12734 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
12735 cx.spawn_in(window, async move |editor, cx| {
12736 if definition.await? == Navigated::Yes {
12737 return Ok(Navigated::Yes);
12738 }
12739 match fallback_strategy {
12740 GoToDefinitionFallback::None => Ok(Navigated::No),
12741 GoToDefinitionFallback::FindAllReferences => {
12742 match editor.update_in(cx, |editor, window, cx| {
12743 editor.find_all_references(&FindAllReferences, window, cx)
12744 })? {
12745 Some(references) => references.await,
12746 None => Ok(Navigated::No),
12747 }
12748 }
12749 }
12750 })
12751 }
12752
12753 pub fn go_to_declaration(
12754 &mut self,
12755 _: &GoToDeclaration,
12756 window: &mut Window,
12757 cx: &mut Context<Self>,
12758 ) -> Task<Result<Navigated>> {
12759 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12760 }
12761
12762 pub fn go_to_declaration_split(
12763 &mut self,
12764 _: &GoToDeclaration,
12765 window: &mut Window,
12766 cx: &mut Context<Self>,
12767 ) -> Task<Result<Navigated>> {
12768 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12769 }
12770
12771 pub fn go_to_implementation(
12772 &mut self,
12773 _: &GoToImplementation,
12774 window: &mut Window,
12775 cx: &mut Context<Self>,
12776 ) -> Task<Result<Navigated>> {
12777 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12778 }
12779
12780 pub fn go_to_implementation_split(
12781 &mut self,
12782 _: &GoToImplementationSplit,
12783 window: &mut Window,
12784 cx: &mut Context<Self>,
12785 ) -> Task<Result<Navigated>> {
12786 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12787 }
12788
12789 pub fn go_to_type_definition(
12790 &mut self,
12791 _: &GoToTypeDefinition,
12792 window: &mut Window,
12793 cx: &mut Context<Self>,
12794 ) -> Task<Result<Navigated>> {
12795 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12796 }
12797
12798 pub fn go_to_definition_split(
12799 &mut self,
12800 _: &GoToDefinitionSplit,
12801 window: &mut Window,
12802 cx: &mut Context<Self>,
12803 ) -> Task<Result<Navigated>> {
12804 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12805 }
12806
12807 pub fn go_to_type_definition_split(
12808 &mut self,
12809 _: &GoToTypeDefinitionSplit,
12810 window: &mut Window,
12811 cx: &mut Context<Self>,
12812 ) -> Task<Result<Navigated>> {
12813 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12814 }
12815
12816 fn go_to_definition_of_kind(
12817 &mut self,
12818 kind: GotoDefinitionKind,
12819 split: bool,
12820 window: &mut Window,
12821 cx: &mut Context<Self>,
12822 ) -> Task<Result<Navigated>> {
12823 let Some(provider) = self.semantics_provider.clone() else {
12824 return Task::ready(Ok(Navigated::No));
12825 };
12826 let head = self.selections.newest::<usize>(cx).head();
12827 let buffer = self.buffer.read(cx);
12828 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12829 text_anchor
12830 } else {
12831 return Task::ready(Ok(Navigated::No));
12832 };
12833
12834 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12835 return Task::ready(Ok(Navigated::No));
12836 };
12837
12838 cx.spawn_in(window, async move |editor, cx| {
12839 let definitions = definitions.await?;
12840 let navigated = editor
12841 .update_in(cx, |editor, window, cx| {
12842 editor.navigate_to_hover_links(
12843 Some(kind),
12844 definitions
12845 .into_iter()
12846 .filter(|location| {
12847 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12848 })
12849 .map(HoverLink::Text)
12850 .collect::<Vec<_>>(),
12851 split,
12852 window,
12853 cx,
12854 )
12855 })?
12856 .await?;
12857 anyhow::Ok(navigated)
12858 })
12859 }
12860
12861 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12862 let selection = self.selections.newest_anchor();
12863 let head = selection.head();
12864 let tail = selection.tail();
12865
12866 let Some((buffer, start_position)) =
12867 self.buffer.read(cx).text_anchor_for_position(head, cx)
12868 else {
12869 return;
12870 };
12871
12872 let end_position = if head != tail {
12873 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12874 return;
12875 };
12876 Some(pos)
12877 } else {
12878 None
12879 };
12880
12881 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12882 let url = if let Some(end_pos) = end_position {
12883 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12884 } else {
12885 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12886 };
12887
12888 if let Some(url) = url {
12889 editor.update(cx, |_, cx| {
12890 cx.open_url(&url);
12891 })
12892 } else {
12893 Ok(())
12894 }
12895 });
12896
12897 url_finder.detach();
12898 }
12899
12900 pub fn open_selected_filename(
12901 &mut self,
12902 _: &OpenSelectedFilename,
12903 window: &mut Window,
12904 cx: &mut Context<Self>,
12905 ) {
12906 let Some(workspace) = self.workspace() else {
12907 return;
12908 };
12909
12910 let position = self.selections.newest_anchor().head();
12911
12912 let Some((buffer, buffer_position)) =
12913 self.buffer.read(cx).text_anchor_for_position(position, cx)
12914 else {
12915 return;
12916 };
12917
12918 let project = self.project.clone();
12919
12920 cx.spawn_in(window, async move |_, cx| {
12921 let result = find_file(&buffer, project, buffer_position, cx).await;
12922
12923 if let Some((_, path)) = result {
12924 workspace
12925 .update_in(cx, |workspace, window, cx| {
12926 workspace.open_resolved_path(path, window, cx)
12927 })?
12928 .await?;
12929 }
12930 anyhow::Ok(())
12931 })
12932 .detach();
12933 }
12934
12935 pub(crate) fn navigate_to_hover_links(
12936 &mut self,
12937 kind: Option<GotoDefinitionKind>,
12938 mut definitions: Vec<HoverLink>,
12939 split: bool,
12940 window: &mut Window,
12941 cx: &mut Context<Editor>,
12942 ) -> Task<Result<Navigated>> {
12943 // If there is one definition, just open it directly
12944 if definitions.len() == 1 {
12945 let definition = definitions.pop().unwrap();
12946
12947 enum TargetTaskResult {
12948 Location(Option<Location>),
12949 AlreadyNavigated,
12950 }
12951
12952 let target_task = match definition {
12953 HoverLink::Text(link) => {
12954 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12955 }
12956 HoverLink::InlayHint(lsp_location, server_id) => {
12957 let computation =
12958 self.compute_target_location(lsp_location, server_id, window, cx);
12959 cx.background_spawn(async move {
12960 let location = computation.await?;
12961 Ok(TargetTaskResult::Location(location))
12962 })
12963 }
12964 HoverLink::Url(url) => {
12965 cx.open_url(&url);
12966 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12967 }
12968 HoverLink::File(path) => {
12969 if let Some(workspace) = self.workspace() {
12970 cx.spawn_in(window, async move |_, cx| {
12971 workspace
12972 .update_in(cx, |workspace, window, cx| {
12973 workspace.open_resolved_path(path, window, cx)
12974 })?
12975 .await
12976 .map(|_| TargetTaskResult::AlreadyNavigated)
12977 })
12978 } else {
12979 Task::ready(Ok(TargetTaskResult::Location(None)))
12980 }
12981 }
12982 };
12983 cx.spawn_in(window, async move |editor, cx| {
12984 let target = match target_task.await.context("target resolution task")? {
12985 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12986 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12987 TargetTaskResult::Location(Some(target)) => target,
12988 };
12989
12990 editor.update_in(cx, |editor, window, cx| {
12991 let Some(workspace) = editor.workspace() else {
12992 return Navigated::No;
12993 };
12994 let pane = workspace.read(cx).active_pane().clone();
12995
12996 let range = target.range.to_point(target.buffer.read(cx));
12997 let range = editor.range_for_match(&range);
12998 let range = collapse_multiline_range(range);
12999
13000 if !split
13001 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
13002 {
13003 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
13004 } else {
13005 window.defer(cx, move |window, cx| {
13006 let target_editor: Entity<Self> =
13007 workspace.update(cx, |workspace, cx| {
13008 let pane = if split {
13009 workspace.adjacent_pane(window, cx)
13010 } else {
13011 workspace.active_pane().clone()
13012 };
13013
13014 workspace.open_project_item(
13015 pane,
13016 target.buffer.clone(),
13017 true,
13018 true,
13019 window,
13020 cx,
13021 )
13022 });
13023 target_editor.update(cx, |target_editor, cx| {
13024 // When selecting a definition in a different buffer, disable the nav history
13025 // to avoid creating a history entry at the previous cursor location.
13026 pane.update(cx, |pane, _| pane.disable_history());
13027 target_editor.go_to_singleton_buffer_range(range, window, cx);
13028 pane.update(cx, |pane, _| pane.enable_history());
13029 });
13030 });
13031 }
13032 Navigated::Yes
13033 })
13034 })
13035 } else if !definitions.is_empty() {
13036 cx.spawn_in(window, async move |editor, cx| {
13037 let (title, location_tasks, workspace) = editor
13038 .update_in(cx, |editor, window, cx| {
13039 let tab_kind = match kind {
13040 Some(GotoDefinitionKind::Implementation) => "Implementations",
13041 _ => "Definitions",
13042 };
13043 let title = definitions
13044 .iter()
13045 .find_map(|definition| match definition {
13046 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
13047 let buffer = origin.buffer.read(cx);
13048 format!(
13049 "{} for {}",
13050 tab_kind,
13051 buffer
13052 .text_for_range(origin.range.clone())
13053 .collect::<String>()
13054 )
13055 }),
13056 HoverLink::InlayHint(_, _) => None,
13057 HoverLink::Url(_) => None,
13058 HoverLink::File(_) => None,
13059 })
13060 .unwrap_or(tab_kind.to_string());
13061 let location_tasks = definitions
13062 .into_iter()
13063 .map(|definition| match definition {
13064 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
13065 HoverLink::InlayHint(lsp_location, server_id) => editor
13066 .compute_target_location(lsp_location, server_id, window, cx),
13067 HoverLink::Url(_) => Task::ready(Ok(None)),
13068 HoverLink::File(_) => Task::ready(Ok(None)),
13069 })
13070 .collect::<Vec<_>>();
13071 (title, location_tasks, editor.workspace().clone())
13072 })
13073 .context("location tasks preparation")?;
13074
13075 let locations = future::join_all(location_tasks)
13076 .await
13077 .into_iter()
13078 .filter_map(|location| location.transpose())
13079 .collect::<Result<_>>()
13080 .context("location tasks")?;
13081
13082 let Some(workspace) = workspace else {
13083 return Ok(Navigated::No);
13084 };
13085 let opened = workspace
13086 .update_in(cx, |workspace, window, cx| {
13087 Self::open_locations_in_multibuffer(
13088 workspace,
13089 locations,
13090 title,
13091 split,
13092 MultibufferSelectionMode::First,
13093 window,
13094 cx,
13095 )
13096 })
13097 .ok();
13098
13099 anyhow::Ok(Navigated::from_bool(opened.is_some()))
13100 })
13101 } else {
13102 Task::ready(Ok(Navigated::No))
13103 }
13104 }
13105
13106 fn compute_target_location(
13107 &self,
13108 lsp_location: lsp::Location,
13109 server_id: LanguageServerId,
13110 window: &mut Window,
13111 cx: &mut Context<Self>,
13112 ) -> Task<anyhow::Result<Option<Location>>> {
13113 let Some(project) = self.project.clone() else {
13114 return Task::ready(Ok(None));
13115 };
13116
13117 cx.spawn_in(window, async move |editor, cx| {
13118 let location_task = editor.update(cx, |_, cx| {
13119 project.update(cx, |project, cx| {
13120 let language_server_name = project
13121 .language_server_statuses(cx)
13122 .find(|(id, _)| server_id == *id)
13123 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
13124 language_server_name.map(|language_server_name| {
13125 project.open_local_buffer_via_lsp(
13126 lsp_location.uri.clone(),
13127 server_id,
13128 language_server_name,
13129 cx,
13130 )
13131 })
13132 })
13133 })?;
13134 let location = match location_task {
13135 Some(task) => Some({
13136 let target_buffer_handle = task.await.context("open local buffer")?;
13137 let range = target_buffer_handle.update(cx, |target_buffer, _| {
13138 let target_start = target_buffer
13139 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
13140 let target_end = target_buffer
13141 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
13142 target_buffer.anchor_after(target_start)
13143 ..target_buffer.anchor_before(target_end)
13144 })?;
13145 Location {
13146 buffer: target_buffer_handle,
13147 range,
13148 }
13149 }),
13150 None => None,
13151 };
13152 Ok(location)
13153 })
13154 }
13155
13156 pub fn find_all_references(
13157 &mut self,
13158 _: &FindAllReferences,
13159 window: &mut Window,
13160 cx: &mut Context<Self>,
13161 ) -> Option<Task<Result<Navigated>>> {
13162 let selection = self.selections.newest::<usize>(cx);
13163 let multi_buffer = self.buffer.read(cx);
13164 let head = selection.head();
13165
13166 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13167 let head_anchor = multi_buffer_snapshot.anchor_at(
13168 head,
13169 if head < selection.tail() {
13170 Bias::Right
13171 } else {
13172 Bias::Left
13173 },
13174 );
13175
13176 match self
13177 .find_all_references_task_sources
13178 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13179 {
13180 Ok(_) => {
13181 log::info!(
13182 "Ignoring repeated FindAllReferences invocation with the position of already running task"
13183 );
13184 return None;
13185 }
13186 Err(i) => {
13187 self.find_all_references_task_sources.insert(i, head_anchor);
13188 }
13189 }
13190
13191 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
13192 let workspace = self.workspace()?;
13193 let project = workspace.read(cx).project().clone();
13194 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
13195 Some(cx.spawn_in(window, async move |editor, cx| {
13196 let _cleanup = cx.on_drop(&editor, move |editor, _| {
13197 if let Ok(i) = editor
13198 .find_all_references_task_sources
13199 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13200 {
13201 editor.find_all_references_task_sources.remove(i);
13202 }
13203 });
13204
13205 let locations = references.await?;
13206 if locations.is_empty() {
13207 return anyhow::Ok(Navigated::No);
13208 }
13209
13210 workspace.update_in(cx, |workspace, window, cx| {
13211 let title = locations
13212 .first()
13213 .as_ref()
13214 .map(|location| {
13215 let buffer = location.buffer.read(cx);
13216 format!(
13217 "References to `{}`",
13218 buffer
13219 .text_for_range(location.range.clone())
13220 .collect::<String>()
13221 )
13222 })
13223 .unwrap();
13224 Self::open_locations_in_multibuffer(
13225 workspace,
13226 locations,
13227 title,
13228 false,
13229 MultibufferSelectionMode::First,
13230 window,
13231 cx,
13232 );
13233 Navigated::Yes
13234 })
13235 }))
13236 }
13237
13238 /// Opens a multibuffer with the given project locations in it
13239 pub fn open_locations_in_multibuffer(
13240 workspace: &mut Workspace,
13241 mut locations: Vec<Location>,
13242 title: String,
13243 split: bool,
13244 multibuffer_selection_mode: MultibufferSelectionMode,
13245 window: &mut Window,
13246 cx: &mut Context<Workspace>,
13247 ) {
13248 // If there are multiple definitions, open them in a multibuffer
13249 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13250 let mut locations = locations.into_iter().peekable();
13251 let mut ranges = Vec::new();
13252 let capability = workspace.project().read(cx).capability();
13253
13254 let excerpt_buffer = cx.new(|cx| {
13255 let mut multibuffer = MultiBuffer::new(capability);
13256 while let Some(location) = locations.next() {
13257 let buffer = location.buffer.read(cx);
13258 let mut ranges_for_buffer = Vec::new();
13259 let range = location.range.to_offset(buffer);
13260 ranges_for_buffer.push(range.clone());
13261
13262 while let Some(next_location) = locations.peek() {
13263 if next_location.buffer == location.buffer {
13264 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13265 locations.next();
13266 } else {
13267 break;
13268 }
13269 }
13270
13271 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13272 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13273 location.buffer.clone(),
13274 ranges_for_buffer,
13275 DEFAULT_MULTIBUFFER_CONTEXT,
13276 cx,
13277 ))
13278 }
13279
13280 multibuffer.with_title(title)
13281 });
13282
13283 let editor = cx.new(|cx| {
13284 Editor::for_multibuffer(
13285 excerpt_buffer,
13286 Some(workspace.project().clone()),
13287 window,
13288 cx,
13289 )
13290 });
13291 editor.update(cx, |editor, cx| {
13292 match multibuffer_selection_mode {
13293 MultibufferSelectionMode::First => {
13294 if let Some(first_range) = ranges.first() {
13295 editor.change_selections(None, window, cx, |selections| {
13296 selections.clear_disjoint();
13297 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13298 });
13299 }
13300 editor.highlight_background::<Self>(
13301 &ranges,
13302 |theme| theme.editor_highlighted_line_background,
13303 cx,
13304 );
13305 }
13306 MultibufferSelectionMode::All => {
13307 editor.change_selections(None, window, cx, |selections| {
13308 selections.clear_disjoint();
13309 selections.select_anchor_ranges(ranges);
13310 });
13311 }
13312 }
13313 editor.register_buffers_with_language_servers(cx);
13314 });
13315
13316 let item = Box::new(editor);
13317 let item_id = item.item_id();
13318
13319 if split {
13320 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13321 } else {
13322 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13323 let (preview_item_id, preview_item_idx) =
13324 workspace.active_pane().update(cx, |pane, _| {
13325 (pane.preview_item_id(), pane.preview_item_idx())
13326 });
13327
13328 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13329
13330 if let Some(preview_item_id) = preview_item_id {
13331 workspace.active_pane().update(cx, |pane, cx| {
13332 pane.remove_item(preview_item_id, false, false, window, cx);
13333 });
13334 }
13335 } else {
13336 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13337 }
13338 }
13339 workspace.active_pane().update(cx, |pane, cx| {
13340 pane.set_preview_item_id(Some(item_id), cx);
13341 });
13342 }
13343
13344 pub fn rename(
13345 &mut self,
13346 _: &Rename,
13347 window: &mut Window,
13348 cx: &mut Context<Self>,
13349 ) -> Option<Task<Result<()>>> {
13350 use language::ToOffset as _;
13351
13352 let provider = self.semantics_provider.clone()?;
13353 let selection = self.selections.newest_anchor().clone();
13354 let (cursor_buffer, cursor_buffer_position) = self
13355 .buffer
13356 .read(cx)
13357 .text_anchor_for_position(selection.head(), cx)?;
13358 let (tail_buffer, cursor_buffer_position_end) = self
13359 .buffer
13360 .read(cx)
13361 .text_anchor_for_position(selection.tail(), cx)?;
13362 if tail_buffer != cursor_buffer {
13363 return None;
13364 }
13365
13366 let snapshot = cursor_buffer.read(cx).snapshot();
13367 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13368 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13369 let prepare_rename = provider
13370 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13371 .unwrap_or_else(|| Task::ready(Ok(None)));
13372 drop(snapshot);
13373
13374 Some(cx.spawn_in(window, async move |this, cx| {
13375 let rename_range = if let Some(range) = prepare_rename.await? {
13376 Some(range)
13377 } else {
13378 this.update(cx, |this, cx| {
13379 let buffer = this.buffer.read(cx).snapshot(cx);
13380 let mut buffer_highlights = this
13381 .document_highlights_for_position(selection.head(), &buffer)
13382 .filter(|highlight| {
13383 highlight.start.excerpt_id == selection.head().excerpt_id
13384 && highlight.end.excerpt_id == selection.head().excerpt_id
13385 });
13386 buffer_highlights
13387 .next()
13388 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13389 })?
13390 };
13391 if let Some(rename_range) = rename_range {
13392 this.update_in(cx, |this, window, cx| {
13393 let snapshot = cursor_buffer.read(cx).snapshot();
13394 let rename_buffer_range = rename_range.to_offset(&snapshot);
13395 let cursor_offset_in_rename_range =
13396 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13397 let cursor_offset_in_rename_range_end =
13398 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13399
13400 this.take_rename(false, window, cx);
13401 let buffer = this.buffer.read(cx).read(cx);
13402 let cursor_offset = selection.head().to_offset(&buffer);
13403 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13404 let rename_end = rename_start + rename_buffer_range.len();
13405 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13406 let mut old_highlight_id = None;
13407 let old_name: Arc<str> = buffer
13408 .chunks(rename_start..rename_end, true)
13409 .map(|chunk| {
13410 if old_highlight_id.is_none() {
13411 old_highlight_id = chunk.syntax_highlight_id;
13412 }
13413 chunk.text
13414 })
13415 .collect::<String>()
13416 .into();
13417
13418 drop(buffer);
13419
13420 // Position the selection in the rename editor so that it matches the current selection.
13421 this.show_local_selections = false;
13422 let rename_editor = cx.new(|cx| {
13423 let mut editor = Editor::single_line(window, cx);
13424 editor.buffer.update(cx, |buffer, cx| {
13425 buffer.edit([(0..0, old_name.clone())], None, cx)
13426 });
13427 let rename_selection_range = match cursor_offset_in_rename_range
13428 .cmp(&cursor_offset_in_rename_range_end)
13429 {
13430 Ordering::Equal => {
13431 editor.select_all(&SelectAll, window, cx);
13432 return editor;
13433 }
13434 Ordering::Less => {
13435 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13436 }
13437 Ordering::Greater => {
13438 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13439 }
13440 };
13441 if rename_selection_range.end > old_name.len() {
13442 editor.select_all(&SelectAll, window, cx);
13443 } else {
13444 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13445 s.select_ranges([rename_selection_range]);
13446 });
13447 }
13448 editor
13449 });
13450 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13451 if e == &EditorEvent::Focused {
13452 cx.emit(EditorEvent::FocusedIn)
13453 }
13454 })
13455 .detach();
13456
13457 let write_highlights =
13458 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13459 let read_highlights =
13460 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13461 let ranges = write_highlights
13462 .iter()
13463 .flat_map(|(_, ranges)| ranges.iter())
13464 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13465 .cloned()
13466 .collect();
13467
13468 this.highlight_text::<Rename>(
13469 ranges,
13470 HighlightStyle {
13471 fade_out: Some(0.6),
13472 ..Default::default()
13473 },
13474 cx,
13475 );
13476 let rename_focus_handle = rename_editor.focus_handle(cx);
13477 window.focus(&rename_focus_handle);
13478 let block_id = this.insert_blocks(
13479 [BlockProperties {
13480 style: BlockStyle::Flex,
13481 placement: BlockPlacement::Below(range.start),
13482 height: 1,
13483 render: Arc::new({
13484 let rename_editor = rename_editor.clone();
13485 move |cx: &mut BlockContext| {
13486 let mut text_style = cx.editor_style.text.clone();
13487 if let Some(highlight_style) = old_highlight_id
13488 .and_then(|h| h.style(&cx.editor_style.syntax))
13489 {
13490 text_style = text_style.highlight(highlight_style);
13491 }
13492 div()
13493 .block_mouse_down()
13494 .pl(cx.anchor_x)
13495 .child(EditorElement::new(
13496 &rename_editor,
13497 EditorStyle {
13498 background: cx.theme().system().transparent,
13499 local_player: cx.editor_style.local_player,
13500 text: text_style,
13501 scrollbar_width: cx.editor_style.scrollbar_width,
13502 syntax: cx.editor_style.syntax.clone(),
13503 status: cx.editor_style.status.clone(),
13504 inlay_hints_style: HighlightStyle {
13505 font_weight: Some(FontWeight::BOLD),
13506 ..make_inlay_hints_style(cx.app)
13507 },
13508 inline_completion_styles: make_suggestion_styles(
13509 cx.app,
13510 ),
13511 ..EditorStyle::default()
13512 },
13513 ))
13514 .into_any_element()
13515 }
13516 }),
13517 priority: 0,
13518 }],
13519 Some(Autoscroll::fit()),
13520 cx,
13521 )[0];
13522 this.pending_rename = Some(RenameState {
13523 range,
13524 old_name,
13525 editor: rename_editor,
13526 block_id,
13527 });
13528 })?;
13529 }
13530
13531 Ok(())
13532 }))
13533 }
13534
13535 pub fn confirm_rename(
13536 &mut self,
13537 _: &ConfirmRename,
13538 window: &mut Window,
13539 cx: &mut Context<Self>,
13540 ) -> Option<Task<Result<()>>> {
13541 let rename = self.take_rename(false, window, cx)?;
13542 let workspace = self.workspace()?.downgrade();
13543 let (buffer, start) = self
13544 .buffer
13545 .read(cx)
13546 .text_anchor_for_position(rename.range.start, cx)?;
13547 let (end_buffer, _) = self
13548 .buffer
13549 .read(cx)
13550 .text_anchor_for_position(rename.range.end, cx)?;
13551 if buffer != end_buffer {
13552 return None;
13553 }
13554
13555 let old_name = rename.old_name;
13556 let new_name = rename.editor.read(cx).text(cx);
13557
13558 let rename = self.semantics_provider.as_ref()?.perform_rename(
13559 &buffer,
13560 start,
13561 new_name.clone(),
13562 cx,
13563 )?;
13564
13565 Some(cx.spawn_in(window, async move |editor, cx| {
13566 let project_transaction = rename.await?;
13567 Self::open_project_transaction(
13568 &editor,
13569 workspace,
13570 project_transaction,
13571 format!("Rename: {} → {}", old_name, new_name),
13572 cx,
13573 )
13574 .await?;
13575
13576 editor.update(cx, |editor, cx| {
13577 editor.refresh_document_highlights(cx);
13578 })?;
13579 Ok(())
13580 }))
13581 }
13582
13583 fn take_rename(
13584 &mut self,
13585 moving_cursor: bool,
13586 window: &mut Window,
13587 cx: &mut Context<Self>,
13588 ) -> Option<RenameState> {
13589 let rename = self.pending_rename.take()?;
13590 if rename.editor.focus_handle(cx).is_focused(window) {
13591 window.focus(&self.focus_handle);
13592 }
13593
13594 self.remove_blocks(
13595 [rename.block_id].into_iter().collect(),
13596 Some(Autoscroll::fit()),
13597 cx,
13598 );
13599 self.clear_highlights::<Rename>(cx);
13600 self.show_local_selections = true;
13601
13602 if moving_cursor {
13603 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13604 editor.selections.newest::<usize>(cx).head()
13605 });
13606
13607 // Update the selection to match the position of the selection inside
13608 // the rename editor.
13609 let snapshot = self.buffer.read(cx).read(cx);
13610 let rename_range = rename.range.to_offset(&snapshot);
13611 let cursor_in_editor = snapshot
13612 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13613 .min(rename_range.end);
13614 drop(snapshot);
13615
13616 self.change_selections(None, window, cx, |s| {
13617 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13618 });
13619 } else {
13620 self.refresh_document_highlights(cx);
13621 }
13622
13623 Some(rename)
13624 }
13625
13626 pub fn pending_rename(&self) -> Option<&RenameState> {
13627 self.pending_rename.as_ref()
13628 }
13629
13630 fn format(
13631 &mut self,
13632 _: &Format,
13633 window: &mut Window,
13634 cx: &mut Context<Self>,
13635 ) -> Option<Task<Result<()>>> {
13636 let project = match &self.project {
13637 Some(project) => project.clone(),
13638 None => return None,
13639 };
13640
13641 Some(self.perform_format(
13642 project,
13643 FormatTrigger::Manual,
13644 FormatTarget::Buffers,
13645 window,
13646 cx,
13647 ))
13648 }
13649
13650 fn format_selections(
13651 &mut self,
13652 _: &FormatSelections,
13653 window: &mut Window,
13654 cx: &mut Context<Self>,
13655 ) -> Option<Task<Result<()>>> {
13656 let project = match &self.project {
13657 Some(project) => project.clone(),
13658 None => return None,
13659 };
13660
13661 let ranges = self
13662 .selections
13663 .all_adjusted(cx)
13664 .into_iter()
13665 .map(|selection| selection.range())
13666 .collect_vec();
13667
13668 Some(self.perform_format(
13669 project,
13670 FormatTrigger::Manual,
13671 FormatTarget::Ranges(ranges),
13672 window,
13673 cx,
13674 ))
13675 }
13676
13677 fn perform_format(
13678 &mut self,
13679 project: Entity<Project>,
13680 trigger: FormatTrigger,
13681 target: FormatTarget,
13682 window: &mut Window,
13683 cx: &mut Context<Self>,
13684 ) -> Task<Result<()>> {
13685 let buffer = self.buffer.clone();
13686 let (buffers, target) = match target {
13687 FormatTarget::Buffers => {
13688 let mut buffers = buffer.read(cx).all_buffers();
13689 if trigger == FormatTrigger::Save {
13690 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13691 }
13692 (buffers, LspFormatTarget::Buffers)
13693 }
13694 FormatTarget::Ranges(selection_ranges) => {
13695 let multi_buffer = buffer.read(cx);
13696 let snapshot = multi_buffer.read(cx);
13697 let mut buffers = HashSet::default();
13698 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13699 BTreeMap::new();
13700 for selection_range in selection_ranges {
13701 for (buffer, buffer_range, _) in
13702 snapshot.range_to_buffer_ranges(selection_range)
13703 {
13704 let buffer_id = buffer.remote_id();
13705 let start = buffer.anchor_before(buffer_range.start);
13706 let end = buffer.anchor_after(buffer_range.end);
13707 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13708 buffer_id_to_ranges
13709 .entry(buffer_id)
13710 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13711 .or_insert_with(|| vec![start..end]);
13712 }
13713 }
13714 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13715 }
13716 };
13717
13718 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13719 let format = project.update(cx, |project, cx| {
13720 project.format(buffers, target, true, trigger, cx)
13721 });
13722
13723 cx.spawn_in(window, async move |_, cx| {
13724 let transaction = futures::select_biased! {
13725 transaction = format.log_err().fuse() => transaction,
13726 () = timeout => {
13727 log::warn!("timed out waiting for formatting");
13728 None
13729 }
13730 };
13731
13732 buffer
13733 .update(cx, |buffer, cx| {
13734 if let Some(transaction) = transaction {
13735 if !buffer.is_singleton() {
13736 buffer.push_transaction(&transaction.0, cx);
13737 }
13738 }
13739 cx.notify();
13740 })
13741 .ok();
13742
13743 Ok(())
13744 })
13745 }
13746
13747 fn organize_imports(
13748 &mut self,
13749 _: &OrganizeImports,
13750 window: &mut Window,
13751 cx: &mut Context<Self>,
13752 ) -> Option<Task<Result<()>>> {
13753 let project = match &self.project {
13754 Some(project) => project.clone(),
13755 None => return None,
13756 };
13757 Some(self.perform_code_action_kind(
13758 project,
13759 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13760 window,
13761 cx,
13762 ))
13763 }
13764
13765 fn perform_code_action_kind(
13766 &mut self,
13767 project: Entity<Project>,
13768 kind: CodeActionKind,
13769 window: &mut Window,
13770 cx: &mut Context<Self>,
13771 ) -> Task<Result<()>> {
13772 let buffer = self.buffer.clone();
13773 let buffers = buffer.read(cx).all_buffers();
13774 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13775 let apply_action = project.update(cx, |project, cx| {
13776 project.apply_code_action_kind(buffers, kind, true, cx)
13777 });
13778 cx.spawn_in(window, async move |_, cx| {
13779 let transaction = futures::select_biased! {
13780 () = timeout => {
13781 log::warn!("timed out waiting for executing code action");
13782 None
13783 }
13784 transaction = apply_action.log_err().fuse() => transaction,
13785 };
13786 buffer
13787 .update(cx, |buffer, cx| {
13788 // check if we need this
13789 if let Some(transaction) = transaction {
13790 if !buffer.is_singleton() {
13791 buffer.push_transaction(&transaction.0, cx);
13792 }
13793 }
13794 cx.notify();
13795 })
13796 .ok();
13797 Ok(())
13798 })
13799 }
13800
13801 fn restart_language_server(
13802 &mut self,
13803 _: &RestartLanguageServer,
13804 _: &mut Window,
13805 cx: &mut Context<Self>,
13806 ) {
13807 if let Some(project) = self.project.clone() {
13808 self.buffer.update(cx, |multi_buffer, cx| {
13809 project.update(cx, |project, cx| {
13810 project.restart_language_servers_for_buffers(
13811 multi_buffer.all_buffers().into_iter().collect(),
13812 cx,
13813 );
13814 });
13815 })
13816 }
13817 }
13818
13819 fn cancel_language_server_work(
13820 workspace: &mut Workspace,
13821 _: &actions::CancelLanguageServerWork,
13822 _: &mut Window,
13823 cx: &mut Context<Workspace>,
13824 ) {
13825 let project = workspace.project();
13826 let buffers = workspace
13827 .active_item(cx)
13828 .and_then(|item| item.act_as::<Editor>(cx))
13829 .map_or(HashSet::default(), |editor| {
13830 editor.read(cx).buffer.read(cx).all_buffers()
13831 });
13832 project.update(cx, |project, cx| {
13833 project.cancel_language_server_work_for_buffers(buffers, cx);
13834 });
13835 }
13836
13837 fn show_character_palette(
13838 &mut self,
13839 _: &ShowCharacterPalette,
13840 window: &mut Window,
13841 _: &mut Context<Self>,
13842 ) {
13843 window.show_character_palette();
13844 }
13845
13846 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13847 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13848 let buffer = self.buffer.read(cx).snapshot(cx);
13849 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13850 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13851 let is_valid = buffer
13852 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13853 .any(|entry| {
13854 entry.diagnostic.is_primary
13855 && !entry.range.is_empty()
13856 && entry.range.start == primary_range_start
13857 && entry.diagnostic.message == active_diagnostics.primary_message
13858 });
13859
13860 if is_valid != active_diagnostics.is_valid {
13861 active_diagnostics.is_valid = is_valid;
13862 if is_valid {
13863 let mut new_styles = HashMap::default();
13864 for (block_id, diagnostic) in &active_diagnostics.blocks {
13865 new_styles.insert(
13866 *block_id,
13867 diagnostic_block_renderer(diagnostic.clone(), None, true),
13868 );
13869 }
13870 self.display_map.update(cx, |display_map, _cx| {
13871 display_map.replace_blocks(new_styles);
13872 });
13873 } else {
13874 self.dismiss_diagnostics(cx);
13875 }
13876 }
13877 }
13878 }
13879
13880 fn activate_diagnostics(
13881 &mut self,
13882 buffer_id: BufferId,
13883 group_id: usize,
13884 window: &mut Window,
13885 cx: &mut Context<Self>,
13886 ) {
13887 self.dismiss_diagnostics(cx);
13888 let snapshot = self.snapshot(window, cx);
13889 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13890 let buffer = self.buffer.read(cx).snapshot(cx);
13891
13892 let mut primary_range = None;
13893 let mut primary_message = None;
13894 let diagnostic_group = buffer
13895 .diagnostic_group(buffer_id, group_id)
13896 .filter_map(|entry| {
13897 let start = entry.range.start;
13898 let end = entry.range.end;
13899 if snapshot.is_line_folded(MultiBufferRow(start.row))
13900 && (start.row == end.row
13901 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13902 {
13903 return None;
13904 }
13905 if entry.diagnostic.is_primary {
13906 primary_range = Some(entry.range.clone());
13907 primary_message = Some(entry.diagnostic.message.clone());
13908 }
13909 Some(entry)
13910 })
13911 .collect::<Vec<_>>();
13912 let primary_range = primary_range?;
13913 let primary_message = primary_message?;
13914
13915 let blocks = display_map
13916 .insert_blocks(
13917 diagnostic_group.iter().map(|entry| {
13918 let diagnostic = entry.diagnostic.clone();
13919 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13920 BlockProperties {
13921 style: BlockStyle::Fixed,
13922 placement: BlockPlacement::Below(
13923 buffer.anchor_after(entry.range.start),
13924 ),
13925 height: message_height,
13926 render: diagnostic_block_renderer(diagnostic, None, true),
13927 priority: 0,
13928 }
13929 }),
13930 cx,
13931 )
13932 .into_iter()
13933 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13934 .collect();
13935
13936 Some(ActiveDiagnosticGroup {
13937 primary_range: buffer.anchor_before(primary_range.start)
13938 ..buffer.anchor_after(primary_range.end),
13939 primary_message,
13940 group_id,
13941 blocks,
13942 is_valid: true,
13943 })
13944 });
13945 }
13946
13947 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13948 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13949 self.display_map.update(cx, |display_map, cx| {
13950 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13951 });
13952 cx.notify();
13953 }
13954 }
13955
13956 /// Disable inline diagnostics rendering for this editor.
13957 pub fn disable_inline_diagnostics(&mut self) {
13958 self.inline_diagnostics_enabled = false;
13959 self.inline_diagnostics_update = Task::ready(());
13960 self.inline_diagnostics.clear();
13961 }
13962
13963 pub fn inline_diagnostics_enabled(&self) -> bool {
13964 self.inline_diagnostics_enabled
13965 }
13966
13967 pub fn show_inline_diagnostics(&self) -> bool {
13968 self.show_inline_diagnostics
13969 }
13970
13971 pub fn toggle_inline_diagnostics(
13972 &mut self,
13973 _: &ToggleInlineDiagnostics,
13974 window: &mut Window,
13975 cx: &mut Context<'_, Editor>,
13976 ) {
13977 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13978 self.refresh_inline_diagnostics(false, window, cx);
13979 }
13980
13981 fn refresh_inline_diagnostics(
13982 &mut self,
13983 debounce: bool,
13984 window: &mut Window,
13985 cx: &mut Context<Self>,
13986 ) {
13987 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13988 self.inline_diagnostics_update = Task::ready(());
13989 self.inline_diagnostics.clear();
13990 return;
13991 }
13992
13993 let debounce_ms = ProjectSettings::get_global(cx)
13994 .diagnostics
13995 .inline
13996 .update_debounce_ms;
13997 let debounce = if debounce && debounce_ms > 0 {
13998 Some(Duration::from_millis(debounce_ms))
13999 } else {
14000 None
14001 };
14002 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
14003 if let Some(debounce) = debounce {
14004 cx.background_executor().timer(debounce).await;
14005 }
14006 let Some(snapshot) = editor
14007 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
14008 .ok()
14009 else {
14010 return;
14011 };
14012
14013 let new_inline_diagnostics = cx
14014 .background_spawn(async move {
14015 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
14016 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
14017 let message = diagnostic_entry
14018 .diagnostic
14019 .message
14020 .split_once('\n')
14021 .map(|(line, _)| line)
14022 .map(SharedString::new)
14023 .unwrap_or_else(|| {
14024 SharedString::from(diagnostic_entry.diagnostic.message)
14025 });
14026 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
14027 let (Ok(i) | Err(i)) = inline_diagnostics
14028 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
14029 inline_diagnostics.insert(
14030 i,
14031 (
14032 start_anchor,
14033 InlineDiagnostic {
14034 message,
14035 group_id: diagnostic_entry.diagnostic.group_id,
14036 start: diagnostic_entry.range.start.to_point(&snapshot),
14037 is_primary: diagnostic_entry.diagnostic.is_primary,
14038 severity: diagnostic_entry.diagnostic.severity,
14039 },
14040 ),
14041 );
14042 }
14043 inline_diagnostics
14044 })
14045 .await;
14046
14047 editor
14048 .update(cx, |editor, cx| {
14049 editor.inline_diagnostics = new_inline_diagnostics;
14050 cx.notify();
14051 })
14052 .ok();
14053 });
14054 }
14055
14056 pub fn set_selections_from_remote(
14057 &mut self,
14058 selections: Vec<Selection<Anchor>>,
14059 pending_selection: Option<Selection<Anchor>>,
14060 window: &mut Window,
14061 cx: &mut Context<Self>,
14062 ) {
14063 let old_cursor_position = self.selections.newest_anchor().head();
14064 self.selections.change_with(cx, |s| {
14065 s.select_anchors(selections);
14066 if let Some(pending_selection) = pending_selection {
14067 s.set_pending(pending_selection, SelectMode::Character);
14068 } else {
14069 s.clear_pending();
14070 }
14071 });
14072 self.selections_did_change(false, &old_cursor_position, true, window, cx);
14073 }
14074
14075 fn push_to_selection_history(&mut self) {
14076 self.selection_history.push(SelectionHistoryEntry {
14077 selections: self.selections.disjoint_anchors(),
14078 select_next_state: self.select_next_state.clone(),
14079 select_prev_state: self.select_prev_state.clone(),
14080 add_selections_state: self.add_selections_state.clone(),
14081 });
14082 }
14083
14084 pub fn transact(
14085 &mut self,
14086 window: &mut Window,
14087 cx: &mut Context<Self>,
14088 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
14089 ) -> Option<TransactionId> {
14090 self.start_transaction_at(Instant::now(), window, cx);
14091 update(self, window, cx);
14092 self.end_transaction_at(Instant::now(), cx)
14093 }
14094
14095 pub fn start_transaction_at(
14096 &mut self,
14097 now: Instant,
14098 window: &mut Window,
14099 cx: &mut Context<Self>,
14100 ) {
14101 self.end_selection(window, cx);
14102 if let Some(tx_id) = self
14103 .buffer
14104 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
14105 {
14106 self.selection_history
14107 .insert_transaction(tx_id, self.selections.disjoint_anchors());
14108 cx.emit(EditorEvent::TransactionBegun {
14109 transaction_id: tx_id,
14110 })
14111 }
14112 }
14113
14114 pub fn end_transaction_at(
14115 &mut self,
14116 now: Instant,
14117 cx: &mut Context<Self>,
14118 ) -> Option<TransactionId> {
14119 if let Some(transaction_id) = self
14120 .buffer
14121 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
14122 {
14123 if let Some((_, end_selections)) =
14124 self.selection_history.transaction_mut(transaction_id)
14125 {
14126 *end_selections = Some(self.selections.disjoint_anchors());
14127 } else {
14128 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
14129 }
14130
14131 cx.emit(EditorEvent::Edited { transaction_id });
14132 Some(transaction_id)
14133 } else {
14134 None
14135 }
14136 }
14137
14138 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
14139 if self.selection_mark_mode {
14140 self.change_selections(None, window, cx, |s| {
14141 s.move_with(|_, sel| {
14142 sel.collapse_to(sel.head(), SelectionGoal::None);
14143 });
14144 })
14145 }
14146 self.selection_mark_mode = true;
14147 cx.notify();
14148 }
14149
14150 pub fn swap_selection_ends(
14151 &mut self,
14152 _: &actions::SwapSelectionEnds,
14153 window: &mut Window,
14154 cx: &mut Context<Self>,
14155 ) {
14156 self.change_selections(None, window, cx, |s| {
14157 s.move_with(|_, sel| {
14158 if sel.start != sel.end {
14159 sel.reversed = !sel.reversed
14160 }
14161 });
14162 });
14163 self.request_autoscroll(Autoscroll::newest(), cx);
14164 cx.notify();
14165 }
14166
14167 pub fn toggle_fold(
14168 &mut self,
14169 _: &actions::ToggleFold,
14170 window: &mut Window,
14171 cx: &mut Context<Self>,
14172 ) {
14173 if self.is_singleton(cx) {
14174 let selection = self.selections.newest::<Point>(cx);
14175
14176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14177 let range = if selection.is_empty() {
14178 let point = selection.head().to_display_point(&display_map);
14179 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14180 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14181 .to_point(&display_map);
14182 start..end
14183 } else {
14184 selection.range()
14185 };
14186 if display_map.folds_in_range(range).next().is_some() {
14187 self.unfold_lines(&Default::default(), window, cx)
14188 } else {
14189 self.fold(&Default::default(), window, cx)
14190 }
14191 } else {
14192 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14193 let buffer_ids: HashSet<_> = self
14194 .selections
14195 .disjoint_anchor_ranges()
14196 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14197 .collect();
14198
14199 let should_unfold = buffer_ids
14200 .iter()
14201 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
14202
14203 for buffer_id in buffer_ids {
14204 if should_unfold {
14205 self.unfold_buffer(buffer_id, cx);
14206 } else {
14207 self.fold_buffer(buffer_id, cx);
14208 }
14209 }
14210 }
14211 }
14212
14213 pub fn toggle_fold_recursive(
14214 &mut self,
14215 _: &actions::ToggleFoldRecursive,
14216 window: &mut Window,
14217 cx: &mut Context<Self>,
14218 ) {
14219 let selection = self.selections.newest::<Point>(cx);
14220
14221 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14222 let range = if selection.is_empty() {
14223 let point = selection.head().to_display_point(&display_map);
14224 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14225 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14226 .to_point(&display_map);
14227 start..end
14228 } else {
14229 selection.range()
14230 };
14231 if display_map.folds_in_range(range).next().is_some() {
14232 self.unfold_recursive(&Default::default(), window, cx)
14233 } else {
14234 self.fold_recursive(&Default::default(), window, cx)
14235 }
14236 }
14237
14238 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14239 if self.is_singleton(cx) {
14240 let mut to_fold = Vec::new();
14241 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14242 let selections = self.selections.all_adjusted(cx);
14243
14244 for selection in selections {
14245 let range = selection.range().sorted();
14246 let buffer_start_row = range.start.row;
14247
14248 if range.start.row != range.end.row {
14249 let mut found = false;
14250 let mut row = range.start.row;
14251 while row <= range.end.row {
14252 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14253 {
14254 found = true;
14255 row = crease.range().end.row + 1;
14256 to_fold.push(crease);
14257 } else {
14258 row += 1
14259 }
14260 }
14261 if found {
14262 continue;
14263 }
14264 }
14265
14266 for row in (0..=range.start.row).rev() {
14267 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14268 if crease.range().end.row >= buffer_start_row {
14269 to_fold.push(crease);
14270 if row <= range.start.row {
14271 break;
14272 }
14273 }
14274 }
14275 }
14276 }
14277
14278 self.fold_creases(to_fold, true, window, cx);
14279 } else {
14280 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14281 let buffer_ids = self
14282 .selections
14283 .disjoint_anchor_ranges()
14284 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14285 .collect::<HashSet<_>>();
14286 for buffer_id in buffer_ids {
14287 self.fold_buffer(buffer_id, cx);
14288 }
14289 }
14290 }
14291
14292 fn fold_at_level(
14293 &mut self,
14294 fold_at: &FoldAtLevel,
14295 window: &mut Window,
14296 cx: &mut Context<Self>,
14297 ) {
14298 if !self.buffer.read(cx).is_singleton() {
14299 return;
14300 }
14301
14302 let fold_at_level = fold_at.0;
14303 let snapshot = self.buffer.read(cx).snapshot(cx);
14304 let mut to_fold = Vec::new();
14305 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14306
14307 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14308 while start_row < end_row {
14309 match self
14310 .snapshot(window, cx)
14311 .crease_for_buffer_row(MultiBufferRow(start_row))
14312 {
14313 Some(crease) => {
14314 let nested_start_row = crease.range().start.row + 1;
14315 let nested_end_row = crease.range().end.row;
14316
14317 if current_level < fold_at_level {
14318 stack.push((nested_start_row, nested_end_row, current_level + 1));
14319 } else if current_level == fold_at_level {
14320 to_fold.push(crease);
14321 }
14322
14323 start_row = nested_end_row + 1;
14324 }
14325 None => start_row += 1,
14326 }
14327 }
14328 }
14329
14330 self.fold_creases(to_fold, true, window, cx);
14331 }
14332
14333 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14334 if self.buffer.read(cx).is_singleton() {
14335 let mut fold_ranges = Vec::new();
14336 let snapshot = self.buffer.read(cx).snapshot(cx);
14337
14338 for row in 0..snapshot.max_row().0 {
14339 if let Some(foldable_range) = self
14340 .snapshot(window, cx)
14341 .crease_for_buffer_row(MultiBufferRow(row))
14342 {
14343 fold_ranges.push(foldable_range);
14344 }
14345 }
14346
14347 self.fold_creases(fold_ranges, true, window, cx);
14348 } else {
14349 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14350 editor
14351 .update_in(cx, |editor, _, cx| {
14352 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14353 editor.fold_buffer(buffer_id, cx);
14354 }
14355 })
14356 .ok();
14357 });
14358 }
14359 }
14360
14361 pub fn fold_function_bodies(
14362 &mut self,
14363 _: &actions::FoldFunctionBodies,
14364 window: &mut Window,
14365 cx: &mut Context<Self>,
14366 ) {
14367 let snapshot = self.buffer.read(cx).snapshot(cx);
14368
14369 let ranges = snapshot
14370 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14371 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14372 .collect::<Vec<_>>();
14373
14374 let creases = ranges
14375 .into_iter()
14376 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14377 .collect();
14378
14379 self.fold_creases(creases, true, window, cx);
14380 }
14381
14382 pub fn fold_recursive(
14383 &mut self,
14384 _: &actions::FoldRecursive,
14385 window: &mut Window,
14386 cx: &mut Context<Self>,
14387 ) {
14388 let mut to_fold = Vec::new();
14389 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14390 let selections = self.selections.all_adjusted(cx);
14391
14392 for selection in selections {
14393 let range = selection.range().sorted();
14394 let buffer_start_row = range.start.row;
14395
14396 if range.start.row != range.end.row {
14397 let mut found = false;
14398 for row in range.start.row..=range.end.row {
14399 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14400 found = true;
14401 to_fold.push(crease);
14402 }
14403 }
14404 if found {
14405 continue;
14406 }
14407 }
14408
14409 for row in (0..=range.start.row).rev() {
14410 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14411 if crease.range().end.row >= buffer_start_row {
14412 to_fold.push(crease);
14413 } else {
14414 break;
14415 }
14416 }
14417 }
14418 }
14419
14420 self.fold_creases(to_fold, true, window, cx);
14421 }
14422
14423 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14424 let buffer_row = fold_at.buffer_row;
14425 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14426
14427 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14428 let autoscroll = self
14429 .selections
14430 .all::<Point>(cx)
14431 .iter()
14432 .any(|selection| crease.range().overlaps(&selection.range()));
14433
14434 self.fold_creases(vec![crease], autoscroll, window, cx);
14435 }
14436 }
14437
14438 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14439 if self.is_singleton(cx) {
14440 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14441 let buffer = &display_map.buffer_snapshot;
14442 let selections = self.selections.all::<Point>(cx);
14443 let ranges = selections
14444 .iter()
14445 .map(|s| {
14446 let range = s.display_range(&display_map).sorted();
14447 let mut start = range.start.to_point(&display_map);
14448 let mut end = range.end.to_point(&display_map);
14449 start.column = 0;
14450 end.column = buffer.line_len(MultiBufferRow(end.row));
14451 start..end
14452 })
14453 .collect::<Vec<_>>();
14454
14455 self.unfold_ranges(&ranges, true, true, cx);
14456 } else {
14457 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14458 let buffer_ids = self
14459 .selections
14460 .disjoint_anchor_ranges()
14461 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14462 .collect::<HashSet<_>>();
14463 for buffer_id in buffer_ids {
14464 self.unfold_buffer(buffer_id, cx);
14465 }
14466 }
14467 }
14468
14469 pub fn unfold_recursive(
14470 &mut self,
14471 _: &UnfoldRecursive,
14472 _window: &mut Window,
14473 cx: &mut Context<Self>,
14474 ) {
14475 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14476 let selections = self.selections.all::<Point>(cx);
14477 let ranges = selections
14478 .iter()
14479 .map(|s| {
14480 let mut range = s.display_range(&display_map).sorted();
14481 *range.start.column_mut() = 0;
14482 *range.end.column_mut() = display_map.line_len(range.end.row());
14483 let start = range.start.to_point(&display_map);
14484 let end = range.end.to_point(&display_map);
14485 start..end
14486 })
14487 .collect::<Vec<_>>();
14488
14489 self.unfold_ranges(&ranges, true, true, cx);
14490 }
14491
14492 pub fn unfold_at(
14493 &mut self,
14494 unfold_at: &UnfoldAt,
14495 _window: &mut Window,
14496 cx: &mut Context<Self>,
14497 ) {
14498 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14499
14500 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14501 ..Point::new(
14502 unfold_at.buffer_row.0,
14503 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14504 );
14505
14506 let autoscroll = self
14507 .selections
14508 .all::<Point>(cx)
14509 .iter()
14510 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14511
14512 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14513 }
14514
14515 pub fn unfold_all(
14516 &mut self,
14517 _: &actions::UnfoldAll,
14518 _window: &mut Window,
14519 cx: &mut Context<Self>,
14520 ) {
14521 if self.buffer.read(cx).is_singleton() {
14522 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14523 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14524 } else {
14525 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14526 editor
14527 .update(cx, |editor, cx| {
14528 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14529 editor.unfold_buffer(buffer_id, cx);
14530 }
14531 })
14532 .ok();
14533 });
14534 }
14535 }
14536
14537 pub fn fold_selected_ranges(
14538 &mut self,
14539 _: &FoldSelectedRanges,
14540 window: &mut Window,
14541 cx: &mut Context<Self>,
14542 ) {
14543 let selections = self.selections.all::<Point>(cx);
14544 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14545 let line_mode = self.selections.line_mode;
14546 let ranges = selections
14547 .into_iter()
14548 .map(|s| {
14549 if line_mode {
14550 let start = Point::new(s.start.row, 0);
14551 let end = Point::new(
14552 s.end.row,
14553 display_map
14554 .buffer_snapshot
14555 .line_len(MultiBufferRow(s.end.row)),
14556 );
14557 Crease::simple(start..end, display_map.fold_placeholder.clone())
14558 } else {
14559 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14560 }
14561 })
14562 .collect::<Vec<_>>();
14563 self.fold_creases(ranges, true, window, cx);
14564 }
14565
14566 pub fn fold_ranges<T: ToOffset + Clone>(
14567 &mut self,
14568 ranges: Vec<Range<T>>,
14569 auto_scroll: bool,
14570 window: &mut Window,
14571 cx: &mut Context<Self>,
14572 ) {
14573 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14574 let ranges = ranges
14575 .into_iter()
14576 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14577 .collect::<Vec<_>>();
14578 self.fold_creases(ranges, auto_scroll, window, cx);
14579 }
14580
14581 pub fn fold_creases<T: ToOffset + Clone>(
14582 &mut self,
14583 creases: Vec<Crease<T>>,
14584 auto_scroll: bool,
14585 window: &mut Window,
14586 cx: &mut Context<Self>,
14587 ) {
14588 if creases.is_empty() {
14589 return;
14590 }
14591
14592 let mut buffers_affected = HashSet::default();
14593 let multi_buffer = self.buffer().read(cx);
14594 for crease in &creases {
14595 if let Some((_, buffer, _)) =
14596 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14597 {
14598 buffers_affected.insert(buffer.read(cx).remote_id());
14599 };
14600 }
14601
14602 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14603
14604 if auto_scroll {
14605 self.request_autoscroll(Autoscroll::fit(), cx);
14606 }
14607
14608 cx.notify();
14609
14610 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14611 // Clear diagnostics block when folding a range that contains it.
14612 let snapshot = self.snapshot(window, cx);
14613 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14614 drop(snapshot);
14615 self.active_diagnostics = Some(active_diagnostics);
14616 self.dismiss_diagnostics(cx);
14617 } else {
14618 self.active_diagnostics = Some(active_diagnostics);
14619 }
14620 }
14621
14622 self.scrollbar_marker_state.dirty = true;
14623 self.folds_did_change(cx);
14624 }
14625
14626 /// Removes any folds whose ranges intersect any of the given ranges.
14627 pub fn unfold_ranges<T: ToOffset + Clone>(
14628 &mut self,
14629 ranges: &[Range<T>],
14630 inclusive: bool,
14631 auto_scroll: bool,
14632 cx: &mut Context<Self>,
14633 ) {
14634 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14635 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14636 });
14637 self.folds_did_change(cx);
14638 }
14639
14640 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14641 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14642 return;
14643 }
14644 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14645 self.display_map.update(cx, |display_map, cx| {
14646 display_map.fold_buffers([buffer_id], cx)
14647 });
14648 cx.emit(EditorEvent::BufferFoldToggled {
14649 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14650 folded: true,
14651 });
14652 cx.notify();
14653 }
14654
14655 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14656 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14657 return;
14658 }
14659 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14660 self.display_map.update(cx, |display_map, cx| {
14661 display_map.unfold_buffers([buffer_id], cx);
14662 });
14663 cx.emit(EditorEvent::BufferFoldToggled {
14664 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14665 folded: false,
14666 });
14667 cx.notify();
14668 }
14669
14670 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14671 self.display_map.read(cx).is_buffer_folded(buffer)
14672 }
14673
14674 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14675 self.display_map.read(cx).folded_buffers()
14676 }
14677
14678 /// Removes any folds with the given ranges.
14679 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14680 &mut self,
14681 ranges: &[Range<T>],
14682 type_id: TypeId,
14683 auto_scroll: bool,
14684 cx: &mut Context<Self>,
14685 ) {
14686 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14687 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14688 });
14689 self.folds_did_change(cx);
14690 }
14691
14692 fn remove_folds_with<T: ToOffset + Clone>(
14693 &mut self,
14694 ranges: &[Range<T>],
14695 auto_scroll: bool,
14696 cx: &mut Context<Self>,
14697 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14698 ) {
14699 if ranges.is_empty() {
14700 return;
14701 }
14702
14703 let mut buffers_affected = HashSet::default();
14704 let multi_buffer = self.buffer().read(cx);
14705 for range in ranges {
14706 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14707 buffers_affected.insert(buffer.read(cx).remote_id());
14708 };
14709 }
14710
14711 self.display_map.update(cx, update);
14712
14713 if auto_scroll {
14714 self.request_autoscroll(Autoscroll::fit(), cx);
14715 }
14716
14717 cx.notify();
14718 self.scrollbar_marker_state.dirty = true;
14719 self.active_indent_guides_state.dirty = true;
14720 }
14721
14722 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14723 self.display_map.read(cx).fold_placeholder.clone()
14724 }
14725
14726 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14727 self.buffer.update(cx, |buffer, cx| {
14728 buffer.set_all_diff_hunks_expanded(cx);
14729 });
14730 }
14731
14732 pub fn expand_all_diff_hunks(
14733 &mut self,
14734 _: &ExpandAllDiffHunks,
14735 _window: &mut Window,
14736 cx: &mut Context<Self>,
14737 ) {
14738 self.buffer.update(cx, |buffer, cx| {
14739 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14740 });
14741 }
14742
14743 pub fn toggle_selected_diff_hunks(
14744 &mut self,
14745 _: &ToggleSelectedDiffHunks,
14746 _window: &mut Window,
14747 cx: &mut Context<Self>,
14748 ) {
14749 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14750 self.toggle_diff_hunks_in_ranges(ranges, cx);
14751 }
14752
14753 pub fn diff_hunks_in_ranges<'a>(
14754 &'a self,
14755 ranges: &'a [Range<Anchor>],
14756 buffer: &'a MultiBufferSnapshot,
14757 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14758 ranges.iter().flat_map(move |range| {
14759 let end_excerpt_id = range.end.excerpt_id;
14760 let range = range.to_point(buffer);
14761 let mut peek_end = range.end;
14762 if range.end.row < buffer.max_row().0 {
14763 peek_end = Point::new(range.end.row + 1, 0);
14764 }
14765 buffer
14766 .diff_hunks_in_range(range.start..peek_end)
14767 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14768 })
14769 }
14770
14771 pub fn has_stageable_diff_hunks_in_ranges(
14772 &self,
14773 ranges: &[Range<Anchor>],
14774 snapshot: &MultiBufferSnapshot,
14775 ) -> bool {
14776 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14777 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14778 }
14779
14780 pub fn toggle_staged_selected_diff_hunks(
14781 &mut self,
14782 _: &::git::ToggleStaged,
14783 _: &mut Window,
14784 cx: &mut Context<Self>,
14785 ) {
14786 let snapshot = self.buffer.read(cx).snapshot(cx);
14787 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14788 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14789 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14790 }
14791
14792 pub fn stage_and_next(
14793 &mut self,
14794 _: &::git::StageAndNext,
14795 window: &mut Window,
14796 cx: &mut Context<Self>,
14797 ) {
14798 self.do_stage_or_unstage_and_next(true, window, cx);
14799 }
14800
14801 pub fn unstage_and_next(
14802 &mut self,
14803 _: &::git::UnstageAndNext,
14804 window: &mut Window,
14805 cx: &mut Context<Self>,
14806 ) {
14807 self.do_stage_or_unstage_and_next(false, window, cx);
14808 }
14809
14810 pub fn stage_or_unstage_diff_hunks(
14811 &mut self,
14812 stage: bool,
14813 ranges: Vec<Range<Anchor>>,
14814 cx: &mut Context<Self>,
14815 ) {
14816 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14817 cx.spawn(async move |this, cx| {
14818 task.await?;
14819 this.update(cx, |this, cx| {
14820 let snapshot = this.buffer.read(cx).snapshot(cx);
14821 let chunk_by = this
14822 .diff_hunks_in_ranges(&ranges, &snapshot)
14823 .chunk_by(|hunk| hunk.buffer_id);
14824 for (buffer_id, hunks) in &chunk_by {
14825 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14826 }
14827 })
14828 })
14829 .detach_and_log_err(cx);
14830 }
14831
14832 fn save_buffers_for_ranges_if_needed(
14833 &mut self,
14834 ranges: &[Range<Anchor>],
14835 cx: &mut Context<'_, Editor>,
14836 ) -> Task<Result<()>> {
14837 let multibuffer = self.buffer.read(cx);
14838 let snapshot = multibuffer.read(cx);
14839 let buffer_ids: HashSet<_> = ranges
14840 .iter()
14841 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14842 .collect();
14843 drop(snapshot);
14844
14845 let mut buffers = HashSet::default();
14846 for buffer_id in buffer_ids {
14847 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14848 let buffer = buffer_entity.read(cx);
14849 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14850 {
14851 buffers.insert(buffer_entity);
14852 }
14853 }
14854 }
14855
14856 if let Some(project) = &self.project {
14857 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14858 } else {
14859 Task::ready(Ok(()))
14860 }
14861 }
14862
14863 fn do_stage_or_unstage_and_next(
14864 &mut self,
14865 stage: bool,
14866 window: &mut Window,
14867 cx: &mut Context<Self>,
14868 ) {
14869 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14870
14871 if ranges.iter().any(|range| range.start != range.end) {
14872 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14873 return;
14874 }
14875
14876 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14877 let snapshot = self.snapshot(window, cx);
14878 let position = self.selections.newest::<Point>(cx).head();
14879 let mut row = snapshot
14880 .buffer_snapshot
14881 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14882 .find(|hunk| hunk.row_range.start.0 > position.row)
14883 .map(|hunk| hunk.row_range.start);
14884
14885 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14886 // Outside of the project diff editor, wrap around to the beginning.
14887 if !all_diff_hunks_expanded {
14888 row = row.or_else(|| {
14889 snapshot
14890 .buffer_snapshot
14891 .diff_hunks_in_range(Point::zero()..position)
14892 .find(|hunk| hunk.row_range.end.0 < position.row)
14893 .map(|hunk| hunk.row_range.start)
14894 });
14895 }
14896
14897 if let Some(row) = row {
14898 let destination = Point::new(row.0, 0);
14899 let autoscroll = Autoscroll::center();
14900
14901 self.unfold_ranges(&[destination..destination], false, false, cx);
14902 self.change_selections(Some(autoscroll), window, cx, |s| {
14903 s.select_ranges([destination..destination]);
14904 });
14905 }
14906 }
14907
14908 fn do_stage_or_unstage(
14909 &self,
14910 stage: bool,
14911 buffer_id: BufferId,
14912 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14913 cx: &mut App,
14914 ) -> Option<()> {
14915 let project = self.project.as_ref()?;
14916 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14917 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14918 let buffer_snapshot = buffer.read(cx).snapshot();
14919 let file_exists = buffer_snapshot
14920 .file()
14921 .is_some_and(|file| file.disk_state().exists());
14922 diff.update(cx, |diff, cx| {
14923 diff.stage_or_unstage_hunks(
14924 stage,
14925 &hunks
14926 .map(|hunk| buffer_diff::DiffHunk {
14927 buffer_range: hunk.buffer_range,
14928 diff_base_byte_range: hunk.diff_base_byte_range,
14929 secondary_status: hunk.secondary_status,
14930 range: Point::zero()..Point::zero(), // unused
14931 })
14932 .collect::<Vec<_>>(),
14933 &buffer_snapshot,
14934 file_exists,
14935 cx,
14936 )
14937 });
14938 None
14939 }
14940
14941 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14942 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14943 self.buffer
14944 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14945 }
14946
14947 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14948 self.buffer.update(cx, |buffer, cx| {
14949 let ranges = vec![Anchor::min()..Anchor::max()];
14950 if !buffer.all_diff_hunks_expanded()
14951 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14952 {
14953 buffer.collapse_diff_hunks(ranges, cx);
14954 true
14955 } else {
14956 false
14957 }
14958 })
14959 }
14960
14961 fn toggle_diff_hunks_in_ranges(
14962 &mut self,
14963 ranges: Vec<Range<Anchor>>,
14964 cx: &mut Context<'_, Editor>,
14965 ) {
14966 self.buffer.update(cx, |buffer, cx| {
14967 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14968 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14969 })
14970 }
14971
14972 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14973 self.buffer.update(cx, |buffer, cx| {
14974 let snapshot = buffer.snapshot(cx);
14975 let excerpt_id = range.end.excerpt_id;
14976 let point_range = range.to_point(&snapshot);
14977 let expand = !buffer.single_hunk_is_expanded(range, cx);
14978 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14979 })
14980 }
14981
14982 pub(crate) fn apply_all_diff_hunks(
14983 &mut self,
14984 _: &ApplyAllDiffHunks,
14985 window: &mut Window,
14986 cx: &mut Context<Self>,
14987 ) {
14988 let buffers = self.buffer.read(cx).all_buffers();
14989 for branch_buffer in buffers {
14990 branch_buffer.update(cx, |branch_buffer, cx| {
14991 branch_buffer.merge_into_base(Vec::new(), cx);
14992 });
14993 }
14994
14995 if let Some(project) = self.project.clone() {
14996 self.save(true, project, window, cx).detach_and_log_err(cx);
14997 }
14998 }
14999
15000 pub(crate) fn apply_selected_diff_hunks(
15001 &mut self,
15002 _: &ApplyDiffHunk,
15003 window: &mut Window,
15004 cx: &mut Context<Self>,
15005 ) {
15006 let snapshot = self.snapshot(window, cx);
15007 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
15008 let mut ranges_by_buffer = HashMap::default();
15009 self.transact(window, cx, |editor, _window, cx| {
15010 for hunk in hunks {
15011 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
15012 ranges_by_buffer
15013 .entry(buffer.clone())
15014 .or_insert_with(Vec::new)
15015 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
15016 }
15017 }
15018
15019 for (buffer, ranges) in ranges_by_buffer {
15020 buffer.update(cx, |buffer, cx| {
15021 buffer.merge_into_base(ranges, cx);
15022 });
15023 }
15024 });
15025
15026 if let Some(project) = self.project.clone() {
15027 self.save(true, project, window, cx).detach_and_log_err(cx);
15028 }
15029 }
15030
15031 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
15032 if hovered != self.gutter_hovered {
15033 self.gutter_hovered = hovered;
15034 cx.notify();
15035 }
15036 }
15037
15038 pub fn insert_blocks(
15039 &mut self,
15040 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
15041 autoscroll: Option<Autoscroll>,
15042 cx: &mut Context<Self>,
15043 ) -> Vec<CustomBlockId> {
15044 let blocks = self
15045 .display_map
15046 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
15047 if let Some(autoscroll) = autoscroll {
15048 self.request_autoscroll(autoscroll, cx);
15049 }
15050 cx.notify();
15051 blocks
15052 }
15053
15054 pub fn resize_blocks(
15055 &mut self,
15056 heights: HashMap<CustomBlockId, u32>,
15057 autoscroll: Option<Autoscroll>,
15058 cx: &mut Context<Self>,
15059 ) {
15060 self.display_map
15061 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
15062 if let Some(autoscroll) = autoscroll {
15063 self.request_autoscroll(autoscroll, cx);
15064 }
15065 cx.notify();
15066 }
15067
15068 pub fn replace_blocks(
15069 &mut self,
15070 renderers: HashMap<CustomBlockId, RenderBlock>,
15071 autoscroll: Option<Autoscroll>,
15072 cx: &mut Context<Self>,
15073 ) {
15074 self.display_map
15075 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
15076 if let Some(autoscroll) = autoscroll {
15077 self.request_autoscroll(autoscroll, cx);
15078 }
15079 cx.notify();
15080 }
15081
15082 pub fn remove_blocks(
15083 &mut self,
15084 block_ids: HashSet<CustomBlockId>,
15085 autoscroll: Option<Autoscroll>,
15086 cx: &mut Context<Self>,
15087 ) {
15088 self.display_map.update(cx, |display_map, cx| {
15089 display_map.remove_blocks(block_ids, cx)
15090 });
15091 if let Some(autoscroll) = autoscroll {
15092 self.request_autoscroll(autoscroll, cx);
15093 }
15094 cx.notify();
15095 }
15096
15097 pub fn row_for_block(
15098 &self,
15099 block_id: CustomBlockId,
15100 cx: &mut Context<Self>,
15101 ) -> Option<DisplayRow> {
15102 self.display_map
15103 .update(cx, |map, cx| map.row_for_block(block_id, cx))
15104 }
15105
15106 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
15107 self.focused_block = Some(focused_block);
15108 }
15109
15110 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
15111 self.focused_block.take()
15112 }
15113
15114 pub fn insert_creases(
15115 &mut self,
15116 creases: impl IntoIterator<Item = Crease<Anchor>>,
15117 cx: &mut Context<Self>,
15118 ) -> Vec<CreaseId> {
15119 self.display_map
15120 .update(cx, |map, cx| map.insert_creases(creases, cx))
15121 }
15122
15123 pub fn remove_creases(
15124 &mut self,
15125 ids: impl IntoIterator<Item = CreaseId>,
15126 cx: &mut Context<Self>,
15127 ) {
15128 self.display_map
15129 .update(cx, |map, cx| map.remove_creases(ids, cx));
15130 }
15131
15132 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
15133 self.display_map
15134 .update(cx, |map, cx| map.snapshot(cx))
15135 .longest_row()
15136 }
15137
15138 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
15139 self.display_map
15140 .update(cx, |map, cx| map.snapshot(cx))
15141 .max_point()
15142 }
15143
15144 pub fn text(&self, cx: &App) -> String {
15145 self.buffer.read(cx).read(cx).text()
15146 }
15147
15148 pub fn is_empty(&self, cx: &App) -> bool {
15149 self.buffer.read(cx).read(cx).is_empty()
15150 }
15151
15152 pub fn text_option(&self, cx: &App) -> Option<String> {
15153 let text = self.text(cx);
15154 let text = text.trim();
15155
15156 if text.is_empty() {
15157 return None;
15158 }
15159
15160 Some(text.to_string())
15161 }
15162
15163 pub fn set_text(
15164 &mut self,
15165 text: impl Into<Arc<str>>,
15166 window: &mut Window,
15167 cx: &mut Context<Self>,
15168 ) {
15169 self.transact(window, cx, |this, _, cx| {
15170 this.buffer
15171 .read(cx)
15172 .as_singleton()
15173 .expect("you can only call set_text on editors for singleton buffers")
15174 .update(cx, |buffer, cx| buffer.set_text(text, cx));
15175 });
15176 }
15177
15178 pub fn display_text(&self, cx: &mut App) -> String {
15179 self.display_map
15180 .update(cx, |map, cx| map.snapshot(cx))
15181 .text()
15182 }
15183
15184 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
15185 let mut wrap_guides = smallvec::smallvec![];
15186
15187 if self.show_wrap_guides == Some(false) {
15188 return wrap_guides;
15189 }
15190
15191 let settings = self.buffer.read(cx).language_settings(cx);
15192 if settings.show_wrap_guides {
15193 match self.soft_wrap_mode(cx) {
15194 SoftWrap::Column(soft_wrap) => {
15195 wrap_guides.push((soft_wrap as usize, true));
15196 }
15197 SoftWrap::Bounded(soft_wrap) => {
15198 wrap_guides.push((soft_wrap as usize, true));
15199 }
15200 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
15201 }
15202 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
15203 }
15204
15205 wrap_guides
15206 }
15207
15208 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
15209 let settings = self.buffer.read(cx).language_settings(cx);
15210 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
15211 match mode {
15212 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
15213 SoftWrap::None
15214 }
15215 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15216 language_settings::SoftWrap::PreferredLineLength => {
15217 SoftWrap::Column(settings.preferred_line_length)
15218 }
15219 language_settings::SoftWrap::Bounded => {
15220 SoftWrap::Bounded(settings.preferred_line_length)
15221 }
15222 }
15223 }
15224
15225 pub fn set_soft_wrap_mode(
15226 &mut self,
15227 mode: language_settings::SoftWrap,
15228
15229 cx: &mut Context<Self>,
15230 ) {
15231 self.soft_wrap_mode_override = Some(mode);
15232 cx.notify();
15233 }
15234
15235 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15236 self.hard_wrap = hard_wrap;
15237 cx.notify();
15238 }
15239
15240 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15241 self.text_style_refinement = Some(style);
15242 }
15243
15244 /// called by the Element so we know what style we were most recently rendered with.
15245 pub(crate) fn set_style(
15246 &mut self,
15247 style: EditorStyle,
15248 window: &mut Window,
15249 cx: &mut Context<Self>,
15250 ) {
15251 let rem_size = window.rem_size();
15252 self.display_map.update(cx, |map, cx| {
15253 map.set_font(
15254 style.text.font(),
15255 style.text.font_size.to_pixels(rem_size),
15256 cx,
15257 )
15258 });
15259 self.style = Some(style);
15260 }
15261
15262 pub fn style(&self) -> Option<&EditorStyle> {
15263 self.style.as_ref()
15264 }
15265
15266 // Called by the element. This method is not designed to be called outside of the editor
15267 // element's layout code because it does not notify when rewrapping is computed synchronously.
15268 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15269 self.display_map
15270 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15271 }
15272
15273 pub fn set_soft_wrap(&mut self) {
15274 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15275 }
15276
15277 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15278 if self.soft_wrap_mode_override.is_some() {
15279 self.soft_wrap_mode_override.take();
15280 } else {
15281 let soft_wrap = match self.soft_wrap_mode(cx) {
15282 SoftWrap::GitDiff => return,
15283 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15284 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15285 language_settings::SoftWrap::None
15286 }
15287 };
15288 self.soft_wrap_mode_override = Some(soft_wrap);
15289 }
15290 cx.notify();
15291 }
15292
15293 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15294 let Some(workspace) = self.workspace() else {
15295 return;
15296 };
15297 let fs = workspace.read(cx).app_state().fs.clone();
15298 let current_show = TabBarSettings::get_global(cx).show;
15299 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15300 setting.show = Some(!current_show);
15301 });
15302 }
15303
15304 pub fn toggle_indent_guides(
15305 &mut self,
15306 _: &ToggleIndentGuides,
15307 _: &mut Window,
15308 cx: &mut Context<Self>,
15309 ) {
15310 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15311 self.buffer
15312 .read(cx)
15313 .language_settings(cx)
15314 .indent_guides
15315 .enabled
15316 });
15317 self.show_indent_guides = Some(!currently_enabled);
15318 cx.notify();
15319 }
15320
15321 fn should_show_indent_guides(&self) -> Option<bool> {
15322 self.show_indent_guides
15323 }
15324
15325 pub fn toggle_line_numbers(
15326 &mut self,
15327 _: &ToggleLineNumbers,
15328 _: &mut Window,
15329 cx: &mut Context<Self>,
15330 ) {
15331 let mut editor_settings = EditorSettings::get_global(cx).clone();
15332 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15333 EditorSettings::override_global(editor_settings, cx);
15334 }
15335
15336 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15337 if let Some(show_line_numbers) = self.show_line_numbers {
15338 return show_line_numbers;
15339 }
15340 EditorSettings::get_global(cx).gutter.line_numbers
15341 }
15342
15343 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15344 self.use_relative_line_numbers
15345 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15346 }
15347
15348 pub fn toggle_relative_line_numbers(
15349 &mut self,
15350 _: &ToggleRelativeLineNumbers,
15351 _: &mut Window,
15352 cx: &mut Context<Self>,
15353 ) {
15354 let is_relative = self.should_use_relative_line_numbers(cx);
15355 self.set_relative_line_number(Some(!is_relative), cx)
15356 }
15357
15358 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15359 self.use_relative_line_numbers = is_relative;
15360 cx.notify();
15361 }
15362
15363 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15364 self.show_gutter = show_gutter;
15365 cx.notify();
15366 }
15367
15368 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15369 self.show_scrollbars = show_scrollbars;
15370 cx.notify();
15371 }
15372
15373 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15374 self.show_line_numbers = Some(show_line_numbers);
15375 cx.notify();
15376 }
15377
15378 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15379 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15380 cx.notify();
15381 }
15382
15383 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15384 self.show_code_actions = Some(show_code_actions);
15385 cx.notify();
15386 }
15387
15388 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15389 self.show_runnables = Some(show_runnables);
15390 cx.notify();
15391 }
15392
15393 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15394 self.show_breakpoints = Some(show_breakpoints);
15395 cx.notify();
15396 }
15397
15398 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15399 if self.display_map.read(cx).masked != masked {
15400 self.display_map.update(cx, |map, _| map.masked = masked);
15401 }
15402 cx.notify()
15403 }
15404
15405 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15406 self.show_wrap_guides = Some(show_wrap_guides);
15407 cx.notify();
15408 }
15409
15410 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15411 self.show_indent_guides = Some(show_indent_guides);
15412 cx.notify();
15413 }
15414
15415 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15416 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15417 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15418 if let Some(dir) = file.abs_path(cx).parent() {
15419 return Some(dir.to_owned());
15420 }
15421 }
15422
15423 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15424 return Some(project_path.path.to_path_buf());
15425 }
15426 }
15427
15428 None
15429 }
15430
15431 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15432 self.active_excerpt(cx)?
15433 .1
15434 .read(cx)
15435 .file()
15436 .and_then(|f| f.as_local())
15437 }
15438
15439 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15440 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15441 let buffer = buffer.read(cx);
15442 if let Some(project_path) = buffer.project_path(cx) {
15443 let project = self.project.as_ref()?.read(cx);
15444 project.absolute_path(&project_path, cx)
15445 } else {
15446 buffer
15447 .file()
15448 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15449 }
15450 })
15451 }
15452
15453 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15454 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15455 let project_path = buffer.read(cx).project_path(cx)?;
15456 let project = self.project.as_ref()?.read(cx);
15457 let entry = project.entry_for_path(&project_path, cx)?;
15458 let path = entry.path.to_path_buf();
15459 Some(path)
15460 })
15461 }
15462
15463 pub fn reveal_in_finder(
15464 &mut self,
15465 _: &RevealInFileManager,
15466 _window: &mut Window,
15467 cx: &mut Context<Self>,
15468 ) {
15469 if let Some(target) = self.target_file(cx) {
15470 cx.reveal_path(&target.abs_path(cx));
15471 }
15472 }
15473
15474 pub fn copy_path(
15475 &mut self,
15476 _: &zed_actions::workspace::CopyPath,
15477 _window: &mut Window,
15478 cx: &mut Context<Self>,
15479 ) {
15480 if let Some(path) = self.target_file_abs_path(cx) {
15481 if let Some(path) = path.to_str() {
15482 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15483 }
15484 }
15485 }
15486
15487 pub fn copy_relative_path(
15488 &mut self,
15489 _: &zed_actions::workspace::CopyRelativePath,
15490 _window: &mut Window,
15491 cx: &mut Context<Self>,
15492 ) {
15493 if let Some(path) = self.target_file_path(cx) {
15494 if let Some(path) = path.to_str() {
15495 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15496 }
15497 }
15498 }
15499
15500 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15501 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15502 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15503 } else {
15504 None
15505 }
15506 }
15507
15508 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15509 let _ = maybe!({
15510 let breakpoint_store = self.breakpoint_store.as_ref()?;
15511
15512 let Some((_, _, active_position)) =
15513 breakpoint_store.read(cx).active_position().cloned()
15514 else {
15515 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15516 return None;
15517 };
15518
15519 let snapshot = self
15520 .project
15521 .as_ref()?
15522 .read(cx)
15523 .buffer_for_id(active_position.buffer_id?, cx)?
15524 .read(cx)
15525 .snapshot();
15526
15527 for (id, ExcerptRange { context, .. }) in self
15528 .buffer
15529 .read(cx)
15530 .excerpts_for_buffer(active_position.buffer_id?, cx)
15531 {
15532 if context.start.cmp(&active_position, &snapshot).is_ge()
15533 || context.end.cmp(&active_position, &snapshot).is_lt()
15534 {
15535 continue;
15536 }
15537 let snapshot = self.buffer.read(cx).snapshot(cx);
15538 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15539
15540 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15541 self.go_to_line::<DebugCurrentRowHighlight>(
15542 multibuffer_anchor,
15543 Some(cx.theme().colors().editor_debugger_active_line_background),
15544 window,
15545 cx,
15546 );
15547
15548 cx.notify();
15549 }
15550
15551 Some(())
15552 });
15553 }
15554
15555 pub fn copy_file_name_without_extension(
15556 &mut self,
15557 _: &CopyFileNameWithoutExtension,
15558 _: &mut Window,
15559 cx: &mut Context<Self>,
15560 ) {
15561 if let Some(file) = self.target_file(cx) {
15562 if let Some(file_stem) = file.path().file_stem() {
15563 if let Some(name) = file_stem.to_str() {
15564 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15565 }
15566 }
15567 }
15568 }
15569
15570 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15571 if let Some(file) = self.target_file(cx) {
15572 if let Some(file_name) = file.path().file_name() {
15573 if let Some(name) = file_name.to_str() {
15574 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15575 }
15576 }
15577 }
15578 }
15579
15580 pub fn toggle_git_blame(
15581 &mut self,
15582 _: &::git::Blame,
15583 window: &mut Window,
15584 cx: &mut Context<Self>,
15585 ) {
15586 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15587
15588 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15589 self.start_git_blame(true, window, cx);
15590 }
15591
15592 cx.notify();
15593 }
15594
15595 pub fn toggle_git_blame_inline(
15596 &mut self,
15597 _: &ToggleGitBlameInline,
15598 window: &mut Window,
15599 cx: &mut Context<Self>,
15600 ) {
15601 self.toggle_git_blame_inline_internal(true, window, cx);
15602 cx.notify();
15603 }
15604
15605 pub fn git_blame_inline_enabled(&self) -> bool {
15606 self.git_blame_inline_enabled
15607 }
15608
15609 pub fn toggle_selection_menu(
15610 &mut self,
15611 _: &ToggleSelectionMenu,
15612 _: &mut Window,
15613 cx: &mut Context<Self>,
15614 ) {
15615 self.show_selection_menu = self
15616 .show_selection_menu
15617 .map(|show_selections_menu| !show_selections_menu)
15618 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15619
15620 cx.notify();
15621 }
15622
15623 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15624 self.show_selection_menu
15625 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15626 }
15627
15628 fn start_git_blame(
15629 &mut self,
15630 user_triggered: bool,
15631 window: &mut Window,
15632 cx: &mut Context<Self>,
15633 ) {
15634 if let Some(project) = self.project.as_ref() {
15635 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15636 return;
15637 };
15638
15639 if buffer.read(cx).file().is_none() {
15640 return;
15641 }
15642
15643 let focused = self.focus_handle(cx).contains_focused(window, cx);
15644
15645 let project = project.clone();
15646 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15647 self.blame_subscription =
15648 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15649 self.blame = Some(blame);
15650 }
15651 }
15652
15653 fn toggle_git_blame_inline_internal(
15654 &mut self,
15655 user_triggered: bool,
15656 window: &mut Window,
15657 cx: &mut Context<Self>,
15658 ) {
15659 if self.git_blame_inline_enabled {
15660 self.git_blame_inline_enabled = false;
15661 self.show_git_blame_inline = false;
15662 self.show_git_blame_inline_delay_task.take();
15663 } else {
15664 self.git_blame_inline_enabled = true;
15665 self.start_git_blame_inline(user_triggered, window, cx);
15666 }
15667
15668 cx.notify();
15669 }
15670
15671 fn start_git_blame_inline(
15672 &mut self,
15673 user_triggered: bool,
15674 window: &mut Window,
15675 cx: &mut Context<Self>,
15676 ) {
15677 self.start_git_blame(user_triggered, window, cx);
15678
15679 if ProjectSettings::get_global(cx)
15680 .git
15681 .inline_blame_delay()
15682 .is_some()
15683 {
15684 self.start_inline_blame_timer(window, cx);
15685 } else {
15686 self.show_git_blame_inline = true
15687 }
15688 }
15689
15690 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15691 self.blame.as_ref()
15692 }
15693
15694 pub fn show_git_blame_gutter(&self) -> bool {
15695 self.show_git_blame_gutter
15696 }
15697
15698 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15699 self.show_git_blame_gutter && self.has_blame_entries(cx)
15700 }
15701
15702 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15703 self.show_git_blame_inline
15704 && (self.focus_handle.is_focused(window)
15705 || self
15706 .git_blame_inline_tooltip
15707 .as_ref()
15708 .and_then(|t| t.upgrade())
15709 .is_some())
15710 && !self.newest_selection_head_on_empty_line(cx)
15711 && self.has_blame_entries(cx)
15712 }
15713
15714 fn has_blame_entries(&self, cx: &App) -> bool {
15715 self.blame()
15716 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15717 }
15718
15719 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15720 let cursor_anchor = self.selections.newest_anchor().head();
15721
15722 let snapshot = self.buffer.read(cx).snapshot(cx);
15723 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15724
15725 snapshot.line_len(buffer_row) == 0
15726 }
15727
15728 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15729 let buffer_and_selection = maybe!({
15730 let selection = self.selections.newest::<Point>(cx);
15731 let selection_range = selection.range();
15732
15733 let multi_buffer = self.buffer().read(cx);
15734 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15735 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15736
15737 let (buffer, range, _) = if selection.reversed {
15738 buffer_ranges.first()
15739 } else {
15740 buffer_ranges.last()
15741 }?;
15742
15743 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15744 ..text::ToPoint::to_point(&range.end, &buffer).row;
15745 Some((
15746 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15747 selection,
15748 ))
15749 });
15750
15751 let Some((buffer, selection)) = buffer_and_selection else {
15752 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15753 };
15754
15755 let Some(project) = self.project.as_ref() else {
15756 return Task::ready(Err(anyhow!("editor does not have project")));
15757 };
15758
15759 project.update(cx, |project, cx| {
15760 project.get_permalink_to_line(&buffer, selection, cx)
15761 })
15762 }
15763
15764 pub fn copy_permalink_to_line(
15765 &mut self,
15766 _: &CopyPermalinkToLine,
15767 window: &mut Window,
15768 cx: &mut Context<Self>,
15769 ) {
15770 let permalink_task = self.get_permalink_to_line(cx);
15771 let workspace = self.workspace();
15772
15773 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15774 Ok(permalink) => {
15775 cx.update(|_, cx| {
15776 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15777 })
15778 .ok();
15779 }
15780 Err(err) => {
15781 let message = format!("Failed to copy permalink: {err}");
15782
15783 Err::<(), anyhow::Error>(err).log_err();
15784
15785 if let Some(workspace) = workspace {
15786 workspace
15787 .update_in(cx, |workspace, _, cx| {
15788 struct CopyPermalinkToLine;
15789
15790 workspace.show_toast(
15791 Toast::new(
15792 NotificationId::unique::<CopyPermalinkToLine>(),
15793 message,
15794 ),
15795 cx,
15796 )
15797 })
15798 .ok();
15799 }
15800 }
15801 })
15802 .detach();
15803 }
15804
15805 pub fn copy_file_location(
15806 &mut self,
15807 _: &CopyFileLocation,
15808 _: &mut Window,
15809 cx: &mut Context<Self>,
15810 ) {
15811 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15812 if let Some(file) = self.target_file(cx) {
15813 if let Some(path) = file.path().to_str() {
15814 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15815 }
15816 }
15817 }
15818
15819 pub fn open_permalink_to_line(
15820 &mut self,
15821 _: &OpenPermalinkToLine,
15822 window: &mut Window,
15823 cx: &mut Context<Self>,
15824 ) {
15825 let permalink_task = self.get_permalink_to_line(cx);
15826 let workspace = self.workspace();
15827
15828 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15829 Ok(permalink) => {
15830 cx.update(|_, cx| {
15831 cx.open_url(permalink.as_ref());
15832 })
15833 .ok();
15834 }
15835 Err(err) => {
15836 let message = format!("Failed to open permalink: {err}");
15837
15838 Err::<(), anyhow::Error>(err).log_err();
15839
15840 if let Some(workspace) = workspace {
15841 workspace
15842 .update(cx, |workspace, cx| {
15843 struct OpenPermalinkToLine;
15844
15845 workspace.show_toast(
15846 Toast::new(
15847 NotificationId::unique::<OpenPermalinkToLine>(),
15848 message,
15849 ),
15850 cx,
15851 )
15852 })
15853 .ok();
15854 }
15855 }
15856 })
15857 .detach();
15858 }
15859
15860 pub fn insert_uuid_v4(
15861 &mut self,
15862 _: &InsertUuidV4,
15863 window: &mut Window,
15864 cx: &mut Context<Self>,
15865 ) {
15866 self.insert_uuid(UuidVersion::V4, window, cx);
15867 }
15868
15869 pub fn insert_uuid_v7(
15870 &mut self,
15871 _: &InsertUuidV7,
15872 window: &mut Window,
15873 cx: &mut Context<Self>,
15874 ) {
15875 self.insert_uuid(UuidVersion::V7, window, cx);
15876 }
15877
15878 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15879 self.transact(window, cx, |this, window, cx| {
15880 let edits = this
15881 .selections
15882 .all::<Point>(cx)
15883 .into_iter()
15884 .map(|selection| {
15885 let uuid = match version {
15886 UuidVersion::V4 => uuid::Uuid::new_v4(),
15887 UuidVersion::V7 => uuid::Uuid::now_v7(),
15888 };
15889
15890 (selection.range(), uuid.to_string())
15891 });
15892 this.edit(edits, cx);
15893 this.refresh_inline_completion(true, false, window, cx);
15894 });
15895 }
15896
15897 pub fn open_selections_in_multibuffer(
15898 &mut self,
15899 _: &OpenSelectionsInMultibuffer,
15900 window: &mut Window,
15901 cx: &mut Context<Self>,
15902 ) {
15903 let multibuffer = self.buffer.read(cx);
15904
15905 let Some(buffer) = multibuffer.as_singleton() else {
15906 return;
15907 };
15908
15909 let Some(workspace) = self.workspace() else {
15910 return;
15911 };
15912
15913 let locations = self
15914 .selections
15915 .disjoint_anchors()
15916 .iter()
15917 .map(|range| Location {
15918 buffer: buffer.clone(),
15919 range: range.start.text_anchor..range.end.text_anchor,
15920 })
15921 .collect::<Vec<_>>();
15922
15923 let title = multibuffer.title(cx).to_string();
15924
15925 cx.spawn_in(window, async move |_, cx| {
15926 workspace.update_in(cx, |workspace, window, cx| {
15927 Self::open_locations_in_multibuffer(
15928 workspace,
15929 locations,
15930 format!("Selections for '{title}'"),
15931 false,
15932 MultibufferSelectionMode::All,
15933 window,
15934 cx,
15935 );
15936 })
15937 })
15938 .detach();
15939 }
15940
15941 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15942 /// last highlight added will be used.
15943 ///
15944 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15945 pub fn highlight_rows<T: 'static>(
15946 &mut self,
15947 range: Range<Anchor>,
15948 color: Hsla,
15949 should_autoscroll: bool,
15950 cx: &mut Context<Self>,
15951 ) {
15952 let snapshot = self.buffer().read(cx).snapshot(cx);
15953 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15954 let ix = row_highlights.binary_search_by(|highlight| {
15955 Ordering::Equal
15956 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15957 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15958 });
15959
15960 if let Err(mut ix) = ix {
15961 let index = post_inc(&mut self.highlight_order);
15962
15963 // If this range intersects with the preceding highlight, then merge it with
15964 // the preceding highlight. Otherwise insert a new highlight.
15965 let mut merged = false;
15966 if ix > 0 {
15967 let prev_highlight = &mut row_highlights[ix - 1];
15968 if prev_highlight
15969 .range
15970 .end
15971 .cmp(&range.start, &snapshot)
15972 .is_ge()
15973 {
15974 ix -= 1;
15975 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15976 prev_highlight.range.end = range.end;
15977 }
15978 merged = true;
15979 prev_highlight.index = index;
15980 prev_highlight.color = color;
15981 prev_highlight.should_autoscroll = should_autoscroll;
15982 }
15983 }
15984
15985 if !merged {
15986 row_highlights.insert(
15987 ix,
15988 RowHighlight {
15989 range: range.clone(),
15990 index,
15991 color,
15992 should_autoscroll,
15993 },
15994 );
15995 }
15996
15997 // If any of the following highlights intersect with this one, merge them.
15998 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15999 let highlight = &row_highlights[ix];
16000 if next_highlight
16001 .range
16002 .start
16003 .cmp(&highlight.range.end, &snapshot)
16004 .is_le()
16005 {
16006 if next_highlight
16007 .range
16008 .end
16009 .cmp(&highlight.range.end, &snapshot)
16010 .is_gt()
16011 {
16012 row_highlights[ix].range.end = next_highlight.range.end;
16013 }
16014 row_highlights.remove(ix + 1);
16015 } else {
16016 break;
16017 }
16018 }
16019 }
16020 }
16021
16022 /// Remove any highlighted row ranges of the given type that intersect the
16023 /// given ranges.
16024 pub fn remove_highlighted_rows<T: 'static>(
16025 &mut self,
16026 ranges_to_remove: Vec<Range<Anchor>>,
16027 cx: &mut Context<Self>,
16028 ) {
16029 let snapshot = self.buffer().read(cx).snapshot(cx);
16030 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
16031 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
16032 row_highlights.retain(|highlight| {
16033 while let Some(range_to_remove) = ranges_to_remove.peek() {
16034 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
16035 Ordering::Less | Ordering::Equal => {
16036 ranges_to_remove.next();
16037 }
16038 Ordering::Greater => {
16039 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
16040 Ordering::Less | Ordering::Equal => {
16041 return false;
16042 }
16043 Ordering::Greater => break,
16044 }
16045 }
16046 }
16047 }
16048
16049 true
16050 })
16051 }
16052
16053 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
16054 pub fn clear_row_highlights<T: 'static>(&mut self) {
16055 self.highlighted_rows.remove(&TypeId::of::<T>());
16056 }
16057
16058 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
16059 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
16060 self.highlighted_rows
16061 .get(&TypeId::of::<T>())
16062 .map_or(&[] as &[_], |vec| vec.as_slice())
16063 .iter()
16064 .map(|highlight| (highlight.range.clone(), highlight.color))
16065 }
16066
16067 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
16068 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
16069 /// Allows to ignore certain kinds of highlights.
16070 pub fn highlighted_display_rows(
16071 &self,
16072 window: &mut Window,
16073 cx: &mut App,
16074 ) -> BTreeMap<DisplayRow, LineHighlight> {
16075 let snapshot = self.snapshot(window, cx);
16076 let mut used_highlight_orders = HashMap::default();
16077 self.highlighted_rows
16078 .iter()
16079 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
16080 .fold(
16081 BTreeMap::<DisplayRow, LineHighlight>::new(),
16082 |mut unique_rows, highlight| {
16083 let start = highlight.range.start.to_display_point(&snapshot);
16084 let end = highlight.range.end.to_display_point(&snapshot);
16085 let start_row = start.row().0;
16086 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
16087 && end.column() == 0
16088 {
16089 end.row().0.saturating_sub(1)
16090 } else {
16091 end.row().0
16092 };
16093 for row in start_row..=end_row {
16094 let used_index =
16095 used_highlight_orders.entry(row).or_insert(highlight.index);
16096 if highlight.index >= *used_index {
16097 *used_index = highlight.index;
16098 unique_rows.insert(DisplayRow(row), highlight.color.into());
16099 }
16100 }
16101 unique_rows
16102 },
16103 )
16104 }
16105
16106 pub fn highlighted_display_row_for_autoscroll(
16107 &self,
16108 snapshot: &DisplaySnapshot,
16109 ) -> Option<DisplayRow> {
16110 self.highlighted_rows
16111 .values()
16112 .flat_map(|highlighted_rows| highlighted_rows.iter())
16113 .filter_map(|highlight| {
16114 if highlight.should_autoscroll {
16115 Some(highlight.range.start.to_display_point(snapshot).row())
16116 } else {
16117 None
16118 }
16119 })
16120 .min()
16121 }
16122
16123 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
16124 self.highlight_background::<SearchWithinRange>(
16125 ranges,
16126 |colors| colors.editor_document_highlight_read_background,
16127 cx,
16128 )
16129 }
16130
16131 pub fn set_breadcrumb_header(&mut self, new_header: String) {
16132 self.breadcrumb_header = Some(new_header);
16133 }
16134
16135 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
16136 self.clear_background_highlights::<SearchWithinRange>(cx);
16137 }
16138
16139 pub fn highlight_background<T: 'static>(
16140 &mut self,
16141 ranges: &[Range<Anchor>],
16142 color_fetcher: fn(&ThemeColors) -> Hsla,
16143 cx: &mut Context<Self>,
16144 ) {
16145 self.background_highlights
16146 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16147 self.scrollbar_marker_state.dirty = true;
16148 cx.notify();
16149 }
16150
16151 pub fn clear_background_highlights<T: 'static>(
16152 &mut self,
16153 cx: &mut Context<Self>,
16154 ) -> Option<BackgroundHighlight> {
16155 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
16156 if !text_highlights.1.is_empty() {
16157 self.scrollbar_marker_state.dirty = true;
16158 cx.notify();
16159 }
16160 Some(text_highlights)
16161 }
16162
16163 pub fn highlight_gutter<T: 'static>(
16164 &mut self,
16165 ranges: &[Range<Anchor>],
16166 color_fetcher: fn(&App) -> Hsla,
16167 cx: &mut Context<Self>,
16168 ) {
16169 self.gutter_highlights
16170 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16171 cx.notify();
16172 }
16173
16174 pub fn clear_gutter_highlights<T: 'static>(
16175 &mut self,
16176 cx: &mut Context<Self>,
16177 ) -> Option<GutterHighlight> {
16178 cx.notify();
16179 self.gutter_highlights.remove(&TypeId::of::<T>())
16180 }
16181
16182 #[cfg(feature = "test-support")]
16183 pub fn all_text_background_highlights(
16184 &self,
16185 window: &mut Window,
16186 cx: &mut Context<Self>,
16187 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16188 let snapshot = self.snapshot(window, cx);
16189 let buffer = &snapshot.buffer_snapshot;
16190 let start = buffer.anchor_before(0);
16191 let end = buffer.anchor_after(buffer.len());
16192 let theme = cx.theme().colors();
16193 self.background_highlights_in_range(start..end, &snapshot, theme)
16194 }
16195
16196 #[cfg(feature = "test-support")]
16197 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
16198 let snapshot = self.buffer().read(cx).snapshot(cx);
16199
16200 let highlights = self
16201 .background_highlights
16202 .get(&TypeId::of::<items::BufferSearchHighlights>());
16203
16204 if let Some((_color, ranges)) = highlights {
16205 ranges
16206 .iter()
16207 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
16208 .collect_vec()
16209 } else {
16210 vec![]
16211 }
16212 }
16213
16214 fn document_highlights_for_position<'a>(
16215 &'a self,
16216 position: Anchor,
16217 buffer: &'a MultiBufferSnapshot,
16218 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16219 let read_highlights = self
16220 .background_highlights
16221 .get(&TypeId::of::<DocumentHighlightRead>())
16222 .map(|h| &h.1);
16223 let write_highlights = self
16224 .background_highlights
16225 .get(&TypeId::of::<DocumentHighlightWrite>())
16226 .map(|h| &h.1);
16227 let left_position = position.bias_left(buffer);
16228 let right_position = position.bias_right(buffer);
16229 read_highlights
16230 .into_iter()
16231 .chain(write_highlights)
16232 .flat_map(move |ranges| {
16233 let start_ix = match ranges.binary_search_by(|probe| {
16234 let cmp = probe.end.cmp(&left_position, buffer);
16235 if cmp.is_ge() {
16236 Ordering::Greater
16237 } else {
16238 Ordering::Less
16239 }
16240 }) {
16241 Ok(i) | Err(i) => i,
16242 };
16243
16244 ranges[start_ix..]
16245 .iter()
16246 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16247 })
16248 }
16249
16250 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16251 self.background_highlights
16252 .get(&TypeId::of::<T>())
16253 .map_or(false, |(_, highlights)| !highlights.is_empty())
16254 }
16255
16256 pub fn background_highlights_in_range(
16257 &self,
16258 search_range: Range<Anchor>,
16259 display_snapshot: &DisplaySnapshot,
16260 theme: &ThemeColors,
16261 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16262 let mut results = Vec::new();
16263 for (color_fetcher, ranges) in self.background_highlights.values() {
16264 let color = color_fetcher(theme);
16265 let start_ix = match ranges.binary_search_by(|probe| {
16266 let cmp = probe
16267 .end
16268 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16269 if cmp.is_gt() {
16270 Ordering::Greater
16271 } else {
16272 Ordering::Less
16273 }
16274 }) {
16275 Ok(i) | Err(i) => i,
16276 };
16277 for range in &ranges[start_ix..] {
16278 if range
16279 .start
16280 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16281 .is_ge()
16282 {
16283 break;
16284 }
16285
16286 let start = range.start.to_display_point(display_snapshot);
16287 let end = range.end.to_display_point(display_snapshot);
16288 results.push((start..end, color))
16289 }
16290 }
16291 results
16292 }
16293
16294 pub fn background_highlight_row_ranges<T: 'static>(
16295 &self,
16296 search_range: Range<Anchor>,
16297 display_snapshot: &DisplaySnapshot,
16298 count: usize,
16299 ) -> Vec<RangeInclusive<DisplayPoint>> {
16300 let mut results = Vec::new();
16301 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16302 return vec![];
16303 };
16304
16305 let start_ix = match ranges.binary_search_by(|probe| {
16306 let cmp = probe
16307 .end
16308 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16309 if cmp.is_gt() {
16310 Ordering::Greater
16311 } else {
16312 Ordering::Less
16313 }
16314 }) {
16315 Ok(i) | Err(i) => i,
16316 };
16317 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16318 if let (Some(start_display), Some(end_display)) = (start, end) {
16319 results.push(
16320 start_display.to_display_point(display_snapshot)
16321 ..=end_display.to_display_point(display_snapshot),
16322 );
16323 }
16324 };
16325 let mut start_row: Option<Point> = None;
16326 let mut end_row: Option<Point> = None;
16327 if ranges.len() > count {
16328 return Vec::new();
16329 }
16330 for range in &ranges[start_ix..] {
16331 if range
16332 .start
16333 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16334 .is_ge()
16335 {
16336 break;
16337 }
16338 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16339 if let Some(current_row) = &end_row {
16340 if end.row == current_row.row {
16341 continue;
16342 }
16343 }
16344 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16345 if start_row.is_none() {
16346 assert_eq!(end_row, None);
16347 start_row = Some(start);
16348 end_row = Some(end);
16349 continue;
16350 }
16351 if let Some(current_end) = end_row.as_mut() {
16352 if start.row > current_end.row + 1 {
16353 push_region(start_row, end_row);
16354 start_row = Some(start);
16355 end_row = Some(end);
16356 } else {
16357 // Merge two hunks.
16358 *current_end = end;
16359 }
16360 } else {
16361 unreachable!();
16362 }
16363 }
16364 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16365 push_region(start_row, end_row);
16366 results
16367 }
16368
16369 pub fn gutter_highlights_in_range(
16370 &self,
16371 search_range: Range<Anchor>,
16372 display_snapshot: &DisplaySnapshot,
16373 cx: &App,
16374 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16375 let mut results = Vec::new();
16376 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16377 let color = color_fetcher(cx);
16378 let start_ix = match ranges.binary_search_by(|probe| {
16379 let cmp = probe
16380 .end
16381 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16382 if cmp.is_gt() {
16383 Ordering::Greater
16384 } else {
16385 Ordering::Less
16386 }
16387 }) {
16388 Ok(i) | Err(i) => i,
16389 };
16390 for range in &ranges[start_ix..] {
16391 if range
16392 .start
16393 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16394 .is_ge()
16395 {
16396 break;
16397 }
16398
16399 let start = range.start.to_display_point(display_snapshot);
16400 let end = range.end.to_display_point(display_snapshot);
16401 results.push((start..end, color))
16402 }
16403 }
16404 results
16405 }
16406
16407 /// Get the text ranges corresponding to the redaction query
16408 pub fn redacted_ranges(
16409 &self,
16410 search_range: Range<Anchor>,
16411 display_snapshot: &DisplaySnapshot,
16412 cx: &App,
16413 ) -> Vec<Range<DisplayPoint>> {
16414 display_snapshot
16415 .buffer_snapshot
16416 .redacted_ranges(search_range, |file| {
16417 if let Some(file) = file {
16418 file.is_private()
16419 && EditorSettings::get(
16420 Some(SettingsLocation {
16421 worktree_id: file.worktree_id(cx),
16422 path: file.path().as_ref(),
16423 }),
16424 cx,
16425 )
16426 .redact_private_values
16427 } else {
16428 false
16429 }
16430 })
16431 .map(|range| {
16432 range.start.to_display_point(display_snapshot)
16433 ..range.end.to_display_point(display_snapshot)
16434 })
16435 .collect()
16436 }
16437
16438 pub fn highlight_text<T: 'static>(
16439 &mut self,
16440 ranges: Vec<Range<Anchor>>,
16441 style: HighlightStyle,
16442 cx: &mut Context<Self>,
16443 ) {
16444 self.display_map.update(cx, |map, _| {
16445 map.highlight_text(TypeId::of::<T>(), ranges, style)
16446 });
16447 cx.notify();
16448 }
16449
16450 pub(crate) fn highlight_inlays<T: 'static>(
16451 &mut self,
16452 highlights: Vec<InlayHighlight>,
16453 style: HighlightStyle,
16454 cx: &mut Context<Self>,
16455 ) {
16456 self.display_map.update(cx, |map, _| {
16457 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16458 });
16459 cx.notify();
16460 }
16461
16462 pub fn text_highlights<'a, T: 'static>(
16463 &'a self,
16464 cx: &'a App,
16465 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16466 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16467 }
16468
16469 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16470 let cleared = self
16471 .display_map
16472 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16473 if cleared {
16474 cx.notify();
16475 }
16476 }
16477
16478 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16479 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16480 && self.focus_handle.is_focused(window)
16481 }
16482
16483 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16484 self.show_cursor_when_unfocused = is_enabled;
16485 cx.notify();
16486 }
16487
16488 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16489 cx.notify();
16490 }
16491
16492 fn on_buffer_event(
16493 &mut self,
16494 multibuffer: &Entity<MultiBuffer>,
16495 event: &multi_buffer::Event,
16496 window: &mut Window,
16497 cx: &mut Context<Self>,
16498 ) {
16499 match event {
16500 multi_buffer::Event::Edited {
16501 singleton_buffer_edited,
16502 edited_buffer: buffer_edited,
16503 } => {
16504 self.scrollbar_marker_state.dirty = true;
16505 self.active_indent_guides_state.dirty = true;
16506 self.refresh_active_diagnostics(cx);
16507 self.refresh_code_actions(window, cx);
16508 if self.has_active_inline_completion() {
16509 self.update_visible_inline_completion(window, cx);
16510 }
16511 if let Some(buffer) = buffer_edited {
16512 let buffer_id = buffer.read(cx).remote_id();
16513 if !self.registered_buffers.contains_key(&buffer_id) {
16514 if let Some(project) = self.project.as_ref() {
16515 project.update(cx, |project, cx| {
16516 self.registered_buffers.insert(
16517 buffer_id,
16518 project.register_buffer_with_language_servers(&buffer, cx),
16519 );
16520 })
16521 }
16522 }
16523 }
16524 cx.emit(EditorEvent::BufferEdited);
16525 cx.emit(SearchEvent::MatchesInvalidated);
16526 if *singleton_buffer_edited {
16527 if let Some(project) = &self.project {
16528 #[allow(clippy::mutable_key_type)]
16529 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16530 multibuffer
16531 .all_buffers()
16532 .into_iter()
16533 .filter_map(|buffer| {
16534 buffer.update(cx, |buffer, cx| {
16535 let language = buffer.language()?;
16536 let should_discard = project.update(cx, |project, cx| {
16537 project.is_local()
16538 && !project.has_language_servers_for(buffer, cx)
16539 });
16540 should_discard.not().then_some(language.clone())
16541 })
16542 })
16543 .collect::<HashSet<_>>()
16544 });
16545 if !languages_affected.is_empty() {
16546 self.refresh_inlay_hints(
16547 InlayHintRefreshReason::BufferEdited(languages_affected),
16548 cx,
16549 );
16550 }
16551 }
16552 }
16553
16554 let Some(project) = &self.project else { return };
16555 let (telemetry, is_via_ssh) = {
16556 let project = project.read(cx);
16557 let telemetry = project.client().telemetry().clone();
16558 let is_via_ssh = project.is_via_ssh();
16559 (telemetry, is_via_ssh)
16560 };
16561 refresh_linked_ranges(self, window, cx);
16562 telemetry.log_edit_event("editor", is_via_ssh);
16563 }
16564 multi_buffer::Event::ExcerptsAdded {
16565 buffer,
16566 predecessor,
16567 excerpts,
16568 } => {
16569 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16570 let buffer_id = buffer.read(cx).remote_id();
16571 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16572 if let Some(project) = &self.project {
16573 get_uncommitted_diff_for_buffer(
16574 project,
16575 [buffer.clone()],
16576 self.buffer.clone(),
16577 cx,
16578 )
16579 .detach();
16580 }
16581 }
16582 cx.emit(EditorEvent::ExcerptsAdded {
16583 buffer: buffer.clone(),
16584 predecessor: *predecessor,
16585 excerpts: excerpts.clone(),
16586 });
16587 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16588 }
16589 multi_buffer::Event::ExcerptsRemoved { ids } => {
16590 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16591 let buffer = self.buffer.read(cx);
16592 self.registered_buffers
16593 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16594 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16595 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16596 }
16597 multi_buffer::Event::ExcerptsEdited {
16598 excerpt_ids,
16599 buffer_ids,
16600 } => {
16601 self.display_map.update(cx, |map, cx| {
16602 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16603 });
16604 cx.emit(EditorEvent::ExcerptsEdited {
16605 ids: excerpt_ids.clone(),
16606 })
16607 }
16608 multi_buffer::Event::ExcerptsExpanded { ids } => {
16609 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16610 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16611 }
16612 multi_buffer::Event::Reparsed(buffer_id) => {
16613 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16614 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16615
16616 cx.emit(EditorEvent::Reparsed(*buffer_id));
16617 }
16618 multi_buffer::Event::DiffHunksToggled => {
16619 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16620 }
16621 multi_buffer::Event::LanguageChanged(buffer_id) => {
16622 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16623 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16624 cx.emit(EditorEvent::Reparsed(*buffer_id));
16625 cx.notify();
16626 }
16627 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16628 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16629 multi_buffer::Event::FileHandleChanged
16630 | multi_buffer::Event::Reloaded
16631 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16632 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16633 multi_buffer::Event::DiagnosticsUpdated => {
16634 self.refresh_active_diagnostics(cx);
16635 self.refresh_inline_diagnostics(true, window, cx);
16636 self.scrollbar_marker_state.dirty = true;
16637 cx.notify();
16638 }
16639 _ => {}
16640 };
16641 }
16642
16643 fn on_display_map_changed(
16644 &mut self,
16645 _: Entity<DisplayMap>,
16646 _: &mut Window,
16647 cx: &mut Context<Self>,
16648 ) {
16649 cx.notify();
16650 }
16651
16652 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16653 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16654 self.update_edit_prediction_settings(cx);
16655 self.refresh_inline_completion(true, false, window, cx);
16656 self.refresh_inlay_hints(
16657 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16658 self.selections.newest_anchor().head(),
16659 &self.buffer.read(cx).snapshot(cx),
16660 cx,
16661 )),
16662 cx,
16663 );
16664
16665 let old_cursor_shape = self.cursor_shape;
16666
16667 {
16668 let editor_settings = EditorSettings::get_global(cx);
16669 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16670 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16671 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16672 }
16673
16674 if old_cursor_shape != self.cursor_shape {
16675 cx.emit(EditorEvent::CursorShapeChanged);
16676 }
16677
16678 let project_settings = ProjectSettings::get_global(cx);
16679 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16680
16681 if self.mode == EditorMode::Full {
16682 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16683 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16684 if self.show_inline_diagnostics != show_inline_diagnostics {
16685 self.show_inline_diagnostics = show_inline_diagnostics;
16686 self.refresh_inline_diagnostics(false, window, cx);
16687 }
16688
16689 if self.git_blame_inline_enabled != inline_blame_enabled {
16690 self.toggle_git_blame_inline_internal(false, window, cx);
16691 }
16692 }
16693
16694 cx.notify();
16695 }
16696
16697 pub fn set_searchable(&mut self, searchable: bool) {
16698 self.searchable = searchable;
16699 }
16700
16701 pub fn searchable(&self) -> bool {
16702 self.searchable
16703 }
16704
16705 fn open_proposed_changes_editor(
16706 &mut self,
16707 _: &OpenProposedChangesEditor,
16708 window: &mut Window,
16709 cx: &mut Context<Self>,
16710 ) {
16711 let Some(workspace) = self.workspace() else {
16712 cx.propagate();
16713 return;
16714 };
16715
16716 let selections = self.selections.all::<usize>(cx);
16717 let multi_buffer = self.buffer.read(cx);
16718 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16719 let mut new_selections_by_buffer = HashMap::default();
16720 for selection in selections {
16721 for (buffer, range, _) in
16722 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16723 {
16724 let mut range = range.to_point(buffer);
16725 range.start.column = 0;
16726 range.end.column = buffer.line_len(range.end.row);
16727 new_selections_by_buffer
16728 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16729 .or_insert(Vec::new())
16730 .push(range)
16731 }
16732 }
16733
16734 let proposed_changes_buffers = new_selections_by_buffer
16735 .into_iter()
16736 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16737 .collect::<Vec<_>>();
16738 let proposed_changes_editor = cx.new(|cx| {
16739 ProposedChangesEditor::new(
16740 "Proposed changes",
16741 proposed_changes_buffers,
16742 self.project.clone(),
16743 window,
16744 cx,
16745 )
16746 });
16747
16748 window.defer(cx, move |window, cx| {
16749 workspace.update(cx, |workspace, cx| {
16750 workspace.active_pane().update(cx, |pane, cx| {
16751 pane.add_item(
16752 Box::new(proposed_changes_editor),
16753 true,
16754 true,
16755 None,
16756 window,
16757 cx,
16758 );
16759 });
16760 });
16761 });
16762 }
16763
16764 pub fn open_excerpts_in_split(
16765 &mut self,
16766 _: &OpenExcerptsSplit,
16767 window: &mut Window,
16768 cx: &mut Context<Self>,
16769 ) {
16770 self.open_excerpts_common(None, true, window, cx)
16771 }
16772
16773 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16774 self.open_excerpts_common(None, false, window, cx)
16775 }
16776
16777 fn open_excerpts_common(
16778 &mut self,
16779 jump_data: Option<JumpData>,
16780 split: bool,
16781 window: &mut Window,
16782 cx: &mut Context<Self>,
16783 ) {
16784 let Some(workspace) = self.workspace() else {
16785 cx.propagate();
16786 return;
16787 };
16788
16789 if self.buffer.read(cx).is_singleton() {
16790 cx.propagate();
16791 return;
16792 }
16793
16794 let mut new_selections_by_buffer = HashMap::default();
16795 match &jump_data {
16796 Some(JumpData::MultiBufferPoint {
16797 excerpt_id,
16798 position,
16799 anchor,
16800 line_offset_from_top,
16801 }) => {
16802 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16803 if let Some(buffer) = multi_buffer_snapshot
16804 .buffer_id_for_excerpt(*excerpt_id)
16805 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16806 {
16807 let buffer_snapshot = buffer.read(cx).snapshot();
16808 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16809 language::ToPoint::to_point(anchor, &buffer_snapshot)
16810 } else {
16811 buffer_snapshot.clip_point(*position, Bias::Left)
16812 };
16813 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16814 new_selections_by_buffer.insert(
16815 buffer,
16816 (
16817 vec![jump_to_offset..jump_to_offset],
16818 Some(*line_offset_from_top),
16819 ),
16820 );
16821 }
16822 }
16823 Some(JumpData::MultiBufferRow {
16824 row,
16825 line_offset_from_top,
16826 }) => {
16827 let point = MultiBufferPoint::new(row.0, 0);
16828 if let Some((buffer, buffer_point, _)) =
16829 self.buffer.read(cx).point_to_buffer_point(point, cx)
16830 {
16831 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16832 new_selections_by_buffer
16833 .entry(buffer)
16834 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16835 .0
16836 .push(buffer_offset..buffer_offset)
16837 }
16838 }
16839 None => {
16840 let selections = self.selections.all::<usize>(cx);
16841 let multi_buffer = self.buffer.read(cx);
16842 for selection in selections {
16843 for (snapshot, range, _, anchor) in multi_buffer
16844 .snapshot(cx)
16845 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16846 {
16847 if let Some(anchor) = anchor {
16848 // selection is in a deleted hunk
16849 let Some(buffer_id) = anchor.buffer_id else {
16850 continue;
16851 };
16852 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16853 continue;
16854 };
16855 let offset = text::ToOffset::to_offset(
16856 &anchor.text_anchor,
16857 &buffer_handle.read(cx).snapshot(),
16858 );
16859 let range = offset..offset;
16860 new_selections_by_buffer
16861 .entry(buffer_handle)
16862 .or_insert((Vec::new(), None))
16863 .0
16864 .push(range)
16865 } else {
16866 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16867 else {
16868 continue;
16869 };
16870 new_selections_by_buffer
16871 .entry(buffer_handle)
16872 .or_insert((Vec::new(), None))
16873 .0
16874 .push(range)
16875 }
16876 }
16877 }
16878 }
16879 }
16880
16881 if new_selections_by_buffer.is_empty() {
16882 return;
16883 }
16884
16885 // We defer the pane interaction because we ourselves are a workspace item
16886 // and activating a new item causes the pane to call a method on us reentrantly,
16887 // which panics if we're on the stack.
16888 window.defer(cx, move |window, cx| {
16889 workspace.update(cx, |workspace, cx| {
16890 let pane = if split {
16891 workspace.adjacent_pane(window, cx)
16892 } else {
16893 workspace.active_pane().clone()
16894 };
16895
16896 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16897 let editor = buffer
16898 .read(cx)
16899 .file()
16900 .is_none()
16901 .then(|| {
16902 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16903 // so `workspace.open_project_item` will never find them, always opening a new editor.
16904 // Instead, we try to activate the existing editor in the pane first.
16905 let (editor, pane_item_index) =
16906 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16907 let editor = item.downcast::<Editor>()?;
16908 let singleton_buffer =
16909 editor.read(cx).buffer().read(cx).as_singleton()?;
16910 if singleton_buffer == buffer {
16911 Some((editor, i))
16912 } else {
16913 None
16914 }
16915 })?;
16916 pane.update(cx, |pane, cx| {
16917 pane.activate_item(pane_item_index, true, true, window, cx)
16918 });
16919 Some(editor)
16920 })
16921 .flatten()
16922 .unwrap_or_else(|| {
16923 workspace.open_project_item::<Self>(
16924 pane.clone(),
16925 buffer,
16926 true,
16927 true,
16928 window,
16929 cx,
16930 )
16931 });
16932
16933 editor.update(cx, |editor, cx| {
16934 let autoscroll = match scroll_offset {
16935 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16936 None => Autoscroll::newest(),
16937 };
16938 let nav_history = editor.nav_history.take();
16939 editor.change_selections(Some(autoscroll), window, cx, |s| {
16940 s.select_ranges(ranges);
16941 });
16942 editor.nav_history = nav_history;
16943 });
16944 }
16945 })
16946 });
16947 }
16948
16949 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16950 let snapshot = self.buffer.read(cx).read(cx);
16951 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16952 Some(
16953 ranges
16954 .iter()
16955 .map(move |range| {
16956 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16957 })
16958 .collect(),
16959 )
16960 }
16961
16962 fn selection_replacement_ranges(
16963 &self,
16964 range: Range<OffsetUtf16>,
16965 cx: &mut App,
16966 ) -> Vec<Range<OffsetUtf16>> {
16967 let selections = self.selections.all::<OffsetUtf16>(cx);
16968 let newest_selection = selections
16969 .iter()
16970 .max_by_key(|selection| selection.id)
16971 .unwrap();
16972 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16973 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16974 let snapshot = self.buffer.read(cx).read(cx);
16975 selections
16976 .into_iter()
16977 .map(|mut selection| {
16978 selection.start.0 =
16979 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16980 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16981 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16982 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16983 })
16984 .collect()
16985 }
16986
16987 fn report_editor_event(
16988 &self,
16989 event_type: &'static str,
16990 file_extension: Option<String>,
16991 cx: &App,
16992 ) {
16993 if cfg!(any(test, feature = "test-support")) {
16994 return;
16995 }
16996
16997 let Some(project) = &self.project else { return };
16998
16999 // If None, we are in a file without an extension
17000 let file = self
17001 .buffer
17002 .read(cx)
17003 .as_singleton()
17004 .and_then(|b| b.read(cx).file());
17005 let file_extension = file_extension.or(file
17006 .as_ref()
17007 .and_then(|file| Path::new(file.file_name(cx)).extension())
17008 .and_then(|e| e.to_str())
17009 .map(|a| a.to_string()));
17010
17011 let vim_mode = cx
17012 .global::<SettingsStore>()
17013 .raw_user_settings()
17014 .get("vim_mode")
17015 == Some(&serde_json::Value::Bool(true));
17016
17017 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
17018 let copilot_enabled = edit_predictions_provider
17019 == language::language_settings::EditPredictionProvider::Copilot;
17020 let copilot_enabled_for_language = self
17021 .buffer
17022 .read(cx)
17023 .language_settings(cx)
17024 .show_edit_predictions;
17025
17026 let project = project.read(cx);
17027 telemetry::event!(
17028 event_type,
17029 file_extension,
17030 vim_mode,
17031 copilot_enabled,
17032 copilot_enabled_for_language,
17033 edit_predictions_provider,
17034 is_via_ssh = project.is_via_ssh(),
17035 );
17036 }
17037
17038 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
17039 /// with each line being an array of {text, highlight} objects.
17040 fn copy_highlight_json(
17041 &mut self,
17042 _: &CopyHighlightJson,
17043 window: &mut Window,
17044 cx: &mut Context<Self>,
17045 ) {
17046 #[derive(Serialize)]
17047 struct Chunk<'a> {
17048 text: String,
17049 highlight: Option<&'a str>,
17050 }
17051
17052 let snapshot = self.buffer.read(cx).snapshot(cx);
17053 let range = self
17054 .selected_text_range(false, window, cx)
17055 .and_then(|selection| {
17056 if selection.range.is_empty() {
17057 None
17058 } else {
17059 Some(selection.range)
17060 }
17061 })
17062 .unwrap_or_else(|| 0..snapshot.len());
17063
17064 let chunks = snapshot.chunks(range, true);
17065 let mut lines = Vec::new();
17066 let mut line: VecDeque<Chunk> = VecDeque::new();
17067
17068 let Some(style) = self.style.as_ref() else {
17069 return;
17070 };
17071
17072 for chunk in chunks {
17073 let highlight = chunk
17074 .syntax_highlight_id
17075 .and_then(|id| id.name(&style.syntax));
17076 let mut chunk_lines = chunk.text.split('\n').peekable();
17077 while let Some(text) = chunk_lines.next() {
17078 let mut merged_with_last_token = false;
17079 if let Some(last_token) = line.back_mut() {
17080 if last_token.highlight == highlight {
17081 last_token.text.push_str(text);
17082 merged_with_last_token = true;
17083 }
17084 }
17085
17086 if !merged_with_last_token {
17087 line.push_back(Chunk {
17088 text: text.into(),
17089 highlight,
17090 });
17091 }
17092
17093 if chunk_lines.peek().is_some() {
17094 if line.len() > 1 && line.front().unwrap().text.is_empty() {
17095 line.pop_front();
17096 }
17097 if line.len() > 1 && line.back().unwrap().text.is_empty() {
17098 line.pop_back();
17099 }
17100
17101 lines.push(mem::take(&mut line));
17102 }
17103 }
17104 }
17105
17106 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
17107 return;
17108 };
17109 cx.write_to_clipboard(ClipboardItem::new_string(lines));
17110 }
17111
17112 pub fn open_context_menu(
17113 &mut self,
17114 _: &OpenContextMenu,
17115 window: &mut Window,
17116 cx: &mut Context<Self>,
17117 ) {
17118 self.request_autoscroll(Autoscroll::newest(), cx);
17119 let position = self.selections.newest_display(cx).start;
17120 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
17121 }
17122
17123 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
17124 &self.inlay_hint_cache
17125 }
17126
17127 pub fn replay_insert_event(
17128 &mut self,
17129 text: &str,
17130 relative_utf16_range: Option<Range<isize>>,
17131 window: &mut Window,
17132 cx: &mut Context<Self>,
17133 ) {
17134 if !self.input_enabled {
17135 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17136 return;
17137 }
17138 if let Some(relative_utf16_range) = relative_utf16_range {
17139 let selections = self.selections.all::<OffsetUtf16>(cx);
17140 self.change_selections(None, window, cx, |s| {
17141 let new_ranges = selections.into_iter().map(|range| {
17142 let start = OffsetUtf16(
17143 range
17144 .head()
17145 .0
17146 .saturating_add_signed(relative_utf16_range.start),
17147 );
17148 let end = OffsetUtf16(
17149 range
17150 .head()
17151 .0
17152 .saturating_add_signed(relative_utf16_range.end),
17153 );
17154 start..end
17155 });
17156 s.select_ranges(new_ranges);
17157 });
17158 }
17159
17160 self.handle_input(text, window, cx);
17161 }
17162
17163 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
17164 let Some(provider) = self.semantics_provider.as_ref() else {
17165 return false;
17166 };
17167
17168 let mut supports = false;
17169 self.buffer().update(cx, |this, cx| {
17170 this.for_each_buffer(|buffer| {
17171 supports |= provider.supports_inlay_hints(buffer, cx);
17172 });
17173 });
17174
17175 supports
17176 }
17177
17178 pub fn is_focused(&self, window: &Window) -> bool {
17179 self.focus_handle.is_focused(window)
17180 }
17181
17182 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17183 cx.emit(EditorEvent::Focused);
17184
17185 if let Some(descendant) = self
17186 .last_focused_descendant
17187 .take()
17188 .and_then(|descendant| descendant.upgrade())
17189 {
17190 window.focus(&descendant);
17191 } else {
17192 if let Some(blame) = self.blame.as_ref() {
17193 blame.update(cx, GitBlame::focus)
17194 }
17195
17196 self.blink_manager.update(cx, BlinkManager::enable);
17197 self.show_cursor_names(window, cx);
17198 self.buffer.update(cx, |buffer, cx| {
17199 buffer.finalize_last_transaction(cx);
17200 if self.leader_peer_id.is_none() {
17201 buffer.set_active_selections(
17202 &self.selections.disjoint_anchors(),
17203 self.selections.line_mode,
17204 self.cursor_shape,
17205 cx,
17206 );
17207 }
17208 });
17209 }
17210 }
17211
17212 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17213 cx.emit(EditorEvent::FocusedIn)
17214 }
17215
17216 fn handle_focus_out(
17217 &mut self,
17218 event: FocusOutEvent,
17219 _window: &mut Window,
17220 cx: &mut Context<Self>,
17221 ) {
17222 if event.blurred != self.focus_handle {
17223 self.last_focused_descendant = Some(event.blurred);
17224 }
17225 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17226 }
17227
17228 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17229 self.blink_manager.update(cx, BlinkManager::disable);
17230 self.buffer
17231 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17232
17233 if let Some(blame) = self.blame.as_ref() {
17234 blame.update(cx, GitBlame::blur)
17235 }
17236 if !self.hover_state.focused(window, cx) {
17237 hide_hover(self, cx);
17238 }
17239 if !self
17240 .context_menu
17241 .borrow()
17242 .as_ref()
17243 .is_some_and(|context_menu| context_menu.focused(window, cx))
17244 {
17245 self.hide_context_menu(window, cx);
17246 }
17247 self.discard_inline_completion(false, cx);
17248 cx.emit(EditorEvent::Blurred);
17249 cx.notify();
17250 }
17251
17252 pub fn register_action<A: Action>(
17253 &mut self,
17254 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17255 ) -> Subscription {
17256 let id = self.next_editor_action_id.post_inc();
17257 let listener = Arc::new(listener);
17258 self.editor_actions.borrow_mut().insert(
17259 id,
17260 Box::new(move |window, _| {
17261 let listener = listener.clone();
17262 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17263 let action = action.downcast_ref().unwrap();
17264 if phase == DispatchPhase::Bubble {
17265 listener(action, window, cx)
17266 }
17267 })
17268 }),
17269 );
17270
17271 let editor_actions = self.editor_actions.clone();
17272 Subscription::new(move || {
17273 editor_actions.borrow_mut().remove(&id);
17274 })
17275 }
17276
17277 pub fn file_header_size(&self) -> u32 {
17278 FILE_HEADER_HEIGHT
17279 }
17280
17281 pub fn restore(
17282 &mut self,
17283 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17284 window: &mut Window,
17285 cx: &mut Context<Self>,
17286 ) {
17287 let workspace = self.workspace();
17288 let project = self.project.as_ref();
17289 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17290 let mut tasks = Vec::new();
17291 for (buffer_id, changes) in revert_changes {
17292 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17293 buffer.update(cx, |buffer, cx| {
17294 buffer.edit(
17295 changes
17296 .into_iter()
17297 .map(|(range, text)| (range, text.to_string())),
17298 None,
17299 cx,
17300 );
17301 });
17302
17303 if let Some(project) =
17304 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17305 {
17306 project.update(cx, |project, cx| {
17307 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17308 })
17309 }
17310 }
17311 }
17312 tasks
17313 });
17314 cx.spawn_in(window, async move |_, cx| {
17315 for (buffer, task) in save_tasks {
17316 let result = task.await;
17317 if result.is_err() {
17318 let Some(path) = buffer
17319 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17320 .ok()
17321 else {
17322 continue;
17323 };
17324 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17325 let Some(task) = cx
17326 .update_window_entity(&workspace, |workspace, window, cx| {
17327 workspace
17328 .open_path_preview(path, None, false, false, false, window, cx)
17329 })
17330 .ok()
17331 else {
17332 continue;
17333 };
17334 task.await.log_err();
17335 }
17336 }
17337 }
17338 })
17339 .detach();
17340 self.change_selections(None, window, cx, |selections| selections.refresh());
17341 }
17342
17343 pub fn to_pixel_point(
17344 &self,
17345 source: multi_buffer::Anchor,
17346 editor_snapshot: &EditorSnapshot,
17347 window: &mut Window,
17348 ) -> Option<gpui::Point<Pixels>> {
17349 let source_point = source.to_display_point(editor_snapshot);
17350 self.display_to_pixel_point(source_point, editor_snapshot, window)
17351 }
17352
17353 pub fn display_to_pixel_point(
17354 &self,
17355 source: DisplayPoint,
17356 editor_snapshot: &EditorSnapshot,
17357 window: &mut Window,
17358 ) -> Option<gpui::Point<Pixels>> {
17359 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17360 let text_layout_details = self.text_layout_details(window);
17361 let scroll_top = text_layout_details
17362 .scroll_anchor
17363 .scroll_position(editor_snapshot)
17364 .y;
17365
17366 if source.row().as_f32() < scroll_top.floor() {
17367 return None;
17368 }
17369 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17370 let source_y = line_height * (source.row().as_f32() - scroll_top);
17371 Some(gpui::Point::new(source_x, source_y))
17372 }
17373
17374 pub fn has_visible_completions_menu(&self) -> bool {
17375 !self.edit_prediction_preview_is_active()
17376 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17377 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17378 })
17379 }
17380
17381 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17382 self.addons
17383 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17384 }
17385
17386 pub fn unregister_addon<T: Addon>(&mut self) {
17387 self.addons.remove(&std::any::TypeId::of::<T>());
17388 }
17389
17390 pub fn addon<T: Addon>(&self) -> Option<&T> {
17391 let type_id = std::any::TypeId::of::<T>();
17392 self.addons
17393 .get(&type_id)
17394 .and_then(|item| item.to_any().downcast_ref::<T>())
17395 }
17396
17397 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17398 let text_layout_details = self.text_layout_details(window);
17399 let style = &text_layout_details.editor_style;
17400 let font_id = window.text_system().resolve_font(&style.text.font());
17401 let font_size = style.text.font_size.to_pixels(window.rem_size());
17402 let line_height = style.text.line_height_in_pixels(window.rem_size());
17403 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17404
17405 gpui::Size::new(em_width, line_height)
17406 }
17407
17408 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17409 self.load_diff_task.clone()
17410 }
17411
17412 fn read_metadata_from_db(
17413 &mut self,
17414 item_id: u64,
17415 workspace_id: WorkspaceId,
17416 window: &mut Window,
17417 cx: &mut Context<Editor>,
17418 ) {
17419 if self.is_singleton(cx)
17420 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17421 {
17422 let buffer_snapshot = OnceCell::new();
17423
17424 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17425 if !selections.is_empty() {
17426 let snapshot =
17427 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17428 self.change_selections(None, window, cx, |s| {
17429 s.select_ranges(selections.into_iter().map(|(start, end)| {
17430 snapshot.clip_offset(start, Bias::Left)
17431 ..snapshot.clip_offset(end, Bias::Right)
17432 }));
17433 });
17434 }
17435 };
17436
17437 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17438 if !folds.is_empty() {
17439 let snapshot =
17440 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17441 self.fold_ranges(
17442 folds
17443 .into_iter()
17444 .map(|(start, end)| {
17445 snapshot.clip_offset(start, Bias::Left)
17446 ..snapshot.clip_offset(end, Bias::Right)
17447 })
17448 .collect(),
17449 false,
17450 window,
17451 cx,
17452 );
17453 }
17454 }
17455 }
17456
17457 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17458 }
17459}
17460
17461fn insert_extra_newline_brackets(
17462 buffer: &MultiBufferSnapshot,
17463 range: Range<usize>,
17464 language: &language::LanguageScope,
17465) -> bool {
17466 let leading_whitespace_len = buffer
17467 .reversed_chars_at(range.start)
17468 .take_while(|c| c.is_whitespace() && *c != '\n')
17469 .map(|c| c.len_utf8())
17470 .sum::<usize>();
17471 let trailing_whitespace_len = buffer
17472 .chars_at(range.end)
17473 .take_while(|c| c.is_whitespace() && *c != '\n')
17474 .map(|c| c.len_utf8())
17475 .sum::<usize>();
17476 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17477
17478 language.brackets().any(|(pair, enabled)| {
17479 let pair_start = pair.start.trim_end();
17480 let pair_end = pair.end.trim_start();
17481
17482 enabled
17483 && pair.newline
17484 && buffer.contains_str_at(range.end, pair_end)
17485 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17486 })
17487}
17488
17489fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17490 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17491 [(buffer, range, _)] => (*buffer, range.clone()),
17492 _ => return false,
17493 };
17494 let pair = {
17495 let mut result: Option<BracketMatch> = None;
17496
17497 for pair in buffer
17498 .all_bracket_ranges(range.clone())
17499 .filter(move |pair| {
17500 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17501 })
17502 {
17503 let len = pair.close_range.end - pair.open_range.start;
17504
17505 if let Some(existing) = &result {
17506 let existing_len = existing.close_range.end - existing.open_range.start;
17507 if len > existing_len {
17508 continue;
17509 }
17510 }
17511
17512 result = Some(pair);
17513 }
17514
17515 result
17516 };
17517 let Some(pair) = pair else {
17518 return false;
17519 };
17520 pair.newline_only
17521 && buffer
17522 .chars_for_range(pair.open_range.end..range.start)
17523 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17524 .all(|c| c.is_whitespace() && c != '\n')
17525}
17526
17527fn get_uncommitted_diff_for_buffer(
17528 project: &Entity<Project>,
17529 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17530 buffer: Entity<MultiBuffer>,
17531 cx: &mut App,
17532) -> Task<()> {
17533 let mut tasks = Vec::new();
17534 project.update(cx, |project, cx| {
17535 for buffer in buffers {
17536 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17537 }
17538 });
17539 cx.spawn(async move |cx| {
17540 let diffs = future::join_all(tasks).await;
17541 buffer
17542 .update(cx, |buffer, cx| {
17543 for diff in diffs.into_iter().flatten() {
17544 buffer.add_diff(diff, cx);
17545 }
17546 })
17547 .ok();
17548 })
17549}
17550
17551fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17552 let tab_size = tab_size.get() as usize;
17553 let mut width = offset;
17554
17555 for ch in text.chars() {
17556 width += if ch == '\t' {
17557 tab_size - (width % tab_size)
17558 } else {
17559 1
17560 };
17561 }
17562
17563 width - offset
17564}
17565
17566#[cfg(test)]
17567mod tests {
17568 use super::*;
17569
17570 #[test]
17571 fn test_string_size_with_expanded_tabs() {
17572 let nz = |val| NonZeroU32::new(val).unwrap();
17573 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17574 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17575 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17576 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17577 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17578 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17579 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17580 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17581 }
17582}
17583
17584/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17585struct WordBreakingTokenizer<'a> {
17586 input: &'a str,
17587}
17588
17589impl<'a> WordBreakingTokenizer<'a> {
17590 fn new(input: &'a str) -> Self {
17591 Self { input }
17592 }
17593}
17594
17595fn is_char_ideographic(ch: char) -> bool {
17596 use unicode_script::Script::*;
17597 use unicode_script::UnicodeScript;
17598 matches!(ch.script(), Han | Tangut | Yi)
17599}
17600
17601fn is_grapheme_ideographic(text: &str) -> bool {
17602 text.chars().any(is_char_ideographic)
17603}
17604
17605fn is_grapheme_whitespace(text: &str) -> bool {
17606 text.chars().any(|x| x.is_whitespace())
17607}
17608
17609fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17610 text.chars().next().map_or(false, |ch| {
17611 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17612 })
17613}
17614
17615#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17616enum WordBreakToken<'a> {
17617 Word { token: &'a str, grapheme_len: usize },
17618 InlineWhitespace { token: &'a str, grapheme_len: usize },
17619 Newline,
17620}
17621
17622impl<'a> Iterator for WordBreakingTokenizer<'a> {
17623 /// Yields a span, the count of graphemes in the token, and whether it was
17624 /// whitespace. Note that it also breaks at word boundaries.
17625 type Item = WordBreakToken<'a>;
17626
17627 fn next(&mut self) -> Option<Self::Item> {
17628 use unicode_segmentation::UnicodeSegmentation;
17629 if self.input.is_empty() {
17630 return None;
17631 }
17632
17633 let mut iter = self.input.graphemes(true).peekable();
17634 let mut offset = 0;
17635 let mut grapheme_len = 0;
17636 if let Some(first_grapheme) = iter.next() {
17637 let is_newline = first_grapheme == "\n";
17638 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17639 offset += first_grapheme.len();
17640 grapheme_len += 1;
17641 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17642 if let Some(grapheme) = iter.peek().copied() {
17643 if should_stay_with_preceding_ideograph(grapheme) {
17644 offset += grapheme.len();
17645 grapheme_len += 1;
17646 }
17647 }
17648 } else {
17649 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17650 let mut next_word_bound = words.peek().copied();
17651 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17652 next_word_bound = words.next();
17653 }
17654 while let Some(grapheme) = iter.peek().copied() {
17655 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17656 break;
17657 };
17658 if is_grapheme_whitespace(grapheme) != is_whitespace
17659 || (grapheme == "\n") != is_newline
17660 {
17661 break;
17662 };
17663 offset += grapheme.len();
17664 grapheme_len += 1;
17665 iter.next();
17666 }
17667 }
17668 let token = &self.input[..offset];
17669 self.input = &self.input[offset..];
17670 if token == "\n" {
17671 Some(WordBreakToken::Newline)
17672 } else if is_whitespace {
17673 Some(WordBreakToken::InlineWhitespace {
17674 token,
17675 grapheme_len,
17676 })
17677 } else {
17678 Some(WordBreakToken::Word {
17679 token,
17680 grapheme_len,
17681 })
17682 }
17683 } else {
17684 None
17685 }
17686 }
17687}
17688
17689#[test]
17690fn test_word_breaking_tokenizer() {
17691 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17692 ("", &[]),
17693 (" ", &[whitespace(" ", 2)]),
17694 ("Ʒ", &[word("Ʒ", 1)]),
17695 ("Ǽ", &[word("Ǽ", 1)]),
17696 ("⋑", &[word("⋑", 1)]),
17697 ("⋑⋑", &[word("⋑⋑", 2)]),
17698 (
17699 "原理,进而",
17700 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17701 ),
17702 (
17703 "hello world",
17704 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17705 ),
17706 (
17707 "hello, world",
17708 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17709 ),
17710 (
17711 " hello world",
17712 &[
17713 whitespace(" ", 2),
17714 word("hello", 5),
17715 whitespace(" ", 1),
17716 word("world", 5),
17717 ],
17718 ),
17719 (
17720 "这是什么 \n 钢笔",
17721 &[
17722 word("这", 1),
17723 word("是", 1),
17724 word("什", 1),
17725 word("么", 1),
17726 whitespace(" ", 1),
17727 newline(),
17728 whitespace(" ", 1),
17729 word("钢", 1),
17730 word("笔", 1),
17731 ],
17732 ),
17733 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17734 ];
17735
17736 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17737 WordBreakToken::Word {
17738 token,
17739 grapheme_len,
17740 }
17741 }
17742
17743 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17744 WordBreakToken::InlineWhitespace {
17745 token,
17746 grapheme_len,
17747 }
17748 }
17749
17750 fn newline() -> WordBreakToken<'static> {
17751 WordBreakToken::Newline
17752 }
17753
17754 for (input, result) in tests {
17755 assert_eq!(
17756 WordBreakingTokenizer::new(input)
17757 .collect::<Vec<_>>()
17758 .as_slice(),
17759 *result,
17760 );
17761 }
17762}
17763
17764fn wrap_with_prefix(
17765 line_prefix: String,
17766 unwrapped_text: String,
17767 wrap_column: usize,
17768 tab_size: NonZeroU32,
17769 preserve_existing_whitespace: bool,
17770) -> String {
17771 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17772 let mut wrapped_text = String::new();
17773 let mut current_line = line_prefix.clone();
17774
17775 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17776 let mut current_line_len = line_prefix_len;
17777 let mut in_whitespace = false;
17778 for token in tokenizer {
17779 let have_preceding_whitespace = in_whitespace;
17780 match token {
17781 WordBreakToken::Word {
17782 token,
17783 grapheme_len,
17784 } => {
17785 in_whitespace = false;
17786 if current_line_len + grapheme_len > wrap_column
17787 && current_line_len != line_prefix_len
17788 {
17789 wrapped_text.push_str(current_line.trim_end());
17790 wrapped_text.push('\n');
17791 current_line.truncate(line_prefix.len());
17792 current_line_len = line_prefix_len;
17793 }
17794 current_line.push_str(token);
17795 current_line_len += grapheme_len;
17796 }
17797 WordBreakToken::InlineWhitespace {
17798 mut token,
17799 mut grapheme_len,
17800 } => {
17801 in_whitespace = true;
17802 if have_preceding_whitespace && !preserve_existing_whitespace {
17803 continue;
17804 }
17805 if !preserve_existing_whitespace {
17806 token = " ";
17807 grapheme_len = 1;
17808 }
17809 if current_line_len + grapheme_len > wrap_column {
17810 wrapped_text.push_str(current_line.trim_end());
17811 wrapped_text.push('\n');
17812 current_line.truncate(line_prefix.len());
17813 current_line_len = line_prefix_len;
17814 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17815 current_line.push_str(token);
17816 current_line_len += grapheme_len;
17817 }
17818 }
17819 WordBreakToken::Newline => {
17820 in_whitespace = true;
17821 if preserve_existing_whitespace {
17822 wrapped_text.push_str(current_line.trim_end());
17823 wrapped_text.push('\n');
17824 current_line.truncate(line_prefix.len());
17825 current_line_len = line_prefix_len;
17826 } else if have_preceding_whitespace {
17827 continue;
17828 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17829 {
17830 wrapped_text.push_str(current_line.trim_end());
17831 wrapped_text.push('\n');
17832 current_line.truncate(line_prefix.len());
17833 current_line_len = line_prefix_len;
17834 } else if current_line_len != line_prefix_len {
17835 current_line.push(' ');
17836 current_line_len += 1;
17837 }
17838 }
17839 }
17840 }
17841
17842 if !current_line.is_empty() {
17843 wrapped_text.push_str(¤t_line);
17844 }
17845 wrapped_text
17846}
17847
17848#[test]
17849fn test_wrap_with_prefix() {
17850 assert_eq!(
17851 wrap_with_prefix(
17852 "# ".to_string(),
17853 "abcdefg".to_string(),
17854 4,
17855 NonZeroU32::new(4).unwrap(),
17856 false,
17857 ),
17858 "# abcdefg"
17859 );
17860 assert_eq!(
17861 wrap_with_prefix(
17862 "".to_string(),
17863 "\thello world".to_string(),
17864 8,
17865 NonZeroU32::new(4).unwrap(),
17866 false,
17867 ),
17868 "hello\nworld"
17869 );
17870 assert_eq!(
17871 wrap_with_prefix(
17872 "// ".to_string(),
17873 "xx \nyy zz aa bb cc".to_string(),
17874 12,
17875 NonZeroU32::new(4).unwrap(),
17876 false,
17877 ),
17878 "// xx yy zz\n// aa bb cc"
17879 );
17880 assert_eq!(
17881 wrap_with_prefix(
17882 String::new(),
17883 "这是什么 \n 钢笔".to_string(),
17884 3,
17885 NonZeroU32::new(4).unwrap(),
17886 false,
17887 ),
17888 "这是什\n么 钢\n笔"
17889 );
17890}
17891
17892pub trait CollaborationHub {
17893 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17894 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17895 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17896}
17897
17898impl CollaborationHub for Entity<Project> {
17899 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17900 self.read(cx).collaborators()
17901 }
17902
17903 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17904 self.read(cx).user_store().read(cx).participant_indices()
17905 }
17906
17907 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17908 let this = self.read(cx);
17909 let user_ids = this.collaborators().values().map(|c| c.user_id);
17910 this.user_store().read_with(cx, |user_store, cx| {
17911 user_store.participant_names(user_ids, cx)
17912 })
17913 }
17914}
17915
17916pub trait SemanticsProvider {
17917 fn hover(
17918 &self,
17919 buffer: &Entity<Buffer>,
17920 position: text::Anchor,
17921 cx: &mut App,
17922 ) -> Option<Task<Vec<project::Hover>>>;
17923
17924 fn inlay_hints(
17925 &self,
17926 buffer_handle: Entity<Buffer>,
17927 range: Range<text::Anchor>,
17928 cx: &mut App,
17929 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17930
17931 fn resolve_inlay_hint(
17932 &self,
17933 hint: InlayHint,
17934 buffer_handle: Entity<Buffer>,
17935 server_id: LanguageServerId,
17936 cx: &mut App,
17937 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17938
17939 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17940
17941 fn document_highlights(
17942 &self,
17943 buffer: &Entity<Buffer>,
17944 position: text::Anchor,
17945 cx: &mut App,
17946 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17947
17948 fn definitions(
17949 &self,
17950 buffer: &Entity<Buffer>,
17951 position: text::Anchor,
17952 kind: GotoDefinitionKind,
17953 cx: &mut App,
17954 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17955
17956 fn range_for_rename(
17957 &self,
17958 buffer: &Entity<Buffer>,
17959 position: text::Anchor,
17960 cx: &mut App,
17961 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17962
17963 fn perform_rename(
17964 &self,
17965 buffer: &Entity<Buffer>,
17966 position: text::Anchor,
17967 new_name: String,
17968 cx: &mut App,
17969 ) -> Option<Task<Result<ProjectTransaction>>>;
17970}
17971
17972pub trait CompletionProvider {
17973 fn completions(
17974 &self,
17975 excerpt_id: ExcerptId,
17976 buffer: &Entity<Buffer>,
17977 buffer_position: text::Anchor,
17978 trigger: CompletionContext,
17979 window: &mut Window,
17980 cx: &mut Context<Editor>,
17981 ) -> Task<Result<Option<Vec<Completion>>>>;
17982
17983 fn resolve_completions(
17984 &self,
17985 buffer: Entity<Buffer>,
17986 completion_indices: Vec<usize>,
17987 completions: Rc<RefCell<Box<[Completion]>>>,
17988 cx: &mut Context<Editor>,
17989 ) -> Task<Result<bool>>;
17990
17991 fn apply_additional_edits_for_completion(
17992 &self,
17993 _buffer: Entity<Buffer>,
17994 _completions: Rc<RefCell<Box<[Completion]>>>,
17995 _completion_index: usize,
17996 _push_to_history: bool,
17997 _cx: &mut Context<Editor>,
17998 ) -> Task<Result<Option<language::Transaction>>> {
17999 Task::ready(Ok(None))
18000 }
18001
18002 fn is_completion_trigger(
18003 &self,
18004 buffer: &Entity<Buffer>,
18005 position: language::Anchor,
18006 text: &str,
18007 trigger_in_words: bool,
18008 cx: &mut Context<Editor>,
18009 ) -> bool;
18010
18011 fn sort_completions(&self) -> bool {
18012 true
18013 }
18014}
18015
18016pub trait CodeActionProvider {
18017 fn id(&self) -> Arc<str>;
18018
18019 fn code_actions(
18020 &self,
18021 buffer: &Entity<Buffer>,
18022 range: Range<text::Anchor>,
18023 window: &mut Window,
18024 cx: &mut App,
18025 ) -> Task<Result<Vec<CodeAction>>>;
18026
18027 fn apply_code_action(
18028 &self,
18029 buffer_handle: Entity<Buffer>,
18030 action: CodeAction,
18031 excerpt_id: ExcerptId,
18032 push_to_history: bool,
18033 window: &mut Window,
18034 cx: &mut App,
18035 ) -> Task<Result<ProjectTransaction>>;
18036}
18037
18038impl CodeActionProvider for Entity<Project> {
18039 fn id(&self) -> Arc<str> {
18040 "project".into()
18041 }
18042
18043 fn code_actions(
18044 &self,
18045 buffer: &Entity<Buffer>,
18046 range: Range<text::Anchor>,
18047 _window: &mut Window,
18048 cx: &mut App,
18049 ) -> Task<Result<Vec<CodeAction>>> {
18050 self.update(cx, |project, cx| {
18051 let code_lens = project.code_lens(buffer, range.clone(), cx);
18052 let code_actions = project.code_actions(buffer, range, None, cx);
18053 cx.background_spawn(async move {
18054 let (code_lens, code_actions) = join(code_lens, code_actions).await;
18055 Ok(code_lens
18056 .context("code lens fetch")?
18057 .into_iter()
18058 .chain(code_actions.context("code action fetch")?)
18059 .collect())
18060 })
18061 })
18062 }
18063
18064 fn apply_code_action(
18065 &self,
18066 buffer_handle: Entity<Buffer>,
18067 action: CodeAction,
18068 _excerpt_id: ExcerptId,
18069 push_to_history: bool,
18070 _window: &mut Window,
18071 cx: &mut App,
18072 ) -> Task<Result<ProjectTransaction>> {
18073 self.update(cx, |project, cx| {
18074 project.apply_code_action(buffer_handle, action, push_to_history, cx)
18075 })
18076 }
18077}
18078
18079fn snippet_completions(
18080 project: &Project,
18081 buffer: &Entity<Buffer>,
18082 buffer_position: text::Anchor,
18083 cx: &mut App,
18084) -> Task<Result<Vec<Completion>>> {
18085 let language = buffer.read(cx).language_at(buffer_position);
18086 let language_name = language.as_ref().map(|language| language.lsp_id());
18087 let snippet_store = project.snippets().read(cx);
18088 let snippets = snippet_store.snippets_for(language_name, cx);
18089
18090 if snippets.is_empty() {
18091 return Task::ready(Ok(vec![]));
18092 }
18093 let snapshot = buffer.read(cx).text_snapshot();
18094 let chars: String = snapshot
18095 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
18096 .collect();
18097
18098 let scope = language.map(|language| language.default_scope());
18099 let executor = cx.background_executor().clone();
18100
18101 cx.background_spawn(async move {
18102 let classifier = CharClassifier::new(scope).for_completion(true);
18103 let mut last_word = chars
18104 .chars()
18105 .take_while(|c| classifier.is_word(*c))
18106 .collect::<String>();
18107 last_word = last_word.chars().rev().collect();
18108
18109 if last_word.is_empty() {
18110 return Ok(vec![]);
18111 }
18112
18113 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
18114 let to_lsp = |point: &text::Anchor| {
18115 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
18116 point_to_lsp(end)
18117 };
18118 let lsp_end = to_lsp(&buffer_position);
18119
18120 let candidates = snippets
18121 .iter()
18122 .enumerate()
18123 .flat_map(|(ix, snippet)| {
18124 snippet
18125 .prefix
18126 .iter()
18127 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
18128 })
18129 .collect::<Vec<StringMatchCandidate>>();
18130
18131 let mut matches = fuzzy::match_strings(
18132 &candidates,
18133 &last_word,
18134 last_word.chars().any(|c| c.is_uppercase()),
18135 100,
18136 &Default::default(),
18137 executor,
18138 )
18139 .await;
18140
18141 // Remove all candidates where the query's start does not match the start of any word in the candidate
18142 if let Some(query_start) = last_word.chars().next() {
18143 matches.retain(|string_match| {
18144 split_words(&string_match.string).any(|word| {
18145 // Check that the first codepoint of the word as lowercase matches the first
18146 // codepoint of the query as lowercase
18147 word.chars()
18148 .flat_map(|codepoint| codepoint.to_lowercase())
18149 .zip(query_start.to_lowercase())
18150 .all(|(word_cp, query_cp)| word_cp == query_cp)
18151 })
18152 });
18153 }
18154
18155 let matched_strings = matches
18156 .into_iter()
18157 .map(|m| m.string)
18158 .collect::<HashSet<_>>();
18159
18160 let result: Vec<Completion> = snippets
18161 .into_iter()
18162 .filter_map(|snippet| {
18163 let matching_prefix = snippet
18164 .prefix
18165 .iter()
18166 .find(|prefix| matched_strings.contains(*prefix))?;
18167 let start = as_offset - last_word.len();
18168 let start = snapshot.anchor_before(start);
18169 let range = start..buffer_position;
18170 let lsp_start = to_lsp(&start);
18171 let lsp_range = lsp::Range {
18172 start: lsp_start,
18173 end: lsp_end,
18174 };
18175 Some(Completion {
18176 old_range: range,
18177 new_text: snippet.body.clone(),
18178 source: CompletionSource::Lsp {
18179 server_id: LanguageServerId(usize::MAX),
18180 resolved: true,
18181 lsp_completion: Box::new(lsp::CompletionItem {
18182 label: snippet.prefix.first().unwrap().clone(),
18183 kind: Some(CompletionItemKind::SNIPPET),
18184 label_details: snippet.description.as_ref().map(|description| {
18185 lsp::CompletionItemLabelDetails {
18186 detail: Some(description.clone()),
18187 description: None,
18188 }
18189 }),
18190 insert_text_format: Some(InsertTextFormat::SNIPPET),
18191 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18192 lsp::InsertReplaceEdit {
18193 new_text: snippet.body.clone(),
18194 insert: lsp_range,
18195 replace: lsp_range,
18196 },
18197 )),
18198 filter_text: Some(snippet.body.clone()),
18199 sort_text: Some(char::MAX.to_string()),
18200 ..lsp::CompletionItem::default()
18201 }),
18202 lsp_defaults: None,
18203 },
18204 label: CodeLabel {
18205 text: matching_prefix.clone(),
18206 runs: Vec::new(),
18207 filter_range: 0..matching_prefix.len(),
18208 },
18209 icon_path: None,
18210 documentation: snippet
18211 .description
18212 .clone()
18213 .map(|description| CompletionDocumentation::SingleLine(description.into())),
18214 confirm: None,
18215 })
18216 })
18217 .collect();
18218
18219 Ok(result)
18220 })
18221}
18222
18223impl CompletionProvider for Entity<Project> {
18224 fn completions(
18225 &self,
18226 _excerpt_id: ExcerptId,
18227 buffer: &Entity<Buffer>,
18228 buffer_position: text::Anchor,
18229 options: CompletionContext,
18230 _window: &mut Window,
18231 cx: &mut Context<Editor>,
18232 ) -> Task<Result<Option<Vec<Completion>>>> {
18233 self.update(cx, |project, cx| {
18234 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18235 let project_completions = project.completions(buffer, buffer_position, options, cx);
18236 cx.background_spawn(async move {
18237 let snippets_completions = snippets.await?;
18238 match project_completions.await? {
18239 Some(mut completions) => {
18240 completions.extend(snippets_completions);
18241 Ok(Some(completions))
18242 }
18243 None => {
18244 if snippets_completions.is_empty() {
18245 Ok(None)
18246 } else {
18247 Ok(Some(snippets_completions))
18248 }
18249 }
18250 }
18251 })
18252 })
18253 }
18254
18255 fn resolve_completions(
18256 &self,
18257 buffer: Entity<Buffer>,
18258 completion_indices: Vec<usize>,
18259 completions: Rc<RefCell<Box<[Completion]>>>,
18260 cx: &mut Context<Editor>,
18261 ) -> Task<Result<bool>> {
18262 self.update(cx, |project, cx| {
18263 project.lsp_store().update(cx, |lsp_store, cx| {
18264 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18265 })
18266 })
18267 }
18268
18269 fn apply_additional_edits_for_completion(
18270 &self,
18271 buffer: Entity<Buffer>,
18272 completions: Rc<RefCell<Box<[Completion]>>>,
18273 completion_index: usize,
18274 push_to_history: bool,
18275 cx: &mut Context<Editor>,
18276 ) -> Task<Result<Option<language::Transaction>>> {
18277 self.update(cx, |project, cx| {
18278 project.lsp_store().update(cx, |lsp_store, cx| {
18279 lsp_store.apply_additional_edits_for_completion(
18280 buffer,
18281 completions,
18282 completion_index,
18283 push_to_history,
18284 cx,
18285 )
18286 })
18287 })
18288 }
18289
18290 fn is_completion_trigger(
18291 &self,
18292 buffer: &Entity<Buffer>,
18293 position: language::Anchor,
18294 text: &str,
18295 trigger_in_words: bool,
18296 cx: &mut Context<Editor>,
18297 ) -> bool {
18298 let mut chars = text.chars();
18299 let char = if let Some(char) = chars.next() {
18300 char
18301 } else {
18302 return false;
18303 };
18304 if chars.next().is_some() {
18305 return false;
18306 }
18307
18308 let buffer = buffer.read(cx);
18309 let snapshot = buffer.snapshot();
18310 if !snapshot.settings_at(position, cx).show_completions_on_input {
18311 return false;
18312 }
18313 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18314 if trigger_in_words && classifier.is_word(char) {
18315 return true;
18316 }
18317
18318 buffer.completion_triggers().contains(text)
18319 }
18320}
18321
18322impl SemanticsProvider for Entity<Project> {
18323 fn hover(
18324 &self,
18325 buffer: &Entity<Buffer>,
18326 position: text::Anchor,
18327 cx: &mut App,
18328 ) -> Option<Task<Vec<project::Hover>>> {
18329 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18330 }
18331
18332 fn document_highlights(
18333 &self,
18334 buffer: &Entity<Buffer>,
18335 position: text::Anchor,
18336 cx: &mut App,
18337 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18338 Some(self.update(cx, |project, cx| {
18339 project.document_highlights(buffer, position, cx)
18340 }))
18341 }
18342
18343 fn definitions(
18344 &self,
18345 buffer: &Entity<Buffer>,
18346 position: text::Anchor,
18347 kind: GotoDefinitionKind,
18348 cx: &mut App,
18349 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18350 Some(self.update(cx, |project, cx| match kind {
18351 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18352 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18353 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18354 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18355 }))
18356 }
18357
18358 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18359 // TODO: make this work for remote projects
18360 self.update(cx, |this, cx| {
18361 buffer.update(cx, |buffer, cx| {
18362 this.any_language_server_supports_inlay_hints(buffer, cx)
18363 })
18364 })
18365 }
18366
18367 fn inlay_hints(
18368 &self,
18369 buffer_handle: Entity<Buffer>,
18370 range: Range<text::Anchor>,
18371 cx: &mut App,
18372 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18373 Some(self.update(cx, |project, cx| {
18374 project.inlay_hints(buffer_handle, range, cx)
18375 }))
18376 }
18377
18378 fn resolve_inlay_hint(
18379 &self,
18380 hint: InlayHint,
18381 buffer_handle: Entity<Buffer>,
18382 server_id: LanguageServerId,
18383 cx: &mut App,
18384 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18385 Some(self.update(cx, |project, cx| {
18386 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18387 }))
18388 }
18389
18390 fn range_for_rename(
18391 &self,
18392 buffer: &Entity<Buffer>,
18393 position: text::Anchor,
18394 cx: &mut App,
18395 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18396 Some(self.update(cx, |project, cx| {
18397 let buffer = buffer.clone();
18398 let task = project.prepare_rename(buffer.clone(), position, cx);
18399 cx.spawn(async move |_, cx| {
18400 Ok(match task.await? {
18401 PrepareRenameResponse::Success(range) => Some(range),
18402 PrepareRenameResponse::InvalidPosition => None,
18403 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18404 // Fallback on using TreeSitter info to determine identifier range
18405 buffer.update(cx, |buffer, _| {
18406 let snapshot = buffer.snapshot();
18407 let (range, kind) = snapshot.surrounding_word(position);
18408 if kind != Some(CharKind::Word) {
18409 return None;
18410 }
18411 Some(
18412 snapshot.anchor_before(range.start)
18413 ..snapshot.anchor_after(range.end),
18414 )
18415 })?
18416 }
18417 })
18418 })
18419 }))
18420 }
18421
18422 fn perform_rename(
18423 &self,
18424 buffer: &Entity<Buffer>,
18425 position: text::Anchor,
18426 new_name: String,
18427 cx: &mut App,
18428 ) -> Option<Task<Result<ProjectTransaction>>> {
18429 Some(self.update(cx, |project, cx| {
18430 project.perform_rename(buffer.clone(), position, new_name, cx)
18431 }))
18432 }
18433}
18434
18435fn inlay_hint_settings(
18436 location: Anchor,
18437 snapshot: &MultiBufferSnapshot,
18438 cx: &mut Context<Editor>,
18439) -> InlayHintSettings {
18440 let file = snapshot.file_at(location);
18441 let language = snapshot.language_at(location).map(|l| l.name());
18442 language_settings(language, file, cx).inlay_hints
18443}
18444
18445fn consume_contiguous_rows(
18446 contiguous_row_selections: &mut Vec<Selection<Point>>,
18447 selection: &Selection<Point>,
18448 display_map: &DisplaySnapshot,
18449 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18450) -> (MultiBufferRow, MultiBufferRow) {
18451 contiguous_row_selections.push(selection.clone());
18452 let start_row = MultiBufferRow(selection.start.row);
18453 let mut end_row = ending_row(selection, display_map);
18454
18455 while let Some(next_selection) = selections.peek() {
18456 if next_selection.start.row <= end_row.0 {
18457 end_row = ending_row(next_selection, display_map);
18458 contiguous_row_selections.push(selections.next().unwrap().clone());
18459 } else {
18460 break;
18461 }
18462 }
18463 (start_row, end_row)
18464}
18465
18466fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18467 if next_selection.end.column > 0 || next_selection.is_empty() {
18468 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18469 } else {
18470 MultiBufferRow(next_selection.end.row)
18471 }
18472}
18473
18474impl EditorSnapshot {
18475 pub fn remote_selections_in_range<'a>(
18476 &'a self,
18477 range: &'a Range<Anchor>,
18478 collaboration_hub: &dyn CollaborationHub,
18479 cx: &'a App,
18480 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18481 let participant_names = collaboration_hub.user_names(cx);
18482 let participant_indices = collaboration_hub.user_participant_indices(cx);
18483 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18484 let collaborators_by_replica_id = collaborators_by_peer_id
18485 .iter()
18486 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18487 .collect::<HashMap<_, _>>();
18488 self.buffer_snapshot
18489 .selections_in_range(range, false)
18490 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18491 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18492 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18493 let user_name = participant_names.get(&collaborator.user_id).cloned();
18494 Some(RemoteSelection {
18495 replica_id,
18496 selection,
18497 cursor_shape,
18498 line_mode,
18499 participant_index,
18500 peer_id: collaborator.peer_id,
18501 user_name,
18502 })
18503 })
18504 }
18505
18506 pub fn hunks_for_ranges(
18507 &self,
18508 ranges: impl IntoIterator<Item = Range<Point>>,
18509 ) -> Vec<MultiBufferDiffHunk> {
18510 let mut hunks = Vec::new();
18511 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18512 HashMap::default();
18513 for query_range in ranges {
18514 let query_rows =
18515 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18516 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18517 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18518 ) {
18519 // Include deleted hunks that are adjacent to the query range, because
18520 // otherwise they would be missed.
18521 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18522 if hunk.status().is_deleted() {
18523 intersects_range |= hunk.row_range.start == query_rows.end;
18524 intersects_range |= hunk.row_range.end == query_rows.start;
18525 }
18526 if intersects_range {
18527 if !processed_buffer_rows
18528 .entry(hunk.buffer_id)
18529 .or_default()
18530 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18531 {
18532 continue;
18533 }
18534 hunks.push(hunk);
18535 }
18536 }
18537 }
18538
18539 hunks
18540 }
18541
18542 fn display_diff_hunks_for_rows<'a>(
18543 &'a self,
18544 display_rows: Range<DisplayRow>,
18545 folded_buffers: &'a HashSet<BufferId>,
18546 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18547 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18548 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18549
18550 self.buffer_snapshot
18551 .diff_hunks_in_range(buffer_start..buffer_end)
18552 .filter_map(|hunk| {
18553 if folded_buffers.contains(&hunk.buffer_id) {
18554 return None;
18555 }
18556
18557 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18558 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18559
18560 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18561 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18562
18563 let display_hunk = if hunk_display_start.column() != 0 {
18564 DisplayDiffHunk::Folded {
18565 display_row: hunk_display_start.row(),
18566 }
18567 } else {
18568 let mut end_row = hunk_display_end.row();
18569 if hunk_display_end.column() > 0 {
18570 end_row.0 += 1;
18571 }
18572 let is_created_file = hunk.is_created_file();
18573 DisplayDiffHunk::Unfolded {
18574 status: hunk.status(),
18575 diff_base_byte_range: hunk.diff_base_byte_range,
18576 display_row_range: hunk_display_start.row()..end_row,
18577 multi_buffer_range: Anchor::range_in_buffer(
18578 hunk.excerpt_id,
18579 hunk.buffer_id,
18580 hunk.buffer_range,
18581 ),
18582 is_created_file,
18583 }
18584 };
18585
18586 Some(display_hunk)
18587 })
18588 }
18589
18590 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18591 self.display_snapshot.buffer_snapshot.language_at(position)
18592 }
18593
18594 pub fn is_focused(&self) -> bool {
18595 self.is_focused
18596 }
18597
18598 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18599 self.placeholder_text.as_ref()
18600 }
18601
18602 pub fn scroll_position(&self) -> gpui::Point<f32> {
18603 self.scroll_anchor.scroll_position(&self.display_snapshot)
18604 }
18605
18606 fn gutter_dimensions(
18607 &self,
18608 font_id: FontId,
18609 font_size: Pixels,
18610 max_line_number_width: Pixels,
18611 cx: &App,
18612 ) -> Option<GutterDimensions> {
18613 if !self.show_gutter {
18614 return None;
18615 }
18616
18617 let descent = cx.text_system().descent(font_id, font_size);
18618 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18619 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18620
18621 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18622 matches!(
18623 ProjectSettings::get_global(cx).git.git_gutter,
18624 Some(GitGutterSetting::TrackedFiles)
18625 )
18626 });
18627 let gutter_settings = EditorSettings::get_global(cx).gutter;
18628 let show_line_numbers = self
18629 .show_line_numbers
18630 .unwrap_or(gutter_settings.line_numbers);
18631 let line_gutter_width = if show_line_numbers {
18632 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18633 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18634 max_line_number_width.max(min_width_for_number_on_gutter)
18635 } else {
18636 0.0.into()
18637 };
18638
18639 let show_code_actions = self
18640 .show_code_actions
18641 .unwrap_or(gutter_settings.code_actions);
18642
18643 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18644 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18645
18646 let git_blame_entries_width =
18647 self.git_blame_gutter_max_author_length
18648 .map(|max_author_length| {
18649 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18650
18651 /// The number of characters to dedicate to gaps and margins.
18652 const SPACING_WIDTH: usize = 4;
18653
18654 let max_char_count = max_author_length
18655 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18656 + ::git::SHORT_SHA_LENGTH
18657 + MAX_RELATIVE_TIMESTAMP.len()
18658 + SPACING_WIDTH;
18659
18660 em_advance * max_char_count
18661 });
18662
18663 let is_singleton = self.buffer_snapshot.is_singleton();
18664
18665 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18666 left_padding += if !is_singleton {
18667 em_width * 4.0
18668 } else if show_code_actions || show_runnables || show_breakpoints {
18669 em_width * 3.0
18670 } else if show_git_gutter && show_line_numbers {
18671 em_width * 2.0
18672 } else if show_git_gutter || show_line_numbers {
18673 em_width
18674 } else {
18675 px(0.)
18676 };
18677
18678 let shows_folds = is_singleton && gutter_settings.folds;
18679
18680 let right_padding = if shows_folds && show_line_numbers {
18681 em_width * 4.0
18682 } else if shows_folds || (!is_singleton && show_line_numbers) {
18683 em_width * 3.0
18684 } else if show_line_numbers {
18685 em_width
18686 } else {
18687 px(0.)
18688 };
18689
18690 Some(GutterDimensions {
18691 left_padding,
18692 right_padding,
18693 width: line_gutter_width + left_padding + right_padding,
18694 margin: -descent,
18695 git_blame_entries_width,
18696 })
18697 }
18698
18699 pub fn render_crease_toggle(
18700 &self,
18701 buffer_row: MultiBufferRow,
18702 row_contains_cursor: bool,
18703 editor: Entity<Editor>,
18704 window: &mut Window,
18705 cx: &mut App,
18706 ) -> Option<AnyElement> {
18707 let folded = self.is_line_folded(buffer_row);
18708 let mut is_foldable = false;
18709
18710 if let Some(crease) = self
18711 .crease_snapshot
18712 .query_row(buffer_row, &self.buffer_snapshot)
18713 {
18714 is_foldable = true;
18715 match crease {
18716 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18717 if let Some(render_toggle) = render_toggle {
18718 let toggle_callback =
18719 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18720 if folded {
18721 editor.update(cx, |editor, cx| {
18722 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18723 });
18724 } else {
18725 editor.update(cx, |editor, cx| {
18726 editor.unfold_at(
18727 &crate::UnfoldAt { buffer_row },
18728 window,
18729 cx,
18730 )
18731 });
18732 }
18733 });
18734 return Some((render_toggle)(
18735 buffer_row,
18736 folded,
18737 toggle_callback,
18738 window,
18739 cx,
18740 ));
18741 }
18742 }
18743 }
18744 }
18745
18746 is_foldable |= self.starts_indent(buffer_row);
18747
18748 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18749 Some(
18750 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18751 .toggle_state(folded)
18752 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18753 if folded {
18754 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18755 } else {
18756 this.fold_at(&FoldAt { buffer_row }, window, cx);
18757 }
18758 }))
18759 .into_any_element(),
18760 )
18761 } else {
18762 None
18763 }
18764 }
18765
18766 pub fn render_crease_trailer(
18767 &self,
18768 buffer_row: MultiBufferRow,
18769 window: &mut Window,
18770 cx: &mut App,
18771 ) -> Option<AnyElement> {
18772 let folded = self.is_line_folded(buffer_row);
18773 if let Crease::Inline { render_trailer, .. } = self
18774 .crease_snapshot
18775 .query_row(buffer_row, &self.buffer_snapshot)?
18776 {
18777 let render_trailer = render_trailer.as_ref()?;
18778 Some(render_trailer(buffer_row, folded, window, cx))
18779 } else {
18780 None
18781 }
18782 }
18783}
18784
18785impl Deref for EditorSnapshot {
18786 type Target = DisplaySnapshot;
18787
18788 fn deref(&self) -> &Self::Target {
18789 &self.display_snapshot
18790 }
18791}
18792
18793#[derive(Clone, Debug, PartialEq, Eq)]
18794pub enum EditorEvent {
18795 InputIgnored {
18796 text: Arc<str>,
18797 },
18798 InputHandled {
18799 utf16_range_to_replace: Option<Range<isize>>,
18800 text: Arc<str>,
18801 },
18802 ExcerptsAdded {
18803 buffer: Entity<Buffer>,
18804 predecessor: ExcerptId,
18805 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18806 },
18807 ExcerptsRemoved {
18808 ids: Vec<ExcerptId>,
18809 },
18810 BufferFoldToggled {
18811 ids: Vec<ExcerptId>,
18812 folded: bool,
18813 },
18814 ExcerptsEdited {
18815 ids: Vec<ExcerptId>,
18816 },
18817 ExcerptsExpanded {
18818 ids: Vec<ExcerptId>,
18819 },
18820 BufferEdited,
18821 Edited {
18822 transaction_id: clock::Lamport,
18823 },
18824 Reparsed(BufferId),
18825 Focused,
18826 FocusedIn,
18827 Blurred,
18828 DirtyChanged,
18829 Saved,
18830 TitleChanged,
18831 DiffBaseChanged,
18832 SelectionsChanged {
18833 local: bool,
18834 },
18835 ScrollPositionChanged {
18836 local: bool,
18837 autoscroll: bool,
18838 },
18839 Closed,
18840 TransactionUndone {
18841 transaction_id: clock::Lamport,
18842 },
18843 TransactionBegun {
18844 transaction_id: clock::Lamport,
18845 },
18846 Reloaded,
18847 CursorShapeChanged,
18848 PushedToNavHistory {
18849 anchor: Anchor,
18850 is_deactivate: bool,
18851 },
18852}
18853
18854impl EventEmitter<EditorEvent> for Editor {}
18855
18856impl Focusable for Editor {
18857 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18858 self.focus_handle.clone()
18859 }
18860}
18861
18862impl Render for Editor {
18863 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18864 let settings = ThemeSettings::get_global(cx);
18865
18866 let mut text_style = match self.mode {
18867 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18868 color: cx.theme().colors().editor_foreground,
18869 font_family: settings.ui_font.family.clone(),
18870 font_features: settings.ui_font.features.clone(),
18871 font_fallbacks: settings.ui_font.fallbacks.clone(),
18872 font_size: rems(0.875).into(),
18873 font_weight: settings.ui_font.weight,
18874 line_height: relative(settings.buffer_line_height.value()),
18875 ..Default::default()
18876 },
18877 EditorMode::Full => TextStyle {
18878 color: cx.theme().colors().editor_foreground,
18879 font_family: settings.buffer_font.family.clone(),
18880 font_features: settings.buffer_font.features.clone(),
18881 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18882 font_size: settings.buffer_font_size(cx).into(),
18883 font_weight: settings.buffer_font.weight,
18884 line_height: relative(settings.buffer_line_height.value()),
18885 ..Default::default()
18886 },
18887 };
18888 if let Some(text_style_refinement) = &self.text_style_refinement {
18889 text_style.refine(text_style_refinement)
18890 }
18891
18892 let background = match self.mode {
18893 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18894 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18895 EditorMode::Full => cx.theme().colors().editor_background,
18896 };
18897
18898 EditorElement::new(
18899 &cx.entity(),
18900 EditorStyle {
18901 background,
18902 local_player: cx.theme().players().local(),
18903 text: text_style,
18904 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18905 syntax: cx.theme().syntax().clone(),
18906 status: cx.theme().status().clone(),
18907 inlay_hints_style: make_inlay_hints_style(cx),
18908 inline_completion_styles: make_suggestion_styles(cx),
18909 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18910 },
18911 )
18912 }
18913}
18914
18915impl EntityInputHandler for Editor {
18916 fn text_for_range(
18917 &mut self,
18918 range_utf16: Range<usize>,
18919 adjusted_range: &mut Option<Range<usize>>,
18920 _: &mut Window,
18921 cx: &mut Context<Self>,
18922 ) -> Option<String> {
18923 let snapshot = self.buffer.read(cx).read(cx);
18924 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18925 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18926 if (start.0..end.0) != range_utf16 {
18927 adjusted_range.replace(start.0..end.0);
18928 }
18929 Some(snapshot.text_for_range(start..end).collect())
18930 }
18931
18932 fn selected_text_range(
18933 &mut self,
18934 ignore_disabled_input: bool,
18935 _: &mut Window,
18936 cx: &mut Context<Self>,
18937 ) -> Option<UTF16Selection> {
18938 // Prevent the IME menu from appearing when holding down an alphabetic key
18939 // while input is disabled.
18940 if !ignore_disabled_input && !self.input_enabled {
18941 return None;
18942 }
18943
18944 let selection = self.selections.newest::<OffsetUtf16>(cx);
18945 let range = selection.range();
18946
18947 Some(UTF16Selection {
18948 range: range.start.0..range.end.0,
18949 reversed: selection.reversed,
18950 })
18951 }
18952
18953 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18954 let snapshot = self.buffer.read(cx).read(cx);
18955 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18956 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18957 }
18958
18959 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18960 self.clear_highlights::<InputComposition>(cx);
18961 self.ime_transaction.take();
18962 }
18963
18964 fn replace_text_in_range(
18965 &mut self,
18966 range_utf16: Option<Range<usize>>,
18967 text: &str,
18968 window: &mut Window,
18969 cx: &mut Context<Self>,
18970 ) {
18971 if !self.input_enabled {
18972 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18973 return;
18974 }
18975
18976 self.transact(window, cx, |this, window, cx| {
18977 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18978 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18979 Some(this.selection_replacement_ranges(range_utf16, cx))
18980 } else {
18981 this.marked_text_ranges(cx)
18982 };
18983
18984 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
18985 let newest_selection_id = this.selections.newest_anchor().id;
18986 this.selections
18987 .all::<OffsetUtf16>(cx)
18988 .iter()
18989 .zip(ranges_to_replace.iter())
18990 .find_map(|(selection, range)| {
18991 if selection.id == newest_selection_id {
18992 Some(
18993 (range.start.0 as isize - selection.head().0 as isize)
18994 ..(range.end.0 as isize - selection.head().0 as isize),
18995 )
18996 } else {
18997 None
18998 }
18999 })
19000 });
19001
19002 cx.emit(EditorEvent::InputHandled {
19003 utf16_range_to_replace: range_to_replace,
19004 text: text.into(),
19005 });
19006
19007 if let Some(new_selected_ranges) = new_selected_ranges {
19008 this.change_selections(None, window, cx, |selections| {
19009 selections.select_ranges(new_selected_ranges)
19010 });
19011 this.backspace(&Default::default(), window, cx);
19012 }
19013
19014 this.handle_input(text, window, cx);
19015 });
19016
19017 if let Some(transaction) = self.ime_transaction {
19018 self.buffer.update(cx, |buffer, cx| {
19019 buffer.group_until_transaction(transaction, cx);
19020 });
19021 }
19022
19023 self.unmark_text(window, cx);
19024 }
19025
19026 fn replace_and_mark_text_in_range(
19027 &mut self,
19028 range_utf16: Option<Range<usize>>,
19029 text: &str,
19030 new_selected_range_utf16: Option<Range<usize>>,
19031 window: &mut Window,
19032 cx: &mut Context<Self>,
19033 ) {
19034 if !self.input_enabled {
19035 return;
19036 }
19037
19038 let transaction = self.transact(window, cx, |this, window, cx| {
19039 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
19040 let snapshot = this.buffer.read(cx).read(cx);
19041 if let Some(relative_range_utf16) = range_utf16.as_ref() {
19042 for marked_range in &mut marked_ranges {
19043 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
19044 marked_range.start.0 += relative_range_utf16.start;
19045 marked_range.start =
19046 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
19047 marked_range.end =
19048 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
19049 }
19050 }
19051 Some(marked_ranges)
19052 } else if let Some(range_utf16) = range_utf16 {
19053 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19054 Some(this.selection_replacement_ranges(range_utf16, cx))
19055 } else {
19056 None
19057 };
19058
19059 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
19060 let newest_selection_id = this.selections.newest_anchor().id;
19061 this.selections
19062 .all::<OffsetUtf16>(cx)
19063 .iter()
19064 .zip(ranges_to_replace.iter())
19065 .find_map(|(selection, range)| {
19066 if selection.id == newest_selection_id {
19067 Some(
19068 (range.start.0 as isize - selection.head().0 as isize)
19069 ..(range.end.0 as isize - selection.head().0 as isize),
19070 )
19071 } else {
19072 None
19073 }
19074 })
19075 });
19076
19077 cx.emit(EditorEvent::InputHandled {
19078 utf16_range_to_replace: range_to_replace,
19079 text: text.into(),
19080 });
19081
19082 if let Some(ranges) = ranges_to_replace {
19083 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
19084 }
19085
19086 let marked_ranges = {
19087 let snapshot = this.buffer.read(cx).read(cx);
19088 this.selections
19089 .disjoint_anchors()
19090 .iter()
19091 .map(|selection| {
19092 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
19093 })
19094 .collect::<Vec<_>>()
19095 };
19096
19097 if text.is_empty() {
19098 this.unmark_text(window, cx);
19099 } else {
19100 this.highlight_text::<InputComposition>(
19101 marked_ranges.clone(),
19102 HighlightStyle {
19103 underline: Some(UnderlineStyle {
19104 thickness: px(1.),
19105 color: None,
19106 wavy: false,
19107 }),
19108 ..Default::default()
19109 },
19110 cx,
19111 );
19112 }
19113
19114 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
19115 let use_autoclose = this.use_autoclose;
19116 let use_auto_surround = this.use_auto_surround;
19117 this.set_use_autoclose(false);
19118 this.set_use_auto_surround(false);
19119 this.handle_input(text, window, cx);
19120 this.set_use_autoclose(use_autoclose);
19121 this.set_use_auto_surround(use_auto_surround);
19122
19123 if let Some(new_selected_range) = new_selected_range_utf16 {
19124 let snapshot = this.buffer.read(cx).read(cx);
19125 let new_selected_ranges = marked_ranges
19126 .into_iter()
19127 .map(|marked_range| {
19128 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
19129 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
19130 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
19131 snapshot.clip_offset_utf16(new_start, Bias::Left)
19132 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
19133 })
19134 .collect::<Vec<_>>();
19135
19136 drop(snapshot);
19137 this.change_selections(None, window, cx, |selections| {
19138 selections.select_ranges(new_selected_ranges)
19139 });
19140 }
19141 });
19142
19143 self.ime_transaction = self.ime_transaction.or(transaction);
19144 if let Some(transaction) = self.ime_transaction {
19145 self.buffer.update(cx, |buffer, cx| {
19146 buffer.group_until_transaction(transaction, cx);
19147 });
19148 }
19149
19150 if self.text_highlights::<InputComposition>(cx).is_none() {
19151 self.ime_transaction.take();
19152 }
19153 }
19154
19155 fn bounds_for_range(
19156 &mut self,
19157 range_utf16: Range<usize>,
19158 element_bounds: gpui::Bounds<Pixels>,
19159 window: &mut Window,
19160 cx: &mut Context<Self>,
19161 ) -> Option<gpui::Bounds<Pixels>> {
19162 let text_layout_details = self.text_layout_details(window);
19163 let gpui::Size {
19164 width: em_width,
19165 height: line_height,
19166 } = self.character_size(window);
19167
19168 let snapshot = self.snapshot(window, cx);
19169 let scroll_position = snapshot.scroll_position();
19170 let scroll_left = scroll_position.x * em_width;
19171
19172 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
19173 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
19174 + self.gutter_dimensions.width
19175 + self.gutter_dimensions.margin;
19176 let y = line_height * (start.row().as_f32() - scroll_position.y);
19177
19178 Some(Bounds {
19179 origin: element_bounds.origin + point(x, y),
19180 size: size(em_width, line_height),
19181 })
19182 }
19183
19184 fn character_index_for_point(
19185 &mut self,
19186 point: gpui::Point<Pixels>,
19187 _window: &mut Window,
19188 _cx: &mut Context<Self>,
19189 ) -> Option<usize> {
19190 let position_map = self.last_position_map.as_ref()?;
19191 if !position_map.text_hitbox.contains(&point) {
19192 return None;
19193 }
19194 let display_point = position_map.point_for_position(point).previous_valid;
19195 let anchor = position_map
19196 .snapshot
19197 .display_point_to_anchor(display_point, Bias::Left);
19198 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
19199 Some(utf16_offset.0)
19200 }
19201}
19202
19203trait SelectionExt {
19204 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
19205 fn spanned_rows(
19206 &self,
19207 include_end_if_at_line_start: bool,
19208 map: &DisplaySnapshot,
19209 ) -> Range<MultiBufferRow>;
19210}
19211
19212impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
19213 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
19214 let start = self
19215 .start
19216 .to_point(&map.buffer_snapshot)
19217 .to_display_point(map);
19218 let end = self
19219 .end
19220 .to_point(&map.buffer_snapshot)
19221 .to_display_point(map);
19222 if self.reversed {
19223 end..start
19224 } else {
19225 start..end
19226 }
19227 }
19228
19229 fn spanned_rows(
19230 &self,
19231 include_end_if_at_line_start: bool,
19232 map: &DisplaySnapshot,
19233 ) -> Range<MultiBufferRow> {
19234 let start = self.start.to_point(&map.buffer_snapshot);
19235 let mut end = self.end.to_point(&map.buffer_snapshot);
19236 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19237 end.row -= 1;
19238 }
19239
19240 let buffer_start = map.prev_line_boundary(start).0;
19241 let buffer_end = map.next_line_boundary(end).0;
19242 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19243 }
19244}
19245
19246impl<T: InvalidationRegion> InvalidationStack<T> {
19247 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19248 where
19249 S: Clone + ToOffset,
19250 {
19251 while let Some(region) = self.last() {
19252 let all_selections_inside_invalidation_ranges =
19253 if selections.len() == region.ranges().len() {
19254 selections
19255 .iter()
19256 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19257 .all(|(selection, invalidation_range)| {
19258 let head = selection.head().to_offset(buffer);
19259 invalidation_range.start <= head && invalidation_range.end >= head
19260 })
19261 } else {
19262 false
19263 };
19264
19265 if all_selections_inside_invalidation_ranges {
19266 break;
19267 } else {
19268 self.pop();
19269 }
19270 }
19271 }
19272}
19273
19274impl<T> Default for InvalidationStack<T> {
19275 fn default() -> Self {
19276 Self(Default::default())
19277 }
19278}
19279
19280impl<T> Deref for InvalidationStack<T> {
19281 type Target = Vec<T>;
19282
19283 fn deref(&self) -> &Self::Target {
19284 &self.0
19285 }
19286}
19287
19288impl<T> DerefMut for InvalidationStack<T> {
19289 fn deref_mut(&mut self) -> &mut Self::Target {
19290 &mut self.0
19291 }
19292}
19293
19294impl InvalidationRegion for SnippetState {
19295 fn ranges(&self) -> &[Range<Anchor>] {
19296 &self.ranges[self.active_index]
19297 }
19298}
19299
19300pub fn diagnostic_block_renderer(
19301 diagnostic: Diagnostic,
19302 max_message_rows: Option<u8>,
19303 allow_closing: bool,
19304) -> RenderBlock {
19305 let (text_without_backticks, code_ranges) =
19306 highlight_diagnostic_message(&diagnostic, max_message_rows);
19307
19308 Arc::new(move |cx: &mut BlockContext| {
19309 let group_id: SharedString = cx.block_id.to_string().into();
19310
19311 let mut text_style = cx.window.text_style().clone();
19312 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19313 let theme_settings = ThemeSettings::get_global(cx);
19314 text_style.font_family = theme_settings.buffer_font.family.clone();
19315 text_style.font_style = theme_settings.buffer_font.style;
19316 text_style.font_features = theme_settings.buffer_font.features.clone();
19317 text_style.font_weight = theme_settings.buffer_font.weight;
19318
19319 let multi_line_diagnostic = diagnostic.message.contains('\n');
19320
19321 let buttons = |diagnostic: &Diagnostic| {
19322 if multi_line_diagnostic {
19323 v_flex()
19324 } else {
19325 h_flex()
19326 }
19327 .when(allow_closing, |div| {
19328 div.children(diagnostic.is_primary.then(|| {
19329 IconButton::new("close-block", IconName::XCircle)
19330 .icon_color(Color::Muted)
19331 .size(ButtonSize::Compact)
19332 .style(ButtonStyle::Transparent)
19333 .visible_on_hover(group_id.clone())
19334 .on_click(move |_click, window, cx| {
19335 window.dispatch_action(Box::new(Cancel), cx)
19336 })
19337 .tooltip(|window, cx| {
19338 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19339 })
19340 }))
19341 })
19342 .child(
19343 IconButton::new("copy-block", IconName::Copy)
19344 .icon_color(Color::Muted)
19345 .size(ButtonSize::Compact)
19346 .style(ButtonStyle::Transparent)
19347 .visible_on_hover(group_id.clone())
19348 .on_click({
19349 let message = diagnostic.message.clone();
19350 move |_click, _, cx| {
19351 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19352 }
19353 })
19354 .tooltip(Tooltip::text("Copy diagnostic message")),
19355 )
19356 };
19357
19358 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19359 AvailableSpace::min_size(),
19360 cx.window,
19361 cx.app,
19362 );
19363
19364 h_flex()
19365 .id(cx.block_id)
19366 .group(group_id.clone())
19367 .relative()
19368 .size_full()
19369 .block_mouse_down()
19370 .pl(cx.gutter_dimensions.width)
19371 .w(cx.max_width - cx.gutter_dimensions.full_width())
19372 .child(
19373 div()
19374 .flex()
19375 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19376 .flex_shrink(),
19377 )
19378 .child(buttons(&diagnostic))
19379 .child(div().flex().flex_shrink_0().child(
19380 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19381 &text_style,
19382 code_ranges.iter().map(|range| {
19383 (
19384 range.clone(),
19385 HighlightStyle {
19386 font_weight: Some(FontWeight::BOLD),
19387 ..Default::default()
19388 },
19389 )
19390 }),
19391 ),
19392 ))
19393 .into_any_element()
19394 })
19395}
19396
19397fn inline_completion_edit_text(
19398 current_snapshot: &BufferSnapshot,
19399 edits: &[(Range<Anchor>, String)],
19400 edit_preview: &EditPreview,
19401 include_deletions: bool,
19402 cx: &App,
19403) -> HighlightedText {
19404 let edits = edits
19405 .iter()
19406 .map(|(anchor, text)| {
19407 (
19408 anchor.start.text_anchor..anchor.end.text_anchor,
19409 text.clone(),
19410 )
19411 })
19412 .collect::<Vec<_>>();
19413
19414 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19415}
19416
19417pub fn highlight_diagnostic_message(
19418 diagnostic: &Diagnostic,
19419 mut max_message_rows: Option<u8>,
19420) -> (SharedString, Vec<Range<usize>>) {
19421 let mut text_without_backticks = String::new();
19422 let mut code_ranges = Vec::new();
19423
19424 if let Some(source) = &diagnostic.source {
19425 text_without_backticks.push_str(source);
19426 code_ranges.push(0..source.len());
19427 text_without_backticks.push_str(": ");
19428 }
19429
19430 let mut prev_offset = 0;
19431 let mut in_code_block = false;
19432 let has_row_limit = max_message_rows.is_some();
19433 let mut newline_indices = diagnostic
19434 .message
19435 .match_indices('\n')
19436 .filter(|_| has_row_limit)
19437 .map(|(ix, _)| ix)
19438 .fuse()
19439 .peekable();
19440
19441 for (quote_ix, _) in diagnostic
19442 .message
19443 .match_indices('`')
19444 .chain([(diagnostic.message.len(), "")])
19445 {
19446 let mut first_newline_ix = None;
19447 let mut last_newline_ix = None;
19448 while let Some(newline_ix) = newline_indices.peek() {
19449 if *newline_ix < quote_ix {
19450 if first_newline_ix.is_none() {
19451 first_newline_ix = Some(*newline_ix);
19452 }
19453 last_newline_ix = Some(*newline_ix);
19454
19455 if let Some(rows_left) = &mut max_message_rows {
19456 if *rows_left == 0 {
19457 break;
19458 } else {
19459 *rows_left -= 1;
19460 }
19461 }
19462 let _ = newline_indices.next();
19463 } else {
19464 break;
19465 }
19466 }
19467 let prev_len = text_without_backticks.len();
19468 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19469 text_without_backticks.push_str(new_text);
19470 if in_code_block {
19471 code_ranges.push(prev_len..text_without_backticks.len());
19472 }
19473 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19474 in_code_block = !in_code_block;
19475 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19476 text_without_backticks.push_str("...");
19477 break;
19478 }
19479 }
19480
19481 (text_without_backticks.into(), code_ranges)
19482}
19483
19484fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19485 match severity {
19486 DiagnosticSeverity::ERROR => colors.error,
19487 DiagnosticSeverity::WARNING => colors.warning,
19488 DiagnosticSeverity::INFORMATION => colors.info,
19489 DiagnosticSeverity::HINT => colors.info,
19490 _ => colors.ignored,
19491 }
19492}
19493
19494pub fn styled_runs_for_code_label<'a>(
19495 label: &'a CodeLabel,
19496 syntax_theme: &'a theme::SyntaxTheme,
19497) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19498 let fade_out = HighlightStyle {
19499 fade_out: Some(0.35),
19500 ..Default::default()
19501 };
19502
19503 let mut prev_end = label.filter_range.end;
19504 label
19505 .runs
19506 .iter()
19507 .enumerate()
19508 .flat_map(move |(ix, (range, highlight_id))| {
19509 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19510 style
19511 } else {
19512 return Default::default();
19513 };
19514 let mut muted_style = style;
19515 muted_style.highlight(fade_out);
19516
19517 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19518 if range.start >= label.filter_range.end {
19519 if range.start > prev_end {
19520 runs.push((prev_end..range.start, fade_out));
19521 }
19522 runs.push((range.clone(), muted_style));
19523 } else if range.end <= label.filter_range.end {
19524 runs.push((range.clone(), style));
19525 } else {
19526 runs.push((range.start..label.filter_range.end, style));
19527 runs.push((label.filter_range.end..range.end, muted_style));
19528 }
19529 prev_end = cmp::max(prev_end, range.end);
19530
19531 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19532 runs.push((prev_end..label.text.len(), fade_out));
19533 }
19534
19535 runs
19536 })
19537}
19538
19539pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19540 let mut prev_index = 0;
19541 let mut prev_codepoint: Option<char> = None;
19542 text.char_indices()
19543 .chain([(text.len(), '\0')])
19544 .filter_map(move |(index, codepoint)| {
19545 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19546 let is_boundary = index == text.len()
19547 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19548 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19549 if is_boundary {
19550 let chunk = &text[prev_index..index];
19551 prev_index = index;
19552 Some(chunk)
19553 } else {
19554 None
19555 }
19556 })
19557}
19558
19559pub trait RangeToAnchorExt: Sized {
19560 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19561
19562 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19563 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19564 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19565 }
19566}
19567
19568impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19569 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19570 let start_offset = self.start.to_offset(snapshot);
19571 let end_offset = self.end.to_offset(snapshot);
19572 if start_offset == end_offset {
19573 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19574 } else {
19575 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19576 }
19577 }
19578}
19579
19580pub trait RowExt {
19581 fn as_f32(&self) -> f32;
19582
19583 fn next_row(&self) -> Self;
19584
19585 fn previous_row(&self) -> Self;
19586
19587 fn minus(&self, other: Self) -> u32;
19588}
19589
19590impl RowExt for DisplayRow {
19591 fn as_f32(&self) -> f32 {
19592 self.0 as f32
19593 }
19594
19595 fn next_row(&self) -> Self {
19596 Self(self.0 + 1)
19597 }
19598
19599 fn previous_row(&self) -> Self {
19600 Self(self.0.saturating_sub(1))
19601 }
19602
19603 fn minus(&self, other: Self) -> u32 {
19604 self.0 - other.0
19605 }
19606}
19607
19608impl RowExt for MultiBufferRow {
19609 fn as_f32(&self) -> f32 {
19610 self.0 as f32
19611 }
19612
19613 fn next_row(&self) -> Self {
19614 Self(self.0 + 1)
19615 }
19616
19617 fn previous_row(&self) -> Self {
19618 Self(self.0.saturating_sub(1))
19619 }
19620
19621 fn minus(&self, other: Self) -> u32 {
19622 self.0 - other.0
19623 }
19624}
19625
19626trait RowRangeExt {
19627 type Row;
19628
19629 fn len(&self) -> usize;
19630
19631 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19632}
19633
19634impl RowRangeExt for Range<MultiBufferRow> {
19635 type Row = MultiBufferRow;
19636
19637 fn len(&self) -> usize {
19638 (self.end.0 - self.start.0) as usize
19639 }
19640
19641 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19642 (self.start.0..self.end.0).map(MultiBufferRow)
19643 }
19644}
19645
19646impl RowRangeExt for Range<DisplayRow> {
19647 type Row = DisplayRow;
19648
19649 fn len(&self) -> usize {
19650 (self.end.0 - self.start.0) as usize
19651 }
19652
19653 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19654 (self.start.0..self.end.0).map(DisplayRow)
19655 }
19656}
19657
19658/// If select range has more than one line, we
19659/// just point the cursor to range.start.
19660fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19661 if range.start.row == range.end.row {
19662 range
19663 } else {
19664 range.start..range.start
19665 }
19666}
19667pub struct KillRing(ClipboardItem);
19668impl Global for KillRing {}
19669
19670const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19671
19672struct BreakpointPromptEditor {
19673 pub(crate) prompt: Entity<Editor>,
19674 editor: WeakEntity<Editor>,
19675 breakpoint_anchor: Anchor,
19676 breakpoint: Breakpoint,
19677 block_ids: HashSet<CustomBlockId>,
19678 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19679 _subscriptions: Vec<Subscription>,
19680}
19681
19682impl BreakpointPromptEditor {
19683 const MAX_LINES: u8 = 4;
19684
19685 fn new(
19686 editor: WeakEntity<Editor>,
19687 breakpoint_anchor: Anchor,
19688 breakpoint: Breakpoint,
19689 window: &mut Window,
19690 cx: &mut Context<Self>,
19691 ) -> Self {
19692 let buffer = cx.new(|cx| {
19693 Buffer::local(
19694 breakpoint
19695 .kind
19696 .log_message()
19697 .map(|msg| msg.to_string())
19698 .unwrap_or_default(),
19699 cx,
19700 )
19701 });
19702 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19703
19704 let prompt = cx.new(|cx| {
19705 let mut prompt = Editor::new(
19706 EditorMode::AutoHeight {
19707 max_lines: Self::MAX_LINES as usize,
19708 },
19709 buffer,
19710 None,
19711 window,
19712 cx,
19713 );
19714 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19715 prompt.set_show_cursor_when_unfocused(false, cx);
19716 prompt.set_placeholder_text(
19717 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19718 cx,
19719 );
19720
19721 prompt
19722 });
19723
19724 Self {
19725 prompt,
19726 editor,
19727 breakpoint_anchor,
19728 breakpoint,
19729 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19730 block_ids: Default::default(),
19731 _subscriptions: vec![],
19732 }
19733 }
19734
19735 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19736 self.block_ids.extend(block_ids)
19737 }
19738
19739 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19740 if let Some(editor) = self.editor.upgrade() {
19741 let log_message = self
19742 .prompt
19743 .read(cx)
19744 .buffer
19745 .read(cx)
19746 .as_singleton()
19747 .expect("A multi buffer in breakpoint prompt isn't possible")
19748 .read(cx)
19749 .as_rope()
19750 .to_string();
19751
19752 editor.update(cx, |editor, cx| {
19753 editor.edit_breakpoint_at_anchor(
19754 self.breakpoint_anchor,
19755 self.breakpoint.clone(),
19756 BreakpointEditAction::EditLogMessage(log_message.into()),
19757 cx,
19758 );
19759
19760 editor.remove_blocks(self.block_ids.clone(), None, cx);
19761 cx.focus_self(window);
19762 });
19763 }
19764 }
19765
19766 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19767 self.editor
19768 .update(cx, |editor, cx| {
19769 editor.remove_blocks(self.block_ids.clone(), None, cx);
19770 window.focus(&editor.focus_handle);
19771 })
19772 .log_err();
19773 }
19774
19775 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19776 let settings = ThemeSettings::get_global(cx);
19777 let text_style = TextStyle {
19778 color: if self.prompt.read(cx).read_only(cx) {
19779 cx.theme().colors().text_disabled
19780 } else {
19781 cx.theme().colors().text
19782 },
19783 font_family: settings.buffer_font.family.clone(),
19784 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19785 font_size: settings.buffer_font_size(cx).into(),
19786 font_weight: settings.buffer_font.weight,
19787 line_height: relative(settings.buffer_line_height.value()),
19788 ..Default::default()
19789 };
19790 EditorElement::new(
19791 &self.prompt,
19792 EditorStyle {
19793 background: cx.theme().colors().editor_background,
19794 local_player: cx.theme().players().local(),
19795 text: text_style,
19796 ..Default::default()
19797 },
19798 )
19799 }
19800}
19801
19802impl Render for BreakpointPromptEditor {
19803 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19804 let gutter_dimensions = *self.gutter_dimensions.lock();
19805 h_flex()
19806 .key_context("Editor")
19807 .bg(cx.theme().colors().editor_background)
19808 .border_y_1()
19809 .border_color(cx.theme().status().info_border)
19810 .size_full()
19811 .py(window.line_height() / 2.5)
19812 .on_action(cx.listener(Self::confirm))
19813 .on_action(cx.listener(Self::cancel))
19814 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19815 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19816 }
19817}
19818
19819impl Focusable for BreakpointPromptEditor {
19820 fn focus_handle(&self, cx: &App) -> FocusHandle {
19821 self.prompt.focus_handle(cx)
19822 }
19823}
19824
19825fn all_edits_insertions_or_deletions(
19826 edits: &Vec<(Range<Anchor>, String)>,
19827 snapshot: &MultiBufferSnapshot,
19828) -> bool {
19829 let mut all_insertions = true;
19830 let mut all_deletions = true;
19831
19832 for (range, new_text) in edits.iter() {
19833 let range_is_empty = range.to_offset(&snapshot).is_empty();
19834 let text_is_empty = new_text.is_empty();
19835
19836 if range_is_empty != text_is_empty {
19837 if range_is_empty {
19838 all_deletions = false;
19839 } else {
19840 all_insertions = false;
19841 }
19842 } else {
19843 return false;
19844 }
19845
19846 if !all_insertions && !all_deletions {
19847 return false;
19848 }
19849 }
19850 all_insertions || all_deletions
19851}
19852
19853struct MissingEditPredictionKeybindingTooltip;
19854
19855impl Render for MissingEditPredictionKeybindingTooltip {
19856 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19857 ui::tooltip_container(window, cx, |container, _, cx| {
19858 container
19859 .flex_shrink_0()
19860 .max_w_80()
19861 .min_h(rems_from_px(124.))
19862 .justify_between()
19863 .child(
19864 v_flex()
19865 .flex_1()
19866 .text_ui_sm(cx)
19867 .child(Label::new("Conflict with Accept Keybinding"))
19868 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19869 )
19870 .child(
19871 h_flex()
19872 .pb_1()
19873 .gap_1()
19874 .items_end()
19875 .w_full()
19876 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19877 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19878 }))
19879 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19880 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19881 })),
19882 )
19883 })
19884 }
19885}
19886
19887#[derive(Debug, Clone, Copy, PartialEq)]
19888pub struct LineHighlight {
19889 pub background: Background,
19890 pub border: Option<gpui::Hsla>,
19891}
19892
19893impl From<Hsla> for LineHighlight {
19894 fn from(hsla: Hsla) -> Self {
19895 Self {
19896 background: hsla.into(),
19897 border: None,
19898 }
19899 }
19900}
19901
19902impl From<Background> for LineHighlight {
19903 fn from(background: Background) -> Self {
19904 Self {
19905 background,
19906 border: None,
19907 }
19908 }
19909}