1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blink_manager;
17mod clangd_ext;
18mod code_context_menus;
19pub mod commit_tooltip;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod indent_guides;
29mod inlay_hint_cache;
30pub mod items;
31mod jsx_tag_auto_close;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51pub(crate) use actions::*;
52pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use buffer_diff::DiffHunkStatus;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63use editor_settings::GoToDefinitionFallback;
64pub use editor_settings::{
65 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
66};
67pub use editor_settings_controls::*;
68use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
69pub use element::{
70 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
71};
72use feature_flags::{Debugger, FeatureFlagAppExt};
73use futures::{
74 future::{self, join, Shared},
75 FutureExt,
76};
77use fuzzy::StringMatchCandidate;
78
79use ::git::Restore;
80use code_context_menus::{
81 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
82 CompletionsMenu, ContextMenuOrigin,
83};
84use git::blame::GitBlame;
85use gpui::{
86 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
87 AnimationExt, AnyElement, App, AppContext, AsyncWindowContext, AvailableSpace, Background,
88 Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity,
89 EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight,
90 Global, HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
91 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
92 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
93 WeakEntity, WeakFocusHandle, Window,
94};
95use highlight_matching_bracket::refresh_matching_bracket_highlights;
96use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
97use hover_popover::{hide_hover, HoverState};
98use indent_guides::ActiveIndentGuidesState;
99use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
100pub use inline_completion::Direction;
101use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
102pub use items::MAX_TAB_TITLE_LEN;
103use itertools::Itertools;
104use language::{
105 language_settings::{
106 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
107 WordsCompletionMode,
108 },
109 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
110 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
111 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
112 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
113};
114use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
115use linked_editing_ranges::refresh_linked_ranges;
116use mouse_context_menu::MouseContextMenu;
117use persistence::DB;
118use project::{
119 debugger::breakpoint_store::{
120 BreakpointEditAction, BreakpointState, BreakpointStore, BreakpointStoreEvent,
121 },
122 ProjectPath,
123};
124
125pub use proposed_changes_editor::{
126 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
127};
128use smallvec::smallvec;
129use std::{cell::OnceCell, iter::Peekable};
130use task::{ResolvedTask, TaskTemplate, TaskVariables};
131
132pub use lsp::CompletionContext;
133use lsp::{
134 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
135 InsertTextFormat, LanguageServerId, LanguageServerName,
136};
137
138use language::BufferSnapshot;
139use movement::TextLayoutDetails;
140pub use multi_buffer::{
141 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
142 ToOffset, ToPoint,
143};
144use multi_buffer::{
145 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
146 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
147};
148use parking_lot::Mutex;
149use project::{
150 debugger::breakpoint_store::{Breakpoint, BreakpointKind},
151 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
152 project_settings::{GitGutterSetting, ProjectSettings},
153 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
154 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
155 TaskSourceKind,
156};
157use rand::prelude::*;
158use rpc::{proto::*, ErrorExt};
159use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
160use selections_collection::{
161 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
162};
163use serde::{Deserialize, Serialize};
164use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
165use smallvec::SmallVec;
166use snippet::Snippet;
167use std::sync::Arc;
168use std::{
169 any::TypeId,
170 borrow::Cow,
171 cell::RefCell,
172 cmp::{self, Ordering, Reverse},
173 mem,
174 num::NonZeroU32,
175 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
176 path::{Path, PathBuf},
177 rc::Rc,
178 time::{Duration, Instant},
179};
180pub use sum_tree::Bias;
181use sum_tree::TreeMap;
182use text::{BufferId, OffsetUtf16, Rope};
183use theme::{
184 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
185 ThemeColors, ThemeSettings,
186};
187use ui::{
188 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
189 Tooltip,
190};
191use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
192use workspace::{
193 item::{ItemHandle, PreviewTabsSettings},
194 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
195 searchable::SearchEvent,
196 Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
197 RestoreOnStartupBehavior, SplitDirection, TabBarSettings, Toast, ViewId, Workspace,
198 WorkspaceId, WorkspaceSettings, SERIALIZATION_THROTTLE_TIME,
199};
200
201use crate::hover_links::{find_url, find_url_from_range};
202use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
203
204pub const FILE_HEADER_HEIGHT: u32 = 2;
205pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
206pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
207const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
208const MAX_LINE_LEN: usize = 1024;
209const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
210const MAX_SELECTION_HISTORY_LEN: usize = 1024;
211pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
212#[doc(hidden)]
213pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
214
215pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
216pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
217pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
218
219pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
220pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
221pub(crate) const MIN_LINE_NUMBER_DIGITS: u32 = 4;
222
223const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
224 alt: true,
225 shift: true,
226 control: false,
227 platform: false,
228 function: false,
229};
230
231#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
232pub enum InlayId {
233 InlineCompletion(usize),
234 Hint(usize),
235}
236
237impl InlayId {
238 fn id(&self) -> usize {
239 match self {
240 Self::InlineCompletion(id) => *id,
241 Self::Hint(id) => *id,
242 }
243 }
244}
245
246pub enum DebugCurrentRowHighlight {}
247enum DocumentHighlightRead {}
248enum DocumentHighlightWrite {}
249enum InputComposition {}
250enum SelectedTextHighlight {}
251
252#[derive(Debug, Copy, Clone, PartialEq, Eq)]
253pub enum Navigated {
254 Yes,
255 No,
256}
257
258impl Navigated {
259 pub fn from_bool(yes: bool) -> Navigated {
260 if yes {
261 Navigated::Yes
262 } else {
263 Navigated::No
264 }
265 }
266}
267
268#[derive(Debug, Clone, PartialEq, Eq)]
269enum DisplayDiffHunk {
270 Folded {
271 display_row: DisplayRow,
272 },
273 Unfolded {
274 is_created_file: bool,
275 diff_base_byte_range: Range<usize>,
276 display_row_range: Range<DisplayRow>,
277 multi_buffer_range: Range<Anchor>,
278 status: DiffHunkStatus,
279 },
280}
281
282pub fn init_settings(cx: &mut App) {
283 EditorSettings::register(cx);
284}
285
286pub fn init(cx: &mut App) {
287 init_settings(cx);
288
289 workspace::register_project_item::<Editor>(cx);
290 workspace::FollowableViewRegistry::register::<Editor>(cx);
291 workspace::register_serializable_item::<Editor>(cx);
292
293 cx.observe_new(
294 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
295 workspace.register_action(Editor::new_file);
296 workspace.register_action(Editor::new_file_vertical);
297 workspace.register_action(Editor::new_file_horizontal);
298 workspace.register_action(Editor::cancel_language_server_work);
299 },
300 )
301 .detach();
302
303 cx.on_action(move |_: &workspace::NewFile, cx| {
304 let app_state = workspace::AppState::global(cx);
305 if let Some(app_state) = app_state.upgrade() {
306 workspace::open_new(
307 Default::default(),
308 app_state,
309 cx,
310 |workspace, window, cx| {
311 Editor::new_file(workspace, &Default::default(), window, cx)
312 },
313 )
314 .detach();
315 }
316 });
317 cx.on_action(move |_: &workspace::NewWindow, cx| {
318 let app_state = workspace::AppState::global(cx);
319 if let Some(app_state) = app_state.upgrade() {
320 workspace::open_new(
321 Default::default(),
322 app_state,
323 cx,
324 |workspace, window, cx| {
325 cx.activate(true);
326 Editor::new_file(workspace, &Default::default(), window, cx)
327 },
328 )
329 .detach();
330 }
331 });
332}
333
334pub struct SearchWithinRange;
335
336trait InvalidationRegion {
337 fn ranges(&self) -> &[Range<Anchor>];
338}
339
340#[derive(Clone, Debug, PartialEq)]
341pub enum SelectPhase {
342 Begin {
343 position: DisplayPoint,
344 add: bool,
345 click_count: usize,
346 },
347 BeginColumnar {
348 position: DisplayPoint,
349 reset: bool,
350 goal_column: u32,
351 },
352 Extend {
353 position: DisplayPoint,
354 click_count: usize,
355 },
356 Update {
357 position: DisplayPoint,
358 goal_column: u32,
359 scroll_delta: gpui::Point<f32>,
360 },
361 End,
362}
363
364#[derive(Clone, Debug)]
365pub enum SelectMode {
366 Character,
367 Word(Range<Anchor>),
368 Line(Range<Anchor>),
369 All,
370}
371
372#[derive(Copy, Clone, PartialEq, Eq, Debug)]
373pub enum EditorMode {
374 SingleLine { auto_width: bool },
375 AutoHeight { max_lines: usize },
376 Full,
377}
378
379#[derive(Copy, Clone, Debug)]
380pub enum SoftWrap {
381 /// Prefer not to wrap at all.
382 ///
383 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
384 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
385 GitDiff,
386 /// Prefer a single line generally, unless an overly long line is encountered.
387 None,
388 /// Soft wrap lines that exceed the editor width.
389 EditorWidth,
390 /// Soft wrap lines at the preferred line length.
391 Column(u32),
392 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
393 Bounded(u32),
394}
395
396#[derive(Clone)]
397pub struct EditorStyle {
398 pub background: Hsla,
399 pub local_player: PlayerColor,
400 pub text: TextStyle,
401 pub scrollbar_width: Pixels,
402 pub syntax: Arc<SyntaxTheme>,
403 pub status: StatusColors,
404 pub inlay_hints_style: HighlightStyle,
405 pub inline_completion_styles: InlineCompletionStyles,
406 pub unnecessary_code_fade: f32,
407}
408
409impl Default for EditorStyle {
410 fn default() -> Self {
411 Self {
412 background: Hsla::default(),
413 local_player: PlayerColor::default(),
414 text: TextStyle::default(),
415 scrollbar_width: Pixels::default(),
416 syntax: Default::default(),
417 // HACK: Status colors don't have a real default.
418 // We should look into removing the status colors from the editor
419 // style and retrieve them directly from the theme.
420 status: StatusColors::dark(),
421 inlay_hints_style: HighlightStyle::default(),
422 inline_completion_styles: InlineCompletionStyles {
423 insertion: HighlightStyle::default(),
424 whitespace: HighlightStyle::default(),
425 },
426 unnecessary_code_fade: Default::default(),
427 }
428 }
429}
430
431pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
432 let show_background = language_settings::language_settings(None, None, cx)
433 .inlay_hints
434 .show_background;
435
436 HighlightStyle {
437 color: Some(cx.theme().status().hint),
438 background_color: show_background.then(|| cx.theme().status().hint_background),
439 ..HighlightStyle::default()
440 }
441}
442
443pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
444 InlineCompletionStyles {
445 insertion: HighlightStyle {
446 color: Some(cx.theme().status().predictive),
447 ..HighlightStyle::default()
448 },
449 whitespace: HighlightStyle {
450 background_color: Some(cx.theme().status().created_background),
451 ..HighlightStyle::default()
452 },
453 }
454}
455
456type CompletionId = usize;
457
458pub(crate) enum EditDisplayMode {
459 TabAccept,
460 DiffPopover,
461 Inline,
462}
463
464enum InlineCompletion {
465 Edit {
466 edits: Vec<(Range<Anchor>, String)>,
467 edit_preview: Option<EditPreview>,
468 display_mode: EditDisplayMode,
469 snapshot: BufferSnapshot,
470 },
471 Move {
472 target: Anchor,
473 snapshot: BufferSnapshot,
474 },
475}
476
477struct InlineCompletionState {
478 inlay_ids: Vec<InlayId>,
479 completion: InlineCompletion,
480 completion_id: Option<SharedString>,
481 invalidation_range: Range<Anchor>,
482}
483
484enum EditPredictionSettings {
485 Disabled,
486 Enabled {
487 show_in_menu: bool,
488 preview_requires_modifier: bool,
489 },
490}
491
492enum InlineCompletionHighlight {}
493
494#[derive(Debug, Clone)]
495struct InlineDiagnostic {
496 message: SharedString,
497 group_id: usize,
498 is_primary: bool,
499 start: Point,
500 severity: DiagnosticSeverity,
501}
502
503pub enum MenuInlineCompletionsPolicy {
504 Never,
505 ByProvider,
506}
507
508pub enum EditPredictionPreview {
509 /// Modifier is not pressed
510 Inactive { released_too_fast: bool },
511 /// Modifier pressed
512 Active {
513 since: Instant,
514 previous_scroll_position: Option<ScrollAnchor>,
515 },
516}
517
518impl EditPredictionPreview {
519 pub fn released_too_fast(&self) -> bool {
520 match self {
521 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
522 EditPredictionPreview::Active { .. } => false,
523 }
524 }
525
526 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
527 if let EditPredictionPreview::Active {
528 previous_scroll_position,
529 ..
530 } = self
531 {
532 *previous_scroll_position = scroll_position;
533 }
534 }
535}
536
537pub struct ContextMenuOptions {
538 pub min_entries_visible: usize,
539 pub max_entries_visible: usize,
540 pub placement: Option<ContextMenuPlacement>,
541}
542
543#[derive(Debug, Clone, PartialEq, Eq)]
544pub enum ContextMenuPlacement {
545 Above,
546 Below,
547}
548
549#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
550struct EditorActionId(usize);
551
552impl EditorActionId {
553 pub fn post_inc(&mut self) -> Self {
554 let answer = self.0;
555
556 *self = Self(answer + 1);
557
558 Self(answer)
559 }
560}
561
562// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
563// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
564
565type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
566type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
567
568#[derive(Default)]
569struct ScrollbarMarkerState {
570 scrollbar_size: Size<Pixels>,
571 dirty: bool,
572 markers: Arc<[PaintQuad]>,
573 pending_refresh: Option<Task<Result<()>>>,
574}
575
576impl ScrollbarMarkerState {
577 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
578 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
579 }
580}
581
582#[derive(Clone, Debug)]
583struct RunnableTasks {
584 templates: Vec<(TaskSourceKind, TaskTemplate)>,
585 offset: multi_buffer::Anchor,
586 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
587 column: u32,
588 // Values of all named captures, including those starting with '_'
589 extra_variables: HashMap<String, String>,
590 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
591 context_range: Range<BufferOffset>,
592}
593
594impl RunnableTasks {
595 fn resolve<'a>(
596 &'a self,
597 cx: &'a task::TaskContext,
598 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
599 self.templates.iter().filter_map(|(kind, template)| {
600 template
601 .resolve_task(&kind.to_id_base(), cx)
602 .map(|task| (kind.clone(), task))
603 })
604 }
605}
606
607#[derive(Clone)]
608struct ResolvedTasks {
609 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
610 position: Anchor,
611}
612
613#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
614struct BufferOffset(usize);
615
616// Addons allow storing per-editor state in other crates (e.g. Vim)
617pub trait Addon: 'static {
618 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
619
620 fn render_buffer_header_controls(
621 &self,
622 _: &ExcerptInfo,
623 _: &Window,
624 _: &App,
625 ) -> Option<AnyElement> {
626 None
627 }
628
629 fn to_any(&self) -> &dyn std::any::Any;
630}
631
632/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
633///
634/// See the [module level documentation](self) for more information.
635pub struct Editor {
636 focus_handle: FocusHandle,
637 last_focused_descendant: Option<WeakFocusHandle>,
638 /// The text buffer being edited
639 buffer: Entity<MultiBuffer>,
640 /// Map of how text in the buffer should be displayed.
641 /// Handles soft wraps, folds, fake inlay text insertions, etc.
642 pub display_map: Entity<DisplayMap>,
643 pub selections: SelectionsCollection,
644 pub scroll_manager: ScrollManager,
645 /// When inline assist editors are linked, they all render cursors because
646 /// typing enters text into each of them, even the ones that aren't focused.
647 pub(crate) show_cursor_when_unfocused: bool,
648 columnar_selection_tail: Option<Anchor>,
649 add_selections_state: Option<AddSelectionsState>,
650 select_next_state: Option<SelectNextState>,
651 select_prev_state: Option<SelectNextState>,
652 selection_history: SelectionHistory,
653 autoclose_regions: Vec<AutocloseRegion>,
654 snippet_stack: InvalidationStack<SnippetState>,
655 select_syntax_node_history: SelectSyntaxNodeHistory,
656 ime_transaction: Option<TransactionId>,
657 active_diagnostics: Option<ActiveDiagnosticGroup>,
658 show_inline_diagnostics: bool,
659 inline_diagnostics_update: Task<()>,
660 inline_diagnostics_enabled: bool,
661 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
662 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
663 hard_wrap: Option<usize>,
664
665 // TODO: make this a access method
666 pub project: Option<Entity<Project>>,
667 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
668 completion_provider: Option<Box<dyn CompletionProvider>>,
669 collaboration_hub: Option<Box<dyn CollaborationHub>>,
670 blink_manager: Entity<BlinkManager>,
671 show_cursor_names: bool,
672 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
673 pub show_local_selections: bool,
674 mode: EditorMode,
675 show_breadcrumbs: bool,
676 show_gutter: bool,
677 show_scrollbars: bool,
678 show_line_numbers: Option<bool>,
679 use_relative_line_numbers: Option<bool>,
680 show_git_diff_gutter: Option<bool>,
681 show_code_actions: Option<bool>,
682 show_runnables: Option<bool>,
683 show_breakpoints: Option<bool>,
684 show_wrap_guides: Option<bool>,
685 show_indent_guides: Option<bool>,
686 placeholder_text: Option<Arc<str>>,
687 highlight_order: usize,
688 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
689 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
690 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
691 scrollbar_marker_state: ScrollbarMarkerState,
692 active_indent_guides_state: ActiveIndentGuidesState,
693 nav_history: Option<ItemNavHistory>,
694 context_menu: RefCell<Option<CodeContextMenu>>,
695 context_menu_options: Option<ContextMenuOptions>,
696 mouse_context_menu: Option<MouseContextMenu>,
697 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
698 signature_help_state: SignatureHelpState,
699 auto_signature_help: Option<bool>,
700 find_all_references_task_sources: Vec<Anchor>,
701 next_completion_id: CompletionId,
702 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
703 code_actions_task: Option<Task<Result<()>>>,
704 selection_highlight_task: Option<Task<()>>,
705 document_highlights_task: Option<Task<()>>,
706 linked_editing_range_task: Option<Task<Option<()>>>,
707 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
708 pending_rename: Option<RenameState>,
709 searchable: bool,
710 cursor_shape: CursorShape,
711 current_line_highlight: Option<CurrentLineHighlight>,
712 collapse_matches: bool,
713 autoindent_mode: Option<AutoindentMode>,
714 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
715 input_enabled: bool,
716 use_modal_editing: bool,
717 read_only: bool,
718 leader_peer_id: Option<PeerId>,
719 remote_id: Option<ViewId>,
720 hover_state: HoverState,
721 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
722 gutter_hovered: bool,
723 hovered_link_state: Option<HoveredLinkState>,
724 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
725 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
726 active_inline_completion: Option<InlineCompletionState>,
727 /// Used to prevent flickering as the user types while the menu is open
728 stale_inline_completion_in_menu: Option<InlineCompletionState>,
729 edit_prediction_settings: EditPredictionSettings,
730 inline_completions_hidden_for_vim_mode: bool,
731 show_inline_completions_override: Option<bool>,
732 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
733 edit_prediction_preview: EditPredictionPreview,
734 edit_prediction_indent_conflict: bool,
735 edit_prediction_requires_modifier_in_indent_conflict: bool,
736 inlay_hint_cache: InlayHintCache,
737 next_inlay_id: usize,
738 _subscriptions: Vec<Subscription>,
739 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
740 gutter_dimensions: GutterDimensions,
741 style: Option<EditorStyle>,
742 text_style_refinement: Option<TextStyleRefinement>,
743 next_editor_action_id: EditorActionId,
744 editor_actions:
745 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
746 use_autoclose: bool,
747 use_auto_surround: bool,
748 auto_replace_emoji_shortcode: bool,
749 jsx_tag_auto_close_enabled_in_any_buffer: bool,
750 show_git_blame_gutter: bool,
751 show_git_blame_inline: bool,
752 show_git_blame_inline_delay_task: Option<Task<()>>,
753 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
754 git_blame_inline_enabled: bool,
755 serialize_dirty_buffers: bool,
756 show_selection_menu: Option<bool>,
757 blame: Option<Entity<GitBlame>>,
758 blame_subscription: Option<Subscription>,
759 custom_context_menu: Option<
760 Box<
761 dyn 'static
762 + Fn(
763 &mut Self,
764 DisplayPoint,
765 &mut Window,
766 &mut Context<Self>,
767 ) -> Option<Entity<ui::ContextMenu>>,
768 >,
769 >,
770 last_bounds: Option<Bounds<Pixels>>,
771 last_position_map: Option<Rc<PositionMap>>,
772 expect_bounds_change: Option<Bounds<Pixels>>,
773 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
774 tasks_update_task: Option<Task<()>>,
775 pub breakpoint_store: Option<Entity<BreakpointStore>>,
776 /// Allow's a user to create a breakpoint by selecting this indicator
777 /// It should be None while a user is not hovering over the gutter
778 /// Otherwise it represents the point that the breakpoint will be shown
779 pub gutter_breakpoint_indicator: Option<DisplayPoint>,
780 in_project_search: bool,
781 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
782 breadcrumb_header: Option<String>,
783 focused_block: Option<FocusedBlock>,
784 next_scroll_position: NextScrollCursorCenterTopBottom,
785 addons: HashMap<TypeId, Box<dyn Addon>>,
786 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
787 load_diff_task: Option<Shared<Task<()>>>,
788 selection_mark_mode: bool,
789 toggle_fold_multiple_buffers: Task<()>,
790 _scroll_cursor_center_top_bottom_task: Task<()>,
791 serialize_selections: Task<()>,
792 serialize_folds: Task<()>,
793 mouse_cursor_hidden: bool,
794 hide_mouse_while_typing: bool,
795}
796
797#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
798enum NextScrollCursorCenterTopBottom {
799 #[default]
800 Center,
801 Top,
802 Bottom,
803}
804
805impl NextScrollCursorCenterTopBottom {
806 fn next(&self) -> Self {
807 match self {
808 Self::Center => Self::Top,
809 Self::Top => Self::Bottom,
810 Self::Bottom => Self::Center,
811 }
812 }
813}
814
815#[derive(Clone)]
816pub struct EditorSnapshot {
817 pub mode: EditorMode,
818 show_gutter: bool,
819 show_line_numbers: Option<bool>,
820 show_git_diff_gutter: Option<bool>,
821 show_code_actions: Option<bool>,
822 show_runnables: Option<bool>,
823 show_breakpoints: Option<bool>,
824 git_blame_gutter_max_author_length: Option<usize>,
825 pub display_snapshot: DisplaySnapshot,
826 pub placeholder_text: Option<Arc<str>>,
827 is_focused: bool,
828 scroll_anchor: ScrollAnchor,
829 ongoing_scroll: OngoingScroll,
830 current_line_highlight: CurrentLineHighlight,
831 gutter_hovered: bool,
832}
833
834const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
835
836#[derive(Default, Debug, Clone, Copy)]
837pub struct GutterDimensions {
838 pub left_padding: Pixels,
839 pub right_padding: Pixels,
840 pub width: Pixels,
841 pub margin: Pixels,
842 pub git_blame_entries_width: Option<Pixels>,
843}
844
845impl GutterDimensions {
846 /// The full width of the space taken up by the gutter.
847 pub fn full_width(&self) -> Pixels {
848 self.margin + self.width
849 }
850
851 /// The width of the space reserved for the fold indicators,
852 /// use alongside 'justify_end' and `gutter_width` to
853 /// right align content with the line numbers
854 pub fn fold_area_width(&self) -> Pixels {
855 self.margin + self.right_padding
856 }
857}
858
859#[derive(Debug)]
860pub struct RemoteSelection {
861 pub replica_id: ReplicaId,
862 pub selection: Selection<Anchor>,
863 pub cursor_shape: CursorShape,
864 pub peer_id: PeerId,
865 pub line_mode: bool,
866 pub participant_index: Option<ParticipantIndex>,
867 pub user_name: Option<SharedString>,
868}
869
870#[derive(Clone, Debug)]
871struct SelectionHistoryEntry {
872 selections: Arc<[Selection<Anchor>]>,
873 select_next_state: Option<SelectNextState>,
874 select_prev_state: Option<SelectNextState>,
875 add_selections_state: Option<AddSelectionsState>,
876}
877
878enum SelectionHistoryMode {
879 Normal,
880 Undoing,
881 Redoing,
882}
883
884#[derive(Clone, PartialEq, Eq, Hash)]
885struct HoveredCursor {
886 replica_id: u16,
887 selection_id: usize,
888}
889
890impl Default for SelectionHistoryMode {
891 fn default() -> Self {
892 Self::Normal
893 }
894}
895
896#[derive(Default)]
897struct SelectionHistory {
898 #[allow(clippy::type_complexity)]
899 selections_by_transaction:
900 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
901 mode: SelectionHistoryMode,
902 undo_stack: VecDeque<SelectionHistoryEntry>,
903 redo_stack: VecDeque<SelectionHistoryEntry>,
904}
905
906impl SelectionHistory {
907 fn insert_transaction(
908 &mut self,
909 transaction_id: TransactionId,
910 selections: Arc<[Selection<Anchor>]>,
911 ) {
912 self.selections_by_transaction
913 .insert(transaction_id, (selections, None));
914 }
915
916 #[allow(clippy::type_complexity)]
917 fn transaction(
918 &self,
919 transaction_id: TransactionId,
920 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
921 self.selections_by_transaction.get(&transaction_id)
922 }
923
924 #[allow(clippy::type_complexity)]
925 fn transaction_mut(
926 &mut self,
927 transaction_id: TransactionId,
928 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
929 self.selections_by_transaction.get_mut(&transaction_id)
930 }
931
932 fn push(&mut self, entry: SelectionHistoryEntry) {
933 if !entry.selections.is_empty() {
934 match self.mode {
935 SelectionHistoryMode::Normal => {
936 self.push_undo(entry);
937 self.redo_stack.clear();
938 }
939 SelectionHistoryMode::Undoing => self.push_redo(entry),
940 SelectionHistoryMode::Redoing => self.push_undo(entry),
941 }
942 }
943 }
944
945 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
946 if self
947 .undo_stack
948 .back()
949 .map_or(true, |e| e.selections != entry.selections)
950 {
951 self.undo_stack.push_back(entry);
952 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
953 self.undo_stack.pop_front();
954 }
955 }
956 }
957
958 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
959 if self
960 .redo_stack
961 .back()
962 .map_or(true, |e| e.selections != entry.selections)
963 {
964 self.redo_stack.push_back(entry);
965 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
966 self.redo_stack.pop_front();
967 }
968 }
969 }
970}
971
972struct RowHighlight {
973 index: usize,
974 range: Range<Anchor>,
975 color: Hsla,
976 should_autoscroll: bool,
977}
978
979#[derive(Clone, Debug)]
980struct AddSelectionsState {
981 above: bool,
982 stack: Vec<usize>,
983}
984
985#[derive(Clone)]
986struct SelectNextState {
987 query: AhoCorasick,
988 wordwise: bool,
989 done: bool,
990}
991
992impl std::fmt::Debug for SelectNextState {
993 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994 f.debug_struct(std::any::type_name::<Self>())
995 .field("wordwise", &self.wordwise)
996 .field("done", &self.done)
997 .finish()
998 }
999}
1000
1001#[derive(Debug)]
1002struct AutocloseRegion {
1003 selection_id: usize,
1004 range: Range<Anchor>,
1005 pair: BracketPair,
1006}
1007
1008#[derive(Debug)]
1009struct SnippetState {
1010 ranges: Vec<Vec<Range<Anchor>>>,
1011 active_index: usize,
1012 choices: Vec<Option<Vec<String>>>,
1013}
1014
1015#[doc(hidden)]
1016pub struct RenameState {
1017 pub range: Range<Anchor>,
1018 pub old_name: Arc<str>,
1019 pub editor: Entity<Editor>,
1020 block_id: CustomBlockId,
1021}
1022
1023struct InvalidationStack<T>(Vec<T>);
1024
1025struct RegisteredInlineCompletionProvider {
1026 provider: Arc<dyn InlineCompletionProviderHandle>,
1027 _subscription: Subscription,
1028}
1029
1030#[derive(Debug, PartialEq, Eq)]
1031struct ActiveDiagnosticGroup {
1032 primary_range: Range<Anchor>,
1033 primary_message: String,
1034 group_id: usize,
1035 blocks: HashMap<CustomBlockId, Diagnostic>,
1036 is_valid: bool,
1037}
1038
1039#[derive(Serialize, Deserialize, Clone, Debug)]
1040pub struct ClipboardSelection {
1041 /// The number of bytes in this selection.
1042 pub len: usize,
1043 /// Whether this was a full-line selection.
1044 pub is_entire_line: bool,
1045 /// The indentation of the first line when this content was originally copied.
1046 pub first_line_indent: u32,
1047}
1048
1049// selections, scroll behavior, was newest selection reversed
1050type SelectSyntaxNodeHistoryState = (
1051 Box<[Selection<usize>]>,
1052 SelectSyntaxNodeScrollBehavior,
1053 bool,
1054);
1055
1056#[derive(Default)]
1057struct SelectSyntaxNodeHistory {
1058 stack: Vec<SelectSyntaxNodeHistoryState>,
1059 // disable temporarily to allow changing selections without losing the stack
1060 pub disable_clearing: bool,
1061}
1062
1063impl SelectSyntaxNodeHistory {
1064 pub fn try_clear(&mut self) {
1065 if !self.disable_clearing {
1066 self.stack.clear();
1067 }
1068 }
1069
1070 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1071 self.stack.push(selection);
1072 }
1073
1074 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1075 self.stack.pop()
1076 }
1077}
1078
1079enum SelectSyntaxNodeScrollBehavior {
1080 CursorTop,
1081 CenterSelection,
1082 CursorBottom,
1083}
1084
1085#[derive(Debug)]
1086pub(crate) struct NavigationData {
1087 cursor_anchor: Anchor,
1088 cursor_position: Point,
1089 scroll_anchor: ScrollAnchor,
1090 scroll_top_row: u32,
1091}
1092
1093#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1094pub enum GotoDefinitionKind {
1095 Symbol,
1096 Declaration,
1097 Type,
1098 Implementation,
1099}
1100
1101#[derive(Debug, Clone)]
1102enum InlayHintRefreshReason {
1103 ModifiersChanged(bool),
1104 Toggle(bool),
1105 SettingsChange(InlayHintSettings),
1106 NewLinesShown,
1107 BufferEdited(HashSet<Arc<Language>>),
1108 RefreshRequested,
1109 ExcerptsRemoved(Vec<ExcerptId>),
1110}
1111
1112impl InlayHintRefreshReason {
1113 fn description(&self) -> &'static str {
1114 match self {
1115 Self::ModifiersChanged(_) => "modifiers changed",
1116 Self::Toggle(_) => "toggle",
1117 Self::SettingsChange(_) => "settings change",
1118 Self::NewLinesShown => "new lines shown",
1119 Self::BufferEdited(_) => "buffer edited",
1120 Self::RefreshRequested => "refresh requested",
1121 Self::ExcerptsRemoved(_) => "excerpts removed",
1122 }
1123 }
1124}
1125
1126pub enum FormatTarget {
1127 Buffers,
1128 Ranges(Vec<Range<MultiBufferPoint>>),
1129}
1130
1131pub(crate) struct FocusedBlock {
1132 id: BlockId,
1133 focus_handle: WeakFocusHandle,
1134}
1135
1136#[derive(Clone)]
1137enum JumpData {
1138 MultiBufferRow {
1139 row: MultiBufferRow,
1140 line_offset_from_top: u32,
1141 },
1142 MultiBufferPoint {
1143 excerpt_id: ExcerptId,
1144 position: Point,
1145 anchor: text::Anchor,
1146 line_offset_from_top: u32,
1147 },
1148}
1149
1150pub enum MultibufferSelectionMode {
1151 First,
1152 All,
1153}
1154
1155#[derive(Clone, Copy, Debug, Default)]
1156pub struct RewrapOptions {
1157 pub override_language_settings: bool,
1158 pub preserve_existing_whitespace: bool,
1159}
1160
1161impl Editor {
1162 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1163 let buffer = cx.new(|cx| Buffer::local("", cx));
1164 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1165 Self::new(
1166 EditorMode::SingleLine { auto_width: false },
1167 buffer,
1168 None,
1169 window,
1170 cx,
1171 )
1172 }
1173
1174 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1175 let buffer = cx.new(|cx| Buffer::local("", cx));
1176 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1177 Self::new(EditorMode::Full, buffer, None, window, cx)
1178 }
1179
1180 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1181 let buffer = cx.new(|cx| Buffer::local("", cx));
1182 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1183 Self::new(
1184 EditorMode::SingleLine { auto_width: true },
1185 buffer,
1186 None,
1187 window,
1188 cx,
1189 )
1190 }
1191
1192 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1193 let buffer = cx.new(|cx| Buffer::local("", cx));
1194 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1195 Self::new(
1196 EditorMode::AutoHeight { max_lines },
1197 buffer,
1198 None,
1199 window,
1200 cx,
1201 )
1202 }
1203
1204 pub fn for_buffer(
1205 buffer: Entity<Buffer>,
1206 project: Option<Entity<Project>>,
1207 window: &mut Window,
1208 cx: &mut Context<Self>,
1209 ) -> Self {
1210 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1211 Self::new(EditorMode::Full, buffer, project, window, cx)
1212 }
1213
1214 pub fn for_multibuffer(
1215 buffer: Entity<MultiBuffer>,
1216 project: Option<Entity<Project>>,
1217 window: &mut Window,
1218 cx: &mut Context<Self>,
1219 ) -> Self {
1220 Self::new(EditorMode::Full, buffer, project, window, cx)
1221 }
1222
1223 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1224 let mut clone = Self::new(
1225 self.mode,
1226 self.buffer.clone(),
1227 self.project.clone(),
1228 window,
1229 cx,
1230 );
1231 self.display_map.update(cx, |display_map, cx| {
1232 let snapshot = display_map.snapshot(cx);
1233 clone.display_map.update(cx, |display_map, cx| {
1234 display_map.set_state(&snapshot, cx);
1235 });
1236 });
1237 clone.folds_did_change(cx);
1238 clone.selections.clone_state(&self.selections);
1239 clone.scroll_manager.clone_state(&self.scroll_manager);
1240 clone.searchable = self.searchable;
1241 clone
1242 }
1243
1244 pub fn new(
1245 mode: EditorMode,
1246 buffer: Entity<MultiBuffer>,
1247 project: Option<Entity<Project>>,
1248 window: &mut Window,
1249 cx: &mut Context<Self>,
1250 ) -> Self {
1251 let style = window.text_style();
1252 let font_size = style.font_size.to_pixels(window.rem_size());
1253 let editor = cx.entity().downgrade();
1254 let fold_placeholder = FoldPlaceholder {
1255 constrain_width: true,
1256 render: Arc::new(move |fold_id, fold_range, cx| {
1257 let editor = editor.clone();
1258 div()
1259 .id(fold_id)
1260 .bg(cx.theme().colors().ghost_element_background)
1261 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1262 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1263 .rounded_xs()
1264 .size_full()
1265 .cursor_pointer()
1266 .child("⋯")
1267 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1268 .on_click(move |_, _window, cx| {
1269 editor
1270 .update(cx, |editor, cx| {
1271 editor.unfold_ranges(
1272 &[fold_range.start..fold_range.end],
1273 true,
1274 false,
1275 cx,
1276 );
1277 cx.stop_propagation();
1278 })
1279 .ok();
1280 })
1281 .into_any()
1282 }),
1283 merge_adjacent: true,
1284 ..Default::default()
1285 };
1286 let display_map = cx.new(|cx| {
1287 DisplayMap::new(
1288 buffer.clone(),
1289 style.font(),
1290 font_size,
1291 None,
1292 FILE_HEADER_HEIGHT,
1293 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1294 fold_placeholder,
1295 cx,
1296 )
1297 });
1298
1299 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1300
1301 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1302
1303 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1304 .then(|| language_settings::SoftWrap::None);
1305
1306 let mut project_subscriptions = Vec::new();
1307 if mode == EditorMode::Full {
1308 if let Some(project) = project.as_ref() {
1309 project_subscriptions.push(cx.subscribe_in(
1310 project,
1311 window,
1312 |editor, _, event, window, cx| match event {
1313 project::Event::RefreshCodeLens => {
1314 // we always query lens with actions, without storing them, always refreshing them
1315 }
1316 project::Event::RefreshInlayHints => {
1317 editor
1318 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1319 }
1320 project::Event::SnippetEdit(id, snippet_edits) => {
1321 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1322 let focus_handle = editor.focus_handle(cx);
1323 if focus_handle.is_focused(window) {
1324 let snapshot = buffer.read(cx).snapshot();
1325 for (range, snippet) in snippet_edits {
1326 let editor_range =
1327 language::range_from_lsp(*range).to_offset(&snapshot);
1328 editor
1329 .insert_snippet(
1330 &[editor_range],
1331 snippet.clone(),
1332 window,
1333 cx,
1334 )
1335 .ok();
1336 }
1337 }
1338 }
1339 }
1340 _ => {}
1341 },
1342 ));
1343 if let Some(task_inventory) = project
1344 .read(cx)
1345 .task_store()
1346 .read(cx)
1347 .task_inventory()
1348 .cloned()
1349 {
1350 project_subscriptions.push(cx.observe_in(
1351 &task_inventory,
1352 window,
1353 |editor, _, window, cx| {
1354 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1355 },
1356 ));
1357 };
1358
1359 project_subscriptions.push(cx.subscribe_in(
1360 &project.read(cx).breakpoint_store(),
1361 window,
1362 |editor, _, event, window, cx| match event {
1363 BreakpointStoreEvent::ActiveDebugLineChanged => {
1364 editor.go_to_active_debug_line(window, cx);
1365 }
1366 _ => {}
1367 },
1368 ));
1369 }
1370 }
1371
1372 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1373
1374 let inlay_hint_settings =
1375 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1376 let focus_handle = cx.focus_handle();
1377 cx.on_focus(&focus_handle, window, Self::handle_focus)
1378 .detach();
1379 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1380 .detach();
1381 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1382 .detach();
1383 cx.on_blur(&focus_handle, window, Self::handle_blur)
1384 .detach();
1385
1386 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1387 Some(false)
1388 } else {
1389 None
1390 };
1391
1392 let breakpoint_store = match (mode, project.as_ref()) {
1393 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1394 _ => None,
1395 };
1396
1397 let mut code_action_providers = Vec::new();
1398 let mut load_uncommitted_diff = None;
1399 if let Some(project) = project.clone() {
1400 load_uncommitted_diff = Some(
1401 get_uncommitted_diff_for_buffer(
1402 &project,
1403 buffer.read(cx).all_buffers(),
1404 buffer.clone(),
1405 cx,
1406 )
1407 .shared(),
1408 );
1409 code_action_providers.push(Rc::new(project) as Rc<_>);
1410 }
1411
1412 let mut this = Self {
1413 focus_handle,
1414 show_cursor_when_unfocused: false,
1415 last_focused_descendant: None,
1416 buffer: buffer.clone(),
1417 display_map: display_map.clone(),
1418 selections,
1419 scroll_manager: ScrollManager::new(cx),
1420 columnar_selection_tail: None,
1421 add_selections_state: None,
1422 select_next_state: None,
1423 select_prev_state: None,
1424 selection_history: Default::default(),
1425 autoclose_regions: Default::default(),
1426 snippet_stack: Default::default(),
1427 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
1428 ime_transaction: Default::default(),
1429 active_diagnostics: None,
1430 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1431 inline_diagnostics_update: Task::ready(()),
1432 inline_diagnostics: Vec::new(),
1433 soft_wrap_mode_override,
1434 hard_wrap: None,
1435 completion_provider: project.clone().map(|project| Box::new(project) as _),
1436 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1437 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1438 project,
1439 blink_manager: blink_manager.clone(),
1440 show_local_selections: true,
1441 show_scrollbars: true,
1442 mode,
1443 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1444 show_gutter: mode == EditorMode::Full,
1445 show_line_numbers: None,
1446 use_relative_line_numbers: None,
1447 show_git_diff_gutter: None,
1448 show_code_actions: None,
1449 show_runnables: None,
1450 show_breakpoints: None,
1451 show_wrap_guides: None,
1452 show_indent_guides,
1453 placeholder_text: None,
1454 highlight_order: 0,
1455 highlighted_rows: HashMap::default(),
1456 background_highlights: Default::default(),
1457 gutter_highlights: TreeMap::default(),
1458 scrollbar_marker_state: ScrollbarMarkerState::default(),
1459 active_indent_guides_state: ActiveIndentGuidesState::default(),
1460 nav_history: None,
1461 context_menu: RefCell::new(None),
1462 context_menu_options: None,
1463 mouse_context_menu: None,
1464 completion_tasks: Default::default(),
1465 signature_help_state: SignatureHelpState::default(),
1466 auto_signature_help: None,
1467 find_all_references_task_sources: Vec::new(),
1468 next_completion_id: 0,
1469 next_inlay_id: 0,
1470 code_action_providers,
1471 available_code_actions: Default::default(),
1472 code_actions_task: Default::default(),
1473 selection_highlight_task: Default::default(),
1474 document_highlights_task: Default::default(),
1475 linked_editing_range_task: Default::default(),
1476 pending_rename: Default::default(),
1477 searchable: true,
1478 cursor_shape: EditorSettings::get_global(cx)
1479 .cursor_shape
1480 .unwrap_or_default(),
1481 current_line_highlight: None,
1482 autoindent_mode: Some(AutoindentMode::EachLine),
1483 collapse_matches: false,
1484 workspace: None,
1485 input_enabled: true,
1486 use_modal_editing: mode == EditorMode::Full,
1487 read_only: false,
1488 use_autoclose: true,
1489 use_auto_surround: true,
1490 auto_replace_emoji_shortcode: false,
1491 jsx_tag_auto_close_enabled_in_any_buffer: false,
1492 leader_peer_id: None,
1493 remote_id: None,
1494 hover_state: Default::default(),
1495 pending_mouse_down: None,
1496 hovered_link_state: Default::default(),
1497 edit_prediction_provider: None,
1498 active_inline_completion: None,
1499 stale_inline_completion_in_menu: None,
1500 edit_prediction_preview: EditPredictionPreview::Inactive {
1501 released_too_fast: false,
1502 },
1503 inline_diagnostics_enabled: mode == EditorMode::Full,
1504 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1505
1506 gutter_hovered: false,
1507 pixel_position_of_newest_cursor: None,
1508 last_bounds: None,
1509 last_position_map: None,
1510 expect_bounds_change: None,
1511 gutter_dimensions: GutterDimensions::default(),
1512 style: None,
1513 show_cursor_names: false,
1514 hovered_cursors: Default::default(),
1515 next_editor_action_id: EditorActionId::default(),
1516 editor_actions: Rc::default(),
1517 inline_completions_hidden_for_vim_mode: false,
1518 show_inline_completions_override: None,
1519 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1520 edit_prediction_settings: EditPredictionSettings::Disabled,
1521 edit_prediction_indent_conflict: false,
1522 edit_prediction_requires_modifier_in_indent_conflict: true,
1523 custom_context_menu: None,
1524 show_git_blame_gutter: false,
1525 show_git_blame_inline: false,
1526 show_selection_menu: None,
1527 show_git_blame_inline_delay_task: None,
1528 git_blame_inline_tooltip: None,
1529 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1530 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1531 .session
1532 .restore_unsaved_buffers,
1533 blame: None,
1534 blame_subscription: None,
1535 tasks: Default::default(),
1536
1537 breakpoint_store,
1538 gutter_breakpoint_indicator: None,
1539 _subscriptions: vec![
1540 cx.observe(&buffer, Self::on_buffer_changed),
1541 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1542 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1543 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1544 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1545 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1546 cx.observe_window_activation(window, |editor, window, cx| {
1547 let active = window.is_window_active();
1548 editor.blink_manager.update(cx, |blink_manager, cx| {
1549 if active {
1550 blink_manager.enable(cx);
1551 } else {
1552 blink_manager.disable(cx);
1553 }
1554 });
1555 }),
1556 ],
1557 tasks_update_task: None,
1558 linked_edit_ranges: Default::default(),
1559 in_project_search: false,
1560 previous_search_ranges: None,
1561 breadcrumb_header: None,
1562 focused_block: None,
1563 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1564 addons: HashMap::default(),
1565 registered_buffers: HashMap::default(),
1566 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1567 selection_mark_mode: false,
1568 toggle_fold_multiple_buffers: Task::ready(()),
1569 serialize_selections: Task::ready(()),
1570 serialize_folds: Task::ready(()),
1571 text_style_refinement: None,
1572 load_diff_task: load_uncommitted_diff,
1573 mouse_cursor_hidden: false,
1574 hide_mouse_while_typing: EditorSettings::get_global(cx)
1575 .hide_mouse_while_typing
1576 .unwrap_or(true),
1577 };
1578 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1579 this._subscriptions
1580 .push(cx.observe(breakpoints, |_, _, cx| {
1581 cx.notify();
1582 }));
1583 }
1584 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1585 this._subscriptions.extend(project_subscriptions);
1586
1587 this.end_selection(window, cx);
1588 this.scroll_manager.show_scrollbars(window, cx);
1589 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1590
1591 if mode == EditorMode::Full {
1592 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1593 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1594
1595 if this.git_blame_inline_enabled {
1596 this.git_blame_inline_enabled = true;
1597 this.start_git_blame_inline(false, window, cx);
1598 }
1599
1600 this.go_to_active_debug_line(window, cx);
1601
1602 if let Some(buffer) = buffer.read(cx).as_singleton() {
1603 if let Some(project) = this.project.as_ref() {
1604 let handle = project.update(cx, |project, cx| {
1605 project.register_buffer_with_language_servers(&buffer, cx)
1606 });
1607 this.registered_buffers
1608 .insert(buffer.read(cx).remote_id(), handle);
1609 }
1610 }
1611 }
1612
1613 this.report_editor_event("Editor Opened", None, cx);
1614 this
1615 }
1616
1617 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1618 self.mouse_context_menu
1619 .as_ref()
1620 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1621 }
1622
1623 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1624 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1625 }
1626
1627 fn key_context_internal(
1628 &self,
1629 has_active_edit_prediction: bool,
1630 window: &Window,
1631 cx: &App,
1632 ) -> KeyContext {
1633 let mut key_context = KeyContext::new_with_defaults();
1634 key_context.add("Editor");
1635 let mode = match self.mode {
1636 EditorMode::SingleLine { .. } => "single_line",
1637 EditorMode::AutoHeight { .. } => "auto_height",
1638 EditorMode::Full => "full",
1639 };
1640
1641 if EditorSettings::jupyter_enabled(cx) {
1642 key_context.add("jupyter");
1643 }
1644
1645 key_context.set("mode", mode);
1646 if self.pending_rename.is_some() {
1647 key_context.add("renaming");
1648 }
1649
1650 match self.context_menu.borrow().as_ref() {
1651 Some(CodeContextMenu::Completions(_)) => {
1652 key_context.add("menu");
1653 key_context.add("showing_completions");
1654 }
1655 Some(CodeContextMenu::CodeActions(_)) => {
1656 key_context.add("menu");
1657 key_context.add("showing_code_actions")
1658 }
1659 None => {}
1660 }
1661
1662 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1663 if !self.focus_handle(cx).contains_focused(window, cx)
1664 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1665 {
1666 for addon in self.addons.values() {
1667 addon.extend_key_context(&mut key_context, cx)
1668 }
1669 }
1670
1671 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1672 if let Some(extension) = singleton_buffer
1673 .read(cx)
1674 .file()
1675 .and_then(|file| file.path().extension()?.to_str())
1676 {
1677 key_context.set("extension", extension.to_string());
1678 }
1679 } else {
1680 key_context.add("multibuffer");
1681 }
1682
1683 if has_active_edit_prediction {
1684 if self.edit_prediction_in_conflict() {
1685 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1686 } else {
1687 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1688 key_context.add("copilot_suggestion");
1689 }
1690 }
1691
1692 if self.selection_mark_mode {
1693 key_context.add("selection_mode");
1694 }
1695
1696 key_context
1697 }
1698
1699 pub fn edit_prediction_in_conflict(&self) -> bool {
1700 if !self.show_edit_predictions_in_menu() {
1701 return false;
1702 }
1703
1704 let showing_completions = self
1705 .context_menu
1706 .borrow()
1707 .as_ref()
1708 .map_or(false, |context| {
1709 matches!(context, CodeContextMenu::Completions(_))
1710 });
1711
1712 showing_completions
1713 || self.edit_prediction_requires_modifier()
1714 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1715 // bindings to insert tab characters.
1716 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1717 }
1718
1719 pub fn accept_edit_prediction_keybind(
1720 &self,
1721 window: &Window,
1722 cx: &App,
1723 ) -> AcceptEditPredictionBinding {
1724 let key_context = self.key_context_internal(true, window, cx);
1725 let in_conflict = self.edit_prediction_in_conflict();
1726
1727 AcceptEditPredictionBinding(
1728 window
1729 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1730 .into_iter()
1731 .filter(|binding| {
1732 !in_conflict
1733 || binding
1734 .keystrokes()
1735 .first()
1736 .map_or(false, |keystroke| keystroke.modifiers.modified())
1737 })
1738 .rev()
1739 .min_by_key(|binding| {
1740 binding
1741 .keystrokes()
1742 .first()
1743 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1744 }),
1745 )
1746 }
1747
1748 pub fn new_file(
1749 workspace: &mut Workspace,
1750 _: &workspace::NewFile,
1751 window: &mut Window,
1752 cx: &mut Context<Workspace>,
1753 ) {
1754 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1755 "Failed to create buffer",
1756 window,
1757 cx,
1758 |e, _, _| match e.error_code() {
1759 ErrorCode::RemoteUpgradeRequired => Some(format!(
1760 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1761 e.error_tag("required").unwrap_or("the latest version")
1762 )),
1763 _ => None,
1764 },
1765 );
1766 }
1767
1768 pub fn new_in_workspace(
1769 workspace: &mut Workspace,
1770 window: &mut Window,
1771 cx: &mut Context<Workspace>,
1772 ) -> Task<Result<Entity<Editor>>> {
1773 let project = workspace.project().clone();
1774 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1775
1776 cx.spawn_in(window, async move |workspace, cx| {
1777 let buffer = create.await?;
1778 workspace.update_in(cx, |workspace, window, cx| {
1779 let editor =
1780 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1781 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1782 editor
1783 })
1784 })
1785 }
1786
1787 fn new_file_vertical(
1788 workspace: &mut Workspace,
1789 _: &workspace::NewFileSplitVertical,
1790 window: &mut Window,
1791 cx: &mut Context<Workspace>,
1792 ) {
1793 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1794 }
1795
1796 fn new_file_horizontal(
1797 workspace: &mut Workspace,
1798 _: &workspace::NewFileSplitHorizontal,
1799 window: &mut Window,
1800 cx: &mut Context<Workspace>,
1801 ) {
1802 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1803 }
1804
1805 fn new_file_in_direction(
1806 workspace: &mut Workspace,
1807 direction: SplitDirection,
1808 window: &mut Window,
1809 cx: &mut Context<Workspace>,
1810 ) {
1811 let project = workspace.project().clone();
1812 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1813
1814 cx.spawn_in(window, async move |workspace, cx| {
1815 let buffer = create.await?;
1816 workspace.update_in(cx, move |workspace, window, cx| {
1817 workspace.split_item(
1818 direction,
1819 Box::new(
1820 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1821 ),
1822 window,
1823 cx,
1824 )
1825 })?;
1826 anyhow::Ok(())
1827 })
1828 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1829 match e.error_code() {
1830 ErrorCode::RemoteUpgradeRequired => Some(format!(
1831 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1832 e.error_tag("required").unwrap_or("the latest version")
1833 )),
1834 _ => None,
1835 }
1836 });
1837 }
1838
1839 pub fn leader_peer_id(&self) -> Option<PeerId> {
1840 self.leader_peer_id
1841 }
1842
1843 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1844 &self.buffer
1845 }
1846
1847 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1848 self.workspace.as_ref()?.0.upgrade()
1849 }
1850
1851 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1852 self.buffer().read(cx).title(cx)
1853 }
1854
1855 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1856 let git_blame_gutter_max_author_length = self
1857 .render_git_blame_gutter(cx)
1858 .then(|| {
1859 if let Some(blame) = self.blame.as_ref() {
1860 let max_author_length =
1861 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1862 Some(max_author_length)
1863 } else {
1864 None
1865 }
1866 })
1867 .flatten();
1868
1869 EditorSnapshot {
1870 mode: self.mode,
1871 show_gutter: self.show_gutter,
1872 show_line_numbers: self.show_line_numbers,
1873 show_git_diff_gutter: self.show_git_diff_gutter,
1874 show_code_actions: self.show_code_actions,
1875 show_runnables: self.show_runnables,
1876 show_breakpoints: self.show_breakpoints,
1877 git_blame_gutter_max_author_length,
1878 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1879 scroll_anchor: self.scroll_manager.anchor(),
1880 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1881 placeholder_text: self.placeholder_text.clone(),
1882 is_focused: self.focus_handle.is_focused(window),
1883 current_line_highlight: self
1884 .current_line_highlight
1885 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1886 gutter_hovered: self.gutter_hovered,
1887 }
1888 }
1889
1890 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1891 self.buffer.read(cx).language_at(point, cx)
1892 }
1893
1894 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1895 self.buffer.read(cx).read(cx).file_at(point).cloned()
1896 }
1897
1898 pub fn active_excerpt(
1899 &self,
1900 cx: &App,
1901 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1902 self.buffer
1903 .read(cx)
1904 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1905 }
1906
1907 pub fn mode(&self) -> EditorMode {
1908 self.mode
1909 }
1910
1911 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1912 self.collaboration_hub.as_deref()
1913 }
1914
1915 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1916 self.collaboration_hub = Some(hub);
1917 }
1918
1919 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1920 self.in_project_search = in_project_search;
1921 }
1922
1923 pub fn set_custom_context_menu(
1924 &mut self,
1925 f: impl 'static
1926 + Fn(
1927 &mut Self,
1928 DisplayPoint,
1929 &mut Window,
1930 &mut Context<Self>,
1931 ) -> Option<Entity<ui::ContextMenu>>,
1932 ) {
1933 self.custom_context_menu = Some(Box::new(f))
1934 }
1935
1936 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1937 self.completion_provider = provider;
1938 }
1939
1940 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1941 self.semantics_provider.clone()
1942 }
1943
1944 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1945 self.semantics_provider = provider;
1946 }
1947
1948 pub fn set_edit_prediction_provider<T>(
1949 &mut self,
1950 provider: Option<Entity<T>>,
1951 window: &mut Window,
1952 cx: &mut Context<Self>,
1953 ) where
1954 T: EditPredictionProvider,
1955 {
1956 self.edit_prediction_provider =
1957 provider.map(|provider| RegisteredInlineCompletionProvider {
1958 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1959 if this.focus_handle.is_focused(window) {
1960 this.update_visible_inline_completion(window, cx);
1961 }
1962 }),
1963 provider: Arc::new(provider),
1964 });
1965 self.update_edit_prediction_settings(cx);
1966 self.refresh_inline_completion(false, false, window, cx);
1967 }
1968
1969 pub fn placeholder_text(&self) -> Option<&str> {
1970 self.placeholder_text.as_deref()
1971 }
1972
1973 pub fn set_placeholder_text(
1974 &mut self,
1975 placeholder_text: impl Into<Arc<str>>,
1976 cx: &mut Context<Self>,
1977 ) {
1978 let placeholder_text = Some(placeholder_text.into());
1979 if self.placeholder_text != placeholder_text {
1980 self.placeholder_text = placeholder_text;
1981 cx.notify();
1982 }
1983 }
1984
1985 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1986 self.cursor_shape = cursor_shape;
1987
1988 // Disrupt blink for immediate user feedback that the cursor shape has changed
1989 self.blink_manager.update(cx, BlinkManager::show_cursor);
1990
1991 cx.notify();
1992 }
1993
1994 pub fn set_current_line_highlight(
1995 &mut self,
1996 current_line_highlight: Option<CurrentLineHighlight>,
1997 ) {
1998 self.current_line_highlight = current_line_highlight;
1999 }
2000
2001 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2002 self.collapse_matches = collapse_matches;
2003 }
2004
2005 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2006 let buffers = self.buffer.read(cx).all_buffers();
2007 let Some(project) = self.project.as_ref() else {
2008 return;
2009 };
2010 project.update(cx, |project, cx| {
2011 for buffer in buffers {
2012 self.registered_buffers
2013 .entry(buffer.read(cx).remote_id())
2014 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2015 }
2016 })
2017 }
2018
2019 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2020 if self.collapse_matches {
2021 return range.start..range.start;
2022 }
2023 range.clone()
2024 }
2025
2026 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2027 if self.display_map.read(cx).clip_at_line_ends != clip {
2028 self.display_map
2029 .update(cx, |map, _| map.clip_at_line_ends = clip);
2030 }
2031 }
2032
2033 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2034 self.input_enabled = input_enabled;
2035 }
2036
2037 pub fn set_inline_completions_hidden_for_vim_mode(
2038 &mut self,
2039 hidden: bool,
2040 window: &mut Window,
2041 cx: &mut Context<Self>,
2042 ) {
2043 if hidden != self.inline_completions_hidden_for_vim_mode {
2044 self.inline_completions_hidden_for_vim_mode = hidden;
2045 if hidden {
2046 self.update_visible_inline_completion(window, cx);
2047 } else {
2048 self.refresh_inline_completion(true, false, window, cx);
2049 }
2050 }
2051 }
2052
2053 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2054 self.menu_inline_completions_policy = value;
2055 }
2056
2057 pub fn set_autoindent(&mut self, autoindent: bool) {
2058 if autoindent {
2059 self.autoindent_mode = Some(AutoindentMode::EachLine);
2060 } else {
2061 self.autoindent_mode = None;
2062 }
2063 }
2064
2065 pub fn read_only(&self, cx: &App) -> bool {
2066 self.read_only || self.buffer.read(cx).read_only()
2067 }
2068
2069 pub fn set_read_only(&mut self, read_only: bool) {
2070 self.read_only = read_only;
2071 }
2072
2073 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2074 self.use_autoclose = autoclose;
2075 }
2076
2077 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2078 self.use_auto_surround = auto_surround;
2079 }
2080
2081 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2082 self.auto_replace_emoji_shortcode = auto_replace;
2083 }
2084
2085 pub fn toggle_edit_predictions(
2086 &mut self,
2087 _: &ToggleEditPrediction,
2088 window: &mut Window,
2089 cx: &mut Context<Self>,
2090 ) {
2091 if self.show_inline_completions_override.is_some() {
2092 self.set_show_edit_predictions(None, window, cx);
2093 } else {
2094 let show_edit_predictions = !self.edit_predictions_enabled();
2095 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2096 }
2097 }
2098
2099 pub fn set_show_edit_predictions(
2100 &mut self,
2101 show_edit_predictions: Option<bool>,
2102 window: &mut Window,
2103 cx: &mut Context<Self>,
2104 ) {
2105 self.show_inline_completions_override = show_edit_predictions;
2106 self.update_edit_prediction_settings(cx);
2107
2108 if let Some(false) = show_edit_predictions {
2109 self.discard_inline_completion(false, cx);
2110 } else {
2111 self.refresh_inline_completion(false, true, window, cx);
2112 }
2113 }
2114
2115 fn inline_completions_disabled_in_scope(
2116 &self,
2117 buffer: &Entity<Buffer>,
2118 buffer_position: language::Anchor,
2119 cx: &App,
2120 ) -> bool {
2121 let snapshot = buffer.read(cx).snapshot();
2122 let settings = snapshot.settings_at(buffer_position, cx);
2123
2124 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2125 return false;
2126 };
2127
2128 scope.override_name().map_or(false, |scope_name| {
2129 settings
2130 .edit_predictions_disabled_in
2131 .iter()
2132 .any(|s| s == scope_name)
2133 })
2134 }
2135
2136 pub fn set_use_modal_editing(&mut self, to: bool) {
2137 self.use_modal_editing = to;
2138 }
2139
2140 pub fn use_modal_editing(&self) -> bool {
2141 self.use_modal_editing
2142 }
2143
2144 fn selections_did_change(
2145 &mut self,
2146 local: bool,
2147 old_cursor_position: &Anchor,
2148 show_completions: bool,
2149 window: &mut Window,
2150 cx: &mut Context<Self>,
2151 ) {
2152 window.invalidate_character_coordinates();
2153
2154 // Copy selections to primary selection buffer
2155 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2156 if local {
2157 let selections = self.selections.all::<usize>(cx);
2158 let buffer_handle = self.buffer.read(cx).read(cx);
2159
2160 let mut text = String::new();
2161 for (index, selection) in selections.iter().enumerate() {
2162 let text_for_selection = buffer_handle
2163 .text_for_range(selection.start..selection.end)
2164 .collect::<String>();
2165
2166 text.push_str(&text_for_selection);
2167 if index != selections.len() - 1 {
2168 text.push('\n');
2169 }
2170 }
2171
2172 if !text.is_empty() {
2173 cx.write_to_primary(ClipboardItem::new_string(text));
2174 }
2175 }
2176
2177 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2178 self.buffer.update(cx, |buffer, cx| {
2179 buffer.set_active_selections(
2180 &self.selections.disjoint_anchors(),
2181 self.selections.line_mode,
2182 self.cursor_shape,
2183 cx,
2184 )
2185 });
2186 }
2187 let display_map = self
2188 .display_map
2189 .update(cx, |display_map, cx| display_map.snapshot(cx));
2190 let buffer = &display_map.buffer_snapshot;
2191 self.add_selections_state = None;
2192 self.select_next_state = None;
2193 self.select_prev_state = None;
2194 self.select_syntax_node_history.try_clear();
2195 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2196 self.snippet_stack
2197 .invalidate(&self.selections.disjoint_anchors(), buffer);
2198 self.take_rename(false, window, cx);
2199
2200 let new_cursor_position = self.selections.newest_anchor().head();
2201
2202 self.push_to_nav_history(
2203 *old_cursor_position,
2204 Some(new_cursor_position.to_point(buffer)),
2205 false,
2206 cx,
2207 );
2208
2209 if local {
2210 let new_cursor_position = self.selections.newest_anchor().head();
2211 let mut context_menu = self.context_menu.borrow_mut();
2212 let completion_menu = match context_menu.as_ref() {
2213 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2214 _ => {
2215 *context_menu = None;
2216 None
2217 }
2218 };
2219 if let Some(buffer_id) = new_cursor_position.buffer_id {
2220 if !self.registered_buffers.contains_key(&buffer_id) {
2221 if let Some(project) = self.project.as_ref() {
2222 project.update(cx, |project, cx| {
2223 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2224 return;
2225 };
2226 self.registered_buffers.insert(
2227 buffer_id,
2228 project.register_buffer_with_language_servers(&buffer, cx),
2229 );
2230 })
2231 }
2232 }
2233 }
2234
2235 if let Some(completion_menu) = completion_menu {
2236 let cursor_position = new_cursor_position.to_offset(buffer);
2237 let (word_range, kind) =
2238 buffer.surrounding_word(completion_menu.initial_position, true);
2239 if kind == Some(CharKind::Word)
2240 && word_range.to_inclusive().contains(&cursor_position)
2241 {
2242 let mut completion_menu = completion_menu.clone();
2243 drop(context_menu);
2244
2245 let query = Self::completion_query(buffer, cursor_position);
2246 cx.spawn(async move |this, cx| {
2247 completion_menu
2248 .filter(query.as_deref(), cx.background_executor().clone())
2249 .await;
2250
2251 this.update(cx, |this, cx| {
2252 let mut context_menu = this.context_menu.borrow_mut();
2253 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2254 else {
2255 return;
2256 };
2257
2258 if menu.id > completion_menu.id {
2259 return;
2260 }
2261
2262 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2263 drop(context_menu);
2264 cx.notify();
2265 })
2266 })
2267 .detach();
2268
2269 if show_completions {
2270 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2271 }
2272 } else {
2273 drop(context_menu);
2274 self.hide_context_menu(window, cx);
2275 }
2276 } else {
2277 drop(context_menu);
2278 }
2279
2280 hide_hover(self, cx);
2281
2282 if old_cursor_position.to_display_point(&display_map).row()
2283 != new_cursor_position.to_display_point(&display_map).row()
2284 {
2285 self.available_code_actions.take();
2286 }
2287 self.refresh_code_actions(window, cx);
2288 self.refresh_document_highlights(cx);
2289 self.refresh_selected_text_highlights(window, cx);
2290 refresh_matching_bracket_highlights(self, window, cx);
2291 self.update_visible_inline_completion(window, cx);
2292 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2293 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2294 if self.git_blame_inline_enabled {
2295 self.start_inline_blame_timer(window, cx);
2296 }
2297 }
2298
2299 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2300 cx.emit(EditorEvent::SelectionsChanged { local });
2301
2302 let selections = &self.selections.disjoint;
2303 if selections.len() == 1 {
2304 cx.emit(SearchEvent::ActiveMatchChanged)
2305 }
2306 if local
2307 && self.is_singleton(cx)
2308 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2309 {
2310 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2311 let background_executor = cx.background_executor().clone();
2312 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2313 let snapshot = self.buffer().read(cx).snapshot(cx);
2314 let selections = selections.clone();
2315 self.serialize_selections = cx.background_spawn(async move {
2316 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2317 let selections = selections
2318 .iter()
2319 .map(|selection| {
2320 (
2321 selection.start.to_offset(&snapshot),
2322 selection.end.to_offset(&snapshot),
2323 )
2324 })
2325 .collect();
2326
2327 DB.save_editor_selections(editor_id, workspace_id, selections)
2328 .await
2329 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2330 .log_err();
2331 });
2332 }
2333 }
2334
2335 cx.notify();
2336 }
2337
2338 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
2339 if !self.is_singleton(cx)
2340 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
2341 {
2342 return;
2343 }
2344
2345 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
2346 return;
2347 };
2348 let background_executor = cx.background_executor().clone();
2349 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2350 let snapshot = self.buffer().read(cx).snapshot(cx);
2351 let folds = self.display_map.update(cx, |display_map, cx| {
2352 display_map
2353 .snapshot(cx)
2354 .folds_in_range(0..snapshot.len())
2355 .map(|fold| {
2356 (
2357 fold.range.start.to_offset(&snapshot),
2358 fold.range.end.to_offset(&snapshot),
2359 )
2360 })
2361 .collect()
2362 });
2363 self.serialize_folds = cx.background_spawn(async move {
2364 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2365 DB.save_editor_folds(editor_id, workspace_id, folds)
2366 .await
2367 .with_context(|| format!("persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"))
2368 .log_err();
2369 });
2370 }
2371
2372 pub fn sync_selections(
2373 &mut self,
2374 other: Entity<Editor>,
2375 cx: &mut Context<Self>,
2376 ) -> gpui::Subscription {
2377 let other_selections = other.read(cx).selections.disjoint.to_vec();
2378 self.selections.change_with(cx, |selections| {
2379 selections.select_anchors(other_selections);
2380 });
2381
2382 let other_subscription =
2383 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2384 EditorEvent::SelectionsChanged { local: true } => {
2385 let other_selections = other.read(cx).selections.disjoint.to_vec();
2386 if other_selections.is_empty() {
2387 return;
2388 }
2389 this.selections.change_with(cx, |selections| {
2390 selections.select_anchors(other_selections);
2391 });
2392 }
2393 _ => {}
2394 });
2395
2396 let this_subscription =
2397 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2398 EditorEvent::SelectionsChanged { local: true } => {
2399 let these_selections = this.selections.disjoint.to_vec();
2400 if these_selections.is_empty() {
2401 return;
2402 }
2403 other.update(cx, |other_editor, cx| {
2404 other_editor.selections.change_with(cx, |selections| {
2405 selections.select_anchors(these_selections);
2406 })
2407 });
2408 }
2409 _ => {}
2410 });
2411
2412 Subscription::join(other_subscription, this_subscription)
2413 }
2414
2415 pub fn change_selections<R>(
2416 &mut self,
2417 autoscroll: Option<Autoscroll>,
2418 window: &mut Window,
2419 cx: &mut Context<Self>,
2420 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2421 ) -> R {
2422 self.change_selections_inner(autoscroll, true, window, cx, change)
2423 }
2424
2425 fn change_selections_inner<R>(
2426 &mut self,
2427 autoscroll: Option<Autoscroll>,
2428 request_completions: bool,
2429 window: &mut Window,
2430 cx: &mut Context<Self>,
2431 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2432 ) -> R {
2433 let old_cursor_position = self.selections.newest_anchor().head();
2434 self.push_to_selection_history();
2435
2436 let (changed, result) = self.selections.change_with(cx, change);
2437
2438 if changed {
2439 if let Some(autoscroll) = autoscroll {
2440 self.request_autoscroll(autoscroll, cx);
2441 }
2442 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2443
2444 if self.should_open_signature_help_automatically(
2445 &old_cursor_position,
2446 self.signature_help_state.backspace_pressed(),
2447 cx,
2448 ) {
2449 self.show_signature_help(&ShowSignatureHelp, window, cx);
2450 }
2451 self.signature_help_state.set_backspace_pressed(false);
2452 }
2453
2454 result
2455 }
2456
2457 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2458 where
2459 I: IntoIterator<Item = (Range<S>, T)>,
2460 S: ToOffset,
2461 T: Into<Arc<str>>,
2462 {
2463 if self.read_only(cx) {
2464 return;
2465 }
2466
2467 self.buffer
2468 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2469 }
2470
2471 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2472 where
2473 I: IntoIterator<Item = (Range<S>, T)>,
2474 S: ToOffset,
2475 T: Into<Arc<str>>,
2476 {
2477 if self.read_only(cx) {
2478 return;
2479 }
2480
2481 self.buffer.update(cx, |buffer, cx| {
2482 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2483 });
2484 }
2485
2486 pub fn edit_with_block_indent<I, S, T>(
2487 &mut self,
2488 edits: I,
2489 original_indent_columns: Vec<Option<u32>>,
2490 cx: &mut Context<Self>,
2491 ) where
2492 I: IntoIterator<Item = (Range<S>, T)>,
2493 S: ToOffset,
2494 T: Into<Arc<str>>,
2495 {
2496 if self.read_only(cx) {
2497 return;
2498 }
2499
2500 self.buffer.update(cx, |buffer, cx| {
2501 buffer.edit(
2502 edits,
2503 Some(AutoindentMode::Block {
2504 original_indent_columns,
2505 }),
2506 cx,
2507 )
2508 });
2509 }
2510
2511 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2512 self.hide_context_menu(window, cx);
2513
2514 match phase {
2515 SelectPhase::Begin {
2516 position,
2517 add,
2518 click_count,
2519 } => self.begin_selection(position, add, click_count, window, cx),
2520 SelectPhase::BeginColumnar {
2521 position,
2522 goal_column,
2523 reset,
2524 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2525 SelectPhase::Extend {
2526 position,
2527 click_count,
2528 } => self.extend_selection(position, click_count, window, cx),
2529 SelectPhase::Update {
2530 position,
2531 goal_column,
2532 scroll_delta,
2533 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2534 SelectPhase::End => self.end_selection(window, cx),
2535 }
2536 }
2537
2538 fn extend_selection(
2539 &mut self,
2540 position: DisplayPoint,
2541 click_count: usize,
2542 window: &mut Window,
2543 cx: &mut Context<Self>,
2544 ) {
2545 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2546 let tail = self.selections.newest::<usize>(cx).tail();
2547 self.begin_selection(position, false, click_count, window, cx);
2548
2549 let position = position.to_offset(&display_map, Bias::Left);
2550 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2551
2552 let mut pending_selection = self
2553 .selections
2554 .pending_anchor()
2555 .expect("extend_selection not called with pending selection");
2556 if position >= tail {
2557 pending_selection.start = tail_anchor;
2558 } else {
2559 pending_selection.end = tail_anchor;
2560 pending_selection.reversed = true;
2561 }
2562
2563 let mut pending_mode = self.selections.pending_mode().unwrap();
2564 match &mut pending_mode {
2565 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2566 _ => {}
2567 }
2568
2569 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2570 s.set_pending(pending_selection, pending_mode)
2571 });
2572 }
2573
2574 fn begin_selection(
2575 &mut self,
2576 position: DisplayPoint,
2577 add: bool,
2578 click_count: usize,
2579 window: &mut Window,
2580 cx: &mut Context<Self>,
2581 ) {
2582 if !self.focus_handle.is_focused(window) {
2583 self.last_focused_descendant = None;
2584 window.focus(&self.focus_handle);
2585 }
2586
2587 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2588 let buffer = &display_map.buffer_snapshot;
2589 let newest_selection = self.selections.newest_anchor().clone();
2590 let position = display_map.clip_point(position, Bias::Left);
2591
2592 let start;
2593 let end;
2594 let mode;
2595 let mut auto_scroll;
2596 match click_count {
2597 1 => {
2598 start = buffer.anchor_before(position.to_point(&display_map));
2599 end = start;
2600 mode = SelectMode::Character;
2601 auto_scroll = true;
2602 }
2603 2 => {
2604 let range = movement::surrounding_word(&display_map, position);
2605 start = buffer.anchor_before(range.start.to_point(&display_map));
2606 end = buffer.anchor_before(range.end.to_point(&display_map));
2607 mode = SelectMode::Word(start..end);
2608 auto_scroll = true;
2609 }
2610 3 => {
2611 let position = display_map
2612 .clip_point(position, Bias::Left)
2613 .to_point(&display_map);
2614 let line_start = display_map.prev_line_boundary(position).0;
2615 let next_line_start = buffer.clip_point(
2616 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2617 Bias::Left,
2618 );
2619 start = buffer.anchor_before(line_start);
2620 end = buffer.anchor_before(next_line_start);
2621 mode = SelectMode::Line(start..end);
2622 auto_scroll = true;
2623 }
2624 _ => {
2625 start = buffer.anchor_before(0);
2626 end = buffer.anchor_before(buffer.len());
2627 mode = SelectMode::All;
2628 auto_scroll = false;
2629 }
2630 }
2631 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2632
2633 let point_to_delete: Option<usize> = {
2634 let selected_points: Vec<Selection<Point>> =
2635 self.selections.disjoint_in_range(start..end, cx);
2636
2637 if !add || click_count > 1 {
2638 None
2639 } else if !selected_points.is_empty() {
2640 Some(selected_points[0].id)
2641 } else {
2642 let clicked_point_already_selected =
2643 self.selections.disjoint.iter().find(|selection| {
2644 selection.start.to_point(buffer) == start.to_point(buffer)
2645 || selection.end.to_point(buffer) == end.to_point(buffer)
2646 });
2647
2648 clicked_point_already_selected.map(|selection| selection.id)
2649 }
2650 };
2651
2652 let selections_count = self.selections.count();
2653
2654 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2655 if let Some(point_to_delete) = point_to_delete {
2656 s.delete(point_to_delete);
2657
2658 if selections_count == 1 {
2659 s.set_pending_anchor_range(start..end, mode);
2660 }
2661 } else {
2662 if !add {
2663 s.clear_disjoint();
2664 } else if click_count > 1 {
2665 s.delete(newest_selection.id)
2666 }
2667
2668 s.set_pending_anchor_range(start..end, mode);
2669 }
2670 });
2671 }
2672
2673 fn begin_columnar_selection(
2674 &mut self,
2675 position: DisplayPoint,
2676 goal_column: u32,
2677 reset: bool,
2678 window: &mut Window,
2679 cx: &mut Context<Self>,
2680 ) {
2681 if !self.focus_handle.is_focused(window) {
2682 self.last_focused_descendant = None;
2683 window.focus(&self.focus_handle);
2684 }
2685
2686 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2687
2688 if reset {
2689 let pointer_position = display_map
2690 .buffer_snapshot
2691 .anchor_before(position.to_point(&display_map));
2692
2693 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2694 s.clear_disjoint();
2695 s.set_pending_anchor_range(
2696 pointer_position..pointer_position,
2697 SelectMode::Character,
2698 );
2699 });
2700 }
2701
2702 let tail = self.selections.newest::<Point>(cx).tail();
2703 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2704
2705 if !reset {
2706 self.select_columns(
2707 tail.to_display_point(&display_map),
2708 position,
2709 goal_column,
2710 &display_map,
2711 window,
2712 cx,
2713 );
2714 }
2715 }
2716
2717 fn update_selection(
2718 &mut self,
2719 position: DisplayPoint,
2720 goal_column: u32,
2721 scroll_delta: gpui::Point<f32>,
2722 window: &mut Window,
2723 cx: &mut Context<Self>,
2724 ) {
2725 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2726
2727 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2728 let tail = tail.to_display_point(&display_map);
2729 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2730 } else if let Some(mut pending) = self.selections.pending_anchor() {
2731 let buffer = self.buffer.read(cx).snapshot(cx);
2732 let head;
2733 let tail;
2734 let mode = self.selections.pending_mode().unwrap();
2735 match &mode {
2736 SelectMode::Character => {
2737 head = position.to_point(&display_map);
2738 tail = pending.tail().to_point(&buffer);
2739 }
2740 SelectMode::Word(original_range) => {
2741 let original_display_range = original_range.start.to_display_point(&display_map)
2742 ..original_range.end.to_display_point(&display_map);
2743 let original_buffer_range = original_display_range.start.to_point(&display_map)
2744 ..original_display_range.end.to_point(&display_map);
2745 if movement::is_inside_word(&display_map, position)
2746 || original_display_range.contains(&position)
2747 {
2748 let word_range = movement::surrounding_word(&display_map, position);
2749 if word_range.start < original_display_range.start {
2750 head = word_range.start.to_point(&display_map);
2751 } else {
2752 head = word_range.end.to_point(&display_map);
2753 }
2754 } else {
2755 head = position.to_point(&display_map);
2756 }
2757
2758 if head <= original_buffer_range.start {
2759 tail = original_buffer_range.end;
2760 } else {
2761 tail = original_buffer_range.start;
2762 }
2763 }
2764 SelectMode::Line(original_range) => {
2765 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2766
2767 let position = display_map
2768 .clip_point(position, Bias::Left)
2769 .to_point(&display_map);
2770 let line_start = display_map.prev_line_boundary(position).0;
2771 let next_line_start = buffer.clip_point(
2772 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2773 Bias::Left,
2774 );
2775
2776 if line_start < original_range.start {
2777 head = line_start
2778 } else {
2779 head = next_line_start
2780 }
2781
2782 if head <= original_range.start {
2783 tail = original_range.end;
2784 } else {
2785 tail = original_range.start;
2786 }
2787 }
2788 SelectMode::All => {
2789 return;
2790 }
2791 };
2792
2793 if head < tail {
2794 pending.start = buffer.anchor_before(head);
2795 pending.end = buffer.anchor_before(tail);
2796 pending.reversed = true;
2797 } else {
2798 pending.start = buffer.anchor_before(tail);
2799 pending.end = buffer.anchor_before(head);
2800 pending.reversed = false;
2801 }
2802
2803 self.change_selections(None, window, cx, |s| {
2804 s.set_pending(pending, mode);
2805 });
2806 } else {
2807 log::error!("update_selection dispatched with no pending selection");
2808 return;
2809 }
2810
2811 self.apply_scroll_delta(scroll_delta, window, cx);
2812 cx.notify();
2813 }
2814
2815 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2816 self.columnar_selection_tail.take();
2817 if self.selections.pending_anchor().is_some() {
2818 let selections = self.selections.all::<usize>(cx);
2819 self.change_selections(None, window, cx, |s| {
2820 s.select(selections);
2821 s.clear_pending();
2822 });
2823 }
2824 }
2825
2826 fn select_columns(
2827 &mut self,
2828 tail: DisplayPoint,
2829 head: DisplayPoint,
2830 goal_column: u32,
2831 display_map: &DisplaySnapshot,
2832 window: &mut Window,
2833 cx: &mut Context<Self>,
2834 ) {
2835 let start_row = cmp::min(tail.row(), head.row());
2836 let end_row = cmp::max(tail.row(), head.row());
2837 let start_column = cmp::min(tail.column(), goal_column);
2838 let end_column = cmp::max(tail.column(), goal_column);
2839 let reversed = start_column < tail.column();
2840
2841 let selection_ranges = (start_row.0..=end_row.0)
2842 .map(DisplayRow)
2843 .filter_map(|row| {
2844 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2845 let start = display_map
2846 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2847 .to_point(display_map);
2848 let end = display_map
2849 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2850 .to_point(display_map);
2851 if reversed {
2852 Some(end..start)
2853 } else {
2854 Some(start..end)
2855 }
2856 } else {
2857 None
2858 }
2859 })
2860 .collect::<Vec<_>>();
2861
2862 self.change_selections(None, window, cx, |s| {
2863 s.select_ranges(selection_ranges);
2864 });
2865 cx.notify();
2866 }
2867
2868 pub fn has_pending_nonempty_selection(&self) -> bool {
2869 let pending_nonempty_selection = match self.selections.pending_anchor() {
2870 Some(Selection { start, end, .. }) => start != end,
2871 None => false,
2872 };
2873
2874 pending_nonempty_selection
2875 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2876 }
2877
2878 pub fn has_pending_selection(&self) -> bool {
2879 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2880 }
2881
2882 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2883 self.selection_mark_mode = false;
2884
2885 if self.clear_expanded_diff_hunks(cx) {
2886 cx.notify();
2887 return;
2888 }
2889 if self.dismiss_menus_and_popups(true, window, cx) {
2890 return;
2891 }
2892
2893 if self.mode == EditorMode::Full
2894 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2895 {
2896 return;
2897 }
2898
2899 cx.propagate();
2900 }
2901
2902 pub fn dismiss_menus_and_popups(
2903 &mut self,
2904 is_user_requested: bool,
2905 window: &mut Window,
2906 cx: &mut Context<Self>,
2907 ) -> bool {
2908 if self.take_rename(false, window, cx).is_some() {
2909 return true;
2910 }
2911
2912 if hide_hover(self, cx) {
2913 return true;
2914 }
2915
2916 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2917 return true;
2918 }
2919
2920 if self.hide_context_menu(window, cx).is_some() {
2921 return true;
2922 }
2923
2924 if self.mouse_context_menu.take().is_some() {
2925 return true;
2926 }
2927
2928 if is_user_requested && self.discard_inline_completion(true, cx) {
2929 return true;
2930 }
2931
2932 if self.snippet_stack.pop().is_some() {
2933 return true;
2934 }
2935
2936 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2937 self.dismiss_diagnostics(cx);
2938 return true;
2939 }
2940
2941 false
2942 }
2943
2944 fn linked_editing_ranges_for(
2945 &self,
2946 selection: Range<text::Anchor>,
2947 cx: &App,
2948 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2949 if self.linked_edit_ranges.is_empty() {
2950 return None;
2951 }
2952 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2953 selection.end.buffer_id.and_then(|end_buffer_id| {
2954 if selection.start.buffer_id != Some(end_buffer_id) {
2955 return None;
2956 }
2957 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2958 let snapshot = buffer.read(cx).snapshot();
2959 self.linked_edit_ranges
2960 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2961 .map(|ranges| (ranges, snapshot, buffer))
2962 })?;
2963 use text::ToOffset as TO;
2964 // find offset from the start of current range to current cursor position
2965 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2966
2967 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2968 let start_difference = start_offset - start_byte_offset;
2969 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2970 let end_difference = end_offset - start_byte_offset;
2971 // Current range has associated linked ranges.
2972 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2973 for range in linked_ranges.iter() {
2974 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2975 let end_offset = start_offset + end_difference;
2976 let start_offset = start_offset + start_difference;
2977 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2978 continue;
2979 }
2980 if self.selections.disjoint_anchor_ranges().any(|s| {
2981 if s.start.buffer_id != selection.start.buffer_id
2982 || s.end.buffer_id != selection.end.buffer_id
2983 {
2984 return false;
2985 }
2986 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2987 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2988 }) {
2989 continue;
2990 }
2991 let start = buffer_snapshot.anchor_after(start_offset);
2992 let end = buffer_snapshot.anchor_after(end_offset);
2993 linked_edits
2994 .entry(buffer.clone())
2995 .or_default()
2996 .push(start..end);
2997 }
2998 Some(linked_edits)
2999 }
3000
3001 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3002 let text: Arc<str> = text.into();
3003
3004 if self.read_only(cx) {
3005 return;
3006 }
3007
3008 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3009
3010 let selections = self.selections.all_adjusted(cx);
3011 let mut bracket_inserted = false;
3012 let mut edits = Vec::new();
3013 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3014 let mut new_selections = Vec::with_capacity(selections.len());
3015 let mut new_autoclose_regions = Vec::new();
3016 let snapshot = self.buffer.read(cx).read(cx);
3017
3018 for (selection, autoclose_region) in
3019 self.selections_with_autoclose_regions(selections, &snapshot)
3020 {
3021 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3022 // Determine if the inserted text matches the opening or closing
3023 // bracket of any of this language's bracket pairs.
3024 let mut bracket_pair = None;
3025 let mut is_bracket_pair_start = false;
3026 let mut is_bracket_pair_end = false;
3027 if !text.is_empty() {
3028 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3029 // and they are removing the character that triggered IME popup.
3030 for (pair, enabled) in scope.brackets() {
3031 if !pair.close && !pair.surround {
3032 continue;
3033 }
3034
3035 if enabled && pair.start.ends_with(text.as_ref()) {
3036 let prefix_len = pair.start.len() - text.len();
3037 let preceding_text_matches_prefix = prefix_len == 0
3038 || (selection.start.column >= (prefix_len as u32)
3039 && snapshot.contains_str_at(
3040 Point::new(
3041 selection.start.row,
3042 selection.start.column - (prefix_len as u32),
3043 ),
3044 &pair.start[..prefix_len],
3045 ));
3046 if preceding_text_matches_prefix {
3047 bracket_pair = Some(pair.clone());
3048 is_bracket_pair_start = true;
3049 break;
3050 }
3051 }
3052 if pair.end.as_str() == text.as_ref() {
3053 bracket_pair = Some(pair.clone());
3054 is_bracket_pair_end = true;
3055 break;
3056 }
3057 }
3058 }
3059
3060 if let Some(bracket_pair) = bracket_pair {
3061 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3062 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3063 let auto_surround =
3064 self.use_auto_surround && snapshot_settings.use_auto_surround;
3065 if selection.is_empty() {
3066 if is_bracket_pair_start {
3067 // If the inserted text is a suffix of an opening bracket and the
3068 // selection is preceded by the rest of the opening bracket, then
3069 // insert the closing bracket.
3070 let following_text_allows_autoclose = snapshot
3071 .chars_at(selection.start)
3072 .next()
3073 .map_or(true, |c| scope.should_autoclose_before(c));
3074
3075 let preceding_text_allows_autoclose = selection.start.column == 0
3076 || snapshot.reversed_chars_at(selection.start).next().map_or(
3077 true,
3078 |c| {
3079 bracket_pair.start != bracket_pair.end
3080 || !snapshot
3081 .char_classifier_at(selection.start)
3082 .is_word(c)
3083 },
3084 );
3085
3086 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3087 && bracket_pair.start.len() == 1
3088 {
3089 let target = bracket_pair.start.chars().next().unwrap();
3090 let current_line_count = snapshot
3091 .reversed_chars_at(selection.start)
3092 .take_while(|&c| c != '\n')
3093 .filter(|&c| c == target)
3094 .count();
3095 current_line_count % 2 == 1
3096 } else {
3097 false
3098 };
3099
3100 if autoclose
3101 && bracket_pair.close
3102 && following_text_allows_autoclose
3103 && preceding_text_allows_autoclose
3104 && !is_closing_quote
3105 {
3106 let anchor = snapshot.anchor_before(selection.end);
3107 new_selections.push((selection.map(|_| anchor), text.len()));
3108 new_autoclose_regions.push((
3109 anchor,
3110 text.len(),
3111 selection.id,
3112 bracket_pair.clone(),
3113 ));
3114 edits.push((
3115 selection.range(),
3116 format!("{}{}", text, bracket_pair.end).into(),
3117 ));
3118 bracket_inserted = true;
3119 continue;
3120 }
3121 }
3122
3123 if let Some(region) = autoclose_region {
3124 // If the selection is followed by an auto-inserted closing bracket,
3125 // then don't insert that closing bracket again; just move the selection
3126 // past the closing bracket.
3127 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3128 && text.as_ref() == region.pair.end.as_str();
3129 if should_skip {
3130 let anchor = snapshot.anchor_after(selection.end);
3131 new_selections
3132 .push((selection.map(|_| anchor), region.pair.end.len()));
3133 continue;
3134 }
3135 }
3136
3137 let always_treat_brackets_as_autoclosed = snapshot
3138 .language_settings_at(selection.start, cx)
3139 .always_treat_brackets_as_autoclosed;
3140 if always_treat_brackets_as_autoclosed
3141 && is_bracket_pair_end
3142 && snapshot.contains_str_at(selection.end, text.as_ref())
3143 {
3144 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3145 // and the inserted text is a closing bracket and the selection is followed
3146 // by the closing bracket then move the selection past the closing bracket.
3147 let anchor = snapshot.anchor_after(selection.end);
3148 new_selections.push((selection.map(|_| anchor), text.len()));
3149 continue;
3150 }
3151 }
3152 // If an opening bracket is 1 character long and is typed while
3153 // text is selected, then surround that text with the bracket pair.
3154 else if auto_surround
3155 && bracket_pair.surround
3156 && is_bracket_pair_start
3157 && bracket_pair.start.chars().count() == 1
3158 {
3159 edits.push((selection.start..selection.start, text.clone()));
3160 edits.push((
3161 selection.end..selection.end,
3162 bracket_pair.end.as_str().into(),
3163 ));
3164 bracket_inserted = true;
3165 new_selections.push((
3166 Selection {
3167 id: selection.id,
3168 start: snapshot.anchor_after(selection.start),
3169 end: snapshot.anchor_before(selection.end),
3170 reversed: selection.reversed,
3171 goal: selection.goal,
3172 },
3173 0,
3174 ));
3175 continue;
3176 }
3177 }
3178 }
3179
3180 if self.auto_replace_emoji_shortcode
3181 && selection.is_empty()
3182 && text.as_ref().ends_with(':')
3183 {
3184 if let Some(possible_emoji_short_code) =
3185 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3186 {
3187 if !possible_emoji_short_code.is_empty() {
3188 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3189 let emoji_shortcode_start = Point::new(
3190 selection.start.row,
3191 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3192 );
3193
3194 // Remove shortcode from buffer
3195 edits.push((
3196 emoji_shortcode_start..selection.start,
3197 "".to_string().into(),
3198 ));
3199 new_selections.push((
3200 Selection {
3201 id: selection.id,
3202 start: snapshot.anchor_after(emoji_shortcode_start),
3203 end: snapshot.anchor_before(selection.start),
3204 reversed: selection.reversed,
3205 goal: selection.goal,
3206 },
3207 0,
3208 ));
3209
3210 // Insert emoji
3211 let selection_start_anchor = snapshot.anchor_after(selection.start);
3212 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3213 edits.push((selection.start..selection.end, emoji.to_string().into()));
3214
3215 continue;
3216 }
3217 }
3218 }
3219 }
3220
3221 // If not handling any auto-close operation, then just replace the selected
3222 // text with the given input and move the selection to the end of the
3223 // newly inserted text.
3224 let anchor = snapshot.anchor_after(selection.end);
3225 if !self.linked_edit_ranges.is_empty() {
3226 let start_anchor = snapshot.anchor_before(selection.start);
3227
3228 let is_word_char = text.chars().next().map_or(true, |char| {
3229 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3230 classifier.is_word(char)
3231 });
3232
3233 if is_word_char {
3234 if let Some(ranges) = self
3235 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3236 {
3237 for (buffer, edits) in ranges {
3238 linked_edits
3239 .entry(buffer.clone())
3240 .or_default()
3241 .extend(edits.into_iter().map(|range| (range, text.clone())));
3242 }
3243 }
3244 }
3245 }
3246
3247 new_selections.push((selection.map(|_| anchor), 0));
3248 edits.push((selection.start..selection.end, text.clone()));
3249 }
3250
3251 drop(snapshot);
3252
3253 self.transact(window, cx, |this, window, cx| {
3254 let initial_buffer_versions =
3255 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3256
3257 this.buffer.update(cx, |buffer, cx| {
3258 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3259 });
3260 for (buffer, edits) in linked_edits {
3261 buffer.update(cx, |buffer, cx| {
3262 let snapshot = buffer.snapshot();
3263 let edits = edits
3264 .into_iter()
3265 .map(|(range, text)| {
3266 use text::ToPoint as TP;
3267 let end_point = TP::to_point(&range.end, &snapshot);
3268 let start_point = TP::to_point(&range.start, &snapshot);
3269 (start_point..end_point, text)
3270 })
3271 .sorted_by_key(|(range, _)| range.start);
3272 buffer.edit(edits, None, cx);
3273 })
3274 }
3275 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3276 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3277 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3278 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3279 .zip(new_selection_deltas)
3280 .map(|(selection, delta)| Selection {
3281 id: selection.id,
3282 start: selection.start + delta,
3283 end: selection.end + delta,
3284 reversed: selection.reversed,
3285 goal: SelectionGoal::None,
3286 })
3287 .collect::<Vec<_>>();
3288
3289 let mut i = 0;
3290 for (position, delta, selection_id, pair) in new_autoclose_regions {
3291 let position = position.to_offset(&map.buffer_snapshot) + delta;
3292 let start = map.buffer_snapshot.anchor_before(position);
3293 let end = map.buffer_snapshot.anchor_after(position);
3294 while let Some(existing_state) = this.autoclose_regions.get(i) {
3295 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3296 Ordering::Less => i += 1,
3297 Ordering::Greater => break,
3298 Ordering::Equal => {
3299 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3300 Ordering::Less => i += 1,
3301 Ordering::Equal => break,
3302 Ordering::Greater => break,
3303 }
3304 }
3305 }
3306 }
3307 this.autoclose_regions.insert(
3308 i,
3309 AutocloseRegion {
3310 selection_id,
3311 range: start..end,
3312 pair,
3313 },
3314 );
3315 }
3316
3317 let had_active_inline_completion = this.has_active_inline_completion();
3318 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3319 s.select(new_selections)
3320 });
3321
3322 if !bracket_inserted {
3323 if let Some(on_type_format_task) =
3324 this.trigger_on_type_formatting(text.to_string(), window, cx)
3325 {
3326 on_type_format_task.detach_and_log_err(cx);
3327 }
3328 }
3329
3330 let editor_settings = EditorSettings::get_global(cx);
3331 if bracket_inserted
3332 && (editor_settings.auto_signature_help
3333 || editor_settings.show_signature_help_after_edits)
3334 {
3335 this.show_signature_help(&ShowSignatureHelp, window, cx);
3336 }
3337
3338 let trigger_in_words =
3339 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3340 if this.hard_wrap.is_some() {
3341 let latest: Range<Point> = this.selections.newest(cx).range();
3342 if latest.is_empty()
3343 && this
3344 .buffer()
3345 .read(cx)
3346 .snapshot(cx)
3347 .line_len(MultiBufferRow(latest.start.row))
3348 == latest.start.column
3349 {
3350 this.rewrap_impl(
3351 RewrapOptions {
3352 override_language_settings: true,
3353 preserve_existing_whitespace: true,
3354 },
3355 cx,
3356 )
3357 }
3358 }
3359 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3360 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3361 this.refresh_inline_completion(true, false, window, cx);
3362 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3363 });
3364 }
3365
3366 fn find_possible_emoji_shortcode_at_position(
3367 snapshot: &MultiBufferSnapshot,
3368 position: Point,
3369 ) -> Option<String> {
3370 let mut chars = Vec::new();
3371 let mut found_colon = false;
3372 for char in snapshot.reversed_chars_at(position).take(100) {
3373 // Found a possible emoji shortcode in the middle of the buffer
3374 if found_colon {
3375 if char.is_whitespace() {
3376 chars.reverse();
3377 return Some(chars.iter().collect());
3378 }
3379 // If the previous character is not a whitespace, we are in the middle of a word
3380 // and we only want to complete the shortcode if the word is made up of other emojis
3381 let mut containing_word = String::new();
3382 for ch in snapshot
3383 .reversed_chars_at(position)
3384 .skip(chars.len() + 1)
3385 .take(100)
3386 {
3387 if ch.is_whitespace() {
3388 break;
3389 }
3390 containing_word.push(ch);
3391 }
3392 let containing_word = containing_word.chars().rev().collect::<String>();
3393 if util::word_consists_of_emojis(containing_word.as_str()) {
3394 chars.reverse();
3395 return Some(chars.iter().collect());
3396 }
3397 }
3398
3399 if char.is_whitespace() || !char.is_ascii() {
3400 return None;
3401 }
3402 if char == ':' {
3403 found_colon = true;
3404 } else {
3405 chars.push(char);
3406 }
3407 }
3408 // Found a possible emoji shortcode at the beginning of the buffer
3409 chars.reverse();
3410 Some(chars.iter().collect())
3411 }
3412
3413 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3414 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3415 self.transact(window, cx, |this, window, cx| {
3416 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3417 let selections = this.selections.all::<usize>(cx);
3418 let multi_buffer = this.buffer.read(cx);
3419 let buffer = multi_buffer.snapshot(cx);
3420 selections
3421 .iter()
3422 .map(|selection| {
3423 let start_point = selection.start.to_point(&buffer);
3424 let mut indent =
3425 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3426 indent.len = cmp::min(indent.len, start_point.column);
3427 let start = selection.start;
3428 let end = selection.end;
3429 let selection_is_empty = start == end;
3430 let language_scope = buffer.language_scope_at(start);
3431 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3432 &language_scope
3433 {
3434 let insert_extra_newline =
3435 insert_extra_newline_brackets(&buffer, start..end, language)
3436 || insert_extra_newline_tree_sitter(&buffer, start..end);
3437
3438 // Comment extension on newline is allowed only for cursor selections
3439 let comment_delimiter = maybe!({
3440 if !selection_is_empty {
3441 return None;
3442 }
3443
3444 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3445 return None;
3446 }
3447
3448 let delimiters = language.line_comment_prefixes();
3449 let max_len_of_delimiter =
3450 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3451 let (snapshot, range) =
3452 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3453
3454 let mut index_of_first_non_whitespace = 0;
3455 let comment_candidate = snapshot
3456 .chars_for_range(range)
3457 .skip_while(|c| {
3458 let should_skip = c.is_whitespace();
3459 if should_skip {
3460 index_of_first_non_whitespace += 1;
3461 }
3462 should_skip
3463 })
3464 .take(max_len_of_delimiter)
3465 .collect::<String>();
3466 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3467 comment_candidate.starts_with(comment_prefix.as_ref())
3468 })?;
3469 let cursor_is_placed_after_comment_marker =
3470 index_of_first_non_whitespace + comment_prefix.len()
3471 <= start_point.column as usize;
3472 if cursor_is_placed_after_comment_marker {
3473 Some(comment_prefix.clone())
3474 } else {
3475 None
3476 }
3477 });
3478 (comment_delimiter, insert_extra_newline)
3479 } else {
3480 (None, false)
3481 };
3482
3483 let capacity_for_delimiter = comment_delimiter
3484 .as_deref()
3485 .map(str::len)
3486 .unwrap_or_default();
3487 let mut new_text =
3488 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3489 new_text.push('\n');
3490 new_text.extend(indent.chars());
3491 if let Some(delimiter) = &comment_delimiter {
3492 new_text.push_str(delimiter);
3493 }
3494 if insert_extra_newline {
3495 new_text = new_text.repeat(2);
3496 }
3497
3498 let anchor = buffer.anchor_after(end);
3499 let new_selection = selection.map(|_| anchor);
3500 (
3501 (start..end, new_text),
3502 (insert_extra_newline, new_selection),
3503 )
3504 })
3505 .unzip()
3506 };
3507
3508 this.edit_with_autoindent(edits, cx);
3509 let buffer = this.buffer.read(cx).snapshot(cx);
3510 let new_selections = selection_fixup_info
3511 .into_iter()
3512 .map(|(extra_newline_inserted, new_selection)| {
3513 let mut cursor = new_selection.end.to_point(&buffer);
3514 if extra_newline_inserted {
3515 cursor.row -= 1;
3516 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3517 }
3518 new_selection.map(|_| cursor)
3519 })
3520 .collect();
3521
3522 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3523 s.select(new_selections)
3524 });
3525 this.refresh_inline_completion(true, false, window, cx);
3526 });
3527 }
3528
3529 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3530 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3531
3532 let buffer = self.buffer.read(cx);
3533 let snapshot = buffer.snapshot(cx);
3534
3535 let mut edits = Vec::new();
3536 let mut rows = Vec::new();
3537
3538 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3539 let cursor = selection.head();
3540 let row = cursor.row;
3541
3542 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3543
3544 let newline = "\n".to_string();
3545 edits.push((start_of_line..start_of_line, newline));
3546
3547 rows.push(row + rows_inserted as u32);
3548 }
3549
3550 self.transact(window, cx, |editor, window, cx| {
3551 editor.edit(edits, cx);
3552
3553 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3554 let mut index = 0;
3555 s.move_cursors_with(|map, _, _| {
3556 let row = rows[index];
3557 index += 1;
3558
3559 let point = Point::new(row, 0);
3560 let boundary = map.next_line_boundary(point).1;
3561 let clipped = map.clip_point(boundary, Bias::Left);
3562
3563 (clipped, SelectionGoal::None)
3564 });
3565 });
3566
3567 let mut indent_edits = Vec::new();
3568 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3569 for row in rows {
3570 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3571 for (row, indent) in indents {
3572 if indent.len == 0 {
3573 continue;
3574 }
3575
3576 let text = match indent.kind {
3577 IndentKind::Space => " ".repeat(indent.len as usize),
3578 IndentKind::Tab => "\t".repeat(indent.len as usize),
3579 };
3580 let point = Point::new(row.0, 0);
3581 indent_edits.push((point..point, text));
3582 }
3583 }
3584 editor.edit(indent_edits, cx);
3585 });
3586 }
3587
3588 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3589 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
3590
3591 let buffer = self.buffer.read(cx);
3592 let snapshot = buffer.snapshot(cx);
3593
3594 let mut edits = Vec::new();
3595 let mut rows = Vec::new();
3596 let mut rows_inserted = 0;
3597
3598 for selection in self.selections.all_adjusted(cx) {
3599 let cursor = selection.head();
3600 let row = cursor.row;
3601
3602 let point = Point::new(row + 1, 0);
3603 let start_of_line = snapshot.clip_point(point, Bias::Left);
3604
3605 let newline = "\n".to_string();
3606 edits.push((start_of_line..start_of_line, newline));
3607
3608 rows_inserted += 1;
3609 rows.push(row + rows_inserted);
3610 }
3611
3612 self.transact(window, cx, |editor, window, cx| {
3613 editor.edit(edits, cx);
3614
3615 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3616 let mut index = 0;
3617 s.move_cursors_with(|map, _, _| {
3618 let row = rows[index];
3619 index += 1;
3620
3621 let point = Point::new(row, 0);
3622 let boundary = map.next_line_boundary(point).1;
3623 let clipped = map.clip_point(boundary, Bias::Left);
3624
3625 (clipped, SelectionGoal::None)
3626 });
3627 });
3628
3629 let mut indent_edits = Vec::new();
3630 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3631 for row in rows {
3632 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3633 for (row, indent) in indents {
3634 if indent.len == 0 {
3635 continue;
3636 }
3637
3638 let text = match indent.kind {
3639 IndentKind::Space => " ".repeat(indent.len as usize),
3640 IndentKind::Tab => "\t".repeat(indent.len as usize),
3641 };
3642 let point = Point::new(row.0, 0);
3643 indent_edits.push((point..point, text));
3644 }
3645 }
3646 editor.edit(indent_edits, cx);
3647 });
3648 }
3649
3650 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3651 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3652 original_indent_columns: Vec::new(),
3653 });
3654 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3655 }
3656
3657 fn insert_with_autoindent_mode(
3658 &mut self,
3659 text: &str,
3660 autoindent_mode: Option<AutoindentMode>,
3661 window: &mut Window,
3662 cx: &mut Context<Self>,
3663 ) {
3664 if self.read_only(cx) {
3665 return;
3666 }
3667
3668 let text: Arc<str> = text.into();
3669 self.transact(window, cx, |this, window, cx| {
3670 let old_selections = this.selections.all_adjusted(cx);
3671 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3672 let anchors = {
3673 let snapshot = buffer.read(cx);
3674 old_selections
3675 .iter()
3676 .map(|s| {
3677 let anchor = snapshot.anchor_after(s.head());
3678 s.map(|_| anchor)
3679 })
3680 .collect::<Vec<_>>()
3681 };
3682 buffer.edit(
3683 old_selections
3684 .iter()
3685 .map(|s| (s.start..s.end, text.clone())),
3686 autoindent_mode,
3687 cx,
3688 );
3689 anchors
3690 });
3691
3692 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3693 s.select_anchors(selection_anchors);
3694 });
3695
3696 cx.notify();
3697 });
3698 }
3699
3700 fn trigger_completion_on_input(
3701 &mut self,
3702 text: &str,
3703 trigger_in_words: bool,
3704 window: &mut Window,
3705 cx: &mut Context<Self>,
3706 ) {
3707 let ignore_completion_provider = self
3708 .context_menu
3709 .borrow()
3710 .as_ref()
3711 .map(|menu| match menu {
3712 CodeContextMenu::Completions(completions_menu) => {
3713 completions_menu.ignore_completion_provider
3714 }
3715 CodeContextMenu::CodeActions(_) => false,
3716 })
3717 .unwrap_or(false);
3718
3719 if ignore_completion_provider {
3720 self.show_word_completions(&ShowWordCompletions, window, cx);
3721 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3722 self.show_completions(
3723 &ShowCompletions {
3724 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3725 },
3726 window,
3727 cx,
3728 );
3729 } else {
3730 self.hide_context_menu(window, cx);
3731 }
3732 }
3733
3734 fn is_completion_trigger(
3735 &self,
3736 text: &str,
3737 trigger_in_words: bool,
3738 cx: &mut Context<Self>,
3739 ) -> bool {
3740 let position = self.selections.newest_anchor().head();
3741 let multibuffer = self.buffer.read(cx);
3742 let Some(buffer) = position
3743 .buffer_id
3744 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3745 else {
3746 return false;
3747 };
3748
3749 if let Some(completion_provider) = &self.completion_provider {
3750 completion_provider.is_completion_trigger(
3751 &buffer,
3752 position.text_anchor,
3753 text,
3754 trigger_in_words,
3755 cx,
3756 )
3757 } else {
3758 false
3759 }
3760 }
3761
3762 /// If any empty selections is touching the start of its innermost containing autoclose
3763 /// region, expand it to select the brackets.
3764 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3765 let selections = self.selections.all::<usize>(cx);
3766 let buffer = self.buffer.read(cx).read(cx);
3767 let new_selections = self
3768 .selections_with_autoclose_regions(selections, &buffer)
3769 .map(|(mut selection, region)| {
3770 if !selection.is_empty() {
3771 return selection;
3772 }
3773
3774 if let Some(region) = region {
3775 let mut range = region.range.to_offset(&buffer);
3776 if selection.start == range.start && range.start >= region.pair.start.len() {
3777 range.start -= region.pair.start.len();
3778 if buffer.contains_str_at(range.start, ®ion.pair.start)
3779 && buffer.contains_str_at(range.end, ®ion.pair.end)
3780 {
3781 range.end += region.pair.end.len();
3782 selection.start = range.start;
3783 selection.end = range.end;
3784
3785 return selection;
3786 }
3787 }
3788 }
3789
3790 let always_treat_brackets_as_autoclosed = buffer
3791 .language_settings_at(selection.start, cx)
3792 .always_treat_brackets_as_autoclosed;
3793
3794 if !always_treat_brackets_as_autoclosed {
3795 return selection;
3796 }
3797
3798 if let Some(scope) = buffer.language_scope_at(selection.start) {
3799 for (pair, enabled) in scope.brackets() {
3800 if !enabled || !pair.close {
3801 continue;
3802 }
3803
3804 if buffer.contains_str_at(selection.start, &pair.end) {
3805 let pair_start_len = pair.start.len();
3806 if buffer.contains_str_at(
3807 selection.start.saturating_sub(pair_start_len),
3808 &pair.start,
3809 ) {
3810 selection.start -= pair_start_len;
3811 selection.end += pair.end.len();
3812
3813 return selection;
3814 }
3815 }
3816 }
3817 }
3818
3819 selection
3820 })
3821 .collect();
3822
3823 drop(buffer);
3824 self.change_selections(None, window, cx, |selections| {
3825 selections.select(new_selections)
3826 });
3827 }
3828
3829 /// Iterate the given selections, and for each one, find the smallest surrounding
3830 /// autoclose region. This uses the ordering of the selections and the autoclose
3831 /// regions to avoid repeated comparisons.
3832 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3833 &'a self,
3834 selections: impl IntoIterator<Item = Selection<D>>,
3835 buffer: &'a MultiBufferSnapshot,
3836 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3837 let mut i = 0;
3838 let mut regions = self.autoclose_regions.as_slice();
3839 selections.into_iter().map(move |selection| {
3840 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3841
3842 let mut enclosing = None;
3843 while let Some(pair_state) = regions.get(i) {
3844 if pair_state.range.end.to_offset(buffer) < range.start {
3845 regions = ®ions[i + 1..];
3846 i = 0;
3847 } else if pair_state.range.start.to_offset(buffer) > range.end {
3848 break;
3849 } else {
3850 if pair_state.selection_id == selection.id {
3851 enclosing = Some(pair_state);
3852 }
3853 i += 1;
3854 }
3855 }
3856
3857 (selection, enclosing)
3858 })
3859 }
3860
3861 /// Remove any autoclose regions that no longer contain their selection.
3862 fn invalidate_autoclose_regions(
3863 &mut self,
3864 mut selections: &[Selection<Anchor>],
3865 buffer: &MultiBufferSnapshot,
3866 ) {
3867 self.autoclose_regions.retain(|state| {
3868 let mut i = 0;
3869 while let Some(selection) = selections.get(i) {
3870 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3871 selections = &selections[1..];
3872 continue;
3873 }
3874 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3875 break;
3876 }
3877 if selection.id == state.selection_id {
3878 return true;
3879 } else {
3880 i += 1;
3881 }
3882 }
3883 false
3884 });
3885 }
3886
3887 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3888 let offset = position.to_offset(buffer);
3889 let (word_range, kind) = buffer.surrounding_word(offset, true);
3890 if offset > word_range.start && kind == Some(CharKind::Word) {
3891 Some(
3892 buffer
3893 .text_for_range(word_range.start..offset)
3894 .collect::<String>(),
3895 )
3896 } else {
3897 None
3898 }
3899 }
3900
3901 pub fn toggle_inlay_hints(
3902 &mut self,
3903 _: &ToggleInlayHints,
3904 _: &mut Window,
3905 cx: &mut Context<Self>,
3906 ) {
3907 self.refresh_inlay_hints(
3908 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3909 cx,
3910 );
3911 }
3912
3913 pub fn inlay_hints_enabled(&self) -> bool {
3914 self.inlay_hint_cache.enabled
3915 }
3916
3917 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3918 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3919 return;
3920 }
3921
3922 let reason_description = reason.description();
3923 let ignore_debounce = matches!(
3924 reason,
3925 InlayHintRefreshReason::SettingsChange(_)
3926 | InlayHintRefreshReason::Toggle(_)
3927 | InlayHintRefreshReason::ExcerptsRemoved(_)
3928 | InlayHintRefreshReason::ModifiersChanged(_)
3929 );
3930 let (invalidate_cache, required_languages) = match reason {
3931 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3932 match self.inlay_hint_cache.modifiers_override(enabled) {
3933 Some(enabled) => {
3934 if enabled {
3935 (InvalidationStrategy::RefreshRequested, None)
3936 } else {
3937 self.splice_inlays(
3938 &self
3939 .visible_inlay_hints(cx)
3940 .iter()
3941 .map(|inlay| inlay.id)
3942 .collect::<Vec<InlayId>>(),
3943 Vec::new(),
3944 cx,
3945 );
3946 return;
3947 }
3948 }
3949 None => return,
3950 }
3951 }
3952 InlayHintRefreshReason::Toggle(enabled) => {
3953 if self.inlay_hint_cache.toggle(enabled) {
3954 if enabled {
3955 (InvalidationStrategy::RefreshRequested, None)
3956 } else {
3957 self.splice_inlays(
3958 &self
3959 .visible_inlay_hints(cx)
3960 .iter()
3961 .map(|inlay| inlay.id)
3962 .collect::<Vec<InlayId>>(),
3963 Vec::new(),
3964 cx,
3965 );
3966 return;
3967 }
3968 } else {
3969 return;
3970 }
3971 }
3972 InlayHintRefreshReason::SettingsChange(new_settings) => {
3973 match self.inlay_hint_cache.update_settings(
3974 &self.buffer,
3975 new_settings,
3976 self.visible_inlay_hints(cx),
3977 cx,
3978 ) {
3979 ControlFlow::Break(Some(InlaySplice {
3980 to_remove,
3981 to_insert,
3982 })) => {
3983 self.splice_inlays(&to_remove, to_insert, cx);
3984 return;
3985 }
3986 ControlFlow::Break(None) => return,
3987 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3988 }
3989 }
3990 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3991 if let Some(InlaySplice {
3992 to_remove,
3993 to_insert,
3994 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3995 {
3996 self.splice_inlays(&to_remove, to_insert, cx);
3997 }
3998 return;
3999 }
4000 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4001 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4002 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4003 }
4004 InlayHintRefreshReason::RefreshRequested => {
4005 (InvalidationStrategy::RefreshRequested, None)
4006 }
4007 };
4008
4009 if let Some(InlaySplice {
4010 to_remove,
4011 to_insert,
4012 }) = self.inlay_hint_cache.spawn_hint_refresh(
4013 reason_description,
4014 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4015 invalidate_cache,
4016 ignore_debounce,
4017 cx,
4018 ) {
4019 self.splice_inlays(&to_remove, to_insert, cx);
4020 }
4021 }
4022
4023 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
4024 self.display_map
4025 .read(cx)
4026 .current_inlays()
4027 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4028 .cloned()
4029 .collect()
4030 }
4031
4032 pub fn excerpts_for_inlay_hints_query(
4033 &self,
4034 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4035 cx: &mut Context<Editor>,
4036 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
4037 let Some(project) = self.project.as_ref() else {
4038 return HashMap::default();
4039 };
4040 let project = project.read(cx);
4041 let multi_buffer = self.buffer().read(cx);
4042 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4043 let multi_buffer_visible_start = self
4044 .scroll_manager
4045 .anchor()
4046 .anchor
4047 .to_point(&multi_buffer_snapshot);
4048 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4049 multi_buffer_visible_start
4050 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4051 Bias::Left,
4052 );
4053 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4054 multi_buffer_snapshot
4055 .range_to_buffer_ranges(multi_buffer_visible_range)
4056 .into_iter()
4057 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4058 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
4059 let buffer_file = project::File::from_dyn(buffer.file())?;
4060 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4061 let worktree_entry = buffer_worktree
4062 .read(cx)
4063 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4064 if worktree_entry.is_ignored {
4065 return None;
4066 }
4067
4068 let language = buffer.language()?;
4069 if let Some(restrict_to_languages) = restrict_to_languages {
4070 if !restrict_to_languages.contains(language) {
4071 return None;
4072 }
4073 }
4074 Some((
4075 excerpt_id,
4076 (
4077 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4078 buffer.version().clone(),
4079 excerpt_visible_range,
4080 ),
4081 ))
4082 })
4083 .collect()
4084 }
4085
4086 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4087 TextLayoutDetails {
4088 text_system: window.text_system().clone(),
4089 editor_style: self.style.clone().unwrap(),
4090 rem_size: window.rem_size(),
4091 scroll_anchor: self.scroll_manager.anchor(),
4092 visible_rows: self.visible_line_count(),
4093 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4094 }
4095 }
4096
4097 pub fn splice_inlays(
4098 &self,
4099 to_remove: &[InlayId],
4100 to_insert: Vec<Inlay>,
4101 cx: &mut Context<Self>,
4102 ) {
4103 self.display_map.update(cx, |display_map, cx| {
4104 display_map.splice_inlays(to_remove, to_insert, cx)
4105 });
4106 cx.notify();
4107 }
4108
4109 fn trigger_on_type_formatting(
4110 &self,
4111 input: String,
4112 window: &mut Window,
4113 cx: &mut Context<Self>,
4114 ) -> Option<Task<Result<()>>> {
4115 if input.len() != 1 {
4116 return None;
4117 }
4118
4119 let project = self.project.as_ref()?;
4120 let position = self.selections.newest_anchor().head();
4121 let (buffer, buffer_position) = self
4122 .buffer
4123 .read(cx)
4124 .text_anchor_for_position(position, cx)?;
4125
4126 let settings = language_settings::language_settings(
4127 buffer
4128 .read(cx)
4129 .language_at(buffer_position)
4130 .map(|l| l.name()),
4131 buffer.read(cx).file(),
4132 cx,
4133 );
4134 if !settings.use_on_type_format {
4135 return None;
4136 }
4137
4138 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4139 // hence we do LSP request & edit on host side only — add formats to host's history.
4140 let push_to_lsp_host_history = true;
4141 // If this is not the host, append its history with new edits.
4142 let push_to_client_history = project.read(cx).is_via_collab();
4143
4144 let on_type_formatting = project.update(cx, |project, cx| {
4145 project.on_type_format(
4146 buffer.clone(),
4147 buffer_position,
4148 input,
4149 push_to_lsp_host_history,
4150 cx,
4151 )
4152 });
4153 Some(cx.spawn_in(window, async move |editor, cx| {
4154 if let Some(transaction) = on_type_formatting.await? {
4155 if push_to_client_history {
4156 buffer
4157 .update(cx, |buffer, _| {
4158 buffer.push_transaction(transaction, Instant::now());
4159 })
4160 .ok();
4161 }
4162 editor.update(cx, |editor, cx| {
4163 editor.refresh_document_highlights(cx);
4164 })?;
4165 }
4166 Ok(())
4167 }))
4168 }
4169
4170 pub fn show_word_completions(
4171 &mut self,
4172 _: &ShowWordCompletions,
4173 window: &mut Window,
4174 cx: &mut Context<Self>,
4175 ) {
4176 self.open_completions_menu(true, None, window, cx);
4177 }
4178
4179 pub fn show_completions(
4180 &mut self,
4181 options: &ShowCompletions,
4182 window: &mut Window,
4183 cx: &mut Context<Self>,
4184 ) {
4185 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4186 }
4187
4188 fn open_completions_menu(
4189 &mut self,
4190 ignore_completion_provider: bool,
4191 trigger: Option<&str>,
4192 window: &mut Window,
4193 cx: &mut Context<Self>,
4194 ) {
4195 if self.pending_rename.is_some() {
4196 return;
4197 }
4198 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4199 return;
4200 }
4201
4202 let position = self.selections.newest_anchor().head();
4203 if position.diff_base_anchor.is_some() {
4204 return;
4205 }
4206 let (buffer, buffer_position) =
4207 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4208 output
4209 } else {
4210 return;
4211 };
4212 let buffer_snapshot = buffer.read(cx).snapshot();
4213 let show_completion_documentation = buffer_snapshot
4214 .settings_at(buffer_position, cx)
4215 .show_completion_documentation;
4216
4217 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4218
4219 let trigger_kind = match trigger {
4220 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4221 CompletionTriggerKind::TRIGGER_CHARACTER
4222 }
4223 _ => CompletionTriggerKind::INVOKED,
4224 };
4225 let completion_context = CompletionContext {
4226 trigger_character: trigger.and_then(|trigger| {
4227 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4228 Some(String::from(trigger))
4229 } else {
4230 None
4231 }
4232 }),
4233 trigger_kind,
4234 };
4235
4236 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4237 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4238 let word_to_exclude = buffer_snapshot
4239 .text_for_range(old_range.clone())
4240 .collect::<String>();
4241 (
4242 buffer_snapshot.anchor_before(old_range.start)
4243 ..buffer_snapshot.anchor_after(old_range.end),
4244 Some(word_to_exclude),
4245 )
4246 } else {
4247 (buffer_position..buffer_position, None)
4248 };
4249
4250 let completion_settings = language_settings(
4251 buffer_snapshot
4252 .language_at(buffer_position)
4253 .map(|language| language.name()),
4254 buffer_snapshot.file(),
4255 cx,
4256 )
4257 .completions;
4258
4259 // The document can be large, so stay in reasonable bounds when searching for words,
4260 // otherwise completion pop-up might be slow to appear.
4261 const WORD_LOOKUP_ROWS: u32 = 5_000;
4262 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4263 let min_word_search = buffer_snapshot.clip_point(
4264 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4265 Bias::Left,
4266 );
4267 let max_word_search = buffer_snapshot.clip_point(
4268 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4269 Bias::Right,
4270 );
4271 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4272 ..buffer_snapshot.point_to_offset(max_word_search);
4273
4274 let provider = self
4275 .completion_provider
4276 .as_ref()
4277 .filter(|_| !ignore_completion_provider);
4278 let skip_digits = query
4279 .as_ref()
4280 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4281
4282 let (mut words, provided_completions) = match provider {
4283 Some(provider) => {
4284 let completions = provider.completions(
4285 position.excerpt_id,
4286 &buffer,
4287 buffer_position,
4288 completion_context,
4289 window,
4290 cx,
4291 );
4292
4293 let words = match completion_settings.words {
4294 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4295 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4296 .background_spawn(async move {
4297 buffer_snapshot.words_in_range(WordsQuery {
4298 fuzzy_contents: None,
4299 range: word_search_range,
4300 skip_digits,
4301 })
4302 }),
4303 };
4304
4305 (words, completions)
4306 }
4307 None => (
4308 cx.background_spawn(async move {
4309 buffer_snapshot.words_in_range(WordsQuery {
4310 fuzzy_contents: None,
4311 range: word_search_range,
4312 skip_digits,
4313 })
4314 }),
4315 Task::ready(Ok(None)),
4316 ),
4317 };
4318
4319 let sort_completions = provider
4320 .as_ref()
4321 .map_or(true, |provider| provider.sort_completions());
4322
4323 let id = post_inc(&mut self.next_completion_id);
4324 let task = cx.spawn_in(window, async move |editor, cx| {
4325 async move {
4326 editor.update(cx, |this, _| {
4327 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4328 })?;
4329
4330 let mut completions = Vec::new();
4331 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4332 completions.extend(provided_completions);
4333 if completion_settings.words == WordsCompletionMode::Fallback {
4334 words = Task::ready(HashMap::default());
4335 }
4336 }
4337
4338 let mut words = words.await;
4339 if let Some(word_to_exclude) = &word_to_exclude {
4340 words.remove(word_to_exclude);
4341 }
4342 for lsp_completion in &completions {
4343 words.remove(&lsp_completion.new_text);
4344 }
4345 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4346 old_range: old_range.clone(),
4347 new_text: word.clone(),
4348 label: CodeLabel::plain(word, None),
4349 icon_path: None,
4350 documentation: None,
4351 source: CompletionSource::BufferWord {
4352 word_range,
4353 resolved: false,
4354 },
4355 confirm: None,
4356 }));
4357
4358 let menu = if completions.is_empty() {
4359 None
4360 } else {
4361 let mut menu = CompletionsMenu::new(
4362 id,
4363 sort_completions,
4364 show_completion_documentation,
4365 ignore_completion_provider,
4366 position,
4367 buffer.clone(),
4368 completions.into(),
4369 );
4370
4371 menu.filter(query.as_deref(), cx.background_executor().clone())
4372 .await;
4373
4374 menu.visible().then_some(menu)
4375 };
4376
4377 editor.update_in(cx, |editor, window, cx| {
4378 match editor.context_menu.borrow().as_ref() {
4379 None => {}
4380 Some(CodeContextMenu::Completions(prev_menu)) => {
4381 if prev_menu.id > id {
4382 return;
4383 }
4384 }
4385 _ => return,
4386 }
4387
4388 if editor.focus_handle.is_focused(window) && menu.is_some() {
4389 let mut menu = menu.unwrap();
4390 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4391
4392 *editor.context_menu.borrow_mut() =
4393 Some(CodeContextMenu::Completions(menu));
4394
4395 if editor.show_edit_predictions_in_menu() {
4396 editor.update_visible_inline_completion(window, cx);
4397 } else {
4398 editor.discard_inline_completion(false, cx);
4399 }
4400
4401 cx.notify();
4402 } else if editor.completion_tasks.len() <= 1 {
4403 // If there are no more completion tasks and the last menu was
4404 // empty, we should hide it.
4405 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4406 // If it was already hidden and we don't show inline
4407 // completions in the menu, we should also show the
4408 // inline-completion when available.
4409 if was_hidden && editor.show_edit_predictions_in_menu() {
4410 editor.update_visible_inline_completion(window, cx);
4411 }
4412 }
4413 })?;
4414
4415 anyhow::Ok(())
4416 }
4417 .log_err()
4418 .await
4419 });
4420
4421 self.completion_tasks.push((id, task));
4422 }
4423
4424 #[cfg(feature = "test-support")]
4425 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
4426 let menu = self.context_menu.borrow();
4427 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
4428 let completions = menu.completions.borrow();
4429 Some(completions.to_vec())
4430 } else {
4431 None
4432 }
4433 }
4434
4435 pub fn confirm_completion(
4436 &mut self,
4437 action: &ConfirmCompletion,
4438 window: &mut Window,
4439 cx: &mut Context<Self>,
4440 ) -> Option<Task<Result<()>>> {
4441 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4442 }
4443
4444 pub fn compose_completion(
4445 &mut self,
4446 action: &ComposeCompletion,
4447 window: &mut Window,
4448 cx: &mut Context<Self>,
4449 ) -> Option<Task<Result<()>>> {
4450 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4451 }
4452
4453 fn do_completion(
4454 &mut self,
4455 item_ix: Option<usize>,
4456 intent: CompletionIntent,
4457 window: &mut Window,
4458 cx: &mut Context<Editor>,
4459 ) -> Option<Task<Result<()>>> {
4460 use language::ToOffset as _;
4461
4462 let completions_menu =
4463 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4464 menu
4465 } else {
4466 return None;
4467 };
4468
4469 let candidate_id = {
4470 let entries = completions_menu.entries.borrow();
4471 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4472 if self.show_edit_predictions_in_menu() {
4473 self.discard_inline_completion(true, cx);
4474 }
4475 mat.candidate_id
4476 };
4477
4478 let buffer_handle = completions_menu.buffer;
4479 let completion = completions_menu
4480 .completions
4481 .borrow()
4482 .get(candidate_id)?
4483 .clone();
4484 cx.stop_propagation();
4485
4486 let snippet;
4487 let new_text;
4488 if completion.is_snippet() {
4489 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4490 new_text = snippet.as_ref().unwrap().text.clone();
4491 } else {
4492 snippet = None;
4493 new_text = completion.new_text.clone();
4494 };
4495 let selections = self.selections.all::<usize>(cx);
4496 let buffer = buffer_handle.read(cx);
4497 let old_range = completion.old_range.to_offset(buffer);
4498 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4499
4500 let newest_selection = self.selections.newest_anchor();
4501 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4502 return None;
4503 }
4504
4505 let lookbehind = newest_selection
4506 .start
4507 .text_anchor
4508 .to_offset(buffer)
4509 .saturating_sub(old_range.start);
4510 let lookahead = old_range
4511 .end
4512 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4513 let mut common_prefix_len = old_text
4514 .bytes()
4515 .zip(new_text.bytes())
4516 .take_while(|(a, b)| a == b)
4517 .count();
4518
4519 let snapshot = self.buffer.read(cx).snapshot(cx);
4520 let mut range_to_replace: Option<Range<isize>> = None;
4521 let mut ranges = Vec::new();
4522 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4523 for selection in &selections {
4524 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4525 let start = selection.start.saturating_sub(lookbehind);
4526 let end = selection.end + lookahead;
4527 if selection.id == newest_selection.id {
4528 range_to_replace = Some(
4529 ((start + common_prefix_len) as isize - selection.start as isize)
4530 ..(end as isize - selection.start as isize),
4531 );
4532 }
4533 ranges.push(start + common_prefix_len..end);
4534 } else {
4535 common_prefix_len = 0;
4536 ranges.clear();
4537 ranges.extend(selections.iter().map(|s| {
4538 if s.id == newest_selection.id {
4539 range_to_replace = Some(
4540 old_range.start.to_offset_utf16(&snapshot).0 as isize
4541 - selection.start as isize
4542 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4543 - selection.start as isize,
4544 );
4545 old_range.clone()
4546 } else {
4547 s.start..s.end
4548 }
4549 }));
4550 break;
4551 }
4552 if !self.linked_edit_ranges.is_empty() {
4553 let start_anchor = snapshot.anchor_before(selection.head());
4554 let end_anchor = snapshot.anchor_after(selection.tail());
4555 if let Some(ranges) = self
4556 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4557 {
4558 for (buffer, edits) in ranges {
4559 linked_edits.entry(buffer.clone()).or_default().extend(
4560 edits
4561 .into_iter()
4562 .map(|range| (range, new_text[common_prefix_len..].to_owned())),
4563 );
4564 }
4565 }
4566 }
4567 }
4568 let text = &new_text[common_prefix_len..];
4569
4570 cx.emit(EditorEvent::InputHandled {
4571 utf16_range_to_replace: range_to_replace,
4572 text: text.into(),
4573 });
4574
4575 self.transact(window, cx, |this, window, cx| {
4576 if let Some(mut snippet) = snippet {
4577 snippet.text = text.to_string();
4578 for tabstop in snippet
4579 .tabstops
4580 .iter_mut()
4581 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4582 {
4583 tabstop.start -= common_prefix_len as isize;
4584 tabstop.end -= common_prefix_len as isize;
4585 }
4586
4587 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4588 } else {
4589 this.buffer.update(cx, |buffer, cx| {
4590 let edits = ranges.iter().map(|range| (range.clone(), text));
4591 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4592 });
4593 }
4594 for (buffer, edits) in linked_edits {
4595 buffer.update(cx, |buffer, cx| {
4596 let snapshot = buffer.snapshot();
4597 let edits = edits
4598 .into_iter()
4599 .map(|(range, text)| {
4600 use text::ToPoint as TP;
4601 let end_point = TP::to_point(&range.end, &snapshot);
4602 let start_point = TP::to_point(&range.start, &snapshot);
4603 (start_point..end_point, text)
4604 })
4605 .sorted_by_key(|(range, _)| range.start);
4606 buffer.edit(edits, None, cx);
4607 })
4608 }
4609
4610 this.refresh_inline_completion(true, false, window, cx);
4611 });
4612
4613 let show_new_completions_on_confirm = completion
4614 .confirm
4615 .as_ref()
4616 .map_or(false, |confirm| confirm(intent, window, cx));
4617 if show_new_completions_on_confirm {
4618 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4619 }
4620
4621 let provider = self.completion_provider.as_ref()?;
4622 drop(completion);
4623 let apply_edits = provider.apply_additional_edits_for_completion(
4624 buffer_handle,
4625 completions_menu.completions.clone(),
4626 candidate_id,
4627 true,
4628 cx,
4629 );
4630
4631 let editor_settings = EditorSettings::get_global(cx);
4632 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4633 // After the code completion is finished, users often want to know what signatures are needed.
4634 // so we should automatically call signature_help
4635 self.show_signature_help(&ShowSignatureHelp, window, cx);
4636 }
4637
4638 Some(cx.foreground_executor().spawn(async move {
4639 apply_edits.await?;
4640 Ok(())
4641 }))
4642 }
4643
4644 pub fn toggle_code_actions(
4645 &mut self,
4646 action: &ToggleCodeActions,
4647 window: &mut Window,
4648 cx: &mut Context<Self>,
4649 ) {
4650 let mut context_menu = self.context_menu.borrow_mut();
4651 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4652 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4653 // Toggle if we're selecting the same one
4654 *context_menu = None;
4655 cx.notify();
4656 return;
4657 } else {
4658 // Otherwise, clear it and start a new one
4659 *context_menu = None;
4660 cx.notify();
4661 }
4662 }
4663 drop(context_menu);
4664 let snapshot = self.snapshot(window, cx);
4665 let deployed_from_indicator = action.deployed_from_indicator;
4666 let mut task = self.code_actions_task.take();
4667 let action = action.clone();
4668 cx.spawn_in(window, async move |editor, cx| {
4669 while let Some(prev_task) = task {
4670 prev_task.await.log_err();
4671 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4672 }
4673
4674 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4675 if editor.focus_handle.is_focused(window) {
4676 let multibuffer_point = action
4677 .deployed_from_indicator
4678 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4679 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4680 let (buffer, buffer_row) = snapshot
4681 .buffer_snapshot
4682 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4683 .and_then(|(buffer_snapshot, range)| {
4684 editor
4685 .buffer
4686 .read(cx)
4687 .buffer(buffer_snapshot.remote_id())
4688 .map(|buffer| (buffer, range.start.row))
4689 })?;
4690 let (_, code_actions) = editor
4691 .available_code_actions
4692 .clone()
4693 .and_then(|(location, code_actions)| {
4694 let snapshot = location.buffer.read(cx).snapshot();
4695 let point_range = location.range.to_point(&snapshot);
4696 let point_range = point_range.start.row..=point_range.end.row;
4697 if point_range.contains(&buffer_row) {
4698 Some((location, code_actions))
4699 } else {
4700 None
4701 }
4702 })
4703 .unzip();
4704 let buffer_id = buffer.read(cx).remote_id();
4705 let tasks = editor
4706 .tasks
4707 .get(&(buffer_id, buffer_row))
4708 .map(|t| Arc::new(t.to_owned()));
4709 if tasks.is_none() && code_actions.is_none() {
4710 return None;
4711 }
4712
4713 editor.completion_tasks.clear();
4714 editor.discard_inline_completion(false, cx);
4715 let task_context =
4716 tasks
4717 .as_ref()
4718 .zip(editor.project.clone())
4719 .map(|(tasks, project)| {
4720 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4721 });
4722
4723 Some(cx.spawn_in(window, async move |editor, cx| {
4724 let task_context = match task_context {
4725 Some(task_context) => task_context.await,
4726 None => None,
4727 };
4728 let resolved_tasks =
4729 tasks.zip(task_context).map(|(tasks, task_context)| {
4730 Rc::new(ResolvedTasks {
4731 templates: tasks.resolve(&task_context).collect(),
4732 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4733 multibuffer_point.row,
4734 tasks.column,
4735 )),
4736 })
4737 });
4738 let spawn_straight_away = resolved_tasks
4739 .as_ref()
4740 .map_or(false, |tasks| tasks.templates.len() == 1)
4741 && code_actions
4742 .as_ref()
4743 .map_or(true, |actions| actions.is_empty());
4744 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4745 *editor.context_menu.borrow_mut() =
4746 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4747 buffer,
4748 actions: CodeActionContents {
4749 tasks: resolved_tasks,
4750 actions: code_actions,
4751 },
4752 selected_item: Default::default(),
4753 scroll_handle: UniformListScrollHandle::default(),
4754 deployed_from_indicator,
4755 }));
4756 if spawn_straight_away {
4757 if let Some(task) = editor.confirm_code_action(
4758 &ConfirmCodeAction { item_ix: Some(0) },
4759 window,
4760 cx,
4761 ) {
4762 cx.notify();
4763 return task;
4764 }
4765 }
4766 cx.notify();
4767 Task::ready(Ok(()))
4768 }) {
4769 task.await
4770 } else {
4771 Ok(())
4772 }
4773 }))
4774 } else {
4775 Some(Task::ready(Ok(())))
4776 }
4777 })?;
4778 if let Some(task) = spawned_test_task {
4779 task.await?;
4780 }
4781
4782 Ok::<_, anyhow::Error>(())
4783 })
4784 .detach_and_log_err(cx);
4785 }
4786
4787 pub fn confirm_code_action(
4788 &mut self,
4789 action: &ConfirmCodeAction,
4790 window: &mut Window,
4791 cx: &mut Context<Self>,
4792 ) -> Option<Task<Result<()>>> {
4793 let actions_menu =
4794 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4795 menu
4796 } else {
4797 return None;
4798 };
4799 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4800 let action = actions_menu.actions.get(action_ix)?;
4801 let title = action.label();
4802 let buffer = actions_menu.buffer;
4803 let workspace = self.workspace()?;
4804
4805 match action {
4806 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4807 workspace.update(cx, |workspace, cx| {
4808 workspace::tasks::schedule_resolved_task(
4809 workspace,
4810 task_source_kind,
4811 resolved_task,
4812 false,
4813 cx,
4814 );
4815
4816 Some(Task::ready(Ok(())))
4817 })
4818 }
4819 CodeActionsItem::CodeAction {
4820 excerpt_id,
4821 action,
4822 provider,
4823 } => {
4824 let apply_code_action =
4825 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4826 let workspace = workspace.downgrade();
4827 Some(cx.spawn_in(window, async move |editor, cx| {
4828 let project_transaction = apply_code_action.await?;
4829 Self::open_project_transaction(
4830 &editor,
4831 workspace,
4832 project_transaction,
4833 title,
4834 cx,
4835 )
4836 .await
4837 }))
4838 }
4839 }
4840 }
4841
4842 pub async fn open_project_transaction(
4843 this: &WeakEntity<Editor>,
4844 workspace: WeakEntity<Workspace>,
4845 transaction: ProjectTransaction,
4846 title: String,
4847 cx: &mut AsyncWindowContext,
4848 ) -> Result<()> {
4849 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4850 cx.update(|_, cx| {
4851 entries.sort_unstable_by_key(|(buffer, _)| {
4852 buffer.read(cx).file().map(|f| f.path().clone())
4853 });
4854 })?;
4855
4856 // If the project transaction's edits are all contained within this editor, then
4857 // avoid opening a new editor to display them.
4858
4859 if let Some((buffer, transaction)) = entries.first() {
4860 if entries.len() == 1 {
4861 let excerpt = this.update(cx, |editor, cx| {
4862 editor
4863 .buffer()
4864 .read(cx)
4865 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4866 })?;
4867 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4868 if excerpted_buffer == *buffer {
4869 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4870 let excerpt_range = excerpt_range.to_offset(buffer);
4871 buffer
4872 .edited_ranges_for_transaction::<usize>(transaction)
4873 .all(|range| {
4874 excerpt_range.start <= range.start
4875 && excerpt_range.end >= range.end
4876 })
4877 })?;
4878
4879 if all_edits_within_excerpt {
4880 return Ok(());
4881 }
4882 }
4883 }
4884 }
4885 } else {
4886 return Ok(());
4887 }
4888
4889 let mut ranges_to_highlight = Vec::new();
4890 let excerpt_buffer = cx.new(|cx| {
4891 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4892 for (buffer_handle, transaction) in &entries {
4893 let buffer = buffer_handle.read(cx);
4894 ranges_to_highlight.extend(
4895 multibuffer.push_excerpts_with_context_lines(
4896 buffer_handle.clone(),
4897 buffer
4898 .edited_ranges_for_transaction::<usize>(transaction)
4899 .collect(),
4900 DEFAULT_MULTIBUFFER_CONTEXT,
4901 cx,
4902 ),
4903 );
4904 }
4905 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4906 multibuffer
4907 })?;
4908
4909 workspace.update_in(cx, |workspace, window, cx| {
4910 let project = workspace.project().clone();
4911 let editor =
4912 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4913 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4914 editor.update(cx, |editor, cx| {
4915 editor.highlight_background::<Self>(
4916 &ranges_to_highlight,
4917 |theme| theme.editor_highlighted_line_background,
4918 cx,
4919 );
4920 });
4921 })?;
4922
4923 Ok(())
4924 }
4925
4926 pub fn clear_code_action_providers(&mut self) {
4927 self.code_action_providers.clear();
4928 self.available_code_actions.take();
4929 }
4930
4931 pub fn add_code_action_provider(
4932 &mut self,
4933 provider: Rc<dyn CodeActionProvider>,
4934 window: &mut Window,
4935 cx: &mut Context<Self>,
4936 ) {
4937 if self
4938 .code_action_providers
4939 .iter()
4940 .any(|existing_provider| existing_provider.id() == provider.id())
4941 {
4942 return;
4943 }
4944
4945 self.code_action_providers.push(provider);
4946 self.refresh_code_actions(window, cx);
4947 }
4948
4949 pub fn remove_code_action_provider(
4950 &mut self,
4951 id: Arc<str>,
4952 window: &mut Window,
4953 cx: &mut Context<Self>,
4954 ) {
4955 self.code_action_providers
4956 .retain(|provider| provider.id() != id);
4957 self.refresh_code_actions(window, cx);
4958 }
4959
4960 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4961 let buffer = self.buffer.read(cx);
4962 let newest_selection = self.selections.newest_anchor().clone();
4963 if newest_selection.head().diff_base_anchor.is_some() {
4964 return None;
4965 }
4966 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4967 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4968 if start_buffer != end_buffer {
4969 return None;
4970 }
4971
4972 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4973 cx.background_executor()
4974 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4975 .await;
4976
4977 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4978 let providers = this.code_action_providers.clone();
4979 let tasks = this
4980 .code_action_providers
4981 .iter()
4982 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4983 .collect::<Vec<_>>();
4984 (providers, tasks)
4985 })?;
4986
4987 let mut actions = Vec::new();
4988 for (provider, provider_actions) in
4989 providers.into_iter().zip(future::join_all(tasks).await)
4990 {
4991 if let Some(provider_actions) = provider_actions.log_err() {
4992 actions.extend(provider_actions.into_iter().map(|action| {
4993 AvailableCodeAction {
4994 excerpt_id: newest_selection.start.excerpt_id,
4995 action,
4996 provider: provider.clone(),
4997 }
4998 }));
4999 }
5000 }
5001
5002 this.update(cx, |this, cx| {
5003 this.available_code_actions = if actions.is_empty() {
5004 None
5005 } else {
5006 Some((
5007 Location {
5008 buffer: start_buffer,
5009 range: start..end,
5010 },
5011 actions.into(),
5012 ))
5013 };
5014 cx.notify();
5015 })
5016 }));
5017 None
5018 }
5019
5020 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5021 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
5022 self.show_git_blame_inline = false;
5023
5024 self.show_git_blame_inline_delay_task =
5025 Some(cx.spawn_in(window, async move |this, cx| {
5026 cx.background_executor().timer(delay).await;
5027
5028 this.update(cx, |this, cx| {
5029 this.show_git_blame_inline = true;
5030 cx.notify();
5031 })
5032 .log_err();
5033 }));
5034 }
5035 }
5036
5037 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
5038 if self.pending_rename.is_some() {
5039 return None;
5040 }
5041
5042 let provider = self.semantics_provider.clone()?;
5043 let buffer = self.buffer.read(cx);
5044 let newest_selection = self.selections.newest_anchor().clone();
5045 let cursor_position = newest_selection.head();
5046 let (cursor_buffer, cursor_buffer_position) =
5047 buffer.text_anchor_for_position(cursor_position, cx)?;
5048 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
5049 if cursor_buffer != tail_buffer {
5050 return None;
5051 }
5052 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
5053 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
5054 cx.background_executor()
5055 .timer(Duration::from_millis(debounce))
5056 .await;
5057
5058 let highlights = if let Some(highlights) = cx
5059 .update(|cx| {
5060 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
5061 })
5062 .ok()
5063 .flatten()
5064 {
5065 highlights.await.log_err()
5066 } else {
5067 None
5068 };
5069
5070 if let Some(highlights) = highlights {
5071 this.update(cx, |this, cx| {
5072 if this.pending_rename.is_some() {
5073 return;
5074 }
5075
5076 let buffer_id = cursor_position.buffer_id;
5077 let buffer = this.buffer.read(cx);
5078 if !buffer
5079 .text_anchor_for_position(cursor_position, cx)
5080 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5081 {
5082 return;
5083 }
5084
5085 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5086 let mut write_ranges = Vec::new();
5087 let mut read_ranges = Vec::new();
5088 for highlight in highlights {
5089 for (excerpt_id, excerpt_range) in
5090 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5091 {
5092 let start = highlight
5093 .range
5094 .start
5095 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5096 let end = highlight
5097 .range
5098 .end
5099 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5100 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5101 continue;
5102 }
5103
5104 let range = Anchor {
5105 buffer_id,
5106 excerpt_id,
5107 text_anchor: start,
5108 diff_base_anchor: None,
5109 }..Anchor {
5110 buffer_id,
5111 excerpt_id,
5112 text_anchor: end,
5113 diff_base_anchor: None,
5114 };
5115 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5116 write_ranges.push(range);
5117 } else {
5118 read_ranges.push(range);
5119 }
5120 }
5121 }
5122
5123 this.highlight_background::<DocumentHighlightRead>(
5124 &read_ranges,
5125 |theme| theme.editor_document_highlight_read_background,
5126 cx,
5127 );
5128 this.highlight_background::<DocumentHighlightWrite>(
5129 &write_ranges,
5130 |theme| theme.editor_document_highlight_write_background,
5131 cx,
5132 );
5133 cx.notify();
5134 })
5135 .log_err();
5136 }
5137 }));
5138 None
5139 }
5140
5141 pub fn refresh_selected_text_highlights(
5142 &mut self,
5143 window: &mut Window,
5144 cx: &mut Context<Editor>,
5145 ) {
5146 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5147 return;
5148 }
5149 self.selection_highlight_task.take();
5150 if !EditorSettings::get_global(cx).selection_highlight {
5151 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5152 return;
5153 }
5154 if self.selections.count() != 1 || self.selections.line_mode {
5155 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5156 return;
5157 }
5158 let selection = self.selections.newest::<Point>(cx);
5159 if selection.is_empty() || selection.start.row != selection.end.row {
5160 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5161 return;
5162 }
5163 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5164 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5165 cx.background_executor()
5166 .timer(Duration::from_millis(debounce))
5167 .await;
5168 let Some(Some(matches_task)) = editor
5169 .update_in(cx, |editor, _, cx| {
5170 if editor.selections.count() != 1 || editor.selections.line_mode {
5171 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5172 return None;
5173 }
5174 let selection = editor.selections.newest::<Point>(cx);
5175 if selection.is_empty() || selection.start.row != selection.end.row {
5176 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5177 return None;
5178 }
5179 let buffer = editor.buffer().read(cx).snapshot(cx);
5180 let query = buffer.text_for_range(selection.range()).collect::<String>();
5181 if query.trim().is_empty() {
5182 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5183 return None;
5184 }
5185 Some(cx.background_spawn(async move {
5186 let mut ranges = Vec::new();
5187 let selection_anchors = selection.range().to_anchors(&buffer);
5188 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5189 for (search_buffer, search_range, excerpt_id) in
5190 buffer.range_to_buffer_ranges(range)
5191 {
5192 ranges.extend(
5193 project::search::SearchQuery::text(
5194 query.clone(),
5195 false,
5196 false,
5197 false,
5198 Default::default(),
5199 Default::default(),
5200 None,
5201 )
5202 .unwrap()
5203 .search(search_buffer, Some(search_range.clone()))
5204 .await
5205 .into_iter()
5206 .filter_map(
5207 |match_range| {
5208 let start = search_buffer.anchor_after(
5209 search_range.start + match_range.start,
5210 );
5211 let end = search_buffer.anchor_before(
5212 search_range.start + match_range.end,
5213 );
5214 let range = Anchor::range_in_buffer(
5215 excerpt_id,
5216 search_buffer.remote_id(),
5217 start..end,
5218 );
5219 (range != selection_anchors).then_some(range)
5220 },
5221 ),
5222 );
5223 }
5224 }
5225 ranges
5226 }))
5227 })
5228 .log_err()
5229 else {
5230 return;
5231 };
5232 let matches = matches_task.await;
5233 editor
5234 .update_in(cx, |editor, _, cx| {
5235 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5236 if !matches.is_empty() {
5237 editor.highlight_background::<SelectedTextHighlight>(
5238 &matches,
5239 |theme| theme.editor_document_highlight_bracket_background,
5240 cx,
5241 )
5242 }
5243 })
5244 .log_err();
5245 }));
5246 }
5247
5248 pub fn refresh_inline_completion(
5249 &mut self,
5250 debounce: bool,
5251 user_requested: bool,
5252 window: &mut Window,
5253 cx: &mut Context<Self>,
5254 ) -> Option<()> {
5255 let provider = self.edit_prediction_provider()?;
5256 let cursor = self.selections.newest_anchor().head();
5257 let (buffer, cursor_buffer_position) =
5258 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5259
5260 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5261 self.discard_inline_completion(false, cx);
5262 return None;
5263 }
5264
5265 if !user_requested
5266 && (!self.should_show_edit_predictions()
5267 || !self.is_focused(window)
5268 || buffer.read(cx).is_empty())
5269 {
5270 self.discard_inline_completion(false, cx);
5271 return None;
5272 }
5273
5274 self.update_visible_inline_completion(window, cx);
5275 provider.refresh(
5276 self.project.clone(),
5277 buffer,
5278 cursor_buffer_position,
5279 debounce,
5280 cx,
5281 );
5282 Some(())
5283 }
5284
5285 fn show_edit_predictions_in_menu(&self) -> bool {
5286 match self.edit_prediction_settings {
5287 EditPredictionSettings::Disabled => false,
5288 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5289 }
5290 }
5291
5292 pub fn edit_predictions_enabled(&self) -> bool {
5293 match self.edit_prediction_settings {
5294 EditPredictionSettings::Disabled => false,
5295 EditPredictionSettings::Enabled { .. } => true,
5296 }
5297 }
5298
5299 fn edit_prediction_requires_modifier(&self) -> bool {
5300 match self.edit_prediction_settings {
5301 EditPredictionSettings::Disabled => false,
5302 EditPredictionSettings::Enabled {
5303 preview_requires_modifier,
5304 ..
5305 } => preview_requires_modifier,
5306 }
5307 }
5308
5309 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5310 if self.edit_prediction_provider.is_none() {
5311 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5312 } else {
5313 let selection = self.selections.newest_anchor();
5314 let cursor = selection.head();
5315
5316 if let Some((buffer, cursor_buffer_position)) =
5317 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5318 {
5319 self.edit_prediction_settings =
5320 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5321 }
5322 }
5323 }
5324
5325 fn edit_prediction_settings_at_position(
5326 &self,
5327 buffer: &Entity<Buffer>,
5328 buffer_position: language::Anchor,
5329 cx: &App,
5330 ) -> EditPredictionSettings {
5331 if self.mode != EditorMode::Full
5332 || !self.show_inline_completions_override.unwrap_or(true)
5333 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5334 {
5335 return EditPredictionSettings::Disabled;
5336 }
5337
5338 let buffer = buffer.read(cx);
5339
5340 let file = buffer.file();
5341
5342 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5343 return EditPredictionSettings::Disabled;
5344 };
5345
5346 let by_provider = matches!(
5347 self.menu_inline_completions_policy,
5348 MenuInlineCompletionsPolicy::ByProvider
5349 );
5350
5351 let show_in_menu = by_provider
5352 && self
5353 .edit_prediction_provider
5354 .as_ref()
5355 .map_or(false, |provider| {
5356 provider.provider.show_completions_in_menu()
5357 });
5358
5359 let preview_requires_modifier =
5360 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5361
5362 EditPredictionSettings::Enabled {
5363 show_in_menu,
5364 preview_requires_modifier,
5365 }
5366 }
5367
5368 fn should_show_edit_predictions(&self) -> bool {
5369 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5370 }
5371
5372 pub fn edit_prediction_preview_is_active(&self) -> bool {
5373 matches!(
5374 self.edit_prediction_preview,
5375 EditPredictionPreview::Active { .. }
5376 )
5377 }
5378
5379 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5380 let cursor = self.selections.newest_anchor().head();
5381 if let Some((buffer, cursor_position)) =
5382 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5383 {
5384 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5385 } else {
5386 false
5387 }
5388 }
5389
5390 fn edit_predictions_enabled_in_buffer(
5391 &self,
5392 buffer: &Entity<Buffer>,
5393 buffer_position: language::Anchor,
5394 cx: &App,
5395 ) -> bool {
5396 maybe!({
5397 if self.read_only(cx) {
5398 return Some(false);
5399 }
5400 let provider = self.edit_prediction_provider()?;
5401 if !provider.is_enabled(&buffer, buffer_position, cx) {
5402 return Some(false);
5403 }
5404 let buffer = buffer.read(cx);
5405 let Some(file) = buffer.file() else {
5406 return Some(true);
5407 };
5408 let settings = all_language_settings(Some(file), cx);
5409 Some(settings.edit_predictions_enabled_for_file(file, cx))
5410 })
5411 .unwrap_or(false)
5412 }
5413
5414 fn cycle_inline_completion(
5415 &mut self,
5416 direction: Direction,
5417 window: &mut Window,
5418 cx: &mut Context<Self>,
5419 ) -> Option<()> {
5420 let provider = self.edit_prediction_provider()?;
5421 let cursor = self.selections.newest_anchor().head();
5422 let (buffer, cursor_buffer_position) =
5423 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5424 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5425 return None;
5426 }
5427
5428 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5429 self.update_visible_inline_completion(window, cx);
5430
5431 Some(())
5432 }
5433
5434 pub fn show_inline_completion(
5435 &mut self,
5436 _: &ShowEditPrediction,
5437 window: &mut Window,
5438 cx: &mut Context<Self>,
5439 ) {
5440 if !self.has_active_inline_completion() {
5441 self.refresh_inline_completion(false, true, window, cx);
5442 return;
5443 }
5444
5445 self.update_visible_inline_completion(window, cx);
5446 }
5447
5448 pub fn display_cursor_names(
5449 &mut self,
5450 _: &DisplayCursorNames,
5451 window: &mut Window,
5452 cx: &mut Context<Self>,
5453 ) {
5454 self.show_cursor_names(window, cx);
5455 }
5456
5457 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5458 self.show_cursor_names = true;
5459 cx.notify();
5460 cx.spawn_in(window, async move |this, cx| {
5461 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5462 this.update(cx, |this, cx| {
5463 this.show_cursor_names = false;
5464 cx.notify()
5465 })
5466 .ok()
5467 })
5468 .detach();
5469 }
5470
5471 pub fn next_edit_prediction(
5472 &mut self,
5473 _: &NextEditPrediction,
5474 window: &mut Window,
5475 cx: &mut Context<Self>,
5476 ) {
5477 if self.has_active_inline_completion() {
5478 self.cycle_inline_completion(Direction::Next, window, cx);
5479 } else {
5480 let is_copilot_disabled = self
5481 .refresh_inline_completion(false, true, window, cx)
5482 .is_none();
5483 if is_copilot_disabled {
5484 cx.propagate();
5485 }
5486 }
5487 }
5488
5489 pub fn previous_edit_prediction(
5490 &mut self,
5491 _: &PreviousEditPrediction,
5492 window: &mut Window,
5493 cx: &mut Context<Self>,
5494 ) {
5495 if self.has_active_inline_completion() {
5496 self.cycle_inline_completion(Direction::Prev, window, cx);
5497 } else {
5498 let is_copilot_disabled = self
5499 .refresh_inline_completion(false, true, window, cx)
5500 .is_none();
5501 if is_copilot_disabled {
5502 cx.propagate();
5503 }
5504 }
5505 }
5506
5507 pub fn accept_edit_prediction(
5508 &mut self,
5509 _: &AcceptEditPrediction,
5510 window: &mut Window,
5511 cx: &mut Context<Self>,
5512 ) {
5513 if self.show_edit_predictions_in_menu() {
5514 self.hide_context_menu(window, cx);
5515 }
5516
5517 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5518 return;
5519 };
5520
5521 self.report_inline_completion_event(
5522 active_inline_completion.completion_id.clone(),
5523 true,
5524 cx,
5525 );
5526
5527 match &active_inline_completion.completion {
5528 InlineCompletion::Move { target, .. } => {
5529 let target = *target;
5530
5531 if let Some(position_map) = &self.last_position_map {
5532 if position_map
5533 .visible_row_range
5534 .contains(&target.to_display_point(&position_map.snapshot).row())
5535 || !self.edit_prediction_requires_modifier()
5536 {
5537 self.unfold_ranges(&[target..target], true, false, cx);
5538 // Note that this is also done in vim's handler of the Tab action.
5539 self.change_selections(
5540 Some(Autoscroll::newest()),
5541 window,
5542 cx,
5543 |selections| {
5544 selections.select_anchor_ranges([target..target]);
5545 },
5546 );
5547 self.clear_row_highlights::<EditPredictionPreview>();
5548
5549 self.edit_prediction_preview
5550 .set_previous_scroll_position(None);
5551 } else {
5552 self.edit_prediction_preview
5553 .set_previous_scroll_position(Some(
5554 position_map.snapshot.scroll_anchor,
5555 ));
5556
5557 self.highlight_rows::<EditPredictionPreview>(
5558 target..target,
5559 cx.theme().colors().editor_highlighted_line_background,
5560 true,
5561 cx,
5562 );
5563 self.request_autoscroll(Autoscroll::fit(), cx);
5564 }
5565 }
5566 }
5567 InlineCompletion::Edit { edits, .. } => {
5568 if let Some(provider) = self.edit_prediction_provider() {
5569 provider.accept(cx);
5570 }
5571
5572 let snapshot = self.buffer.read(cx).snapshot(cx);
5573 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5574
5575 self.buffer.update(cx, |buffer, cx| {
5576 buffer.edit(edits.iter().cloned(), None, cx)
5577 });
5578
5579 self.change_selections(None, window, cx, |s| {
5580 s.select_anchor_ranges([last_edit_end..last_edit_end])
5581 });
5582
5583 self.update_visible_inline_completion(window, cx);
5584 if self.active_inline_completion.is_none() {
5585 self.refresh_inline_completion(true, true, window, cx);
5586 }
5587
5588 cx.notify();
5589 }
5590 }
5591
5592 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5593 }
5594
5595 pub fn accept_partial_inline_completion(
5596 &mut self,
5597 _: &AcceptPartialEditPrediction,
5598 window: &mut Window,
5599 cx: &mut Context<Self>,
5600 ) {
5601 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5602 return;
5603 };
5604 if self.selections.count() != 1 {
5605 return;
5606 }
5607
5608 self.report_inline_completion_event(
5609 active_inline_completion.completion_id.clone(),
5610 true,
5611 cx,
5612 );
5613
5614 match &active_inline_completion.completion {
5615 InlineCompletion::Move { target, .. } => {
5616 let target = *target;
5617 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5618 selections.select_anchor_ranges([target..target]);
5619 });
5620 }
5621 InlineCompletion::Edit { edits, .. } => {
5622 // Find an insertion that starts at the cursor position.
5623 let snapshot = self.buffer.read(cx).snapshot(cx);
5624 let cursor_offset = self.selections.newest::<usize>(cx).head();
5625 let insertion = edits.iter().find_map(|(range, text)| {
5626 let range = range.to_offset(&snapshot);
5627 if range.is_empty() && range.start == cursor_offset {
5628 Some(text)
5629 } else {
5630 None
5631 }
5632 });
5633
5634 if let Some(text) = insertion {
5635 let mut partial_completion = text
5636 .chars()
5637 .by_ref()
5638 .take_while(|c| c.is_alphabetic())
5639 .collect::<String>();
5640 if partial_completion.is_empty() {
5641 partial_completion = text
5642 .chars()
5643 .by_ref()
5644 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5645 .collect::<String>();
5646 }
5647
5648 cx.emit(EditorEvent::InputHandled {
5649 utf16_range_to_replace: None,
5650 text: partial_completion.clone().into(),
5651 });
5652
5653 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5654
5655 self.refresh_inline_completion(true, true, window, cx);
5656 cx.notify();
5657 } else {
5658 self.accept_edit_prediction(&Default::default(), window, cx);
5659 }
5660 }
5661 }
5662 }
5663
5664 fn discard_inline_completion(
5665 &mut self,
5666 should_report_inline_completion_event: bool,
5667 cx: &mut Context<Self>,
5668 ) -> bool {
5669 if should_report_inline_completion_event {
5670 let completion_id = self
5671 .active_inline_completion
5672 .as_ref()
5673 .and_then(|active_completion| active_completion.completion_id.clone());
5674
5675 self.report_inline_completion_event(completion_id, false, cx);
5676 }
5677
5678 if let Some(provider) = self.edit_prediction_provider() {
5679 provider.discard(cx);
5680 }
5681
5682 self.take_active_inline_completion(cx)
5683 }
5684
5685 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5686 let Some(provider) = self.edit_prediction_provider() else {
5687 return;
5688 };
5689
5690 let Some((_, buffer, _)) = self
5691 .buffer
5692 .read(cx)
5693 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5694 else {
5695 return;
5696 };
5697
5698 let extension = buffer
5699 .read(cx)
5700 .file()
5701 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5702
5703 let event_type = match accepted {
5704 true => "Edit Prediction Accepted",
5705 false => "Edit Prediction Discarded",
5706 };
5707 telemetry::event!(
5708 event_type,
5709 provider = provider.name(),
5710 prediction_id = id,
5711 suggestion_accepted = accepted,
5712 file_extension = extension,
5713 );
5714 }
5715
5716 pub fn has_active_inline_completion(&self) -> bool {
5717 self.active_inline_completion.is_some()
5718 }
5719
5720 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5721 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5722 return false;
5723 };
5724
5725 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5726 self.clear_highlights::<InlineCompletionHighlight>(cx);
5727 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5728 true
5729 }
5730
5731 /// Returns true when we're displaying the edit prediction popover below the cursor
5732 /// like we are not previewing and the LSP autocomplete menu is visible
5733 /// or we are in `when_holding_modifier` mode.
5734 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5735 if self.edit_prediction_preview_is_active()
5736 || !self.show_edit_predictions_in_menu()
5737 || !self.edit_predictions_enabled()
5738 {
5739 return false;
5740 }
5741
5742 if self.has_visible_completions_menu() {
5743 return true;
5744 }
5745
5746 has_completion && self.edit_prediction_requires_modifier()
5747 }
5748
5749 fn handle_modifiers_changed(
5750 &mut self,
5751 modifiers: Modifiers,
5752 position_map: &PositionMap,
5753 window: &mut Window,
5754 cx: &mut Context<Self>,
5755 ) {
5756 if self.show_edit_predictions_in_menu() {
5757 self.update_edit_prediction_preview(&modifiers, window, cx);
5758 }
5759
5760 self.update_selection_mode(&modifiers, position_map, window, cx);
5761
5762 let mouse_position = window.mouse_position();
5763 if !position_map.text_hitbox.is_hovered(window) {
5764 return;
5765 }
5766
5767 self.update_hovered_link(
5768 position_map.point_for_position(mouse_position),
5769 &position_map.snapshot,
5770 modifiers,
5771 window,
5772 cx,
5773 )
5774 }
5775
5776 fn update_selection_mode(
5777 &mut self,
5778 modifiers: &Modifiers,
5779 position_map: &PositionMap,
5780 window: &mut Window,
5781 cx: &mut Context<Self>,
5782 ) {
5783 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5784 return;
5785 }
5786
5787 let mouse_position = window.mouse_position();
5788 let point_for_position = position_map.point_for_position(mouse_position);
5789 let position = point_for_position.previous_valid;
5790
5791 self.select(
5792 SelectPhase::BeginColumnar {
5793 position,
5794 reset: false,
5795 goal_column: point_for_position.exact_unclipped.column(),
5796 },
5797 window,
5798 cx,
5799 );
5800 }
5801
5802 fn update_edit_prediction_preview(
5803 &mut self,
5804 modifiers: &Modifiers,
5805 window: &mut Window,
5806 cx: &mut Context<Self>,
5807 ) {
5808 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5809 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5810 return;
5811 };
5812
5813 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5814 if matches!(
5815 self.edit_prediction_preview,
5816 EditPredictionPreview::Inactive { .. }
5817 ) {
5818 self.edit_prediction_preview = EditPredictionPreview::Active {
5819 previous_scroll_position: None,
5820 since: Instant::now(),
5821 };
5822
5823 self.update_visible_inline_completion(window, cx);
5824 cx.notify();
5825 }
5826 } else if let EditPredictionPreview::Active {
5827 previous_scroll_position,
5828 since,
5829 } = self.edit_prediction_preview
5830 {
5831 if let (Some(previous_scroll_position), Some(position_map)) =
5832 (previous_scroll_position, self.last_position_map.as_ref())
5833 {
5834 self.set_scroll_position(
5835 previous_scroll_position
5836 .scroll_position(&position_map.snapshot.display_snapshot),
5837 window,
5838 cx,
5839 );
5840 }
5841
5842 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5843 released_too_fast: since.elapsed() < Duration::from_millis(200),
5844 };
5845 self.clear_row_highlights::<EditPredictionPreview>();
5846 self.update_visible_inline_completion(window, cx);
5847 cx.notify();
5848 }
5849 }
5850
5851 fn update_visible_inline_completion(
5852 &mut self,
5853 _window: &mut Window,
5854 cx: &mut Context<Self>,
5855 ) -> Option<()> {
5856 let selection = self.selections.newest_anchor();
5857 let cursor = selection.head();
5858 let multibuffer = self.buffer.read(cx).snapshot(cx);
5859 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5860 let excerpt_id = cursor.excerpt_id;
5861
5862 let show_in_menu = self.show_edit_predictions_in_menu();
5863 let completions_menu_has_precedence = !show_in_menu
5864 && (self.context_menu.borrow().is_some()
5865 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5866
5867 if completions_menu_has_precedence
5868 || !offset_selection.is_empty()
5869 || self
5870 .active_inline_completion
5871 .as_ref()
5872 .map_or(false, |completion| {
5873 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5874 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5875 !invalidation_range.contains(&offset_selection.head())
5876 })
5877 {
5878 self.discard_inline_completion(false, cx);
5879 return None;
5880 }
5881
5882 self.take_active_inline_completion(cx);
5883 let Some(provider) = self.edit_prediction_provider() else {
5884 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5885 return None;
5886 };
5887
5888 let (buffer, cursor_buffer_position) =
5889 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5890
5891 self.edit_prediction_settings =
5892 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5893
5894 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5895
5896 if self.edit_prediction_indent_conflict {
5897 let cursor_point = cursor.to_point(&multibuffer);
5898
5899 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5900
5901 if let Some((_, indent)) = indents.iter().next() {
5902 if indent.len == cursor_point.column {
5903 self.edit_prediction_indent_conflict = false;
5904 }
5905 }
5906 }
5907
5908 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5909 let edits = inline_completion
5910 .edits
5911 .into_iter()
5912 .flat_map(|(range, new_text)| {
5913 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5914 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5915 Some((start..end, new_text))
5916 })
5917 .collect::<Vec<_>>();
5918 if edits.is_empty() {
5919 return None;
5920 }
5921
5922 let first_edit_start = edits.first().unwrap().0.start;
5923 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5924 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5925
5926 let last_edit_end = edits.last().unwrap().0.end;
5927 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5928 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5929
5930 let cursor_row = cursor.to_point(&multibuffer).row;
5931
5932 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5933
5934 let mut inlay_ids = Vec::new();
5935 let invalidation_row_range;
5936 let move_invalidation_row_range = if cursor_row < edit_start_row {
5937 Some(cursor_row..edit_end_row)
5938 } else if cursor_row > edit_end_row {
5939 Some(edit_start_row..cursor_row)
5940 } else {
5941 None
5942 };
5943 let is_move =
5944 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5945 let completion = if is_move {
5946 invalidation_row_range =
5947 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5948 let target = first_edit_start;
5949 InlineCompletion::Move { target, snapshot }
5950 } else {
5951 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5952 && !self.inline_completions_hidden_for_vim_mode;
5953
5954 if show_completions_in_buffer {
5955 if edits
5956 .iter()
5957 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5958 {
5959 let mut inlays = Vec::new();
5960 for (range, new_text) in &edits {
5961 let inlay = Inlay::inline_completion(
5962 post_inc(&mut self.next_inlay_id),
5963 range.start,
5964 new_text.as_str(),
5965 );
5966 inlay_ids.push(inlay.id);
5967 inlays.push(inlay);
5968 }
5969
5970 self.splice_inlays(&[], inlays, cx);
5971 } else {
5972 let background_color = cx.theme().status().deleted_background;
5973 self.highlight_text::<InlineCompletionHighlight>(
5974 edits.iter().map(|(range, _)| range.clone()).collect(),
5975 HighlightStyle {
5976 background_color: Some(background_color),
5977 ..Default::default()
5978 },
5979 cx,
5980 );
5981 }
5982 }
5983
5984 invalidation_row_range = edit_start_row..edit_end_row;
5985
5986 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5987 if provider.show_tab_accept_marker() {
5988 EditDisplayMode::TabAccept
5989 } else {
5990 EditDisplayMode::Inline
5991 }
5992 } else {
5993 EditDisplayMode::DiffPopover
5994 };
5995
5996 InlineCompletion::Edit {
5997 edits,
5998 edit_preview: inline_completion.edit_preview,
5999 display_mode,
6000 snapshot,
6001 }
6002 };
6003
6004 let invalidation_range = multibuffer
6005 .anchor_before(Point::new(invalidation_row_range.start, 0))
6006 ..multibuffer.anchor_after(Point::new(
6007 invalidation_row_range.end,
6008 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
6009 ));
6010
6011 self.stale_inline_completion_in_menu = None;
6012 self.active_inline_completion = Some(InlineCompletionState {
6013 inlay_ids,
6014 completion,
6015 completion_id: inline_completion.id,
6016 invalidation_range,
6017 });
6018
6019 cx.notify();
6020
6021 Some(())
6022 }
6023
6024 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
6025 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
6026 }
6027
6028 fn render_code_actions_indicator(
6029 &self,
6030 _style: &EditorStyle,
6031 row: DisplayRow,
6032 is_active: bool,
6033 breakpoint: Option<&(Anchor, Breakpoint)>,
6034 cx: &mut Context<Self>,
6035 ) -> Option<IconButton> {
6036 let color = Color::Muted;
6037 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6038
6039 if self.available_code_actions.is_some() {
6040 Some(
6041 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
6042 .shape(ui::IconButtonShape::Square)
6043 .icon_size(IconSize::XSmall)
6044 .icon_color(color)
6045 .toggle_state(is_active)
6046 .tooltip({
6047 let focus_handle = self.focus_handle.clone();
6048 move |window, cx| {
6049 Tooltip::for_action_in(
6050 "Toggle Code Actions",
6051 &ToggleCodeActions {
6052 deployed_from_indicator: None,
6053 },
6054 &focus_handle,
6055 window,
6056 cx,
6057 )
6058 }
6059 })
6060 .on_click(cx.listener(move |editor, _e, window, cx| {
6061 window.focus(&editor.focus_handle(cx));
6062 editor.toggle_code_actions(
6063 &ToggleCodeActions {
6064 deployed_from_indicator: Some(row),
6065 },
6066 window,
6067 cx,
6068 );
6069 }))
6070 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6071 editor.set_breakpoint_context_menu(
6072 row,
6073 position,
6074 event.down.position,
6075 window,
6076 cx,
6077 );
6078 })),
6079 )
6080 } else {
6081 None
6082 }
6083 }
6084
6085 fn clear_tasks(&mut self) {
6086 self.tasks.clear()
6087 }
6088
6089 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6090 if self.tasks.insert(key, value).is_some() {
6091 // This case should hopefully be rare, but just in case...
6092 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6093 }
6094 }
6095
6096 /// Get all display points of breakpoints that will be rendered within editor
6097 ///
6098 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6099 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6100 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6101 fn active_breakpoints(
6102 &mut self,
6103 range: Range<DisplayRow>,
6104 window: &mut Window,
6105 cx: &mut Context<Self>,
6106 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6107 let mut breakpoint_display_points = HashMap::default();
6108
6109 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6110 return breakpoint_display_points;
6111 };
6112
6113 let snapshot = self.snapshot(window, cx);
6114
6115 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6116 let Some(project) = self.project.as_ref() else {
6117 return breakpoint_display_points;
6118 };
6119
6120 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6121 let buffer_snapshot = buffer.read(cx).snapshot();
6122
6123 for breakpoint in
6124 breakpoint_store
6125 .read(cx)
6126 .breakpoints(&buffer, None, &buffer_snapshot, cx)
6127 {
6128 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6129 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6130 anchor.text_anchor = breakpoint.0;
6131
6132 breakpoint_display_points.insert(
6133 snapshot
6134 .point_to_display_point(
6135 MultiBufferPoint {
6136 row: point.row,
6137 column: point.column,
6138 },
6139 Bias::Left,
6140 )
6141 .row(),
6142 (anchor, breakpoint.1.clone()),
6143 );
6144 }
6145
6146 return breakpoint_display_points;
6147 }
6148
6149 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6150 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6151
6152 for (buffer_snapshot, range, excerpt_id) in
6153 multi_buffer_snapshot.range_to_buffer_ranges(range)
6154 {
6155 let Some(buffer) = project.read_with(cx, |this, cx| {
6156 this.buffer_for_id(buffer_snapshot.remote_id(), cx)
6157 }) else {
6158 continue;
6159 };
6160 let breakpoints = breakpoint_store.read(cx).breakpoints(
6161 &buffer,
6162 Some(
6163 buffer_snapshot.anchor_before(range.start)
6164 ..buffer_snapshot.anchor_after(range.end),
6165 ),
6166 buffer_snapshot,
6167 cx,
6168 );
6169 for (anchor, breakpoint) in breakpoints {
6170 let multi_buffer_anchor =
6171 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), *anchor);
6172 let position = multi_buffer_anchor
6173 .to_point(&multi_buffer_snapshot)
6174 .to_display_point(&snapshot);
6175
6176 breakpoint_display_points
6177 .insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
6178 }
6179 }
6180
6181 breakpoint_display_points
6182 }
6183
6184 fn breakpoint_context_menu(
6185 &self,
6186 anchor: Anchor,
6187 window: &mut Window,
6188 cx: &mut Context<Self>,
6189 ) -> Entity<ui::ContextMenu> {
6190 let weak_editor = cx.weak_entity();
6191 let focus_handle = self.focus_handle(cx);
6192
6193 let row = self
6194 .buffer
6195 .read(cx)
6196 .snapshot(cx)
6197 .summary_for_anchor::<Point>(&anchor)
6198 .row;
6199
6200 let breakpoint = self
6201 .breakpoint_at_row(row, window, cx)
6202 .map(|(_, bp)| Arc::from(bp));
6203
6204 let log_breakpoint_msg = if breakpoint
6205 .as_ref()
6206 .is_some_and(|bp| bp.kind.log_message().is_some())
6207 {
6208 "Edit Log Breakpoint"
6209 } else {
6210 "Set Log Breakpoint"
6211 };
6212
6213 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
6214 "Unset Breakpoint"
6215 } else {
6216 "Set Breakpoint"
6217 };
6218
6219 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.state {
6220 BreakpointState::Enabled => Some("Disable"),
6221 BreakpointState::Disabled => Some("Enable"),
6222 });
6223
6224 let breakpoint = breakpoint.unwrap_or_else(|| {
6225 Arc::new(Breakpoint {
6226 state: BreakpointState::Enabled,
6227 kind: BreakpointKind::Standard,
6228 })
6229 });
6230
6231 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6232 menu.on_blur_subscription(Subscription::new(|| {}))
6233 .context(focus_handle)
6234 .when_some(toggle_state_msg, |this, msg| {
6235 this.entry(msg, None, {
6236 let weak_editor = weak_editor.clone();
6237 let breakpoint = breakpoint.clone();
6238 move |_window, cx| {
6239 weak_editor
6240 .update(cx, |this, cx| {
6241 this.edit_breakpoint_at_anchor(
6242 anchor,
6243 breakpoint.as_ref().clone(),
6244 BreakpointEditAction::InvertState,
6245 cx,
6246 );
6247 })
6248 .log_err();
6249 }
6250 })
6251 })
6252 .entry(set_breakpoint_msg, None, {
6253 let weak_editor = weak_editor.clone();
6254 let breakpoint = breakpoint.clone();
6255 move |_window, cx| {
6256 weak_editor
6257 .update(cx, |this, cx| {
6258 this.edit_breakpoint_at_anchor(
6259 anchor,
6260 breakpoint.as_ref().clone(),
6261 BreakpointEditAction::Toggle,
6262 cx,
6263 );
6264 })
6265 .log_err();
6266 }
6267 })
6268 .entry(log_breakpoint_msg, None, move |window, cx| {
6269 weak_editor
6270 .update(cx, |this, cx| {
6271 this.add_edit_breakpoint_block(anchor, breakpoint.as_ref(), window, cx);
6272 })
6273 .log_err();
6274 })
6275 })
6276 }
6277
6278 fn render_breakpoint(
6279 &self,
6280 position: Anchor,
6281 row: DisplayRow,
6282 breakpoint: &Breakpoint,
6283 cx: &mut Context<Self>,
6284 ) -> IconButton {
6285 let (color, icon) = {
6286 let color = if self
6287 .gutter_breakpoint_indicator
6288 .is_some_and(|point| point.row() == row)
6289 {
6290 Color::Hint
6291 } else if breakpoint.is_disabled() {
6292 Color::Custom(Color::Debugger.color(cx).opacity(0.5))
6293 } else {
6294 Color::Debugger
6295 };
6296 let icon = match &breakpoint.kind {
6297 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6298 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6299 };
6300 (color, icon)
6301 };
6302
6303 let breakpoint = Arc::from(breakpoint.clone());
6304
6305 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6306 .icon_size(IconSize::XSmall)
6307 .size(ui::ButtonSize::None)
6308 .icon_color(color)
6309 .style(ButtonStyle::Transparent)
6310 .on_click(cx.listener({
6311 let breakpoint = breakpoint.clone();
6312
6313 move |editor, _e, window, cx| {
6314 window.focus(&editor.focus_handle(cx));
6315 editor.edit_breakpoint_at_anchor(
6316 position,
6317 breakpoint.as_ref().clone(),
6318 BreakpointEditAction::Toggle,
6319 cx,
6320 );
6321 }
6322 }))
6323 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6324 editor.set_breakpoint_context_menu(
6325 row,
6326 Some(position),
6327 event.down.position,
6328 window,
6329 cx,
6330 );
6331 }))
6332 }
6333
6334 fn build_tasks_context(
6335 project: &Entity<Project>,
6336 buffer: &Entity<Buffer>,
6337 buffer_row: u32,
6338 tasks: &Arc<RunnableTasks>,
6339 cx: &mut Context<Self>,
6340 ) -> Task<Option<task::TaskContext>> {
6341 let position = Point::new(buffer_row, tasks.column);
6342 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6343 let location = Location {
6344 buffer: buffer.clone(),
6345 range: range_start..range_start,
6346 };
6347 // Fill in the environmental variables from the tree-sitter captures
6348 let mut captured_task_variables = TaskVariables::default();
6349 for (capture_name, value) in tasks.extra_variables.clone() {
6350 captured_task_variables.insert(
6351 task::VariableName::Custom(capture_name.into()),
6352 value.clone(),
6353 );
6354 }
6355 project.update(cx, |project, cx| {
6356 project.task_store().update(cx, |task_store, cx| {
6357 task_store.task_context_for_location(captured_task_variables, location, cx)
6358 })
6359 })
6360 }
6361
6362 pub fn spawn_nearest_task(
6363 &mut self,
6364 action: &SpawnNearestTask,
6365 window: &mut Window,
6366 cx: &mut Context<Self>,
6367 ) {
6368 let Some((workspace, _)) = self.workspace.clone() else {
6369 return;
6370 };
6371 let Some(project) = self.project.clone() else {
6372 return;
6373 };
6374
6375 // Try to find a closest, enclosing node using tree-sitter that has a
6376 // task
6377 let Some((buffer, buffer_row, tasks)) = self
6378 .find_enclosing_node_task(cx)
6379 // Or find the task that's closest in row-distance.
6380 .or_else(|| self.find_closest_task(cx))
6381 else {
6382 return;
6383 };
6384
6385 let reveal_strategy = action.reveal;
6386 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6387 cx.spawn_in(window, async move |_, cx| {
6388 let context = task_context.await?;
6389 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6390
6391 let resolved = resolved_task.resolved.as_mut()?;
6392 resolved.reveal = reveal_strategy;
6393
6394 workspace
6395 .update(cx, |workspace, cx| {
6396 workspace::tasks::schedule_resolved_task(
6397 workspace,
6398 task_source_kind,
6399 resolved_task,
6400 false,
6401 cx,
6402 );
6403 })
6404 .ok()
6405 })
6406 .detach();
6407 }
6408
6409 fn find_closest_task(
6410 &mut self,
6411 cx: &mut Context<Self>,
6412 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6413 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6414
6415 let ((buffer_id, row), tasks) = self
6416 .tasks
6417 .iter()
6418 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6419
6420 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6421 let tasks = Arc::new(tasks.to_owned());
6422 Some((buffer, *row, tasks))
6423 }
6424
6425 fn find_enclosing_node_task(
6426 &mut self,
6427 cx: &mut Context<Self>,
6428 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6429 let snapshot = self.buffer.read(cx).snapshot(cx);
6430 let offset = self.selections.newest::<usize>(cx).head();
6431 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6432 let buffer_id = excerpt.buffer().remote_id();
6433
6434 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6435 let mut cursor = layer.node().walk();
6436
6437 while cursor.goto_first_child_for_byte(offset).is_some() {
6438 if cursor.node().end_byte() == offset {
6439 cursor.goto_next_sibling();
6440 }
6441 }
6442
6443 // Ascend to the smallest ancestor that contains the range and has a task.
6444 loop {
6445 let node = cursor.node();
6446 let node_range = node.byte_range();
6447 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6448
6449 // Check if this node contains our offset
6450 if node_range.start <= offset && node_range.end >= offset {
6451 // If it contains offset, check for task
6452 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6453 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6454 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6455 }
6456 }
6457
6458 if !cursor.goto_parent() {
6459 break;
6460 }
6461 }
6462 None
6463 }
6464
6465 fn render_run_indicator(
6466 &self,
6467 _style: &EditorStyle,
6468 is_active: bool,
6469 row: DisplayRow,
6470 breakpoint: Option<(Anchor, Breakpoint)>,
6471 cx: &mut Context<Self>,
6472 ) -> IconButton {
6473 let color = Color::Muted;
6474 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6475
6476 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6477 .shape(ui::IconButtonShape::Square)
6478 .icon_size(IconSize::XSmall)
6479 .icon_color(color)
6480 .toggle_state(is_active)
6481 .on_click(cx.listener(move |editor, _e, window, cx| {
6482 window.focus(&editor.focus_handle(cx));
6483 editor.toggle_code_actions(
6484 &ToggleCodeActions {
6485 deployed_from_indicator: Some(row),
6486 },
6487 window,
6488 cx,
6489 );
6490 }))
6491 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6492 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
6493 }))
6494 }
6495
6496 pub fn context_menu_visible(&self) -> bool {
6497 !self.edit_prediction_preview_is_active()
6498 && self
6499 .context_menu
6500 .borrow()
6501 .as_ref()
6502 .map_or(false, |menu| menu.visible())
6503 }
6504
6505 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6506 self.context_menu
6507 .borrow()
6508 .as_ref()
6509 .map(|menu| menu.origin())
6510 }
6511
6512 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
6513 self.context_menu_options = Some(options);
6514 }
6515
6516 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6517 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6518
6519 fn render_edit_prediction_popover(
6520 &mut self,
6521 text_bounds: &Bounds<Pixels>,
6522 content_origin: gpui::Point<Pixels>,
6523 editor_snapshot: &EditorSnapshot,
6524 visible_row_range: Range<DisplayRow>,
6525 scroll_top: f32,
6526 scroll_bottom: f32,
6527 line_layouts: &[LineWithInvisibles],
6528 line_height: Pixels,
6529 scroll_pixel_position: gpui::Point<Pixels>,
6530 newest_selection_head: Option<DisplayPoint>,
6531 editor_width: Pixels,
6532 style: &EditorStyle,
6533 window: &mut Window,
6534 cx: &mut App,
6535 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6536 let active_inline_completion = self.active_inline_completion.as_ref()?;
6537
6538 if self.edit_prediction_visible_in_cursor_popover(true) {
6539 return None;
6540 }
6541
6542 match &active_inline_completion.completion {
6543 InlineCompletion::Move { target, .. } => {
6544 let target_display_point = target.to_display_point(editor_snapshot);
6545
6546 if self.edit_prediction_requires_modifier() {
6547 if !self.edit_prediction_preview_is_active() {
6548 return None;
6549 }
6550
6551 self.render_edit_prediction_modifier_jump_popover(
6552 text_bounds,
6553 content_origin,
6554 visible_row_range,
6555 line_layouts,
6556 line_height,
6557 scroll_pixel_position,
6558 newest_selection_head,
6559 target_display_point,
6560 window,
6561 cx,
6562 )
6563 } else {
6564 self.render_edit_prediction_eager_jump_popover(
6565 text_bounds,
6566 content_origin,
6567 editor_snapshot,
6568 visible_row_range,
6569 scroll_top,
6570 scroll_bottom,
6571 line_height,
6572 scroll_pixel_position,
6573 target_display_point,
6574 editor_width,
6575 window,
6576 cx,
6577 )
6578 }
6579 }
6580 InlineCompletion::Edit {
6581 display_mode: EditDisplayMode::Inline,
6582 ..
6583 } => None,
6584 InlineCompletion::Edit {
6585 display_mode: EditDisplayMode::TabAccept,
6586 edits,
6587 ..
6588 } => {
6589 let range = &edits.first()?.0;
6590 let target_display_point = range.end.to_display_point(editor_snapshot);
6591
6592 self.render_edit_prediction_end_of_line_popover(
6593 "Accept",
6594 editor_snapshot,
6595 visible_row_range,
6596 target_display_point,
6597 line_height,
6598 scroll_pixel_position,
6599 content_origin,
6600 editor_width,
6601 window,
6602 cx,
6603 )
6604 }
6605 InlineCompletion::Edit {
6606 edits,
6607 edit_preview,
6608 display_mode: EditDisplayMode::DiffPopover,
6609 snapshot,
6610 } => self.render_edit_prediction_diff_popover(
6611 text_bounds,
6612 content_origin,
6613 editor_snapshot,
6614 visible_row_range,
6615 line_layouts,
6616 line_height,
6617 scroll_pixel_position,
6618 newest_selection_head,
6619 editor_width,
6620 style,
6621 edits,
6622 edit_preview,
6623 snapshot,
6624 window,
6625 cx,
6626 ),
6627 }
6628 }
6629
6630 fn render_edit_prediction_modifier_jump_popover(
6631 &mut self,
6632 text_bounds: &Bounds<Pixels>,
6633 content_origin: gpui::Point<Pixels>,
6634 visible_row_range: Range<DisplayRow>,
6635 line_layouts: &[LineWithInvisibles],
6636 line_height: Pixels,
6637 scroll_pixel_position: gpui::Point<Pixels>,
6638 newest_selection_head: Option<DisplayPoint>,
6639 target_display_point: DisplayPoint,
6640 window: &mut Window,
6641 cx: &mut App,
6642 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6643 let scrolled_content_origin =
6644 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6645
6646 const SCROLL_PADDING_Y: Pixels = px(12.);
6647
6648 if target_display_point.row() < visible_row_range.start {
6649 return self.render_edit_prediction_scroll_popover(
6650 |_| SCROLL_PADDING_Y,
6651 IconName::ArrowUp,
6652 visible_row_range,
6653 line_layouts,
6654 newest_selection_head,
6655 scrolled_content_origin,
6656 window,
6657 cx,
6658 );
6659 } else if target_display_point.row() >= visible_row_range.end {
6660 return self.render_edit_prediction_scroll_popover(
6661 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6662 IconName::ArrowDown,
6663 visible_row_range,
6664 line_layouts,
6665 newest_selection_head,
6666 scrolled_content_origin,
6667 window,
6668 cx,
6669 );
6670 }
6671
6672 const POLE_WIDTH: Pixels = px(2.);
6673
6674 let line_layout =
6675 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6676 let target_column = target_display_point.column() as usize;
6677
6678 let target_x = line_layout.x_for_index(target_column);
6679 let target_y =
6680 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6681
6682 let flag_on_right = target_x < text_bounds.size.width / 2.;
6683
6684 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6685 border_color.l += 0.001;
6686
6687 let mut element = v_flex()
6688 .items_end()
6689 .when(flag_on_right, |el| el.items_start())
6690 .child(if flag_on_right {
6691 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6692 .rounded_bl(px(0.))
6693 .rounded_tl(px(0.))
6694 .border_l_2()
6695 .border_color(border_color)
6696 } else {
6697 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6698 .rounded_br(px(0.))
6699 .rounded_tr(px(0.))
6700 .border_r_2()
6701 .border_color(border_color)
6702 })
6703 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6704 .into_any();
6705
6706 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6707
6708 let mut origin = scrolled_content_origin + point(target_x, target_y)
6709 - point(
6710 if flag_on_right {
6711 POLE_WIDTH
6712 } else {
6713 size.width - POLE_WIDTH
6714 },
6715 size.height - line_height,
6716 );
6717
6718 origin.x = origin.x.max(content_origin.x);
6719
6720 element.prepaint_at(origin, window, cx);
6721
6722 Some((element, origin))
6723 }
6724
6725 fn render_edit_prediction_scroll_popover(
6726 &mut self,
6727 to_y: impl Fn(Size<Pixels>) -> Pixels,
6728 scroll_icon: IconName,
6729 visible_row_range: Range<DisplayRow>,
6730 line_layouts: &[LineWithInvisibles],
6731 newest_selection_head: Option<DisplayPoint>,
6732 scrolled_content_origin: gpui::Point<Pixels>,
6733 window: &mut Window,
6734 cx: &mut App,
6735 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6736 let mut element = self
6737 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6738 .into_any();
6739
6740 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6741
6742 let cursor = newest_selection_head?;
6743 let cursor_row_layout =
6744 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6745 let cursor_column = cursor.column() as usize;
6746
6747 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6748
6749 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6750
6751 element.prepaint_at(origin, window, cx);
6752 Some((element, origin))
6753 }
6754
6755 fn render_edit_prediction_eager_jump_popover(
6756 &mut self,
6757 text_bounds: &Bounds<Pixels>,
6758 content_origin: gpui::Point<Pixels>,
6759 editor_snapshot: &EditorSnapshot,
6760 visible_row_range: Range<DisplayRow>,
6761 scroll_top: f32,
6762 scroll_bottom: f32,
6763 line_height: Pixels,
6764 scroll_pixel_position: gpui::Point<Pixels>,
6765 target_display_point: DisplayPoint,
6766 editor_width: Pixels,
6767 window: &mut Window,
6768 cx: &mut App,
6769 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6770 if target_display_point.row().as_f32() < scroll_top {
6771 let mut element = self
6772 .render_edit_prediction_line_popover(
6773 "Jump to Edit",
6774 Some(IconName::ArrowUp),
6775 window,
6776 cx,
6777 )?
6778 .into_any();
6779
6780 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6781 let offset = point(
6782 (text_bounds.size.width - size.width) / 2.,
6783 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6784 );
6785
6786 let origin = text_bounds.origin + offset;
6787 element.prepaint_at(origin, window, cx);
6788 Some((element, origin))
6789 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6790 let mut element = self
6791 .render_edit_prediction_line_popover(
6792 "Jump to Edit",
6793 Some(IconName::ArrowDown),
6794 window,
6795 cx,
6796 )?
6797 .into_any();
6798
6799 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6800 let offset = point(
6801 (text_bounds.size.width - size.width) / 2.,
6802 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6803 );
6804
6805 let origin = text_bounds.origin + offset;
6806 element.prepaint_at(origin, window, cx);
6807 Some((element, origin))
6808 } else {
6809 self.render_edit_prediction_end_of_line_popover(
6810 "Jump to Edit",
6811 editor_snapshot,
6812 visible_row_range,
6813 target_display_point,
6814 line_height,
6815 scroll_pixel_position,
6816 content_origin,
6817 editor_width,
6818 window,
6819 cx,
6820 )
6821 }
6822 }
6823
6824 fn render_edit_prediction_end_of_line_popover(
6825 self: &mut Editor,
6826 label: &'static str,
6827 editor_snapshot: &EditorSnapshot,
6828 visible_row_range: Range<DisplayRow>,
6829 target_display_point: DisplayPoint,
6830 line_height: Pixels,
6831 scroll_pixel_position: gpui::Point<Pixels>,
6832 content_origin: gpui::Point<Pixels>,
6833 editor_width: Pixels,
6834 window: &mut Window,
6835 cx: &mut App,
6836 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6837 let target_line_end = DisplayPoint::new(
6838 target_display_point.row(),
6839 editor_snapshot.line_len(target_display_point.row()),
6840 );
6841
6842 let mut element = self
6843 .render_edit_prediction_line_popover(label, None, window, cx)?
6844 .into_any();
6845
6846 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6847
6848 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6849
6850 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6851 let mut origin = start_point
6852 + line_origin
6853 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6854 origin.x = origin.x.max(content_origin.x);
6855
6856 let max_x = content_origin.x + editor_width - size.width;
6857
6858 if origin.x > max_x {
6859 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6860
6861 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6862 origin.y += offset;
6863 IconName::ArrowUp
6864 } else {
6865 origin.y -= offset;
6866 IconName::ArrowDown
6867 };
6868
6869 element = self
6870 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6871 .into_any();
6872
6873 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6874
6875 origin.x = content_origin.x + editor_width - size.width - px(2.);
6876 }
6877
6878 element.prepaint_at(origin, window, cx);
6879 Some((element, origin))
6880 }
6881
6882 fn render_edit_prediction_diff_popover(
6883 self: &Editor,
6884 text_bounds: &Bounds<Pixels>,
6885 content_origin: gpui::Point<Pixels>,
6886 editor_snapshot: &EditorSnapshot,
6887 visible_row_range: Range<DisplayRow>,
6888 line_layouts: &[LineWithInvisibles],
6889 line_height: Pixels,
6890 scroll_pixel_position: gpui::Point<Pixels>,
6891 newest_selection_head: Option<DisplayPoint>,
6892 editor_width: Pixels,
6893 style: &EditorStyle,
6894 edits: &Vec<(Range<Anchor>, String)>,
6895 edit_preview: &Option<language::EditPreview>,
6896 snapshot: &language::BufferSnapshot,
6897 window: &mut Window,
6898 cx: &mut App,
6899 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6900 let edit_start = edits
6901 .first()
6902 .unwrap()
6903 .0
6904 .start
6905 .to_display_point(editor_snapshot);
6906 let edit_end = edits
6907 .last()
6908 .unwrap()
6909 .0
6910 .end
6911 .to_display_point(editor_snapshot);
6912
6913 let is_visible = visible_row_range.contains(&edit_start.row())
6914 || visible_row_range.contains(&edit_end.row());
6915 if !is_visible {
6916 return None;
6917 }
6918
6919 let highlighted_edits =
6920 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6921
6922 let styled_text = highlighted_edits.to_styled_text(&style.text);
6923 let line_count = highlighted_edits.text.lines().count();
6924
6925 const BORDER_WIDTH: Pixels = px(1.);
6926
6927 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6928 let has_keybind = keybind.is_some();
6929
6930 let mut element = h_flex()
6931 .items_start()
6932 .child(
6933 h_flex()
6934 .bg(cx.theme().colors().editor_background)
6935 .border(BORDER_WIDTH)
6936 .shadow_sm()
6937 .border_color(cx.theme().colors().border)
6938 .rounded_l_lg()
6939 .when(line_count > 1, |el| el.rounded_br_lg())
6940 .pr_1()
6941 .child(styled_text),
6942 )
6943 .child(
6944 h_flex()
6945 .h(line_height + BORDER_WIDTH * 2.)
6946 .px_1p5()
6947 .gap_1()
6948 // Workaround: For some reason, there's a gap if we don't do this
6949 .ml(-BORDER_WIDTH)
6950 .shadow(smallvec![gpui::BoxShadow {
6951 color: gpui::black().opacity(0.05),
6952 offset: point(px(1.), px(1.)),
6953 blur_radius: px(2.),
6954 spread_radius: px(0.),
6955 }])
6956 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6957 .border(BORDER_WIDTH)
6958 .border_color(cx.theme().colors().border)
6959 .rounded_r_lg()
6960 .id("edit_prediction_diff_popover_keybind")
6961 .when(!has_keybind, |el| {
6962 let status_colors = cx.theme().status();
6963
6964 el.bg(status_colors.error_background)
6965 .border_color(status_colors.error.opacity(0.6))
6966 .child(Icon::new(IconName::Info).color(Color::Error))
6967 .cursor_default()
6968 .hoverable_tooltip(move |_window, cx| {
6969 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6970 })
6971 })
6972 .children(keybind),
6973 )
6974 .into_any();
6975
6976 let longest_row =
6977 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6978 let longest_line_width = if visible_row_range.contains(&longest_row) {
6979 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6980 } else {
6981 layout_line(
6982 longest_row,
6983 editor_snapshot,
6984 style,
6985 editor_width,
6986 |_| false,
6987 window,
6988 cx,
6989 )
6990 .width
6991 };
6992
6993 let viewport_bounds =
6994 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6995 right: -EditorElement::SCROLLBAR_WIDTH,
6996 ..Default::default()
6997 });
6998
6999 let x_after_longest =
7000 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
7001 - scroll_pixel_position.x;
7002
7003 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
7004
7005 // Fully visible if it can be displayed within the window (allow overlapping other
7006 // panes). However, this is only allowed if the popover starts within text_bounds.
7007 let can_position_to_the_right = x_after_longest < text_bounds.right()
7008 && x_after_longest + element_bounds.width < viewport_bounds.right();
7009
7010 let mut origin = if can_position_to_the_right {
7011 point(
7012 x_after_longest,
7013 text_bounds.origin.y + edit_start.row().as_f32() * line_height
7014 - scroll_pixel_position.y,
7015 )
7016 } else {
7017 let cursor_row = newest_selection_head.map(|head| head.row());
7018 let above_edit = edit_start
7019 .row()
7020 .0
7021 .checked_sub(line_count as u32)
7022 .map(DisplayRow);
7023 let below_edit = Some(edit_end.row() + 1);
7024 let above_cursor =
7025 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
7026 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
7027
7028 // Place the edit popover adjacent to the edit if there is a location
7029 // available that is onscreen and does not obscure the cursor. Otherwise,
7030 // place it adjacent to the cursor.
7031 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
7032 .into_iter()
7033 .flatten()
7034 .find(|&start_row| {
7035 let end_row = start_row + line_count as u32;
7036 visible_row_range.contains(&start_row)
7037 && visible_row_range.contains(&end_row)
7038 && cursor_row.map_or(true, |cursor_row| {
7039 !((start_row..end_row).contains(&cursor_row))
7040 })
7041 })?;
7042
7043 content_origin
7044 + point(
7045 -scroll_pixel_position.x,
7046 row_target.as_f32() * line_height - scroll_pixel_position.y,
7047 )
7048 };
7049
7050 origin.x -= BORDER_WIDTH;
7051
7052 window.defer_draw(element, origin, 1);
7053
7054 // Do not return an element, since it will already be drawn due to defer_draw.
7055 None
7056 }
7057
7058 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
7059 px(30.)
7060 }
7061
7062 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
7063 if self.read_only(cx) {
7064 cx.theme().players().read_only()
7065 } else {
7066 self.style.as_ref().unwrap().local_player
7067 }
7068 }
7069
7070 fn render_edit_prediction_accept_keybind(
7071 &self,
7072 window: &mut Window,
7073 cx: &App,
7074 ) -> Option<AnyElement> {
7075 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
7076 let accept_keystroke = accept_binding.keystroke()?;
7077
7078 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7079
7080 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
7081 Color::Accent
7082 } else {
7083 Color::Muted
7084 };
7085
7086 h_flex()
7087 .px_0p5()
7088 .when(is_platform_style_mac, |parent| parent.gap_0p5())
7089 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7090 .text_size(TextSize::XSmall.rems(cx))
7091 .child(h_flex().children(ui::render_modifiers(
7092 &accept_keystroke.modifiers,
7093 PlatformStyle::platform(),
7094 Some(modifiers_color),
7095 Some(IconSize::XSmall.rems().into()),
7096 true,
7097 )))
7098 .when(is_platform_style_mac, |parent| {
7099 parent.child(accept_keystroke.key.clone())
7100 })
7101 .when(!is_platform_style_mac, |parent| {
7102 parent.child(
7103 Key::new(
7104 util::capitalize(&accept_keystroke.key),
7105 Some(Color::Default),
7106 )
7107 .size(Some(IconSize::XSmall.rems().into())),
7108 )
7109 })
7110 .into_any()
7111 .into()
7112 }
7113
7114 fn render_edit_prediction_line_popover(
7115 &self,
7116 label: impl Into<SharedString>,
7117 icon: Option<IconName>,
7118 window: &mut Window,
7119 cx: &App,
7120 ) -> Option<Stateful<Div>> {
7121 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7122
7123 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7124 let has_keybind = keybind.is_some();
7125
7126 let result = h_flex()
7127 .id("ep-line-popover")
7128 .py_0p5()
7129 .pl_1()
7130 .pr(padding_right)
7131 .gap_1()
7132 .rounded_md()
7133 .border_1()
7134 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7135 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7136 .shadow_sm()
7137 .when(!has_keybind, |el| {
7138 let status_colors = cx.theme().status();
7139
7140 el.bg(status_colors.error_background)
7141 .border_color(status_colors.error.opacity(0.6))
7142 .pl_2()
7143 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7144 .cursor_default()
7145 .hoverable_tooltip(move |_window, cx| {
7146 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7147 })
7148 })
7149 .children(keybind)
7150 .child(
7151 Label::new(label)
7152 .size(LabelSize::Small)
7153 .when(!has_keybind, |el| {
7154 el.color(cx.theme().status().error.into()).strikethrough()
7155 }),
7156 )
7157 .when(!has_keybind, |el| {
7158 el.child(
7159 h_flex().ml_1().child(
7160 Icon::new(IconName::Info)
7161 .size(IconSize::Small)
7162 .color(cx.theme().status().error.into()),
7163 ),
7164 )
7165 })
7166 .when_some(icon, |element, icon| {
7167 element.child(
7168 div()
7169 .mt(px(1.5))
7170 .child(Icon::new(icon).size(IconSize::Small)),
7171 )
7172 });
7173
7174 Some(result)
7175 }
7176
7177 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7178 let accent_color = cx.theme().colors().text_accent;
7179 let editor_bg_color = cx.theme().colors().editor_background;
7180 editor_bg_color.blend(accent_color.opacity(0.1))
7181 }
7182
7183 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7184 let accent_color = cx.theme().colors().text_accent;
7185 let editor_bg_color = cx.theme().colors().editor_background;
7186 editor_bg_color.blend(accent_color.opacity(0.6))
7187 }
7188
7189 fn render_edit_prediction_cursor_popover(
7190 &self,
7191 min_width: Pixels,
7192 max_width: Pixels,
7193 cursor_point: Point,
7194 style: &EditorStyle,
7195 accept_keystroke: Option<&gpui::Keystroke>,
7196 _window: &Window,
7197 cx: &mut Context<Editor>,
7198 ) -> Option<AnyElement> {
7199 let provider = self.edit_prediction_provider.as_ref()?;
7200
7201 if provider.provider.needs_terms_acceptance(cx) {
7202 return Some(
7203 h_flex()
7204 .min_w(min_width)
7205 .flex_1()
7206 .px_2()
7207 .py_1()
7208 .gap_3()
7209 .elevation_2(cx)
7210 .hover(|style| style.bg(cx.theme().colors().element_hover))
7211 .id("accept-terms")
7212 .cursor_pointer()
7213 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7214 .on_click(cx.listener(|this, _event, window, cx| {
7215 cx.stop_propagation();
7216 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7217 window.dispatch_action(
7218 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7219 cx,
7220 );
7221 }))
7222 .child(
7223 h_flex()
7224 .flex_1()
7225 .gap_2()
7226 .child(Icon::new(IconName::ZedPredict))
7227 .child(Label::new("Accept Terms of Service"))
7228 .child(div().w_full())
7229 .child(
7230 Icon::new(IconName::ArrowUpRight)
7231 .color(Color::Muted)
7232 .size(IconSize::Small),
7233 )
7234 .into_any_element(),
7235 )
7236 .into_any(),
7237 );
7238 }
7239
7240 let is_refreshing = provider.provider.is_refreshing(cx);
7241
7242 fn pending_completion_container() -> Div {
7243 h_flex()
7244 .h_full()
7245 .flex_1()
7246 .gap_2()
7247 .child(Icon::new(IconName::ZedPredict))
7248 }
7249
7250 let completion = match &self.active_inline_completion {
7251 Some(prediction) => {
7252 if !self.has_visible_completions_menu() {
7253 const RADIUS: Pixels = px(6.);
7254 const BORDER_WIDTH: Pixels = px(1.);
7255
7256 return Some(
7257 h_flex()
7258 .elevation_2(cx)
7259 .border(BORDER_WIDTH)
7260 .border_color(cx.theme().colors().border)
7261 .when(accept_keystroke.is_none(), |el| {
7262 el.border_color(cx.theme().status().error)
7263 })
7264 .rounded(RADIUS)
7265 .rounded_tl(px(0.))
7266 .overflow_hidden()
7267 .child(div().px_1p5().child(match &prediction.completion {
7268 InlineCompletion::Move { target, snapshot } => {
7269 use text::ToPoint as _;
7270 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7271 {
7272 Icon::new(IconName::ZedPredictDown)
7273 } else {
7274 Icon::new(IconName::ZedPredictUp)
7275 }
7276 }
7277 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7278 }))
7279 .child(
7280 h_flex()
7281 .gap_1()
7282 .py_1()
7283 .px_2()
7284 .rounded_r(RADIUS - BORDER_WIDTH)
7285 .border_l_1()
7286 .border_color(cx.theme().colors().border)
7287 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7288 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7289 el.child(
7290 Label::new("Hold")
7291 .size(LabelSize::Small)
7292 .when(accept_keystroke.is_none(), |el| {
7293 el.strikethrough()
7294 })
7295 .line_height_style(LineHeightStyle::UiLabel),
7296 )
7297 })
7298 .id("edit_prediction_cursor_popover_keybind")
7299 .when(accept_keystroke.is_none(), |el| {
7300 let status_colors = cx.theme().status();
7301
7302 el.bg(status_colors.error_background)
7303 .border_color(status_colors.error.opacity(0.6))
7304 .child(Icon::new(IconName::Info).color(Color::Error))
7305 .cursor_default()
7306 .hoverable_tooltip(move |_window, cx| {
7307 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7308 .into()
7309 })
7310 })
7311 .when_some(
7312 accept_keystroke.as_ref(),
7313 |el, accept_keystroke| {
7314 el.child(h_flex().children(ui::render_modifiers(
7315 &accept_keystroke.modifiers,
7316 PlatformStyle::platform(),
7317 Some(Color::Default),
7318 Some(IconSize::XSmall.rems().into()),
7319 false,
7320 )))
7321 },
7322 ),
7323 )
7324 .into_any(),
7325 );
7326 }
7327
7328 self.render_edit_prediction_cursor_popover_preview(
7329 prediction,
7330 cursor_point,
7331 style,
7332 cx,
7333 )?
7334 }
7335
7336 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7337 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7338 stale_completion,
7339 cursor_point,
7340 style,
7341 cx,
7342 )?,
7343
7344 None => {
7345 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7346 }
7347 },
7348
7349 None => pending_completion_container().child(Label::new("No Prediction")),
7350 };
7351
7352 let completion = if is_refreshing {
7353 completion
7354 .with_animation(
7355 "loading-completion",
7356 Animation::new(Duration::from_secs(2))
7357 .repeat()
7358 .with_easing(pulsating_between(0.4, 0.8)),
7359 |label, delta| label.opacity(delta),
7360 )
7361 .into_any_element()
7362 } else {
7363 completion.into_any_element()
7364 };
7365
7366 let has_completion = self.active_inline_completion.is_some();
7367
7368 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7369 Some(
7370 h_flex()
7371 .min_w(min_width)
7372 .max_w(max_width)
7373 .flex_1()
7374 .elevation_2(cx)
7375 .border_color(cx.theme().colors().border)
7376 .child(
7377 div()
7378 .flex_1()
7379 .py_1()
7380 .px_2()
7381 .overflow_hidden()
7382 .child(completion),
7383 )
7384 .when_some(accept_keystroke, |el, accept_keystroke| {
7385 if !accept_keystroke.modifiers.modified() {
7386 return el;
7387 }
7388
7389 el.child(
7390 h_flex()
7391 .h_full()
7392 .border_l_1()
7393 .rounded_r_lg()
7394 .border_color(cx.theme().colors().border)
7395 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7396 .gap_1()
7397 .py_1()
7398 .px_2()
7399 .child(
7400 h_flex()
7401 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7402 .when(is_platform_style_mac, |parent| parent.gap_1())
7403 .child(h_flex().children(ui::render_modifiers(
7404 &accept_keystroke.modifiers,
7405 PlatformStyle::platform(),
7406 Some(if !has_completion {
7407 Color::Muted
7408 } else {
7409 Color::Default
7410 }),
7411 None,
7412 false,
7413 ))),
7414 )
7415 .child(Label::new("Preview").into_any_element())
7416 .opacity(if has_completion { 1.0 } else { 0.4 }),
7417 )
7418 })
7419 .into_any(),
7420 )
7421 }
7422
7423 fn render_edit_prediction_cursor_popover_preview(
7424 &self,
7425 completion: &InlineCompletionState,
7426 cursor_point: Point,
7427 style: &EditorStyle,
7428 cx: &mut Context<Editor>,
7429 ) -> Option<Div> {
7430 use text::ToPoint as _;
7431
7432 fn render_relative_row_jump(
7433 prefix: impl Into<String>,
7434 current_row: u32,
7435 target_row: u32,
7436 ) -> Div {
7437 let (row_diff, arrow) = if target_row < current_row {
7438 (current_row - target_row, IconName::ArrowUp)
7439 } else {
7440 (target_row - current_row, IconName::ArrowDown)
7441 };
7442
7443 h_flex()
7444 .child(
7445 Label::new(format!("{}{}", prefix.into(), row_diff))
7446 .color(Color::Muted)
7447 .size(LabelSize::Small),
7448 )
7449 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7450 }
7451
7452 match &completion.completion {
7453 InlineCompletion::Move {
7454 target, snapshot, ..
7455 } => Some(
7456 h_flex()
7457 .px_2()
7458 .gap_2()
7459 .flex_1()
7460 .child(
7461 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7462 Icon::new(IconName::ZedPredictDown)
7463 } else {
7464 Icon::new(IconName::ZedPredictUp)
7465 },
7466 )
7467 .child(Label::new("Jump to Edit")),
7468 ),
7469
7470 InlineCompletion::Edit {
7471 edits,
7472 edit_preview,
7473 snapshot,
7474 display_mode: _,
7475 } => {
7476 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7477
7478 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7479 &snapshot,
7480 &edits,
7481 edit_preview.as_ref()?,
7482 true,
7483 cx,
7484 )
7485 .first_line_preview();
7486
7487 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7488 .with_default_highlights(&style.text, highlighted_edits.highlights);
7489
7490 let preview = h_flex()
7491 .gap_1()
7492 .min_w_16()
7493 .child(styled_text)
7494 .when(has_more_lines, |parent| parent.child("…"));
7495
7496 let left = if first_edit_row != cursor_point.row {
7497 render_relative_row_jump("", cursor_point.row, first_edit_row)
7498 .into_any_element()
7499 } else {
7500 Icon::new(IconName::ZedPredict).into_any_element()
7501 };
7502
7503 Some(
7504 h_flex()
7505 .h_full()
7506 .flex_1()
7507 .gap_2()
7508 .pr_1()
7509 .overflow_x_hidden()
7510 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7511 .child(left)
7512 .child(preview),
7513 )
7514 }
7515 }
7516 }
7517
7518 fn render_context_menu(
7519 &self,
7520 style: &EditorStyle,
7521 max_height_in_lines: u32,
7522 y_flipped: bool,
7523 window: &mut Window,
7524 cx: &mut Context<Editor>,
7525 ) -> Option<AnyElement> {
7526 let menu = self.context_menu.borrow();
7527 let menu = menu.as_ref()?;
7528 if !menu.visible() {
7529 return None;
7530 };
7531 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7532 }
7533
7534 fn render_context_menu_aside(
7535 &mut self,
7536 max_size: Size<Pixels>,
7537 window: &mut Window,
7538 cx: &mut Context<Editor>,
7539 ) -> Option<AnyElement> {
7540 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7541 if menu.visible() {
7542 menu.render_aside(self, max_size, window, cx)
7543 } else {
7544 None
7545 }
7546 })
7547 }
7548
7549 fn hide_context_menu(
7550 &mut self,
7551 window: &mut Window,
7552 cx: &mut Context<Self>,
7553 ) -> Option<CodeContextMenu> {
7554 cx.notify();
7555 self.completion_tasks.clear();
7556 let context_menu = self.context_menu.borrow_mut().take();
7557 self.stale_inline_completion_in_menu.take();
7558 self.update_visible_inline_completion(window, cx);
7559 context_menu
7560 }
7561
7562 fn show_snippet_choices(
7563 &mut self,
7564 choices: &Vec<String>,
7565 selection: Range<Anchor>,
7566 cx: &mut Context<Self>,
7567 ) {
7568 if selection.start.buffer_id.is_none() {
7569 return;
7570 }
7571 let buffer_id = selection.start.buffer_id.unwrap();
7572 let buffer = self.buffer().read(cx).buffer(buffer_id);
7573 let id = post_inc(&mut self.next_completion_id);
7574
7575 if let Some(buffer) = buffer {
7576 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7577 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7578 ));
7579 }
7580 }
7581
7582 pub fn insert_snippet(
7583 &mut self,
7584 insertion_ranges: &[Range<usize>],
7585 snippet: Snippet,
7586 window: &mut Window,
7587 cx: &mut Context<Self>,
7588 ) -> Result<()> {
7589 struct Tabstop<T> {
7590 is_end_tabstop: bool,
7591 ranges: Vec<Range<T>>,
7592 choices: Option<Vec<String>>,
7593 }
7594
7595 let tabstops = self.buffer.update(cx, |buffer, cx| {
7596 let snippet_text: Arc<str> = snippet.text.clone().into();
7597 let edits = insertion_ranges
7598 .iter()
7599 .cloned()
7600 .map(|range| (range, snippet_text.clone()));
7601 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
7602
7603 let snapshot = &*buffer.read(cx);
7604 let snippet = &snippet;
7605 snippet
7606 .tabstops
7607 .iter()
7608 .map(|tabstop| {
7609 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7610 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7611 });
7612 let mut tabstop_ranges = tabstop
7613 .ranges
7614 .iter()
7615 .flat_map(|tabstop_range| {
7616 let mut delta = 0_isize;
7617 insertion_ranges.iter().map(move |insertion_range| {
7618 let insertion_start = insertion_range.start as isize + delta;
7619 delta +=
7620 snippet.text.len() as isize - insertion_range.len() as isize;
7621
7622 let start = ((insertion_start + tabstop_range.start) as usize)
7623 .min(snapshot.len());
7624 let end = ((insertion_start + tabstop_range.end) as usize)
7625 .min(snapshot.len());
7626 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7627 })
7628 })
7629 .collect::<Vec<_>>();
7630 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7631
7632 Tabstop {
7633 is_end_tabstop,
7634 ranges: tabstop_ranges,
7635 choices: tabstop.choices.clone(),
7636 }
7637 })
7638 .collect::<Vec<_>>()
7639 });
7640 if let Some(tabstop) = tabstops.first() {
7641 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7642 s.select_ranges(tabstop.ranges.iter().cloned());
7643 });
7644
7645 if let Some(choices) = &tabstop.choices {
7646 if let Some(selection) = tabstop.ranges.first() {
7647 self.show_snippet_choices(choices, selection.clone(), cx)
7648 }
7649 }
7650
7651 // If we're already at the last tabstop and it's at the end of the snippet,
7652 // we're done, we don't need to keep the state around.
7653 if !tabstop.is_end_tabstop {
7654 let choices = tabstops
7655 .iter()
7656 .map(|tabstop| tabstop.choices.clone())
7657 .collect();
7658
7659 let ranges = tabstops
7660 .into_iter()
7661 .map(|tabstop| tabstop.ranges)
7662 .collect::<Vec<_>>();
7663
7664 self.snippet_stack.push(SnippetState {
7665 active_index: 0,
7666 ranges,
7667 choices,
7668 });
7669 }
7670
7671 // Check whether the just-entered snippet ends with an auto-closable bracket.
7672 if self.autoclose_regions.is_empty() {
7673 let snapshot = self.buffer.read(cx).snapshot(cx);
7674 for selection in &mut self.selections.all::<Point>(cx) {
7675 let selection_head = selection.head();
7676 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7677 continue;
7678 };
7679
7680 let mut bracket_pair = None;
7681 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7682 let prev_chars = snapshot
7683 .reversed_chars_at(selection_head)
7684 .collect::<String>();
7685 for (pair, enabled) in scope.brackets() {
7686 if enabled
7687 && pair.close
7688 && prev_chars.starts_with(pair.start.as_str())
7689 && next_chars.starts_with(pair.end.as_str())
7690 {
7691 bracket_pair = Some(pair.clone());
7692 break;
7693 }
7694 }
7695 if let Some(pair) = bracket_pair {
7696 let start = snapshot.anchor_after(selection_head);
7697 let end = snapshot.anchor_after(selection_head);
7698 self.autoclose_regions.push(AutocloseRegion {
7699 selection_id: selection.id,
7700 range: start..end,
7701 pair,
7702 });
7703 }
7704 }
7705 }
7706 }
7707 Ok(())
7708 }
7709
7710 pub fn move_to_next_snippet_tabstop(
7711 &mut self,
7712 window: &mut Window,
7713 cx: &mut Context<Self>,
7714 ) -> bool {
7715 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7716 }
7717
7718 pub fn move_to_prev_snippet_tabstop(
7719 &mut self,
7720 window: &mut Window,
7721 cx: &mut Context<Self>,
7722 ) -> bool {
7723 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7724 }
7725
7726 pub fn move_to_snippet_tabstop(
7727 &mut self,
7728 bias: Bias,
7729 window: &mut Window,
7730 cx: &mut Context<Self>,
7731 ) -> bool {
7732 if let Some(mut snippet) = self.snippet_stack.pop() {
7733 match bias {
7734 Bias::Left => {
7735 if snippet.active_index > 0 {
7736 snippet.active_index -= 1;
7737 } else {
7738 self.snippet_stack.push(snippet);
7739 return false;
7740 }
7741 }
7742 Bias::Right => {
7743 if snippet.active_index + 1 < snippet.ranges.len() {
7744 snippet.active_index += 1;
7745 } else {
7746 self.snippet_stack.push(snippet);
7747 return false;
7748 }
7749 }
7750 }
7751 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7752 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7753 s.select_anchor_ranges(current_ranges.iter().cloned())
7754 });
7755
7756 if let Some(choices) = &snippet.choices[snippet.active_index] {
7757 if let Some(selection) = current_ranges.first() {
7758 self.show_snippet_choices(&choices, selection.clone(), cx);
7759 }
7760 }
7761
7762 // If snippet state is not at the last tabstop, push it back on the stack
7763 if snippet.active_index + 1 < snippet.ranges.len() {
7764 self.snippet_stack.push(snippet);
7765 }
7766 return true;
7767 }
7768 }
7769
7770 false
7771 }
7772
7773 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7774 self.transact(window, cx, |this, window, cx| {
7775 this.select_all(&SelectAll, window, cx);
7776 this.insert("", window, cx);
7777 });
7778 }
7779
7780 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7781 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7782 self.transact(window, cx, |this, window, cx| {
7783 this.select_autoclose_pair(window, cx);
7784 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7785 if !this.linked_edit_ranges.is_empty() {
7786 let selections = this.selections.all::<MultiBufferPoint>(cx);
7787 let snapshot = this.buffer.read(cx).snapshot(cx);
7788
7789 for selection in selections.iter() {
7790 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7791 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7792 if selection_start.buffer_id != selection_end.buffer_id {
7793 continue;
7794 }
7795 if let Some(ranges) =
7796 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7797 {
7798 for (buffer, entries) in ranges {
7799 linked_ranges.entry(buffer).or_default().extend(entries);
7800 }
7801 }
7802 }
7803 }
7804
7805 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7806 if !this.selections.line_mode {
7807 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7808 for selection in &mut selections {
7809 if selection.is_empty() {
7810 let old_head = selection.head();
7811 let mut new_head =
7812 movement::left(&display_map, old_head.to_display_point(&display_map))
7813 .to_point(&display_map);
7814 if let Some((buffer, line_buffer_range)) = display_map
7815 .buffer_snapshot
7816 .buffer_line_for_row(MultiBufferRow(old_head.row))
7817 {
7818 let indent_size =
7819 buffer.indent_size_for_line(line_buffer_range.start.row);
7820 let indent_len = match indent_size.kind {
7821 IndentKind::Space => {
7822 buffer.settings_at(line_buffer_range.start, cx).tab_size
7823 }
7824 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7825 };
7826 if old_head.column <= indent_size.len && old_head.column > 0 {
7827 let indent_len = indent_len.get();
7828 new_head = cmp::min(
7829 new_head,
7830 MultiBufferPoint::new(
7831 old_head.row,
7832 ((old_head.column - 1) / indent_len) * indent_len,
7833 ),
7834 );
7835 }
7836 }
7837
7838 selection.set_head(new_head, SelectionGoal::None);
7839 }
7840 }
7841 }
7842
7843 this.signature_help_state.set_backspace_pressed(true);
7844 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7845 s.select(selections)
7846 });
7847 this.insert("", window, cx);
7848 let empty_str: Arc<str> = Arc::from("");
7849 for (buffer, edits) in linked_ranges {
7850 let snapshot = buffer.read(cx).snapshot();
7851 use text::ToPoint as TP;
7852
7853 let edits = edits
7854 .into_iter()
7855 .map(|range| {
7856 let end_point = TP::to_point(&range.end, &snapshot);
7857 let mut start_point = TP::to_point(&range.start, &snapshot);
7858
7859 if end_point == start_point {
7860 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7861 .saturating_sub(1);
7862 start_point =
7863 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7864 };
7865
7866 (start_point..end_point, empty_str.clone())
7867 })
7868 .sorted_by_key(|(range, _)| range.start)
7869 .collect::<Vec<_>>();
7870 buffer.update(cx, |this, cx| {
7871 this.edit(edits, None, cx);
7872 })
7873 }
7874 this.refresh_inline_completion(true, false, window, cx);
7875 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7876 });
7877 }
7878
7879 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7880 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7881 self.transact(window, cx, |this, window, cx| {
7882 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7883 let line_mode = s.line_mode;
7884 s.move_with(|map, selection| {
7885 if selection.is_empty() && !line_mode {
7886 let cursor = movement::right(map, selection.head());
7887 selection.end = cursor;
7888 selection.reversed = true;
7889 selection.goal = SelectionGoal::None;
7890 }
7891 })
7892 });
7893 this.insert("", window, cx);
7894 this.refresh_inline_completion(true, false, window, cx);
7895 });
7896 }
7897
7898 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7899 if self.move_to_prev_snippet_tabstop(window, cx) {
7900 return;
7901 }
7902 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7903 self.outdent(&Outdent, window, cx);
7904 }
7905
7906 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7907 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7908 return;
7909 }
7910 self.mouse_cursor_hidden = self.hide_mouse_while_typing;
7911 let mut selections = self.selections.all_adjusted(cx);
7912 let buffer = self.buffer.read(cx);
7913 let snapshot = buffer.snapshot(cx);
7914 let rows_iter = selections.iter().map(|s| s.head().row);
7915 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7916
7917 let mut edits = Vec::new();
7918 let mut prev_edited_row = 0;
7919 let mut row_delta = 0;
7920 for selection in &mut selections {
7921 if selection.start.row != prev_edited_row {
7922 row_delta = 0;
7923 }
7924 prev_edited_row = selection.end.row;
7925
7926 // If the selection is non-empty, then increase the indentation of the selected lines.
7927 if !selection.is_empty() {
7928 row_delta =
7929 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7930 continue;
7931 }
7932
7933 // If the selection is empty and the cursor is in the leading whitespace before the
7934 // suggested indentation, then auto-indent the line.
7935 let cursor = selection.head();
7936 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7937 if let Some(suggested_indent) =
7938 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7939 {
7940 if cursor.column < suggested_indent.len
7941 && cursor.column <= current_indent.len
7942 && current_indent.len <= suggested_indent.len
7943 {
7944 selection.start = Point::new(cursor.row, suggested_indent.len);
7945 selection.end = selection.start;
7946 if row_delta == 0 {
7947 edits.extend(Buffer::edit_for_indent_size_adjustment(
7948 cursor.row,
7949 current_indent,
7950 suggested_indent,
7951 ));
7952 row_delta = suggested_indent.len - current_indent.len;
7953 }
7954 continue;
7955 }
7956 }
7957
7958 // Otherwise, insert a hard or soft tab.
7959 let settings = buffer.language_settings_at(cursor, cx);
7960 let tab_size = if settings.hard_tabs {
7961 IndentSize::tab()
7962 } else {
7963 let tab_size = settings.tab_size.get();
7964 let char_column = snapshot
7965 .text_for_range(Point::new(cursor.row, 0)..cursor)
7966 .flat_map(str::chars)
7967 .count()
7968 + row_delta as usize;
7969 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7970 IndentSize::spaces(chars_to_next_tab_stop)
7971 };
7972 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7973 selection.end = selection.start;
7974 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7975 row_delta += tab_size.len;
7976 }
7977
7978 self.transact(window, cx, |this, window, cx| {
7979 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7980 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7981 s.select(selections)
7982 });
7983 this.refresh_inline_completion(true, false, window, cx);
7984 });
7985 }
7986
7987 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7988 if self.read_only(cx) {
7989 return;
7990 }
7991 let mut selections = self.selections.all::<Point>(cx);
7992 let mut prev_edited_row = 0;
7993 let mut row_delta = 0;
7994 let mut edits = Vec::new();
7995 let buffer = self.buffer.read(cx);
7996 let snapshot = buffer.snapshot(cx);
7997 for selection in &mut selections {
7998 if selection.start.row != prev_edited_row {
7999 row_delta = 0;
8000 }
8001 prev_edited_row = selection.end.row;
8002
8003 row_delta =
8004 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
8005 }
8006
8007 self.transact(window, cx, |this, window, cx| {
8008 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
8009 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8010 s.select(selections)
8011 });
8012 });
8013 }
8014
8015 fn indent_selection(
8016 buffer: &MultiBuffer,
8017 snapshot: &MultiBufferSnapshot,
8018 selection: &mut Selection<Point>,
8019 edits: &mut Vec<(Range<Point>, String)>,
8020 delta_for_start_row: u32,
8021 cx: &App,
8022 ) -> u32 {
8023 let settings = buffer.language_settings_at(selection.start, cx);
8024 let tab_size = settings.tab_size.get();
8025 let indent_kind = if settings.hard_tabs {
8026 IndentKind::Tab
8027 } else {
8028 IndentKind::Space
8029 };
8030 let mut start_row = selection.start.row;
8031 let mut end_row = selection.end.row + 1;
8032
8033 // If a selection ends at the beginning of a line, don't indent
8034 // that last line.
8035 if selection.end.column == 0 && selection.end.row > selection.start.row {
8036 end_row -= 1;
8037 }
8038
8039 // Avoid re-indenting a row that has already been indented by a
8040 // previous selection, but still update this selection's column
8041 // to reflect that indentation.
8042 if delta_for_start_row > 0 {
8043 start_row += 1;
8044 selection.start.column += delta_for_start_row;
8045 if selection.end.row == selection.start.row {
8046 selection.end.column += delta_for_start_row;
8047 }
8048 }
8049
8050 let mut delta_for_end_row = 0;
8051 let has_multiple_rows = start_row + 1 != end_row;
8052 for row in start_row..end_row {
8053 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
8054 let indent_delta = match (current_indent.kind, indent_kind) {
8055 (IndentKind::Space, IndentKind::Space) => {
8056 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
8057 IndentSize::spaces(columns_to_next_tab_stop)
8058 }
8059 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
8060 (_, IndentKind::Tab) => IndentSize::tab(),
8061 };
8062
8063 let start = if has_multiple_rows || current_indent.len < selection.start.column {
8064 0
8065 } else {
8066 selection.start.column
8067 };
8068 let row_start = Point::new(row, start);
8069 edits.push((
8070 row_start..row_start,
8071 indent_delta.chars().collect::<String>(),
8072 ));
8073
8074 // Update this selection's endpoints to reflect the indentation.
8075 if row == selection.start.row {
8076 selection.start.column += indent_delta.len;
8077 }
8078 if row == selection.end.row {
8079 selection.end.column += indent_delta.len;
8080 delta_for_end_row = indent_delta.len;
8081 }
8082 }
8083
8084 if selection.start.row == selection.end.row {
8085 delta_for_start_row + delta_for_end_row
8086 } else {
8087 delta_for_end_row
8088 }
8089 }
8090
8091 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
8092 if self.read_only(cx) {
8093 return;
8094 }
8095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8096 let selections = self.selections.all::<Point>(cx);
8097 let mut deletion_ranges = Vec::new();
8098 let mut last_outdent = None;
8099 {
8100 let buffer = self.buffer.read(cx);
8101 let snapshot = buffer.snapshot(cx);
8102 for selection in &selections {
8103 let settings = buffer.language_settings_at(selection.start, cx);
8104 let tab_size = settings.tab_size.get();
8105 let mut rows = selection.spanned_rows(false, &display_map);
8106
8107 // Avoid re-outdenting a row that has already been outdented by a
8108 // previous selection.
8109 if let Some(last_row) = last_outdent {
8110 if last_row == rows.start {
8111 rows.start = rows.start.next_row();
8112 }
8113 }
8114 let has_multiple_rows = rows.len() > 1;
8115 for row in rows.iter_rows() {
8116 let indent_size = snapshot.indent_size_for_line(row);
8117 if indent_size.len > 0 {
8118 let deletion_len = match indent_size.kind {
8119 IndentKind::Space => {
8120 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8121 if columns_to_prev_tab_stop == 0 {
8122 tab_size
8123 } else {
8124 columns_to_prev_tab_stop
8125 }
8126 }
8127 IndentKind::Tab => 1,
8128 };
8129 let start = if has_multiple_rows
8130 || deletion_len > selection.start.column
8131 || indent_size.len < selection.start.column
8132 {
8133 0
8134 } else {
8135 selection.start.column - deletion_len
8136 };
8137 deletion_ranges.push(
8138 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8139 );
8140 last_outdent = Some(row);
8141 }
8142 }
8143 }
8144 }
8145
8146 self.transact(window, cx, |this, window, cx| {
8147 this.buffer.update(cx, |buffer, cx| {
8148 let empty_str: Arc<str> = Arc::default();
8149 buffer.edit(
8150 deletion_ranges
8151 .into_iter()
8152 .map(|range| (range, empty_str.clone())),
8153 None,
8154 cx,
8155 );
8156 });
8157 let selections = this.selections.all::<usize>(cx);
8158 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8159 s.select(selections)
8160 });
8161 });
8162 }
8163
8164 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8165 if self.read_only(cx) {
8166 return;
8167 }
8168 let selections = self
8169 .selections
8170 .all::<usize>(cx)
8171 .into_iter()
8172 .map(|s| s.range());
8173
8174 self.transact(window, cx, |this, window, cx| {
8175 this.buffer.update(cx, |buffer, cx| {
8176 buffer.autoindent_ranges(selections, cx);
8177 });
8178 let selections = this.selections.all::<usize>(cx);
8179 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8180 s.select(selections)
8181 });
8182 });
8183 }
8184
8185 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8186 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8187 let selections = self.selections.all::<Point>(cx);
8188
8189 let mut new_cursors = Vec::new();
8190 let mut edit_ranges = Vec::new();
8191 let mut selections = selections.iter().peekable();
8192 while let Some(selection) = selections.next() {
8193 let mut rows = selection.spanned_rows(false, &display_map);
8194 let goal_display_column = selection.head().to_display_point(&display_map).column();
8195
8196 // Accumulate contiguous regions of rows that we want to delete.
8197 while let Some(next_selection) = selections.peek() {
8198 let next_rows = next_selection.spanned_rows(false, &display_map);
8199 if next_rows.start <= rows.end {
8200 rows.end = next_rows.end;
8201 selections.next().unwrap();
8202 } else {
8203 break;
8204 }
8205 }
8206
8207 let buffer = &display_map.buffer_snapshot;
8208 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8209 let edit_end;
8210 let cursor_buffer_row;
8211 if buffer.max_point().row >= rows.end.0 {
8212 // If there's a line after the range, delete the \n from the end of the row range
8213 // and position the cursor on the next line.
8214 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8215 cursor_buffer_row = rows.end;
8216 } else {
8217 // If there isn't a line after the range, delete the \n from the line before the
8218 // start of the row range and position the cursor there.
8219 edit_start = edit_start.saturating_sub(1);
8220 edit_end = buffer.len();
8221 cursor_buffer_row = rows.start.previous_row();
8222 }
8223
8224 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8225 *cursor.column_mut() =
8226 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8227
8228 new_cursors.push((
8229 selection.id,
8230 buffer.anchor_after(cursor.to_point(&display_map)),
8231 ));
8232 edit_ranges.push(edit_start..edit_end);
8233 }
8234
8235 self.transact(window, cx, |this, window, cx| {
8236 let buffer = this.buffer.update(cx, |buffer, cx| {
8237 let empty_str: Arc<str> = Arc::default();
8238 buffer.edit(
8239 edit_ranges
8240 .into_iter()
8241 .map(|range| (range, empty_str.clone())),
8242 None,
8243 cx,
8244 );
8245 buffer.snapshot(cx)
8246 });
8247 let new_selections = new_cursors
8248 .into_iter()
8249 .map(|(id, cursor)| {
8250 let cursor = cursor.to_point(&buffer);
8251 Selection {
8252 id,
8253 start: cursor,
8254 end: cursor,
8255 reversed: false,
8256 goal: SelectionGoal::None,
8257 }
8258 })
8259 .collect();
8260
8261 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8262 s.select(new_selections);
8263 });
8264 });
8265 }
8266
8267 pub fn join_lines_impl(
8268 &mut self,
8269 insert_whitespace: bool,
8270 window: &mut Window,
8271 cx: &mut Context<Self>,
8272 ) {
8273 if self.read_only(cx) {
8274 return;
8275 }
8276 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8277 for selection in self.selections.all::<Point>(cx) {
8278 let start = MultiBufferRow(selection.start.row);
8279 // Treat single line selections as if they include the next line. Otherwise this action
8280 // would do nothing for single line selections individual cursors.
8281 let end = if selection.start.row == selection.end.row {
8282 MultiBufferRow(selection.start.row + 1)
8283 } else {
8284 MultiBufferRow(selection.end.row)
8285 };
8286
8287 if let Some(last_row_range) = row_ranges.last_mut() {
8288 if start <= last_row_range.end {
8289 last_row_range.end = end;
8290 continue;
8291 }
8292 }
8293 row_ranges.push(start..end);
8294 }
8295
8296 let snapshot = self.buffer.read(cx).snapshot(cx);
8297 let mut cursor_positions = Vec::new();
8298 for row_range in &row_ranges {
8299 let anchor = snapshot.anchor_before(Point::new(
8300 row_range.end.previous_row().0,
8301 snapshot.line_len(row_range.end.previous_row()),
8302 ));
8303 cursor_positions.push(anchor..anchor);
8304 }
8305
8306 self.transact(window, cx, |this, window, cx| {
8307 for row_range in row_ranges.into_iter().rev() {
8308 for row in row_range.iter_rows().rev() {
8309 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8310 let next_line_row = row.next_row();
8311 let indent = snapshot.indent_size_for_line(next_line_row);
8312 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8313
8314 let replace =
8315 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8316 " "
8317 } else {
8318 ""
8319 };
8320
8321 this.buffer.update(cx, |buffer, cx| {
8322 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8323 });
8324 }
8325 }
8326
8327 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8328 s.select_anchor_ranges(cursor_positions)
8329 });
8330 });
8331 }
8332
8333 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8334 self.join_lines_impl(true, window, cx);
8335 }
8336
8337 pub fn sort_lines_case_sensitive(
8338 &mut self,
8339 _: &SortLinesCaseSensitive,
8340 window: &mut Window,
8341 cx: &mut Context<Self>,
8342 ) {
8343 self.manipulate_lines(window, cx, |lines| lines.sort())
8344 }
8345
8346 pub fn sort_lines_case_insensitive(
8347 &mut self,
8348 _: &SortLinesCaseInsensitive,
8349 window: &mut Window,
8350 cx: &mut Context<Self>,
8351 ) {
8352 self.manipulate_lines(window, cx, |lines| {
8353 lines.sort_by_key(|line| line.to_lowercase())
8354 })
8355 }
8356
8357 pub fn unique_lines_case_insensitive(
8358 &mut self,
8359 _: &UniqueLinesCaseInsensitive,
8360 window: &mut Window,
8361 cx: &mut Context<Self>,
8362 ) {
8363 self.manipulate_lines(window, cx, |lines| {
8364 let mut seen = HashSet::default();
8365 lines.retain(|line| seen.insert(line.to_lowercase()));
8366 })
8367 }
8368
8369 pub fn unique_lines_case_sensitive(
8370 &mut self,
8371 _: &UniqueLinesCaseSensitive,
8372 window: &mut Window,
8373 cx: &mut Context<Self>,
8374 ) {
8375 self.manipulate_lines(window, cx, |lines| {
8376 let mut seen = HashSet::default();
8377 lines.retain(|line| seen.insert(*line));
8378 })
8379 }
8380
8381 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8382 let Some(project) = self.project.clone() else {
8383 return;
8384 };
8385 self.reload(project, window, cx)
8386 .detach_and_notify_err(window, cx);
8387 }
8388
8389 pub fn restore_file(
8390 &mut self,
8391 _: &::git::RestoreFile,
8392 window: &mut Window,
8393 cx: &mut Context<Self>,
8394 ) {
8395 let mut buffer_ids = HashSet::default();
8396 let snapshot = self.buffer().read(cx).snapshot(cx);
8397 for selection in self.selections.all::<usize>(cx) {
8398 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8399 }
8400
8401 let buffer = self.buffer().read(cx);
8402 let ranges = buffer_ids
8403 .into_iter()
8404 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8405 .collect::<Vec<_>>();
8406
8407 self.restore_hunks_in_ranges(ranges, window, cx);
8408 }
8409
8410 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8411 let selections = self
8412 .selections
8413 .all(cx)
8414 .into_iter()
8415 .map(|s| s.range())
8416 .collect();
8417 self.restore_hunks_in_ranges(selections, window, cx);
8418 }
8419
8420 fn restore_hunks_in_ranges(
8421 &mut self,
8422 ranges: Vec<Range<Point>>,
8423 window: &mut Window,
8424 cx: &mut Context<Editor>,
8425 ) {
8426 let mut revert_changes = HashMap::default();
8427 let chunk_by = self
8428 .snapshot(window, cx)
8429 .hunks_for_ranges(ranges)
8430 .into_iter()
8431 .chunk_by(|hunk| hunk.buffer_id);
8432 for (buffer_id, hunks) in &chunk_by {
8433 let hunks = hunks.collect::<Vec<_>>();
8434 for hunk in &hunks {
8435 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8436 }
8437 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8438 }
8439 drop(chunk_by);
8440 if !revert_changes.is_empty() {
8441 self.transact(window, cx, |editor, window, cx| {
8442 editor.restore(revert_changes, window, cx);
8443 });
8444 }
8445 }
8446
8447 pub fn open_active_item_in_terminal(
8448 &mut self,
8449 _: &OpenInTerminal,
8450 window: &mut Window,
8451 cx: &mut Context<Self>,
8452 ) {
8453 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8454 let project_path = buffer.read(cx).project_path(cx)?;
8455 let project = self.project.as_ref()?.read(cx);
8456 let entry = project.entry_for_path(&project_path, cx)?;
8457 let parent = match &entry.canonical_path {
8458 Some(canonical_path) => canonical_path.to_path_buf(),
8459 None => project.absolute_path(&project_path, cx)?,
8460 }
8461 .parent()?
8462 .to_path_buf();
8463 Some(parent)
8464 }) {
8465 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8466 }
8467 }
8468
8469 fn set_breakpoint_context_menu(
8470 &mut self,
8471 display_row: DisplayRow,
8472 position: Option<Anchor>,
8473 clicked_point: gpui::Point<Pixels>,
8474 window: &mut Window,
8475 cx: &mut Context<Self>,
8476 ) {
8477 if !cx.has_flag::<Debugger>() {
8478 return;
8479 }
8480 let source = self
8481 .buffer
8482 .read(cx)
8483 .snapshot(cx)
8484 .anchor_before(Point::new(display_row.0, 0u32));
8485
8486 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
8487
8488 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8489 self,
8490 source,
8491 clicked_point,
8492 context_menu,
8493 window,
8494 cx,
8495 );
8496 }
8497
8498 fn add_edit_breakpoint_block(
8499 &mut self,
8500 anchor: Anchor,
8501 breakpoint: &Breakpoint,
8502 window: &mut Window,
8503 cx: &mut Context<Self>,
8504 ) {
8505 let weak_editor = cx.weak_entity();
8506 let bp_prompt = cx.new(|cx| {
8507 BreakpointPromptEditor::new(weak_editor, anchor, breakpoint.clone(), window, cx)
8508 });
8509
8510 let height = bp_prompt.update(cx, |this, cx| {
8511 this.prompt
8512 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8513 });
8514 let cloned_prompt = bp_prompt.clone();
8515 let blocks = vec![BlockProperties {
8516 style: BlockStyle::Sticky,
8517 placement: BlockPlacement::Above(anchor),
8518 height,
8519 render: Arc::new(move |cx| {
8520 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8521 cloned_prompt.clone().into_any_element()
8522 }),
8523 priority: 0,
8524 }];
8525
8526 let focus_handle = bp_prompt.focus_handle(cx);
8527 window.focus(&focus_handle);
8528
8529 let block_ids = self.insert_blocks(blocks, None, cx);
8530 bp_prompt.update(cx, |prompt, _| {
8531 prompt.add_block_ids(block_ids);
8532 });
8533 }
8534
8535 fn breakpoint_at_cursor_head(
8536 &self,
8537 window: &mut Window,
8538 cx: &mut Context<Self>,
8539 ) -> Option<(Anchor, Breakpoint)> {
8540 let cursor_position: Point = self.selections.newest(cx).head();
8541 self.breakpoint_at_row(cursor_position.row, window, cx)
8542 }
8543
8544 pub(crate) fn breakpoint_at_row(
8545 &self,
8546 row: u32,
8547 window: &mut Window,
8548 cx: &mut Context<Self>,
8549 ) -> Option<(Anchor, Breakpoint)> {
8550 let snapshot = self.snapshot(window, cx);
8551 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
8552
8553 let project = self.project.clone()?;
8554
8555 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
8556 snapshot
8557 .buffer_snapshot
8558 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
8559 })?;
8560
8561 let enclosing_excerpt = breakpoint_position.excerpt_id;
8562 let buffer = project.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8563 let buffer_snapshot = buffer.read(cx).snapshot();
8564
8565 let row = buffer_snapshot
8566 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
8567 .row;
8568
8569 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
8570 let anchor_end = snapshot
8571 .buffer_snapshot
8572 .anchor_before(Point::new(row, line_len));
8573
8574 let bp = self
8575 .breakpoint_store
8576 .as_ref()?
8577 .read_with(cx, |breakpoint_store, cx| {
8578 breakpoint_store
8579 .breakpoints(
8580 &buffer,
8581 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
8582 &buffer_snapshot,
8583 cx,
8584 )
8585 .next()
8586 .and_then(|(anchor, bp)| {
8587 let breakpoint_row = buffer_snapshot
8588 .summary_for_anchor::<text::PointUtf16>(anchor)
8589 .row;
8590
8591 if breakpoint_row == row {
8592 snapshot
8593 .buffer_snapshot
8594 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8595 .map(|anchor| (anchor, bp.clone()))
8596 } else {
8597 None
8598 }
8599 })
8600 });
8601 bp
8602 }
8603
8604 pub fn edit_log_breakpoint(
8605 &mut self,
8606 _: &EditLogBreakpoint,
8607 window: &mut Window,
8608 cx: &mut Context<Self>,
8609 ) {
8610 let (anchor, bp) = self
8611 .breakpoint_at_cursor_head(window, cx)
8612 .unwrap_or_else(|| {
8613 let cursor_position: Point = self.selections.newest(cx).head();
8614
8615 let breakpoint_position = self
8616 .snapshot(window, cx)
8617 .display_snapshot
8618 .buffer_snapshot
8619 .anchor_before(Point::new(cursor_position.row, 0));
8620
8621 (
8622 breakpoint_position,
8623 Breakpoint {
8624 kind: BreakpointKind::Standard,
8625 state: BreakpointState::Enabled,
8626 },
8627 )
8628 });
8629
8630 self.add_edit_breakpoint_block(anchor, &bp, window, cx);
8631 }
8632
8633 pub fn enable_breakpoint(
8634 &mut self,
8635 _: &crate::actions::EnableBreakpoint,
8636 window: &mut Window,
8637 cx: &mut Context<Self>,
8638 ) {
8639 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8640 if breakpoint.is_disabled() {
8641 self.edit_breakpoint_at_anchor(
8642 anchor,
8643 breakpoint,
8644 BreakpointEditAction::InvertState,
8645 cx,
8646 );
8647 }
8648 }
8649 }
8650
8651 pub fn disable_breakpoint(
8652 &mut self,
8653 _: &crate::actions::DisableBreakpoint,
8654 window: &mut Window,
8655 cx: &mut Context<Self>,
8656 ) {
8657 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8658 if breakpoint.is_enabled() {
8659 self.edit_breakpoint_at_anchor(
8660 anchor,
8661 breakpoint,
8662 BreakpointEditAction::InvertState,
8663 cx,
8664 );
8665 }
8666 }
8667 }
8668
8669 pub fn toggle_breakpoint(
8670 &mut self,
8671 _: &crate::actions::ToggleBreakpoint,
8672 window: &mut Window,
8673 cx: &mut Context<Self>,
8674 ) {
8675 let edit_action = BreakpointEditAction::Toggle;
8676
8677 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8678 self.edit_breakpoint_at_anchor(anchor, breakpoint, edit_action, cx);
8679 } else {
8680 let cursor_position: Point = self.selections.newest(cx).head();
8681
8682 let breakpoint_position = self
8683 .snapshot(window, cx)
8684 .display_snapshot
8685 .buffer_snapshot
8686 .anchor_before(Point::new(cursor_position.row, 0));
8687
8688 self.edit_breakpoint_at_anchor(
8689 breakpoint_position,
8690 Breakpoint::new_standard(),
8691 edit_action,
8692 cx,
8693 );
8694 }
8695 }
8696
8697 pub fn edit_breakpoint_at_anchor(
8698 &mut self,
8699 breakpoint_position: Anchor,
8700 breakpoint: Breakpoint,
8701 edit_action: BreakpointEditAction,
8702 cx: &mut Context<Self>,
8703 ) {
8704 let Some(breakpoint_store) = &self.breakpoint_store else {
8705 return;
8706 };
8707
8708 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8709 if breakpoint_position == Anchor::min() {
8710 self.buffer()
8711 .read(cx)
8712 .excerpt_buffer_ids()
8713 .into_iter()
8714 .next()
8715 } else {
8716 None
8717 }
8718 }) else {
8719 return;
8720 };
8721
8722 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8723 return;
8724 };
8725
8726 breakpoint_store.update(cx, |breakpoint_store, cx| {
8727 breakpoint_store.toggle_breakpoint(
8728 buffer,
8729 (breakpoint_position.text_anchor, breakpoint),
8730 edit_action,
8731 cx,
8732 );
8733 });
8734
8735 cx.notify();
8736 }
8737
8738 #[cfg(any(test, feature = "test-support"))]
8739 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8740 self.breakpoint_store.clone()
8741 }
8742
8743 pub fn prepare_restore_change(
8744 &self,
8745 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8746 hunk: &MultiBufferDiffHunk,
8747 cx: &mut App,
8748 ) -> Option<()> {
8749 if hunk.is_created_file() {
8750 return None;
8751 }
8752 let buffer = self.buffer.read(cx);
8753 let diff = buffer.diff_for(hunk.buffer_id)?;
8754 let buffer = buffer.buffer(hunk.buffer_id)?;
8755 let buffer = buffer.read(cx);
8756 let original_text = diff
8757 .read(cx)
8758 .base_text()
8759 .as_rope()
8760 .slice(hunk.diff_base_byte_range.clone());
8761 let buffer_snapshot = buffer.snapshot();
8762 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8763 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8764 probe
8765 .0
8766 .start
8767 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8768 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8769 }) {
8770 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8771 Some(())
8772 } else {
8773 None
8774 }
8775 }
8776
8777 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8778 self.manipulate_lines(window, cx, |lines| lines.reverse())
8779 }
8780
8781 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8782 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8783 }
8784
8785 fn manipulate_lines<Fn>(
8786 &mut self,
8787 window: &mut Window,
8788 cx: &mut Context<Self>,
8789 mut callback: Fn,
8790 ) where
8791 Fn: FnMut(&mut Vec<&str>),
8792 {
8793 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8794 let buffer = self.buffer.read(cx).snapshot(cx);
8795
8796 let mut edits = Vec::new();
8797
8798 let selections = self.selections.all::<Point>(cx);
8799 let mut selections = selections.iter().peekable();
8800 let mut contiguous_row_selections = Vec::new();
8801 let mut new_selections = Vec::new();
8802 let mut added_lines = 0;
8803 let mut removed_lines = 0;
8804
8805 while let Some(selection) = selections.next() {
8806 let (start_row, end_row) = consume_contiguous_rows(
8807 &mut contiguous_row_selections,
8808 selection,
8809 &display_map,
8810 &mut selections,
8811 );
8812
8813 let start_point = Point::new(start_row.0, 0);
8814 let end_point = Point::new(
8815 end_row.previous_row().0,
8816 buffer.line_len(end_row.previous_row()),
8817 );
8818 let text = buffer
8819 .text_for_range(start_point..end_point)
8820 .collect::<String>();
8821
8822 let mut lines = text.split('\n').collect_vec();
8823
8824 let lines_before = lines.len();
8825 callback(&mut lines);
8826 let lines_after = lines.len();
8827
8828 edits.push((start_point..end_point, lines.join("\n")));
8829
8830 // Selections must change based on added and removed line count
8831 let start_row =
8832 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8833 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8834 new_selections.push(Selection {
8835 id: selection.id,
8836 start: start_row,
8837 end: end_row,
8838 goal: SelectionGoal::None,
8839 reversed: selection.reversed,
8840 });
8841
8842 if lines_after > lines_before {
8843 added_lines += lines_after - lines_before;
8844 } else if lines_before > lines_after {
8845 removed_lines += lines_before - lines_after;
8846 }
8847 }
8848
8849 self.transact(window, cx, |this, window, cx| {
8850 let buffer = this.buffer.update(cx, |buffer, cx| {
8851 buffer.edit(edits, None, cx);
8852 buffer.snapshot(cx)
8853 });
8854
8855 // Recalculate offsets on newly edited buffer
8856 let new_selections = new_selections
8857 .iter()
8858 .map(|s| {
8859 let start_point = Point::new(s.start.0, 0);
8860 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8861 Selection {
8862 id: s.id,
8863 start: buffer.point_to_offset(start_point),
8864 end: buffer.point_to_offset(end_point),
8865 goal: s.goal,
8866 reversed: s.reversed,
8867 }
8868 })
8869 .collect();
8870
8871 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8872 s.select(new_selections);
8873 });
8874
8875 this.request_autoscroll(Autoscroll::fit(), cx);
8876 });
8877 }
8878
8879 pub fn convert_to_upper_case(
8880 &mut self,
8881 _: &ConvertToUpperCase,
8882 window: &mut Window,
8883 cx: &mut Context<Self>,
8884 ) {
8885 self.manipulate_text(window, cx, |text| text.to_uppercase())
8886 }
8887
8888 pub fn convert_to_lower_case(
8889 &mut self,
8890 _: &ConvertToLowerCase,
8891 window: &mut Window,
8892 cx: &mut Context<Self>,
8893 ) {
8894 self.manipulate_text(window, cx, |text| text.to_lowercase())
8895 }
8896
8897 pub fn convert_to_title_case(
8898 &mut self,
8899 _: &ConvertToTitleCase,
8900 window: &mut Window,
8901 cx: &mut Context<Self>,
8902 ) {
8903 self.manipulate_text(window, cx, |text| {
8904 text.split('\n')
8905 .map(|line| line.to_case(Case::Title))
8906 .join("\n")
8907 })
8908 }
8909
8910 pub fn convert_to_snake_case(
8911 &mut self,
8912 _: &ConvertToSnakeCase,
8913 window: &mut Window,
8914 cx: &mut Context<Self>,
8915 ) {
8916 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8917 }
8918
8919 pub fn convert_to_kebab_case(
8920 &mut self,
8921 _: &ConvertToKebabCase,
8922 window: &mut Window,
8923 cx: &mut Context<Self>,
8924 ) {
8925 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8926 }
8927
8928 pub fn convert_to_upper_camel_case(
8929 &mut self,
8930 _: &ConvertToUpperCamelCase,
8931 window: &mut Window,
8932 cx: &mut Context<Self>,
8933 ) {
8934 self.manipulate_text(window, cx, |text| {
8935 text.split('\n')
8936 .map(|line| line.to_case(Case::UpperCamel))
8937 .join("\n")
8938 })
8939 }
8940
8941 pub fn convert_to_lower_camel_case(
8942 &mut self,
8943 _: &ConvertToLowerCamelCase,
8944 window: &mut Window,
8945 cx: &mut Context<Self>,
8946 ) {
8947 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8948 }
8949
8950 pub fn convert_to_opposite_case(
8951 &mut self,
8952 _: &ConvertToOppositeCase,
8953 window: &mut Window,
8954 cx: &mut Context<Self>,
8955 ) {
8956 self.manipulate_text(window, cx, |text| {
8957 text.chars()
8958 .fold(String::with_capacity(text.len()), |mut t, c| {
8959 if c.is_uppercase() {
8960 t.extend(c.to_lowercase());
8961 } else {
8962 t.extend(c.to_uppercase());
8963 }
8964 t
8965 })
8966 })
8967 }
8968
8969 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8970 where
8971 Fn: FnMut(&str) -> String,
8972 {
8973 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8974 let buffer = self.buffer.read(cx).snapshot(cx);
8975
8976 let mut new_selections = Vec::new();
8977 let mut edits = Vec::new();
8978 let mut selection_adjustment = 0i32;
8979
8980 for selection in self.selections.all::<usize>(cx) {
8981 let selection_is_empty = selection.is_empty();
8982
8983 let (start, end) = if selection_is_empty {
8984 let word_range = movement::surrounding_word(
8985 &display_map,
8986 selection.start.to_display_point(&display_map),
8987 );
8988 let start = word_range.start.to_offset(&display_map, Bias::Left);
8989 let end = word_range.end.to_offset(&display_map, Bias::Left);
8990 (start, end)
8991 } else {
8992 (selection.start, selection.end)
8993 };
8994
8995 let text = buffer.text_for_range(start..end).collect::<String>();
8996 let old_length = text.len() as i32;
8997 let text = callback(&text);
8998
8999 new_selections.push(Selection {
9000 start: (start as i32 - selection_adjustment) as usize,
9001 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
9002 goal: SelectionGoal::None,
9003 ..selection
9004 });
9005
9006 selection_adjustment += old_length - text.len() as i32;
9007
9008 edits.push((start..end, text));
9009 }
9010
9011 self.transact(window, cx, |this, window, cx| {
9012 this.buffer.update(cx, |buffer, cx| {
9013 buffer.edit(edits, None, cx);
9014 });
9015
9016 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9017 s.select(new_selections);
9018 });
9019
9020 this.request_autoscroll(Autoscroll::fit(), cx);
9021 });
9022 }
9023
9024 pub fn duplicate(
9025 &mut self,
9026 upwards: bool,
9027 whole_lines: bool,
9028 window: &mut Window,
9029 cx: &mut Context<Self>,
9030 ) {
9031 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9032 let buffer = &display_map.buffer_snapshot;
9033 let selections = self.selections.all::<Point>(cx);
9034
9035 let mut edits = Vec::new();
9036 let mut selections_iter = selections.iter().peekable();
9037 while let Some(selection) = selections_iter.next() {
9038 let mut rows = selection.spanned_rows(false, &display_map);
9039 // duplicate line-wise
9040 if whole_lines || selection.start == selection.end {
9041 // Avoid duplicating the same lines twice.
9042 while let Some(next_selection) = selections_iter.peek() {
9043 let next_rows = next_selection.spanned_rows(false, &display_map);
9044 if next_rows.start < rows.end {
9045 rows.end = next_rows.end;
9046 selections_iter.next().unwrap();
9047 } else {
9048 break;
9049 }
9050 }
9051
9052 // Copy the text from the selected row region and splice it either at the start
9053 // or end of the region.
9054 let start = Point::new(rows.start.0, 0);
9055 let end = Point::new(
9056 rows.end.previous_row().0,
9057 buffer.line_len(rows.end.previous_row()),
9058 );
9059 let text = buffer
9060 .text_for_range(start..end)
9061 .chain(Some("\n"))
9062 .collect::<String>();
9063 let insert_location = if upwards {
9064 Point::new(rows.end.0, 0)
9065 } else {
9066 start
9067 };
9068 edits.push((insert_location..insert_location, text));
9069 } else {
9070 // duplicate character-wise
9071 let start = selection.start;
9072 let end = selection.end;
9073 let text = buffer.text_for_range(start..end).collect::<String>();
9074 edits.push((selection.end..selection.end, text));
9075 }
9076 }
9077
9078 self.transact(window, cx, |this, _, cx| {
9079 this.buffer.update(cx, |buffer, cx| {
9080 buffer.edit(edits, None, cx);
9081 });
9082
9083 this.request_autoscroll(Autoscroll::fit(), cx);
9084 });
9085 }
9086
9087 pub fn duplicate_line_up(
9088 &mut self,
9089 _: &DuplicateLineUp,
9090 window: &mut Window,
9091 cx: &mut Context<Self>,
9092 ) {
9093 self.duplicate(true, true, window, cx);
9094 }
9095
9096 pub fn duplicate_line_down(
9097 &mut self,
9098 _: &DuplicateLineDown,
9099 window: &mut Window,
9100 cx: &mut Context<Self>,
9101 ) {
9102 self.duplicate(false, true, window, cx);
9103 }
9104
9105 pub fn duplicate_selection(
9106 &mut self,
9107 _: &DuplicateSelection,
9108 window: &mut Window,
9109 cx: &mut Context<Self>,
9110 ) {
9111 self.duplicate(false, false, window, cx);
9112 }
9113
9114 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
9115 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9116 let buffer = self.buffer.read(cx).snapshot(cx);
9117
9118 let mut edits = Vec::new();
9119 let mut unfold_ranges = Vec::new();
9120 let mut refold_creases = Vec::new();
9121
9122 let selections = self.selections.all::<Point>(cx);
9123 let mut selections = selections.iter().peekable();
9124 let mut contiguous_row_selections = Vec::new();
9125 let mut new_selections = Vec::new();
9126
9127 while let Some(selection) = selections.next() {
9128 // Find all the selections that span a contiguous row range
9129 let (start_row, end_row) = consume_contiguous_rows(
9130 &mut contiguous_row_selections,
9131 selection,
9132 &display_map,
9133 &mut selections,
9134 );
9135
9136 // Move the text spanned by the row range to be before the line preceding the row range
9137 if start_row.0 > 0 {
9138 let range_to_move = Point::new(
9139 start_row.previous_row().0,
9140 buffer.line_len(start_row.previous_row()),
9141 )
9142 ..Point::new(
9143 end_row.previous_row().0,
9144 buffer.line_len(end_row.previous_row()),
9145 );
9146 let insertion_point = display_map
9147 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9148 .0;
9149
9150 // Don't move lines across excerpts
9151 if buffer
9152 .excerpt_containing(insertion_point..range_to_move.end)
9153 .is_some()
9154 {
9155 let text = buffer
9156 .text_for_range(range_to_move.clone())
9157 .flat_map(|s| s.chars())
9158 .skip(1)
9159 .chain(['\n'])
9160 .collect::<String>();
9161
9162 edits.push((
9163 buffer.anchor_after(range_to_move.start)
9164 ..buffer.anchor_before(range_to_move.end),
9165 String::new(),
9166 ));
9167 let insertion_anchor = buffer.anchor_after(insertion_point);
9168 edits.push((insertion_anchor..insertion_anchor, text));
9169
9170 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9171
9172 // Move selections up
9173 new_selections.extend(contiguous_row_selections.drain(..).map(
9174 |mut selection| {
9175 selection.start.row -= row_delta;
9176 selection.end.row -= row_delta;
9177 selection
9178 },
9179 ));
9180
9181 // Move folds up
9182 unfold_ranges.push(range_to_move.clone());
9183 for fold in display_map.folds_in_range(
9184 buffer.anchor_before(range_to_move.start)
9185 ..buffer.anchor_after(range_to_move.end),
9186 ) {
9187 let mut start = fold.range.start.to_point(&buffer);
9188 let mut end = fold.range.end.to_point(&buffer);
9189 start.row -= row_delta;
9190 end.row -= row_delta;
9191 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9192 }
9193 }
9194 }
9195
9196 // If we didn't move line(s), preserve the existing selections
9197 new_selections.append(&mut contiguous_row_selections);
9198 }
9199
9200 self.transact(window, cx, |this, window, cx| {
9201 this.unfold_ranges(&unfold_ranges, true, true, cx);
9202 this.buffer.update(cx, |buffer, cx| {
9203 for (range, text) in edits {
9204 buffer.edit([(range, text)], None, cx);
9205 }
9206 });
9207 this.fold_creases(refold_creases, true, window, cx);
9208 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9209 s.select(new_selections);
9210 })
9211 });
9212 }
9213
9214 pub fn move_line_down(
9215 &mut self,
9216 _: &MoveLineDown,
9217 window: &mut Window,
9218 cx: &mut Context<Self>,
9219 ) {
9220 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9221 let buffer = self.buffer.read(cx).snapshot(cx);
9222
9223 let mut edits = Vec::new();
9224 let mut unfold_ranges = Vec::new();
9225 let mut refold_creases = Vec::new();
9226
9227 let selections = self.selections.all::<Point>(cx);
9228 let mut selections = selections.iter().peekable();
9229 let mut contiguous_row_selections = Vec::new();
9230 let mut new_selections = Vec::new();
9231
9232 while let Some(selection) = selections.next() {
9233 // Find all the selections that span a contiguous row range
9234 let (start_row, end_row) = consume_contiguous_rows(
9235 &mut contiguous_row_selections,
9236 selection,
9237 &display_map,
9238 &mut selections,
9239 );
9240
9241 // Move the text spanned by the row range to be after the last line of the row range
9242 if end_row.0 <= buffer.max_point().row {
9243 let range_to_move =
9244 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9245 let insertion_point = display_map
9246 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9247 .0;
9248
9249 // Don't move lines across excerpt boundaries
9250 if buffer
9251 .excerpt_containing(range_to_move.start..insertion_point)
9252 .is_some()
9253 {
9254 let mut text = String::from("\n");
9255 text.extend(buffer.text_for_range(range_to_move.clone()));
9256 text.pop(); // Drop trailing newline
9257 edits.push((
9258 buffer.anchor_after(range_to_move.start)
9259 ..buffer.anchor_before(range_to_move.end),
9260 String::new(),
9261 ));
9262 let insertion_anchor = buffer.anchor_after(insertion_point);
9263 edits.push((insertion_anchor..insertion_anchor, text));
9264
9265 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9266
9267 // Move selections down
9268 new_selections.extend(contiguous_row_selections.drain(..).map(
9269 |mut selection| {
9270 selection.start.row += row_delta;
9271 selection.end.row += row_delta;
9272 selection
9273 },
9274 ));
9275
9276 // Move folds down
9277 unfold_ranges.push(range_to_move.clone());
9278 for fold in display_map.folds_in_range(
9279 buffer.anchor_before(range_to_move.start)
9280 ..buffer.anchor_after(range_to_move.end),
9281 ) {
9282 let mut start = fold.range.start.to_point(&buffer);
9283 let mut end = fold.range.end.to_point(&buffer);
9284 start.row += row_delta;
9285 end.row += row_delta;
9286 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9287 }
9288 }
9289 }
9290
9291 // If we didn't move line(s), preserve the existing selections
9292 new_selections.append(&mut contiguous_row_selections);
9293 }
9294
9295 self.transact(window, cx, |this, window, cx| {
9296 this.unfold_ranges(&unfold_ranges, true, true, cx);
9297 this.buffer.update(cx, |buffer, cx| {
9298 for (range, text) in edits {
9299 buffer.edit([(range, text)], None, cx);
9300 }
9301 });
9302 this.fold_creases(refold_creases, true, window, cx);
9303 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9304 s.select(new_selections)
9305 });
9306 });
9307 }
9308
9309 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9310 let text_layout_details = &self.text_layout_details(window);
9311 self.transact(window, cx, |this, window, cx| {
9312 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9313 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9314 let line_mode = s.line_mode;
9315 s.move_with(|display_map, selection| {
9316 if !selection.is_empty() || line_mode {
9317 return;
9318 }
9319
9320 let mut head = selection.head();
9321 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9322 if head.column() == display_map.line_len(head.row()) {
9323 transpose_offset = display_map
9324 .buffer_snapshot
9325 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9326 }
9327
9328 if transpose_offset == 0 {
9329 return;
9330 }
9331
9332 *head.column_mut() += 1;
9333 head = display_map.clip_point(head, Bias::Right);
9334 let goal = SelectionGoal::HorizontalPosition(
9335 display_map
9336 .x_for_display_point(head, text_layout_details)
9337 .into(),
9338 );
9339 selection.collapse_to(head, goal);
9340
9341 let transpose_start = display_map
9342 .buffer_snapshot
9343 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9344 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9345 let transpose_end = display_map
9346 .buffer_snapshot
9347 .clip_offset(transpose_offset + 1, Bias::Right);
9348 if let Some(ch) =
9349 display_map.buffer_snapshot.chars_at(transpose_start).next()
9350 {
9351 edits.push((transpose_start..transpose_offset, String::new()));
9352 edits.push((transpose_end..transpose_end, ch.to_string()));
9353 }
9354 }
9355 });
9356 edits
9357 });
9358 this.buffer
9359 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9360 let selections = this.selections.all::<usize>(cx);
9361 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9362 s.select(selections);
9363 });
9364 });
9365 }
9366
9367 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9368 self.rewrap_impl(RewrapOptions::default(), cx)
9369 }
9370
9371 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9372 let buffer = self.buffer.read(cx).snapshot(cx);
9373 let selections = self.selections.all::<Point>(cx);
9374 let mut selections = selections.iter().peekable();
9375
9376 let mut edits = Vec::new();
9377 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9378
9379 while let Some(selection) = selections.next() {
9380 let mut start_row = selection.start.row;
9381 let mut end_row = selection.end.row;
9382
9383 // Skip selections that overlap with a range that has already been rewrapped.
9384 let selection_range = start_row..end_row;
9385 if rewrapped_row_ranges
9386 .iter()
9387 .any(|range| range.overlaps(&selection_range))
9388 {
9389 continue;
9390 }
9391
9392 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9393
9394 // Since not all lines in the selection may be at the same indent
9395 // level, choose the indent size that is the most common between all
9396 // of the lines.
9397 //
9398 // If there is a tie, we use the deepest indent.
9399 let (indent_size, indent_end) = {
9400 let mut indent_size_occurrences = HashMap::default();
9401 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9402
9403 for row in start_row..=end_row {
9404 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9405 rows_by_indent_size.entry(indent).or_default().push(row);
9406 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9407 }
9408
9409 let indent_size = indent_size_occurrences
9410 .into_iter()
9411 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9412 .map(|(indent, _)| indent)
9413 .unwrap_or_default();
9414 let row = rows_by_indent_size[&indent_size][0];
9415 let indent_end = Point::new(row, indent_size.len);
9416
9417 (indent_size, indent_end)
9418 };
9419
9420 let mut line_prefix = indent_size.chars().collect::<String>();
9421
9422 let mut inside_comment = false;
9423 if let Some(comment_prefix) =
9424 buffer
9425 .language_scope_at(selection.head())
9426 .and_then(|language| {
9427 language
9428 .line_comment_prefixes()
9429 .iter()
9430 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9431 .cloned()
9432 })
9433 {
9434 line_prefix.push_str(&comment_prefix);
9435 inside_comment = true;
9436 }
9437
9438 let language_settings = buffer.language_settings_at(selection.head(), cx);
9439 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9440 RewrapBehavior::InComments => inside_comment,
9441 RewrapBehavior::InSelections => !selection.is_empty(),
9442 RewrapBehavior::Anywhere => true,
9443 };
9444
9445 let should_rewrap = options.override_language_settings
9446 || allow_rewrap_based_on_language
9447 || self.hard_wrap.is_some();
9448 if !should_rewrap {
9449 continue;
9450 }
9451
9452 if selection.is_empty() {
9453 'expand_upwards: while start_row > 0 {
9454 let prev_row = start_row - 1;
9455 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9456 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9457 {
9458 start_row = prev_row;
9459 } else {
9460 break 'expand_upwards;
9461 }
9462 }
9463
9464 'expand_downwards: while end_row < buffer.max_point().row {
9465 let next_row = end_row + 1;
9466 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9467 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9468 {
9469 end_row = next_row;
9470 } else {
9471 break 'expand_downwards;
9472 }
9473 }
9474 }
9475
9476 let start = Point::new(start_row, 0);
9477 let start_offset = start.to_offset(&buffer);
9478 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9479 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9480 let Some(lines_without_prefixes) = selection_text
9481 .lines()
9482 .map(|line| {
9483 line.strip_prefix(&line_prefix)
9484 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9485 .ok_or_else(|| {
9486 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9487 })
9488 })
9489 .collect::<Result<Vec<_>, _>>()
9490 .log_err()
9491 else {
9492 continue;
9493 };
9494
9495 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9496 buffer
9497 .language_settings_at(Point::new(start_row, 0), cx)
9498 .preferred_line_length as usize
9499 });
9500 let wrapped_text = wrap_with_prefix(
9501 line_prefix,
9502 lines_without_prefixes.join("\n"),
9503 wrap_column,
9504 tab_size,
9505 options.preserve_existing_whitespace,
9506 );
9507
9508 // TODO: should always use char-based diff while still supporting cursor behavior that
9509 // matches vim.
9510 let mut diff_options = DiffOptions::default();
9511 if options.override_language_settings {
9512 diff_options.max_word_diff_len = 0;
9513 diff_options.max_word_diff_line_count = 0;
9514 } else {
9515 diff_options.max_word_diff_len = usize::MAX;
9516 diff_options.max_word_diff_line_count = usize::MAX;
9517 }
9518
9519 for (old_range, new_text) in
9520 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9521 {
9522 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9523 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9524 edits.push((edit_start..edit_end, new_text));
9525 }
9526
9527 rewrapped_row_ranges.push(start_row..=end_row);
9528 }
9529
9530 self.buffer
9531 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9532 }
9533
9534 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9535 let mut text = String::new();
9536 let buffer = self.buffer.read(cx).snapshot(cx);
9537 let mut selections = self.selections.all::<Point>(cx);
9538 let mut clipboard_selections = Vec::with_capacity(selections.len());
9539 {
9540 let max_point = buffer.max_point();
9541 let mut is_first = true;
9542 for selection in &mut selections {
9543 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9544 if is_entire_line {
9545 selection.start = Point::new(selection.start.row, 0);
9546 if !selection.is_empty() && selection.end.column == 0 {
9547 selection.end = cmp::min(max_point, selection.end);
9548 } else {
9549 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9550 }
9551 selection.goal = SelectionGoal::None;
9552 }
9553 if is_first {
9554 is_first = false;
9555 } else {
9556 text += "\n";
9557 }
9558 let mut len = 0;
9559 for chunk in buffer.text_for_range(selection.start..selection.end) {
9560 text.push_str(chunk);
9561 len += chunk.len();
9562 }
9563 clipboard_selections.push(ClipboardSelection {
9564 len,
9565 is_entire_line,
9566 first_line_indent: buffer
9567 .indent_size_for_line(MultiBufferRow(selection.start.row))
9568 .len,
9569 });
9570 }
9571 }
9572
9573 self.transact(window, cx, |this, window, cx| {
9574 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9575 s.select(selections);
9576 });
9577 this.insert("", window, cx);
9578 });
9579 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9580 }
9581
9582 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9583 let item = self.cut_common(window, cx);
9584 cx.write_to_clipboard(item);
9585 }
9586
9587 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9588 self.change_selections(None, window, cx, |s| {
9589 s.move_with(|snapshot, sel| {
9590 if sel.is_empty() {
9591 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9592 }
9593 });
9594 });
9595 let item = self.cut_common(window, cx);
9596 cx.set_global(KillRing(item))
9597 }
9598
9599 pub fn kill_ring_yank(
9600 &mut self,
9601 _: &KillRingYank,
9602 window: &mut Window,
9603 cx: &mut Context<Self>,
9604 ) {
9605 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9606 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9607 (kill_ring.text().to_string(), kill_ring.metadata_json())
9608 } else {
9609 return;
9610 }
9611 } else {
9612 return;
9613 };
9614 self.do_paste(&text, metadata, false, window, cx);
9615 }
9616
9617 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9618 self.do_copy(true, cx);
9619 }
9620
9621 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9622 self.do_copy(false, cx);
9623 }
9624
9625 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9626 let selections = self.selections.all::<Point>(cx);
9627 let buffer = self.buffer.read(cx).read(cx);
9628 let mut text = String::new();
9629
9630 let mut clipboard_selections = Vec::with_capacity(selections.len());
9631 {
9632 let max_point = buffer.max_point();
9633 let mut is_first = true;
9634 for selection in &selections {
9635 let mut start = selection.start;
9636 let mut end = selection.end;
9637 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9638 if is_entire_line {
9639 start = Point::new(start.row, 0);
9640 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9641 }
9642
9643 let mut trimmed_selections = Vec::new();
9644 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9645 let row = MultiBufferRow(start.row);
9646 let first_indent = buffer.indent_size_for_line(row);
9647 if first_indent.len == 0 || start.column > first_indent.len {
9648 trimmed_selections.push(start..end);
9649 } else {
9650 trimmed_selections.push(
9651 Point::new(row.0, first_indent.len)
9652 ..Point::new(row.0, buffer.line_len(row)),
9653 );
9654 for row in start.row + 1..=end.row {
9655 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9656 if row_indent_size.len >= first_indent.len {
9657 trimmed_selections.push(
9658 Point::new(row, first_indent.len)
9659 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9660 );
9661 } else {
9662 trimmed_selections.clear();
9663 trimmed_selections.push(start..end);
9664 break;
9665 }
9666 }
9667 }
9668 } else {
9669 trimmed_selections.push(start..end);
9670 }
9671
9672 for trimmed_range in trimmed_selections {
9673 if is_first {
9674 is_first = false;
9675 } else {
9676 text += "\n";
9677 }
9678 let mut len = 0;
9679 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9680 text.push_str(chunk);
9681 len += chunk.len();
9682 }
9683 clipboard_selections.push(ClipboardSelection {
9684 len,
9685 is_entire_line,
9686 first_line_indent: buffer
9687 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9688 .len,
9689 });
9690 }
9691 }
9692 }
9693
9694 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9695 text,
9696 clipboard_selections,
9697 ));
9698 }
9699
9700 pub fn do_paste(
9701 &mut self,
9702 text: &String,
9703 clipboard_selections: Option<Vec<ClipboardSelection>>,
9704 handle_entire_lines: bool,
9705 window: &mut Window,
9706 cx: &mut Context<Self>,
9707 ) {
9708 if self.read_only(cx) {
9709 return;
9710 }
9711
9712 let clipboard_text = Cow::Borrowed(text);
9713
9714 self.transact(window, cx, |this, window, cx| {
9715 if let Some(mut clipboard_selections) = clipboard_selections {
9716 let old_selections = this.selections.all::<usize>(cx);
9717 let all_selections_were_entire_line =
9718 clipboard_selections.iter().all(|s| s.is_entire_line);
9719 let first_selection_indent_column =
9720 clipboard_selections.first().map(|s| s.first_line_indent);
9721 if clipboard_selections.len() != old_selections.len() {
9722 clipboard_selections.drain(..);
9723 }
9724 let cursor_offset = this.selections.last::<usize>(cx).head();
9725 let mut auto_indent_on_paste = true;
9726
9727 this.buffer.update(cx, |buffer, cx| {
9728 let snapshot = buffer.read(cx);
9729 auto_indent_on_paste = snapshot
9730 .language_settings_at(cursor_offset, cx)
9731 .auto_indent_on_paste;
9732
9733 let mut start_offset = 0;
9734 let mut edits = Vec::new();
9735 let mut original_indent_columns = Vec::new();
9736 for (ix, selection) in old_selections.iter().enumerate() {
9737 let to_insert;
9738 let entire_line;
9739 let original_indent_column;
9740 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9741 let end_offset = start_offset + clipboard_selection.len;
9742 to_insert = &clipboard_text[start_offset..end_offset];
9743 entire_line = clipboard_selection.is_entire_line;
9744 start_offset = end_offset + 1;
9745 original_indent_column = Some(clipboard_selection.first_line_indent);
9746 } else {
9747 to_insert = clipboard_text.as_str();
9748 entire_line = all_selections_were_entire_line;
9749 original_indent_column = first_selection_indent_column
9750 }
9751
9752 // If the corresponding selection was empty when this slice of the
9753 // clipboard text was written, then the entire line containing the
9754 // selection was copied. If this selection is also currently empty,
9755 // then paste the line before the current line of the buffer.
9756 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9757 let column = selection.start.to_point(&snapshot).column as usize;
9758 let line_start = selection.start - column;
9759 line_start..line_start
9760 } else {
9761 selection.range()
9762 };
9763
9764 edits.push((range, to_insert));
9765 original_indent_columns.push(original_indent_column);
9766 }
9767 drop(snapshot);
9768
9769 buffer.edit(
9770 edits,
9771 if auto_indent_on_paste {
9772 Some(AutoindentMode::Block {
9773 original_indent_columns,
9774 })
9775 } else {
9776 None
9777 },
9778 cx,
9779 );
9780 });
9781
9782 let selections = this.selections.all::<usize>(cx);
9783 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9784 s.select(selections)
9785 });
9786 } else {
9787 this.insert(&clipboard_text, window, cx);
9788 }
9789 });
9790 }
9791
9792 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9793 if let Some(item) = cx.read_from_clipboard() {
9794 let entries = item.entries();
9795
9796 match entries.first() {
9797 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9798 // of all the pasted entries.
9799 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9800 .do_paste(
9801 clipboard_string.text(),
9802 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9803 true,
9804 window,
9805 cx,
9806 ),
9807 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9808 }
9809 }
9810 }
9811
9812 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9813 if self.read_only(cx) {
9814 return;
9815 }
9816
9817 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9818 if let Some((selections, _)) =
9819 self.selection_history.transaction(transaction_id).cloned()
9820 {
9821 self.change_selections(None, window, cx, |s| {
9822 s.select_anchors(selections.to_vec());
9823 });
9824 } else {
9825 log::error!(
9826 "No entry in selection_history found for undo. \
9827 This may correspond to a bug where undo does not update the selection. \
9828 If this is occurring, please add details to \
9829 https://github.com/zed-industries/zed/issues/22692"
9830 );
9831 }
9832 self.request_autoscroll(Autoscroll::fit(), cx);
9833 self.unmark_text(window, cx);
9834 self.refresh_inline_completion(true, false, window, cx);
9835 cx.emit(EditorEvent::Edited { transaction_id });
9836 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9837 }
9838 }
9839
9840 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9841 if self.read_only(cx) {
9842 return;
9843 }
9844
9845 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9846 if let Some((_, Some(selections))) =
9847 self.selection_history.transaction(transaction_id).cloned()
9848 {
9849 self.change_selections(None, window, cx, |s| {
9850 s.select_anchors(selections.to_vec());
9851 });
9852 } else {
9853 log::error!(
9854 "No entry in selection_history found for redo. \
9855 This may correspond to a bug where undo does not update the selection. \
9856 If this is occurring, please add details to \
9857 https://github.com/zed-industries/zed/issues/22692"
9858 );
9859 }
9860 self.request_autoscroll(Autoscroll::fit(), cx);
9861 self.unmark_text(window, cx);
9862 self.refresh_inline_completion(true, false, window, cx);
9863 cx.emit(EditorEvent::Edited { transaction_id });
9864 }
9865 }
9866
9867 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9868 self.buffer
9869 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9870 }
9871
9872 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9873 self.buffer
9874 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9875 }
9876
9877 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9878 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9879 let line_mode = s.line_mode;
9880 s.move_with(|map, selection| {
9881 let cursor = if selection.is_empty() && !line_mode {
9882 movement::left(map, selection.start)
9883 } else {
9884 selection.start
9885 };
9886 selection.collapse_to(cursor, SelectionGoal::None);
9887 });
9888 })
9889 }
9890
9891 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9892 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9893 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9894 })
9895 }
9896
9897 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9898 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9899 let line_mode = s.line_mode;
9900 s.move_with(|map, selection| {
9901 let cursor = if selection.is_empty() && !line_mode {
9902 movement::right(map, selection.end)
9903 } else {
9904 selection.end
9905 };
9906 selection.collapse_to(cursor, SelectionGoal::None)
9907 });
9908 })
9909 }
9910
9911 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9912 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9913 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9914 })
9915 }
9916
9917 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9918 if self.take_rename(true, window, cx).is_some() {
9919 return;
9920 }
9921
9922 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9923 cx.propagate();
9924 return;
9925 }
9926
9927 let text_layout_details = &self.text_layout_details(window);
9928 let selection_count = self.selections.count();
9929 let first_selection = self.selections.first_anchor();
9930
9931 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9932 let line_mode = s.line_mode;
9933 s.move_with(|map, selection| {
9934 if !selection.is_empty() && !line_mode {
9935 selection.goal = SelectionGoal::None;
9936 }
9937 let (cursor, goal) = movement::up(
9938 map,
9939 selection.start,
9940 selection.goal,
9941 false,
9942 text_layout_details,
9943 );
9944 selection.collapse_to(cursor, goal);
9945 });
9946 });
9947
9948 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9949 {
9950 cx.propagate();
9951 }
9952 }
9953
9954 pub fn move_up_by_lines(
9955 &mut self,
9956 action: &MoveUpByLines,
9957 window: &mut Window,
9958 cx: &mut Context<Self>,
9959 ) {
9960 if self.take_rename(true, window, cx).is_some() {
9961 return;
9962 }
9963
9964 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9965 cx.propagate();
9966 return;
9967 }
9968
9969 let text_layout_details = &self.text_layout_details(window);
9970
9971 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9972 let line_mode = s.line_mode;
9973 s.move_with(|map, selection| {
9974 if !selection.is_empty() && !line_mode {
9975 selection.goal = SelectionGoal::None;
9976 }
9977 let (cursor, goal) = movement::up_by_rows(
9978 map,
9979 selection.start,
9980 action.lines,
9981 selection.goal,
9982 false,
9983 text_layout_details,
9984 );
9985 selection.collapse_to(cursor, goal);
9986 });
9987 })
9988 }
9989
9990 pub fn move_down_by_lines(
9991 &mut self,
9992 action: &MoveDownByLines,
9993 window: &mut Window,
9994 cx: &mut Context<Self>,
9995 ) {
9996 if self.take_rename(true, window, cx).is_some() {
9997 return;
9998 }
9999
10000 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10001 cx.propagate();
10002 return;
10003 }
10004
10005 let text_layout_details = &self.text_layout_details(window);
10006
10007 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10008 let line_mode = s.line_mode;
10009 s.move_with(|map, selection| {
10010 if !selection.is_empty() && !line_mode {
10011 selection.goal = SelectionGoal::None;
10012 }
10013 let (cursor, goal) = movement::down_by_rows(
10014 map,
10015 selection.start,
10016 action.lines,
10017 selection.goal,
10018 false,
10019 text_layout_details,
10020 );
10021 selection.collapse_to(cursor, goal);
10022 });
10023 })
10024 }
10025
10026 pub fn select_down_by_lines(
10027 &mut self,
10028 action: &SelectDownByLines,
10029 window: &mut Window,
10030 cx: &mut Context<Self>,
10031 ) {
10032 let text_layout_details = &self.text_layout_details(window);
10033 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10034 s.move_heads_with(|map, head, goal| {
10035 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
10036 })
10037 })
10038 }
10039
10040 pub fn select_up_by_lines(
10041 &mut self,
10042 action: &SelectUpByLines,
10043 window: &mut Window,
10044 cx: &mut Context<Self>,
10045 ) {
10046 let text_layout_details = &self.text_layout_details(window);
10047 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10048 s.move_heads_with(|map, head, goal| {
10049 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
10050 })
10051 })
10052 }
10053
10054 pub fn select_page_up(
10055 &mut self,
10056 _: &SelectPageUp,
10057 window: &mut Window,
10058 cx: &mut Context<Self>,
10059 ) {
10060 let Some(row_count) = self.visible_row_count() else {
10061 return;
10062 };
10063
10064 let text_layout_details = &self.text_layout_details(window);
10065
10066 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10067 s.move_heads_with(|map, head, goal| {
10068 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
10069 })
10070 })
10071 }
10072
10073 pub fn move_page_up(
10074 &mut self,
10075 action: &MovePageUp,
10076 window: &mut Window,
10077 cx: &mut Context<Self>,
10078 ) {
10079 if self.take_rename(true, window, cx).is_some() {
10080 return;
10081 }
10082
10083 if self
10084 .context_menu
10085 .borrow_mut()
10086 .as_mut()
10087 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
10088 .unwrap_or(false)
10089 {
10090 return;
10091 }
10092
10093 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10094 cx.propagate();
10095 return;
10096 }
10097
10098 let Some(row_count) = self.visible_row_count() else {
10099 return;
10100 };
10101
10102 let autoscroll = if action.center_cursor {
10103 Autoscroll::center()
10104 } else {
10105 Autoscroll::fit()
10106 };
10107
10108 let text_layout_details = &self.text_layout_details(window);
10109
10110 self.change_selections(Some(autoscroll), window, cx, |s| {
10111 let line_mode = s.line_mode;
10112 s.move_with(|map, selection| {
10113 if !selection.is_empty() && !line_mode {
10114 selection.goal = SelectionGoal::None;
10115 }
10116 let (cursor, goal) = movement::up_by_rows(
10117 map,
10118 selection.end,
10119 row_count,
10120 selection.goal,
10121 false,
10122 text_layout_details,
10123 );
10124 selection.collapse_to(cursor, goal);
10125 });
10126 });
10127 }
10128
10129 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
10130 let text_layout_details = &self.text_layout_details(window);
10131 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10132 s.move_heads_with(|map, head, goal| {
10133 movement::up(map, head, goal, false, text_layout_details)
10134 })
10135 })
10136 }
10137
10138 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
10139 self.take_rename(true, window, cx);
10140
10141 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10142 cx.propagate();
10143 return;
10144 }
10145
10146 let text_layout_details = &self.text_layout_details(window);
10147 let selection_count = self.selections.count();
10148 let first_selection = self.selections.first_anchor();
10149
10150 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10151 let line_mode = s.line_mode;
10152 s.move_with(|map, selection| {
10153 if !selection.is_empty() && !line_mode {
10154 selection.goal = SelectionGoal::None;
10155 }
10156 let (cursor, goal) = movement::down(
10157 map,
10158 selection.end,
10159 selection.goal,
10160 false,
10161 text_layout_details,
10162 );
10163 selection.collapse_to(cursor, goal);
10164 });
10165 });
10166
10167 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10168 {
10169 cx.propagate();
10170 }
10171 }
10172
10173 pub fn select_page_down(
10174 &mut self,
10175 _: &SelectPageDown,
10176 window: &mut Window,
10177 cx: &mut Context<Self>,
10178 ) {
10179 let Some(row_count) = self.visible_row_count() else {
10180 return;
10181 };
10182
10183 let text_layout_details = &self.text_layout_details(window);
10184
10185 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10186 s.move_heads_with(|map, head, goal| {
10187 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10188 })
10189 })
10190 }
10191
10192 pub fn move_page_down(
10193 &mut self,
10194 action: &MovePageDown,
10195 window: &mut Window,
10196 cx: &mut Context<Self>,
10197 ) {
10198 if self.take_rename(true, window, cx).is_some() {
10199 return;
10200 }
10201
10202 if self
10203 .context_menu
10204 .borrow_mut()
10205 .as_mut()
10206 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10207 .unwrap_or(false)
10208 {
10209 return;
10210 }
10211
10212 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10213 cx.propagate();
10214 return;
10215 }
10216
10217 let Some(row_count) = self.visible_row_count() else {
10218 return;
10219 };
10220
10221 let autoscroll = if action.center_cursor {
10222 Autoscroll::center()
10223 } else {
10224 Autoscroll::fit()
10225 };
10226
10227 let text_layout_details = &self.text_layout_details(window);
10228 self.change_selections(Some(autoscroll), window, cx, |s| {
10229 let line_mode = s.line_mode;
10230 s.move_with(|map, selection| {
10231 if !selection.is_empty() && !line_mode {
10232 selection.goal = SelectionGoal::None;
10233 }
10234 let (cursor, goal) = movement::down_by_rows(
10235 map,
10236 selection.end,
10237 row_count,
10238 selection.goal,
10239 false,
10240 text_layout_details,
10241 );
10242 selection.collapse_to(cursor, goal);
10243 });
10244 });
10245 }
10246
10247 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10248 let text_layout_details = &self.text_layout_details(window);
10249 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10250 s.move_heads_with(|map, head, goal| {
10251 movement::down(map, head, goal, false, text_layout_details)
10252 })
10253 });
10254 }
10255
10256 pub fn context_menu_first(
10257 &mut self,
10258 _: &ContextMenuFirst,
10259 _window: &mut Window,
10260 cx: &mut Context<Self>,
10261 ) {
10262 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10263 context_menu.select_first(self.completion_provider.as_deref(), cx);
10264 }
10265 }
10266
10267 pub fn context_menu_prev(
10268 &mut self,
10269 _: &ContextMenuPrevious,
10270 _window: &mut Window,
10271 cx: &mut Context<Self>,
10272 ) {
10273 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10274 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10275 }
10276 }
10277
10278 pub fn context_menu_next(
10279 &mut self,
10280 _: &ContextMenuNext,
10281 _window: &mut Window,
10282 cx: &mut Context<Self>,
10283 ) {
10284 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10285 context_menu.select_next(self.completion_provider.as_deref(), cx);
10286 }
10287 }
10288
10289 pub fn context_menu_last(
10290 &mut self,
10291 _: &ContextMenuLast,
10292 _window: &mut Window,
10293 cx: &mut Context<Self>,
10294 ) {
10295 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10296 context_menu.select_last(self.completion_provider.as_deref(), cx);
10297 }
10298 }
10299
10300 pub fn move_to_previous_word_start(
10301 &mut self,
10302 _: &MoveToPreviousWordStart,
10303 window: &mut Window,
10304 cx: &mut Context<Self>,
10305 ) {
10306 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10307 s.move_cursors_with(|map, head, _| {
10308 (
10309 movement::previous_word_start(map, head),
10310 SelectionGoal::None,
10311 )
10312 });
10313 })
10314 }
10315
10316 pub fn move_to_previous_subword_start(
10317 &mut self,
10318 _: &MoveToPreviousSubwordStart,
10319 window: &mut Window,
10320 cx: &mut Context<Self>,
10321 ) {
10322 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10323 s.move_cursors_with(|map, head, _| {
10324 (
10325 movement::previous_subword_start(map, head),
10326 SelectionGoal::None,
10327 )
10328 });
10329 })
10330 }
10331
10332 pub fn select_to_previous_word_start(
10333 &mut self,
10334 _: &SelectToPreviousWordStart,
10335 window: &mut Window,
10336 cx: &mut Context<Self>,
10337 ) {
10338 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10339 s.move_heads_with(|map, head, _| {
10340 (
10341 movement::previous_word_start(map, head),
10342 SelectionGoal::None,
10343 )
10344 });
10345 })
10346 }
10347
10348 pub fn select_to_previous_subword_start(
10349 &mut self,
10350 _: &SelectToPreviousSubwordStart,
10351 window: &mut Window,
10352 cx: &mut Context<Self>,
10353 ) {
10354 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10355 s.move_heads_with(|map, head, _| {
10356 (
10357 movement::previous_subword_start(map, head),
10358 SelectionGoal::None,
10359 )
10360 });
10361 })
10362 }
10363
10364 pub fn delete_to_previous_word_start(
10365 &mut self,
10366 action: &DeleteToPreviousWordStart,
10367 window: &mut Window,
10368 cx: &mut Context<Self>,
10369 ) {
10370 self.transact(window, cx, |this, window, cx| {
10371 this.select_autoclose_pair(window, cx);
10372 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10373 let line_mode = s.line_mode;
10374 s.move_with(|map, selection| {
10375 if selection.is_empty() && !line_mode {
10376 let cursor = if action.ignore_newlines {
10377 movement::previous_word_start(map, selection.head())
10378 } else {
10379 movement::previous_word_start_or_newline(map, selection.head())
10380 };
10381 selection.set_head(cursor, SelectionGoal::None);
10382 }
10383 });
10384 });
10385 this.insert("", window, cx);
10386 });
10387 }
10388
10389 pub fn delete_to_previous_subword_start(
10390 &mut self,
10391 _: &DeleteToPreviousSubwordStart,
10392 window: &mut Window,
10393 cx: &mut Context<Self>,
10394 ) {
10395 self.transact(window, cx, |this, window, cx| {
10396 this.select_autoclose_pair(window, cx);
10397 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10398 let line_mode = s.line_mode;
10399 s.move_with(|map, selection| {
10400 if selection.is_empty() && !line_mode {
10401 let cursor = movement::previous_subword_start(map, selection.head());
10402 selection.set_head(cursor, SelectionGoal::None);
10403 }
10404 });
10405 });
10406 this.insert("", window, cx);
10407 });
10408 }
10409
10410 pub fn move_to_next_word_end(
10411 &mut self,
10412 _: &MoveToNextWordEnd,
10413 window: &mut Window,
10414 cx: &mut Context<Self>,
10415 ) {
10416 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10417 s.move_cursors_with(|map, head, _| {
10418 (movement::next_word_end(map, head), SelectionGoal::None)
10419 });
10420 })
10421 }
10422
10423 pub fn move_to_next_subword_end(
10424 &mut self,
10425 _: &MoveToNextSubwordEnd,
10426 window: &mut Window,
10427 cx: &mut Context<Self>,
10428 ) {
10429 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10430 s.move_cursors_with(|map, head, _| {
10431 (movement::next_subword_end(map, head), SelectionGoal::None)
10432 });
10433 })
10434 }
10435
10436 pub fn select_to_next_word_end(
10437 &mut self,
10438 _: &SelectToNextWordEnd,
10439 window: &mut Window,
10440 cx: &mut Context<Self>,
10441 ) {
10442 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10443 s.move_heads_with(|map, head, _| {
10444 (movement::next_word_end(map, head), SelectionGoal::None)
10445 });
10446 })
10447 }
10448
10449 pub fn select_to_next_subword_end(
10450 &mut self,
10451 _: &SelectToNextSubwordEnd,
10452 window: &mut Window,
10453 cx: &mut Context<Self>,
10454 ) {
10455 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10456 s.move_heads_with(|map, head, _| {
10457 (movement::next_subword_end(map, head), SelectionGoal::None)
10458 });
10459 })
10460 }
10461
10462 pub fn delete_to_next_word_end(
10463 &mut self,
10464 action: &DeleteToNextWordEnd,
10465 window: &mut Window,
10466 cx: &mut Context<Self>,
10467 ) {
10468 self.transact(window, cx, |this, window, cx| {
10469 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10470 let line_mode = s.line_mode;
10471 s.move_with(|map, selection| {
10472 if selection.is_empty() && !line_mode {
10473 let cursor = if action.ignore_newlines {
10474 movement::next_word_end(map, selection.head())
10475 } else {
10476 movement::next_word_end_or_newline(map, selection.head())
10477 };
10478 selection.set_head(cursor, SelectionGoal::None);
10479 }
10480 });
10481 });
10482 this.insert("", window, cx);
10483 });
10484 }
10485
10486 pub fn delete_to_next_subword_end(
10487 &mut self,
10488 _: &DeleteToNextSubwordEnd,
10489 window: &mut Window,
10490 cx: &mut Context<Self>,
10491 ) {
10492 self.transact(window, cx, |this, window, cx| {
10493 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10494 s.move_with(|map, selection| {
10495 if selection.is_empty() {
10496 let cursor = movement::next_subword_end(map, selection.head());
10497 selection.set_head(cursor, SelectionGoal::None);
10498 }
10499 });
10500 });
10501 this.insert("", window, cx);
10502 });
10503 }
10504
10505 pub fn move_to_beginning_of_line(
10506 &mut self,
10507 action: &MoveToBeginningOfLine,
10508 window: &mut Window,
10509 cx: &mut Context<Self>,
10510 ) {
10511 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10512 s.move_cursors_with(|map, head, _| {
10513 (
10514 movement::indented_line_beginning(
10515 map,
10516 head,
10517 action.stop_at_soft_wraps,
10518 action.stop_at_indent,
10519 ),
10520 SelectionGoal::None,
10521 )
10522 });
10523 })
10524 }
10525
10526 pub fn select_to_beginning_of_line(
10527 &mut self,
10528 action: &SelectToBeginningOfLine,
10529 window: &mut Window,
10530 cx: &mut Context<Self>,
10531 ) {
10532 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10533 s.move_heads_with(|map, head, _| {
10534 (
10535 movement::indented_line_beginning(
10536 map,
10537 head,
10538 action.stop_at_soft_wraps,
10539 action.stop_at_indent,
10540 ),
10541 SelectionGoal::None,
10542 )
10543 });
10544 });
10545 }
10546
10547 pub fn delete_to_beginning_of_line(
10548 &mut self,
10549 action: &DeleteToBeginningOfLine,
10550 window: &mut Window,
10551 cx: &mut Context<Self>,
10552 ) {
10553 self.transact(window, cx, |this, window, cx| {
10554 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10555 s.move_with(|_, selection| {
10556 selection.reversed = true;
10557 });
10558 });
10559
10560 this.select_to_beginning_of_line(
10561 &SelectToBeginningOfLine {
10562 stop_at_soft_wraps: false,
10563 stop_at_indent: action.stop_at_indent,
10564 },
10565 window,
10566 cx,
10567 );
10568 this.backspace(&Backspace, window, cx);
10569 });
10570 }
10571
10572 pub fn move_to_end_of_line(
10573 &mut self,
10574 action: &MoveToEndOfLine,
10575 window: &mut Window,
10576 cx: &mut Context<Self>,
10577 ) {
10578 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10579 s.move_cursors_with(|map, head, _| {
10580 (
10581 movement::line_end(map, head, action.stop_at_soft_wraps),
10582 SelectionGoal::None,
10583 )
10584 });
10585 })
10586 }
10587
10588 pub fn select_to_end_of_line(
10589 &mut self,
10590 action: &SelectToEndOfLine,
10591 window: &mut Window,
10592 cx: &mut Context<Self>,
10593 ) {
10594 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10595 s.move_heads_with(|map, head, _| {
10596 (
10597 movement::line_end(map, head, action.stop_at_soft_wraps),
10598 SelectionGoal::None,
10599 )
10600 });
10601 })
10602 }
10603
10604 pub fn delete_to_end_of_line(
10605 &mut self,
10606 _: &DeleteToEndOfLine,
10607 window: &mut Window,
10608 cx: &mut Context<Self>,
10609 ) {
10610 self.transact(window, cx, |this, window, cx| {
10611 this.select_to_end_of_line(
10612 &SelectToEndOfLine {
10613 stop_at_soft_wraps: false,
10614 },
10615 window,
10616 cx,
10617 );
10618 this.delete(&Delete, window, cx);
10619 });
10620 }
10621
10622 pub fn cut_to_end_of_line(
10623 &mut self,
10624 _: &CutToEndOfLine,
10625 window: &mut Window,
10626 cx: &mut Context<Self>,
10627 ) {
10628 self.transact(window, cx, |this, window, cx| {
10629 this.select_to_end_of_line(
10630 &SelectToEndOfLine {
10631 stop_at_soft_wraps: false,
10632 },
10633 window,
10634 cx,
10635 );
10636 this.cut(&Cut, window, cx);
10637 });
10638 }
10639
10640 pub fn move_to_start_of_paragraph(
10641 &mut self,
10642 _: &MoveToStartOfParagraph,
10643 window: &mut Window,
10644 cx: &mut Context<Self>,
10645 ) {
10646 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10647 cx.propagate();
10648 return;
10649 }
10650
10651 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10652 s.move_with(|map, selection| {
10653 selection.collapse_to(
10654 movement::start_of_paragraph(map, selection.head(), 1),
10655 SelectionGoal::None,
10656 )
10657 });
10658 })
10659 }
10660
10661 pub fn move_to_end_of_paragraph(
10662 &mut self,
10663 _: &MoveToEndOfParagraph,
10664 window: &mut Window,
10665 cx: &mut Context<Self>,
10666 ) {
10667 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10668 cx.propagate();
10669 return;
10670 }
10671
10672 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10673 s.move_with(|map, selection| {
10674 selection.collapse_to(
10675 movement::end_of_paragraph(map, selection.head(), 1),
10676 SelectionGoal::None,
10677 )
10678 });
10679 })
10680 }
10681
10682 pub fn select_to_start_of_paragraph(
10683 &mut self,
10684 _: &SelectToStartOfParagraph,
10685 window: &mut Window,
10686 cx: &mut Context<Self>,
10687 ) {
10688 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10689 cx.propagate();
10690 return;
10691 }
10692
10693 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10694 s.move_heads_with(|map, head, _| {
10695 (
10696 movement::start_of_paragraph(map, head, 1),
10697 SelectionGoal::None,
10698 )
10699 });
10700 })
10701 }
10702
10703 pub fn select_to_end_of_paragraph(
10704 &mut self,
10705 _: &SelectToEndOfParagraph,
10706 window: &mut Window,
10707 cx: &mut Context<Self>,
10708 ) {
10709 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10710 cx.propagate();
10711 return;
10712 }
10713
10714 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10715 s.move_heads_with(|map, head, _| {
10716 (
10717 movement::end_of_paragraph(map, head, 1),
10718 SelectionGoal::None,
10719 )
10720 });
10721 })
10722 }
10723
10724 pub fn move_to_start_of_excerpt(
10725 &mut self,
10726 _: &MoveToStartOfExcerpt,
10727 window: &mut Window,
10728 cx: &mut Context<Self>,
10729 ) {
10730 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10731 cx.propagate();
10732 return;
10733 }
10734
10735 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10736 s.move_with(|map, selection| {
10737 selection.collapse_to(
10738 movement::start_of_excerpt(
10739 map,
10740 selection.head(),
10741 workspace::searchable::Direction::Prev,
10742 ),
10743 SelectionGoal::None,
10744 )
10745 });
10746 })
10747 }
10748
10749 pub fn move_to_start_of_next_excerpt(
10750 &mut self,
10751 _: &MoveToStartOfNextExcerpt,
10752 window: &mut Window,
10753 cx: &mut Context<Self>,
10754 ) {
10755 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10756 cx.propagate();
10757 return;
10758 }
10759
10760 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10761 s.move_with(|map, selection| {
10762 selection.collapse_to(
10763 movement::start_of_excerpt(
10764 map,
10765 selection.head(),
10766 workspace::searchable::Direction::Next,
10767 ),
10768 SelectionGoal::None,
10769 )
10770 });
10771 })
10772 }
10773
10774 pub fn move_to_end_of_excerpt(
10775 &mut self,
10776 _: &MoveToEndOfExcerpt,
10777 window: &mut Window,
10778 cx: &mut Context<Self>,
10779 ) {
10780 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10781 cx.propagate();
10782 return;
10783 }
10784
10785 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10786 s.move_with(|map, selection| {
10787 selection.collapse_to(
10788 movement::end_of_excerpt(
10789 map,
10790 selection.head(),
10791 workspace::searchable::Direction::Next,
10792 ),
10793 SelectionGoal::None,
10794 )
10795 });
10796 })
10797 }
10798
10799 pub fn move_to_end_of_previous_excerpt(
10800 &mut self,
10801 _: &MoveToEndOfPreviousExcerpt,
10802 window: &mut Window,
10803 cx: &mut Context<Self>,
10804 ) {
10805 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10806 cx.propagate();
10807 return;
10808 }
10809
10810 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10811 s.move_with(|map, selection| {
10812 selection.collapse_to(
10813 movement::end_of_excerpt(
10814 map,
10815 selection.head(),
10816 workspace::searchable::Direction::Prev,
10817 ),
10818 SelectionGoal::None,
10819 )
10820 });
10821 })
10822 }
10823
10824 pub fn select_to_start_of_excerpt(
10825 &mut self,
10826 _: &SelectToStartOfExcerpt,
10827 window: &mut Window,
10828 cx: &mut Context<Self>,
10829 ) {
10830 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10831 cx.propagate();
10832 return;
10833 }
10834
10835 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10836 s.move_heads_with(|map, head, _| {
10837 (
10838 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10839 SelectionGoal::None,
10840 )
10841 });
10842 })
10843 }
10844
10845 pub fn select_to_start_of_next_excerpt(
10846 &mut self,
10847 _: &SelectToStartOfNextExcerpt,
10848 window: &mut Window,
10849 cx: &mut Context<Self>,
10850 ) {
10851 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10852 cx.propagate();
10853 return;
10854 }
10855
10856 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10857 s.move_heads_with(|map, head, _| {
10858 (
10859 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10860 SelectionGoal::None,
10861 )
10862 });
10863 })
10864 }
10865
10866 pub fn select_to_end_of_excerpt(
10867 &mut self,
10868 _: &SelectToEndOfExcerpt,
10869 window: &mut Window,
10870 cx: &mut Context<Self>,
10871 ) {
10872 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10873 cx.propagate();
10874 return;
10875 }
10876
10877 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10878 s.move_heads_with(|map, head, _| {
10879 (
10880 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10881 SelectionGoal::None,
10882 )
10883 });
10884 })
10885 }
10886
10887 pub fn select_to_end_of_previous_excerpt(
10888 &mut self,
10889 _: &SelectToEndOfPreviousExcerpt,
10890 window: &mut Window,
10891 cx: &mut Context<Self>,
10892 ) {
10893 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10894 cx.propagate();
10895 return;
10896 }
10897
10898 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10899 s.move_heads_with(|map, head, _| {
10900 (
10901 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10902 SelectionGoal::None,
10903 )
10904 });
10905 })
10906 }
10907
10908 pub fn move_to_beginning(
10909 &mut self,
10910 _: &MoveToBeginning,
10911 window: &mut Window,
10912 cx: &mut Context<Self>,
10913 ) {
10914 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10915 cx.propagate();
10916 return;
10917 }
10918
10919 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10920 s.select_ranges(vec![0..0]);
10921 });
10922 }
10923
10924 pub fn select_to_beginning(
10925 &mut self,
10926 _: &SelectToBeginning,
10927 window: &mut Window,
10928 cx: &mut Context<Self>,
10929 ) {
10930 let mut selection = self.selections.last::<Point>(cx);
10931 selection.set_head(Point::zero(), SelectionGoal::None);
10932
10933 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10934 s.select(vec![selection]);
10935 });
10936 }
10937
10938 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10939 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10940 cx.propagate();
10941 return;
10942 }
10943
10944 let cursor = self.buffer.read(cx).read(cx).len();
10945 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10946 s.select_ranges(vec![cursor..cursor])
10947 });
10948 }
10949
10950 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10951 self.nav_history = nav_history;
10952 }
10953
10954 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10955 self.nav_history.as_ref()
10956 }
10957
10958 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10959 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10960 }
10961
10962 fn push_to_nav_history(
10963 &mut self,
10964 cursor_anchor: Anchor,
10965 new_position: Option<Point>,
10966 is_deactivate: bool,
10967 cx: &mut Context<Self>,
10968 ) {
10969 if let Some(nav_history) = self.nav_history.as_mut() {
10970 let buffer = self.buffer.read(cx).read(cx);
10971 let cursor_position = cursor_anchor.to_point(&buffer);
10972 let scroll_state = self.scroll_manager.anchor();
10973 let scroll_top_row = scroll_state.top_row(&buffer);
10974 drop(buffer);
10975
10976 if let Some(new_position) = new_position {
10977 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10978 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10979 return;
10980 }
10981 }
10982
10983 nav_history.push(
10984 Some(NavigationData {
10985 cursor_anchor,
10986 cursor_position,
10987 scroll_anchor: scroll_state,
10988 scroll_top_row,
10989 }),
10990 cx,
10991 );
10992 cx.emit(EditorEvent::PushedToNavHistory {
10993 anchor: cursor_anchor,
10994 is_deactivate,
10995 })
10996 }
10997 }
10998
10999 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
11000 let buffer = self.buffer.read(cx).snapshot(cx);
11001 let mut selection = self.selections.first::<usize>(cx);
11002 selection.set_head(buffer.len(), SelectionGoal::None);
11003 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11004 s.select(vec![selection]);
11005 });
11006 }
11007
11008 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
11009 let end = self.buffer.read(cx).read(cx).len();
11010 self.change_selections(None, window, cx, |s| {
11011 s.select_ranges(vec![0..end]);
11012 });
11013 }
11014
11015 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
11016 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11017 let mut selections = self.selections.all::<Point>(cx);
11018 let max_point = display_map.buffer_snapshot.max_point();
11019 for selection in &mut selections {
11020 let rows = selection.spanned_rows(true, &display_map);
11021 selection.start = Point::new(rows.start.0, 0);
11022 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
11023 selection.reversed = false;
11024 }
11025 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11026 s.select(selections);
11027 });
11028 }
11029
11030 pub fn split_selection_into_lines(
11031 &mut self,
11032 _: &SplitSelectionIntoLines,
11033 window: &mut Window,
11034 cx: &mut Context<Self>,
11035 ) {
11036 let selections = self
11037 .selections
11038 .all::<Point>(cx)
11039 .into_iter()
11040 .map(|selection| selection.start..selection.end)
11041 .collect::<Vec<_>>();
11042 self.unfold_ranges(&selections, true, true, cx);
11043
11044 let mut new_selection_ranges = Vec::new();
11045 {
11046 let buffer = self.buffer.read(cx).read(cx);
11047 for selection in selections {
11048 for row in selection.start.row..selection.end.row {
11049 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11050 new_selection_ranges.push(cursor..cursor);
11051 }
11052
11053 let is_multiline_selection = selection.start.row != selection.end.row;
11054 // Don't insert last one if it's a multi-line selection ending at the start of a line,
11055 // so this action feels more ergonomic when paired with other selection operations
11056 let should_skip_last = is_multiline_selection && selection.end.column == 0;
11057 if !should_skip_last {
11058 new_selection_ranges.push(selection.end..selection.end);
11059 }
11060 }
11061 }
11062 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11063 s.select_ranges(new_selection_ranges);
11064 });
11065 }
11066
11067 pub fn add_selection_above(
11068 &mut self,
11069 _: &AddSelectionAbove,
11070 window: &mut Window,
11071 cx: &mut Context<Self>,
11072 ) {
11073 self.add_selection(true, window, cx);
11074 }
11075
11076 pub fn add_selection_below(
11077 &mut self,
11078 _: &AddSelectionBelow,
11079 window: &mut Window,
11080 cx: &mut Context<Self>,
11081 ) {
11082 self.add_selection(false, window, cx);
11083 }
11084
11085 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
11086 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11087 let mut selections = self.selections.all::<Point>(cx);
11088 let text_layout_details = self.text_layout_details(window);
11089 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
11090 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
11091 let range = oldest_selection.display_range(&display_map).sorted();
11092
11093 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
11094 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
11095 let positions = start_x.min(end_x)..start_x.max(end_x);
11096
11097 selections.clear();
11098 let mut stack = Vec::new();
11099 for row in range.start.row().0..=range.end.row().0 {
11100 if let Some(selection) = self.selections.build_columnar_selection(
11101 &display_map,
11102 DisplayRow(row),
11103 &positions,
11104 oldest_selection.reversed,
11105 &text_layout_details,
11106 ) {
11107 stack.push(selection.id);
11108 selections.push(selection);
11109 }
11110 }
11111
11112 if above {
11113 stack.reverse();
11114 }
11115
11116 AddSelectionsState { above, stack }
11117 });
11118
11119 let last_added_selection = *state.stack.last().unwrap();
11120 let mut new_selections = Vec::new();
11121 if above == state.above {
11122 let end_row = if above {
11123 DisplayRow(0)
11124 } else {
11125 display_map.max_point().row()
11126 };
11127
11128 'outer: for selection in selections {
11129 if selection.id == last_added_selection {
11130 let range = selection.display_range(&display_map).sorted();
11131 debug_assert_eq!(range.start.row(), range.end.row());
11132 let mut row = range.start.row();
11133 let positions =
11134 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
11135 px(start)..px(end)
11136 } else {
11137 let start_x =
11138 display_map.x_for_display_point(range.start, &text_layout_details);
11139 let end_x =
11140 display_map.x_for_display_point(range.end, &text_layout_details);
11141 start_x.min(end_x)..start_x.max(end_x)
11142 };
11143
11144 while row != end_row {
11145 if above {
11146 row.0 -= 1;
11147 } else {
11148 row.0 += 1;
11149 }
11150
11151 if let Some(new_selection) = self.selections.build_columnar_selection(
11152 &display_map,
11153 row,
11154 &positions,
11155 selection.reversed,
11156 &text_layout_details,
11157 ) {
11158 state.stack.push(new_selection.id);
11159 if above {
11160 new_selections.push(new_selection);
11161 new_selections.push(selection);
11162 } else {
11163 new_selections.push(selection);
11164 new_selections.push(new_selection);
11165 }
11166
11167 continue 'outer;
11168 }
11169 }
11170 }
11171
11172 new_selections.push(selection);
11173 }
11174 } else {
11175 new_selections = selections;
11176 new_selections.retain(|s| s.id != last_added_selection);
11177 state.stack.pop();
11178 }
11179
11180 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11181 s.select(new_selections);
11182 });
11183 if state.stack.len() > 1 {
11184 self.add_selections_state = Some(state);
11185 }
11186 }
11187
11188 pub fn select_next_match_internal(
11189 &mut self,
11190 display_map: &DisplaySnapshot,
11191 replace_newest: bool,
11192 autoscroll: Option<Autoscroll>,
11193 window: &mut Window,
11194 cx: &mut Context<Self>,
11195 ) -> Result<()> {
11196 fn select_next_match_ranges(
11197 this: &mut Editor,
11198 range: Range<usize>,
11199 replace_newest: bool,
11200 auto_scroll: Option<Autoscroll>,
11201 window: &mut Window,
11202 cx: &mut Context<Editor>,
11203 ) {
11204 this.unfold_ranges(&[range.clone()], false, true, cx);
11205 this.change_selections(auto_scroll, window, cx, |s| {
11206 if replace_newest {
11207 s.delete(s.newest_anchor().id);
11208 }
11209 s.insert_range(range.clone());
11210 });
11211 }
11212
11213 let buffer = &display_map.buffer_snapshot;
11214 let mut selections = self.selections.all::<usize>(cx);
11215 if let Some(mut select_next_state) = self.select_next_state.take() {
11216 let query = &select_next_state.query;
11217 if !select_next_state.done {
11218 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11219 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11220 let mut next_selected_range = None;
11221
11222 let bytes_after_last_selection =
11223 buffer.bytes_in_range(last_selection.end..buffer.len());
11224 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11225 let query_matches = query
11226 .stream_find_iter(bytes_after_last_selection)
11227 .map(|result| (last_selection.end, result))
11228 .chain(
11229 query
11230 .stream_find_iter(bytes_before_first_selection)
11231 .map(|result| (0, result)),
11232 );
11233
11234 for (start_offset, query_match) in query_matches {
11235 let query_match = query_match.unwrap(); // can only fail due to I/O
11236 let offset_range =
11237 start_offset + query_match.start()..start_offset + query_match.end();
11238 let display_range = offset_range.start.to_display_point(display_map)
11239 ..offset_range.end.to_display_point(display_map);
11240
11241 if !select_next_state.wordwise
11242 || (!movement::is_inside_word(display_map, display_range.start)
11243 && !movement::is_inside_word(display_map, display_range.end))
11244 {
11245 // TODO: This is n^2, because we might check all the selections
11246 if !selections
11247 .iter()
11248 .any(|selection| selection.range().overlaps(&offset_range))
11249 {
11250 next_selected_range = Some(offset_range);
11251 break;
11252 }
11253 }
11254 }
11255
11256 if let Some(next_selected_range) = next_selected_range {
11257 select_next_match_ranges(
11258 self,
11259 next_selected_range,
11260 replace_newest,
11261 autoscroll,
11262 window,
11263 cx,
11264 );
11265 } else {
11266 select_next_state.done = true;
11267 }
11268 }
11269
11270 self.select_next_state = Some(select_next_state);
11271 } else {
11272 let mut only_carets = true;
11273 let mut same_text_selected = true;
11274 let mut selected_text = None;
11275
11276 let mut selections_iter = selections.iter().peekable();
11277 while let Some(selection) = selections_iter.next() {
11278 if selection.start != selection.end {
11279 only_carets = false;
11280 }
11281
11282 if same_text_selected {
11283 if selected_text.is_none() {
11284 selected_text =
11285 Some(buffer.text_for_range(selection.range()).collect::<String>());
11286 }
11287
11288 if let Some(next_selection) = selections_iter.peek() {
11289 if next_selection.range().len() == selection.range().len() {
11290 let next_selected_text = buffer
11291 .text_for_range(next_selection.range())
11292 .collect::<String>();
11293 if Some(next_selected_text) != selected_text {
11294 same_text_selected = false;
11295 selected_text = None;
11296 }
11297 } else {
11298 same_text_selected = false;
11299 selected_text = None;
11300 }
11301 }
11302 }
11303 }
11304
11305 if only_carets {
11306 for selection in &mut selections {
11307 let word_range = movement::surrounding_word(
11308 display_map,
11309 selection.start.to_display_point(display_map),
11310 );
11311 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11312 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11313 selection.goal = SelectionGoal::None;
11314 selection.reversed = false;
11315 select_next_match_ranges(
11316 self,
11317 selection.start..selection.end,
11318 replace_newest,
11319 autoscroll,
11320 window,
11321 cx,
11322 );
11323 }
11324
11325 if selections.len() == 1 {
11326 let selection = selections
11327 .last()
11328 .expect("ensured that there's only one selection");
11329 let query = buffer
11330 .text_for_range(selection.start..selection.end)
11331 .collect::<String>();
11332 let is_empty = query.is_empty();
11333 let select_state = SelectNextState {
11334 query: AhoCorasick::new(&[query])?,
11335 wordwise: true,
11336 done: is_empty,
11337 };
11338 self.select_next_state = Some(select_state);
11339 } else {
11340 self.select_next_state = None;
11341 }
11342 } else if let Some(selected_text) = selected_text {
11343 self.select_next_state = Some(SelectNextState {
11344 query: AhoCorasick::new(&[selected_text])?,
11345 wordwise: false,
11346 done: false,
11347 });
11348 self.select_next_match_internal(
11349 display_map,
11350 replace_newest,
11351 autoscroll,
11352 window,
11353 cx,
11354 )?;
11355 }
11356 }
11357 Ok(())
11358 }
11359
11360 pub fn select_all_matches(
11361 &mut self,
11362 _action: &SelectAllMatches,
11363 window: &mut Window,
11364 cx: &mut Context<Self>,
11365 ) -> Result<()> {
11366 self.push_to_selection_history();
11367 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11368
11369 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11370 let Some(select_next_state) = self.select_next_state.as_mut() else {
11371 return Ok(());
11372 };
11373 if select_next_state.done {
11374 return Ok(());
11375 }
11376
11377 let mut new_selections = self.selections.all::<usize>(cx);
11378
11379 let buffer = &display_map.buffer_snapshot;
11380 let query_matches = select_next_state
11381 .query
11382 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11383
11384 for query_match in query_matches {
11385 let query_match = query_match.unwrap(); // can only fail due to I/O
11386 let offset_range = query_match.start()..query_match.end();
11387 let display_range = offset_range.start.to_display_point(&display_map)
11388 ..offset_range.end.to_display_point(&display_map);
11389
11390 if !select_next_state.wordwise
11391 || (!movement::is_inside_word(&display_map, display_range.start)
11392 && !movement::is_inside_word(&display_map, display_range.end))
11393 {
11394 self.selections.change_with(cx, |selections| {
11395 new_selections.push(Selection {
11396 id: selections.new_selection_id(),
11397 start: offset_range.start,
11398 end: offset_range.end,
11399 reversed: false,
11400 goal: SelectionGoal::None,
11401 });
11402 });
11403 }
11404 }
11405
11406 new_selections.sort_by_key(|selection| selection.start);
11407 let mut ix = 0;
11408 while ix + 1 < new_selections.len() {
11409 let current_selection = &new_selections[ix];
11410 let next_selection = &new_selections[ix + 1];
11411 if current_selection.range().overlaps(&next_selection.range()) {
11412 if current_selection.id < next_selection.id {
11413 new_selections.remove(ix + 1);
11414 } else {
11415 new_selections.remove(ix);
11416 }
11417 } else {
11418 ix += 1;
11419 }
11420 }
11421
11422 let reversed = self.selections.oldest::<usize>(cx).reversed;
11423
11424 for selection in new_selections.iter_mut() {
11425 selection.reversed = reversed;
11426 }
11427
11428 select_next_state.done = true;
11429 self.unfold_ranges(
11430 &new_selections
11431 .iter()
11432 .map(|selection| selection.range())
11433 .collect::<Vec<_>>(),
11434 false,
11435 false,
11436 cx,
11437 );
11438 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11439 selections.select(new_selections)
11440 });
11441
11442 Ok(())
11443 }
11444
11445 pub fn select_next(
11446 &mut self,
11447 action: &SelectNext,
11448 window: &mut Window,
11449 cx: &mut Context<Self>,
11450 ) -> Result<()> {
11451 self.push_to_selection_history();
11452 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11453 self.select_next_match_internal(
11454 &display_map,
11455 action.replace_newest,
11456 Some(Autoscroll::newest()),
11457 window,
11458 cx,
11459 )?;
11460 Ok(())
11461 }
11462
11463 pub fn select_previous(
11464 &mut self,
11465 action: &SelectPrevious,
11466 window: &mut Window,
11467 cx: &mut Context<Self>,
11468 ) -> Result<()> {
11469 self.push_to_selection_history();
11470 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11471 let buffer = &display_map.buffer_snapshot;
11472 let mut selections = self.selections.all::<usize>(cx);
11473 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11474 let query = &select_prev_state.query;
11475 if !select_prev_state.done {
11476 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11477 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11478 let mut next_selected_range = None;
11479 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11480 let bytes_before_last_selection =
11481 buffer.reversed_bytes_in_range(0..last_selection.start);
11482 let bytes_after_first_selection =
11483 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11484 let query_matches = query
11485 .stream_find_iter(bytes_before_last_selection)
11486 .map(|result| (last_selection.start, result))
11487 .chain(
11488 query
11489 .stream_find_iter(bytes_after_first_selection)
11490 .map(|result| (buffer.len(), result)),
11491 );
11492 for (end_offset, query_match) in query_matches {
11493 let query_match = query_match.unwrap(); // can only fail due to I/O
11494 let offset_range =
11495 end_offset - query_match.end()..end_offset - query_match.start();
11496 let display_range = offset_range.start.to_display_point(&display_map)
11497 ..offset_range.end.to_display_point(&display_map);
11498
11499 if !select_prev_state.wordwise
11500 || (!movement::is_inside_word(&display_map, display_range.start)
11501 && !movement::is_inside_word(&display_map, display_range.end))
11502 {
11503 next_selected_range = Some(offset_range);
11504 break;
11505 }
11506 }
11507
11508 if let Some(next_selected_range) = next_selected_range {
11509 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11510 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11511 if action.replace_newest {
11512 s.delete(s.newest_anchor().id);
11513 }
11514 s.insert_range(next_selected_range);
11515 });
11516 } else {
11517 select_prev_state.done = true;
11518 }
11519 }
11520
11521 self.select_prev_state = Some(select_prev_state);
11522 } else {
11523 let mut only_carets = true;
11524 let mut same_text_selected = true;
11525 let mut selected_text = None;
11526
11527 let mut selections_iter = selections.iter().peekable();
11528 while let Some(selection) = selections_iter.next() {
11529 if selection.start != selection.end {
11530 only_carets = false;
11531 }
11532
11533 if same_text_selected {
11534 if selected_text.is_none() {
11535 selected_text =
11536 Some(buffer.text_for_range(selection.range()).collect::<String>());
11537 }
11538
11539 if let Some(next_selection) = selections_iter.peek() {
11540 if next_selection.range().len() == selection.range().len() {
11541 let next_selected_text = buffer
11542 .text_for_range(next_selection.range())
11543 .collect::<String>();
11544 if Some(next_selected_text) != selected_text {
11545 same_text_selected = false;
11546 selected_text = None;
11547 }
11548 } else {
11549 same_text_selected = false;
11550 selected_text = None;
11551 }
11552 }
11553 }
11554 }
11555
11556 if only_carets {
11557 for selection in &mut selections {
11558 let word_range = movement::surrounding_word(
11559 &display_map,
11560 selection.start.to_display_point(&display_map),
11561 );
11562 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11563 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11564 selection.goal = SelectionGoal::None;
11565 selection.reversed = false;
11566 }
11567 if selections.len() == 1 {
11568 let selection = selections
11569 .last()
11570 .expect("ensured that there's only one selection");
11571 let query = buffer
11572 .text_for_range(selection.start..selection.end)
11573 .collect::<String>();
11574 let is_empty = query.is_empty();
11575 let select_state = SelectNextState {
11576 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11577 wordwise: true,
11578 done: is_empty,
11579 };
11580 self.select_prev_state = Some(select_state);
11581 } else {
11582 self.select_prev_state = None;
11583 }
11584
11585 self.unfold_ranges(
11586 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11587 false,
11588 true,
11589 cx,
11590 );
11591 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11592 s.select(selections);
11593 });
11594 } else if let Some(selected_text) = selected_text {
11595 self.select_prev_state = Some(SelectNextState {
11596 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11597 wordwise: false,
11598 done: false,
11599 });
11600 self.select_previous(action, window, cx)?;
11601 }
11602 }
11603 Ok(())
11604 }
11605
11606 pub fn toggle_comments(
11607 &mut self,
11608 action: &ToggleComments,
11609 window: &mut Window,
11610 cx: &mut Context<Self>,
11611 ) {
11612 if self.read_only(cx) {
11613 return;
11614 }
11615 let text_layout_details = &self.text_layout_details(window);
11616 self.transact(window, cx, |this, window, cx| {
11617 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11618 let mut edits = Vec::new();
11619 let mut selection_edit_ranges = Vec::new();
11620 let mut last_toggled_row = None;
11621 let snapshot = this.buffer.read(cx).read(cx);
11622 let empty_str: Arc<str> = Arc::default();
11623 let mut suffixes_inserted = Vec::new();
11624 let ignore_indent = action.ignore_indent;
11625
11626 fn comment_prefix_range(
11627 snapshot: &MultiBufferSnapshot,
11628 row: MultiBufferRow,
11629 comment_prefix: &str,
11630 comment_prefix_whitespace: &str,
11631 ignore_indent: bool,
11632 ) -> Range<Point> {
11633 let indent_size = if ignore_indent {
11634 0
11635 } else {
11636 snapshot.indent_size_for_line(row).len
11637 };
11638
11639 let start = Point::new(row.0, indent_size);
11640
11641 let mut line_bytes = snapshot
11642 .bytes_in_range(start..snapshot.max_point())
11643 .flatten()
11644 .copied();
11645
11646 // If this line currently begins with the line comment prefix, then record
11647 // the range containing the prefix.
11648 if line_bytes
11649 .by_ref()
11650 .take(comment_prefix.len())
11651 .eq(comment_prefix.bytes())
11652 {
11653 // Include any whitespace that matches the comment prefix.
11654 let matching_whitespace_len = line_bytes
11655 .zip(comment_prefix_whitespace.bytes())
11656 .take_while(|(a, b)| a == b)
11657 .count() as u32;
11658 let end = Point::new(
11659 start.row,
11660 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11661 );
11662 start..end
11663 } else {
11664 start..start
11665 }
11666 }
11667
11668 fn comment_suffix_range(
11669 snapshot: &MultiBufferSnapshot,
11670 row: MultiBufferRow,
11671 comment_suffix: &str,
11672 comment_suffix_has_leading_space: bool,
11673 ) -> Range<Point> {
11674 let end = Point::new(row.0, snapshot.line_len(row));
11675 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11676
11677 let mut line_end_bytes = snapshot
11678 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11679 .flatten()
11680 .copied();
11681
11682 let leading_space_len = if suffix_start_column > 0
11683 && line_end_bytes.next() == Some(b' ')
11684 && comment_suffix_has_leading_space
11685 {
11686 1
11687 } else {
11688 0
11689 };
11690
11691 // If this line currently begins with the line comment prefix, then record
11692 // the range containing the prefix.
11693 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11694 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11695 start..end
11696 } else {
11697 end..end
11698 }
11699 }
11700
11701 // TODO: Handle selections that cross excerpts
11702 for selection in &mut selections {
11703 let start_column = snapshot
11704 .indent_size_for_line(MultiBufferRow(selection.start.row))
11705 .len;
11706 let language = if let Some(language) =
11707 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11708 {
11709 language
11710 } else {
11711 continue;
11712 };
11713
11714 selection_edit_ranges.clear();
11715
11716 // If multiple selections contain a given row, avoid processing that
11717 // row more than once.
11718 let mut start_row = MultiBufferRow(selection.start.row);
11719 if last_toggled_row == Some(start_row) {
11720 start_row = start_row.next_row();
11721 }
11722 let end_row =
11723 if selection.end.row > selection.start.row && selection.end.column == 0 {
11724 MultiBufferRow(selection.end.row - 1)
11725 } else {
11726 MultiBufferRow(selection.end.row)
11727 };
11728 last_toggled_row = Some(end_row);
11729
11730 if start_row > end_row {
11731 continue;
11732 }
11733
11734 // If the language has line comments, toggle those.
11735 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11736
11737 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11738 if ignore_indent {
11739 full_comment_prefixes = full_comment_prefixes
11740 .into_iter()
11741 .map(|s| Arc::from(s.trim_end()))
11742 .collect();
11743 }
11744
11745 if !full_comment_prefixes.is_empty() {
11746 let first_prefix = full_comment_prefixes
11747 .first()
11748 .expect("prefixes is non-empty");
11749 let prefix_trimmed_lengths = full_comment_prefixes
11750 .iter()
11751 .map(|p| p.trim_end_matches(' ').len())
11752 .collect::<SmallVec<[usize; 4]>>();
11753
11754 let mut all_selection_lines_are_comments = true;
11755
11756 for row in start_row.0..=end_row.0 {
11757 let row = MultiBufferRow(row);
11758 if start_row < end_row && snapshot.is_line_blank(row) {
11759 continue;
11760 }
11761
11762 let prefix_range = full_comment_prefixes
11763 .iter()
11764 .zip(prefix_trimmed_lengths.iter().copied())
11765 .map(|(prefix, trimmed_prefix_len)| {
11766 comment_prefix_range(
11767 snapshot.deref(),
11768 row,
11769 &prefix[..trimmed_prefix_len],
11770 &prefix[trimmed_prefix_len..],
11771 ignore_indent,
11772 )
11773 })
11774 .max_by_key(|range| range.end.column - range.start.column)
11775 .expect("prefixes is non-empty");
11776
11777 if prefix_range.is_empty() {
11778 all_selection_lines_are_comments = false;
11779 }
11780
11781 selection_edit_ranges.push(prefix_range);
11782 }
11783
11784 if all_selection_lines_are_comments {
11785 edits.extend(
11786 selection_edit_ranges
11787 .iter()
11788 .cloned()
11789 .map(|range| (range, empty_str.clone())),
11790 );
11791 } else {
11792 let min_column = selection_edit_ranges
11793 .iter()
11794 .map(|range| range.start.column)
11795 .min()
11796 .unwrap_or(0);
11797 edits.extend(selection_edit_ranges.iter().map(|range| {
11798 let position = Point::new(range.start.row, min_column);
11799 (position..position, first_prefix.clone())
11800 }));
11801 }
11802 } else if let Some((full_comment_prefix, comment_suffix)) =
11803 language.block_comment_delimiters()
11804 {
11805 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11806 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11807 let prefix_range = comment_prefix_range(
11808 snapshot.deref(),
11809 start_row,
11810 comment_prefix,
11811 comment_prefix_whitespace,
11812 ignore_indent,
11813 );
11814 let suffix_range = comment_suffix_range(
11815 snapshot.deref(),
11816 end_row,
11817 comment_suffix.trim_start_matches(' '),
11818 comment_suffix.starts_with(' '),
11819 );
11820
11821 if prefix_range.is_empty() || suffix_range.is_empty() {
11822 edits.push((
11823 prefix_range.start..prefix_range.start,
11824 full_comment_prefix.clone(),
11825 ));
11826 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11827 suffixes_inserted.push((end_row, comment_suffix.len()));
11828 } else {
11829 edits.push((prefix_range, empty_str.clone()));
11830 edits.push((suffix_range, empty_str.clone()));
11831 }
11832 } else {
11833 continue;
11834 }
11835 }
11836
11837 drop(snapshot);
11838 this.buffer.update(cx, |buffer, cx| {
11839 buffer.edit(edits, None, cx);
11840 });
11841
11842 // Adjust selections so that they end before any comment suffixes that
11843 // were inserted.
11844 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11845 let mut selections = this.selections.all::<Point>(cx);
11846 let snapshot = this.buffer.read(cx).read(cx);
11847 for selection in &mut selections {
11848 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11849 match row.cmp(&MultiBufferRow(selection.end.row)) {
11850 Ordering::Less => {
11851 suffixes_inserted.next();
11852 continue;
11853 }
11854 Ordering::Greater => break,
11855 Ordering::Equal => {
11856 if selection.end.column == snapshot.line_len(row) {
11857 if selection.is_empty() {
11858 selection.start.column -= suffix_len as u32;
11859 }
11860 selection.end.column -= suffix_len as u32;
11861 }
11862 break;
11863 }
11864 }
11865 }
11866 }
11867
11868 drop(snapshot);
11869 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11870 s.select(selections)
11871 });
11872
11873 let selections = this.selections.all::<Point>(cx);
11874 let selections_on_single_row = selections.windows(2).all(|selections| {
11875 selections[0].start.row == selections[1].start.row
11876 && selections[0].end.row == selections[1].end.row
11877 && selections[0].start.row == selections[0].end.row
11878 });
11879 let selections_selecting = selections
11880 .iter()
11881 .any(|selection| selection.start != selection.end);
11882 let advance_downwards = action.advance_downwards
11883 && selections_on_single_row
11884 && !selections_selecting
11885 && !matches!(this.mode, EditorMode::SingleLine { .. });
11886
11887 if advance_downwards {
11888 let snapshot = this.buffer.read(cx).snapshot(cx);
11889
11890 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11891 s.move_cursors_with(|display_snapshot, display_point, _| {
11892 let mut point = display_point.to_point(display_snapshot);
11893 point.row += 1;
11894 point = snapshot.clip_point(point, Bias::Left);
11895 let display_point = point.to_display_point(display_snapshot);
11896 let goal = SelectionGoal::HorizontalPosition(
11897 display_snapshot
11898 .x_for_display_point(display_point, text_layout_details)
11899 .into(),
11900 );
11901 (display_point, goal)
11902 })
11903 });
11904 }
11905 });
11906 }
11907
11908 pub fn select_enclosing_symbol(
11909 &mut self,
11910 _: &SelectEnclosingSymbol,
11911 window: &mut Window,
11912 cx: &mut Context<Self>,
11913 ) {
11914 let buffer = self.buffer.read(cx).snapshot(cx);
11915 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11916
11917 fn update_selection(
11918 selection: &Selection<usize>,
11919 buffer_snap: &MultiBufferSnapshot,
11920 ) -> Option<Selection<usize>> {
11921 let cursor = selection.head();
11922 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11923 for symbol in symbols.iter().rev() {
11924 let start = symbol.range.start.to_offset(buffer_snap);
11925 let end = symbol.range.end.to_offset(buffer_snap);
11926 let new_range = start..end;
11927 if start < selection.start || end > selection.end {
11928 return Some(Selection {
11929 id: selection.id,
11930 start: new_range.start,
11931 end: new_range.end,
11932 goal: SelectionGoal::None,
11933 reversed: selection.reversed,
11934 });
11935 }
11936 }
11937 None
11938 }
11939
11940 let mut selected_larger_symbol = false;
11941 let new_selections = old_selections
11942 .iter()
11943 .map(|selection| match update_selection(selection, &buffer) {
11944 Some(new_selection) => {
11945 if new_selection.range() != selection.range() {
11946 selected_larger_symbol = true;
11947 }
11948 new_selection
11949 }
11950 None => selection.clone(),
11951 })
11952 .collect::<Vec<_>>();
11953
11954 if selected_larger_symbol {
11955 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11956 s.select(new_selections);
11957 });
11958 }
11959 }
11960
11961 pub fn select_larger_syntax_node(
11962 &mut self,
11963 _: &SelectLargerSyntaxNode,
11964 window: &mut Window,
11965 cx: &mut Context<Self>,
11966 ) {
11967 let Some(visible_row_count) = self.visible_row_count() else {
11968 return;
11969 };
11970 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
11971 if old_selections.is_empty() {
11972 return;
11973 }
11974
11975 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11976 let buffer = self.buffer.read(cx).snapshot(cx);
11977
11978 let mut selected_larger_node = false;
11979 let mut new_selections = old_selections
11980 .iter()
11981 .map(|selection| {
11982 let old_range = selection.start..selection.end;
11983 let mut new_range = old_range.clone();
11984 let mut new_node = None;
11985 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11986 {
11987 new_node = Some(node);
11988 new_range = match containing_range {
11989 MultiOrSingleBufferOffsetRange::Single(_) => break,
11990 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11991 };
11992 if !display_map.intersects_fold(new_range.start)
11993 && !display_map.intersects_fold(new_range.end)
11994 {
11995 break;
11996 }
11997 }
11998
11999 if let Some(node) = new_node {
12000 // Log the ancestor, to support using this action as a way to explore TreeSitter
12001 // nodes. Parent and grandparent are also logged because this operation will not
12002 // visit nodes that have the same range as their parent.
12003 log::info!("Node: {node:?}");
12004 let parent = node.parent();
12005 log::info!("Parent: {parent:?}");
12006 let grandparent = parent.and_then(|x| x.parent());
12007 log::info!("Grandparent: {grandparent:?}");
12008 }
12009
12010 selected_larger_node |= new_range != old_range;
12011 Selection {
12012 id: selection.id,
12013 start: new_range.start,
12014 end: new_range.end,
12015 goal: SelectionGoal::None,
12016 reversed: selection.reversed,
12017 }
12018 })
12019 .collect::<Vec<_>>();
12020
12021 if !selected_larger_node {
12022 return; // don't put this call in the history
12023 }
12024
12025 // scroll based on transformation done to the last selection created by the user
12026 let (last_old, last_new) = old_selections
12027 .last()
12028 .zip(new_selections.last().cloned())
12029 .expect("old_selections isn't empty");
12030
12031 // revert selection
12032 let is_selection_reversed = {
12033 let should_newest_selection_be_reversed = last_old.start != last_new.start;
12034 new_selections.last_mut().expect("checked above").reversed =
12035 should_newest_selection_be_reversed;
12036 should_newest_selection_be_reversed
12037 };
12038
12039 if selected_larger_node {
12040 self.select_syntax_node_history.disable_clearing = true;
12041 self.change_selections(None, window, cx, |s| {
12042 s.select(new_selections.clone());
12043 });
12044 self.select_syntax_node_history.disable_clearing = false;
12045 }
12046
12047 let start_row = last_new.start.to_display_point(&display_map).row().0;
12048 let end_row = last_new.end.to_display_point(&display_map).row().0;
12049 let selection_height = end_row - start_row + 1;
12050 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
12051
12052 // if fits on screen (considering margin), keep it in the middle, else, scroll to selection head
12053 let scroll_behavior = if visible_row_count >= selection_height + scroll_margin_rows * 2 {
12054 let middle_row = (end_row + start_row) / 2;
12055 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12056 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12057 SelectSyntaxNodeScrollBehavior::CenterSelection
12058 } else if is_selection_reversed {
12059 self.scroll_cursor_top(&Default::default(), window, cx);
12060 SelectSyntaxNodeScrollBehavior::CursorTop
12061 } else {
12062 self.scroll_cursor_bottom(&Default::default(), window, cx);
12063 SelectSyntaxNodeScrollBehavior::CursorBottom
12064 };
12065
12066 self.select_syntax_node_history.push((
12067 old_selections,
12068 scroll_behavior,
12069 is_selection_reversed,
12070 ));
12071 }
12072
12073 pub fn select_smaller_syntax_node(
12074 &mut self,
12075 _: &SelectSmallerSyntaxNode,
12076 window: &mut Window,
12077 cx: &mut Context<Self>,
12078 ) {
12079 let Some(visible_row_count) = self.visible_row_count() else {
12080 return;
12081 };
12082
12083 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
12084 self.select_syntax_node_history.pop()
12085 {
12086 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12087
12088 if let Some(selection) = selections.last_mut() {
12089 selection.reversed = is_selection_reversed;
12090 }
12091
12092 self.select_syntax_node_history.disable_clearing = true;
12093 self.change_selections(None, window, cx, |s| {
12094 s.select(selections.to_vec());
12095 });
12096 self.select_syntax_node_history.disable_clearing = false;
12097
12098 let newest = self.selections.newest::<usize>(cx);
12099 let start_row = newest.start.to_display_point(&display_map).row().0;
12100 let end_row = newest.end.to_display_point(&display_map).row().0;
12101
12102 match scroll_behavior {
12103 SelectSyntaxNodeScrollBehavior::CursorTop => {
12104 self.scroll_cursor_top(&Default::default(), window, cx);
12105 }
12106 SelectSyntaxNodeScrollBehavior::CenterSelection => {
12107 let middle_row = (end_row + start_row) / 2;
12108 let selection_center = middle_row.saturating_sub(visible_row_count / 2);
12109 // centralize the selection, not the cursor
12110 self.set_scroll_top_row(DisplayRow(selection_center), window, cx);
12111 }
12112 SelectSyntaxNodeScrollBehavior::CursorBottom => {
12113 self.scroll_cursor_bottom(&Default::default(), window, cx);
12114 }
12115 }
12116 }
12117 }
12118
12119 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
12120 if !EditorSettings::get_global(cx).gutter.runnables {
12121 self.clear_tasks();
12122 return Task::ready(());
12123 }
12124 let project = self.project.as_ref().map(Entity::downgrade);
12125 cx.spawn_in(window, async move |this, cx| {
12126 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
12127 let Some(project) = project.and_then(|p| p.upgrade()) else {
12128 return;
12129 };
12130 let Ok(display_snapshot) = this.update(cx, |this, cx| {
12131 this.display_map.update(cx, |map, cx| map.snapshot(cx))
12132 }) else {
12133 return;
12134 };
12135
12136 let hide_runnables = project
12137 .update(cx, |project, cx| {
12138 // Do not display any test indicators in non-dev server remote projects.
12139 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
12140 })
12141 .unwrap_or(true);
12142 if hide_runnables {
12143 return;
12144 }
12145 let new_rows =
12146 cx.background_spawn({
12147 let snapshot = display_snapshot.clone();
12148 async move {
12149 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
12150 }
12151 })
12152 .await;
12153
12154 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
12155 this.update(cx, |this, _| {
12156 this.clear_tasks();
12157 for (key, value) in rows {
12158 this.insert_tasks(key, value);
12159 }
12160 })
12161 .ok();
12162 })
12163 }
12164 fn fetch_runnable_ranges(
12165 snapshot: &DisplaySnapshot,
12166 range: Range<Anchor>,
12167 ) -> Vec<language::RunnableRange> {
12168 snapshot.buffer_snapshot.runnable_ranges(range).collect()
12169 }
12170
12171 fn runnable_rows(
12172 project: Entity<Project>,
12173 snapshot: DisplaySnapshot,
12174 runnable_ranges: Vec<RunnableRange>,
12175 mut cx: AsyncWindowContext,
12176 ) -> Vec<((BufferId, u32), RunnableTasks)> {
12177 runnable_ranges
12178 .into_iter()
12179 .filter_map(|mut runnable| {
12180 let tasks = cx
12181 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
12182 .ok()?;
12183 if tasks.is_empty() {
12184 return None;
12185 }
12186
12187 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
12188
12189 let row = snapshot
12190 .buffer_snapshot
12191 .buffer_line_for_row(MultiBufferRow(point.row))?
12192 .1
12193 .start
12194 .row;
12195
12196 let context_range =
12197 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
12198 Some((
12199 (runnable.buffer_id, row),
12200 RunnableTasks {
12201 templates: tasks,
12202 offset: snapshot
12203 .buffer_snapshot
12204 .anchor_before(runnable.run_range.start),
12205 context_range,
12206 column: point.column,
12207 extra_variables: runnable.extra_captures,
12208 },
12209 ))
12210 })
12211 .collect()
12212 }
12213
12214 fn templates_with_tags(
12215 project: &Entity<Project>,
12216 runnable: &mut Runnable,
12217 cx: &mut App,
12218 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
12219 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
12220 let (worktree_id, file) = project
12221 .buffer_for_id(runnable.buffer, cx)
12222 .and_then(|buffer| buffer.read(cx).file())
12223 .map(|file| (file.worktree_id(cx), file.clone()))
12224 .unzip();
12225
12226 (
12227 project.task_store().read(cx).task_inventory().cloned(),
12228 worktree_id,
12229 file,
12230 )
12231 });
12232
12233 let tags = mem::take(&mut runnable.tags);
12234 let mut tags: Vec<_> = tags
12235 .into_iter()
12236 .flat_map(|tag| {
12237 let tag = tag.0.clone();
12238 inventory
12239 .as_ref()
12240 .into_iter()
12241 .flat_map(|inventory| {
12242 inventory.read(cx).list_tasks(
12243 file.clone(),
12244 Some(runnable.language.clone()),
12245 worktree_id,
12246 cx,
12247 )
12248 })
12249 .filter(move |(_, template)| {
12250 template.tags.iter().any(|source_tag| source_tag == &tag)
12251 })
12252 })
12253 .sorted_by_key(|(kind, _)| kind.to_owned())
12254 .collect();
12255 if let Some((leading_tag_source, _)) = tags.first() {
12256 // Strongest source wins; if we have worktree tag binding, prefer that to
12257 // global and language bindings;
12258 // if we have a global binding, prefer that to language binding.
12259 let first_mismatch = tags
12260 .iter()
12261 .position(|(tag_source, _)| tag_source != leading_tag_source);
12262 if let Some(index) = first_mismatch {
12263 tags.truncate(index);
12264 }
12265 }
12266
12267 tags
12268 }
12269
12270 pub fn move_to_enclosing_bracket(
12271 &mut self,
12272 _: &MoveToEnclosingBracket,
12273 window: &mut Window,
12274 cx: &mut Context<Self>,
12275 ) {
12276 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12277 s.move_offsets_with(|snapshot, selection| {
12278 let Some(enclosing_bracket_ranges) =
12279 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12280 else {
12281 return;
12282 };
12283
12284 let mut best_length = usize::MAX;
12285 let mut best_inside = false;
12286 let mut best_in_bracket_range = false;
12287 let mut best_destination = None;
12288 for (open, close) in enclosing_bracket_ranges {
12289 let close = close.to_inclusive();
12290 let length = close.end() - open.start;
12291 let inside = selection.start >= open.end && selection.end <= *close.start();
12292 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12293 || close.contains(&selection.head());
12294
12295 // If best is next to a bracket and current isn't, skip
12296 if !in_bracket_range && best_in_bracket_range {
12297 continue;
12298 }
12299
12300 // Prefer smaller lengths unless best is inside and current isn't
12301 if length > best_length && (best_inside || !inside) {
12302 continue;
12303 }
12304
12305 best_length = length;
12306 best_inside = inside;
12307 best_in_bracket_range = in_bracket_range;
12308 best_destination = Some(
12309 if close.contains(&selection.start) && close.contains(&selection.end) {
12310 if inside {
12311 open.end
12312 } else {
12313 open.start
12314 }
12315 } else if inside {
12316 *close.start()
12317 } else {
12318 *close.end()
12319 },
12320 );
12321 }
12322
12323 if let Some(destination) = best_destination {
12324 selection.collapse_to(destination, SelectionGoal::None);
12325 }
12326 })
12327 });
12328 }
12329
12330 pub fn undo_selection(
12331 &mut self,
12332 _: &UndoSelection,
12333 window: &mut Window,
12334 cx: &mut Context<Self>,
12335 ) {
12336 self.end_selection(window, cx);
12337 self.selection_history.mode = SelectionHistoryMode::Undoing;
12338 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12339 self.change_selections(None, window, cx, |s| {
12340 s.select_anchors(entry.selections.to_vec())
12341 });
12342 self.select_next_state = entry.select_next_state;
12343 self.select_prev_state = entry.select_prev_state;
12344 self.add_selections_state = entry.add_selections_state;
12345 self.request_autoscroll(Autoscroll::newest(), cx);
12346 }
12347 self.selection_history.mode = SelectionHistoryMode::Normal;
12348 }
12349
12350 pub fn redo_selection(
12351 &mut self,
12352 _: &RedoSelection,
12353 window: &mut Window,
12354 cx: &mut Context<Self>,
12355 ) {
12356 self.end_selection(window, cx);
12357 self.selection_history.mode = SelectionHistoryMode::Redoing;
12358 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12359 self.change_selections(None, window, cx, |s| {
12360 s.select_anchors(entry.selections.to_vec())
12361 });
12362 self.select_next_state = entry.select_next_state;
12363 self.select_prev_state = entry.select_prev_state;
12364 self.add_selections_state = entry.add_selections_state;
12365 self.request_autoscroll(Autoscroll::newest(), cx);
12366 }
12367 self.selection_history.mode = SelectionHistoryMode::Normal;
12368 }
12369
12370 pub fn expand_excerpts(
12371 &mut self,
12372 action: &ExpandExcerpts,
12373 _: &mut Window,
12374 cx: &mut Context<Self>,
12375 ) {
12376 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12377 }
12378
12379 pub fn expand_excerpts_down(
12380 &mut self,
12381 action: &ExpandExcerptsDown,
12382 _: &mut Window,
12383 cx: &mut Context<Self>,
12384 ) {
12385 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12386 }
12387
12388 pub fn expand_excerpts_up(
12389 &mut self,
12390 action: &ExpandExcerptsUp,
12391 _: &mut Window,
12392 cx: &mut Context<Self>,
12393 ) {
12394 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12395 }
12396
12397 pub fn expand_excerpts_for_direction(
12398 &mut self,
12399 lines: u32,
12400 direction: ExpandExcerptDirection,
12401
12402 cx: &mut Context<Self>,
12403 ) {
12404 let selections = self.selections.disjoint_anchors();
12405
12406 let lines = if lines == 0 {
12407 EditorSettings::get_global(cx).expand_excerpt_lines
12408 } else {
12409 lines
12410 };
12411
12412 self.buffer.update(cx, |buffer, cx| {
12413 let snapshot = buffer.snapshot(cx);
12414 let mut excerpt_ids = selections
12415 .iter()
12416 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12417 .collect::<Vec<_>>();
12418 excerpt_ids.sort();
12419 excerpt_ids.dedup();
12420 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12421 })
12422 }
12423
12424 pub fn expand_excerpt(
12425 &mut self,
12426 excerpt: ExcerptId,
12427 direction: ExpandExcerptDirection,
12428 window: &mut Window,
12429 cx: &mut Context<Self>,
12430 ) {
12431 let current_scroll_position = self.scroll_position(cx);
12432 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12433 self.buffer.update(cx, |buffer, cx| {
12434 buffer.expand_excerpts([excerpt], lines, direction, cx)
12435 });
12436 if direction == ExpandExcerptDirection::Down {
12437 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12438 self.set_scroll_position(new_scroll_position, window, cx);
12439 }
12440 }
12441
12442 pub fn go_to_singleton_buffer_point(
12443 &mut self,
12444 point: Point,
12445 window: &mut Window,
12446 cx: &mut Context<Self>,
12447 ) {
12448 self.go_to_singleton_buffer_range(point..point, window, cx);
12449 }
12450
12451 pub fn go_to_singleton_buffer_range(
12452 &mut self,
12453 range: Range<Point>,
12454 window: &mut Window,
12455 cx: &mut Context<Self>,
12456 ) {
12457 let multibuffer = self.buffer().read(cx);
12458 let Some(buffer) = multibuffer.as_singleton() else {
12459 return;
12460 };
12461 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12462 return;
12463 };
12464 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12465 return;
12466 };
12467 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12468 s.select_anchor_ranges([start..end])
12469 });
12470 }
12471
12472 fn go_to_diagnostic(
12473 &mut self,
12474 _: &GoToDiagnostic,
12475 window: &mut Window,
12476 cx: &mut Context<Self>,
12477 ) {
12478 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12479 }
12480
12481 fn go_to_prev_diagnostic(
12482 &mut self,
12483 _: &GoToPreviousDiagnostic,
12484 window: &mut Window,
12485 cx: &mut Context<Self>,
12486 ) {
12487 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12488 }
12489
12490 pub fn go_to_diagnostic_impl(
12491 &mut self,
12492 direction: Direction,
12493 window: &mut Window,
12494 cx: &mut Context<Self>,
12495 ) {
12496 let buffer = self.buffer.read(cx).snapshot(cx);
12497 let selection = self.selections.newest::<usize>(cx);
12498
12499 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12500 if direction == Direction::Next {
12501 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12502 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12503 return;
12504 };
12505 self.activate_diagnostics(
12506 buffer_id,
12507 popover.local_diagnostic.diagnostic.group_id,
12508 window,
12509 cx,
12510 );
12511 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12512 let primary_range_start = active_diagnostics.primary_range.start;
12513 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12514 let mut new_selection = s.newest_anchor().clone();
12515 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12516 s.select_anchors(vec![new_selection.clone()]);
12517 });
12518 self.refresh_inline_completion(false, true, window, cx);
12519 }
12520 return;
12521 }
12522 }
12523
12524 let active_group_id = self
12525 .active_diagnostics
12526 .as_ref()
12527 .map(|active_group| active_group.group_id);
12528 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12529 active_diagnostics
12530 .primary_range
12531 .to_offset(&buffer)
12532 .to_inclusive()
12533 });
12534 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12535 if active_primary_range.contains(&selection.head()) {
12536 *active_primary_range.start()
12537 } else {
12538 selection.head()
12539 }
12540 } else {
12541 selection.head()
12542 };
12543
12544 let snapshot = self.snapshot(window, cx);
12545 let primary_diagnostics_before = buffer
12546 .diagnostics_in_range::<usize>(0..search_start)
12547 .filter(|entry| entry.diagnostic.is_primary)
12548 .filter(|entry| entry.range.start != entry.range.end)
12549 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12550 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12551 .collect::<Vec<_>>();
12552 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12553 primary_diagnostics_before
12554 .iter()
12555 .position(|entry| entry.diagnostic.group_id == active_group_id)
12556 });
12557
12558 let primary_diagnostics_after = buffer
12559 .diagnostics_in_range::<usize>(search_start..buffer.len())
12560 .filter(|entry| entry.diagnostic.is_primary)
12561 .filter(|entry| entry.range.start != entry.range.end)
12562 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12563 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12564 .collect::<Vec<_>>();
12565 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12566 primary_diagnostics_after
12567 .iter()
12568 .enumerate()
12569 .rev()
12570 .find_map(|(i, entry)| {
12571 if entry.diagnostic.group_id == active_group_id {
12572 Some(i)
12573 } else {
12574 None
12575 }
12576 })
12577 });
12578
12579 let next_primary_diagnostic = match direction {
12580 Direction::Prev => primary_diagnostics_before
12581 .iter()
12582 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12583 .rev()
12584 .next(),
12585 Direction::Next => primary_diagnostics_after
12586 .iter()
12587 .skip(
12588 last_same_group_diagnostic_after
12589 .map(|index| index + 1)
12590 .unwrap_or(0),
12591 )
12592 .next(),
12593 };
12594
12595 // Cycle around to the start of the buffer, potentially moving back to the start of
12596 // the currently active diagnostic.
12597 let cycle_around = || match direction {
12598 Direction::Prev => primary_diagnostics_after
12599 .iter()
12600 .rev()
12601 .chain(primary_diagnostics_before.iter().rev())
12602 .next(),
12603 Direction::Next => primary_diagnostics_before
12604 .iter()
12605 .chain(primary_diagnostics_after.iter())
12606 .next(),
12607 };
12608
12609 if let Some((primary_range, group_id)) = next_primary_diagnostic
12610 .or_else(cycle_around)
12611 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12612 {
12613 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12614 return;
12615 };
12616 self.activate_diagnostics(buffer_id, group_id, window, cx);
12617 if self.active_diagnostics.is_some() {
12618 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12619 s.select(vec![Selection {
12620 id: selection.id,
12621 start: primary_range.start,
12622 end: primary_range.start,
12623 reversed: false,
12624 goal: SelectionGoal::None,
12625 }]);
12626 });
12627 self.refresh_inline_completion(false, true, window, cx);
12628 }
12629 }
12630 }
12631
12632 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12633 let snapshot = self.snapshot(window, cx);
12634 let selection = self.selections.newest::<Point>(cx);
12635 self.go_to_hunk_before_or_after_position(
12636 &snapshot,
12637 selection.head(),
12638 Direction::Next,
12639 window,
12640 cx,
12641 );
12642 }
12643
12644 fn go_to_hunk_before_or_after_position(
12645 &mut self,
12646 snapshot: &EditorSnapshot,
12647 position: Point,
12648 direction: Direction,
12649 window: &mut Window,
12650 cx: &mut Context<Editor>,
12651 ) {
12652 let row = if direction == Direction::Next {
12653 self.hunk_after_position(snapshot, position)
12654 .map(|hunk| hunk.row_range.start)
12655 } else {
12656 self.hunk_before_position(snapshot, position)
12657 };
12658
12659 if let Some(row) = row {
12660 let destination = Point::new(row.0, 0);
12661 let autoscroll = Autoscroll::center();
12662
12663 self.unfold_ranges(&[destination..destination], false, false, cx);
12664 self.change_selections(Some(autoscroll), window, cx, |s| {
12665 s.select_ranges([destination..destination]);
12666 });
12667 }
12668 }
12669
12670 fn hunk_after_position(
12671 &mut self,
12672 snapshot: &EditorSnapshot,
12673 position: Point,
12674 ) -> Option<MultiBufferDiffHunk> {
12675 snapshot
12676 .buffer_snapshot
12677 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12678 .find(|hunk| hunk.row_range.start.0 > position.row)
12679 .or_else(|| {
12680 snapshot
12681 .buffer_snapshot
12682 .diff_hunks_in_range(Point::zero()..position)
12683 .find(|hunk| hunk.row_range.end.0 < position.row)
12684 })
12685 }
12686
12687 fn go_to_prev_hunk(
12688 &mut self,
12689 _: &GoToPreviousHunk,
12690 window: &mut Window,
12691 cx: &mut Context<Self>,
12692 ) {
12693 let snapshot = self.snapshot(window, cx);
12694 let selection = self.selections.newest::<Point>(cx);
12695 self.go_to_hunk_before_or_after_position(
12696 &snapshot,
12697 selection.head(),
12698 Direction::Prev,
12699 window,
12700 cx,
12701 );
12702 }
12703
12704 fn hunk_before_position(
12705 &mut self,
12706 snapshot: &EditorSnapshot,
12707 position: Point,
12708 ) -> Option<MultiBufferRow> {
12709 snapshot
12710 .buffer_snapshot
12711 .diff_hunk_before(position)
12712 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12713 }
12714
12715 fn go_to_line<T: 'static>(
12716 &mut self,
12717 position: Anchor,
12718 highlight_color: Option<Hsla>,
12719 window: &mut Window,
12720 cx: &mut Context<Self>,
12721 ) {
12722 let snapshot = self.snapshot(window, cx).display_snapshot;
12723 let position = position.to_point(&snapshot.buffer_snapshot);
12724 let start = snapshot
12725 .buffer_snapshot
12726 .clip_point(Point::new(position.row, 0), Bias::Left);
12727 let end = start + Point::new(1, 0);
12728 let start = snapshot.buffer_snapshot.anchor_before(start);
12729 let end = snapshot.buffer_snapshot.anchor_before(end);
12730
12731 self.highlight_rows::<T>(
12732 start..end,
12733 highlight_color
12734 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12735 false,
12736 cx,
12737 );
12738 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
12739 }
12740
12741 pub fn go_to_definition(
12742 &mut self,
12743 _: &GoToDefinition,
12744 window: &mut Window,
12745 cx: &mut Context<Self>,
12746 ) -> Task<Result<Navigated>> {
12747 let definition =
12748 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12749 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
12750 cx.spawn_in(window, async move |editor, cx| {
12751 if definition.await? == Navigated::Yes {
12752 return Ok(Navigated::Yes);
12753 }
12754 match fallback_strategy {
12755 GoToDefinitionFallback::None => Ok(Navigated::No),
12756 GoToDefinitionFallback::FindAllReferences => {
12757 match editor.update_in(cx, |editor, window, cx| {
12758 editor.find_all_references(&FindAllReferences, window, cx)
12759 })? {
12760 Some(references) => references.await,
12761 None => Ok(Navigated::No),
12762 }
12763 }
12764 }
12765 })
12766 }
12767
12768 pub fn go_to_declaration(
12769 &mut self,
12770 _: &GoToDeclaration,
12771 window: &mut Window,
12772 cx: &mut Context<Self>,
12773 ) -> Task<Result<Navigated>> {
12774 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12775 }
12776
12777 pub fn go_to_declaration_split(
12778 &mut self,
12779 _: &GoToDeclaration,
12780 window: &mut Window,
12781 cx: &mut Context<Self>,
12782 ) -> Task<Result<Navigated>> {
12783 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12784 }
12785
12786 pub fn go_to_implementation(
12787 &mut self,
12788 _: &GoToImplementation,
12789 window: &mut Window,
12790 cx: &mut Context<Self>,
12791 ) -> Task<Result<Navigated>> {
12792 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12793 }
12794
12795 pub fn go_to_implementation_split(
12796 &mut self,
12797 _: &GoToImplementationSplit,
12798 window: &mut Window,
12799 cx: &mut Context<Self>,
12800 ) -> Task<Result<Navigated>> {
12801 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12802 }
12803
12804 pub fn go_to_type_definition(
12805 &mut self,
12806 _: &GoToTypeDefinition,
12807 window: &mut Window,
12808 cx: &mut Context<Self>,
12809 ) -> Task<Result<Navigated>> {
12810 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12811 }
12812
12813 pub fn go_to_definition_split(
12814 &mut self,
12815 _: &GoToDefinitionSplit,
12816 window: &mut Window,
12817 cx: &mut Context<Self>,
12818 ) -> Task<Result<Navigated>> {
12819 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12820 }
12821
12822 pub fn go_to_type_definition_split(
12823 &mut self,
12824 _: &GoToTypeDefinitionSplit,
12825 window: &mut Window,
12826 cx: &mut Context<Self>,
12827 ) -> Task<Result<Navigated>> {
12828 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12829 }
12830
12831 fn go_to_definition_of_kind(
12832 &mut self,
12833 kind: GotoDefinitionKind,
12834 split: bool,
12835 window: &mut Window,
12836 cx: &mut Context<Self>,
12837 ) -> Task<Result<Navigated>> {
12838 let Some(provider) = self.semantics_provider.clone() else {
12839 return Task::ready(Ok(Navigated::No));
12840 };
12841 let head = self.selections.newest::<usize>(cx).head();
12842 let buffer = self.buffer.read(cx);
12843 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12844 text_anchor
12845 } else {
12846 return Task::ready(Ok(Navigated::No));
12847 };
12848
12849 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12850 return Task::ready(Ok(Navigated::No));
12851 };
12852
12853 cx.spawn_in(window, async move |editor, cx| {
12854 let definitions = definitions.await?;
12855 let navigated = editor
12856 .update_in(cx, |editor, window, cx| {
12857 editor.navigate_to_hover_links(
12858 Some(kind),
12859 definitions
12860 .into_iter()
12861 .filter(|location| {
12862 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12863 })
12864 .map(HoverLink::Text)
12865 .collect::<Vec<_>>(),
12866 split,
12867 window,
12868 cx,
12869 )
12870 })?
12871 .await?;
12872 anyhow::Ok(navigated)
12873 })
12874 }
12875
12876 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12877 let selection = self.selections.newest_anchor();
12878 let head = selection.head();
12879 let tail = selection.tail();
12880
12881 let Some((buffer, start_position)) =
12882 self.buffer.read(cx).text_anchor_for_position(head, cx)
12883 else {
12884 return;
12885 };
12886
12887 let end_position = if head != tail {
12888 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12889 return;
12890 };
12891 Some(pos)
12892 } else {
12893 None
12894 };
12895
12896 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12897 let url = if let Some(end_pos) = end_position {
12898 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12899 } else {
12900 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12901 };
12902
12903 if let Some(url) = url {
12904 editor.update(cx, |_, cx| {
12905 cx.open_url(&url);
12906 })
12907 } else {
12908 Ok(())
12909 }
12910 });
12911
12912 url_finder.detach();
12913 }
12914
12915 pub fn open_selected_filename(
12916 &mut self,
12917 _: &OpenSelectedFilename,
12918 window: &mut Window,
12919 cx: &mut Context<Self>,
12920 ) {
12921 let Some(workspace) = self.workspace() else {
12922 return;
12923 };
12924
12925 let position = self.selections.newest_anchor().head();
12926
12927 let Some((buffer, buffer_position)) =
12928 self.buffer.read(cx).text_anchor_for_position(position, cx)
12929 else {
12930 return;
12931 };
12932
12933 let project = self.project.clone();
12934
12935 cx.spawn_in(window, async move |_, cx| {
12936 let result = find_file(&buffer, project, buffer_position, cx).await;
12937
12938 if let Some((_, path)) = result {
12939 workspace
12940 .update_in(cx, |workspace, window, cx| {
12941 workspace.open_resolved_path(path, window, cx)
12942 })?
12943 .await?;
12944 }
12945 anyhow::Ok(())
12946 })
12947 .detach();
12948 }
12949
12950 pub(crate) fn navigate_to_hover_links(
12951 &mut self,
12952 kind: Option<GotoDefinitionKind>,
12953 mut definitions: Vec<HoverLink>,
12954 split: bool,
12955 window: &mut Window,
12956 cx: &mut Context<Editor>,
12957 ) -> Task<Result<Navigated>> {
12958 // If there is one definition, just open it directly
12959 if definitions.len() == 1 {
12960 let definition = definitions.pop().unwrap();
12961
12962 enum TargetTaskResult {
12963 Location(Option<Location>),
12964 AlreadyNavigated,
12965 }
12966
12967 let target_task = match definition {
12968 HoverLink::Text(link) => {
12969 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12970 }
12971 HoverLink::InlayHint(lsp_location, server_id) => {
12972 let computation =
12973 self.compute_target_location(lsp_location, server_id, window, cx);
12974 cx.background_spawn(async move {
12975 let location = computation.await?;
12976 Ok(TargetTaskResult::Location(location))
12977 })
12978 }
12979 HoverLink::Url(url) => {
12980 cx.open_url(&url);
12981 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12982 }
12983 HoverLink::File(path) => {
12984 if let Some(workspace) = self.workspace() {
12985 cx.spawn_in(window, async move |_, cx| {
12986 workspace
12987 .update_in(cx, |workspace, window, cx| {
12988 workspace.open_resolved_path(path, window, cx)
12989 })?
12990 .await
12991 .map(|_| TargetTaskResult::AlreadyNavigated)
12992 })
12993 } else {
12994 Task::ready(Ok(TargetTaskResult::Location(None)))
12995 }
12996 }
12997 };
12998 cx.spawn_in(window, async move |editor, cx| {
12999 let target = match target_task.await.context("target resolution task")? {
13000 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
13001 TargetTaskResult::Location(None) => return Ok(Navigated::No),
13002 TargetTaskResult::Location(Some(target)) => target,
13003 };
13004
13005 editor.update_in(cx, |editor, window, cx| {
13006 let Some(workspace) = editor.workspace() else {
13007 return Navigated::No;
13008 };
13009 let pane = workspace.read(cx).active_pane().clone();
13010
13011 let range = target.range.to_point(target.buffer.read(cx));
13012 let range = editor.range_for_match(&range);
13013 let range = collapse_multiline_range(range);
13014
13015 if !split
13016 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
13017 {
13018 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
13019 } else {
13020 window.defer(cx, move |window, cx| {
13021 let target_editor: Entity<Self> =
13022 workspace.update(cx, |workspace, cx| {
13023 let pane = if split {
13024 workspace.adjacent_pane(window, cx)
13025 } else {
13026 workspace.active_pane().clone()
13027 };
13028
13029 workspace.open_project_item(
13030 pane,
13031 target.buffer.clone(),
13032 true,
13033 true,
13034 window,
13035 cx,
13036 )
13037 });
13038 target_editor.update(cx, |target_editor, cx| {
13039 // When selecting a definition in a different buffer, disable the nav history
13040 // to avoid creating a history entry at the previous cursor location.
13041 pane.update(cx, |pane, _| pane.disable_history());
13042 target_editor.go_to_singleton_buffer_range(range, window, cx);
13043 pane.update(cx, |pane, _| pane.enable_history());
13044 });
13045 });
13046 }
13047 Navigated::Yes
13048 })
13049 })
13050 } else if !definitions.is_empty() {
13051 cx.spawn_in(window, async move |editor, cx| {
13052 let (title, location_tasks, workspace) = editor
13053 .update_in(cx, |editor, window, cx| {
13054 let tab_kind = match kind {
13055 Some(GotoDefinitionKind::Implementation) => "Implementations",
13056 _ => "Definitions",
13057 };
13058 let title = definitions
13059 .iter()
13060 .find_map(|definition| match definition {
13061 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
13062 let buffer = origin.buffer.read(cx);
13063 format!(
13064 "{} for {}",
13065 tab_kind,
13066 buffer
13067 .text_for_range(origin.range.clone())
13068 .collect::<String>()
13069 )
13070 }),
13071 HoverLink::InlayHint(_, _) => None,
13072 HoverLink::Url(_) => None,
13073 HoverLink::File(_) => None,
13074 })
13075 .unwrap_or(tab_kind.to_string());
13076 let location_tasks = definitions
13077 .into_iter()
13078 .map(|definition| match definition {
13079 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
13080 HoverLink::InlayHint(lsp_location, server_id) => editor
13081 .compute_target_location(lsp_location, server_id, window, cx),
13082 HoverLink::Url(_) => Task::ready(Ok(None)),
13083 HoverLink::File(_) => Task::ready(Ok(None)),
13084 })
13085 .collect::<Vec<_>>();
13086 (title, location_tasks, editor.workspace().clone())
13087 })
13088 .context("location tasks preparation")?;
13089
13090 let locations = future::join_all(location_tasks)
13091 .await
13092 .into_iter()
13093 .filter_map(|location| location.transpose())
13094 .collect::<Result<_>>()
13095 .context("location tasks")?;
13096
13097 let Some(workspace) = workspace else {
13098 return Ok(Navigated::No);
13099 };
13100 let opened = workspace
13101 .update_in(cx, |workspace, window, cx| {
13102 Self::open_locations_in_multibuffer(
13103 workspace,
13104 locations,
13105 title,
13106 split,
13107 MultibufferSelectionMode::First,
13108 window,
13109 cx,
13110 )
13111 })
13112 .ok();
13113
13114 anyhow::Ok(Navigated::from_bool(opened.is_some()))
13115 })
13116 } else {
13117 Task::ready(Ok(Navigated::No))
13118 }
13119 }
13120
13121 fn compute_target_location(
13122 &self,
13123 lsp_location: lsp::Location,
13124 server_id: LanguageServerId,
13125 window: &mut Window,
13126 cx: &mut Context<Self>,
13127 ) -> Task<anyhow::Result<Option<Location>>> {
13128 let Some(project) = self.project.clone() else {
13129 return Task::ready(Ok(None));
13130 };
13131
13132 cx.spawn_in(window, async move |editor, cx| {
13133 let location_task = editor.update(cx, |_, cx| {
13134 project.update(cx, |project, cx| {
13135 let language_server_name = project
13136 .language_server_statuses(cx)
13137 .find(|(id, _)| server_id == *id)
13138 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
13139 language_server_name.map(|language_server_name| {
13140 project.open_local_buffer_via_lsp(
13141 lsp_location.uri.clone(),
13142 server_id,
13143 language_server_name,
13144 cx,
13145 )
13146 })
13147 })
13148 })?;
13149 let location = match location_task {
13150 Some(task) => Some({
13151 let target_buffer_handle = task.await.context("open local buffer")?;
13152 let range = target_buffer_handle.update(cx, |target_buffer, _| {
13153 let target_start = target_buffer
13154 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
13155 let target_end = target_buffer
13156 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
13157 target_buffer.anchor_after(target_start)
13158 ..target_buffer.anchor_before(target_end)
13159 })?;
13160 Location {
13161 buffer: target_buffer_handle,
13162 range,
13163 }
13164 }),
13165 None => None,
13166 };
13167 Ok(location)
13168 })
13169 }
13170
13171 pub fn find_all_references(
13172 &mut self,
13173 _: &FindAllReferences,
13174 window: &mut Window,
13175 cx: &mut Context<Self>,
13176 ) -> Option<Task<Result<Navigated>>> {
13177 let selection = self.selections.newest::<usize>(cx);
13178 let multi_buffer = self.buffer.read(cx);
13179 let head = selection.head();
13180
13181 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13182 let head_anchor = multi_buffer_snapshot.anchor_at(
13183 head,
13184 if head < selection.tail() {
13185 Bias::Right
13186 } else {
13187 Bias::Left
13188 },
13189 );
13190
13191 match self
13192 .find_all_references_task_sources
13193 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13194 {
13195 Ok(_) => {
13196 log::info!(
13197 "Ignoring repeated FindAllReferences invocation with the position of already running task"
13198 );
13199 return None;
13200 }
13201 Err(i) => {
13202 self.find_all_references_task_sources.insert(i, head_anchor);
13203 }
13204 }
13205
13206 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
13207 let workspace = self.workspace()?;
13208 let project = workspace.read(cx).project().clone();
13209 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
13210 Some(cx.spawn_in(window, async move |editor, cx| {
13211 let _cleanup = cx.on_drop(&editor, move |editor, _| {
13212 if let Ok(i) = editor
13213 .find_all_references_task_sources
13214 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
13215 {
13216 editor.find_all_references_task_sources.remove(i);
13217 }
13218 });
13219
13220 let locations = references.await?;
13221 if locations.is_empty() {
13222 return anyhow::Ok(Navigated::No);
13223 }
13224
13225 workspace.update_in(cx, |workspace, window, cx| {
13226 let title = locations
13227 .first()
13228 .as_ref()
13229 .map(|location| {
13230 let buffer = location.buffer.read(cx);
13231 format!(
13232 "References to `{}`",
13233 buffer
13234 .text_for_range(location.range.clone())
13235 .collect::<String>()
13236 )
13237 })
13238 .unwrap();
13239 Self::open_locations_in_multibuffer(
13240 workspace,
13241 locations,
13242 title,
13243 false,
13244 MultibufferSelectionMode::First,
13245 window,
13246 cx,
13247 );
13248 Navigated::Yes
13249 })
13250 }))
13251 }
13252
13253 /// Opens a multibuffer with the given project locations in it
13254 pub fn open_locations_in_multibuffer(
13255 workspace: &mut Workspace,
13256 mut locations: Vec<Location>,
13257 title: String,
13258 split: bool,
13259 multibuffer_selection_mode: MultibufferSelectionMode,
13260 window: &mut Window,
13261 cx: &mut Context<Workspace>,
13262 ) {
13263 // If there are multiple definitions, open them in a multibuffer
13264 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13265 let mut locations = locations.into_iter().peekable();
13266 let mut ranges = Vec::new();
13267 let capability = workspace.project().read(cx).capability();
13268
13269 let excerpt_buffer = cx.new(|cx| {
13270 let mut multibuffer = MultiBuffer::new(capability);
13271 while let Some(location) = locations.next() {
13272 let buffer = location.buffer.read(cx);
13273 let mut ranges_for_buffer = Vec::new();
13274 let range = location.range.to_offset(buffer);
13275 ranges_for_buffer.push(range.clone());
13276
13277 while let Some(next_location) = locations.peek() {
13278 if next_location.buffer == location.buffer {
13279 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13280 locations.next();
13281 } else {
13282 break;
13283 }
13284 }
13285
13286 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13287 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13288 location.buffer.clone(),
13289 ranges_for_buffer,
13290 DEFAULT_MULTIBUFFER_CONTEXT,
13291 cx,
13292 ))
13293 }
13294
13295 multibuffer.with_title(title)
13296 });
13297
13298 let editor = cx.new(|cx| {
13299 Editor::for_multibuffer(
13300 excerpt_buffer,
13301 Some(workspace.project().clone()),
13302 window,
13303 cx,
13304 )
13305 });
13306 editor.update(cx, |editor, cx| {
13307 match multibuffer_selection_mode {
13308 MultibufferSelectionMode::First => {
13309 if let Some(first_range) = ranges.first() {
13310 editor.change_selections(None, window, cx, |selections| {
13311 selections.clear_disjoint();
13312 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13313 });
13314 }
13315 editor.highlight_background::<Self>(
13316 &ranges,
13317 |theme| theme.editor_highlighted_line_background,
13318 cx,
13319 );
13320 }
13321 MultibufferSelectionMode::All => {
13322 editor.change_selections(None, window, cx, |selections| {
13323 selections.clear_disjoint();
13324 selections.select_anchor_ranges(ranges);
13325 });
13326 }
13327 }
13328 editor.register_buffers_with_language_servers(cx);
13329 });
13330
13331 let item = Box::new(editor);
13332 let item_id = item.item_id();
13333
13334 if split {
13335 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13336 } else {
13337 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13338 let (preview_item_id, preview_item_idx) =
13339 workspace.active_pane().update(cx, |pane, _| {
13340 (pane.preview_item_id(), pane.preview_item_idx())
13341 });
13342
13343 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13344
13345 if let Some(preview_item_id) = preview_item_id {
13346 workspace.active_pane().update(cx, |pane, cx| {
13347 pane.remove_item(preview_item_id, false, false, window, cx);
13348 });
13349 }
13350 } else {
13351 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13352 }
13353 }
13354 workspace.active_pane().update(cx, |pane, cx| {
13355 pane.set_preview_item_id(Some(item_id), cx);
13356 });
13357 }
13358
13359 pub fn rename(
13360 &mut self,
13361 _: &Rename,
13362 window: &mut Window,
13363 cx: &mut Context<Self>,
13364 ) -> Option<Task<Result<()>>> {
13365 use language::ToOffset as _;
13366
13367 let provider = self.semantics_provider.clone()?;
13368 let selection = self.selections.newest_anchor().clone();
13369 let (cursor_buffer, cursor_buffer_position) = self
13370 .buffer
13371 .read(cx)
13372 .text_anchor_for_position(selection.head(), cx)?;
13373 let (tail_buffer, cursor_buffer_position_end) = self
13374 .buffer
13375 .read(cx)
13376 .text_anchor_for_position(selection.tail(), cx)?;
13377 if tail_buffer != cursor_buffer {
13378 return None;
13379 }
13380
13381 let snapshot = cursor_buffer.read(cx).snapshot();
13382 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13383 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13384 let prepare_rename = provider
13385 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13386 .unwrap_or_else(|| Task::ready(Ok(None)));
13387 drop(snapshot);
13388
13389 Some(cx.spawn_in(window, async move |this, cx| {
13390 let rename_range = if let Some(range) = prepare_rename.await? {
13391 Some(range)
13392 } else {
13393 this.update(cx, |this, cx| {
13394 let buffer = this.buffer.read(cx).snapshot(cx);
13395 let mut buffer_highlights = this
13396 .document_highlights_for_position(selection.head(), &buffer)
13397 .filter(|highlight| {
13398 highlight.start.excerpt_id == selection.head().excerpt_id
13399 && highlight.end.excerpt_id == selection.head().excerpt_id
13400 });
13401 buffer_highlights
13402 .next()
13403 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13404 })?
13405 };
13406 if let Some(rename_range) = rename_range {
13407 this.update_in(cx, |this, window, cx| {
13408 let snapshot = cursor_buffer.read(cx).snapshot();
13409 let rename_buffer_range = rename_range.to_offset(&snapshot);
13410 let cursor_offset_in_rename_range =
13411 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13412 let cursor_offset_in_rename_range_end =
13413 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13414
13415 this.take_rename(false, window, cx);
13416 let buffer = this.buffer.read(cx).read(cx);
13417 let cursor_offset = selection.head().to_offset(&buffer);
13418 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13419 let rename_end = rename_start + rename_buffer_range.len();
13420 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13421 let mut old_highlight_id = None;
13422 let old_name: Arc<str> = buffer
13423 .chunks(rename_start..rename_end, true)
13424 .map(|chunk| {
13425 if old_highlight_id.is_none() {
13426 old_highlight_id = chunk.syntax_highlight_id;
13427 }
13428 chunk.text
13429 })
13430 .collect::<String>()
13431 .into();
13432
13433 drop(buffer);
13434
13435 // Position the selection in the rename editor so that it matches the current selection.
13436 this.show_local_selections = false;
13437 let rename_editor = cx.new(|cx| {
13438 let mut editor = Editor::single_line(window, cx);
13439 editor.buffer.update(cx, |buffer, cx| {
13440 buffer.edit([(0..0, old_name.clone())], None, cx)
13441 });
13442 let rename_selection_range = match cursor_offset_in_rename_range
13443 .cmp(&cursor_offset_in_rename_range_end)
13444 {
13445 Ordering::Equal => {
13446 editor.select_all(&SelectAll, window, cx);
13447 return editor;
13448 }
13449 Ordering::Less => {
13450 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13451 }
13452 Ordering::Greater => {
13453 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13454 }
13455 };
13456 if rename_selection_range.end > old_name.len() {
13457 editor.select_all(&SelectAll, window, cx);
13458 } else {
13459 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13460 s.select_ranges([rename_selection_range]);
13461 });
13462 }
13463 editor
13464 });
13465 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13466 if e == &EditorEvent::Focused {
13467 cx.emit(EditorEvent::FocusedIn)
13468 }
13469 })
13470 .detach();
13471
13472 let write_highlights =
13473 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13474 let read_highlights =
13475 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13476 let ranges = write_highlights
13477 .iter()
13478 .flat_map(|(_, ranges)| ranges.iter())
13479 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13480 .cloned()
13481 .collect();
13482
13483 this.highlight_text::<Rename>(
13484 ranges,
13485 HighlightStyle {
13486 fade_out: Some(0.6),
13487 ..Default::default()
13488 },
13489 cx,
13490 );
13491 let rename_focus_handle = rename_editor.focus_handle(cx);
13492 window.focus(&rename_focus_handle);
13493 let block_id = this.insert_blocks(
13494 [BlockProperties {
13495 style: BlockStyle::Flex,
13496 placement: BlockPlacement::Below(range.start),
13497 height: 1,
13498 render: Arc::new({
13499 let rename_editor = rename_editor.clone();
13500 move |cx: &mut BlockContext| {
13501 let mut text_style = cx.editor_style.text.clone();
13502 if let Some(highlight_style) = old_highlight_id
13503 .and_then(|h| h.style(&cx.editor_style.syntax))
13504 {
13505 text_style = text_style.highlight(highlight_style);
13506 }
13507 div()
13508 .block_mouse_down()
13509 .pl(cx.anchor_x)
13510 .child(EditorElement::new(
13511 &rename_editor,
13512 EditorStyle {
13513 background: cx.theme().system().transparent,
13514 local_player: cx.editor_style.local_player,
13515 text: text_style,
13516 scrollbar_width: cx.editor_style.scrollbar_width,
13517 syntax: cx.editor_style.syntax.clone(),
13518 status: cx.editor_style.status.clone(),
13519 inlay_hints_style: HighlightStyle {
13520 font_weight: Some(FontWeight::BOLD),
13521 ..make_inlay_hints_style(cx.app)
13522 },
13523 inline_completion_styles: make_suggestion_styles(
13524 cx.app,
13525 ),
13526 ..EditorStyle::default()
13527 },
13528 ))
13529 .into_any_element()
13530 }
13531 }),
13532 priority: 0,
13533 }],
13534 Some(Autoscroll::fit()),
13535 cx,
13536 )[0];
13537 this.pending_rename = Some(RenameState {
13538 range,
13539 old_name,
13540 editor: rename_editor,
13541 block_id,
13542 });
13543 })?;
13544 }
13545
13546 Ok(())
13547 }))
13548 }
13549
13550 pub fn confirm_rename(
13551 &mut self,
13552 _: &ConfirmRename,
13553 window: &mut Window,
13554 cx: &mut Context<Self>,
13555 ) -> Option<Task<Result<()>>> {
13556 let rename = self.take_rename(false, window, cx)?;
13557 let workspace = self.workspace()?.downgrade();
13558 let (buffer, start) = self
13559 .buffer
13560 .read(cx)
13561 .text_anchor_for_position(rename.range.start, cx)?;
13562 let (end_buffer, _) = self
13563 .buffer
13564 .read(cx)
13565 .text_anchor_for_position(rename.range.end, cx)?;
13566 if buffer != end_buffer {
13567 return None;
13568 }
13569
13570 let old_name = rename.old_name;
13571 let new_name = rename.editor.read(cx).text(cx);
13572
13573 let rename = self.semantics_provider.as_ref()?.perform_rename(
13574 &buffer,
13575 start,
13576 new_name.clone(),
13577 cx,
13578 )?;
13579
13580 Some(cx.spawn_in(window, async move |editor, cx| {
13581 let project_transaction = rename.await?;
13582 Self::open_project_transaction(
13583 &editor,
13584 workspace,
13585 project_transaction,
13586 format!("Rename: {} → {}", old_name, new_name),
13587 cx,
13588 )
13589 .await?;
13590
13591 editor.update(cx, |editor, cx| {
13592 editor.refresh_document_highlights(cx);
13593 })?;
13594 Ok(())
13595 }))
13596 }
13597
13598 fn take_rename(
13599 &mut self,
13600 moving_cursor: bool,
13601 window: &mut Window,
13602 cx: &mut Context<Self>,
13603 ) -> Option<RenameState> {
13604 let rename = self.pending_rename.take()?;
13605 if rename.editor.focus_handle(cx).is_focused(window) {
13606 window.focus(&self.focus_handle);
13607 }
13608
13609 self.remove_blocks(
13610 [rename.block_id].into_iter().collect(),
13611 Some(Autoscroll::fit()),
13612 cx,
13613 );
13614 self.clear_highlights::<Rename>(cx);
13615 self.show_local_selections = true;
13616
13617 if moving_cursor {
13618 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13619 editor.selections.newest::<usize>(cx).head()
13620 });
13621
13622 // Update the selection to match the position of the selection inside
13623 // the rename editor.
13624 let snapshot = self.buffer.read(cx).read(cx);
13625 let rename_range = rename.range.to_offset(&snapshot);
13626 let cursor_in_editor = snapshot
13627 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13628 .min(rename_range.end);
13629 drop(snapshot);
13630
13631 self.change_selections(None, window, cx, |s| {
13632 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13633 });
13634 } else {
13635 self.refresh_document_highlights(cx);
13636 }
13637
13638 Some(rename)
13639 }
13640
13641 pub fn pending_rename(&self) -> Option<&RenameState> {
13642 self.pending_rename.as_ref()
13643 }
13644
13645 fn format(
13646 &mut self,
13647 _: &Format,
13648 window: &mut Window,
13649 cx: &mut Context<Self>,
13650 ) -> Option<Task<Result<()>>> {
13651 let project = match &self.project {
13652 Some(project) => project.clone(),
13653 None => return None,
13654 };
13655
13656 Some(self.perform_format(
13657 project,
13658 FormatTrigger::Manual,
13659 FormatTarget::Buffers,
13660 window,
13661 cx,
13662 ))
13663 }
13664
13665 fn format_selections(
13666 &mut self,
13667 _: &FormatSelections,
13668 window: &mut Window,
13669 cx: &mut Context<Self>,
13670 ) -> Option<Task<Result<()>>> {
13671 let project = match &self.project {
13672 Some(project) => project.clone(),
13673 None => return None,
13674 };
13675
13676 let ranges = self
13677 .selections
13678 .all_adjusted(cx)
13679 .into_iter()
13680 .map(|selection| selection.range())
13681 .collect_vec();
13682
13683 Some(self.perform_format(
13684 project,
13685 FormatTrigger::Manual,
13686 FormatTarget::Ranges(ranges),
13687 window,
13688 cx,
13689 ))
13690 }
13691
13692 fn perform_format(
13693 &mut self,
13694 project: Entity<Project>,
13695 trigger: FormatTrigger,
13696 target: FormatTarget,
13697 window: &mut Window,
13698 cx: &mut Context<Self>,
13699 ) -> Task<Result<()>> {
13700 let buffer = self.buffer.clone();
13701 let (buffers, target) = match target {
13702 FormatTarget::Buffers => {
13703 let mut buffers = buffer.read(cx).all_buffers();
13704 if trigger == FormatTrigger::Save {
13705 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13706 }
13707 (buffers, LspFormatTarget::Buffers)
13708 }
13709 FormatTarget::Ranges(selection_ranges) => {
13710 let multi_buffer = buffer.read(cx);
13711 let snapshot = multi_buffer.read(cx);
13712 let mut buffers = HashSet::default();
13713 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13714 BTreeMap::new();
13715 for selection_range in selection_ranges {
13716 for (buffer, buffer_range, _) in
13717 snapshot.range_to_buffer_ranges(selection_range)
13718 {
13719 let buffer_id = buffer.remote_id();
13720 let start = buffer.anchor_before(buffer_range.start);
13721 let end = buffer.anchor_after(buffer_range.end);
13722 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13723 buffer_id_to_ranges
13724 .entry(buffer_id)
13725 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13726 .or_insert_with(|| vec![start..end]);
13727 }
13728 }
13729 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13730 }
13731 };
13732
13733 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13734 let format = project.update(cx, |project, cx| {
13735 project.format(buffers, target, true, trigger, cx)
13736 });
13737
13738 cx.spawn_in(window, async move |_, cx| {
13739 let transaction = futures::select_biased! {
13740 transaction = format.log_err().fuse() => transaction,
13741 () = timeout => {
13742 log::warn!("timed out waiting for formatting");
13743 None
13744 }
13745 };
13746
13747 buffer
13748 .update(cx, |buffer, cx| {
13749 if let Some(transaction) = transaction {
13750 if !buffer.is_singleton() {
13751 buffer.push_transaction(&transaction.0, cx);
13752 }
13753 }
13754 cx.notify();
13755 })
13756 .ok();
13757
13758 Ok(())
13759 })
13760 }
13761
13762 fn organize_imports(
13763 &mut self,
13764 _: &OrganizeImports,
13765 window: &mut Window,
13766 cx: &mut Context<Self>,
13767 ) -> Option<Task<Result<()>>> {
13768 let project = match &self.project {
13769 Some(project) => project.clone(),
13770 None => return None,
13771 };
13772 Some(self.perform_code_action_kind(
13773 project,
13774 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13775 window,
13776 cx,
13777 ))
13778 }
13779
13780 fn perform_code_action_kind(
13781 &mut self,
13782 project: Entity<Project>,
13783 kind: CodeActionKind,
13784 window: &mut Window,
13785 cx: &mut Context<Self>,
13786 ) -> Task<Result<()>> {
13787 let buffer = self.buffer.clone();
13788 let buffers = buffer.read(cx).all_buffers();
13789 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13790 let apply_action = project.update(cx, |project, cx| {
13791 project.apply_code_action_kind(buffers, kind, true, cx)
13792 });
13793 cx.spawn_in(window, async move |_, cx| {
13794 let transaction = futures::select_biased! {
13795 () = timeout => {
13796 log::warn!("timed out waiting for executing code action");
13797 None
13798 }
13799 transaction = apply_action.log_err().fuse() => transaction,
13800 };
13801 buffer
13802 .update(cx, |buffer, cx| {
13803 // check if we need this
13804 if let Some(transaction) = transaction {
13805 if !buffer.is_singleton() {
13806 buffer.push_transaction(&transaction.0, cx);
13807 }
13808 }
13809 cx.notify();
13810 })
13811 .ok();
13812 Ok(())
13813 })
13814 }
13815
13816 fn restart_language_server(
13817 &mut self,
13818 _: &RestartLanguageServer,
13819 _: &mut Window,
13820 cx: &mut Context<Self>,
13821 ) {
13822 if let Some(project) = self.project.clone() {
13823 self.buffer.update(cx, |multi_buffer, cx| {
13824 project.update(cx, |project, cx| {
13825 project.restart_language_servers_for_buffers(
13826 multi_buffer.all_buffers().into_iter().collect(),
13827 cx,
13828 );
13829 });
13830 })
13831 }
13832 }
13833
13834 fn cancel_language_server_work(
13835 workspace: &mut Workspace,
13836 _: &actions::CancelLanguageServerWork,
13837 _: &mut Window,
13838 cx: &mut Context<Workspace>,
13839 ) {
13840 let project = workspace.project();
13841 let buffers = workspace
13842 .active_item(cx)
13843 .and_then(|item| item.act_as::<Editor>(cx))
13844 .map_or(HashSet::default(), |editor| {
13845 editor.read(cx).buffer.read(cx).all_buffers()
13846 });
13847 project.update(cx, |project, cx| {
13848 project.cancel_language_server_work_for_buffers(buffers, cx);
13849 });
13850 }
13851
13852 fn show_character_palette(
13853 &mut self,
13854 _: &ShowCharacterPalette,
13855 window: &mut Window,
13856 _: &mut Context<Self>,
13857 ) {
13858 window.show_character_palette();
13859 }
13860
13861 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13862 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13863 let buffer = self.buffer.read(cx).snapshot(cx);
13864 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13865 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13866 let is_valid = buffer
13867 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13868 .any(|entry| {
13869 entry.diagnostic.is_primary
13870 && !entry.range.is_empty()
13871 && entry.range.start == primary_range_start
13872 && entry.diagnostic.message == active_diagnostics.primary_message
13873 });
13874
13875 if is_valid != active_diagnostics.is_valid {
13876 active_diagnostics.is_valid = is_valid;
13877 if is_valid {
13878 let mut new_styles = HashMap::default();
13879 for (block_id, diagnostic) in &active_diagnostics.blocks {
13880 new_styles.insert(
13881 *block_id,
13882 diagnostic_block_renderer(diagnostic.clone(), None, true),
13883 );
13884 }
13885 self.display_map.update(cx, |display_map, _cx| {
13886 display_map.replace_blocks(new_styles);
13887 });
13888 } else {
13889 self.dismiss_diagnostics(cx);
13890 }
13891 }
13892 }
13893 }
13894
13895 fn activate_diagnostics(
13896 &mut self,
13897 buffer_id: BufferId,
13898 group_id: usize,
13899 window: &mut Window,
13900 cx: &mut Context<Self>,
13901 ) {
13902 self.dismiss_diagnostics(cx);
13903 let snapshot = self.snapshot(window, cx);
13904 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13905 let buffer = self.buffer.read(cx).snapshot(cx);
13906
13907 let mut primary_range = None;
13908 let mut primary_message = None;
13909 let diagnostic_group = buffer
13910 .diagnostic_group(buffer_id, group_id)
13911 .filter_map(|entry| {
13912 let start = entry.range.start;
13913 let end = entry.range.end;
13914 if snapshot.is_line_folded(MultiBufferRow(start.row))
13915 && (start.row == end.row
13916 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13917 {
13918 return None;
13919 }
13920 if entry.diagnostic.is_primary {
13921 primary_range = Some(entry.range.clone());
13922 primary_message = Some(entry.diagnostic.message.clone());
13923 }
13924 Some(entry)
13925 })
13926 .collect::<Vec<_>>();
13927 let primary_range = primary_range?;
13928 let primary_message = primary_message?;
13929
13930 let blocks = display_map
13931 .insert_blocks(
13932 diagnostic_group.iter().map(|entry| {
13933 let diagnostic = entry.diagnostic.clone();
13934 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13935 BlockProperties {
13936 style: BlockStyle::Fixed,
13937 placement: BlockPlacement::Below(
13938 buffer.anchor_after(entry.range.start),
13939 ),
13940 height: message_height,
13941 render: diagnostic_block_renderer(diagnostic, None, true),
13942 priority: 0,
13943 }
13944 }),
13945 cx,
13946 )
13947 .into_iter()
13948 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13949 .collect();
13950
13951 Some(ActiveDiagnosticGroup {
13952 primary_range: buffer.anchor_before(primary_range.start)
13953 ..buffer.anchor_after(primary_range.end),
13954 primary_message,
13955 group_id,
13956 blocks,
13957 is_valid: true,
13958 })
13959 });
13960 }
13961
13962 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13963 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13964 self.display_map.update(cx, |display_map, cx| {
13965 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13966 });
13967 cx.notify();
13968 }
13969 }
13970
13971 /// Disable inline diagnostics rendering for this editor.
13972 pub fn disable_inline_diagnostics(&mut self) {
13973 self.inline_diagnostics_enabled = false;
13974 self.inline_diagnostics_update = Task::ready(());
13975 self.inline_diagnostics.clear();
13976 }
13977
13978 pub fn inline_diagnostics_enabled(&self) -> bool {
13979 self.inline_diagnostics_enabled
13980 }
13981
13982 pub fn show_inline_diagnostics(&self) -> bool {
13983 self.show_inline_diagnostics
13984 }
13985
13986 pub fn toggle_inline_diagnostics(
13987 &mut self,
13988 _: &ToggleInlineDiagnostics,
13989 window: &mut Window,
13990 cx: &mut Context<'_, Editor>,
13991 ) {
13992 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13993 self.refresh_inline_diagnostics(false, window, cx);
13994 }
13995
13996 fn refresh_inline_diagnostics(
13997 &mut self,
13998 debounce: bool,
13999 window: &mut Window,
14000 cx: &mut Context<Self>,
14001 ) {
14002 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
14003 self.inline_diagnostics_update = Task::ready(());
14004 self.inline_diagnostics.clear();
14005 return;
14006 }
14007
14008 let debounce_ms = ProjectSettings::get_global(cx)
14009 .diagnostics
14010 .inline
14011 .update_debounce_ms;
14012 let debounce = if debounce && debounce_ms > 0 {
14013 Some(Duration::from_millis(debounce_ms))
14014 } else {
14015 None
14016 };
14017 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
14018 if let Some(debounce) = debounce {
14019 cx.background_executor().timer(debounce).await;
14020 }
14021 let Some(snapshot) = editor
14022 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
14023 .ok()
14024 else {
14025 return;
14026 };
14027
14028 let new_inline_diagnostics = cx
14029 .background_spawn(async move {
14030 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
14031 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
14032 let message = diagnostic_entry
14033 .diagnostic
14034 .message
14035 .split_once('\n')
14036 .map(|(line, _)| line)
14037 .map(SharedString::new)
14038 .unwrap_or_else(|| {
14039 SharedString::from(diagnostic_entry.diagnostic.message)
14040 });
14041 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
14042 let (Ok(i) | Err(i)) = inline_diagnostics
14043 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
14044 inline_diagnostics.insert(
14045 i,
14046 (
14047 start_anchor,
14048 InlineDiagnostic {
14049 message,
14050 group_id: diagnostic_entry.diagnostic.group_id,
14051 start: diagnostic_entry.range.start.to_point(&snapshot),
14052 is_primary: diagnostic_entry.diagnostic.is_primary,
14053 severity: diagnostic_entry.diagnostic.severity,
14054 },
14055 ),
14056 );
14057 }
14058 inline_diagnostics
14059 })
14060 .await;
14061
14062 editor
14063 .update(cx, |editor, cx| {
14064 editor.inline_diagnostics = new_inline_diagnostics;
14065 cx.notify();
14066 })
14067 .ok();
14068 });
14069 }
14070
14071 pub fn set_selections_from_remote(
14072 &mut self,
14073 selections: Vec<Selection<Anchor>>,
14074 pending_selection: Option<Selection<Anchor>>,
14075 window: &mut Window,
14076 cx: &mut Context<Self>,
14077 ) {
14078 let old_cursor_position = self.selections.newest_anchor().head();
14079 self.selections.change_with(cx, |s| {
14080 s.select_anchors(selections);
14081 if let Some(pending_selection) = pending_selection {
14082 s.set_pending(pending_selection, SelectMode::Character);
14083 } else {
14084 s.clear_pending();
14085 }
14086 });
14087 self.selections_did_change(false, &old_cursor_position, true, window, cx);
14088 }
14089
14090 fn push_to_selection_history(&mut self) {
14091 self.selection_history.push(SelectionHistoryEntry {
14092 selections: self.selections.disjoint_anchors(),
14093 select_next_state: self.select_next_state.clone(),
14094 select_prev_state: self.select_prev_state.clone(),
14095 add_selections_state: self.add_selections_state.clone(),
14096 });
14097 }
14098
14099 pub fn transact(
14100 &mut self,
14101 window: &mut Window,
14102 cx: &mut Context<Self>,
14103 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
14104 ) -> Option<TransactionId> {
14105 self.start_transaction_at(Instant::now(), window, cx);
14106 update(self, window, cx);
14107 self.end_transaction_at(Instant::now(), cx)
14108 }
14109
14110 pub fn start_transaction_at(
14111 &mut self,
14112 now: Instant,
14113 window: &mut Window,
14114 cx: &mut Context<Self>,
14115 ) {
14116 self.end_selection(window, cx);
14117 if let Some(tx_id) = self
14118 .buffer
14119 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
14120 {
14121 self.selection_history
14122 .insert_transaction(tx_id, self.selections.disjoint_anchors());
14123 cx.emit(EditorEvent::TransactionBegun {
14124 transaction_id: tx_id,
14125 })
14126 }
14127 }
14128
14129 pub fn end_transaction_at(
14130 &mut self,
14131 now: Instant,
14132 cx: &mut Context<Self>,
14133 ) -> Option<TransactionId> {
14134 if let Some(transaction_id) = self
14135 .buffer
14136 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
14137 {
14138 if let Some((_, end_selections)) =
14139 self.selection_history.transaction_mut(transaction_id)
14140 {
14141 *end_selections = Some(self.selections.disjoint_anchors());
14142 } else {
14143 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
14144 }
14145
14146 cx.emit(EditorEvent::Edited { transaction_id });
14147 Some(transaction_id)
14148 } else {
14149 None
14150 }
14151 }
14152
14153 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
14154 if self.selection_mark_mode {
14155 self.change_selections(None, window, cx, |s| {
14156 s.move_with(|_, sel| {
14157 sel.collapse_to(sel.head(), SelectionGoal::None);
14158 });
14159 })
14160 }
14161 self.selection_mark_mode = true;
14162 cx.notify();
14163 }
14164
14165 pub fn swap_selection_ends(
14166 &mut self,
14167 _: &actions::SwapSelectionEnds,
14168 window: &mut Window,
14169 cx: &mut Context<Self>,
14170 ) {
14171 self.change_selections(None, window, cx, |s| {
14172 s.move_with(|_, sel| {
14173 if sel.start != sel.end {
14174 sel.reversed = !sel.reversed
14175 }
14176 });
14177 });
14178 self.request_autoscroll(Autoscroll::newest(), cx);
14179 cx.notify();
14180 }
14181
14182 pub fn toggle_fold(
14183 &mut self,
14184 _: &actions::ToggleFold,
14185 window: &mut Window,
14186 cx: &mut Context<Self>,
14187 ) {
14188 if self.is_singleton(cx) {
14189 let selection = self.selections.newest::<Point>(cx);
14190
14191 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14192 let range = if selection.is_empty() {
14193 let point = selection.head().to_display_point(&display_map);
14194 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14195 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14196 .to_point(&display_map);
14197 start..end
14198 } else {
14199 selection.range()
14200 };
14201 if display_map.folds_in_range(range).next().is_some() {
14202 self.unfold_lines(&Default::default(), window, cx)
14203 } else {
14204 self.fold(&Default::default(), window, cx)
14205 }
14206 } else {
14207 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14208 let buffer_ids: HashSet<_> = self
14209 .selections
14210 .disjoint_anchor_ranges()
14211 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14212 .collect();
14213
14214 let should_unfold = buffer_ids
14215 .iter()
14216 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
14217
14218 for buffer_id in buffer_ids {
14219 if should_unfold {
14220 self.unfold_buffer(buffer_id, cx);
14221 } else {
14222 self.fold_buffer(buffer_id, cx);
14223 }
14224 }
14225 }
14226 }
14227
14228 pub fn toggle_fold_recursive(
14229 &mut self,
14230 _: &actions::ToggleFoldRecursive,
14231 window: &mut Window,
14232 cx: &mut Context<Self>,
14233 ) {
14234 let selection = self.selections.newest::<Point>(cx);
14235
14236 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14237 let range = if selection.is_empty() {
14238 let point = selection.head().to_display_point(&display_map);
14239 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14240 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14241 .to_point(&display_map);
14242 start..end
14243 } else {
14244 selection.range()
14245 };
14246 if display_map.folds_in_range(range).next().is_some() {
14247 self.unfold_recursive(&Default::default(), window, cx)
14248 } else {
14249 self.fold_recursive(&Default::default(), window, cx)
14250 }
14251 }
14252
14253 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14254 if self.is_singleton(cx) {
14255 let mut to_fold = Vec::new();
14256 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14257 let selections = self.selections.all_adjusted(cx);
14258
14259 for selection in selections {
14260 let range = selection.range().sorted();
14261 let buffer_start_row = range.start.row;
14262
14263 if range.start.row != range.end.row {
14264 let mut found = false;
14265 let mut row = range.start.row;
14266 while row <= range.end.row {
14267 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14268 {
14269 found = true;
14270 row = crease.range().end.row + 1;
14271 to_fold.push(crease);
14272 } else {
14273 row += 1
14274 }
14275 }
14276 if found {
14277 continue;
14278 }
14279 }
14280
14281 for row in (0..=range.start.row).rev() {
14282 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14283 if crease.range().end.row >= buffer_start_row {
14284 to_fold.push(crease);
14285 if row <= range.start.row {
14286 break;
14287 }
14288 }
14289 }
14290 }
14291 }
14292
14293 self.fold_creases(to_fold, true, window, cx);
14294 } else {
14295 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14296 let buffer_ids = self
14297 .selections
14298 .disjoint_anchor_ranges()
14299 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14300 .collect::<HashSet<_>>();
14301 for buffer_id in buffer_ids {
14302 self.fold_buffer(buffer_id, cx);
14303 }
14304 }
14305 }
14306
14307 fn fold_at_level(
14308 &mut self,
14309 fold_at: &FoldAtLevel,
14310 window: &mut Window,
14311 cx: &mut Context<Self>,
14312 ) {
14313 if !self.buffer.read(cx).is_singleton() {
14314 return;
14315 }
14316
14317 let fold_at_level = fold_at.0;
14318 let snapshot = self.buffer.read(cx).snapshot(cx);
14319 let mut to_fold = Vec::new();
14320 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14321
14322 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14323 while start_row < end_row {
14324 match self
14325 .snapshot(window, cx)
14326 .crease_for_buffer_row(MultiBufferRow(start_row))
14327 {
14328 Some(crease) => {
14329 let nested_start_row = crease.range().start.row + 1;
14330 let nested_end_row = crease.range().end.row;
14331
14332 if current_level < fold_at_level {
14333 stack.push((nested_start_row, nested_end_row, current_level + 1));
14334 } else if current_level == fold_at_level {
14335 to_fold.push(crease);
14336 }
14337
14338 start_row = nested_end_row + 1;
14339 }
14340 None => start_row += 1,
14341 }
14342 }
14343 }
14344
14345 self.fold_creases(to_fold, true, window, cx);
14346 }
14347
14348 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14349 if self.buffer.read(cx).is_singleton() {
14350 let mut fold_ranges = Vec::new();
14351 let snapshot = self.buffer.read(cx).snapshot(cx);
14352
14353 for row in 0..snapshot.max_row().0 {
14354 if let Some(foldable_range) = self
14355 .snapshot(window, cx)
14356 .crease_for_buffer_row(MultiBufferRow(row))
14357 {
14358 fold_ranges.push(foldable_range);
14359 }
14360 }
14361
14362 self.fold_creases(fold_ranges, true, window, cx);
14363 } else {
14364 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14365 editor
14366 .update_in(cx, |editor, _, cx| {
14367 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14368 editor.fold_buffer(buffer_id, cx);
14369 }
14370 })
14371 .ok();
14372 });
14373 }
14374 }
14375
14376 pub fn fold_function_bodies(
14377 &mut self,
14378 _: &actions::FoldFunctionBodies,
14379 window: &mut Window,
14380 cx: &mut Context<Self>,
14381 ) {
14382 let snapshot = self.buffer.read(cx).snapshot(cx);
14383
14384 let ranges = snapshot
14385 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14386 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14387 .collect::<Vec<_>>();
14388
14389 let creases = ranges
14390 .into_iter()
14391 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14392 .collect();
14393
14394 self.fold_creases(creases, true, window, cx);
14395 }
14396
14397 pub fn fold_recursive(
14398 &mut self,
14399 _: &actions::FoldRecursive,
14400 window: &mut Window,
14401 cx: &mut Context<Self>,
14402 ) {
14403 let mut to_fold = Vec::new();
14404 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14405 let selections = self.selections.all_adjusted(cx);
14406
14407 for selection in selections {
14408 let range = selection.range().sorted();
14409 let buffer_start_row = range.start.row;
14410
14411 if range.start.row != range.end.row {
14412 let mut found = false;
14413 for row in range.start.row..=range.end.row {
14414 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14415 found = true;
14416 to_fold.push(crease);
14417 }
14418 }
14419 if found {
14420 continue;
14421 }
14422 }
14423
14424 for row in (0..=range.start.row).rev() {
14425 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14426 if crease.range().end.row >= buffer_start_row {
14427 to_fold.push(crease);
14428 } else {
14429 break;
14430 }
14431 }
14432 }
14433 }
14434
14435 self.fold_creases(to_fold, true, window, cx);
14436 }
14437
14438 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14439 let buffer_row = fold_at.buffer_row;
14440 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14441
14442 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14443 let autoscroll = self
14444 .selections
14445 .all::<Point>(cx)
14446 .iter()
14447 .any(|selection| crease.range().overlaps(&selection.range()));
14448
14449 self.fold_creases(vec![crease], autoscroll, window, cx);
14450 }
14451 }
14452
14453 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14454 if self.is_singleton(cx) {
14455 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14456 let buffer = &display_map.buffer_snapshot;
14457 let selections = self.selections.all::<Point>(cx);
14458 let ranges = selections
14459 .iter()
14460 .map(|s| {
14461 let range = s.display_range(&display_map).sorted();
14462 let mut start = range.start.to_point(&display_map);
14463 let mut end = range.end.to_point(&display_map);
14464 start.column = 0;
14465 end.column = buffer.line_len(MultiBufferRow(end.row));
14466 start..end
14467 })
14468 .collect::<Vec<_>>();
14469
14470 self.unfold_ranges(&ranges, true, true, cx);
14471 } else {
14472 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14473 let buffer_ids = self
14474 .selections
14475 .disjoint_anchor_ranges()
14476 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14477 .collect::<HashSet<_>>();
14478 for buffer_id in buffer_ids {
14479 self.unfold_buffer(buffer_id, cx);
14480 }
14481 }
14482 }
14483
14484 pub fn unfold_recursive(
14485 &mut self,
14486 _: &UnfoldRecursive,
14487 _window: &mut Window,
14488 cx: &mut Context<Self>,
14489 ) {
14490 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14491 let selections = self.selections.all::<Point>(cx);
14492 let ranges = selections
14493 .iter()
14494 .map(|s| {
14495 let mut range = s.display_range(&display_map).sorted();
14496 *range.start.column_mut() = 0;
14497 *range.end.column_mut() = display_map.line_len(range.end.row());
14498 let start = range.start.to_point(&display_map);
14499 let end = range.end.to_point(&display_map);
14500 start..end
14501 })
14502 .collect::<Vec<_>>();
14503
14504 self.unfold_ranges(&ranges, true, true, cx);
14505 }
14506
14507 pub fn unfold_at(
14508 &mut self,
14509 unfold_at: &UnfoldAt,
14510 _window: &mut Window,
14511 cx: &mut Context<Self>,
14512 ) {
14513 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14514
14515 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14516 ..Point::new(
14517 unfold_at.buffer_row.0,
14518 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14519 );
14520
14521 let autoscroll = self
14522 .selections
14523 .all::<Point>(cx)
14524 .iter()
14525 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14526
14527 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14528 }
14529
14530 pub fn unfold_all(
14531 &mut self,
14532 _: &actions::UnfoldAll,
14533 _window: &mut Window,
14534 cx: &mut Context<Self>,
14535 ) {
14536 if self.buffer.read(cx).is_singleton() {
14537 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14538 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14539 } else {
14540 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14541 editor
14542 .update(cx, |editor, cx| {
14543 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14544 editor.unfold_buffer(buffer_id, cx);
14545 }
14546 })
14547 .ok();
14548 });
14549 }
14550 }
14551
14552 pub fn fold_selected_ranges(
14553 &mut self,
14554 _: &FoldSelectedRanges,
14555 window: &mut Window,
14556 cx: &mut Context<Self>,
14557 ) {
14558 let selections = self.selections.all::<Point>(cx);
14559 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14560 let line_mode = self.selections.line_mode;
14561 let ranges = selections
14562 .into_iter()
14563 .map(|s| {
14564 if line_mode {
14565 let start = Point::new(s.start.row, 0);
14566 let end = Point::new(
14567 s.end.row,
14568 display_map
14569 .buffer_snapshot
14570 .line_len(MultiBufferRow(s.end.row)),
14571 );
14572 Crease::simple(start..end, display_map.fold_placeholder.clone())
14573 } else {
14574 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14575 }
14576 })
14577 .collect::<Vec<_>>();
14578 self.fold_creases(ranges, true, window, cx);
14579 }
14580
14581 pub fn fold_ranges<T: ToOffset + Clone>(
14582 &mut self,
14583 ranges: Vec<Range<T>>,
14584 auto_scroll: bool,
14585 window: &mut Window,
14586 cx: &mut Context<Self>,
14587 ) {
14588 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14589 let ranges = ranges
14590 .into_iter()
14591 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14592 .collect::<Vec<_>>();
14593 self.fold_creases(ranges, auto_scroll, window, cx);
14594 }
14595
14596 pub fn fold_creases<T: ToOffset + Clone>(
14597 &mut self,
14598 creases: Vec<Crease<T>>,
14599 auto_scroll: bool,
14600 window: &mut Window,
14601 cx: &mut Context<Self>,
14602 ) {
14603 if creases.is_empty() {
14604 return;
14605 }
14606
14607 let mut buffers_affected = HashSet::default();
14608 let multi_buffer = self.buffer().read(cx);
14609 for crease in &creases {
14610 if let Some((_, buffer, _)) =
14611 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14612 {
14613 buffers_affected.insert(buffer.read(cx).remote_id());
14614 };
14615 }
14616
14617 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14618
14619 if auto_scroll {
14620 self.request_autoscroll(Autoscroll::fit(), cx);
14621 }
14622
14623 cx.notify();
14624
14625 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14626 // Clear diagnostics block when folding a range that contains it.
14627 let snapshot = self.snapshot(window, cx);
14628 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14629 drop(snapshot);
14630 self.active_diagnostics = Some(active_diagnostics);
14631 self.dismiss_diagnostics(cx);
14632 } else {
14633 self.active_diagnostics = Some(active_diagnostics);
14634 }
14635 }
14636
14637 self.scrollbar_marker_state.dirty = true;
14638 self.folds_did_change(cx);
14639 }
14640
14641 /// Removes any folds whose ranges intersect any of the given ranges.
14642 pub fn unfold_ranges<T: ToOffset + Clone>(
14643 &mut self,
14644 ranges: &[Range<T>],
14645 inclusive: bool,
14646 auto_scroll: bool,
14647 cx: &mut Context<Self>,
14648 ) {
14649 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14650 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14651 });
14652 self.folds_did_change(cx);
14653 }
14654
14655 pub fn fold_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 folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14660 self.display_map.update(cx, |display_map, cx| {
14661 display_map.fold_buffers([buffer_id], cx)
14662 });
14663 cx.emit(EditorEvent::BufferFoldToggled {
14664 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14665 folded: true,
14666 });
14667 cx.notify();
14668 }
14669
14670 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14671 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14672 return;
14673 }
14674 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14675 self.display_map.update(cx, |display_map, cx| {
14676 display_map.unfold_buffers([buffer_id], cx);
14677 });
14678 cx.emit(EditorEvent::BufferFoldToggled {
14679 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14680 folded: false,
14681 });
14682 cx.notify();
14683 }
14684
14685 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14686 self.display_map.read(cx).is_buffer_folded(buffer)
14687 }
14688
14689 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14690 self.display_map.read(cx).folded_buffers()
14691 }
14692
14693 /// Removes any folds with the given ranges.
14694 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14695 &mut self,
14696 ranges: &[Range<T>],
14697 type_id: TypeId,
14698 auto_scroll: bool,
14699 cx: &mut Context<Self>,
14700 ) {
14701 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14702 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14703 });
14704 self.folds_did_change(cx);
14705 }
14706
14707 fn remove_folds_with<T: ToOffset + Clone>(
14708 &mut self,
14709 ranges: &[Range<T>],
14710 auto_scroll: bool,
14711 cx: &mut Context<Self>,
14712 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14713 ) {
14714 if ranges.is_empty() {
14715 return;
14716 }
14717
14718 let mut buffers_affected = HashSet::default();
14719 let multi_buffer = self.buffer().read(cx);
14720 for range in ranges {
14721 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14722 buffers_affected.insert(buffer.read(cx).remote_id());
14723 };
14724 }
14725
14726 self.display_map.update(cx, update);
14727
14728 if auto_scroll {
14729 self.request_autoscroll(Autoscroll::fit(), cx);
14730 }
14731
14732 cx.notify();
14733 self.scrollbar_marker_state.dirty = true;
14734 self.active_indent_guides_state.dirty = true;
14735 }
14736
14737 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14738 self.display_map.read(cx).fold_placeholder.clone()
14739 }
14740
14741 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14742 self.buffer.update(cx, |buffer, cx| {
14743 buffer.set_all_diff_hunks_expanded(cx);
14744 });
14745 }
14746
14747 pub fn expand_all_diff_hunks(
14748 &mut self,
14749 _: &ExpandAllDiffHunks,
14750 _window: &mut Window,
14751 cx: &mut Context<Self>,
14752 ) {
14753 self.buffer.update(cx, |buffer, cx| {
14754 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14755 });
14756 }
14757
14758 pub fn toggle_selected_diff_hunks(
14759 &mut self,
14760 _: &ToggleSelectedDiffHunks,
14761 _window: &mut Window,
14762 cx: &mut Context<Self>,
14763 ) {
14764 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14765 self.toggle_diff_hunks_in_ranges(ranges, cx);
14766 }
14767
14768 pub fn diff_hunks_in_ranges<'a>(
14769 &'a self,
14770 ranges: &'a [Range<Anchor>],
14771 buffer: &'a MultiBufferSnapshot,
14772 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14773 ranges.iter().flat_map(move |range| {
14774 let end_excerpt_id = range.end.excerpt_id;
14775 let range = range.to_point(buffer);
14776 let mut peek_end = range.end;
14777 if range.end.row < buffer.max_row().0 {
14778 peek_end = Point::new(range.end.row + 1, 0);
14779 }
14780 buffer
14781 .diff_hunks_in_range(range.start..peek_end)
14782 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14783 })
14784 }
14785
14786 pub fn has_stageable_diff_hunks_in_ranges(
14787 &self,
14788 ranges: &[Range<Anchor>],
14789 snapshot: &MultiBufferSnapshot,
14790 ) -> bool {
14791 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14792 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14793 }
14794
14795 pub fn toggle_staged_selected_diff_hunks(
14796 &mut self,
14797 _: &::git::ToggleStaged,
14798 _: &mut Window,
14799 cx: &mut Context<Self>,
14800 ) {
14801 let snapshot = self.buffer.read(cx).snapshot(cx);
14802 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14803 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14804 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14805 }
14806
14807 pub fn stage_and_next(
14808 &mut self,
14809 _: &::git::StageAndNext,
14810 window: &mut Window,
14811 cx: &mut Context<Self>,
14812 ) {
14813 self.do_stage_or_unstage_and_next(true, window, cx);
14814 }
14815
14816 pub fn unstage_and_next(
14817 &mut self,
14818 _: &::git::UnstageAndNext,
14819 window: &mut Window,
14820 cx: &mut Context<Self>,
14821 ) {
14822 self.do_stage_or_unstage_and_next(false, window, cx);
14823 }
14824
14825 pub fn stage_or_unstage_diff_hunks(
14826 &mut self,
14827 stage: bool,
14828 ranges: Vec<Range<Anchor>>,
14829 cx: &mut Context<Self>,
14830 ) {
14831 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14832 cx.spawn(async move |this, cx| {
14833 task.await?;
14834 this.update(cx, |this, cx| {
14835 let snapshot = this.buffer.read(cx).snapshot(cx);
14836 let chunk_by = this
14837 .diff_hunks_in_ranges(&ranges, &snapshot)
14838 .chunk_by(|hunk| hunk.buffer_id);
14839 for (buffer_id, hunks) in &chunk_by {
14840 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14841 }
14842 })
14843 })
14844 .detach_and_log_err(cx);
14845 }
14846
14847 fn save_buffers_for_ranges_if_needed(
14848 &mut self,
14849 ranges: &[Range<Anchor>],
14850 cx: &mut Context<'_, Editor>,
14851 ) -> Task<Result<()>> {
14852 let multibuffer = self.buffer.read(cx);
14853 let snapshot = multibuffer.read(cx);
14854 let buffer_ids: HashSet<_> = ranges
14855 .iter()
14856 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14857 .collect();
14858 drop(snapshot);
14859
14860 let mut buffers = HashSet::default();
14861 for buffer_id in buffer_ids {
14862 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14863 let buffer = buffer_entity.read(cx);
14864 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14865 {
14866 buffers.insert(buffer_entity);
14867 }
14868 }
14869 }
14870
14871 if let Some(project) = &self.project {
14872 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14873 } else {
14874 Task::ready(Ok(()))
14875 }
14876 }
14877
14878 fn do_stage_or_unstage_and_next(
14879 &mut self,
14880 stage: bool,
14881 window: &mut Window,
14882 cx: &mut Context<Self>,
14883 ) {
14884 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14885
14886 if ranges.iter().any(|range| range.start != range.end) {
14887 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14888 return;
14889 }
14890
14891 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14892 let snapshot = self.snapshot(window, cx);
14893 let position = self.selections.newest::<Point>(cx).head();
14894 let mut row = snapshot
14895 .buffer_snapshot
14896 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14897 .find(|hunk| hunk.row_range.start.0 > position.row)
14898 .map(|hunk| hunk.row_range.start);
14899
14900 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14901 // Outside of the project diff editor, wrap around to the beginning.
14902 if !all_diff_hunks_expanded {
14903 row = row.or_else(|| {
14904 snapshot
14905 .buffer_snapshot
14906 .diff_hunks_in_range(Point::zero()..position)
14907 .find(|hunk| hunk.row_range.end.0 < position.row)
14908 .map(|hunk| hunk.row_range.start)
14909 });
14910 }
14911
14912 if let Some(row) = row {
14913 let destination = Point::new(row.0, 0);
14914 let autoscroll = Autoscroll::center();
14915
14916 self.unfold_ranges(&[destination..destination], false, false, cx);
14917 self.change_selections(Some(autoscroll), window, cx, |s| {
14918 s.select_ranges([destination..destination]);
14919 });
14920 }
14921 }
14922
14923 fn do_stage_or_unstage(
14924 &self,
14925 stage: bool,
14926 buffer_id: BufferId,
14927 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14928 cx: &mut App,
14929 ) -> Option<()> {
14930 let project = self.project.as_ref()?;
14931 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14932 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14933 let buffer_snapshot = buffer.read(cx).snapshot();
14934 let file_exists = buffer_snapshot
14935 .file()
14936 .is_some_and(|file| file.disk_state().exists());
14937 diff.update(cx, |diff, cx| {
14938 diff.stage_or_unstage_hunks(
14939 stage,
14940 &hunks
14941 .map(|hunk| buffer_diff::DiffHunk {
14942 buffer_range: hunk.buffer_range,
14943 diff_base_byte_range: hunk.diff_base_byte_range,
14944 secondary_status: hunk.secondary_status,
14945 range: Point::zero()..Point::zero(), // unused
14946 })
14947 .collect::<Vec<_>>(),
14948 &buffer_snapshot,
14949 file_exists,
14950 cx,
14951 )
14952 });
14953 None
14954 }
14955
14956 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14957 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14958 self.buffer
14959 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14960 }
14961
14962 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14963 self.buffer.update(cx, |buffer, cx| {
14964 let ranges = vec![Anchor::min()..Anchor::max()];
14965 if !buffer.all_diff_hunks_expanded()
14966 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14967 {
14968 buffer.collapse_diff_hunks(ranges, cx);
14969 true
14970 } else {
14971 false
14972 }
14973 })
14974 }
14975
14976 fn toggle_diff_hunks_in_ranges(
14977 &mut self,
14978 ranges: Vec<Range<Anchor>>,
14979 cx: &mut Context<'_, Editor>,
14980 ) {
14981 self.buffer.update(cx, |buffer, cx| {
14982 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14983 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14984 })
14985 }
14986
14987 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14988 self.buffer.update(cx, |buffer, cx| {
14989 let snapshot = buffer.snapshot(cx);
14990 let excerpt_id = range.end.excerpt_id;
14991 let point_range = range.to_point(&snapshot);
14992 let expand = !buffer.single_hunk_is_expanded(range, cx);
14993 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14994 })
14995 }
14996
14997 pub(crate) fn apply_all_diff_hunks(
14998 &mut self,
14999 _: &ApplyAllDiffHunks,
15000 window: &mut Window,
15001 cx: &mut Context<Self>,
15002 ) {
15003 let buffers = self.buffer.read(cx).all_buffers();
15004 for branch_buffer in buffers {
15005 branch_buffer.update(cx, |branch_buffer, cx| {
15006 branch_buffer.merge_into_base(Vec::new(), cx);
15007 });
15008 }
15009
15010 if let Some(project) = self.project.clone() {
15011 self.save(true, project, window, cx).detach_and_log_err(cx);
15012 }
15013 }
15014
15015 pub(crate) fn apply_selected_diff_hunks(
15016 &mut self,
15017 _: &ApplyDiffHunk,
15018 window: &mut Window,
15019 cx: &mut Context<Self>,
15020 ) {
15021 let snapshot = self.snapshot(window, cx);
15022 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
15023 let mut ranges_by_buffer = HashMap::default();
15024 self.transact(window, cx, |editor, _window, cx| {
15025 for hunk in hunks {
15026 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
15027 ranges_by_buffer
15028 .entry(buffer.clone())
15029 .or_insert_with(Vec::new)
15030 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
15031 }
15032 }
15033
15034 for (buffer, ranges) in ranges_by_buffer {
15035 buffer.update(cx, |buffer, cx| {
15036 buffer.merge_into_base(ranges, cx);
15037 });
15038 }
15039 });
15040
15041 if let Some(project) = self.project.clone() {
15042 self.save(true, project, window, cx).detach_and_log_err(cx);
15043 }
15044 }
15045
15046 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
15047 if hovered != self.gutter_hovered {
15048 self.gutter_hovered = hovered;
15049 cx.notify();
15050 }
15051 }
15052
15053 pub fn insert_blocks(
15054 &mut self,
15055 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
15056 autoscroll: Option<Autoscroll>,
15057 cx: &mut Context<Self>,
15058 ) -> Vec<CustomBlockId> {
15059 let blocks = self
15060 .display_map
15061 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
15062 if let Some(autoscroll) = autoscroll {
15063 self.request_autoscroll(autoscroll, cx);
15064 }
15065 cx.notify();
15066 blocks
15067 }
15068
15069 pub fn resize_blocks(
15070 &mut self,
15071 heights: HashMap<CustomBlockId, u32>,
15072 autoscroll: Option<Autoscroll>,
15073 cx: &mut Context<Self>,
15074 ) {
15075 self.display_map
15076 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
15077 if let Some(autoscroll) = autoscroll {
15078 self.request_autoscroll(autoscroll, cx);
15079 }
15080 cx.notify();
15081 }
15082
15083 pub fn replace_blocks(
15084 &mut self,
15085 renderers: HashMap<CustomBlockId, RenderBlock>,
15086 autoscroll: Option<Autoscroll>,
15087 cx: &mut Context<Self>,
15088 ) {
15089 self.display_map
15090 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
15091 if let Some(autoscroll) = autoscroll {
15092 self.request_autoscroll(autoscroll, cx);
15093 }
15094 cx.notify();
15095 }
15096
15097 pub fn remove_blocks(
15098 &mut self,
15099 block_ids: HashSet<CustomBlockId>,
15100 autoscroll: Option<Autoscroll>,
15101 cx: &mut Context<Self>,
15102 ) {
15103 self.display_map.update(cx, |display_map, cx| {
15104 display_map.remove_blocks(block_ids, cx)
15105 });
15106 if let Some(autoscroll) = autoscroll {
15107 self.request_autoscroll(autoscroll, cx);
15108 }
15109 cx.notify();
15110 }
15111
15112 pub fn row_for_block(
15113 &self,
15114 block_id: CustomBlockId,
15115 cx: &mut Context<Self>,
15116 ) -> Option<DisplayRow> {
15117 self.display_map
15118 .update(cx, |map, cx| map.row_for_block(block_id, cx))
15119 }
15120
15121 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
15122 self.focused_block = Some(focused_block);
15123 }
15124
15125 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
15126 self.focused_block.take()
15127 }
15128
15129 pub fn insert_creases(
15130 &mut self,
15131 creases: impl IntoIterator<Item = Crease<Anchor>>,
15132 cx: &mut Context<Self>,
15133 ) -> Vec<CreaseId> {
15134 self.display_map
15135 .update(cx, |map, cx| map.insert_creases(creases, cx))
15136 }
15137
15138 pub fn remove_creases(
15139 &mut self,
15140 ids: impl IntoIterator<Item = CreaseId>,
15141 cx: &mut Context<Self>,
15142 ) {
15143 self.display_map
15144 .update(cx, |map, cx| map.remove_creases(ids, cx));
15145 }
15146
15147 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
15148 self.display_map
15149 .update(cx, |map, cx| map.snapshot(cx))
15150 .longest_row()
15151 }
15152
15153 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
15154 self.display_map
15155 .update(cx, |map, cx| map.snapshot(cx))
15156 .max_point()
15157 }
15158
15159 pub fn text(&self, cx: &App) -> String {
15160 self.buffer.read(cx).read(cx).text()
15161 }
15162
15163 pub fn is_empty(&self, cx: &App) -> bool {
15164 self.buffer.read(cx).read(cx).is_empty()
15165 }
15166
15167 pub fn text_option(&self, cx: &App) -> Option<String> {
15168 let text = self.text(cx);
15169 let text = text.trim();
15170
15171 if text.is_empty() {
15172 return None;
15173 }
15174
15175 Some(text.to_string())
15176 }
15177
15178 pub fn set_text(
15179 &mut self,
15180 text: impl Into<Arc<str>>,
15181 window: &mut Window,
15182 cx: &mut Context<Self>,
15183 ) {
15184 self.transact(window, cx, |this, _, cx| {
15185 this.buffer
15186 .read(cx)
15187 .as_singleton()
15188 .expect("you can only call set_text on editors for singleton buffers")
15189 .update(cx, |buffer, cx| buffer.set_text(text, cx));
15190 });
15191 }
15192
15193 pub fn display_text(&self, cx: &mut App) -> String {
15194 self.display_map
15195 .update(cx, |map, cx| map.snapshot(cx))
15196 .text()
15197 }
15198
15199 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
15200 let mut wrap_guides = smallvec::smallvec![];
15201
15202 if self.show_wrap_guides == Some(false) {
15203 return wrap_guides;
15204 }
15205
15206 let settings = self.buffer.read(cx).language_settings(cx);
15207 if settings.show_wrap_guides {
15208 match self.soft_wrap_mode(cx) {
15209 SoftWrap::Column(soft_wrap) => {
15210 wrap_guides.push((soft_wrap as usize, true));
15211 }
15212 SoftWrap::Bounded(soft_wrap) => {
15213 wrap_guides.push((soft_wrap as usize, true));
15214 }
15215 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
15216 }
15217 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
15218 }
15219
15220 wrap_guides
15221 }
15222
15223 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
15224 let settings = self.buffer.read(cx).language_settings(cx);
15225 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
15226 match mode {
15227 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
15228 SoftWrap::None
15229 }
15230 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15231 language_settings::SoftWrap::PreferredLineLength => {
15232 SoftWrap::Column(settings.preferred_line_length)
15233 }
15234 language_settings::SoftWrap::Bounded => {
15235 SoftWrap::Bounded(settings.preferred_line_length)
15236 }
15237 }
15238 }
15239
15240 pub fn set_soft_wrap_mode(
15241 &mut self,
15242 mode: language_settings::SoftWrap,
15243
15244 cx: &mut Context<Self>,
15245 ) {
15246 self.soft_wrap_mode_override = Some(mode);
15247 cx.notify();
15248 }
15249
15250 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15251 self.hard_wrap = hard_wrap;
15252 cx.notify();
15253 }
15254
15255 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15256 self.text_style_refinement = Some(style);
15257 }
15258
15259 /// called by the Element so we know what style we were most recently rendered with.
15260 pub(crate) fn set_style(
15261 &mut self,
15262 style: EditorStyle,
15263 window: &mut Window,
15264 cx: &mut Context<Self>,
15265 ) {
15266 let rem_size = window.rem_size();
15267 self.display_map.update(cx, |map, cx| {
15268 map.set_font(
15269 style.text.font(),
15270 style.text.font_size.to_pixels(rem_size),
15271 cx,
15272 )
15273 });
15274 self.style = Some(style);
15275 }
15276
15277 pub fn style(&self) -> Option<&EditorStyle> {
15278 self.style.as_ref()
15279 }
15280
15281 // Called by the element. This method is not designed to be called outside of the editor
15282 // element's layout code because it does not notify when rewrapping is computed synchronously.
15283 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15284 self.display_map
15285 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15286 }
15287
15288 pub fn set_soft_wrap(&mut self) {
15289 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15290 }
15291
15292 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15293 if self.soft_wrap_mode_override.is_some() {
15294 self.soft_wrap_mode_override.take();
15295 } else {
15296 let soft_wrap = match self.soft_wrap_mode(cx) {
15297 SoftWrap::GitDiff => return,
15298 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15299 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15300 language_settings::SoftWrap::None
15301 }
15302 };
15303 self.soft_wrap_mode_override = Some(soft_wrap);
15304 }
15305 cx.notify();
15306 }
15307
15308 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15309 let Some(workspace) = self.workspace() else {
15310 return;
15311 };
15312 let fs = workspace.read(cx).app_state().fs.clone();
15313 let current_show = TabBarSettings::get_global(cx).show;
15314 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15315 setting.show = Some(!current_show);
15316 });
15317 }
15318
15319 pub fn toggle_indent_guides(
15320 &mut self,
15321 _: &ToggleIndentGuides,
15322 _: &mut Window,
15323 cx: &mut Context<Self>,
15324 ) {
15325 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15326 self.buffer
15327 .read(cx)
15328 .language_settings(cx)
15329 .indent_guides
15330 .enabled
15331 });
15332 self.show_indent_guides = Some(!currently_enabled);
15333 cx.notify();
15334 }
15335
15336 fn should_show_indent_guides(&self) -> Option<bool> {
15337 self.show_indent_guides
15338 }
15339
15340 pub fn toggle_line_numbers(
15341 &mut self,
15342 _: &ToggleLineNumbers,
15343 _: &mut Window,
15344 cx: &mut Context<Self>,
15345 ) {
15346 let mut editor_settings = EditorSettings::get_global(cx).clone();
15347 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15348 EditorSettings::override_global(editor_settings, cx);
15349 }
15350
15351 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15352 if let Some(show_line_numbers) = self.show_line_numbers {
15353 return show_line_numbers;
15354 }
15355 EditorSettings::get_global(cx).gutter.line_numbers
15356 }
15357
15358 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15359 self.use_relative_line_numbers
15360 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15361 }
15362
15363 pub fn toggle_relative_line_numbers(
15364 &mut self,
15365 _: &ToggleRelativeLineNumbers,
15366 _: &mut Window,
15367 cx: &mut Context<Self>,
15368 ) {
15369 let is_relative = self.should_use_relative_line_numbers(cx);
15370 self.set_relative_line_number(Some(!is_relative), cx)
15371 }
15372
15373 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15374 self.use_relative_line_numbers = is_relative;
15375 cx.notify();
15376 }
15377
15378 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15379 self.show_gutter = show_gutter;
15380 cx.notify();
15381 }
15382
15383 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15384 self.show_scrollbars = show_scrollbars;
15385 cx.notify();
15386 }
15387
15388 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15389 self.show_line_numbers = Some(show_line_numbers);
15390 cx.notify();
15391 }
15392
15393 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15394 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15395 cx.notify();
15396 }
15397
15398 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15399 self.show_code_actions = Some(show_code_actions);
15400 cx.notify();
15401 }
15402
15403 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15404 self.show_runnables = Some(show_runnables);
15405 cx.notify();
15406 }
15407
15408 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15409 self.show_breakpoints = Some(show_breakpoints);
15410 cx.notify();
15411 }
15412
15413 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15414 if self.display_map.read(cx).masked != masked {
15415 self.display_map.update(cx, |map, _| map.masked = masked);
15416 }
15417 cx.notify()
15418 }
15419
15420 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15421 self.show_wrap_guides = Some(show_wrap_guides);
15422 cx.notify();
15423 }
15424
15425 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15426 self.show_indent_guides = Some(show_indent_guides);
15427 cx.notify();
15428 }
15429
15430 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15431 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15432 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15433 if let Some(dir) = file.abs_path(cx).parent() {
15434 return Some(dir.to_owned());
15435 }
15436 }
15437
15438 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15439 return Some(project_path.path.to_path_buf());
15440 }
15441 }
15442
15443 None
15444 }
15445
15446 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15447 self.active_excerpt(cx)?
15448 .1
15449 .read(cx)
15450 .file()
15451 .and_then(|f| f.as_local())
15452 }
15453
15454 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15455 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15456 let buffer = buffer.read(cx);
15457 if let Some(project_path) = buffer.project_path(cx) {
15458 let project = self.project.as_ref()?.read(cx);
15459 project.absolute_path(&project_path, cx)
15460 } else {
15461 buffer
15462 .file()
15463 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15464 }
15465 })
15466 }
15467
15468 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15469 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15470 let project_path = buffer.read(cx).project_path(cx)?;
15471 let project = self.project.as_ref()?.read(cx);
15472 let entry = project.entry_for_path(&project_path, cx)?;
15473 let path = entry.path.to_path_buf();
15474 Some(path)
15475 })
15476 }
15477
15478 pub fn reveal_in_finder(
15479 &mut self,
15480 _: &RevealInFileManager,
15481 _window: &mut Window,
15482 cx: &mut Context<Self>,
15483 ) {
15484 if let Some(target) = self.target_file(cx) {
15485 cx.reveal_path(&target.abs_path(cx));
15486 }
15487 }
15488
15489 pub fn copy_path(
15490 &mut self,
15491 _: &zed_actions::workspace::CopyPath,
15492 _window: &mut Window,
15493 cx: &mut Context<Self>,
15494 ) {
15495 if let Some(path) = self.target_file_abs_path(cx) {
15496 if let Some(path) = path.to_str() {
15497 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15498 }
15499 }
15500 }
15501
15502 pub fn copy_relative_path(
15503 &mut self,
15504 _: &zed_actions::workspace::CopyRelativePath,
15505 _window: &mut Window,
15506 cx: &mut Context<Self>,
15507 ) {
15508 if let Some(path) = self.target_file_path(cx) {
15509 if let Some(path) = path.to_str() {
15510 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15511 }
15512 }
15513 }
15514
15515 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15516 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15517 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15518 } else {
15519 None
15520 }
15521 }
15522
15523 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15524 let _ = maybe!({
15525 let breakpoint_store = self.breakpoint_store.as_ref()?;
15526
15527 let Some((_, _, active_position)) =
15528 breakpoint_store.read(cx).active_position().cloned()
15529 else {
15530 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15531 return None;
15532 };
15533
15534 let snapshot = self
15535 .project
15536 .as_ref()?
15537 .read(cx)
15538 .buffer_for_id(active_position.buffer_id?, cx)?
15539 .read(cx)
15540 .snapshot();
15541
15542 for (id, ExcerptRange { context, .. }) in self
15543 .buffer
15544 .read(cx)
15545 .excerpts_for_buffer(active_position.buffer_id?, cx)
15546 {
15547 if context.start.cmp(&active_position, &snapshot).is_ge()
15548 || context.end.cmp(&active_position, &snapshot).is_lt()
15549 {
15550 continue;
15551 }
15552 let snapshot = self.buffer.read(cx).snapshot(cx);
15553 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15554
15555 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15556 self.go_to_line::<DebugCurrentRowHighlight>(
15557 multibuffer_anchor,
15558 Some(cx.theme().colors().editor_debugger_active_line_background),
15559 window,
15560 cx,
15561 );
15562
15563 cx.notify();
15564 }
15565
15566 Some(())
15567 });
15568 }
15569
15570 pub fn copy_file_name_without_extension(
15571 &mut self,
15572 _: &CopyFileNameWithoutExtension,
15573 _: &mut Window,
15574 cx: &mut Context<Self>,
15575 ) {
15576 if let Some(file) = self.target_file(cx) {
15577 if let Some(file_stem) = file.path().file_stem() {
15578 if let Some(name) = file_stem.to_str() {
15579 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15580 }
15581 }
15582 }
15583 }
15584
15585 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15586 if let Some(file) = self.target_file(cx) {
15587 if let Some(file_name) = file.path().file_name() {
15588 if let Some(name) = file_name.to_str() {
15589 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15590 }
15591 }
15592 }
15593 }
15594
15595 pub fn toggle_git_blame(
15596 &mut self,
15597 _: &::git::Blame,
15598 window: &mut Window,
15599 cx: &mut Context<Self>,
15600 ) {
15601 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15602
15603 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15604 self.start_git_blame(true, window, cx);
15605 }
15606
15607 cx.notify();
15608 }
15609
15610 pub fn toggle_git_blame_inline(
15611 &mut self,
15612 _: &ToggleGitBlameInline,
15613 window: &mut Window,
15614 cx: &mut Context<Self>,
15615 ) {
15616 self.toggle_git_blame_inline_internal(true, window, cx);
15617 cx.notify();
15618 }
15619
15620 pub fn git_blame_inline_enabled(&self) -> bool {
15621 self.git_blame_inline_enabled
15622 }
15623
15624 pub fn toggle_selection_menu(
15625 &mut self,
15626 _: &ToggleSelectionMenu,
15627 _: &mut Window,
15628 cx: &mut Context<Self>,
15629 ) {
15630 self.show_selection_menu = self
15631 .show_selection_menu
15632 .map(|show_selections_menu| !show_selections_menu)
15633 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15634
15635 cx.notify();
15636 }
15637
15638 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15639 self.show_selection_menu
15640 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15641 }
15642
15643 fn start_git_blame(
15644 &mut self,
15645 user_triggered: bool,
15646 window: &mut Window,
15647 cx: &mut Context<Self>,
15648 ) {
15649 if let Some(project) = self.project.as_ref() {
15650 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15651 return;
15652 };
15653
15654 if buffer.read(cx).file().is_none() {
15655 return;
15656 }
15657
15658 let focused = self.focus_handle(cx).contains_focused(window, cx);
15659
15660 let project = project.clone();
15661 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15662 self.blame_subscription =
15663 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15664 self.blame = Some(blame);
15665 }
15666 }
15667
15668 fn toggle_git_blame_inline_internal(
15669 &mut self,
15670 user_triggered: bool,
15671 window: &mut Window,
15672 cx: &mut Context<Self>,
15673 ) {
15674 if self.git_blame_inline_enabled {
15675 self.git_blame_inline_enabled = false;
15676 self.show_git_blame_inline = false;
15677 self.show_git_blame_inline_delay_task.take();
15678 } else {
15679 self.git_blame_inline_enabled = true;
15680 self.start_git_blame_inline(user_triggered, window, cx);
15681 }
15682
15683 cx.notify();
15684 }
15685
15686 fn start_git_blame_inline(
15687 &mut self,
15688 user_triggered: bool,
15689 window: &mut Window,
15690 cx: &mut Context<Self>,
15691 ) {
15692 self.start_git_blame(user_triggered, window, cx);
15693
15694 if ProjectSettings::get_global(cx)
15695 .git
15696 .inline_blame_delay()
15697 .is_some()
15698 {
15699 self.start_inline_blame_timer(window, cx);
15700 } else {
15701 self.show_git_blame_inline = true
15702 }
15703 }
15704
15705 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15706 self.blame.as_ref()
15707 }
15708
15709 pub fn show_git_blame_gutter(&self) -> bool {
15710 self.show_git_blame_gutter
15711 }
15712
15713 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15714 self.show_git_blame_gutter && self.has_blame_entries(cx)
15715 }
15716
15717 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15718 self.show_git_blame_inline
15719 && (self.focus_handle.is_focused(window)
15720 || self
15721 .git_blame_inline_tooltip
15722 .as_ref()
15723 .and_then(|t| t.upgrade())
15724 .is_some())
15725 && !self.newest_selection_head_on_empty_line(cx)
15726 && self.has_blame_entries(cx)
15727 }
15728
15729 fn has_blame_entries(&self, cx: &App) -> bool {
15730 self.blame()
15731 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15732 }
15733
15734 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15735 let cursor_anchor = self.selections.newest_anchor().head();
15736
15737 let snapshot = self.buffer.read(cx).snapshot(cx);
15738 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15739
15740 snapshot.line_len(buffer_row) == 0
15741 }
15742
15743 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15744 let buffer_and_selection = maybe!({
15745 let selection = self.selections.newest::<Point>(cx);
15746 let selection_range = selection.range();
15747
15748 let multi_buffer = self.buffer().read(cx);
15749 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15750 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15751
15752 let (buffer, range, _) = if selection.reversed {
15753 buffer_ranges.first()
15754 } else {
15755 buffer_ranges.last()
15756 }?;
15757
15758 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15759 ..text::ToPoint::to_point(&range.end, &buffer).row;
15760 Some((
15761 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15762 selection,
15763 ))
15764 });
15765
15766 let Some((buffer, selection)) = buffer_and_selection else {
15767 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15768 };
15769
15770 let Some(project) = self.project.as_ref() else {
15771 return Task::ready(Err(anyhow!("editor does not have project")));
15772 };
15773
15774 project.update(cx, |project, cx| {
15775 project.get_permalink_to_line(&buffer, selection, cx)
15776 })
15777 }
15778
15779 pub fn copy_permalink_to_line(
15780 &mut self,
15781 _: &CopyPermalinkToLine,
15782 window: &mut Window,
15783 cx: &mut Context<Self>,
15784 ) {
15785 let permalink_task = self.get_permalink_to_line(cx);
15786 let workspace = self.workspace();
15787
15788 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15789 Ok(permalink) => {
15790 cx.update(|_, cx| {
15791 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15792 })
15793 .ok();
15794 }
15795 Err(err) => {
15796 let message = format!("Failed to copy permalink: {err}");
15797
15798 Err::<(), anyhow::Error>(err).log_err();
15799
15800 if let Some(workspace) = workspace {
15801 workspace
15802 .update_in(cx, |workspace, _, cx| {
15803 struct CopyPermalinkToLine;
15804
15805 workspace.show_toast(
15806 Toast::new(
15807 NotificationId::unique::<CopyPermalinkToLine>(),
15808 message,
15809 ),
15810 cx,
15811 )
15812 })
15813 .ok();
15814 }
15815 }
15816 })
15817 .detach();
15818 }
15819
15820 pub fn copy_file_location(
15821 &mut self,
15822 _: &CopyFileLocation,
15823 _: &mut Window,
15824 cx: &mut Context<Self>,
15825 ) {
15826 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15827 if let Some(file) = self.target_file(cx) {
15828 if let Some(path) = file.path().to_str() {
15829 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15830 }
15831 }
15832 }
15833
15834 pub fn open_permalink_to_line(
15835 &mut self,
15836 _: &OpenPermalinkToLine,
15837 window: &mut Window,
15838 cx: &mut Context<Self>,
15839 ) {
15840 let permalink_task = self.get_permalink_to_line(cx);
15841 let workspace = self.workspace();
15842
15843 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15844 Ok(permalink) => {
15845 cx.update(|_, cx| {
15846 cx.open_url(permalink.as_ref());
15847 })
15848 .ok();
15849 }
15850 Err(err) => {
15851 let message = format!("Failed to open permalink: {err}");
15852
15853 Err::<(), anyhow::Error>(err).log_err();
15854
15855 if let Some(workspace) = workspace {
15856 workspace
15857 .update(cx, |workspace, cx| {
15858 struct OpenPermalinkToLine;
15859
15860 workspace.show_toast(
15861 Toast::new(
15862 NotificationId::unique::<OpenPermalinkToLine>(),
15863 message,
15864 ),
15865 cx,
15866 )
15867 })
15868 .ok();
15869 }
15870 }
15871 })
15872 .detach();
15873 }
15874
15875 pub fn insert_uuid_v4(
15876 &mut self,
15877 _: &InsertUuidV4,
15878 window: &mut Window,
15879 cx: &mut Context<Self>,
15880 ) {
15881 self.insert_uuid(UuidVersion::V4, window, cx);
15882 }
15883
15884 pub fn insert_uuid_v7(
15885 &mut self,
15886 _: &InsertUuidV7,
15887 window: &mut Window,
15888 cx: &mut Context<Self>,
15889 ) {
15890 self.insert_uuid(UuidVersion::V7, window, cx);
15891 }
15892
15893 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15894 self.transact(window, cx, |this, window, cx| {
15895 let edits = this
15896 .selections
15897 .all::<Point>(cx)
15898 .into_iter()
15899 .map(|selection| {
15900 let uuid = match version {
15901 UuidVersion::V4 => uuid::Uuid::new_v4(),
15902 UuidVersion::V7 => uuid::Uuid::now_v7(),
15903 };
15904
15905 (selection.range(), uuid.to_string())
15906 });
15907 this.edit(edits, cx);
15908 this.refresh_inline_completion(true, false, window, cx);
15909 });
15910 }
15911
15912 pub fn open_selections_in_multibuffer(
15913 &mut self,
15914 _: &OpenSelectionsInMultibuffer,
15915 window: &mut Window,
15916 cx: &mut Context<Self>,
15917 ) {
15918 let multibuffer = self.buffer.read(cx);
15919
15920 let Some(buffer) = multibuffer.as_singleton() else {
15921 return;
15922 };
15923
15924 let Some(workspace) = self.workspace() else {
15925 return;
15926 };
15927
15928 let locations = self
15929 .selections
15930 .disjoint_anchors()
15931 .iter()
15932 .map(|range| Location {
15933 buffer: buffer.clone(),
15934 range: range.start.text_anchor..range.end.text_anchor,
15935 })
15936 .collect::<Vec<_>>();
15937
15938 let title = multibuffer.title(cx).to_string();
15939
15940 cx.spawn_in(window, async move |_, cx| {
15941 workspace.update_in(cx, |workspace, window, cx| {
15942 Self::open_locations_in_multibuffer(
15943 workspace,
15944 locations,
15945 format!("Selections for '{title}'"),
15946 false,
15947 MultibufferSelectionMode::All,
15948 window,
15949 cx,
15950 );
15951 })
15952 })
15953 .detach();
15954 }
15955
15956 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15957 /// last highlight added will be used.
15958 ///
15959 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15960 pub fn highlight_rows<T: 'static>(
15961 &mut self,
15962 range: Range<Anchor>,
15963 color: Hsla,
15964 should_autoscroll: bool,
15965 cx: &mut Context<Self>,
15966 ) {
15967 let snapshot = self.buffer().read(cx).snapshot(cx);
15968 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15969 let ix = row_highlights.binary_search_by(|highlight| {
15970 Ordering::Equal
15971 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15972 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15973 });
15974
15975 if let Err(mut ix) = ix {
15976 let index = post_inc(&mut self.highlight_order);
15977
15978 // If this range intersects with the preceding highlight, then merge it with
15979 // the preceding highlight. Otherwise insert a new highlight.
15980 let mut merged = false;
15981 if ix > 0 {
15982 let prev_highlight = &mut row_highlights[ix - 1];
15983 if prev_highlight
15984 .range
15985 .end
15986 .cmp(&range.start, &snapshot)
15987 .is_ge()
15988 {
15989 ix -= 1;
15990 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15991 prev_highlight.range.end = range.end;
15992 }
15993 merged = true;
15994 prev_highlight.index = index;
15995 prev_highlight.color = color;
15996 prev_highlight.should_autoscroll = should_autoscroll;
15997 }
15998 }
15999
16000 if !merged {
16001 row_highlights.insert(
16002 ix,
16003 RowHighlight {
16004 range: range.clone(),
16005 index,
16006 color,
16007 should_autoscroll,
16008 },
16009 );
16010 }
16011
16012 // If any of the following highlights intersect with this one, merge them.
16013 while let Some(next_highlight) = row_highlights.get(ix + 1) {
16014 let highlight = &row_highlights[ix];
16015 if next_highlight
16016 .range
16017 .start
16018 .cmp(&highlight.range.end, &snapshot)
16019 .is_le()
16020 {
16021 if next_highlight
16022 .range
16023 .end
16024 .cmp(&highlight.range.end, &snapshot)
16025 .is_gt()
16026 {
16027 row_highlights[ix].range.end = next_highlight.range.end;
16028 }
16029 row_highlights.remove(ix + 1);
16030 } else {
16031 break;
16032 }
16033 }
16034 }
16035 }
16036
16037 /// Remove any highlighted row ranges of the given type that intersect the
16038 /// given ranges.
16039 pub fn remove_highlighted_rows<T: 'static>(
16040 &mut self,
16041 ranges_to_remove: Vec<Range<Anchor>>,
16042 cx: &mut Context<Self>,
16043 ) {
16044 let snapshot = self.buffer().read(cx).snapshot(cx);
16045 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
16046 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
16047 row_highlights.retain(|highlight| {
16048 while let Some(range_to_remove) = ranges_to_remove.peek() {
16049 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
16050 Ordering::Less | Ordering::Equal => {
16051 ranges_to_remove.next();
16052 }
16053 Ordering::Greater => {
16054 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
16055 Ordering::Less | Ordering::Equal => {
16056 return false;
16057 }
16058 Ordering::Greater => break,
16059 }
16060 }
16061 }
16062 }
16063
16064 true
16065 })
16066 }
16067
16068 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
16069 pub fn clear_row_highlights<T: 'static>(&mut self) {
16070 self.highlighted_rows.remove(&TypeId::of::<T>());
16071 }
16072
16073 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
16074 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
16075 self.highlighted_rows
16076 .get(&TypeId::of::<T>())
16077 .map_or(&[] as &[_], |vec| vec.as_slice())
16078 .iter()
16079 .map(|highlight| (highlight.range.clone(), highlight.color))
16080 }
16081
16082 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
16083 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
16084 /// Allows to ignore certain kinds of highlights.
16085 pub fn highlighted_display_rows(
16086 &self,
16087 window: &mut Window,
16088 cx: &mut App,
16089 ) -> BTreeMap<DisplayRow, LineHighlight> {
16090 let snapshot = self.snapshot(window, cx);
16091 let mut used_highlight_orders = HashMap::default();
16092 self.highlighted_rows
16093 .iter()
16094 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
16095 .fold(
16096 BTreeMap::<DisplayRow, LineHighlight>::new(),
16097 |mut unique_rows, highlight| {
16098 let start = highlight.range.start.to_display_point(&snapshot);
16099 let end = highlight.range.end.to_display_point(&snapshot);
16100 let start_row = start.row().0;
16101 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
16102 && end.column() == 0
16103 {
16104 end.row().0.saturating_sub(1)
16105 } else {
16106 end.row().0
16107 };
16108 for row in start_row..=end_row {
16109 let used_index =
16110 used_highlight_orders.entry(row).or_insert(highlight.index);
16111 if highlight.index >= *used_index {
16112 *used_index = highlight.index;
16113 unique_rows.insert(DisplayRow(row), highlight.color.into());
16114 }
16115 }
16116 unique_rows
16117 },
16118 )
16119 }
16120
16121 pub fn highlighted_display_row_for_autoscroll(
16122 &self,
16123 snapshot: &DisplaySnapshot,
16124 ) -> Option<DisplayRow> {
16125 self.highlighted_rows
16126 .values()
16127 .flat_map(|highlighted_rows| highlighted_rows.iter())
16128 .filter_map(|highlight| {
16129 if highlight.should_autoscroll {
16130 Some(highlight.range.start.to_display_point(snapshot).row())
16131 } else {
16132 None
16133 }
16134 })
16135 .min()
16136 }
16137
16138 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
16139 self.highlight_background::<SearchWithinRange>(
16140 ranges,
16141 |colors| colors.editor_document_highlight_read_background,
16142 cx,
16143 )
16144 }
16145
16146 pub fn set_breadcrumb_header(&mut self, new_header: String) {
16147 self.breadcrumb_header = Some(new_header);
16148 }
16149
16150 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
16151 self.clear_background_highlights::<SearchWithinRange>(cx);
16152 }
16153
16154 pub fn highlight_background<T: 'static>(
16155 &mut self,
16156 ranges: &[Range<Anchor>],
16157 color_fetcher: fn(&ThemeColors) -> Hsla,
16158 cx: &mut Context<Self>,
16159 ) {
16160 self.background_highlights
16161 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16162 self.scrollbar_marker_state.dirty = true;
16163 cx.notify();
16164 }
16165
16166 pub fn clear_background_highlights<T: 'static>(
16167 &mut self,
16168 cx: &mut Context<Self>,
16169 ) -> Option<BackgroundHighlight> {
16170 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
16171 if !text_highlights.1.is_empty() {
16172 self.scrollbar_marker_state.dirty = true;
16173 cx.notify();
16174 }
16175 Some(text_highlights)
16176 }
16177
16178 pub fn highlight_gutter<T: 'static>(
16179 &mut self,
16180 ranges: &[Range<Anchor>],
16181 color_fetcher: fn(&App) -> Hsla,
16182 cx: &mut Context<Self>,
16183 ) {
16184 self.gutter_highlights
16185 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
16186 cx.notify();
16187 }
16188
16189 pub fn clear_gutter_highlights<T: 'static>(
16190 &mut self,
16191 cx: &mut Context<Self>,
16192 ) -> Option<GutterHighlight> {
16193 cx.notify();
16194 self.gutter_highlights.remove(&TypeId::of::<T>())
16195 }
16196
16197 #[cfg(feature = "test-support")]
16198 pub fn all_text_background_highlights(
16199 &self,
16200 window: &mut Window,
16201 cx: &mut Context<Self>,
16202 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16203 let snapshot = self.snapshot(window, cx);
16204 let buffer = &snapshot.buffer_snapshot;
16205 let start = buffer.anchor_before(0);
16206 let end = buffer.anchor_after(buffer.len());
16207 let theme = cx.theme().colors();
16208 self.background_highlights_in_range(start..end, &snapshot, theme)
16209 }
16210
16211 #[cfg(feature = "test-support")]
16212 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
16213 let snapshot = self.buffer().read(cx).snapshot(cx);
16214
16215 let highlights = self
16216 .background_highlights
16217 .get(&TypeId::of::<items::BufferSearchHighlights>());
16218
16219 if let Some((_color, ranges)) = highlights {
16220 ranges
16221 .iter()
16222 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
16223 .collect_vec()
16224 } else {
16225 vec![]
16226 }
16227 }
16228
16229 fn document_highlights_for_position<'a>(
16230 &'a self,
16231 position: Anchor,
16232 buffer: &'a MultiBufferSnapshot,
16233 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16234 let read_highlights = self
16235 .background_highlights
16236 .get(&TypeId::of::<DocumentHighlightRead>())
16237 .map(|h| &h.1);
16238 let write_highlights = self
16239 .background_highlights
16240 .get(&TypeId::of::<DocumentHighlightWrite>())
16241 .map(|h| &h.1);
16242 let left_position = position.bias_left(buffer);
16243 let right_position = position.bias_right(buffer);
16244 read_highlights
16245 .into_iter()
16246 .chain(write_highlights)
16247 .flat_map(move |ranges| {
16248 let start_ix = match ranges.binary_search_by(|probe| {
16249 let cmp = probe.end.cmp(&left_position, buffer);
16250 if cmp.is_ge() {
16251 Ordering::Greater
16252 } else {
16253 Ordering::Less
16254 }
16255 }) {
16256 Ok(i) | Err(i) => i,
16257 };
16258
16259 ranges[start_ix..]
16260 .iter()
16261 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16262 })
16263 }
16264
16265 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16266 self.background_highlights
16267 .get(&TypeId::of::<T>())
16268 .map_or(false, |(_, highlights)| !highlights.is_empty())
16269 }
16270
16271 pub fn background_highlights_in_range(
16272 &self,
16273 search_range: Range<Anchor>,
16274 display_snapshot: &DisplaySnapshot,
16275 theme: &ThemeColors,
16276 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16277 let mut results = Vec::new();
16278 for (color_fetcher, ranges) in self.background_highlights.values() {
16279 let color = color_fetcher(theme);
16280 let start_ix = match ranges.binary_search_by(|probe| {
16281 let cmp = probe
16282 .end
16283 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16284 if cmp.is_gt() {
16285 Ordering::Greater
16286 } else {
16287 Ordering::Less
16288 }
16289 }) {
16290 Ok(i) | Err(i) => i,
16291 };
16292 for range in &ranges[start_ix..] {
16293 if range
16294 .start
16295 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16296 .is_ge()
16297 {
16298 break;
16299 }
16300
16301 let start = range.start.to_display_point(display_snapshot);
16302 let end = range.end.to_display_point(display_snapshot);
16303 results.push((start..end, color))
16304 }
16305 }
16306 results
16307 }
16308
16309 pub fn background_highlight_row_ranges<T: 'static>(
16310 &self,
16311 search_range: Range<Anchor>,
16312 display_snapshot: &DisplaySnapshot,
16313 count: usize,
16314 ) -> Vec<RangeInclusive<DisplayPoint>> {
16315 let mut results = Vec::new();
16316 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16317 return vec![];
16318 };
16319
16320 let start_ix = match ranges.binary_search_by(|probe| {
16321 let cmp = probe
16322 .end
16323 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16324 if cmp.is_gt() {
16325 Ordering::Greater
16326 } else {
16327 Ordering::Less
16328 }
16329 }) {
16330 Ok(i) | Err(i) => i,
16331 };
16332 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16333 if let (Some(start_display), Some(end_display)) = (start, end) {
16334 results.push(
16335 start_display.to_display_point(display_snapshot)
16336 ..=end_display.to_display_point(display_snapshot),
16337 );
16338 }
16339 };
16340 let mut start_row: Option<Point> = None;
16341 let mut end_row: Option<Point> = None;
16342 if ranges.len() > count {
16343 return Vec::new();
16344 }
16345 for range in &ranges[start_ix..] {
16346 if range
16347 .start
16348 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16349 .is_ge()
16350 {
16351 break;
16352 }
16353 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16354 if let Some(current_row) = &end_row {
16355 if end.row == current_row.row {
16356 continue;
16357 }
16358 }
16359 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16360 if start_row.is_none() {
16361 assert_eq!(end_row, None);
16362 start_row = Some(start);
16363 end_row = Some(end);
16364 continue;
16365 }
16366 if let Some(current_end) = end_row.as_mut() {
16367 if start.row > current_end.row + 1 {
16368 push_region(start_row, end_row);
16369 start_row = Some(start);
16370 end_row = Some(end);
16371 } else {
16372 // Merge two hunks.
16373 *current_end = end;
16374 }
16375 } else {
16376 unreachable!();
16377 }
16378 }
16379 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16380 push_region(start_row, end_row);
16381 results
16382 }
16383
16384 pub fn gutter_highlights_in_range(
16385 &self,
16386 search_range: Range<Anchor>,
16387 display_snapshot: &DisplaySnapshot,
16388 cx: &App,
16389 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16390 let mut results = Vec::new();
16391 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16392 let color = color_fetcher(cx);
16393 let start_ix = match ranges.binary_search_by(|probe| {
16394 let cmp = probe
16395 .end
16396 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16397 if cmp.is_gt() {
16398 Ordering::Greater
16399 } else {
16400 Ordering::Less
16401 }
16402 }) {
16403 Ok(i) | Err(i) => i,
16404 };
16405 for range in &ranges[start_ix..] {
16406 if range
16407 .start
16408 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16409 .is_ge()
16410 {
16411 break;
16412 }
16413
16414 let start = range.start.to_display_point(display_snapshot);
16415 let end = range.end.to_display_point(display_snapshot);
16416 results.push((start..end, color))
16417 }
16418 }
16419 results
16420 }
16421
16422 /// Get the text ranges corresponding to the redaction query
16423 pub fn redacted_ranges(
16424 &self,
16425 search_range: Range<Anchor>,
16426 display_snapshot: &DisplaySnapshot,
16427 cx: &App,
16428 ) -> Vec<Range<DisplayPoint>> {
16429 display_snapshot
16430 .buffer_snapshot
16431 .redacted_ranges(search_range, |file| {
16432 if let Some(file) = file {
16433 file.is_private()
16434 && EditorSettings::get(
16435 Some(SettingsLocation {
16436 worktree_id: file.worktree_id(cx),
16437 path: file.path().as_ref(),
16438 }),
16439 cx,
16440 )
16441 .redact_private_values
16442 } else {
16443 false
16444 }
16445 })
16446 .map(|range| {
16447 range.start.to_display_point(display_snapshot)
16448 ..range.end.to_display_point(display_snapshot)
16449 })
16450 .collect()
16451 }
16452
16453 pub fn highlight_text<T: 'static>(
16454 &mut self,
16455 ranges: Vec<Range<Anchor>>,
16456 style: HighlightStyle,
16457 cx: &mut Context<Self>,
16458 ) {
16459 self.display_map.update(cx, |map, _| {
16460 map.highlight_text(TypeId::of::<T>(), ranges, style)
16461 });
16462 cx.notify();
16463 }
16464
16465 pub(crate) fn highlight_inlays<T: 'static>(
16466 &mut self,
16467 highlights: Vec<InlayHighlight>,
16468 style: HighlightStyle,
16469 cx: &mut Context<Self>,
16470 ) {
16471 self.display_map.update(cx, |map, _| {
16472 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16473 });
16474 cx.notify();
16475 }
16476
16477 pub fn text_highlights<'a, T: 'static>(
16478 &'a self,
16479 cx: &'a App,
16480 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16481 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16482 }
16483
16484 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16485 let cleared = self
16486 .display_map
16487 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16488 if cleared {
16489 cx.notify();
16490 }
16491 }
16492
16493 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16494 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16495 && self.focus_handle.is_focused(window)
16496 }
16497
16498 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16499 self.show_cursor_when_unfocused = is_enabled;
16500 cx.notify();
16501 }
16502
16503 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16504 cx.notify();
16505 }
16506
16507 fn on_buffer_event(
16508 &mut self,
16509 multibuffer: &Entity<MultiBuffer>,
16510 event: &multi_buffer::Event,
16511 window: &mut Window,
16512 cx: &mut Context<Self>,
16513 ) {
16514 match event {
16515 multi_buffer::Event::Edited {
16516 singleton_buffer_edited,
16517 edited_buffer: buffer_edited,
16518 } => {
16519 self.scrollbar_marker_state.dirty = true;
16520 self.active_indent_guides_state.dirty = true;
16521 self.refresh_active_diagnostics(cx);
16522 self.refresh_code_actions(window, cx);
16523 if self.has_active_inline_completion() {
16524 self.update_visible_inline_completion(window, cx);
16525 }
16526 if let Some(buffer) = buffer_edited {
16527 let buffer_id = buffer.read(cx).remote_id();
16528 if !self.registered_buffers.contains_key(&buffer_id) {
16529 if let Some(project) = self.project.as_ref() {
16530 project.update(cx, |project, cx| {
16531 self.registered_buffers.insert(
16532 buffer_id,
16533 project.register_buffer_with_language_servers(&buffer, cx),
16534 );
16535 })
16536 }
16537 }
16538 }
16539 cx.emit(EditorEvent::BufferEdited);
16540 cx.emit(SearchEvent::MatchesInvalidated);
16541 if *singleton_buffer_edited {
16542 if let Some(project) = &self.project {
16543 #[allow(clippy::mutable_key_type)]
16544 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16545 multibuffer
16546 .all_buffers()
16547 .into_iter()
16548 .filter_map(|buffer| {
16549 buffer.update(cx, |buffer, cx| {
16550 let language = buffer.language()?;
16551 let should_discard = project.update(cx, |project, cx| {
16552 project.is_local()
16553 && !project.has_language_servers_for(buffer, cx)
16554 });
16555 should_discard.not().then_some(language.clone())
16556 })
16557 })
16558 .collect::<HashSet<_>>()
16559 });
16560 if !languages_affected.is_empty() {
16561 self.refresh_inlay_hints(
16562 InlayHintRefreshReason::BufferEdited(languages_affected),
16563 cx,
16564 );
16565 }
16566 }
16567 }
16568
16569 let Some(project) = &self.project else { return };
16570 let (telemetry, is_via_ssh) = {
16571 let project = project.read(cx);
16572 let telemetry = project.client().telemetry().clone();
16573 let is_via_ssh = project.is_via_ssh();
16574 (telemetry, is_via_ssh)
16575 };
16576 refresh_linked_ranges(self, window, cx);
16577 telemetry.log_edit_event("editor", is_via_ssh);
16578 }
16579 multi_buffer::Event::ExcerptsAdded {
16580 buffer,
16581 predecessor,
16582 excerpts,
16583 } => {
16584 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16585 let buffer_id = buffer.read(cx).remote_id();
16586 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16587 if let Some(project) = &self.project {
16588 get_uncommitted_diff_for_buffer(
16589 project,
16590 [buffer.clone()],
16591 self.buffer.clone(),
16592 cx,
16593 )
16594 .detach();
16595 }
16596 }
16597 cx.emit(EditorEvent::ExcerptsAdded {
16598 buffer: buffer.clone(),
16599 predecessor: *predecessor,
16600 excerpts: excerpts.clone(),
16601 });
16602 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16603 }
16604 multi_buffer::Event::ExcerptsRemoved { ids } => {
16605 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16606 let buffer = self.buffer.read(cx);
16607 self.registered_buffers
16608 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16609 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16610 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16611 }
16612 multi_buffer::Event::ExcerptsEdited {
16613 excerpt_ids,
16614 buffer_ids,
16615 } => {
16616 self.display_map.update(cx, |map, cx| {
16617 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16618 });
16619 cx.emit(EditorEvent::ExcerptsEdited {
16620 ids: excerpt_ids.clone(),
16621 })
16622 }
16623 multi_buffer::Event::ExcerptsExpanded { ids } => {
16624 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16625 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16626 }
16627 multi_buffer::Event::Reparsed(buffer_id) => {
16628 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16629 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16630
16631 cx.emit(EditorEvent::Reparsed(*buffer_id));
16632 }
16633 multi_buffer::Event::DiffHunksToggled => {
16634 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16635 }
16636 multi_buffer::Event::LanguageChanged(buffer_id) => {
16637 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16638 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16639 cx.emit(EditorEvent::Reparsed(*buffer_id));
16640 cx.notify();
16641 }
16642 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16643 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16644 multi_buffer::Event::FileHandleChanged
16645 | multi_buffer::Event::Reloaded
16646 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16647 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16648 multi_buffer::Event::DiagnosticsUpdated => {
16649 self.refresh_active_diagnostics(cx);
16650 self.refresh_inline_diagnostics(true, window, cx);
16651 self.scrollbar_marker_state.dirty = true;
16652 cx.notify();
16653 }
16654 _ => {}
16655 };
16656 }
16657
16658 fn on_display_map_changed(
16659 &mut self,
16660 _: Entity<DisplayMap>,
16661 _: &mut Window,
16662 cx: &mut Context<Self>,
16663 ) {
16664 cx.notify();
16665 }
16666
16667 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16668 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16669 self.update_edit_prediction_settings(cx);
16670 self.refresh_inline_completion(true, false, window, cx);
16671 self.refresh_inlay_hints(
16672 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16673 self.selections.newest_anchor().head(),
16674 &self.buffer.read(cx).snapshot(cx),
16675 cx,
16676 )),
16677 cx,
16678 );
16679
16680 let old_cursor_shape = self.cursor_shape;
16681
16682 {
16683 let editor_settings = EditorSettings::get_global(cx);
16684 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16685 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16686 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16687 self.hide_mouse_while_typing = editor_settings.hide_mouse_while_typing.unwrap_or(true);
16688
16689 if !self.hide_mouse_while_typing {
16690 self.mouse_cursor_hidden = false;
16691 }
16692 }
16693
16694 if old_cursor_shape != self.cursor_shape {
16695 cx.emit(EditorEvent::CursorShapeChanged);
16696 }
16697
16698 let project_settings = ProjectSettings::get_global(cx);
16699 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16700
16701 if self.mode == EditorMode::Full {
16702 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16703 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16704 if self.show_inline_diagnostics != show_inline_diagnostics {
16705 self.show_inline_diagnostics = show_inline_diagnostics;
16706 self.refresh_inline_diagnostics(false, window, cx);
16707 }
16708
16709 if self.git_blame_inline_enabled != inline_blame_enabled {
16710 self.toggle_git_blame_inline_internal(false, window, cx);
16711 }
16712 }
16713
16714 cx.notify();
16715 }
16716
16717 pub fn set_searchable(&mut self, searchable: bool) {
16718 self.searchable = searchable;
16719 }
16720
16721 pub fn searchable(&self) -> bool {
16722 self.searchable
16723 }
16724
16725 fn open_proposed_changes_editor(
16726 &mut self,
16727 _: &OpenProposedChangesEditor,
16728 window: &mut Window,
16729 cx: &mut Context<Self>,
16730 ) {
16731 let Some(workspace) = self.workspace() else {
16732 cx.propagate();
16733 return;
16734 };
16735
16736 let selections = self.selections.all::<usize>(cx);
16737 let multi_buffer = self.buffer.read(cx);
16738 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16739 let mut new_selections_by_buffer = HashMap::default();
16740 for selection in selections {
16741 for (buffer, range, _) in
16742 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16743 {
16744 let mut range = range.to_point(buffer);
16745 range.start.column = 0;
16746 range.end.column = buffer.line_len(range.end.row);
16747 new_selections_by_buffer
16748 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16749 .or_insert(Vec::new())
16750 .push(range)
16751 }
16752 }
16753
16754 let proposed_changes_buffers = new_selections_by_buffer
16755 .into_iter()
16756 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16757 .collect::<Vec<_>>();
16758 let proposed_changes_editor = cx.new(|cx| {
16759 ProposedChangesEditor::new(
16760 "Proposed changes",
16761 proposed_changes_buffers,
16762 self.project.clone(),
16763 window,
16764 cx,
16765 )
16766 });
16767
16768 window.defer(cx, move |window, cx| {
16769 workspace.update(cx, |workspace, cx| {
16770 workspace.active_pane().update(cx, |pane, cx| {
16771 pane.add_item(
16772 Box::new(proposed_changes_editor),
16773 true,
16774 true,
16775 None,
16776 window,
16777 cx,
16778 );
16779 });
16780 });
16781 });
16782 }
16783
16784 pub fn open_excerpts_in_split(
16785 &mut self,
16786 _: &OpenExcerptsSplit,
16787 window: &mut Window,
16788 cx: &mut Context<Self>,
16789 ) {
16790 self.open_excerpts_common(None, true, window, cx)
16791 }
16792
16793 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16794 self.open_excerpts_common(None, false, window, cx)
16795 }
16796
16797 fn open_excerpts_common(
16798 &mut self,
16799 jump_data: Option<JumpData>,
16800 split: bool,
16801 window: &mut Window,
16802 cx: &mut Context<Self>,
16803 ) {
16804 let Some(workspace) = self.workspace() else {
16805 cx.propagate();
16806 return;
16807 };
16808
16809 if self.buffer.read(cx).is_singleton() {
16810 cx.propagate();
16811 return;
16812 }
16813
16814 let mut new_selections_by_buffer = HashMap::default();
16815 match &jump_data {
16816 Some(JumpData::MultiBufferPoint {
16817 excerpt_id,
16818 position,
16819 anchor,
16820 line_offset_from_top,
16821 }) => {
16822 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16823 if let Some(buffer) = multi_buffer_snapshot
16824 .buffer_id_for_excerpt(*excerpt_id)
16825 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16826 {
16827 let buffer_snapshot = buffer.read(cx).snapshot();
16828 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16829 language::ToPoint::to_point(anchor, &buffer_snapshot)
16830 } else {
16831 buffer_snapshot.clip_point(*position, Bias::Left)
16832 };
16833 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16834 new_selections_by_buffer.insert(
16835 buffer,
16836 (
16837 vec![jump_to_offset..jump_to_offset],
16838 Some(*line_offset_from_top),
16839 ),
16840 );
16841 }
16842 }
16843 Some(JumpData::MultiBufferRow {
16844 row,
16845 line_offset_from_top,
16846 }) => {
16847 let point = MultiBufferPoint::new(row.0, 0);
16848 if let Some((buffer, buffer_point, _)) =
16849 self.buffer.read(cx).point_to_buffer_point(point, cx)
16850 {
16851 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16852 new_selections_by_buffer
16853 .entry(buffer)
16854 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16855 .0
16856 .push(buffer_offset..buffer_offset)
16857 }
16858 }
16859 None => {
16860 let selections = self.selections.all::<usize>(cx);
16861 let multi_buffer = self.buffer.read(cx);
16862 for selection in selections {
16863 for (snapshot, range, _, anchor) in multi_buffer
16864 .snapshot(cx)
16865 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16866 {
16867 if let Some(anchor) = anchor {
16868 // selection is in a deleted hunk
16869 let Some(buffer_id) = anchor.buffer_id else {
16870 continue;
16871 };
16872 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16873 continue;
16874 };
16875 let offset = text::ToOffset::to_offset(
16876 &anchor.text_anchor,
16877 &buffer_handle.read(cx).snapshot(),
16878 );
16879 let range = offset..offset;
16880 new_selections_by_buffer
16881 .entry(buffer_handle)
16882 .or_insert((Vec::new(), None))
16883 .0
16884 .push(range)
16885 } else {
16886 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16887 else {
16888 continue;
16889 };
16890 new_selections_by_buffer
16891 .entry(buffer_handle)
16892 .or_insert((Vec::new(), None))
16893 .0
16894 .push(range)
16895 }
16896 }
16897 }
16898 }
16899 }
16900
16901 if new_selections_by_buffer.is_empty() {
16902 return;
16903 }
16904
16905 // We defer the pane interaction because we ourselves are a workspace item
16906 // and activating a new item causes the pane to call a method on us reentrantly,
16907 // which panics if we're on the stack.
16908 window.defer(cx, move |window, cx| {
16909 workspace.update(cx, |workspace, cx| {
16910 let pane = if split {
16911 workspace.adjacent_pane(window, cx)
16912 } else {
16913 workspace.active_pane().clone()
16914 };
16915
16916 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16917 let editor = buffer
16918 .read(cx)
16919 .file()
16920 .is_none()
16921 .then(|| {
16922 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16923 // so `workspace.open_project_item` will never find them, always opening a new editor.
16924 // Instead, we try to activate the existing editor in the pane first.
16925 let (editor, pane_item_index) =
16926 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16927 let editor = item.downcast::<Editor>()?;
16928 let singleton_buffer =
16929 editor.read(cx).buffer().read(cx).as_singleton()?;
16930 if singleton_buffer == buffer {
16931 Some((editor, i))
16932 } else {
16933 None
16934 }
16935 })?;
16936 pane.update(cx, |pane, cx| {
16937 pane.activate_item(pane_item_index, true, true, window, cx)
16938 });
16939 Some(editor)
16940 })
16941 .flatten()
16942 .unwrap_or_else(|| {
16943 workspace.open_project_item::<Self>(
16944 pane.clone(),
16945 buffer,
16946 true,
16947 true,
16948 window,
16949 cx,
16950 )
16951 });
16952
16953 editor.update(cx, |editor, cx| {
16954 let autoscroll = match scroll_offset {
16955 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16956 None => Autoscroll::newest(),
16957 };
16958 let nav_history = editor.nav_history.take();
16959 editor.change_selections(Some(autoscroll), window, cx, |s| {
16960 s.select_ranges(ranges);
16961 });
16962 editor.nav_history = nav_history;
16963 });
16964 }
16965 })
16966 });
16967 }
16968
16969 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16970 let snapshot = self.buffer.read(cx).read(cx);
16971 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16972 Some(
16973 ranges
16974 .iter()
16975 .map(move |range| {
16976 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16977 })
16978 .collect(),
16979 )
16980 }
16981
16982 fn selection_replacement_ranges(
16983 &self,
16984 range: Range<OffsetUtf16>,
16985 cx: &mut App,
16986 ) -> Vec<Range<OffsetUtf16>> {
16987 let selections = self.selections.all::<OffsetUtf16>(cx);
16988 let newest_selection = selections
16989 .iter()
16990 .max_by_key(|selection| selection.id)
16991 .unwrap();
16992 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16993 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16994 let snapshot = self.buffer.read(cx).read(cx);
16995 selections
16996 .into_iter()
16997 .map(|mut selection| {
16998 selection.start.0 =
16999 (selection.start.0 as isize).saturating_add(start_delta) as usize;
17000 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
17001 snapshot.clip_offset_utf16(selection.start, Bias::Left)
17002 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
17003 })
17004 .collect()
17005 }
17006
17007 fn report_editor_event(
17008 &self,
17009 event_type: &'static str,
17010 file_extension: Option<String>,
17011 cx: &App,
17012 ) {
17013 if cfg!(any(test, feature = "test-support")) {
17014 return;
17015 }
17016
17017 let Some(project) = &self.project else { return };
17018
17019 // If None, we are in a file without an extension
17020 let file = self
17021 .buffer
17022 .read(cx)
17023 .as_singleton()
17024 .and_then(|b| b.read(cx).file());
17025 let file_extension = file_extension.or(file
17026 .as_ref()
17027 .and_then(|file| Path::new(file.file_name(cx)).extension())
17028 .and_then(|e| e.to_str())
17029 .map(|a| a.to_string()));
17030
17031 let vim_mode = cx
17032 .global::<SettingsStore>()
17033 .raw_user_settings()
17034 .get("vim_mode")
17035 == Some(&serde_json::Value::Bool(true));
17036
17037 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
17038 let copilot_enabled = edit_predictions_provider
17039 == language::language_settings::EditPredictionProvider::Copilot;
17040 let copilot_enabled_for_language = self
17041 .buffer
17042 .read(cx)
17043 .language_settings(cx)
17044 .show_edit_predictions;
17045
17046 let project = project.read(cx);
17047 telemetry::event!(
17048 event_type,
17049 file_extension,
17050 vim_mode,
17051 copilot_enabled,
17052 copilot_enabled_for_language,
17053 edit_predictions_provider,
17054 is_via_ssh = project.is_via_ssh(),
17055 );
17056 }
17057
17058 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
17059 /// with each line being an array of {text, highlight} objects.
17060 fn copy_highlight_json(
17061 &mut self,
17062 _: &CopyHighlightJson,
17063 window: &mut Window,
17064 cx: &mut Context<Self>,
17065 ) {
17066 #[derive(Serialize)]
17067 struct Chunk<'a> {
17068 text: String,
17069 highlight: Option<&'a str>,
17070 }
17071
17072 let snapshot = self.buffer.read(cx).snapshot(cx);
17073 let range = self
17074 .selected_text_range(false, window, cx)
17075 .and_then(|selection| {
17076 if selection.range.is_empty() {
17077 None
17078 } else {
17079 Some(selection.range)
17080 }
17081 })
17082 .unwrap_or_else(|| 0..snapshot.len());
17083
17084 let chunks = snapshot.chunks(range, true);
17085 let mut lines = Vec::new();
17086 let mut line: VecDeque<Chunk> = VecDeque::new();
17087
17088 let Some(style) = self.style.as_ref() else {
17089 return;
17090 };
17091
17092 for chunk in chunks {
17093 let highlight = chunk
17094 .syntax_highlight_id
17095 .and_then(|id| id.name(&style.syntax));
17096 let mut chunk_lines = chunk.text.split('\n').peekable();
17097 while let Some(text) = chunk_lines.next() {
17098 let mut merged_with_last_token = false;
17099 if let Some(last_token) = line.back_mut() {
17100 if last_token.highlight == highlight {
17101 last_token.text.push_str(text);
17102 merged_with_last_token = true;
17103 }
17104 }
17105
17106 if !merged_with_last_token {
17107 line.push_back(Chunk {
17108 text: text.into(),
17109 highlight,
17110 });
17111 }
17112
17113 if chunk_lines.peek().is_some() {
17114 if line.len() > 1 && line.front().unwrap().text.is_empty() {
17115 line.pop_front();
17116 }
17117 if line.len() > 1 && line.back().unwrap().text.is_empty() {
17118 line.pop_back();
17119 }
17120
17121 lines.push(mem::take(&mut line));
17122 }
17123 }
17124 }
17125
17126 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
17127 return;
17128 };
17129 cx.write_to_clipboard(ClipboardItem::new_string(lines));
17130 }
17131
17132 pub fn open_context_menu(
17133 &mut self,
17134 _: &OpenContextMenu,
17135 window: &mut Window,
17136 cx: &mut Context<Self>,
17137 ) {
17138 self.request_autoscroll(Autoscroll::newest(), cx);
17139 let position = self.selections.newest_display(cx).start;
17140 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
17141 }
17142
17143 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
17144 &self.inlay_hint_cache
17145 }
17146
17147 pub fn replay_insert_event(
17148 &mut self,
17149 text: &str,
17150 relative_utf16_range: Option<Range<isize>>,
17151 window: &mut Window,
17152 cx: &mut Context<Self>,
17153 ) {
17154 if !self.input_enabled {
17155 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17156 return;
17157 }
17158 if let Some(relative_utf16_range) = relative_utf16_range {
17159 let selections = self.selections.all::<OffsetUtf16>(cx);
17160 self.change_selections(None, window, cx, |s| {
17161 let new_ranges = selections.into_iter().map(|range| {
17162 let start = OffsetUtf16(
17163 range
17164 .head()
17165 .0
17166 .saturating_add_signed(relative_utf16_range.start),
17167 );
17168 let end = OffsetUtf16(
17169 range
17170 .head()
17171 .0
17172 .saturating_add_signed(relative_utf16_range.end),
17173 );
17174 start..end
17175 });
17176 s.select_ranges(new_ranges);
17177 });
17178 }
17179
17180 self.handle_input(text, window, cx);
17181 }
17182
17183 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
17184 let Some(provider) = self.semantics_provider.as_ref() else {
17185 return false;
17186 };
17187
17188 let mut supports = false;
17189 self.buffer().update(cx, |this, cx| {
17190 this.for_each_buffer(|buffer| {
17191 supports |= provider.supports_inlay_hints(buffer, cx);
17192 });
17193 });
17194
17195 supports
17196 }
17197
17198 pub fn is_focused(&self, window: &Window) -> bool {
17199 self.focus_handle.is_focused(window)
17200 }
17201
17202 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17203 cx.emit(EditorEvent::Focused);
17204
17205 if let Some(descendant) = self
17206 .last_focused_descendant
17207 .take()
17208 .and_then(|descendant| descendant.upgrade())
17209 {
17210 window.focus(&descendant);
17211 } else {
17212 if let Some(blame) = self.blame.as_ref() {
17213 blame.update(cx, GitBlame::focus)
17214 }
17215
17216 self.blink_manager.update(cx, BlinkManager::enable);
17217 self.show_cursor_names(window, cx);
17218 self.buffer.update(cx, |buffer, cx| {
17219 buffer.finalize_last_transaction(cx);
17220 if self.leader_peer_id.is_none() {
17221 buffer.set_active_selections(
17222 &self.selections.disjoint_anchors(),
17223 self.selections.line_mode,
17224 self.cursor_shape,
17225 cx,
17226 );
17227 }
17228 });
17229 }
17230 }
17231
17232 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17233 cx.emit(EditorEvent::FocusedIn)
17234 }
17235
17236 fn handle_focus_out(
17237 &mut self,
17238 event: FocusOutEvent,
17239 _window: &mut Window,
17240 cx: &mut Context<Self>,
17241 ) {
17242 if event.blurred != self.focus_handle {
17243 self.last_focused_descendant = Some(event.blurred);
17244 }
17245 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17246 }
17247
17248 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17249 self.blink_manager.update(cx, BlinkManager::disable);
17250 self.buffer
17251 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17252
17253 if let Some(blame) = self.blame.as_ref() {
17254 blame.update(cx, GitBlame::blur)
17255 }
17256 if !self.hover_state.focused(window, cx) {
17257 hide_hover(self, cx);
17258 }
17259 if !self
17260 .context_menu
17261 .borrow()
17262 .as_ref()
17263 .is_some_and(|context_menu| context_menu.focused(window, cx))
17264 {
17265 self.hide_context_menu(window, cx);
17266 }
17267 self.discard_inline_completion(false, cx);
17268 cx.emit(EditorEvent::Blurred);
17269 cx.notify();
17270 }
17271
17272 pub fn register_action<A: Action>(
17273 &mut self,
17274 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17275 ) -> Subscription {
17276 let id = self.next_editor_action_id.post_inc();
17277 let listener = Arc::new(listener);
17278 self.editor_actions.borrow_mut().insert(
17279 id,
17280 Box::new(move |window, _| {
17281 let listener = listener.clone();
17282 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17283 let action = action.downcast_ref().unwrap();
17284 if phase == DispatchPhase::Bubble {
17285 listener(action, window, cx)
17286 }
17287 })
17288 }),
17289 );
17290
17291 let editor_actions = self.editor_actions.clone();
17292 Subscription::new(move || {
17293 editor_actions.borrow_mut().remove(&id);
17294 })
17295 }
17296
17297 pub fn file_header_size(&self) -> u32 {
17298 FILE_HEADER_HEIGHT
17299 }
17300
17301 pub fn restore(
17302 &mut self,
17303 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17304 window: &mut Window,
17305 cx: &mut Context<Self>,
17306 ) {
17307 let workspace = self.workspace();
17308 let project = self.project.as_ref();
17309 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17310 let mut tasks = Vec::new();
17311 for (buffer_id, changes) in revert_changes {
17312 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17313 buffer.update(cx, |buffer, cx| {
17314 buffer.edit(
17315 changes
17316 .into_iter()
17317 .map(|(range, text)| (range, text.to_string())),
17318 None,
17319 cx,
17320 );
17321 });
17322
17323 if let Some(project) =
17324 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17325 {
17326 project.update(cx, |project, cx| {
17327 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17328 })
17329 }
17330 }
17331 }
17332 tasks
17333 });
17334 cx.spawn_in(window, async move |_, cx| {
17335 for (buffer, task) in save_tasks {
17336 let result = task.await;
17337 if result.is_err() {
17338 let Some(path) = buffer
17339 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17340 .ok()
17341 else {
17342 continue;
17343 };
17344 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17345 let Some(task) = cx
17346 .update_window_entity(&workspace, |workspace, window, cx| {
17347 workspace
17348 .open_path_preview(path, None, false, false, false, window, cx)
17349 })
17350 .ok()
17351 else {
17352 continue;
17353 };
17354 task.await.log_err();
17355 }
17356 }
17357 }
17358 })
17359 .detach();
17360 self.change_selections(None, window, cx, |selections| selections.refresh());
17361 }
17362
17363 pub fn to_pixel_point(
17364 &self,
17365 source: multi_buffer::Anchor,
17366 editor_snapshot: &EditorSnapshot,
17367 window: &mut Window,
17368 ) -> Option<gpui::Point<Pixels>> {
17369 let source_point = source.to_display_point(editor_snapshot);
17370 self.display_to_pixel_point(source_point, editor_snapshot, window)
17371 }
17372
17373 pub fn display_to_pixel_point(
17374 &self,
17375 source: DisplayPoint,
17376 editor_snapshot: &EditorSnapshot,
17377 window: &mut Window,
17378 ) -> Option<gpui::Point<Pixels>> {
17379 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17380 let text_layout_details = self.text_layout_details(window);
17381 let scroll_top = text_layout_details
17382 .scroll_anchor
17383 .scroll_position(editor_snapshot)
17384 .y;
17385
17386 if source.row().as_f32() < scroll_top.floor() {
17387 return None;
17388 }
17389 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17390 let source_y = line_height * (source.row().as_f32() - scroll_top);
17391 Some(gpui::Point::new(source_x, source_y))
17392 }
17393
17394 pub fn has_visible_completions_menu(&self) -> bool {
17395 !self.edit_prediction_preview_is_active()
17396 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17397 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17398 })
17399 }
17400
17401 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17402 self.addons
17403 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17404 }
17405
17406 pub fn unregister_addon<T: Addon>(&mut self) {
17407 self.addons.remove(&std::any::TypeId::of::<T>());
17408 }
17409
17410 pub fn addon<T: Addon>(&self) -> Option<&T> {
17411 let type_id = std::any::TypeId::of::<T>();
17412 self.addons
17413 .get(&type_id)
17414 .and_then(|item| item.to_any().downcast_ref::<T>())
17415 }
17416
17417 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17418 let text_layout_details = self.text_layout_details(window);
17419 let style = &text_layout_details.editor_style;
17420 let font_id = window.text_system().resolve_font(&style.text.font());
17421 let font_size = style.text.font_size.to_pixels(window.rem_size());
17422 let line_height = style.text.line_height_in_pixels(window.rem_size());
17423 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17424
17425 gpui::Size::new(em_width, line_height)
17426 }
17427
17428 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17429 self.load_diff_task.clone()
17430 }
17431
17432 fn read_metadata_from_db(
17433 &mut self,
17434 item_id: u64,
17435 workspace_id: WorkspaceId,
17436 window: &mut Window,
17437 cx: &mut Context<Editor>,
17438 ) {
17439 if self.is_singleton(cx)
17440 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17441 {
17442 let buffer_snapshot = OnceCell::new();
17443
17444 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17445 if !selections.is_empty() {
17446 let snapshot =
17447 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17448 self.change_selections(None, window, cx, |s| {
17449 s.select_ranges(selections.into_iter().map(|(start, end)| {
17450 snapshot.clip_offset(start, Bias::Left)
17451 ..snapshot.clip_offset(end, Bias::Right)
17452 }));
17453 });
17454 }
17455 };
17456
17457 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17458 if !folds.is_empty() {
17459 let snapshot =
17460 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17461 self.fold_ranges(
17462 folds
17463 .into_iter()
17464 .map(|(start, end)| {
17465 snapshot.clip_offset(start, Bias::Left)
17466 ..snapshot.clip_offset(end, Bias::Right)
17467 })
17468 .collect(),
17469 false,
17470 window,
17471 cx,
17472 );
17473 }
17474 }
17475 }
17476
17477 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17478 }
17479}
17480
17481fn insert_extra_newline_brackets(
17482 buffer: &MultiBufferSnapshot,
17483 range: Range<usize>,
17484 language: &language::LanguageScope,
17485) -> bool {
17486 let leading_whitespace_len = buffer
17487 .reversed_chars_at(range.start)
17488 .take_while(|c| c.is_whitespace() && *c != '\n')
17489 .map(|c| c.len_utf8())
17490 .sum::<usize>();
17491 let trailing_whitespace_len = buffer
17492 .chars_at(range.end)
17493 .take_while(|c| c.is_whitespace() && *c != '\n')
17494 .map(|c| c.len_utf8())
17495 .sum::<usize>();
17496 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17497
17498 language.brackets().any(|(pair, enabled)| {
17499 let pair_start = pair.start.trim_end();
17500 let pair_end = pair.end.trim_start();
17501
17502 enabled
17503 && pair.newline
17504 && buffer.contains_str_at(range.end, pair_end)
17505 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17506 })
17507}
17508
17509fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17510 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17511 [(buffer, range, _)] => (*buffer, range.clone()),
17512 _ => return false,
17513 };
17514 let pair = {
17515 let mut result: Option<BracketMatch> = None;
17516
17517 for pair in buffer
17518 .all_bracket_ranges(range.clone())
17519 .filter(move |pair| {
17520 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17521 })
17522 {
17523 let len = pair.close_range.end - pair.open_range.start;
17524
17525 if let Some(existing) = &result {
17526 let existing_len = existing.close_range.end - existing.open_range.start;
17527 if len > existing_len {
17528 continue;
17529 }
17530 }
17531
17532 result = Some(pair);
17533 }
17534
17535 result
17536 };
17537 let Some(pair) = pair else {
17538 return false;
17539 };
17540 pair.newline_only
17541 && buffer
17542 .chars_for_range(pair.open_range.end..range.start)
17543 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17544 .all(|c| c.is_whitespace() && c != '\n')
17545}
17546
17547fn get_uncommitted_diff_for_buffer(
17548 project: &Entity<Project>,
17549 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17550 buffer: Entity<MultiBuffer>,
17551 cx: &mut App,
17552) -> Task<()> {
17553 let mut tasks = Vec::new();
17554 project.update(cx, |project, cx| {
17555 for buffer in buffers {
17556 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17557 }
17558 });
17559 cx.spawn(async move |cx| {
17560 let diffs = future::join_all(tasks).await;
17561 buffer
17562 .update(cx, |buffer, cx| {
17563 for diff in diffs.into_iter().flatten() {
17564 buffer.add_diff(diff, cx);
17565 }
17566 })
17567 .ok();
17568 })
17569}
17570
17571fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17572 let tab_size = tab_size.get() as usize;
17573 let mut width = offset;
17574
17575 for ch in text.chars() {
17576 width += if ch == '\t' {
17577 tab_size - (width % tab_size)
17578 } else {
17579 1
17580 };
17581 }
17582
17583 width - offset
17584}
17585
17586#[cfg(test)]
17587mod tests {
17588 use super::*;
17589
17590 #[test]
17591 fn test_string_size_with_expanded_tabs() {
17592 let nz = |val| NonZeroU32::new(val).unwrap();
17593 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17594 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17595 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17596 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17597 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17598 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17599 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17600 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17601 }
17602}
17603
17604/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17605struct WordBreakingTokenizer<'a> {
17606 input: &'a str,
17607}
17608
17609impl<'a> WordBreakingTokenizer<'a> {
17610 fn new(input: &'a str) -> Self {
17611 Self { input }
17612 }
17613}
17614
17615fn is_char_ideographic(ch: char) -> bool {
17616 use unicode_script::Script::*;
17617 use unicode_script::UnicodeScript;
17618 matches!(ch.script(), Han | Tangut | Yi)
17619}
17620
17621fn is_grapheme_ideographic(text: &str) -> bool {
17622 text.chars().any(is_char_ideographic)
17623}
17624
17625fn is_grapheme_whitespace(text: &str) -> bool {
17626 text.chars().any(|x| x.is_whitespace())
17627}
17628
17629fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17630 text.chars().next().map_or(false, |ch| {
17631 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17632 })
17633}
17634
17635#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17636enum WordBreakToken<'a> {
17637 Word { token: &'a str, grapheme_len: usize },
17638 InlineWhitespace { token: &'a str, grapheme_len: usize },
17639 Newline,
17640}
17641
17642impl<'a> Iterator for WordBreakingTokenizer<'a> {
17643 /// Yields a span, the count of graphemes in the token, and whether it was
17644 /// whitespace. Note that it also breaks at word boundaries.
17645 type Item = WordBreakToken<'a>;
17646
17647 fn next(&mut self) -> Option<Self::Item> {
17648 use unicode_segmentation::UnicodeSegmentation;
17649 if self.input.is_empty() {
17650 return None;
17651 }
17652
17653 let mut iter = self.input.graphemes(true).peekable();
17654 let mut offset = 0;
17655 let mut grapheme_len = 0;
17656 if let Some(first_grapheme) = iter.next() {
17657 let is_newline = first_grapheme == "\n";
17658 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17659 offset += first_grapheme.len();
17660 grapheme_len += 1;
17661 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17662 if let Some(grapheme) = iter.peek().copied() {
17663 if should_stay_with_preceding_ideograph(grapheme) {
17664 offset += grapheme.len();
17665 grapheme_len += 1;
17666 }
17667 }
17668 } else {
17669 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17670 let mut next_word_bound = words.peek().copied();
17671 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17672 next_word_bound = words.next();
17673 }
17674 while let Some(grapheme) = iter.peek().copied() {
17675 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17676 break;
17677 };
17678 if is_grapheme_whitespace(grapheme) != is_whitespace
17679 || (grapheme == "\n") != is_newline
17680 {
17681 break;
17682 };
17683 offset += grapheme.len();
17684 grapheme_len += 1;
17685 iter.next();
17686 }
17687 }
17688 let token = &self.input[..offset];
17689 self.input = &self.input[offset..];
17690 if token == "\n" {
17691 Some(WordBreakToken::Newline)
17692 } else if is_whitespace {
17693 Some(WordBreakToken::InlineWhitespace {
17694 token,
17695 grapheme_len,
17696 })
17697 } else {
17698 Some(WordBreakToken::Word {
17699 token,
17700 grapheme_len,
17701 })
17702 }
17703 } else {
17704 None
17705 }
17706 }
17707}
17708
17709#[test]
17710fn test_word_breaking_tokenizer() {
17711 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17712 ("", &[]),
17713 (" ", &[whitespace(" ", 2)]),
17714 ("Ʒ", &[word("Ʒ", 1)]),
17715 ("Ǽ", &[word("Ǽ", 1)]),
17716 ("⋑", &[word("⋑", 1)]),
17717 ("⋑⋑", &[word("⋑⋑", 2)]),
17718 (
17719 "原理,进而",
17720 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17721 ),
17722 (
17723 "hello world",
17724 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17725 ),
17726 (
17727 "hello, world",
17728 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17729 ),
17730 (
17731 " hello world",
17732 &[
17733 whitespace(" ", 2),
17734 word("hello", 5),
17735 whitespace(" ", 1),
17736 word("world", 5),
17737 ],
17738 ),
17739 (
17740 "这是什么 \n 钢笔",
17741 &[
17742 word("这", 1),
17743 word("是", 1),
17744 word("什", 1),
17745 word("么", 1),
17746 whitespace(" ", 1),
17747 newline(),
17748 whitespace(" ", 1),
17749 word("钢", 1),
17750 word("笔", 1),
17751 ],
17752 ),
17753 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17754 ];
17755
17756 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17757 WordBreakToken::Word {
17758 token,
17759 grapheme_len,
17760 }
17761 }
17762
17763 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17764 WordBreakToken::InlineWhitespace {
17765 token,
17766 grapheme_len,
17767 }
17768 }
17769
17770 fn newline() -> WordBreakToken<'static> {
17771 WordBreakToken::Newline
17772 }
17773
17774 for (input, result) in tests {
17775 assert_eq!(
17776 WordBreakingTokenizer::new(input)
17777 .collect::<Vec<_>>()
17778 .as_slice(),
17779 *result,
17780 );
17781 }
17782}
17783
17784fn wrap_with_prefix(
17785 line_prefix: String,
17786 unwrapped_text: String,
17787 wrap_column: usize,
17788 tab_size: NonZeroU32,
17789 preserve_existing_whitespace: bool,
17790) -> String {
17791 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17792 let mut wrapped_text = String::new();
17793 let mut current_line = line_prefix.clone();
17794
17795 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17796 let mut current_line_len = line_prefix_len;
17797 let mut in_whitespace = false;
17798 for token in tokenizer {
17799 let have_preceding_whitespace = in_whitespace;
17800 match token {
17801 WordBreakToken::Word {
17802 token,
17803 grapheme_len,
17804 } => {
17805 in_whitespace = false;
17806 if current_line_len + grapheme_len > wrap_column
17807 && current_line_len != line_prefix_len
17808 {
17809 wrapped_text.push_str(current_line.trim_end());
17810 wrapped_text.push('\n');
17811 current_line.truncate(line_prefix.len());
17812 current_line_len = line_prefix_len;
17813 }
17814 current_line.push_str(token);
17815 current_line_len += grapheme_len;
17816 }
17817 WordBreakToken::InlineWhitespace {
17818 mut token,
17819 mut grapheme_len,
17820 } => {
17821 in_whitespace = true;
17822 if have_preceding_whitespace && !preserve_existing_whitespace {
17823 continue;
17824 }
17825 if !preserve_existing_whitespace {
17826 token = " ";
17827 grapheme_len = 1;
17828 }
17829 if current_line_len + grapheme_len > wrap_column {
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 || preserve_existing_whitespace {
17835 current_line.push_str(token);
17836 current_line_len += grapheme_len;
17837 }
17838 }
17839 WordBreakToken::Newline => {
17840 in_whitespace = true;
17841 if preserve_existing_whitespace {
17842 wrapped_text.push_str(current_line.trim_end());
17843 wrapped_text.push('\n');
17844 current_line.truncate(line_prefix.len());
17845 current_line_len = line_prefix_len;
17846 } else if have_preceding_whitespace {
17847 continue;
17848 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17849 {
17850 wrapped_text.push_str(current_line.trim_end());
17851 wrapped_text.push('\n');
17852 current_line.truncate(line_prefix.len());
17853 current_line_len = line_prefix_len;
17854 } else if current_line_len != line_prefix_len {
17855 current_line.push(' ');
17856 current_line_len += 1;
17857 }
17858 }
17859 }
17860 }
17861
17862 if !current_line.is_empty() {
17863 wrapped_text.push_str(¤t_line);
17864 }
17865 wrapped_text
17866}
17867
17868#[test]
17869fn test_wrap_with_prefix() {
17870 assert_eq!(
17871 wrap_with_prefix(
17872 "# ".to_string(),
17873 "abcdefg".to_string(),
17874 4,
17875 NonZeroU32::new(4).unwrap(),
17876 false,
17877 ),
17878 "# abcdefg"
17879 );
17880 assert_eq!(
17881 wrap_with_prefix(
17882 "".to_string(),
17883 "\thello world".to_string(),
17884 8,
17885 NonZeroU32::new(4).unwrap(),
17886 false,
17887 ),
17888 "hello\nworld"
17889 );
17890 assert_eq!(
17891 wrap_with_prefix(
17892 "// ".to_string(),
17893 "xx \nyy zz aa bb cc".to_string(),
17894 12,
17895 NonZeroU32::new(4).unwrap(),
17896 false,
17897 ),
17898 "// xx yy zz\n// aa bb cc"
17899 );
17900 assert_eq!(
17901 wrap_with_prefix(
17902 String::new(),
17903 "这是什么 \n 钢笔".to_string(),
17904 3,
17905 NonZeroU32::new(4).unwrap(),
17906 false,
17907 ),
17908 "这是什\n么 钢\n笔"
17909 );
17910}
17911
17912pub trait CollaborationHub {
17913 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17914 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17915 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17916}
17917
17918impl CollaborationHub for Entity<Project> {
17919 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17920 self.read(cx).collaborators()
17921 }
17922
17923 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17924 self.read(cx).user_store().read(cx).participant_indices()
17925 }
17926
17927 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17928 let this = self.read(cx);
17929 let user_ids = this.collaborators().values().map(|c| c.user_id);
17930 this.user_store().read_with(cx, |user_store, cx| {
17931 user_store.participant_names(user_ids, cx)
17932 })
17933 }
17934}
17935
17936pub trait SemanticsProvider {
17937 fn hover(
17938 &self,
17939 buffer: &Entity<Buffer>,
17940 position: text::Anchor,
17941 cx: &mut App,
17942 ) -> Option<Task<Vec<project::Hover>>>;
17943
17944 fn inlay_hints(
17945 &self,
17946 buffer_handle: Entity<Buffer>,
17947 range: Range<text::Anchor>,
17948 cx: &mut App,
17949 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17950
17951 fn resolve_inlay_hint(
17952 &self,
17953 hint: InlayHint,
17954 buffer_handle: Entity<Buffer>,
17955 server_id: LanguageServerId,
17956 cx: &mut App,
17957 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17958
17959 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17960
17961 fn document_highlights(
17962 &self,
17963 buffer: &Entity<Buffer>,
17964 position: text::Anchor,
17965 cx: &mut App,
17966 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17967
17968 fn definitions(
17969 &self,
17970 buffer: &Entity<Buffer>,
17971 position: text::Anchor,
17972 kind: GotoDefinitionKind,
17973 cx: &mut App,
17974 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17975
17976 fn range_for_rename(
17977 &self,
17978 buffer: &Entity<Buffer>,
17979 position: text::Anchor,
17980 cx: &mut App,
17981 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17982
17983 fn perform_rename(
17984 &self,
17985 buffer: &Entity<Buffer>,
17986 position: text::Anchor,
17987 new_name: String,
17988 cx: &mut App,
17989 ) -> Option<Task<Result<ProjectTransaction>>>;
17990}
17991
17992pub trait CompletionProvider {
17993 fn completions(
17994 &self,
17995 excerpt_id: ExcerptId,
17996 buffer: &Entity<Buffer>,
17997 buffer_position: text::Anchor,
17998 trigger: CompletionContext,
17999 window: &mut Window,
18000 cx: &mut Context<Editor>,
18001 ) -> Task<Result<Option<Vec<Completion>>>>;
18002
18003 fn resolve_completions(
18004 &self,
18005 buffer: Entity<Buffer>,
18006 completion_indices: Vec<usize>,
18007 completions: Rc<RefCell<Box<[Completion]>>>,
18008 cx: &mut Context<Editor>,
18009 ) -> Task<Result<bool>>;
18010
18011 fn apply_additional_edits_for_completion(
18012 &self,
18013 _buffer: Entity<Buffer>,
18014 _completions: Rc<RefCell<Box<[Completion]>>>,
18015 _completion_index: usize,
18016 _push_to_history: bool,
18017 _cx: &mut Context<Editor>,
18018 ) -> Task<Result<Option<language::Transaction>>> {
18019 Task::ready(Ok(None))
18020 }
18021
18022 fn is_completion_trigger(
18023 &self,
18024 buffer: &Entity<Buffer>,
18025 position: language::Anchor,
18026 text: &str,
18027 trigger_in_words: bool,
18028 cx: &mut Context<Editor>,
18029 ) -> bool;
18030
18031 fn sort_completions(&self) -> bool {
18032 true
18033 }
18034}
18035
18036pub trait CodeActionProvider {
18037 fn id(&self) -> Arc<str>;
18038
18039 fn code_actions(
18040 &self,
18041 buffer: &Entity<Buffer>,
18042 range: Range<text::Anchor>,
18043 window: &mut Window,
18044 cx: &mut App,
18045 ) -> Task<Result<Vec<CodeAction>>>;
18046
18047 fn apply_code_action(
18048 &self,
18049 buffer_handle: Entity<Buffer>,
18050 action: CodeAction,
18051 excerpt_id: ExcerptId,
18052 push_to_history: bool,
18053 window: &mut Window,
18054 cx: &mut App,
18055 ) -> Task<Result<ProjectTransaction>>;
18056}
18057
18058impl CodeActionProvider for Entity<Project> {
18059 fn id(&self) -> Arc<str> {
18060 "project".into()
18061 }
18062
18063 fn code_actions(
18064 &self,
18065 buffer: &Entity<Buffer>,
18066 range: Range<text::Anchor>,
18067 _window: &mut Window,
18068 cx: &mut App,
18069 ) -> Task<Result<Vec<CodeAction>>> {
18070 self.update(cx, |project, cx| {
18071 let code_lens = project.code_lens(buffer, range.clone(), cx);
18072 let code_actions = project.code_actions(buffer, range, None, cx);
18073 cx.background_spawn(async move {
18074 let (code_lens, code_actions) = join(code_lens, code_actions).await;
18075 Ok(code_lens
18076 .context("code lens fetch")?
18077 .into_iter()
18078 .chain(code_actions.context("code action fetch")?)
18079 .collect())
18080 })
18081 })
18082 }
18083
18084 fn apply_code_action(
18085 &self,
18086 buffer_handle: Entity<Buffer>,
18087 action: CodeAction,
18088 _excerpt_id: ExcerptId,
18089 push_to_history: bool,
18090 _window: &mut Window,
18091 cx: &mut App,
18092 ) -> Task<Result<ProjectTransaction>> {
18093 self.update(cx, |project, cx| {
18094 project.apply_code_action(buffer_handle, action, push_to_history, cx)
18095 })
18096 }
18097}
18098
18099fn snippet_completions(
18100 project: &Project,
18101 buffer: &Entity<Buffer>,
18102 buffer_position: text::Anchor,
18103 cx: &mut App,
18104) -> Task<Result<Vec<Completion>>> {
18105 let language = buffer.read(cx).language_at(buffer_position);
18106 let language_name = language.as_ref().map(|language| language.lsp_id());
18107 let snippet_store = project.snippets().read(cx);
18108 let snippets = snippet_store.snippets_for(language_name, cx);
18109
18110 if snippets.is_empty() {
18111 return Task::ready(Ok(vec![]));
18112 }
18113 let snapshot = buffer.read(cx).text_snapshot();
18114 let chars: String = snapshot
18115 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
18116 .collect();
18117
18118 let scope = language.map(|language| language.default_scope());
18119 let executor = cx.background_executor().clone();
18120
18121 cx.background_spawn(async move {
18122 let classifier = CharClassifier::new(scope).for_completion(true);
18123 let mut last_word = chars
18124 .chars()
18125 .take_while(|c| classifier.is_word(*c))
18126 .collect::<String>();
18127 last_word = last_word.chars().rev().collect();
18128
18129 if last_word.is_empty() {
18130 return Ok(vec![]);
18131 }
18132
18133 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
18134 let to_lsp = |point: &text::Anchor| {
18135 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
18136 point_to_lsp(end)
18137 };
18138 let lsp_end = to_lsp(&buffer_position);
18139
18140 let candidates = snippets
18141 .iter()
18142 .enumerate()
18143 .flat_map(|(ix, snippet)| {
18144 snippet
18145 .prefix
18146 .iter()
18147 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
18148 })
18149 .collect::<Vec<StringMatchCandidate>>();
18150
18151 let mut matches = fuzzy::match_strings(
18152 &candidates,
18153 &last_word,
18154 last_word.chars().any(|c| c.is_uppercase()),
18155 100,
18156 &Default::default(),
18157 executor,
18158 )
18159 .await;
18160
18161 // Remove all candidates where the query's start does not match the start of any word in the candidate
18162 if let Some(query_start) = last_word.chars().next() {
18163 matches.retain(|string_match| {
18164 split_words(&string_match.string).any(|word| {
18165 // Check that the first codepoint of the word as lowercase matches the first
18166 // codepoint of the query as lowercase
18167 word.chars()
18168 .flat_map(|codepoint| codepoint.to_lowercase())
18169 .zip(query_start.to_lowercase())
18170 .all(|(word_cp, query_cp)| word_cp == query_cp)
18171 })
18172 });
18173 }
18174
18175 let matched_strings = matches
18176 .into_iter()
18177 .map(|m| m.string)
18178 .collect::<HashSet<_>>();
18179
18180 let result: Vec<Completion> = snippets
18181 .into_iter()
18182 .filter_map(|snippet| {
18183 let matching_prefix = snippet
18184 .prefix
18185 .iter()
18186 .find(|prefix| matched_strings.contains(*prefix))?;
18187 let start = as_offset - last_word.len();
18188 let start = snapshot.anchor_before(start);
18189 let range = start..buffer_position;
18190 let lsp_start = to_lsp(&start);
18191 let lsp_range = lsp::Range {
18192 start: lsp_start,
18193 end: lsp_end,
18194 };
18195 Some(Completion {
18196 old_range: range,
18197 new_text: snippet.body.clone(),
18198 source: CompletionSource::Lsp {
18199 server_id: LanguageServerId(usize::MAX),
18200 resolved: true,
18201 lsp_completion: Box::new(lsp::CompletionItem {
18202 label: snippet.prefix.first().unwrap().clone(),
18203 kind: Some(CompletionItemKind::SNIPPET),
18204 label_details: snippet.description.as_ref().map(|description| {
18205 lsp::CompletionItemLabelDetails {
18206 detail: Some(description.clone()),
18207 description: None,
18208 }
18209 }),
18210 insert_text_format: Some(InsertTextFormat::SNIPPET),
18211 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18212 lsp::InsertReplaceEdit {
18213 new_text: snippet.body.clone(),
18214 insert: lsp_range,
18215 replace: lsp_range,
18216 },
18217 )),
18218 filter_text: Some(snippet.body.clone()),
18219 sort_text: Some(char::MAX.to_string()),
18220 ..lsp::CompletionItem::default()
18221 }),
18222 lsp_defaults: None,
18223 },
18224 label: CodeLabel {
18225 text: matching_prefix.clone(),
18226 runs: Vec::new(),
18227 filter_range: 0..matching_prefix.len(),
18228 },
18229 icon_path: None,
18230 documentation: snippet
18231 .description
18232 .clone()
18233 .map(|description| CompletionDocumentation::SingleLine(description.into())),
18234 confirm: None,
18235 })
18236 })
18237 .collect();
18238
18239 Ok(result)
18240 })
18241}
18242
18243impl CompletionProvider for Entity<Project> {
18244 fn completions(
18245 &self,
18246 _excerpt_id: ExcerptId,
18247 buffer: &Entity<Buffer>,
18248 buffer_position: text::Anchor,
18249 options: CompletionContext,
18250 _window: &mut Window,
18251 cx: &mut Context<Editor>,
18252 ) -> Task<Result<Option<Vec<Completion>>>> {
18253 self.update(cx, |project, cx| {
18254 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18255 let project_completions = project.completions(buffer, buffer_position, options, cx);
18256 cx.background_spawn(async move {
18257 let snippets_completions = snippets.await?;
18258 match project_completions.await? {
18259 Some(mut completions) => {
18260 completions.extend(snippets_completions);
18261 Ok(Some(completions))
18262 }
18263 None => {
18264 if snippets_completions.is_empty() {
18265 Ok(None)
18266 } else {
18267 Ok(Some(snippets_completions))
18268 }
18269 }
18270 }
18271 })
18272 })
18273 }
18274
18275 fn resolve_completions(
18276 &self,
18277 buffer: Entity<Buffer>,
18278 completion_indices: Vec<usize>,
18279 completions: Rc<RefCell<Box<[Completion]>>>,
18280 cx: &mut Context<Editor>,
18281 ) -> Task<Result<bool>> {
18282 self.update(cx, |project, cx| {
18283 project.lsp_store().update(cx, |lsp_store, cx| {
18284 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18285 })
18286 })
18287 }
18288
18289 fn apply_additional_edits_for_completion(
18290 &self,
18291 buffer: Entity<Buffer>,
18292 completions: Rc<RefCell<Box<[Completion]>>>,
18293 completion_index: usize,
18294 push_to_history: bool,
18295 cx: &mut Context<Editor>,
18296 ) -> Task<Result<Option<language::Transaction>>> {
18297 self.update(cx, |project, cx| {
18298 project.lsp_store().update(cx, |lsp_store, cx| {
18299 lsp_store.apply_additional_edits_for_completion(
18300 buffer,
18301 completions,
18302 completion_index,
18303 push_to_history,
18304 cx,
18305 )
18306 })
18307 })
18308 }
18309
18310 fn is_completion_trigger(
18311 &self,
18312 buffer: &Entity<Buffer>,
18313 position: language::Anchor,
18314 text: &str,
18315 trigger_in_words: bool,
18316 cx: &mut Context<Editor>,
18317 ) -> bool {
18318 let mut chars = text.chars();
18319 let char = if let Some(char) = chars.next() {
18320 char
18321 } else {
18322 return false;
18323 };
18324 if chars.next().is_some() {
18325 return false;
18326 }
18327
18328 let buffer = buffer.read(cx);
18329 let snapshot = buffer.snapshot();
18330 if !snapshot.settings_at(position, cx).show_completions_on_input {
18331 return false;
18332 }
18333 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18334 if trigger_in_words && classifier.is_word(char) {
18335 return true;
18336 }
18337
18338 buffer.completion_triggers().contains(text)
18339 }
18340}
18341
18342impl SemanticsProvider for Entity<Project> {
18343 fn hover(
18344 &self,
18345 buffer: &Entity<Buffer>,
18346 position: text::Anchor,
18347 cx: &mut App,
18348 ) -> Option<Task<Vec<project::Hover>>> {
18349 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18350 }
18351
18352 fn document_highlights(
18353 &self,
18354 buffer: &Entity<Buffer>,
18355 position: text::Anchor,
18356 cx: &mut App,
18357 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18358 Some(self.update(cx, |project, cx| {
18359 project.document_highlights(buffer, position, cx)
18360 }))
18361 }
18362
18363 fn definitions(
18364 &self,
18365 buffer: &Entity<Buffer>,
18366 position: text::Anchor,
18367 kind: GotoDefinitionKind,
18368 cx: &mut App,
18369 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18370 Some(self.update(cx, |project, cx| match kind {
18371 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18372 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18373 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18374 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18375 }))
18376 }
18377
18378 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18379 // TODO: make this work for remote projects
18380 self.update(cx, |this, cx| {
18381 buffer.update(cx, |buffer, cx| {
18382 this.any_language_server_supports_inlay_hints(buffer, cx)
18383 })
18384 })
18385 }
18386
18387 fn inlay_hints(
18388 &self,
18389 buffer_handle: Entity<Buffer>,
18390 range: Range<text::Anchor>,
18391 cx: &mut App,
18392 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18393 Some(self.update(cx, |project, cx| {
18394 project.inlay_hints(buffer_handle, range, cx)
18395 }))
18396 }
18397
18398 fn resolve_inlay_hint(
18399 &self,
18400 hint: InlayHint,
18401 buffer_handle: Entity<Buffer>,
18402 server_id: LanguageServerId,
18403 cx: &mut App,
18404 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18405 Some(self.update(cx, |project, cx| {
18406 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18407 }))
18408 }
18409
18410 fn range_for_rename(
18411 &self,
18412 buffer: &Entity<Buffer>,
18413 position: text::Anchor,
18414 cx: &mut App,
18415 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18416 Some(self.update(cx, |project, cx| {
18417 let buffer = buffer.clone();
18418 let task = project.prepare_rename(buffer.clone(), position, cx);
18419 cx.spawn(async move |_, cx| {
18420 Ok(match task.await? {
18421 PrepareRenameResponse::Success(range) => Some(range),
18422 PrepareRenameResponse::InvalidPosition => None,
18423 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18424 // Fallback on using TreeSitter info to determine identifier range
18425 buffer.update(cx, |buffer, _| {
18426 let snapshot = buffer.snapshot();
18427 let (range, kind) = snapshot.surrounding_word(position);
18428 if kind != Some(CharKind::Word) {
18429 return None;
18430 }
18431 Some(
18432 snapshot.anchor_before(range.start)
18433 ..snapshot.anchor_after(range.end),
18434 )
18435 })?
18436 }
18437 })
18438 })
18439 }))
18440 }
18441
18442 fn perform_rename(
18443 &self,
18444 buffer: &Entity<Buffer>,
18445 position: text::Anchor,
18446 new_name: String,
18447 cx: &mut App,
18448 ) -> Option<Task<Result<ProjectTransaction>>> {
18449 Some(self.update(cx, |project, cx| {
18450 project.perform_rename(buffer.clone(), position, new_name, cx)
18451 }))
18452 }
18453}
18454
18455fn inlay_hint_settings(
18456 location: Anchor,
18457 snapshot: &MultiBufferSnapshot,
18458 cx: &mut Context<Editor>,
18459) -> InlayHintSettings {
18460 let file = snapshot.file_at(location);
18461 let language = snapshot.language_at(location).map(|l| l.name());
18462 language_settings(language, file, cx).inlay_hints
18463}
18464
18465fn consume_contiguous_rows(
18466 contiguous_row_selections: &mut Vec<Selection<Point>>,
18467 selection: &Selection<Point>,
18468 display_map: &DisplaySnapshot,
18469 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18470) -> (MultiBufferRow, MultiBufferRow) {
18471 contiguous_row_selections.push(selection.clone());
18472 let start_row = MultiBufferRow(selection.start.row);
18473 let mut end_row = ending_row(selection, display_map);
18474
18475 while let Some(next_selection) = selections.peek() {
18476 if next_selection.start.row <= end_row.0 {
18477 end_row = ending_row(next_selection, display_map);
18478 contiguous_row_selections.push(selections.next().unwrap().clone());
18479 } else {
18480 break;
18481 }
18482 }
18483 (start_row, end_row)
18484}
18485
18486fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18487 if next_selection.end.column > 0 || next_selection.is_empty() {
18488 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18489 } else {
18490 MultiBufferRow(next_selection.end.row)
18491 }
18492}
18493
18494impl EditorSnapshot {
18495 pub fn remote_selections_in_range<'a>(
18496 &'a self,
18497 range: &'a Range<Anchor>,
18498 collaboration_hub: &dyn CollaborationHub,
18499 cx: &'a App,
18500 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18501 let participant_names = collaboration_hub.user_names(cx);
18502 let participant_indices = collaboration_hub.user_participant_indices(cx);
18503 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18504 let collaborators_by_replica_id = collaborators_by_peer_id
18505 .iter()
18506 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18507 .collect::<HashMap<_, _>>();
18508 self.buffer_snapshot
18509 .selections_in_range(range, false)
18510 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18511 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18512 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18513 let user_name = participant_names.get(&collaborator.user_id).cloned();
18514 Some(RemoteSelection {
18515 replica_id,
18516 selection,
18517 cursor_shape,
18518 line_mode,
18519 participant_index,
18520 peer_id: collaborator.peer_id,
18521 user_name,
18522 })
18523 })
18524 }
18525
18526 pub fn hunks_for_ranges(
18527 &self,
18528 ranges: impl IntoIterator<Item = Range<Point>>,
18529 ) -> Vec<MultiBufferDiffHunk> {
18530 let mut hunks = Vec::new();
18531 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18532 HashMap::default();
18533 for query_range in ranges {
18534 let query_rows =
18535 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18536 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18537 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18538 ) {
18539 // Include deleted hunks that are adjacent to the query range, because
18540 // otherwise they would be missed.
18541 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18542 if hunk.status().is_deleted() {
18543 intersects_range |= hunk.row_range.start == query_rows.end;
18544 intersects_range |= hunk.row_range.end == query_rows.start;
18545 }
18546 if intersects_range {
18547 if !processed_buffer_rows
18548 .entry(hunk.buffer_id)
18549 .or_default()
18550 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18551 {
18552 continue;
18553 }
18554 hunks.push(hunk);
18555 }
18556 }
18557 }
18558
18559 hunks
18560 }
18561
18562 fn display_diff_hunks_for_rows<'a>(
18563 &'a self,
18564 display_rows: Range<DisplayRow>,
18565 folded_buffers: &'a HashSet<BufferId>,
18566 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18567 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18568 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18569
18570 self.buffer_snapshot
18571 .diff_hunks_in_range(buffer_start..buffer_end)
18572 .filter_map(|hunk| {
18573 if folded_buffers.contains(&hunk.buffer_id) {
18574 return None;
18575 }
18576
18577 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18578 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18579
18580 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18581 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18582
18583 let display_hunk = if hunk_display_start.column() != 0 {
18584 DisplayDiffHunk::Folded {
18585 display_row: hunk_display_start.row(),
18586 }
18587 } else {
18588 let mut end_row = hunk_display_end.row();
18589 if hunk_display_end.column() > 0 {
18590 end_row.0 += 1;
18591 }
18592 let is_created_file = hunk.is_created_file();
18593 DisplayDiffHunk::Unfolded {
18594 status: hunk.status(),
18595 diff_base_byte_range: hunk.diff_base_byte_range,
18596 display_row_range: hunk_display_start.row()..end_row,
18597 multi_buffer_range: Anchor::range_in_buffer(
18598 hunk.excerpt_id,
18599 hunk.buffer_id,
18600 hunk.buffer_range,
18601 ),
18602 is_created_file,
18603 }
18604 };
18605
18606 Some(display_hunk)
18607 })
18608 }
18609
18610 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18611 self.display_snapshot.buffer_snapshot.language_at(position)
18612 }
18613
18614 pub fn is_focused(&self) -> bool {
18615 self.is_focused
18616 }
18617
18618 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18619 self.placeholder_text.as_ref()
18620 }
18621
18622 pub fn scroll_position(&self) -> gpui::Point<f32> {
18623 self.scroll_anchor.scroll_position(&self.display_snapshot)
18624 }
18625
18626 fn gutter_dimensions(
18627 &self,
18628 font_id: FontId,
18629 font_size: Pixels,
18630 max_line_number_width: Pixels,
18631 cx: &App,
18632 ) -> Option<GutterDimensions> {
18633 if !self.show_gutter {
18634 return None;
18635 }
18636
18637 let descent = cx.text_system().descent(font_id, font_size);
18638 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18639 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18640
18641 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18642 matches!(
18643 ProjectSettings::get_global(cx).git.git_gutter,
18644 Some(GitGutterSetting::TrackedFiles)
18645 )
18646 });
18647 let gutter_settings = EditorSettings::get_global(cx).gutter;
18648 let show_line_numbers = self
18649 .show_line_numbers
18650 .unwrap_or(gutter_settings.line_numbers);
18651 let line_gutter_width = if show_line_numbers {
18652 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18653 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18654 max_line_number_width.max(min_width_for_number_on_gutter)
18655 } else {
18656 0.0.into()
18657 };
18658
18659 let show_code_actions = self
18660 .show_code_actions
18661 .unwrap_or(gutter_settings.code_actions);
18662
18663 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18664 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18665
18666 let git_blame_entries_width =
18667 self.git_blame_gutter_max_author_length
18668 .map(|max_author_length| {
18669 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18670
18671 /// The number of characters to dedicate to gaps and margins.
18672 const SPACING_WIDTH: usize = 4;
18673
18674 let max_char_count = max_author_length
18675 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18676 + ::git::SHORT_SHA_LENGTH
18677 + MAX_RELATIVE_TIMESTAMP.len()
18678 + SPACING_WIDTH;
18679
18680 em_advance * max_char_count
18681 });
18682
18683 let is_singleton = self.buffer_snapshot.is_singleton();
18684
18685 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18686 left_padding += if !is_singleton {
18687 em_width * 4.0
18688 } else if show_code_actions || show_runnables || show_breakpoints {
18689 em_width * 3.0
18690 } else if show_git_gutter && show_line_numbers {
18691 em_width * 2.0
18692 } else if show_git_gutter || show_line_numbers {
18693 em_width
18694 } else {
18695 px(0.)
18696 };
18697
18698 let shows_folds = is_singleton && gutter_settings.folds;
18699
18700 let right_padding = if shows_folds && show_line_numbers {
18701 em_width * 4.0
18702 } else if shows_folds || (!is_singleton && show_line_numbers) {
18703 em_width * 3.0
18704 } else if show_line_numbers {
18705 em_width
18706 } else {
18707 px(0.)
18708 };
18709
18710 Some(GutterDimensions {
18711 left_padding,
18712 right_padding,
18713 width: line_gutter_width + left_padding + right_padding,
18714 margin: -descent,
18715 git_blame_entries_width,
18716 })
18717 }
18718
18719 pub fn render_crease_toggle(
18720 &self,
18721 buffer_row: MultiBufferRow,
18722 row_contains_cursor: bool,
18723 editor: Entity<Editor>,
18724 window: &mut Window,
18725 cx: &mut App,
18726 ) -> Option<AnyElement> {
18727 let folded = self.is_line_folded(buffer_row);
18728 let mut is_foldable = false;
18729
18730 if let Some(crease) = self
18731 .crease_snapshot
18732 .query_row(buffer_row, &self.buffer_snapshot)
18733 {
18734 is_foldable = true;
18735 match crease {
18736 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18737 if let Some(render_toggle) = render_toggle {
18738 let toggle_callback =
18739 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18740 if folded {
18741 editor.update(cx, |editor, cx| {
18742 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18743 });
18744 } else {
18745 editor.update(cx, |editor, cx| {
18746 editor.unfold_at(
18747 &crate::UnfoldAt { buffer_row },
18748 window,
18749 cx,
18750 )
18751 });
18752 }
18753 });
18754 return Some((render_toggle)(
18755 buffer_row,
18756 folded,
18757 toggle_callback,
18758 window,
18759 cx,
18760 ));
18761 }
18762 }
18763 }
18764 }
18765
18766 is_foldable |= self.starts_indent(buffer_row);
18767
18768 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18769 Some(
18770 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18771 .toggle_state(folded)
18772 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18773 if folded {
18774 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18775 } else {
18776 this.fold_at(&FoldAt { buffer_row }, window, cx);
18777 }
18778 }))
18779 .into_any_element(),
18780 )
18781 } else {
18782 None
18783 }
18784 }
18785
18786 pub fn render_crease_trailer(
18787 &self,
18788 buffer_row: MultiBufferRow,
18789 window: &mut Window,
18790 cx: &mut App,
18791 ) -> Option<AnyElement> {
18792 let folded = self.is_line_folded(buffer_row);
18793 if let Crease::Inline { render_trailer, .. } = self
18794 .crease_snapshot
18795 .query_row(buffer_row, &self.buffer_snapshot)?
18796 {
18797 let render_trailer = render_trailer.as_ref()?;
18798 Some(render_trailer(buffer_row, folded, window, cx))
18799 } else {
18800 None
18801 }
18802 }
18803}
18804
18805impl Deref for EditorSnapshot {
18806 type Target = DisplaySnapshot;
18807
18808 fn deref(&self) -> &Self::Target {
18809 &self.display_snapshot
18810 }
18811}
18812
18813#[derive(Clone, Debug, PartialEq, Eq)]
18814pub enum EditorEvent {
18815 InputIgnored {
18816 text: Arc<str>,
18817 },
18818 InputHandled {
18819 utf16_range_to_replace: Option<Range<isize>>,
18820 text: Arc<str>,
18821 },
18822 ExcerptsAdded {
18823 buffer: Entity<Buffer>,
18824 predecessor: ExcerptId,
18825 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18826 },
18827 ExcerptsRemoved {
18828 ids: Vec<ExcerptId>,
18829 },
18830 BufferFoldToggled {
18831 ids: Vec<ExcerptId>,
18832 folded: bool,
18833 },
18834 ExcerptsEdited {
18835 ids: Vec<ExcerptId>,
18836 },
18837 ExcerptsExpanded {
18838 ids: Vec<ExcerptId>,
18839 },
18840 BufferEdited,
18841 Edited {
18842 transaction_id: clock::Lamport,
18843 },
18844 Reparsed(BufferId),
18845 Focused,
18846 FocusedIn,
18847 Blurred,
18848 DirtyChanged,
18849 Saved,
18850 TitleChanged,
18851 DiffBaseChanged,
18852 SelectionsChanged {
18853 local: bool,
18854 },
18855 ScrollPositionChanged {
18856 local: bool,
18857 autoscroll: bool,
18858 },
18859 Closed,
18860 TransactionUndone {
18861 transaction_id: clock::Lamport,
18862 },
18863 TransactionBegun {
18864 transaction_id: clock::Lamport,
18865 },
18866 Reloaded,
18867 CursorShapeChanged,
18868 PushedToNavHistory {
18869 anchor: Anchor,
18870 is_deactivate: bool,
18871 },
18872}
18873
18874impl EventEmitter<EditorEvent> for Editor {}
18875
18876impl Focusable for Editor {
18877 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18878 self.focus_handle.clone()
18879 }
18880}
18881
18882impl Render for Editor {
18883 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18884 let settings = ThemeSettings::get_global(cx);
18885
18886 let mut text_style = match self.mode {
18887 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18888 color: cx.theme().colors().editor_foreground,
18889 font_family: settings.ui_font.family.clone(),
18890 font_features: settings.ui_font.features.clone(),
18891 font_fallbacks: settings.ui_font.fallbacks.clone(),
18892 font_size: rems(0.875).into(),
18893 font_weight: settings.ui_font.weight,
18894 line_height: relative(settings.buffer_line_height.value()),
18895 ..Default::default()
18896 },
18897 EditorMode::Full => TextStyle {
18898 color: cx.theme().colors().editor_foreground,
18899 font_family: settings.buffer_font.family.clone(),
18900 font_features: settings.buffer_font.features.clone(),
18901 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18902 font_size: settings.buffer_font_size(cx).into(),
18903 font_weight: settings.buffer_font.weight,
18904 line_height: relative(settings.buffer_line_height.value()),
18905 ..Default::default()
18906 },
18907 };
18908 if let Some(text_style_refinement) = &self.text_style_refinement {
18909 text_style.refine(text_style_refinement)
18910 }
18911
18912 let background = match self.mode {
18913 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18914 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18915 EditorMode::Full => cx.theme().colors().editor_background,
18916 };
18917
18918 EditorElement::new(
18919 &cx.entity(),
18920 EditorStyle {
18921 background,
18922 local_player: cx.theme().players().local(),
18923 text: text_style,
18924 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18925 syntax: cx.theme().syntax().clone(),
18926 status: cx.theme().status().clone(),
18927 inlay_hints_style: make_inlay_hints_style(cx),
18928 inline_completion_styles: make_suggestion_styles(cx),
18929 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18930 },
18931 )
18932 }
18933}
18934
18935impl EntityInputHandler for Editor {
18936 fn text_for_range(
18937 &mut self,
18938 range_utf16: Range<usize>,
18939 adjusted_range: &mut Option<Range<usize>>,
18940 _: &mut Window,
18941 cx: &mut Context<Self>,
18942 ) -> Option<String> {
18943 let snapshot = self.buffer.read(cx).read(cx);
18944 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18945 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18946 if (start.0..end.0) != range_utf16 {
18947 adjusted_range.replace(start.0..end.0);
18948 }
18949 Some(snapshot.text_for_range(start..end).collect())
18950 }
18951
18952 fn selected_text_range(
18953 &mut self,
18954 ignore_disabled_input: bool,
18955 _: &mut Window,
18956 cx: &mut Context<Self>,
18957 ) -> Option<UTF16Selection> {
18958 // Prevent the IME menu from appearing when holding down an alphabetic key
18959 // while input is disabled.
18960 if !ignore_disabled_input && !self.input_enabled {
18961 return None;
18962 }
18963
18964 let selection = self.selections.newest::<OffsetUtf16>(cx);
18965 let range = selection.range();
18966
18967 Some(UTF16Selection {
18968 range: range.start.0..range.end.0,
18969 reversed: selection.reversed,
18970 })
18971 }
18972
18973 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18974 let snapshot = self.buffer.read(cx).read(cx);
18975 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18976 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18977 }
18978
18979 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18980 self.clear_highlights::<InputComposition>(cx);
18981 self.ime_transaction.take();
18982 }
18983
18984 fn replace_text_in_range(
18985 &mut self,
18986 range_utf16: Option<Range<usize>>,
18987 text: &str,
18988 window: &mut Window,
18989 cx: &mut Context<Self>,
18990 ) {
18991 if !self.input_enabled {
18992 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18993 return;
18994 }
18995
18996 self.transact(window, cx, |this, window, cx| {
18997 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18998 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18999 Some(this.selection_replacement_ranges(range_utf16, cx))
19000 } else {
19001 this.marked_text_ranges(cx)
19002 };
19003
19004 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
19005 let newest_selection_id = this.selections.newest_anchor().id;
19006 this.selections
19007 .all::<OffsetUtf16>(cx)
19008 .iter()
19009 .zip(ranges_to_replace.iter())
19010 .find_map(|(selection, range)| {
19011 if selection.id == newest_selection_id {
19012 Some(
19013 (range.start.0 as isize - selection.head().0 as isize)
19014 ..(range.end.0 as isize - selection.head().0 as isize),
19015 )
19016 } else {
19017 None
19018 }
19019 })
19020 });
19021
19022 cx.emit(EditorEvent::InputHandled {
19023 utf16_range_to_replace: range_to_replace,
19024 text: text.into(),
19025 });
19026
19027 if let Some(new_selected_ranges) = new_selected_ranges {
19028 this.change_selections(None, window, cx, |selections| {
19029 selections.select_ranges(new_selected_ranges)
19030 });
19031 this.backspace(&Default::default(), window, cx);
19032 }
19033
19034 this.handle_input(text, window, cx);
19035 });
19036
19037 if let Some(transaction) = self.ime_transaction {
19038 self.buffer.update(cx, |buffer, cx| {
19039 buffer.group_until_transaction(transaction, cx);
19040 });
19041 }
19042
19043 self.unmark_text(window, cx);
19044 }
19045
19046 fn replace_and_mark_text_in_range(
19047 &mut self,
19048 range_utf16: Option<Range<usize>>,
19049 text: &str,
19050 new_selected_range_utf16: Option<Range<usize>>,
19051 window: &mut Window,
19052 cx: &mut Context<Self>,
19053 ) {
19054 if !self.input_enabled {
19055 return;
19056 }
19057
19058 let transaction = self.transact(window, cx, |this, window, cx| {
19059 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
19060 let snapshot = this.buffer.read(cx).read(cx);
19061 if let Some(relative_range_utf16) = range_utf16.as_ref() {
19062 for marked_range in &mut marked_ranges {
19063 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
19064 marked_range.start.0 += relative_range_utf16.start;
19065 marked_range.start =
19066 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
19067 marked_range.end =
19068 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
19069 }
19070 }
19071 Some(marked_ranges)
19072 } else if let Some(range_utf16) = range_utf16 {
19073 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
19074 Some(this.selection_replacement_ranges(range_utf16, cx))
19075 } else {
19076 None
19077 };
19078
19079 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
19080 let newest_selection_id = this.selections.newest_anchor().id;
19081 this.selections
19082 .all::<OffsetUtf16>(cx)
19083 .iter()
19084 .zip(ranges_to_replace.iter())
19085 .find_map(|(selection, range)| {
19086 if selection.id == newest_selection_id {
19087 Some(
19088 (range.start.0 as isize - selection.head().0 as isize)
19089 ..(range.end.0 as isize - selection.head().0 as isize),
19090 )
19091 } else {
19092 None
19093 }
19094 })
19095 });
19096
19097 cx.emit(EditorEvent::InputHandled {
19098 utf16_range_to_replace: range_to_replace,
19099 text: text.into(),
19100 });
19101
19102 if let Some(ranges) = ranges_to_replace {
19103 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
19104 }
19105
19106 let marked_ranges = {
19107 let snapshot = this.buffer.read(cx).read(cx);
19108 this.selections
19109 .disjoint_anchors()
19110 .iter()
19111 .map(|selection| {
19112 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
19113 })
19114 .collect::<Vec<_>>()
19115 };
19116
19117 if text.is_empty() {
19118 this.unmark_text(window, cx);
19119 } else {
19120 this.highlight_text::<InputComposition>(
19121 marked_ranges.clone(),
19122 HighlightStyle {
19123 underline: Some(UnderlineStyle {
19124 thickness: px(1.),
19125 color: None,
19126 wavy: false,
19127 }),
19128 ..Default::default()
19129 },
19130 cx,
19131 );
19132 }
19133
19134 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
19135 let use_autoclose = this.use_autoclose;
19136 let use_auto_surround = this.use_auto_surround;
19137 this.set_use_autoclose(false);
19138 this.set_use_auto_surround(false);
19139 this.handle_input(text, window, cx);
19140 this.set_use_autoclose(use_autoclose);
19141 this.set_use_auto_surround(use_auto_surround);
19142
19143 if let Some(new_selected_range) = new_selected_range_utf16 {
19144 let snapshot = this.buffer.read(cx).read(cx);
19145 let new_selected_ranges = marked_ranges
19146 .into_iter()
19147 .map(|marked_range| {
19148 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
19149 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
19150 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
19151 snapshot.clip_offset_utf16(new_start, Bias::Left)
19152 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
19153 })
19154 .collect::<Vec<_>>();
19155
19156 drop(snapshot);
19157 this.change_selections(None, window, cx, |selections| {
19158 selections.select_ranges(new_selected_ranges)
19159 });
19160 }
19161 });
19162
19163 self.ime_transaction = self.ime_transaction.or(transaction);
19164 if let Some(transaction) = self.ime_transaction {
19165 self.buffer.update(cx, |buffer, cx| {
19166 buffer.group_until_transaction(transaction, cx);
19167 });
19168 }
19169
19170 if self.text_highlights::<InputComposition>(cx).is_none() {
19171 self.ime_transaction.take();
19172 }
19173 }
19174
19175 fn bounds_for_range(
19176 &mut self,
19177 range_utf16: Range<usize>,
19178 element_bounds: gpui::Bounds<Pixels>,
19179 window: &mut Window,
19180 cx: &mut Context<Self>,
19181 ) -> Option<gpui::Bounds<Pixels>> {
19182 let text_layout_details = self.text_layout_details(window);
19183 let gpui::Size {
19184 width: em_width,
19185 height: line_height,
19186 } = self.character_size(window);
19187
19188 let snapshot = self.snapshot(window, cx);
19189 let scroll_position = snapshot.scroll_position();
19190 let scroll_left = scroll_position.x * em_width;
19191
19192 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
19193 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
19194 + self.gutter_dimensions.width
19195 + self.gutter_dimensions.margin;
19196 let y = line_height * (start.row().as_f32() - scroll_position.y);
19197
19198 Some(Bounds {
19199 origin: element_bounds.origin + point(x, y),
19200 size: size(em_width, line_height),
19201 })
19202 }
19203
19204 fn character_index_for_point(
19205 &mut self,
19206 point: gpui::Point<Pixels>,
19207 _window: &mut Window,
19208 _cx: &mut Context<Self>,
19209 ) -> Option<usize> {
19210 let position_map = self.last_position_map.as_ref()?;
19211 if !position_map.text_hitbox.contains(&point) {
19212 return None;
19213 }
19214 let display_point = position_map.point_for_position(point).previous_valid;
19215 let anchor = position_map
19216 .snapshot
19217 .display_point_to_anchor(display_point, Bias::Left);
19218 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
19219 Some(utf16_offset.0)
19220 }
19221}
19222
19223trait SelectionExt {
19224 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
19225 fn spanned_rows(
19226 &self,
19227 include_end_if_at_line_start: bool,
19228 map: &DisplaySnapshot,
19229 ) -> Range<MultiBufferRow>;
19230}
19231
19232impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
19233 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
19234 let start = self
19235 .start
19236 .to_point(&map.buffer_snapshot)
19237 .to_display_point(map);
19238 let end = self
19239 .end
19240 .to_point(&map.buffer_snapshot)
19241 .to_display_point(map);
19242 if self.reversed {
19243 end..start
19244 } else {
19245 start..end
19246 }
19247 }
19248
19249 fn spanned_rows(
19250 &self,
19251 include_end_if_at_line_start: bool,
19252 map: &DisplaySnapshot,
19253 ) -> Range<MultiBufferRow> {
19254 let start = self.start.to_point(&map.buffer_snapshot);
19255 let mut end = self.end.to_point(&map.buffer_snapshot);
19256 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19257 end.row -= 1;
19258 }
19259
19260 let buffer_start = map.prev_line_boundary(start).0;
19261 let buffer_end = map.next_line_boundary(end).0;
19262 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19263 }
19264}
19265
19266impl<T: InvalidationRegion> InvalidationStack<T> {
19267 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19268 where
19269 S: Clone + ToOffset,
19270 {
19271 while let Some(region) = self.last() {
19272 let all_selections_inside_invalidation_ranges =
19273 if selections.len() == region.ranges().len() {
19274 selections
19275 .iter()
19276 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19277 .all(|(selection, invalidation_range)| {
19278 let head = selection.head().to_offset(buffer);
19279 invalidation_range.start <= head && invalidation_range.end >= head
19280 })
19281 } else {
19282 false
19283 };
19284
19285 if all_selections_inside_invalidation_ranges {
19286 break;
19287 } else {
19288 self.pop();
19289 }
19290 }
19291 }
19292}
19293
19294impl<T> Default for InvalidationStack<T> {
19295 fn default() -> Self {
19296 Self(Default::default())
19297 }
19298}
19299
19300impl<T> Deref for InvalidationStack<T> {
19301 type Target = Vec<T>;
19302
19303 fn deref(&self) -> &Self::Target {
19304 &self.0
19305 }
19306}
19307
19308impl<T> DerefMut for InvalidationStack<T> {
19309 fn deref_mut(&mut self) -> &mut Self::Target {
19310 &mut self.0
19311 }
19312}
19313
19314impl InvalidationRegion for SnippetState {
19315 fn ranges(&self) -> &[Range<Anchor>] {
19316 &self.ranges[self.active_index]
19317 }
19318}
19319
19320pub fn diagnostic_block_renderer(
19321 diagnostic: Diagnostic,
19322 max_message_rows: Option<u8>,
19323 allow_closing: bool,
19324) -> RenderBlock {
19325 let (text_without_backticks, code_ranges) =
19326 highlight_diagnostic_message(&diagnostic, max_message_rows);
19327
19328 Arc::new(move |cx: &mut BlockContext| {
19329 let group_id: SharedString = cx.block_id.to_string().into();
19330
19331 let mut text_style = cx.window.text_style().clone();
19332 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19333 let theme_settings = ThemeSettings::get_global(cx);
19334 text_style.font_family = theme_settings.buffer_font.family.clone();
19335 text_style.font_style = theme_settings.buffer_font.style;
19336 text_style.font_features = theme_settings.buffer_font.features.clone();
19337 text_style.font_weight = theme_settings.buffer_font.weight;
19338
19339 let multi_line_diagnostic = diagnostic.message.contains('\n');
19340
19341 let buttons = |diagnostic: &Diagnostic| {
19342 if multi_line_diagnostic {
19343 v_flex()
19344 } else {
19345 h_flex()
19346 }
19347 .when(allow_closing, |div| {
19348 div.children(diagnostic.is_primary.then(|| {
19349 IconButton::new("close-block", IconName::XCircle)
19350 .icon_color(Color::Muted)
19351 .size(ButtonSize::Compact)
19352 .style(ButtonStyle::Transparent)
19353 .visible_on_hover(group_id.clone())
19354 .on_click(move |_click, window, cx| {
19355 window.dispatch_action(Box::new(Cancel), cx)
19356 })
19357 .tooltip(|window, cx| {
19358 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19359 })
19360 }))
19361 })
19362 .child(
19363 IconButton::new("copy-block", IconName::Copy)
19364 .icon_color(Color::Muted)
19365 .size(ButtonSize::Compact)
19366 .style(ButtonStyle::Transparent)
19367 .visible_on_hover(group_id.clone())
19368 .on_click({
19369 let message = diagnostic.message.clone();
19370 move |_click, _, cx| {
19371 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19372 }
19373 })
19374 .tooltip(Tooltip::text("Copy diagnostic message")),
19375 )
19376 };
19377
19378 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19379 AvailableSpace::min_size(),
19380 cx.window,
19381 cx.app,
19382 );
19383
19384 h_flex()
19385 .id(cx.block_id)
19386 .group(group_id.clone())
19387 .relative()
19388 .size_full()
19389 .block_mouse_down()
19390 .pl(cx.gutter_dimensions.width)
19391 .w(cx.max_width - cx.gutter_dimensions.full_width())
19392 .child(
19393 div()
19394 .flex()
19395 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19396 .flex_shrink(),
19397 )
19398 .child(buttons(&diagnostic))
19399 .child(div().flex().flex_shrink_0().child(
19400 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19401 &text_style,
19402 code_ranges.iter().map(|range| {
19403 (
19404 range.clone(),
19405 HighlightStyle {
19406 font_weight: Some(FontWeight::BOLD),
19407 ..Default::default()
19408 },
19409 )
19410 }),
19411 ),
19412 ))
19413 .into_any_element()
19414 })
19415}
19416
19417fn inline_completion_edit_text(
19418 current_snapshot: &BufferSnapshot,
19419 edits: &[(Range<Anchor>, String)],
19420 edit_preview: &EditPreview,
19421 include_deletions: bool,
19422 cx: &App,
19423) -> HighlightedText {
19424 let edits = edits
19425 .iter()
19426 .map(|(anchor, text)| {
19427 (
19428 anchor.start.text_anchor..anchor.end.text_anchor,
19429 text.clone(),
19430 )
19431 })
19432 .collect::<Vec<_>>();
19433
19434 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19435}
19436
19437pub fn highlight_diagnostic_message(
19438 diagnostic: &Diagnostic,
19439 mut max_message_rows: Option<u8>,
19440) -> (SharedString, Vec<Range<usize>>) {
19441 let mut text_without_backticks = String::new();
19442 let mut code_ranges = Vec::new();
19443
19444 if let Some(source) = &diagnostic.source {
19445 text_without_backticks.push_str(source);
19446 code_ranges.push(0..source.len());
19447 text_without_backticks.push_str(": ");
19448 }
19449
19450 let mut prev_offset = 0;
19451 let mut in_code_block = false;
19452 let has_row_limit = max_message_rows.is_some();
19453 let mut newline_indices = diagnostic
19454 .message
19455 .match_indices('\n')
19456 .filter(|_| has_row_limit)
19457 .map(|(ix, _)| ix)
19458 .fuse()
19459 .peekable();
19460
19461 for (quote_ix, _) in diagnostic
19462 .message
19463 .match_indices('`')
19464 .chain([(diagnostic.message.len(), "")])
19465 {
19466 let mut first_newline_ix = None;
19467 let mut last_newline_ix = None;
19468 while let Some(newline_ix) = newline_indices.peek() {
19469 if *newline_ix < quote_ix {
19470 if first_newline_ix.is_none() {
19471 first_newline_ix = Some(*newline_ix);
19472 }
19473 last_newline_ix = Some(*newline_ix);
19474
19475 if let Some(rows_left) = &mut max_message_rows {
19476 if *rows_left == 0 {
19477 break;
19478 } else {
19479 *rows_left -= 1;
19480 }
19481 }
19482 let _ = newline_indices.next();
19483 } else {
19484 break;
19485 }
19486 }
19487 let prev_len = text_without_backticks.len();
19488 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19489 text_without_backticks.push_str(new_text);
19490 if in_code_block {
19491 code_ranges.push(prev_len..text_without_backticks.len());
19492 }
19493 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19494 in_code_block = !in_code_block;
19495 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19496 text_without_backticks.push_str("...");
19497 break;
19498 }
19499 }
19500
19501 (text_without_backticks.into(), code_ranges)
19502}
19503
19504fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19505 match severity {
19506 DiagnosticSeverity::ERROR => colors.error,
19507 DiagnosticSeverity::WARNING => colors.warning,
19508 DiagnosticSeverity::INFORMATION => colors.info,
19509 DiagnosticSeverity::HINT => colors.info,
19510 _ => colors.ignored,
19511 }
19512}
19513
19514pub fn styled_runs_for_code_label<'a>(
19515 label: &'a CodeLabel,
19516 syntax_theme: &'a theme::SyntaxTheme,
19517) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19518 let fade_out = HighlightStyle {
19519 fade_out: Some(0.35),
19520 ..Default::default()
19521 };
19522
19523 let mut prev_end = label.filter_range.end;
19524 label
19525 .runs
19526 .iter()
19527 .enumerate()
19528 .flat_map(move |(ix, (range, highlight_id))| {
19529 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19530 style
19531 } else {
19532 return Default::default();
19533 };
19534 let mut muted_style = style;
19535 muted_style.highlight(fade_out);
19536
19537 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19538 if range.start >= label.filter_range.end {
19539 if range.start > prev_end {
19540 runs.push((prev_end..range.start, fade_out));
19541 }
19542 runs.push((range.clone(), muted_style));
19543 } else if range.end <= label.filter_range.end {
19544 runs.push((range.clone(), style));
19545 } else {
19546 runs.push((range.start..label.filter_range.end, style));
19547 runs.push((label.filter_range.end..range.end, muted_style));
19548 }
19549 prev_end = cmp::max(prev_end, range.end);
19550
19551 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19552 runs.push((prev_end..label.text.len(), fade_out));
19553 }
19554
19555 runs
19556 })
19557}
19558
19559pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19560 let mut prev_index = 0;
19561 let mut prev_codepoint: Option<char> = None;
19562 text.char_indices()
19563 .chain([(text.len(), '\0')])
19564 .filter_map(move |(index, codepoint)| {
19565 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19566 let is_boundary = index == text.len()
19567 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19568 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19569 if is_boundary {
19570 let chunk = &text[prev_index..index];
19571 prev_index = index;
19572 Some(chunk)
19573 } else {
19574 None
19575 }
19576 })
19577}
19578
19579pub trait RangeToAnchorExt: Sized {
19580 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19581
19582 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19583 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19584 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19585 }
19586}
19587
19588impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19589 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19590 let start_offset = self.start.to_offset(snapshot);
19591 let end_offset = self.end.to_offset(snapshot);
19592 if start_offset == end_offset {
19593 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19594 } else {
19595 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19596 }
19597 }
19598}
19599
19600pub trait RowExt {
19601 fn as_f32(&self) -> f32;
19602
19603 fn next_row(&self) -> Self;
19604
19605 fn previous_row(&self) -> Self;
19606
19607 fn minus(&self, other: Self) -> u32;
19608}
19609
19610impl RowExt for DisplayRow {
19611 fn as_f32(&self) -> f32 {
19612 self.0 as f32
19613 }
19614
19615 fn next_row(&self) -> Self {
19616 Self(self.0 + 1)
19617 }
19618
19619 fn previous_row(&self) -> Self {
19620 Self(self.0.saturating_sub(1))
19621 }
19622
19623 fn minus(&self, other: Self) -> u32 {
19624 self.0 - other.0
19625 }
19626}
19627
19628impl RowExt for MultiBufferRow {
19629 fn as_f32(&self) -> f32 {
19630 self.0 as f32
19631 }
19632
19633 fn next_row(&self) -> Self {
19634 Self(self.0 + 1)
19635 }
19636
19637 fn previous_row(&self) -> Self {
19638 Self(self.0.saturating_sub(1))
19639 }
19640
19641 fn minus(&self, other: Self) -> u32 {
19642 self.0 - other.0
19643 }
19644}
19645
19646trait RowRangeExt {
19647 type Row;
19648
19649 fn len(&self) -> usize;
19650
19651 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19652}
19653
19654impl RowRangeExt for Range<MultiBufferRow> {
19655 type Row = MultiBufferRow;
19656
19657 fn len(&self) -> usize {
19658 (self.end.0 - self.start.0) as usize
19659 }
19660
19661 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19662 (self.start.0..self.end.0).map(MultiBufferRow)
19663 }
19664}
19665
19666impl RowRangeExt for Range<DisplayRow> {
19667 type Row = DisplayRow;
19668
19669 fn len(&self) -> usize {
19670 (self.end.0 - self.start.0) as usize
19671 }
19672
19673 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19674 (self.start.0..self.end.0).map(DisplayRow)
19675 }
19676}
19677
19678/// If select range has more than one line, we
19679/// just point the cursor to range.start.
19680fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19681 if range.start.row == range.end.row {
19682 range
19683 } else {
19684 range.start..range.start
19685 }
19686}
19687pub struct KillRing(ClipboardItem);
19688impl Global for KillRing {}
19689
19690const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19691
19692struct BreakpointPromptEditor {
19693 pub(crate) prompt: Entity<Editor>,
19694 editor: WeakEntity<Editor>,
19695 breakpoint_anchor: Anchor,
19696 breakpoint: Breakpoint,
19697 block_ids: HashSet<CustomBlockId>,
19698 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19699 _subscriptions: Vec<Subscription>,
19700}
19701
19702impl BreakpointPromptEditor {
19703 const MAX_LINES: u8 = 4;
19704
19705 fn new(
19706 editor: WeakEntity<Editor>,
19707 breakpoint_anchor: Anchor,
19708 breakpoint: Breakpoint,
19709 window: &mut Window,
19710 cx: &mut Context<Self>,
19711 ) -> Self {
19712 let buffer = cx.new(|cx| {
19713 Buffer::local(
19714 breakpoint
19715 .kind
19716 .log_message()
19717 .map(|msg| msg.to_string())
19718 .unwrap_or_default(),
19719 cx,
19720 )
19721 });
19722 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19723
19724 let prompt = cx.new(|cx| {
19725 let mut prompt = Editor::new(
19726 EditorMode::AutoHeight {
19727 max_lines: Self::MAX_LINES as usize,
19728 },
19729 buffer,
19730 None,
19731 window,
19732 cx,
19733 );
19734 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19735 prompt.set_show_cursor_when_unfocused(false, cx);
19736 prompt.set_placeholder_text(
19737 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19738 cx,
19739 );
19740
19741 prompt
19742 });
19743
19744 Self {
19745 prompt,
19746 editor,
19747 breakpoint_anchor,
19748 breakpoint,
19749 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19750 block_ids: Default::default(),
19751 _subscriptions: vec![],
19752 }
19753 }
19754
19755 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19756 self.block_ids.extend(block_ids)
19757 }
19758
19759 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19760 if let Some(editor) = self.editor.upgrade() {
19761 let log_message = self
19762 .prompt
19763 .read(cx)
19764 .buffer
19765 .read(cx)
19766 .as_singleton()
19767 .expect("A multi buffer in breakpoint prompt isn't possible")
19768 .read(cx)
19769 .as_rope()
19770 .to_string();
19771
19772 editor.update(cx, |editor, cx| {
19773 editor.edit_breakpoint_at_anchor(
19774 self.breakpoint_anchor,
19775 self.breakpoint.clone(),
19776 BreakpointEditAction::EditLogMessage(log_message.into()),
19777 cx,
19778 );
19779
19780 editor.remove_blocks(self.block_ids.clone(), None, cx);
19781 cx.focus_self(window);
19782 });
19783 }
19784 }
19785
19786 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19787 self.editor
19788 .update(cx, |editor, cx| {
19789 editor.remove_blocks(self.block_ids.clone(), None, cx);
19790 window.focus(&editor.focus_handle);
19791 })
19792 .log_err();
19793 }
19794
19795 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19796 let settings = ThemeSettings::get_global(cx);
19797 let text_style = TextStyle {
19798 color: if self.prompt.read(cx).read_only(cx) {
19799 cx.theme().colors().text_disabled
19800 } else {
19801 cx.theme().colors().text
19802 },
19803 font_family: settings.buffer_font.family.clone(),
19804 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19805 font_size: settings.buffer_font_size(cx).into(),
19806 font_weight: settings.buffer_font.weight,
19807 line_height: relative(settings.buffer_line_height.value()),
19808 ..Default::default()
19809 };
19810 EditorElement::new(
19811 &self.prompt,
19812 EditorStyle {
19813 background: cx.theme().colors().editor_background,
19814 local_player: cx.theme().players().local(),
19815 text: text_style,
19816 ..Default::default()
19817 },
19818 )
19819 }
19820}
19821
19822impl Render for BreakpointPromptEditor {
19823 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19824 let gutter_dimensions = *self.gutter_dimensions.lock();
19825 h_flex()
19826 .key_context("Editor")
19827 .bg(cx.theme().colors().editor_background)
19828 .border_y_1()
19829 .border_color(cx.theme().status().info_border)
19830 .size_full()
19831 .py(window.line_height() / 2.5)
19832 .on_action(cx.listener(Self::confirm))
19833 .on_action(cx.listener(Self::cancel))
19834 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19835 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19836 }
19837}
19838
19839impl Focusable for BreakpointPromptEditor {
19840 fn focus_handle(&self, cx: &App) -> FocusHandle {
19841 self.prompt.focus_handle(cx)
19842 }
19843}
19844
19845fn all_edits_insertions_or_deletions(
19846 edits: &Vec<(Range<Anchor>, String)>,
19847 snapshot: &MultiBufferSnapshot,
19848) -> bool {
19849 let mut all_insertions = true;
19850 let mut all_deletions = true;
19851
19852 for (range, new_text) in edits.iter() {
19853 let range_is_empty = range.to_offset(&snapshot).is_empty();
19854 let text_is_empty = new_text.is_empty();
19855
19856 if range_is_empty != text_is_empty {
19857 if range_is_empty {
19858 all_deletions = false;
19859 } else {
19860 all_insertions = false;
19861 }
19862 } else {
19863 return false;
19864 }
19865
19866 if !all_insertions && !all_deletions {
19867 return false;
19868 }
19869 }
19870 all_insertions || all_deletions
19871}
19872
19873struct MissingEditPredictionKeybindingTooltip;
19874
19875impl Render for MissingEditPredictionKeybindingTooltip {
19876 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19877 ui::tooltip_container(window, cx, |container, _, cx| {
19878 container
19879 .flex_shrink_0()
19880 .max_w_80()
19881 .min_h(rems_from_px(124.))
19882 .justify_between()
19883 .child(
19884 v_flex()
19885 .flex_1()
19886 .text_ui_sm(cx)
19887 .child(Label::new("Conflict with Accept Keybinding"))
19888 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19889 )
19890 .child(
19891 h_flex()
19892 .pb_1()
19893 .gap_1()
19894 .items_end()
19895 .w_full()
19896 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19897 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19898 }))
19899 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19900 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19901 })),
19902 )
19903 })
19904 }
19905}
19906
19907#[derive(Debug, Clone, Copy, PartialEq)]
19908pub struct LineHighlight {
19909 pub background: Background,
19910 pub border: Option<gpui::Hsla>,
19911}
19912
19913impl From<Hsla> for LineHighlight {
19914 fn from(hsla: Hsla) -> Self {
19915 Self {
19916 background: hsla.into(),
19917 border: None,
19918 }
19919 }
19920}
19921
19922impl From<Background> for LineHighlight {
19923 fn from(background: Background) -> Self {
19924 Self {
19925 background,
19926 border: None,
19927 }
19928 }
19929}