1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blink_manager;
17mod clangd_ext;
18mod code_context_menus;
19pub mod commit_tooltip;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod indent_guides;
29mod inlay_hint_cache;
30pub mod items;
31mod jsx_tag_auto_close;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51pub(crate) use actions::*;
52pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use buffer_diff::DiffHunkStatus;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use feature_flags::{Debugger, FeatureFlagAppExt};
72use futures::{
73 future::{self, join, Shared},
74 FutureExt,
75};
76use fuzzy::StringMatchCandidate;
77
78use ::git::Restore;
79use code_context_menus::{
80 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
81 CompletionsMenu, ContextMenuOrigin,
82};
83use git::blame::GitBlame;
84use gpui::{
85 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
86 AnimationExt, AnyElement, App, AppContext, AsyncWindowContext, AvailableSpace, Background,
87 Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity,
88 EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight,
89 Global, HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
90 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
91 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
92 WeakEntity, WeakFocusHandle, Window,
93};
94use highlight_matching_bracket::refresh_matching_bracket_highlights;
95use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
96use hover_popover::{hide_hover, HoverState};
97use indent_guides::ActiveIndentGuidesState;
98use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
99pub use inline_completion::Direction;
100use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
101pub use items::MAX_TAB_TITLE_LEN;
102use itertools::Itertools;
103use language::{
104 language_settings::{
105 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
106 WordsCompletionMode,
107 },
108 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
109 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
110 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
111 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
112};
113use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
114use linked_editing_ranges::refresh_linked_ranges;
115use mouse_context_menu::MouseContextMenu;
116use persistence::DB;
117use project::{
118 debugger::breakpoint_store::{BreakpointEditAction, BreakpointStore, BreakpointStoreEvent},
119 ProjectPath,
120};
121
122pub use proposed_changes_editor::{
123 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
124};
125use smallvec::smallvec;
126use std::{cell::OnceCell, iter::Peekable};
127use task::{ResolvedTask, TaskTemplate, TaskVariables};
128
129pub use lsp::CompletionContext;
130use lsp::{
131 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
132 InsertTextFormat, LanguageServerId, LanguageServerName,
133};
134
135use language::BufferSnapshot;
136use movement::TextLayoutDetails;
137pub use multi_buffer::{
138 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
139 ToOffset, ToPoint,
140};
141use multi_buffer::{
142 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
143 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
144};
145use parking_lot::Mutex;
146use project::{
147 debugger::breakpoint_store::{Breakpoint, BreakpointKind},
148 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
149 project_settings::{GitGutterSetting, ProjectSettings},
150 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
151 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
152 TaskSourceKind,
153};
154use rand::prelude::*;
155use rpc::{proto::*, ErrorExt};
156use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
157use selections_collection::{
158 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
159};
160use serde::{Deserialize, Serialize};
161use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
162use smallvec::SmallVec;
163use snippet::Snippet;
164use std::sync::Arc;
165use std::{
166 any::TypeId,
167 borrow::Cow,
168 cell::RefCell,
169 cmp::{self, Ordering, Reverse},
170 mem,
171 num::NonZeroU32,
172 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
173 path::{Path, PathBuf},
174 rc::Rc,
175 time::{Duration, Instant},
176};
177pub use sum_tree::Bias;
178use sum_tree::TreeMap;
179use text::{BufferId, OffsetUtf16, Rope};
180use theme::{
181 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
182 ThemeColors, ThemeSettings,
183};
184use ui::{
185 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
186 Tooltip,
187};
188use util::{maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
189use workspace::{
190 item::{ItemHandle, PreviewTabsSettings},
191 ItemId, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME,
192};
193use workspace::{
194 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
195 WorkspaceSettings,
196};
197use workspace::{
198 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
199};
200use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
201
202use crate::hover_links::{find_url, find_url_from_range};
203use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
204
205pub const FILE_HEADER_HEIGHT: u32 = 2;
206pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
207pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
208const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
209const MAX_LINE_LEN: usize = 1024;
210const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
211const MAX_SELECTION_HISTORY_LEN: usize = 1024;
212pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
213#[doc(hidden)]
214pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
215
216pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
217pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
218pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
219
220pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
221pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
222pub(crate) const MIN_LINE_NUMBER_DIGITS: u32 = 4;
223
224const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
225 alt: true,
226 shift: true,
227 control: false,
228 platform: false,
229 function: false,
230};
231
232#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
233pub enum InlayId {
234 InlineCompletion(usize),
235 Hint(usize),
236}
237
238impl InlayId {
239 fn id(&self) -> usize {
240 match self {
241 Self::InlineCompletion(id) => *id,
242 Self::Hint(id) => *id,
243 }
244 }
245}
246
247pub enum DebugCurrentRowHighlight {}
248enum DocumentHighlightRead {}
249enum DocumentHighlightWrite {}
250enum InputComposition {}
251enum SelectedTextHighlight {}
252
253#[derive(Debug, Copy, Clone, PartialEq, Eq)]
254pub enum Navigated {
255 Yes,
256 No,
257}
258
259impl Navigated {
260 pub fn from_bool(yes: bool) -> Navigated {
261 if yes {
262 Navigated::Yes
263 } else {
264 Navigated::No
265 }
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270enum DisplayDiffHunk {
271 Folded {
272 display_row: DisplayRow,
273 },
274 Unfolded {
275 is_created_file: bool,
276 diff_base_byte_range: Range<usize>,
277 display_row_range: Range<DisplayRow>,
278 multi_buffer_range: Range<Anchor>,
279 status: DiffHunkStatus,
280 },
281}
282
283pub fn init_settings(cx: &mut App) {
284 EditorSettings::register(cx);
285}
286
287pub fn init(cx: &mut App) {
288 init_settings(cx);
289
290 workspace::register_project_item::<Editor>(cx);
291 workspace::FollowableViewRegistry::register::<Editor>(cx);
292 workspace::register_serializable_item::<Editor>(cx);
293
294 cx.observe_new(
295 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
296 workspace.register_action(Editor::new_file);
297 workspace.register_action(Editor::new_file_vertical);
298 workspace.register_action(Editor::new_file_horizontal);
299 workspace.register_action(Editor::cancel_language_server_work);
300 },
301 )
302 .detach();
303
304 cx.on_action(move |_: &workspace::NewFile, cx| {
305 let app_state = workspace::AppState::global(cx);
306 if let Some(app_state) = app_state.upgrade() {
307 workspace::open_new(
308 Default::default(),
309 app_state,
310 cx,
311 |workspace, window, cx| {
312 Editor::new_file(workspace, &Default::default(), window, cx)
313 },
314 )
315 .detach();
316 }
317 });
318 cx.on_action(move |_: &workspace::NewWindow, cx| {
319 let app_state = workspace::AppState::global(cx);
320 if let Some(app_state) = app_state.upgrade() {
321 workspace::open_new(
322 Default::default(),
323 app_state,
324 cx,
325 |workspace, window, cx| {
326 cx.activate(true);
327 Editor::new_file(workspace, &Default::default(), window, cx)
328 },
329 )
330 .detach();
331 }
332 });
333}
334
335pub struct SearchWithinRange;
336
337trait InvalidationRegion {
338 fn ranges(&self) -> &[Range<Anchor>];
339}
340
341#[derive(Clone, Debug, PartialEq)]
342pub enum SelectPhase {
343 Begin {
344 position: DisplayPoint,
345 add: bool,
346 click_count: usize,
347 },
348 BeginColumnar {
349 position: DisplayPoint,
350 reset: bool,
351 goal_column: u32,
352 },
353 Extend {
354 position: DisplayPoint,
355 click_count: usize,
356 },
357 Update {
358 position: DisplayPoint,
359 goal_column: u32,
360 scroll_delta: gpui::Point<f32>,
361 },
362 End,
363}
364
365#[derive(Clone, Debug)]
366pub enum SelectMode {
367 Character,
368 Word(Range<Anchor>),
369 Line(Range<Anchor>),
370 All,
371}
372
373#[derive(Copy, Clone, PartialEq, Eq, Debug)]
374pub enum EditorMode {
375 SingleLine { auto_width: bool },
376 AutoHeight { max_lines: usize },
377 Full,
378}
379
380#[derive(Copy, Clone, Debug)]
381pub enum SoftWrap {
382 /// Prefer not to wrap at all.
383 ///
384 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
385 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
386 GitDiff,
387 /// Prefer a single line generally, unless an overly long line is encountered.
388 None,
389 /// Soft wrap lines that exceed the editor width.
390 EditorWidth,
391 /// Soft wrap lines at the preferred line length.
392 Column(u32),
393 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
394 Bounded(u32),
395}
396
397#[derive(Clone)]
398pub struct EditorStyle {
399 pub background: Hsla,
400 pub local_player: PlayerColor,
401 pub text: TextStyle,
402 pub scrollbar_width: Pixels,
403 pub syntax: Arc<SyntaxTheme>,
404 pub status: StatusColors,
405 pub inlay_hints_style: HighlightStyle,
406 pub inline_completion_styles: InlineCompletionStyles,
407 pub unnecessary_code_fade: f32,
408}
409
410impl Default for EditorStyle {
411 fn default() -> Self {
412 Self {
413 background: Hsla::default(),
414 local_player: PlayerColor::default(),
415 text: TextStyle::default(),
416 scrollbar_width: Pixels::default(),
417 syntax: Default::default(),
418 // HACK: Status colors don't have a real default.
419 // We should look into removing the status colors from the editor
420 // style and retrieve them directly from the theme.
421 status: StatusColors::dark(),
422 inlay_hints_style: HighlightStyle::default(),
423 inline_completion_styles: InlineCompletionStyles {
424 insertion: HighlightStyle::default(),
425 whitespace: HighlightStyle::default(),
426 },
427 unnecessary_code_fade: Default::default(),
428 }
429 }
430}
431
432pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
433 let show_background = language_settings::language_settings(None, None, cx)
434 .inlay_hints
435 .show_background;
436
437 HighlightStyle {
438 color: Some(cx.theme().status().hint),
439 background_color: show_background.then(|| cx.theme().status().hint_background),
440 ..HighlightStyle::default()
441 }
442}
443
444pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
445 InlineCompletionStyles {
446 insertion: HighlightStyle {
447 color: Some(cx.theme().status().predictive),
448 ..HighlightStyle::default()
449 },
450 whitespace: HighlightStyle {
451 background_color: Some(cx.theme().status().created_background),
452 ..HighlightStyle::default()
453 },
454 }
455}
456
457type CompletionId = usize;
458
459pub(crate) enum EditDisplayMode {
460 TabAccept,
461 DiffPopover,
462 Inline,
463}
464
465enum InlineCompletion {
466 Edit {
467 edits: Vec<(Range<Anchor>, String)>,
468 edit_preview: Option<EditPreview>,
469 display_mode: EditDisplayMode,
470 snapshot: BufferSnapshot,
471 },
472 Move {
473 target: Anchor,
474 snapshot: BufferSnapshot,
475 },
476}
477
478struct InlineCompletionState {
479 inlay_ids: Vec<InlayId>,
480 completion: InlineCompletion,
481 completion_id: Option<SharedString>,
482 invalidation_range: Range<Anchor>,
483}
484
485enum EditPredictionSettings {
486 Disabled,
487 Enabled {
488 show_in_menu: bool,
489 preview_requires_modifier: bool,
490 },
491}
492
493enum InlineCompletionHighlight {}
494
495#[derive(Debug, Clone)]
496struct InlineDiagnostic {
497 message: SharedString,
498 group_id: usize,
499 is_primary: bool,
500 start: Point,
501 severity: DiagnosticSeverity,
502}
503
504pub enum MenuInlineCompletionsPolicy {
505 Never,
506 ByProvider,
507}
508
509pub enum EditPredictionPreview {
510 /// Modifier is not pressed
511 Inactive { released_too_fast: bool },
512 /// Modifier pressed
513 Active {
514 since: Instant,
515 previous_scroll_position: Option<ScrollAnchor>,
516 },
517}
518
519impl EditPredictionPreview {
520 pub fn released_too_fast(&self) -> bool {
521 match self {
522 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
523 EditPredictionPreview::Active { .. } => false,
524 }
525 }
526
527 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
528 if let EditPredictionPreview::Active {
529 previous_scroll_position,
530 ..
531 } = self
532 {
533 *previous_scroll_position = scroll_position;
534 }
535 }
536}
537
538#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
539struct EditorActionId(usize);
540
541impl EditorActionId {
542 pub fn post_inc(&mut self) -> Self {
543 let answer = self.0;
544
545 *self = Self(answer + 1);
546
547 Self(answer)
548 }
549}
550
551// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
552// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
553
554type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
555type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
556
557#[derive(Default)]
558struct ScrollbarMarkerState {
559 scrollbar_size: Size<Pixels>,
560 dirty: bool,
561 markers: Arc<[PaintQuad]>,
562 pending_refresh: Option<Task<Result<()>>>,
563}
564
565impl ScrollbarMarkerState {
566 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
567 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
568 }
569}
570
571#[derive(Clone, Debug)]
572struct RunnableTasks {
573 templates: Vec<(TaskSourceKind, TaskTemplate)>,
574 offset: multi_buffer::Anchor,
575 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
576 column: u32,
577 // Values of all named captures, including those starting with '_'
578 extra_variables: HashMap<String, String>,
579 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
580 context_range: Range<BufferOffset>,
581}
582
583impl RunnableTasks {
584 fn resolve<'a>(
585 &'a self,
586 cx: &'a task::TaskContext,
587 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
588 self.templates.iter().filter_map(|(kind, template)| {
589 template
590 .resolve_task(&kind.to_id_base(), cx)
591 .map(|task| (kind.clone(), task))
592 })
593 }
594}
595
596#[derive(Clone)]
597struct ResolvedTasks {
598 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
599 position: Anchor,
600}
601
602#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
603struct BufferOffset(usize);
604
605// Addons allow storing per-editor state in other crates (e.g. Vim)
606pub trait Addon: 'static {
607 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
608
609 fn render_buffer_header_controls(
610 &self,
611 _: &ExcerptInfo,
612 _: &Window,
613 _: &App,
614 ) -> Option<AnyElement> {
615 None
616 }
617
618 fn to_any(&self) -> &dyn std::any::Any;
619}
620
621/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
622///
623/// See the [module level documentation](self) for more information.
624pub struct Editor {
625 focus_handle: FocusHandle,
626 last_focused_descendant: Option<WeakFocusHandle>,
627 /// The text buffer being edited
628 buffer: Entity<MultiBuffer>,
629 /// Map of how text in the buffer should be displayed.
630 /// Handles soft wraps, folds, fake inlay text insertions, etc.
631 pub display_map: Entity<DisplayMap>,
632 pub selections: SelectionsCollection,
633 pub scroll_manager: ScrollManager,
634 /// When inline assist editors are linked, they all render cursors because
635 /// typing enters text into each of them, even the ones that aren't focused.
636 pub(crate) show_cursor_when_unfocused: bool,
637 columnar_selection_tail: Option<Anchor>,
638 add_selections_state: Option<AddSelectionsState>,
639 select_next_state: Option<SelectNextState>,
640 select_prev_state: Option<SelectNextState>,
641 selection_history: SelectionHistory,
642 autoclose_regions: Vec<AutocloseRegion>,
643 snippet_stack: InvalidationStack<SnippetState>,
644 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
645 ime_transaction: Option<TransactionId>,
646 active_diagnostics: Option<ActiveDiagnosticGroup>,
647 show_inline_diagnostics: bool,
648 inline_diagnostics_update: Task<()>,
649 inline_diagnostics_enabled: bool,
650 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
651 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
652 hard_wrap: Option<usize>,
653
654 // TODO: make this a access method
655 pub project: Option<Entity<Project>>,
656 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
657 completion_provider: Option<Box<dyn CompletionProvider>>,
658 collaboration_hub: Option<Box<dyn CollaborationHub>>,
659 blink_manager: Entity<BlinkManager>,
660 show_cursor_names: bool,
661 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
662 pub show_local_selections: bool,
663 mode: EditorMode,
664 show_breadcrumbs: bool,
665 show_gutter: bool,
666 show_scrollbars: bool,
667 show_line_numbers: Option<bool>,
668 use_relative_line_numbers: Option<bool>,
669 show_git_diff_gutter: Option<bool>,
670 show_code_actions: Option<bool>,
671 show_runnables: Option<bool>,
672 show_breakpoints: Option<bool>,
673 show_wrap_guides: Option<bool>,
674 show_indent_guides: Option<bool>,
675 placeholder_text: Option<Arc<str>>,
676 highlight_order: usize,
677 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
678 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
679 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
680 scrollbar_marker_state: ScrollbarMarkerState,
681 active_indent_guides_state: ActiveIndentGuidesState,
682 nav_history: Option<ItemNavHistory>,
683 context_menu: RefCell<Option<CodeContextMenu>>,
684 mouse_context_menu: Option<MouseContextMenu>,
685 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
686 signature_help_state: SignatureHelpState,
687 auto_signature_help: Option<bool>,
688 find_all_references_task_sources: Vec<Anchor>,
689 next_completion_id: CompletionId,
690 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
691 code_actions_task: Option<Task<Result<()>>>,
692 selection_highlight_task: Option<Task<()>>,
693 document_highlights_task: Option<Task<()>>,
694 linked_editing_range_task: Option<Task<Option<()>>>,
695 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
696 pending_rename: Option<RenameState>,
697 searchable: bool,
698 cursor_shape: CursorShape,
699 current_line_highlight: Option<CurrentLineHighlight>,
700 collapse_matches: bool,
701 autoindent_mode: Option<AutoindentMode>,
702 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
703 input_enabled: bool,
704 use_modal_editing: bool,
705 read_only: bool,
706 leader_peer_id: Option<PeerId>,
707 remote_id: Option<ViewId>,
708 hover_state: HoverState,
709 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
710 gutter_hovered: bool,
711 hovered_link_state: Option<HoveredLinkState>,
712 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
713 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
714 active_inline_completion: Option<InlineCompletionState>,
715 /// Used to prevent flickering as the user types while the menu is open
716 stale_inline_completion_in_menu: Option<InlineCompletionState>,
717 edit_prediction_settings: EditPredictionSettings,
718 inline_completions_hidden_for_vim_mode: bool,
719 show_inline_completions_override: Option<bool>,
720 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
721 edit_prediction_preview: EditPredictionPreview,
722 edit_prediction_indent_conflict: bool,
723 edit_prediction_requires_modifier_in_indent_conflict: bool,
724 inlay_hint_cache: InlayHintCache,
725 next_inlay_id: usize,
726 _subscriptions: Vec<Subscription>,
727 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
728 gutter_dimensions: GutterDimensions,
729 style: Option<EditorStyle>,
730 text_style_refinement: Option<TextStyleRefinement>,
731 next_editor_action_id: EditorActionId,
732 editor_actions:
733 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
734 use_autoclose: bool,
735 use_auto_surround: bool,
736 auto_replace_emoji_shortcode: bool,
737 jsx_tag_auto_close_enabled_in_any_buffer: bool,
738 show_git_blame_gutter: bool,
739 show_git_blame_inline: bool,
740 show_git_blame_inline_delay_task: Option<Task<()>>,
741 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
742 git_blame_inline_enabled: bool,
743 serialize_dirty_buffers: bool,
744 show_selection_menu: Option<bool>,
745 blame: Option<Entity<GitBlame>>,
746 blame_subscription: Option<Subscription>,
747 custom_context_menu: Option<
748 Box<
749 dyn 'static
750 + Fn(
751 &mut Self,
752 DisplayPoint,
753 &mut Window,
754 &mut Context<Self>,
755 ) -> Option<Entity<ui::ContextMenu>>,
756 >,
757 >,
758 last_bounds: Option<Bounds<Pixels>>,
759 last_position_map: Option<Rc<PositionMap>>,
760 expect_bounds_change: Option<Bounds<Pixels>>,
761 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
762 tasks_update_task: Option<Task<()>>,
763 pub breakpoint_store: Option<Entity<BreakpointStore>>,
764 /// Allow's a user to create a breakpoint by selecting this indicator
765 /// It should be None while a user is not hovering over the gutter
766 /// Otherwise it represents the point that the breakpoint will be shown
767 pub gutter_breakpoint_indicator: Option<DisplayPoint>,
768 in_project_search: bool,
769 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
770 breadcrumb_header: Option<String>,
771 focused_block: Option<FocusedBlock>,
772 next_scroll_position: NextScrollCursorCenterTopBottom,
773 addons: HashMap<TypeId, Box<dyn Addon>>,
774 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
775 load_diff_task: Option<Shared<Task<()>>>,
776 selection_mark_mode: bool,
777 toggle_fold_multiple_buffers: Task<()>,
778 _scroll_cursor_center_top_bottom_task: Task<()>,
779 serialize_selections: Task<()>,
780 serialize_folds: Task<()>,
781}
782
783#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
784enum NextScrollCursorCenterTopBottom {
785 #[default]
786 Center,
787 Top,
788 Bottom,
789}
790
791impl NextScrollCursorCenterTopBottom {
792 fn next(&self) -> Self {
793 match self {
794 Self::Center => Self::Top,
795 Self::Top => Self::Bottom,
796 Self::Bottom => Self::Center,
797 }
798 }
799}
800
801#[derive(Clone)]
802pub struct EditorSnapshot {
803 pub mode: EditorMode,
804 show_gutter: bool,
805 show_line_numbers: Option<bool>,
806 show_git_diff_gutter: Option<bool>,
807 show_code_actions: Option<bool>,
808 show_runnables: Option<bool>,
809 show_breakpoints: Option<bool>,
810 git_blame_gutter_max_author_length: Option<usize>,
811 pub display_snapshot: DisplaySnapshot,
812 pub placeholder_text: Option<Arc<str>>,
813 is_focused: bool,
814 scroll_anchor: ScrollAnchor,
815 ongoing_scroll: OngoingScroll,
816 current_line_highlight: CurrentLineHighlight,
817 gutter_hovered: bool,
818}
819
820const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
821
822#[derive(Default, Debug, Clone, Copy)]
823pub struct GutterDimensions {
824 pub left_padding: Pixels,
825 pub right_padding: Pixels,
826 pub width: Pixels,
827 pub margin: Pixels,
828 pub git_blame_entries_width: Option<Pixels>,
829}
830
831impl GutterDimensions {
832 /// The full width of the space taken up by the gutter.
833 pub fn full_width(&self) -> Pixels {
834 self.margin + self.width
835 }
836
837 /// The width of the space reserved for the fold indicators,
838 /// use alongside 'justify_end' and `gutter_width` to
839 /// right align content with the line numbers
840 pub fn fold_area_width(&self) -> Pixels {
841 self.margin + self.right_padding
842 }
843}
844
845#[derive(Debug)]
846pub struct RemoteSelection {
847 pub replica_id: ReplicaId,
848 pub selection: Selection<Anchor>,
849 pub cursor_shape: CursorShape,
850 pub peer_id: PeerId,
851 pub line_mode: bool,
852 pub participant_index: Option<ParticipantIndex>,
853 pub user_name: Option<SharedString>,
854}
855
856#[derive(Clone, Debug)]
857struct SelectionHistoryEntry {
858 selections: Arc<[Selection<Anchor>]>,
859 select_next_state: Option<SelectNextState>,
860 select_prev_state: Option<SelectNextState>,
861 add_selections_state: Option<AddSelectionsState>,
862}
863
864enum SelectionHistoryMode {
865 Normal,
866 Undoing,
867 Redoing,
868}
869
870#[derive(Clone, PartialEq, Eq, Hash)]
871struct HoveredCursor {
872 replica_id: u16,
873 selection_id: usize,
874}
875
876impl Default for SelectionHistoryMode {
877 fn default() -> Self {
878 Self::Normal
879 }
880}
881
882#[derive(Default)]
883struct SelectionHistory {
884 #[allow(clippy::type_complexity)]
885 selections_by_transaction:
886 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
887 mode: SelectionHistoryMode,
888 undo_stack: VecDeque<SelectionHistoryEntry>,
889 redo_stack: VecDeque<SelectionHistoryEntry>,
890}
891
892impl SelectionHistory {
893 fn insert_transaction(
894 &mut self,
895 transaction_id: TransactionId,
896 selections: Arc<[Selection<Anchor>]>,
897 ) {
898 self.selections_by_transaction
899 .insert(transaction_id, (selections, None));
900 }
901
902 #[allow(clippy::type_complexity)]
903 fn transaction(
904 &self,
905 transaction_id: TransactionId,
906 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
907 self.selections_by_transaction.get(&transaction_id)
908 }
909
910 #[allow(clippy::type_complexity)]
911 fn transaction_mut(
912 &mut self,
913 transaction_id: TransactionId,
914 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
915 self.selections_by_transaction.get_mut(&transaction_id)
916 }
917
918 fn push(&mut self, entry: SelectionHistoryEntry) {
919 if !entry.selections.is_empty() {
920 match self.mode {
921 SelectionHistoryMode::Normal => {
922 self.push_undo(entry);
923 self.redo_stack.clear();
924 }
925 SelectionHistoryMode::Undoing => self.push_redo(entry),
926 SelectionHistoryMode::Redoing => self.push_undo(entry),
927 }
928 }
929 }
930
931 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
932 if self
933 .undo_stack
934 .back()
935 .map_or(true, |e| e.selections != entry.selections)
936 {
937 self.undo_stack.push_back(entry);
938 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
939 self.undo_stack.pop_front();
940 }
941 }
942 }
943
944 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
945 if self
946 .redo_stack
947 .back()
948 .map_or(true, |e| e.selections != entry.selections)
949 {
950 self.redo_stack.push_back(entry);
951 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
952 self.redo_stack.pop_front();
953 }
954 }
955 }
956}
957
958struct RowHighlight {
959 index: usize,
960 range: Range<Anchor>,
961 color: Hsla,
962 should_autoscroll: bool,
963}
964
965#[derive(Clone, Debug)]
966struct AddSelectionsState {
967 above: bool,
968 stack: Vec<usize>,
969}
970
971#[derive(Clone)]
972struct SelectNextState {
973 query: AhoCorasick,
974 wordwise: bool,
975 done: bool,
976}
977
978impl std::fmt::Debug for SelectNextState {
979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
980 f.debug_struct(std::any::type_name::<Self>())
981 .field("wordwise", &self.wordwise)
982 .field("done", &self.done)
983 .finish()
984 }
985}
986
987#[derive(Debug)]
988struct AutocloseRegion {
989 selection_id: usize,
990 range: Range<Anchor>,
991 pair: BracketPair,
992}
993
994#[derive(Debug)]
995struct SnippetState {
996 ranges: Vec<Vec<Range<Anchor>>>,
997 active_index: usize,
998 choices: Vec<Option<Vec<String>>>,
999}
1000
1001#[doc(hidden)]
1002pub struct RenameState {
1003 pub range: Range<Anchor>,
1004 pub old_name: Arc<str>,
1005 pub editor: Entity<Editor>,
1006 block_id: CustomBlockId,
1007}
1008
1009struct InvalidationStack<T>(Vec<T>);
1010
1011struct RegisteredInlineCompletionProvider {
1012 provider: Arc<dyn InlineCompletionProviderHandle>,
1013 _subscription: Subscription,
1014}
1015
1016#[derive(Debug, PartialEq, Eq)]
1017struct ActiveDiagnosticGroup {
1018 primary_range: Range<Anchor>,
1019 primary_message: String,
1020 group_id: usize,
1021 blocks: HashMap<CustomBlockId, Diagnostic>,
1022 is_valid: bool,
1023}
1024
1025#[derive(Serialize, Deserialize, Clone, Debug)]
1026pub struct ClipboardSelection {
1027 /// The number of bytes in this selection.
1028 pub len: usize,
1029 /// Whether this was a full-line selection.
1030 pub is_entire_line: bool,
1031 /// The indentation of the first line when this content was originally copied.
1032 pub first_line_indent: u32,
1033}
1034
1035#[derive(Debug)]
1036pub(crate) struct NavigationData {
1037 cursor_anchor: Anchor,
1038 cursor_position: Point,
1039 scroll_anchor: ScrollAnchor,
1040 scroll_top_row: u32,
1041}
1042
1043#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1044pub enum GotoDefinitionKind {
1045 Symbol,
1046 Declaration,
1047 Type,
1048 Implementation,
1049}
1050
1051#[derive(Debug, Clone)]
1052enum InlayHintRefreshReason {
1053 ModifiersChanged(bool),
1054 Toggle(bool),
1055 SettingsChange(InlayHintSettings),
1056 NewLinesShown,
1057 BufferEdited(HashSet<Arc<Language>>),
1058 RefreshRequested,
1059 ExcerptsRemoved(Vec<ExcerptId>),
1060}
1061
1062impl InlayHintRefreshReason {
1063 fn description(&self) -> &'static str {
1064 match self {
1065 Self::ModifiersChanged(_) => "modifiers changed",
1066 Self::Toggle(_) => "toggle",
1067 Self::SettingsChange(_) => "settings change",
1068 Self::NewLinesShown => "new lines shown",
1069 Self::BufferEdited(_) => "buffer edited",
1070 Self::RefreshRequested => "refresh requested",
1071 Self::ExcerptsRemoved(_) => "excerpts removed",
1072 }
1073 }
1074}
1075
1076pub enum FormatTarget {
1077 Buffers,
1078 Ranges(Vec<Range<MultiBufferPoint>>),
1079}
1080
1081pub(crate) struct FocusedBlock {
1082 id: BlockId,
1083 focus_handle: WeakFocusHandle,
1084}
1085
1086#[derive(Clone)]
1087enum JumpData {
1088 MultiBufferRow {
1089 row: MultiBufferRow,
1090 line_offset_from_top: u32,
1091 },
1092 MultiBufferPoint {
1093 excerpt_id: ExcerptId,
1094 position: Point,
1095 anchor: text::Anchor,
1096 line_offset_from_top: u32,
1097 },
1098}
1099
1100pub enum MultibufferSelectionMode {
1101 First,
1102 All,
1103}
1104
1105#[derive(Clone, Copy, Debug, Default)]
1106pub struct RewrapOptions {
1107 pub override_language_settings: bool,
1108 pub preserve_existing_whitespace: bool,
1109}
1110
1111impl Editor {
1112 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1113 let buffer = cx.new(|cx| Buffer::local("", cx));
1114 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1115 Self::new(
1116 EditorMode::SingleLine { auto_width: false },
1117 buffer,
1118 None,
1119 window,
1120 cx,
1121 )
1122 }
1123
1124 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1125 let buffer = cx.new(|cx| Buffer::local("", cx));
1126 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1127 Self::new(EditorMode::Full, buffer, None, window, cx)
1128 }
1129
1130 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1131 let buffer = cx.new(|cx| Buffer::local("", cx));
1132 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1133 Self::new(
1134 EditorMode::SingleLine { auto_width: true },
1135 buffer,
1136 None,
1137 window,
1138 cx,
1139 )
1140 }
1141
1142 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1143 let buffer = cx.new(|cx| Buffer::local("", cx));
1144 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1145 Self::new(
1146 EditorMode::AutoHeight { max_lines },
1147 buffer,
1148 None,
1149 window,
1150 cx,
1151 )
1152 }
1153
1154 pub fn for_buffer(
1155 buffer: Entity<Buffer>,
1156 project: Option<Entity<Project>>,
1157 window: &mut Window,
1158 cx: &mut Context<Self>,
1159 ) -> Self {
1160 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1161 Self::new(EditorMode::Full, buffer, project, window, cx)
1162 }
1163
1164 pub fn for_multibuffer(
1165 buffer: Entity<MultiBuffer>,
1166 project: Option<Entity<Project>>,
1167 window: &mut Window,
1168 cx: &mut Context<Self>,
1169 ) -> Self {
1170 Self::new(EditorMode::Full, buffer, project, window, cx)
1171 }
1172
1173 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1174 let mut clone = Self::new(
1175 self.mode,
1176 self.buffer.clone(),
1177 self.project.clone(),
1178 window,
1179 cx,
1180 );
1181 self.display_map.update(cx, |display_map, cx| {
1182 let snapshot = display_map.snapshot(cx);
1183 clone.display_map.update(cx, |display_map, cx| {
1184 display_map.set_state(&snapshot, cx);
1185 });
1186 });
1187 clone.folds_did_change(cx);
1188 clone.selections.clone_state(&self.selections);
1189 clone.scroll_manager.clone_state(&self.scroll_manager);
1190 clone.searchable = self.searchable;
1191 clone
1192 }
1193
1194 pub fn new(
1195 mode: EditorMode,
1196 buffer: Entity<MultiBuffer>,
1197 project: Option<Entity<Project>>,
1198 window: &mut Window,
1199 cx: &mut Context<Self>,
1200 ) -> Self {
1201 let style = window.text_style();
1202 let font_size = style.font_size.to_pixels(window.rem_size());
1203 let editor = cx.entity().downgrade();
1204 let fold_placeholder = FoldPlaceholder {
1205 constrain_width: true,
1206 render: Arc::new(move |fold_id, fold_range, cx| {
1207 let editor = editor.clone();
1208 div()
1209 .id(fold_id)
1210 .bg(cx.theme().colors().ghost_element_background)
1211 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1212 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1213 .rounded_xs()
1214 .size_full()
1215 .cursor_pointer()
1216 .child("⋯")
1217 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1218 .on_click(move |_, _window, cx| {
1219 editor
1220 .update(cx, |editor, cx| {
1221 editor.unfold_ranges(
1222 &[fold_range.start..fold_range.end],
1223 true,
1224 false,
1225 cx,
1226 );
1227 cx.stop_propagation();
1228 })
1229 .ok();
1230 })
1231 .into_any()
1232 }),
1233 merge_adjacent: true,
1234 ..Default::default()
1235 };
1236 let display_map = cx.new(|cx| {
1237 DisplayMap::new(
1238 buffer.clone(),
1239 style.font(),
1240 font_size,
1241 None,
1242 FILE_HEADER_HEIGHT,
1243 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1244 fold_placeholder,
1245 cx,
1246 )
1247 });
1248
1249 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1250
1251 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1252
1253 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1254 .then(|| language_settings::SoftWrap::None);
1255
1256 let mut project_subscriptions = Vec::new();
1257 if mode == EditorMode::Full {
1258 if let Some(project) = project.as_ref() {
1259 project_subscriptions.push(cx.subscribe_in(
1260 project,
1261 window,
1262 |editor, _, event, window, cx| match event {
1263 project::Event::RefreshCodeLens => {
1264 // we always query lens with actions, without storing them, always refreshing them
1265 }
1266 project::Event::RefreshInlayHints => {
1267 editor
1268 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1269 }
1270 project::Event::SnippetEdit(id, snippet_edits) => {
1271 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1272 let focus_handle = editor.focus_handle(cx);
1273 if focus_handle.is_focused(window) {
1274 let snapshot = buffer.read(cx).snapshot();
1275 for (range, snippet) in snippet_edits {
1276 let editor_range =
1277 language::range_from_lsp(*range).to_offset(&snapshot);
1278 editor
1279 .insert_snippet(
1280 &[editor_range],
1281 snippet.clone(),
1282 window,
1283 cx,
1284 )
1285 .ok();
1286 }
1287 }
1288 }
1289 }
1290 _ => {}
1291 },
1292 ));
1293 if let Some(task_inventory) = project
1294 .read(cx)
1295 .task_store()
1296 .read(cx)
1297 .task_inventory()
1298 .cloned()
1299 {
1300 project_subscriptions.push(cx.observe_in(
1301 &task_inventory,
1302 window,
1303 |editor, _, window, cx| {
1304 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1305 },
1306 ));
1307 };
1308
1309 project_subscriptions.push(cx.subscribe_in(
1310 &project.read(cx).breakpoint_store(),
1311 window,
1312 |editor, _, event, window, cx| match event {
1313 BreakpointStoreEvent::ActiveDebugLineChanged => {
1314 editor.go_to_active_debug_line(window, cx);
1315 }
1316 _ => {}
1317 },
1318 ));
1319 }
1320 }
1321
1322 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1323
1324 let inlay_hint_settings =
1325 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1326 let focus_handle = cx.focus_handle();
1327 cx.on_focus(&focus_handle, window, Self::handle_focus)
1328 .detach();
1329 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1330 .detach();
1331 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1332 .detach();
1333 cx.on_blur(&focus_handle, window, Self::handle_blur)
1334 .detach();
1335
1336 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1337 Some(false)
1338 } else {
1339 None
1340 };
1341
1342 let breakpoint_store = match (mode, project.as_ref()) {
1343 (EditorMode::Full, Some(project)) => Some(project.read(cx).breakpoint_store()),
1344 _ => None,
1345 };
1346
1347 let mut code_action_providers = Vec::new();
1348 let mut load_uncommitted_diff = None;
1349 if let Some(project) = project.clone() {
1350 load_uncommitted_diff = Some(
1351 get_uncommitted_diff_for_buffer(
1352 &project,
1353 buffer.read(cx).all_buffers(),
1354 buffer.clone(),
1355 cx,
1356 )
1357 .shared(),
1358 );
1359 code_action_providers.push(Rc::new(project) as Rc<_>);
1360 }
1361
1362 let mut this = Self {
1363 focus_handle,
1364 show_cursor_when_unfocused: false,
1365 last_focused_descendant: None,
1366 buffer: buffer.clone(),
1367 display_map: display_map.clone(),
1368 selections,
1369 scroll_manager: ScrollManager::new(cx),
1370 columnar_selection_tail: None,
1371 add_selections_state: None,
1372 select_next_state: None,
1373 select_prev_state: None,
1374 selection_history: Default::default(),
1375 autoclose_regions: Default::default(),
1376 snippet_stack: Default::default(),
1377 select_larger_syntax_node_stack: Vec::new(),
1378 ime_transaction: Default::default(),
1379 active_diagnostics: None,
1380 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1381 inline_diagnostics_update: Task::ready(()),
1382 inline_diagnostics: Vec::new(),
1383 soft_wrap_mode_override,
1384 hard_wrap: None,
1385 completion_provider: project.clone().map(|project| Box::new(project) as _),
1386 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1387 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1388 project,
1389 blink_manager: blink_manager.clone(),
1390 show_local_selections: true,
1391 show_scrollbars: true,
1392 mode,
1393 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1394 show_gutter: mode == EditorMode::Full,
1395 show_line_numbers: None,
1396 use_relative_line_numbers: None,
1397 show_git_diff_gutter: None,
1398 show_code_actions: None,
1399 show_runnables: None,
1400 show_breakpoints: None,
1401 show_wrap_guides: None,
1402 show_indent_guides,
1403 placeholder_text: None,
1404 highlight_order: 0,
1405 highlighted_rows: HashMap::default(),
1406 background_highlights: Default::default(),
1407 gutter_highlights: TreeMap::default(),
1408 scrollbar_marker_state: ScrollbarMarkerState::default(),
1409 active_indent_guides_state: ActiveIndentGuidesState::default(),
1410 nav_history: None,
1411 context_menu: RefCell::new(None),
1412 mouse_context_menu: None,
1413 completion_tasks: Default::default(),
1414 signature_help_state: SignatureHelpState::default(),
1415 auto_signature_help: None,
1416 find_all_references_task_sources: Vec::new(),
1417 next_completion_id: 0,
1418 next_inlay_id: 0,
1419 code_action_providers,
1420 available_code_actions: Default::default(),
1421 code_actions_task: Default::default(),
1422 selection_highlight_task: Default::default(),
1423 document_highlights_task: Default::default(),
1424 linked_editing_range_task: Default::default(),
1425 pending_rename: Default::default(),
1426 searchable: true,
1427 cursor_shape: EditorSettings::get_global(cx)
1428 .cursor_shape
1429 .unwrap_or_default(),
1430 current_line_highlight: None,
1431 autoindent_mode: Some(AutoindentMode::EachLine),
1432 collapse_matches: false,
1433 workspace: None,
1434 input_enabled: true,
1435 use_modal_editing: mode == EditorMode::Full,
1436 read_only: false,
1437 use_autoclose: true,
1438 use_auto_surround: true,
1439 auto_replace_emoji_shortcode: false,
1440 jsx_tag_auto_close_enabled_in_any_buffer: false,
1441 leader_peer_id: None,
1442 remote_id: None,
1443 hover_state: Default::default(),
1444 pending_mouse_down: None,
1445 hovered_link_state: Default::default(),
1446 edit_prediction_provider: None,
1447 active_inline_completion: None,
1448 stale_inline_completion_in_menu: None,
1449 edit_prediction_preview: EditPredictionPreview::Inactive {
1450 released_too_fast: false,
1451 },
1452 inline_diagnostics_enabled: mode == EditorMode::Full,
1453 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1454
1455 gutter_hovered: false,
1456 pixel_position_of_newest_cursor: None,
1457 last_bounds: None,
1458 last_position_map: None,
1459 expect_bounds_change: None,
1460 gutter_dimensions: GutterDimensions::default(),
1461 style: None,
1462 show_cursor_names: false,
1463 hovered_cursors: Default::default(),
1464 next_editor_action_id: EditorActionId::default(),
1465 editor_actions: Rc::default(),
1466 inline_completions_hidden_for_vim_mode: false,
1467 show_inline_completions_override: None,
1468 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1469 edit_prediction_settings: EditPredictionSettings::Disabled,
1470 edit_prediction_indent_conflict: false,
1471 edit_prediction_requires_modifier_in_indent_conflict: true,
1472 custom_context_menu: None,
1473 show_git_blame_gutter: false,
1474 show_git_blame_inline: false,
1475 show_selection_menu: None,
1476 show_git_blame_inline_delay_task: None,
1477 git_blame_inline_tooltip: None,
1478 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1479 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1480 .session
1481 .restore_unsaved_buffers,
1482 blame: None,
1483 blame_subscription: None,
1484 tasks: Default::default(),
1485
1486 breakpoint_store,
1487 gutter_breakpoint_indicator: None,
1488 _subscriptions: vec![
1489 cx.observe(&buffer, Self::on_buffer_changed),
1490 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1491 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1492 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1493 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1494 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1495 cx.observe_window_activation(window, |editor, window, cx| {
1496 let active = window.is_window_active();
1497 editor.blink_manager.update(cx, |blink_manager, cx| {
1498 if active {
1499 blink_manager.enable(cx);
1500 } else {
1501 blink_manager.disable(cx);
1502 }
1503 });
1504 }),
1505 ],
1506 tasks_update_task: None,
1507 linked_edit_ranges: Default::default(),
1508 in_project_search: false,
1509 previous_search_ranges: None,
1510 breadcrumb_header: None,
1511 focused_block: None,
1512 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1513 addons: HashMap::default(),
1514 registered_buffers: HashMap::default(),
1515 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1516 selection_mark_mode: false,
1517 toggle_fold_multiple_buffers: Task::ready(()),
1518 serialize_selections: Task::ready(()),
1519 serialize_folds: Task::ready(()),
1520 text_style_refinement: None,
1521 load_diff_task: load_uncommitted_diff,
1522 };
1523 if let Some(breakpoints) = this.breakpoint_store.as_ref() {
1524 this._subscriptions
1525 .push(cx.observe(breakpoints, |_, _, cx| {
1526 cx.notify();
1527 }));
1528 }
1529 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1530 this._subscriptions.extend(project_subscriptions);
1531
1532 this.end_selection(window, cx);
1533 this.scroll_manager.show_scrollbar(window, cx);
1534 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1535
1536 if mode == EditorMode::Full {
1537 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1538 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1539
1540 if this.git_blame_inline_enabled {
1541 this.git_blame_inline_enabled = true;
1542 this.start_git_blame_inline(false, window, cx);
1543 }
1544
1545 this.go_to_active_debug_line(window, cx);
1546
1547 if let Some(buffer) = buffer.read(cx).as_singleton() {
1548 if let Some(project) = this.project.as_ref() {
1549 let handle = project.update(cx, |project, cx| {
1550 project.register_buffer_with_language_servers(&buffer, cx)
1551 });
1552 this.registered_buffers
1553 .insert(buffer.read(cx).remote_id(), handle);
1554 }
1555 }
1556 }
1557
1558 this.report_editor_event("Editor Opened", None, cx);
1559 this
1560 }
1561
1562 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1563 self.mouse_context_menu
1564 .as_ref()
1565 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1566 }
1567
1568 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1569 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1570 }
1571
1572 fn key_context_internal(
1573 &self,
1574 has_active_edit_prediction: bool,
1575 window: &Window,
1576 cx: &App,
1577 ) -> KeyContext {
1578 let mut key_context = KeyContext::new_with_defaults();
1579 key_context.add("Editor");
1580 let mode = match self.mode {
1581 EditorMode::SingleLine { .. } => "single_line",
1582 EditorMode::AutoHeight { .. } => "auto_height",
1583 EditorMode::Full => "full",
1584 };
1585
1586 if EditorSettings::jupyter_enabled(cx) {
1587 key_context.add("jupyter");
1588 }
1589
1590 key_context.set("mode", mode);
1591 if self.pending_rename.is_some() {
1592 key_context.add("renaming");
1593 }
1594
1595 match self.context_menu.borrow().as_ref() {
1596 Some(CodeContextMenu::Completions(_)) => {
1597 key_context.add("menu");
1598 key_context.add("showing_completions");
1599 }
1600 Some(CodeContextMenu::CodeActions(_)) => {
1601 key_context.add("menu");
1602 key_context.add("showing_code_actions")
1603 }
1604 None => {}
1605 }
1606
1607 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1608 if !self.focus_handle(cx).contains_focused(window, cx)
1609 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1610 {
1611 for addon in self.addons.values() {
1612 addon.extend_key_context(&mut key_context, cx)
1613 }
1614 }
1615
1616 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1617 if let Some(extension) = singleton_buffer
1618 .read(cx)
1619 .file()
1620 .and_then(|file| file.path().extension()?.to_str())
1621 {
1622 key_context.set("extension", extension.to_string());
1623 }
1624 } else {
1625 key_context.add("multibuffer");
1626 }
1627
1628 if has_active_edit_prediction {
1629 if self.edit_prediction_in_conflict() {
1630 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1631 } else {
1632 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1633 key_context.add("copilot_suggestion");
1634 }
1635 }
1636
1637 if self.selection_mark_mode {
1638 key_context.add("selection_mode");
1639 }
1640
1641 key_context
1642 }
1643
1644 pub fn edit_prediction_in_conflict(&self) -> bool {
1645 if !self.show_edit_predictions_in_menu() {
1646 return false;
1647 }
1648
1649 let showing_completions = self
1650 .context_menu
1651 .borrow()
1652 .as_ref()
1653 .map_or(false, |context| {
1654 matches!(context, CodeContextMenu::Completions(_))
1655 });
1656
1657 showing_completions
1658 || self.edit_prediction_requires_modifier()
1659 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1660 // bindings to insert tab characters.
1661 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1662 }
1663
1664 pub fn accept_edit_prediction_keybind(
1665 &self,
1666 window: &Window,
1667 cx: &App,
1668 ) -> AcceptEditPredictionBinding {
1669 let key_context = self.key_context_internal(true, window, cx);
1670 let in_conflict = self.edit_prediction_in_conflict();
1671
1672 AcceptEditPredictionBinding(
1673 window
1674 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1675 .into_iter()
1676 .filter(|binding| {
1677 !in_conflict
1678 || binding
1679 .keystrokes()
1680 .first()
1681 .map_or(false, |keystroke| keystroke.modifiers.modified())
1682 })
1683 .rev()
1684 .min_by_key(|binding| {
1685 binding
1686 .keystrokes()
1687 .first()
1688 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1689 }),
1690 )
1691 }
1692
1693 pub fn new_file(
1694 workspace: &mut Workspace,
1695 _: &workspace::NewFile,
1696 window: &mut Window,
1697 cx: &mut Context<Workspace>,
1698 ) {
1699 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1700 "Failed to create buffer",
1701 window,
1702 cx,
1703 |e, _, _| match e.error_code() {
1704 ErrorCode::RemoteUpgradeRequired => Some(format!(
1705 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1706 e.error_tag("required").unwrap_or("the latest version")
1707 )),
1708 _ => None,
1709 },
1710 );
1711 }
1712
1713 pub fn new_in_workspace(
1714 workspace: &mut Workspace,
1715 window: &mut Window,
1716 cx: &mut Context<Workspace>,
1717 ) -> Task<Result<Entity<Editor>>> {
1718 let project = workspace.project().clone();
1719 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1720
1721 cx.spawn_in(window, async move |workspace, cx| {
1722 let buffer = create.await?;
1723 workspace.update_in(cx, |workspace, window, cx| {
1724 let editor =
1725 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1726 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1727 editor
1728 })
1729 })
1730 }
1731
1732 fn new_file_vertical(
1733 workspace: &mut Workspace,
1734 _: &workspace::NewFileSplitVertical,
1735 window: &mut Window,
1736 cx: &mut Context<Workspace>,
1737 ) {
1738 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1739 }
1740
1741 fn new_file_horizontal(
1742 workspace: &mut Workspace,
1743 _: &workspace::NewFileSplitHorizontal,
1744 window: &mut Window,
1745 cx: &mut Context<Workspace>,
1746 ) {
1747 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1748 }
1749
1750 fn new_file_in_direction(
1751 workspace: &mut Workspace,
1752 direction: SplitDirection,
1753 window: &mut Window,
1754 cx: &mut Context<Workspace>,
1755 ) {
1756 let project = workspace.project().clone();
1757 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1758
1759 cx.spawn_in(window, async move |workspace, cx| {
1760 let buffer = create.await?;
1761 workspace.update_in(cx, move |workspace, window, cx| {
1762 workspace.split_item(
1763 direction,
1764 Box::new(
1765 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1766 ),
1767 window,
1768 cx,
1769 )
1770 })?;
1771 anyhow::Ok(())
1772 })
1773 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1774 match e.error_code() {
1775 ErrorCode::RemoteUpgradeRequired => Some(format!(
1776 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1777 e.error_tag("required").unwrap_or("the latest version")
1778 )),
1779 _ => None,
1780 }
1781 });
1782 }
1783
1784 pub fn leader_peer_id(&self) -> Option<PeerId> {
1785 self.leader_peer_id
1786 }
1787
1788 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1789 &self.buffer
1790 }
1791
1792 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1793 self.workspace.as_ref()?.0.upgrade()
1794 }
1795
1796 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1797 self.buffer().read(cx).title(cx)
1798 }
1799
1800 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1801 let git_blame_gutter_max_author_length = self
1802 .render_git_blame_gutter(cx)
1803 .then(|| {
1804 if let Some(blame) = self.blame.as_ref() {
1805 let max_author_length =
1806 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1807 Some(max_author_length)
1808 } else {
1809 None
1810 }
1811 })
1812 .flatten();
1813
1814 EditorSnapshot {
1815 mode: self.mode,
1816 show_gutter: self.show_gutter,
1817 show_line_numbers: self.show_line_numbers,
1818 show_git_diff_gutter: self.show_git_diff_gutter,
1819 show_code_actions: self.show_code_actions,
1820 show_runnables: self.show_runnables,
1821 show_breakpoints: self.show_breakpoints,
1822 git_blame_gutter_max_author_length,
1823 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1824 scroll_anchor: self.scroll_manager.anchor(),
1825 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1826 placeholder_text: self.placeholder_text.clone(),
1827 is_focused: self.focus_handle.is_focused(window),
1828 current_line_highlight: self
1829 .current_line_highlight
1830 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1831 gutter_hovered: self.gutter_hovered,
1832 }
1833 }
1834
1835 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1836 self.buffer.read(cx).language_at(point, cx)
1837 }
1838
1839 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1840 self.buffer.read(cx).read(cx).file_at(point).cloned()
1841 }
1842
1843 pub fn active_excerpt(
1844 &self,
1845 cx: &App,
1846 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1847 self.buffer
1848 .read(cx)
1849 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1850 }
1851
1852 pub fn mode(&self) -> EditorMode {
1853 self.mode
1854 }
1855
1856 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1857 self.collaboration_hub.as_deref()
1858 }
1859
1860 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1861 self.collaboration_hub = Some(hub);
1862 }
1863
1864 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1865 self.in_project_search = in_project_search;
1866 }
1867
1868 pub fn set_custom_context_menu(
1869 &mut self,
1870 f: impl 'static
1871 + Fn(
1872 &mut Self,
1873 DisplayPoint,
1874 &mut Window,
1875 &mut Context<Self>,
1876 ) -> Option<Entity<ui::ContextMenu>>,
1877 ) {
1878 self.custom_context_menu = Some(Box::new(f))
1879 }
1880
1881 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1882 self.completion_provider = provider;
1883 }
1884
1885 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1886 self.semantics_provider.clone()
1887 }
1888
1889 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1890 self.semantics_provider = provider;
1891 }
1892
1893 pub fn set_edit_prediction_provider<T>(
1894 &mut self,
1895 provider: Option<Entity<T>>,
1896 window: &mut Window,
1897 cx: &mut Context<Self>,
1898 ) where
1899 T: EditPredictionProvider,
1900 {
1901 self.edit_prediction_provider =
1902 provider.map(|provider| RegisteredInlineCompletionProvider {
1903 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1904 if this.focus_handle.is_focused(window) {
1905 this.update_visible_inline_completion(window, cx);
1906 }
1907 }),
1908 provider: Arc::new(provider),
1909 });
1910 self.update_edit_prediction_settings(cx);
1911 self.refresh_inline_completion(false, false, window, cx);
1912 }
1913
1914 pub fn placeholder_text(&self) -> Option<&str> {
1915 self.placeholder_text.as_deref()
1916 }
1917
1918 pub fn set_placeholder_text(
1919 &mut self,
1920 placeholder_text: impl Into<Arc<str>>,
1921 cx: &mut Context<Self>,
1922 ) {
1923 let placeholder_text = Some(placeholder_text.into());
1924 if self.placeholder_text != placeholder_text {
1925 self.placeholder_text = placeholder_text;
1926 cx.notify();
1927 }
1928 }
1929
1930 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1931 self.cursor_shape = cursor_shape;
1932
1933 // Disrupt blink for immediate user feedback that the cursor shape has changed
1934 self.blink_manager.update(cx, BlinkManager::show_cursor);
1935
1936 cx.notify();
1937 }
1938
1939 pub fn set_current_line_highlight(
1940 &mut self,
1941 current_line_highlight: Option<CurrentLineHighlight>,
1942 ) {
1943 self.current_line_highlight = current_line_highlight;
1944 }
1945
1946 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1947 self.collapse_matches = collapse_matches;
1948 }
1949
1950 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1951 let buffers = self.buffer.read(cx).all_buffers();
1952 let Some(project) = self.project.as_ref() else {
1953 return;
1954 };
1955 project.update(cx, |project, cx| {
1956 for buffer in buffers {
1957 self.registered_buffers
1958 .entry(buffer.read(cx).remote_id())
1959 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1960 }
1961 })
1962 }
1963
1964 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1965 if self.collapse_matches {
1966 return range.start..range.start;
1967 }
1968 range.clone()
1969 }
1970
1971 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1972 if self.display_map.read(cx).clip_at_line_ends != clip {
1973 self.display_map
1974 .update(cx, |map, _| map.clip_at_line_ends = clip);
1975 }
1976 }
1977
1978 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1979 self.input_enabled = input_enabled;
1980 }
1981
1982 pub fn set_inline_completions_hidden_for_vim_mode(
1983 &mut self,
1984 hidden: bool,
1985 window: &mut Window,
1986 cx: &mut Context<Self>,
1987 ) {
1988 if hidden != self.inline_completions_hidden_for_vim_mode {
1989 self.inline_completions_hidden_for_vim_mode = hidden;
1990 if hidden {
1991 self.update_visible_inline_completion(window, cx);
1992 } else {
1993 self.refresh_inline_completion(true, false, window, cx);
1994 }
1995 }
1996 }
1997
1998 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1999 self.menu_inline_completions_policy = value;
2000 }
2001
2002 pub fn set_autoindent(&mut self, autoindent: bool) {
2003 if autoindent {
2004 self.autoindent_mode = Some(AutoindentMode::EachLine);
2005 } else {
2006 self.autoindent_mode = None;
2007 }
2008 }
2009
2010 pub fn read_only(&self, cx: &App) -> bool {
2011 self.read_only || self.buffer.read(cx).read_only()
2012 }
2013
2014 pub fn set_read_only(&mut self, read_only: bool) {
2015 self.read_only = read_only;
2016 }
2017
2018 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2019 self.use_autoclose = autoclose;
2020 }
2021
2022 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2023 self.use_auto_surround = auto_surround;
2024 }
2025
2026 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2027 self.auto_replace_emoji_shortcode = auto_replace;
2028 }
2029
2030 pub fn toggle_edit_predictions(
2031 &mut self,
2032 _: &ToggleEditPrediction,
2033 window: &mut Window,
2034 cx: &mut Context<Self>,
2035 ) {
2036 if self.show_inline_completions_override.is_some() {
2037 self.set_show_edit_predictions(None, window, cx);
2038 } else {
2039 let show_edit_predictions = !self.edit_predictions_enabled();
2040 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2041 }
2042 }
2043
2044 pub fn set_show_edit_predictions(
2045 &mut self,
2046 show_edit_predictions: Option<bool>,
2047 window: &mut Window,
2048 cx: &mut Context<Self>,
2049 ) {
2050 self.show_inline_completions_override = show_edit_predictions;
2051 self.update_edit_prediction_settings(cx);
2052
2053 if let Some(false) = show_edit_predictions {
2054 self.discard_inline_completion(false, cx);
2055 } else {
2056 self.refresh_inline_completion(false, true, window, cx);
2057 }
2058 }
2059
2060 fn inline_completions_disabled_in_scope(
2061 &self,
2062 buffer: &Entity<Buffer>,
2063 buffer_position: language::Anchor,
2064 cx: &App,
2065 ) -> bool {
2066 let snapshot = buffer.read(cx).snapshot();
2067 let settings = snapshot.settings_at(buffer_position, cx);
2068
2069 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2070 return false;
2071 };
2072
2073 scope.override_name().map_or(false, |scope_name| {
2074 settings
2075 .edit_predictions_disabled_in
2076 .iter()
2077 .any(|s| s == scope_name)
2078 })
2079 }
2080
2081 pub fn set_use_modal_editing(&mut self, to: bool) {
2082 self.use_modal_editing = to;
2083 }
2084
2085 pub fn use_modal_editing(&self) -> bool {
2086 self.use_modal_editing
2087 }
2088
2089 fn selections_did_change(
2090 &mut self,
2091 local: bool,
2092 old_cursor_position: &Anchor,
2093 show_completions: bool,
2094 window: &mut Window,
2095 cx: &mut Context<Self>,
2096 ) {
2097 window.invalidate_character_coordinates();
2098
2099 // Copy selections to primary selection buffer
2100 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2101 if local {
2102 let selections = self.selections.all::<usize>(cx);
2103 let buffer_handle = self.buffer.read(cx).read(cx);
2104
2105 let mut text = String::new();
2106 for (index, selection) in selections.iter().enumerate() {
2107 let text_for_selection = buffer_handle
2108 .text_for_range(selection.start..selection.end)
2109 .collect::<String>();
2110
2111 text.push_str(&text_for_selection);
2112 if index != selections.len() - 1 {
2113 text.push('\n');
2114 }
2115 }
2116
2117 if !text.is_empty() {
2118 cx.write_to_primary(ClipboardItem::new_string(text));
2119 }
2120 }
2121
2122 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2123 self.buffer.update(cx, |buffer, cx| {
2124 buffer.set_active_selections(
2125 &self.selections.disjoint_anchors(),
2126 self.selections.line_mode,
2127 self.cursor_shape,
2128 cx,
2129 )
2130 });
2131 }
2132 let display_map = self
2133 .display_map
2134 .update(cx, |display_map, cx| display_map.snapshot(cx));
2135 let buffer = &display_map.buffer_snapshot;
2136 self.add_selections_state = None;
2137 self.select_next_state = None;
2138 self.select_prev_state = None;
2139 self.select_larger_syntax_node_stack.clear();
2140 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2141 self.snippet_stack
2142 .invalidate(&self.selections.disjoint_anchors(), buffer);
2143 self.take_rename(false, window, cx);
2144
2145 let new_cursor_position = self.selections.newest_anchor().head();
2146
2147 self.push_to_nav_history(
2148 *old_cursor_position,
2149 Some(new_cursor_position.to_point(buffer)),
2150 false,
2151 cx,
2152 );
2153
2154 if local {
2155 let new_cursor_position = self.selections.newest_anchor().head();
2156 let mut context_menu = self.context_menu.borrow_mut();
2157 let completion_menu = match context_menu.as_ref() {
2158 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2159 _ => {
2160 *context_menu = None;
2161 None
2162 }
2163 };
2164 if let Some(buffer_id) = new_cursor_position.buffer_id {
2165 if !self.registered_buffers.contains_key(&buffer_id) {
2166 if let Some(project) = self.project.as_ref() {
2167 project.update(cx, |project, cx| {
2168 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2169 return;
2170 };
2171 self.registered_buffers.insert(
2172 buffer_id,
2173 project.register_buffer_with_language_servers(&buffer, cx),
2174 );
2175 })
2176 }
2177 }
2178 }
2179
2180 if let Some(completion_menu) = completion_menu {
2181 let cursor_position = new_cursor_position.to_offset(buffer);
2182 let (word_range, kind) =
2183 buffer.surrounding_word(completion_menu.initial_position, true);
2184 if kind == Some(CharKind::Word)
2185 && word_range.to_inclusive().contains(&cursor_position)
2186 {
2187 let mut completion_menu = completion_menu.clone();
2188 drop(context_menu);
2189
2190 let query = Self::completion_query(buffer, cursor_position);
2191 cx.spawn(async move |this, cx| {
2192 completion_menu
2193 .filter(query.as_deref(), cx.background_executor().clone())
2194 .await;
2195
2196 this.update(cx, |this, cx| {
2197 let mut context_menu = this.context_menu.borrow_mut();
2198 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2199 else {
2200 return;
2201 };
2202
2203 if menu.id > completion_menu.id {
2204 return;
2205 }
2206
2207 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2208 drop(context_menu);
2209 cx.notify();
2210 })
2211 })
2212 .detach();
2213
2214 if show_completions {
2215 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2216 }
2217 } else {
2218 drop(context_menu);
2219 self.hide_context_menu(window, cx);
2220 }
2221 } else {
2222 drop(context_menu);
2223 }
2224
2225 hide_hover(self, cx);
2226
2227 if old_cursor_position.to_display_point(&display_map).row()
2228 != new_cursor_position.to_display_point(&display_map).row()
2229 {
2230 self.available_code_actions.take();
2231 }
2232 self.refresh_code_actions(window, cx);
2233 self.refresh_document_highlights(cx);
2234 self.refresh_selected_text_highlights(window, cx);
2235 refresh_matching_bracket_highlights(self, window, cx);
2236 self.update_visible_inline_completion(window, cx);
2237 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2238 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2239 if self.git_blame_inline_enabled {
2240 self.start_inline_blame_timer(window, cx);
2241 }
2242 }
2243
2244 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2245 cx.emit(EditorEvent::SelectionsChanged { local });
2246
2247 let selections = &self.selections.disjoint;
2248 if selections.len() == 1 {
2249 cx.emit(SearchEvent::ActiveMatchChanged)
2250 }
2251 if local
2252 && self.is_singleton(cx)
2253 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2254 {
2255 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2256 let background_executor = cx.background_executor().clone();
2257 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2258 let snapshot = self.buffer().read(cx).snapshot(cx);
2259 let selections = selections.clone();
2260 self.serialize_selections = cx.background_spawn(async move {
2261 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2262 let selections = selections
2263 .iter()
2264 .map(|selection| {
2265 (
2266 selection.start.to_offset(&snapshot),
2267 selection.end.to_offset(&snapshot),
2268 )
2269 })
2270 .collect();
2271 DB.save_editor_selections(editor_id, workspace_id, selections)
2272 .await
2273 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2274 .log_err();
2275 });
2276 }
2277 }
2278
2279 cx.notify();
2280 }
2281
2282 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
2283 if !self.is_singleton(cx)
2284 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
2285 {
2286 return;
2287 }
2288
2289 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
2290 return;
2291 };
2292 let background_executor = cx.background_executor().clone();
2293 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2294 let snapshot = self.buffer().read(cx).snapshot(cx);
2295 let folds = self.display_map.update(cx, |display_map, cx| {
2296 display_map
2297 .snapshot(cx)
2298 .folds_in_range(0..snapshot.len())
2299 .map(|fold| {
2300 (
2301 fold.range.start.to_offset(&snapshot),
2302 fold.range.end.to_offset(&snapshot),
2303 )
2304 })
2305 .collect()
2306 });
2307 self.serialize_folds = cx.background_spawn(async move {
2308 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
2309 DB.save_editor_folds(editor_id, workspace_id, folds)
2310 .await
2311 .with_context(|| format!("persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"))
2312 .log_err();
2313 });
2314 }
2315
2316 pub fn sync_selections(
2317 &mut self,
2318 other: Entity<Editor>,
2319 cx: &mut Context<Self>,
2320 ) -> gpui::Subscription {
2321 let other_selections = other.read(cx).selections.disjoint.to_vec();
2322 self.selections.change_with(cx, |selections| {
2323 selections.select_anchors(other_selections);
2324 });
2325
2326 let other_subscription =
2327 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2328 EditorEvent::SelectionsChanged { local: true } => {
2329 let other_selections = other.read(cx).selections.disjoint.to_vec();
2330 if other_selections.is_empty() {
2331 return;
2332 }
2333 this.selections.change_with(cx, |selections| {
2334 selections.select_anchors(other_selections);
2335 });
2336 }
2337 _ => {}
2338 });
2339
2340 let this_subscription =
2341 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2342 EditorEvent::SelectionsChanged { local: true } => {
2343 let these_selections = this.selections.disjoint.to_vec();
2344 if these_selections.is_empty() {
2345 return;
2346 }
2347 other.update(cx, |other_editor, cx| {
2348 other_editor.selections.change_with(cx, |selections| {
2349 selections.select_anchors(these_selections);
2350 })
2351 });
2352 }
2353 _ => {}
2354 });
2355
2356 Subscription::join(other_subscription, this_subscription)
2357 }
2358
2359 pub fn change_selections<R>(
2360 &mut self,
2361 autoscroll: Option<Autoscroll>,
2362 window: &mut Window,
2363 cx: &mut Context<Self>,
2364 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2365 ) -> R {
2366 self.change_selections_inner(autoscroll, true, window, cx, change)
2367 }
2368
2369 fn change_selections_inner<R>(
2370 &mut self,
2371 autoscroll: Option<Autoscroll>,
2372 request_completions: bool,
2373 window: &mut Window,
2374 cx: &mut Context<Self>,
2375 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2376 ) -> R {
2377 let old_cursor_position = self.selections.newest_anchor().head();
2378 self.push_to_selection_history();
2379
2380 let (changed, result) = self.selections.change_with(cx, change);
2381
2382 if changed {
2383 if let Some(autoscroll) = autoscroll {
2384 self.request_autoscroll(autoscroll, cx);
2385 }
2386 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2387
2388 if self.should_open_signature_help_automatically(
2389 &old_cursor_position,
2390 self.signature_help_state.backspace_pressed(),
2391 cx,
2392 ) {
2393 self.show_signature_help(&ShowSignatureHelp, window, cx);
2394 }
2395 self.signature_help_state.set_backspace_pressed(false);
2396 }
2397
2398 result
2399 }
2400
2401 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2402 where
2403 I: IntoIterator<Item = (Range<S>, T)>,
2404 S: ToOffset,
2405 T: Into<Arc<str>>,
2406 {
2407 if self.read_only(cx) {
2408 return;
2409 }
2410
2411 self.buffer
2412 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2413 }
2414
2415 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2416 where
2417 I: IntoIterator<Item = (Range<S>, T)>,
2418 S: ToOffset,
2419 T: Into<Arc<str>>,
2420 {
2421 if self.read_only(cx) {
2422 return;
2423 }
2424
2425 self.buffer.update(cx, |buffer, cx| {
2426 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2427 });
2428 }
2429
2430 pub fn edit_with_block_indent<I, S, T>(
2431 &mut self,
2432 edits: I,
2433 original_indent_columns: Vec<Option<u32>>,
2434 cx: &mut Context<Self>,
2435 ) where
2436 I: IntoIterator<Item = (Range<S>, T)>,
2437 S: ToOffset,
2438 T: Into<Arc<str>>,
2439 {
2440 if self.read_only(cx) {
2441 return;
2442 }
2443
2444 self.buffer.update(cx, |buffer, cx| {
2445 buffer.edit(
2446 edits,
2447 Some(AutoindentMode::Block {
2448 original_indent_columns,
2449 }),
2450 cx,
2451 )
2452 });
2453 }
2454
2455 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2456 self.hide_context_menu(window, cx);
2457
2458 match phase {
2459 SelectPhase::Begin {
2460 position,
2461 add,
2462 click_count,
2463 } => self.begin_selection(position, add, click_count, window, cx),
2464 SelectPhase::BeginColumnar {
2465 position,
2466 goal_column,
2467 reset,
2468 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2469 SelectPhase::Extend {
2470 position,
2471 click_count,
2472 } => self.extend_selection(position, click_count, window, cx),
2473 SelectPhase::Update {
2474 position,
2475 goal_column,
2476 scroll_delta,
2477 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2478 SelectPhase::End => self.end_selection(window, cx),
2479 }
2480 }
2481
2482 fn extend_selection(
2483 &mut self,
2484 position: DisplayPoint,
2485 click_count: usize,
2486 window: &mut Window,
2487 cx: &mut Context<Self>,
2488 ) {
2489 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2490 let tail = self.selections.newest::<usize>(cx).tail();
2491 self.begin_selection(position, false, click_count, window, cx);
2492
2493 let position = position.to_offset(&display_map, Bias::Left);
2494 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2495
2496 let mut pending_selection = self
2497 .selections
2498 .pending_anchor()
2499 .expect("extend_selection not called with pending selection");
2500 if position >= tail {
2501 pending_selection.start = tail_anchor;
2502 } else {
2503 pending_selection.end = tail_anchor;
2504 pending_selection.reversed = true;
2505 }
2506
2507 let mut pending_mode = self.selections.pending_mode().unwrap();
2508 match &mut pending_mode {
2509 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2510 _ => {}
2511 }
2512
2513 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2514 s.set_pending(pending_selection, pending_mode)
2515 });
2516 }
2517
2518 fn begin_selection(
2519 &mut self,
2520 position: DisplayPoint,
2521 add: bool,
2522 click_count: usize,
2523 window: &mut Window,
2524 cx: &mut Context<Self>,
2525 ) {
2526 if !self.focus_handle.is_focused(window) {
2527 self.last_focused_descendant = None;
2528 window.focus(&self.focus_handle);
2529 }
2530
2531 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2532 let buffer = &display_map.buffer_snapshot;
2533 let newest_selection = self.selections.newest_anchor().clone();
2534 let position = display_map.clip_point(position, Bias::Left);
2535
2536 let start;
2537 let end;
2538 let mode;
2539 let mut auto_scroll;
2540 match click_count {
2541 1 => {
2542 start = buffer.anchor_before(position.to_point(&display_map));
2543 end = start;
2544 mode = SelectMode::Character;
2545 auto_scroll = true;
2546 }
2547 2 => {
2548 let range = movement::surrounding_word(&display_map, position);
2549 start = buffer.anchor_before(range.start.to_point(&display_map));
2550 end = buffer.anchor_before(range.end.to_point(&display_map));
2551 mode = SelectMode::Word(start..end);
2552 auto_scroll = true;
2553 }
2554 3 => {
2555 let position = display_map
2556 .clip_point(position, Bias::Left)
2557 .to_point(&display_map);
2558 let line_start = display_map.prev_line_boundary(position).0;
2559 let next_line_start = buffer.clip_point(
2560 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2561 Bias::Left,
2562 );
2563 start = buffer.anchor_before(line_start);
2564 end = buffer.anchor_before(next_line_start);
2565 mode = SelectMode::Line(start..end);
2566 auto_scroll = true;
2567 }
2568 _ => {
2569 start = buffer.anchor_before(0);
2570 end = buffer.anchor_before(buffer.len());
2571 mode = SelectMode::All;
2572 auto_scroll = false;
2573 }
2574 }
2575 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2576
2577 let point_to_delete: Option<usize> = {
2578 let selected_points: Vec<Selection<Point>> =
2579 self.selections.disjoint_in_range(start..end, cx);
2580
2581 if !add || click_count > 1 {
2582 None
2583 } else if !selected_points.is_empty() {
2584 Some(selected_points[0].id)
2585 } else {
2586 let clicked_point_already_selected =
2587 self.selections.disjoint.iter().find(|selection| {
2588 selection.start.to_point(buffer) == start.to_point(buffer)
2589 || selection.end.to_point(buffer) == end.to_point(buffer)
2590 });
2591
2592 clicked_point_already_selected.map(|selection| selection.id)
2593 }
2594 };
2595
2596 let selections_count = self.selections.count();
2597
2598 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2599 if let Some(point_to_delete) = point_to_delete {
2600 s.delete(point_to_delete);
2601
2602 if selections_count == 1 {
2603 s.set_pending_anchor_range(start..end, mode);
2604 }
2605 } else {
2606 if !add {
2607 s.clear_disjoint();
2608 } else if click_count > 1 {
2609 s.delete(newest_selection.id)
2610 }
2611
2612 s.set_pending_anchor_range(start..end, mode);
2613 }
2614 });
2615 }
2616
2617 fn begin_columnar_selection(
2618 &mut self,
2619 position: DisplayPoint,
2620 goal_column: u32,
2621 reset: bool,
2622 window: &mut Window,
2623 cx: &mut Context<Self>,
2624 ) {
2625 if !self.focus_handle.is_focused(window) {
2626 self.last_focused_descendant = None;
2627 window.focus(&self.focus_handle);
2628 }
2629
2630 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2631
2632 if reset {
2633 let pointer_position = display_map
2634 .buffer_snapshot
2635 .anchor_before(position.to_point(&display_map));
2636
2637 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2638 s.clear_disjoint();
2639 s.set_pending_anchor_range(
2640 pointer_position..pointer_position,
2641 SelectMode::Character,
2642 );
2643 });
2644 }
2645
2646 let tail = self.selections.newest::<Point>(cx).tail();
2647 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2648
2649 if !reset {
2650 self.select_columns(
2651 tail.to_display_point(&display_map),
2652 position,
2653 goal_column,
2654 &display_map,
2655 window,
2656 cx,
2657 );
2658 }
2659 }
2660
2661 fn update_selection(
2662 &mut self,
2663 position: DisplayPoint,
2664 goal_column: u32,
2665 scroll_delta: gpui::Point<f32>,
2666 window: &mut Window,
2667 cx: &mut Context<Self>,
2668 ) {
2669 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2670
2671 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2672 let tail = tail.to_display_point(&display_map);
2673 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2674 } else if let Some(mut pending) = self.selections.pending_anchor() {
2675 let buffer = self.buffer.read(cx).snapshot(cx);
2676 let head;
2677 let tail;
2678 let mode = self.selections.pending_mode().unwrap();
2679 match &mode {
2680 SelectMode::Character => {
2681 head = position.to_point(&display_map);
2682 tail = pending.tail().to_point(&buffer);
2683 }
2684 SelectMode::Word(original_range) => {
2685 let original_display_range = original_range.start.to_display_point(&display_map)
2686 ..original_range.end.to_display_point(&display_map);
2687 let original_buffer_range = original_display_range.start.to_point(&display_map)
2688 ..original_display_range.end.to_point(&display_map);
2689 if movement::is_inside_word(&display_map, position)
2690 || original_display_range.contains(&position)
2691 {
2692 let word_range = movement::surrounding_word(&display_map, position);
2693 if word_range.start < original_display_range.start {
2694 head = word_range.start.to_point(&display_map);
2695 } else {
2696 head = word_range.end.to_point(&display_map);
2697 }
2698 } else {
2699 head = position.to_point(&display_map);
2700 }
2701
2702 if head <= original_buffer_range.start {
2703 tail = original_buffer_range.end;
2704 } else {
2705 tail = original_buffer_range.start;
2706 }
2707 }
2708 SelectMode::Line(original_range) => {
2709 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2710
2711 let position = display_map
2712 .clip_point(position, Bias::Left)
2713 .to_point(&display_map);
2714 let line_start = display_map.prev_line_boundary(position).0;
2715 let next_line_start = buffer.clip_point(
2716 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2717 Bias::Left,
2718 );
2719
2720 if line_start < original_range.start {
2721 head = line_start
2722 } else {
2723 head = next_line_start
2724 }
2725
2726 if head <= original_range.start {
2727 tail = original_range.end;
2728 } else {
2729 tail = original_range.start;
2730 }
2731 }
2732 SelectMode::All => {
2733 return;
2734 }
2735 };
2736
2737 if head < tail {
2738 pending.start = buffer.anchor_before(head);
2739 pending.end = buffer.anchor_before(tail);
2740 pending.reversed = true;
2741 } else {
2742 pending.start = buffer.anchor_before(tail);
2743 pending.end = buffer.anchor_before(head);
2744 pending.reversed = false;
2745 }
2746
2747 self.change_selections(None, window, cx, |s| {
2748 s.set_pending(pending, mode);
2749 });
2750 } else {
2751 log::error!("update_selection dispatched with no pending selection");
2752 return;
2753 }
2754
2755 self.apply_scroll_delta(scroll_delta, window, cx);
2756 cx.notify();
2757 }
2758
2759 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2760 self.columnar_selection_tail.take();
2761 if self.selections.pending_anchor().is_some() {
2762 let selections = self.selections.all::<usize>(cx);
2763 self.change_selections(None, window, cx, |s| {
2764 s.select(selections);
2765 s.clear_pending();
2766 });
2767 }
2768 }
2769
2770 fn select_columns(
2771 &mut self,
2772 tail: DisplayPoint,
2773 head: DisplayPoint,
2774 goal_column: u32,
2775 display_map: &DisplaySnapshot,
2776 window: &mut Window,
2777 cx: &mut Context<Self>,
2778 ) {
2779 let start_row = cmp::min(tail.row(), head.row());
2780 let end_row = cmp::max(tail.row(), head.row());
2781 let start_column = cmp::min(tail.column(), goal_column);
2782 let end_column = cmp::max(tail.column(), goal_column);
2783 let reversed = start_column < tail.column();
2784
2785 let selection_ranges = (start_row.0..=end_row.0)
2786 .map(DisplayRow)
2787 .filter_map(|row| {
2788 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2789 let start = display_map
2790 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2791 .to_point(display_map);
2792 let end = display_map
2793 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2794 .to_point(display_map);
2795 if reversed {
2796 Some(end..start)
2797 } else {
2798 Some(start..end)
2799 }
2800 } else {
2801 None
2802 }
2803 })
2804 .collect::<Vec<_>>();
2805
2806 self.change_selections(None, window, cx, |s| {
2807 s.select_ranges(selection_ranges);
2808 });
2809 cx.notify();
2810 }
2811
2812 pub fn has_pending_nonempty_selection(&self) -> bool {
2813 let pending_nonempty_selection = match self.selections.pending_anchor() {
2814 Some(Selection { start, end, .. }) => start != end,
2815 None => false,
2816 };
2817
2818 pending_nonempty_selection
2819 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2820 }
2821
2822 pub fn has_pending_selection(&self) -> bool {
2823 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2824 }
2825
2826 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2827 self.selection_mark_mode = false;
2828
2829 if self.clear_expanded_diff_hunks(cx) {
2830 cx.notify();
2831 return;
2832 }
2833 if self.dismiss_menus_and_popups(true, window, cx) {
2834 return;
2835 }
2836
2837 if self.mode == EditorMode::Full
2838 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2839 {
2840 return;
2841 }
2842
2843 cx.propagate();
2844 }
2845
2846 pub fn dismiss_menus_and_popups(
2847 &mut self,
2848 is_user_requested: bool,
2849 window: &mut Window,
2850 cx: &mut Context<Self>,
2851 ) -> bool {
2852 if self.take_rename(false, window, cx).is_some() {
2853 return true;
2854 }
2855
2856 if hide_hover(self, cx) {
2857 return true;
2858 }
2859
2860 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2861 return true;
2862 }
2863
2864 if self.hide_context_menu(window, cx).is_some() {
2865 return true;
2866 }
2867
2868 if self.mouse_context_menu.take().is_some() {
2869 return true;
2870 }
2871
2872 if is_user_requested && self.discard_inline_completion(true, cx) {
2873 return true;
2874 }
2875
2876 if self.snippet_stack.pop().is_some() {
2877 return true;
2878 }
2879
2880 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2881 self.dismiss_diagnostics(cx);
2882 return true;
2883 }
2884
2885 false
2886 }
2887
2888 fn linked_editing_ranges_for(
2889 &self,
2890 selection: Range<text::Anchor>,
2891 cx: &App,
2892 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2893 if self.linked_edit_ranges.is_empty() {
2894 return None;
2895 }
2896 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2897 selection.end.buffer_id.and_then(|end_buffer_id| {
2898 if selection.start.buffer_id != Some(end_buffer_id) {
2899 return None;
2900 }
2901 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2902 let snapshot = buffer.read(cx).snapshot();
2903 self.linked_edit_ranges
2904 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2905 .map(|ranges| (ranges, snapshot, buffer))
2906 })?;
2907 use text::ToOffset as TO;
2908 // find offset from the start of current range to current cursor position
2909 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2910
2911 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2912 let start_difference = start_offset - start_byte_offset;
2913 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2914 let end_difference = end_offset - start_byte_offset;
2915 // Current range has associated linked ranges.
2916 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2917 for range in linked_ranges.iter() {
2918 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2919 let end_offset = start_offset + end_difference;
2920 let start_offset = start_offset + start_difference;
2921 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2922 continue;
2923 }
2924 if self.selections.disjoint_anchor_ranges().any(|s| {
2925 if s.start.buffer_id != selection.start.buffer_id
2926 || s.end.buffer_id != selection.end.buffer_id
2927 {
2928 return false;
2929 }
2930 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2931 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2932 }) {
2933 continue;
2934 }
2935 let start = buffer_snapshot.anchor_after(start_offset);
2936 let end = buffer_snapshot.anchor_after(end_offset);
2937 linked_edits
2938 .entry(buffer.clone())
2939 .or_default()
2940 .push(start..end);
2941 }
2942 Some(linked_edits)
2943 }
2944
2945 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2946 let text: Arc<str> = text.into();
2947
2948 if self.read_only(cx) {
2949 return;
2950 }
2951
2952 let selections = self.selections.all_adjusted(cx);
2953 let mut bracket_inserted = false;
2954 let mut edits = Vec::new();
2955 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2956 let mut new_selections = Vec::with_capacity(selections.len());
2957 let mut new_autoclose_regions = Vec::new();
2958 let snapshot = self.buffer.read(cx).read(cx);
2959
2960 for (selection, autoclose_region) in
2961 self.selections_with_autoclose_regions(selections, &snapshot)
2962 {
2963 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2964 // Determine if the inserted text matches the opening or closing
2965 // bracket of any of this language's bracket pairs.
2966 let mut bracket_pair = None;
2967 let mut is_bracket_pair_start = false;
2968 let mut is_bracket_pair_end = false;
2969 if !text.is_empty() {
2970 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2971 // and they are removing the character that triggered IME popup.
2972 for (pair, enabled) in scope.brackets() {
2973 if !pair.close && !pair.surround {
2974 continue;
2975 }
2976
2977 if enabled && pair.start.ends_with(text.as_ref()) {
2978 let prefix_len = pair.start.len() - text.len();
2979 let preceding_text_matches_prefix = prefix_len == 0
2980 || (selection.start.column >= (prefix_len as u32)
2981 && snapshot.contains_str_at(
2982 Point::new(
2983 selection.start.row,
2984 selection.start.column - (prefix_len as u32),
2985 ),
2986 &pair.start[..prefix_len],
2987 ));
2988 if preceding_text_matches_prefix {
2989 bracket_pair = Some(pair.clone());
2990 is_bracket_pair_start = true;
2991 break;
2992 }
2993 }
2994 if pair.end.as_str() == text.as_ref() {
2995 bracket_pair = Some(pair.clone());
2996 is_bracket_pair_end = true;
2997 break;
2998 }
2999 }
3000 }
3001
3002 if let Some(bracket_pair) = bracket_pair {
3003 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3004 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3005 let auto_surround =
3006 self.use_auto_surround && snapshot_settings.use_auto_surround;
3007 if selection.is_empty() {
3008 if is_bracket_pair_start {
3009 // If the inserted text is a suffix of an opening bracket and the
3010 // selection is preceded by the rest of the opening bracket, then
3011 // insert the closing bracket.
3012 let following_text_allows_autoclose = snapshot
3013 .chars_at(selection.start)
3014 .next()
3015 .map_or(true, |c| scope.should_autoclose_before(c));
3016
3017 let preceding_text_allows_autoclose = selection.start.column == 0
3018 || snapshot.reversed_chars_at(selection.start).next().map_or(
3019 true,
3020 |c| {
3021 bracket_pair.start != bracket_pair.end
3022 || !snapshot
3023 .char_classifier_at(selection.start)
3024 .is_word(c)
3025 },
3026 );
3027
3028 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3029 && bracket_pair.start.len() == 1
3030 {
3031 let target = bracket_pair.start.chars().next().unwrap();
3032 let current_line_count = snapshot
3033 .reversed_chars_at(selection.start)
3034 .take_while(|&c| c != '\n')
3035 .filter(|&c| c == target)
3036 .count();
3037 current_line_count % 2 == 1
3038 } else {
3039 false
3040 };
3041
3042 if autoclose
3043 && bracket_pair.close
3044 && following_text_allows_autoclose
3045 && preceding_text_allows_autoclose
3046 && !is_closing_quote
3047 {
3048 let anchor = snapshot.anchor_before(selection.end);
3049 new_selections.push((selection.map(|_| anchor), text.len()));
3050 new_autoclose_regions.push((
3051 anchor,
3052 text.len(),
3053 selection.id,
3054 bracket_pair.clone(),
3055 ));
3056 edits.push((
3057 selection.range(),
3058 format!("{}{}", text, bracket_pair.end).into(),
3059 ));
3060 bracket_inserted = true;
3061 continue;
3062 }
3063 }
3064
3065 if let Some(region) = autoclose_region {
3066 // If the selection is followed by an auto-inserted closing bracket,
3067 // then don't insert that closing bracket again; just move the selection
3068 // past the closing bracket.
3069 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3070 && text.as_ref() == region.pair.end.as_str();
3071 if should_skip {
3072 let anchor = snapshot.anchor_after(selection.end);
3073 new_selections
3074 .push((selection.map(|_| anchor), region.pair.end.len()));
3075 continue;
3076 }
3077 }
3078
3079 let always_treat_brackets_as_autoclosed = snapshot
3080 .language_settings_at(selection.start, cx)
3081 .always_treat_brackets_as_autoclosed;
3082 if always_treat_brackets_as_autoclosed
3083 && is_bracket_pair_end
3084 && snapshot.contains_str_at(selection.end, text.as_ref())
3085 {
3086 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3087 // and the inserted text is a closing bracket and the selection is followed
3088 // by the closing bracket then move the selection past the closing bracket.
3089 let anchor = snapshot.anchor_after(selection.end);
3090 new_selections.push((selection.map(|_| anchor), text.len()));
3091 continue;
3092 }
3093 }
3094 // If an opening bracket is 1 character long and is typed while
3095 // text is selected, then surround that text with the bracket pair.
3096 else if auto_surround
3097 && bracket_pair.surround
3098 && is_bracket_pair_start
3099 && bracket_pair.start.chars().count() == 1
3100 {
3101 edits.push((selection.start..selection.start, text.clone()));
3102 edits.push((
3103 selection.end..selection.end,
3104 bracket_pair.end.as_str().into(),
3105 ));
3106 bracket_inserted = true;
3107 new_selections.push((
3108 Selection {
3109 id: selection.id,
3110 start: snapshot.anchor_after(selection.start),
3111 end: snapshot.anchor_before(selection.end),
3112 reversed: selection.reversed,
3113 goal: selection.goal,
3114 },
3115 0,
3116 ));
3117 continue;
3118 }
3119 }
3120 }
3121
3122 if self.auto_replace_emoji_shortcode
3123 && selection.is_empty()
3124 && text.as_ref().ends_with(':')
3125 {
3126 if let Some(possible_emoji_short_code) =
3127 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3128 {
3129 if !possible_emoji_short_code.is_empty() {
3130 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3131 let emoji_shortcode_start = Point::new(
3132 selection.start.row,
3133 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3134 );
3135
3136 // Remove shortcode from buffer
3137 edits.push((
3138 emoji_shortcode_start..selection.start,
3139 "".to_string().into(),
3140 ));
3141 new_selections.push((
3142 Selection {
3143 id: selection.id,
3144 start: snapshot.anchor_after(emoji_shortcode_start),
3145 end: snapshot.anchor_before(selection.start),
3146 reversed: selection.reversed,
3147 goal: selection.goal,
3148 },
3149 0,
3150 ));
3151
3152 // Insert emoji
3153 let selection_start_anchor = snapshot.anchor_after(selection.start);
3154 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3155 edits.push((selection.start..selection.end, emoji.to_string().into()));
3156
3157 continue;
3158 }
3159 }
3160 }
3161 }
3162
3163 // If not handling any auto-close operation, then just replace the selected
3164 // text with the given input and move the selection to the end of the
3165 // newly inserted text.
3166 let anchor = snapshot.anchor_after(selection.end);
3167 if !self.linked_edit_ranges.is_empty() {
3168 let start_anchor = snapshot.anchor_before(selection.start);
3169
3170 let is_word_char = text.chars().next().map_or(true, |char| {
3171 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3172 classifier.is_word(char)
3173 });
3174
3175 if is_word_char {
3176 if let Some(ranges) = self
3177 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3178 {
3179 for (buffer, edits) in ranges {
3180 linked_edits
3181 .entry(buffer.clone())
3182 .or_default()
3183 .extend(edits.into_iter().map(|range| (range, text.clone())));
3184 }
3185 }
3186 }
3187 }
3188
3189 new_selections.push((selection.map(|_| anchor), 0));
3190 edits.push((selection.start..selection.end, text.clone()));
3191 }
3192
3193 drop(snapshot);
3194
3195 self.transact(window, cx, |this, window, cx| {
3196 let initial_buffer_versions =
3197 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3198
3199 this.buffer.update(cx, |buffer, cx| {
3200 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3201 });
3202 for (buffer, edits) in linked_edits {
3203 buffer.update(cx, |buffer, cx| {
3204 let snapshot = buffer.snapshot();
3205 let edits = edits
3206 .into_iter()
3207 .map(|(range, text)| {
3208 use text::ToPoint as TP;
3209 let end_point = TP::to_point(&range.end, &snapshot);
3210 let start_point = TP::to_point(&range.start, &snapshot);
3211 (start_point..end_point, text)
3212 })
3213 .sorted_by_key(|(range, _)| range.start)
3214 .collect::<Vec<_>>();
3215 buffer.edit(edits, None, cx);
3216 })
3217 }
3218 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3219 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3220 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3221 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3222 .zip(new_selection_deltas)
3223 .map(|(selection, delta)| Selection {
3224 id: selection.id,
3225 start: selection.start + delta,
3226 end: selection.end + delta,
3227 reversed: selection.reversed,
3228 goal: SelectionGoal::None,
3229 })
3230 .collect::<Vec<_>>();
3231
3232 let mut i = 0;
3233 for (position, delta, selection_id, pair) in new_autoclose_regions {
3234 let position = position.to_offset(&map.buffer_snapshot) + delta;
3235 let start = map.buffer_snapshot.anchor_before(position);
3236 let end = map.buffer_snapshot.anchor_after(position);
3237 while let Some(existing_state) = this.autoclose_regions.get(i) {
3238 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3239 Ordering::Less => i += 1,
3240 Ordering::Greater => break,
3241 Ordering::Equal => {
3242 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3243 Ordering::Less => i += 1,
3244 Ordering::Equal => break,
3245 Ordering::Greater => break,
3246 }
3247 }
3248 }
3249 }
3250 this.autoclose_regions.insert(
3251 i,
3252 AutocloseRegion {
3253 selection_id,
3254 range: start..end,
3255 pair,
3256 },
3257 );
3258 }
3259
3260 let had_active_inline_completion = this.has_active_inline_completion();
3261 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3262 s.select(new_selections)
3263 });
3264
3265 if !bracket_inserted {
3266 if let Some(on_type_format_task) =
3267 this.trigger_on_type_formatting(text.to_string(), window, cx)
3268 {
3269 on_type_format_task.detach_and_log_err(cx);
3270 }
3271 }
3272
3273 let editor_settings = EditorSettings::get_global(cx);
3274 if bracket_inserted
3275 && (editor_settings.auto_signature_help
3276 || editor_settings.show_signature_help_after_edits)
3277 {
3278 this.show_signature_help(&ShowSignatureHelp, window, cx);
3279 }
3280
3281 let trigger_in_words =
3282 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3283 if this.hard_wrap.is_some() {
3284 let latest: Range<Point> = this.selections.newest(cx).range();
3285 if latest.is_empty()
3286 && this
3287 .buffer()
3288 .read(cx)
3289 .snapshot(cx)
3290 .line_len(MultiBufferRow(latest.start.row))
3291 == latest.start.column
3292 {
3293 this.rewrap_impl(
3294 RewrapOptions {
3295 override_language_settings: true,
3296 preserve_existing_whitespace: true,
3297 },
3298 cx,
3299 )
3300 }
3301 }
3302 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3303 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3304 this.refresh_inline_completion(true, false, window, cx);
3305 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3306 });
3307 }
3308
3309 fn find_possible_emoji_shortcode_at_position(
3310 snapshot: &MultiBufferSnapshot,
3311 position: Point,
3312 ) -> Option<String> {
3313 let mut chars = Vec::new();
3314 let mut found_colon = false;
3315 for char in snapshot.reversed_chars_at(position).take(100) {
3316 // Found a possible emoji shortcode in the middle of the buffer
3317 if found_colon {
3318 if char.is_whitespace() {
3319 chars.reverse();
3320 return Some(chars.iter().collect());
3321 }
3322 // If the previous character is not a whitespace, we are in the middle of a word
3323 // and we only want to complete the shortcode if the word is made up of other emojis
3324 let mut containing_word = String::new();
3325 for ch in snapshot
3326 .reversed_chars_at(position)
3327 .skip(chars.len() + 1)
3328 .take(100)
3329 {
3330 if ch.is_whitespace() {
3331 break;
3332 }
3333 containing_word.push(ch);
3334 }
3335 let containing_word = containing_word.chars().rev().collect::<String>();
3336 if util::word_consists_of_emojis(containing_word.as_str()) {
3337 chars.reverse();
3338 return Some(chars.iter().collect());
3339 }
3340 }
3341
3342 if char.is_whitespace() || !char.is_ascii() {
3343 return None;
3344 }
3345 if char == ':' {
3346 found_colon = true;
3347 } else {
3348 chars.push(char);
3349 }
3350 }
3351 // Found a possible emoji shortcode at the beginning of the buffer
3352 chars.reverse();
3353 Some(chars.iter().collect())
3354 }
3355
3356 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3357 self.transact(window, cx, |this, window, cx| {
3358 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3359 let selections = this.selections.all::<usize>(cx);
3360 let multi_buffer = this.buffer.read(cx);
3361 let buffer = multi_buffer.snapshot(cx);
3362 selections
3363 .iter()
3364 .map(|selection| {
3365 let start_point = selection.start.to_point(&buffer);
3366 let mut indent =
3367 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3368 indent.len = cmp::min(indent.len, start_point.column);
3369 let start = selection.start;
3370 let end = selection.end;
3371 let selection_is_empty = start == end;
3372 let language_scope = buffer.language_scope_at(start);
3373 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3374 &language_scope
3375 {
3376 let insert_extra_newline =
3377 insert_extra_newline_brackets(&buffer, start..end, language)
3378 || insert_extra_newline_tree_sitter(&buffer, start..end);
3379
3380 // Comment extension on newline is allowed only for cursor selections
3381 let comment_delimiter = maybe!({
3382 if !selection_is_empty {
3383 return None;
3384 }
3385
3386 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3387 return None;
3388 }
3389
3390 let delimiters = language.line_comment_prefixes();
3391 let max_len_of_delimiter =
3392 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3393 let (snapshot, range) =
3394 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3395
3396 let mut index_of_first_non_whitespace = 0;
3397 let comment_candidate = snapshot
3398 .chars_for_range(range)
3399 .skip_while(|c| {
3400 let should_skip = c.is_whitespace();
3401 if should_skip {
3402 index_of_first_non_whitespace += 1;
3403 }
3404 should_skip
3405 })
3406 .take(max_len_of_delimiter)
3407 .collect::<String>();
3408 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3409 comment_candidate.starts_with(comment_prefix.as_ref())
3410 })?;
3411 let cursor_is_placed_after_comment_marker =
3412 index_of_first_non_whitespace + comment_prefix.len()
3413 <= start_point.column as usize;
3414 if cursor_is_placed_after_comment_marker {
3415 Some(comment_prefix.clone())
3416 } else {
3417 None
3418 }
3419 });
3420 (comment_delimiter, insert_extra_newline)
3421 } else {
3422 (None, false)
3423 };
3424
3425 let capacity_for_delimiter = comment_delimiter
3426 .as_deref()
3427 .map(str::len)
3428 .unwrap_or_default();
3429 let mut new_text =
3430 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3431 new_text.push('\n');
3432 new_text.extend(indent.chars());
3433 if let Some(delimiter) = &comment_delimiter {
3434 new_text.push_str(delimiter);
3435 }
3436 if insert_extra_newline {
3437 new_text = new_text.repeat(2);
3438 }
3439
3440 let anchor = buffer.anchor_after(end);
3441 let new_selection = selection.map(|_| anchor);
3442 (
3443 (start..end, new_text),
3444 (insert_extra_newline, new_selection),
3445 )
3446 })
3447 .unzip()
3448 };
3449
3450 this.edit_with_autoindent(edits, cx);
3451 let buffer = this.buffer.read(cx).snapshot(cx);
3452 let new_selections = selection_fixup_info
3453 .into_iter()
3454 .map(|(extra_newline_inserted, new_selection)| {
3455 let mut cursor = new_selection.end.to_point(&buffer);
3456 if extra_newline_inserted {
3457 cursor.row -= 1;
3458 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3459 }
3460 new_selection.map(|_| cursor)
3461 })
3462 .collect();
3463
3464 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3465 s.select(new_selections)
3466 });
3467 this.refresh_inline_completion(true, false, window, cx);
3468 });
3469 }
3470
3471 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3472 let buffer = self.buffer.read(cx);
3473 let snapshot = buffer.snapshot(cx);
3474
3475 let mut edits = Vec::new();
3476 let mut rows = Vec::new();
3477
3478 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3479 let cursor = selection.head();
3480 let row = cursor.row;
3481
3482 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3483
3484 let newline = "\n".to_string();
3485 edits.push((start_of_line..start_of_line, newline));
3486
3487 rows.push(row + rows_inserted as u32);
3488 }
3489
3490 self.transact(window, cx, |editor, window, cx| {
3491 editor.edit(edits, cx);
3492
3493 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3494 let mut index = 0;
3495 s.move_cursors_with(|map, _, _| {
3496 let row = rows[index];
3497 index += 1;
3498
3499 let point = Point::new(row, 0);
3500 let boundary = map.next_line_boundary(point).1;
3501 let clipped = map.clip_point(boundary, Bias::Left);
3502
3503 (clipped, SelectionGoal::None)
3504 });
3505 });
3506
3507 let mut indent_edits = Vec::new();
3508 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3509 for row in rows {
3510 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3511 for (row, indent) in indents {
3512 if indent.len == 0 {
3513 continue;
3514 }
3515
3516 let text = match indent.kind {
3517 IndentKind::Space => " ".repeat(indent.len as usize),
3518 IndentKind::Tab => "\t".repeat(indent.len as usize),
3519 };
3520 let point = Point::new(row.0, 0);
3521 indent_edits.push((point..point, text));
3522 }
3523 }
3524 editor.edit(indent_edits, cx);
3525 });
3526 }
3527
3528 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3529 let buffer = self.buffer.read(cx);
3530 let snapshot = buffer.snapshot(cx);
3531
3532 let mut edits = Vec::new();
3533 let mut rows = Vec::new();
3534 let mut rows_inserted = 0;
3535
3536 for selection in self.selections.all_adjusted(cx) {
3537 let cursor = selection.head();
3538 let row = cursor.row;
3539
3540 let point = Point::new(row + 1, 0);
3541 let start_of_line = snapshot.clip_point(point, Bias::Left);
3542
3543 let newline = "\n".to_string();
3544 edits.push((start_of_line..start_of_line, newline));
3545
3546 rows_inserted += 1;
3547 rows.push(row + rows_inserted);
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 insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3589 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3590 original_indent_columns: Vec::new(),
3591 });
3592 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3593 }
3594
3595 fn insert_with_autoindent_mode(
3596 &mut self,
3597 text: &str,
3598 autoindent_mode: Option<AutoindentMode>,
3599 window: &mut Window,
3600 cx: &mut Context<Self>,
3601 ) {
3602 if self.read_only(cx) {
3603 return;
3604 }
3605
3606 let text: Arc<str> = text.into();
3607 self.transact(window, cx, |this, window, cx| {
3608 let old_selections = this.selections.all_adjusted(cx);
3609 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3610 let anchors = {
3611 let snapshot = buffer.read(cx);
3612 old_selections
3613 .iter()
3614 .map(|s| {
3615 let anchor = snapshot.anchor_after(s.head());
3616 s.map(|_| anchor)
3617 })
3618 .collect::<Vec<_>>()
3619 };
3620 buffer.edit(
3621 old_selections
3622 .iter()
3623 .map(|s| (s.start..s.end, text.clone())),
3624 autoindent_mode,
3625 cx,
3626 );
3627 anchors
3628 });
3629
3630 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3631 s.select_anchors(selection_anchors);
3632 });
3633
3634 cx.notify();
3635 });
3636 }
3637
3638 fn trigger_completion_on_input(
3639 &mut self,
3640 text: &str,
3641 trigger_in_words: bool,
3642 window: &mut Window,
3643 cx: &mut Context<Self>,
3644 ) {
3645 let ignore_completion_provider = self
3646 .context_menu
3647 .borrow()
3648 .as_ref()
3649 .map(|menu| match menu {
3650 CodeContextMenu::Completions(completions_menu) => {
3651 completions_menu.ignore_completion_provider
3652 }
3653 CodeContextMenu::CodeActions(_) => false,
3654 })
3655 .unwrap_or(false);
3656
3657 if ignore_completion_provider {
3658 self.show_word_completions(&ShowWordCompletions, window, cx);
3659 } else if self.is_completion_trigger(text, trigger_in_words, cx) {
3660 self.show_completions(
3661 &ShowCompletions {
3662 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3663 },
3664 window,
3665 cx,
3666 );
3667 } else {
3668 self.hide_context_menu(window, cx);
3669 }
3670 }
3671
3672 fn is_completion_trigger(
3673 &self,
3674 text: &str,
3675 trigger_in_words: bool,
3676 cx: &mut Context<Self>,
3677 ) -> bool {
3678 let position = self.selections.newest_anchor().head();
3679 let multibuffer = self.buffer.read(cx);
3680 let Some(buffer) = position
3681 .buffer_id
3682 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3683 else {
3684 return false;
3685 };
3686
3687 if let Some(completion_provider) = &self.completion_provider {
3688 completion_provider.is_completion_trigger(
3689 &buffer,
3690 position.text_anchor,
3691 text,
3692 trigger_in_words,
3693 cx,
3694 )
3695 } else {
3696 false
3697 }
3698 }
3699
3700 /// If any empty selections is touching the start of its innermost containing autoclose
3701 /// region, expand it to select the brackets.
3702 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3703 let selections = self.selections.all::<usize>(cx);
3704 let buffer = self.buffer.read(cx).read(cx);
3705 let new_selections = self
3706 .selections_with_autoclose_regions(selections, &buffer)
3707 .map(|(mut selection, region)| {
3708 if !selection.is_empty() {
3709 return selection;
3710 }
3711
3712 if let Some(region) = region {
3713 let mut range = region.range.to_offset(&buffer);
3714 if selection.start == range.start && range.start >= region.pair.start.len() {
3715 range.start -= region.pair.start.len();
3716 if buffer.contains_str_at(range.start, ®ion.pair.start)
3717 && buffer.contains_str_at(range.end, ®ion.pair.end)
3718 {
3719 range.end += region.pair.end.len();
3720 selection.start = range.start;
3721 selection.end = range.end;
3722
3723 return selection;
3724 }
3725 }
3726 }
3727
3728 let always_treat_brackets_as_autoclosed = buffer
3729 .language_settings_at(selection.start, cx)
3730 .always_treat_brackets_as_autoclosed;
3731
3732 if !always_treat_brackets_as_autoclosed {
3733 return selection;
3734 }
3735
3736 if let Some(scope) = buffer.language_scope_at(selection.start) {
3737 for (pair, enabled) in scope.brackets() {
3738 if !enabled || !pair.close {
3739 continue;
3740 }
3741
3742 if buffer.contains_str_at(selection.start, &pair.end) {
3743 let pair_start_len = pair.start.len();
3744 if buffer.contains_str_at(
3745 selection.start.saturating_sub(pair_start_len),
3746 &pair.start,
3747 ) {
3748 selection.start -= pair_start_len;
3749 selection.end += pair.end.len();
3750
3751 return selection;
3752 }
3753 }
3754 }
3755 }
3756
3757 selection
3758 })
3759 .collect();
3760
3761 drop(buffer);
3762 self.change_selections(None, window, cx, |selections| {
3763 selections.select(new_selections)
3764 });
3765 }
3766
3767 /// Iterate the given selections, and for each one, find the smallest surrounding
3768 /// autoclose region. This uses the ordering of the selections and the autoclose
3769 /// regions to avoid repeated comparisons.
3770 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3771 &'a self,
3772 selections: impl IntoIterator<Item = Selection<D>>,
3773 buffer: &'a MultiBufferSnapshot,
3774 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3775 let mut i = 0;
3776 let mut regions = self.autoclose_regions.as_slice();
3777 selections.into_iter().map(move |selection| {
3778 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3779
3780 let mut enclosing = None;
3781 while let Some(pair_state) = regions.get(i) {
3782 if pair_state.range.end.to_offset(buffer) < range.start {
3783 regions = ®ions[i + 1..];
3784 i = 0;
3785 } else if pair_state.range.start.to_offset(buffer) > range.end {
3786 break;
3787 } else {
3788 if pair_state.selection_id == selection.id {
3789 enclosing = Some(pair_state);
3790 }
3791 i += 1;
3792 }
3793 }
3794
3795 (selection, enclosing)
3796 })
3797 }
3798
3799 /// Remove any autoclose regions that no longer contain their selection.
3800 fn invalidate_autoclose_regions(
3801 &mut self,
3802 mut selections: &[Selection<Anchor>],
3803 buffer: &MultiBufferSnapshot,
3804 ) {
3805 self.autoclose_regions.retain(|state| {
3806 let mut i = 0;
3807 while let Some(selection) = selections.get(i) {
3808 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3809 selections = &selections[1..];
3810 continue;
3811 }
3812 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3813 break;
3814 }
3815 if selection.id == state.selection_id {
3816 return true;
3817 } else {
3818 i += 1;
3819 }
3820 }
3821 false
3822 });
3823 }
3824
3825 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3826 let offset = position.to_offset(buffer);
3827 let (word_range, kind) = buffer.surrounding_word(offset, true);
3828 if offset > word_range.start && kind == Some(CharKind::Word) {
3829 Some(
3830 buffer
3831 .text_for_range(word_range.start..offset)
3832 .collect::<String>(),
3833 )
3834 } else {
3835 None
3836 }
3837 }
3838
3839 pub fn toggle_inlay_hints(
3840 &mut self,
3841 _: &ToggleInlayHints,
3842 _: &mut Window,
3843 cx: &mut Context<Self>,
3844 ) {
3845 self.refresh_inlay_hints(
3846 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3847 cx,
3848 );
3849 }
3850
3851 pub fn inlay_hints_enabled(&self) -> bool {
3852 self.inlay_hint_cache.enabled
3853 }
3854
3855 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3856 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3857 return;
3858 }
3859
3860 let reason_description = reason.description();
3861 let ignore_debounce = matches!(
3862 reason,
3863 InlayHintRefreshReason::SettingsChange(_)
3864 | InlayHintRefreshReason::Toggle(_)
3865 | InlayHintRefreshReason::ExcerptsRemoved(_)
3866 | InlayHintRefreshReason::ModifiersChanged(_)
3867 );
3868 let (invalidate_cache, required_languages) = match reason {
3869 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3870 match self.inlay_hint_cache.modifiers_override(enabled) {
3871 Some(enabled) => {
3872 if enabled {
3873 (InvalidationStrategy::RefreshRequested, None)
3874 } else {
3875 self.splice_inlays(
3876 &self
3877 .visible_inlay_hints(cx)
3878 .iter()
3879 .map(|inlay| inlay.id)
3880 .collect::<Vec<InlayId>>(),
3881 Vec::new(),
3882 cx,
3883 );
3884 return;
3885 }
3886 }
3887 None => return,
3888 }
3889 }
3890 InlayHintRefreshReason::Toggle(enabled) => {
3891 if self.inlay_hint_cache.toggle(enabled) {
3892 if enabled {
3893 (InvalidationStrategy::RefreshRequested, None)
3894 } else {
3895 self.splice_inlays(
3896 &self
3897 .visible_inlay_hints(cx)
3898 .iter()
3899 .map(|inlay| inlay.id)
3900 .collect::<Vec<InlayId>>(),
3901 Vec::new(),
3902 cx,
3903 );
3904 return;
3905 }
3906 } else {
3907 return;
3908 }
3909 }
3910 InlayHintRefreshReason::SettingsChange(new_settings) => {
3911 match self.inlay_hint_cache.update_settings(
3912 &self.buffer,
3913 new_settings,
3914 self.visible_inlay_hints(cx),
3915 cx,
3916 ) {
3917 ControlFlow::Break(Some(InlaySplice {
3918 to_remove,
3919 to_insert,
3920 })) => {
3921 self.splice_inlays(&to_remove, to_insert, cx);
3922 return;
3923 }
3924 ControlFlow::Break(None) => return,
3925 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3926 }
3927 }
3928 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3929 if let Some(InlaySplice {
3930 to_remove,
3931 to_insert,
3932 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3933 {
3934 self.splice_inlays(&to_remove, to_insert, cx);
3935 }
3936 return;
3937 }
3938 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3939 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3940 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3941 }
3942 InlayHintRefreshReason::RefreshRequested => {
3943 (InvalidationStrategy::RefreshRequested, None)
3944 }
3945 };
3946
3947 if let Some(InlaySplice {
3948 to_remove,
3949 to_insert,
3950 }) = self.inlay_hint_cache.spawn_hint_refresh(
3951 reason_description,
3952 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3953 invalidate_cache,
3954 ignore_debounce,
3955 cx,
3956 ) {
3957 self.splice_inlays(&to_remove, to_insert, cx);
3958 }
3959 }
3960
3961 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3962 self.display_map
3963 .read(cx)
3964 .current_inlays()
3965 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3966 .cloned()
3967 .collect()
3968 }
3969
3970 pub fn excerpts_for_inlay_hints_query(
3971 &self,
3972 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3973 cx: &mut Context<Editor>,
3974 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3975 let Some(project) = self.project.as_ref() else {
3976 return HashMap::default();
3977 };
3978 let project = project.read(cx);
3979 let multi_buffer = self.buffer().read(cx);
3980 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3981 let multi_buffer_visible_start = self
3982 .scroll_manager
3983 .anchor()
3984 .anchor
3985 .to_point(&multi_buffer_snapshot);
3986 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3987 multi_buffer_visible_start
3988 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3989 Bias::Left,
3990 );
3991 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3992 multi_buffer_snapshot
3993 .range_to_buffer_ranges(multi_buffer_visible_range)
3994 .into_iter()
3995 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3996 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3997 let buffer_file = project::File::from_dyn(buffer.file())?;
3998 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3999 let worktree_entry = buffer_worktree
4000 .read(cx)
4001 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4002 if worktree_entry.is_ignored {
4003 return None;
4004 }
4005
4006 let language = buffer.language()?;
4007 if let Some(restrict_to_languages) = restrict_to_languages {
4008 if !restrict_to_languages.contains(language) {
4009 return None;
4010 }
4011 }
4012 Some((
4013 excerpt_id,
4014 (
4015 multi_buffer.buffer(buffer.remote_id()).unwrap(),
4016 buffer.version().clone(),
4017 excerpt_visible_range,
4018 ),
4019 ))
4020 })
4021 .collect()
4022 }
4023
4024 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
4025 TextLayoutDetails {
4026 text_system: window.text_system().clone(),
4027 editor_style: self.style.clone().unwrap(),
4028 rem_size: window.rem_size(),
4029 scroll_anchor: self.scroll_manager.anchor(),
4030 visible_rows: self.visible_line_count(),
4031 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4032 }
4033 }
4034
4035 pub fn splice_inlays(
4036 &self,
4037 to_remove: &[InlayId],
4038 to_insert: Vec<Inlay>,
4039 cx: &mut Context<Self>,
4040 ) {
4041 self.display_map.update(cx, |display_map, cx| {
4042 display_map.splice_inlays(to_remove, to_insert, cx)
4043 });
4044 cx.notify();
4045 }
4046
4047 fn trigger_on_type_formatting(
4048 &self,
4049 input: String,
4050 window: &mut Window,
4051 cx: &mut Context<Self>,
4052 ) -> Option<Task<Result<()>>> {
4053 if input.len() != 1 {
4054 return None;
4055 }
4056
4057 let project = self.project.as_ref()?;
4058 let position = self.selections.newest_anchor().head();
4059 let (buffer, buffer_position) = self
4060 .buffer
4061 .read(cx)
4062 .text_anchor_for_position(position, cx)?;
4063
4064 let settings = language_settings::language_settings(
4065 buffer
4066 .read(cx)
4067 .language_at(buffer_position)
4068 .map(|l| l.name()),
4069 buffer.read(cx).file(),
4070 cx,
4071 );
4072 if !settings.use_on_type_format {
4073 return None;
4074 }
4075
4076 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4077 // hence we do LSP request & edit on host side only — add formats to host's history.
4078 let push_to_lsp_host_history = true;
4079 // If this is not the host, append its history with new edits.
4080 let push_to_client_history = project.read(cx).is_via_collab();
4081
4082 let on_type_formatting = project.update(cx, |project, cx| {
4083 project.on_type_format(
4084 buffer.clone(),
4085 buffer_position,
4086 input,
4087 push_to_lsp_host_history,
4088 cx,
4089 )
4090 });
4091 Some(cx.spawn_in(window, async move |editor, cx| {
4092 if let Some(transaction) = on_type_formatting.await? {
4093 if push_to_client_history {
4094 buffer
4095 .update(cx, |buffer, _| {
4096 buffer.push_transaction(transaction, Instant::now());
4097 })
4098 .ok();
4099 }
4100 editor.update(cx, |editor, cx| {
4101 editor.refresh_document_highlights(cx);
4102 })?;
4103 }
4104 Ok(())
4105 }))
4106 }
4107
4108 pub fn show_word_completions(
4109 &mut self,
4110 _: &ShowWordCompletions,
4111 window: &mut Window,
4112 cx: &mut Context<Self>,
4113 ) {
4114 self.open_completions_menu(true, None, window, cx);
4115 }
4116
4117 pub fn show_completions(
4118 &mut self,
4119 options: &ShowCompletions,
4120 window: &mut Window,
4121 cx: &mut Context<Self>,
4122 ) {
4123 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4124 }
4125
4126 fn open_completions_menu(
4127 &mut self,
4128 ignore_completion_provider: bool,
4129 trigger: Option<&str>,
4130 window: &mut Window,
4131 cx: &mut Context<Self>,
4132 ) {
4133 if self.pending_rename.is_some() {
4134 return;
4135 }
4136 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4137 return;
4138 }
4139
4140 let position = self.selections.newest_anchor().head();
4141 if position.diff_base_anchor.is_some() {
4142 return;
4143 }
4144 let (buffer, buffer_position) =
4145 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4146 output
4147 } else {
4148 return;
4149 };
4150 let buffer_snapshot = buffer.read(cx).snapshot();
4151 let show_completion_documentation = buffer_snapshot
4152 .settings_at(buffer_position, cx)
4153 .show_completion_documentation;
4154
4155 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4156
4157 let trigger_kind = match trigger {
4158 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4159 CompletionTriggerKind::TRIGGER_CHARACTER
4160 }
4161 _ => CompletionTriggerKind::INVOKED,
4162 };
4163 let completion_context = CompletionContext {
4164 trigger_character: trigger.and_then(|trigger| {
4165 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4166 Some(String::from(trigger))
4167 } else {
4168 None
4169 }
4170 }),
4171 trigger_kind,
4172 };
4173
4174 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4175 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4176 let word_to_exclude = buffer_snapshot
4177 .text_for_range(old_range.clone())
4178 .collect::<String>();
4179 (
4180 buffer_snapshot.anchor_before(old_range.start)
4181 ..buffer_snapshot.anchor_after(old_range.end),
4182 Some(word_to_exclude),
4183 )
4184 } else {
4185 (buffer_position..buffer_position, None)
4186 };
4187
4188 let completion_settings = language_settings(
4189 buffer_snapshot
4190 .language_at(buffer_position)
4191 .map(|language| language.name()),
4192 buffer_snapshot.file(),
4193 cx,
4194 )
4195 .completions;
4196
4197 // The document can be large, so stay in reasonable bounds when searching for words,
4198 // otherwise completion pop-up might be slow to appear.
4199 const WORD_LOOKUP_ROWS: u32 = 5_000;
4200 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4201 let min_word_search = buffer_snapshot.clip_point(
4202 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4203 Bias::Left,
4204 );
4205 let max_word_search = buffer_snapshot.clip_point(
4206 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4207 Bias::Right,
4208 );
4209 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4210 ..buffer_snapshot.point_to_offset(max_word_search);
4211
4212 let provider = self
4213 .completion_provider
4214 .as_ref()
4215 .filter(|_| !ignore_completion_provider);
4216 let skip_digits = query
4217 .as_ref()
4218 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4219
4220 let (mut words, provided_completions) = match provider {
4221 Some(provider) => {
4222 let completions =
4223 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4224
4225 let words = match completion_settings.words {
4226 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4227 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4228 .background_spawn(async move {
4229 buffer_snapshot.words_in_range(WordsQuery {
4230 fuzzy_contents: None,
4231 range: word_search_range,
4232 skip_digits,
4233 })
4234 }),
4235 };
4236
4237 (words, completions)
4238 }
4239 None => (
4240 cx.background_spawn(async move {
4241 buffer_snapshot.words_in_range(WordsQuery {
4242 fuzzy_contents: None,
4243 range: word_search_range,
4244 skip_digits,
4245 })
4246 }),
4247 Task::ready(Ok(None)),
4248 ),
4249 };
4250
4251 let sort_completions = provider
4252 .as_ref()
4253 .map_or(true, |provider| provider.sort_completions());
4254
4255 let id = post_inc(&mut self.next_completion_id);
4256 let task = cx.spawn_in(window, async move |editor, cx| {
4257 async move {
4258 editor.update(cx, |this, _| {
4259 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4260 })?;
4261
4262 let mut completions = Vec::new();
4263 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4264 completions.extend(provided_completions);
4265 if completion_settings.words == WordsCompletionMode::Fallback {
4266 words = Task::ready(HashMap::default());
4267 }
4268 }
4269
4270 let mut words = words.await;
4271 if let Some(word_to_exclude) = &word_to_exclude {
4272 words.remove(word_to_exclude);
4273 }
4274 for lsp_completion in &completions {
4275 words.remove(&lsp_completion.new_text);
4276 }
4277 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4278 old_range: old_range.clone(),
4279 new_text: word.clone(),
4280 label: CodeLabel::plain(word, None),
4281 documentation: None,
4282 source: CompletionSource::BufferWord {
4283 word_range,
4284 resolved: false,
4285 },
4286 confirm: None,
4287 }));
4288
4289 let menu = if completions.is_empty() {
4290 None
4291 } else {
4292 let mut menu = CompletionsMenu::new(
4293 id,
4294 sort_completions,
4295 show_completion_documentation,
4296 ignore_completion_provider,
4297 position,
4298 buffer.clone(),
4299 completions.into(),
4300 );
4301
4302 menu.filter(query.as_deref(), cx.background_executor().clone())
4303 .await;
4304
4305 menu.visible().then_some(menu)
4306 };
4307
4308 editor.update_in(cx, |editor, window, cx| {
4309 match editor.context_menu.borrow().as_ref() {
4310 None => {}
4311 Some(CodeContextMenu::Completions(prev_menu)) => {
4312 if prev_menu.id > id {
4313 return;
4314 }
4315 }
4316 _ => return,
4317 }
4318
4319 if editor.focus_handle.is_focused(window) && menu.is_some() {
4320 let mut menu = menu.unwrap();
4321 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4322
4323 *editor.context_menu.borrow_mut() =
4324 Some(CodeContextMenu::Completions(menu));
4325
4326 if editor.show_edit_predictions_in_menu() {
4327 editor.update_visible_inline_completion(window, cx);
4328 } else {
4329 editor.discard_inline_completion(false, cx);
4330 }
4331
4332 cx.notify();
4333 } else if editor.completion_tasks.len() <= 1 {
4334 // If there are no more completion tasks and the last menu was
4335 // empty, we should hide it.
4336 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4337 // If it was already hidden and we don't show inline
4338 // completions in the menu, we should also show the
4339 // inline-completion when available.
4340 if was_hidden && editor.show_edit_predictions_in_menu() {
4341 editor.update_visible_inline_completion(window, cx);
4342 }
4343 }
4344 })?;
4345
4346 anyhow::Ok(())
4347 }
4348 .log_err()
4349 .await
4350 });
4351
4352 self.completion_tasks.push((id, task));
4353 }
4354
4355 pub fn confirm_completion(
4356 &mut self,
4357 action: &ConfirmCompletion,
4358 window: &mut Window,
4359 cx: &mut Context<Self>,
4360 ) -> Option<Task<Result<()>>> {
4361 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4362 }
4363
4364 pub fn compose_completion(
4365 &mut self,
4366 action: &ComposeCompletion,
4367 window: &mut Window,
4368 cx: &mut Context<Self>,
4369 ) -> Option<Task<Result<()>>> {
4370 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4371 }
4372
4373 fn do_completion(
4374 &mut self,
4375 item_ix: Option<usize>,
4376 intent: CompletionIntent,
4377 window: &mut Window,
4378 cx: &mut Context<Editor>,
4379 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4380 use language::ToOffset as _;
4381
4382 let completions_menu =
4383 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4384 menu
4385 } else {
4386 return None;
4387 };
4388
4389 let entries = completions_menu.entries.borrow();
4390 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4391 if self.show_edit_predictions_in_menu() {
4392 self.discard_inline_completion(true, cx);
4393 }
4394 let candidate_id = mat.candidate_id;
4395 drop(entries);
4396
4397 let buffer_handle = completions_menu.buffer;
4398 let completion = completions_menu
4399 .completions
4400 .borrow()
4401 .get(candidate_id)?
4402 .clone();
4403 cx.stop_propagation();
4404
4405 let snippet;
4406 let text;
4407
4408 if completion.is_snippet() {
4409 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4410 text = snippet.as_ref().unwrap().text.clone();
4411 } else {
4412 snippet = None;
4413 text = completion.new_text.clone();
4414 };
4415 let selections = self.selections.all::<usize>(cx);
4416 let buffer = buffer_handle.read(cx);
4417 let old_range = completion.old_range.to_offset(buffer);
4418 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4419
4420 let newest_selection = self.selections.newest_anchor();
4421 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4422 return None;
4423 }
4424
4425 let lookbehind = newest_selection
4426 .start
4427 .text_anchor
4428 .to_offset(buffer)
4429 .saturating_sub(old_range.start);
4430 let lookahead = old_range
4431 .end
4432 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4433 let mut common_prefix_len = old_text
4434 .bytes()
4435 .zip(text.bytes())
4436 .take_while(|(a, b)| a == b)
4437 .count();
4438
4439 let snapshot = self.buffer.read(cx).snapshot(cx);
4440 let mut range_to_replace: Option<Range<isize>> = None;
4441 let mut ranges = Vec::new();
4442 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4443 for selection in &selections {
4444 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4445 let start = selection.start.saturating_sub(lookbehind);
4446 let end = selection.end + lookahead;
4447 if selection.id == newest_selection.id {
4448 range_to_replace = Some(
4449 ((start + common_prefix_len) as isize - selection.start as isize)
4450 ..(end as isize - selection.start as isize),
4451 );
4452 }
4453 ranges.push(start + common_prefix_len..end);
4454 } else {
4455 common_prefix_len = 0;
4456 ranges.clear();
4457 ranges.extend(selections.iter().map(|s| {
4458 if s.id == newest_selection.id {
4459 range_to_replace = Some(
4460 old_range.start.to_offset_utf16(&snapshot).0 as isize
4461 - selection.start as isize
4462 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4463 - selection.start as isize,
4464 );
4465 old_range.clone()
4466 } else {
4467 s.start..s.end
4468 }
4469 }));
4470 break;
4471 }
4472 if !self.linked_edit_ranges.is_empty() {
4473 let start_anchor = snapshot.anchor_before(selection.head());
4474 let end_anchor = snapshot.anchor_after(selection.tail());
4475 if let Some(ranges) = self
4476 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4477 {
4478 for (buffer, edits) in ranges {
4479 linked_edits.entry(buffer.clone()).or_default().extend(
4480 edits
4481 .into_iter()
4482 .map(|range| (range, text[common_prefix_len..].to_owned())),
4483 );
4484 }
4485 }
4486 }
4487 }
4488 let text = &text[common_prefix_len..];
4489
4490 cx.emit(EditorEvent::InputHandled {
4491 utf16_range_to_replace: range_to_replace,
4492 text: text.into(),
4493 });
4494
4495 self.transact(window, cx, |this, window, cx| {
4496 if let Some(mut snippet) = snippet {
4497 snippet.text = text.to_string();
4498 for tabstop in snippet
4499 .tabstops
4500 .iter_mut()
4501 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4502 {
4503 tabstop.start -= common_prefix_len as isize;
4504 tabstop.end -= common_prefix_len as isize;
4505 }
4506
4507 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4508 } else {
4509 this.buffer.update(cx, |buffer, cx| {
4510 buffer.edit(
4511 ranges.iter().map(|range| (range.clone(), text)),
4512 this.autoindent_mode.clone(),
4513 cx,
4514 );
4515 });
4516 }
4517 for (buffer, edits) in linked_edits {
4518 buffer.update(cx, |buffer, cx| {
4519 let snapshot = buffer.snapshot();
4520 let edits = edits
4521 .into_iter()
4522 .map(|(range, text)| {
4523 use text::ToPoint as TP;
4524 let end_point = TP::to_point(&range.end, &snapshot);
4525 let start_point = TP::to_point(&range.start, &snapshot);
4526 (start_point..end_point, text)
4527 })
4528 .sorted_by_key(|(range, _)| range.start)
4529 .collect::<Vec<_>>();
4530 buffer.edit(edits, None, cx);
4531 })
4532 }
4533
4534 this.refresh_inline_completion(true, false, window, cx);
4535 });
4536
4537 let show_new_completions_on_confirm = completion
4538 .confirm
4539 .as_ref()
4540 .map_or(false, |confirm| confirm(intent, window, cx));
4541 if show_new_completions_on_confirm {
4542 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4543 }
4544
4545 let provider = self.completion_provider.as_ref()?;
4546 drop(completion);
4547 let apply_edits = provider.apply_additional_edits_for_completion(
4548 buffer_handle,
4549 completions_menu.completions.clone(),
4550 candidate_id,
4551 true,
4552 cx,
4553 );
4554
4555 let editor_settings = EditorSettings::get_global(cx);
4556 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4557 // After the code completion is finished, users often want to know what signatures are needed.
4558 // so we should automatically call signature_help
4559 self.show_signature_help(&ShowSignatureHelp, window, cx);
4560 }
4561
4562 Some(cx.foreground_executor().spawn(async move {
4563 apply_edits.await?;
4564 Ok(())
4565 }))
4566 }
4567
4568 pub fn toggle_code_actions(
4569 &mut self,
4570 action: &ToggleCodeActions,
4571 window: &mut Window,
4572 cx: &mut Context<Self>,
4573 ) {
4574 let mut context_menu = self.context_menu.borrow_mut();
4575 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4576 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4577 // Toggle if we're selecting the same one
4578 *context_menu = None;
4579 cx.notify();
4580 return;
4581 } else {
4582 // Otherwise, clear it and start a new one
4583 *context_menu = None;
4584 cx.notify();
4585 }
4586 }
4587 drop(context_menu);
4588 let snapshot = self.snapshot(window, cx);
4589 let deployed_from_indicator = action.deployed_from_indicator;
4590 let mut task = self.code_actions_task.take();
4591 let action = action.clone();
4592 cx.spawn_in(window, async move |editor, cx| {
4593 while let Some(prev_task) = task {
4594 prev_task.await.log_err();
4595 task = editor.update(cx, |this, _| this.code_actions_task.take())?;
4596 }
4597
4598 let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
4599 if editor.focus_handle.is_focused(window) {
4600 let multibuffer_point = action
4601 .deployed_from_indicator
4602 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4603 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4604 let (buffer, buffer_row) = snapshot
4605 .buffer_snapshot
4606 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4607 .and_then(|(buffer_snapshot, range)| {
4608 editor
4609 .buffer
4610 .read(cx)
4611 .buffer(buffer_snapshot.remote_id())
4612 .map(|buffer| (buffer, range.start.row))
4613 })?;
4614 let (_, code_actions) = editor
4615 .available_code_actions
4616 .clone()
4617 .and_then(|(location, code_actions)| {
4618 let snapshot = location.buffer.read(cx).snapshot();
4619 let point_range = location.range.to_point(&snapshot);
4620 let point_range = point_range.start.row..=point_range.end.row;
4621 if point_range.contains(&buffer_row) {
4622 Some((location, code_actions))
4623 } else {
4624 None
4625 }
4626 })
4627 .unzip();
4628 let buffer_id = buffer.read(cx).remote_id();
4629 let tasks = editor
4630 .tasks
4631 .get(&(buffer_id, buffer_row))
4632 .map(|t| Arc::new(t.to_owned()));
4633 if tasks.is_none() && code_actions.is_none() {
4634 return None;
4635 }
4636
4637 editor.completion_tasks.clear();
4638 editor.discard_inline_completion(false, cx);
4639 let task_context =
4640 tasks
4641 .as_ref()
4642 .zip(editor.project.clone())
4643 .map(|(tasks, project)| {
4644 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4645 });
4646
4647 Some(cx.spawn_in(window, async move |editor, cx| {
4648 let task_context = match task_context {
4649 Some(task_context) => task_context.await,
4650 None => None,
4651 };
4652 let resolved_tasks =
4653 tasks.zip(task_context).map(|(tasks, task_context)| {
4654 Rc::new(ResolvedTasks {
4655 templates: tasks.resolve(&task_context).collect(),
4656 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4657 multibuffer_point.row,
4658 tasks.column,
4659 )),
4660 })
4661 });
4662 let spawn_straight_away = resolved_tasks
4663 .as_ref()
4664 .map_or(false, |tasks| tasks.templates.len() == 1)
4665 && code_actions
4666 .as_ref()
4667 .map_or(true, |actions| actions.is_empty());
4668 if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
4669 *editor.context_menu.borrow_mut() =
4670 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4671 buffer,
4672 actions: CodeActionContents {
4673 tasks: resolved_tasks,
4674 actions: code_actions,
4675 },
4676 selected_item: Default::default(),
4677 scroll_handle: UniformListScrollHandle::default(),
4678 deployed_from_indicator,
4679 }));
4680 if spawn_straight_away {
4681 if let Some(task) = editor.confirm_code_action(
4682 &ConfirmCodeAction { item_ix: Some(0) },
4683 window,
4684 cx,
4685 ) {
4686 cx.notify();
4687 return task;
4688 }
4689 }
4690 cx.notify();
4691 Task::ready(Ok(()))
4692 }) {
4693 task.await
4694 } else {
4695 Ok(())
4696 }
4697 }))
4698 } else {
4699 Some(Task::ready(Ok(())))
4700 }
4701 })?;
4702 if let Some(task) = spawned_test_task {
4703 task.await?;
4704 }
4705
4706 Ok::<_, anyhow::Error>(())
4707 })
4708 .detach_and_log_err(cx);
4709 }
4710
4711 pub fn confirm_code_action(
4712 &mut self,
4713 action: &ConfirmCodeAction,
4714 window: &mut Window,
4715 cx: &mut Context<Self>,
4716 ) -> Option<Task<Result<()>>> {
4717 let actions_menu =
4718 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4719 menu
4720 } else {
4721 return None;
4722 };
4723 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4724 let action = actions_menu.actions.get(action_ix)?;
4725 let title = action.label();
4726 let buffer = actions_menu.buffer;
4727 let workspace = self.workspace()?;
4728
4729 match action {
4730 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4731 workspace.update(cx, |workspace, cx| {
4732 workspace::tasks::schedule_resolved_task(
4733 workspace,
4734 task_source_kind,
4735 resolved_task,
4736 false,
4737 cx,
4738 );
4739
4740 Some(Task::ready(Ok(())))
4741 })
4742 }
4743 CodeActionsItem::CodeAction {
4744 excerpt_id,
4745 action,
4746 provider,
4747 } => {
4748 let apply_code_action =
4749 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4750 let workspace = workspace.downgrade();
4751 Some(cx.spawn_in(window, async move |editor, cx| {
4752 let project_transaction = apply_code_action.await?;
4753 Self::open_project_transaction(
4754 &editor,
4755 workspace,
4756 project_transaction,
4757 title,
4758 cx,
4759 )
4760 .await
4761 }))
4762 }
4763 }
4764 }
4765
4766 pub async fn open_project_transaction(
4767 this: &WeakEntity<Editor>,
4768 workspace: WeakEntity<Workspace>,
4769 transaction: ProjectTransaction,
4770 title: String,
4771 cx: &mut AsyncWindowContext,
4772 ) -> Result<()> {
4773 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4774 cx.update(|_, cx| {
4775 entries.sort_unstable_by_key(|(buffer, _)| {
4776 buffer.read(cx).file().map(|f| f.path().clone())
4777 });
4778 })?;
4779
4780 // If the project transaction's edits are all contained within this editor, then
4781 // avoid opening a new editor to display them.
4782
4783 if let Some((buffer, transaction)) = entries.first() {
4784 if entries.len() == 1 {
4785 let excerpt = this.update(cx, |editor, cx| {
4786 editor
4787 .buffer()
4788 .read(cx)
4789 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4790 })?;
4791 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4792 if excerpted_buffer == *buffer {
4793 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
4794 let excerpt_range = excerpt_range.to_offset(buffer);
4795 buffer
4796 .edited_ranges_for_transaction::<usize>(transaction)
4797 .all(|range| {
4798 excerpt_range.start <= range.start
4799 && excerpt_range.end >= range.end
4800 })
4801 })?;
4802
4803 if all_edits_within_excerpt {
4804 return Ok(());
4805 }
4806 }
4807 }
4808 }
4809 } else {
4810 return Ok(());
4811 }
4812
4813 let mut ranges_to_highlight = Vec::new();
4814 let excerpt_buffer = cx.new(|cx| {
4815 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4816 for (buffer_handle, transaction) in &entries {
4817 let buffer = buffer_handle.read(cx);
4818 ranges_to_highlight.extend(
4819 multibuffer.push_excerpts_with_context_lines(
4820 buffer_handle.clone(),
4821 buffer
4822 .edited_ranges_for_transaction::<usize>(transaction)
4823 .collect(),
4824 DEFAULT_MULTIBUFFER_CONTEXT,
4825 cx,
4826 ),
4827 );
4828 }
4829 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4830 multibuffer
4831 })?;
4832
4833 workspace.update_in(cx, |workspace, window, cx| {
4834 let project = workspace.project().clone();
4835 let editor =
4836 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4837 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4838 editor.update(cx, |editor, cx| {
4839 editor.highlight_background::<Self>(
4840 &ranges_to_highlight,
4841 |theme| theme.editor_highlighted_line_background,
4842 cx,
4843 );
4844 });
4845 })?;
4846
4847 Ok(())
4848 }
4849
4850 pub fn clear_code_action_providers(&mut self) {
4851 self.code_action_providers.clear();
4852 self.available_code_actions.take();
4853 }
4854
4855 pub fn add_code_action_provider(
4856 &mut self,
4857 provider: Rc<dyn CodeActionProvider>,
4858 window: &mut Window,
4859 cx: &mut Context<Self>,
4860 ) {
4861 if self
4862 .code_action_providers
4863 .iter()
4864 .any(|existing_provider| existing_provider.id() == provider.id())
4865 {
4866 return;
4867 }
4868
4869 self.code_action_providers.push(provider);
4870 self.refresh_code_actions(window, cx);
4871 }
4872
4873 pub fn remove_code_action_provider(
4874 &mut self,
4875 id: Arc<str>,
4876 window: &mut Window,
4877 cx: &mut Context<Self>,
4878 ) {
4879 self.code_action_providers
4880 .retain(|provider| provider.id() != id);
4881 self.refresh_code_actions(window, cx);
4882 }
4883
4884 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4885 let buffer = self.buffer.read(cx);
4886 let newest_selection = self.selections.newest_anchor().clone();
4887 if newest_selection.head().diff_base_anchor.is_some() {
4888 return None;
4889 }
4890 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4891 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4892 if start_buffer != end_buffer {
4893 return None;
4894 }
4895
4896 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
4897 cx.background_executor()
4898 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4899 .await;
4900
4901 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
4902 let providers = this.code_action_providers.clone();
4903 let tasks = this
4904 .code_action_providers
4905 .iter()
4906 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4907 .collect::<Vec<_>>();
4908 (providers, tasks)
4909 })?;
4910
4911 let mut actions = Vec::new();
4912 for (provider, provider_actions) in
4913 providers.into_iter().zip(future::join_all(tasks).await)
4914 {
4915 if let Some(provider_actions) = provider_actions.log_err() {
4916 actions.extend(provider_actions.into_iter().map(|action| {
4917 AvailableCodeAction {
4918 excerpt_id: newest_selection.start.excerpt_id,
4919 action,
4920 provider: provider.clone(),
4921 }
4922 }));
4923 }
4924 }
4925
4926 this.update(cx, |this, cx| {
4927 this.available_code_actions = if actions.is_empty() {
4928 None
4929 } else {
4930 Some((
4931 Location {
4932 buffer: start_buffer,
4933 range: start..end,
4934 },
4935 actions.into(),
4936 ))
4937 };
4938 cx.notify();
4939 })
4940 }));
4941 None
4942 }
4943
4944 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4945 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4946 self.show_git_blame_inline = false;
4947
4948 self.show_git_blame_inline_delay_task =
4949 Some(cx.spawn_in(window, async move |this, cx| {
4950 cx.background_executor().timer(delay).await;
4951
4952 this.update(cx, |this, cx| {
4953 this.show_git_blame_inline = true;
4954 cx.notify();
4955 })
4956 .log_err();
4957 }));
4958 }
4959 }
4960
4961 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4962 if self.pending_rename.is_some() {
4963 return None;
4964 }
4965
4966 let provider = self.semantics_provider.clone()?;
4967 let buffer = self.buffer.read(cx);
4968 let newest_selection = self.selections.newest_anchor().clone();
4969 let cursor_position = newest_selection.head();
4970 let (cursor_buffer, cursor_buffer_position) =
4971 buffer.text_anchor_for_position(cursor_position, cx)?;
4972 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4973 if cursor_buffer != tail_buffer {
4974 return None;
4975 }
4976 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4977 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
4978 cx.background_executor()
4979 .timer(Duration::from_millis(debounce))
4980 .await;
4981
4982 let highlights = if let Some(highlights) = cx
4983 .update(|cx| {
4984 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4985 })
4986 .ok()
4987 .flatten()
4988 {
4989 highlights.await.log_err()
4990 } else {
4991 None
4992 };
4993
4994 if let Some(highlights) = highlights {
4995 this.update(cx, |this, cx| {
4996 if this.pending_rename.is_some() {
4997 return;
4998 }
4999
5000 let buffer_id = cursor_position.buffer_id;
5001 let buffer = this.buffer.read(cx);
5002 if !buffer
5003 .text_anchor_for_position(cursor_position, cx)
5004 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
5005 {
5006 return;
5007 }
5008
5009 let cursor_buffer_snapshot = cursor_buffer.read(cx);
5010 let mut write_ranges = Vec::new();
5011 let mut read_ranges = Vec::new();
5012 for highlight in highlights {
5013 for (excerpt_id, excerpt_range) in
5014 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
5015 {
5016 let start = highlight
5017 .range
5018 .start
5019 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
5020 let end = highlight
5021 .range
5022 .end
5023 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
5024 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
5025 continue;
5026 }
5027
5028 let range = Anchor {
5029 buffer_id,
5030 excerpt_id,
5031 text_anchor: start,
5032 diff_base_anchor: None,
5033 }..Anchor {
5034 buffer_id,
5035 excerpt_id,
5036 text_anchor: end,
5037 diff_base_anchor: None,
5038 };
5039 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
5040 write_ranges.push(range);
5041 } else {
5042 read_ranges.push(range);
5043 }
5044 }
5045 }
5046
5047 this.highlight_background::<DocumentHighlightRead>(
5048 &read_ranges,
5049 |theme| theme.editor_document_highlight_read_background,
5050 cx,
5051 );
5052 this.highlight_background::<DocumentHighlightWrite>(
5053 &write_ranges,
5054 |theme| theme.editor_document_highlight_write_background,
5055 cx,
5056 );
5057 cx.notify();
5058 })
5059 .log_err();
5060 }
5061 }));
5062 None
5063 }
5064
5065 pub fn refresh_selected_text_highlights(
5066 &mut self,
5067 window: &mut Window,
5068 cx: &mut Context<Editor>,
5069 ) {
5070 if matches!(self.mode, EditorMode::SingleLine { .. }) {
5071 return;
5072 }
5073 self.selection_highlight_task.take();
5074 if !EditorSettings::get_global(cx).selection_highlight {
5075 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5076 return;
5077 }
5078 if self.selections.count() != 1 || self.selections.line_mode {
5079 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5080 return;
5081 }
5082 let selection = self.selections.newest::<Point>(cx);
5083 if selection.is_empty() || selection.start.row != selection.end.row {
5084 self.clear_background_highlights::<SelectedTextHighlight>(cx);
5085 return;
5086 }
5087 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
5088 self.selection_highlight_task = Some(cx.spawn_in(window, async move |editor, cx| {
5089 cx.background_executor()
5090 .timer(Duration::from_millis(debounce))
5091 .await;
5092 let Some(Some(matches_task)) = editor
5093 .update_in(cx, |editor, _, cx| {
5094 if editor.selections.count() != 1 || editor.selections.line_mode {
5095 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5096 return None;
5097 }
5098 let selection = editor.selections.newest::<Point>(cx);
5099 if selection.is_empty() || selection.start.row != selection.end.row {
5100 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5101 return None;
5102 }
5103 let buffer = editor.buffer().read(cx).snapshot(cx);
5104 let query = buffer.text_for_range(selection.range()).collect::<String>();
5105 if query.trim().is_empty() {
5106 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5107 return None;
5108 }
5109 Some(cx.background_spawn(async move {
5110 let mut ranges = Vec::new();
5111 let selection_anchors = selection.range().to_anchors(&buffer);
5112 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
5113 for (search_buffer, search_range, excerpt_id) in
5114 buffer.range_to_buffer_ranges(range)
5115 {
5116 ranges.extend(
5117 project::search::SearchQuery::text(
5118 query.clone(),
5119 false,
5120 false,
5121 false,
5122 Default::default(),
5123 Default::default(),
5124 None,
5125 )
5126 .unwrap()
5127 .search(search_buffer, Some(search_range.clone()))
5128 .await
5129 .into_iter()
5130 .filter_map(
5131 |match_range| {
5132 let start = search_buffer.anchor_after(
5133 search_range.start + match_range.start,
5134 );
5135 let end = search_buffer.anchor_before(
5136 search_range.start + match_range.end,
5137 );
5138 let range = Anchor::range_in_buffer(
5139 excerpt_id,
5140 search_buffer.remote_id(),
5141 start..end,
5142 );
5143 (range != selection_anchors).then_some(range)
5144 },
5145 ),
5146 );
5147 }
5148 }
5149 ranges
5150 }))
5151 })
5152 .log_err()
5153 else {
5154 return;
5155 };
5156 let matches = matches_task.await;
5157 editor
5158 .update_in(cx, |editor, _, cx| {
5159 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5160 if !matches.is_empty() {
5161 editor.highlight_background::<SelectedTextHighlight>(
5162 &matches,
5163 |theme| theme.editor_document_highlight_bracket_background,
5164 cx,
5165 )
5166 }
5167 })
5168 .log_err();
5169 }));
5170 }
5171
5172 pub fn refresh_inline_completion(
5173 &mut self,
5174 debounce: bool,
5175 user_requested: bool,
5176 window: &mut Window,
5177 cx: &mut Context<Self>,
5178 ) -> Option<()> {
5179 let provider = self.edit_prediction_provider()?;
5180 let cursor = self.selections.newest_anchor().head();
5181 let (buffer, cursor_buffer_position) =
5182 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5183
5184 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5185 self.discard_inline_completion(false, cx);
5186 return None;
5187 }
5188
5189 if !user_requested
5190 && (!self.should_show_edit_predictions()
5191 || !self.is_focused(window)
5192 || buffer.read(cx).is_empty())
5193 {
5194 self.discard_inline_completion(false, cx);
5195 return None;
5196 }
5197
5198 self.update_visible_inline_completion(window, cx);
5199 provider.refresh(
5200 self.project.clone(),
5201 buffer,
5202 cursor_buffer_position,
5203 debounce,
5204 cx,
5205 );
5206 Some(())
5207 }
5208
5209 fn show_edit_predictions_in_menu(&self) -> bool {
5210 match self.edit_prediction_settings {
5211 EditPredictionSettings::Disabled => false,
5212 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5213 }
5214 }
5215
5216 pub fn edit_predictions_enabled(&self) -> bool {
5217 match self.edit_prediction_settings {
5218 EditPredictionSettings::Disabled => false,
5219 EditPredictionSettings::Enabled { .. } => true,
5220 }
5221 }
5222
5223 fn edit_prediction_requires_modifier(&self) -> bool {
5224 match self.edit_prediction_settings {
5225 EditPredictionSettings::Disabled => false,
5226 EditPredictionSettings::Enabled {
5227 preview_requires_modifier,
5228 ..
5229 } => preview_requires_modifier,
5230 }
5231 }
5232
5233 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5234 if self.edit_prediction_provider.is_none() {
5235 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5236 } else {
5237 let selection = self.selections.newest_anchor();
5238 let cursor = selection.head();
5239
5240 if let Some((buffer, cursor_buffer_position)) =
5241 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5242 {
5243 self.edit_prediction_settings =
5244 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5245 }
5246 }
5247 }
5248
5249 fn edit_prediction_settings_at_position(
5250 &self,
5251 buffer: &Entity<Buffer>,
5252 buffer_position: language::Anchor,
5253 cx: &App,
5254 ) -> EditPredictionSettings {
5255 if self.mode != EditorMode::Full
5256 || !self.show_inline_completions_override.unwrap_or(true)
5257 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5258 {
5259 return EditPredictionSettings::Disabled;
5260 }
5261
5262 let buffer = buffer.read(cx);
5263
5264 let file = buffer.file();
5265
5266 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5267 return EditPredictionSettings::Disabled;
5268 };
5269
5270 let by_provider = matches!(
5271 self.menu_inline_completions_policy,
5272 MenuInlineCompletionsPolicy::ByProvider
5273 );
5274
5275 let show_in_menu = by_provider
5276 && self
5277 .edit_prediction_provider
5278 .as_ref()
5279 .map_or(false, |provider| {
5280 provider.provider.show_completions_in_menu()
5281 });
5282
5283 let preview_requires_modifier =
5284 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5285
5286 EditPredictionSettings::Enabled {
5287 show_in_menu,
5288 preview_requires_modifier,
5289 }
5290 }
5291
5292 fn should_show_edit_predictions(&self) -> bool {
5293 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5294 }
5295
5296 pub fn edit_prediction_preview_is_active(&self) -> bool {
5297 matches!(
5298 self.edit_prediction_preview,
5299 EditPredictionPreview::Active { .. }
5300 )
5301 }
5302
5303 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5304 let cursor = self.selections.newest_anchor().head();
5305 if let Some((buffer, cursor_position)) =
5306 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5307 {
5308 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5309 } else {
5310 false
5311 }
5312 }
5313
5314 fn edit_predictions_enabled_in_buffer(
5315 &self,
5316 buffer: &Entity<Buffer>,
5317 buffer_position: language::Anchor,
5318 cx: &App,
5319 ) -> bool {
5320 maybe!({
5321 if self.read_only(cx) {
5322 return Some(false);
5323 }
5324 let provider = self.edit_prediction_provider()?;
5325 if !provider.is_enabled(&buffer, buffer_position, cx) {
5326 return Some(false);
5327 }
5328 let buffer = buffer.read(cx);
5329 let Some(file) = buffer.file() else {
5330 return Some(true);
5331 };
5332 let settings = all_language_settings(Some(file), cx);
5333 Some(settings.edit_predictions_enabled_for_file(file, cx))
5334 })
5335 .unwrap_or(false)
5336 }
5337
5338 fn cycle_inline_completion(
5339 &mut self,
5340 direction: Direction,
5341 window: &mut Window,
5342 cx: &mut Context<Self>,
5343 ) -> Option<()> {
5344 let provider = self.edit_prediction_provider()?;
5345 let cursor = self.selections.newest_anchor().head();
5346 let (buffer, cursor_buffer_position) =
5347 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5348 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5349 return None;
5350 }
5351
5352 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5353 self.update_visible_inline_completion(window, cx);
5354
5355 Some(())
5356 }
5357
5358 pub fn show_inline_completion(
5359 &mut self,
5360 _: &ShowEditPrediction,
5361 window: &mut Window,
5362 cx: &mut Context<Self>,
5363 ) {
5364 if !self.has_active_inline_completion() {
5365 self.refresh_inline_completion(false, true, window, cx);
5366 return;
5367 }
5368
5369 self.update_visible_inline_completion(window, cx);
5370 }
5371
5372 pub fn display_cursor_names(
5373 &mut self,
5374 _: &DisplayCursorNames,
5375 window: &mut Window,
5376 cx: &mut Context<Self>,
5377 ) {
5378 self.show_cursor_names(window, cx);
5379 }
5380
5381 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5382 self.show_cursor_names = true;
5383 cx.notify();
5384 cx.spawn_in(window, async move |this, cx| {
5385 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5386 this.update(cx, |this, cx| {
5387 this.show_cursor_names = false;
5388 cx.notify()
5389 })
5390 .ok()
5391 })
5392 .detach();
5393 }
5394
5395 pub fn next_edit_prediction(
5396 &mut self,
5397 _: &NextEditPrediction,
5398 window: &mut Window,
5399 cx: &mut Context<Self>,
5400 ) {
5401 if self.has_active_inline_completion() {
5402 self.cycle_inline_completion(Direction::Next, window, cx);
5403 } else {
5404 let is_copilot_disabled = self
5405 .refresh_inline_completion(false, true, window, cx)
5406 .is_none();
5407 if is_copilot_disabled {
5408 cx.propagate();
5409 }
5410 }
5411 }
5412
5413 pub fn previous_edit_prediction(
5414 &mut self,
5415 _: &PreviousEditPrediction,
5416 window: &mut Window,
5417 cx: &mut Context<Self>,
5418 ) {
5419 if self.has_active_inline_completion() {
5420 self.cycle_inline_completion(Direction::Prev, window, cx);
5421 } else {
5422 let is_copilot_disabled = self
5423 .refresh_inline_completion(false, true, window, cx)
5424 .is_none();
5425 if is_copilot_disabled {
5426 cx.propagate();
5427 }
5428 }
5429 }
5430
5431 pub fn accept_edit_prediction(
5432 &mut self,
5433 _: &AcceptEditPrediction,
5434 window: &mut Window,
5435 cx: &mut Context<Self>,
5436 ) {
5437 if self.show_edit_predictions_in_menu() {
5438 self.hide_context_menu(window, cx);
5439 }
5440
5441 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5442 return;
5443 };
5444
5445 self.report_inline_completion_event(
5446 active_inline_completion.completion_id.clone(),
5447 true,
5448 cx,
5449 );
5450
5451 match &active_inline_completion.completion {
5452 InlineCompletion::Move { target, .. } => {
5453 let target = *target;
5454
5455 if let Some(position_map) = &self.last_position_map {
5456 if position_map
5457 .visible_row_range
5458 .contains(&target.to_display_point(&position_map.snapshot).row())
5459 || !self.edit_prediction_requires_modifier()
5460 {
5461 self.unfold_ranges(&[target..target], true, false, cx);
5462 // Note that this is also done in vim's handler of the Tab action.
5463 self.change_selections(
5464 Some(Autoscroll::newest()),
5465 window,
5466 cx,
5467 |selections| {
5468 selections.select_anchor_ranges([target..target]);
5469 },
5470 );
5471 self.clear_row_highlights::<EditPredictionPreview>();
5472
5473 self.edit_prediction_preview
5474 .set_previous_scroll_position(None);
5475 } else {
5476 self.edit_prediction_preview
5477 .set_previous_scroll_position(Some(
5478 position_map.snapshot.scroll_anchor,
5479 ));
5480
5481 self.highlight_rows::<EditPredictionPreview>(
5482 target..target,
5483 cx.theme().colors().editor_highlighted_line_background,
5484 true,
5485 cx,
5486 );
5487 self.request_autoscroll(Autoscroll::fit(), cx);
5488 }
5489 }
5490 }
5491 InlineCompletion::Edit { edits, .. } => {
5492 if let Some(provider) = self.edit_prediction_provider() {
5493 provider.accept(cx);
5494 }
5495
5496 let snapshot = self.buffer.read(cx).snapshot(cx);
5497 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5498
5499 self.buffer.update(cx, |buffer, cx| {
5500 buffer.edit(edits.iter().cloned(), None, cx)
5501 });
5502
5503 self.change_selections(None, window, cx, |s| {
5504 s.select_anchor_ranges([last_edit_end..last_edit_end])
5505 });
5506
5507 self.update_visible_inline_completion(window, cx);
5508 if self.active_inline_completion.is_none() {
5509 self.refresh_inline_completion(true, true, window, cx);
5510 }
5511
5512 cx.notify();
5513 }
5514 }
5515
5516 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5517 }
5518
5519 pub fn accept_partial_inline_completion(
5520 &mut self,
5521 _: &AcceptPartialEditPrediction,
5522 window: &mut Window,
5523 cx: &mut Context<Self>,
5524 ) {
5525 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5526 return;
5527 };
5528 if self.selections.count() != 1 {
5529 return;
5530 }
5531
5532 self.report_inline_completion_event(
5533 active_inline_completion.completion_id.clone(),
5534 true,
5535 cx,
5536 );
5537
5538 match &active_inline_completion.completion {
5539 InlineCompletion::Move { target, .. } => {
5540 let target = *target;
5541 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5542 selections.select_anchor_ranges([target..target]);
5543 });
5544 }
5545 InlineCompletion::Edit { edits, .. } => {
5546 // Find an insertion that starts at the cursor position.
5547 let snapshot = self.buffer.read(cx).snapshot(cx);
5548 let cursor_offset = self.selections.newest::<usize>(cx).head();
5549 let insertion = edits.iter().find_map(|(range, text)| {
5550 let range = range.to_offset(&snapshot);
5551 if range.is_empty() && range.start == cursor_offset {
5552 Some(text)
5553 } else {
5554 None
5555 }
5556 });
5557
5558 if let Some(text) = insertion {
5559 let mut partial_completion = text
5560 .chars()
5561 .by_ref()
5562 .take_while(|c| c.is_alphabetic())
5563 .collect::<String>();
5564 if partial_completion.is_empty() {
5565 partial_completion = text
5566 .chars()
5567 .by_ref()
5568 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5569 .collect::<String>();
5570 }
5571
5572 cx.emit(EditorEvent::InputHandled {
5573 utf16_range_to_replace: None,
5574 text: partial_completion.clone().into(),
5575 });
5576
5577 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5578
5579 self.refresh_inline_completion(true, true, window, cx);
5580 cx.notify();
5581 } else {
5582 self.accept_edit_prediction(&Default::default(), window, cx);
5583 }
5584 }
5585 }
5586 }
5587
5588 fn discard_inline_completion(
5589 &mut self,
5590 should_report_inline_completion_event: bool,
5591 cx: &mut Context<Self>,
5592 ) -> bool {
5593 if should_report_inline_completion_event {
5594 let completion_id = self
5595 .active_inline_completion
5596 .as_ref()
5597 .and_then(|active_completion| active_completion.completion_id.clone());
5598
5599 self.report_inline_completion_event(completion_id, false, cx);
5600 }
5601
5602 if let Some(provider) = self.edit_prediction_provider() {
5603 provider.discard(cx);
5604 }
5605
5606 self.take_active_inline_completion(cx)
5607 }
5608
5609 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5610 let Some(provider) = self.edit_prediction_provider() else {
5611 return;
5612 };
5613
5614 let Some((_, buffer, _)) = self
5615 .buffer
5616 .read(cx)
5617 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5618 else {
5619 return;
5620 };
5621
5622 let extension = buffer
5623 .read(cx)
5624 .file()
5625 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5626
5627 let event_type = match accepted {
5628 true => "Edit Prediction Accepted",
5629 false => "Edit Prediction Discarded",
5630 };
5631 telemetry::event!(
5632 event_type,
5633 provider = provider.name(),
5634 prediction_id = id,
5635 suggestion_accepted = accepted,
5636 file_extension = extension,
5637 );
5638 }
5639
5640 pub fn has_active_inline_completion(&self) -> bool {
5641 self.active_inline_completion.is_some()
5642 }
5643
5644 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5645 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5646 return false;
5647 };
5648
5649 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5650 self.clear_highlights::<InlineCompletionHighlight>(cx);
5651 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5652 true
5653 }
5654
5655 /// Returns true when we're displaying the edit prediction popover below the cursor
5656 /// like we are not previewing and the LSP autocomplete menu is visible
5657 /// or we are in `when_holding_modifier` mode.
5658 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5659 if self.edit_prediction_preview_is_active()
5660 || !self.show_edit_predictions_in_menu()
5661 || !self.edit_predictions_enabled()
5662 {
5663 return false;
5664 }
5665
5666 if self.has_visible_completions_menu() {
5667 return true;
5668 }
5669
5670 has_completion && self.edit_prediction_requires_modifier()
5671 }
5672
5673 fn handle_modifiers_changed(
5674 &mut self,
5675 modifiers: Modifiers,
5676 position_map: &PositionMap,
5677 window: &mut Window,
5678 cx: &mut Context<Self>,
5679 ) {
5680 if self.show_edit_predictions_in_menu() {
5681 self.update_edit_prediction_preview(&modifiers, window, cx);
5682 }
5683
5684 self.update_selection_mode(&modifiers, position_map, window, cx);
5685
5686 let mouse_position = window.mouse_position();
5687 if !position_map.text_hitbox.is_hovered(window) {
5688 return;
5689 }
5690
5691 self.update_hovered_link(
5692 position_map.point_for_position(mouse_position),
5693 &position_map.snapshot,
5694 modifiers,
5695 window,
5696 cx,
5697 )
5698 }
5699
5700 fn update_selection_mode(
5701 &mut self,
5702 modifiers: &Modifiers,
5703 position_map: &PositionMap,
5704 window: &mut Window,
5705 cx: &mut Context<Self>,
5706 ) {
5707 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5708 return;
5709 }
5710
5711 let mouse_position = window.mouse_position();
5712 let point_for_position = position_map.point_for_position(mouse_position);
5713 let position = point_for_position.previous_valid;
5714
5715 self.select(
5716 SelectPhase::BeginColumnar {
5717 position,
5718 reset: false,
5719 goal_column: point_for_position.exact_unclipped.column(),
5720 },
5721 window,
5722 cx,
5723 );
5724 }
5725
5726 fn update_edit_prediction_preview(
5727 &mut self,
5728 modifiers: &Modifiers,
5729 window: &mut Window,
5730 cx: &mut Context<Self>,
5731 ) {
5732 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5733 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5734 return;
5735 };
5736
5737 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5738 if matches!(
5739 self.edit_prediction_preview,
5740 EditPredictionPreview::Inactive { .. }
5741 ) {
5742 self.edit_prediction_preview = EditPredictionPreview::Active {
5743 previous_scroll_position: None,
5744 since: Instant::now(),
5745 };
5746
5747 self.update_visible_inline_completion(window, cx);
5748 cx.notify();
5749 }
5750 } else if let EditPredictionPreview::Active {
5751 previous_scroll_position,
5752 since,
5753 } = self.edit_prediction_preview
5754 {
5755 if let (Some(previous_scroll_position), Some(position_map)) =
5756 (previous_scroll_position, self.last_position_map.as_ref())
5757 {
5758 self.set_scroll_position(
5759 previous_scroll_position
5760 .scroll_position(&position_map.snapshot.display_snapshot),
5761 window,
5762 cx,
5763 );
5764 }
5765
5766 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5767 released_too_fast: since.elapsed() < Duration::from_millis(200),
5768 };
5769 self.clear_row_highlights::<EditPredictionPreview>();
5770 self.update_visible_inline_completion(window, cx);
5771 cx.notify();
5772 }
5773 }
5774
5775 fn update_visible_inline_completion(
5776 &mut self,
5777 _window: &mut Window,
5778 cx: &mut Context<Self>,
5779 ) -> Option<()> {
5780 let selection = self.selections.newest_anchor();
5781 let cursor = selection.head();
5782 let multibuffer = self.buffer.read(cx).snapshot(cx);
5783 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5784 let excerpt_id = cursor.excerpt_id;
5785
5786 let show_in_menu = self.show_edit_predictions_in_menu();
5787 let completions_menu_has_precedence = !show_in_menu
5788 && (self.context_menu.borrow().is_some()
5789 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5790
5791 if completions_menu_has_precedence
5792 || !offset_selection.is_empty()
5793 || self
5794 .active_inline_completion
5795 .as_ref()
5796 .map_or(false, |completion| {
5797 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5798 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5799 !invalidation_range.contains(&offset_selection.head())
5800 })
5801 {
5802 self.discard_inline_completion(false, cx);
5803 return None;
5804 }
5805
5806 self.take_active_inline_completion(cx);
5807 let Some(provider) = self.edit_prediction_provider() else {
5808 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5809 return None;
5810 };
5811
5812 let (buffer, cursor_buffer_position) =
5813 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5814
5815 self.edit_prediction_settings =
5816 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5817
5818 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5819
5820 if self.edit_prediction_indent_conflict {
5821 let cursor_point = cursor.to_point(&multibuffer);
5822
5823 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5824
5825 if let Some((_, indent)) = indents.iter().next() {
5826 if indent.len == cursor_point.column {
5827 self.edit_prediction_indent_conflict = false;
5828 }
5829 }
5830 }
5831
5832 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5833 let edits = inline_completion
5834 .edits
5835 .into_iter()
5836 .flat_map(|(range, new_text)| {
5837 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5838 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5839 Some((start..end, new_text))
5840 })
5841 .collect::<Vec<_>>();
5842 if edits.is_empty() {
5843 return None;
5844 }
5845
5846 let first_edit_start = edits.first().unwrap().0.start;
5847 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5848 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5849
5850 let last_edit_end = edits.last().unwrap().0.end;
5851 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5852 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5853
5854 let cursor_row = cursor.to_point(&multibuffer).row;
5855
5856 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5857
5858 let mut inlay_ids = Vec::new();
5859 let invalidation_row_range;
5860 let move_invalidation_row_range = if cursor_row < edit_start_row {
5861 Some(cursor_row..edit_end_row)
5862 } else if cursor_row > edit_end_row {
5863 Some(edit_start_row..cursor_row)
5864 } else {
5865 None
5866 };
5867 let is_move =
5868 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5869 let completion = if is_move {
5870 invalidation_row_range =
5871 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5872 let target = first_edit_start;
5873 InlineCompletion::Move { target, snapshot }
5874 } else {
5875 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5876 && !self.inline_completions_hidden_for_vim_mode;
5877
5878 if show_completions_in_buffer {
5879 if edits
5880 .iter()
5881 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5882 {
5883 let mut inlays = Vec::new();
5884 for (range, new_text) in &edits {
5885 let inlay = Inlay::inline_completion(
5886 post_inc(&mut self.next_inlay_id),
5887 range.start,
5888 new_text.as_str(),
5889 );
5890 inlay_ids.push(inlay.id);
5891 inlays.push(inlay);
5892 }
5893
5894 self.splice_inlays(&[], inlays, cx);
5895 } else {
5896 let background_color = cx.theme().status().deleted_background;
5897 self.highlight_text::<InlineCompletionHighlight>(
5898 edits.iter().map(|(range, _)| range.clone()).collect(),
5899 HighlightStyle {
5900 background_color: Some(background_color),
5901 ..Default::default()
5902 },
5903 cx,
5904 );
5905 }
5906 }
5907
5908 invalidation_row_range = edit_start_row..edit_end_row;
5909
5910 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5911 if provider.show_tab_accept_marker() {
5912 EditDisplayMode::TabAccept
5913 } else {
5914 EditDisplayMode::Inline
5915 }
5916 } else {
5917 EditDisplayMode::DiffPopover
5918 };
5919
5920 InlineCompletion::Edit {
5921 edits,
5922 edit_preview: inline_completion.edit_preview,
5923 display_mode,
5924 snapshot,
5925 }
5926 };
5927
5928 let invalidation_range = multibuffer
5929 .anchor_before(Point::new(invalidation_row_range.start, 0))
5930 ..multibuffer.anchor_after(Point::new(
5931 invalidation_row_range.end,
5932 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5933 ));
5934
5935 self.stale_inline_completion_in_menu = None;
5936 self.active_inline_completion = Some(InlineCompletionState {
5937 inlay_ids,
5938 completion,
5939 completion_id: inline_completion.id,
5940 invalidation_range,
5941 });
5942
5943 cx.notify();
5944
5945 Some(())
5946 }
5947
5948 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5949 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5950 }
5951
5952 fn render_code_actions_indicator(
5953 &self,
5954 _style: &EditorStyle,
5955 row: DisplayRow,
5956 is_active: bool,
5957 breakpoint: Option<&(Anchor, Breakpoint)>,
5958 cx: &mut Context<Self>,
5959 ) -> Option<IconButton> {
5960 let color = Color::Muted;
5961
5962 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
5963 let bp_kind = Arc::new(
5964 breakpoint
5965 .map(|(_, bp)| bp.kind.clone())
5966 .unwrap_or(BreakpointKind::Standard),
5967 );
5968
5969 if self.available_code_actions.is_some() {
5970 Some(
5971 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5972 .shape(ui::IconButtonShape::Square)
5973 .icon_size(IconSize::XSmall)
5974 .icon_color(color)
5975 .toggle_state(is_active)
5976 .tooltip({
5977 let focus_handle = self.focus_handle.clone();
5978 move |window, cx| {
5979 Tooltip::for_action_in(
5980 "Toggle Code Actions",
5981 &ToggleCodeActions {
5982 deployed_from_indicator: None,
5983 },
5984 &focus_handle,
5985 window,
5986 cx,
5987 )
5988 }
5989 })
5990 .on_click(cx.listener(move |editor, _e, window, cx| {
5991 window.focus(&editor.focus_handle(cx));
5992 editor.toggle_code_actions(
5993 &ToggleCodeActions {
5994 deployed_from_indicator: Some(row),
5995 },
5996 window,
5997 cx,
5998 );
5999 }))
6000 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6001 editor.set_breakpoint_context_menu(
6002 row,
6003 position,
6004 bp_kind.clone(),
6005 event.down.position,
6006 window,
6007 cx,
6008 );
6009 })),
6010 )
6011 } else {
6012 None
6013 }
6014 }
6015
6016 fn clear_tasks(&mut self) {
6017 self.tasks.clear()
6018 }
6019
6020 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
6021 if self.tasks.insert(key, value).is_some() {
6022 // This case should hopefully be rare, but just in case...
6023 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
6024 }
6025 }
6026
6027 /// Get all display points of breakpoints that will be rendered within editor
6028 ///
6029 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
6030 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
6031 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
6032 fn active_breakpoints(
6033 &mut self,
6034 range: Range<DisplayRow>,
6035 window: &mut Window,
6036 cx: &mut Context<Self>,
6037 ) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
6038 let mut breakpoint_display_points = HashMap::default();
6039
6040 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
6041 return breakpoint_display_points;
6042 };
6043
6044 let snapshot = self.snapshot(window, cx);
6045
6046 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
6047 let Some(project) = self.project.as_ref() else {
6048 return breakpoint_display_points;
6049 };
6050
6051 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
6052 let buffer_snapshot = buffer.read(cx).snapshot();
6053
6054 for breakpoint in
6055 breakpoint_store
6056 .read(cx)
6057 .breakpoints(&buffer, None, buffer_snapshot.clone(), cx)
6058 {
6059 let point = buffer_snapshot.summary_for_anchor::<Point>(&breakpoint.0);
6060 let mut anchor = multi_buffer_snapshot.anchor_before(point);
6061 anchor.text_anchor = breakpoint.0;
6062
6063 breakpoint_display_points.insert(
6064 snapshot
6065 .point_to_display_point(
6066 MultiBufferPoint {
6067 row: point.row,
6068 column: point.column,
6069 },
6070 Bias::Left,
6071 )
6072 .row(),
6073 (anchor, breakpoint.1.clone()),
6074 );
6075 }
6076
6077 return breakpoint_display_points;
6078 }
6079
6080 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
6081 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
6082 for excerpt_boundary in multi_buffer_snapshot.excerpt_boundaries_in_range(range) {
6083 let info = excerpt_boundary.next;
6084
6085 let Some(excerpt_ranges) = multi_buffer_snapshot.range_for_excerpt(info.id) else {
6086 continue;
6087 };
6088
6089 let Some(buffer) =
6090 project.read_with(cx, |this, cx| this.buffer_for_id(info.buffer_id, cx))
6091 else {
6092 continue;
6093 };
6094
6095 if buffer.read(cx).file().is_none() {
6096 continue;
6097 }
6098 let breakpoints = breakpoint_store.read(cx).breakpoints(
6099 &buffer,
6100 Some(info.range.context.start..info.range.context.end),
6101 info.buffer.clone(),
6102 cx,
6103 );
6104
6105 // To translate a breakpoint's position within a singular buffer to a multi buffer
6106 // position we need to know it's excerpt starting location, it's position within
6107 // the singular buffer, and if that position is within the excerpt's range.
6108 let excerpt_head = excerpt_ranges
6109 .start
6110 .to_display_point(&snapshot.display_snapshot);
6111
6112 let buffer_start = info
6113 .buffer
6114 .summary_for_anchor::<Point>(&info.range.context.start);
6115
6116 for (anchor, breakpoint) in breakpoints {
6117 let as_row = info.buffer.summary_for_anchor::<Point>(&anchor).row;
6118 let delta = as_row - buffer_start.row;
6119
6120 let position = excerpt_head + DisplayPoint::new(DisplayRow(delta), 0);
6121
6122 let anchor = snapshot.display_point_to_anchor(position, Bias::Left);
6123
6124 breakpoint_display_points.insert(position.row(), (anchor, breakpoint.clone()));
6125 }
6126 }
6127
6128 breakpoint_display_points
6129 }
6130
6131 fn breakpoint_context_menu(
6132 &self,
6133 anchor: Anchor,
6134 kind: Arc<BreakpointKind>,
6135 window: &mut Window,
6136 cx: &mut Context<Self>,
6137 ) -> Entity<ui::ContextMenu> {
6138 let weak_editor = cx.weak_entity();
6139 let focus_handle = self.focus_handle(cx);
6140
6141 let second_entry_msg = if kind.log_message().is_some() {
6142 "Edit Log Breakpoint"
6143 } else {
6144 "Add Log Breakpoint"
6145 };
6146
6147 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
6148 menu.on_blur_subscription(Subscription::new(|| {}))
6149 .context(focus_handle)
6150 .entry("Toggle Breakpoint", None, {
6151 let weak_editor = weak_editor.clone();
6152 move |_window, cx| {
6153 weak_editor
6154 .update(cx, |this, cx| {
6155 this.edit_breakpoint_at_anchor(
6156 anchor,
6157 BreakpointKind::Standard,
6158 BreakpointEditAction::Toggle,
6159 cx,
6160 );
6161 })
6162 .log_err();
6163 }
6164 })
6165 .entry(second_entry_msg, None, move |window, cx| {
6166 weak_editor
6167 .update(cx, |this, cx| {
6168 this.add_edit_breakpoint_block(anchor, kind.as_ref(), window, cx);
6169 })
6170 .log_err();
6171 })
6172 })
6173 }
6174
6175 fn render_breakpoint(
6176 &self,
6177 position: Anchor,
6178 row: DisplayRow,
6179 kind: &BreakpointKind,
6180 cx: &mut Context<Self>,
6181 ) -> IconButton {
6182 let color = if self
6183 .gutter_breakpoint_indicator
6184 .is_some_and(|gutter_bp| gutter_bp.row() == row)
6185 {
6186 Color::Hint
6187 } else {
6188 Color::Debugger
6189 };
6190
6191 let icon = match &kind {
6192 BreakpointKind::Standard => ui::IconName::DebugBreakpoint,
6193 BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
6194 };
6195 let arc_kind = Arc::new(kind.clone());
6196 let arc_kind2 = arc_kind.clone();
6197
6198 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
6199 .icon_size(IconSize::XSmall)
6200 .size(ui::ButtonSize::None)
6201 .icon_color(color)
6202 .style(ButtonStyle::Transparent)
6203 .on_click(cx.listener(move |editor, _e, window, cx| {
6204 window.focus(&editor.focus_handle(cx));
6205 editor.edit_breakpoint_at_anchor(
6206 position,
6207 arc_kind.as_ref().clone(),
6208 BreakpointEditAction::Toggle,
6209 cx,
6210 );
6211 }))
6212 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6213 editor.set_breakpoint_context_menu(
6214 row,
6215 Some(position),
6216 arc_kind2.clone(),
6217 event.down.position,
6218 window,
6219 cx,
6220 );
6221 }))
6222 }
6223
6224 fn build_tasks_context(
6225 project: &Entity<Project>,
6226 buffer: &Entity<Buffer>,
6227 buffer_row: u32,
6228 tasks: &Arc<RunnableTasks>,
6229 cx: &mut Context<Self>,
6230 ) -> Task<Option<task::TaskContext>> {
6231 let position = Point::new(buffer_row, tasks.column);
6232 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
6233 let location = Location {
6234 buffer: buffer.clone(),
6235 range: range_start..range_start,
6236 };
6237 // Fill in the environmental variables from the tree-sitter captures
6238 let mut captured_task_variables = TaskVariables::default();
6239 for (capture_name, value) in tasks.extra_variables.clone() {
6240 captured_task_variables.insert(
6241 task::VariableName::Custom(capture_name.into()),
6242 value.clone(),
6243 );
6244 }
6245 project.update(cx, |project, cx| {
6246 project.task_store().update(cx, |task_store, cx| {
6247 task_store.task_context_for_location(captured_task_variables, location, cx)
6248 })
6249 })
6250 }
6251
6252 pub fn spawn_nearest_task(
6253 &mut self,
6254 action: &SpawnNearestTask,
6255 window: &mut Window,
6256 cx: &mut Context<Self>,
6257 ) {
6258 let Some((workspace, _)) = self.workspace.clone() else {
6259 return;
6260 };
6261 let Some(project) = self.project.clone() else {
6262 return;
6263 };
6264
6265 // Try to find a closest, enclosing node using tree-sitter that has a
6266 // task
6267 let Some((buffer, buffer_row, tasks)) = self
6268 .find_enclosing_node_task(cx)
6269 // Or find the task that's closest in row-distance.
6270 .or_else(|| self.find_closest_task(cx))
6271 else {
6272 return;
6273 };
6274
6275 let reveal_strategy = action.reveal;
6276 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6277 cx.spawn_in(window, async move |_, cx| {
6278 let context = task_context.await?;
6279 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
6280
6281 let resolved = resolved_task.resolved.as_mut()?;
6282 resolved.reveal = reveal_strategy;
6283
6284 workspace
6285 .update(cx, |workspace, cx| {
6286 workspace::tasks::schedule_resolved_task(
6287 workspace,
6288 task_source_kind,
6289 resolved_task,
6290 false,
6291 cx,
6292 );
6293 })
6294 .ok()
6295 })
6296 .detach();
6297 }
6298
6299 fn find_closest_task(
6300 &mut self,
6301 cx: &mut Context<Self>,
6302 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6303 let cursor_row = self.selections.newest_adjusted(cx).head().row;
6304
6305 let ((buffer_id, row), tasks) = self
6306 .tasks
6307 .iter()
6308 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
6309
6310 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
6311 let tasks = Arc::new(tasks.to_owned());
6312 Some((buffer, *row, tasks))
6313 }
6314
6315 fn find_enclosing_node_task(
6316 &mut self,
6317 cx: &mut Context<Self>,
6318 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
6319 let snapshot = self.buffer.read(cx).snapshot(cx);
6320 let offset = self.selections.newest::<usize>(cx).head();
6321 let excerpt = snapshot.excerpt_containing(offset..offset)?;
6322 let buffer_id = excerpt.buffer().remote_id();
6323
6324 let layer = excerpt.buffer().syntax_layer_at(offset)?;
6325 let mut cursor = layer.node().walk();
6326
6327 while cursor.goto_first_child_for_byte(offset).is_some() {
6328 if cursor.node().end_byte() == offset {
6329 cursor.goto_next_sibling();
6330 }
6331 }
6332
6333 // Ascend to the smallest ancestor that contains the range and has a task.
6334 loop {
6335 let node = cursor.node();
6336 let node_range = node.byte_range();
6337 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6338
6339 // Check if this node contains our offset
6340 if node_range.start <= offset && node_range.end >= offset {
6341 // If it contains offset, check for task
6342 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6343 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6344 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6345 }
6346 }
6347
6348 if !cursor.goto_parent() {
6349 break;
6350 }
6351 }
6352 None
6353 }
6354
6355 fn render_run_indicator(
6356 &self,
6357 _style: &EditorStyle,
6358 is_active: bool,
6359 row: DisplayRow,
6360 breakpoint: Option<(Anchor, Breakpoint)>,
6361 cx: &mut Context<Self>,
6362 ) -> IconButton {
6363 let color = Color::Muted;
6364
6365 let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
6366 let bp_kind = Arc::new(
6367 breakpoint
6368 .map(|(_, bp)| bp.kind)
6369 .unwrap_or(BreakpointKind::Standard),
6370 );
6371
6372 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6373 .shape(ui::IconButtonShape::Square)
6374 .icon_size(IconSize::XSmall)
6375 .icon_color(color)
6376 .toggle_state(is_active)
6377 .on_click(cx.listener(move |editor, _e, window, cx| {
6378 window.focus(&editor.focus_handle(cx));
6379 editor.toggle_code_actions(
6380 &ToggleCodeActions {
6381 deployed_from_indicator: Some(row),
6382 },
6383 window,
6384 cx,
6385 );
6386 }))
6387 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
6388 editor.set_breakpoint_context_menu(
6389 row,
6390 position,
6391 bp_kind.clone(),
6392 event.down.position,
6393 window,
6394 cx,
6395 );
6396 }))
6397 }
6398
6399 pub fn context_menu_visible(&self) -> bool {
6400 !self.edit_prediction_preview_is_active()
6401 && self
6402 .context_menu
6403 .borrow()
6404 .as_ref()
6405 .map_or(false, |menu| menu.visible())
6406 }
6407
6408 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6409 self.context_menu
6410 .borrow()
6411 .as_ref()
6412 .map(|menu| menu.origin())
6413 }
6414
6415 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6416 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6417
6418 fn render_edit_prediction_popover(
6419 &mut self,
6420 text_bounds: &Bounds<Pixels>,
6421 content_origin: gpui::Point<Pixels>,
6422 editor_snapshot: &EditorSnapshot,
6423 visible_row_range: Range<DisplayRow>,
6424 scroll_top: f32,
6425 scroll_bottom: f32,
6426 line_layouts: &[LineWithInvisibles],
6427 line_height: Pixels,
6428 scroll_pixel_position: gpui::Point<Pixels>,
6429 newest_selection_head: Option<DisplayPoint>,
6430 editor_width: Pixels,
6431 style: &EditorStyle,
6432 window: &mut Window,
6433 cx: &mut App,
6434 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6435 let active_inline_completion = self.active_inline_completion.as_ref()?;
6436
6437 if self.edit_prediction_visible_in_cursor_popover(true) {
6438 return None;
6439 }
6440
6441 match &active_inline_completion.completion {
6442 InlineCompletion::Move { target, .. } => {
6443 let target_display_point = target.to_display_point(editor_snapshot);
6444
6445 if self.edit_prediction_requires_modifier() {
6446 if !self.edit_prediction_preview_is_active() {
6447 return None;
6448 }
6449
6450 self.render_edit_prediction_modifier_jump_popover(
6451 text_bounds,
6452 content_origin,
6453 visible_row_range,
6454 line_layouts,
6455 line_height,
6456 scroll_pixel_position,
6457 newest_selection_head,
6458 target_display_point,
6459 window,
6460 cx,
6461 )
6462 } else {
6463 self.render_edit_prediction_eager_jump_popover(
6464 text_bounds,
6465 content_origin,
6466 editor_snapshot,
6467 visible_row_range,
6468 scroll_top,
6469 scroll_bottom,
6470 line_height,
6471 scroll_pixel_position,
6472 target_display_point,
6473 editor_width,
6474 window,
6475 cx,
6476 )
6477 }
6478 }
6479 InlineCompletion::Edit {
6480 display_mode: EditDisplayMode::Inline,
6481 ..
6482 } => None,
6483 InlineCompletion::Edit {
6484 display_mode: EditDisplayMode::TabAccept,
6485 edits,
6486 ..
6487 } => {
6488 let range = &edits.first()?.0;
6489 let target_display_point = range.end.to_display_point(editor_snapshot);
6490
6491 self.render_edit_prediction_end_of_line_popover(
6492 "Accept",
6493 editor_snapshot,
6494 visible_row_range,
6495 target_display_point,
6496 line_height,
6497 scroll_pixel_position,
6498 content_origin,
6499 editor_width,
6500 window,
6501 cx,
6502 )
6503 }
6504 InlineCompletion::Edit {
6505 edits,
6506 edit_preview,
6507 display_mode: EditDisplayMode::DiffPopover,
6508 snapshot,
6509 } => self.render_edit_prediction_diff_popover(
6510 text_bounds,
6511 content_origin,
6512 editor_snapshot,
6513 visible_row_range,
6514 line_layouts,
6515 line_height,
6516 scroll_pixel_position,
6517 newest_selection_head,
6518 editor_width,
6519 style,
6520 edits,
6521 edit_preview,
6522 snapshot,
6523 window,
6524 cx,
6525 ),
6526 }
6527 }
6528
6529 fn render_edit_prediction_modifier_jump_popover(
6530 &mut self,
6531 text_bounds: &Bounds<Pixels>,
6532 content_origin: gpui::Point<Pixels>,
6533 visible_row_range: Range<DisplayRow>,
6534 line_layouts: &[LineWithInvisibles],
6535 line_height: Pixels,
6536 scroll_pixel_position: gpui::Point<Pixels>,
6537 newest_selection_head: Option<DisplayPoint>,
6538 target_display_point: DisplayPoint,
6539 window: &mut Window,
6540 cx: &mut App,
6541 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6542 let scrolled_content_origin =
6543 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6544
6545 const SCROLL_PADDING_Y: Pixels = px(12.);
6546
6547 if target_display_point.row() < visible_row_range.start {
6548 return self.render_edit_prediction_scroll_popover(
6549 |_| SCROLL_PADDING_Y,
6550 IconName::ArrowUp,
6551 visible_row_range,
6552 line_layouts,
6553 newest_selection_head,
6554 scrolled_content_origin,
6555 window,
6556 cx,
6557 );
6558 } else if target_display_point.row() >= visible_row_range.end {
6559 return self.render_edit_prediction_scroll_popover(
6560 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6561 IconName::ArrowDown,
6562 visible_row_range,
6563 line_layouts,
6564 newest_selection_head,
6565 scrolled_content_origin,
6566 window,
6567 cx,
6568 );
6569 }
6570
6571 const POLE_WIDTH: Pixels = px(2.);
6572
6573 let line_layout =
6574 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6575 let target_column = target_display_point.column() as usize;
6576
6577 let target_x = line_layout.x_for_index(target_column);
6578 let target_y =
6579 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6580
6581 let flag_on_right = target_x < text_bounds.size.width / 2.;
6582
6583 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6584 border_color.l += 0.001;
6585
6586 let mut element = v_flex()
6587 .items_end()
6588 .when(flag_on_right, |el| el.items_start())
6589 .child(if flag_on_right {
6590 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6591 .rounded_bl(px(0.))
6592 .rounded_tl(px(0.))
6593 .border_l_2()
6594 .border_color(border_color)
6595 } else {
6596 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6597 .rounded_br(px(0.))
6598 .rounded_tr(px(0.))
6599 .border_r_2()
6600 .border_color(border_color)
6601 })
6602 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6603 .into_any();
6604
6605 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6606
6607 let mut origin = scrolled_content_origin + point(target_x, target_y)
6608 - point(
6609 if flag_on_right {
6610 POLE_WIDTH
6611 } else {
6612 size.width - POLE_WIDTH
6613 },
6614 size.height - line_height,
6615 );
6616
6617 origin.x = origin.x.max(content_origin.x);
6618
6619 element.prepaint_at(origin, window, cx);
6620
6621 Some((element, origin))
6622 }
6623
6624 fn render_edit_prediction_scroll_popover(
6625 &mut self,
6626 to_y: impl Fn(Size<Pixels>) -> Pixels,
6627 scroll_icon: IconName,
6628 visible_row_range: Range<DisplayRow>,
6629 line_layouts: &[LineWithInvisibles],
6630 newest_selection_head: Option<DisplayPoint>,
6631 scrolled_content_origin: gpui::Point<Pixels>,
6632 window: &mut Window,
6633 cx: &mut App,
6634 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6635 let mut element = self
6636 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6637 .into_any();
6638
6639 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6640
6641 let cursor = newest_selection_head?;
6642 let cursor_row_layout =
6643 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6644 let cursor_column = cursor.column() as usize;
6645
6646 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6647
6648 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6649
6650 element.prepaint_at(origin, window, cx);
6651 Some((element, origin))
6652 }
6653
6654 fn render_edit_prediction_eager_jump_popover(
6655 &mut self,
6656 text_bounds: &Bounds<Pixels>,
6657 content_origin: gpui::Point<Pixels>,
6658 editor_snapshot: &EditorSnapshot,
6659 visible_row_range: Range<DisplayRow>,
6660 scroll_top: f32,
6661 scroll_bottom: f32,
6662 line_height: Pixels,
6663 scroll_pixel_position: gpui::Point<Pixels>,
6664 target_display_point: DisplayPoint,
6665 editor_width: Pixels,
6666 window: &mut Window,
6667 cx: &mut App,
6668 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6669 if target_display_point.row().as_f32() < scroll_top {
6670 let mut element = self
6671 .render_edit_prediction_line_popover(
6672 "Jump to Edit",
6673 Some(IconName::ArrowUp),
6674 window,
6675 cx,
6676 )?
6677 .into_any();
6678
6679 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6680 let offset = point(
6681 (text_bounds.size.width - size.width) / 2.,
6682 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6683 );
6684
6685 let origin = text_bounds.origin + offset;
6686 element.prepaint_at(origin, window, cx);
6687 Some((element, origin))
6688 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6689 let mut element = self
6690 .render_edit_prediction_line_popover(
6691 "Jump to Edit",
6692 Some(IconName::ArrowDown),
6693 window,
6694 cx,
6695 )?
6696 .into_any();
6697
6698 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6699 let offset = point(
6700 (text_bounds.size.width - size.width) / 2.,
6701 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6702 );
6703
6704 let origin = text_bounds.origin + offset;
6705 element.prepaint_at(origin, window, cx);
6706 Some((element, origin))
6707 } else {
6708 self.render_edit_prediction_end_of_line_popover(
6709 "Jump to Edit",
6710 editor_snapshot,
6711 visible_row_range,
6712 target_display_point,
6713 line_height,
6714 scroll_pixel_position,
6715 content_origin,
6716 editor_width,
6717 window,
6718 cx,
6719 )
6720 }
6721 }
6722
6723 fn render_edit_prediction_end_of_line_popover(
6724 self: &mut Editor,
6725 label: &'static str,
6726 editor_snapshot: &EditorSnapshot,
6727 visible_row_range: Range<DisplayRow>,
6728 target_display_point: DisplayPoint,
6729 line_height: Pixels,
6730 scroll_pixel_position: gpui::Point<Pixels>,
6731 content_origin: gpui::Point<Pixels>,
6732 editor_width: Pixels,
6733 window: &mut Window,
6734 cx: &mut App,
6735 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6736 let target_line_end = DisplayPoint::new(
6737 target_display_point.row(),
6738 editor_snapshot.line_len(target_display_point.row()),
6739 );
6740
6741 let mut element = self
6742 .render_edit_prediction_line_popover(label, None, window, cx)?
6743 .into_any();
6744
6745 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6746
6747 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6748
6749 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6750 let mut origin = start_point
6751 + line_origin
6752 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6753 origin.x = origin.x.max(content_origin.x);
6754
6755 let max_x = content_origin.x + editor_width - size.width;
6756
6757 if origin.x > max_x {
6758 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6759
6760 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6761 origin.y += offset;
6762 IconName::ArrowUp
6763 } else {
6764 origin.y -= offset;
6765 IconName::ArrowDown
6766 };
6767
6768 element = self
6769 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6770 .into_any();
6771
6772 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6773
6774 origin.x = content_origin.x + editor_width - size.width - px(2.);
6775 }
6776
6777 element.prepaint_at(origin, window, cx);
6778 Some((element, origin))
6779 }
6780
6781 fn render_edit_prediction_diff_popover(
6782 self: &Editor,
6783 text_bounds: &Bounds<Pixels>,
6784 content_origin: gpui::Point<Pixels>,
6785 editor_snapshot: &EditorSnapshot,
6786 visible_row_range: Range<DisplayRow>,
6787 line_layouts: &[LineWithInvisibles],
6788 line_height: Pixels,
6789 scroll_pixel_position: gpui::Point<Pixels>,
6790 newest_selection_head: Option<DisplayPoint>,
6791 editor_width: Pixels,
6792 style: &EditorStyle,
6793 edits: &Vec<(Range<Anchor>, String)>,
6794 edit_preview: &Option<language::EditPreview>,
6795 snapshot: &language::BufferSnapshot,
6796 window: &mut Window,
6797 cx: &mut App,
6798 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6799 let edit_start = edits
6800 .first()
6801 .unwrap()
6802 .0
6803 .start
6804 .to_display_point(editor_snapshot);
6805 let edit_end = edits
6806 .last()
6807 .unwrap()
6808 .0
6809 .end
6810 .to_display_point(editor_snapshot);
6811
6812 let is_visible = visible_row_range.contains(&edit_start.row())
6813 || visible_row_range.contains(&edit_end.row());
6814 if !is_visible {
6815 return None;
6816 }
6817
6818 let highlighted_edits =
6819 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6820
6821 let styled_text = highlighted_edits.to_styled_text(&style.text);
6822 let line_count = highlighted_edits.text.lines().count();
6823
6824 const BORDER_WIDTH: Pixels = px(1.);
6825
6826 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6827 let has_keybind = keybind.is_some();
6828
6829 let mut element = h_flex()
6830 .items_start()
6831 .child(
6832 h_flex()
6833 .bg(cx.theme().colors().editor_background)
6834 .border(BORDER_WIDTH)
6835 .shadow_sm()
6836 .border_color(cx.theme().colors().border)
6837 .rounded_l_lg()
6838 .when(line_count > 1, |el| el.rounded_br_lg())
6839 .pr_1()
6840 .child(styled_text),
6841 )
6842 .child(
6843 h_flex()
6844 .h(line_height + BORDER_WIDTH * px(2.))
6845 .px_1p5()
6846 .gap_1()
6847 // Workaround: For some reason, there's a gap if we don't do this
6848 .ml(-BORDER_WIDTH)
6849 .shadow(smallvec![gpui::BoxShadow {
6850 color: gpui::black().opacity(0.05),
6851 offset: point(px(1.), px(1.)),
6852 blur_radius: px(2.),
6853 spread_radius: px(0.),
6854 }])
6855 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6856 .border(BORDER_WIDTH)
6857 .border_color(cx.theme().colors().border)
6858 .rounded_r_lg()
6859 .id("edit_prediction_diff_popover_keybind")
6860 .when(!has_keybind, |el| {
6861 let status_colors = cx.theme().status();
6862
6863 el.bg(status_colors.error_background)
6864 .border_color(status_colors.error.opacity(0.6))
6865 .child(Icon::new(IconName::Info).color(Color::Error))
6866 .cursor_default()
6867 .hoverable_tooltip(move |_window, cx| {
6868 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6869 })
6870 })
6871 .children(keybind),
6872 )
6873 .into_any();
6874
6875 let longest_row =
6876 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6877 let longest_line_width = if visible_row_range.contains(&longest_row) {
6878 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6879 } else {
6880 layout_line(
6881 longest_row,
6882 editor_snapshot,
6883 style,
6884 editor_width,
6885 |_| false,
6886 window,
6887 cx,
6888 )
6889 .width
6890 };
6891
6892 let viewport_bounds =
6893 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6894 right: -EditorElement::SCROLLBAR_WIDTH,
6895 ..Default::default()
6896 });
6897
6898 let x_after_longest =
6899 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6900 - scroll_pixel_position.x;
6901
6902 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6903
6904 // Fully visible if it can be displayed within the window (allow overlapping other
6905 // panes). However, this is only allowed if the popover starts within text_bounds.
6906 let can_position_to_the_right = x_after_longest < text_bounds.right()
6907 && x_after_longest + element_bounds.width < viewport_bounds.right();
6908
6909 let mut origin = if can_position_to_the_right {
6910 point(
6911 x_after_longest,
6912 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6913 - scroll_pixel_position.y,
6914 )
6915 } else {
6916 let cursor_row = newest_selection_head.map(|head| head.row());
6917 let above_edit = edit_start
6918 .row()
6919 .0
6920 .checked_sub(line_count as u32)
6921 .map(DisplayRow);
6922 let below_edit = Some(edit_end.row() + 1);
6923 let above_cursor =
6924 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6925 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6926
6927 // Place the edit popover adjacent to the edit if there is a location
6928 // available that is onscreen and does not obscure the cursor. Otherwise,
6929 // place it adjacent to the cursor.
6930 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6931 .into_iter()
6932 .flatten()
6933 .find(|&start_row| {
6934 let end_row = start_row + line_count as u32;
6935 visible_row_range.contains(&start_row)
6936 && visible_row_range.contains(&end_row)
6937 && cursor_row.map_or(true, |cursor_row| {
6938 !((start_row..end_row).contains(&cursor_row))
6939 })
6940 })?;
6941
6942 content_origin
6943 + point(
6944 -scroll_pixel_position.x,
6945 row_target.as_f32() * line_height - scroll_pixel_position.y,
6946 )
6947 };
6948
6949 origin.x -= BORDER_WIDTH;
6950
6951 window.defer_draw(element, origin, 1);
6952
6953 // Do not return an element, since it will already be drawn due to defer_draw.
6954 None
6955 }
6956
6957 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6958 px(30.)
6959 }
6960
6961 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6962 if self.read_only(cx) {
6963 cx.theme().players().read_only()
6964 } else {
6965 self.style.as_ref().unwrap().local_player
6966 }
6967 }
6968
6969 fn render_edit_prediction_accept_keybind(
6970 &self,
6971 window: &mut Window,
6972 cx: &App,
6973 ) -> Option<AnyElement> {
6974 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6975 let accept_keystroke = accept_binding.keystroke()?;
6976
6977 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6978
6979 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6980 Color::Accent
6981 } else {
6982 Color::Muted
6983 };
6984
6985 h_flex()
6986 .px_0p5()
6987 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6988 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6989 .text_size(TextSize::XSmall.rems(cx))
6990 .child(h_flex().children(ui::render_modifiers(
6991 &accept_keystroke.modifiers,
6992 PlatformStyle::platform(),
6993 Some(modifiers_color),
6994 Some(IconSize::XSmall.rems().into()),
6995 true,
6996 )))
6997 .when(is_platform_style_mac, |parent| {
6998 parent.child(accept_keystroke.key.clone())
6999 })
7000 .when(!is_platform_style_mac, |parent| {
7001 parent.child(
7002 Key::new(
7003 util::capitalize(&accept_keystroke.key),
7004 Some(Color::Default),
7005 )
7006 .size(Some(IconSize::XSmall.rems().into())),
7007 )
7008 })
7009 .into_any()
7010 .into()
7011 }
7012
7013 fn render_edit_prediction_line_popover(
7014 &self,
7015 label: impl Into<SharedString>,
7016 icon: Option<IconName>,
7017 window: &mut Window,
7018 cx: &App,
7019 ) -> Option<Stateful<Div>> {
7020 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
7021
7022 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
7023 let has_keybind = keybind.is_some();
7024
7025 let result = h_flex()
7026 .id("ep-line-popover")
7027 .py_0p5()
7028 .pl_1()
7029 .pr(padding_right)
7030 .gap_1()
7031 .rounded_md()
7032 .border_1()
7033 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7034 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
7035 .shadow_sm()
7036 .when(!has_keybind, |el| {
7037 let status_colors = cx.theme().status();
7038
7039 el.bg(status_colors.error_background)
7040 .border_color(status_colors.error.opacity(0.6))
7041 .pl_2()
7042 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
7043 .cursor_default()
7044 .hoverable_tooltip(move |_window, cx| {
7045 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
7046 })
7047 })
7048 .children(keybind)
7049 .child(
7050 Label::new(label)
7051 .size(LabelSize::Small)
7052 .when(!has_keybind, |el| {
7053 el.color(cx.theme().status().error.into()).strikethrough()
7054 }),
7055 )
7056 .when(!has_keybind, |el| {
7057 el.child(
7058 h_flex().ml_1().child(
7059 Icon::new(IconName::Info)
7060 .size(IconSize::Small)
7061 .color(cx.theme().status().error.into()),
7062 ),
7063 )
7064 })
7065 .when_some(icon, |element, icon| {
7066 element.child(
7067 div()
7068 .mt(px(1.5))
7069 .child(Icon::new(icon).size(IconSize::Small)),
7070 )
7071 });
7072
7073 Some(result)
7074 }
7075
7076 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
7077 let accent_color = cx.theme().colors().text_accent;
7078 let editor_bg_color = cx.theme().colors().editor_background;
7079 editor_bg_color.blend(accent_color.opacity(0.1))
7080 }
7081
7082 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
7083 let accent_color = cx.theme().colors().text_accent;
7084 let editor_bg_color = cx.theme().colors().editor_background;
7085 editor_bg_color.blend(accent_color.opacity(0.6))
7086 }
7087
7088 fn render_edit_prediction_cursor_popover(
7089 &self,
7090 min_width: Pixels,
7091 max_width: Pixels,
7092 cursor_point: Point,
7093 style: &EditorStyle,
7094 accept_keystroke: Option<&gpui::Keystroke>,
7095 _window: &Window,
7096 cx: &mut Context<Editor>,
7097 ) -> Option<AnyElement> {
7098 let provider = self.edit_prediction_provider.as_ref()?;
7099
7100 if provider.provider.needs_terms_acceptance(cx) {
7101 return Some(
7102 h_flex()
7103 .min_w(min_width)
7104 .flex_1()
7105 .px_2()
7106 .py_1()
7107 .gap_3()
7108 .elevation_2(cx)
7109 .hover(|style| style.bg(cx.theme().colors().element_hover))
7110 .id("accept-terms")
7111 .cursor_pointer()
7112 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
7113 .on_click(cx.listener(|this, _event, window, cx| {
7114 cx.stop_propagation();
7115 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
7116 window.dispatch_action(
7117 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
7118 cx,
7119 );
7120 }))
7121 .child(
7122 h_flex()
7123 .flex_1()
7124 .gap_2()
7125 .child(Icon::new(IconName::ZedPredict))
7126 .child(Label::new("Accept Terms of Service"))
7127 .child(div().w_full())
7128 .child(
7129 Icon::new(IconName::ArrowUpRight)
7130 .color(Color::Muted)
7131 .size(IconSize::Small),
7132 )
7133 .into_any_element(),
7134 )
7135 .into_any(),
7136 );
7137 }
7138
7139 let is_refreshing = provider.provider.is_refreshing(cx);
7140
7141 fn pending_completion_container() -> Div {
7142 h_flex()
7143 .h_full()
7144 .flex_1()
7145 .gap_2()
7146 .child(Icon::new(IconName::ZedPredict))
7147 }
7148
7149 let completion = match &self.active_inline_completion {
7150 Some(prediction) => {
7151 if !self.has_visible_completions_menu() {
7152 const RADIUS: Pixels = px(6.);
7153 const BORDER_WIDTH: Pixels = px(1.);
7154
7155 return Some(
7156 h_flex()
7157 .elevation_2(cx)
7158 .border(BORDER_WIDTH)
7159 .border_color(cx.theme().colors().border)
7160 .when(accept_keystroke.is_none(), |el| {
7161 el.border_color(cx.theme().status().error)
7162 })
7163 .rounded(RADIUS)
7164 .rounded_tl(px(0.))
7165 .overflow_hidden()
7166 .child(div().px_1p5().child(match &prediction.completion {
7167 InlineCompletion::Move { target, snapshot } => {
7168 use text::ToPoint as _;
7169 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
7170 {
7171 Icon::new(IconName::ZedPredictDown)
7172 } else {
7173 Icon::new(IconName::ZedPredictUp)
7174 }
7175 }
7176 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
7177 }))
7178 .child(
7179 h_flex()
7180 .gap_1()
7181 .py_1()
7182 .px_2()
7183 .rounded_r(RADIUS - BORDER_WIDTH)
7184 .border_l_1()
7185 .border_color(cx.theme().colors().border)
7186 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7187 .when(self.edit_prediction_preview.released_too_fast(), |el| {
7188 el.child(
7189 Label::new("Hold")
7190 .size(LabelSize::Small)
7191 .when(accept_keystroke.is_none(), |el| {
7192 el.strikethrough()
7193 })
7194 .line_height_style(LineHeightStyle::UiLabel),
7195 )
7196 })
7197 .id("edit_prediction_cursor_popover_keybind")
7198 .when(accept_keystroke.is_none(), |el| {
7199 let status_colors = cx.theme().status();
7200
7201 el.bg(status_colors.error_background)
7202 .border_color(status_colors.error.opacity(0.6))
7203 .child(Icon::new(IconName::Info).color(Color::Error))
7204 .cursor_default()
7205 .hoverable_tooltip(move |_window, cx| {
7206 cx.new(|_| MissingEditPredictionKeybindingTooltip)
7207 .into()
7208 })
7209 })
7210 .when_some(
7211 accept_keystroke.as_ref(),
7212 |el, accept_keystroke| {
7213 el.child(h_flex().children(ui::render_modifiers(
7214 &accept_keystroke.modifiers,
7215 PlatformStyle::platform(),
7216 Some(Color::Default),
7217 Some(IconSize::XSmall.rems().into()),
7218 false,
7219 )))
7220 },
7221 ),
7222 )
7223 .into_any(),
7224 );
7225 }
7226
7227 self.render_edit_prediction_cursor_popover_preview(
7228 prediction,
7229 cursor_point,
7230 style,
7231 cx,
7232 )?
7233 }
7234
7235 None if is_refreshing => match &self.stale_inline_completion_in_menu {
7236 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
7237 stale_completion,
7238 cursor_point,
7239 style,
7240 cx,
7241 )?,
7242
7243 None => {
7244 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
7245 }
7246 },
7247
7248 None => pending_completion_container().child(Label::new("No Prediction")),
7249 };
7250
7251 let completion = if is_refreshing {
7252 completion
7253 .with_animation(
7254 "loading-completion",
7255 Animation::new(Duration::from_secs(2))
7256 .repeat()
7257 .with_easing(pulsating_between(0.4, 0.8)),
7258 |label, delta| label.opacity(delta),
7259 )
7260 .into_any_element()
7261 } else {
7262 completion.into_any_element()
7263 };
7264
7265 let has_completion = self.active_inline_completion.is_some();
7266
7267 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
7268 Some(
7269 h_flex()
7270 .min_w(min_width)
7271 .max_w(max_width)
7272 .flex_1()
7273 .elevation_2(cx)
7274 .border_color(cx.theme().colors().border)
7275 .child(
7276 div()
7277 .flex_1()
7278 .py_1()
7279 .px_2()
7280 .overflow_hidden()
7281 .child(completion),
7282 )
7283 .when_some(accept_keystroke, |el, accept_keystroke| {
7284 if !accept_keystroke.modifiers.modified() {
7285 return el;
7286 }
7287
7288 el.child(
7289 h_flex()
7290 .h_full()
7291 .border_l_1()
7292 .rounded_r_lg()
7293 .border_color(cx.theme().colors().border)
7294 .bg(Self::edit_prediction_line_popover_bg_color(cx))
7295 .gap_1()
7296 .py_1()
7297 .px_2()
7298 .child(
7299 h_flex()
7300 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7301 .when(is_platform_style_mac, |parent| parent.gap_1())
7302 .child(h_flex().children(ui::render_modifiers(
7303 &accept_keystroke.modifiers,
7304 PlatformStyle::platform(),
7305 Some(if !has_completion {
7306 Color::Muted
7307 } else {
7308 Color::Default
7309 }),
7310 None,
7311 false,
7312 ))),
7313 )
7314 .child(Label::new("Preview").into_any_element())
7315 .opacity(if has_completion { 1.0 } else { 0.4 }),
7316 )
7317 })
7318 .into_any(),
7319 )
7320 }
7321
7322 fn render_edit_prediction_cursor_popover_preview(
7323 &self,
7324 completion: &InlineCompletionState,
7325 cursor_point: Point,
7326 style: &EditorStyle,
7327 cx: &mut Context<Editor>,
7328 ) -> Option<Div> {
7329 use text::ToPoint as _;
7330
7331 fn render_relative_row_jump(
7332 prefix: impl Into<String>,
7333 current_row: u32,
7334 target_row: u32,
7335 ) -> Div {
7336 let (row_diff, arrow) = if target_row < current_row {
7337 (current_row - target_row, IconName::ArrowUp)
7338 } else {
7339 (target_row - current_row, IconName::ArrowDown)
7340 };
7341
7342 h_flex()
7343 .child(
7344 Label::new(format!("{}{}", prefix.into(), row_diff))
7345 .color(Color::Muted)
7346 .size(LabelSize::Small),
7347 )
7348 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
7349 }
7350
7351 match &completion.completion {
7352 InlineCompletion::Move {
7353 target, snapshot, ..
7354 } => Some(
7355 h_flex()
7356 .px_2()
7357 .gap_2()
7358 .flex_1()
7359 .child(
7360 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7361 Icon::new(IconName::ZedPredictDown)
7362 } else {
7363 Icon::new(IconName::ZedPredictUp)
7364 },
7365 )
7366 .child(Label::new("Jump to Edit")),
7367 ),
7368
7369 InlineCompletion::Edit {
7370 edits,
7371 edit_preview,
7372 snapshot,
7373 display_mode: _,
7374 } => {
7375 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7376
7377 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7378 &snapshot,
7379 &edits,
7380 edit_preview.as_ref()?,
7381 true,
7382 cx,
7383 )
7384 .first_line_preview();
7385
7386 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7387 .with_default_highlights(&style.text, highlighted_edits.highlights);
7388
7389 let preview = h_flex()
7390 .gap_1()
7391 .min_w_16()
7392 .child(styled_text)
7393 .when(has_more_lines, |parent| parent.child("…"));
7394
7395 let left = if first_edit_row != cursor_point.row {
7396 render_relative_row_jump("", cursor_point.row, first_edit_row)
7397 .into_any_element()
7398 } else {
7399 Icon::new(IconName::ZedPredict).into_any_element()
7400 };
7401
7402 Some(
7403 h_flex()
7404 .h_full()
7405 .flex_1()
7406 .gap_2()
7407 .pr_1()
7408 .overflow_x_hidden()
7409 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7410 .child(left)
7411 .child(preview),
7412 )
7413 }
7414 }
7415 }
7416
7417 fn render_context_menu(
7418 &self,
7419 style: &EditorStyle,
7420 max_height_in_lines: u32,
7421 y_flipped: bool,
7422 window: &mut Window,
7423 cx: &mut Context<Editor>,
7424 ) -> Option<AnyElement> {
7425 let menu = self.context_menu.borrow();
7426 let menu = menu.as_ref()?;
7427 if !menu.visible() {
7428 return None;
7429 };
7430 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7431 }
7432
7433 fn render_context_menu_aside(
7434 &mut self,
7435 max_size: Size<Pixels>,
7436 window: &mut Window,
7437 cx: &mut Context<Editor>,
7438 ) -> Option<AnyElement> {
7439 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7440 if menu.visible() {
7441 menu.render_aside(self, max_size, window, cx)
7442 } else {
7443 None
7444 }
7445 })
7446 }
7447
7448 fn hide_context_menu(
7449 &mut self,
7450 window: &mut Window,
7451 cx: &mut Context<Self>,
7452 ) -> Option<CodeContextMenu> {
7453 cx.notify();
7454 self.completion_tasks.clear();
7455 let context_menu = self.context_menu.borrow_mut().take();
7456 self.stale_inline_completion_in_menu.take();
7457 self.update_visible_inline_completion(window, cx);
7458 context_menu
7459 }
7460
7461 fn show_snippet_choices(
7462 &mut self,
7463 choices: &Vec<String>,
7464 selection: Range<Anchor>,
7465 cx: &mut Context<Self>,
7466 ) {
7467 if selection.start.buffer_id.is_none() {
7468 return;
7469 }
7470 let buffer_id = selection.start.buffer_id.unwrap();
7471 let buffer = self.buffer().read(cx).buffer(buffer_id);
7472 let id = post_inc(&mut self.next_completion_id);
7473
7474 if let Some(buffer) = buffer {
7475 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7476 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7477 ));
7478 }
7479 }
7480
7481 pub fn insert_snippet(
7482 &mut self,
7483 insertion_ranges: &[Range<usize>],
7484 snippet: Snippet,
7485 window: &mut Window,
7486 cx: &mut Context<Self>,
7487 ) -> Result<()> {
7488 struct Tabstop<T> {
7489 is_end_tabstop: bool,
7490 ranges: Vec<Range<T>>,
7491 choices: Option<Vec<String>>,
7492 }
7493
7494 let tabstops = self.buffer.update(cx, |buffer, cx| {
7495 let snippet_text: Arc<str> = snippet.text.clone().into();
7496 buffer.edit(
7497 insertion_ranges
7498 .iter()
7499 .cloned()
7500 .map(|range| (range, snippet_text.clone())),
7501 Some(AutoindentMode::EachLine),
7502 cx,
7503 );
7504
7505 let snapshot = &*buffer.read(cx);
7506 let snippet = &snippet;
7507 snippet
7508 .tabstops
7509 .iter()
7510 .map(|tabstop| {
7511 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7512 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7513 });
7514 let mut tabstop_ranges = tabstop
7515 .ranges
7516 .iter()
7517 .flat_map(|tabstop_range| {
7518 let mut delta = 0_isize;
7519 insertion_ranges.iter().map(move |insertion_range| {
7520 let insertion_start = insertion_range.start as isize + delta;
7521 delta +=
7522 snippet.text.len() as isize - insertion_range.len() as isize;
7523
7524 let start = ((insertion_start + tabstop_range.start) as usize)
7525 .min(snapshot.len());
7526 let end = ((insertion_start + tabstop_range.end) as usize)
7527 .min(snapshot.len());
7528 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7529 })
7530 })
7531 .collect::<Vec<_>>();
7532 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7533
7534 Tabstop {
7535 is_end_tabstop,
7536 ranges: tabstop_ranges,
7537 choices: tabstop.choices.clone(),
7538 }
7539 })
7540 .collect::<Vec<_>>()
7541 });
7542 if let Some(tabstop) = tabstops.first() {
7543 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7544 s.select_ranges(tabstop.ranges.iter().cloned());
7545 });
7546
7547 if let Some(choices) = &tabstop.choices {
7548 if let Some(selection) = tabstop.ranges.first() {
7549 self.show_snippet_choices(choices, selection.clone(), cx)
7550 }
7551 }
7552
7553 // If we're already at the last tabstop and it's at the end of the snippet,
7554 // we're done, we don't need to keep the state around.
7555 if !tabstop.is_end_tabstop {
7556 let choices = tabstops
7557 .iter()
7558 .map(|tabstop| tabstop.choices.clone())
7559 .collect();
7560
7561 let ranges = tabstops
7562 .into_iter()
7563 .map(|tabstop| tabstop.ranges)
7564 .collect::<Vec<_>>();
7565
7566 self.snippet_stack.push(SnippetState {
7567 active_index: 0,
7568 ranges,
7569 choices,
7570 });
7571 }
7572
7573 // Check whether the just-entered snippet ends with an auto-closable bracket.
7574 if self.autoclose_regions.is_empty() {
7575 let snapshot = self.buffer.read(cx).snapshot(cx);
7576 for selection in &mut self.selections.all::<Point>(cx) {
7577 let selection_head = selection.head();
7578 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7579 continue;
7580 };
7581
7582 let mut bracket_pair = None;
7583 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7584 let prev_chars = snapshot
7585 .reversed_chars_at(selection_head)
7586 .collect::<String>();
7587 for (pair, enabled) in scope.brackets() {
7588 if enabled
7589 && pair.close
7590 && prev_chars.starts_with(pair.start.as_str())
7591 && next_chars.starts_with(pair.end.as_str())
7592 {
7593 bracket_pair = Some(pair.clone());
7594 break;
7595 }
7596 }
7597 if let Some(pair) = bracket_pair {
7598 let start = snapshot.anchor_after(selection_head);
7599 let end = snapshot.anchor_after(selection_head);
7600 self.autoclose_regions.push(AutocloseRegion {
7601 selection_id: selection.id,
7602 range: start..end,
7603 pair,
7604 });
7605 }
7606 }
7607 }
7608 }
7609 Ok(())
7610 }
7611
7612 pub fn move_to_next_snippet_tabstop(
7613 &mut self,
7614 window: &mut Window,
7615 cx: &mut Context<Self>,
7616 ) -> bool {
7617 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7618 }
7619
7620 pub fn move_to_prev_snippet_tabstop(
7621 &mut self,
7622 window: &mut Window,
7623 cx: &mut Context<Self>,
7624 ) -> bool {
7625 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7626 }
7627
7628 pub fn move_to_snippet_tabstop(
7629 &mut self,
7630 bias: Bias,
7631 window: &mut Window,
7632 cx: &mut Context<Self>,
7633 ) -> bool {
7634 if let Some(mut snippet) = self.snippet_stack.pop() {
7635 match bias {
7636 Bias::Left => {
7637 if snippet.active_index > 0 {
7638 snippet.active_index -= 1;
7639 } else {
7640 self.snippet_stack.push(snippet);
7641 return false;
7642 }
7643 }
7644 Bias::Right => {
7645 if snippet.active_index + 1 < snippet.ranges.len() {
7646 snippet.active_index += 1;
7647 } else {
7648 self.snippet_stack.push(snippet);
7649 return false;
7650 }
7651 }
7652 }
7653 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7655 s.select_anchor_ranges(current_ranges.iter().cloned())
7656 });
7657
7658 if let Some(choices) = &snippet.choices[snippet.active_index] {
7659 if let Some(selection) = current_ranges.first() {
7660 self.show_snippet_choices(&choices, selection.clone(), cx);
7661 }
7662 }
7663
7664 // If snippet state is not at the last tabstop, push it back on the stack
7665 if snippet.active_index + 1 < snippet.ranges.len() {
7666 self.snippet_stack.push(snippet);
7667 }
7668 return true;
7669 }
7670 }
7671
7672 false
7673 }
7674
7675 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7676 self.transact(window, cx, |this, window, cx| {
7677 this.select_all(&SelectAll, window, cx);
7678 this.insert("", window, cx);
7679 });
7680 }
7681
7682 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7683 self.transact(window, cx, |this, window, cx| {
7684 this.select_autoclose_pair(window, cx);
7685 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7686 if !this.linked_edit_ranges.is_empty() {
7687 let selections = this.selections.all::<MultiBufferPoint>(cx);
7688 let snapshot = this.buffer.read(cx).snapshot(cx);
7689
7690 for selection in selections.iter() {
7691 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7692 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7693 if selection_start.buffer_id != selection_end.buffer_id {
7694 continue;
7695 }
7696 if let Some(ranges) =
7697 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7698 {
7699 for (buffer, entries) in ranges {
7700 linked_ranges.entry(buffer).or_default().extend(entries);
7701 }
7702 }
7703 }
7704 }
7705
7706 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7707 if !this.selections.line_mode {
7708 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7709 for selection in &mut selections {
7710 if selection.is_empty() {
7711 let old_head = selection.head();
7712 let mut new_head =
7713 movement::left(&display_map, old_head.to_display_point(&display_map))
7714 .to_point(&display_map);
7715 if let Some((buffer, line_buffer_range)) = display_map
7716 .buffer_snapshot
7717 .buffer_line_for_row(MultiBufferRow(old_head.row))
7718 {
7719 let indent_size =
7720 buffer.indent_size_for_line(line_buffer_range.start.row);
7721 let indent_len = match indent_size.kind {
7722 IndentKind::Space => {
7723 buffer.settings_at(line_buffer_range.start, cx).tab_size
7724 }
7725 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7726 };
7727 if old_head.column <= indent_size.len && old_head.column > 0 {
7728 let indent_len = indent_len.get();
7729 new_head = cmp::min(
7730 new_head,
7731 MultiBufferPoint::new(
7732 old_head.row,
7733 ((old_head.column - 1) / indent_len) * indent_len,
7734 ),
7735 );
7736 }
7737 }
7738
7739 selection.set_head(new_head, SelectionGoal::None);
7740 }
7741 }
7742 }
7743
7744 this.signature_help_state.set_backspace_pressed(true);
7745 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7746 s.select(selections)
7747 });
7748 this.insert("", window, cx);
7749 let empty_str: Arc<str> = Arc::from("");
7750 for (buffer, edits) in linked_ranges {
7751 let snapshot = buffer.read(cx).snapshot();
7752 use text::ToPoint as TP;
7753
7754 let edits = edits
7755 .into_iter()
7756 .map(|range| {
7757 let end_point = TP::to_point(&range.end, &snapshot);
7758 let mut start_point = TP::to_point(&range.start, &snapshot);
7759
7760 if end_point == start_point {
7761 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7762 .saturating_sub(1);
7763 start_point =
7764 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7765 };
7766
7767 (start_point..end_point, empty_str.clone())
7768 })
7769 .sorted_by_key(|(range, _)| range.start)
7770 .collect::<Vec<_>>();
7771 buffer.update(cx, |this, cx| {
7772 this.edit(edits, None, cx);
7773 })
7774 }
7775 this.refresh_inline_completion(true, false, window, cx);
7776 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7777 });
7778 }
7779
7780 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7781 self.transact(window, cx, |this, window, cx| {
7782 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7783 let line_mode = s.line_mode;
7784 s.move_with(|map, selection| {
7785 if selection.is_empty() && !line_mode {
7786 let cursor = movement::right(map, selection.head());
7787 selection.end = cursor;
7788 selection.reversed = true;
7789 selection.goal = SelectionGoal::None;
7790 }
7791 })
7792 });
7793 this.insert("", window, cx);
7794 this.refresh_inline_completion(true, false, window, cx);
7795 });
7796 }
7797
7798 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7799 if self.move_to_prev_snippet_tabstop(window, cx) {
7800 return;
7801 }
7802
7803 self.outdent(&Outdent, window, cx);
7804 }
7805
7806 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7807 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7808 return;
7809 }
7810
7811 let mut selections = self.selections.all_adjusted(cx);
7812 let buffer = self.buffer.read(cx);
7813 let snapshot = buffer.snapshot(cx);
7814 let rows_iter = selections.iter().map(|s| s.head().row);
7815 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7816
7817 let mut edits = Vec::new();
7818 let mut prev_edited_row = 0;
7819 let mut row_delta = 0;
7820 for selection in &mut selections {
7821 if selection.start.row != prev_edited_row {
7822 row_delta = 0;
7823 }
7824 prev_edited_row = selection.end.row;
7825
7826 // If the selection is non-empty, then increase the indentation of the selected lines.
7827 if !selection.is_empty() {
7828 row_delta =
7829 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7830 continue;
7831 }
7832
7833 // If the selection is empty and the cursor is in the leading whitespace before the
7834 // suggested indentation, then auto-indent the line.
7835 let cursor = selection.head();
7836 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7837 if let Some(suggested_indent) =
7838 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7839 {
7840 if cursor.column < suggested_indent.len
7841 && cursor.column <= current_indent.len
7842 && current_indent.len <= suggested_indent.len
7843 {
7844 selection.start = Point::new(cursor.row, suggested_indent.len);
7845 selection.end = selection.start;
7846 if row_delta == 0 {
7847 edits.extend(Buffer::edit_for_indent_size_adjustment(
7848 cursor.row,
7849 current_indent,
7850 suggested_indent,
7851 ));
7852 row_delta = suggested_indent.len - current_indent.len;
7853 }
7854 continue;
7855 }
7856 }
7857
7858 // Otherwise, insert a hard or soft tab.
7859 let settings = buffer.language_settings_at(cursor, cx);
7860 let tab_size = if settings.hard_tabs {
7861 IndentSize::tab()
7862 } else {
7863 let tab_size = settings.tab_size.get();
7864 let char_column = snapshot
7865 .text_for_range(Point::new(cursor.row, 0)..cursor)
7866 .flat_map(str::chars)
7867 .count()
7868 + row_delta as usize;
7869 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7870 IndentSize::spaces(chars_to_next_tab_stop)
7871 };
7872 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7873 selection.end = selection.start;
7874 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7875 row_delta += tab_size.len;
7876 }
7877
7878 self.transact(window, cx, |this, window, cx| {
7879 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7880 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7881 s.select(selections)
7882 });
7883 this.refresh_inline_completion(true, false, window, cx);
7884 });
7885 }
7886
7887 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7888 if self.read_only(cx) {
7889 return;
7890 }
7891 let mut selections = self.selections.all::<Point>(cx);
7892 let mut prev_edited_row = 0;
7893 let mut row_delta = 0;
7894 let mut edits = Vec::new();
7895 let buffer = self.buffer.read(cx);
7896 let snapshot = buffer.snapshot(cx);
7897 for selection in &mut selections {
7898 if selection.start.row != prev_edited_row {
7899 row_delta = 0;
7900 }
7901 prev_edited_row = selection.end.row;
7902
7903 row_delta =
7904 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7905 }
7906
7907 self.transact(window, cx, |this, window, cx| {
7908 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7909 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7910 s.select(selections)
7911 });
7912 });
7913 }
7914
7915 fn indent_selection(
7916 buffer: &MultiBuffer,
7917 snapshot: &MultiBufferSnapshot,
7918 selection: &mut Selection<Point>,
7919 edits: &mut Vec<(Range<Point>, String)>,
7920 delta_for_start_row: u32,
7921 cx: &App,
7922 ) -> u32 {
7923 let settings = buffer.language_settings_at(selection.start, cx);
7924 let tab_size = settings.tab_size.get();
7925 let indent_kind = if settings.hard_tabs {
7926 IndentKind::Tab
7927 } else {
7928 IndentKind::Space
7929 };
7930 let mut start_row = selection.start.row;
7931 let mut end_row = selection.end.row + 1;
7932
7933 // If a selection ends at the beginning of a line, don't indent
7934 // that last line.
7935 if selection.end.column == 0 && selection.end.row > selection.start.row {
7936 end_row -= 1;
7937 }
7938
7939 // Avoid re-indenting a row that has already been indented by a
7940 // previous selection, but still update this selection's column
7941 // to reflect that indentation.
7942 if delta_for_start_row > 0 {
7943 start_row += 1;
7944 selection.start.column += delta_for_start_row;
7945 if selection.end.row == selection.start.row {
7946 selection.end.column += delta_for_start_row;
7947 }
7948 }
7949
7950 let mut delta_for_end_row = 0;
7951 let has_multiple_rows = start_row + 1 != end_row;
7952 for row in start_row..end_row {
7953 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7954 let indent_delta = match (current_indent.kind, indent_kind) {
7955 (IndentKind::Space, IndentKind::Space) => {
7956 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7957 IndentSize::spaces(columns_to_next_tab_stop)
7958 }
7959 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7960 (_, IndentKind::Tab) => IndentSize::tab(),
7961 };
7962
7963 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7964 0
7965 } else {
7966 selection.start.column
7967 };
7968 let row_start = Point::new(row, start);
7969 edits.push((
7970 row_start..row_start,
7971 indent_delta.chars().collect::<String>(),
7972 ));
7973
7974 // Update this selection's endpoints to reflect the indentation.
7975 if row == selection.start.row {
7976 selection.start.column += indent_delta.len;
7977 }
7978 if row == selection.end.row {
7979 selection.end.column += indent_delta.len;
7980 delta_for_end_row = indent_delta.len;
7981 }
7982 }
7983
7984 if selection.start.row == selection.end.row {
7985 delta_for_start_row + delta_for_end_row
7986 } else {
7987 delta_for_end_row
7988 }
7989 }
7990
7991 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7992 if self.read_only(cx) {
7993 return;
7994 }
7995 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7996 let selections = self.selections.all::<Point>(cx);
7997 let mut deletion_ranges = Vec::new();
7998 let mut last_outdent = None;
7999 {
8000 let buffer = self.buffer.read(cx);
8001 let snapshot = buffer.snapshot(cx);
8002 for selection in &selections {
8003 let settings = buffer.language_settings_at(selection.start, cx);
8004 let tab_size = settings.tab_size.get();
8005 let mut rows = selection.spanned_rows(false, &display_map);
8006
8007 // Avoid re-outdenting a row that has already been outdented by a
8008 // previous selection.
8009 if let Some(last_row) = last_outdent {
8010 if last_row == rows.start {
8011 rows.start = rows.start.next_row();
8012 }
8013 }
8014 let has_multiple_rows = rows.len() > 1;
8015 for row in rows.iter_rows() {
8016 let indent_size = snapshot.indent_size_for_line(row);
8017 if indent_size.len > 0 {
8018 let deletion_len = match indent_size.kind {
8019 IndentKind::Space => {
8020 let columns_to_prev_tab_stop = indent_size.len % tab_size;
8021 if columns_to_prev_tab_stop == 0 {
8022 tab_size
8023 } else {
8024 columns_to_prev_tab_stop
8025 }
8026 }
8027 IndentKind::Tab => 1,
8028 };
8029 let start = if has_multiple_rows
8030 || deletion_len > selection.start.column
8031 || indent_size.len < selection.start.column
8032 {
8033 0
8034 } else {
8035 selection.start.column - deletion_len
8036 };
8037 deletion_ranges.push(
8038 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
8039 );
8040 last_outdent = Some(row);
8041 }
8042 }
8043 }
8044 }
8045
8046 self.transact(window, cx, |this, window, cx| {
8047 this.buffer.update(cx, |buffer, cx| {
8048 let empty_str: Arc<str> = Arc::default();
8049 buffer.edit(
8050 deletion_ranges
8051 .into_iter()
8052 .map(|range| (range, empty_str.clone())),
8053 None,
8054 cx,
8055 );
8056 });
8057 let selections = this.selections.all::<usize>(cx);
8058 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8059 s.select(selections)
8060 });
8061 });
8062 }
8063
8064 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
8065 if self.read_only(cx) {
8066 return;
8067 }
8068 let selections = self
8069 .selections
8070 .all::<usize>(cx)
8071 .into_iter()
8072 .map(|s| s.range());
8073
8074 self.transact(window, cx, |this, window, cx| {
8075 this.buffer.update(cx, |buffer, cx| {
8076 buffer.autoindent_ranges(selections, cx);
8077 });
8078 let selections = this.selections.all::<usize>(cx);
8079 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8080 s.select(selections)
8081 });
8082 });
8083 }
8084
8085 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
8086 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8087 let selections = self.selections.all::<Point>(cx);
8088
8089 let mut new_cursors = Vec::new();
8090 let mut edit_ranges = Vec::new();
8091 let mut selections = selections.iter().peekable();
8092 while let Some(selection) = selections.next() {
8093 let mut rows = selection.spanned_rows(false, &display_map);
8094 let goal_display_column = selection.head().to_display_point(&display_map).column();
8095
8096 // Accumulate contiguous regions of rows that we want to delete.
8097 while let Some(next_selection) = selections.peek() {
8098 let next_rows = next_selection.spanned_rows(false, &display_map);
8099 if next_rows.start <= rows.end {
8100 rows.end = next_rows.end;
8101 selections.next().unwrap();
8102 } else {
8103 break;
8104 }
8105 }
8106
8107 let buffer = &display_map.buffer_snapshot;
8108 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
8109 let edit_end;
8110 let cursor_buffer_row;
8111 if buffer.max_point().row >= rows.end.0 {
8112 // If there's a line after the range, delete the \n from the end of the row range
8113 // and position the cursor on the next line.
8114 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
8115 cursor_buffer_row = rows.end;
8116 } else {
8117 // If there isn't a line after the range, delete the \n from the line before the
8118 // start of the row range and position the cursor there.
8119 edit_start = edit_start.saturating_sub(1);
8120 edit_end = buffer.len();
8121 cursor_buffer_row = rows.start.previous_row();
8122 }
8123
8124 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
8125 *cursor.column_mut() =
8126 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
8127
8128 new_cursors.push((
8129 selection.id,
8130 buffer.anchor_after(cursor.to_point(&display_map)),
8131 ));
8132 edit_ranges.push(edit_start..edit_end);
8133 }
8134
8135 self.transact(window, cx, |this, window, cx| {
8136 let buffer = this.buffer.update(cx, |buffer, cx| {
8137 let empty_str: Arc<str> = Arc::default();
8138 buffer.edit(
8139 edit_ranges
8140 .into_iter()
8141 .map(|range| (range, empty_str.clone())),
8142 None,
8143 cx,
8144 );
8145 buffer.snapshot(cx)
8146 });
8147 let new_selections = new_cursors
8148 .into_iter()
8149 .map(|(id, cursor)| {
8150 let cursor = cursor.to_point(&buffer);
8151 Selection {
8152 id,
8153 start: cursor,
8154 end: cursor,
8155 reversed: false,
8156 goal: SelectionGoal::None,
8157 }
8158 })
8159 .collect();
8160
8161 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8162 s.select(new_selections);
8163 });
8164 });
8165 }
8166
8167 pub fn join_lines_impl(
8168 &mut self,
8169 insert_whitespace: bool,
8170 window: &mut Window,
8171 cx: &mut Context<Self>,
8172 ) {
8173 if self.read_only(cx) {
8174 return;
8175 }
8176 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
8177 for selection in self.selections.all::<Point>(cx) {
8178 let start = MultiBufferRow(selection.start.row);
8179 // Treat single line selections as if they include the next line. Otherwise this action
8180 // would do nothing for single line selections individual cursors.
8181 let end = if selection.start.row == selection.end.row {
8182 MultiBufferRow(selection.start.row + 1)
8183 } else {
8184 MultiBufferRow(selection.end.row)
8185 };
8186
8187 if let Some(last_row_range) = row_ranges.last_mut() {
8188 if start <= last_row_range.end {
8189 last_row_range.end = end;
8190 continue;
8191 }
8192 }
8193 row_ranges.push(start..end);
8194 }
8195
8196 let snapshot = self.buffer.read(cx).snapshot(cx);
8197 let mut cursor_positions = Vec::new();
8198 for row_range in &row_ranges {
8199 let anchor = snapshot.anchor_before(Point::new(
8200 row_range.end.previous_row().0,
8201 snapshot.line_len(row_range.end.previous_row()),
8202 ));
8203 cursor_positions.push(anchor..anchor);
8204 }
8205
8206 self.transact(window, cx, |this, window, cx| {
8207 for row_range in row_ranges.into_iter().rev() {
8208 for row in row_range.iter_rows().rev() {
8209 let end_of_line = Point::new(row.0, snapshot.line_len(row));
8210 let next_line_row = row.next_row();
8211 let indent = snapshot.indent_size_for_line(next_line_row);
8212 let start_of_next_line = Point::new(next_line_row.0, indent.len);
8213
8214 let replace =
8215 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
8216 " "
8217 } else {
8218 ""
8219 };
8220
8221 this.buffer.update(cx, |buffer, cx| {
8222 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
8223 });
8224 }
8225 }
8226
8227 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8228 s.select_anchor_ranges(cursor_positions)
8229 });
8230 });
8231 }
8232
8233 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
8234 self.join_lines_impl(true, window, cx);
8235 }
8236
8237 pub fn sort_lines_case_sensitive(
8238 &mut self,
8239 _: &SortLinesCaseSensitive,
8240 window: &mut Window,
8241 cx: &mut Context<Self>,
8242 ) {
8243 self.manipulate_lines(window, cx, |lines| lines.sort())
8244 }
8245
8246 pub fn sort_lines_case_insensitive(
8247 &mut self,
8248 _: &SortLinesCaseInsensitive,
8249 window: &mut Window,
8250 cx: &mut Context<Self>,
8251 ) {
8252 self.manipulate_lines(window, cx, |lines| {
8253 lines.sort_by_key(|line| line.to_lowercase())
8254 })
8255 }
8256
8257 pub fn unique_lines_case_insensitive(
8258 &mut self,
8259 _: &UniqueLinesCaseInsensitive,
8260 window: &mut Window,
8261 cx: &mut Context<Self>,
8262 ) {
8263 self.manipulate_lines(window, cx, |lines| {
8264 let mut seen = HashSet::default();
8265 lines.retain(|line| seen.insert(line.to_lowercase()));
8266 })
8267 }
8268
8269 pub fn unique_lines_case_sensitive(
8270 &mut self,
8271 _: &UniqueLinesCaseSensitive,
8272 window: &mut Window,
8273 cx: &mut Context<Self>,
8274 ) {
8275 self.manipulate_lines(window, cx, |lines| {
8276 let mut seen = HashSet::default();
8277 lines.retain(|line| seen.insert(*line));
8278 })
8279 }
8280
8281 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
8282 let Some(project) = self.project.clone() else {
8283 return;
8284 };
8285 self.reload(project, window, cx)
8286 .detach_and_notify_err(window, cx);
8287 }
8288
8289 pub fn restore_file(
8290 &mut self,
8291 _: &::git::RestoreFile,
8292 window: &mut Window,
8293 cx: &mut Context<Self>,
8294 ) {
8295 let mut buffer_ids = HashSet::default();
8296 let snapshot = self.buffer().read(cx).snapshot(cx);
8297 for selection in self.selections.all::<usize>(cx) {
8298 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
8299 }
8300
8301 let buffer = self.buffer().read(cx);
8302 let ranges = buffer_ids
8303 .into_iter()
8304 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
8305 .collect::<Vec<_>>();
8306
8307 self.restore_hunks_in_ranges(ranges, window, cx);
8308 }
8309
8310 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
8311 let selections = self
8312 .selections
8313 .all(cx)
8314 .into_iter()
8315 .map(|s| s.range())
8316 .collect();
8317 self.restore_hunks_in_ranges(selections, window, cx);
8318 }
8319
8320 fn restore_hunks_in_ranges(
8321 &mut self,
8322 ranges: Vec<Range<Point>>,
8323 window: &mut Window,
8324 cx: &mut Context<Editor>,
8325 ) {
8326 let mut revert_changes = HashMap::default();
8327 let chunk_by = self
8328 .snapshot(window, cx)
8329 .hunks_for_ranges(ranges)
8330 .into_iter()
8331 .chunk_by(|hunk| hunk.buffer_id);
8332 for (buffer_id, hunks) in &chunk_by {
8333 let hunks = hunks.collect::<Vec<_>>();
8334 for hunk in &hunks {
8335 self.prepare_restore_change(&mut revert_changes, hunk, cx);
8336 }
8337 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
8338 }
8339 drop(chunk_by);
8340 if !revert_changes.is_empty() {
8341 self.transact(window, cx, |editor, window, cx| {
8342 editor.restore(revert_changes, window, cx);
8343 });
8344 }
8345 }
8346
8347 pub fn open_active_item_in_terminal(
8348 &mut self,
8349 _: &OpenInTerminal,
8350 window: &mut Window,
8351 cx: &mut Context<Self>,
8352 ) {
8353 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
8354 let project_path = buffer.read(cx).project_path(cx)?;
8355 let project = self.project.as_ref()?.read(cx);
8356 let entry = project.entry_for_path(&project_path, cx)?;
8357 let parent = match &entry.canonical_path {
8358 Some(canonical_path) => canonical_path.to_path_buf(),
8359 None => project.absolute_path(&project_path, cx)?,
8360 }
8361 .parent()?
8362 .to_path_buf();
8363 Some(parent)
8364 }) {
8365 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8366 }
8367 }
8368
8369 fn set_breakpoint_context_menu(
8370 &mut self,
8371 row: DisplayRow,
8372 position: Option<Anchor>,
8373 kind: Arc<BreakpointKind>,
8374 clicked_point: gpui::Point<Pixels>,
8375 window: &mut Window,
8376 cx: &mut Context<Self>,
8377 ) {
8378 if !cx.has_flag::<Debugger>() {
8379 return;
8380 }
8381 let source = self
8382 .buffer
8383 .read(cx)
8384 .snapshot(cx)
8385 .anchor_before(Point::new(row.0, 0u32));
8386
8387 let context_menu =
8388 self.breakpoint_context_menu(position.unwrap_or(source), kind, window, cx);
8389
8390 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
8391 self,
8392 source,
8393 clicked_point,
8394 context_menu,
8395 window,
8396 cx,
8397 );
8398 }
8399
8400 fn add_edit_breakpoint_block(
8401 &mut self,
8402 anchor: Anchor,
8403 kind: &BreakpointKind,
8404 window: &mut Window,
8405 cx: &mut Context<Self>,
8406 ) {
8407 let weak_editor = cx.weak_entity();
8408 let bp_prompt =
8409 cx.new(|cx| BreakpointPromptEditor::new(weak_editor, anchor, kind.clone(), window, cx));
8410
8411 let height = bp_prompt.update(cx, |this, cx| {
8412 this.prompt
8413 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
8414 });
8415 let cloned_prompt = bp_prompt.clone();
8416 let blocks = vec![BlockProperties {
8417 style: BlockStyle::Sticky,
8418 placement: BlockPlacement::Above(anchor),
8419 height,
8420 render: Arc::new(move |cx| {
8421 *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
8422 cloned_prompt.clone().into_any_element()
8423 }),
8424 priority: 0,
8425 }];
8426
8427 let focus_handle = bp_prompt.focus_handle(cx);
8428 window.focus(&focus_handle);
8429
8430 let block_ids = self.insert_blocks(blocks, None, cx);
8431 bp_prompt.update(cx, |prompt, _| {
8432 prompt.add_block_ids(block_ids);
8433 });
8434 }
8435
8436 pub(crate) fn breakpoint_at_cursor_head(
8437 &self,
8438 window: &mut Window,
8439 cx: &mut Context<Self>,
8440 ) -> Option<(Anchor, Breakpoint)> {
8441 let cursor_position: Point = self.selections.newest(cx).head();
8442 let snapshot = self.snapshot(window, cx);
8443 // We Set the column position to zero so this function interacts correctly
8444 // between calls by clicking on the gutter & using an action to toggle a
8445 // breakpoint. Otherwise, toggling a breakpoint through an action wouldn't
8446 // untoggle a breakpoint that was added through clicking on the gutter
8447 let cursor_position = snapshot
8448 .display_snapshot
8449 .buffer_snapshot
8450 .anchor_before(Point::new(cursor_position.row, 0));
8451
8452 let project = self.project.clone();
8453
8454 let buffer_id = cursor_position.text_anchor.buffer_id?;
8455 let enclosing_excerpt = snapshot
8456 .buffer_snapshot
8457 .excerpt_ids_for_range(cursor_position..cursor_position)
8458 .next()?;
8459 let buffer = project?.read_with(cx, |project, cx| project.buffer_for_id(buffer_id, cx))?;
8460 let buffer_snapshot = buffer.read(cx).snapshot();
8461
8462 let row = buffer_snapshot
8463 .summary_for_anchor::<text::PointUtf16>(&cursor_position.text_anchor)
8464 .row;
8465
8466 let bp = self
8467 .breakpoint_store
8468 .as_ref()?
8469 .read_with(cx, |breakpoint_store, cx| {
8470 breakpoint_store
8471 .breakpoints(
8472 &buffer,
8473 Some(cursor_position.text_anchor..(text::Anchor::MAX)),
8474 buffer_snapshot.clone(),
8475 cx,
8476 )
8477 .next()
8478 .and_then(move |(anchor, bp)| {
8479 let breakpoint_row = buffer_snapshot
8480 .summary_for_anchor::<text::PointUtf16>(anchor)
8481 .row;
8482
8483 if breakpoint_row == row {
8484 snapshot
8485 .buffer_snapshot
8486 .anchor_in_excerpt(enclosing_excerpt, *anchor)
8487 .map(|anchor| (anchor, bp.clone()))
8488 } else {
8489 None
8490 }
8491 })
8492 });
8493 bp
8494 }
8495
8496 pub fn edit_log_breakpoint(
8497 &mut self,
8498 _: &EditLogBreakpoint,
8499 window: &mut Window,
8500 cx: &mut Context<Self>,
8501 ) {
8502 let (anchor, bp) = self
8503 .breakpoint_at_cursor_head(window, cx)
8504 .unwrap_or_else(|| {
8505 let cursor_position: Point = self.selections.newest(cx).head();
8506
8507 let breakpoint_position = self
8508 .snapshot(window, cx)
8509 .display_snapshot
8510 .buffer_snapshot
8511 .anchor_before(Point::new(cursor_position.row, 0));
8512
8513 (
8514 breakpoint_position,
8515 Breakpoint {
8516 kind: BreakpointKind::Standard,
8517 },
8518 )
8519 });
8520
8521 self.add_edit_breakpoint_block(anchor, &bp.kind, window, cx);
8522 }
8523
8524 pub fn toggle_breakpoint(
8525 &mut self,
8526 _: &crate::actions::ToggleBreakpoint,
8527 window: &mut Window,
8528 cx: &mut Context<Self>,
8529 ) {
8530 let edit_action = BreakpointEditAction::Toggle;
8531
8532 if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
8533 self.edit_breakpoint_at_anchor(anchor, breakpoint.kind, edit_action, cx);
8534 } else {
8535 let cursor_position: Point = self.selections.newest(cx).head();
8536
8537 let breakpoint_position = self
8538 .snapshot(window, cx)
8539 .display_snapshot
8540 .buffer_snapshot
8541 .anchor_before(Point::new(cursor_position.row, 0));
8542
8543 self.edit_breakpoint_at_anchor(
8544 breakpoint_position,
8545 BreakpointKind::Standard,
8546 edit_action,
8547 cx,
8548 );
8549 }
8550 }
8551
8552 pub fn edit_breakpoint_at_anchor(
8553 &mut self,
8554 breakpoint_position: Anchor,
8555 kind: BreakpointKind,
8556 edit_action: BreakpointEditAction,
8557 cx: &mut Context<Self>,
8558 ) {
8559 let Some(breakpoint_store) = &self.breakpoint_store else {
8560 return;
8561 };
8562
8563 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
8564 if breakpoint_position == Anchor::min() {
8565 self.buffer()
8566 .read(cx)
8567 .excerpt_buffer_ids()
8568 .into_iter()
8569 .next()
8570 } else {
8571 None
8572 }
8573 }) else {
8574 return;
8575 };
8576
8577 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
8578 return;
8579 };
8580
8581 breakpoint_store.update(cx, |breakpoint_store, cx| {
8582 breakpoint_store.toggle_breakpoint(
8583 buffer,
8584 (breakpoint_position.text_anchor, Breakpoint { kind }),
8585 edit_action,
8586 cx,
8587 );
8588 });
8589
8590 cx.notify();
8591 }
8592
8593 #[cfg(any(test, feature = "test-support"))]
8594 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
8595 self.breakpoint_store.clone()
8596 }
8597
8598 pub fn prepare_restore_change(
8599 &self,
8600 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8601 hunk: &MultiBufferDiffHunk,
8602 cx: &mut App,
8603 ) -> Option<()> {
8604 if hunk.is_created_file() {
8605 return None;
8606 }
8607 let buffer = self.buffer.read(cx);
8608 let diff = buffer.diff_for(hunk.buffer_id)?;
8609 let buffer = buffer.buffer(hunk.buffer_id)?;
8610 let buffer = buffer.read(cx);
8611 let original_text = diff
8612 .read(cx)
8613 .base_text()
8614 .as_rope()
8615 .slice(hunk.diff_base_byte_range.clone());
8616 let buffer_snapshot = buffer.snapshot();
8617 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8618 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8619 probe
8620 .0
8621 .start
8622 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8623 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8624 }) {
8625 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8626 Some(())
8627 } else {
8628 None
8629 }
8630 }
8631
8632 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8633 self.manipulate_lines(window, cx, |lines| lines.reverse())
8634 }
8635
8636 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8637 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8638 }
8639
8640 fn manipulate_lines<Fn>(
8641 &mut self,
8642 window: &mut Window,
8643 cx: &mut Context<Self>,
8644 mut callback: Fn,
8645 ) where
8646 Fn: FnMut(&mut Vec<&str>),
8647 {
8648 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8649 let buffer = self.buffer.read(cx).snapshot(cx);
8650
8651 let mut edits = Vec::new();
8652
8653 let selections = self.selections.all::<Point>(cx);
8654 let mut selections = selections.iter().peekable();
8655 let mut contiguous_row_selections = Vec::new();
8656 let mut new_selections = Vec::new();
8657 let mut added_lines = 0;
8658 let mut removed_lines = 0;
8659
8660 while let Some(selection) = selections.next() {
8661 let (start_row, end_row) = consume_contiguous_rows(
8662 &mut contiguous_row_selections,
8663 selection,
8664 &display_map,
8665 &mut selections,
8666 );
8667
8668 let start_point = Point::new(start_row.0, 0);
8669 let end_point = Point::new(
8670 end_row.previous_row().0,
8671 buffer.line_len(end_row.previous_row()),
8672 );
8673 let text = buffer
8674 .text_for_range(start_point..end_point)
8675 .collect::<String>();
8676
8677 let mut lines = text.split('\n').collect_vec();
8678
8679 let lines_before = lines.len();
8680 callback(&mut lines);
8681 let lines_after = lines.len();
8682
8683 edits.push((start_point..end_point, lines.join("\n")));
8684
8685 // Selections must change based on added and removed line count
8686 let start_row =
8687 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8688 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8689 new_selections.push(Selection {
8690 id: selection.id,
8691 start: start_row,
8692 end: end_row,
8693 goal: SelectionGoal::None,
8694 reversed: selection.reversed,
8695 });
8696
8697 if lines_after > lines_before {
8698 added_lines += lines_after - lines_before;
8699 } else if lines_before > lines_after {
8700 removed_lines += lines_before - lines_after;
8701 }
8702 }
8703
8704 self.transact(window, cx, |this, window, cx| {
8705 let buffer = this.buffer.update(cx, |buffer, cx| {
8706 buffer.edit(edits, None, cx);
8707 buffer.snapshot(cx)
8708 });
8709
8710 // Recalculate offsets on newly edited buffer
8711 let new_selections = new_selections
8712 .iter()
8713 .map(|s| {
8714 let start_point = Point::new(s.start.0, 0);
8715 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8716 Selection {
8717 id: s.id,
8718 start: buffer.point_to_offset(start_point),
8719 end: buffer.point_to_offset(end_point),
8720 goal: s.goal,
8721 reversed: s.reversed,
8722 }
8723 })
8724 .collect();
8725
8726 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8727 s.select(new_selections);
8728 });
8729
8730 this.request_autoscroll(Autoscroll::fit(), cx);
8731 });
8732 }
8733
8734 pub fn convert_to_upper_case(
8735 &mut self,
8736 _: &ConvertToUpperCase,
8737 window: &mut Window,
8738 cx: &mut Context<Self>,
8739 ) {
8740 self.manipulate_text(window, cx, |text| text.to_uppercase())
8741 }
8742
8743 pub fn convert_to_lower_case(
8744 &mut self,
8745 _: &ConvertToLowerCase,
8746 window: &mut Window,
8747 cx: &mut Context<Self>,
8748 ) {
8749 self.manipulate_text(window, cx, |text| text.to_lowercase())
8750 }
8751
8752 pub fn convert_to_title_case(
8753 &mut self,
8754 _: &ConvertToTitleCase,
8755 window: &mut Window,
8756 cx: &mut Context<Self>,
8757 ) {
8758 self.manipulate_text(window, cx, |text| {
8759 text.split('\n')
8760 .map(|line| line.to_case(Case::Title))
8761 .join("\n")
8762 })
8763 }
8764
8765 pub fn convert_to_snake_case(
8766 &mut self,
8767 _: &ConvertToSnakeCase,
8768 window: &mut Window,
8769 cx: &mut Context<Self>,
8770 ) {
8771 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8772 }
8773
8774 pub fn convert_to_kebab_case(
8775 &mut self,
8776 _: &ConvertToKebabCase,
8777 window: &mut Window,
8778 cx: &mut Context<Self>,
8779 ) {
8780 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8781 }
8782
8783 pub fn convert_to_upper_camel_case(
8784 &mut self,
8785 _: &ConvertToUpperCamelCase,
8786 window: &mut Window,
8787 cx: &mut Context<Self>,
8788 ) {
8789 self.manipulate_text(window, cx, |text| {
8790 text.split('\n')
8791 .map(|line| line.to_case(Case::UpperCamel))
8792 .join("\n")
8793 })
8794 }
8795
8796 pub fn convert_to_lower_camel_case(
8797 &mut self,
8798 _: &ConvertToLowerCamelCase,
8799 window: &mut Window,
8800 cx: &mut Context<Self>,
8801 ) {
8802 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8803 }
8804
8805 pub fn convert_to_opposite_case(
8806 &mut self,
8807 _: &ConvertToOppositeCase,
8808 window: &mut Window,
8809 cx: &mut Context<Self>,
8810 ) {
8811 self.manipulate_text(window, cx, |text| {
8812 text.chars()
8813 .fold(String::with_capacity(text.len()), |mut t, c| {
8814 if c.is_uppercase() {
8815 t.extend(c.to_lowercase());
8816 } else {
8817 t.extend(c.to_uppercase());
8818 }
8819 t
8820 })
8821 })
8822 }
8823
8824 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8825 where
8826 Fn: FnMut(&str) -> String,
8827 {
8828 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8829 let buffer = self.buffer.read(cx).snapshot(cx);
8830
8831 let mut new_selections = Vec::new();
8832 let mut edits = Vec::new();
8833 let mut selection_adjustment = 0i32;
8834
8835 for selection in self.selections.all::<usize>(cx) {
8836 let selection_is_empty = selection.is_empty();
8837
8838 let (start, end) = if selection_is_empty {
8839 let word_range = movement::surrounding_word(
8840 &display_map,
8841 selection.start.to_display_point(&display_map),
8842 );
8843 let start = word_range.start.to_offset(&display_map, Bias::Left);
8844 let end = word_range.end.to_offset(&display_map, Bias::Left);
8845 (start, end)
8846 } else {
8847 (selection.start, selection.end)
8848 };
8849
8850 let text = buffer.text_for_range(start..end).collect::<String>();
8851 let old_length = text.len() as i32;
8852 let text = callback(&text);
8853
8854 new_selections.push(Selection {
8855 start: (start as i32 - selection_adjustment) as usize,
8856 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8857 goal: SelectionGoal::None,
8858 ..selection
8859 });
8860
8861 selection_adjustment += old_length - text.len() as i32;
8862
8863 edits.push((start..end, text));
8864 }
8865
8866 self.transact(window, cx, |this, window, cx| {
8867 this.buffer.update(cx, |buffer, cx| {
8868 buffer.edit(edits, None, cx);
8869 });
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 duplicate(
8880 &mut self,
8881 upwards: bool,
8882 whole_lines: bool,
8883 window: &mut Window,
8884 cx: &mut Context<Self>,
8885 ) {
8886 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8887 let buffer = &display_map.buffer_snapshot;
8888 let selections = self.selections.all::<Point>(cx);
8889
8890 let mut edits = Vec::new();
8891 let mut selections_iter = selections.iter().peekable();
8892 while let Some(selection) = selections_iter.next() {
8893 let mut rows = selection.spanned_rows(false, &display_map);
8894 // duplicate line-wise
8895 if whole_lines || selection.start == selection.end {
8896 // Avoid duplicating the same lines twice.
8897 while let Some(next_selection) = selections_iter.peek() {
8898 let next_rows = next_selection.spanned_rows(false, &display_map);
8899 if next_rows.start < rows.end {
8900 rows.end = next_rows.end;
8901 selections_iter.next().unwrap();
8902 } else {
8903 break;
8904 }
8905 }
8906
8907 // Copy the text from the selected row region and splice it either at the start
8908 // or end of the region.
8909 let start = Point::new(rows.start.0, 0);
8910 let end = Point::new(
8911 rows.end.previous_row().0,
8912 buffer.line_len(rows.end.previous_row()),
8913 );
8914 let text = buffer
8915 .text_for_range(start..end)
8916 .chain(Some("\n"))
8917 .collect::<String>();
8918 let insert_location = if upwards {
8919 Point::new(rows.end.0, 0)
8920 } else {
8921 start
8922 };
8923 edits.push((insert_location..insert_location, text));
8924 } else {
8925 // duplicate character-wise
8926 let start = selection.start;
8927 let end = selection.end;
8928 let text = buffer.text_for_range(start..end).collect::<String>();
8929 edits.push((selection.end..selection.end, text));
8930 }
8931 }
8932
8933 self.transact(window, cx, |this, _, cx| {
8934 this.buffer.update(cx, |buffer, cx| {
8935 buffer.edit(edits, None, cx);
8936 });
8937
8938 this.request_autoscroll(Autoscroll::fit(), cx);
8939 });
8940 }
8941
8942 pub fn duplicate_line_up(
8943 &mut self,
8944 _: &DuplicateLineUp,
8945 window: &mut Window,
8946 cx: &mut Context<Self>,
8947 ) {
8948 self.duplicate(true, true, window, cx);
8949 }
8950
8951 pub fn duplicate_line_down(
8952 &mut self,
8953 _: &DuplicateLineDown,
8954 window: &mut Window,
8955 cx: &mut Context<Self>,
8956 ) {
8957 self.duplicate(false, true, window, cx);
8958 }
8959
8960 pub fn duplicate_selection(
8961 &mut self,
8962 _: &DuplicateSelection,
8963 window: &mut Window,
8964 cx: &mut Context<Self>,
8965 ) {
8966 self.duplicate(false, false, window, cx);
8967 }
8968
8969 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8970 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8971 let buffer = self.buffer.read(cx).snapshot(cx);
8972
8973 let mut edits = Vec::new();
8974 let mut unfold_ranges = Vec::new();
8975 let mut refold_creases = Vec::new();
8976
8977 let selections = self.selections.all::<Point>(cx);
8978 let mut selections = selections.iter().peekable();
8979 let mut contiguous_row_selections = Vec::new();
8980 let mut new_selections = Vec::new();
8981
8982 while let Some(selection) = selections.next() {
8983 // Find all the selections that span a contiguous row range
8984 let (start_row, end_row) = consume_contiguous_rows(
8985 &mut contiguous_row_selections,
8986 selection,
8987 &display_map,
8988 &mut selections,
8989 );
8990
8991 // Move the text spanned by the row range to be before the line preceding the row range
8992 if start_row.0 > 0 {
8993 let range_to_move = Point::new(
8994 start_row.previous_row().0,
8995 buffer.line_len(start_row.previous_row()),
8996 )
8997 ..Point::new(
8998 end_row.previous_row().0,
8999 buffer.line_len(end_row.previous_row()),
9000 );
9001 let insertion_point = display_map
9002 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
9003 .0;
9004
9005 // Don't move lines across excerpts
9006 if buffer
9007 .excerpt_containing(insertion_point..range_to_move.end)
9008 .is_some()
9009 {
9010 let text = buffer
9011 .text_for_range(range_to_move.clone())
9012 .flat_map(|s| s.chars())
9013 .skip(1)
9014 .chain(['\n'])
9015 .collect::<String>();
9016
9017 edits.push((
9018 buffer.anchor_after(range_to_move.start)
9019 ..buffer.anchor_before(range_to_move.end),
9020 String::new(),
9021 ));
9022 let insertion_anchor = buffer.anchor_after(insertion_point);
9023 edits.push((insertion_anchor..insertion_anchor, text));
9024
9025 let row_delta = range_to_move.start.row - insertion_point.row + 1;
9026
9027 // Move selections up
9028 new_selections.extend(contiguous_row_selections.drain(..).map(
9029 |mut selection| {
9030 selection.start.row -= row_delta;
9031 selection.end.row -= row_delta;
9032 selection
9033 },
9034 ));
9035
9036 // Move folds up
9037 unfold_ranges.push(range_to_move.clone());
9038 for fold in display_map.folds_in_range(
9039 buffer.anchor_before(range_to_move.start)
9040 ..buffer.anchor_after(range_to_move.end),
9041 ) {
9042 let mut start = fold.range.start.to_point(&buffer);
9043 let mut end = fold.range.end.to_point(&buffer);
9044 start.row -= row_delta;
9045 end.row -= row_delta;
9046 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9047 }
9048 }
9049 }
9050
9051 // If we didn't move line(s), preserve the existing selections
9052 new_selections.append(&mut contiguous_row_selections);
9053 }
9054
9055 self.transact(window, cx, |this, window, cx| {
9056 this.unfold_ranges(&unfold_ranges, true, true, cx);
9057 this.buffer.update(cx, |buffer, cx| {
9058 for (range, text) in edits {
9059 buffer.edit([(range, text)], None, cx);
9060 }
9061 });
9062 this.fold_creases(refold_creases, true, window, cx);
9063 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9064 s.select(new_selections);
9065 })
9066 });
9067 }
9068
9069 pub fn move_line_down(
9070 &mut self,
9071 _: &MoveLineDown,
9072 window: &mut Window,
9073 cx: &mut Context<Self>,
9074 ) {
9075 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9076 let buffer = self.buffer.read(cx).snapshot(cx);
9077
9078 let mut edits = Vec::new();
9079 let mut unfold_ranges = Vec::new();
9080 let mut refold_creases = Vec::new();
9081
9082 let selections = self.selections.all::<Point>(cx);
9083 let mut selections = selections.iter().peekable();
9084 let mut contiguous_row_selections = Vec::new();
9085 let mut new_selections = Vec::new();
9086
9087 while let Some(selection) = selections.next() {
9088 // Find all the selections that span a contiguous row range
9089 let (start_row, end_row) = consume_contiguous_rows(
9090 &mut contiguous_row_selections,
9091 selection,
9092 &display_map,
9093 &mut selections,
9094 );
9095
9096 // Move the text spanned by the row range to be after the last line of the row range
9097 if end_row.0 <= buffer.max_point().row {
9098 let range_to_move =
9099 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
9100 let insertion_point = display_map
9101 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
9102 .0;
9103
9104 // Don't move lines across excerpt boundaries
9105 if buffer
9106 .excerpt_containing(range_to_move.start..insertion_point)
9107 .is_some()
9108 {
9109 let mut text = String::from("\n");
9110 text.extend(buffer.text_for_range(range_to_move.clone()));
9111 text.pop(); // Drop trailing newline
9112 edits.push((
9113 buffer.anchor_after(range_to_move.start)
9114 ..buffer.anchor_before(range_to_move.end),
9115 String::new(),
9116 ));
9117 let insertion_anchor = buffer.anchor_after(insertion_point);
9118 edits.push((insertion_anchor..insertion_anchor, text));
9119
9120 let row_delta = insertion_point.row - range_to_move.end.row + 1;
9121
9122 // Move selections down
9123 new_selections.extend(contiguous_row_selections.drain(..).map(
9124 |mut selection| {
9125 selection.start.row += row_delta;
9126 selection.end.row += row_delta;
9127 selection
9128 },
9129 ));
9130
9131 // Move folds down
9132 unfold_ranges.push(range_to_move.clone());
9133 for fold in display_map.folds_in_range(
9134 buffer.anchor_before(range_to_move.start)
9135 ..buffer.anchor_after(range_to_move.end),
9136 ) {
9137 let mut start = fold.range.start.to_point(&buffer);
9138 let mut end = fold.range.end.to_point(&buffer);
9139 start.row += row_delta;
9140 end.row += row_delta;
9141 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
9142 }
9143 }
9144 }
9145
9146 // If we didn't move line(s), preserve the existing selections
9147 new_selections.append(&mut contiguous_row_selections);
9148 }
9149
9150 self.transact(window, cx, |this, window, cx| {
9151 this.unfold_ranges(&unfold_ranges, true, true, cx);
9152 this.buffer.update(cx, |buffer, cx| {
9153 for (range, text) in edits {
9154 buffer.edit([(range, text)], None, cx);
9155 }
9156 });
9157 this.fold_creases(refold_creases, true, window, cx);
9158 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9159 s.select(new_selections)
9160 });
9161 });
9162 }
9163
9164 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
9165 let text_layout_details = &self.text_layout_details(window);
9166 self.transact(window, cx, |this, window, cx| {
9167 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9168 let mut edits: Vec<(Range<usize>, String)> = Default::default();
9169 let line_mode = s.line_mode;
9170 s.move_with(|display_map, selection| {
9171 if !selection.is_empty() || line_mode {
9172 return;
9173 }
9174
9175 let mut head = selection.head();
9176 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
9177 if head.column() == display_map.line_len(head.row()) {
9178 transpose_offset = display_map
9179 .buffer_snapshot
9180 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9181 }
9182
9183 if transpose_offset == 0 {
9184 return;
9185 }
9186
9187 *head.column_mut() += 1;
9188 head = display_map.clip_point(head, Bias::Right);
9189 let goal = SelectionGoal::HorizontalPosition(
9190 display_map
9191 .x_for_display_point(head, text_layout_details)
9192 .into(),
9193 );
9194 selection.collapse_to(head, goal);
9195
9196 let transpose_start = display_map
9197 .buffer_snapshot
9198 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
9199 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
9200 let transpose_end = display_map
9201 .buffer_snapshot
9202 .clip_offset(transpose_offset + 1, Bias::Right);
9203 if let Some(ch) =
9204 display_map.buffer_snapshot.chars_at(transpose_start).next()
9205 {
9206 edits.push((transpose_start..transpose_offset, String::new()));
9207 edits.push((transpose_end..transpose_end, ch.to_string()));
9208 }
9209 }
9210 });
9211 edits
9212 });
9213 this.buffer
9214 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9215 let selections = this.selections.all::<usize>(cx);
9216 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9217 s.select(selections);
9218 });
9219 });
9220 }
9221
9222 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
9223 self.rewrap_impl(RewrapOptions::default(), cx)
9224 }
9225
9226 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
9227 let buffer = self.buffer.read(cx).snapshot(cx);
9228 let selections = self.selections.all::<Point>(cx);
9229 let mut selections = selections.iter().peekable();
9230
9231 let mut edits = Vec::new();
9232 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
9233
9234 while let Some(selection) = selections.next() {
9235 let mut start_row = selection.start.row;
9236 let mut end_row = selection.end.row;
9237
9238 // Skip selections that overlap with a range that has already been rewrapped.
9239 let selection_range = start_row..end_row;
9240 if rewrapped_row_ranges
9241 .iter()
9242 .any(|range| range.overlaps(&selection_range))
9243 {
9244 continue;
9245 }
9246
9247 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
9248
9249 // Since not all lines in the selection may be at the same indent
9250 // level, choose the indent size that is the most common between all
9251 // of the lines.
9252 //
9253 // If there is a tie, we use the deepest indent.
9254 let (indent_size, indent_end) = {
9255 let mut indent_size_occurrences = HashMap::default();
9256 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
9257
9258 for row in start_row..=end_row {
9259 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
9260 rows_by_indent_size.entry(indent).or_default().push(row);
9261 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
9262 }
9263
9264 let indent_size = indent_size_occurrences
9265 .into_iter()
9266 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
9267 .map(|(indent, _)| indent)
9268 .unwrap_or_default();
9269 let row = rows_by_indent_size[&indent_size][0];
9270 let indent_end = Point::new(row, indent_size.len);
9271
9272 (indent_size, indent_end)
9273 };
9274
9275 let mut line_prefix = indent_size.chars().collect::<String>();
9276
9277 let mut inside_comment = false;
9278 if let Some(comment_prefix) =
9279 buffer
9280 .language_scope_at(selection.head())
9281 .and_then(|language| {
9282 language
9283 .line_comment_prefixes()
9284 .iter()
9285 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
9286 .cloned()
9287 })
9288 {
9289 line_prefix.push_str(&comment_prefix);
9290 inside_comment = true;
9291 }
9292
9293 let language_settings = buffer.language_settings_at(selection.head(), cx);
9294 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
9295 RewrapBehavior::InComments => inside_comment,
9296 RewrapBehavior::InSelections => !selection.is_empty(),
9297 RewrapBehavior::Anywhere => true,
9298 };
9299
9300 let should_rewrap = options.override_language_settings
9301 || allow_rewrap_based_on_language
9302 || self.hard_wrap.is_some();
9303 if !should_rewrap {
9304 continue;
9305 }
9306
9307 if selection.is_empty() {
9308 'expand_upwards: while start_row > 0 {
9309 let prev_row = start_row - 1;
9310 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
9311 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
9312 {
9313 start_row = prev_row;
9314 } else {
9315 break 'expand_upwards;
9316 }
9317 }
9318
9319 'expand_downwards: while end_row < buffer.max_point().row {
9320 let next_row = end_row + 1;
9321 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
9322 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
9323 {
9324 end_row = next_row;
9325 } else {
9326 break 'expand_downwards;
9327 }
9328 }
9329 }
9330
9331 let start = Point::new(start_row, 0);
9332 let start_offset = start.to_offset(&buffer);
9333 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
9334 let selection_text = buffer.text_for_range(start..end).collect::<String>();
9335 let Some(lines_without_prefixes) = selection_text
9336 .lines()
9337 .map(|line| {
9338 line.strip_prefix(&line_prefix)
9339 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
9340 .ok_or_else(|| {
9341 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
9342 })
9343 })
9344 .collect::<Result<Vec<_>, _>>()
9345 .log_err()
9346 else {
9347 continue;
9348 };
9349
9350 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
9351 buffer
9352 .language_settings_at(Point::new(start_row, 0), cx)
9353 .preferred_line_length as usize
9354 });
9355 let wrapped_text = wrap_with_prefix(
9356 line_prefix,
9357 lines_without_prefixes.join("\n"),
9358 wrap_column,
9359 tab_size,
9360 options.preserve_existing_whitespace,
9361 );
9362
9363 // TODO: should always use char-based diff while still supporting cursor behavior that
9364 // matches vim.
9365 let mut diff_options = DiffOptions::default();
9366 if options.override_language_settings {
9367 diff_options.max_word_diff_len = 0;
9368 diff_options.max_word_diff_line_count = 0;
9369 } else {
9370 diff_options.max_word_diff_len = usize::MAX;
9371 diff_options.max_word_diff_line_count = usize::MAX;
9372 }
9373
9374 for (old_range, new_text) in
9375 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
9376 {
9377 let edit_start = buffer.anchor_after(start_offset + old_range.start);
9378 let edit_end = buffer.anchor_after(start_offset + old_range.end);
9379 edits.push((edit_start..edit_end, new_text));
9380 }
9381
9382 rewrapped_row_ranges.push(start_row..=end_row);
9383 }
9384
9385 self.buffer
9386 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
9387 }
9388
9389 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
9390 let mut text = String::new();
9391 let buffer = self.buffer.read(cx).snapshot(cx);
9392 let mut selections = self.selections.all::<Point>(cx);
9393 let mut clipboard_selections = Vec::with_capacity(selections.len());
9394 {
9395 let max_point = buffer.max_point();
9396 let mut is_first = true;
9397 for selection in &mut selections {
9398 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9399 if is_entire_line {
9400 selection.start = Point::new(selection.start.row, 0);
9401 if !selection.is_empty() && selection.end.column == 0 {
9402 selection.end = cmp::min(max_point, selection.end);
9403 } else {
9404 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
9405 }
9406 selection.goal = SelectionGoal::None;
9407 }
9408 if is_first {
9409 is_first = false;
9410 } else {
9411 text += "\n";
9412 }
9413 let mut len = 0;
9414 for chunk in buffer.text_for_range(selection.start..selection.end) {
9415 text.push_str(chunk);
9416 len += chunk.len();
9417 }
9418 clipboard_selections.push(ClipboardSelection {
9419 len,
9420 is_entire_line,
9421 first_line_indent: buffer
9422 .indent_size_for_line(MultiBufferRow(selection.start.row))
9423 .len,
9424 });
9425 }
9426 }
9427
9428 self.transact(window, cx, |this, window, cx| {
9429 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9430 s.select(selections);
9431 });
9432 this.insert("", window, cx);
9433 });
9434 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
9435 }
9436
9437 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
9438 let item = self.cut_common(window, cx);
9439 cx.write_to_clipboard(item);
9440 }
9441
9442 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
9443 self.change_selections(None, window, cx, |s| {
9444 s.move_with(|snapshot, sel| {
9445 if sel.is_empty() {
9446 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
9447 }
9448 });
9449 });
9450 let item = self.cut_common(window, cx);
9451 cx.set_global(KillRing(item))
9452 }
9453
9454 pub fn kill_ring_yank(
9455 &mut self,
9456 _: &KillRingYank,
9457 window: &mut Window,
9458 cx: &mut Context<Self>,
9459 ) {
9460 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
9461 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
9462 (kill_ring.text().to_string(), kill_ring.metadata_json())
9463 } else {
9464 return;
9465 }
9466 } else {
9467 return;
9468 };
9469 self.do_paste(&text, metadata, false, window, cx);
9470 }
9471
9472 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
9473 self.do_copy(true, cx);
9474 }
9475
9476 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
9477 self.do_copy(false, cx);
9478 }
9479
9480 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
9481 let selections = self.selections.all::<Point>(cx);
9482 let buffer = self.buffer.read(cx).read(cx);
9483 let mut text = String::new();
9484
9485 let mut clipboard_selections = Vec::with_capacity(selections.len());
9486 {
9487 let max_point = buffer.max_point();
9488 let mut is_first = true;
9489 for selection in &selections {
9490 let mut start = selection.start;
9491 let mut end = selection.end;
9492 let is_entire_line = selection.is_empty() || self.selections.line_mode;
9493 if is_entire_line {
9494 start = Point::new(start.row, 0);
9495 end = cmp::min(max_point, Point::new(end.row + 1, 0));
9496 }
9497
9498 let mut trimmed_selections = Vec::new();
9499 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
9500 let row = MultiBufferRow(start.row);
9501 let first_indent = buffer.indent_size_for_line(row);
9502 if first_indent.len == 0 || start.column > first_indent.len {
9503 trimmed_selections.push(start..end);
9504 } else {
9505 trimmed_selections.push(
9506 Point::new(row.0, first_indent.len)
9507 ..Point::new(row.0, buffer.line_len(row)),
9508 );
9509 for row in start.row + 1..=end.row {
9510 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
9511 if row_indent_size.len >= first_indent.len {
9512 trimmed_selections.push(
9513 Point::new(row, first_indent.len)
9514 ..Point::new(row, buffer.line_len(MultiBufferRow(row))),
9515 );
9516 } else {
9517 trimmed_selections.clear();
9518 trimmed_selections.push(start..end);
9519 break;
9520 }
9521 }
9522 }
9523 } else {
9524 trimmed_selections.push(start..end);
9525 }
9526
9527 for trimmed_range in trimmed_selections {
9528 if is_first {
9529 is_first = false;
9530 } else {
9531 text += "\n";
9532 }
9533 let mut len = 0;
9534 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
9535 text.push_str(chunk);
9536 len += chunk.len();
9537 }
9538 clipboard_selections.push(ClipboardSelection {
9539 len,
9540 is_entire_line,
9541 first_line_indent: buffer
9542 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
9543 .len,
9544 });
9545 }
9546 }
9547 }
9548
9549 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
9550 text,
9551 clipboard_selections,
9552 ));
9553 }
9554
9555 pub fn do_paste(
9556 &mut self,
9557 text: &String,
9558 clipboard_selections: Option<Vec<ClipboardSelection>>,
9559 handle_entire_lines: bool,
9560 window: &mut Window,
9561 cx: &mut Context<Self>,
9562 ) {
9563 if self.read_only(cx) {
9564 return;
9565 }
9566
9567 let clipboard_text = Cow::Borrowed(text);
9568
9569 self.transact(window, cx, |this, window, cx| {
9570 if let Some(mut clipboard_selections) = clipboard_selections {
9571 let old_selections = this.selections.all::<usize>(cx);
9572 let all_selections_were_entire_line =
9573 clipboard_selections.iter().all(|s| s.is_entire_line);
9574 let first_selection_indent_column =
9575 clipboard_selections.first().map(|s| s.first_line_indent);
9576 if clipboard_selections.len() != old_selections.len() {
9577 clipboard_selections.drain(..);
9578 }
9579 let cursor_offset = this.selections.last::<usize>(cx).head();
9580 let mut auto_indent_on_paste = true;
9581
9582 this.buffer.update(cx, |buffer, cx| {
9583 let snapshot = buffer.read(cx);
9584 auto_indent_on_paste = snapshot
9585 .language_settings_at(cursor_offset, cx)
9586 .auto_indent_on_paste;
9587
9588 let mut start_offset = 0;
9589 let mut edits = Vec::new();
9590 let mut original_indent_columns = Vec::new();
9591 for (ix, selection) in old_selections.iter().enumerate() {
9592 let to_insert;
9593 let entire_line;
9594 let original_indent_column;
9595 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
9596 let end_offset = start_offset + clipboard_selection.len;
9597 to_insert = &clipboard_text[start_offset..end_offset];
9598 entire_line = clipboard_selection.is_entire_line;
9599 start_offset = end_offset + 1;
9600 original_indent_column = Some(clipboard_selection.first_line_indent);
9601 } else {
9602 to_insert = clipboard_text.as_str();
9603 entire_line = all_selections_were_entire_line;
9604 original_indent_column = first_selection_indent_column
9605 }
9606
9607 // If the corresponding selection was empty when this slice of the
9608 // clipboard text was written, then the entire line containing the
9609 // selection was copied. If this selection is also currently empty,
9610 // then paste the line before the current line of the buffer.
9611 let range = if selection.is_empty() && handle_entire_lines && entire_line {
9612 let column = selection.start.to_point(&snapshot).column as usize;
9613 let line_start = selection.start - column;
9614 line_start..line_start
9615 } else {
9616 selection.range()
9617 };
9618
9619 edits.push((range, to_insert));
9620 original_indent_columns.push(original_indent_column);
9621 }
9622 drop(snapshot);
9623
9624 buffer.edit(
9625 edits,
9626 if auto_indent_on_paste {
9627 Some(AutoindentMode::Block {
9628 original_indent_columns,
9629 })
9630 } else {
9631 None
9632 },
9633 cx,
9634 );
9635 });
9636
9637 let selections = this.selections.all::<usize>(cx);
9638 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9639 s.select(selections)
9640 });
9641 } else {
9642 this.insert(&clipboard_text, window, cx);
9643 }
9644 });
9645 }
9646
9647 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9648 if let Some(item) = cx.read_from_clipboard() {
9649 let entries = item.entries();
9650
9651 match entries.first() {
9652 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9653 // of all the pasted entries.
9654 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9655 .do_paste(
9656 clipboard_string.text(),
9657 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9658 true,
9659 window,
9660 cx,
9661 ),
9662 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9663 }
9664 }
9665 }
9666
9667 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9668 if self.read_only(cx) {
9669 return;
9670 }
9671
9672 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9673 if let Some((selections, _)) =
9674 self.selection_history.transaction(transaction_id).cloned()
9675 {
9676 self.change_selections(None, window, cx, |s| {
9677 s.select_anchors(selections.to_vec());
9678 });
9679 } else {
9680 log::error!(
9681 "No entry in selection_history found for undo. \
9682 This may correspond to a bug where undo does not update the selection. \
9683 If this is occurring, please add details to \
9684 https://github.com/zed-industries/zed/issues/22692"
9685 );
9686 }
9687 self.request_autoscroll(Autoscroll::fit(), cx);
9688 self.unmark_text(window, cx);
9689 self.refresh_inline_completion(true, false, window, cx);
9690 cx.emit(EditorEvent::Edited { transaction_id });
9691 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9692 }
9693 }
9694
9695 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9696 if self.read_only(cx) {
9697 return;
9698 }
9699
9700 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9701 if let Some((_, Some(selections))) =
9702 self.selection_history.transaction(transaction_id).cloned()
9703 {
9704 self.change_selections(None, window, cx, |s| {
9705 s.select_anchors(selections.to_vec());
9706 });
9707 } else {
9708 log::error!(
9709 "No entry in selection_history found for redo. \
9710 This may correspond to a bug where undo does not update the selection. \
9711 If this is occurring, please add details to \
9712 https://github.com/zed-industries/zed/issues/22692"
9713 );
9714 }
9715 self.request_autoscroll(Autoscroll::fit(), cx);
9716 self.unmark_text(window, cx);
9717 self.refresh_inline_completion(true, false, window, cx);
9718 cx.emit(EditorEvent::Edited { transaction_id });
9719 }
9720 }
9721
9722 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9723 self.buffer
9724 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9725 }
9726
9727 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9728 self.buffer
9729 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9730 }
9731
9732 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9733 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9734 let line_mode = s.line_mode;
9735 s.move_with(|map, selection| {
9736 let cursor = if selection.is_empty() && !line_mode {
9737 movement::left(map, selection.start)
9738 } else {
9739 selection.start
9740 };
9741 selection.collapse_to(cursor, SelectionGoal::None);
9742 });
9743 })
9744 }
9745
9746 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9747 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9748 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9749 })
9750 }
9751
9752 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9753 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9754 let line_mode = s.line_mode;
9755 s.move_with(|map, selection| {
9756 let cursor = if selection.is_empty() && !line_mode {
9757 movement::right(map, selection.end)
9758 } else {
9759 selection.end
9760 };
9761 selection.collapse_to(cursor, SelectionGoal::None)
9762 });
9763 })
9764 }
9765
9766 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9767 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9768 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9769 })
9770 }
9771
9772 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9773 if self.take_rename(true, window, cx).is_some() {
9774 return;
9775 }
9776
9777 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9778 cx.propagate();
9779 return;
9780 }
9781
9782 let text_layout_details = &self.text_layout_details(window);
9783 let selection_count = self.selections.count();
9784 let first_selection = self.selections.first_anchor();
9785
9786 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9787 let line_mode = s.line_mode;
9788 s.move_with(|map, selection| {
9789 if !selection.is_empty() && !line_mode {
9790 selection.goal = SelectionGoal::None;
9791 }
9792 let (cursor, goal) = movement::up(
9793 map,
9794 selection.start,
9795 selection.goal,
9796 false,
9797 text_layout_details,
9798 );
9799 selection.collapse_to(cursor, goal);
9800 });
9801 });
9802
9803 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9804 {
9805 cx.propagate();
9806 }
9807 }
9808
9809 pub fn move_up_by_lines(
9810 &mut self,
9811 action: &MoveUpByLines,
9812 window: &mut Window,
9813 cx: &mut Context<Self>,
9814 ) {
9815 if self.take_rename(true, window, cx).is_some() {
9816 return;
9817 }
9818
9819 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9820 cx.propagate();
9821 return;
9822 }
9823
9824 let text_layout_details = &self.text_layout_details(window);
9825
9826 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9827 let line_mode = s.line_mode;
9828 s.move_with(|map, selection| {
9829 if !selection.is_empty() && !line_mode {
9830 selection.goal = SelectionGoal::None;
9831 }
9832 let (cursor, goal) = movement::up_by_rows(
9833 map,
9834 selection.start,
9835 action.lines,
9836 selection.goal,
9837 false,
9838 text_layout_details,
9839 );
9840 selection.collapse_to(cursor, goal);
9841 });
9842 })
9843 }
9844
9845 pub fn move_down_by_lines(
9846 &mut self,
9847 action: &MoveDownByLines,
9848 window: &mut Window,
9849 cx: &mut Context<Self>,
9850 ) {
9851 if self.take_rename(true, window, cx).is_some() {
9852 return;
9853 }
9854
9855 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9856 cx.propagate();
9857 return;
9858 }
9859
9860 let text_layout_details = &self.text_layout_details(window);
9861
9862 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9863 let line_mode = s.line_mode;
9864 s.move_with(|map, selection| {
9865 if !selection.is_empty() && !line_mode {
9866 selection.goal = SelectionGoal::None;
9867 }
9868 let (cursor, goal) = movement::down_by_rows(
9869 map,
9870 selection.start,
9871 action.lines,
9872 selection.goal,
9873 false,
9874 text_layout_details,
9875 );
9876 selection.collapse_to(cursor, goal);
9877 });
9878 })
9879 }
9880
9881 pub fn select_down_by_lines(
9882 &mut self,
9883 action: &SelectDownByLines,
9884 window: &mut Window,
9885 cx: &mut Context<Self>,
9886 ) {
9887 let text_layout_details = &self.text_layout_details(window);
9888 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9889 s.move_heads_with(|map, head, goal| {
9890 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9891 })
9892 })
9893 }
9894
9895 pub fn select_up_by_lines(
9896 &mut self,
9897 action: &SelectUpByLines,
9898 window: &mut Window,
9899 cx: &mut Context<Self>,
9900 ) {
9901 let text_layout_details = &self.text_layout_details(window);
9902 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9903 s.move_heads_with(|map, head, goal| {
9904 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9905 })
9906 })
9907 }
9908
9909 pub fn select_page_up(
9910 &mut self,
9911 _: &SelectPageUp,
9912 window: &mut Window,
9913 cx: &mut Context<Self>,
9914 ) {
9915 let Some(row_count) = self.visible_row_count() else {
9916 return;
9917 };
9918
9919 let text_layout_details = &self.text_layout_details(window);
9920
9921 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9922 s.move_heads_with(|map, head, goal| {
9923 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9924 })
9925 })
9926 }
9927
9928 pub fn move_page_up(
9929 &mut self,
9930 action: &MovePageUp,
9931 window: &mut Window,
9932 cx: &mut Context<Self>,
9933 ) {
9934 if self.take_rename(true, window, cx).is_some() {
9935 return;
9936 }
9937
9938 if self
9939 .context_menu
9940 .borrow_mut()
9941 .as_mut()
9942 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9943 .unwrap_or(false)
9944 {
9945 return;
9946 }
9947
9948 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9949 cx.propagate();
9950 return;
9951 }
9952
9953 let Some(row_count) = self.visible_row_count() else {
9954 return;
9955 };
9956
9957 let autoscroll = if action.center_cursor {
9958 Autoscroll::center()
9959 } else {
9960 Autoscroll::fit()
9961 };
9962
9963 let text_layout_details = &self.text_layout_details(window);
9964
9965 self.change_selections(Some(autoscroll), window, cx, |s| {
9966 let line_mode = s.line_mode;
9967 s.move_with(|map, selection| {
9968 if !selection.is_empty() && !line_mode {
9969 selection.goal = SelectionGoal::None;
9970 }
9971 let (cursor, goal) = movement::up_by_rows(
9972 map,
9973 selection.end,
9974 row_count,
9975 selection.goal,
9976 false,
9977 text_layout_details,
9978 );
9979 selection.collapse_to(cursor, goal);
9980 });
9981 });
9982 }
9983
9984 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9985 let text_layout_details = &self.text_layout_details(window);
9986 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9987 s.move_heads_with(|map, head, goal| {
9988 movement::up(map, head, goal, false, text_layout_details)
9989 })
9990 })
9991 }
9992
9993 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9994 self.take_rename(true, window, cx);
9995
9996 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9997 cx.propagate();
9998 return;
9999 }
10000
10001 let text_layout_details = &self.text_layout_details(window);
10002 let selection_count = self.selections.count();
10003 let first_selection = self.selections.first_anchor();
10004
10005 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10006 let line_mode = s.line_mode;
10007 s.move_with(|map, selection| {
10008 if !selection.is_empty() && !line_mode {
10009 selection.goal = SelectionGoal::None;
10010 }
10011 let (cursor, goal) = movement::down(
10012 map,
10013 selection.end,
10014 selection.goal,
10015 false,
10016 text_layout_details,
10017 );
10018 selection.collapse_to(cursor, goal);
10019 });
10020 });
10021
10022 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
10023 {
10024 cx.propagate();
10025 }
10026 }
10027
10028 pub fn select_page_down(
10029 &mut self,
10030 _: &SelectPageDown,
10031 window: &mut Window,
10032 cx: &mut Context<Self>,
10033 ) {
10034 let Some(row_count) = self.visible_row_count() else {
10035 return;
10036 };
10037
10038 let text_layout_details = &self.text_layout_details(window);
10039
10040 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10041 s.move_heads_with(|map, head, goal| {
10042 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
10043 })
10044 })
10045 }
10046
10047 pub fn move_page_down(
10048 &mut self,
10049 action: &MovePageDown,
10050 window: &mut Window,
10051 cx: &mut Context<Self>,
10052 ) {
10053 if self.take_rename(true, window, cx).is_some() {
10054 return;
10055 }
10056
10057 if self
10058 .context_menu
10059 .borrow_mut()
10060 .as_mut()
10061 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
10062 .unwrap_or(false)
10063 {
10064 return;
10065 }
10066
10067 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10068 cx.propagate();
10069 return;
10070 }
10071
10072 let Some(row_count) = self.visible_row_count() else {
10073 return;
10074 };
10075
10076 let autoscroll = if action.center_cursor {
10077 Autoscroll::center()
10078 } else {
10079 Autoscroll::fit()
10080 };
10081
10082 let text_layout_details = &self.text_layout_details(window);
10083 self.change_selections(Some(autoscroll), window, cx, |s| {
10084 let line_mode = s.line_mode;
10085 s.move_with(|map, selection| {
10086 if !selection.is_empty() && !line_mode {
10087 selection.goal = SelectionGoal::None;
10088 }
10089 let (cursor, goal) = movement::down_by_rows(
10090 map,
10091 selection.end,
10092 row_count,
10093 selection.goal,
10094 false,
10095 text_layout_details,
10096 );
10097 selection.collapse_to(cursor, goal);
10098 });
10099 });
10100 }
10101
10102 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
10103 let text_layout_details = &self.text_layout_details(window);
10104 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10105 s.move_heads_with(|map, head, goal| {
10106 movement::down(map, head, goal, false, text_layout_details)
10107 })
10108 });
10109 }
10110
10111 pub fn context_menu_first(
10112 &mut self,
10113 _: &ContextMenuFirst,
10114 _window: &mut Window,
10115 cx: &mut Context<Self>,
10116 ) {
10117 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10118 context_menu.select_first(self.completion_provider.as_deref(), cx);
10119 }
10120 }
10121
10122 pub fn context_menu_prev(
10123 &mut self,
10124 _: &ContextMenuPrevious,
10125 _window: &mut Window,
10126 cx: &mut Context<Self>,
10127 ) {
10128 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10129 context_menu.select_prev(self.completion_provider.as_deref(), cx);
10130 }
10131 }
10132
10133 pub fn context_menu_next(
10134 &mut self,
10135 _: &ContextMenuNext,
10136 _window: &mut Window,
10137 cx: &mut Context<Self>,
10138 ) {
10139 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10140 context_menu.select_next(self.completion_provider.as_deref(), cx);
10141 }
10142 }
10143
10144 pub fn context_menu_last(
10145 &mut self,
10146 _: &ContextMenuLast,
10147 _window: &mut Window,
10148 cx: &mut Context<Self>,
10149 ) {
10150 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
10151 context_menu.select_last(self.completion_provider.as_deref(), cx);
10152 }
10153 }
10154
10155 pub fn move_to_previous_word_start(
10156 &mut self,
10157 _: &MoveToPreviousWordStart,
10158 window: &mut Window,
10159 cx: &mut Context<Self>,
10160 ) {
10161 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10162 s.move_cursors_with(|map, head, _| {
10163 (
10164 movement::previous_word_start(map, head),
10165 SelectionGoal::None,
10166 )
10167 });
10168 })
10169 }
10170
10171 pub fn move_to_previous_subword_start(
10172 &mut self,
10173 _: &MoveToPreviousSubwordStart,
10174 window: &mut Window,
10175 cx: &mut Context<Self>,
10176 ) {
10177 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10178 s.move_cursors_with(|map, head, _| {
10179 (
10180 movement::previous_subword_start(map, head),
10181 SelectionGoal::None,
10182 )
10183 });
10184 })
10185 }
10186
10187 pub fn select_to_previous_word_start(
10188 &mut self,
10189 _: &SelectToPreviousWordStart,
10190 window: &mut Window,
10191 cx: &mut Context<Self>,
10192 ) {
10193 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10194 s.move_heads_with(|map, head, _| {
10195 (
10196 movement::previous_word_start(map, head),
10197 SelectionGoal::None,
10198 )
10199 });
10200 })
10201 }
10202
10203 pub fn select_to_previous_subword_start(
10204 &mut self,
10205 _: &SelectToPreviousSubwordStart,
10206 window: &mut Window,
10207 cx: &mut Context<Self>,
10208 ) {
10209 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10210 s.move_heads_with(|map, head, _| {
10211 (
10212 movement::previous_subword_start(map, head),
10213 SelectionGoal::None,
10214 )
10215 });
10216 })
10217 }
10218
10219 pub fn delete_to_previous_word_start(
10220 &mut self,
10221 action: &DeleteToPreviousWordStart,
10222 window: &mut Window,
10223 cx: &mut Context<Self>,
10224 ) {
10225 self.transact(window, cx, |this, window, cx| {
10226 this.select_autoclose_pair(window, cx);
10227 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10228 let line_mode = s.line_mode;
10229 s.move_with(|map, selection| {
10230 if selection.is_empty() && !line_mode {
10231 let cursor = if action.ignore_newlines {
10232 movement::previous_word_start(map, selection.head())
10233 } else {
10234 movement::previous_word_start_or_newline(map, selection.head())
10235 };
10236 selection.set_head(cursor, SelectionGoal::None);
10237 }
10238 });
10239 });
10240 this.insert("", window, cx);
10241 });
10242 }
10243
10244 pub fn delete_to_previous_subword_start(
10245 &mut self,
10246 _: &DeleteToPreviousSubwordStart,
10247 window: &mut Window,
10248 cx: &mut Context<Self>,
10249 ) {
10250 self.transact(window, cx, |this, window, cx| {
10251 this.select_autoclose_pair(window, cx);
10252 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10253 let line_mode = s.line_mode;
10254 s.move_with(|map, selection| {
10255 if selection.is_empty() && !line_mode {
10256 let cursor = movement::previous_subword_start(map, selection.head());
10257 selection.set_head(cursor, SelectionGoal::None);
10258 }
10259 });
10260 });
10261 this.insert("", window, cx);
10262 });
10263 }
10264
10265 pub fn move_to_next_word_end(
10266 &mut self,
10267 _: &MoveToNextWordEnd,
10268 window: &mut Window,
10269 cx: &mut Context<Self>,
10270 ) {
10271 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10272 s.move_cursors_with(|map, head, _| {
10273 (movement::next_word_end(map, head), SelectionGoal::None)
10274 });
10275 })
10276 }
10277
10278 pub fn move_to_next_subword_end(
10279 &mut self,
10280 _: &MoveToNextSubwordEnd,
10281 window: &mut Window,
10282 cx: &mut Context<Self>,
10283 ) {
10284 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10285 s.move_cursors_with(|map, head, _| {
10286 (movement::next_subword_end(map, head), SelectionGoal::None)
10287 });
10288 })
10289 }
10290
10291 pub fn select_to_next_word_end(
10292 &mut self,
10293 _: &SelectToNextWordEnd,
10294 window: &mut Window,
10295 cx: &mut Context<Self>,
10296 ) {
10297 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10298 s.move_heads_with(|map, head, _| {
10299 (movement::next_word_end(map, head), SelectionGoal::None)
10300 });
10301 })
10302 }
10303
10304 pub fn select_to_next_subword_end(
10305 &mut self,
10306 _: &SelectToNextSubwordEnd,
10307 window: &mut Window,
10308 cx: &mut Context<Self>,
10309 ) {
10310 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10311 s.move_heads_with(|map, head, _| {
10312 (movement::next_subword_end(map, head), SelectionGoal::None)
10313 });
10314 })
10315 }
10316
10317 pub fn delete_to_next_word_end(
10318 &mut self,
10319 action: &DeleteToNextWordEnd,
10320 window: &mut Window,
10321 cx: &mut Context<Self>,
10322 ) {
10323 self.transact(window, cx, |this, window, cx| {
10324 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10325 let line_mode = s.line_mode;
10326 s.move_with(|map, selection| {
10327 if selection.is_empty() && !line_mode {
10328 let cursor = if action.ignore_newlines {
10329 movement::next_word_end(map, selection.head())
10330 } else {
10331 movement::next_word_end_or_newline(map, selection.head())
10332 };
10333 selection.set_head(cursor, SelectionGoal::None);
10334 }
10335 });
10336 });
10337 this.insert("", window, cx);
10338 });
10339 }
10340
10341 pub fn delete_to_next_subword_end(
10342 &mut self,
10343 _: &DeleteToNextSubwordEnd,
10344 window: &mut Window,
10345 cx: &mut Context<Self>,
10346 ) {
10347 self.transact(window, cx, |this, window, cx| {
10348 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10349 s.move_with(|map, selection| {
10350 if selection.is_empty() {
10351 let cursor = movement::next_subword_end(map, selection.head());
10352 selection.set_head(cursor, SelectionGoal::None);
10353 }
10354 });
10355 });
10356 this.insert("", window, cx);
10357 });
10358 }
10359
10360 pub fn move_to_beginning_of_line(
10361 &mut self,
10362 action: &MoveToBeginningOfLine,
10363 window: &mut Window,
10364 cx: &mut Context<Self>,
10365 ) {
10366 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10367 s.move_cursors_with(|map, head, _| {
10368 (
10369 movement::indented_line_beginning(
10370 map,
10371 head,
10372 action.stop_at_soft_wraps,
10373 action.stop_at_indent,
10374 ),
10375 SelectionGoal::None,
10376 )
10377 });
10378 })
10379 }
10380
10381 pub fn select_to_beginning_of_line(
10382 &mut self,
10383 action: &SelectToBeginningOfLine,
10384 window: &mut Window,
10385 cx: &mut Context<Self>,
10386 ) {
10387 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10388 s.move_heads_with(|map, head, _| {
10389 (
10390 movement::indented_line_beginning(
10391 map,
10392 head,
10393 action.stop_at_soft_wraps,
10394 action.stop_at_indent,
10395 ),
10396 SelectionGoal::None,
10397 )
10398 });
10399 });
10400 }
10401
10402 pub fn delete_to_beginning_of_line(
10403 &mut self,
10404 action: &DeleteToBeginningOfLine,
10405 window: &mut Window,
10406 cx: &mut Context<Self>,
10407 ) {
10408 self.transact(window, cx, |this, window, cx| {
10409 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10410 s.move_with(|_, selection| {
10411 selection.reversed = true;
10412 });
10413 });
10414
10415 this.select_to_beginning_of_line(
10416 &SelectToBeginningOfLine {
10417 stop_at_soft_wraps: false,
10418 stop_at_indent: action.stop_at_indent,
10419 },
10420 window,
10421 cx,
10422 );
10423 this.backspace(&Backspace, window, cx);
10424 });
10425 }
10426
10427 pub fn move_to_end_of_line(
10428 &mut self,
10429 action: &MoveToEndOfLine,
10430 window: &mut Window,
10431 cx: &mut Context<Self>,
10432 ) {
10433 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10434 s.move_cursors_with(|map, head, _| {
10435 (
10436 movement::line_end(map, head, action.stop_at_soft_wraps),
10437 SelectionGoal::None,
10438 )
10439 });
10440 })
10441 }
10442
10443 pub fn select_to_end_of_line(
10444 &mut self,
10445 action: &SelectToEndOfLine,
10446 window: &mut Window,
10447 cx: &mut Context<Self>,
10448 ) {
10449 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10450 s.move_heads_with(|map, head, _| {
10451 (
10452 movement::line_end(map, head, action.stop_at_soft_wraps),
10453 SelectionGoal::None,
10454 )
10455 });
10456 })
10457 }
10458
10459 pub fn delete_to_end_of_line(
10460 &mut self,
10461 _: &DeleteToEndOfLine,
10462 window: &mut Window,
10463 cx: &mut Context<Self>,
10464 ) {
10465 self.transact(window, cx, |this, window, cx| {
10466 this.select_to_end_of_line(
10467 &SelectToEndOfLine {
10468 stop_at_soft_wraps: false,
10469 },
10470 window,
10471 cx,
10472 );
10473 this.delete(&Delete, window, cx);
10474 });
10475 }
10476
10477 pub fn cut_to_end_of_line(
10478 &mut self,
10479 _: &CutToEndOfLine,
10480 window: &mut Window,
10481 cx: &mut Context<Self>,
10482 ) {
10483 self.transact(window, cx, |this, window, cx| {
10484 this.select_to_end_of_line(
10485 &SelectToEndOfLine {
10486 stop_at_soft_wraps: false,
10487 },
10488 window,
10489 cx,
10490 );
10491 this.cut(&Cut, window, cx);
10492 });
10493 }
10494
10495 pub fn move_to_start_of_paragraph(
10496 &mut self,
10497 _: &MoveToStartOfParagraph,
10498 window: &mut Window,
10499 cx: &mut Context<Self>,
10500 ) {
10501 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10502 cx.propagate();
10503 return;
10504 }
10505
10506 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10507 s.move_with(|map, selection| {
10508 selection.collapse_to(
10509 movement::start_of_paragraph(map, selection.head(), 1),
10510 SelectionGoal::None,
10511 )
10512 });
10513 })
10514 }
10515
10516 pub fn move_to_end_of_paragraph(
10517 &mut self,
10518 _: &MoveToEndOfParagraph,
10519 window: &mut Window,
10520 cx: &mut Context<Self>,
10521 ) {
10522 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10523 cx.propagate();
10524 return;
10525 }
10526
10527 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10528 s.move_with(|map, selection| {
10529 selection.collapse_to(
10530 movement::end_of_paragraph(map, selection.head(), 1),
10531 SelectionGoal::None,
10532 )
10533 });
10534 })
10535 }
10536
10537 pub fn select_to_start_of_paragraph(
10538 &mut self,
10539 _: &SelectToStartOfParagraph,
10540 window: &mut Window,
10541 cx: &mut Context<Self>,
10542 ) {
10543 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10544 cx.propagate();
10545 return;
10546 }
10547
10548 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10549 s.move_heads_with(|map, head, _| {
10550 (
10551 movement::start_of_paragraph(map, head, 1),
10552 SelectionGoal::None,
10553 )
10554 });
10555 })
10556 }
10557
10558 pub fn select_to_end_of_paragraph(
10559 &mut self,
10560 _: &SelectToEndOfParagraph,
10561 window: &mut Window,
10562 cx: &mut Context<Self>,
10563 ) {
10564 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10565 cx.propagate();
10566 return;
10567 }
10568
10569 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10570 s.move_heads_with(|map, head, _| {
10571 (
10572 movement::end_of_paragraph(map, head, 1),
10573 SelectionGoal::None,
10574 )
10575 });
10576 })
10577 }
10578
10579 pub fn move_to_start_of_excerpt(
10580 &mut self,
10581 _: &MoveToStartOfExcerpt,
10582 window: &mut Window,
10583 cx: &mut Context<Self>,
10584 ) {
10585 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10586 cx.propagate();
10587 return;
10588 }
10589
10590 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10591 s.move_with(|map, selection| {
10592 selection.collapse_to(
10593 movement::start_of_excerpt(
10594 map,
10595 selection.head(),
10596 workspace::searchable::Direction::Prev,
10597 ),
10598 SelectionGoal::None,
10599 )
10600 });
10601 })
10602 }
10603
10604 pub fn move_to_start_of_next_excerpt(
10605 &mut self,
10606 _: &MoveToStartOfNextExcerpt,
10607 window: &mut Window,
10608 cx: &mut Context<Self>,
10609 ) {
10610 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10611 cx.propagate();
10612 return;
10613 }
10614
10615 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10616 s.move_with(|map, selection| {
10617 selection.collapse_to(
10618 movement::start_of_excerpt(
10619 map,
10620 selection.head(),
10621 workspace::searchable::Direction::Next,
10622 ),
10623 SelectionGoal::None,
10624 )
10625 });
10626 })
10627 }
10628
10629 pub fn move_to_end_of_excerpt(
10630 &mut self,
10631 _: &MoveToEndOfExcerpt,
10632 window: &mut Window,
10633 cx: &mut Context<Self>,
10634 ) {
10635 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10636 cx.propagate();
10637 return;
10638 }
10639
10640 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10641 s.move_with(|map, selection| {
10642 selection.collapse_to(
10643 movement::end_of_excerpt(
10644 map,
10645 selection.head(),
10646 workspace::searchable::Direction::Next,
10647 ),
10648 SelectionGoal::None,
10649 )
10650 });
10651 })
10652 }
10653
10654 pub fn move_to_end_of_previous_excerpt(
10655 &mut self,
10656 _: &MoveToEndOfPreviousExcerpt,
10657 window: &mut Window,
10658 cx: &mut Context<Self>,
10659 ) {
10660 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10661 cx.propagate();
10662 return;
10663 }
10664
10665 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10666 s.move_with(|map, selection| {
10667 selection.collapse_to(
10668 movement::end_of_excerpt(
10669 map,
10670 selection.head(),
10671 workspace::searchable::Direction::Prev,
10672 ),
10673 SelectionGoal::None,
10674 )
10675 });
10676 })
10677 }
10678
10679 pub fn select_to_start_of_excerpt(
10680 &mut self,
10681 _: &SelectToStartOfExcerpt,
10682 window: &mut Window,
10683 cx: &mut Context<Self>,
10684 ) {
10685 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10686 cx.propagate();
10687 return;
10688 }
10689
10690 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10691 s.move_heads_with(|map, head, _| {
10692 (
10693 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10694 SelectionGoal::None,
10695 )
10696 });
10697 })
10698 }
10699
10700 pub fn select_to_start_of_next_excerpt(
10701 &mut self,
10702 _: &SelectToStartOfNextExcerpt,
10703 window: &mut Window,
10704 cx: &mut Context<Self>,
10705 ) {
10706 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10707 cx.propagate();
10708 return;
10709 }
10710
10711 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10712 s.move_heads_with(|map, head, _| {
10713 (
10714 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10715 SelectionGoal::None,
10716 )
10717 });
10718 })
10719 }
10720
10721 pub fn select_to_end_of_excerpt(
10722 &mut self,
10723 _: &SelectToEndOfExcerpt,
10724 window: &mut Window,
10725 cx: &mut Context<Self>,
10726 ) {
10727 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10728 cx.propagate();
10729 return;
10730 }
10731
10732 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10733 s.move_heads_with(|map, head, _| {
10734 (
10735 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10736 SelectionGoal::None,
10737 )
10738 });
10739 })
10740 }
10741
10742 pub fn select_to_end_of_previous_excerpt(
10743 &mut self,
10744 _: &SelectToEndOfPreviousExcerpt,
10745 window: &mut Window,
10746 cx: &mut Context<Self>,
10747 ) {
10748 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10749 cx.propagate();
10750 return;
10751 }
10752
10753 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10754 s.move_heads_with(|map, head, _| {
10755 (
10756 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10757 SelectionGoal::None,
10758 )
10759 });
10760 })
10761 }
10762
10763 pub fn move_to_beginning(
10764 &mut self,
10765 _: &MoveToBeginning,
10766 window: &mut Window,
10767 cx: &mut Context<Self>,
10768 ) {
10769 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10770 cx.propagate();
10771 return;
10772 }
10773
10774 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10775 s.select_ranges(vec![0..0]);
10776 });
10777 }
10778
10779 pub fn select_to_beginning(
10780 &mut self,
10781 _: &SelectToBeginning,
10782 window: &mut Window,
10783 cx: &mut Context<Self>,
10784 ) {
10785 let mut selection = self.selections.last::<Point>(cx);
10786 selection.set_head(Point::zero(), SelectionGoal::None);
10787
10788 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10789 s.select(vec![selection]);
10790 });
10791 }
10792
10793 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10794 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10795 cx.propagate();
10796 return;
10797 }
10798
10799 let cursor = self.buffer.read(cx).read(cx).len();
10800 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10801 s.select_ranges(vec![cursor..cursor])
10802 });
10803 }
10804
10805 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10806 self.nav_history = nav_history;
10807 }
10808
10809 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10810 self.nav_history.as_ref()
10811 }
10812
10813 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
10814 self.push_to_nav_history(self.selections.newest_anchor().head(), None, false, cx);
10815 }
10816
10817 fn push_to_nav_history(
10818 &mut self,
10819 cursor_anchor: Anchor,
10820 new_position: Option<Point>,
10821 is_deactivate: bool,
10822 cx: &mut Context<Self>,
10823 ) {
10824 if let Some(nav_history) = self.nav_history.as_mut() {
10825 let buffer = self.buffer.read(cx).read(cx);
10826 let cursor_position = cursor_anchor.to_point(&buffer);
10827 let scroll_state = self.scroll_manager.anchor();
10828 let scroll_top_row = scroll_state.top_row(&buffer);
10829 drop(buffer);
10830
10831 if let Some(new_position) = new_position {
10832 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10833 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10834 return;
10835 }
10836 }
10837
10838 nav_history.push(
10839 Some(NavigationData {
10840 cursor_anchor,
10841 cursor_position,
10842 scroll_anchor: scroll_state,
10843 scroll_top_row,
10844 }),
10845 cx,
10846 );
10847 cx.emit(EditorEvent::PushedToNavHistory {
10848 anchor: cursor_anchor,
10849 is_deactivate,
10850 })
10851 }
10852 }
10853
10854 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10855 let buffer = self.buffer.read(cx).snapshot(cx);
10856 let mut selection = self.selections.first::<usize>(cx);
10857 selection.set_head(buffer.len(), SelectionGoal::None);
10858 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10859 s.select(vec![selection]);
10860 });
10861 }
10862
10863 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10864 let end = self.buffer.read(cx).read(cx).len();
10865 self.change_selections(None, window, cx, |s| {
10866 s.select_ranges(vec![0..end]);
10867 });
10868 }
10869
10870 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10871 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10872 let mut selections = self.selections.all::<Point>(cx);
10873 let max_point = display_map.buffer_snapshot.max_point();
10874 for selection in &mut selections {
10875 let rows = selection.spanned_rows(true, &display_map);
10876 selection.start = Point::new(rows.start.0, 0);
10877 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10878 selection.reversed = false;
10879 }
10880 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10881 s.select(selections);
10882 });
10883 }
10884
10885 pub fn split_selection_into_lines(
10886 &mut self,
10887 _: &SplitSelectionIntoLines,
10888 window: &mut Window,
10889 cx: &mut Context<Self>,
10890 ) {
10891 let selections = self
10892 .selections
10893 .all::<Point>(cx)
10894 .into_iter()
10895 .map(|selection| selection.start..selection.end)
10896 .collect::<Vec<_>>();
10897 self.unfold_ranges(&selections, true, true, cx);
10898
10899 let mut new_selection_ranges = Vec::new();
10900 {
10901 let buffer = self.buffer.read(cx).read(cx);
10902 for selection in selections {
10903 for row in selection.start.row..selection.end.row {
10904 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10905 new_selection_ranges.push(cursor..cursor);
10906 }
10907
10908 let is_multiline_selection = selection.start.row != selection.end.row;
10909 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10910 // so this action feels more ergonomic when paired with other selection operations
10911 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10912 if !should_skip_last {
10913 new_selection_ranges.push(selection.end..selection.end);
10914 }
10915 }
10916 }
10917 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10918 s.select_ranges(new_selection_ranges);
10919 });
10920 }
10921
10922 pub fn add_selection_above(
10923 &mut self,
10924 _: &AddSelectionAbove,
10925 window: &mut Window,
10926 cx: &mut Context<Self>,
10927 ) {
10928 self.add_selection(true, window, cx);
10929 }
10930
10931 pub fn add_selection_below(
10932 &mut self,
10933 _: &AddSelectionBelow,
10934 window: &mut Window,
10935 cx: &mut Context<Self>,
10936 ) {
10937 self.add_selection(false, window, cx);
10938 }
10939
10940 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10941 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10942 let mut selections = self.selections.all::<Point>(cx);
10943 let text_layout_details = self.text_layout_details(window);
10944 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10945 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10946 let range = oldest_selection.display_range(&display_map).sorted();
10947
10948 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10949 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10950 let positions = start_x.min(end_x)..start_x.max(end_x);
10951
10952 selections.clear();
10953 let mut stack = Vec::new();
10954 for row in range.start.row().0..=range.end.row().0 {
10955 if let Some(selection) = self.selections.build_columnar_selection(
10956 &display_map,
10957 DisplayRow(row),
10958 &positions,
10959 oldest_selection.reversed,
10960 &text_layout_details,
10961 ) {
10962 stack.push(selection.id);
10963 selections.push(selection);
10964 }
10965 }
10966
10967 if above {
10968 stack.reverse();
10969 }
10970
10971 AddSelectionsState { above, stack }
10972 });
10973
10974 let last_added_selection = *state.stack.last().unwrap();
10975 let mut new_selections = Vec::new();
10976 if above == state.above {
10977 let end_row = if above {
10978 DisplayRow(0)
10979 } else {
10980 display_map.max_point().row()
10981 };
10982
10983 'outer: for selection in selections {
10984 if selection.id == last_added_selection {
10985 let range = selection.display_range(&display_map).sorted();
10986 debug_assert_eq!(range.start.row(), range.end.row());
10987 let mut row = range.start.row();
10988 let positions =
10989 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10990 px(start)..px(end)
10991 } else {
10992 let start_x =
10993 display_map.x_for_display_point(range.start, &text_layout_details);
10994 let end_x =
10995 display_map.x_for_display_point(range.end, &text_layout_details);
10996 start_x.min(end_x)..start_x.max(end_x)
10997 };
10998
10999 while row != end_row {
11000 if above {
11001 row.0 -= 1;
11002 } else {
11003 row.0 += 1;
11004 }
11005
11006 if let Some(new_selection) = self.selections.build_columnar_selection(
11007 &display_map,
11008 row,
11009 &positions,
11010 selection.reversed,
11011 &text_layout_details,
11012 ) {
11013 state.stack.push(new_selection.id);
11014 if above {
11015 new_selections.push(new_selection);
11016 new_selections.push(selection);
11017 } else {
11018 new_selections.push(selection);
11019 new_selections.push(new_selection);
11020 }
11021
11022 continue 'outer;
11023 }
11024 }
11025 }
11026
11027 new_selections.push(selection);
11028 }
11029 } else {
11030 new_selections = selections;
11031 new_selections.retain(|s| s.id != last_added_selection);
11032 state.stack.pop();
11033 }
11034
11035 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11036 s.select(new_selections);
11037 });
11038 if state.stack.len() > 1 {
11039 self.add_selections_state = Some(state);
11040 }
11041 }
11042
11043 pub fn select_next_match_internal(
11044 &mut self,
11045 display_map: &DisplaySnapshot,
11046 replace_newest: bool,
11047 autoscroll: Option<Autoscroll>,
11048 window: &mut Window,
11049 cx: &mut Context<Self>,
11050 ) -> Result<()> {
11051 fn select_next_match_ranges(
11052 this: &mut Editor,
11053 range: Range<usize>,
11054 replace_newest: bool,
11055 auto_scroll: Option<Autoscroll>,
11056 window: &mut Window,
11057 cx: &mut Context<Editor>,
11058 ) {
11059 this.unfold_ranges(&[range.clone()], false, true, cx);
11060 this.change_selections(auto_scroll, window, cx, |s| {
11061 if replace_newest {
11062 s.delete(s.newest_anchor().id);
11063 }
11064 s.insert_range(range.clone());
11065 });
11066 }
11067
11068 let buffer = &display_map.buffer_snapshot;
11069 let mut selections = self.selections.all::<usize>(cx);
11070 if let Some(mut select_next_state) = self.select_next_state.take() {
11071 let query = &select_next_state.query;
11072 if !select_next_state.done {
11073 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11074 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11075 let mut next_selected_range = None;
11076
11077 let bytes_after_last_selection =
11078 buffer.bytes_in_range(last_selection.end..buffer.len());
11079 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
11080 let query_matches = query
11081 .stream_find_iter(bytes_after_last_selection)
11082 .map(|result| (last_selection.end, result))
11083 .chain(
11084 query
11085 .stream_find_iter(bytes_before_first_selection)
11086 .map(|result| (0, result)),
11087 );
11088
11089 for (start_offset, query_match) in query_matches {
11090 let query_match = query_match.unwrap(); // can only fail due to I/O
11091 let offset_range =
11092 start_offset + query_match.start()..start_offset + query_match.end();
11093 let display_range = offset_range.start.to_display_point(display_map)
11094 ..offset_range.end.to_display_point(display_map);
11095
11096 if !select_next_state.wordwise
11097 || (!movement::is_inside_word(display_map, display_range.start)
11098 && !movement::is_inside_word(display_map, display_range.end))
11099 {
11100 // TODO: This is n^2, because we might check all the selections
11101 if !selections
11102 .iter()
11103 .any(|selection| selection.range().overlaps(&offset_range))
11104 {
11105 next_selected_range = Some(offset_range);
11106 break;
11107 }
11108 }
11109 }
11110
11111 if let Some(next_selected_range) = next_selected_range {
11112 select_next_match_ranges(
11113 self,
11114 next_selected_range,
11115 replace_newest,
11116 autoscroll,
11117 window,
11118 cx,
11119 );
11120 } else {
11121 select_next_state.done = true;
11122 }
11123 }
11124
11125 self.select_next_state = Some(select_next_state);
11126 } else {
11127 let mut only_carets = true;
11128 let mut same_text_selected = true;
11129 let mut selected_text = None;
11130
11131 let mut selections_iter = selections.iter().peekable();
11132 while let Some(selection) = selections_iter.next() {
11133 if selection.start != selection.end {
11134 only_carets = false;
11135 }
11136
11137 if same_text_selected {
11138 if selected_text.is_none() {
11139 selected_text =
11140 Some(buffer.text_for_range(selection.range()).collect::<String>());
11141 }
11142
11143 if let Some(next_selection) = selections_iter.peek() {
11144 if next_selection.range().len() == selection.range().len() {
11145 let next_selected_text = buffer
11146 .text_for_range(next_selection.range())
11147 .collect::<String>();
11148 if Some(next_selected_text) != selected_text {
11149 same_text_selected = false;
11150 selected_text = None;
11151 }
11152 } else {
11153 same_text_selected = false;
11154 selected_text = None;
11155 }
11156 }
11157 }
11158 }
11159
11160 if only_carets {
11161 for selection in &mut selections {
11162 let word_range = movement::surrounding_word(
11163 display_map,
11164 selection.start.to_display_point(display_map),
11165 );
11166 selection.start = word_range.start.to_offset(display_map, Bias::Left);
11167 selection.end = word_range.end.to_offset(display_map, Bias::Left);
11168 selection.goal = SelectionGoal::None;
11169 selection.reversed = false;
11170 select_next_match_ranges(
11171 self,
11172 selection.start..selection.end,
11173 replace_newest,
11174 autoscroll,
11175 window,
11176 cx,
11177 );
11178 }
11179
11180 if selections.len() == 1 {
11181 let selection = selections
11182 .last()
11183 .expect("ensured that there's only one selection");
11184 let query = buffer
11185 .text_for_range(selection.start..selection.end)
11186 .collect::<String>();
11187 let is_empty = query.is_empty();
11188 let select_state = SelectNextState {
11189 query: AhoCorasick::new(&[query])?,
11190 wordwise: true,
11191 done: is_empty,
11192 };
11193 self.select_next_state = Some(select_state);
11194 } else {
11195 self.select_next_state = None;
11196 }
11197 } else if let Some(selected_text) = selected_text {
11198 self.select_next_state = Some(SelectNextState {
11199 query: AhoCorasick::new(&[selected_text])?,
11200 wordwise: false,
11201 done: false,
11202 });
11203 self.select_next_match_internal(
11204 display_map,
11205 replace_newest,
11206 autoscroll,
11207 window,
11208 cx,
11209 )?;
11210 }
11211 }
11212 Ok(())
11213 }
11214
11215 pub fn select_all_matches(
11216 &mut self,
11217 _action: &SelectAllMatches,
11218 window: &mut Window,
11219 cx: &mut Context<Self>,
11220 ) -> Result<()> {
11221 self.push_to_selection_history();
11222 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11223
11224 self.select_next_match_internal(&display_map, false, None, window, cx)?;
11225 let Some(select_next_state) = self.select_next_state.as_mut() else {
11226 return Ok(());
11227 };
11228 if select_next_state.done {
11229 return Ok(());
11230 }
11231
11232 let mut new_selections = self.selections.all::<usize>(cx);
11233
11234 let buffer = &display_map.buffer_snapshot;
11235 let query_matches = select_next_state
11236 .query
11237 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
11238
11239 for query_match in query_matches {
11240 let query_match = query_match.unwrap(); // can only fail due to I/O
11241 let offset_range = query_match.start()..query_match.end();
11242 let display_range = offset_range.start.to_display_point(&display_map)
11243 ..offset_range.end.to_display_point(&display_map);
11244
11245 if !select_next_state.wordwise
11246 || (!movement::is_inside_word(&display_map, display_range.start)
11247 && !movement::is_inside_word(&display_map, display_range.end))
11248 {
11249 self.selections.change_with(cx, |selections| {
11250 new_selections.push(Selection {
11251 id: selections.new_selection_id(),
11252 start: offset_range.start,
11253 end: offset_range.end,
11254 reversed: false,
11255 goal: SelectionGoal::None,
11256 });
11257 });
11258 }
11259 }
11260
11261 new_selections.sort_by_key(|selection| selection.start);
11262 let mut ix = 0;
11263 while ix + 1 < new_selections.len() {
11264 let current_selection = &new_selections[ix];
11265 let next_selection = &new_selections[ix + 1];
11266 if current_selection.range().overlaps(&next_selection.range()) {
11267 if current_selection.id < next_selection.id {
11268 new_selections.remove(ix + 1);
11269 } else {
11270 new_selections.remove(ix);
11271 }
11272 } else {
11273 ix += 1;
11274 }
11275 }
11276
11277 let reversed = self.selections.oldest::<usize>(cx).reversed;
11278
11279 for selection in new_selections.iter_mut() {
11280 selection.reversed = reversed;
11281 }
11282
11283 select_next_state.done = true;
11284 self.unfold_ranges(
11285 &new_selections
11286 .iter()
11287 .map(|selection| selection.range())
11288 .collect::<Vec<_>>(),
11289 false,
11290 false,
11291 cx,
11292 );
11293 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
11294 selections.select(new_selections)
11295 });
11296
11297 Ok(())
11298 }
11299
11300 pub fn select_next(
11301 &mut self,
11302 action: &SelectNext,
11303 window: &mut Window,
11304 cx: &mut Context<Self>,
11305 ) -> Result<()> {
11306 self.push_to_selection_history();
11307 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11308 self.select_next_match_internal(
11309 &display_map,
11310 action.replace_newest,
11311 Some(Autoscroll::newest()),
11312 window,
11313 cx,
11314 )?;
11315 Ok(())
11316 }
11317
11318 pub fn select_previous(
11319 &mut self,
11320 action: &SelectPrevious,
11321 window: &mut Window,
11322 cx: &mut Context<Self>,
11323 ) -> Result<()> {
11324 self.push_to_selection_history();
11325 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11326 let buffer = &display_map.buffer_snapshot;
11327 let mut selections = self.selections.all::<usize>(cx);
11328 if let Some(mut select_prev_state) = self.select_prev_state.take() {
11329 let query = &select_prev_state.query;
11330 if !select_prev_state.done {
11331 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
11332 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
11333 let mut next_selected_range = None;
11334 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
11335 let bytes_before_last_selection =
11336 buffer.reversed_bytes_in_range(0..last_selection.start);
11337 let bytes_after_first_selection =
11338 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
11339 let query_matches = query
11340 .stream_find_iter(bytes_before_last_selection)
11341 .map(|result| (last_selection.start, result))
11342 .chain(
11343 query
11344 .stream_find_iter(bytes_after_first_selection)
11345 .map(|result| (buffer.len(), result)),
11346 );
11347 for (end_offset, query_match) in query_matches {
11348 let query_match = query_match.unwrap(); // can only fail due to I/O
11349 let offset_range =
11350 end_offset - query_match.end()..end_offset - query_match.start();
11351 let display_range = offset_range.start.to_display_point(&display_map)
11352 ..offset_range.end.to_display_point(&display_map);
11353
11354 if !select_prev_state.wordwise
11355 || (!movement::is_inside_word(&display_map, display_range.start)
11356 && !movement::is_inside_word(&display_map, display_range.end))
11357 {
11358 next_selected_range = Some(offset_range);
11359 break;
11360 }
11361 }
11362
11363 if let Some(next_selected_range) = next_selected_range {
11364 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
11365 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11366 if action.replace_newest {
11367 s.delete(s.newest_anchor().id);
11368 }
11369 s.insert_range(next_selected_range);
11370 });
11371 } else {
11372 select_prev_state.done = true;
11373 }
11374 }
11375
11376 self.select_prev_state = Some(select_prev_state);
11377 } else {
11378 let mut only_carets = true;
11379 let mut same_text_selected = true;
11380 let mut selected_text = None;
11381
11382 let mut selections_iter = selections.iter().peekable();
11383 while let Some(selection) = selections_iter.next() {
11384 if selection.start != selection.end {
11385 only_carets = false;
11386 }
11387
11388 if same_text_selected {
11389 if selected_text.is_none() {
11390 selected_text =
11391 Some(buffer.text_for_range(selection.range()).collect::<String>());
11392 }
11393
11394 if let Some(next_selection) = selections_iter.peek() {
11395 if next_selection.range().len() == selection.range().len() {
11396 let next_selected_text = buffer
11397 .text_for_range(next_selection.range())
11398 .collect::<String>();
11399 if Some(next_selected_text) != selected_text {
11400 same_text_selected = false;
11401 selected_text = None;
11402 }
11403 } else {
11404 same_text_selected = false;
11405 selected_text = None;
11406 }
11407 }
11408 }
11409 }
11410
11411 if only_carets {
11412 for selection in &mut selections {
11413 let word_range = movement::surrounding_word(
11414 &display_map,
11415 selection.start.to_display_point(&display_map),
11416 );
11417 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
11418 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
11419 selection.goal = SelectionGoal::None;
11420 selection.reversed = false;
11421 }
11422 if selections.len() == 1 {
11423 let selection = selections
11424 .last()
11425 .expect("ensured that there's only one selection");
11426 let query = buffer
11427 .text_for_range(selection.start..selection.end)
11428 .collect::<String>();
11429 let is_empty = query.is_empty();
11430 let select_state = SelectNextState {
11431 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
11432 wordwise: true,
11433 done: is_empty,
11434 };
11435 self.select_prev_state = Some(select_state);
11436 } else {
11437 self.select_prev_state = None;
11438 }
11439
11440 self.unfold_ranges(
11441 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
11442 false,
11443 true,
11444 cx,
11445 );
11446 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
11447 s.select(selections);
11448 });
11449 } else if let Some(selected_text) = selected_text {
11450 self.select_prev_state = Some(SelectNextState {
11451 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
11452 wordwise: false,
11453 done: false,
11454 });
11455 self.select_previous(action, window, cx)?;
11456 }
11457 }
11458 Ok(())
11459 }
11460
11461 pub fn toggle_comments(
11462 &mut self,
11463 action: &ToggleComments,
11464 window: &mut Window,
11465 cx: &mut Context<Self>,
11466 ) {
11467 if self.read_only(cx) {
11468 return;
11469 }
11470 let text_layout_details = &self.text_layout_details(window);
11471 self.transact(window, cx, |this, window, cx| {
11472 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
11473 let mut edits = Vec::new();
11474 let mut selection_edit_ranges = Vec::new();
11475 let mut last_toggled_row = None;
11476 let snapshot = this.buffer.read(cx).read(cx);
11477 let empty_str: Arc<str> = Arc::default();
11478 let mut suffixes_inserted = Vec::new();
11479 let ignore_indent = action.ignore_indent;
11480
11481 fn comment_prefix_range(
11482 snapshot: &MultiBufferSnapshot,
11483 row: MultiBufferRow,
11484 comment_prefix: &str,
11485 comment_prefix_whitespace: &str,
11486 ignore_indent: bool,
11487 ) -> Range<Point> {
11488 let indent_size = if ignore_indent {
11489 0
11490 } else {
11491 snapshot.indent_size_for_line(row).len
11492 };
11493
11494 let start = Point::new(row.0, indent_size);
11495
11496 let mut line_bytes = snapshot
11497 .bytes_in_range(start..snapshot.max_point())
11498 .flatten()
11499 .copied();
11500
11501 // If this line currently begins with the line comment prefix, then record
11502 // the range containing the prefix.
11503 if line_bytes
11504 .by_ref()
11505 .take(comment_prefix.len())
11506 .eq(comment_prefix.bytes())
11507 {
11508 // Include any whitespace that matches the comment prefix.
11509 let matching_whitespace_len = line_bytes
11510 .zip(comment_prefix_whitespace.bytes())
11511 .take_while(|(a, b)| a == b)
11512 .count() as u32;
11513 let end = Point::new(
11514 start.row,
11515 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
11516 );
11517 start..end
11518 } else {
11519 start..start
11520 }
11521 }
11522
11523 fn comment_suffix_range(
11524 snapshot: &MultiBufferSnapshot,
11525 row: MultiBufferRow,
11526 comment_suffix: &str,
11527 comment_suffix_has_leading_space: bool,
11528 ) -> Range<Point> {
11529 let end = Point::new(row.0, snapshot.line_len(row));
11530 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
11531
11532 let mut line_end_bytes = snapshot
11533 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
11534 .flatten()
11535 .copied();
11536
11537 let leading_space_len = if suffix_start_column > 0
11538 && line_end_bytes.next() == Some(b' ')
11539 && comment_suffix_has_leading_space
11540 {
11541 1
11542 } else {
11543 0
11544 };
11545
11546 // If this line currently begins with the line comment prefix, then record
11547 // the range containing the prefix.
11548 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
11549 let start = Point::new(end.row, suffix_start_column - leading_space_len);
11550 start..end
11551 } else {
11552 end..end
11553 }
11554 }
11555
11556 // TODO: Handle selections that cross excerpts
11557 for selection in &mut selections {
11558 let start_column = snapshot
11559 .indent_size_for_line(MultiBufferRow(selection.start.row))
11560 .len;
11561 let language = if let Some(language) =
11562 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
11563 {
11564 language
11565 } else {
11566 continue;
11567 };
11568
11569 selection_edit_ranges.clear();
11570
11571 // If multiple selections contain a given row, avoid processing that
11572 // row more than once.
11573 let mut start_row = MultiBufferRow(selection.start.row);
11574 if last_toggled_row == Some(start_row) {
11575 start_row = start_row.next_row();
11576 }
11577 let end_row =
11578 if selection.end.row > selection.start.row && selection.end.column == 0 {
11579 MultiBufferRow(selection.end.row - 1)
11580 } else {
11581 MultiBufferRow(selection.end.row)
11582 };
11583 last_toggled_row = Some(end_row);
11584
11585 if start_row > end_row {
11586 continue;
11587 }
11588
11589 // If the language has line comments, toggle those.
11590 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
11591
11592 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
11593 if ignore_indent {
11594 full_comment_prefixes = full_comment_prefixes
11595 .into_iter()
11596 .map(|s| Arc::from(s.trim_end()))
11597 .collect();
11598 }
11599
11600 if !full_comment_prefixes.is_empty() {
11601 let first_prefix = full_comment_prefixes
11602 .first()
11603 .expect("prefixes is non-empty");
11604 let prefix_trimmed_lengths = full_comment_prefixes
11605 .iter()
11606 .map(|p| p.trim_end_matches(' ').len())
11607 .collect::<SmallVec<[usize; 4]>>();
11608
11609 let mut all_selection_lines_are_comments = true;
11610
11611 for row in start_row.0..=end_row.0 {
11612 let row = MultiBufferRow(row);
11613 if start_row < end_row && snapshot.is_line_blank(row) {
11614 continue;
11615 }
11616
11617 let prefix_range = full_comment_prefixes
11618 .iter()
11619 .zip(prefix_trimmed_lengths.iter().copied())
11620 .map(|(prefix, trimmed_prefix_len)| {
11621 comment_prefix_range(
11622 snapshot.deref(),
11623 row,
11624 &prefix[..trimmed_prefix_len],
11625 &prefix[trimmed_prefix_len..],
11626 ignore_indent,
11627 )
11628 })
11629 .max_by_key(|range| range.end.column - range.start.column)
11630 .expect("prefixes is non-empty");
11631
11632 if prefix_range.is_empty() {
11633 all_selection_lines_are_comments = false;
11634 }
11635
11636 selection_edit_ranges.push(prefix_range);
11637 }
11638
11639 if all_selection_lines_are_comments {
11640 edits.extend(
11641 selection_edit_ranges
11642 .iter()
11643 .cloned()
11644 .map(|range| (range, empty_str.clone())),
11645 );
11646 } else {
11647 let min_column = selection_edit_ranges
11648 .iter()
11649 .map(|range| range.start.column)
11650 .min()
11651 .unwrap_or(0);
11652 edits.extend(selection_edit_ranges.iter().map(|range| {
11653 let position = Point::new(range.start.row, min_column);
11654 (position..position, first_prefix.clone())
11655 }));
11656 }
11657 } else if let Some((full_comment_prefix, comment_suffix)) =
11658 language.block_comment_delimiters()
11659 {
11660 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11661 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11662 let prefix_range = comment_prefix_range(
11663 snapshot.deref(),
11664 start_row,
11665 comment_prefix,
11666 comment_prefix_whitespace,
11667 ignore_indent,
11668 );
11669 let suffix_range = comment_suffix_range(
11670 snapshot.deref(),
11671 end_row,
11672 comment_suffix.trim_start_matches(' '),
11673 comment_suffix.starts_with(' '),
11674 );
11675
11676 if prefix_range.is_empty() || suffix_range.is_empty() {
11677 edits.push((
11678 prefix_range.start..prefix_range.start,
11679 full_comment_prefix.clone(),
11680 ));
11681 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11682 suffixes_inserted.push((end_row, comment_suffix.len()));
11683 } else {
11684 edits.push((prefix_range, empty_str.clone()));
11685 edits.push((suffix_range, empty_str.clone()));
11686 }
11687 } else {
11688 continue;
11689 }
11690 }
11691
11692 drop(snapshot);
11693 this.buffer.update(cx, |buffer, cx| {
11694 buffer.edit(edits, None, cx);
11695 });
11696
11697 // Adjust selections so that they end before any comment suffixes that
11698 // were inserted.
11699 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11700 let mut selections = this.selections.all::<Point>(cx);
11701 let snapshot = this.buffer.read(cx).read(cx);
11702 for selection in &mut selections {
11703 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11704 match row.cmp(&MultiBufferRow(selection.end.row)) {
11705 Ordering::Less => {
11706 suffixes_inserted.next();
11707 continue;
11708 }
11709 Ordering::Greater => break,
11710 Ordering::Equal => {
11711 if selection.end.column == snapshot.line_len(row) {
11712 if selection.is_empty() {
11713 selection.start.column -= suffix_len as u32;
11714 }
11715 selection.end.column -= suffix_len as u32;
11716 }
11717 break;
11718 }
11719 }
11720 }
11721 }
11722
11723 drop(snapshot);
11724 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11725 s.select(selections)
11726 });
11727
11728 let selections = this.selections.all::<Point>(cx);
11729 let selections_on_single_row = selections.windows(2).all(|selections| {
11730 selections[0].start.row == selections[1].start.row
11731 && selections[0].end.row == selections[1].end.row
11732 && selections[0].start.row == selections[0].end.row
11733 });
11734 let selections_selecting = selections
11735 .iter()
11736 .any(|selection| selection.start != selection.end);
11737 let advance_downwards = action.advance_downwards
11738 && selections_on_single_row
11739 && !selections_selecting
11740 && !matches!(this.mode, EditorMode::SingleLine { .. });
11741
11742 if advance_downwards {
11743 let snapshot = this.buffer.read(cx).snapshot(cx);
11744
11745 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11746 s.move_cursors_with(|display_snapshot, display_point, _| {
11747 let mut point = display_point.to_point(display_snapshot);
11748 point.row += 1;
11749 point = snapshot.clip_point(point, Bias::Left);
11750 let display_point = point.to_display_point(display_snapshot);
11751 let goal = SelectionGoal::HorizontalPosition(
11752 display_snapshot
11753 .x_for_display_point(display_point, text_layout_details)
11754 .into(),
11755 );
11756 (display_point, goal)
11757 })
11758 });
11759 }
11760 });
11761 }
11762
11763 pub fn select_enclosing_symbol(
11764 &mut self,
11765 _: &SelectEnclosingSymbol,
11766 window: &mut Window,
11767 cx: &mut Context<Self>,
11768 ) {
11769 let buffer = self.buffer.read(cx).snapshot(cx);
11770 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11771
11772 fn update_selection(
11773 selection: &Selection<usize>,
11774 buffer_snap: &MultiBufferSnapshot,
11775 ) -> Option<Selection<usize>> {
11776 let cursor = selection.head();
11777 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11778 for symbol in symbols.iter().rev() {
11779 let start = symbol.range.start.to_offset(buffer_snap);
11780 let end = symbol.range.end.to_offset(buffer_snap);
11781 let new_range = start..end;
11782 if start < selection.start || end > selection.end {
11783 return Some(Selection {
11784 id: selection.id,
11785 start: new_range.start,
11786 end: new_range.end,
11787 goal: SelectionGoal::None,
11788 reversed: selection.reversed,
11789 });
11790 }
11791 }
11792 None
11793 }
11794
11795 let mut selected_larger_symbol = false;
11796 let new_selections = old_selections
11797 .iter()
11798 .map(|selection| match update_selection(selection, &buffer) {
11799 Some(new_selection) => {
11800 if new_selection.range() != selection.range() {
11801 selected_larger_symbol = true;
11802 }
11803 new_selection
11804 }
11805 None => selection.clone(),
11806 })
11807 .collect::<Vec<_>>();
11808
11809 if selected_larger_symbol {
11810 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11811 s.select(new_selections);
11812 });
11813 }
11814 }
11815
11816 pub fn select_larger_syntax_node(
11817 &mut self,
11818 _: &SelectLargerSyntaxNode,
11819 window: &mut Window,
11820 cx: &mut Context<Self>,
11821 ) {
11822 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11823 let buffer = self.buffer.read(cx).snapshot(cx);
11824 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11825
11826 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11827 let mut selected_larger_node = false;
11828 let new_selections = old_selections
11829 .iter()
11830 .map(|selection| {
11831 let old_range = selection.start..selection.end;
11832 let mut new_range = old_range.clone();
11833 let mut new_node = None;
11834 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11835 {
11836 new_node = Some(node);
11837 new_range = match containing_range {
11838 MultiOrSingleBufferOffsetRange::Single(_) => break,
11839 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11840 };
11841 if !display_map.intersects_fold(new_range.start)
11842 && !display_map.intersects_fold(new_range.end)
11843 {
11844 break;
11845 }
11846 }
11847
11848 if let Some(node) = new_node {
11849 // Log the ancestor, to support using this action as a way to explore TreeSitter
11850 // nodes. Parent and grandparent are also logged because this operation will not
11851 // visit nodes that have the same range as their parent.
11852 log::info!("Node: {node:?}");
11853 let parent = node.parent();
11854 log::info!("Parent: {parent:?}");
11855 let grandparent = parent.and_then(|x| x.parent());
11856 log::info!("Grandparent: {grandparent:?}");
11857 }
11858
11859 selected_larger_node |= new_range != old_range;
11860 Selection {
11861 id: selection.id,
11862 start: new_range.start,
11863 end: new_range.end,
11864 goal: SelectionGoal::None,
11865 reversed: selection.reversed,
11866 }
11867 })
11868 .collect::<Vec<_>>();
11869
11870 if selected_larger_node {
11871 stack.push(old_selections);
11872 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11873 s.select(new_selections);
11874 });
11875 }
11876 self.select_larger_syntax_node_stack = stack;
11877 }
11878
11879 pub fn select_smaller_syntax_node(
11880 &mut self,
11881 _: &SelectSmallerSyntaxNode,
11882 window: &mut Window,
11883 cx: &mut Context<Self>,
11884 ) {
11885 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11886 if let Some(selections) = stack.pop() {
11887 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11888 s.select(selections.to_vec());
11889 });
11890 }
11891 self.select_larger_syntax_node_stack = stack;
11892 }
11893
11894 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11895 if !EditorSettings::get_global(cx).gutter.runnables {
11896 self.clear_tasks();
11897 return Task::ready(());
11898 }
11899 let project = self.project.as_ref().map(Entity::downgrade);
11900 cx.spawn_in(window, async move |this, cx| {
11901 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11902 let Some(project) = project.and_then(|p| p.upgrade()) else {
11903 return;
11904 };
11905 let Ok(display_snapshot) = this.update(cx, |this, cx| {
11906 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11907 }) else {
11908 return;
11909 };
11910
11911 let hide_runnables = project
11912 .update(cx, |project, cx| {
11913 // Do not display any test indicators in non-dev server remote projects.
11914 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11915 })
11916 .unwrap_or(true);
11917 if hide_runnables {
11918 return;
11919 }
11920 let new_rows =
11921 cx.background_spawn({
11922 let snapshot = display_snapshot.clone();
11923 async move {
11924 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11925 }
11926 })
11927 .await;
11928
11929 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11930 this.update(cx, |this, _| {
11931 this.clear_tasks();
11932 for (key, value) in rows {
11933 this.insert_tasks(key, value);
11934 }
11935 })
11936 .ok();
11937 })
11938 }
11939 fn fetch_runnable_ranges(
11940 snapshot: &DisplaySnapshot,
11941 range: Range<Anchor>,
11942 ) -> Vec<language::RunnableRange> {
11943 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11944 }
11945
11946 fn runnable_rows(
11947 project: Entity<Project>,
11948 snapshot: DisplaySnapshot,
11949 runnable_ranges: Vec<RunnableRange>,
11950 mut cx: AsyncWindowContext,
11951 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11952 runnable_ranges
11953 .into_iter()
11954 .filter_map(|mut runnable| {
11955 let tasks = cx
11956 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11957 .ok()?;
11958 if tasks.is_empty() {
11959 return None;
11960 }
11961
11962 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11963
11964 let row = snapshot
11965 .buffer_snapshot
11966 .buffer_line_for_row(MultiBufferRow(point.row))?
11967 .1
11968 .start
11969 .row;
11970
11971 let context_range =
11972 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11973 Some((
11974 (runnable.buffer_id, row),
11975 RunnableTasks {
11976 templates: tasks,
11977 offset: snapshot
11978 .buffer_snapshot
11979 .anchor_before(runnable.run_range.start),
11980 context_range,
11981 column: point.column,
11982 extra_variables: runnable.extra_captures,
11983 },
11984 ))
11985 })
11986 .collect()
11987 }
11988
11989 fn templates_with_tags(
11990 project: &Entity<Project>,
11991 runnable: &mut Runnable,
11992 cx: &mut App,
11993 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11994 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11995 let (worktree_id, file) = project
11996 .buffer_for_id(runnable.buffer, cx)
11997 .and_then(|buffer| buffer.read(cx).file())
11998 .map(|file| (file.worktree_id(cx), file.clone()))
11999 .unzip();
12000
12001 (
12002 project.task_store().read(cx).task_inventory().cloned(),
12003 worktree_id,
12004 file,
12005 )
12006 });
12007
12008 let tags = mem::take(&mut runnable.tags);
12009 let mut tags: Vec<_> = tags
12010 .into_iter()
12011 .flat_map(|tag| {
12012 let tag = tag.0.clone();
12013 inventory
12014 .as_ref()
12015 .into_iter()
12016 .flat_map(|inventory| {
12017 inventory.read(cx).list_tasks(
12018 file.clone(),
12019 Some(runnable.language.clone()),
12020 worktree_id,
12021 cx,
12022 )
12023 })
12024 .filter(move |(_, template)| {
12025 template.tags.iter().any(|source_tag| source_tag == &tag)
12026 })
12027 })
12028 .sorted_by_key(|(kind, _)| kind.to_owned())
12029 .collect();
12030 if let Some((leading_tag_source, _)) = tags.first() {
12031 // Strongest source wins; if we have worktree tag binding, prefer that to
12032 // global and language bindings;
12033 // if we have a global binding, prefer that to language binding.
12034 let first_mismatch = tags
12035 .iter()
12036 .position(|(tag_source, _)| tag_source != leading_tag_source);
12037 if let Some(index) = first_mismatch {
12038 tags.truncate(index);
12039 }
12040 }
12041
12042 tags
12043 }
12044
12045 pub fn move_to_enclosing_bracket(
12046 &mut self,
12047 _: &MoveToEnclosingBracket,
12048 window: &mut Window,
12049 cx: &mut Context<Self>,
12050 ) {
12051 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12052 s.move_offsets_with(|snapshot, selection| {
12053 let Some(enclosing_bracket_ranges) =
12054 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
12055 else {
12056 return;
12057 };
12058
12059 let mut best_length = usize::MAX;
12060 let mut best_inside = false;
12061 let mut best_in_bracket_range = false;
12062 let mut best_destination = None;
12063 for (open, close) in enclosing_bracket_ranges {
12064 let close = close.to_inclusive();
12065 let length = close.end() - open.start;
12066 let inside = selection.start >= open.end && selection.end <= *close.start();
12067 let in_bracket_range = open.to_inclusive().contains(&selection.head())
12068 || close.contains(&selection.head());
12069
12070 // If best is next to a bracket and current isn't, skip
12071 if !in_bracket_range && best_in_bracket_range {
12072 continue;
12073 }
12074
12075 // Prefer smaller lengths unless best is inside and current isn't
12076 if length > best_length && (best_inside || !inside) {
12077 continue;
12078 }
12079
12080 best_length = length;
12081 best_inside = inside;
12082 best_in_bracket_range = in_bracket_range;
12083 best_destination = Some(
12084 if close.contains(&selection.start) && close.contains(&selection.end) {
12085 if inside {
12086 open.end
12087 } else {
12088 open.start
12089 }
12090 } else if inside {
12091 *close.start()
12092 } else {
12093 *close.end()
12094 },
12095 );
12096 }
12097
12098 if let Some(destination) = best_destination {
12099 selection.collapse_to(destination, SelectionGoal::None);
12100 }
12101 })
12102 });
12103 }
12104
12105 pub fn undo_selection(
12106 &mut self,
12107 _: &UndoSelection,
12108 window: &mut Window,
12109 cx: &mut Context<Self>,
12110 ) {
12111 self.end_selection(window, cx);
12112 self.selection_history.mode = SelectionHistoryMode::Undoing;
12113 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
12114 self.change_selections(None, window, cx, |s| {
12115 s.select_anchors(entry.selections.to_vec())
12116 });
12117 self.select_next_state = entry.select_next_state;
12118 self.select_prev_state = entry.select_prev_state;
12119 self.add_selections_state = entry.add_selections_state;
12120 self.request_autoscroll(Autoscroll::newest(), cx);
12121 }
12122 self.selection_history.mode = SelectionHistoryMode::Normal;
12123 }
12124
12125 pub fn redo_selection(
12126 &mut self,
12127 _: &RedoSelection,
12128 window: &mut Window,
12129 cx: &mut Context<Self>,
12130 ) {
12131 self.end_selection(window, cx);
12132 self.selection_history.mode = SelectionHistoryMode::Redoing;
12133 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
12134 self.change_selections(None, window, cx, |s| {
12135 s.select_anchors(entry.selections.to_vec())
12136 });
12137 self.select_next_state = entry.select_next_state;
12138 self.select_prev_state = entry.select_prev_state;
12139 self.add_selections_state = entry.add_selections_state;
12140 self.request_autoscroll(Autoscroll::newest(), cx);
12141 }
12142 self.selection_history.mode = SelectionHistoryMode::Normal;
12143 }
12144
12145 pub fn expand_excerpts(
12146 &mut self,
12147 action: &ExpandExcerpts,
12148 _: &mut Window,
12149 cx: &mut Context<Self>,
12150 ) {
12151 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
12152 }
12153
12154 pub fn expand_excerpts_down(
12155 &mut self,
12156 action: &ExpandExcerptsDown,
12157 _: &mut Window,
12158 cx: &mut Context<Self>,
12159 ) {
12160 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
12161 }
12162
12163 pub fn expand_excerpts_up(
12164 &mut self,
12165 action: &ExpandExcerptsUp,
12166 _: &mut Window,
12167 cx: &mut Context<Self>,
12168 ) {
12169 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
12170 }
12171
12172 pub fn expand_excerpts_for_direction(
12173 &mut self,
12174 lines: u32,
12175 direction: ExpandExcerptDirection,
12176
12177 cx: &mut Context<Self>,
12178 ) {
12179 let selections = self.selections.disjoint_anchors();
12180
12181 let lines = if lines == 0 {
12182 EditorSettings::get_global(cx).expand_excerpt_lines
12183 } else {
12184 lines
12185 };
12186
12187 self.buffer.update(cx, |buffer, cx| {
12188 let snapshot = buffer.snapshot(cx);
12189 let mut excerpt_ids = selections
12190 .iter()
12191 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
12192 .collect::<Vec<_>>();
12193 excerpt_ids.sort();
12194 excerpt_ids.dedup();
12195 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
12196 })
12197 }
12198
12199 pub fn expand_excerpt(
12200 &mut self,
12201 excerpt: ExcerptId,
12202 direction: ExpandExcerptDirection,
12203 window: &mut Window,
12204 cx: &mut Context<Self>,
12205 ) {
12206 let current_scroll_position = self.scroll_position(cx);
12207 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
12208 self.buffer.update(cx, |buffer, cx| {
12209 buffer.expand_excerpts([excerpt], lines, direction, cx)
12210 });
12211 if direction == ExpandExcerptDirection::Down {
12212 let new_scroll_position = current_scroll_position + gpui::Point::new(0.0, lines as f32);
12213 self.set_scroll_position(new_scroll_position, window, cx);
12214 }
12215 }
12216
12217 pub fn go_to_singleton_buffer_point(
12218 &mut self,
12219 point: Point,
12220 window: &mut Window,
12221 cx: &mut Context<Self>,
12222 ) {
12223 self.go_to_singleton_buffer_range(point..point, window, cx);
12224 }
12225
12226 pub fn go_to_singleton_buffer_range(
12227 &mut self,
12228 range: Range<Point>,
12229 window: &mut Window,
12230 cx: &mut Context<Self>,
12231 ) {
12232 let multibuffer = self.buffer().read(cx);
12233 let Some(buffer) = multibuffer.as_singleton() else {
12234 return;
12235 };
12236 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
12237 return;
12238 };
12239 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
12240 return;
12241 };
12242 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
12243 s.select_anchor_ranges([start..end])
12244 });
12245 }
12246
12247 fn go_to_diagnostic(
12248 &mut self,
12249 _: &GoToDiagnostic,
12250 window: &mut Window,
12251 cx: &mut Context<Self>,
12252 ) {
12253 self.go_to_diagnostic_impl(Direction::Next, window, cx)
12254 }
12255
12256 fn go_to_prev_diagnostic(
12257 &mut self,
12258 _: &GoToPreviousDiagnostic,
12259 window: &mut Window,
12260 cx: &mut Context<Self>,
12261 ) {
12262 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
12263 }
12264
12265 pub fn go_to_diagnostic_impl(
12266 &mut self,
12267 direction: Direction,
12268 window: &mut Window,
12269 cx: &mut Context<Self>,
12270 ) {
12271 let buffer = self.buffer.read(cx).snapshot(cx);
12272 let selection = self.selections.newest::<usize>(cx);
12273
12274 // If there is an active Diagnostic Popover jump to its diagnostic instead.
12275 if direction == Direction::Next {
12276 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
12277 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
12278 return;
12279 };
12280 self.activate_diagnostics(
12281 buffer_id,
12282 popover.local_diagnostic.diagnostic.group_id,
12283 window,
12284 cx,
12285 );
12286 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
12287 let primary_range_start = active_diagnostics.primary_range.start;
12288 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12289 let mut new_selection = s.newest_anchor().clone();
12290 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
12291 s.select_anchors(vec![new_selection.clone()]);
12292 });
12293 self.refresh_inline_completion(false, true, window, cx);
12294 }
12295 return;
12296 }
12297 }
12298
12299 let active_group_id = self
12300 .active_diagnostics
12301 .as_ref()
12302 .map(|active_group| active_group.group_id);
12303 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
12304 active_diagnostics
12305 .primary_range
12306 .to_offset(&buffer)
12307 .to_inclusive()
12308 });
12309 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
12310 if active_primary_range.contains(&selection.head()) {
12311 *active_primary_range.start()
12312 } else {
12313 selection.head()
12314 }
12315 } else {
12316 selection.head()
12317 };
12318
12319 let snapshot = self.snapshot(window, cx);
12320 let primary_diagnostics_before = buffer
12321 .diagnostics_in_range::<usize>(0..search_start)
12322 .filter(|entry| entry.diagnostic.is_primary)
12323 .filter(|entry| entry.range.start != entry.range.end)
12324 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12325 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
12326 .collect::<Vec<_>>();
12327 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
12328 primary_diagnostics_before
12329 .iter()
12330 .position(|entry| entry.diagnostic.group_id == active_group_id)
12331 });
12332
12333 let primary_diagnostics_after = buffer
12334 .diagnostics_in_range::<usize>(search_start..buffer.len())
12335 .filter(|entry| entry.diagnostic.is_primary)
12336 .filter(|entry| entry.range.start != entry.range.end)
12337 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
12338 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
12339 .collect::<Vec<_>>();
12340 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
12341 primary_diagnostics_after
12342 .iter()
12343 .enumerate()
12344 .rev()
12345 .find_map(|(i, entry)| {
12346 if entry.diagnostic.group_id == active_group_id {
12347 Some(i)
12348 } else {
12349 None
12350 }
12351 })
12352 });
12353
12354 let next_primary_diagnostic = match direction {
12355 Direction::Prev => primary_diagnostics_before
12356 .iter()
12357 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
12358 .rev()
12359 .next(),
12360 Direction::Next => primary_diagnostics_after
12361 .iter()
12362 .skip(
12363 last_same_group_diagnostic_after
12364 .map(|index| index + 1)
12365 .unwrap_or(0),
12366 )
12367 .next(),
12368 };
12369
12370 // Cycle around to the start of the buffer, potentially moving back to the start of
12371 // the currently active diagnostic.
12372 let cycle_around = || match direction {
12373 Direction::Prev => primary_diagnostics_after
12374 .iter()
12375 .rev()
12376 .chain(primary_diagnostics_before.iter().rev())
12377 .next(),
12378 Direction::Next => primary_diagnostics_before
12379 .iter()
12380 .chain(primary_diagnostics_after.iter())
12381 .next(),
12382 };
12383
12384 if let Some((primary_range, group_id)) = next_primary_diagnostic
12385 .or_else(cycle_around)
12386 .map(|entry| (&entry.range, entry.diagnostic.group_id))
12387 {
12388 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
12389 return;
12390 };
12391 self.activate_diagnostics(buffer_id, group_id, window, cx);
12392 if self.active_diagnostics.is_some() {
12393 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12394 s.select(vec![Selection {
12395 id: selection.id,
12396 start: primary_range.start,
12397 end: primary_range.start,
12398 reversed: false,
12399 goal: SelectionGoal::None,
12400 }]);
12401 });
12402 self.refresh_inline_completion(false, true, window, cx);
12403 }
12404 }
12405 }
12406
12407 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
12408 let snapshot = self.snapshot(window, cx);
12409 let selection = self.selections.newest::<Point>(cx);
12410 self.go_to_hunk_before_or_after_position(
12411 &snapshot,
12412 selection.head(),
12413 Direction::Next,
12414 window,
12415 cx,
12416 );
12417 }
12418
12419 fn go_to_hunk_before_or_after_position(
12420 &mut self,
12421 snapshot: &EditorSnapshot,
12422 position: Point,
12423 direction: Direction,
12424 window: &mut Window,
12425 cx: &mut Context<Editor>,
12426 ) {
12427 let row = if direction == Direction::Next {
12428 self.hunk_after_position(snapshot, position)
12429 .map(|hunk| hunk.row_range.start)
12430 } else {
12431 self.hunk_before_position(snapshot, position)
12432 };
12433
12434 if let Some(row) = row {
12435 let destination = Point::new(row.0, 0);
12436 let autoscroll = Autoscroll::center();
12437
12438 self.unfold_ranges(&[destination..destination], false, false, cx);
12439 self.change_selections(Some(autoscroll), window, cx, |s| {
12440 s.select_ranges([destination..destination]);
12441 });
12442 }
12443 }
12444
12445 fn hunk_after_position(
12446 &mut self,
12447 snapshot: &EditorSnapshot,
12448 position: Point,
12449 ) -> Option<MultiBufferDiffHunk> {
12450 snapshot
12451 .buffer_snapshot
12452 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
12453 .find(|hunk| hunk.row_range.start.0 > position.row)
12454 .or_else(|| {
12455 snapshot
12456 .buffer_snapshot
12457 .diff_hunks_in_range(Point::zero()..position)
12458 .find(|hunk| hunk.row_range.end.0 < position.row)
12459 })
12460 }
12461
12462 fn go_to_prev_hunk(
12463 &mut self,
12464 _: &GoToPreviousHunk,
12465 window: &mut Window,
12466 cx: &mut Context<Self>,
12467 ) {
12468 let snapshot = self.snapshot(window, cx);
12469 let selection = self.selections.newest::<Point>(cx);
12470 self.go_to_hunk_before_or_after_position(
12471 &snapshot,
12472 selection.head(),
12473 Direction::Prev,
12474 window,
12475 cx,
12476 );
12477 }
12478
12479 fn hunk_before_position(
12480 &mut self,
12481 snapshot: &EditorSnapshot,
12482 position: Point,
12483 ) -> Option<MultiBufferRow> {
12484 snapshot
12485 .buffer_snapshot
12486 .diff_hunk_before(position)
12487 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
12488 }
12489
12490 fn go_to_line<T: 'static>(
12491 &mut self,
12492 position: Anchor,
12493 highlight_color: Option<Hsla>,
12494 window: &mut Window,
12495 cx: &mut Context<Self>,
12496 ) {
12497 let snapshot = self.snapshot(window, cx).display_snapshot;
12498 let position = position.to_point(&snapshot.buffer_snapshot);
12499 let start = snapshot
12500 .buffer_snapshot
12501 .clip_point(Point::new(position.row, 0), Bias::Left);
12502 let end = start + Point::new(1, 0);
12503 let start = snapshot.buffer_snapshot.anchor_before(start);
12504 let end = snapshot.buffer_snapshot.anchor_before(end);
12505
12506 self.clear_row_highlights::<T>();
12507 self.highlight_rows::<T>(
12508 start..end,
12509 highlight_color
12510 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
12511 true,
12512 cx,
12513 );
12514 self.request_autoscroll(Autoscroll::center(), cx);
12515 }
12516
12517 pub fn go_to_definition(
12518 &mut self,
12519 _: &GoToDefinition,
12520 window: &mut Window,
12521 cx: &mut Context<Self>,
12522 ) -> Task<Result<Navigated>> {
12523 let definition =
12524 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
12525 cx.spawn_in(window, async move |editor, cx| {
12526 if definition.await? == Navigated::Yes {
12527 return Ok(Navigated::Yes);
12528 }
12529 match editor.update_in(cx, |editor, window, cx| {
12530 editor.find_all_references(&FindAllReferences, window, cx)
12531 })? {
12532 Some(references) => references.await,
12533 None => Ok(Navigated::No),
12534 }
12535 })
12536 }
12537
12538 pub fn go_to_declaration(
12539 &mut self,
12540 _: &GoToDeclaration,
12541 window: &mut Window,
12542 cx: &mut Context<Self>,
12543 ) -> Task<Result<Navigated>> {
12544 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
12545 }
12546
12547 pub fn go_to_declaration_split(
12548 &mut self,
12549 _: &GoToDeclaration,
12550 window: &mut Window,
12551 cx: &mut Context<Self>,
12552 ) -> Task<Result<Navigated>> {
12553 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
12554 }
12555
12556 pub fn go_to_implementation(
12557 &mut self,
12558 _: &GoToImplementation,
12559 window: &mut Window,
12560 cx: &mut Context<Self>,
12561 ) -> Task<Result<Navigated>> {
12562 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
12563 }
12564
12565 pub fn go_to_implementation_split(
12566 &mut self,
12567 _: &GoToImplementationSplit,
12568 window: &mut Window,
12569 cx: &mut Context<Self>,
12570 ) -> Task<Result<Navigated>> {
12571 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
12572 }
12573
12574 pub fn go_to_type_definition(
12575 &mut self,
12576 _: &GoToTypeDefinition,
12577 window: &mut Window,
12578 cx: &mut Context<Self>,
12579 ) -> Task<Result<Navigated>> {
12580 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
12581 }
12582
12583 pub fn go_to_definition_split(
12584 &mut self,
12585 _: &GoToDefinitionSplit,
12586 window: &mut Window,
12587 cx: &mut Context<Self>,
12588 ) -> Task<Result<Navigated>> {
12589 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
12590 }
12591
12592 pub fn go_to_type_definition_split(
12593 &mut self,
12594 _: &GoToTypeDefinitionSplit,
12595 window: &mut Window,
12596 cx: &mut Context<Self>,
12597 ) -> Task<Result<Navigated>> {
12598 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
12599 }
12600
12601 fn go_to_definition_of_kind(
12602 &mut self,
12603 kind: GotoDefinitionKind,
12604 split: bool,
12605 window: &mut Window,
12606 cx: &mut Context<Self>,
12607 ) -> Task<Result<Navigated>> {
12608 let Some(provider) = self.semantics_provider.clone() else {
12609 return Task::ready(Ok(Navigated::No));
12610 };
12611 let head = self.selections.newest::<usize>(cx).head();
12612 let buffer = self.buffer.read(cx);
12613 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
12614 text_anchor
12615 } else {
12616 return Task::ready(Ok(Navigated::No));
12617 };
12618
12619 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
12620 return Task::ready(Ok(Navigated::No));
12621 };
12622
12623 cx.spawn_in(window, async move |editor, cx| {
12624 let definitions = definitions.await?;
12625 let navigated = editor
12626 .update_in(cx, |editor, window, cx| {
12627 editor.navigate_to_hover_links(
12628 Some(kind),
12629 definitions
12630 .into_iter()
12631 .filter(|location| {
12632 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
12633 })
12634 .map(HoverLink::Text)
12635 .collect::<Vec<_>>(),
12636 split,
12637 window,
12638 cx,
12639 )
12640 })?
12641 .await?;
12642 anyhow::Ok(navigated)
12643 })
12644 }
12645
12646 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
12647 let selection = self.selections.newest_anchor();
12648 let head = selection.head();
12649 let tail = selection.tail();
12650
12651 let Some((buffer, start_position)) =
12652 self.buffer.read(cx).text_anchor_for_position(head, cx)
12653 else {
12654 return;
12655 };
12656
12657 let end_position = if head != tail {
12658 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
12659 return;
12660 };
12661 Some(pos)
12662 } else {
12663 None
12664 };
12665
12666 let url_finder = cx.spawn_in(window, async move |editor, cx| {
12667 let url = if let Some(end_pos) = end_position {
12668 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
12669 } else {
12670 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
12671 };
12672
12673 if let Some(url) = url {
12674 editor.update(cx, |_, cx| {
12675 cx.open_url(&url);
12676 })
12677 } else {
12678 Ok(())
12679 }
12680 });
12681
12682 url_finder.detach();
12683 }
12684
12685 pub fn open_selected_filename(
12686 &mut self,
12687 _: &OpenSelectedFilename,
12688 window: &mut Window,
12689 cx: &mut Context<Self>,
12690 ) {
12691 let Some(workspace) = self.workspace() else {
12692 return;
12693 };
12694
12695 let position = self.selections.newest_anchor().head();
12696
12697 let Some((buffer, buffer_position)) =
12698 self.buffer.read(cx).text_anchor_for_position(position, cx)
12699 else {
12700 return;
12701 };
12702
12703 let project = self.project.clone();
12704
12705 cx.spawn_in(window, async move |_, cx| {
12706 let result = find_file(&buffer, project, buffer_position, cx).await;
12707
12708 if let Some((_, path)) = result {
12709 workspace
12710 .update_in(cx, |workspace, window, cx| {
12711 workspace.open_resolved_path(path, window, cx)
12712 })?
12713 .await?;
12714 }
12715 anyhow::Ok(())
12716 })
12717 .detach();
12718 }
12719
12720 pub(crate) fn navigate_to_hover_links(
12721 &mut self,
12722 kind: Option<GotoDefinitionKind>,
12723 mut definitions: Vec<HoverLink>,
12724 split: bool,
12725 window: &mut Window,
12726 cx: &mut Context<Editor>,
12727 ) -> Task<Result<Navigated>> {
12728 // If there is one definition, just open it directly
12729 if definitions.len() == 1 {
12730 let definition = definitions.pop().unwrap();
12731
12732 enum TargetTaskResult {
12733 Location(Option<Location>),
12734 AlreadyNavigated,
12735 }
12736
12737 let target_task = match definition {
12738 HoverLink::Text(link) => {
12739 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12740 }
12741 HoverLink::InlayHint(lsp_location, server_id) => {
12742 let computation =
12743 self.compute_target_location(lsp_location, server_id, window, cx);
12744 cx.background_spawn(async move {
12745 let location = computation.await?;
12746 Ok(TargetTaskResult::Location(location))
12747 })
12748 }
12749 HoverLink::Url(url) => {
12750 cx.open_url(&url);
12751 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12752 }
12753 HoverLink::File(path) => {
12754 if let Some(workspace) = self.workspace() {
12755 cx.spawn_in(window, async move |_, cx| {
12756 workspace
12757 .update_in(cx, |workspace, window, cx| {
12758 workspace.open_resolved_path(path, window, cx)
12759 })?
12760 .await
12761 .map(|_| TargetTaskResult::AlreadyNavigated)
12762 })
12763 } else {
12764 Task::ready(Ok(TargetTaskResult::Location(None)))
12765 }
12766 }
12767 };
12768 cx.spawn_in(window, async move |editor, cx| {
12769 let target = match target_task.await.context("target resolution task")? {
12770 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12771 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12772 TargetTaskResult::Location(Some(target)) => target,
12773 };
12774
12775 editor.update_in(cx, |editor, window, cx| {
12776 let Some(workspace) = editor.workspace() else {
12777 return Navigated::No;
12778 };
12779 let pane = workspace.read(cx).active_pane().clone();
12780
12781 let range = target.range.to_point(target.buffer.read(cx));
12782 let range = editor.range_for_match(&range);
12783 let range = collapse_multiline_range(range);
12784
12785 if !split
12786 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12787 {
12788 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12789 } else {
12790 window.defer(cx, move |window, cx| {
12791 let target_editor: Entity<Self> =
12792 workspace.update(cx, |workspace, cx| {
12793 let pane = if split {
12794 workspace.adjacent_pane(window, cx)
12795 } else {
12796 workspace.active_pane().clone()
12797 };
12798
12799 workspace.open_project_item(
12800 pane,
12801 target.buffer.clone(),
12802 true,
12803 true,
12804 window,
12805 cx,
12806 )
12807 });
12808 target_editor.update(cx, |target_editor, cx| {
12809 // When selecting a definition in a different buffer, disable the nav history
12810 // to avoid creating a history entry at the previous cursor location.
12811 pane.update(cx, |pane, _| pane.disable_history());
12812 target_editor.go_to_singleton_buffer_range(range, window, cx);
12813 pane.update(cx, |pane, _| pane.enable_history());
12814 });
12815 });
12816 }
12817 Navigated::Yes
12818 })
12819 })
12820 } else if !definitions.is_empty() {
12821 cx.spawn_in(window, async move |editor, cx| {
12822 let (title, location_tasks, workspace) = editor
12823 .update_in(cx, |editor, window, cx| {
12824 let tab_kind = match kind {
12825 Some(GotoDefinitionKind::Implementation) => "Implementations",
12826 _ => "Definitions",
12827 };
12828 let title = definitions
12829 .iter()
12830 .find_map(|definition| match definition {
12831 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12832 let buffer = origin.buffer.read(cx);
12833 format!(
12834 "{} for {}",
12835 tab_kind,
12836 buffer
12837 .text_for_range(origin.range.clone())
12838 .collect::<String>()
12839 )
12840 }),
12841 HoverLink::InlayHint(_, _) => None,
12842 HoverLink::Url(_) => None,
12843 HoverLink::File(_) => None,
12844 })
12845 .unwrap_or(tab_kind.to_string());
12846 let location_tasks = definitions
12847 .into_iter()
12848 .map(|definition| match definition {
12849 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12850 HoverLink::InlayHint(lsp_location, server_id) => editor
12851 .compute_target_location(lsp_location, server_id, window, cx),
12852 HoverLink::Url(_) => Task::ready(Ok(None)),
12853 HoverLink::File(_) => Task::ready(Ok(None)),
12854 })
12855 .collect::<Vec<_>>();
12856 (title, location_tasks, editor.workspace().clone())
12857 })
12858 .context("location tasks preparation")?;
12859
12860 let locations = future::join_all(location_tasks)
12861 .await
12862 .into_iter()
12863 .filter_map(|location| location.transpose())
12864 .collect::<Result<_>>()
12865 .context("location tasks")?;
12866
12867 let Some(workspace) = workspace else {
12868 return Ok(Navigated::No);
12869 };
12870 let opened = workspace
12871 .update_in(cx, |workspace, window, cx| {
12872 Self::open_locations_in_multibuffer(
12873 workspace,
12874 locations,
12875 title,
12876 split,
12877 MultibufferSelectionMode::First,
12878 window,
12879 cx,
12880 )
12881 })
12882 .ok();
12883
12884 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12885 })
12886 } else {
12887 Task::ready(Ok(Navigated::No))
12888 }
12889 }
12890
12891 fn compute_target_location(
12892 &self,
12893 lsp_location: lsp::Location,
12894 server_id: LanguageServerId,
12895 window: &mut Window,
12896 cx: &mut Context<Self>,
12897 ) -> Task<anyhow::Result<Option<Location>>> {
12898 let Some(project) = self.project.clone() else {
12899 return Task::ready(Ok(None));
12900 };
12901
12902 cx.spawn_in(window, async move |editor, cx| {
12903 let location_task = editor.update(cx, |_, cx| {
12904 project.update(cx, |project, cx| {
12905 let language_server_name = project
12906 .language_server_statuses(cx)
12907 .find(|(id, _)| server_id == *id)
12908 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12909 language_server_name.map(|language_server_name| {
12910 project.open_local_buffer_via_lsp(
12911 lsp_location.uri.clone(),
12912 server_id,
12913 language_server_name,
12914 cx,
12915 )
12916 })
12917 })
12918 })?;
12919 let location = match location_task {
12920 Some(task) => Some({
12921 let target_buffer_handle = task.await.context("open local buffer")?;
12922 let range = target_buffer_handle.update(cx, |target_buffer, _| {
12923 let target_start = target_buffer
12924 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12925 let target_end = target_buffer
12926 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12927 target_buffer.anchor_after(target_start)
12928 ..target_buffer.anchor_before(target_end)
12929 })?;
12930 Location {
12931 buffer: target_buffer_handle,
12932 range,
12933 }
12934 }),
12935 None => None,
12936 };
12937 Ok(location)
12938 })
12939 }
12940
12941 pub fn find_all_references(
12942 &mut self,
12943 _: &FindAllReferences,
12944 window: &mut Window,
12945 cx: &mut Context<Self>,
12946 ) -> Option<Task<Result<Navigated>>> {
12947 let selection = self.selections.newest::<usize>(cx);
12948 let multi_buffer = self.buffer.read(cx);
12949 let head = selection.head();
12950
12951 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12952 let head_anchor = multi_buffer_snapshot.anchor_at(
12953 head,
12954 if head < selection.tail() {
12955 Bias::Right
12956 } else {
12957 Bias::Left
12958 },
12959 );
12960
12961 match self
12962 .find_all_references_task_sources
12963 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12964 {
12965 Ok(_) => {
12966 log::info!(
12967 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12968 );
12969 return None;
12970 }
12971 Err(i) => {
12972 self.find_all_references_task_sources.insert(i, head_anchor);
12973 }
12974 }
12975
12976 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12977 let workspace = self.workspace()?;
12978 let project = workspace.read(cx).project().clone();
12979 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12980 Some(cx.spawn_in(window, async move |editor, cx| {
12981 let _cleanup = cx.on_drop(&editor, move |editor, _| {
12982 if let Ok(i) = editor
12983 .find_all_references_task_sources
12984 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12985 {
12986 editor.find_all_references_task_sources.remove(i);
12987 }
12988 });
12989
12990 let locations = references.await?;
12991 if locations.is_empty() {
12992 return anyhow::Ok(Navigated::No);
12993 }
12994
12995 workspace.update_in(cx, |workspace, window, cx| {
12996 let title = locations
12997 .first()
12998 .as_ref()
12999 .map(|location| {
13000 let buffer = location.buffer.read(cx);
13001 format!(
13002 "References to `{}`",
13003 buffer
13004 .text_for_range(location.range.clone())
13005 .collect::<String>()
13006 )
13007 })
13008 .unwrap();
13009 Self::open_locations_in_multibuffer(
13010 workspace,
13011 locations,
13012 title,
13013 false,
13014 MultibufferSelectionMode::First,
13015 window,
13016 cx,
13017 );
13018 Navigated::Yes
13019 })
13020 }))
13021 }
13022
13023 /// Opens a multibuffer with the given project locations in it
13024 pub fn open_locations_in_multibuffer(
13025 workspace: &mut Workspace,
13026 mut locations: Vec<Location>,
13027 title: String,
13028 split: bool,
13029 multibuffer_selection_mode: MultibufferSelectionMode,
13030 window: &mut Window,
13031 cx: &mut Context<Workspace>,
13032 ) {
13033 // If there are multiple definitions, open them in a multibuffer
13034 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
13035 let mut locations = locations.into_iter().peekable();
13036 let mut ranges = Vec::new();
13037 let capability = workspace.project().read(cx).capability();
13038
13039 let excerpt_buffer = cx.new(|cx| {
13040 let mut multibuffer = MultiBuffer::new(capability);
13041 while let Some(location) = locations.next() {
13042 let buffer = location.buffer.read(cx);
13043 let mut ranges_for_buffer = Vec::new();
13044 let range = location.range.to_offset(buffer);
13045 ranges_for_buffer.push(range.clone());
13046
13047 while let Some(next_location) = locations.peek() {
13048 if next_location.buffer == location.buffer {
13049 ranges_for_buffer.push(next_location.range.to_offset(buffer));
13050 locations.next();
13051 } else {
13052 break;
13053 }
13054 }
13055
13056 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
13057 ranges.extend(multibuffer.push_excerpts_with_context_lines(
13058 location.buffer.clone(),
13059 ranges_for_buffer,
13060 DEFAULT_MULTIBUFFER_CONTEXT,
13061 cx,
13062 ))
13063 }
13064
13065 multibuffer.with_title(title)
13066 });
13067
13068 let editor = cx.new(|cx| {
13069 Editor::for_multibuffer(
13070 excerpt_buffer,
13071 Some(workspace.project().clone()),
13072 window,
13073 cx,
13074 )
13075 });
13076 editor.update(cx, |editor, cx| {
13077 match multibuffer_selection_mode {
13078 MultibufferSelectionMode::First => {
13079 if let Some(first_range) = ranges.first() {
13080 editor.change_selections(None, window, cx, |selections| {
13081 selections.clear_disjoint();
13082 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
13083 });
13084 }
13085 editor.highlight_background::<Self>(
13086 &ranges,
13087 |theme| theme.editor_highlighted_line_background,
13088 cx,
13089 );
13090 }
13091 MultibufferSelectionMode::All => {
13092 editor.change_selections(None, window, cx, |selections| {
13093 selections.clear_disjoint();
13094 selections.select_anchor_ranges(ranges);
13095 });
13096 }
13097 }
13098 editor.register_buffers_with_language_servers(cx);
13099 });
13100
13101 let item = Box::new(editor);
13102 let item_id = item.item_id();
13103
13104 if split {
13105 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
13106 } else {
13107 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
13108 let (preview_item_id, preview_item_idx) =
13109 workspace.active_pane().update(cx, |pane, _| {
13110 (pane.preview_item_id(), pane.preview_item_idx())
13111 });
13112
13113 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
13114
13115 if let Some(preview_item_id) = preview_item_id {
13116 workspace.active_pane().update(cx, |pane, cx| {
13117 pane.remove_item(preview_item_id, false, false, window, cx);
13118 });
13119 }
13120 } else {
13121 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
13122 }
13123 }
13124 workspace.active_pane().update(cx, |pane, cx| {
13125 pane.set_preview_item_id(Some(item_id), cx);
13126 });
13127 }
13128
13129 pub fn rename(
13130 &mut self,
13131 _: &Rename,
13132 window: &mut Window,
13133 cx: &mut Context<Self>,
13134 ) -> Option<Task<Result<()>>> {
13135 use language::ToOffset as _;
13136
13137 let provider = self.semantics_provider.clone()?;
13138 let selection = self.selections.newest_anchor().clone();
13139 let (cursor_buffer, cursor_buffer_position) = self
13140 .buffer
13141 .read(cx)
13142 .text_anchor_for_position(selection.head(), cx)?;
13143 let (tail_buffer, cursor_buffer_position_end) = self
13144 .buffer
13145 .read(cx)
13146 .text_anchor_for_position(selection.tail(), cx)?;
13147 if tail_buffer != cursor_buffer {
13148 return None;
13149 }
13150
13151 let snapshot = cursor_buffer.read(cx).snapshot();
13152 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
13153 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
13154 let prepare_rename = provider
13155 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
13156 .unwrap_or_else(|| Task::ready(Ok(None)));
13157 drop(snapshot);
13158
13159 Some(cx.spawn_in(window, async move |this, cx| {
13160 let rename_range = if let Some(range) = prepare_rename.await? {
13161 Some(range)
13162 } else {
13163 this.update(cx, |this, cx| {
13164 let buffer = this.buffer.read(cx).snapshot(cx);
13165 let mut buffer_highlights = this
13166 .document_highlights_for_position(selection.head(), &buffer)
13167 .filter(|highlight| {
13168 highlight.start.excerpt_id == selection.head().excerpt_id
13169 && highlight.end.excerpt_id == selection.head().excerpt_id
13170 });
13171 buffer_highlights
13172 .next()
13173 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
13174 })?
13175 };
13176 if let Some(rename_range) = rename_range {
13177 this.update_in(cx, |this, window, cx| {
13178 let snapshot = cursor_buffer.read(cx).snapshot();
13179 let rename_buffer_range = rename_range.to_offset(&snapshot);
13180 let cursor_offset_in_rename_range =
13181 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
13182 let cursor_offset_in_rename_range_end =
13183 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
13184
13185 this.take_rename(false, window, cx);
13186 let buffer = this.buffer.read(cx).read(cx);
13187 let cursor_offset = selection.head().to_offset(&buffer);
13188 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
13189 let rename_end = rename_start + rename_buffer_range.len();
13190 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
13191 let mut old_highlight_id = None;
13192 let old_name: Arc<str> = buffer
13193 .chunks(rename_start..rename_end, true)
13194 .map(|chunk| {
13195 if old_highlight_id.is_none() {
13196 old_highlight_id = chunk.syntax_highlight_id;
13197 }
13198 chunk.text
13199 })
13200 .collect::<String>()
13201 .into();
13202
13203 drop(buffer);
13204
13205 // Position the selection in the rename editor so that it matches the current selection.
13206 this.show_local_selections = false;
13207 let rename_editor = cx.new(|cx| {
13208 let mut editor = Editor::single_line(window, cx);
13209 editor.buffer.update(cx, |buffer, cx| {
13210 buffer.edit([(0..0, old_name.clone())], None, cx)
13211 });
13212 let rename_selection_range = match cursor_offset_in_rename_range
13213 .cmp(&cursor_offset_in_rename_range_end)
13214 {
13215 Ordering::Equal => {
13216 editor.select_all(&SelectAll, window, cx);
13217 return editor;
13218 }
13219 Ordering::Less => {
13220 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
13221 }
13222 Ordering::Greater => {
13223 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
13224 }
13225 };
13226 if rename_selection_range.end > old_name.len() {
13227 editor.select_all(&SelectAll, window, cx);
13228 } else {
13229 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
13230 s.select_ranges([rename_selection_range]);
13231 });
13232 }
13233 editor
13234 });
13235 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
13236 if e == &EditorEvent::Focused {
13237 cx.emit(EditorEvent::FocusedIn)
13238 }
13239 })
13240 .detach();
13241
13242 let write_highlights =
13243 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
13244 let read_highlights =
13245 this.clear_background_highlights::<DocumentHighlightRead>(cx);
13246 let ranges = write_highlights
13247 .iter()
13248 .flat_map(|(_, ranges)| ranges.iter())
13249 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
13250 .cloned()
13251 .collect();
13252
13253 this.highlight_text::<Rename>(
13254 ranges,
13255 HighlightStyle {
13256 fade_out: Some(0.6),
13257 ..Default::default()
13258 },
13259 cx,
13260 );
13261 let rename_focus_handle = rename_editor.focus_handle(cx);
13262 window.focus(&rename_focus_handle);
13263 let block_id = this.insert_blocks(
13264 [BlockProperties {
13265 style: BlockStyle::Flex,
13266 placement: BlockPlacement::Below(range.start),
13267 height: 1,
13268 render: Arc::new({
13269 let rename_editor = rename_editor.clone();
13270 move |cx: &mut BlockContext| {
13271 let mut text_style = cx.editor_style.text.clone();
13272 if let Some(highlight_style) = old_highlight_id
13273 .and_then(|h| h.style(&cx.editor_style.syntax))
13274 {
13275 text_style = text_style.highlight(highlight_style);
13276 }
13277 div()
13278 .block_mouse_down()
13279 .pl(cx.anchor_x)
13280 .child(EditorElement::new(
13281 &rename_editor,
13282 EditorStyle {
13283 background: cx.theme().system().transparent,
13284 local_player: cx.editor_style.local_player,
13285 text: text_style,
13286 scrollbar_width: cx.editor_style.scrollbar_width,
13287 syntax: cx.editor_style.syntax.clone(),
13288 status: cx.editor_style.status.clone(),
13289 inlay_hints_style: HighlightStyle {
13290 font_weight: Some(FontWeight::BOLD),
13291 ..make_inlay_hints_style(cx.app)
13292 },
13293 inline_completion_styles: make_suggestion_styles(
13294 cx.app,
13295 ),
13296 ..EditorStyle::default()
13297 },
13298 ))
13299 .into_any_element()
13300 }
13301 }),
13302 priority: 0,
13303 }],
13304 Some(Autoscroll::fit()),
13305 cx,
13306 )[0];
13307 this.pending_rename = Some(RenameState {
13308 range,
13309 old_name,
13310 editor: rename_editor,
13311 block_id,
13312 });
13313 })?;
13314 }
13315
13316 Ok(())
13317 }))
13318 }
13319
13320 pub fn confirm_rename(
13321 &mut self,
13322 _: &ConfirmRename,
13323 window: &mut Window,
13324 cx: &mut Context<Self>,
13325 ) -> Option<Task<Result<()>>> {
13326 let rename = self.take_rename(false, window, cx)?;
13327 let workspace = self.workspace()?.downgrade();
13328 let (buffer, start) = self
13329 .buffer
13330 .read(cx)
13331 .text_anchor_for_position(rename.range.start, cx)?;
13332 let (end_buffer, _) = self
13333 .buffer
13334 .read(cx)
13335 .text_anchor_for_position(rename.range.end, cx)?;
13336 if buffer != end_buffer {
13337 return None;
13338 }
13339
13340 let old_name = rename.old_name;
13341 let new_name = rename.editor.read(cx).text(cx);
13342
13343 let rename = self.semantics_provider.as_ref()?.perform_rename(
13344 &buffer,
13345 start,
13346 new_name.clone(),
13347 cx,
13348 )?;
13349
13350 Some(cx.spawn_in(window, async move |editor, cx| {
13351 let project_transaction = rename.await?;
13352 Self::open_project_transaction(
13353 &editor,
13354 workspace,
13355 project_transaction,
13356 format!("Rename: {} → {}", old_name, new_name),
13357 cx,
13358 )
13359 .await?;
13360
13361 editor.update(cx, |editor, cx| {
13362 editor.refresh_document_highlights(cx);
13363 })?;
13364 Ok(())
13365 }))
13366 }
13367
13368 fn take_rename(
13369 &mut self,
13370 moving_cursor: bool,
13371 window: &mut Window,
13372 cx: &mut Context<Self>,
13373 ) -> Option<RenameState> {
13374 let rename = self.pending_rename.take()?;
13375 if rename.editor.focus_handle(cx).is_focused(window) {
13376 window.focus(&self.focus_handle);
13377 }
13378
13379 self.remove_blocks(
13380 [rename.block_id].into_iter().collect(),
13381 Some(Autoscroll::fit()),
13382 cx,
13383 );
13384 self.clear_highlights::<Rename>(cx);
13385 self.show_local_selections = true;
13386
13387 if moving_cursor {
13388 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
13389 editor.selections.newest::<usize>(cx).head()
13390 });
13391
13392 // Update the selection to match the position of the selection inside
13393 // the rename editor.
13394 let snapshot = self.buffer.read(cx).read(cx);
13395 let rename_range = rename.range.to_offset(&snapshot);
13396 let cursor_in_editor = snapshot
13397 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
13398 .min(rename_range.end);
13399 drop(snapshot);
13400
13401 self.change_selections(None, window, cx, |s| {
13402 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
13403 });
13404 } else {
13405 self.refresh_document_highlights(cx);
13406 }
13407
13408 Some(rename)
13409 }
13410
13411 pub fn pending_rename(&self) -> Option<&RenameState> {
13412 self.pending_rename.as_ref()
13413 }
13414
13415 fn format(
13416 &mut self,
13417 _: &Format,
13418 window: &mut Window,
13419 cx: &mut Context<Self>,
13420 ) -> Option<Task<Result<()>>> {
13421 let project = match &self.project {
13422 Some(project) => project.clone(),
13423 None => return None,
13424 };
13425
13426 Some(self.perform_format(
13427 project,
13428 FormatTrigger::Manual,
13429 FormatTarget::Buffers,
13430 window,
13431 cx,
13432 ))
13433 }
13434
13435 fn format_selections(
13436 &mut self,
13437 _: &FormatSelections,
13438 window: &mut Window,
13439 cx: &mut Context<Self>,
13440 ) -> Option<Task<Result<()>>> {
13441 let project = match &self.project {
13442 Some(project) => project.clone(),
13443 None => return None,
13444 };
13445
13446 let ranges = self
13447 .selections
13448 .all_adjusted(cx)
13449 .into_iter()
13450 .map(|selection| selection.range())
13451 .collect_vec();
13452
13453 Some(self.perform_format(
13454 project,
13455 FormatTrigger::Manual,
13456 FormatTarget::Ranges(ranges),
13457 window,
13458 cx,
13459 ))
13460 }
13461
13462 fn perform_format(
13463 &mut self,
13464 project: Entity<Project>,
13465 trigger: FormatTrigger,
13466 target: FormatTarget,
13467 window: &mut Window,
13468 cx: &mut Context<Self>,
13469 ) -> Task<Result<()>> {
13470 let buffer = self.buffer.clone();
13471 let (buffers, target) = match target {
13472 FormatTarget::Buffers => {
13473 let mut buffers = buffer.read(cx).all_buffers();
13474 if trigger == FormatTrigger::Save {
13475 buffers.retain(|buffer| buffer.read(cx).is_dirty());
13476 }
13477 (buffers, LspFormatTarget::Buffers)
13478 }
13479 FormatTarget::Ranges(selection_ranges) => {
13480 let multi_buffer = buffer.read(cx);
13481 let snapshot = multi_buffer.read(cx);
13482 let mut buffers = HashSet::default();
13483 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
13484 BTreeMap::new();
13485 for selection_range in selection_ranges {
13486 for (buffer, buffer_range, _) in
13487 snapshot.range_to_buffer_ranges(selection_range)
13488 {
13489 let buffer_id = buffer.remote_id();
13490 let start = buffer.anchor_before(buffer_range.start);
13491 let end = buffer.anchor_after(buffer_range.end);
13492 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
13493 buffer_id_to_ranges
13494 .entry(buffer_id)
13495 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
13496 .or_insert_with(|| vec![start..end]);
13497 }
13498 }
13499 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
13500 }
13501 };
13502
13503 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
13504 let format = project.update(cx, |project, cx| {
13505 project.format(buffers, target, true, trigger, cx)
13506 });
13507
13508 cx.spawn_in(window, async move |_, cx| {
13509 let transaction = futures::select_biased! {
13510 transaction = format.log_err().fuse() => transaction,
13511 () = timeout => {
13512 log::warn!("timed out waiting for formatting");
13513 None
13514 }
13515 };
13516
13517 buffer
13518 .update(cx, |buffer, cx| {
13519 if let Some(transaction) = transaction {
13520 if !buffer.is_singleton() {
13521 buffer.push_transaction(&transaction.0, cx);
13522 }
13523 }
13524 cx.notify();
13525 })
13526 .ok();
13527
13528 Ok(())
13529 })
13530 }
13531
13532 fn organize_imports(
13533 &mut self,
13534 _: &OrganizeImports,
13535 window: &mut Window,
13536 cx: &mut Context<Self>,
13537 ) -> Option<Task<Result<()>>> {
13538 let project = match &self.project {
13539 Some(project) => project.clone(),
13540 None => return None,
13541 };
13542 Some(self.perform_code_action_kind(
13543 project,
13544 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
13545 window,
13546 cx,
13547 ))
13548 }
13549
13550 fn perform_code_action_kind(
13551 &mut self,
13552 project: Entity<Project>,
13553 kind: CodeActionKind,
13554 window: &mut Window,
13555 cx: &mut Context<Self>,
13556 ) -> Task<Result<()>> {
13557 let buffer = self.buffer.clone();
13558 let buffers = buffer.read(cx).all_buffers();
13559 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
13560 let apply_action = project.update(cx, |project, cx| {
13561 project.apply_code_action_kind(buffers, kind, true, cx)
13562 });
13563 cx.spawn_in(window, async move |_, cx| {
13564 let transaction = futures::select_biased! {
13565 () = timeout => {
13566 log::warn!("timed out waiting for executing code action");
13567 None
13568 }
13569 transaction = apply_action.log_err().fuse() => transaction,
13570 };
13571 buffer
13572 .update(cx, |buffer, cx| {
13573 // check if we need this
13574 if let Some(transaction) = transaction {
13575 if !buffer.is_singleton() {
13576 buffer.push_transaction(&transaction.0, cx);
13577 }
13578 }
13579 cx.notify();
13580 })
13581 .ok();
13582 Ok(())
13583 })
13584 }
13585
13586 fn restart_language_server(
13587 &mut self,
13588 _: &RestartLanguageServer,
13589 _: &mut Window,
13590 cx: &mut Context<Self>,
13591 ) {
13592 if let Some(project) = self.project.clone() {
13593 self.buffer.update(cx, |multi_buffer, cx| {
13594 project.update(cx, |project, cx| {
13595 project.restart_language_servers_for_buffers(
13596 multi_buffer.all_buffers().into_iter().collect(),
13597 cx,
13598 );
13599 });
13600 })
13601 }
13602 }
13603
13604 fn cancel_language_server_work(
13605 workspace: &mut Workspace,
13606 _: &actions::CancelLanguageServerWork,
13607 _: &mut Window,
13608 cx: &mut Context<Workspace>,
13609 ) {
13610 let project = workspace.project();
13611 let buffers = workspace
13612 .active_item(cx)
13613 .and_then(|item| item.act_as::<Editor>(cx))
13614 .map_or(HashSet::default(), |editor| {
13615 editor.read(cx).buffer.read(cx).all_buffers()
13616 });
13617 project.update(cx, |project, cx| {
13618 project.cancel_language_server_work_for_buffers(buffers, cx);
13619 });
13620 }
13621
13622 fn show_character_palette(
13623 &mut self,
13624 _: &ShowCharacterPalette,
13625 window: &mut Window,
13626 _: &mut Context<Self>,
13627 ) {
13628 window.show_character_palette();
13629 }
13630
13631 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
13632 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
13633 let buffer = self.buffer.read(cx).snapshot(cx);
13634 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
13635 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
13636 let is_valid = buffer
13637 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
13638 .any(|entry| {
13639 entry.diagnostic.is_primary
13640 && !entry.range.is_empty()
13641 && entry.range.start == primary_range_start
13642 && entry.diagnostic.message == active_diagnostics.primary_message
13643 });
13644
13645 if is_valid != active_diagnostics.is_valid {
13646 active_diagnostics.is_valid = is_valid;
13647 if is_valid {
13648 let mut new_styles = HashMap::default();
13649 for (block_id, diagnostic) in &active_diagnostics.blocks {
13650 new_styles.insert(
13651 *block_id,
13652 diagnostic_block_renderer(diagnostic.clone(), None, true),
13653 );
13654 }
13655 self.display_map.update(cx, |display_map, _cx| {
13656 display_map.replace_blocks(new_styles);
13657 });
13658 } else {
13659 self.dismiss_diagnostics(cx);
13660 }
13661 }
13662 }
13663 }
13664
13665 fn activate_diagnostics(
13666 &mut self,
13667 buffer_id: BufferId,
13668 group_id: usize,
13669 window: &mut Window,
13670 cx: &mut Context<Self>,
13671 ) {
13672 self.dismiss_diagnostics(cx);
13673 let snapshot = self.snapshot(window, cx);
13674 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13675 let buffer = self.buffer.read(cx).snapshot(cx);
13676
13677 let mut primary_range = None;
13678 let mut primary_message = None;
13679 let diagnostic_group = buffer
13680 .diagnostic_group(buffer_id, group_id)
13681 .filter_map(|entry| {
13682 let start = entry.range.start;
13683 let end = entry.range.end;
13684 if snapshot.is_line_folded(MultiBufferRow(start.row))
13685 && (start.row == end.row
13686 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13687 {
13688 return None;
13689 }
13690 if entry.diagnostic.is_primary {
13691 primary_range = Some(entry.range.clone());
13692 primary_message = Some(entry.diagnostic.message.clone());
13693 }
13694 Some(entry)
13695 })
13696 .collect::<Vec<_>>();
13697 let primary_range = primary_range?;
13698 let primary_message = primary_message?;
13699
13700 let blocks = display_map
13701 .insert_blocks(
13702 diagnostic_group.iter().map(|entry| {
13703 let diagnostic = entry.diagnostic.clone();
13704 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13705 BlockProperties {
13706 style: BlockStyle::Fixed,
13707 placement: BlockPlacement::Below(
13708 buffer.anchor_after(entry.range.start),
13709 ),
13710 height: message_height,
13711 render: diagnostic_block_renderer(diagnostic, None, true),
13712 priority: 0,
13713 }
13714 }),
13715 cx,
13716 )
13717 .into_iter()
13718 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13719 .collect();
13720
13721 Some(ActiveDiagnosticGroup {
13722 primary_range: buffer.anchor_before(primary_range.start)
13723 ..buffer.anchor_after(primary_range.end),
13724 primary_message,
13725 group_id,
13726 blocks,
13727 is_valid: true,
13728 })
13729 });
13730 }
13731
13732 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13733 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13734 self.display_map.update(cx, |display_map, cx| {
13735 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13736 });
13737 cx.notify();
13738 }
13739 }
13740
13741 /// Disable inline diagnostics rendering for this editor.
13742 pub fn disable_inline_diagnostics(&mut self) {
13743 self.inline_diagnostics_enabled = false;
13744 self.inline_diagnostics_update = Task::ready(());
13745 self.inline_diagnostics.clear();
13746 }
13747
13748 pub fn inline_diagnostics_enabled(&self) -> bool {
13749 self.inline_diagnostics_enabled
13750 }
13751
13752 pub fn show_inline_diagnostics(&self) -> bool {
13753 self.show_inline_diagnostics
13754 }
13755
13756 pub fn toggle_inline_diagnostics(
13757 &mut self,
13758 _: &ToggleInlineDiagnostics,
13759 window: &mut Window,
13760 cx: &mut Context<'_, Editor>,
13761 ) {
13762 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13763 self.refresh_inline_diagnostics(false, window, cx);
13764 }
13765
13766 fn refresh_inline_diagnostics(
13767 &mut self,
13768 debounce: bool,
13769 window: &mut Window,
13770 cx: &mut Context<Self>,
13771 ) {
13772 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13773 self.inline_diagnostics_update = Task::ready(());
13774 self.inline_diagnostics.clear();
13775 return;
13776 }
13777
13778 let debounce_ms = ProjectSettings::get_global(cx)
13779 .diagnostics
13780 .inline
13781 .update_debounce_ms;
13782 let debounce = if debounce && debounce_ms > 0 {
13783 Some(Duration::from_millis(debounce_ms))
13784 } else {
13785 None
13786 };
13787 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
13788 if let Some(debounce) = debounce {
13789 cx.background_executor().timer(debounce).await;
13790 }
13791 let Some(snapshot) = editor
13792 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13793 .ok()
13794 else {
13795 return;
13796 };
13797
13798 let new_inline_diagnostics = cx
13799 .background_spawn(async move {
13800 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13801 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13802 let message = diagnostic_entry
13803 .diagnostic
13804 .message
13805 .split_once('\n')
13806 .map(|(line, _)| line)
13807 .map(SharedString::new)
13808 .unwrap_or_else(|| {
13809 SharedString::from(diagnostic_entry.diagnostic.message)
13810 });
13811 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13812 let (Ok(i) | Err(i)) = inline_diagnostics
13813 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13814 inline_diagnostics.insert(
13815 i,
13816 (
13817 start_anchor,
13818 InlineDiagnostic {
13819 message,
13820 group_id: diagnostic_entry.diagnostic.group_id,
13821 start: diagnostic_entry.range.start.to_point(&snapshot),
13822 is_primary: diagnostic_entry.diagnostic.is_primary,
13823 severity: diagnostic_entry.diagnostic.severity,
13824 },
13825 ),
13826 );
13827 }
13828 inline_diagnostics
13829 })
13830 .await;
13831
13832 editor
13833 .update(cx, |editor, cx| {
13834 editor.inline_diagnostics = new_inline_diagnostics;
13835 cx.notify();
13836 })
13837 .ok();
13838 });
13839 }
13840
13841 pub fn set_selections_from_remote(
13842 &mut self,
13843 selections: Vec<Selection<Anchor>>,
13844 pending_selection: Option<Selection<Anchor>>,
13845 window: &mut Window,
13846 cx: &mut Context<Self>,
13847 ) {
13848 let old_cursor_position = self.selections.newest_anchor().head();
13849 self.selections.change_with(cx, |s| {
13850 s.select_anchors(selections);
13851 if let Some(pending_selection) = pending_selection {
13852 s.set_pending(pending_selection, SelectMode::Character);
13853 } else {
13854 s.clear_pending();
13855 }
13856 });
13857 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13858 }
13859
13860 fn push_to_selection_history(&mut self) {
13861 self.selection_history.push(SelectionHistoryEntry {
13862 selections: self.selections.disjoint_anchors(),
13863 select_next_state: self.select_next_state.clone(),
13864 select_prev_state: self.select_prev_state.clone(),
13865 add_selections_state: self.add_selections_state.clone(),
13866 });
13867 }
13868
13869 pub fn transact(
13870 &mut self,
13871 window: &mut Window,
13872 cx: &mut Context<Self>,
13873 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13874 ) -> Option<TransactionId> {
13875 self.start_transaction_at(Instant::now(), window, cx);
13876 update(self, window, cx);
13877 self.end_transaction_at(Instant::now(), cx)
13878 }
13879
13880 pub fn start_transaction_at(
13881 &mut self,
13882 now: Instant,
13883 window: &mut Window,
13884 cx: &mut Context<Self>,
13885 ) {
13886 self.end_selection(window, cx);
13887 if let Some(tx_id) = self
13888 .buffer
13889 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13890 {
13891 self.selection_history
13892 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13893 cx.emit(EditorEvent::TransactionBegun {
13894 transaction_id: tx_id,
13895 })
13896 }
13897 }
13898
13899 pub fn end_transaction_at(
13900 &mut self,
13901 now: Instant,
13902 cx: &mut Context<Self>,
13903 ) -> Option<TransactionId> {
13904 if let Some(transaction_id) = self
13905 .buffer
13906 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13907 {
13908 if let Some((_, end_selections)) =
13909 self.selection_history.transaction_mut(transaction_id)
13910 {
13911 *end_selections = Some(self.selections.disjoint_anchors());
13912 } else {
13913 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13914 }
13915
13916 cx.emit(EditorEvent::Edited { transaction_id });
13917 Some(transaction_id)
13918 } else {
13919 None
13920 }
13921 }
13922
13923 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13924 if self.selection_mark_mode {
13925 self.change_selections(None, window, cx, |s| {
13926 s.move_with(|_, sel| {
13927 sel.collapse_to(sel.head(), SelectionGoal::None);
13928 });
13929 })
13930 }
13931 self.selection_mark_mode = true;
13932 cx.notify();
13933 }
13934
13935 pub fn swap_selection_ends(
13936 &mut self,
13937 _: &actions::SwapSelectionEnds,
13938 window: &mut Window,
13939 cx: &mut Context<Self>,
13940 ) {
13941 self.change_selections(None, window, cx, |s| {
13942 s.move_with(|_, sel| {
13943 if sel.start != sel.end {
13944 sel.reversed = !sel.reversed
13945 }
13946 });
13947 });
13948 self.request_autoscroll(Autoscroll::newest(), cx);
13949 cx.notify();
13950 }
13951
13952 pub fn toggle_fold(
13953 &mut self,
13954 _: &actions::ToggleFold,
13955 window: &mut Window,
13956 cx: &mut Context<Self>,
13957 ) {
13958 if self.is_singleton(cx) {
13959 let selection = self.selections.newest::<Point>(cx);
13960
13961 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13962 let range = if selection.is_empty() {
13963 let point = selection.head().to_display_point(&display_map);
13964 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13965 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13966 .to_point(&display_map);
13967 start..end
13968 } else {
13969 selection.range()
13970 };
13971 if display_map.folds_in_range(range).next().is_some() {
13972 self.unfold_lines(&Default::default(), window, cx)
13973 } else {
13974 self.fold(&Default::default(), window, cx)
13975 }
13976 } else {
13977 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13978 let buffer_ids: HashSet<_> = self
13979 .selections
13980 .disjoint_anchor_ranges()
13981 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13982 .collect();
13983
13984 let should_unfold = buffer_ids
13985 .iter()
13986 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13987
13988 for buffer_id in buffer_ids {
13989 if should_unfold {
13990 self.unfold_buffer(buffer_id, cx);
13991 } else {
13992 self.fold_buffer(buffer_id, cx);
13993 }
13994 }
13995 }
13996 }
13997
13998 pub fn toggle_fold_recursive(
13999 &mut self,
14000 _: &actions::ToggleFoldRecursive,
14001 window: &mut Window,
14002 cx: &mut Context<Self>,
14003 ) {
14004 let selection = self.selections.newest::<Point>(cx);
14005
14006 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14007 let range = if selection.is_empty() {
14008 let point = selection.head().to_display_point(&display_map);
14009 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
14010 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
14011 .to_point(&display_map);
14012 start..end
14013 } else {
14014 selection.range()
14015 };
14016 if display_map.folds_in_range(range).next().is_some() {
14017 self.unfold_recursive(&Default::default(), window, cx)
14018 } else {
14019 self.fold_recursive(&Default::default(), window, cx)
14020 }
14021 }
14022
14023 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
14024 if self.is_singleton(cx) {
14025 let mut to_fold = Vec::new();
14026 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14027 let selections = self.selections.all_adjusted(cx);
14028
14029 for selection in selections {
14030 let range = selection.range().sorted();
14031 let buffer_start_row = range.start.row;
14032
14033 if range.start.row != range.end.row {
14034 let mut found = false;
14035 let mut row = range.start.row;
14036 while row <= range.end.row {
14037 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
14038 {
14039 found = true;
14040 row = crease.range().end.row + 1;
14041 to_fold.push(crease);
14042 } else {
14043 row += 1
14044 }
14045 }
14046 if found {
14047 continue;
14048 }
14049 }
14050
14051 for row in (0..=range.start.row).rev() {
14052 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14053 if crease.range().end.row >= buffer_start_row {
14054 to_fold.push(crease);
14055 if row <= range.start.row {
14056 break;
14057 }
14058 }
14059 }
14060 }
14061 }
14062
14063 self.fold_creases(to_fold, true, window, cx);
14064 } else {
14065 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14066 let buffer_ids = self
14067 .selections
14068 .disjoint_anchor_ranges()
14069 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14070 .collect::<HashSet<_>>();
14071 for buffer_id in buffer_ids {
14072 self.fold_buffer(buffer_id, cx);
14073 }
14074 }
14075 }
14076
14077 fn fold_at_level(
14078 &mut self,
14079 fold_at: &FoldAtLevel,
14080 window: &mut Window,
14081 cx: &mut Context<Self>,
14082 ) {
14083 if !self.buffer.read(cx).is_singleton() {
14084 return;
14085 }
14086
14087 let fold_at_level = fold_at.0;
14088 let snapshot = self.buffer.read(cx).snapshot(cx);
14089 let mut to_fold = Vec::new();
14090 let mut stack = vec![(0, snapshot.max_row().0, 1)];
14091
14092 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
14093 while start_row < end_row {
14094 match self
14095 .snapshot(window, cx)
14096 .crease_for_buffer_row(MultiBufferRow(start_row))
14097 {
14098 Some(crease) => {
14099 let nested_start_row = crease.range().start.row + 1;
14100 let nested_end_row = crease.range().end.row;
14101
14102 if current_level < fold_at_level {
14103 stack.push((nested_start_row, nested_end_row, current_level + 1));
14104 } else if current_level == fold_at_level {
14105 to_fold.push(crease);
14106 }
14107
14108 start_row = nested_end_row + 1;
14109 }
14110 None => start_row += 1,
14111 }
14112 }
14113 }
14114
14115 self.fold_creases(to_fold, true, window, cx);
14116 }
14117
14118 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
14119 if self.buffer.read(cx).is_singleton() {
14120 let mut fold_ranges = Vec::new();
14121 let snapshot = self.buffer.read(cx).snapshot(cx);
14122
14123 for row in 0..snapshot.max_row().0 {
14124 if let Some(foldable_range) = self
14125 .snapshot(window, cx)
14126 .crease_for_buffer_row(MultiBufferRow(row))
14127 {
14128 fold_ranges.push(foldable_range);
14129 }
14130 }
14131
14132 self.fold_creases(fold_ranges, true, window, cx);
14133 } else {
14134 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
14135 editor
14136 .update_in(cx, |editor, _, cx| {
14137 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14138 editor.fold_buffer(buffer_id, cx);
14139 }
14140 })
14141 .ok();
14142 });
14143 }
14144 }
14145
14146 pub fn fold_function_bodies(
14147 &mut self,
14148 _: &actions::FoldFunctionBodies,
14149 window: &mut Window,
14150 cx: &mut Context<Self>,
14151 ) {
14152 let snapshot = self.buffer.read(cx).snapshot(cx);
14153
14154 let ranges = snapshot
14155 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
14156 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
14157 .collect::<Vec<_>>();
14158
14159 let creases = ranges
14160 .into_iter()
14161 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
14162 .collect();
14163
14164 self.fold_creases(creases, true, window, cx);
14165 }
14166
14167 pub fn fold_recursive(
14168 &mut self,
14169 _: &actions::FoldRecursive,
14170 window: &mut Window,
14171 cx: &mut Context<Self>,
14172 ) {
14173 let mut to_fold = Vec::new();
14174 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14175 let selections = self.selections.all_adjusted(cx);
14176
14177 for selection in selections {
14178 let range = selection.range().sorted();
14179 let buffer_start_row = range.start.row;
14180
14181 if range.start.row != range.end.row {
14182 let mut found = false;
14183 for row in range.start.row..=range.end.row {
14184 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14185 found = true;
14186 to_fold.push(crease);
14187 }
14188 }
14189 if found {
14190 continue;
14191 }
14192 }
14193
14194 for row in (0..=range.start.row).rev() {
14195 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
14196 if crease.range().end.row >= buffer_start_row {
14197 to_fold.push(crease);
14198 } else {
14199 break;
14200 }
14201 }
14202 }
14203 }
14204
14205 self.fold_creases(to_fold, true, window, cx);
14206 }
14207
14208 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
14209 let buffer_row = fold_at.buffer_row;
14210 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14211
14212 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
14213 let autoscroll = self
14214 .selections
14215 .all::<Point>(cx)
14216 .iter()
14217 .any(|selection| crease.range().overlaps(&selection.range()));
14218
14219 self.fold_creases(vec![crease], autoscroll, window, cx);
14220 }
14221 }
14222
14223 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
14224 if self.is_singleton(cx) {
14225 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14226 let buffer = &display_map.buffer_snapshot;
14227 let selections = self.selections.all::<Point>(cx);
14228 let ranges = selections
14229 .iter()
14230 .map(|s| {
14231 let range = s.display_range(&display_map).sorted();
14232 let mut start = range.start.to_point(&display_map);
14233 let mut end = range.end.to_point(&display_map);
14234 start.column = 0;
14235 end.column = buffer.line_len(MultiBufferRow(end.row));
14236 start..end
14237 })
14238 .collect::<Vec<_>>();
14239
14240 self.unfold_ranges(&ranges, true, true, cx);
14241 } else {
14242 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14243 let buffer_ids = self
14244 .selections
14245 .disjoint_anchor_ranges()
14246 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
14247 .collect::<HashSet<_>>();
14248 for buffer_id in buffer_ids {
14249 self.unfold_buffer(buffer_id, cx);
14250 }
14251 }
14252 }
14253
14254 pub fn unfold_recursive(
14255 &mut self,
14256 _: &UnfoldRecursive,
14257 _window: &mut Window,
14258 cx: &mut Context<Self>,
14259 ) {
14260 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14261 let selections = self.selections.all::<Point>(cx);
14262 let ranges = selections
14263 .iter()
14264 .map(|s| {
14265 let mut range = s.display_range(&display_map).sorted();
14266 *range.start.column_mut() = 0;
14267 *range.end.column_mut() = display_map.line_len(range.end.row());
14268 let start = range.start.to_point(&display_map);
14269 let end = range.end.to_point(&display_map);
14270 start..end
14271 })
14272 .collect::<Vec<_>>();
14273
14274 self.unfold_ranges(&ranges, true, true, cx);
14275 }
14276
14277 pub fn unfold_at(
14278 &mut self,
14279 unfold_at: &UnfoldAt,
14280 _window: &mut Window,
14281 cx: &mut Context<Self>,
14282 ) {
14283 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14284
14285 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
14286 ..Point::new(
14287 unfold_at.buffer_row.0,
14288 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
14289 );
14290
14291 let autoscroll = self
14292 .selections
14293 .all::<Point>(cx)
14294 .iter()
14295 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
14296
14297 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
14298 }
14299
14300 pub fn unfold_all(
14301 &mut self,
14302 _: &actions::UnfoldAll,
14303 _window: &mut Window,
14304 cx: &mut Context<Self>,
14305 ) {
14306 if self.buffer.read(cx).is_singleton() {
14307 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14308 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
14309 } else {
14310 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
14311 editor
14312 .update(cx, |editor, cx| {
14313 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
14314 editor.unfold_buffer(buffer_id, cx);
14315 }
14316 })
14317 .ok();
14318 });
14319 }
14320 }
14321
14322 pub fn fold_selected_ranges(
14323 &mut self,
14324 _: &FoldSelectedRanges,
14325 window: &mut Window,
14326 cx: &mut Context<Self>,
14327 ) {
14328 let selections = self.selections.all::<Point>(cx);
14329 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14330 let line_mode = self.selections.line_mode;
14331 let ranges = selections
14332 .into_iter()
14333 .map(|s| {
14334 if line_mode {
14335 let start = Point::new(s.start.row, 0);
14336 let end = Point::new(
14337 s.end.row,
14338 display_map
14339 .buffer_snapshot
14340 .line_len(MultiBufferRow(s.end.row)),
14341 );
14342 Crease::simple(start..end, display_map.fold_placeholder.clone())
14343 } else {
14344 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
14345 }
14346 })
14347 .collect::<Vec<_>>();
14348 self.fold_creases(ranges, true, window, cx);
14349 }
14350
14351 pub fn fold_ranges<T: ToOffset + Clone>(
14352 &mut self,
14353 ranges: Vec<Range<T>>,
14354 auto_scroll: bool,
14355 window: &mut Window,
14356 cx: &mut Context<Self>,
14357 ) {
14358 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14359 let ranges = ranges
14360 .into_iter()
14361 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
14362 .collect::<Vec<_>>();
14363 self.fold_creases(ranges, auto_scroll, window, cx);
14364 }
14365
14366 pub fn fold_creases<T: ToOffset + Clone>(
14367 &mut self,
14368 creases: Vec<Crease<T>>,
14369 auto_scroll: bool,
14370 window: &mut Window,
14371 cx: &mut Context<Self>,
14372 ) {
14373 if creases.is_empty() {
14374 return;
14375 }
14376
14377 let mut buffers_affected = HashSet::default();
14378 let multi_buffer = self.buffer().read(cx);
14379 for crease in &creases {
14380 if let Some((_, buffer, _)) =
14381 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
14382 {
14383 buffers_affected.insert(buffer.read(cx).remote_id());
14384 };
14385 }
14386
14387 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
14388
14389 if auto_scroll {
14390 self.request_autoscroll(Autoscroll::fit(), cx);
14391 }
14392
14393 cx.notify();
14394
14395 if let Some(active_diagnostics) = self.active_diagnostics.take() {
14396 // Clear diagnostics block when folding a range that contains it.
14397 let snapshot = self.snapshot(window, cx);
14398 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
14399 drop(snapshot);
14400 self.active_diagnostics = Some(active_diagnostics);
14401 self.dismiss_diagnostics(cx);
14402 } else {
14403 self.active_diagnostics = Some(active_diagnostics);
14404 }
14405 }
14406
14407 self.scrollbar_marker_state.dirty = true;
14408 self.folds_did_change(cx);
14409 }
14410
14411 /// Removes any folds whose ranges intersect any of the given ranges.
14412 pub fn unfold_ranges<T: ToOffset + Clone>(
14413 &mut self,
14414 ranges: &[Range<T>],
14415 inclusive: bool,
14416 auto_scroll: bool,
14417 cx: &mut Context<Self>,
14418 ) {
14419 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14420 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
14421 });
14422 self.folds_did_change(cx);
14423 }
14424
14425 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14426 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
14427 return;
14428 }
14429 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14430 self.display_map.update(cx, |display_map, cx| {
14431 display_map.fold_buffers([buffer_id], cx)
14432 });
14433 cx.emit(EditorEvent::BufferFoldToggled {
14434 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
14435 folded: true,
14436 });
14437 cx.notify();
14438 }
14439
14440 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
14441 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
14442 return;
14443 }
14444 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
14445 self.display_map.update(cx, |display_map, cx| {
14446 display_map.unfold_buffers([buffer_id], cx);
14447 });
14448 cx.emit(EditorEvent::BufferFoldToggled {
14449 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
14450 folded: false,
14451 });
14452 cx.notify();
14453 }
14454
14455 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
14456 self.display_map.read(cx).is_buffer_folded(buffer)
14457 }
14458
14459 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
14460 self.display_map.read(cx).folded_buffers()
14461 }
14462
14463 /// Removes any folds with the given ranges.
14464 pub fn remove_folds_with_type<T: ToOffset + Clone>(
14465 &mut self,
14466 ranges: &[Range<T>],
14467 type_id: TypeId,
14468 auto_scroll: bool,
14469 cx: &mut Context<Self>,
14470 ) {
14471 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
14472 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
14473 });
14474 self.folds_did_change(cx);
14475 }
14476
14477 fn remove_folds_with<T: ToOffset + Clone>(
14478 &mut self,
14479 ranges: &[Range<T>],
14480 auto_scroll: bool,
14481 cx: &mut Context<Self>,
14482 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
14483 ) {
14484 if ranges.is_empty() {
14485 return;
14486 }
14487
14488 let mut buffers_affected = HashSet::default();
14489 let multi_buffer = self.buffer().read(cx);
14490 for range in ranges {
14491 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
14492 buffers_affected.insert(buffer.read(cx).remote_id());
14493 };
14494 }
14495
14496 self.display_map.update(cx, update);
14497
14498 if auto_scroll {
14499 self.request_autoscroll(Autoscroll::fit(), cx);
14500 }
14501
14502 cx.notify();
14503 self.scrollbar_marker_state.dirty = true;
14504 self.active_indent_guides_state.dirty = true;
14505 }
14506
14507 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
14508 self.display_map.read(cx).fold_placeholder.clone()
14509 }
14510
14511 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
14512 self.buffer.update(cx, |buffer, cx| {
14513 buffer.set_all_diff_hunks_expanded(cx);
14514 });
14515 }
14516
14517 pub fn expand_all_diff_hunks(
14518 &mut self,
14519 _: &ExpandAllDiffHunks,
14520 _window: &mut Window,
14521 cx: &mut Context<Self>,
14522 ) {
14523 self.buffer.update(cx, |buffer, cx| {
14524 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
14525 });
14526 }
14527
14528 pub fn toggle_selected_diff_hunks(
14529 &mut self,
14530 _: &ToggleSelectedDiffHunks,
14531 _window: &mut Window,
14532 cx: &mut Context<Self>,
14533 ) {
14534 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14535 self.toggle_diff_hunks_in_ranges(ranges, cx);
14536 }
14537
14538 pub fn diff_hunks_in_ranges<'a>(
14539 &'a self,
14540 ranges: &'a [Range<Anchor>],
14541 buffer: &'a MultiBufferSnapshot,
14542 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
14543 ranges.iter().flat_map(move |range| {
14544 let end_excerpt_id = range.end.excerpt_id;
14545 let range = range.to_point(buffer);
14546 let mut peek_end = range.end;
14547 if range.end.row < buffer.max_row().0 {
14548 peek_end = Point::new(range.end.row + 1, 0);
14549 }
14550 buffer
14551 .diff_hunks_in_range(range.start..peek_end)
14552 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
14553 })
14554 }
14555
14556 pub fn has_stageable_diff_hunks_in_ranges(
14557 &self,
14558 ranges: &[Range<Anchor>],
14559 snapshot: &MultiBufferSnapshot,
14560 ) -> bool {
14561 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
14562 hunks.any(|hunk| hunk.status().has_secondary_hunk())
14563 }
14564
14565 pub fn toggle_staged_selected_diff_hunks(
14566 &mut self,
14567 _: &::git::ToggleStaged,
14568 _: &mut Window,
14569 cx: &mut Context<Self>,
14570 ) {
14571 let snapshot = self.buffer.read(cx).snapshot(cx);
14572 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14573 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
14574 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14575 }
14576
14577 pub fn stage_and_next(
14578 &mut self,
14579 _: &::git::StageAndNext,
14580 window: &mut Window,
14581 cx: &mut Context<Self>,
14582 ) {
14583 self.do_stage_or_unstage_and_next(true, window, cx);
14584 }
14585
14586 pub fn unstage_and_next(
14587 &mut self,
14588 _: &::git::UnstageAndNext,
14589 window: &mut Window,
14590 cx: &mut Context<Self>,
14591 ) {
14592 self.do_stage_or_unstage_and_next(false, window, cx);
14593 }
14594
14595 pub fn stage_or_unstage_diff_hunks(
14596 &mut self,
14597 stage: bool,
14598 ranges: Vec<Range<Anchor>>,
14599 cx: &mut Context<Self>,
14600 ) {
14601 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
14602 cx.spawn(async move |this, cx| {
14603 task.await?;
14604 this.update(cx, |this, cx| {
14605 let snapshot = this.buffer.read(cx).snapshot(cx);
14606 let chunk_by = this
14607 .diff_hunks_in_ranges(&ranges, &snapshot)
14608 .chunk_by(|hunk| hunk.buffer_id);
14609 for (buffer_id, hunks) in &chunk_by {
14610 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
14611 }
14612 })
14613 })
14614 .detach_and_log_err(cx);
14615 }
14616
14617 fn save_buffers_for_ranges_if_needed(
14618 &mut self,
14619 ranges: &[Range<Anchor>],
14620 cx: &mut Context<'_, Editor>,
14621 ) -> Task<Result<()>> {
14622 let multibuffer = self.buffer.read(cx);
14623 let snapshot = multibuffer.read(cx);
14624 let buffer_ids: HashSet<_> = ranges
14625 .iter()
14626 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
14627 .collect();
14628 drop(snapshot);
14629
14630 let mut buffers = HashSet::default();
14631 for buffer_id in buffer_ids {
14632 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
14633 let buffer = buffer_entity.read(cx);
14634 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
14635 {
14636 buffers.insert(buffer_entity);
14637 }
14638 }
14639 }
14640
14641 if let Some(project) = &self.project {
14642 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
14643 } else {
14644 Task::ready(Ok(()))
14645 }
14646 }
14647
14648 fn do_stage_or_unstage_and_next(
14649 &mut self,
14650 stage: bool,
14651 window: &mut Window,
14652 cx: &mut Context<Self>,
14653 ) {
14654 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
14655
14656 if ranges.iter().any(|range| range.start != range.end) {
14657 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14658 return;
14659 }
14660
14661 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
14662 let snapshot = self.snapshot(window, cx);
14663 let position = self.selections.newest::<Point>(cx).head();
14664 let mut row = snapshot
14665 .buffer_snapshot
14666 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
14667 .find(|hunk| hunk.row_range.start.0 > position.row)
14668 .map(|hunk| hunk.row_range.start);
14669
14670 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
14671 // Outside of the project diff editor, wrap around to the beginning.
14672 if !all_diff_hunks_expanded {
14673 row = row.or_else(|| {
14674 snapshot
14675 .buffer_snapshot
14676 .diff_hunks_in_range(Point::zero()..position)
14677 .find(|hunk| hunk.row_range.end.0 < position.row)
14678 .map(|hunk| hunk.row_range.start)
14679 });
14680 }
14681
14682 if let Some(row) = row {
14683 let destination = Point::new(row.0, 0);
14684 let autoscroll = Autoscroll::center();
14685
14686 self.unfold_ranges(&[destination..destination], false, false, cx);
14687 self.change_selections(Some(autoscroll), window, cx, |s| {
14688 s.select_ranges([destination..destination]);
14689 });
14690 }
14691 }
14692
14693 fn do_stage_or_unstage(
14694 &self,
14695 stage: bool,
14696 buffer_id: BufferId,
14697 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14698 cx: &mut App,
14699 ) -> Option<()> {
14700 let project = self.project.as_ref()?;
14701 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14702 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14703 let buffer_snapshot = buffer.read(cx).snapshot();
14704 let file_exists = buffer_snapshot
14705 .file()
14706 .is_some_and(|file| file.disk_state().exists());
14707 diff.update(cx, |diff, cx| {
14708 diff.stage_or_unstage_hunks(
14709 stage,
14710 &hunks
14711 .map(|hunk| buffer_diff::DiffHunk {
14712 buffer_range: hunk.buffer_range,
14713 diff_base_byte_range: hunk.diff_base_byte_range,
14714 secondary_status: hunk.secondary_status,
14715 range: Point::zero()..Point::zero(), // unused
14716 })
14717 .collect::<Vec<_>>(),
14718 &buffer_snapshot,
14719 file_exists,
14720 cx,
14721 )
14722 });
14723 None
14724 }
14725
14726 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14727 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14728 self.buffer
14729 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14730 }
14731
14732 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14733 self.buffer.update(cx, |buffer, cx| {
14734 let ranges = vec![Anchor::min()..Anchor::max()];
14735 if !buffer.all_diff_hunks_expanded()
14736 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14737 {
14738 buffer.collapse_diff_hunks(ranges, cx);
14739 true
14740 } else {
14741 false
14742 }
14743 })
14744 }
14745
14746 fn toggle_diff_hunks_in_ranges(
14747 &mut self,
14748 ranges: Vec<Range<Anchor>>,
14749 cx: &mut Context<'_, Editor>,
14750 ) {
14751 self.buffer.update(cx, |buffer, cx| {
14752 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14753 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14754 })
14755 }
14756
14757 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14758 self.buffer.update(cx, |buffer, cx| {
14759 let snapshot = buffer.snapshot(cx);
14760 let excerpt_id = range.end.excerpt_id;
14761 let point_range = range.to_point(&snapshot);
14762 let expand = !buffer.single_hunk_is_expanded(range, cx);
14763 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14764 })
14765 }
14766
14767 pub(crate) fn apply_all_diff_hunks(
14768 &mut self,
14769 _: &ApplyAllDiffHunks,
14770 window: &mut Window,
14771 cx: &mut Context<Self>,
14772 ) {
14773 let buffers = self.buffer.read(cx).all_buffers();
14774 for branch_buffer in buffers {
14775 branch_buffer.update(cx, |branch_buffer, cx| {
14776 branch_buffer.merge_into_base(Vec::new(), cx);
14777 });
14778 }
14779
14780 if let Some(project) = self.project.clone() {
14781 self.save(true, project, window, cx).detach_and_log_err(cx);
14782 }
14783 }
14784
14785 pub(crate) fn apply_selected_diff_hunks(
14786 &mut self,
14787 _: &ApplyDiffHunk,
14788 window: &mut Window,
14789 cx: &mut Context<Self>,
14790 ) {
14791 let snapshot = self.snapshot(window, cx);
14792 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14793 let mut ranges_by_buffer = HashMap::default();
14794 self.transact(window, cx, |editor, _window, cx| {
14795 for hunk in hunks {
14796 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14797 ranges_by_buffer
14798 .entry(buffer.clone())
14799 .or_insert_with(Vec::new)
14800 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14801 }
14802 }
14803
14804 for (buffer, ranges) in ranges_by_buffer {
14805 buffer.update(cx, |buffer, cx| {
14806 buffer.merge_into_base(ranges, cx);
14807 });
14808 }
14809 });
14810
14811 if let Some(project) = self.project.clone() {
14812 self.save(true, project, window, cx).detach_and_log_err(cx);
14813 }
14814 }
14815
14816 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14817 if hovered != self.gutter_hovered {
14818 self.gutter_hovered = hovered;
14819 cx.notify();
14820 }
14821 }
14822
14823 pub fn insert_blocks(
14824 &mut self,
14825 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14826 autoscroll: Option<Autoscroll>,
14827 cx: &mut Context<Self>,
14828 ) -> Vec<CustomBlockId> {
14829 let blocks = self
14830 .display_map
14831 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14832 if let Some(autoscroll) = autoscroll {
14833 self.request_autoscroll(autoscroll, cx);
14834 }
14835 cx.notify();
14836 blocks
14837 }
14838
14839 pub fn resize_blocks(
14840 &mut self,
14841 heights: HashMap<CustomBlockId, u32>,
14842 autoscroll: Option<Autoscroll>,
14843 cx: &mut Context<Self>,
14844 ) {
14845 self.display_map
14846 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14847 if let Some(autoscroll) = autoscroll {
14848 self.request_autoscroll(autoscroll, cx);
14849 }
14850 cx.notify();
14851 }
14852
14853 pub fn replace_blocks(
14854 &mut self,
14855 renderers: HashMap<CustomBlockId, RenderBlock>,
14856 autoscroll: Option<Autoscroll>,
14857 cx: &mut Context<Self>,
14858 ) {
14859 self.display_map
14860 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14861 if let Some(autoscroll) = autoscroll {
14862 self.request_autoscroll(autoscroll, cx);
14863 }
14864 cx.notify();
14865 }
14866
14867 pub fn remove_blocks(
14868 &mut self,
14869 block_ids: HashSet<CustomBlockId>,
14870 autoscroll: Option<Autoscroll>,
14871 cx: &mut Context<Self>,
14872 ) {
14873 self.display_map.update(cx, |display_map, cx| {
14874 display_map.remove_blocks(block_ids, cx)
14875 });
14876 if let Some(autoscroll) = autoscroll {
14877 self.request_autoscroll(autoscroll, cx);
14878 }
14879 cx.notify();
14880 }
14881
14882 pub fn row_for_block(
14883 &self,
14884 block_id: CustomBlockId,
14885 cx: &mut Context<Self>,
14886 ) -> Option<DisplayRow> {
14887 self.display_map
14888 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14889 }
14890
14891 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14892 self.focused_block = Some(focused_block);
14893 }
14894
14895 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14896 self.focused_block.take()
14897 }
14898
14899 pub fn insert_creases(
14900 &mut self,
14901 creases: impl IntoIterator<Item = Crease<Anchor>>,
14902 cx: &mut Context<Self>,
14903 ) -> Vec<CreaseId> {
14904 self.display_map
14905 .update(cx, |map, cx| map.insert_creases(creases, cx))
14906 }
14907
14908 pub fn remove_creases(
14909 &mut self,
14910 ids: impl IntoIterator<Item = CreaseId>,
14911 cx: &mut Context<Self>,
14912 ) {
14913 self.display_map
14914 .update(cx, |map, cx| map.remove_creases(ids, cx));
14915 }
14916
14917 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14918 self.display_map
14919 .update(cx, |map, cx| map.snapshot(cx))
14920 .longest_row()
14921 }
14922
14923 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14924 self.display_map
14925 .update(cx, |map, cx| map.snapshot(cx))
14926 .max_point()
14927 }
14928
14929 pub fn text(&self, cx: &App) -> String {
14930 self.buffer.read(cx).read(cx).text()
14931 }
14932
14933 pub fn is_empty(&self, cx: &App) -> bool {
14934 self.buffer.read(cx).read(cx).is_empty()
14935 }
14936
14937 pub fn text_option(&self, cx: &App) -> Option<String> {
14938 let text = self.text(cx);
14939 let text = text.trim();
14940
14941 if text.is_empty() {
14942 return None;
14943 }
14944
14945 Some(text.to_string())
14946 }
14947
14948 pub fn set_text(
14949 &mut self,
14950 text: impl Into<Arc<str>>,
14951 window: &mut Window,
14952 cx: &mut Context<Self>,
14953 ) {
14954 self.transact(window, cx, |this, _, cx| {
14955 this.buffer
14956 .read(cx)
14957 .as_singleton()
14958 .expect("you can only call set_text on editors for singleton buffers")
14959 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14960 });
14961 }
14962
14963 pub fn display_text(&self, cx: &mut App) -> String {
14964 self.display_map
14965 .update(cx, |map, cx| map.snapshot(cx))
14966 .text()
14967 }
14968
14969 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14970 let mut wrap_guides = smallvec::smallvec![];
14971
14972 if self.show_wrap_guides == Some(false) {
14973 return wrap_guides;
14974 }
14975
14976 let settings = self.buffer.read(cx).language_settings(cx);
14977 if settings.show_wrap_guides {
14978 match self.soft_wrap_mode(cx) {
14979 SoftWrap::Column(soft_wrap) => {
14980 wrap_guides.push((soft_wrap as usize, true));
14981 }
14982 SoftWrap::Bounded(soft_wrap) => {
14983 wrap_guides.push((soft_wrap as usize, true));
14984 }
14985 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14986 }
14987 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14988 }
14989
14990 wrap_guides
14991 }
14992
14993 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14994 let settings = self.buffer.read(cx).language_settings(cx);
14995 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14996 match mode {
14997 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14998 SoftWrap::None
14999 }
15000 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
15001 language_settings::SoftWrap::PreferredLineLength => {
15002 SoftWrap::Column(settings.preferred_line_length)
15003 }
15004 language_settings::SoftWrap::Bounded => {
15005 SoftWrap::Bounded(settings.preferred_line_length)
15006 }
15007 }
15008 }
15009
15010 pub fn set_soft_wrap_mode(
15011 &mut self,
15012 mode: language_settings::SoftWrap,
15013
15014 cx: &mut Context<Self>,
15015 ) {
15016 self.soft_wrap_mode_override = Some(mode);
15017 cx.notify();
15018 }
15019
15020 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
15021 self.hard_wrap = hard_wrap;
15022 cx.notify();
15023 }
15024
15025 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
15026 self.text_style_refinement = Some(style);
15027 }
15028
15029 /// called by the Element so we know what style we were most recently rendered with.
15030 pub(crate) fn set_style(
15031 &mut self,
15032 style: EditorStyle,
15033 window: &mut Window,
15034 cx: &mut Context<Self>,
15035 ) {
15036 let rem_size = window.rem_size();
15037 self.display_map.update(cx, |map, cx| {
15038 map.set_font(
15039 style.text.font(),
15040 style.text.font_size.to_pixels(rem_size),
15041 cx,
15042 )
15043 });
15044 self.style = Some(style);
15045 }
15046
15047 pub fn style(&self) -> Option<&EditorStyle> {
15048 self.style.as_ref()
15049 }
15050
15051 // Called by the element. This method is not designed to be called outside of the editor
15052 // element's layout code because it does not notify when rewrapping is computed synchronously.
15053 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
15054 self.display_map
15055 .update(cx, |map, cx| map.set_wrap_width(width, cx))
15056 }
15057
15058 pub fn set_soft_wrap(&mut self) {
15059 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
15060 }
15061
15062 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
15063 if self.soft_wrap_mode_override.is_some() {
15064 self.soft_wrap_mode_override.take();
15065 } else {
15066 let soft_wrap = match self.soft_wrap_mode(cx) {
15067 SoftWrap::GitDiff => return,
15068 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
15069 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
15070 language_settings::SoftWrap::None
15071 }
15072 };
15073 self.soft_wrap_mode_override = Some(soft_wrap);
15074 }
15075 cx.notify();
15076 }
15077
15078 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
15079 let Some(workspace) = self.workspace() else {
15080 return;
15081 };
15082 let fs = workspace.read(cx).app_state().fs.clone();
15083 let current_show = TabBarSettings::get_global(cx).show;
15084 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
15085 setting.show = Some(!current_show);
15086 });
15087 }
15088
15089 pub fn toggle_indent_guides(
15090 &mut self,
15091 _: &ToggleIndentGuides,
15092 _: &mut Window,
15093 cx: &mut Context<Self>,
15094 ) {
15095 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
15096 self.buffer
15097 .read(cx)
15098 .language_settings(cx)
15099 .indent_guides
15100 .enabled
15101 });
15102 self.show_indent_guides = Some(!currently_enabled);
15103 cx.notify();
15104 }
15105
15106 fn should_show_indent_guides(&self) -> Option<bool> {
15107 self.show_indent_guides
15108 }
15109
15110 pub fn toggle_line_numbers(
15111 &mut self,
15112 _: &ToggleLineNumbers,
15113 _: &mut Window,
15114 cx: &mut Context<Self>,
15115 ) {
15116 let mut editor_settings = EditorSettings::get_global(cx).clone();
15117 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
15118 EditorSettings::override_global(editor_settings, cx);
15119 }
15120
15121 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
15122 if let Some(show_line_numbers) = self.show_line_numbers {
15123 return show_line_numbers;
15124 }
15125 EditorSettings::get_global(cx).gutter.line_numbers
15126 }
15127
15128 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
15129 self.use_relative_line_numbers
15130 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
15131 }
15132
15133 pub fn toggle_relative_line_numbers(
15134 &mut self,
15135 _: &ToggleRelativeLineNumbers,
15136 _: &mut Window,
15137 cx: &mut Context<Self>,
15138 ) {
15139 let is_relative = self.should_use_relative_line_numbers(cx);
15140 self.set_relative_line_number(Some(!is_relative), cx)
15141 }
15142
15143 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
15144 self.use_relative_line_numbers = is_relative;
15145 cx.notify();
15146 }
15147
15148 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
15149 self.show_gutter = show_gutter;
15150 cx.notify();
15151 }
15152
15153 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
15154 self.show_scrollbars = show_scrollbars;
15155 cx.notify();
15156 }
15157
15158 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
15159 self.show_line_numbers = Some(show_line_numbers);
15160 cx.notify();
15161 }
15162
15163 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
15164 self.show_git_diff_gutter = Some(show_git_diff_gutter);
15165 cx.notify();
15166 }
15167
15168 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
15169 self.show_code_actions = Some(show_code_actions);
15170 cx.notify();
15171 }
15172
15173 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
15174 self.show_runnables = Some(show_runnables);
15175 cx.notify();
15176 }
15177
15178 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
15179 self.show_breakpoints = Some(show_breakpoints);
15180 cx.notify();
15181 }
15182
15183 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
15184 if self.display_map.read(cx).masked != masked {
15185 self.display_map.update(cx, |map, _| map.masked = masked);
15186 }
15187 cx.notify()
15188 }
15189
15190 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
15191 self.show_wrap_guides = Some(show_wrap_guides);
15192 cx.notify();
15193 }
15194
15195 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
15196 self.show_indent_guides = Some(show_indent_guides);
15197 cx.notify();
15198 }
15199
15200 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
15201 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
15202 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
15203 if let Some(dir) = file.abs_path(cx).parent() {
15204 return Some(dir.to_owned());
15205 }
15206 }
15207
15208 if let Some(project_path) = buffer.read(cx).project_path(cx) {
15209 return Some(project_path.path.to_path_buf());
15210 }
15211 }
15212
15213 None
15214 }
15215
15216 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
15217 self.active_excerpt(cx)?
15218 .1
15219 .read(cx)
15220 .file()
15221 .and_then(|f| f.as_local())
15222 }
15223
15224 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15225 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15226 let buffer = buffer.read(cx);
15227 if let Some(project_path) = buffer.project_path(cx) {
15228 let project = self.project.as_ref()?.read(cx);
15229 project.absolute_path(&project_path, cx)
15230 } else {
15231 buffer
15232 .file()
15233 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
15234 }
15235 })
15236 }
15237
15238 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
15239 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
15240 let project_path = buffer.read(cx).project_path(cx)?;
15241 let project = self.project.as_ref()?.read(cx);
15242 let entry = project.entry_for_path(&project_path, cx)?;
15243 let path = entry.path.to_path_buf();
15244 Some(path)
15245 })
15246 }
15247
15248 pub fn reveal_in_finder(
15249 &mut self,
15250 _: &RevealInFileManager,
15251 _window: &mut Window,
15252 cx: &mut Context<Self>,
15253 ) {
15254 if let Some(target) = self.target_file(cx) {
15255 cx.reveal_path(&target.abs_path(cx));
15256 }
15257 }
15258
15259 pub fn copy_path(
15260 &mut self,
15261 _: &zed_actions::workspace::CopyPath,
15262 _window: &mut Window,
15263 cx: &mut Context<Self>,
15264 ) {
15265 if let Some(path) = self.target_file_abs_path(cx) {
15266 if let Some(path) = path.to_str() {
15267 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15268 }
15269 }
15270 }
15271
15272 pub fn copy_relative_path(
15273 &mut self,
15274 _: &zed_actions::workspace::CopyRelativePath,
15275 _window: &mut Window,
15276 cx: &mut Context<Self>,
15277 ) {
15278 if let Some(path) = self.target_file_path(cx) {
15279 if let Some(path) = path.to_str() {
15280 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
15281 }
15282 }
15283 }
15284
15285 pub fn project_path(&self, cx: &mut Context<Self>) -> Option<ProjectPath> {
15286 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
15287 buffer.read_with(cx, |buffer, cx| buffer.project_path(cx))
15288 } else {
15289 None
15290 }
15291 }
15292
15293 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15294 let _ = maybe!({
15295 let breakpoint_store = self.breakpoint_store.as_ref()?;
15296
15297 let Some((_, _, active_position)) =
15298 breakpoint_store.read(cx).active_position().cloned()
15299 else {
15300 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15301 return None;
15302 };
15303
15304 let snapshot = self
15305 .project
15306 .as_ref()?
15307 .read(cx)
15308 .buffer_for_id(active_position.buffer_id?, cx)?
15309 .read(cx)
15310 .snapshot();
15311
15312 for (id, ExcerptRange { context, .. }) in self
15313 .buffer
15314 .read(cx)
15315 .excerpts_for_buffer(active_position.buffer_id?, cx)
15316 {
15317 if context.start.cmp(&active_position, &snapshot).is_ge()
15318 || context.end.cmp(&active_position, &snapshot).is_lt()
15319 {
15320 continue;
15321 }
15322 let snapshot = self.buffer.read(cx).snapshot(cx);
15323 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, active_position)?;
15324
15325 self.clear_row_highlights::<DebugCurrentRowHighlight>();
15326 self.go_to_line::<DebugCurrentRowHighlight>(
15327 multibuffer_anchor,
15328 Some(cx.theme().colors().editor_debugger_active_line_background),
15329 window,
15330 cx,
15331 );
15332
15333 cx.notify();
15334 }
15335
15336 Some(())
15337 });
15338 }
15339
15340 pub fn copy_file_name_without_extension(
15341 &mut self,
15342 _: &CopyFileNameWithoutExtension,
15343 _: &mut Window,
15344 cx: &mut Context<Self>,
15345 ) {
15346 if let Some(file) = self.target_file(cx) {
15347 if let Some(file_stem) = file.path().file_stem() {
15348 if let Some(name) = file_stem.to_str() {
15349 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15350 }
15351 }
15352 }
15353 }
15354
15355 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
15356 if let Some(file) = self.target_file(cx) {
15357 if let Some(file_name) = file.path().file_name() {
15358 if let Some(name) = file_name.to_str() {
15359 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
15360 }
15361 }
15362 }
15363 }
15364
15365 pub fn toggle_git_blame(
15366 &mut self,
15367 _: &::git::Blame,
15368 window: &mut Window,
15369 cx: &mut Context<Self>,
15370 ) {
15371 self.show_git_blame_gutter = !self.show_git_blame_gutter;
15372
15373 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
15374 self.start_git_blame(true, window, cx);
15375 }
15376
15377 cx.notify();
15378 }
15379
15380 pub fn toggle_git_blame_inline(
15381 &mut self,
15382 _: &ToggleGitBlameInline,
15383 window: &mut Window,
15384 cx: &mut Context<Self>,
15385 ) {
15386 self.toggle_git_blame_inline_internal(true, window, cx);
15387 cx.notify();
15388 }
15389
15390 pub fn git_blame_inline_enabled(&self) -> bool {
15391 self.git_blame_inline_enabled
15392 }
15393
15394 pub fn toggle_selection_menu(
15395 &mut self,
15396 _: &ToggleSelectionMenu,
15397 _: &mut Window,
15398 cx: &mut Context<Self>,
15399 ) {
15400 self.show_selection_menu = self
15401 .show_selection_menu
15402 .map(|show_selections_menu| !show_selections_menu)
15403 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
15404
15405 cx.notify();
15406 }
15407
15408 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
15409 self.show_selection_menu
15410 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
15411 }
15412
15413 fn start_git_blame(
15414 &mut self,
15415 user_triggered: bool,
15416 window: &mut Window,
15417 cx: &mut Context<Self>,
15418 ) {
15419 if let Some(project) = self.project.as_ref() {
15420 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
15421 return;
15422 };
15423
15424 if buffer.read(cx).file().is_none() {
15425 return;
15426 }
15427
15428 let focused = self.focus_handle(cx).contains_focused(window, cx);
15429
15430 let project = project.clone();
15431 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
15432 self.blame_subscription =
15433 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
15434 self.blame = Some(blame);
15435 }
15436 }
15437
15438 fn toggle_git_blame_inline_internal(
15439 &mut self,
15440 user_triggered: bool,
15441 window: &mut Window,
15442 cx: &mut Context<Self>,
15443 ) {
15444 if self.git_blame_inline_enabled {
15445 self.git_blame_inline_enabled = false;
15446 self.show_git_blame_inline = false;
15447 self.show_git_blame_inline_delay_task.take();
15448 } else {
15449 self.git_blame_inline_enabled = true;
15450 self.start_git_blame_inline(user_triggered, window, cx);
15451 }
15452
15453 cx.notify();
15454 }
15455
15456 fn start_git_blame_inline(
15457 &mut self,
15458 user_triggered: bool,
15459 window: &mut Window,
15460 cx: &mut Context<Self>,
15461 ) {
15462 self.start_git_blame(user_triggered, window, cx);
15463
15464 if ProjectSettings::get_global(cx)
15465 .git
15466 .inline_blame_delay()
15467 .is_some()
15468 {
15469 self.start_inline_blame_timer(window, cx);
15470 } else {
15471 self.show_git_blame_inline = true
15472 }
15473 }
15474
15475 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
15476 self.blame.as_ref()
15477 }
15478
15479 pub fn show_git_blame_gutter(&self) -> bool {
15480 self.show_git_blame_gutter
15481 }
15482
15483 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
15484 self.show_git_blame_gutter && self.has_blame_entries(cx)
15485 }
15486
15487 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
15488 self.show_git_blame_inline
15489 && (self.focus_handle.is_focused(window)
15490 || self
15491 .git_blame_inline_tooltip
15492 .as_ref()
15493 .and_then(|t| t.upgrade())
15494 .is_some())
15495 && !self.newest_selection_head_on_empty_line(cx)
15496 && self.has_blame_entries(cx)
15497 }
15498
15499 fn has_blame_entries(&self, cx: &App) -> bool {
15500 self.blame()
15501 .map_or(false, |blame| blame.read(cx).has_generated_entries())
15502 }
15503
15504 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
15505 let cursor_anchor = self.selections.newest_anchor().head();
15506
15507 let snapshot = self.buffer.read(cx).snapshot(cx);
15508 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
15509
15510 snapshot.line_len(buffer_row) == 0
15511 }
15512
15513 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
15514 let buffer_and_selection = maybe!({
15515 let selection = self.selections.newest::<Point>(cx);
15516 let selection_range = selection.range();
15517
15518 let multi_buffer = self.buffer().read(cx);
15519 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15520 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
15521
15522 let (buffer, range, _) = if selection.reversed {
15523 buffer_ranges.first()
15524 } else {
15525 buffer_ranges.last()
15526 }?;
15527
15528 let selection = text::ToPoint::to_point(&range.start, &buffer).row
15529 ..text::ToPoint::to_point(&range.end, &buffer).row;
15530 Some((
15531 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
15532 selection,
15533 ))
15534 });
15535
15536 let Some((buffer, selection)) = buffer_and_selection else {
15537 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
15538 };
15539
15540 let Some(project) = self.project.as_ref() else {
15541 return Task::ready(Err(anyhow!("editor does not have project")));
15542 };
15543
15544 project.update(cx, |project, cx| {
15545 project.get_permalink_to_line(&buffer, selection, cx)
15546 })
15547 }
15548
15549 pub fn copy_permalink_to_line(
15550 &mut self,
15551 _: &CopyPermalinkToLine,
15552 window: &mut Window,
15553 cx: &mut Context<Self>,
15554 ) {
15555 let permalink_task = self.get_permalink_to_line(cx);
15556 let workspace = self.workspace();
15557
15558 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15559 Ok(permalink) => {
15560 cx.update(|_, cx| {
15561 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
15562 })
15563 .ok();
15564 }
15565 Err(err) => {
15566 let message = format!("Failed to copy permalink: {err}");
15567
15568 Err::<(), anyhow::Error>(err).log_err();
15569
15570 if let Some(workspace) = workspace {
15571 workspace
15572 .update_in(cx, |workspace, _, cx| {
15573 struct CopyPermalinkToLine;
15574
15575 workspace.show_toast(
15576 Toast::new(
15577 NotificationId::unique::<CopyPermalinkToLine>(),
15578 message,
15579 ),
15580 cx,
15581 )
15582 })
15583 .ok();
15584 }
15585 }
15586 })
15587 .detach();
15588 }
15589
15590 pub fn copy_file_location(
15591 &mut self,
15592 _: &CopyFileLocation,
15593 _: &mut Window,
15594 cx: &mut Context<Self>,
15595 ) {
15596 let selection = self.selections.newest::<Point>(cx).start.row + 1;
15597 if let Some(file) = self.target_file(cx) {
15598 if let Some(path) = file.path().to_str() {
15599 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
15600 }
15601 }
15602 }
15603
15604 pub fn open_permalink_to_line(
15605 &mut self,
15606 _: &OpenPermalinkToLine,
15607 window: &mut Window,
15608 cx: &mut Context<Self>,
15609 ) {
15610 let permalink_task = self.get_permalink_to_line(cx);
15611 let workspace = self.workspace();
15612
15613 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
15614 Ok(permalink) => {
15615 cx.update(|_, cx| {
15616 cx.open_url(permalink.as_ref());
15617 })
15618 .ok();
15619 }
15620 Err(err) => {
15621 let message = format!("Failed to open permalink: {err}");
15622
15623 Err::<(), anyhow::Error>(err).log_err();
15624
15625 if let Some(workspace) = workspace {
15626 workspace
15627 .update(cx, |workspace, cx| {
15628 struct OpenPermalinkToLine;
15629
15630 workspace.show_toast(
15631 Toast::new(
15632 NotificationId::unique::<OpenPermalinkToLine>(),
15633 message,
15634 ),
15635 cx,
15636 )
15637 })
15638 .ok();
15639 }
15640 }
15641 })
15642 .detach();
15643 }
15644
15645 pub fn insert_uuid_v4(
15646 &mut self,
15647 _: &InsertUuidV4,
15648 window: &mut Window,
15649 cx: &mut Context<Self>,
15650 ) {
15651 self.insert_uuid(UuidVersion::V4, window, cx);
15652 }
15653
15654 pub fn insert_uuid_v7(
15655 &mut self,
15656 _: &InsertUuidV7,
15657 window: &mut Window,
15658 cx: &mut Context<Self>,
15659 ) {
15660 self.insert_uuid(UuidVersion::V7, window, cx);
15661 }
15662
15663 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
15664 self.transact(window, cx, |this, window, cx| {
15665 let edits = this
15666 .selections
15667 .all::<Point>(cx)
15668 .into_iter()
15669 .map(|selection| {
15670 let uuid = match version {
15671 UuidVersion::V4 => uuid::Uuid::new_v4(),
15672 UuidVersion::V7 => uuid::Uuid::now_v7(),
15673 };
15674
15675 (selection.range(), uuid.to_string())
15676 });
15677 this.edit(edits, cx);
15678 this.refresh_inline_completion(true, false, window, cx);
15679 });
15680 }
15681
15682 pub fn open_selections_in_multibuffer(
15683 &mut self,
15684 _: &OpenSelectionsInMultibuffer,
15685 window: &mut Window,
15686 cx: &mut Context<Self>,
15687 ) {
15688 let multibuffer = self.buffer.read(cx);
15689
15690 let Some(buffer) = multibuffer.as_singleton() else {
15691 return;
15692 };
15693
15694 let Some(workspace) = self.workspace() else {
15695 return;
15696 };
15697
15698 let locations = self
15699 .selections
15700 .disjoint_anchors()
15701 .iter()
15702 .map(|range| Location {
15703 buffer: buffer.clone(),
15704 range: range.start.text_anchor..range.end.text_anchor,
15705 })
15706 .collect::<Vec<_>>();
15707
15708 let title = multibuffer.title(cx).to_string();
15709
15710 cx.spawn_in(window, async move |_, cx| {
15711 workspace.update_in(cx, |workspace, window, cx| {
15712 Self::open_locations_in_multibuffer(
15713 workspace,
15714 locations,
15715 format!("Selections for '{title}'"),
15716 false,
15717 MultibufferSelectionMode::All,
15718 window,
15719 cx,
15720 );
15721 })
15722 })
15723 .detach();
15724 }
15725
15726 /// Adds a row highlight for the given range. If a row has multiple highlights, the
15727 /// last highlight added will be used.
15728 ///
15729 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15730 pub fn highlight_rows<T: 'static>(
15731 &mut self,
15732 range: Range<Anchor>,
15733 color: Hsla,
15734 should_autoscroll: bool,
15735 cx: &mut Context<Self>,
15736 ) {
15737 let snapshot = self.buffer().read(cx).snapshot(cx);
15738 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15739 let ix = row_highlights.binary_search_by(|highlight| {
15740 Ordering::Equal
15741 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15742 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15743 });
15744
15745 if let Err(mut ix) = ix {
15746 let index = post_inc(&mut self.highlight_order);
15747
15748 // If this range intersects with the preceding highlight, then merge it with
15749 // the preceding highlight. Otherwise insert a new highlight.
15750 let mut merged = false;
15751 if ix > 0 {
15752 let prev_highlight = &mut row_highlights[ix - 1];
15753 if prev_highlight
15754 .range
15755 .end
15756 .cmp(&range.start, &snapshot)
15757 .is_ge()
15758 {
15759 ix -= 1;
15760 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15761 prev_highlight.range.end = range.end;
15762 }
15763 merged = true;
15764 prev_highlight.index = index;
15765 prev_highlight.color = color;
15766 prev_highlight.should_autoscroll = should_autoscroll;
15767 }
15768 }
15769
15770 if !merged {
15771 row_highlights.insert(
15772 ix,
15773 RowHighlight {
15774 range: range.clone(),
15775 index,
15776 color,
15777 should_autoscroll,
15778 },
15779 );
15780 }
15781
15782 // If any of the following highlights intersect with this one, merge them.
15783 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15784 let highlight = &row_highlights[ix];
15785 if next_highlight
15786 .range
15787 .start
15788 .cmp(&highlight.range.end, &snapshot)
15789 .is_le()
15790 {
15791 if next_highlight
15792 .range
15793 .end
15794 .cmp(&highlight.range.end, &snapshot)
15795 .is_gt()
15796 {
15797 row_highlights[ix].range.end = next_highlight.range.end;
15798 }
15799 row_highlights.remove(ix + 1);
15800 } else {
15801 break;
15802 }
15803 }
15804 }
15805 }
15806
15807 /// Remove any highlighted row ranges of the given type that intersect the
15808 /// given ranges.
15809 pub fn remove_highlighted_rows<T: 'static>(
15810 &mut self,
15811 ranges_to_remove: Vec<Range<Anchor>>,
15812 cx: &mut Context<Self>,
15813 ) {
15814 let snapshot = self.buffer().read(cx).snapshot(cx);
15815 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15816 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15817 row_highlights.retain(|highlight| {
15818 while let Some(range_to_remove) = ranges_to_remove.peek() {
15819 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15820 Ordering::Less | Ordering::Equal => {
15821 ranges_to_remove.next();
15822 }
15823 Ordering::Greater => {
15824 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15825 Ordering::Less | Ordering::Equal => {
15826 return false;
15827 }
15828 Ordering::Greater => break,
15829 }
15830 }
15831 }
15832 }
15833
15834 true
15835 })
15836 }
15837
15838 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15839 pub fn clear_row_highlights<T: 'static>(&mut self) {
15840 self.highlighted_rows.remove(&TypeId::of::<T>());
15841 }
15842
15843 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15844 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15845 self.highlighted_rows
15846 .get(&TypeId::of::<T>())
15847 .map_or(&[] as &[_], |vec| vec.as_slice())
15848 .iter()
15849 .map(|highlight| (highlight.range.clone(), highlight.color))
15850 }
15851
15852 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15853 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15854 /// Allows to ignore certain kinds of highlights.
15855 pub fn highlighted_display_rows(
15856 &self,
15857 window: &mut Window,
15858 cx: &mut App,
15859 ) -> BTreeMap<DisplayRow, LineHighlight> {
15860 let snapshot = self.snapshot(window, cx);
15861 let mut used_highlight_orders = HashMap::default();
15862 self.highlighted_rows
15863 .iter()
15864 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15865 .fold(
15866 BTreeMap::<DisplayRow, LineHighlight>::new(),
15867 |mut unique_rows, highlight| {
15868 let start = highlight.range.start.to_display_point(&snapshot);
15869 let end = highlight.range.end.to_display_point(&snapshot);
15870 let start_row = start.row().0;
15871 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15872 && end.column() == 0
15873 {
15874 end.row().0.saturating_sub(1)
15875 } else {
15876 end.row().0
15877 };
15878 for row in start_row..=end_row {
15879 let used_index =
15880 used_highlight_orders.entry(row).or_insert(highlight.index);
15881 if highlight.index >= *used_index {
15882 *used_index = highlight.index;
15883 unique_rows.insert(DisplayRow(row), highlight.color.into());
15884 }
15885 }
15886 unique_rows
15887 },
15888 )
15889 }
15890
15891 pub fn highlighted_display_row_for_autoscroll(
15892 &self,
15893 snapshot: &DisplaySnapshot,
15894 ) -> Option<DisplayRow> {
15895 self.highlighted_rows
15896 .values()
15897 .flat_map(|highlighted_rows| highlighted_rows.iter())
15898 .filter_map(|highlight| {
15899 if highlight.should_autoscroll {
15900 Some(highlight.range.start.to_display_point(snapshot).row())
15901 } else {
15902 None
15903 }
15904 })
15905 .min()
15906 }
15907
15908 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
15909 self.highlight_background::<SearchWithinRange>(
15910 ranges,
15911 |colors| colors.editor_document_highlight_read_background,
15912 cx,
15913 )
15914 }
15915
15916 pub fn set_breadcrumb_header(&mut self, new_header: String) {
15917 self.breadcrumb_header = Some(new_header);
15918 }
15919
15920 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
15921 self.clear_background_highlights::<SearchWithinRange>(cx);
15922 }
15923
15924 pub fn highlight_background<T: 'static>(
15925 &mut self,
15926 ranges: &[Range<Anchor>],
15927 color_fetcher: fn(&ThemeColors) -> Hsla,
15928 cx: &mut Context<Self>,
15929 ) {
15930 self.background_highlights
15931 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15932 self.scrollbar_marker_state.dirty = true;
15933 cx.notify();
15934 }
15935
15936 pub fn clear_background_highlights<T: 'static>(
15937 &mut self,
15938 cx: &mut Context<Self>,
15939 ) -> Option<BackgroundHighlight> {
15940 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15941 if !text_highlights.1.is_empty() {
15942 self.scrollbar_marker_state.dirty = true;
15943 cx.notify();
15944 }
15945 Some(text_highlights)
15946 }
15947
15948 pub fn highlight_gutter<T: 'static>(
15949 &mut self,
15950 ranges: &[Range<Anchor>],
15951 color_fetcher: fn(&App) -> Hsla,
15952 cx: &mut Context<Self>,
15953 ) {
15954 self.gutter_highlights
15955 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15956 cx.notify();
15957 }
15958
15959 pub fn clear_gutter_highlights<T: 'static>(
15960 &mut self,
15961 cx: &mut Context<Self>,
15962 ) -> Option<GutterHighlight> {
15963 cx.notify();
15964 self.gutter_highlights.remove(&TypeId::of::<T>())
15965 }
15966
15967 #[cfg(feature = "test-support")]
15968 pub fn all_text_background_highlights(
15969 &self,
15970 window: &mut Window,
15971 cx: &mut Context<Self>,
15972 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15973 let snapshot = self.snapshot(window, cx);
15974 let buffer = &snapshot.buffer_snapshot;
15975 let start = buffer.anchor_before(0);
15976 let end = buffer.anchor_after(buffer.len());
15977 let theme = cx.theme().colors();
15978 self.background_highlights_in_range(start..end, &snapshot, theme)
15979 }
15980
15981 #[cfg(feature = "test-support")]
15982 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15983 let snapshot = self.buffer().read(cx).snapshot(cx);
15984
15985 let highlights = self
15986 .background_highlights
15987 .get(&TypeId::of::<items::BufferSearchHighlights>());
15988
15989 if let Some((_color, ranges)) = highlights {
15990 ranges
15991 .iter()
15992 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15993 .collect_vec()
15994 } else {
15995 vec![]
15996 }
15997 }
15998
15999 fn document_highlights_for_position<'a>(
16000 &'a self,
16001 position: Anchor,
16002 buffer: &'a MultiBufferSnapshot,
16003 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
16004 let read_highlights = self
16005 .background_highlights
16006 .get(&TypeId::of::<DocumentHighlightRead>())
16007 .map(|h| &h.1);
16008 let write_highlights = self
16009 .background_highlights
16010 .get(&TypeId::of::<DocumentHighlightWrite>())
16011 .map(|h| &h.1);
16012 let left_position = position.bias_left(buffer);
16013 let right_position = position.bias_right(buffer);
16014 read_highlights
16015 .into_iter()
16016 .chain(write_highlights)
16017 .flat_map(move |ranges| {
16018 let start_ix = match ranges.binary_search_by(|probe| {
16019 let cmp = probe.end.cmp(&left_position, buffer);
16020 if cmp.is_ge() {
16021 Ordering::Greater
16022 } else {
16023 Ordering::Less
16024 }
16025 }) {
16026 Ok(i) | Err(i) => i,
16027 };
16028
16029 ranges[start_ix..]
16030 .iter()
16031 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
16032 })
16033 }
16034
16035 pub fn has_background_highlights<T: 'static>(&self) -> bool {
16036 self.background_highlights
16037 .get(&TypeId::of::<T>())
16038 .map_or(false, |(_, highlights)| !highlights.is_empty())
16039 }
16040
16041 pub fn background_highlights_in_range(
16042 &self,
16043 search_range: Range<Anchor>,
16044 display_snapshot: &DisplaySnapshot,
16045 theme: &ThemeColors,
16046 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16047 let mut results = Vec::new();
16048 for (color_fetcher, ranges) in self.background_highlights.values() {
16049 let color = color_fetcher(theme);
16050 let start_ix = match ranges.binary_search_by(|probe| {
16051 let cmp = probe
16052 .end
16053 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16054 if cmp.is_gt() {
16055 Ordering::Greater
16056 } else {
16057 Ordering::Less
16058 }
16059 }) {
16060 Ok(i) | Err(i) => i,
16061 };
16062 for range in &ranges[start_ix..] {
16063 if range
16064 .start
16065 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16066 .is_ge()
16067 {
16068 break;
16069 }
16070
16071 let start = range.start.to_display_point(display_snapshot);
16072 let end = range.end.to_display_point(display_snapshot);
16073 results.push((start..end, color))
16074 }
16075 }
16076 results
16077 }
16078
16079 pub fn background_highlight_row_ranges<T: 'static>(
16080 &self,
16081 search_range: Range<Anchor>,
16082 display_snapshot: &DisplaySnapshot,
16083 count: usize,
16084 ) -> Vec<RangeInclusive<DisplayPoint>> {
16085 let mut results = Vec::new();
16086 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
16087 return vec![];
16088 };
16089
16090 let start_ix = match ranges.binary_search_by(|probe| {
16091 let cmp = probe
16092 .end
16093 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16094 if cmp.is_gt() {
16095 Ordering::Greater
16096 } else {
16097 Ordering::Less
16098 }
16099 }) {
16100 Ok(i) | Err(i) => i,
16101 };
16102 let mut push_region = |start: Option<Point>, end: Option<Point>| {
16103 if let (Some(start_display), Some(end_display)) = (start, end) {
16104 results.push(
16105 start_display.to_display_point(display_snapshot)
16106 ..=end_display.to_display_point(display_snapshot),
16107 );
16108 }
16109 };
16110 let mut start_row: Option<Point> = None;
16111 let mut end_row: Option<Point> = None;
16112 if ranges.len() > count {
16113 return Vec::new();
16114 }
16115 for range in &ranges[start_ix..] {
16116 if range
16117 .start
16118 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16119 .is_ge()
16120 {
16121 break;
16122 }
16123 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
16124 if let Some(current_row) = &end_row {
16125 if end.row == current_row.row {
16126 continue;
16127 }
16128 }
16129 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
16130 if start_row.is_none() {
16131 assert_eq!(end_row, None);
16132 start_row = Some(start);
16133 end_row = Some(end);
16134 continue;
16135 }
16136 if let Some(current_end) = end_row.as_mut() {
16137 if start.row > current_end.row + 1 {
16138 push_region(start_row, end_row);
16139 start_row = Some(start);
16140 end_row = Some(end);
16141 } else {
16142 // Merge two hunks.
16143 *current_end = end;
16144 }
16145 } else {
16146 unreachable!();
16147 }
16148 }
16149 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
16150 push_region(start_row, end_row);
16151 results
16152 }
16153
16154 pub fn gutter_highlights_in_range(
16155 &self,
16156 search_range: Range<Anchor>,
16157 display_snapshot: &DisplaySnapshot,
16158 cx: &App,
16159 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
16160 let mut results = Vec::new();
16161 for (color_fetcher, ranges) in self.gutter_highlights.values() {
16162 let color = color_fetcher(cx);
16163 let start_ix = match ranges.binary_search_by(|probe| {
16164 let cmp = probe
16165 .end
16166 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
16167 if cmp.is_gt() {
16168 Ordering::Greater
16169 } else {
16170 Ordering::Less
16171 }
16172 }) {
16173 Ok(i) | Err(i) => i,
16174 };
16175 for range in &ranges[start_ix..] {
16176 if range
16177 .start
16178 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
16179 .is_ge()
16180 {
16181 break;
16182 }
16183
16184 let start = range.start.to_display_point(display_snapshot);
16185 let end = range.end.to_display_point(display_snapshot);
16186 results.push((start..end, color))
16187 }
16188 }
16189 results
16190 }
16191
16192 /// Get the text ranges corresponding to the redaction query
16193 pub fn redacted_ranges(
16194 &self,
16195 search_range: Range<Anchor>,
16196 display_snapshot: &DisplaySnapshot,
16197 cx: &App,
16198 ) -> Vec<Range<DisplayPoint>> {
16199 display_snapshot
16200 .buffer_snapshot
16201 .redacted_ranges(search_range, |file| {
16202 if let Some(file) = file {
16203 file.is_private()
16204 && EditorSettings::get(
16205 Some(SettingsLocation {
16206 worktree_id: file.worktree_id(cx),
16207 path: file.path().as_ref(),
16208 }),
16209 cx,
16210 )
16211 .redact_private_values
16212 } else {
16213 false
16214 }
16215 })
16216 .map(|range| {
16217 range.start.to_display_point(display_snapshot)
16218 ..range.end.to_display_point(display_snapshot)
16219 })
16220 .collect()
16221 }
16222
16223 pub fn highlight_text<T: 'static>(
16224 &mut self,
16225 ranges: Vec<Range<Anchor>>,
16226 style: HighlightStyle,
16227 cx: &mut Context<Self>,
16228 ) {
16229 self.display_map.update(cx, |map, _| {
16230 map.highlight_text(TypeId::of::<T>(), ranges, style)
16231 });
16232 cx.notify();
16233 }
16234
16235 pub(crate) fn highlight_inlays<T: 'static>(
16236 &mut self,
16237 highlights: Vec<InlayHighlight>,
16238 style: HighlightStyle,
16239 cx: &mut Context<Self>,
16240 ) {
16241 self.display_map.update(cx, |map, _| {
16242 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
16243 });
16244 cx.notify();
16245 }
16246
16247 pub fn text_highlights<'a, T: 'static>(
16248 &'a self,
16249 cx: &'a App,
16250 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
16251 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
16252 }
16253
16254 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
16255 let cleared = self
16256 .display_map
16257 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
16258 if cleared {
16259 cx.notify();
16260 }
16261 }
16262
16263 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
16264 (self.read_only(cx) || self.blink_manager.read(cx).visible())
16265 && self.focus_handle.is_focused(window)
16266 }
16267
16268 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
16269 self.show_cursor_when_unfocused = is_enabled;
16270 cx.notify();
16271 }
16272
16273 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
16274 cx.notify();
16275 }
16276
16277 fn on_buffer_event(
16278 &mut self,
16279 multibuffer: &Entity<MultiBuffer>,
16280 event: &multi_buffer::Event,
16281 window: &mut Window,
16282 cx: &mut Context<Self>,
16283 ) {
16284 match event {
16285 multi_buffer::Event::Edited {
16286 singleton_buffer_edited,
16287 edited_buffer: buffer_edited,
16288 } => {
16289 self.scrollbar_marker_state.dirty = true;
16290 self.active_indent_guides_state.dirty = true;
16291 self.refresh_active_diagnostics(cx);
16292 self.refresh_code_actions(window, cx);
16293 if self.has_active_inline_completion() {
16294 self.update_visible_inline_completion(window, cx);
16295 }
16296 if let Some(buffer) = buffer_edited {
16297 let buffer_id = buffer.read(cx).remote_id();
16298 if !self.registered_buffers.contains_key(&buffer_id) {
16299 if let Some(project) = self.project.as_ref() {
16300 project.update(cx, |project, cx| {
16301 self.registered_buffers.insert(
16302 buffer_id,
16303 project.register_buffer_with_language_servers(&buffer, cx),
16304 );
16305 })
16306 }
16307 }
16308 }
16309 cx.emit(EditorEvent::BufferEdited);
16310 cx.emit(SearchEvent::MatchesInvalidated);
16311 if *singleton_buffer_edited {
16312 if let Some(project) = &self.project {
16313 #[allow(clippy::mutable_key_type)]
16314 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
16315 multibuffer
16316 .all_buffers()
16317 .into_iter()
16318 .filter_map(|buffer| {
16319 buffer.update(cx, |buffer, cx| {
16320 let language = buffer.language()?;
16321 let should_discard = project.update(cx, |project, cx| {
16322 project.is_local()
16323 && !project.has_language_servers_for(buffer, cx)
16324 });
16325 should_discard.not().then_some(language.clone())
16326 })
16327 })
16328 .collect::<HashSet<_>>()
16329 });
16330 if !languages_affected.is_empty() {
16331 self.refresh_inlay_hints(
16332 InlayHintRefreshReason::BufferEdited(languages_affected),
16333 cx,
16334 );
16335 }
16336 }
16337 }
16338
16339 let Some(project) = &self.project else { return };
16340 let (telemetry, is_via_ssh) = {
16341 let project = project.read(cx);
16342 let telemetry = project.client().telemetry().clone();
16343 let is_via_ssh = project.is_via_ssh();
16344 (telemetry, is_via_ssh)
16345 };
16346 refresh_linked_ranges(self, window, cx);
16347 telemetry.log_edit_event("editor", is_via_ssh);
16348 }
16349 multi_buffer::Event::ExcerptsAdded {
16350 buffer,
16351 predecessor,
16352 excerpts,
16353 } => {
16354 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16355 let buffer_id = buffer.read(cx).remote_id();
16356 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
16357 if let Some(project) = &self.project {
16358 get_uncommitted_diff_for_buffer(
16359 project,
16360 [buffer.clone()],
16361 self.buffer.clone(),
16362 cx,
16363 )
16364 .detach();
16365 }
16366 }
16367 cx.emit(EditorEvent::ExcerptsAdded {
16368 buffer: buffer.clone(),
16369 predecessor: *predecessor,
16370 excerpts: excerpts.clone(),
16371 });
16372 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16373 }
16374 multi_buffer::Event::ExcerptsRemoved { ids } => {
16375 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
16376 let buffer = self.buffer.read(cx);
16377 self.registered_buffers
16378 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
16379 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16380 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
16381 }
16382 multi_buffer::Event::ExcerptsEdited {
16383 excerpt_ids,
16384 buffer_ids,
16385 } => {
16386 self.display_map.update(cx, |map, cx| {
16387 map.unfold_buffers(buffer_ids.iter().copied(), cx)
16388 });
16389 cx.emit(EditorEvent::ExcerptsEdited {
16390 ids: excerpt_ids.clone(),
16391 })
16392 }
16393 multi_buffer::Event::ExcerptsExpanded { ids } => {
16394 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
16395 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
16396 }
16397 multi_buffer::Event::Reparsed(buffer_id) => {
16398 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16399 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16400
16401 cx.emit(EditorEvent::Reparsed(*buffer_id));
16402 }
16403 multi_buffer::Event::DiffHunksToggled => {
16404 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16405 }
16406 multi_buffer::Event::LanguageChanged(buffer_id) => {
16407 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
16408 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
16409 cx.emit(EditorEvent::Reparsed(*buffer_id));
16410 cx.notify();
16411 }
16412 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
16413 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
16414 multi_buffer::Event::FileHandleChanged
16415 | multi_buffer::Event::Reloaded
16416 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
16417 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
16418 multi_buffer::Event::DiagnosticsUpdated => {
16419 self.refresh_active_diagnostics(cx);
16420 self.refresh_inline_diagnostics(true, window, cx);
16421 self.scrollbar_marker_state.dirty = true;
16422 cx.notify();
16423 }
16424 _ => {}
16425 };
16426 }
16427
16428 fn on_display_map_changed(
16429 &mut self,
16430 _: Entity<DisplayMap>,
16431 _: &mut Window,
16432 cx: &mut Context<Self>,
16433 ) {
16434 cx.notify();
16435 }
16436
16437 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16438 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
16439 self.update_edit_prediction_settings(cx);
16440 self.refresh_inline_completion(true, false, window, cx);
16441 self.refresh_inlay_hints(
16442 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
16443 self.selections.newest_anchor().head(),
16444 &self.buffer.read(cx).snapshot(cx),
16445 cx,
16446 )),
16447 cx,
16448 );
16449
16450 let old_cursor_shape = self.cursor_shape;
16451
16452 {
16453 let editor_settings = EditorSettings::get_global(cx);
16454 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
16455 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
16456 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
16457 }
16458
16459 if old_cursor_shape != self.cursor_shape {
16460 cx.emit(EditorEvent::CursorShapeChanged);
16461 }
16462
16463 let project_settings = ProjectSettings::get_global(cx);
16464 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
16465
16466 if self.mode == EditorMode::Full {
16467 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
16468 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
16469 if self.show_inline_diagnostics != show_inline_diagnostics {
16470 self.show_inline_diagnostics = show_inline_diagnostics;
16471 self.refresh_inline_diagnostics(false, window, cx);
16472 }
16473
16474 if self.git_blame_inline_enabled != inline_blame_enabled {
16475 self.toggle_git_blame_inline_internal(false, window, cx);
16476 }
16477 }
16478
16479 cx.notify();
16480 }
16481
16482 pub fn set_searchable(&mut self, searchable: bool) {
16483 self.searchable = searchable;
16484 }
16485
16486 pub fn searchable(&self) -> bool {
16487 self.searchable
16488 }
16489
16490 fn open_proposed_changes_editor(
16491 &mut self,
16492 _: &OpenProposedChangesEditor,
16493 window: &mut Window,
16494 cx: &mut Context<Self>,
16495 ) {
16496 let Some(workspace) = self.workspace() else {
16497 cx.propagate();
16498 return;
16499 };
16500
16501 let selections = self.selections.all::<usize>(cx);
16502 let multi_buffer = self.buffer.read(cx);
16503 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
16504 let mut new_selections_by_buffer = HashMap::default();
16505 for selection in selections {
16506 for (buffer, range, _) in
16507 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
16508 {
16509 let mut range = range.to_point(buffer);
16510 range.start.column = 0;
16511 range.end.column = buffer.line_len(range.end.row);
16512 new_selections_by_buffer
16513 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
16514 .or_insert(Vec::new())
16515 .push(range)
16516 }
16517 }
16518
16519 let proposed_changes_buffers = new_selections_by_buffer
16520 .into_iter()
16521 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
16522 .collect::<Vec<_>>();
16523 let proposed_changes_editor = cx.new(|cx| {
16524 ProposedChangesEditor::new(
16525 "Proposed changes",
16526 proposed_changes_buffers,
16527 self.project.clone(),
16528 window,
16529 cx,
16530 )
16531 });
16532
16533 window.defer(cx, move |window, cx| {
16534 workspace.update(cx, |workspace, cx| {
16535 workspace.active_pane().update(cx, |pane, cx| {
16536 pane.add_item(
16537 Box::new(proposed_changes_editor),
16538 true,
16539 true,
16540 None,
16541 window,
16542 cx,
16543 );
16544 });
16545 });
16546 });
16547 }
16548
16549 pub fn open_excerpts_in_split(
16550 &mut self,
16551 _: &OpenExcerptsSplit,
16552 window: &mut Window,
16553 cx: &mut Context<Self>,
16554 ) {
16555 self.open_excerpts_common(None, true, window, cx)
16556 }
16557
16558 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
16559 self.open_excerpts_common(None, false, window, cx)
16560 }
16561
16562 fn open_excerpts_common(
16563 &mut self,
16564 jump_data: Option<JumpData>,
16565 split: bool,
16566 window: &mut Window,
16567 cx: &mut Context<Self>,
16568 ) {
16569 let Some(workspace) = self.workspace() else {
16570 cx.propagate();
16571 return;
16572 };
16573
16574 if self.buffer.read(cx).is_singleton() {
16575 cx.propagate();
16576 return;
16577 }
16578
16579 let mut new_selections_by_buffer = HashMap::default();
16580 match &jump_data {
16581 Some(JumpData::MultiBufferPoint {
16582 excerpt_id,
16583 position,
16584 anchor,
16585 line_offset_from_top,
16586 }) => {
16587 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16588 if let Some(buffer) = multi_buffer_snapshot
16589 .buffer_id_for_excerpt(*excerpt_id)
16590 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
16591 {
16592 let buffer_snapshot = buffer.read(cx).snapshot();
16593 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
16594 language::ToPoint::to_point(anchor, &buffer_snapshot)
16595 } else {
16596 buffer_snapshot.clip_point(*position, Bias::Left)
16597 };
16598 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
16599 new_selections_by_buffer.insert(
16600 buffer,
16601 (
16602 vec![jump_to_offset..jump_to_offset],
16603 Some(*line_offset_from_top),
16604 ),
16605 );
16606 }
16607 }
16608 Some(JumpData::MultiBufferRow {
16609 row,
16610 line_offset_from_top,
16611 }) => {
16612 let point = MultiBufferPoint::new(row.0, 0);
16613 if let Some((buffer, buffer_point, _)) =
16614 self.buffer.read(cx).point_to_buffer_point(point, cx)
16615 {
16616 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
16617 new_selections_by_buffer
16618 .entry(buffer)
16619 .or_insert((Vec::new(), Some(*line_offset_from_top)))
16620 .0
16621 .push(buffer_offset..buffer_offset)
16622 }
16623 }
16624 None => {
16625 let selections = self.selections.all::<usize>(cx);
16626 let multi_buffer = self.buffer.read(cx);
16627 for selection in selections {
16628 for (snapshot, range, _, anchor) in multi_buffer
16629 .snapshot(cx)
16630 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
16631 {
16632 if let Some(anchor) = anchor {
16633 // selection is in a deleted hunk
16634 let Some(buffer_id) = anchor.buffer_id else {
16635 continue;
16636 };
16637 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
16638 continue;
16639 };
16640 let offset = text::ToOffset::to_offset(
16641 &anchor.text_anchor,
16642 &buffer_handle.read(cx).snapshot(),
16643 );
16644 let range = offset..offset;
16645 new_selections_by_buffer
16646 .entry(buffer_handle)
16647 .or_insert((Vec::new(), None))
16648 .0
16649 .push(range)
16650 } else {
16651 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
16652 else {
16653 continue;
16654 };
16655 new_selections_by_buffer
16656 .entry(buffer_handle)
16657 .or_insert((Vec::new(), None))
16658 .0
16659 .push(range)
16660 }
16661 }
16662 }
16663 }
16664 }
16665
16666 if new_selections_by_buffer.is_empty() {
16667 return;
16668 }
16669
16670 // We defer the pane interaction because we ourselves are a workspace item
16671 // and activating a new item causes the pane to call a method on us reentrantly,
16672 // which panics if we're on the stack.
16673 window.defer(cx, move |window, cx| {
16674 workspace.update(cx, |workspace, cx| {
16675 let pane = if split {
16676 workspace.adjacent_pane(window, cx)
16677 } else {
16678 workspace.active_pane().clone()
16679 };
16680
16681 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
16682 let editor = buffer
16683 .read(cx)
16684 .file()
16685 .is_none()
16686 .then(|| {
16687 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
16688 // so `workspace.open_project_item` will never find them, always opening a new editor.
16689 // Instead, we try to activate the existing editor in the pane first.
16690 let (editor, pane_item_index) =
16691 pane.read(cx).items().enumerate().find_map(|(i, item)| {
16692 let editor = item.downcast::<Editor>()?;
16693 let singleton_buffer =
16694 editor.read(cx).buffer().read(cx).as_singleton()?;
16695 if singleton_buffer == buffer {
16696 Some((editor, i))
16697 } else {
16698 None
16699 }
16700 })?;
16701 pane.update(cx, |pane, cx| {
16702 pane.activate_item(pane_item_index, true, true, window, cx)
16703 });
16704 Some(editor)
16705 })
16706 .flatten()
16707 .unwrap_or_else(|| {
16708 workspace.open_project_item::<Self>(
16709 pane.clone(),
16710 buffer,
16711 true,
16712 true,
16713 window,
16714 cx,
16715 )
16716 });
16717
16718 editor.update(cx, |editor, cx| {
16719 let autoscroll = match scroll_offset {
16720 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
16721 None => Autoscroll::newest(),
16722 };
16723 let nav_history = editor.nav_history.take();
16724 editor.change_selections(Some(autoscroll), window, cx, |s| {
16725 s.select_ranges(ranges);
16726 });
16727 editor.nav_history = nav_history;
16728 });
16729 }
16730 })
16731 });
16732 }
16733
16734 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16735 let snapshot = self.buffer.read(cx).read(cx);
16736 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16737 Some(
16738 ranges
16739 .iter()
16740 .map(move |range| {
16741 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16742 })
16743 .collect(),
16744 )
16745 }
16746
16747 fn selection_replacement_ranges(
16748 &self,
16749 range: Range<OffsetUtf16>,
16750 cx: &mut App,
16751 ) -> Vec<Range<OffsetUtf16>> {
16752 let selections = self.selections.all::<OffsetUtf16>(cx);
16753 let newest_selection = selections
16754 .iter()
16755 .max_by_key(|selection| selection.id)
16756 .unwrap();
16757 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16758 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16759 let snapshot = self.buffer.read(cx).read(cx);
16760 selections
16761 .into_iter()
16762 .map(|mut selection| {
16763 selection.start.0 =
16764 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16765 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16766 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16767 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16768 })
16769 .collect()
16770 }
16771
16772 fn report_editor_event(
16773 &self,
16774 event_type: &'static str,
16775 file_extension: Option<String>,
16776 cx: &App,
16777 ) {
16778 if cfg!(any(test, feature = "test-support")) {
16779 return;
16780 }
16781
16782 let Some(project) = &self.project else { return };
16783
16784 // If None, we are in a file without an extension
16785 let file = self
16786 .buffer
16787 .read(cx)
16788 .as_singleton()
16789 .and_then(|b| b.read(cx).file());
16790 let file_extension = file_extension.or(file
16791 .as_ref()
16792 .and_then(|file| Path::new(file.file_name(cx)).extension())
16793 .and_then(|e| e.to_str())
16794 .map(|a| a.to_string()));
16795
16796 let vim_mode = cx
16797 .global::<SettingsStore>()
16798 .raw_user_settings()
16799 .get("vim_mode")
16800 == Some(&serde_json::Value::Bool(true));
16801
16802 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16803 let copilot_enabled = edit_predictions_provider
16804 == language::language_settings::EditPredictionProvider::Copilot;
16805 let copilot_enabled_for_language = self
16806 .buffer
16807 .read(cx)
16808 .language_settings(cx)
16809 .show_edit_predictions;
16810
16811 let project = project.read(cx);
16812 telemetry::event!(
16813 event_type,
16814 file_extension,
16815 vim_mode,
16816 copilot_enabled,
16817 copilot_enabled_for_language,
16818 edit_predictions_provider,
16819 is_via_ssh = project.is_via_ssh(),
16820 );
16821 }
16822
16823 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16824 /// with each line being an array of {text, highlight} objects.
16825 fn copy_highlight_json(
16826 &mut self,
16827 _: &CopyHighlightJson,
16828 window: &mut Window,
16829 cx: &mut Context<Self>,
16830 ) {
16831 #[derive(Serialize)]
16832 struct Chunk<'a> {
16833 text: String,
16834 highlight: Option<&'a str>,
16835 }
16836
16837 let snapshot = self.buffer.read(cx).snapshot(cx);
16838 let range = self
16839 .selected_text_range(false, window, cx)
16840 .and_then(|selection| {
16841 if selection.range.is_empty() {
16842 None
16843 } else {
16844 Some(selection.range)
16845 }
16846 })
16847 .unwrap_or_else(|| 0..snapshot.len());
16848
16849 let chunks = snapshot.chunks(range, true);
16850 let mut lines = Vec::new();
16851 let mut line: VecDeque<Chunk> = VecDeque::new();
16852
16853 let Some(style) = self.style.as_ref() else {
16854 return;
16855 };
16856
16857 for chunk in chunks {
16858 let highlight = chunk
16859 .syntax_highlight_id
16860 .and_then(|id| id.name(&style.syntax));
16861 let mut chunk_lines = chunk.text.split('\n').peekable();
16862 while let Some(text) = chunk_lines.next() {
16863 let mut merged_with_last_token = false;
16864 if let Some(last_token) = line.back_mut() {
16865 if last_token.highlight == highlight {
16866 last_token.text.push_str(text);
16867 merged_with_last_token = true;
16868 }
16869 }
16870
16871 if !merged_with_last_token {
16872 line.push_back(Chunk {
16873 text: text.into(),
16874 highlight,
16875 });
16876 }
16877
16878 if chunk_lines.peek().is_some() {
16879 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16880 line.pop_front();
16881 }
16882 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16883 line.pop_back();
16884 }
16885
16886 lines.push(mem::take(&mut line));
16887 }
16888 }
16889 }
16890
16891 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
16892 return;
16893 };
16894 cx.write_to_clipboard(ClipboardItem::new_string(lines));
16895 }
16896
16897 pub fn open_context_menu(
16898 &mut self,
16899 _: &OpenContextMenu,
16900 window: &mut Window,
16901 cx: &mut Context<Self>,
16902 ) {
16903 self.request_autoscroll(Autoscroll::newest(), cx);
16904 let position = self.selections.newest_display(cx).start;
16905 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
16906 }
16907
16908 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
16909 &self.inlay_hint_cache
16910 }
16911
16912 pub fn replay_insert_event(
16913 &mut self,
16914 text: &str,
16915 relative_utf16_range: Option<Range<isize>>,
16916 window: &mut Window,
16917 cx: &mut Context<Self>,
16918 ) {
16919 if !self.input_enabled {
16920 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16921 return;
16922 }
16923 if let Some(relative_utf16_range) = relative_utf16_range {
16924 let selections = self.selections.all::<OffsetUtf16>(cx);
16925 self.change_selections(None, window, cx, |s| {
16926 let new_ranges = selections.into_iter().map(|range| {
16927 let start = OffsetUtf16(
16928 range
16929 .head()
16930 .0
16931 .saturating_add_signed(relative_utf16_range.start),
16932 );
16933 let end = OffsetUtf16(
16934 range
16935 .head()
16936 .0
16937 .saturating_add_signed(relative_utf16_range.end),
16938 );
16939 start..end
16940 });
16941 s.select_ranges(new_ranges);
16942 });
16943 }
16944
16945 self.handle_input(text, window, cx);
16946 }
16947
16948 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16949 let Some(provider) = self.semantics_provider.as_ref() else {
16950 return false;
16951 };
16952
16953 let mut supports = false;
16954 self.buffer().update(cx, |this, cx| {
16955 this.for_each_buffer(|buffer| {
16956 supports |= provider.supports_inlay_hints(buffer, cx);
16957 });
16958 });
16959
16960 supports
16961 }
16962
16963 pub fn is_focused(&self, window: &Window) -> bool {
16964 self.focus_handle.is_focused(window)
16965 }
16966
16967 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16968 cx.emit(EditorEvent::Focused);
16969
16970 if let Some(descendant) = self
16971 .last_focused_descendant
16972 .take()
16973 .and_then(|descendant| descendant.upgrade())
16974 {
16975 window.focus(&descendant);
16976 } else {
16977 if let Some(blame) = self.blame.as_ref() {
16978 blame.update(cx, GitBlame::focus)
16979 }
16980
16981 self.blink_manager.update(cx, BlinkManager::enable);
16982 self.show_cursor_names(window, cx);
16983 self.buffer.update(cx, |buffer, cx| {
16984 buffer.finalize_last_transaction(cx);
16985 if self.leader_peer_id.is_none() {
16986 buffer.set_active_selections(
16987 &self.selections.disjoint_anchors(),
16988 self.selections.line_mode,
16989 self.cursor_shape,
16990 cx,
16991 );
16992 }
16993 });
16994 }
16995 }
16996
16997 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16998 cx.emit(EditorEvent::FocusedIn)
16999 }
17000
17001 fn handle_focus_out(
17002 &mut self,
17003 event: FocusOutEvent,
17004 _window: &mut Window,
17005 cx: &mut Context<Self>,
17006 ) {
17007 if event.blurred != self.focus_handle {
17008 self.last_focused_descendant = Some(event.blurred);
17009 }
17010 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
17011 }
17012
17013 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
17014 self.blink_manager.update(cx, BlinkManager::disable);
17015 self.buffer
17016 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
17017
17018 if let Some(blame) = self.blame.as_ref() {
17019 blame.update(cx, GitBlame::blur)
17020 }
17021 if !self.hover_state.focused(window, cx) {
17022 hide_hover(self, cx);
17023 }
17024 if !self
17025 .context_menu
17026 .borrow()
17027 .as_ref()
17028 .is_some_and(|context_menu| context_menu.focused(window, cx))
17029 {
17030 self.hide_context_menu(window, cx);
17031 }
17032 self.discard_inline_completion(false, cx);
17033 cx.emit(EditorEvent::Blurred);
17034 cx.notify();
17035 }
17036
17037 pub fn register_action<A: Action>(
17038 &mut self,
17039 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
17040 ) -> Subscription {
17041 let id = self.next_editor_action_id.post_inc();
17042 let listener = Arc::new(listener);
17043 self.editor_actions.borrow_mut().insert(
17044 id,
17045 Box::new(move |window, _| {
17046 let listener = listener.clone();
17047 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
17048 let action = action.downcast_ref().unwrap();
17049 if phase == DispatchPhase::Bubble {
17050 listener(action, window, cx)
17051 }
17052 })
17053 }),
17054 );
17055
17056 let editor_actions = self.editor_actions.clone();
17057 Subscription::new(move || {
17058 editor_actions.borrow_mut().remove(&id);
17059 })
17060 }
17061
17062 pub fn file_header_size(&self) -> u32 {
17063 FILE_HEADER_HEIGHT
17064 }
17065
17066 pub fn restore(
17067 &mut self,
17068 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
17069 window: &mut Window,
17070 cx: &mut Context<Self>,
17071 ) {
17072 let workspace = self.workspace();
17073 let project = self.project.as_ref();
17074 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
17075 let mut tasks = Vec::new();
17076 for (buffer_id, changes) in revert_changes {
17077 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
17078 buffer.update(cx, |buffer, cx| {
17079 buffer.edit(
17080 changes
17081 .into_iter()
17082 .map(|(range, text)| (range, text.to_string())),
17083 None,
17084 cx,
17085 );
17086 });
17087
17088 if let Some(project) =
17089 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
17090 {
17091 project.update(cx, |project, cx| {
17092 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
17093 })
17094 }
17095 }
17096 }
17097 tasks
17098 });
17099 cx.spawn_in(window, async move |_, cx| {
17100 for (buffer, task) in save_tasks {
17101 let result = task.await;
17102 if result.is_err() {
17103 let Some(path) = buffer
17104 .read_with(cx, |buffer, cx| buffer.project_path(cx))
17105 .ok()
17106 else {
17107 continue;
17108 };
17109 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
17110 let Some(task) = cx
17111 .update_window_entity(&workspace, |workspace, window, cx| {
17112 workspace
17113 .open_path_preview(path, None, false, false, false, window, cx)
17114 })
17115 .ok()
17116 else {
17117 continue;
17118 };
17119 task.await.log_err();
17120 }
17121 }
17122 }
17123 })
17124 .detach();
17125 self.change_selections(None, window, cx, |selections| selections.refresh());
17126 }
17127
17128 pub fn to_pixel_point(
17129 &self,
17130 source: multi_buffer::Anchor,
17131 editor_snapshot: &EditorSnapshot,
17132 window: &mut Window,
17133 ) -> Option<gpui::Point<Pixels>> {
17134 let source_point = source.to_display_point(editor_snapshot);
17135 self.display_to_pixel_point(source_point, editor_snapshot, window)
17136 }
17137
17138 pub fn display_to_pixel_point(
17139 &self,
17140 source: DisplayPoint,
17141 editor_snapshot: &EditorSnapshot,
17142 window: &mut Window,
17143 ) -> Option<gpui::Point<Pixels>> {
17144 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
17145 let text_layout_details = self.text_layout_details(window);
17146 let scroll_top = text_layout_details
17147 .scroll_anchor
17148 .scroll_position(editor_snapshot)
17149 .y;
17150
17151 if source.row().as_f32() < scroll_top.floor() {
17152 return None;
17153 }
17154 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
17155 let source_y = line_height * (source.row().as_f32() - scroll_top);
17156 Some(gpui::Point::new(source_x, source_y))
17157 }
17158
17159 pub fn has_visible_completions_menu(&self) -> bool {
17160 !self.edit_prediction_preview_is_active()
17161 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
17162 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
17163 })
17164 }
17165
17166 pub fn register_addon<T: Addon>(&mut self, instance: T) {
17167 self.addons
17168 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
17169 }
17170
17171 pub fn unregister_addon<T: Addon>(&mut self) {
17172 self.addons.remove(&std::any::TypeId::of::<T>());
17173 }
17174
17175 pub fn addon<T: Addon>(&self) -> Option<&T> {
17176 let type_id = std::any::TypeId::of::<T>();
17177 self.addons
17178 .get(&type_id)
17179 .and_then(|item| item.to_any().downcast_ref::<T>())
17180 }
17181
17182 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
17183 let text_layout_details = self.text_layout_details(window);
17184 let style = &text_layout_details.editor_style;
17185 let font_id = window.text_system().resolve_font(&style.text.font());
17186 let font_size = style.text.font_size.to_pixels(window.rem_size());
17187 let line_height = style.text.line_height_in_pixels(window.rem_size());
17188 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
17189
17190 gpui::Size::new(em_width, line_height)
17191 }
17192
17193 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
17194 self.load_diff_task.clone()
17195 }
17196
17197 fn read_metadata_from_db(
17198 &mut self,
17199 item_id: u64,
17200 workspace_id: WorkspaceId,
17201 window: &mut Window,
17202 cx: &mut Context<Editor>,
17203 ) {
17204 if self.is_singleton(cx)
17205 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
17206 {
17207 let buffer_snapshot = OnceCell::new();
17208
17209 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
17210 if !selections.is_empty() {
17211 let snapshot =
17212 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17213 self.change_selections(None, window, cx, |s| {
17214 s.select_ranges(selections.into_iter().map(|(start, end)| {
17215 snapshot.clip_offset(start, Bias::Left)
17216 ..snapshot.clip_offset(end, Bias::Right)
17217 }));
17218 });
17219 }
17220 };
17221
17222 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
17223 if !folds.is_empty() {
17224 let snapshot =
17225 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
17226 self.fold_ranges(
17227 folds
17228 .into_iter()
17229 .map(|(start, end)| {
17230 snapshot.clip_offset(start, Bias::Left)
17231 ..snapshot.clip_offset(end, Bias::Right)
17232 })
17233 .collect(),
17234 false,
17235 window,
17236 cx,
17237 );
17238 }
17239 }
17240 }
17241
17242 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
17243 }
17244}
17245
17246fn insert_extra_newline_brackets(
17247 buffer: &MultiBufferSnapshot,
17248 range: Range<usize>,
17249 language: &language::LanguageScope,
17250) -> bool {
17251 let leading_whitespace_len = buffer
17252 .reversed_chars_at(range.start)
17253 .take_while(|c| c.is_whitespace() && *c != '\n')
17254 .map(|c| c.len_utf8())
17255 .sum::<usize>();
17256 let trailing_whitespace_len = buffer
17257 .chars_at(range.end)
17258 .take_while(|c| c.is_whitespace() && *c != '\n')
17259 .map(|c| c.len_utf8())
17260 .sum::<usize>();
17261 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
17262
17263 language.brackets().any(|(pair, enabled)| {
17264 let pair_start = pair.start.trim_end();
17265 let pair_end = pair.end.trim_start();
17266
17267 enabled
17268 && pair.newline
17269 && buffer.contains_str_at(range.end, pair_end)
17270 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
17271 })
17272}
17273
17274fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
17275 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
17276 [(buffer, range, _)] => (*buffer, range.clone()),
17277 _ => return false,
17278 };
17279 let pair = {
17280 let mut result: Option<BracketMatch> = None;
17281
17282 for pair in buffer
17283 .all_bracket_ranges(range.clone())
17284 .filter(move |pair| {
17285 pair.open_range.start <= range.start && pair.close_range.end >= range.end
17286 })
17287 {
17288 let len = pair.close_range.end - pair.open_range.start;
17289
17290 if let Some(existing) = &result {
17291 let existing_len = existing.close_range.end - existing.open_range.start;
17292 if len > existing_len {
17293 continue;
17294 }
17295 }
17296
17297 result = Some(pair);
17298 }
17299
17300 result
17301 };
17302 let Some(pair) = pair else {
17303 return false;
17304 };
17305 pair.newline_only
17306 && buffer
17307 .chars_for_range(pair.open_range.end..range.start)
17308 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
17309 .all(|c| c.is_whitespace() && c != '\n')
17310}
17311
17312fn get_uncommitted_diff_for_buffer(
17313 project: &Entity<Project>,
17314 buffers: impl IntoIterator<Item = Entity<Buffer>>,
17315 buffer: Entity<MultiBuffer>,
17316 cx: &mut App,
17317) -> Task<()> {
17318 let mut tasks = Vec::new();
17319 project.update(cx, |project, cx| {
17320 for buffer in buffers {
17321 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
17322 }
17323 });
17324 cx.spawn(async move |cx| {
17325 let diffs = future::join_all(tasks).await;
17326 buffer
17327 .update(cx, |buffer, cx| {
17328 for diff in diffs.into_iter().flatten() {
17329 buffer.add_diff(diff, cx);
17330 }
17331 })
17332 .ok();
17333 })
17334}
17335
17336fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
17337 let tab_size = tab_size.get() as usize;
17338 let mut width = offset;
17339
17340 for ch in text.chars() {
17341 width += if ch == '\t' {
17342 tab_size - (width % tab_size)
17343 } else {
17344 1
17345 };
17346 }
17347
17348 width - offset
17349}
17350
17351#[cfg(test)]
17352mod tests {
17353 use super::*;
17354
17355 #[test]
17356 fn test_string_size_with_expanded_tabs() {
17357 let nz = |val| NonZeroU32::new(val).unwrap();
17358 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
17359 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
17360 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
17361 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
17362 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
17363 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
17364 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
17365 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
17366 }
17367}
17368
17369/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
17370struct WordBreakingTokenizer<'a> {
17371 input: &'a str,
17372}
17373
17374impl<'a> WordBreakingTokenizer<'a> {
17375 fn new(input: &'a str) -> Self {
17376 Self { input }
17377 }
17378}
17379
17380fn is_char_ideographic(ch: char) -> bool {
17381 use unicode_script::Script::*;
17382 use unicode_script::UnicodeScript;
17383 matches!(ch.script(), Han | Tangut | Yi)
17384}
17385
17386fn is_grapheme_ideographic(text: &str) -> bool {
17387 text.chars().any(is_char_ideographic)
17388}
17389
17390fn is_grapheme_whitespace(text: &str) -> bool {
17391 text.chars().any(|x| x.is_whitespace())
17392}
17393
17394fn should_stay_with_preceding_ideograph(text: &str) -> bool {
17395 text.chars().next().map_or(false, |ch| {
17396 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
17397 })
17398}
17399
17400#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17401enum WordBreakToken<'a> {
17402 Word { token: &'a str, grapheme_len: usize },
17403 InlineWhitespace { token: &'a str, grapheme_len: usize },
17404 Newline,
17405}
17406
17407impl<'a> Iterator for WordBreakingTokenizer<'a> {
17408 /// Yields a span, the count of graphemes in the token, and whether it was
17409 /// whitespace. Note that it also breaks at word boundaries.
17410 type Item = WordBreakToken<'a>;
17411
17412 fn next(&mut self) -> Option<Self::Item> {
17413 use unicode_segmentation::UnicodeSegmentation;
17414 if self.input.is_empty() {
17415 return None;
17416 }
17417
17418 let mut iter = self.input.graphemes(true).peekable();
17419 let mut offset = 0;
17420 let mut grapheme_len = 0;
17421 if let Some(first_grapheme) = iter.next() {
17422 let is_newline = first_grapheme == "\n";
17423 let is_whitespace = is_grapheme_whitespace(first_grapheme);
17424 offset += first_grapheme.len();
17425 grapheme_len += 1;
17426 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
17427 if let Some(grapheme) = iter.peek().copied() {
17428 if should_stay_with_preceding_ideograph(grapheme) {
17429 offset += grapheme.len();
17430 grapheme_len += 1;
17431 }
17432 }
17433 } else {
17434 let mut words = self.input[offset..].split_word_bound_indices().peekable();
17435 let mut next_word_bound = words.peek().copied();
17436 if next_word_bound.map_or(false, |(i, _)| i == 0) {
17437 next_word_bound = words.next();
17438 }
17439 while let Some(grapheme) = iter.peek().copied() {
17440 if next_word_bound.map_or(false, |(i, _)| i == offset) {
17441 break;
17442 };
17443 if is_grapheme_whitespace(grapheme) != is_whitespace
17444 || (grapheme == "\n") != is_newline
17445 {
17446 break;
17447 };
17448 offset += grapheme.len();
17449 grapheme_len += 1;
17450 iter.next();
17451 }
17452 }
17453 let token = &self.input[..offset];
17454 self.input = &self.input[offset..];
17455 if token == "\n" {
17456 Some(WordBreakToken::Newline)
17457 } else if is_whitespace {
17458 Some(WordBreakToken::InlineWhitespace {
17459 token,
17460 grapheme_len,
17461 })
17462 } else {
17463 Some(WordBreakToken::Word {
17464 token,
17465 grapheme_len,
17466 })
17467 }
17468 } else {
17469 None
17470 }
17471 }
17472}
17473
17474#[test]
17475fn test_word_breaking_tokenizer() {
17476 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
17477 ("", &[]),
17478 (" ", &[whitespace(" ", 2)]),
17479 ("Ʒ", &[word("Ʒ", 1)]),
17480 ("Ǽ", &[word("Ǽ", 1)]),
17481 ("⋑", &[word("⋑", 1)]),
17482 ("⋑⋑", &[word("⋑⋑", 2)]),
17483 (
17484 "原理,进而",
17485 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
17486 ),
17487 (
17488 "hello world",
17489 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
17490 ),
17491 (
17492 "hello, world",
17493 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
17494 ),
17495 (
17496 " hello world",
17497 &[
17498 whitespace(" ", 2),
17499 word("hello", 5),
17500 whitespace(" ", 1),
17501 word("world", 5),
17502 ],
17503 ),
17504 (
17505 "这是什么 \n 钢笔",
17506 &[
17507 word("这", 1),
17508 word("是", 1),
17509 word("什", 1),
17510 word("么", 1),
17511 whitespace(" ", 1),
17512 newline(),
17513 whitespace(" ", 1),
17514 word("钢", 1),
17515 word("笔", 1),
17516 ],
17517 ),
17518 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
17519 ];
17520
17521 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17522 WordBreakToken::Word {
17523 token,
17524 grapheme_len,
17525 }
17526 }
17527
17528 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
17529 WordBreakToken::InlineWhitespace {
17530 token,
17531 grapheme_len,
17532 }
17533 }
17534
17535 fn newline() -> WordBreakToken<'static> {
17536 WordBreakToken::Newline
17537 }
17538
17539 for (input, result) in tests {
17540 assert_eq!(
17541 WordBreakingTokenizer::new(input)
17542 .collect::<Vec<_>>()
17543 .as_slice(),
17544 *result,
17545 );
17546 }
17547}
17548
17549fn wrap_with_prefix(
17550 line_prefix: String,
17551 unwrapped_text: String,
17552 wrap_column: usize,
17553 tab_size: NonZeroU32,
17554 preserve_existing_whitespace: bool,
17555) -> String {
17556 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
17557 let mut wrapped_text = String::new();
17558 let mut current_line = line_prefix.clone();
17559
17560 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
17561 let mut current_line_len = line_prefix_len;
17562 let mut in_whitespace = false;
17563 for token in tokenizer {
17564 let have_preceding_whitespace = in_whitespace;
17565 match token {
17566 WordBreakToken::Word {
17567 token,
17568 grapheme_len,
17569 } => {
17570 in_whitespace = false;
17571 if current_line_len + grapheme_len > wrap_column
17572 && current_line_len != line_prefix_len
17573 {
17574 wrapped_text.push_str(current_line.trim_end());
17575 wrapped_text.push('\n');
17576 current_line.truncate(line_prefix.len());
17577 current_line_len = line_prefix_len;
17578 }
17579 current_line.push_str(token);
17580 current_line_len += grapheme_len;
17581 }
17582 WordBreakToken::InlineWhitespace {
17583 mut token,
17584 mut grapheme_len,
17585 } => {
17586 in_whitespace = true;
17587 if have_preceding_whitespace && !preserve_existing_whitespace {
17588 continue;
17589 }
17590 if !preserve_existing_whitespace {
17591 token = " ";
17592 grapheme_len = 1;
17593 }
17594 if current_line_len + grapheme_len > wrap_column {
17595 wrapped_text.push_str(current_line.trim_end());
17596 wrapped_text.push('\n');
17597 current_line.truncate(line_prefix.len());
17598 current_line_len = line_prefix_len;
17599 } else if current_line_len != line_prefix_len || preserve_existing_whitespace {
17600 current_line.push_str(token);
17601 current_line_len += grapheme_len;
17602 }
17603 }
17604 WordBreakToken::Newline => {
17605 in_whitespace = true;
17606 if preserve_existing_whitespace {
17607 wrapped_text.push_str(current_line.trim_end());
17608 wrapped_text.push('\n');
17609 current_line.truncate(line_prefix.len());
17610 current_line_len = line_prefix_len;
17611 } else if have_preceding_whitespace {
17612 continue;
17613 } else if current_line_len + 1 > wrap_column && current_line_len != line_prefix_len
17614 {
17615 wrapped_text.push_str(current_line.trim_end());
17616 wrapped_text.push('\n');
17617 current_line.truncate(line_prefix.len());
17618 current_line_len = line_prefix_len;
17619 } else if current_line_len != line_prefix_len {
17620 current_line.push(' ');
17621 current_line_len += 1;
17622 }
17623 }
17624 }
17625 }
17626
17627 if !current_line.is_empty() {
17628 wrapped_text.push_str(¤t_line);
17629 }
17630 wrapped_text
17631}
17632
17633#[test]
17634fn test_wrap_with_prefix() {
17635 assert_eq!(
17636 wrap_with_prefix(
17637 "# ".to_string(),
17638 "abcdefg".to_string(),
17639 4,
17640 NonZeroU32::new(4).unwrap(),
17641 false,
17642 ),
17643 "# abcdefg"
17644 );
17645 assert_eq!(
17646 wrap_with_prefix(
17647 "".to_string(),
17648 "\thello world".to_string(),
17649 8,
17650 NonZeroU32::new(4).unwrap(),
17651 false,
17652 ),
17653 "hello\nworld"
17654 );
17655 assert_eq!(
17656 wrap_with_prefix(
17657 "// ".to_string(),
17658 "xx \nyy zz aa bb cc".to_string(),
17659 12,
17660 NonZeroU32::new(4).unwrap(),
17661 false,
17662 ),
17663 "// xx yy zz\n// aa bb cc"
17664 );
17665 assert_eq!(
17666 wrap_with_prefix(
17667 String::new(),
17668 "这是什么 \n 钢笔".to_string(),
17669 3,
17670 NonZeroU32::new(4).unwrap(),
17671 false,
17672 ),
17673 "这是什\n么 钢\n笔"
17674 );
17675}
17676
17677pub trait CollaborationHub {
17678 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
17679 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
17680 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
17681}
17682
17683impl CollaborationHub for Entity<Project> {
17684 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
17685 self.read(cx).collaborators()
17686 }
17687
17688 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
17689 self.read(cx).user_store().read(cx).participant_indices()
17690 }
17691
17692 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
17693 let this = self.read(cx);
17694 let user_ids = this.collaborators().values().map(|c| c.user_id);
17695 this.user_store().read_with(cx, |user_store, cx| {
17696 user_store.participant_names(user_ids, cx)
17697 })
17698 }
17699}
17700
17701pub trait SemanticsProvider {
17702 fn hover(
17703 &self,
17704 buffer: &Entity<Buffer>,
17705 position: text::Anchor,
17706 cx: &mut App,
17707 ) -> Option<Task<Vec<project::Hover>>>;
17708
17709 fn inlay_hints(
17710 &self,
17711 buffer_handle: Entity<Buffer>,
17712 range: Range<text::Anchor>,
17713 cx: &mut App,
17714 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
17715
17716 fn resolve_inlay_hint(
17717 &self,
17718 hint: InlayHint,
17719 buffer_handle: Entity<Buffer>,
17720 server_id: LanguageServerId,
17721 cx: &mut App,
17722 ) -> Option<Task<anyhow::Result<InlayHint>>>;
17723
17724 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
17725
17726 fn document_highlights(
17727 &self,
17728 buffer: &Entity<Buffer>,
17729 position: text::Anchor,
17730 cx: &mut App,
17731 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
17732
17733 fn definitions(
17734 &self,
17735 buffer: &Entity<Buffer>,
17736 position: text::Anchor,
17737 kind: GotoDefinitionKind,
17738 cx: &mut App,
17739 ) -> Option<Task<Result<Vec<LocationLink>>>>;
17740
17741 fn range_for_rename(
17742 &self,
17743 buffer: &Entity<Buffer>,
17744 position: text::Anchor,
17745 cx: &mut App,
17746 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
17747
17748 fn perform_rename(
17749 &self,
17750 buffer: &Entity<Buffer>,
17751 position: text::Anchor,
17752 new_name: String,
17753 cx: &mut App,
17754 ) -> Option<Task<Result<ProjectTransaction>>>;
17755}
17756
17757pub trait CompletionProvider {
17758 fn completions(
17759 &self,
17760 buffer: &Entity<Buffer>,
17761 buffer_position: text::Anchor,
17762 trigger: CompletionContext,
17763 window: &mut Window,
17764 cx: &mut Context<Editor>,
17765 ) -> Task<Result<Option<Vec<Completion>>>>;
17766
17767 fn resolve_completions(
17768 &self,
17769 buffer: Entity<Buffer>,
17770 completion_indices: Vec<usize>,
17771 completions: Rc<RefCell<Box<[Completion]>>>,
17772 cx: &mut Context<Editor>,
17773 ) -> Task<Result<bool>>;
17774
17775 fn apply_additional_edits_for_completion(
17776 &self,
17777 _buffer: Entity<Buffer>,
17778 _completions: Rc<RefCell<Box<[Completion]>>>,
17779 _completion_index: usize,
17780 _push_to_history: bool,
17781 _cx: &mut Context<Editor>,
17782 ) -> Task<Result<Option<language::Transaction>>> {
17783 Task::ready(Ok(None))
17784 }
17785
17786 fn is_completion_trigger(
17787 &self,
17788 buffer: &Entity<Buffer>,
17789 position: language::Anchor,
17790 text: &str,
17791 trigger_in_words: bool,
17792 cx: &mut Context<Editor>,
17793 ) -> bool;
17794
17795 fn sort_completions(&self) -> bool {
17796 true
17797 }
17798}
17799
17800pub trait CodeActionProvider {
17801 fn id(&self) -> Arc<str>;
17802
17803 fn code_actions(
17804 &self,
17805 buffer: &Entity<Buffer>,
17806 range: Range<text::Anchor>,
17807 window: &mut Window,
17808 cx: &mut App,
17809 ) -> Task<Result<Vec<CodeAction>>>;
17810
17811 fn apply_code_action(
17812 &self,
17813 buffer_handle: Entity<Buffer>,
17814 action: CodeAction,
17815 excerpt_id: ExcerptId,
17816 push_to_history: bool,
17817 window: &mut Window,
17818 cx: &mut App,
17819 ) -> Task<Result<ProjectTransaction>>;
17820}
17821
17822impl CodeActionProvider for Entity<Project> {
17823 fn id(&self) -> Arc<str> {
17824 "project".into()
17825 }
17826
17827 fn code_actions(
17828 &self,
17829 buffer: &Entity<Buffer>,
17830 range: Range<text::Anchor>,
17831 _window: &mut Window,
17832 cx: &mut App,
17833 ) -> Task<Result<Vec<CodeAction>>> {
17834 self.update(cx, |project, cx| {
17835 let code_lens = project.code_lens(buffer, range.clone(), cx);
17836 let code_actions = project.code_actions(buffer, range, None, cx);
17837 cx.background_spawn(async move {
17838 let (code_lens, code_actions) = join(code_lens, code_actions).await;
17839 Ok(code_lens
17840 .context("code lens fetch")?
17841 .into_iter()
17842 .chain(code_actions.context("code action fetch")?)
17843 .collect())
17844 })
17845 })
17846 }
17847
17848 fn apply_code_action(
17849 &self,
17850 buffer_handle: Entity<Buffer>,
17851 action: CodeAction,
17852 _excerpt_id: ExcerptId,
17853 push_to_history: bool,
17854 _window: &mut Window,
17855 cx: &mut App,
17856 ) -> Task<Result<ProjectTransaction>> {
17857 self.update(cx, |project, cx| {
17858 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17859 })
17860 }
17861}
17862
17863fn snippet_completions(
17864 project: &Project,
17865 buffer: &Entity<Buffer>,
17866 buffer_position: text::Anchor,
17867 cx: &mut App,
17868) -> Task<Result<Vec<Completion>>> {
17869 let language = buffer.read(cx).language_at(buffer_position);
17870 let language_name = language.as_ref().map(|language| language.lsp_id());
17871 let snippet_store = project.snippets().read(cx);
17872 let snippets = snippet_store.snippets_for(language_name, cx);
17873
17874 if snippets.is_empty() {
17875 return Task::ready(Ok(vec![]));
17876 }
17877 let snapshot = buffer.read(cx).text_snapshot();
17878 let chars: String = snapshot
17879 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17880 .collect();
17881
17882 let scope = language.map(|language| language.default_scope());
17883 let executor = cx.background_executor().clone();
17884
17885 cx.background_spawn(async move {
17886 let classifier = CharClassifier::new(scope).for_completion(true);
17887 let mut last_word = chars
17888 .chars()
17889 .take_while(|c| classifier.is_word(*c))
17890 .collect::<String>();
17891 last_word = last_word.chars().rev().collect();
17892
17893 if last_word.is_empty() {
17894 return Ok(vec![]);
17895 }
17896
17897 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
17898 let to_lsp = |point: &text::Anchor| {
17899 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
17900 point_to_lsp(end)
17901 };
17902 let lsp_end = to_lsp(&buffer_position);
17903
17904 let candidates = snippets
17905 .iter()
17906 .enumerate()
17907 .flat_map(|(ix, snippet)| {
17908 snippet
17909 .prefix
17910 .iter()
17911 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
17912 })
17913 .collect::<Vec<StringMatchCandidate>>();
17914
17915 let mut matches = fuzzy::match_strings(
17916 &candidates,
17917 &last_word,
17918 last_word.chars().any(|c| c.is_uppercase()),
17919 100,
17920 &Default::default(),
17921 executor,
17922 )
17923 .await;
17924
17925 // Remove all candidates where the query's start does not match the start of any word in the candidate
17926 if let Some(query_start) = last_word.chars().next() {
17927 matches.retain(|string_match| {
17928 split_words(&string_match.string).any(|word| {
17929 // Check that the first codepoint of the word as lowercase matches the first
17930 // codepoint of the query as lowercase
17931 word.chars()
17932 .flat_map(|codepoint| codepoint.to_lowercase())
17933 .zip(query_start.to_lowercase())
17934 .all(|(word_cp, query_cp)| word_cp == query_cp)
17935 })
17936 });
17937 }
17938
17939 let matched_strings = matches
17940 .into_iter()
17941 .map(|m| m.string)
17942 .collect::<HashSet<_>>();
17943
17944 let result: Vec<Completion> = snippets
17945 .into_iter()
17946 .filter_map(|snippet| {
17947 let matching_prefix = snippet
17948 .prefix
17949 .iter()
17950 .find(|prefix| matched_strings.contains(*prefix))?;
17951 let start = as_offset - last_word.len();
17952 let start = snapshot.anchor_before(start);
17953 let range = start..buffer_position;
17954 let lsp_start = to_lsp(&start);
17955 let lsp_range = lsp::Range {
17956 start: lsp_start,
17957 end: lsp_end,
17958 };
17959 Some(Completion {
17960 old_range: range,
17961 new_text: snippet.body.clone(),
17962 source: CompletionSource::Lsp {
17963 server_id: LanguageServerId(usize::MAX),
17964 resolved: true,
17965 lsp_completion: Box::new(lsp::CompletionItem {
17966 label: snippet.prefix.first().unwrap().clone(),
17967 kind: Some(CompletionItemKind::SNIPPET),
17968 label_details: snippet.description.as_ref().map(|description| {
17969 lsp::CompletionItemLabelDetails {
17970 detail: Some(description.clone()),
17971 description: None,
17972 }
17973 }),
17974 insert_text_format: Some(InsertTextFormat::SNIPPET),
17975 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
17976 lsp::InsertReplaceEdit {
17977 new_text: snippet.body.clone(),
17978 insert: lsp_range,
17979 replace: lsp_range,
17980 },
17981 )),
17982 filter_text: Some(snippet.body.clone()),
17983 sort_text: Some(char::MAX.to_string()),
17984 ..lsp::CompletionItem::default()
17985 }),
17986 lsp_defaults: None,
17987 },
17988 label: CodeLabel {
17989 text: matching_prefix.clone(),
17990 runs: Vec::new(),
17991 filter_range: 0..matching_prefix.len(),
17992 },
17993 documentation: snippet
17994 .description
17995 .clone()
17996 .map(|description| CompletionDocumentation::SingleLine(description.into())),
17997 confirm: None,
17998 })
17999 })
18000 .collect();
18001
18002 Ok(result)
18003 })
18004}
18005
18006impl CompletionProvider for Entity<Project> {
18007 fn completions(
18008 &self,
18009 buffer: &Entity<Buffer>,
18010 buffer_position: text::Anchor,
18011 options: CompletionContext,
18012 _window: &mut Window,
18013 cx: &mut Context<Editor>,
18014 ) -> Task<Result<Option<Vec<Completion>>>> {
18015 self.update(cx, |project, cx| {
18016 let snippets = snippet_completions(project, buffer, buffer_position, cx);
18017 let project_completions = project.completions(buffer, buffer_position, options, cx);
18018 cx.background_spawn(async move {
18019 let snippets_completions = snippets.await?;
18020 match project_completions.await? {
18021 Some(mut completions) => {
18022 completions.extend(snippets_completions);
18023 Ok(Some(completions))
18024 }
18025 None => {
18026 if snippets_completions.is_empty() {
18027 Ok(None)
18028 } else {
18029 Ok(Some(snippets_completions))
18030 }
18031 }
18032 }
18033 })
18034 })
18035 }
18036
18037 fn resolve_completions(
18038 &self,
18039 buffer: Entity<Buffer>,
18040 completion_indices: Vec<usize>,
18041 completions: Rc<RefCell<Box<[Completion]>>>,
18042 cx: &mut Context<Editor>,
18043 ) -> Task<Result<bool>> {
18044 self.update(cx, |project, cx| {
18045 project.lsp_store().update(cx, |lsp_store, cx| {
18046 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
18047 })
18048 })
18049 }
18050
18051 fn apply_additional_edits_for_completion(
18052 &self,
18053 buffer: Entity<Buffer>,
18054 completions: Rc<RefCell<Box<[Completion]>>>,
18055 completion_index: usize,
18056 push_to_history: bool,
18057 cx: &mut Context<Editor>,
18058 ) -> Task<Result<Option<language::Transaction>>> {
18059 self.update(cx, |project, cx| {
18060 project.lsp_store().update(cx, |lsp_store, cx| {
18061 lsp_store.apply_additional_edits_for_completion(
18062 buffer,
18063 completions,
18064 completion_index,
18065 push_to_history,
18066 cx,
18067 )
18068 })
18069 })
18070 }
18071
18072 fn is_completion_trigger(
18073 &self,
18074 buffer: &Entity<Buffer>,
18075 position: language::Anchor,
18076 text: &str,
18077 trigger_in_words: bool,
18078 cx: &mut Context<Editor>,
18079 ) -> bool {
18080 let mut chars = text.chars();
18081 let char = if let Some(char) = chars.next() {
18082 char
18083 } else {
18084 return false;
18085 };
18086 if chars.next().is_some() {
18087 return false;
18088 }
18089
18090 let buffer = buffer.read(cx);
18091 let snapshot = buffer.snapshot();
18092 if !snapshot.settings_at(position, cx).show_completions_on_input {
18093 return false;
18094 }
18095 let classifier = snapshot.char_classifier_at(position).for_completion(true);
18096 if trigger_in_words && classifier.is_word(char) {
18097 return true;
18098 }
18099
18100 buffer.completion_triggers().contains(text)
18101 }
18102}
18103
18104impl SemanticsProvider for Entity<Project> {
18105 fn hover(
18106 &self,
18107 buffer: &Entity<Buffer>,
18108 position: text::Anchor,
18109 cx: &mut App,
18110 ) -> Option<Task<Vec<project::Hover>>> {
18111 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
18112 }
18113
18114 fn document_highlights(
18115 &self,
18116 buffer: &Entity<Buffer>,
18117 position: text::Anchor,
18118 cx: &mut App,
18119 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
18120 Some(self.update(cx, |project, cx| {
18121 project.document_highlights(buffer, position, cx)
18122 }))
18123 }
18124
18125 fn definitions(
18126 &self,
18127 buffer: &Entity<Buffer>,
18128 position: text::Anchor,
18129 kind: GotoDefinitionKind,
18130 cx: &mut App,
18131 ) -> Option<Task<Result<Vec<LocationLink>>>> {
18132 Some(self.update(cx, |project, cx| match kind {
18133 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
18134 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
18135 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
18136 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
18137 }))
18138 }
18139
18140 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
18141 // TODO: make this work for remote projects
18142 self.update(cx, |this, cx| {
18143 buffer.update(cx, |buffer, cx| {
18144 this.any_language_server_supports_inlay_hints(buffer, cx)
18145 })
18146 })
18147 }
18148
18149 fn inlay_hints(
18150 &self,
18151 buffer_handle: Entity<Buffer>,
18152 range: Range<text::Anchor>,
18153 cx: &mut App,
18154 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
18155 Some(self.update(cx, |project, cx| {
18156 project.inlay_hints(buffer_handle, range, cx)
18157 }))
18158 }
18159
18160 fn resolve_inlay_hint(
18161 &self,
18162 hint: InlayHint,
18163 buffer_handle: Entity<Buffer>,
18164 server_id: LanguageServerId,
18165 cx: &mut App,
18166 ) -> Option<Task<anyhow::Result<InlayHint>>> {
18167 Some(self.update(cx, |project, cx| {
18168 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
18169 }))
18170 }
18171
18172 fn range_for_rename(
18173 &self,
18174 buffer: &Entity<Buffer>,
18175 position: text::Anchor,
18176 cx: &mut App,
18177 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
18178 Some(self.update(cx, |project, cx| {
18179 let buffer = buffer.clone();
18180 let task = project.prepare_rename(buffer.clone(), position, cx);
18181 cx.spawn(async move |_, cx| {
18182 Ok(match task.await? {
18183 PrepareRenameResponse::Success(range) => Some(range),
18184 PrepareRenameResponse::InvalidPosition => None,
18185 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
18186 // Fallback on using TreeSitter info to determine identifier range
18187 buffer.update(cx, |buffer, _| {
18188 let snapshot = buffer.snapshot();
18189 let (range, kind) = snapshot.surrounding_word(position);
18190 if kind != Some(CharKind::Word) {
18191 return None;
18192 }
18193 Some(
18194 snapshot.anchor_before(range.start)
18195 ..snapshot.anchor_after(range.end),
18196 )
18197 })?
18198 }
18199 })
18200 })
18201 }))
18202 }
18203
18204 fn perform_rename(
18205 &self,
18206 buffer: &Entity<Buffer>,
18207 position: text::Anchor,
18208 new_name: String,
18209 cx: &mut App,
18210 ) -> Option<Task<Result<ProjectTransaction>>> {
18211 Some(self.update(cx, |project, cx| {
18212 project.perform_rename(buffer.clone(), position, new_name, cx)
18213 }))
18214 }
18215}
18216
18217fn inlay_hint_settings(
18218 location: Anchor,
18219 snapshot: &MultiBufferSnapshot,
18220 cx: &mut Context<Editor>,
18221) -> InlayHintSettings {
18222 let file = snapshot.file_at(location);
18223 let language = snapshot.language_at(location).map(|l| l.name());
18224 language_settings(language, file, cx).inlay_hints
18225}
18226
18227fn consume_contiguous_rows(
18228 contiguous_row_selections: &mut Vec<Selection<Point>>,
18229 selection: &Selection<Point>,
18230 display_map: &DisplaySnapshot,
18231 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
18232) -> (MultiBufferRow, MultiBufferRow) {
18233 contiguous_row_selections.push(selection.clone());
18234 let start_row = MultiBufferRow(selection.start.row);
18235 let mut end_row = ending_row(selection, display_map);
18236
18237 while let Some(next_selection) = selections.peek() {
18238 if next_selection.start.row <= end_row.0 {
18239 end_row = ending_row(next_selection, display_map);
18240 contiguous_row_selections.push(selections.next().unwrap().clone());
18241 } else {
18242 break;
18243 }
18244 }
18245 (start_row, end_row)
18246}
18247
18248fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
18249 if next_selection.end.column > 0 || next_selection.is_empty() {
18250 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
18251 } else {
18252 MultiBufferRow(next_selection.end.row)
18253 }
18254}
18255
18256impl EditorSnapshot {
18257 pub fn remote_selections_in_range<'a>(
18258 &'a self,
18259 range: &'a Range<Anchor>,
18260 collaboration_hub: &dyn CollaborationHub,
18261 cx: &'a App,
18262 ) -> impl 'a + Iterator<Item = RemoteSelection> {
18263 let participant_names = collaboration_hub.user_names(cx);
18264 let participant_indices = collaboration_hub.user_participant_indices(cx);
18265 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
18266 let collaborators_by_replica_id = collaborators_by_peer_id
18267 .iter()
18268 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
18269 .collect::<HashMap<_, _>>();
18270 self.buffer_snapshot
18271 .selections_in_range(range, false)
18272 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
18273 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
18274 let participant_index = participant_indices.get(&collaborator.user_id).copied();
18275 let user_name = participant_names.get(&collaborator.user_id).cloned();
18276 Some(RemoteSelection {
18277 replica_id,
18278 selection,
18279 cursor_shape,
18280 line_mode,
18281 participant_index,
18282 peer_id: collaborator.peer_id,
18283 user_name,
18284 })
18285 })
18286 }
18287
18288 pub fn hunks_for_ranges(
18289 &self,
18290 ranges: impl IntoIterator<Item = Range<Point>>,
18291 ) -> Vec<MultiBufferDiffHunk> {
18292 let mut hunks = Vec::new();
18293 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
18294 HashMap::default();
18295 for query_range in ranges {
18296 let query_rows =
18297 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
18298 for hunk in self.buffer_snapshot.diff_hunks_in_range(
18299 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
18300 ) {
18301 // Include deleted hunks that are adjacent to the query range, because
18302 // otherwise they would be missed.
18303 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
18304 if hunk.status().is_deleted() {
18305 intersects_range |= hunk.row_range.start == query_rows.end;
18306 intersects_range |= hunk.row_range.end == query_rows.start;
18307 }
18308 if intersects_range {
18309 if !processed_buffer_rows
18310 .entry(hunk.buffer_id)
18311 .or_default()
18312 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
18313 {
18314 continue;
18315 }
18316 hunks.push(hunk);
18317 }
18318 }
18319 }
18320
18321 hunks
18322 }
18323
18324 fn display_diff_hunks_for_rows<'a>(
18325 &'a self,
18326 display_rows: Range<DisplayRow>,
18327 folded_buffers: &'a HashSet<BufferId>,
18328 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
18329 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
18330 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
18331
18332 self.buffer_snapshot
18333 .diff_hunks_in_range(buffer_start..buffer_end)
18334 .filter_map(|hunk| {
18335 if folded_buffers.contains(&hunk.buffer_id) {
18336 return None;
18337 }
18338
18339 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
18340 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
18341
18342 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
18343 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
18344
18345 let display_hunk = if hunk_display_start.column() != 0 {
18346 DisplayDiffHunk::Folded {
18347 display_row: hunk_display_start.row(),
18348 }
18349 } else {
18350 let mut end_row = hunk_display_end.row();
18351 if hunk_display_end.column() > 0 {
18352 end_row.0 += 1;
18353 }
18354 let is_created_file = hunk.is_created_file();
18355 DisplayDiffHunk::Unfolded {
18356 status: hunk.status(),
18357 diff_base_byte_range: hunk.diff_base_byte_range,
18358 display_row_range: hunk_display_start.row()..end_row,
18359 multi_buffer_range: Anchor::range_in_buffer(
18360 hunk.excerpt_id,
18361 hunk.buffer_id,
18362 hunk.buffer_range,
18363 ),
18364 is_created_file,
18365 }
18366 };
18367
18368 Some(display_hunk)
18369 })
18370 }
18371
18372 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
18373 self.display_snapshot.buffer_snapshot.language_at(position)
18374 }
18375
18376 pub fn is_focused(&self) -> bool {
18377 self.is_focused
18378 }
18379
18380 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
18381 self.placeholder_text.as_ref()
18382 }
18383
18384 pub fn scroll_position(&self) -> gpui::Point<f32> {
18385 self.scroll_anchor.scroll_position(&self.display_snapshot)
18386 }
18387
18388 fn gutter_dimensions(
18389 &self,
18390 font_id: FontId,
18391 font_size: Pixels,
18392 max_line_number_width: Pixels,
18393 cx: &App,
18394 ) -> Option<GutterDimensions> {
18395 if !self.show_gutter {
18396 return None;
18397 }
18398
18399 let descent = cx.text_system().descent(font_id, font_size);
18400 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
18401 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
18402
18403 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
18404 matches!(
18405 ProjectSettings::get_global(cx).git.git_gutter,
18406 Some(GitGutterSetting::TrackedFiles)
18407 )
18408 });
18409 let gutter_settings = EditorSettings::get_global(cx).gutter;
18410 let show_line_numbers = self
18411 .show_line_numbers
18412 .unwrap_or(gutter_settings.line_numbers);
18413 let line_gutter_width = if show_line_numbers {
18414 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
18415 let min_width_for_number_on_gutter = em_advance * MIN_LINE_NUMBER_DIGITS as f32;
18416 max_line_number_width.max(min_width_for_number_on_gutter)
18417 } else {
18418 0.0.into()
18419 };
18420
18421 let show_code_actions = self
18422 .show_code_actions
18423 .unwrap_or(gutter_settings.code_actions);
18424
18425 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
18426 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
18427
18428 let git_blame_entries_width =
18429 self.git_blame_gutter_max_author_length
18430 .map(|max_author_length| {
18431 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
18432
18433 /// The number of characters to dedicate to gaps and margins.
18434 const SPACING_WIDTH: usize = 4;
18435
18436 let max_char_count = max_author_length
18437 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
18438 + ::git::SHORT_SHA_LENGTH
18439 + MAX_RELATIVE_TIMESTAMP.len()
18440 + SPACING_WIDTH;
18441
18442 em_advance * max_char_count
18443 });
18444
18445 let is_singleton = self.buffer_snapshot.is_singleton();
18446
18447 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
18448 left_padding += if !is_singleton {
18449 em_width * 4.0
18450 } else if show_code_actions || show_runnables || show_breakpoints {
18451 em_width * 3.0
18452 } else if show_git_gutter && show_line_numbers {
18453 em_width * 2.0
18454 } else if show_git_gutter || show_line_numbers {
18455 em_width
18456 } else {
18457 px(0.)
18458 };
18459
18460 let shows_folds = is_singleton && gutter_settings.folds;
18461
18462 let right_padding = if shows_folds && show_line_numbers {
18463 em_width * 4.0
18464 } else if shows_folds || (!is_singleton && show_line_numbers) {
18465 em_width * 3.0
18466 } else if show_line_numbers {
18467 em_width
18468 } else {
18469 px(0.)
18470 };
18471
18472 Some(GutterDimensions {
18473 left_padding,
18474 right_padding,
18475 width: line_gutter_width + left_padding + right_padding,
18476 margin: -descent,
18477 git_blame_entries_width,
18478 })
18479 }
18480
18481 pub fn render_crease_toggle(
18482 &self,
18483 buffer_row: MultiBufferRow,
18484 row_contains_cursor: bool,
18485 editor: Entity<Editor>,
18486 window: &mut Window,
18487 cx: &mut App,
18488 ) -> Option<AnyElement> {
18489 let folded = self.is_line_folded(buffer_row);
18490 let mut is_foldable = false;
18491
18492 if let Some(crease) = self
18493 .crease_snapshot
18494 .query_row(buffer_row, &self.buffer_snapshot)
18495 {
18496 is_foldable = true;
18497 match crease {
18498 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
18499 if let Some(render_toggle) = render_toggle {
18500 let toggle_callback =
18501 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
18502 if folded {
18503 editor.update(cx, |editor, cx| {
18504 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
18505 });
18506 } else {
18507 editor.update(cx, |editor, cx| {
18508 editor.unfold_at(
18509 &crate::UnfoldAt { buffer_row },
18510 window,
18511 cx,
18512 )
18513 });
18514 }
18515 });
18516 return Some((render_toggle)(
18517 buffer_row,
18518 folded,
18519 toggle_callback,
18520 window,
18521 cx,
18522 ));
18523 }
18524 }
18525 }
18526 }
18527
18528 is_foldable |= self.starts_indent(buffer_row);
18529
18530 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
18531 Some(
18532 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
18533 .toggle_state(folded)
18534 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
18535 if folded {
18536 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
18537 } else {
18538 this.fold_at(&FoldAt { buffer_row }, window, cx);
18539 }
18540 }))
18541 .into_any_element(),
18542 )
18543 } else {
18544 None
18545 }
18546 }
18547
18548 pub fn render_crease_trailer(
18549 &self,
18550 buffer_row: MultiBufferRow,
18551 window: &mut Window,
18552 cx: &mut App,
18553 ) -> Option<AnyElement> {
18554 let folded = self.is_line_folded(buffer_row);
18555 if let Crease::Inline { render_trailer, .. } = self
18556 .crease_snapshot
18557 .query_row(buffer_row, &self.buffer_snapshot)?
18558 {
18559 let render_trailer = render_trailer.as_ref()?;
18560 Some(render_trailer(buffer_row, folded, window, cx))
18561 } else {
18562 None
18563 }
18564 }
18565}
18566
18567impl Deref for EditorSnapshot {
18568 type Target = DisplaySnapshot;
18569
18570 fn deref(&self) -> &Self::Target {
18571 &self.display_snapshot
18572 }
18573}
18574
18575#[derive(Clone, Debug, PartialEq, Eq)]
18576pub enum EditorEvent {
18577 InputIgnored {
18578 text: Arc<str>,
18579 },
18580 InputHandled {
18581 utf16_range_to_replace: Option<Range<isize>>,
18582 text: Arc<str>,
18583 },
18584 ExcerptsAdded {
18585 buffer: Entity<Buffer>,
18586 predecessor: ExcerptId,
18587 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
18588 },
18589 ExcerptsRemoved {
18590 ids: Vec<ExcerptId>,
18591 },
18592 BufferFoldToggled {
18593 ids: Vec<ExcerptId>,
18594 folded: bool,
18595 },
18596 ExcerptsEdited {
18597 ids: Vec<ExcerptId>,
18598 },
18599 ExcerptsExpanded {
18600 ids: Vec<ExcerptId>,
18601 },
18602 BufferEdited,
18603 Edited {
18604 transaction_id: clock::Lamport,
18605 },
18606 Reparsed(BufferId),
18607 Focused,
18608 FocusedIn,
18609 Blurred,
18610 DirtyChanged,
18611 Saved,
18612 TitleChanged,
18613 DiffBaseChanged,
18614 SelectionsChanged {
18615 local: bool,
18616 },
18617 ScrollPositionChanged {
18618 local: bool,
18619 autoscroll: bool,
18620 },
18621 Closed,
18622 TransactionUndone {
18623 transaction_id: clock::Lamport,
18624 },
18625 TransactionBegun {
18626 transaction_id: clock::Lamport,
18627 },
18628 Reloaded,
18629 CursorShapeChanged,
18630 PushedToNavHistory {
18631 anchor: Anchor,
18632 is_deactivate: bool,
18633 },
18634}
18635
18636impl EventEmitter<EditorEvent> for Editor {}
18637
18638impl Focusable for Editor {
18639 fn focus_handle(&self, _cx: &App) -> FocusHandle {
18640 self.focus_handle.clone()
18641 }
18642}
18643
18644impl Render for Editor {
18645 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18646 let settings = ThemeSettings::get_global(cx);
18647
18648 let mut text_style = match self.mode {
18649 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
18650 color: cx.theme().colors().editor_foreground,
18651 font_family: settings.ui_font.family.clone(),
18652 font_features: settings.ui_font.features.clone(),
18653 font_fallbacks: settings.ui_font.fallbacks.clone(),
18654 font_size: rems(0.875).into(),
18655 font_weight: settings.ui_font.weight,
18656 line_height: relative(settings.buffer_line_height.value()),
18657 ..Default::default()
18658 },
18659 EditorMode::Full => TextStyle {
18660 color: cx.theme().colors().editor_foreground,
18661 font_family: settings.buffer_font.family.clone(),
18662 font_features: settings.buffer_font.features.clone(),
18663 font_fallbacks: settings.buffer_font.fallbacks.clone(),
18664 font_size: settings.buffer_font_size(cx).into(),
18665 font_weight: settings.buffer_font.weight,
18666 line_height: relative(settings.buffer_line_height.value()),
18667 ..Default::default()
18668 },
18669 };
18670 if let Some(text_style_refinement) = &self.text_style_refinement {
18671 text_style.refine(text_style_refinement)
18672 }
18673
18674 let background = match self.mode {
18675 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
18676 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
18677 EditorMode::Full => cx.theme().colors().editor_background,
18678 };
18679
18680 EditorElement::new(
18681 &cx.entity(),
18682 EditorStyle {
18683 background,
18684 local_player: cx.theme().players().local(),
18685 text: text_style,
18686 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
18687 syntax: cx.theme().syntax().clone(),
18688 status: cx.theme().status().clone(),
18689 inlay_hints_style: make_inlay_hints_style(cx),
18690 inline_completion_styles: make_suggestion_styles(cx),
18691 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
18692 },
18693 )
18694 }
18695}
18696
18697impl EntityInputHandler for Editor {
18698 fn text_for_range(
18699 &mut self,
18700 range_utf16: Range<usize>,
18701 adjusted_range: &mut Option<Range<usize>>,
18702 _: &mut Window,
18703 cx: &mut Context<Self>,
18704 ) -> Option<String> {
18705 let snapshot = self.buffer.read(cx).read(cx);
18706 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
18707 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
18708 if (start.0..end.0) != range_utf16 {
18709 adjusted_range.replace(start.0..end.0);
18710 }
18711 Some(snapshot.text_for_range(start..end).collect())
18712 }
18713
18714 fn selected_text_range(
18715 &mut self,
18716 ignore_disabled_input: bool,
18717 _: &mut Window,
18718 cx: &mut Context<Self>,
18719 ) -> Option<UTF16Selection> {
18720 // Prevent the IME menu from appearing when holding down an alphabetic key
18721 // while input is disabled.
18722 if !ignore_disabled_input && !self.input_enabled {
18723 return None;
18724 }
18725
18726 let selection = self.selections.newest::<OffsetUtf16>(cx);
18727 let range = selection.range();
18728
18729 Some(UTF16Selection {
18730 range: range.start.0..range.end.0,
18731 reversed: selection.reversed,
18732 })
18733 }
18734
18735 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
18736 let snapshot = self.buffer.read(cx).read(cx);
18737 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
18738 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
18739 }
18740
18741 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
18742 self.clear_highlights::<InputComposition>(cx);
18743 self.ime_transaction.take();
18744 }
18745
18746 fn replace_text_in_range(
18747 &mut self,
18748 range_utf16: Option<Range<usize>>,
18749 text: &str,
18750 window: &mut Window,
18751 cx: &mut Context<Self>,
18752 ) {
18753 if !self.input_enabled {
18754 cx.emit(EditorEvent::InputIgnored { text: text.into() });
18755 return;
18756 }
18757
18758 self.transact(window, cx, |this, window, cx| {
18759 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
18760 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18761 Some(this.selection_replacement_ranges(range_utf16, cx))
18762 } else {
18763 this.marked_text_ranges(cx)
18764 };
18765
18766 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
18767 let newest_selection_id = this.selections.newest_anchor().id;
18768 this.selections
18769 .all::<OffsetUtf16>(cx)
18770 .iter()
18771 .zip(ranges_to_replace.iter())
18772 .find_map(|(selection, range)| {
18773 if selection.id == newest_selection_id {
18774 Some(
18775 (range.start.0 as isize - selection.head().0 as isize)
18776 ..(range.end.0 as isize - selection.head().0 as isize),
18777 )
18778 } else {
18779 None
18780 }
18781 })
18782 });
18783
18784 cx.emit(EditorEvent::InputHandled {
18785 utf16_range_to_replace: range_to_replace,
18786 text: text.into(),
18787 });
18788
18789 if let Some(new_selected_ranges) = new_selected_ranges {
18790 this.change_selections(None, window, cx, |selections| {
18791 selections.select_ranges(new_selected_ranges)
18792 });
18793 this.backspace(&Default::default(), window, cx);
18794 }
18795
18796 this.handle_input(text, window, cx);
18797 });
18798
18799 if let Some(transaction) = self.ime_transaction {
18800 self.buffer.update(cx, |buffer, cx| {
18801 buffer.group_until_transaction(transaction, cx);
18802 });
18803 }
18804
18805 self.unmark_text(window, cx);
18806 }
18807
18808 fn replace_and_mark_text_in_range(
18809 &mut self,
18810 range_utf16: Option<Range<usize>>,
18811 text: &str,
18812 new_selected_range_utf16: Option<Range<usize>>,
18813 window: &mut Window,
18814 cx: &mut Context<Self>,
18815 ) {
18816 if !self.input_enabled {
18817 return;
18818 }
18819
18820 let transaction = self.transact(window, cx, |this, window, cx| {
18821 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
18822 let snapshot = this.buffer.read(cx).read(cx);
18823 if let Some(relative_range_utf16) = range_utf16.as_ref() {
18824 for marked_range in &mut marked_ranges {
18825 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
18826 marked_range.start.0 += relative_range_utf16.start;
18827 marked_range.start =
18828 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
18829 marked_range.end =
18830 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
18831 }
18832 }
18833 Some(marked_ranges)
18834 } else if let Some(range_utf16) = range_utf16 {
18835 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18836 Some(this.selection_replacement_ranges(range_utf16, cx))
18837 } else {
18838 None
18839 };
18840
18841 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
18842 let newest_selection_id = this.selections.newest_anchor().id;
18843 this.selections
18844 .all::<OffsetUtf16>(cx)
18845 .iter()
18846 .zip(ranges_to_replace.iter())
18847 .find_map(|(selection, range)| {
18848 if selection.id == newest_selection_id {
18849 Some(
18850 (range.start.0 as isize - selection.head().0 as isize)
18851 ..(range.end.0 as isize - selection.head().0 as isize),
18852 )
18853 } else {
18854 None
18855 }
18856 })
18857 });
18858
18859 cx.emit(EditorEvent::InputHandled {
18860 utf16_range_to_replace: range_to_replace,
18861 text: text.into(),
18862 });
18863
18864 if let Some(ranges) = ranges_to_replace {
18865 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18866 }
18867
18868 let marked_ranges = {
18869 let snapshot = this.buffer.read(cx).read(cx);
18870 this.selections
18871 .disjoint_anchors()
18872 .iter()
18873 .map(|selection| {
18874 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18875 })
18876 .collect::<Vec<_>>()
18877 };
18878
18879 if text.is_empty() {
18880 this.unmark_text(window, cx);
18881 } else {
18882 this.highlight_text::<InputComposition>(
18883 marked_ranges.clone(),
18884 HighlightStyle {
18885 underline: Some(UnderlineStyle {
18886 thickness: px(1.),
18887 color: None,
18888 wavy: false,
18889 }),
18890 ..Default::default()
18891 },
18892 cx,
18893 );
18894 }
18895
18896 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
18897 let use_autoclose = this.use_autoclose;
18898 let use_auto_surround = this.use_auto_surround;
18899 this.set_use_autoclose(false);
18900 this.set_use_auto_surround(false);
18901 this.handle_input(text, window, cx);
18902 this.set_use_autoclose(use_autoclose);
18903 this.set_use_auto_surround(use_auto_surround);
18904
18905 if let Some(new_selected_range) = new_selected_range_utf16 {
18906 let snapshot = this.buffer.read(cx).read(cx);
18907 let new_selected_ranges = marked_ranges
18908 .into_iter()
18909 .map(|marked_range| {
18910 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
18911 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
18912 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
18913 snapshot.clip_offset_utf16(new_start, Bias::Left)
18914 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
18915 })
18916 .collect::<Vec<_>>();
18917
18918 drop(snapshot);
18919 this.change_selections(None, window, cx, |selections| {
18920 selections.select_ranges(new_selected_ranges)
18921 });
18922 }
18923 });
18924
18925 self.ime_transaction = self.ime_transaction.or(transaction);
18926 if let Some(transaction) = self.ime_transaction {
18927 self.buffer.update(cx, |buffer, cx| {
18928 buffer.group_until_transaction(transaction, cx);
18929 });
18930 }
18931
18932 if self.text_highlights::<InputComposition>(cx).is_none() {
18933 self.ime_transaction.take();
18934 }
18935 }
18936
18937 fn bounds_for_range(
18938 &mut self,
18939 range_utf16: Range<usize>,
18940 element_bounds: gpui::Bounds<Pixels>,
18941 window: &mut Window,
18942 cx: &mut Context<Self>,
18943 ) -> Option<gpui::Bounds<Pixels>> {
18944 let text_layout_details = self.text_layout_details(window);
18945 let gpui::Size {
18946 width: em_width,
18947 height: line_height,
18948 } = self.character_size(window);
18949
18950 let snapshot = self.snapshot(window, cx);
18951 let scroll_position = snapshot.scroll_position();
18952 let scroll_left = scroll_position.x * em_width;
18953
18954 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
18955 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
18956 + self.gutter_dimensions.width
18957 + self.gutter_dimensions.margin;
18958 let y = line_height * (start.row().as_f32() - scroll_position.y);
18959
18960 Some(Bounds {
18961 origin: element_bounds.origin + point(x, y),
18962 size: size(em_width, line_height),
18963 })
18964 }
18965
18966 fn character_index_for_point(
18967 &mut self,
18968 point: gpui::Point<Pixels>,
18969 _window: &mut Window,
18970 _cx: &mut Context<Self>,
18971 ) -> Option<usize> {
18972 let position_map = self.last_position_map.as_ref()?;
18973 if !position_map.text_hitbox.contains(&point) {
18974 return None;
18975 }
18976 let display_point = position_map.point_for_position(point).previous_valid;
18977 let anchor = position_map
18978 .snapshot
18979 .display_point_to_anchor(display_point, Bias::Left);
18980 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
18981 Some(utf16_offset.0)
18982 }
18983}
18984
18985trait SelectionExt {
18986 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
18987 fn spanned_rows(
18988 &self,
18989 include_end_if_at_line_start: bool,
18990 map: &DisplaySnapshot,
18991 ) -> Range<MultiBufferRow>;
18992}
18993
18994impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
18995 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
18996 let start = self
18997 .start
18998 .to_point(&map.buffer_snapshot)
18999 .to_display_point(map);
19000 let end = self
19001 .end
19002 .to_point(&map.buffer_snapshot)
19003 .to_display_point(map);
19004 if self.reversed {
19005 end..start
19006 } else {
19007 start..end
19008 }
19009 }
19010
19011 fn spanned_rows(
19012 &self,
19013 include_end_if_at_line_start: bool,
19014 map: &DisplaySnapshot,
19015 ) -> Range<MultiBufferRow> {
19016 let start = self.start.to_point(&map.buffer_snapshot);
19017 let mut end = self.end.to_point(&map.buffer_snapshot);
19018 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
19019 end.row -= 1;
19020 }
19021
19022 let buffer_start = map.prev_line_boundary(start).0;
19023 let buffer_end = map.next_line_boundary(end).0;
19024 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
19025 }
19026}
19027
19028impl<T: InvalidationRegion> InvalidationStack<T> {
19029 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
19030 where
19031 S: Clone + ToOffset,
19032 {
19033 while let Some(region) = self.last() {
19034 let all_selections_inside_invalidation_ranges =
19035 if selections.len() == region.ranges().len() {
19036 selections
19037 .iter()
19038 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
19039 .all(|(selection, invalidation_range)| {
19040 let head = selection.head().to_offset(buffer);
19041 invalidation_range.start <= head && invalidation_range.end >= head
19042 })
19043 } else {
19044 false
19045 };
19046
19047 if all_selections_inside_invalidation_ranges {
19048 break;
19049 } else {
19050 self.pop();
19051 }
19052 }
19053 }
19054}
19055
19056impl<T> Default for InvalidationStack<T> {
19057 fn default() -> Self {
19058 Self(Default::default())
19059 }
19060}
19061
19062impl<T> Deref for InvalidationStack<T> {
19063 type Target = Vec<T>;
19064
19065 fn deref(&self) -> &Self::Target {
19066 &self.0
19067 }
19068}
19069
19070impl<T> DerefMut for InvalidationStack<T> {
19071 fn deref_mut(&mut self) -> &mut Self::Target {
19072 &mut self.0
19073 }
19074}
19075
19076impl InvalidationRegion for SnippetState {
19077 fn ranges(&self) -> &[Range<Anchor>] {
19078 &self.ranges[self.active_index]
19079 }
19080}
19081
19082pub fn diagnostic_block_renderer(
19083 diagnostic: Diagnostic,
19084 max_message_rows: Option<u8>,
19085 allow_closing: bool,
19086) -> RenderBlock {
19087 let (text_without_backticks, code_ranges) =
19088 highlight_diagnostic_message(&diagnostic, max_message_rows);
19089
19090 Arc::new(move |cx: &mut BlockContext| {
19091 let group_id: SharedString = cx.block_id.to_string().into();
19092
19093 let mut text_style = cx.window.text_style().clone();
19094 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
19095 let theme_settings = ThemeSettings::get_global(cx);
19096 text_style.font_family = theme_settings.buffer_font.family.clone();
19097 text_style.font_style = theme_settings.buffer_font.style;
19098 text_style.font_features = theme_settings.buffer_font.features.clone();
19099 text_style.font_weight = theme_settings.buffer_font.weight;
19100
19101 let multi_line_diagnostic = diagnostic.message.contains('\n');
19102
19103 let buttons = |diagnostic: &Diagnostic| {
19104 if multi_line_diagnostic {
19105 v_flex()
19106 } else {
19107 h_flex()
19108 }
19109 .when(allow_closing, |div| {
19110 div.children(diagnostic.is_primary.then(|| {
19111 IconButton::new("close-block", IconName::XCircle)
19112 .icon_color(Color::Muted)
19113 .size(ButtonSize::Compact)
19114 .style(ButtonStyle::Transparent)
19115 .visible_on_hover(group_id.clone())
19116 .on_click(move |_click, window, cx| {
19117 window.dispatch_action(Box::new(Cancel), cx)
19118 })
19119 .tooltip(|window, cx| {
19120 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
19121 })
19122 }))
19123 })
19124 .child(
19125 IconButton::new("copy-block", IconName::Copy)
19126 .icon_color(Color::Muted)
19127 .size(ButtonSize::Compact)
19128 .style(ButtonStyle::Transparent)
19129 .visible_on_hover(group_id.clone())
19130 .on_click({
19131 let message = diagnostic.message.clone();
19132 move |_click, _, cx| {
19133 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
19134 }
19135 })
19136 .tooltip(Tooltip::text("Copy diagnostic message")),
19137 )
19138 };
19139
19140 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
19141 AvailableSpace::min_size(),
19142 cx.window,
19143 cx.app,
19144 );
19145
19146 h_flex()
19147 .id(cx.block_id)
19148 .group(group_id.clone())
19149 .relative()
19150 .size_full()
19151 .block_mouse_down()
19152 .pl(cx.gutter_dimensions.width)
19153 .w(cx.max_width - cx.gutter_dimensions.full_width())
19154 .child(
19155 div()
19156 .flex()
19157 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
19158 .flex_shrink(),
19159 )
19160 .child(buttons(&diagnostic))
19161 .child(div().flex().flex_shrink_0().child(
19162 StyledText::new(text_without_backticks.clone()).with_default_highlights(
19163 &text_style,
19164 code_ranges.iter().map(|range| {
19165 (
19166 range.clone(),
19167 HighlightStyle {
19168 font_weight: Some(FontWeight::BOLD),
19169 ..Default::default()
19170 },
19171 )
19172 }),
19173 ),
19174 ))
19175 .into_any_element()
19176 })
19177}
19178
19179fn inline_completion_edit_text(
19180 current_snapshot: &BufferSnapshot,
19181 edits: &[(Range<Anchor>, String)],
19182 edit_preview: &EditPreview,
19183 include_deletions: bool,
19184 cx: &App,
19185) -> HighlightedText {
19186 let edits = edits
19187 .iter()
19188 .map(|(anchor, text)| {
19189 (
19190 anchor.start.text_anchor..anchor.end.text_anchor,
19191 text.clone(),
19192 )
19193 })
19194 .collect::<Vec<_>>();
19195
19196 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
19197}
19198
19199pub fn highlight_diagnostic_message(
19200 diagnostic: &Diagnostic,
19201 mut max_message_rows: Option<u8>,
19202) -> (SharedString, Vec<Range<usize>>) {
19203 let mut text_without_backticks = String::new();
19204 let mut code_ranges = Vec::new();
19205
19206 if let Some(source) = &diagnostic.source {
19207 text_without_backticks.push_str(source);
19208 code_ranges.push(0..source.len());
19209 text_without_backticks.push_str(": ");
19210 }
19211
19212 let mut prev_offset = 0;
19213 let mut in_code_block = false;
19214 let has_row_limit = max_message_rows.is_some();
19215 let mut newline_indices = diagnostic
19216 .message
19217 .match_indices('\n')
19218 .filter(|_| has_row_limit)
19219 .map(|(ix, _)| ix)
19220 .fuse()
19221 .peekable();
19222
19223 for (quote_ix, _) in diagnostic
19224 .message
19225 .match_indices('`')
19226 .chain([(diagnostic.message.len(), "")])
19227 {
19228 let mut first_newline_ix = None;
19229 let mut last_newline_ix = None;
19230 while let Some(newline_ix) = newline_indices.peek() {
19231 if *newline_ix < quote_ix {
19232 if first_newline_ix.is_none() {
19233 first_newline_ix = Some(*newline_ix);
19234 }
19235 last_newline_ix = Some(*newline_ix);
19236
19237 if let Some(rows_left) = &mut max_message_rows {
19238 if *rows_left == 0 {
19239 break;
19240 } else {
19241 *rows_left -= 1;
19242 }
19243 }
19244 let _ = newline_indices.next();
19245 } else {
19246 break;
19247 }
19248 }
19249 let prev_len = text_without_backticks.len();
19250 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
19251 text_without_backticks.push_str(new_text);
19252 if in_code_block {
19253 code_ranges.push(prev_len..text_without_backticks.len());
19254 }
19255 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
19256 in_code_block = !in_code_block;
19257 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
19258 text_without_backticks.push_str("...");
19259 break;
19260 }
19261 }
19262
19263 (text_without_backticks.into(), code_ranges)
19264}
19265
19266fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
19267 match severity {
19268 DiagnosticSeverity::ERROR => colors.error,
19269 DiagnosticSeverity::WARNING => colors.warning,
19270 DiagnosticSeverity::INFORMATION => colors.info,
19271 DiagnosticSeverity::HINT => colors.info,
19272 _ => colors.ignored,
19273 }
19274}
19275
19276pub fn styled_runs_for_code_label<'a>(
19277 label: &'a CodeLabel,
19278 syntax_theme: &'a theme::SyntaxTheme,
19279) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
19280 let fade_out = HighlightStyle {
19281 fade_out: Some(0.35),
19282 ..Default::default()
19283 };
19284
19285 let mut prev_end = label.filter_range.end;
19286 label
19287 .runs
19288 .iter()
19289 .enumerate()
19290 .flat_map(move |(ix, (range, highlight_id))| {
19291 let style = if let Some(style) = highlight_id.style(syntax_theme) {
19292 style
19293 } else {
19294 return Default::default();
19295 };
19296 let mut muted_style = style;
19297 muted_style.highlight(fade_out);
19298
19299 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
19300 if range.start >= label.filter_range.end {
19301 if range.start > prev_end {
19302 runs.push((prev_end..range.start, fade_out));
19303 }
19304 runs.push((range.clone(), muted_style));
19305 } else if range.end <= label.filter_range.end {
19306 runs.push((range.clone(), style));
19307 } else {
19308 runs.push((range.start..label.filter_range.end, style));
19309 runs.push((label.filter_range.end..range.end, muted_style));
19310 }
19311 prev_end = cmp::max(prev_end, range.end);
19312
19313 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
19314 runs.push((prev_end..label.text.len(), fade_out));
19315 }
19316
19317 runs
19318 })
19319}
19320
19321pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
19322 let mut prev_index = 0;
19323 let mut prev_codepoint: Option<char> = None;
19324 text.char_indices()
19325 .chain([(text.len(), '\0')])
19326 .filter_map(move |(index, codepoint)| {
19327 let prev_codepoint = prev_codepoint.replace(codepoint)?;
19328 let is_boundary = index == text.len()
19329 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
19330 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
19331 if is_boundary {
19332 let chunk = &text[prev_index..index];
19333 prev_index = index;
19334 Some(chunk)
19335 } else {
19336 None
19337 }
19338 })
19339}
19340
19341pub trait RangeToAnchorExt: Sized {
19342 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
19343
19344 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
19345 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
19346 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
19347 }
19348}
19349
19350impl<T: ToOffset> RangeToAnchorExt for Range<T> {
19351 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
19352 let start_offset = self.start.to_offset(snapshot);
19353 let end_offset = self.end.to_offset(snapshot);
19354 if start_offset == end_offset {
19355 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
19356 } else {
19357 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
19358 }
19359 }
19360}
19361
19362pub trait RowExt {
19363 fn as_f32(&self) -> f32;
19364
19365 fn next_row(&self) -> Self;
19366
19367 fn previous_row(&self) -> Self;
19368
19369 fn minus(&self, other: Self) -> u32;
19370}
19371
19372impl RowExt for DisplayRow {
19373 fn as_f32(&self) -> f32 {
19374 self.0 as f32
19375 }
19376
19377 fn next_row(&self) -> Self {
19378 Self(self.0 + 1)
19379 }
19380
19381 fn previous_row(&self) -> Self {
19382 Self(self.0.saturating_sub(1))
19383 }
19384
19385 fn minus(&self, other: Self) -> u32 {
19386 self.0 - other.0
19387 }
19388}
19389
19390impl RowExt for MultiBufferRow {
19391 fn as_f32(&self) -> f32 {
19392 self.0 as f32
19393 }
19394
19395 fn next_row(&self) -> Self {
19396 Self(self.0 + 1)
19397 }
19398
19399 fn previous_row(&self) -> Self {
19400 Self(self.0.saturating_sub(1))
19401 }
19402
19403 fn minus(&self, other: Self) -> u32 {
19404 self.0 - other.0
19405 }
19406}
19407
19408trait RowRangeExt {
19409 type Row;
19410
19411 fn len(&self) -> usize;
19412
19413 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
19414}
19415
19416impl RowRangeExt for Range<MultiBufferRow> {
19417 type Row = MultiBufferRow;
19418
19419 fn len(&self) -> usize {
19420 (self.end.0 - self.start.0) as usize
19421 }
19422
19423 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
19424 (self.start.0..self.end.0).map(MultiBufferRow)
19425 }
19426}
19427
19428impl RowRangeExt for Range<DisplayRow> {
19429 type Row = DisplayRow;
19430
19431 fn len(&self) -> usize {
19432 (self.end.0 - self.start.0) as usize
19433 }
19434
19435 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
19436 (self.start.0..self.end.0).map(DisplayRow)
19437 }
19438}
19439
19440/// If select range has more than one line, we
19441/// just point the cursor to range.start.
19442fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
19443 if range.start.row == range.end.row {
19444 range
19445 } else {
19446 range.start..range.start
19447 }
19448}
19449pub struct KillRing(ClipboardItem);
19450impl Global for KillRing {}
19451
19452const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
19453
19454struct BreakpointPromptEditor {
19455 pub(crate) prompt: Entity<Editor>,
19456 editor: WeakEntity<Editor>,
19457 breakpoint_anchor: Anchor,
19458 kind: BreakpointKind,
19459 block_ids: HashSet<CustomBlockId>,
19460 gutter_dimensions: Arc<Mutex<GutterDimensions>>,
19461 _subscriptions: Vec<Subscription>,
19462}
19463
19464impl BreakpointPromptEditor {
19465 const MAX_LINES: u8 = 4;
19466
19467 fn new(
19468 editor: WeakEntity<Editor>,
19469 breakpoint_anchor: Anchor,
19470 kind: BreakpointKind,
19471 window: &mut Window,
19472 cx: &mut Context<Self>,
19473 ) -> Self {
19474 let buffer = cx.new(|cx| {
19475 Buffer::local(
19476 kind.log_message()
19477 .map(|msg| msg.to_string())
19478 .unwrap_or_default(),
19479 cx,
19480 )
19481 });
19482 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
19483
19484 let prompt = cx.new(|cx| {
19485 let mut prompt = Editor::new(
19486 EditorMode::AutoHeight {
19487 max_lines: Self::MAX_LINES as usize,
19488 },
19489 buffer,
19490 None,
19491 window,
19492 cx,
19493 );
19494 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
19495 prompt.set_show_cursor_when_unfocused(false, cx);
19496 prompt.set_placeholder_text(
19497 "Message to log when breakpoint is hit. Expressions within {} are interpolated.",
19498 cx,
19499 );
19500
19501 prompt
19502 });
19503
19504 Self {
19505 prompt,
19506 editor,
19507 breakpoint_anchor,
19508 kind,
19509 gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
19510 block_ids: Default::default(),
19511 _subscriptions: vec![],
19512 }
19513 }
19514
19515 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
19516 self.block_ids.extend(block_ids)
19517 }
19518
19519 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
19520 if let Some(editor) = self.editor.upgrade() {
19521 let log_message = self
19522 .prompt
19523 .read(cx)
19524 .buffer
19525 .read(cx)
19526 .as_singleton()
19527 .expect("A multi buffer in breakpoint prompt isn't possible")
19528 .read(cx)
19529 .as_rope()
19530 .to_string();
19531
19532 editor.update(cx, |editor, cx| {
19533 editor.edit_breakpoint_at_anchor(
19534 self.breakpoint_anchor,
19535 self.kind.clone(),
19536 BreakpointEditAction::EditLogMessage(log_message.into()),
19537 cx,
19538 );
19539
19540 editor.remove_blocks(self.block_ids.clone(), None, cx);
19541 cx.focus_self(window);
19542 });
19543 }
19544 }
19545
19546 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
19547 self.editor
19548 .update(cx, |editor, cx| {
19549 editor.remove_blocks(self.block_ids.clone(), None, cx);
19550 window.focus(&editor.focus_handle);
19551 })
19552 .log_err();
19553 }
19554
19555 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
19556 let settings = ThemeSettings::get_global(cx);
19557 let text_style = TextStyle {
19558 color: if self.prompt.read(cx).read_only(cx) {
19559 cx.theme().colors().text_disabled
19560 } else {
19561 cx.theme().colors().text
19562 },
19563 font_family: settings.buffer_font.family.clone(),
19564 font_fallbacks: settings.buffer_font.fallbacks.clone(),
19565 font_size: settings.buffer_font_size(cx).into(),
19566 font_weight: settings.buffer_font.weight,
19567 line_height: relative(settings.buffer_line_height.value()),
19568 ..Default::default()
19569 };
19570 EditorElement::new(
19571 &self.prompt,
19572 EditorStyle {
19573 background: cx.theme().colors().editor_background,
19574 local_player: cx.theme().players().local(),
19575 text: text_style,
19576 ..Default::default()
19577 },
19578 )
19579 }
19580}
19581
19582impl Render for BreakpointPromptEditor {
19583 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
19584 let gutter_dimensions = *self.gutter_dimensions.lock();
19585 h_flex()
19586 .key_context("Editor")
19587 .bg(cx.theme().colors().editor_background)
19588 .border_y_1()
19589 .border_color(cx.theme().status().info_border)
19590 .size_full()
19591 .py(window.line_height() / 2.5)
19592 .on_action(cx.listener(Self::confirm))
19593 .on_action(cx.listener(Self::cancel))
19594 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
19595 .child(div().flex_1().child(self.render_prompt_editor(cx)))
19596 }
19597}
19598
19599impl Focusable for BreakpointPromptEditor {
19600 fn focus_handle(&self, cx: &App) -> FocusHandle {
19601 self.prompt.focus_handle(cx)
19602 }
19603}
19604
19605fn all_edits_insertions_or_deletions(
19606 edits: &Vec<(Range<Anchor>, String)>,
19607 snapshot: &MultiBufferSnapshot,
19608) -> bool {
19609 let mut all_insertions = true;
19610 let mut all_deletions = true;
19611
19612 for (range, new_text) in edits.iter() {
19613 let range_is_empty = range.to_offset(&snapshot).is_empty();
19614 let text_is_empty = new_text.is_empty();
19615
19616 if range_is_empty != text_is_empty {
19617 if range_is_empty {
19618 all_deletions = false;
19619 } else {
19620 all_insertions = false;
19621 }
19622 } else {
19623 return false;
19624 }
19625
19626 if !all_insertions && !all_deletions {
19627 return false;
19628 }
19629 }
19630 all_insertions || all_deletions
19631}
19632
19633struct MissingEditPredictionKeybindingTooltip;
19634
19635impl Render for MissingEditPredictionKeybindingTooltip {
19636 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
19637 ui::tooltip_container(window, cx, |container, _, cx| {
19638 container
19639 .flex_shrink_0()
19640 .max_w_80()
19641 .min_h(rems_from_px(124.))
19642 .justify_between()
19643 .child(
19644 v_flex()
19645 .flex_1()
19646 .text_ui_sm(cx)
19647 .child(Label::new("Conflict with Accept Keybinding"))
19648 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
19649 )
19650 .child(
19651 h_flex()
19652 .pb_1()
19653 .gap_1()
19654 .items_end()
19655 .w_full()
19656 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
19657 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
19658 }))
19659 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
19660 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
19661 })),
19662 )
19663 })
19664 }
19665}
19666
19667#[derive(Debug, Clone, Copy, PartialEq)]
19668pub struct LineHighlight {
19669 pub background: Background,
19670 pub border: Option<gpui::Hsla>,
19671}
19672
19673impl From<Hsla> for LineHighlight {
19674 fn from(hsla: Hsla) -> Self {
19675 Self {
19676 background: hsla.into(),
19677 border: None,
19678 }
19679 }
19680}
19681
19682impl From<Background> for LineHighlight {
19683 fn from(background: Background) -> Self {
19684 Self {
19685 background,
19686 border: None,
19687 }
19688 }
19689}