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 behaviour.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18pub mod display_map;
19mod editor_settings;
20mod element;
21mod hunk_diff;
22mod inlay_hint_cache;
23
24mod debounced_delay;
25mod git;
26mod highlight_matching_bracket;
27mod hover_links;
28mod hover_popover;
29mod inline_completion_provider;
30pub mod items;
31mod mouse_context_menu;
32pub mod movement;
33mod persistence;
34mod rust_analyzer_ext;
35pub mod scroll;
36mod selections_collection;
37pub mod tasks;
38
39#[cfg(test)]
40mod editor_tests;
41#[cfg(any(test, feature = "test-support"))]
42pub mod test;
43use ::git::diff::{DiffHunk, DiffHunkStatus};
44use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
45pub(crate) use actions::*;
46use aho_corasick::AhoCorasick;
47use anyhow::{anyhow, Context as _, Result};
48use blink_manager::BlinkManager;
49use client::{Collaborator, ParticipantIndex};
50use clock::ReplicaId;
51use collections::{hash_map, BTreeMap, Bound, HashMap, HashSet, VecDeque};
52use convert_case::{Case, Casing};
53use debounced_delay::DebouncedDelay;
54pub use display_map::DisplayPoint;
55use display_map::*;
56use editor_settings::CurrentLineHighlight;
57pub use editor_settings::EditorSettings;
58use element::LineWithInvisibles;
59pub use element::{
60 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
61};
62use futures::FutureExt;
63use fuzzy::{StringMatch, StringMatchCandidate};
64use git::blame::GitBlame;
65use git::diff_hunk_to_display;
66use gpui::{
67 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
68 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
69 Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontId, FontStyle,
70 FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton, PaintQuad,
71 ParentElement, Pixels, Render, SharedString, Size, StrikethroughStyle, Styled, StyledText,
72 Subscription, Task, TextStyle, UnderlineStyle, UniformListScrollHandle, View, ViewContext,
73 ViewInputHandler, VisualContext, WeakView, WhiteSpace, WindowContext,
74};
75use highlight_matching_bracket::refresh_matching_bracket_highlights;
76use hover_popover::{hide_hover, HoverState};
77use hunk_diff::ExpandedHunks;
78pub(crate) use hunk_diff::HunkToExpand;
79use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
80pub use inline_completion_provider::*;
81pub use items::MAX_TAB_TITLE_LEN;
82use itertools::Itertools;
83use language::{
84 char_kind,
85 language_settings::{self, all_language_settings, InlayHintSettings},
86 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
87 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
88 Point, Selection, SelectionGoal, TransactionId,
89};
90use language::{BufferRow, Runnable, RunnableRange};
91use task::{ResolvedTask, TaskTemplate, TaskVariables};
92
93use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
94use lsp::{DiagnosticSeverity, LanguageServerId};
95use mouse_context_menu::MouseContextMenu;
96use movement::TextLayoutDetails;
97pub use multi_buffer::{
98 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
99 ToPoint,
100};
101use multi_buffer::{MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
102use ordered_float::OrderedFloat;
103use parking_lot::{Mutex, RwLock};
104use project::project_settings::{GitGutterSetting, ProjectSettings};
105use project::{
106 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
107 ProjectTransaction, TaskSourceKind, WorktreeId,
108};
109use rand::prelude::*;
110use rpc::{proto::*, ErrorExt};
111use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
112use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
113use serde::{Deserialize, Serialize};
114use settings::{Settings, SettingsStore};
115use smallvec::SmallVec;
116use snippet::Snippet;
117use std::ops::Not as _;
118use std::{
119 any::TypeId,
120 borrow::Cow,
121 cmp::{self, Ordering, Reverse},
122 mem,
123 num::NonZeroU32,
124 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
125 path::Path,
126 sync::Arc,
127 time::{Duration, Instant},
128};
129pub use sum_tree::Bias;
130use sum_tree::TreeMap;
131use text::{BufferId, OffsetUtf16, Rope};
132use theme::{
133 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
134 ThemeColors, ThemeSettings,
135};
136use ui::{
137 h_flex, prelude::*, ButtonSize, ButtonStyle, IconButton, IconName, IconSize, ListItem, Popover,
138 Tooltip,
139};
140use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
141use workspace::item::{ItemHandle, PreviewTabsSettings};
142use workspace::notifications::{DetachAndPromptErr, NotificationId};
143use workspace::{
144 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
145};
146use workspace::{OpenInTerminal, OpenTerminal, Toast};
147
148use crate::hover_links::find_url;
149
150pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
151const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
152const MAX_LINE_LEN: usize = 1024;
153const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
154const MAX_SELECTION_HISTORY_LEN: usize = 1024;
155pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
156#[doc(hidden)]
157pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
158#[doc(hidden)]
159pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
160
161pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
162
163pub fn render_parsed_markdown(
164 element_id: impl Into<ElementId>,
165 parsed: &language::ParsedMarkdown,
166 editor_style: &EditorStyle,
167 workspace: Option<WeakView<Workspace>>,
168 cx: &mut WindowContext,
169) -> InteractiveText {
170 let code_span_background_color = cx
171 .theme()
172 .colors()
173 .editor_document_highlight_read_background;
174
175 let highlights = gpui::combine_highlights(
176 parsed.highlights.iter().filter_map(|(range, highlight)| {
177 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
178 Some((range.clone(), highlight))
179 }),
180 parsed
181 .regions
182 .iter()
183 .zip(&parsed.region_ranges)
184 .filter_map(|(region, range)| {
185 if region.code {
186 Some((
187 range.clone(),
188 HighlightStyle {
189 background_color: Some(code_span_background_color),
190 ..Default::default()
191 },
192 ))
193 } else {
194 None
195 }
196 }),
197 );
198
199 let mut links = Vec::new();
200 let mut link_ranges = Vec::new();
201 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
202 if let Some(link) = region.link.clone() {
203 links.push(link);
204 link_ranges.push(range.clone());
205 }
206 }
207
208 InteractiveText::new(
209 element_id,
210 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
211 )
212 .on_click(link_ranges, move |clicked_range_ix, cx| {
213 match &links[clicked_range_ix] {
214 markdown::Link::Web { url } => cx.open_url(url),
215 markdown::Link::Path { path } => {
216 if let Some(workspace) = &workspace {
217 _ = workspace.update(cx, |workspace, cx| {
218 workspace.open_abs_path(path.clone(), false, cx).detach();
219 });
220 }
221 }
222 }
223 })
224}
225
226#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub(crate) enum InlayId {
228 Suggestion(usize),
229 Hint(usize),
230}
231
232impl InlayId {
233 fn id(&self) -> usize {
234 match self {
235 Self::Suggestion(id) => *id,
236 Self::Hint(id) => *id,
237 }
238 }
239}
240
241enum DiffRowHighlight {}
242enum DocumentHighlightRead {}
243enum DocumentHighlightWrite {}
244enum InputComposition {}
245
246#[derive(Copy, Clone, PartialEq, Eq)]
247pub enum Direction {
248 Prev,
249 Next,
250}
251
252pub fn init_settings(cx: &mut AppContext) {
253 EditorSettings::register(cx);
254}
255
256pub fn init(cx: &mut AppContext) {
257 init_settings(cx);
258
259 workspace::register_project_item::<Editor>(cx);
260 workspace::register_followable_item::<Editor>(cx);
261 workspace::register_deserializable_item::<Editor>(cx);
262 cx.observe_new_views(
263 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
264 workspace.register_action(Editor::new_file);
265 workspace.register_action(Editor::new_file_in_direction);
266 },
267 )
268 .detach();
269
270 cx.on_action(move |_: &workspace::NewFile, cx| {
271 let app_state = workspace::AppState::global(cx);
272 if let Some(app_state) = app_state.upgrade() {
273 workspace::open_new(app_state, cx, |workspace, cx| {
274 Editor::new_file(workspace, &Default::default(), cx)
275 })
276 .detach();
277 }
278 });
279 cx.on_action(move |_: &workspace::NewWindow, cx| {
280 let app_state = workspace::AppState::global(cx);
281 if let Some(app_state) = app_state.upgrade() {
282 workspace::open_new(app_state, cx, |workspace, cx| {
283 Editor::new_file(workspace, &Default::default(), cx)
284 })
285 .detach();
286 }
287 });
288}
289
290pub struct SearchWithinRange;
291
292trait InvalidationRegion {
293 fn ranges(&self) -> &[Range<Anchor>];
294}
295
296#[derive(Clone, Debug, PartialEq)]
297pub enum SelectPhase {
298 Begin {
299 position: DisplayPoint,
300 add: bool,
301 click_count: usize,
302 },
303 BeginColumnar {
304 position: DisplayPoint,
305 goal_column: u32,
306 },
307 Extend {
308 position: DisplayPoint,
309 click_count: usize,
310 },
311 Update {
312 position: DisplayPoint,
313 goal_column: u32,
314 scroll_delta: gpui::Point<f32>,
315 },
316 End,
317}
318
319#[derive(Clone, Debug)]
320pub enum SelectMode {
321 Character,
322 Word(Range<Anchor>),
323 Line(Range<Anchor>),
324 All,
325}
326
327#[derive(Copy, Clone, PartialEq, Eq, Debug)]
328pub enum EditorMode {
329 SingleLine,
330 AutoHeight { max_lines: usize },
331 Full,
332}
333
334#[derive(Clone, Debug)]
335pub enum SoftWrap {
336 None,
337 PreferLine,
338 EditorWidth,
339 Column(u32),
340}
341
342#[derive(Clone)]
343pub struct EditorStyle {
344 pub background: Hsla,
345 pub local_player: PlayerColor,
346 pub text: TextStyle,
347 pub scrollbar_width: Pixels,
348 pub syntax: Arc<SyntaxTheme>,
349 pub status: StatusColors,
350 pub inlay_hints_style: HighlightStyle,
351 pub suggestions_style: HighlightStyle,
352}
353
354impl Default for EditorStyle {
355 fn default() -> Self {
356 Self {
357 background: Hsla::default(),
358 local_player: PlayerColor::default(),
359 text: TextStyle::default(),
360 scrollbar_width: Pixels::default(),
361 syntax: Default::default(),
362 // HACK: Status colors don't have a real default.
363 // We should look into removing the status colors from the editor
364 // style and retrieve them directly from the theme.
365 status: StatusColors::dark(),
366 inlay_hints_style: HighlightStyle::default(),
367 suggestions_style: HighlightStyle::default(),
368 }
369 }
370}
371
372type CompletionId = usize;
373
374// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
375// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
376
377type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
378
379struct ScrollbarMarkerState {
380 scrollbar_size: Size<Pixels>,
381 dirty: bool,
382 markers: Arc<[PaintQuad]>,
383 pending_refresh: Option<Task<Result<()>>>,
384}
385
386impl ScrollbarMarkerState {
387 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
388 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
389 }
390}
391
392impl Default for ScrollbarMarkerState {
393 fn default() -> Self {
394 Self {
395 scrollbar_size: Size::default(),
396 dirty: false,
397 markers: Arc::from([]),
398 pending_refresh: None,
399 }
400 }
401}
402
403#[derive(Clone, Debug)]
404struct RunnableTasks {
405 templates: Vec<(TaskSourceKind, TaskTemplate)>,
406 // We need the column at which the task context evaluation should take place.
407 column: u32,
408 extra_variables: HashMap<String, String>,
409}
410
411#[derive(Clone)]
412struct ResolvedTasks {
413 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
414 position: Anchor,
415}
416
417/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
418///
419/// See the [module level documentation](self) for more information.
420pub struct Editor {
421 focus_handle: FocusHandle,
422 /// The text buffer being edited
423 buffer: Model<MultiBuffer>,
424 /// Map of how text in the buffer should be displayed.
425 /// Handles soft wraps, folds, fake inlay text insertions, etc.
426 pub display_map: Model<DisplayMap>,
427 pub selections: SelectionsCollection,
428 pub scroll_manager: ScrollManager,
429 columnar_selection_tail: Option<Anchor>,
430 add_selections_state: Option<AddSelectionsState>,
431 select_next_state: Option<SelectNextState>,
432 select_prev_state: Option<SelectNextState>,
433 selection_history: SelectionHistory,
434 autoclose_regions: Vec<AutocloseRegion>,
435 snippet_stack: InvalidationStack<SnippetState>,
436 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
437 ime_transaction: Option<TransactionId>,
438 active_diagnostics: Option<ActiveDiagnosticGroup>,
439 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
440 project: Option<Model<Project>>,
441 completion_provider: Option<Box<dyn CompletionProvider>>,
442 collaboration_hub: Option<Box<dyn CollaborationHub>>,
443 blink_manager: Model<BlinkManager>,
444 show_cursor_names: bool,
445 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
446 pub show_local_selections: bool,
447 mode: EditorMode,
448 show_breadcrumbs: bool,
449 show_gutter: bool,
450 show_wrap_guides: Option<bool>,
451 placeholder_text: Option<Arc<str>>,
452 highlight_order: usize,
453 highlighted_rows: HashMap<TypeId, Vec<(usize, RangeInclusive<Anchor>, Option<Hsla>)>>,
454 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
455 scrollbar_marker_state: ScrollbarMarkerState,
456 nav_history: Option<ItemNavHistory>,
457 context_menu: RwLock<Option<ContextMenu>>,
458 mouse_context_menu: Option<MouseContextMenu>,
459 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
460 find_all_references_task_sources: Vec<Anchor>,
461 next_completion_id: CompletionId,
462 completion_documentation_pre_resolve_debounce: DebouncedDelay,
463 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
464 code_actions_task: Option<Task<()>>,
465 document_highlights_task: Option<Task<()>>,
466 pending_rename: Option<RenameState>,
467 searchable: bool,
468 cursor_shape: CursorShape,
469 current_line_highlight: CurrentLineHighlight,
470 collapse_matches: bool,
471 autoindent_mode: Option<AutoindentMode>,
472 workspace: Option<(WeakView<Workspace>, WorkspaceId)>,
473 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
474 input_enabled: bool,
475 use_modal_editing: bool,
476 read_only: bool,
477 leader_peer_id: Option<PeerId>,
478 remote_id: Option<ViewId>,
479 hover_state: HoverState,
480 gutter_hovered: bool,
481 hovered_link_state: Option<HoveredLinkState>,
482 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
483 active_inline_completion: Option<Inlay>,
484 show_inline_completions: bool,
485 inlay_hint_cache: InlayHintCache,
486 expanded_hunks: ExpandedHunks,
487 next_inlay_id: usize,
488 _subscriptions: Vec<Subscription>,
489 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
490 gutter_dimensions: GutterDimensions,
491 pub vim_replace_map: HashMap<Range<usize>, String>,
492 style: Option<EditorStyle>,
493 editor_actions: Vec<Box<dyn Fn(&mut ViewContext<Self>)>>,
494 use_autoclose: bool,
495 auto_replace_emoji_shortcode: bool,
496 show_git_blame_gutter: bool,
497 show_git_blame_inline: bool,
498 show_git_blame_inline_delay_task: Option<Task<()>>,
499 git_blame_inline_enabled: bool,
500 blame: Option<Model<GitBlame>>,
501 blame_subscription: Option<Subscription>,
502 custom_context_menu: Option<
503 Box<
504 dyn 'static
505 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
506 >,
507 >,
508 last_bounds: Option<Bounds<Pixels>>,
509 expect_bounds_change: Option<Bounds<Pixels>>,
510 tasks: HashMap<(BufferId, BufferRow), (usize, RunnableTasks)>,
511 tasks_update_task: Option<Task<()>>,
512}
513
514#[derive(Clone)]
515pub struct EditorSnapshot {
516 pub mode: EditorMode,
517 show_gutter: bool,
518 render_git_blame_gutter: bool,
519 pub display_snapshot: DisplaySnapshot,
520 pub placeholder_text: Option<Arc<str>>,
521 is_focused: bool,
522 scroll_anchor: ScrollAnchor,
523 ongoing_scroll: OngoingScroll,
524 current_line_highlight: CurrentLineHighlight,
525}
526
527const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
528
529#[derive(Debug, Clone, Copy)]
530pub struct GutterDimensions {
531 pub left_padding: Pixels,
532 pub right_padding: Pixels,
533 pub width: Pixels,
534 pub margin: Pixels,
535 pub git_blame_entries_width: Option<Pixels>,
536}
537
538impl Default for GutterDimensions {
539 fn default() -> Self {
540 Self {
541 left_padding: Pixels::ZERO,
542 right_padding: Pixels::ZERO,
543 width: Pixels::ZERO,
544 margin: Pixels::ZERO,
545 git_blame_entries_width: None,
546 }
547 }
548}
549
550#[derive(Debug)]
551pub struct RemoteSelection {
552 pub replica_id: ReplicaId,
553 pub selection: Selection<Anchor>,
554 pub cursor_shape: CursorShape,
555 pub peer_id: PeerId,
556 pub line_mode: bool,
557 pub participant_index: Option<ParticipantIndex>,
558 pub user_name: Option<SharedString>,
559}
560
561#[derive(Clone, Debug)]
562struct SelectionHistoryEntry {
563 selections: Arc<[Selection<Anchor>]>,
564 select_next_state: Option<SelectNextState>,
565 select_prev_state: Option<SelectNextState>,
566 add_selections_state: Option<AddSelectionsState>,
567}
568
569enum SelectionHistoryMode {
570 Normal,
571 Undoing,
572 Redoing,
573}
574
575#[derive(Clone, PartialEq, Eq, Hash)]
576struct HoveredCursor {
577 replica_id: u16,
578 selection_id: usize,
579}
580
581impl Default for SelectionHistoryMode {
582 fn default() -> Self {
583 Self::Normal
584 }
585}
586
587#[derive(Default)]
588struct SelectionHistory {
589 #[allow(clippy::type_complexity)]
590 selections_by_transaction:
591 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
592 mode: SelectionHistoryMode,
593 undo_stack: VecDeque<SelectionHistoryEntry>,
594 redo_stack: VecDeque<SelectionHistoryEntry>,
595}
596
597impl SelectionHistory {
598 fn insert_transaction(
599 &mut self,
600 transaction_id: TransactionId,
601 selections: Arc<[Selection<Anchor>]>,
602 ) {
603 self.selections_by_transaction
604 .insert(transaction_id, (selections, None));
605 }
606
607 #[allow(clippy::type_complexity)]
608 fn transaction(
609 &self,
610 transaction_id: TransactionId,
611 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
612 self.selections_by_transaction.get(&transaction_id)
613 }
614
615 #[allow(clippy::type_complexity)]
616 fn transaction_mut(
617 &mut self,
618 transaction_id: TransactionId,
619 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
620 self.selections_by_transaction.get_mut(&transaction_id)
621 }
622
623 fn push(&mut self, entry: SelectionHistoryEntry) {
624 if !entry.selections.is_empty() {
625 match self.mode {
626 SelectionHistoryMode::Normal => {
627 self.push_undo(entry);
628 self.redo_stack.clear();
629 }
630 SelectionHistoryMode::Undoing => self.push_redo(entry),
631 SelectionHistoryMode::Redoing => self.push_undo(entry),
632 }
633 }
634 }
635
636 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
637 if self
638 .undo_stack
639 .back()
640 .map_or(true, |e| e.selections != entry.selections)
641 {
642 self.undo_stack.push_back(entry);
643 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
644 self.undo_stack.pop_front();
645 }
646 }
647 }
648
649 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
650 if self
651 .redo_stack
652 .back()
653 .map_or(true, |e| e.selections != entry.selections)
654 {
655 self.redo_stack.push_back(entry);
656 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
657 self.redo_stack.pop_front();
658 }
659 }
660 }
661}
662
663#[derive(Clone, Debug)]
664struct AddSelectionsState {
665 above: bool,
666 stack: Vec<usize>,
667}
668
669#[derive(Clone)]
670struct SelectNextState {
671 query: AhoCorasick,
672 wordwise: bool,
673 done: bool,
674}
675
676impl std::fmt::Debug for SelectNextState {
677 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678 f.debug_struct(std::any::type_name::<Self>())
679 .field("wordwise", &self.wordwise)
680 .field("done", &self.done)
681 .finish()
682 }
683}
684
685#[derive(Debug)]
686struct AutocloseRegion {
687 selection_id: usize,
688 range: Range<Anchor>,
689 pair: BracketPair,
690}
691
692#[derive(Debug)]
693struct SnippetState {
694 ranges: Vec<Vec<Range<Anchor>>>,
695 active_index: usize,
696}
697
698#[doc(hidden)]
699pub struct RenameState {
700 pub range: Range<Anchor>,
701 pub old_name: Arc<str>,
702 pub editor: View<Editor>,
703 block_id: BlockId,
704}
705
706struct InvalidationStack<T>(Vec<T>);
707
708struct RegisteredInlineCompletionProvider {
709 provider: Arc<dyn InlineCompletionProviderHandle>,
710 _subscription: Subscription,
711}
712
713enum ContextMenu {
714 Completions(CompletionsMenu),
715 CodeActions(CodeActionsMenu),
716}
717
718impl ContextMenu {
719 fn select_first(
720 &mut self,
721 project: Option<&Model<Project>>,
722 cx: &mut ViewContext<Editor>,
723 ) -> bool {
724 if self.visible() {
725 match self {
726 ContextMenu::Completions(menu) => menu.select_first(project, cx),
727 ContextMenu::CodeActions(menu) => menu.select_first(cx),
728 }
729 true
730 } else {
731 false
732 }
733 }
734
735 fn select_prev(
736 &mut self,
737 project: Option<&Model<Project>>,
738 cx: &mut ViewContext<Editor>,
739 ) -> bool {
740 if self.visible() {
741 match self {
742 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
743 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
744 }
745 true
746 } else {
747 false
748 }
749 }
750
751 fn select_next(
752 &mut self,
753 project: Option<&Model<Project>>,
754 cx: &mut ViewContext<Editor>,
755 ) -> bool {
756 if self.visible() {
757 match self {
758 ContextMenu::Completions(menu) => menu.select_next(project, cx),
759 ContextMenu::CodeActions(menu) => menu.select_next(cx),
760 }
761 true
762 } else {
763 false
764 }
765 }
766
767 fn select_last(
768 &mut self,
769 project: Option<&Model<Project>>,
770 cx: &mut ViewContext<Editor>,
771 ) -> bool {
772 if self.visible() {
773 match self {
774 ContextMenu::Completions(menu) => menu.select_last(project, cx),
775 ContextMenu::CodeActions(menu) => menu.select_last(cx),
776 }
777 true
778 } else {
779 false
780 }
781 }
782
783 fn visible(&self) -> bool {
784 match self {
785 ContextMenu::Completions(menu) => menu.visible(),
786 ContextMenu::CodeActions(menu) => menu.visible(),
787 }
788 }
789
790 fn render(
791 &self,
792 cursor_position: DisplayPoint,
793 style: &EditorStyle,
794 max_height: Pixels,
795 workspace: Option<WeakView<Workspace>>,
796 cx: &mut ViewContext<Editor>,
797 ) -> (ContextMenuOrigin, AnyElement) {
798 match self {
799 ContextMenu::Completions(menu) => (
800 ContextMenuOrigin::EditorPoint(cursor_position),
801 menu.render(style, max_height, workspace, cx),
802 ),
803 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
804 }
805 }
806}
807
808enum ContextMenuOrigin {
809 EditorPoint(DisplayPoint),
810 GutterIndicator(DisplayRow),
811}
812
813#[derive(Clone)]
814struct CompletionsMenu {
815 id: CompletionId,
816 initial_position: Anchor,
817 buffer: Model<Buffer>,
818 completions: Arc<RwLock<Box<[Completion]>>>,
819 match_candidates: Arc<[StringMatchCandidate]>,
820 matches: Arc<[StringMatch]>,
821 selected_item: usize,
822 scroll_handle: UniformListScrollHandle,
823 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
824}
825
826impl CompletionsMenu {
827 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
828 self.selected_item = 0;
829 self.scroll_handle.scroll_to_item(self.selected_item);
830 self.attempt_resolve_selected_completion_documentation(project, cx);
831 cx.notify();
832 }
833
834 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
835 if self.selected_item > 0 {
836 self.selected_item -= 1;
837 } else {
838 self.selected_item = self.matches.len() - 1;
839 }
840 self.scroll_handle.scroll_to_item(self.selected_item);
841 self.attempt_resolve_selected_completion_documentation(project, cx);
842 cx.notify();
843 }
844
845 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
846 if self.selected_item + 1 < self.matches.len() {
847 self.selected_item += 1;
848 } else {
849 self.selected_item = 0;
850 }
851 self.scroll_handle.scroll_to_item(self.selected_item);
852 self.attempt_resolve_selected_completion_documentation(project, cx);
853 cx.notify();
854 }
855
856 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
857 self.selected_item = self.matches.len() - 1;
858 self.scroll_handle.scroll_to_item(self.selected_item);
859 self.attempt_resolve_selected_completion_documentation(project, cx);
860 cx.notify();
861 }
862
863 fn pre_resolve_completion_documentation(
864 buffer: Model<Buffer>,
865 completions: Arc<RwLock<Box<[Completion]>>>,
866 matches: Arc<[StringMatch]>,
867 editor: &Editor,
868 cx: &mut ViewContext<Editor>,
869 ) -> Task<()> {
870 let settings = EditorSettings::get_global(cx);
871 if !settings.show_completion_documentation {
872 return Task::ready(());
873 }
874
875 let Some(provider) = editor.completion_provider.as_ref() else {
876 return Task::ready(());
877 };
878
879 let resolve_task = provider.resolve_completions(
880 buffer,
881 matches.iter().map(|m| m.candidate_id).collect(),
882 completions.clone(),
883 cx,
884 );
885
886 return cx.spawn(move |this, mut cx| async move {
887 if let Some(true) = resolve_task.await.log_err() {
888 this.update(&mut cx, |_, cx| cx.notify()).ok();
889 }
890 });
891 }
892
893 fn attempt_resolve_selected_completion_documentation(
894 &mut self,
895 project: Option<&Model<Project>>,
896 cx: &mut ViewContext<Editor>,
897 ) {
898 let settings = EditorSettings::get_global(cx);
899 if !settings.show_completion_documentation {
900 return;
901 }
902
903 let completion_index = self.matches[self.selected_item].candidate_id;
904 let Some(project) = project else {
905 return;
906 };
907
908 let resolve_task = project.update(cx, |project, cx| {
909 project.resolve_completions(
910 self.buffer.clone(),
911 vec![completion_index],
912 self.completions.clone(),
913 cx,
914 )
915 });
916
917 let delay_ms =
918 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
919 let delay = Duration::from_millis(delay_ms);
920
921 self.selected_completion_documentation_resolve_debounce
922 .lock()
923 .fire_new(delay, cx, |_, cx| {
924 cx.spawn(move |this, mut cx| async move {
925 if let Some(true) = resolve_task.await.log_err() {
926 this.update(&mut cx, |_, cx| cx.notify()).ok();
927 }
928 })
929 });
930 }
931
932 fn visible(&self) -> bool {
933 !self.matches.is_empty()
934 }
935
936 fn render(
937 &self,
938 style: &EditorStyle,
939 max_height: Pixels,
940 workspace: Option<WeakView<Workspace>>,
941 cx: &mut ViewContext<Editor>,
942 ) -> AnyElement {
943 let settings = EditorSettings::get_global(cx);
944 let show_completion_documentation = settings.show_completion_documentation;
945
946 let widest_completion_ix = self
947 .matches
948 .iter()
949 .enumerate()
950 .max_by_key(|(_, mat)| {
951 let completions = self.completions.read();
952 let completion = &completions[mat.candidate_id];
953 let documentation = &completion.documentation;
954
955 let mut len = completion.label.text.chars().count();
956 if let Some(Documentation::SingleLine(text)) = documentation {
957 if show_completion_documentation {
958 len += text.chars().count();
959 }
960 }
961
962 len
963 })
964 .map(|(ix, _)| ix);
965
966 let completions = self.completions.clone();
967 let matches = self.matches.clone();
968 let selected_item = self.selected_item;
969 let style = style.clone();
970
971 let multiline_docs = if show_completion_documentation {
972 let mat = &self.matches[selected_item];
973 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
974 Some(Documentation::MultiLinePlainText(text)) => {
975 Some(div().child(SharedString::from(text.clone())))
976 }
977 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
978 Some(div().child(render_parsed_markdown(
979 "completions_markdown",
980 parsed,
981 &style,
982 workspace,
983 cx,
984 )))
985 }
986 _ => None,
987 };
988 multiline_docs.map(|div| {
989 div.id("multiline_docs")
990 .max_h(max_height)
991 .flex_1()
992 .px_1p5()
993 .py_1()
994 .min_w(px(260.))
995 .max_w(px(640.))
996 .w(px(500.))
997 .overflow_y_scroll()
998 .occlude()
999 })
1000 } else {
1001 None
1002 };
1003
1004 let list = uniform_list(
1005 cx.view().clone(),
1006 "completions",
1007 matches.len(),
1008 move |_editor, range, cx| {
1009 let start_ix = range.start;
1010 let completions_guard = completions.read();
1011
1012 matches[range]
1013 .iter()
1014 .enumerate()
1015 .map(|(ix, mat)| {
1016 let item_ix = start_ix + ix;
1017 let candidate_id = mat.candidate_id;
1018 let completion = &completions_guard[candidate_id];
1019
1020 let documentation = if show_completion_documentation {
1021 &completion.documentation
1022 } else {
1023 &None
1024 };
1025
1026 let highlights = gpui::combine_highlights(
1027 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1028 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1029 |(range, mut highlight)| {
1030 // Ignore font weight for syntax highlighting, as we'll use it
1031 // for fuzzy matches.
1032 highlight.font_weight = None;
1033
1034 if completion.lsp_completion.deprecated.unwrap_or(false) {
1035 highlight.strikethrough = Some(StrikethroughStyle {
1036 thickness: 1.0.into(),
1037 ..Default::default()
1038 });
1039 highlight.color = Some(cx.theme().colors().text_muted);
1040 }
1041
1042 (range, highlight)
1043 },
1044 ),
1045 );
1046 let completion_label = StyledText::new(completion.label.text.clone())
1047 .with_highlights(&style.text, highlights);
1048 let documentation_label =
1049 if let Some(Documentation::SingleLine(text)) = documentation {
1050 if text.trim().is_empty() {
1051 None
1052 } else {
1053 Some(
1054 h_flex().ml_4().child(
1055 Label::new(text.clone())
1056 .size(LabelSize::Small)
1057 .color(Color::Muted),
1058 ),
1059 )
1060 }
1061 } else {
1062 None
1063 };
1064
1065 div().min_w(px(220.)).max_w(px(540.)).child(
1066 ListItem::new(mat.candidate_id)
1067 .inset(true)
1068 .selected(item_ix == selected_item)
1069 .on_click(cx.listener(move |editor, _event, cx| {
1070 cx.stop_propagation();
1071 if let Some(task) = editor.confirm_completion(
1072 &ConfirmCompletion {
1073 item_ix: Some(item_ix),
1074 },
1075 cx,
1076 ) {
1077 task.detach_and_log_err(cx)
1078 }
1079 }))
1080 .child(h_flex().overflow_hidden().child(completion_label))
1081 .end_slot::<Div>(documentation_label),
1082 )
1083 })
1084 .collect()
1085 },
1086 )
1087 .occlude()
1088 .max_h(max_height)
1089 .track_scroll(self.scroll_handle.clone())
1090 .with_width_from_item(widest_completion_ix);
1091
1092 Popover::new()
1093 .child(list)
1094 .when_some(multiline_docs, |popover, multiline_docs| {
1095 popover.aside(multiline_docs)
1096 })
1097 .into_any_element()
1098 }
1099
1100 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1101 let mut matches = if let Some(query) = query {
1102 fuzzy::match_strings(
1103 &self.match_candidates,
1104 query,
1105 query.chars().any(|c| c.is_uppercase()),
1106 100,
1107 &Default::default(),
1108 executor,
1109 )
1110 .await
1111 } else {
1112 self.match_candidates
1113 .iter()
1114 .enumerate()
1115 .map(|(candidate_id, candidate)| StringMatch {
1116 candidate_id,
1117 score: Default::default(),
1118 positions: Default::default(),
1119 string: candidate.string.clone(),
1120 })
1121 .collect()
1122 };
1123
1124 // Remove all candidates where the query's start does not match the start of any word in the candidate
1125 if let Some(query) = query {
1126 if let Some(query_start) = query.chars().next() {
1127 matches.retain(|string_match| {
1128 split_words(&string_match.string).any(|word| {
1129 // Check that the first codepoint of the word as lowercase matches the first
1130 // codepoint of the query as lowercase
1131 word.chars()
1132 .flat_map(|codepoint| codepoint.to_lowercase())
1133 .zip(query_start.to_lowercase())
1134 .all(|(word_cp, query_cp)| word_cp == query_cp)
1135 })
1136 });
1137 }
1138 }
1139
1140 let completions = self.completions.read();
1141 matches.sort_unstable_by_key(|mat| {
1142 // We do want to strike a balance here between what the language server tells us
1143 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1144 // `Creat` and there is a local variable called `CreateComponent`).
1145 // So what we do is: we bucket all matches into two buckets
1146 // - Strong matches
1147 // - Weak matches
1148 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1149 // and the Weak matches are the rest.
1150 //
1151 // For the strong matches, we sort by the language-servers score first and for the weak
1152 // matches, we prefer our fuzzy finder first.
1153 //
1154 // The thinking behind that: it's useless to take the sort_text the language-server gives
1155 // us into account when it's obviously a bad match.
1156
1157 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1158 enum MatchScore<'a> {
1159 Strong {
1160 sort_text: Option<&'a str>,
1161 score: Reverse<OrderedFloat<f64>>,
1162 sort_key: (usize, &'a str),
1163 },
1164 Weak {
1165 score: Reverse<OrderedFloat<f64>>,
1166 sort_text: Option<&'a str>,
1167 sort_key: (usize, &'a str),
1168 },
1169 }
1170
1171 let completion = &completions[mat.candidate_id];
1172 let sort_key = completion.sort_key();
1173 let sort_text = completion.lsp_completion.sort_text.as_deref();
1174 let score = Reverse(OrderedFloat(mat.score));
1175
1176 if mat.score >= 0.2 {
1177 MatchScore::Strong {
1178 sort_text,
1179 score,
1180 sort_key,
1181 }
1182 } else {
1183 MatchScore::Weak {
1184 score,
1185 sort_text,
1186 sort_key,
1187 }
1188 }
1189 });
1190
1191 for mat in &mut matches {
1192 let completion = &completions[mat.candidate_id];
1193 mat.string.clone_from(&completion.label.text);
1194 for position in &mut mat.positions {
1195 *position += completion.label.filter_range.start;
1196 }
1197 }
1198 drop(completions);
1199
1200 self.matches = matches.into();
1201 self.selected_item = 0;
1202 }
1203}
1204
1205#[derive(Clone)]
1206struct CodeActionContents {
1207 tasks: Option<Arc<ResolvedTasks>>,
1208 actions: Option<Arc<[CodeAction]>>,
1209}
1210
1211impl CodeActionContents {
1212 fn len(&self) -> usize {
1213 match (&self.tasks, &self.actions) {
1214 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1215 (Some(tasks), None) => tasks.templates.len(),
1216 (None, Some(actions)) => actions.len(),
1217 (None, None) => 0,
1218 }
1219 }
1220
1221 fn is_empty(&self) -> bool {
1222 match (&self.tasks, &self.actions) {
1223 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1224 (Some(tasks), None) => tasks.templates.is_empty(),
1225 (None, Some(actions)) => actions.is_empty(),
1226 (None, None) => true,
1227 }
1228 }
1229
1230 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1231 self.tasks
1232 .iter()
1233 .flat_map(|tasks| {
1234 tasks
1235 .templates
1236 .iter()
1237 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1238 })
1239 .chain(self.actions.iter().flat_map(|actions| {
1240 actions
1241 .iter()
1242 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1243 }))
1244 }
1245 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1246 match (&self.tasks, &self.actions) {
1247 (Some(tasks), Some(actions)) => {
1248 if index < tasks.templates.len() {
1249 tasks
1250 .templates
1251 .get(index)
1252 .cloned()
1253 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1254 } else {
1255 actions
1256 .get(index - tasks.templates.len())
1257 .cloned()
1258 .map(CodeActionsItem::CodeAction)
1259 }
1260 }
1261 (Some(tasks), None) => tasks
1262 .templates
1263 .get(index)
1264 .cloned()
1265 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1266 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1267 (None, None) => None,
1268 }
1269 }
1270}
1271
1272#[allow(clippy::large_enum_variant)]
1273#[derive(Clone)]
1274enum CodeActionsItem {
1275 Task(TaskSourceKind, ResolvedTask),
1276 CodeAction(CodeAction),
1277}
1278
1279impl CodeActionsItem {
1280 fn as_task(&self) -> Option<&ResolvedTask> {
1281 let Self::Task(_, task) = self else {
1282 return None;
1283 };
1284 Some(task)
1285 }
1286 fn as_code_action(&self) -> Option<&CodeAction> {
1287 let Self::CodeAction(action) = self else {
1288 return None;
1289 };
1290 Some(action)
1291 }
1292 fn label(&self) -> String {
1293 match self {
1294 Self::CodeAction(action) => action.lsp_action.title.clone(),
1295 Self::Task(_, task) => task.resolved_label.clone(),
1296 }
1297 }
1298}
1299
1300struct CodeActionsMenu {
1301 actions: CodeActionContents,
1302 buffer: Model<Buffer>,
1303 selected_item: usize,
1304 scroll_handle: UniformListScrollHandle,
1305 deployed_from_indicator: Option<DisplayRow>,
1306}
1307
1308impl CodeActionsMenu {
1309 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1310 self.selected_item = 0;
1311 self.scroll_handle.scroll_to_item(self.selected_item);
1312 cx.notify()
1313 }
1314
1315 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1316 if self.selected_item > 0 {
1317 self.selected_item -= 1;
1318 } else {
1319 self.selected_item = self.actions.len() - 1;
1320 }
1321 self.scroll_handle.scroll_to_item(self.selected_item);
1322 cx.notify();
1323 }
1324
1325 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1326 if self.selected_item + 1 < self.actions.len() {
1327 self.selected_item += 1;
1328 } else {
1329 self.selected_item = 0;
1330 }
1331 self.scroll_handle.scroll_to_item(self.selected_item);
1332 cx.notify();
1333 }
1334
1335 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1336 self.selected_item = self.actions.len() - 1;
1337 self.scroll_handle.scroll_to_item(self.selected_item);
1338 cx.notify()
1339 }
1340
1341 fn visible(&self) -> bool {
1342 !self.actions.is_empty()
1343 }
1344
1345 fn render(
1346 &self,
1347 cursor_position: DisplayPoint,
1348 _style: &EditorStyle,
1349 max_height: Pixels,
1350 cx: &mut ViewContext<Editor>,
1351 ) -> (ContextMenuOrigin, AnyElement) {
1352 let actions = self.actions.clone();
1353 let selected_item = self.selected_item;
1354 let element = uniform_list(
1355 cx.view().clone(),
1356 "code_actions_menu",
1357 self.actions.len(),
1358 move |_this, range, cx| {
1359 actions
1360 .iter()
1361 .skip(range.start)
1362 .take(range.end - range.start)
1363 .enumerate()
1364 .map(|(ix, action)| {
1365 let item_ix = range.start + ix;
1366 let selected = selected_item == item_ix;
1367 let colors = cx.theme().colors();
1368 div()
1369 .px_2()
1370 .text_color(colors.text)
1371 .when(selected, |style| {
1372 style
1373 .bg(colors.element_active)
1374 .text_color(colors.text_accent)
1375 })
1376 .hover(|style| {
1377 style
1378 .bg(colors.element_hover)
1379 .text_color(colors.text_accent)
1380 })
1381 .whitespace_nowrap()
1382 .when_some(action.as_code_action(), |this, action| {
1383 this.on_mouse_down(
1384 MouseButton::Left,
1385 cx.listener(move |editor, _, cx| {
1386 cx.stop_propagation();
1387 if let Some(task) = editor.confirm_code_action(
1388 &ConfirmCodeAction {
1389 item_ix: Some(item_ix),
1390 },
1391 cx,
1392 ) {
1393 task.detach_and_log_err(cx)
1394 }
1395 }),
1396 )
1397 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1398 .child(SharedString::from(action.lsp_action.title.clone()))
1399 })
1400 .when_some(action.as_task(), |this, task| {
1401 this.on_mouse_down(
1402 MouseButton::Left,
1403 cx.listener(move |editor, _, cx| {
1404 cx.stop_propagation();
1405 if let Some(task) = editor.confirm_code_action(
1406 &ConfirmCodeAction {
1407 item_ix: Some(item_ix),
1408 },
1409 cx,
1410 ) {
1411 task.detach_and_log_err(cx)
1412 }
1413 }),
1414 )
1415 .child(SharedString::from(task.resolved_label.clone()))
1416 })
1417 })
1418 .collect()
1419 },
1420 )
1421 .elevation_1(cx)
1422 .px_2()
1423 .py_1()
1424 .max_h(max_height)
1425 .occlude()
1426 .track_scroll(self.scroll_handle.clone())
1427 .with_width_from_item(
1428 self.actions
1429 .iter()
1430 .enumerate()
1431 .max_by_key(|(_, action)| match action {
1432 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1433 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1434 })
1435 .map(|(ix, _)| ix),
1436 )
1437 .into_any_element();
1438
1439 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1440 ContextMenuOrigin::GutterIndicator(row)
1441 } else {
1442 ContextMenuOrigin::EditorPoint(cursor_position)
1443 };
1444
1445 (cursor_position, element)
1446 }
1447}
1448
1449#[derive(Debug)]
1450struct ActiveDiagnosticGroup {
1451 primary_range: Range<Anchor>,
1452 primary_message: String,
1453 group_id: usize,
1454 blocks: HashMap<BlockId, Diagnostic>,
1455 is_valid: bool,
1456}
1457
1458#[derive(Serialize, Deserialize)]
1459pub struct ClipboardSelection {
1460 pub len: usize,
1461 pub is_entire_line: bool,
1462 pub first_line_indent: u32,
1463}
1464
1465#[derive(Debug)]
1466pub(crate) struct NavigationData {
1467 cursor_anchor: Anchor,
1468 cursor_position: Point,
1469 scroll_anchor: ScrollAnchor,
1470 scroll_top_row: u32,
1471}
1472
1473enum GotoDefinitionKind {
1474 Symbol,
1475 Type,
1476 Implementation,
1477}
1478
1479#[derive(Debug, Clone)]
1480enum InlayHintRefreshReason {
1481 Toggle(bool),
1482 SettingsChange(InlayHintSettings),
1483 NewLinesShown,
1484 BufferEdited(HashSet<Arc<Language>>),
1485 RefreshRequested,
1486 ExcerptsRemoved(Vec<ExcerptId>),
1487}
1488
1489impl InlayHintRefreshReason {
1490 fn description(&self) -> &'static str {
1491 match self {
1492 Self::Toggle(_) => "toggle",
1493 Self::SettingsChange(_) => "settings change",
1494 Self::NewLinesShown => "new lines shown",
1495 Self::BufferEdited(_) => "buffer edited",
1496 Self::RefreshRequested => "refresh requested",
1497 Self::ExcerptsRemoved(_) => "excerpts removed",
1498 }
1499 }
1500}
1501
1502impl Editor {
1503 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1504 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1505 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1506 Self::new(EditorMode::SingleLine, buffer, None, cx)
1507 }
1508
1509 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1510 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1511 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1512 Self::new(EditorMode::Full, buffer, None, cx)
1513 }
1514
1515 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1516 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1517 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1518 Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx)
1519 }
1520
1521 pub fn for_buffer(
1522 buffer: Model<Buffer>,
1523 project: Option<Model<Project>>,
1524 cx: &mut ViewContext<Self>,
1525 ) -> Self {
1526 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1527 Self::new(EditorMode::Full, buffer, project, cx)
1528 }
1529
1530 pub fn for_multibuffer(
1531 buffer: Model<MultiBuffer>,
1532 project: Option<Model<Project>>,
1533 cx: &mut ViewContext<Self>,
1534 ) -> Self {
1535 Self::new(EditorMode::Full, buffer, project, cx)
1536 }
1537
1538 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1539 let mut clone = Self::new(self.mode, self.buffer.clone(), self.project.clone(), cx);
1540 self.display_map.update(cx, |display_map, cx| {
1541 let snapshot = display_map.snapshot(cx);
1542 clone.display_map.update(cx, |display_map, cx| {
1543 display_map.set_state(&snapshot, cx);
1544 });
1545 });
1546 clone.selections.clone_state(&self.selections);
1547 clone.scroll_manager.clone_state(&self.scroll_manager);
1548 clone.searchable = self.searchable;
1549 clone
1550 }
1551
1552 fn new(
1553 mode: EditorMode,
1554 buffer: Model<MultiBuffer>,
1555 project: Option<Model<Project>>,
1556 cx: &mut ViewContext<Self>,
1557 ) -> Self {
1558 let style = cx.text_style();
1559 let font_size = style.font_size.to_pixels(cx.rem_size());
1560 let display_map = cx.new_model(|cx| {
1561 DisplayMap::new(buffer.clone(), style.font(), font_size, None, 2, 1, cx)
1562 });
1563
1564 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1565
1566 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1567
1568 let soft_wrap_mode_override =
1569 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::PreferLine);
1570
1571 let mut project_subscriptions = Vec::new();
1572 if mode == EditorMode::Full {
1573 if let Some(project) = project.as_ref() {
1574 if buffer.read(cx).is_singleton() {
1575 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1576 cx.emit(EditorEvent::TitleChanged);
1577 }));
1578 }
1579 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1580 if let project::Event::RefreshInlayHints = event {
1581 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1582 };
1583 }));
1584 }
1585 }
1586
1587 let inlay_hint_settings = inlay_hint_settings(
1588 selections.newest_anchor().head(),
1589 &buffer.read(cx).snapshot(cx),
1590 cx,
1591 );
1592
1593 let focus_handle = cx.focus_handle();
1594 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1595 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1596
1597 let mut this = Self {
1598 focus_handle,
1599 buffer: buffer.clone(),
1600 display_map: display_map.clone(),
1601 selections,
1602 scroll_manager: ScrollManager::new(cx),
1603 columnar_selection_tail: None,
1604 add_selections_state: None,
1605 select_next_state: None,
1606 select_prev_state: None,
1607 selection_history: Default::default(),
1608 autoclose_regions: Default::default(),
1609 snippet_stack: Default::default(),
1610 select_larger_syntax_node_stack: Vec::new(),
1611 ime_transaction: Default::default(),
1612 active_diagnostics: None,
1613 soft_wrap_mode_override,
1614 completion_provider: project.clone().map(|project| Box::new(project) as _),
1615 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1616 project,
1617 blink_manager: blink_manager.clone(),
1618 show_local_selections: true,
1619 mode,
1620 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1621 show_gutter: mode == EditorMode::Full,
1622 show_wrap_guides: None,
1623 placeholder_text: None,
1624 highlight_order: 0,
1625 highlighted_rows: HashMap::default(),
1626 background_highlights: Default::default(),
1627 scrollbar_marker_state: ScrollbarMarkerState::default(),
1628 nav_history: None,
1629 context_menu: RwLock::new(None),
1630 mouse_context_menu: None,
1631 completion_tasks: Default::default(),
1632 find_all_references_task_sources: Vec::new(),
1633 next_completion_id: 0,
1634 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1635 next_inlay_id: 0,
1636 available_code_actions: Default::default(),
1637 code_actions_task: Default::default(),
1638 document_highlights_task: Default::default(),
1639 pending_rename: Default::default(),
1640 searchable: true,
1641 cursor_shape: Default::default(),
1642 current_line_highlight: EditorSettings::get_global(cx).current_line_highlight,
1643 autoindent_mode: Some(AutoindentMode::EachLine),
1644 collapse_matches: false,
1645 workspace: None,
1646 keymap_context_layers: Default::default(),
1647 input_enabled: true,
1648 use_modal_editing: mode == EditorMode::Full,
1649 read_only: false,
1650 use_autoclose: true,
1651 auto_replace_emoji_shortcode: false,
1652 leader_peer_id: None,
1653 remote_id: None,
1654 hover_state: Default::default(),
1655 hovered_link_state: Default::default(),
1656 inline_completion_provider: None,
1657 active_inline_completion: None,
1658 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1659 expanded_hunks: ExpandedHunks::default(),
1660 gutter_hovered: false,
1661 pixel_position_of_newest_cursor: None,
1662 last_bounds: None,
1663 expect_bounds_change: None,
1664 gutter_dimensions: GutterDimensions::default(),
1665 style: None,
1666 show_cursor_names: false,
1667 hovered_cursors: Default::default(),
1668 editor_actions: Default::default(),
1669 vim_replace_map: Default::default(),
1670 show_inline_completions: mode == EditorMode::Full,
1671 custom_context_menu: None,
1672 show_git_blame_gutter: false,
1673 show_git_blame_inline: false,
1674 show_git_blame_inline_delay_task: None,
1675 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1676 blame: None,
1677 blame_subscription: None,
1678 tasks: Default::default(),
1679 _subscriptions: vec![
1680 cx.observe(&buffer, Self::on_buffer_changed),
1681 cx.subscribe(&buffer, Self::on_buffer_event),
1682 cx.observe(&display_map, Self::on_display_map_changed),
1683 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1684 cx.observe_global::<SettingsStore>(Self::settings_changed),
1685 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1686 cx.observe_window_activation(|editor, cx| {
1687 let active = cx.is_window_active();
1688 editor.blink_manager.update(cx, |blink_manager, cx| {
1689 if active {
1690 blink_manager.enable(cx);
1691 } else {
1692 blink_manager.show_cursor(cx);
1693 blink_manager.disable(cx);
1694 }
1695 });
1696 }),
1697 ],
1698 tasks_update_task: None,
1699 };
1700 this.tasks_update_task = Some(this.refresh_runnables(cx));
1701 this._subscriptions.extend(project_subscriptions);
1702
1703 this.end_selection(cx);
1704 this.scroll_manager.show_scrollbar(cx);
1705
1706 if mode == EditorMode::Full {
1707 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1708 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1709
1710 if this.git_blame_inline_enabled {
1711 this.git_blame_inline_enabled = true;
1712 this.start_git_blame_inline(false, cx);
1713 }
1714 }
1715
1716 this.report_editor_event("open", None, cx);
1717 this
1718 }
1719
1720 fn key_context(&self, cx: &AppContext) -> KeyContext {
1721 let mut key_context = KeyContext::new_with_defaults();
1722 key_context.add("Editor");
1723 let mode = match self.mode {
1724 EditorMode::SingleLine => "single_line",
1725 EditorMode::AutoHeight { .. } => "auto_height",
1726 EditorMode::Full => "full",
1727 };
1728 key_context.set("mode", mode);
1729 if self.pending_rename.is_some() {
1730 key_context.add("renaming");
1731 }
1732 if self.context_menu_visible() {
1733 match self.context_menu.read().as_ref() {
1734 Some(ContextMenu::Completions(_)) => {
1735 key_context.add("menu");
1736 key_context.add("showing_completions")
1737 }
1738 Some(ContextMenu::CodeActions(_)) => {
1739 key_context.add("menu");
1740 key_context.add("showing_code_actions")
1741 }
1742 None => {}
1743 }
1744 }
1745
1746 for layer in self.keymap_context_layers.values() {
1747 key_context.extend(layer);
1748 }
1749
1750 if let Some(extension) = self
1751 .buffer
1752 .read(cx)
1753 .as_singleton()
1754 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1755 {
1756 key_context.set("extension", extension.to_string());
1757 }
1758
1759 if self.has_active_inline_completion(cx) {
1760 key_context.add("copilot_suggestion");
1761 key_context.add("inline_completion");
1762 }
1763
1764 key_context
1765 }
1766
1767 pub fn new_file(
1768 workspace: &mut Workspace,
1769 _: &workspace::NewFile,
1770 cx: &mut ViewContext<Workspace>,
1771 ) {
1772 let project = workspace.project().clone();
1773 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1774
1775 cx.spawn(|workspace, mut cx| async move {
1776 let buffer = create.await?;
1777 workspace.update(&mut cx, |workspace, cx| {
1778 workspace.add_item_to_active_pane(
1779 Box::new(
1780 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1781 ),
1782 None,
1783 cx,
1784 )
1785 })
1786 })
1787 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1788 ErrorCode::RemoteUpgradeRequired => Some(format!(
1789 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1790 e.error_tag("required").unwrap_or("the latest version")
1791 )),
1792 _ => None,
1793 });
1794 }
1795
1796 pub fn new_file_in_direction(
1797 workspace: &mut Workspace,
1798 action: &workspace::NewFileInDirection,
1799 cx: &mut ViewContext<Workspace>,
1800 ) {
1801 let project = workspace.project().clone();
1802 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1803 let direction = action.0;
1804
1805 cx.spawn(|workspace, mut cx| async move {
1806 let buffer = create.await?;
1807 workspace.update(&mut cx, move |workspace, cx| {
1808 workspace.split_item(
1809 direction,
1810 Box::new(
1811 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1812 ),
1813 cx,
1814 )
1815 })?;
1816 anyhow::Ok(())
1817 })
1818 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1819 ErrorCode::RemoteUpgradeRequired => Some(format!(
1820 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1821 e.error_tag("required").unwrap_or("the latest version")
1822 )),
1823 _ => None,
1824 });
1825 }
1826
1827 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1828 self.buffer.read(cx).replica_id()
1829 }
1830
1831 pub fn leader_peer_id(&self) -> Option<PeerId> {
1832 self.leader_peer_id
1833 }
1834
1835 pub fn buffer(&self) -> &Model<MultiBuffer> {
1836 &self.buffer
1837 }
1838
1839 pub fn workspace(&self) -> Option<View<Workspace>> {
1840 self.workspace.as_ref()?.0.upgrade()
1841 }
1842
1843 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1844 self.buffer().read(cx).title(cx)
1845 }
1846
1847 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1848 EditorSnapshot {
1849 mode: self.mode,
1850 show_gutter: self.show_gutter,
1851 render_git_blame_gutter: self.render_git_blame_gutter(cx),
1852 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1853 scroll_anchor: self.scroll_manager.anchor(),
1854 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1855 placeholder_text: self.placeholder_text.clone(),
1856 is_focused: self.focus_handle.is_focused(cx),
1857 current_line_highlight: self.current_line_highlight,
1858 }
1859 }
1860
1861 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1862 self.buffer.read(cx).language_at(point, cx)
1863 }
1864
1865 pub fn file_at<T: ToOffset>(
1866 &self,
1867 point: T,
1868 cx: &AppContext,
1869 ) -> Option<Arc<dyn language::File>> {
1870 self.buffer.read(cx).read(cx).file_at(point).cloned()
1871 }
1872
1873 pub fn active_excerpt(
1874 &self,
1875 cx: &AppContext,
1876 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1877 self.buffer
1878 .read(cx)
1879 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1880 }
1881
1882 pub fn mode(&self) -> EditorMode {
1883 self.mode
1884 }
1885
1886 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1887 self.collaboration_hub.as_deref()
1888 }
1889
1890 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1891 self.collaboration_hub = Some(hub);
1892 }
1893
1894 pub fn set_custom_context_menu(
1895 &mut self,
1896 f: impl 'static
1897 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1898 ) {
1899 self.custom_context_menu = Some(Box::new(f))
1900 }
1901
1902 pub fn set_completion_provider(&mut self, hub: Box<dyn CompletionProvider>) {
1903 self.completion_provider = Some(hub);
1904 }
1905
1906 pub fn set_inline_completion_provider<T>(
1907 &mut self,
1908 provider: Option<Model<T>>,
1909 cx: &mut ViewContext<Self>,
1910 ) where
1911 T: InlineCompletionProvider,
1912 {
1913 self.inline_completion_provider =
1914 provider.map(|provider| RegisteredInlineCompletionProvider {
1915 _subscription: cx.observe(&provider, |this, _, cx| {
1916 if this.focus_handle.is_focused(cx) {
1917 this.update_visible_inline_completion(cx);
1918 }
1919 }),
1920 provider: Arc::new(provider),
1921 });
1922 self.refresh_inline_completion(false, cx);
1923 }
1924
1925 pub fn placeholder_text(&self, _cx: &mut WindowContext) -> Option<&str> {
1926 self.placeholder_text.as_deref()
1927 }
1928
1929 pub fn set_placeholder_text(
1930 &mut self,
1931 placeholder_text: impl Into<Arc<str>>,
1932 cx: &mut ViewContext<Self>,
1933 ) {
1934 let placeholder_text = Some(placeholder_text.into());
1935 if self.placeholder_text != placeholder_text {
1936 self.placeholder_text = placeholder_text;
1937 cx.notify();
1938 }
1939 }
1940
1941 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1942 self.cursor_shape = cursor_shape;
1943 cx.notify();
1944 }
1945
1946 pub fn set_current_line_highlight(&mut self, current_line_highlight: CurrentLineHighlight) {
1947 self.current_line_highlight = current_line_highlight;
1948 }
1949
1950 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1951 self.collapse_matches = collapse_matches;
1952 }
1953
1954 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1955 if self.collapse_matches {
1956 return range.start..range.start;
1957 }
1958 range.clone()
1959 }
1960
1961 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1962 if self.display_map.read(cx).clip_at_line_ends != clip {
1963 self.display_map
1964 .update(cx, |map, _| map.clip_at_line_ends = clip);
1965 }
1966 }
1967
1968 pub fn set_keymap_context_layer<Tag: 'static>(
1969 &mut self,
1970 context: KeyContext,
1971 cx: &mut ViewContext<Self>,
1972 ) {
1973 self.keymap_context_layers
1974 .insert(TypeId::of::<Tag>(), context);
1975 cx.notify();
1976 }
1977
1978 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1979 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1980 cx.notify();
1981 }
1982
1983 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1984 self.input_enabled = input_enabled;
1985 }
1986
1987 pub fn set_autoindent(&mut self, autoindent: bool) {
1988 if autoindent {
1989 self.autoindent_mode = Some(AutoindentMode::EachLine);
1990 } else {
1991 self.autoindent_mode = None;
1992 }
1993 }
1994
1995 pub fn read_only(&self, cx: &AppContext) -> bool {
1996 self.read_only || self.buffer.read(cx).read_only()
1997 }
1998
1999 pub fn set_read_only(&mut self, read_only: bool) {
2000 self.read_only = read_only;
2001 }
2002
2003 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2004 self.use_autoclose = autoclose;
2005 }
2006
2007 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2008 self.auto_replace_emoji_shortcode = auto_replace;
2009 }
2010
2011 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2012 self.show_inline_completions = show_inline_completions;
2013 }
2014
2015 pub fn set_use_modal_editing(&mut self, to: bool) {
2016 self.use_modal_editing = to;
2017 }
2018
2019 pub fn use_modal_editing(&self) -> bool {
2020 self.use_modal_editing
2021 }
2022
2023 fn selections_did_change(
2024 &mut self,
2025 local: bool,
2026 old_cursor_position: &Anchor,
2027 cx: &mut ViewContext<Self>,
2028 ) {
2029 // Copy selections to primary selection buffer
2030 #[cfg(target_os = "linux")]
2031 if local {
2032 let selections = self.selections.all::<usize>(cx);
2033 let buffer_handle = self.buffer.read(cx).read(cx);
2034
2035 let mut text = String::new();
2036 for (index, selection) in selections.iter().enumerate() {
2037 let text_for_selection = buffer_handle
2038 .text_for_range(selection.start..selection.end)
2039 .collect::<String>();
2040
2041 text.push_str(&text_for_selection);
2042 if index != selections.len() - 1 {
2043 text.push('\n');
2044 }
2045 }
2046
2047 if !text.is_empty() {
2048 cx.write_to_primary(ClipboardItem::new(text));
2049 }
2050 }
2051
2052 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2053 self.buffer.update(cx, |buffer, cx| {
2054 buffer.set_active_selections(
2055 &self.selections.disjoint_anchors(),
2056 self.selections.line_mode,
2057 self.cursor_shape,
2058 cx,
2059 )
2060 });
2061 }
2062
2063 let display_map = self
2064 .display_map
2065 .update(cx, |display_map, cx| display_map.snapshot(cx));
2066 let buffer = &display_map.buffer_snapshot;
2067 self.add_selections_state = None;
2068 self.select_next_state = None;
2069 self.select_prev_state = None;
2070 self.select_larger_syntax_node_stack.clear();
2071 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2072 self.snippet_stack
2073 .invalidate(&self.selections.disjoint_anchors(), buffer);
2074 self.take_rename(false, cx);
2075
2076 let new_cursor_position = self.selections.newest_anchor().head();
2077
2078 self.push_to_nav_history(
2079 *old_cursor_position,
2080 Some(new_cursor_position.to_point(buffer)),
2081 cx,
2082 );
2083
2084 if local {
2085 let new_cursor_position = self.selections.newest_anchor().head();
2086 let mut context_menu = self.context_menu.write();
2087 let completion_menu = match context_menu.as_ref() {
2088 Some(ContextMenu::Completions(menu)) => Some(menu),
2089
2090 _ => {
2091 *context_menu = None;
2092 None
2093 }
2094 };
2095
2096 if let Some(completion_menu) = completion_menu {
2097 let cursor_position = new_cursor_position.to_offset(buffer);
2098 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2099 if kind == Some(CharKind::Word)
2100 && word_range.to_inclusive().contains(&cursor_position)
2101 {
2102 let mut completion_menu = completion_menu.clone();
2103 drop(context_menu);
2104
2105 let query = Self::completion_query(buffer, cursor_position);
2106 cx.spawn(move |this, mut cx| async move {
2107 completion_menu
2108 .filter(query.as_deref(), cx.background_executor().clone())
2109 .await;
2110
2111 this.update(&mut cx, |this, cx| {
2112 let mut context_menu = this.context_menu.write();
2113 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2114 return;
2115 };
2116
2117 if menu.id > completion_menu.id {
2118 return;
2119 }
2120
2121 *context_menu = Some(ContextMenu::Completions(completion_menu));
2122 drop(context_menu);
2123 cx.notify();
2124 })
2125 })
2126 .detach();
2127
2128 self.show_completions(&ShowCompletions, cx);
2129 } else {
2130 drop(context_menu);
2131 self.hide_context_menu(cx);
2132 }
2133 } else {
2134 drop(context_menu);
2135 }
2136
2137 hide_hover(self, cx);
2138
2139 if old_cursor_position.to_display_point(&display_map).row()
2140 != new_cursor_position.to_display_point(&display_map).row()
2141 {
2142 self.available_code_actions.take();
2143 }
2144 self.refresh_code_actions(cx);
2145 self.refresh_document_highlights(cx);
2146 refresh_matching_bracket_highlights(self, cx);
2147 self.discard_inline_completion(cx);
2148 if self.git_blame_inline_enabled {
2149 self.start_inline_blame_timer(cx);
2150 }
2151 }
2152
2153 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2154 cx.emit(EditorEvent::SelectionsChanged { local });
2155
2156 if self.selections.disjoint_anchors().len() == 1 {
2157 cx.emit(SearchEvent::ActiveMatchChanged)
2158 }
2159
2160 cx.notify();
2161 }
2162
2163 pub fn change_selections<R>(
2164 &mut self,
2165 autoscroll: Option<Autoscroll>,
2166 cx: &mut ViewContext<Self>,
2167 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2168 ) -> R {
2169 let old_cursor_position = self.selections.newest_anchor().head();
2170 self.push_to_selection_history();
2171
2172 let (changed, result) = self.selections.change_with(cx, change);
2173
2174 if changed {
2175 if let Some(autoscroll) = autoscroll {
2176 self.request_autoscroll(autoscroll, cx);
2177 }
2178 self.selections_did_change(true, &old_cursor_position, cx);
2179 }
2180
2181 result
2182 }
2183
2184 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2185 where
2186 I: IntoIterator<Item = (Range<S>, T)>,
2187 S: ToOffset,
2188 T: Into<Arc<str>>,
2189 {
2190 if self.read_only(cx) {
2191 return;
2192 }
2193
2194 self.buffer
2195 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2196 }
2197
2198 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2199 where
2200 I: IntoIterator<Item = (Range<S>, T)>,
2201 S: ToOffset,
2202 T: Into<Arc<str>>,
2203 {
2204 if self.read_only(cx) {
2205 return;
2206 }
2207
2208 self.buffer.update(cx, |buffer, cx| {
2209 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2210 });
2211 }
2212
2213 pub fn edit_with_block_indent<I, S, T>(
2214 &mut self,
2215 edits: I,
2216 original_indent_columns: Vec<u32>,
2217 cx: &mut ViewContext<Self>,
2218 ) where
2219 I: IntoIterator<Item = (Range<S>, T)>,
2220 S: ToOffset,
2221 T: Into<Arc<str>>,
2222 {
2223 if self.read_only(cx) {
2224 return;
2225 }
2226
2227 self.buffer.update(cx, |buffer, cx| {
2228 buffer.edit(
2229 edits,
2230 Some(AutoindentMode::Block {
2231 original_indent_columns,
2232 }),
2233 cx,
2234 )
2235 });
2236 }
2237
2238 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2239 self.hide_context_menu(cx);
2240
2241 match phase {
2242 SelectPhase::Begin {
2243 position,
2244 add,
2245 click_count,
2246 } => self.begin_selection(position, add, click_count, cx),
2247 SelectPhase::BeginColumnar {
2248 position,
2249 goal_column,
2250 } => self.begin_columnar_selection(position, goal_column, cx),
2251 SelectPhase::Extend {
2252 position,
2253 click_count,
2254 } => self.extend_selection(position, click_count, cx),
2255 SelectPhase::Update {
2256 position,
2257 goal_column,
2258 scroll_delta,
2259 } => self.update_selection(position, goal_column, scroll_delta, cx),
2260 SelectPhase::End => self.end_selection(cx),
2261 }
2262 }
2263
2264 fn extend_selection(
2265 &mut self,
2266 position: DisplayPoint,
2267 click_count: usize,
2268 cx: &mut ViewContext<Self>,
2269 ) {
2270 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2271 let tail = self.selections.newest::<usize>(cx).tail();
2272 self.begin_selection(position, false, click_count, cx);
2273
2274 let position = position.to_offset(&display_map, Bias::Left);
2275 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2276
2277 let mut pending_selection = self
2278 .selections
2279 .pending_anchor()
2280 .expect("extend_selection not called with pending selection");
2281 if position >= tail {
2282 pending_selection.start = tail_anchor;
2283 } else {
2284 pending_selection.end = tail_anchor;
2285 pending_selection.reversed = true;
2286 }
2287
2288 let mut pending_mode = self.selections.pending_mode().unwrap();
2289 match &mut pending_mode {
2290 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2291 _ => {}
2292 }
2293
2294 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2295 s.set_pending(pending_selection, pending_mode)
2296 });
2297 }
2298
2299 fn begin_selection(
2300 &mut self,
2301 position: DisplayPoint,
2302 add: bool,
2303 click_count: usize,
2304 cx: &mut ViewContext<Self>,
2305 ) {
2306 if !self.focus_handle.is_focused(cx) {
2307 cx.focus(&self.focus_handle);
2308 }
2309
2310 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2311 let buffer = &display_map.buffer_snapshot;
2312 let newest_selection = self.selections.newest_anchor().clone();
2313 let position = display_map.clip_point(position, Bias::Left);
2314
2315 let start;
2316 let end;
2317 let mode;
2318 let auto_scroll;
2319 match click_count {
2320 1 => {
2321 start = buffer.anchor_before(position.to_point(&display_map));
2322 end = start;
2323 mode = SelectMode::Character;
2324 auto_scroll = true;
2325 }
2326 2 => {
2327 let range = movement::surrounding_word(&display_map, position);
2328 start = buffer.anchor_before(range.start.to_point(&display_map));
2329 end = buffer.anchor_before(range.end.to_point(&display_map));
2330 mode = SelectMode::Word(start..end);
2331 auto_scroll = true;
2332 }
2333 3 => {
2334 let position = display_map
2335 .clip_point(position, Bias::Left)
2336 .to_point(&display_map);
2337 let line_start = display_map.prev_line_boundary(position).0;
2338 let next_line_start = buffer.clip_point(
2339 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2340 Bias::Left,
2341 );
2342 start = buffer.anchor_before(line_start);
2343 end = buffer.anchor_before(next_line_start);
2344 mode = SelectMode::Line(start..end);
2345 auto_scroll = true;
2346 }
2347 _ => {
2348 start = buffer.anchor_before(0);
2349 end = buffer.anchor_before(buffer.len());
2350 mode = SelectMode::All;
2351 auto_scroll = false;
2352 }
2353 }
2354
2355 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2356 if !add {
2357 s.clear_disjoint();
2358 } else if click_count > 1 {
2359 s.delete(newest_selection.id)
2360 }
2361
2362 s.set_pending_anchor_range(start..end, mode);
2363 });
2364 }
2365
2366 fn begin_columnar_selection(
2367 &mut self,
2368 position: DisplayPoint,
2369 goal_column: u32,
2370 cx: &mut ViewContext<Self>,
2371 ) {
2372 if !self.focus_handle.is_focused(cx) {
2373 cx.focus(&self.focus_handle);
2374 }
2375
2376 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2377 let tail = self.selections.newest::<Point>(cx).tail();
2378 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2379
2380 self.select_columns(
2381 tail.to_display_point(&display_map),
2382 position,
2383 goal_column,
2384 &display_map,
2385 cx,
2386 );
2387 }
2388
2389 fn update_selection(
2390 &mut self,
2391 position: DisplayPoint,
2392 goal_column: u32,
2393 scroll_delta: gpui::Point<f32>,
2394 cx: &mut ViewContext<Self>,
2395 ) {
2396 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2397
2398 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2399 let tail = tail.to_display_point(&display_map);
2400 self.select_columns(tail, position, goal_column, &display_map, cx);
2401 } else if let Some(mut pending) = self.selections.pending_anchor() {
2402 let buffer = self.buffer.read(cx).snapshot(cx);
2403 let head;
2404 let tail;
2405 let mode = self.selections.pending_mode().unwrap();
2406 match &mode {
2407 SelectMode::Character => {
2408 head = position.to_point(&display_map);
2409 tail = pending.tail().to_point(&buffer);
2410 }
2411 SelectMode::Word(original_range) => {
2412 let original_display_range = original_range.start.to_display_point(&display_map)
2413 ..original_range.end.to_display_point(&display_map);
2414 let original_buffer_range = original_display_range.start.to_point(&display_map)
2415 ..original_display_range.end.to_point(&display_map);
2416 if movement::is_inside_word(&display_map, position)
2417 || original_display_range.contains(&position)
2418 {
2419 let word_range = movement::surrounding_word(&display_map, position);
2420 if word_range.start < original_display_range.start {
2421 head = word_range.start.to_point(&display_map);
2422 } else {
2423 head = word_range.end.to_point(&display_map);
2424 }
2425 } else {
2426 head = position.to_point(&display_map);
2427 }
2428
2429 if head <= original_buffer_range.start {
2430 tail = original_buffer_range.end;
2431 } else {
2432 tail = original_buffer_range.start;
2433 }
2434 }
2435 SelectMode::Line(original_range) => {
2436 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2437
2438 let position = display_map
2439 .clip_point(position, Bias::Left)
2440 .to_point(&display_map);
2441 let line_start = display_map.prev_line_boundary(position).0;
2442 let next_line_start = buffer.clip_point(
2443 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2444 Bias::Left,
2445 );
2446
2447 if line_start < original_range.start {
2448 head = line_start
2449 } else {
2450 head = next_line_start
2451 }
2452
2453 if head <= original_range.start {
2454 tail = original_range.end;
2455 } else {
2456 tail = original_range.start;
2457 }
2458 }
2459 SelectMode::All => {
2460 return;
2461 }
2462 };
2463
2464 if head < tail {
2465 pending.start = buffer.anchor_before(head);
2466 pending.end = buffer.anchor_before(tail);
2467 pending.reversed = true;
2468 } else {
2469 pending.start = buffer.anchor_before(tail);
2470 pending.end = buffer.anchor_before(head);
2471 pending.reversed = false;
2472 }
2473
2474 self.change_selections(None, cx, |s| {
2475 s.set_pending(pending, mode);
2476 });
2477 } else {
2478 log::error!("update_selection dispatched with no pending selection");
2479 return;
2480 }
2481
2482 self.apply_scroll_delta(scroll_delta, cx);
2483 cx.notify();
2484 }
2485
2486 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2487 self.columnar_selection_tail.take();
2488 if self.selections.pending_anchor().is_some() {
2489 let selections = self.selections.all::<usize>(cx);
2490 self.change_selections(None, cx, |s| {
2491 s.select(selections);
2492 s.clear_pending();
2493 });
2494 }
2495 }
2496
2497 fn select_columns(
2498 &mut self,
2499 tail: DisplayPoint,
2500 head: DisplayPoint,
2501 goal_column: u32,
2502 display_map: &DisplaySnapshot,
2503 cx: &mut ViewContext<Self>,
2504 ) {
2505 let start_row = cmp::min(tail.row(), head.row());
2506 let end_row = cmp::max(tail.row(), head.row());
2507 let start_column = cmp::min(tail.column(), goal_column);
2508 let end_column = cmp::max(tail.column(), goal_column);
2509 let reversed = start_column < tail.column();
2510
2511 let selection_ranges = (start_row.0..=end_row.0)
2512 .map(DisplayRow)
2513 .filter_map(|row| {
2514 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2515 let start = display_map
2516 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2517 .to_point(display_map);
2518 let end = display_map
2519 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2520 .to_point(display_map);
2521 if reversed {
2522 Some(end..start)
2523 } else {
2524 Some(start..end)
2525 }
2526 } else {
2527 None
2528 }
2529 })
2530 .collect::<Vec<_>>();
2531
2532 self.change_selections(None, cx, |s| {
2533 s.select_ranges(selection_ranges);
2534 });
2535 cx.notify();
2536 }
2537
2538 pub fn has_pending_nonempty_selection(&self) -> bool {
2539 let pending_nonempty_selection = match self.selections.pending_anchor() {
2540 Some(Selection { start, end, .. }) => start != end,
2541 None => false,
2542 };
2543 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2544 }
2545
2546 pub fn has_pending_selection(&self) -> bool {
2547 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2548 }
2549
2550 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2551 self.clear_expanded_diff_hunks(cx);
2552 if self.dismiss_menus_and_popups(cx) {
2553 return;
2554 }
2555
2556 if self.mode == EditorMode::Full {
2557 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2558 return;
2559 }
2560 }
2561
2562 cx.propagate();
2563 }
2564
2565 pub fn dismiss_menus_and_popups(&mut self, cx: &mut ViewContext<Self>) -> bool {
2566 if self.take_rename(false, cx).is_some() {
2567 return true;
2568 }
2569
2570 if hide_hover(self, cx) {
2571 return true;
2572 }
2573
2574 if self.hide_context_menu(cx).is_some() {
2575 return true;
2576 }
2577
2578 if self.discard_inline_completion(cx) {
2579 return true;
2580 }
2581
2582 if self.snippet_stack.pop().is_some() {
2583 return true;
2584 }
2585
2586 if self.mode == EditorMode::Full {
2587 if self.active_diagnostics.is_some() {
2588 self.dismiss_diagnostics(cx);
2589 return true;
2590 }
2591 }
2592
2593 false
2594 }
2595
2596 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2597 let text: Arc<str> = text.into();
2598
2599 if self.read_only(cx) {
2600 return;
2601 }
2602
2603 let selections = self.selections.all_adjusted(cx);
2604 let mut brace_inserted = false;
2605 let mut edits = Vec::new();
2606 let mut new_selections = Vec::with_capacity(selections.len());
2607 let mut new_autoclose_regions = Vec::new();
2608 let snapshot = self.buffer.read(cx).read(cx);
2609
2610 for (selection, autoclose_region) in
2611 self.selections_with_autoclose_regions(selections, &snapshot)
2612 {
2613 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2614 // Determine if the inserted text matches the opening or closing
2615 // bracket of any of this language's bracket pairs.
2616 let mut bracket_pair = None;
2617 let mut is_bracket_pair_start = false;
2618 let mut is_bracket_pair_end = false;
2619 if !text.is_empty() {
2620 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2621 // and they are removing the character that triggered IME popup.
2622 for (pair, enabled) in scope.brackets() {
2623 if !pair.close {
2624 continue;
2625 }
2626
2627 if enabled && pair.start.ends_with(text.as_ref()) {
2628 bracket_pair = Some(pair.clone());
2629 is_bracket_pair_start = true;
2630 break;
2631 }
2632 if pair.end.as_str() == text.as_ref() {
2633 bracket_pair = Some(pair.clone());
2634 is_bracket_pair_end = true;
2635 break;
2636 }
2637 }
2638 }
2639
2640 if let Some(bracket_pair) = bracket_pair {
2641 if selection.is_empty() {
2642 if is_bracket_pair_start {
2643 let prefix_len = bracket_pair.start.len() - text.len();
2644
2645 // If the inserted text is a suffix of an opening bracket and the
2646 // selection is preceded by the rest of the opening bracket, then
2647 // insert the closing bracket.
2648 let following_text_allows_autoclose = snapshot
2649 .chars_at(selection.start)
2650 .next()
2651 .map_or(true, |c| scope.should_autoclose_before(c));
2652 let preceding_text_matches_prefix = prefix_len == 0
2653 || (selection.start.column >= (prefix_len as u32)
2654 && snapshot.contains_str_at(
2655 Point::new(
2656 selection.start.row,
2657 selection.start.column - (prefix_len as u32),
2658 ),
2659 &bracket_pair.start[..prefix_len],
2660 ));
2661 let autoclose = self.use_autoclose
2662 && snapshot.settings_at(selection.start, cx).use_autoclose;
2663 if autoclose
2664 && following_text_allows_autoclose
2665 && preceding_text_matches_prefix
2666 {
2667 let anchor = snapshot.anchor_before(selection.end);
2668 new_selections.push((selection.map(|_| anchor), text.len()));
2669 new_autoclose_regions.push((
2670 anchor,
2671 text.len(),
2672 selection.id,
2673 bracket_pair.clone(),
2674 ));
2675 edits.push((
2676 selection.range(),
2677 format!("{}{}", text, bracket_pair.end).into(),
2678 ));
2679 brace_inserted = true;
2680 continue;
2681 }
2682 }
2683
2684 if let Some(region) = autoclose_region {
2685 // If the selection is followed by an auto-inserted closing bracket,
2686 // then don't insert that closing bracket again; just move the selection
2687 // past the closing bracket.
2688 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2689 && text.as_ref() == region.pair.end.as_str();
2690 if should_skip {
2691 let anchor = snapshot.anchor_after(selection.end);
2692 new_selections
2693 .push((selection.map(|_| anchor), region.pair.end.len()));
2694 continue;
2695 }
2696 }
2697
2698 let always_treat_brackets_as_autoclosed = snapshot
2699 .settings_at(selection.start, cx)
2700 .always_treat_brackets_as_autoclosed;
2701 if always_treat_brackets_as_autoclosed
2702 && is_bracket_pair_end
2703 && snapshot.contains_str_at(selection.end, text.as_ref())
2704 {
2705 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2706 // and the inserted text is a closing bracket and the selection is followed
2707 // by the closing bracket then move the selection past the closing bracket.
2708 let anchor = snapshot.anchor_after(selection.end);
2709 new_selections.push((selection.map(|_| anchor), text.len()));
2710 continue;
2711 }
2712 }
2713 // If an opening bracket is 1 character long and is typed while
2714 // text is selected, then surround that text with the bracket pair.
2715 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2716 edits.push((selection.start..selection.start, text.clone()));
2717 edits.push((
2718 selection.end..selection.end,
2719 bracket_pair.end.as_str().into(),
2720 ));
2721 brace_inserted = true;
2722 new_selections.push((
2723 Selection {
2724 id: selection.id,
2725 start: snapshot.anchor_after(selection.start),
2726 end: snapshot.anchor_before(selection.end),
2727 reversed: selection.reversed,
2728 goal: selection.goal,
2729 },
2730 0,
2731 ));
2732 continue;
2733 }
2734 }
2735 }
2736
2737 if self.auto_replace_emoji_shortcode
2738 && selection.is_empty()
2739 && text.as_ref().ends_with(':')
2740 {
2741 if let Some(possible_emoji_short_code) =
2742 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2743 {
2744 if !possible_emoji_short_code.is_empty() {
2745 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2746 let emoji_shortcode_start = Point::new(
2747 selection.start.row,
2748 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2749 );
2750
2751 // Remove shortcode from buffer
2752 edits.push((
2753 emoji_shortcode_start..selection.start,
2754 "".to_string().into(),
2755 ));
2756 new_selections.push((
2757 Selection {
2758 id: selection.id,
2759 start: snapshot.anchor_after(emoji_shortcode_start),
2760 end: snapshot.anchor_before(selection.start),
2761 reversed: selection.reversed,
2762 goal: selection.goal,
2763 },
2764 0,
2765 ));
2766
2767 // Insert emoji
2768 let selection_start_anchor = snapshot.anchor_after(selection.start);
2769 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2770 edits.push((selection.start..selection.end, emoji.to_string().into()));
2771
2772 continue;
2773 }
2774 }
2775 }
2776 }
2777
2778 // If not handling any auto-close operation, then just replace the selected
2779 // text with the given input and move the selection to the end of the
2780 // newly inserted text.
2781 let anchor = snapshot.anchor_after(selection.end);
2782 new_selections.push((selection.map(|_| anchor), 0));
2783 edits.push((selection.start..selection.end, text.clone()));
2784 }
2785
2786 drop(snapshot);
2787 self.transact(cx, |this, cx| {
2788 this.buffer.update(cx, |buffer, cx| {
2789 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2790 });
2791
2792 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2793 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2794 let snapshot = this.buffer.read(cx).read(cx);
2795 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2796 .zip(new_selection_deltas)
2797 .map(|(selection, delta)| Selection {
2798 id: selection.id,
2799 start: selection.start + delta,
2800 end: selection.end + delta,
2801 reversed: selection.reversed,
2802 goal: SelectionGoal::None,
2803 })
2804 .collect::<Vec<_>>();
2805
2806 let mut i = 0;
2807 for (position, delta, selection_id, pair) in new_autoclose_regions {
2808 let position = position.to_offset(&snapshot) + delta;
2809 let start = snapshot.anchor_before(position);
2810 let end = snapshot.anchor_after(position);
2811 while let Some(existing_state) = this.autoclose_regions.get(i) {
2812 match existing_state.range.start.cmp(&start, &snapshot) {
2813 Ordering::Less => i += 1,
2814 Ordering::Greater => break,
2815 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2816 Ordering::Less => i += 1,
2817 Ordering::Equal => break,
2818 Ordering::Greater => break,
2819 },
2820 }
2821 }
2822 this.autoclose_regions.insert(
2823 i,
2824 AutocloseRegion {
2825 selection_id,
2826 range: start..end,
2827 pair,
2828 },
2829 );
2830 }
2831
2832 drop(snapshot);
2833 let had_active_inline_completion = this.has_active_inline_completion(cx);
2834 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2835
2836 if brace_inserted {
2837 // If we inserted a brace while composing text (i.e. typing `"` on a
2838 // Brazilian keyboard), exit the composing state because most likely
2839 // the user wanted to surround the selection.
2840 this.unmark_text(cx);
2841 } else if EditorSettings::get_global(cx).use_on_type_format {
2842 if let Some(on_type_format_task) =
2843 this.trigger_on_type_formatting(text.to_string(), cx)
2844 {
2845 on_type_format_task.detach_and_log_err(cx);
2846 }
2847 }
2848
2849 let trigger_in_words = !had_active_inline_completion;
2850 this.trigger_completion_on_input(&text, trigger_in_words, cx);
2851 this.refresh_inline_completion(true, cx);
2852 });
2853 }
2854
2855 fn find_possible_emoji_shortcode_at_position(
2856 snapshot: &MultiBufferSnapshot,
2857 position: Point,
2858 ) -> Option<String> {
2859 let mut chars = Vec::new();
2860 let mut found_colon = false;
2861 for char in snapshot.reversed_chars_at(position).take(100) {
2862 // Found a possible emoji shortcode in the middle of the buffer
2863 if found_colon {
2864 if char.is_whitespace() {
2865 chars.reverse();
2866 return Some(chars.iter().collect());
2867 }
2868 // If the previous character is not a whitespace, we are in the middle of a word
2869 // and we only want to complete the shortcode if the word is made up of other emojis
2870 let mut containing_word = String::new();
2871 for ch in snapshot
2872 .reversed_chars_at(position)
2873 .skip(chars.len() + 1)
2874 .take(100)
2875 {
2876 if ch.is_whitespace() {
2877 break;
2878 }
2879 containing_word.push(ch);
2880 }
2881 let containing_word = containing_word.chars().rev().collect::<String>();
2882 if util::word_consists_of_emojis(containing_word.as_str()) {
2883 chars.reverse();
2884 return Some(chars.iter().collect());
2885 }
2886 }
2887
2888 if char.is_whitespace() || !char.is_ascii() {
2889 return None;
2890 }
2891 if char == ':' {
2892 found_colon = true;
2893 } else {
2894 chars.push(char);
2895 }
2896 }
2897 // Found a possible emoji shortcode at the beginning of the buffer
2898 chars.reverse();
2899 Some(chars.iter().collect())
2900 }
2901
2902 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2903 self.transact(cx, |this, cx| {
2904 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2905 let selections = this.selections.all::<usize>(cx);
2906 let multi_buffer = this.buffer.read(cx);
2907 let buffer = multi_buffer.snapshot(cx);
2908 selections
2909 .iter()
2910 .map(|selection| {
2911 let start_point = selection.start.to_point(&buffer);
2912 let mut indent =
2913 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
2914 indent.len = cmp::min(indent.len, start_point.column);
2915 let start = selection.start;
2916 let end = selection.end;
2917 let selection_is_empty = start == end;
2918 let language_scope = buffer.language_scope_at(start);
2919 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2920 &language_scope
2921 {
2922 let leading_whitespace_len = buffer
2923 .reversed_chars_at(start)
2924 .take_while(|c| c.is_whitespace() && *c != '\n')
2925 .map(|c| c.len_utf8())
2926 .sum::<usize>();
2927
2928 let trailing_whitespace_len = buffer
2929 .chars_at(end)
2930 .take_while(|c| c.is_whitespace() && *c != '\n')
2931 .map(|c| c.len_utf8())
2932 .sum::<usize>();
2933
2934 let insert_extra_newline =
2935 language.brackets().any(|(pair, enabled)| {
2936 let pair_start = pair.start.trim_end();
2937 let pair_end = pair.end.trim_start();
2938
2939 enabled
2940 && pair.newline
2941 && buffer.contains_str_at(
2942 end + trailing_whitespace_len,
2943 pair_end,
2944 )
2945 && buffer.contains_str_at(
2946 (start - leading_whitespace_len)
2947 .saturating_sub(pair_start.len()),
2948 pair_start,
2949 )
2950 });
2951
2952 // Comment extension on newline is allowed only for cursor selections
2953 let comment_delimiter = maybe!({
2954 if !selection_is_empty {
2955 return None;
2956 }
2957
2958 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
2959 return None;
2960 }
2961
2962 let delimiters = language.line_comment_prefixes();
2963 let max_len_of_delimiter =
2964 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
2965 let (snapshot, range) =
2966 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
2967
2968 let mut index_of_first_non_whitespace = 0;
2969 let comment_candidate = snapshot
2970 .chars_for_range(range)
2971 .skip_while(|c| {
2972 let should_skip = c.is_whitespace();
2973 if should_skip {
2974 index_of_first_non_whitespace += 1;
2975 }
2976 should_skip
2977 })
2978 .take(max_len_of_delimiter)
2979 .collect::<String>();
2980 let comment_prefix = delimiters.iter().find(|comment_prefix| {
2981 comment_candidate.starts_with(comment_prefix.as_ref())
2982 })?;
2983 let cursor_is_placed_after_comment_marker =
2984 index_of_first_non_whitespace + comment_prefix.len()
2985 <= start_point.column as usize;
2986 if cursor_is_placed_after_comment_marker {
2987 Some(comment_prefix.clone())
2988 } else {
2989 None
2990 }
2991 });
2992 (comment_delimiter, insert_extra_newline)
2993 } else {
2994 (None, false)
2995 };
2996
2997 let capacity_for_delimiter = comment_delimiter
2998 .as_deref()
2999 .map(str::len)
3000 .unwrap_or_default();
3001 let mut new_text =
3002 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3003 new_text.push_str("\n");
3004 new_text.extend(indent.chars());
3005 if let Some(delimiter) = &comment_delimiter {
3006 new_text.push_str(&delimiter);
3007 }
3008 if insert_extra_newline {
3009 new_text = new_text.repeat(2);
3010 }
3011
3012 let anchor = buffer.anchor_after(end);
3013 let new_selection = selection.map(|_| anchor);
3014 (
3015 (start..end, new_text),
3016 (insert_extra_newline, new_selection),
3017 )
3018 })
3019 .unzip()
3020 };
3021
3022 this.edit_with_autoindent(edits, cx);
3023 let buffer = this.buffer.read(cx).snapshot(cx);
3024 let new_selections = selection_fixup_info
3025 .into_iter()
3026 .map(|(extra_newline_inserted, new_selection)| {
3027 let mut cursor = new_selection.end.to_point(&buffer);
3028 if extra_newline_inserted {
3029 cursor.row -= 1;
3030 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3031 }
3032 new_selection.map(|_| cursor)
3033 })
3034 .collect();
3035
3036 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3037 this.refresh_inline_completion(true, cx);
3038 });
3039 }
3040
3041 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3042 let buffer = self.buffer.read(cx);
3043 let snapshot = buffer.snapshot(cx);
3044
3045 let mut edits = Vec::new();
3046 let mut rows = Vec::new();
3047
3048 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3049 let cursor = selection.head();
3050 let row = cursor.row;
3051
3052 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3053
3054 let newline = "\n".to_string();
3055 edits.push((start_of_line..start_of_line, newline));
3056
3057 rows.push(row + rows_inserted as u32);
3058 }
3059
3060 self.transact(cx, |editor, cx| {
3061 editor.edit(edits, cx);
3062
3063 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3064 let mut index = 0;
3065 s.move_cursors_with(|map, _, _| {
3066 let row = rows[index];
3067 index += 1;
3068
3069 let point = Point::new(row, 0);
3070 let boundary = map.next_line_boundary(point).1;
3071 let clipped = map.clip_point(boundary, Bias::Left);
3072
3073 (clipped, SelectionGoal::None)
3074 });
3075 });
3076
3077 let mut indent_edits = Vec::new();
3078 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3079 for row in rows {
3080 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3081 for (row, indent) in indents {
3082 if indent.len == 0 {
3083 continue;
3084 }
3085
3086 let text = match indent.kind {
3087 IndentKind::Space => " ".repeat(indent.len as usize),
3088 IndentKind::Tab => "\t".repeat(indent.len as usize),
3089 };
3090 let point = Point::new(row.0, 0);
3091 indent_edits.push((point..point, text));
3092 }
3093 }
3094 editor.edit(indent_edits, cx);
3095 });
3096 }
3097
3098 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3099 let buffer = self.buffer.read(cx);
3100 let snapshot = buffer.snapshot(cx);
3101
3102 let mut edits = Vec::new();
3103 let mut rows = Vec::new();
3104 let mut rows_inserted = 0;
3105
3106 for selection in self.selections.all_adjusted(cx) {
3107 let cursor = selection.head();
3108 let row = cursor.row;
3109
3110 let point = Point::new(row + 1, 0);
3111 let start_of_line = snapshot.clip_point(point, Bias::Left);
3112
3113 let newline = "\n".to_string();
3114 edits.push((start_of_line..start_of_line, newline));
3115
3116 rows_inserted += 1;
3117 rows.push(row + rows_inserted);
3118 }
3119
3120 self.transact(cx, |editor, cx| {
3121 editor.edit(edits, cx);
3122
3123 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3124 let mut index = 0;
3125 s.move_cursors_with(|map, _, _| {
3126 let row = rows[index];
3127 index += 1;
3128
3129 let point = Point::new(row, 0);
3130 let boundary = map.next_line_boundary(point).1;
3131 let clipped = map.clip_point(boundary, Bias::Left);
3132
3133 (clipped, SelectionGoal::None)
3134 });
3135 });
3136
3137 let mut indent_edits = Vec::new();
3138 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3139 for row in rows {
3140 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3141 for (row, indent) in indents {
3142 if indent.len == 0 {
3143 continue;
3144 }
3145
3146 let text = match indent.kind {
3147 IndentKind::Space => " ".repeat(indent.len as usize),
3148 IndentKind::Tab => "\t".repeat(indent.len as usize),
3149 };
3150 let point = Point::new(row.0, 0);
3151 indent_edits.push((point..point, text));
3152 }
3153 }
3154 editor.edit(indent_edits, cx);
3155 });
3156 }
3157
3158 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3159 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3160 original_indent_columns: Vec::new(),
3161 });
3162 self.insert_with_autoindent_mode(text, autoindent, cx);
3163 }
3164
3165 fn insert_with_autoindent_mode(
3166 &mut self,
3167 text: &str,
3168 autoindent_mode: Option<AutoindentMode>,
3169 cx: &mut ViewContext<Self>,
3170 ) {
3171 if self.read_only(cx) {
3172 return;
3173 }
3174
3175 let text: Arc<str> = text.into();
3176 self.transact(cx, |this, cx| {
3177 let old_selections = this.selections.all_adjusted(cx);
3178 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3179 let anchors = {
3180 let snapshot = buffer.read(cx);
3181 old_selections
3182 .iter()
3183 .map(|s| {
3184 let anchor = snapshot.anchor_after(s.head());
3185 s.map(|_| anchor)
3186 })
3187 .collect::<Vec<_>>()
3188 };
3189 buffer.edit(
3190 old_selections
3191 .iter()
3192 .map(|s| (s.start..s.end, text.clone())),
3193 autoindent_mode,
3194 cx,
3195 );
3196 anchors
3197 });
3198
3199 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3200 s.select_anchors(selection_anchors);
3201 })
3202 });
3203 }
3204
3205 fn trigger_completion_on_input(
3206 &mut self,
3207 text: &str,
3208 trigger_in_words: bool,
3209 cx: &mut ViewContext<Self>,
3210 ) {
3211 if !EditorSettings::get_global(cx).show_completions_on_input {
3212 return;
3213 }
3214
3215 let selection = self.selections.newest_anchor();
3216 if self
3217 .buffer
3218 .read(cx)
3219 .is_completion_trigger(selection.head(), text, trigger_in_words, cx)
3220 {
3221 self.show_completions(&ShowCompletions, cx);
3222 } else {
3223 self.hide_context_menu(cx);
3224 }
3225 }
3226
3227 /// If any empty selections is touching the start of its innermost containing autoclose
3228 /// region, expand it to select the brackets.
3229 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3230 let selections = self.selections.all::<usize>(cx);
3231 let buffer = self.buffer.read(cx).read(cx);
3232 let new_selections = self
3233 .selections_with_autoclose_regions(selections, &buffer)
3234 .map(|(mut selection, region)| {
3235 if !selection.is_empty() {
3236 return selection;
3237 }
3238
3239 if let Some(region) = region {
3240 let mut range = region.range.to_offset(&buffer);
3241 if selection.start == range.start && range.start >= region.pair.start.len() {
3242 range.start -= region.pair.start.len();
3243 if buffer.contains_str_at(range.start, ®ion.pair.start)
3244 && buffer.contains_str_at(range.end, ®ion.pair.end)
3245 {
3246 range.end += region.pair.end.len();
3247 selection.start = range.start;
3248 selection.end = range.end;
3249
3250 return selection;
3251 }
3252 }
3253 }
3254
3255 let always_treat_brackets_as_autoclosed = buffer
3256 .settings_at(selection.start, cx)
3257 .always_treat_brackets_as_autoclosed;
3258
3259 if !always_treat_brackets_as_autoclosed {
3260 return selection;
3261 }
3262
3263 if let Some(scope) = buffer.language_scope_at(selection.start) {
3264 for (pair, enabled) in scope.brackets() {
3265 if !enabled || !pair.close {
3266 continue;
3267 }
3268
3269 if buffer.contains_str_at(selection.start, &pair.end) {
3270 let pair_start_len = pair.start.len();
3271 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3272 {
3273 selection.start -= pair_start_len;
3274 selection.end += pair.end.len();
3275
3276 return selection;
3277 }
3278 }
3279 }
3280 }
3281
3282 selection
3283 })
3284 .collect();
3285
3286 drop(buffer);
3287 self.change_selections(None, cx, |selections| selections.select(new_selections));
3288 }
3289
3290 /// Iterate the given selections, and for each one, find the smallest surrounding
3291 /// autoclose region. This uses the ordering of the selections and the autoclose
3292 /// regions to avoid repeated comparisons.
3293 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3294 &'a self,
3295 selections: impl IntoIterator<Item = Selection<D>>,
3296 buffer: &'a MultiBufferSnapshot,
3297 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3298 let mut i = 0;
3299 let mut regions = self.autoclose_regions.as_slice();
3300 selections.into_iter().map(move |selection| {
3301 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3302
3303 let mut enclosing = None;
3304 while let Some(pair_state) = regions.get(i) {
3305 if pair_state.range.end.to_offset(buffer) < range.start {
3306 regions = ®ions[i + 1..];
3307 i = 0;
3308 } else if pair_state.range.start.to_offset(buffer) > range.end {
3309 break;
3310 } else {
3311 if pair_state.selection_id == selection.id {
3312 enclosing = Some(pair_state);
3313 }
3314 i += 1;
3315 }
3316 }
3317
3318 (selection.clone(), enclosing)
3319 })
3320 }
3321
3322 /// Remove any autoclose regions that no longer contain their selection.
3323 fn invalidate_autoclose_regions(
3324 &mut self,
3325 mut selections: &[Selection<Anchor>],
3326 buffer: &MultiBufferSnapshot,
3327 ) {
3328 self.autoclose_regions.retain(|state| {
3329 let mut i = 0;
3330 while let Some(selection) = selections.get(i) {
3331 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3332 selections = &selections[1..];
3333 continue;
3334 }
3335 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3336 break;
3337 }
3338 if selection.id == state.selection_id {
3339 return true;
3340 } else {
3341 i += 1;
3342 }
3343 }
3344 false
3345 });
3346 }
3347
3348 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3349 let offset = position.to_offset(buffer);
3350 let (word_range, kind) = buffer.surrounding_word(offset);
3351 if offset > word_range.start && kind == Some(CharKind::Word) {
3352 Some(
3353 buffer
3354 .text_for_range(word_range.start..offset)
3355 .collect::<String>(),
3356 )
3357 } else {
3358 None
3359 }
3360 }
3361
3362 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3363 self.refresh_inlay_hints(
3364 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3365 cx,
3366 );
3367 }
3368
3369 pub fn inlay_hints_enabled(&self) -> bool {
3370 self.inlay_hint_cache.enabled
3371 }
3372
3373 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3374 if self.project.is_none() || self.mode != EditorMode::Full {
3375 return;
3376 }
3377
3378 let reason_description = reason.description();
3379 let ignore_debounce = matches!(
3380 reason,
3381 InlayHintRefreshReason::SettingsChange(_)
3382 | InlayHintRefreshReason::Toggle(_)
3383 | InlayHintRefreshReason::ExcerptsRemoved(_)
3384 );
3385 let (invalidate_cache, required_languages) = match reason {
3386 InlayHintRefreshReason::Toggle(enabled) => {
3387 self.inlay_hint_cache.enabled = enabled;
3388 if enabled {
3389 (InvalidationStrategy::RefreshRequested, None)
3390 } else {
3391 self.inlay_hint_cache.clear();
3392 self.splice_inlays(
3393 self.visible_inlay_hints(cx)
3394 .iter()
3395 .map(|inlay| inlay.id)
3396 .collect(),
3397 Vec::new(),
3398 cx,
3399 );
3400 return;
3401 }
3402 }
3403 InlayHintRefreshReason::SettingsChange(new_settings) => {
3404 match self.inlay_hint_cache.update_settings(
3405 &self.buffer,
3406 new_settings,
3407 self.visible_inlay_hints(cx),
3408 cx,
3409 ) {
3410 ControlFlow::Break(Some(InlaySplice {
3411 to_remove,
3412 to_insert,
3413 })) => {
3414 self.splice_inlays(to_remove, to_insert, cx);
3415 return;
3416 }
3417 ControlFlow::Break(None) => return,
3418 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3419 }
3420 }
3421 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3422 if let Some(InlaySplice {
3423 to_remove,
3424 to_insert,
3425 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3426 {
3427 self.splice_inlays(to_remove, to_insert, cx);
3428 }
3429 return;
3430 }
3431 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3432 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3433 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3434 }
3435 InlayHintRefreshReason::RefreshRequested => {
3436 (InvalidationStrategy::RefreshRequested, None)
3437 }
3438 };
3439
3440 if let Some(InlaySplice {
3441 to_remove,
3442 to_insert,
3443 }) = self.inlay_hint_cache.spawn_hint_refresh(
3444 reason_description,
3445 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3446 invalidate_cache,
3447 ignore_debounce,
3448 cx,
3449 ) {
3450 self.splice_inlays(to_remove, to_insert, cx);
3451 }
3452 }
3453
3454 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3455 self.display_map
3456 .read(cx)
3457 .current_inlays()
3458 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3459 .cloned()
3460 .collect()
3461 }
3462
3463 pub fn excerpts_for_inlay_hints_query(
3464 &self,
3465 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3466 cx: &mut ViewContext<Editor>,
3467 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3468 let Some(project) = self.project.as_ref() else {
3469 return HashMap::default();
3470 };
3471 let project = project.read(cx);
3472 let multi_buffer = self.buffer().read(cx);
3473 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3474 let multi_buffer_visible_start = self
3475 .scroll_manager
3476 .anchor()
3477 .anchor
3478 .to_point(&multi_buffer_snapshot);
3479 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3480 multi_buffer_visible_start
3481 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3482 Bias::Left,
3483 );
3484 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3485 multi_buffer
3486 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3487 .into_iter()
3488 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3489 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3490 let buffer = buffer_handle.read(cx);
3491 let buffer_file = project::File::from_dyn(buffer.file())?;
3492 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3493 let worktree_entry = buffer_worktree
3494 .read(cx)
3495 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3496 if worktree_entry.is_ignored {
3497 return None;
3498 }
3499
3500 let language = buffer.language()?;
3501 if let Some(restrict_to_languages) = restrict_to_languages {
3502 if !restrict_to_languages.contains(language) {
3503 return None;
3504 }
3505 }
3506 Some((
3507 excerpt_id,
3508 (
3509 buffer_handle,
3510 buffer.version().clone(),
3511 excerpt_visible_range,
3512 ),
3513 ))
3514 })
3515 .collect()
3516 }
3517
3518 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3519 TextLayoutDetails {
3520 text_system: cx.text_system().clone(),
3521 editor_style: self.style.clone().unwrap(),
3522 rem_size: cx.rem_size(),
3523 scroll_anchor: self.scroll_manager.anchor(),
3524 visible_rows: self.visible_line_count(),
3525 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3526 }
3527 }
3528
3529 fn splice_inlays(
3530 &self,
3531 to_remove: Vec<InlayId>,
3532 to_insert: Vec<Inlay>,
3533 cx: &mut ViewContext<Self>,
3534 ) {
3535 self.display_map.update(cx, |display_map, cx| {
3536 display_map.splice_inlays(to_remove, to_insert, cx);
3537 });
3538 cx.notify();
3539 }
3540
3541 fn trigger_on_type_formatting(
3542 &self,
3543 input: String,
3544 cx: &mut ViewContext<Self>,
3545 ) -> Option<Task<Result<()>>> {
3546 if input.len() != 1 {
3547 return None;
3548 }
3549
3550 let project = self.project.as_ref()?;
3551 let position = self.selections.newest_anchor().head();
3552 let (buffer, buffer_position) = self
3553 .buffer
3554 .read(cx)
3555 .text_anchor_for_position(position, cx)?;
3556
3557 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3558 // hence we do LSP request & edit on host side only — add formats to host's history.
3559 let push_to_lsp_host_history = true;
3560 // If this is not the host, append its history with new edits.
3561 let push_to_client_history = project.read(cx).is_remote();
3562
3563 let on_type_formatting = project.update(cx, |project, cx| {
3564 project.on_type_format(
3565 buffer.clone(),
3566 buffer_position,
3567 input,
3568 push_to_lsp_host_history,
3569 cx,
3570 )
3571 });
3572 Some(cx.spawn(|editor, mut cx| async move {
3573 if let Some(transaction) = on_type_formatting.await? {
3574 if push_to_client_history {
3575 buffer
3576 .update(&mut cx, |buffer, _| {
3577 buffer.push_transaction(transaction, Instant::now());
3578 })
3579 .ok();
3580 }
3581 editor.update(&mut cx, |editor, cx| {
3582 editor.refresh_document_highlights(cx);
3583 })?;
3584 }
3585 Ok(())
3586 }))
3587 }
3588
3589 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3590 if self.pending_rename.is_some() {
3591 return;
3592 }
3593
3594 let Some(provider) = self.completion_provider.as_ref() else {
3595 return;
3596 };
3597
3598 let position = self.selections.newest_anchor().head();
3599 let (buffer, buffer_position) =
3600 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3601 output
3602 } else {
3603 return;
3604 };
3605
3606 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3607 let completions = provider.completions(&buffer, buffer_position, cx);
3608
3609 let id = post_inc(&mut self.next_completion_id);
3610 let task = cx.spawn(|this, mut cx| {
3611 async move {
3612 let completions = completions.await.log_err();
3613 let menu = if let Some(completions) = completions {
3614 let mut menu = CompletionsMenu {
3615 id,
3616 initial_position: position,
3617 match_candidates: completions
3618 .iter()
3619 .enumerate()
3620 .map(|(id, completion)| {
3621 StringMatchCandidate::new(
3622 id,
3623 completion.label.text[completion.label.filter_range.clone()]
3624 .into(),
3625 )
3626 })
3627 .collect(),
3628 buffer: buffer.clone(),
3629 completions: Arc::new(RwLock::new(completions.into())),
3630 matches: Vec::new().into(),
3631 selected_item: 0,
3632 scroll_handle: UniformListScrollHandle::new(),
3633 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
3634 DebouncedDelay::new(),
3635 )),
3636 };
3637 menu.filter(query.as_deref(), cx.background_executor().clone())
3638 .await;
3639
3640 if menu.matches.is_empty() {
3641 None
3642 } else {
3643 this.update(&mut cx, |editor, cx| {
3644 let completions = menu.completions.clone();
3645 let matches = menu.matches.clone();
3646
3647 let delay_ms = EditorSettings::get_global(cx)
3648 .completion_documentation_secondary_query_debounce;
3649 let delay = Duration::from_millis(delay_ms);
3650
3651 editor
3652 .completion_documentation_pre_resolve_debounce
3653 .fire_new(delay, cx, |editor, cx| {
3654 CompletionsMenu::pre_resolve_completion_documentation(
3655 buffer,
3656 completions,
3657 matches,
3658 editor,
3659 cx,
3660 )
3661 });
3662 })
3663 .ok();
3664 Some(menu)
3665 }
3666 } else {
3667 None
3668 };
3669
3670 this.update(&mut cx, |this, cx| {
3671 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3672
3673 let mut context_menu = this.context_menu.write();
3674 match context_menu.as_ref() {
3675 None => {}
3676
3677 Some(ContextMenu::Completions(prev_menu)) => {
3678 if prev_menu.id > id {
3679 return;
3680 }
3681 }
3682
3683 _ => return,
3684 }
3685
3686 if this.focus_handle.is_focused(cx) && menu.is_some() {
3687 let menu = menu.unwrap();
3688 *context_menu = Some(ContextMenu::Completions(menu));
3689 drop(context_menu);
3690 this.discard_inline_completion(cx);
3691 cx.notify();
3692 } else if this.completion_tasks.len() <= 1 {
3693 // If there are no more completion tasks and the last menu was
3694 // empty, we should hide it. If it was already hidden, we should
3695 // also show the copilot completion when available.
3696 drop(context_menu);
3697 if this.hide_context_menu(cx).is_none() {
3698 this.update_visible_inline_completion(cx);
3699 }
3700 }
3701 })?;
3702
3703 Ok::<_, anyhow::Error>(())
3704 }
3705 .log_err()
3706 });
3707
3708 self.completion_tasks.push((id, task));
3709 }
3710
3711 pub fn confirm_completion(
3712 &mut self,
3713 action: &ConfirmCompletion,
3714 cx: &mut ViewContext<Self>,
3715 ) -> Option<Task<Result<()>>> {
3716 use language::ToOffset as _;
3717
3718 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3719 menu
3720 } else {
3721 return None;
3722 };
3723
3724 let mat = completions_menu
3725 .matches
3726 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3727 let buffer_handle = completions_menu.buffer;
3728 let completions = completions_menu.completions.read();
3729 let completion = completions.get(mat.candidate_id)?;
3730 cx.stop_propagation();
3731
3732 let snippet;
3733 let text;
3734 if completion.is_snippet() {
3735 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3736 text = snippet.as_ref().unwrap().text.clone();
3737 } else {
3738 snippet = None;
3739 text = completion.new_text.clone();
3740 };
3741 let selections = self.selections.all::<usize>(cx);
3742 let buffer = buffer_handle.read(cx);
3743 let old_range = completion.old_range.to_offset(buffer);
3744 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3745
3746 let newest_selection = self.selections.newest_anchor();
3747 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3748 return None;
3749 }
3750
3751 let lookbehind = newest_selection
3752 .start
3753 .text_anchor
3754 .to_offset(buffer)
3755 .saturating_sub(old_range.start);
3756 let lookahead = old_range
3757 .end
3758 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3759 let mut common_prefix_len = old_text
3760 .bytes()
3761 .zip(text.bytes())
3762 .take_while(|(a, b)| a == b)
3763 .count();
3764
3765 let snapshot = self.buffer.read(cx).snapshot(cx);
3766 let mut range_to_replace: Option<Range<isize>> = None;
3767 let mut ranges = Vec::new();
3768 for selection in &selections {
3769 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3770 let start = selection.start.saturating_sub(lookbehind);
3771 let end = selection.end + lookahead;
3772 if selection.id == newest_selection.id {
3773 range_to_replace = Some(
3774 ((start + common_prefix_len) as isize - selection.start as isize)
3775 ..(end as isize - selection.start as isize),
3776 );
3777 }
3778 ranges.push(start + common_prefix_len..end);
3779 } else {
3780 common_prefix_len = 0;
3781 ranges.clear();
3782 ranges.extend(selections.iter().map(|s| {
3783 if s.id == newest_selection.id {
3784 range_to_replace = Some(
3785 old_range.start.to_offset_utf16(&snapshot).0 as isize
3786 - selection.start as isize
3787 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3788 - selection.start as isize,
3789 );
3790 old_range.clone()
3791 } else {
3792 s.start..s.end
3793 }
3794 }));
3795 break;
3796 }
3797 }
3798 let text = &text[common_prefix_len..];
3799
3800 cx.emit(EditorEvent::InputHandled {
3801 utf16_range_to_replace: range_to_replace,
3802 text: text.into(),
3803 });
3804
3805 self.transact(cx, |this, cx| {
3806 if let Some(mut snippet) = snippet {
3807 snippet.text = text.to_string();
3808 for tabstop in snippet.tabstops.iter_mut().flatten() {
3809 tabstop.start -= common_prefix_len as isize;
3810 tabstop.end -= common_prefix_len as isize;
3811 }
3812
3813 this.insert_snippet(&ranges, snippet, cx).log_err();
3814 } else {
3815 this.buffer.update(cx, |buffer, cx| {
3816 buffer.edit(
3817 ranges.iter().map(|range| (range.clone(), text)),
3818 this.autoindent_mode.clone(),
3819 cx,
3820 );
3821 });
3822 }
3823
3824 this.refresh_inline_completion(true, cx);
3825 });
3826
3827 let provider = self.completion_provider.as_ref()?;
3828 let apply_edits = provider.apply_additional_edits_for_completion(
3829 buffer_handle,
3830 completion.clone(),
3831 true,
3832 cx,
3833 );
3834 Some(cx.foreground_executor().spawn(async move {
3835 apply_edits.await?;
3836 Ok(())
3837 }))
3838 }
3839
3840 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3841 let mut context_menu = self.context_menu.write();
3842 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
3843 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
3844 // Toggle if we're selecting the same one
3845 *context_menu = None;
3846 cx.notify();
3847 return;
3848 } else {
3849 // Otherwise, clear it and start a new one
3850 *context_menu = None;
3851 cx.notify();
3852 }
3853 }
3854 drop(context_menu);
3855 let snapshot = self.snapshot(cx);
3856 let deployed_from_indicator = action.deployed_from_indicator;
3857 let mut task = self.code_actions_task.take();
3858 let action = action.clone();
3859 cx.spawn(|this, mut cx| async move {
3860 while let Some(prev_task) = task {
3861 prev_task.await;
3862 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3863 }
3864
3865 let spawned_test_task = this.update(&mut cx, |this, cx| {
3866 if this.focus_handle.is_focused(cx) {
3867 let multibuffer_point = action
3868 .deployed_from_indicator
3869 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
3870 .unwrap_or_else(|| this.selections.newest::<Point>(cx).head());
3871 let (buffer, buffer_row) = snapshot
3872 .buffer_snapshot
3873 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
3874 .and_then(|(buffer_snapshot, range)| {
3875 this.buffer
3876 .read(cx)
3877 .buffer(buffer_snapshot.remote_id())
3878 .map(|buffer| (buffer, range.start.row))
3879 })?;
3880 let (_, code_actions) = this
3881 .available_code_actions
3882 .clone()
3883 .and_then(|(location, code_actions)| {
3884 let snapshot = location.buffer.read(cx).snapshot();
3885 let point_range = location.range.to_point(&snapshot);
3886 let point_range = point_range.start.row..=point_range.end.row;
3887 if point_range.contains(&buffer_row) {
3888 Some((location, code_actions))
3889 } else {
3890 None
3891 }
3892 })
3893 .unzip();
3894 let buffer_id = buffer.read(cx).remote_id();
3895 let tasks = this
3896 .tasks
3897 .get(&(buffer_id, buffer_row))
3898 .map(|t| Arc::new(t.to_owned()));
3899 if tasks.is_none() && code_actions.is_none() {
3900 return None;
3901 }
3902
3903 this.completion_tasks.clear();
3904 this.discard_inline_completion(cx);
3905 let task_context = tasks.as_ref().zip(this.workspace.clone()).and_then(
3906 |(tasks, (workspace, _))| {
3907 let position = Point::new(buffer_row, tasks.1.column);
3908 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
3909 let location = Location {
3910 buffer: buffer.clone(),
3911 range: range_start..range_start,
3912 };
3913 workspace
3914 .update(cx, |workspace, cx| {
3915 tasks::task_context_for_location(workspace, location, cx)
3916 })
3917 .ok()
3918 .flatten()
3919 },
3920 );
3921 let tasks = tasks.zip(task_context).map(|(tasks, mut task_context)| {
3922 // Fill in the environmental variables from the tree-sitter captures
3923 let mut additional_task_variables = TaskVariables::default();
3924 for (capture_name, value) in tasks.1.extra_variables.clone() {
3925 additional_task_variables.insert(
3926 task::VariableName::Custom(capture_name.into()),
3927 value.clone(),
3928 );
3929 }
3930 task_context
3931 .task_variables
3932 .extend(additional_task_variables);
3933
3934 Arc::new(ResolvedTasks {
3935 templates: tasks
3936 .1
3937 .templates
3938 .iter()
3939 .filter_map(|(kind, template)| {
3940 template
3941 .resolve_task(&kind.to_id_base(), &task_context)
3942 .map(|task| (kind.clone(), task))
3943 })
3944 .collect(),
3945 position: snapshot
3946 .buffer_snapshot
3947 .anchor_before(Point::new(multibuffer_point.row, tasks.1.column)),
3948 })
3949 });
3950 let spawn_straight_away = tasks
3951 .as_ref()
3952 .map_or(false, |tasks| tasks.templates.len() == 1)
3953 && code_actions
3954 .as_ref()
3955 .map_or(true, |actions| actions.is_empty());
3956 *this.context_menu.write() = Some(ContextMenu::CodeActions(CodeActionsMenu {
3957 buffer,
3958 actions: CodeActionContents {
3959 tasks,
3960 actions: code_actions,
3961 },
3962 selected_item: Default::default(),
3963 scroll_handle: UniformListScrollHandle::default(),
3964 deployed_from_indicator,
3965 }));
3966 if spawn_straight_away {
3967 if let Some(task) =
3968 this.confirm_code_action(&ConfirmCodeAction { item_ix: Some(0) }, cx)
3969 {
3970 cx.notify();
3971 return Some(task);
3972 }
3973 }
3974 cx.notify();
3975 }
3976 Some(Task::ready(Ok(())))
3977 })?;
3978 if let Some(task) = spawned_test_task {
3979 task.await?;
3980 }
3981
3982 Ok::<_, anyhow::Error>(())
3983 })
3984 .detach_and_log_err(cx);
3985 }
3986
3987 pub fn confirm_code_action(
3988 &mut self,
3989 action: &ConfirmCodeAction,
3990 cx: &mut ViewContext<Self>,
3991 ) -> Option<Task<Result<()>>> {
3992 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
3993 menu
3994 } else {
3995 return None;
3996 };
3997 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3998 let action = actions_menu.actions.get(action_ix)?;
3999 let title = action.label();
4000 let buffer = actions_menu.buffer;
4001 let workspace = self.workspace()?;
4002
4003 match action {
4004 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4005 workspace.update(cx, |workspace, cx| {
4006 workspace::tasks::schedule_resolved_task(
4007 workspace,
4008 task_source_kind,
4009 resolved_task,
4010 false,
4011 cx,
4012 );
4013
4014 Some(Task::ready(Ok(())))
4015 })
4016 }
4017 CodeActionsItem::CodeAction(action) => {
4018 let apply_code_actions = workspace
4019 .read(cx)
4020 .project()
4021 .clone()
4022 .update(cx, |project, cx| {
4023 project.apply_code_action(buffer, action, true, cx)
4024 });
4025 let workspace = workspace.downgrade();
4026 Some(cx.spawn(|editor, cx| async move {
4027 let project_transaction = apply_code_actions.await?;
4028 Self::open_project_transaction(
4029 &editor,
4030 workspace,
4031 project_transaction,
4032 title,
4033 cx,
4034 )
4035 .await
4036 }))
4037 }
4038 }
4039 }
4040
4041 async fn open_project_transaction(
4042 this: &WeakView<Editor>,
4043 workspace: WeakView<Workspace>,
4044 transaction: ProjectTransaction,
4045 title: String,
4046 mut cx: AsyncWindowContext,
4047 ) -> Result<()> {
4048 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4049
4050 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4051 cx.update(|cx| {
4052 entries.sort_unstable_by_key(|(buffer, _)| {
4053 buffer.read(cx).file().map(|f| f.path().clone())
4054 });
4055 })?;
4056
4057 // If the project transaction's edits are all contained within this editor, then
4058 // avoid opening a new editor to display them.
4059
4060 if let Some((buffer, transaction)) = entries.first() {
4061 if entries.len() == 1 {
4062 let excerpt = this.update(&mut cx, |editor, cx| {
4063 editor
4064 .buffer()
4065 .read(cx)
4066 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4067 })?;
4068 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4069 if excerpted_buffer == *buffer {
4070 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4071 let excerpt_range = excerpt_range.to_offset(buffer);
4072 buffer
4073 .edited_ranges_for_transaction::<usize>(transaction)
4074 .all(|range| {
4075 excerpt_range.start <= range.start
4076 && excerpt_range.end >= range.end
4077 })
4078 })?;
4079
4080 if all_edits_within_excerpt {
4081 return Ok(());
4082 }
4083 }
4084 }
4085 }
4086 } else {
4087 return Ok(());
4088 }
4089
4090 let mut ranges_to_highlight = Vec::new();
4091 let excerpt_buffer = cx.new_model(|cx| {
4092 let mut multibuffer =
4093 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4094 for (buffer_handle, transaction) in &entries {
4095 let buffer = buffer_handle.read(cx);
4096 ranges_to_highlight.extend(
4097 multibuffer.push_excerpts_with_context_lines(
4098 buffer_handle.clone(),
4099 buffer
4100 .edited_ranges_for_transaction::<usize>(transaction)
4101 .collect(),
4102 DEFAULT_MULTIBUFFER_CONTEXT,
4103 cx,
4104 ),
4105 );
4106 }
4107 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4108 multibuffer
4109 })?;
4110
4111 workspace.update(&mut cx, |workspace, cx| {
4112 let project = workspace.project().clone();
4113 let editor =
4114 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4115 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
4116 editor.update(cx, |editor, cx| {
4117 editor.highlight_background::<Self>(
4118 &ranges_to_highlight,
4119 |theme| theme.editor_highlighted_line_background,
4120 cx,
4121 );
4122 });
4123 })?;
4124
4125 Ok(())
4126 }
4127
4128 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4129 let project = self.project.clone()?;
4130 let buffer = self.buffer.read(cx);
4131 let newest_selection = self.selections.newest_anchor().clone();
4132 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4133 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4134 if start_buffer != end_buffer {
4135 return None;
4136 }
4137
4138 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4139 cx.background_executor()
4140 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4141 .await;
4142
4143 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4144 project.code_actions(&start_buffer, start..end, cx)
4145 }) {
4146 code_actions.await
4147 } else {
4148 Vec::new()
4149 };
4150
4151 this.update(&mut cx, |this, cx| {
4152 this.available_code_actions = if actions.is_empty() {
4153 None
4154 } else {
4155 Some((
4156 Location {
4157 buffer: start_buffer,
4158 range: start..end,
4159 },
4160 actions.into(),
4161 ))
4162 };
4163 cx.notify();
4164 })
4165 .log_err();
4166 }));
4167 None
4168 }
4169
4170 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4171 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4172 self.show_git_blame_inline = false;
4173
4174 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4175 cx.background_executor().timer(delay).await;
4176
4177 this.update(&mut cx, |this, cx| {
4178 this.show_git_blame_inline = true;
4179 cx.notify();
4180 })
4181 .log_err();
4182 }));
4183 }
4184 }
4185
4186 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4187 if self.pending_rename.is_some() {
4188 return None;
4189 }
4190
4191 let project = self.project.clone()?;
4192 let buffer = self.buffer.read(cx);
4193 let newest_selection = self.selections.newest_anchor().clone();
4194 let cursor_position = newest_selection.head();
4195 let (cursor_buffer, cursor_buffer_position) =
4196 buffer.text_anchor_for_position(cursor_position, cx)?;
4197 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4198 if cursor_buffer != tail_buffer {
4199 return None;
4200 }
4201
4202 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4203 cx.background_executor()
4204 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4205 .await;
4206
4207 let highlights = if let Some(highlights) = project
4208 .update(&mut cx, |project, cx| {
4209 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4210 })
4211 .log_err()
4212 {
4213 highlights.await.log_err()
4214 } else {
4215 None
4216 };
4217
4218 if let Some(highlights) = highlights {
4219 this.update(&mut cx, |this, cx| {
4220 if this.pending_rename.is_some() {
4221 return;
4222 }
4223
4224 let buffer_id = cursor_position.buffer_id;
4225 let buffer = this.buffer.read(cx);
4226 if !buffer
4227 .text_anchor_for_position(cursor_position, cx)
4228 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4229 {
4230 return;
4231 }
4232
4233 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4234 let mut write_ranges = Vec::new();
4235 let mut read_ranges = Vec::new();
4236 for highlight in highlights {
4237 for (excerpt_id, excerpt_range) in
4238 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4239 {
4240 let start = highlight
4241 .range
4242 .start
4243 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4244 let end = highlight
4245 .range
4246 .end
4247 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4248 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4249 continue;
4250 }
4251
4252 let range = Anchor {
4253 buffer_id,
4254 excerpt_id: excerpt_id,
4255 text_anchor: start,
4256 }..Anchor {
4257 buffer_id,
4258 excerpt_id,
4259 text_anchor: end,
4260 };
4261 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4262 write_ranges.push(range);
4263 } else {
4264 read_ranges.push(range);
4265 }
4266 }
4267 }
4268
4269 this.highlight_background::<DocumentHighlightRead>(
4270 &read_ranges,
4271 |theme| theme.editor_document_highlight_read_background,
4272 cx,
4273 );
4274 this.highlight_background::<DocumentHighlightWrite>(
4275 &write_ranges,
4276 |theme| theme.editor_document_highlight_write_background,
4277 cx,
4278 );
4279 cx.notify();
4280 })
4281 .log_err();
4282 }
4283 }));
4284 None
4285 }
4286
4287 fn refresh_inline_completion(
4288 &mut self,
4289 debounce: bool,
4290 cx: &mut ViewContext<Self>,
4291 ) -> Option<()> {
4292 let provider = self.inline_completion_provider()?;
4293 let cursor = self.selections.newest_anchor().head();
4294 let (buffer, cursor_buffer_position) =
4295 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4296 if !self.show_inline_completions
4297 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4298 {
4299 self.discard_inline_completion(cx);
4300 return None;
4301 }
4302
4303 self.update_visible_inline_completion(cx);
4304 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4305 Some(())
4306 }
4307
4308 fn cycle_inline_completion(
4309 &mut self,
4310 direction: Direction,
4311 cx: &mut ViewContext<Self>,
4312 ) -> Option<()> {
4313 let provider = self.inline_completion_provider()?;
4314 let cursor = self.selections.newest_anchor().head();
4315 let (buffer, cursor_buffer_position) =
4316 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4317 if !self.show_inline_completions
4318 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4319 {
4320 return None;
4321 }
4322
4323 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4324 self.update_visible_inline_completion(cx);
4325
4326 Some(())
4327 }
4328
4329 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4330 if !self.has_active_inline_completion(cx) {
4331 self.refresh_inline_completion(false, cx);
4332 return;
4333 }
4334
4335 self.update_visible_inline_completion(cx);
4336 }
4337
4338 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4339 self.show_cursor_names(cx);
4340 }
4341
4342 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4343 self.show_cursor_names = true;
4344 cx.notify();
4345 cx.spawn(|this, mut cx| async move {
4346 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4347 this.update(&mut cx, |this, cx| {
4348 this.show_cursor_names = false;
4349 cx.notify()
4350 })
4351 .ok()
4352 })
4353 .detach();
4354 }
4355
4356 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4357 if self.has_active_inline_completion(cx) {
4358 self.cycle_inline_completion(Direction::Next, cx);
4359 } else {
4360 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4361 if is_copilot_disabled {
4362 cx.propagate();
4363 }
4364 }
4365 }
4366
4367 pub fn previous_inline_completion(
4368 &mut self,
4369 _: &PreviousInlineCompletion,
4370 cx: &mut ViewContext<Self>,
4371 ) {
4372 if self.has_active_inline_completion(cx) {
4373 self.cycle_inline_completion(Direction::Prev, cx);
4374 } else {
4375 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4376 if is_copilot_disabled {
4377 cx.propagate();
4378 }
4379 }
4380 }
4381
4382 fn accept_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4383 if let Some(completion) = self.take_active_inline_completion(cx) {
4384 if let Some(provider) = self.inline_completion_provider() {
4385 provider.accept(cx);
4386 }
4387
4388 cx.emit(EditorEvent::InputHandled {
4389 utf16_range_to_replace: None,
4390 text: completion.text.to_string().into(),
4391 });
4392 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4393 self.refresh_inline_completion(true, cx);
4394 cx.notify();
4395 true
4396 } else {
4397 false
4398 }
4399 }
4400
4401 pub fn accept_partial_inline_completion(
4402 &mut self,
4403 _: &AcceptPartialInlineCompletion,
4404 cx: &mut ViewContext<Self>,
4405 ) {
4406 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4407 if let Some(completion) = self.take_active_inline_completion(cx) {
4408 let mut partial_completion = completion
4409 .text
4410 .chars()
4411 .by_ref()
4412 .take_while(|c| c.is_alphabetic())
4413 .collect::<String>();
4414 if partial_completion.is_empty() {
4415 partial_completion = completion
4416 .text
4417 .chars()
4418 .by_ref()
4419 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4420 .collect::<String>();
4421 }
4422
4423 cx.emit(EditorEvent::InputHandled {
4424 utf16_range_to_replace: None,
4425 text: partial_completion.clone().into(),
4426 });
4427 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4428 self.refresh_inline_completion(true, cx);
4429 cx.notify();
4430 }
4431 }
4432 }
4433
4434 fn discard_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> bool {
4435 if let Some(provider) = self.inline_completion_provider() {
4436 provider.discard(cx);
4437 }
4438
4439 self.take_active_inline_completion(cx).is_some()
4440 }
4441
4442 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
4443 if let Some(completion) = self.active_inline_completion.as_ref() {
4444 let buffer = self.buffer.read(cx).read(cx);
4445 completion.position.is_valid(&buffer)
4446 } else {
4447 false
4448 }
4449 }
4450
4451 fn take_active_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4452 let completion = self.active_inline_completion.take()?;
4453 self.display_map.update(cx, |map, cx| {
4454 map.splice_inlays(vec![completion.id], Default::default(), cx);
4455 });
4456 let buffer = self.buffer.read(cx).read(cx);
4457
4458 if completion.position.is_valid(&buffer) {
4459 Some(completion)
4460 } else {
4461 None
4462 }
4463 }
4464
4465 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
4466 let selection = self.selections.newest_anchor();
4467 let cursor = selection.head();
4468
4469 if self.context_menu.read().is_none()
4470 && self.completion_tasks.is_empty()
4471 && selection.start == selection.end
4472 {
4473 if let Some(provider) = self.inline_completion_provider() {
4474 if let Some((buffer, cursor_buffer_position)) =
4475 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4476 {
4477 if let Some(text) =
4478 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
4479 {
4480 let text = Rope::from(text);
4481 let mut to_remove = Vec::new();
4482 if let Some(completion) = self.active_inline_completion.take() {
4483 to_remove.push(completion.id);
4484 }
4485
4486 let completion_inlay =
4487 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4488 self.active_inline_completion = Some(completion_inlay.clone());
4489 self.display_map.update(cx, move |map, cx| {
4490 map.splice_inlays(to_remove, vec![completion_inlay], cx)
4491 });
4492 cx.notify();
4493 return;
4494 }
4495 }
4496 }
4497 }
4498
4499 self.discard_inline_completion(cx);
4500 }
4501
4502 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4503 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4504 }
4505
4506 fn render_code_actions_indicator(
4507 &self,
4508 _style: &EditorStyle,
4509 row: DisplayRow,
4510 is_active: bool,
4511 cx: &mut ViewContext<Self>,
4512 ) -> Option<IconButton> {
4513 if self.available_code_actions.is_some() {
4514 Some(
4515 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4516 .icon_size(IconSize::XSmall)
4517 .size(ui::ButtonSize::None)
4518 .icon_color(Color::Muted)
4519 .selected(is_active)
4520 .on_click(cx.listener(move |editor, _e, cx| {
4521 editor.focus(cx);
4522 editor.toggle_code_actions(
4523 &ToggleCodeActions {
4524 deployed_from_indicator: Some(row),
4525 },
4526 cx,
4527 );
4528 })),
4529 )
4530 } else {
4531 None
4532 }
4533 }
4534
4535 fn clear_tasks(&mut self) {
4536 self.tasks.clear()
4537 }
4538
4539 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: (usize, RunnableTasks)) {
4540 if let Some(_) = self.tasks.insert(key, value) {
4541 // This case should hopefully be rare, but just in case...
4542 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
4543 }
4544 }
4545
4546 fn render_run_indicator(
4547 &self,
4548 _style: &EditorStyle,
4549 is_active: bool,
4550 row: DisplayRow,
4551 cx: &mut ViewContext<Self>,
4552 ) -> IconButton {
4553 IconButton::new("run_indicator", ui::IconName::Play)
4554 .icon_size(IconSize::XSmall)
4555 .size(ui::ButtonSize::None)
4556 .icon_color(Color::Muted)
4557 .selected(is_active)
4558 .on_click(cx.listener(move |editor, _e, cx| {
4559 editor.focus(cx);
4560 editor.toggle_code_actions(
4561 &ToggleCodeActions {
4562 deployed_from_indicator: Some(row),
4563 },
4564 cx,
4565 );
4566 }))
4567 }
4568
4569 pub fn render_fold_indicators(
4570 &mut self,
4571 fold_data: Vec<Option<(FoldStatus, MultiBufferRow, bool)>>,
4572 _style: &EditorStyle,
4573 gutter_hovered: bool,
4574 _line_height: Pixels,
4575 _gutter_margin: Pixels,
4576 cx: &mut ViewContext<Self>,
4577 ) -> Vec<Option<AnyElement>> {
4578 fold_data
4579 .iter()
4580 .enumerate()
4581 .map(|(ix, fold_data)| {
4582 fold_data
4583 .map(|(fold_status, buffer_row, active)| {
4584 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
4585 IconButton::new(ix, ui::IconName::ChevronDown)
4586 .on_click(cx.listener(move |this, _e, cx| match fold_status {
4587 FoldStatus::Folded => {
4588 this.unfold_at(&UnfoldAt { buffer_row }, cx);
4589 }
4590 FoldStatus::Foldable => {
4591 this.fold_at(&FoldAt { buffer_row }, cx);
4592 }
4593 }))
4594 .icon_color(ui::Color::Muted)
4595 .icon_size(ui::IconSize::Small)
4596 .selected(fold_status == FoldStatus::Folded)
4597 .selected_icon(ui::IconName::ChevronRight)
4598 .size(ui::ButtonSize::None)
4599 .into_any_element()
4600 })
4601 })
4602 .flatten()
4603 })
4604 .collect()
4605 }
4606
4607 pub fn context_menu_visible(&self) -> bool {
4608 self.context_menu
4609 .read()
4610 .as_ref()
4611 .map_or(false, |menu| menu.visible())
4612 }
4613
4614 fn render_context_menu(
4615 &self,
4616 cursor_position: DisplayPoint,
4617 style: &EditorStyle,
4618 max_height: Pixels,
4619 cx: &mut ViewContext<Editor>,
4620 ) -> Option<(ContextMenuOrigin, AnyElement)> {
4621 self.context_menu.read().as_ref().map(|menu| {
4622 menu.render(
4623 cursor_position,
4624 style,
4625 max_height,
4626 self.workspace.as_ref().map(|(w, _)| w.clone()),
4627 cx,
4628 )
4629 })
4630 }
4631
4632 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4633 cx.notify();
4634 self.completion_tasks.clear();
4635 let context_menu = self.context_menu.write().take();
4636 if context_menu.is_some() {
4637 self.update_visible_inline_completion(cx);
4638 }
4639 context_menu
4640 }
4641
4642 pub fn insert_snippet(
4643 &mut self,
4644 insertion_ranges: &[Range<usize>],
4645 snippet: Snippet,
4646 cx: &mut ViewContext<Self>,
4647 ) -> Result<()> {
4648 struct Tabstop<T> {
4649 is_end_tabstop: bool,
4650 ranges: Vec<Range<T>>,
4651 }
4652
4653 let tabstops = self.buffer.update(cx, |buffer, cx| {
4654 let snippet_text: Arc<str> = snippet.text.clone().into();
4655 buffer.edit(
4656 insertion_ranges
4657 .iter()
4658 .cloned()
4659 .map(|range| (range, snippet_text.clone())),
4660 Some(AutoindentMode::EachLine),
4661 cx,
4662 );
4663
4664 let snapshot = &*buffer.read(cx);
4665 let snippet = &snippet;
4666 snippet
4667 .tabstops
4668 .iter()
4669 .map(|tabstop| {
4670 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
4671 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
4672 });
4673 let mut tabstop_ranges = tabstop
4674 .iter()
4675 .flat_map(|tabstop_range| {
4676 let mut delta = 0_isize;
4677 insertion_ranges.iter().map(move |insertion_range| {
4678 let insertion_start = insertion_range.start as isize + delta;
4679 delta +=
4680 snippet.text.len() as isize - insertion_range.len() as isize;
4681
4682 let start = ((insertion_start + tabstop_range.start) as usize)
4683 .min(snapshot.len());
4684 let end = ((insertion_start + tabstop_range.end) as usize)
4685 .min(snapshot.len());
4686 snapshot.anchor_before(start)..snapshot.anchor_after(end)
4687 })
4688 })
4689 .collect::<Vec<_>>();
4690 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4691
4692 Tabstop {
4693 is_end_tabstop,
4694 ranges: tabstop_ranges,
4695 }
4696 })
4697 .collect::<Vec<_>>()
4698 });
4699
4700 if let Some(tabstop) = tabstops.first() {
4701 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4702 s.select_ranges(tabstop.ranges.iter().cloned());
4703 });
4704
4705 // If we're already at the last tabstop and it's at the end of the snippet,
4706 // we're done, we don't need to keep the state around.
4707 if !tabstop.is_end_tabstop {
4708 let ranges = tabstops
4709 .into_iter()
4710 .map(|tabstop| tabstop.ranges)
4711 .collect::<Vec<_>>();
4712 self.snippet_stack.push(SnippetState {
4713 active_index: 0,
4714 ranges,
4715 });
4716 }
4717
4718 // Check whether the just-entered snippet ends with an auto-closable bracket.
4719 if self.autoclose_regions.is_empty() {
4720 let snapshot = self.buffer.read(cx).snapshot(cx);
4721 for selection in &mut self.selections.all::<Point>(cx) {
4722 let selection_head = selection.head();
4723 let Some(scope) = snapshot.language_scope_at(selection_head) else {
4724 continue;
4725 };
4726
4727 let mut bracket_pair = None;
4728 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
4729 let prev_chars = snapshot
4730 .reversed_chars_at(selection_head)
4731 .collect::<String>();
4732 for (pair, enabled) in scope.brackets() {
4733 if enabled
4734 && pair.close
4735 && prev_chars.starts_with(pair.start.as_str())
4736 && next_chars.starts_with(pair.end.as_str())
4737 {
4738 bracket_pair = Some(pair.clone());
4739 break;
4740 }
4741 }
4742 if let Some(pair) = bracket_pair {
4743 let start = snapshot.anchor_after(selection_head);
4744 let end = snapshot.anchor_after(selection_head);
4745 self.autoclose_regions.push(AutocloseRegion {
4746 selection_id: selection.id,
4747 range: start..end,
4748 pair,
4749 });
4750 }
4751 }
4752 }
4753 }
4754 Ok(())
4755 }
4756
4757 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4758 self.move_to_snippet_tabstop(Bias::Right, cx)
4759 }
4760
4761 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4762 self.move_to_snippet_tabstop(Bias::Left, cx)
4763 }
4764
4765 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4766 if let Some(mut snippet) = self.snippet_stack.pop() {
4767 match bias {
4768 Bias::Left => {
4769 if snippet.active_index > 0 {
4770 snippet.active_index -= 1;
4771 } else {
4772 self.snippet_stack.push(snippet);
4773 return false;
4774 }
4775 }
4776 Bias::Right => {
4777 if snippet.active_index + 1 < snippet.ranges.len() {
4778 snippet.active_index += 1;
4779 } else {
4780 self.snippet_stack.push(snippet);
4781 return false;
4782 }
4783 }
4784 }
4785 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4786 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4787 s.select_anchor_ranges(current_ranges.iter().cloned())
4788 });
4789 // If snippet state is not at the last tabstop, push it back on the stack
4790 if snippet.active_index + 1 < snippet.ranges.len() {
4791 self.snippet_stack.push(snippet);
4792 }
4793 return true;
4794 }
4795 }
4796
4797 false
4798 }
4799
4800 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4801 self.transact(cx, |this, cx| {
4802 this.select_all(&SelectAll, cx);
4803 this.insert("", cx);
4804 });
4805 }
4806
4807 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4808 self.transact(cx, |this, cx| {
4809 this.select_autoclose_pair(cx);
4810 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
4811 if !this.selections.line_mode {
4812 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4813 for selection in &mut selections {
4814 if selection.is_empty() {
4815 let old_head = selection.head();
4816 let mut new_head =
4817 movement::left(&display_map, old_head.to_display_point(&display_map))
4818 .to_point(&display_map);
4819 if let Some((buffer, line_buffer_range)) = display_map
4820 .buffer_snapshot
4821 .buffer_line_for_row(MultiBufferRow(old_head.row))
4822 {
4823 let indent_size =
4824 buffer.indent_size_for_line(line_buffer_range.start.row);
4825 let indent_len = match indent_size.kind {
4826 IndentKind::Space => {
4827 buffer.settings_at(line_buffer_range.start, cx).tab_size
4828 }
4829 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4830 };
4831 if old_head.column <= indent_size.len && old_head.column > 0 {
4832 let indent_len = indent_len.get();
4833 new_head = cmp::min(
4834 new_head,
4835 MultiBufferPoint::new(
4836 old_head.row,
4837 ((old_head.column - 1) / indent_len) * indent_len,
4838 ),
4839 );
4840 }
4841 }
4842
4843 selection.set_head(new_head, SelectionGoal::None);
4844 }
4845 }
4846 }
4847
4848 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4849 this.insert("", cx);
4850 this.refresh_inline_completion(true, cx);
4851 });
4852 }
4853
4854 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4855 self.transact(cx, |this, cx| {
4856 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4857 let line_mode = s.line_mode;
4858 s.move_with(|map, selection| {
4859 if selection.is_empty() && !line_mode {
4860 let cursor = movement::right(map, selection.head());
4861 selection.end = cursor;
4862 selection.reversed = true;
4863 selection.goal = SelectionGoal::None;
4864 }
4865 })
4866 });
4867 this.insert("", cx);
4868 this.refresh_inline_completion(true, cx);
4869 });
4870 }
4871
4872 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4873 if self.move_to_prev_snippet_tabstop(cx) {
4874 return;
4875 }
4876
4877 self.outdent(&Outdent, cx);
4878 }
4879
4880 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4881 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
4882 return;
4883 }
4884
4885 let mut selections = self.selections.all_adjusted(cx);
4886 let buffer = self.buffer.read(cx);
4887 let snapshot = buffer.snapshot(cx);
4888 let rows_iter = selections.iter().map(|s| s.head().row);
4889 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4890
4891 let mut edits = Vec::new();
4892 let mut prev_edited_row = 0;
4893 let mut row_delta = 0;
4894 for selection in &mut selections {
4895 if selection.start.row != prev_edited_row {
4896 row_delta = 0;
4897 }
4898 prev_edited_row = selection.end.row;
4899
4900 // If the selection is non-empty, then increase the indentation of the selected lines.
4901 if !selection.is_empty() {
4902 row_delta =
4903 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4904 continue;
4905 }
4906
4907 // If the selection is empty and the cursor is in the leading whitespace before the
4908 // suggested indentation, then auto-indent the line.
4909 let cursor = selection.head();
4910 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4911 if let Some(suggested_indent) =
4912 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4913 {
4914 if cursor.column < suggested_indent.len
4915 && cursor.column <= current_indent.len
4916 && current_indent.len <= suggested_indent.len
4917 {
4918 selection.start = Point::new(cursor.row, suggested_indent.len);
4919 selection.end = selection.start;
4920 if row_delta == 0 {
4921 edits.extend(Buffer::edit_for_indent_size_adjustment(
4922 cursor.row,
4923 current_indent,
4924 suggested_indent,
4925 ));
4926 row_delta = suggested_indent.len - current_indent.len;
4927 }
4928 continue;
4929 }
4930 }
4931
4932 // Accept copilot completion if there is only one selection and the cursor is not
4933 // in the leading whitespace.
4934 if self.selections.count() == 1
4935 && cursor.column >= current_indent.len
4936 && self.has_active_inline_completion(cx)
4937 {
4938 self.accept_inline_completion(cx);
4939 return;
4940 }
4941
4942 // Otherwise, insert a hard or soft tab.
4943 let settings = buffer.settings_at(cursor, cx);
4944 let tab_size = if settings.hard_tabs {
4945 IndentSize::tab()
4946 } else {
4947 let tab_size = settings.tab_size.get();
4948 let char_column = snapshot
4949 .text_for_range(Point::new(cursor.row, 0)..cursor)
4950 .flat_map(str::chars)
4951 .count()
4952 + row_delta as usize;
4953 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4954 IndentSize::spaces(chars_to_next_tab_stop)
4955 };
4956 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4957 selection.end = selection.start;
4958 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4959 row_delta += tab_size.len;
4960 }
4961
4962 self.transact(cx, |this, cx| {
4963 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4964 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4965 this.refresh_inline_completion(true, cx);
4966 });
4967 }
4968
4969 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4970 if self.read_only(cx) {
4971 return;
4972 }
4973 let mut selections = self.selections.all::<Point>(cx);
4974 let mut prev_edited_row = 0;
4975 let mut row_delta = 0;
4976 let mut edits = Vec::new();
4977 let buffer = self.buffer.read(cx);
4978 let snapshot = buffer.snapshot(cx);
4979 for selection in &mut selections {
4980 if selection.start.row != prev_edited_row {
4981 row_delta = 0;
4982 }
4983 prev_edited_row = selection.end.row;
4984
4985 row_delta =
4986 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4987 }
4988
4989 self.transact(cx, |this, cx| {
4990 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4991 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4992 });
4993 }
4994
4995 fn indent_selection(
4996 buffer: &MultiBuffer,
4997 snapshot: &MultiBufferSnapshot,
4998 selection: &mut Selection<Point>,
4999 edits: &mut Vec<(Range<Point>, String)>,
5000 delta_for_start_row: u32,
5001 cx: &AppContext,
5002 ) -> u32 {
5003 let settings = buffer.settings_at(selection.start, cx);
5004 let tab_size = settings.tab_size.get();
5005 let indent_kind = if settings.hard_tabs {
5006 IndentKind::Tab
5007 } else {
5008 IndentKind::Space
5009 };
5010 let mut start_row = selection.start.row;
5011 let mut end_row = selection.end.row + 1;
5012
5013 // If a selection ends at the beginning of a line, don't indent
5014 // that last line.
5015 if selection.end.column == 0 && selection.end.row > selection.start.row {
5016 end_row -= 1;
5017 }
5018
5019 // Avoid re-indenting a row that has already been indented by a
5020 // previous selection, but still update this selection's column
5021 // to reflect that indentation.
5022 if delta_for_start_row > 0 {
5023 start_row += 1;
5024 selection.start.column += delta_for_start_row;
5025 if selection.end.row == selection.start.row {
5026 selection.end.column += delta_for_start_row;
5027 }
5028 }
5029
5030 let mut delta_for_end_row = 0;
5031 let has_multiple_rows = start_row + 1 != end_row;
5032 for row in start_row..end_row {
5033 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5034 let indent_delta = match (current_indent.kind, indent_kind) {
5035 (IndentKind::Space, IndentKind::Space) => {
5036 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5037 IndentSize::spaces(columns_to_next_tab_stop)
5038 }
5039 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5040 (_, IndentKind::Tab) => IndentSize::tab(),
5041 };
5042
5043 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5044 0
5045 } else {
5046 selection.start.column
5047 };
5048 let row_start = Point::new(row, start);
5049 edits.push((
5050 row_start..row_start,
5051 indent_delta.chars().collect::<String>(),
5052 ));
5053
5054 // Update this selection's endpoints to reflect the indentation.
5055 if row == selection.start.row {
5056 selection.start.column += indent_delta.len;
5057 }
5058 if row == selection.end.row {
5059 selection.end.column += indent_delta.len;
5060 delta_for_end_row = indent_delta.len;
5061 }
5062 }
5063
5064 if selection.start.row == selection.end.row {
5065 delta_for_start_row + delta_for_end_row
5066 } else {
5067 delta_for_end_row
5068 }
5069 }
5070
5071 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5072 if self.read_only(cx) {
5073 return;
5074 }
5075 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5076 let selections = self.selections.all::<Point>(cx);
5077 let mut deletion_ranges = Vec::new();
5078 let mut last_outdent = None;
5079 {
5080 let buffer = self.buffer.read(cx);
5081 let snapshot = buffer.snapshot(cx);
5082 for selection in &selections {
5083 let settings = buffer.settings_at(selection.start, cx);
5084 let tab_size = settings.tab_size.get();
5085 let mut rows = selection.spanned_rows(false, &display_map);
5086
5087 // Avoid re-outdenting a row that has already been outdented by a
5088 // previous selection.
5089 if let Some(last_row) = last_outdent {
5090 if last_row == rows.start {
5091 rows.start = rows.start.next_row();
5092 }
5093 }
5094 let has_multiple_rows = rows.len() > 1;
5095 for row in rows.iter_rows() {
5096 let indent_size = snapshot.indent_size_for_line(row);
5097 if indent_size.len > 0 {
5098 let deletion_len = match indent_size.kind {
5099 IndentKind::Space => {
5100 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5101 if columns_to_prev_tab_stop == 0 {
5102 tab_size
5103 } else {
5104 columns_to_prev_tab_stop
5105 }
5106 }
5107 IndentKind::Tab => 1,
5108 };
5109 let start = if has_multiple_rows
5110 || deletion_len > selection.start.column
5111 || indent_size.len < selection.start.column
5112 {
5113 0
5114 } else {
5115 selection.start.column - deletion_len
5116 };
5117 deletion_ranges.push(
5118 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5119 );
5120 last_outdent = Some(row);
5121 }
5122 }
5123 }
5124 }
5125
5126 self.transact(cx, |this, cx| {
5127 this.buffer.update(cx, |buffer, cx| {
5128 let empty_str: Arc<str> = "".into();
5129 buffer.edit(
5130 deletion_ranges
5131 .into_iter()
5132 .map(|range| (range, empty_str.clone())),
5133 None,
5134 cx,
5135 );
5136 });
5137 let selections = this.selections.all::<usize>(cx);
5138 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5139 });
5140 }
5141
5142 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5143 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5144 let selections = self.selections.all::<Point>(cx);
5145
5146 let mut new_cursors = Vec::new();
5147 let mut edit_ranges = Vec::new();
5148 let mut selections = selections.iter().peekable();
5149 while let Some(selection) = selections.next() {
5150 let mut rows = selection.spanned_rows(false, &display_map);
5151 let goal_display_column = selection.head().to_display_point(&display_map).column();
5152
5153 // Accumulate contiguous regions of rows that we want to delete.
5154 while let Some(next_selection) = selections.peek() {
5155 let next_rows = next_selection.spanned_rows(false, &display_map);
5156 if next_rows.start <= rows.end {
5157 rows.end = next_rows.end;
5158 selections.next().unwrap();
5159 } else {
5160 break;
5161 }
5162 }
5163
5164 let buffer = &display_map.buffer_snapshot;
5165 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5166 let edit_end;
5167 let cursor_buffer_row;
5168 if buffer.max_point().row >= rows.end.0 {
5169 // If there's a line after the range, delete the \n from the end of the row range
5170 // and position the cursor on the next line.
5171 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5172 cursor_buffer_row = rows.end;
5173 } else {
5174 // If there isn't a line after the range, delete the \n from the line before the
5175 // start of the row range and position the cursor there.
5176 edit_start = edit_start.saturating_sub(1);
5177 edit_end = buffer.len();
5178 cursor_buffer_row = rows.start.previous_row();
5179 }
5180
5181 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5182 *cursor.column_mut() =
5183 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5184
5185 new_cursors.push((
5186 selection.id,
5187 buffer.anchor_after(cursor.to_point(&display_map)),
5188 ));
5189 edit_ranges.push(edit_start..edit_end);
5190 }
5191
5192 self.transact(cx, |this, cx| {
5193 let buffer = this.buffer.update(cx, |buffer, cx| {
5194 let empty_str: Arc<str> = "".into();
5195 buffer.edit(
5196 edit_ranges
5197 .into_iter()
5198 .map(|range| (range, empty_str.clone())),
5199 None,
5200 cx,
5201 );
5202 buffer.snapshot(cx)
5203 });
5204 let new_selections = new_cursors
5205 .into_iter()
5206 .map(|(id, cursor)| {
5207 let cursor = cursor.to_point(&buffer);
5208 Selection {
5209 id,
5210 start: cursor,
5211 end: cursor,
5212 reversed: false,
5213 goal: SelectionGoal::None,
5214 }
5215 })
5216 .collect();
5217
5218 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5219 s.select(new_selections);
5220 });
5221 });
5222 }
5223
5224 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5225 if self.read_only(cx) {
5226 return;
5227 }
5228 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5229 for selection in self.selections.all::<Point>(cx) {
5230 let start = MultiBufferRow(selection.start.row);
5231 let end = if selection.start.row == selection.end.row {
5232 MultiBufferRow(selection.start.row + 1)
5233 } else {
5234 MultiBufferRow(selection.end.row)
5235 };
5236
5237 if let Some(last_row_range) = row_ranges.last_mut() {
5238 if start <= last_row_range.end {
5239 last_row_range.end = end;
5240 continue;
5241 }
5242 }
5243 row_ranges.push(start..end);
5244 }
5245
5246 let snapshot = self.buffer.read(cx).snapshot(cx);
5247 let mut cursor_positions = Vec::new();
5248 for row_range in &row_ranges {
5249 let anchor = snapshot.anchor_before(Point::new(
5250 row_range.end.previous_row().0,
5251 snapshot.line_len(row_range.end.previous_row()),
5252 ));
5253 cursor_positions.push(anchor..anchor);
5254 }
5255
5256 self.transact(cx, |this, cx| {
5257 for row_range in row_ranges.into_iter().rev() {
5258 for row in row_range.iter_rows().rev() {
5259 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5260 let next_line_row = row.next_row();
5261 let indent = snapshot.indent_size_for_line(next_line_row);
5262 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5263
5264 let replace = if snapshot.line_len(next_line_row) > indent.len {
5265 " "
5266 } else {
5267 ""
5268 };
5269
5270 this.buffer.update(cx, |buffer, cx| {
5271 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5272 });
5273 }
5274 }
5275
5276 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5277 s.select_anchor_ranges(cursor_positions)
5278 });
5279 });
5280 }
5281
5282 pub fn sort_lines_case_sensitive(
5283 &mut self,
5284 _: &SortLinesCaseSensitive,
5285 cx: &mut ViewContext<Self>,
5286 ) {
5287 self.manipulate_lines(cx, |lines| lines.sort())
5288 }
5289
5290 pub fn sort_lines_case_insensitive(
5291 &mut self,
5292 _: &SortLinesCaseInsensitive,
5293 cx: &mut ViewContext<Self>,
5294 ) {
5295 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5296 }
5297
5298 pub fn unique_lines_case_insensitive(
5299 &mut self,
5300 _: &UniqueLinesCaseInsensitive,
5301 cx: &mut ViewContext<Self>,
5302 ) {
5303 self.manipulate_lines(cx, |lines| {
5304 let mut seen = HashSet::default();
5305 lines.retain(|line| seen.insert(line.to_lowercase()));
5306 })
5307 }
5308
5309 pub fn unique_lines_case_sensitive(
5310 &mut self,
5311 _: &UniqueLinesCaseSensitive,
5312 cx: &mut ViewContext<Self>,
5313 ) {
5314 self.manipulate_lines(cx, |lines| {
5315 let mut seen = HashSet::default();
5316 lines.retain(|line| seen.insert(*line));
5317 })
5318 }
5319
5320 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5321 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5322 if !revert_changes.is_empty() {
5323 self.transact(cx, |editor, cx| {
5324 editor.buffer().update(cx, |multi_buffer, cx| {
5325 for (buffer_id, changes) in revert_changes {
5326 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
5327 buffer.update(cx, |buffer, cx| {
5328 buffer.edit(
5329 changes.into_iter().map(|(range, text)| {
5330 (range, text.to_string().map(Arc::<str>::from))
5331 }),
5332 None,
5333 cx,
5334 );
5335 });
5336 }
5337 }
5338 });
5339 editor.change_selections(None, cx, |selections| selections.refresh());
5340 });
5341 }
5342 }
5343
5344 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5345 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5346 let project_path = buffer.read(cx).project_path(cx)?;
5347 let project = self.project.as_ref()?.read(cx);
5348 let entry = project.entry_for_path(&project_path, cx)?;
5349 let abs_path = project.absolute_path(&project_path, cx)?;
5350 let parent = if entry.is_symlink {
5351 abs_path.canonicalize().ok()?
5352 } else {
5353 abs_path
5354 }
5355 .parent()?
5356 .to_path_buf();
5357 Some(parent)
5358 }) {
5359 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5360 }
5361 }
5362
5363 fn gather_revert_changes(
5364 &mut self,
5365 selections: &[Selection<Anchor>],
5366 cx: &mut ViewContext<'_, Editor>,
5367 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5368 let mut revert_changes = HashMap::default();
5369 self.buffer.update(cx, |multi_buffer, cx| {
5370 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5371 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5372 Self::prepare_revert_change(&mut revert_changes, &multi_buffer, &hunk, cx);
5373 }
5374 });
5375 revert_changes
5376 }
5377
5378 fn prepare_revert_change(
5379 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5380 multi_buffer: &MultiBuffer,
5381 hunk: &DiffHunk<MultiBufferRow>,
5382 cx: &mut AppContext,
5383 ) -> Option<()> {
5384 let buffer = multi_buffer.buffer(hunk.buffer_id)?;
5385 let buffer = buffer.read(cx);
5386 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5387 let buffer_snapshot = buffer.snapshot();
5388 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5389 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5390 probe
5391 .0
5392 .start
5393 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5394 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5395 }) {
5396 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5397 Some(())
5398 } else {
5399 None
5400 }
5401 }
5402
5403 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5404 self.manipulate_lines(cx, |lines| lines.reverse())
5405 }
5406
5407 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5408 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5409 }
5410
5411 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5412 where
5413 Fn: FnMut(&mut Vec<&str>),
5414 {
5415 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5416 let buffer = self.buffer.read(cx).snapshot(cx);
5417
5418 let mut edits = Vec::new();
5419
5420 let selections = self.selections.all::<Point>(cx);
5421 let mut selections = selections.iter().peekable();
5422 let mut contiguous_row_selections = Vec::new();
5423 let mut new_selections = Vec::new();
5424 let mut added_lines = 0;
5425 let mut removed_lines = 0;
5426
5427 while let Some(selection) = selections.next() {
5428 let (start_row, end_row) = consume_contiguous_rows(
5429 &mut contiguous_row_selections,
5430 selection,
5431 &display_map,
5432 &mut selections,
5433 );
5434
5435 let start_point = Point::new(start_row.0, 0);
5436 let end_point = Point::new(
5437 end_row.previous_row().0,
5438 buffer.line_len(end_row.previous_row()),
5439 );
5440 let text = buffer
5441 .text_for_range(start_point..end_point)
5442 .collect::<String>();
5443
5444 let mut lines = text.split('\n').collect_vec();
5445
5446 let lines_before = lines.len();
5447 callback(&mut lines);
5448 let lines_after = lines.len();
5449
5450 edits.push((start_point..end_point, lines.join("\n")));
5451
5452 // Selections must change based on added and removed line count
5453 let start_row =
5454 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5455 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5456 new_selections.push(Selection {
5457 id: selection.id,
5458 start: start_row,
5459 end: end_row,
5460 goal: SelectionGoal::None,
5461 reversed: selection.reversed,
5462 });
5463
5464 if lines_after > lines_before {
5465 added_lines += lines_after - lines_before;
5466 } else if lines_before > lines_after {
5467 removed_lines += lines_before - lines_after;
5468 }
5469 }
5470
5471 self.transact(cx, |this, cx| {
5472 let buffer = this.buffer.update(cx, |buffer, cx| {
5473 buffer.edit(edits, None, cx);
5474 buffer.snapshot(cx)
5475 });
5476
5477 // Recalculate offsets on newly edited buffer
5478 let new_selections = new_selections
5479 .iter()
5480 .map(|s| {
5481 let start_point = Point::new(s.start.0, 0);
5482 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
5483 Selection {
5484 id: s.id,
5485 start: buffer.point_to_offset(start_point),
5486 end: buffer.point_to_offset(end_point),
5487 goal: s.goal,
5488 reversed: s.reversed,
5489 }
5490 })
5491 .collect();
5492
5493 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5494 s.select(new_selections);
5495 });
5496
5497 this.request_autoscroll(Autoscroll::fit(), cx);
5498 });
5499 }
5500
5501 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
5502 self.manipulate_text(cx, |text| text.to_uppercase())
5503 }
5504
5505 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
5506 self.manipulate_text(cx, |text| text.to_lowercase())
5507 }
5508
5509 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
5510 self.manipulate_text(cx, |text| {
5511 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5512 // https://github.com/rutrum/convert-case/issues/16
5513 text.split('\n')
5514 .map(|line| line.to_case(Case::Title))
5515 .join("\n")
5516 })
5517 }
5518
5519 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
5520 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
5521 }
5522
5523 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
5524 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
5525 }
5526
5527 pub fn convert_to_upper_camel_case(
5528 &mut self,
5529 _: &ConvertToUpperCamelCase,
5530 cx: &mut ViewContext<Self>,
5531 ) {
5532 self.manipulate_text(cx, |text| {
5533 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
5534 // https://github.com/rutrum/convert-case/issues/16
5535 text.split('\n')
5536 .map(|line| line.to_case(Case::UpperCamel))
5537 .join("\n")
5538 })
5539 }
5540
5541 pub fn convert_to_lower_camel_case(
5542 &mut self,
5543 _: &ConvertToLowerCamelCase,
5544 cx: &mut ViewContext<Self>,
5545 ) {
5546 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
5547 }
5548
5549 pub fn convert_to_opposite_case(
5550 &mut self,
5551 _: &ConvertToOppositeCase,
5552 cx: &mut ViewContext<Self>,
5553 ) {
5554 self.manipulate_text(cx, |text| {
5555 text.chars()
5556 .fold(String::with_capacity(text.len()), |mut t, c| {
5557 if c.is_uppercase() {
5558 t.extend(c.to_lowercase());
5559 } else {
5560 t.extend(c.to_uppercase());
5561 }
5562 t
5563 })
5564 })
5565 }
5566
5567 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5568 where
5569 Fn: FnMut(&str) -> String,
5570 {
5571 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5572 let buffer = self.buffer.read(cx).snapshot(cx);
5573
5574 let mut new_selections = Vec::new();
5575 let mut edits = Vec::new();
5576 let mut selection_adjustment = 0i32;
5577
5578 for selection in self.selections.all::<usize>(cx) {
5579 let selection_is_empty = selection.is_empty();
5580
5581 let (start, end) = if selection_is_empty {
5582 let word_range = movement::surrounding_word(
5583 &display_map,
5584 selection.start.to_display_point(&display_map),
5585 );
5586 let start = word_range.start.to_offset(&display_map, Bias::Left);
5587 let end = word_range.end.to_offset(&display_map, Bias::Left);
5588 (start, end)
5589 } else {
5590 (selection.start, selection.end)
5591 };
5592
5593 let text = buffer.text_for_range(start..end).collect::<String>();
5594 let old_length = text.len() as i32;
5595 let text = callback(&text);
5596
5597 new_selections.push(Selection {
5598 start: (start as i32 - selection_adjustment) as usize,
5599 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
5600 goal: SelectionGoal::None,
5601 ..selection
5602 });
5603
5604 selection_adjustment += old_length - text.len() as i32;
5605
5606 edits.push((start..end, text));
5607 }
5608
5609 self.transact(cx, |this, cx| {
5610 this.buffer.update(cx, |buffer, cx| {
5611 buffer.edit(edits, None, cx);
5612 });
5613
5614 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5615 s.select(new_selections);
5616 });
5617
5618 this.request_autoscroll(Autoscroll::fit(), cx);
5619 });
5620 }
5621
5622 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
5623 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5624 let buffer = &display_map.buffer_snapshot;
5625 let selections = self.selections.all::<Point>(cx);
5626
5627 let mut edits = Vec::new();
5628 let mut selections_iter = selections.iter().peekable();
5629 while let Some(selection) = selections_iter.next() {
5630 // Avoid duplicating the same lines twice.
5631 let mut rows = selection.spanned_rows(false, &display_map);
5632
5633 while let Some(next_selection) = selections_iter.peek() {
5634 let next_rows = next_selection.spanned_rows(false, &display_map);
5635 if next_rows.start < rows.end {
5636 rows.end = next_rows.end;
5637 selections_iter.next().unwrap();
5638 } else {
5639 break;
5640 }
5641 }
5642
5643 // Copy the text from the selected row region and splice it either at the start
5644 // or end of the region.
5645 let start = Point::new(rows.start.0, 0);
5646 let end = Point::new(
5647 rows.end.previous_row().0,
5648 buffer.line_len(rows.end.previous_row()),
5649 );
5650 let text = buffer
5651 .text_for_range(start..end)
5652 .chain(Some("\n"))
5653 .collect::<String>();
5654 let insert_location = if upwards {
5655 Point::new(rows.end.0, 0)
5656 } else {
5657 start
5658 };
5659 edits.push((insert_location..insert_location, text));
5660 }
5661
5662 self.transact(cx, |this, cx| {
5663 this.buffer.update(cx, |buffer, cx| {
5664 buffer.edit(edits, None, cx);
5665 });
5666
5667 this.request_autoscroll(Autoscroll::fit(), cx);
5668 });
5669 }
5670
5671 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
5672 self.duplicate_line(true, cx);
5673 }
5674
5675 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
5676 self.duplicate_line(false, cx);
5677 }
5678
5679 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
5680 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5681 let buffer = self.buffer.read(cx).snapshot(cx);
5682
5683 let mut edits = Vec::new();
5684 let mut unfold_ranges = Vec::new();
5685 let mut refold_ranges = Vec::new();
5686
5687 let selections = self.selections.all::<Point>(cx);
5688 let mut selections = selections.iter().peekable();
5689 let mut contiguous_row_selections = Vec::new();
5690 let mut new_selections = Vec::new();
5691
5692 while let Some(selection) = selections.next() {
5693 // Find all the selections that span a contiguous row range
5694 let (start_row, end_row) = consume_contiguous_rows(
5695 &mut contiguous_row_selections,
5696 selection,
5697 &display_map,
5698 &mut selections,
5699 );
5700
5701 // Move the text spanned by the row range to be before the line preceding the row range
5702 if start_row.0 > 0 {
5703 let range_to_move = Point::new(
5704 start_row.previous_row().0,
5705 buffer.line_len(start_row.previous_row()),
5706 )
5707 ..Point::new(
5708 end_row.previous_row().0,
5709 buffer.line_len(end_row.previous_row()),
5710 );
5711 let insertion_point = display_map
5712 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
5713 .0;
5714
5715 // Don't move lines across excerpts
5716 if buffer
5717 .excerpt_boundaries_in_range((
5718 Bound::Excluded(insertion_point),
5719 Bound::Included(range_to_move.end),
5720 ))
5721 .next()
5722 .is_none()
5723 {
5724 let text = buffer
5725 .text_for_range(range_to_move.clone())
5726 .flat_map(|s| s.chars())
5727 .skip(1)
5728 .chain(['\n'])
5729 .collect::<String>();
5730
5731 edits.push((
5732 buffer.anchor_after(range_to_move.start)
5733 ..buffer.anchor_before(range_to_move.end),
5734 String::new(),
5735 ));
5736 let insertion_anchor = buffer.anchor_after(insertion_point);
5737 edits.push((insertion_anchor..insertion_anchor, text));
5738
5739 let row_delta = range_to_move.start.row - insertion_point.row + 1;
5740
5741 // Move selections up
5742 new_selections.extend(contiguous_row_selections.drain(..).map(
5743 |mut selection| {
5744 selection.start.row -= row_delta;
5745 selection.end.row -= row_delta;
5746 selection
5747 },
5748 ));
5749
5750 // Move folds up
5751 unfold_ranges.push(range_to_move.clone());
5752 for fold in display_map.folds_in_range(
5753 buffer.anchor_before(range_to_move.start)
5754 ..buffer.anchor_after(range_to_move.end),
5755 ) {
5756 let mut start = fold.range.start.to_point(&buffer);
5757 let mut end = fold.range.end.to_point(&buffer);
5758 start.row -= row_delta;
5759 end.row -= row_delta;
5760 refold_ranges.push(start..end);
5761 }
5762 }
5763 }
5764
5765 // If we didn't move line(s), preserve the existing selections
5766 new_selections.append(&mut contiguous_row_selections);
5767 }
5768
5769 self.transact(cx, |this, cx| {
5770 this.unfold_ranges(unfold_ranges, true, true, cx);
5771 this.buffer.update(cx, |buffer, cx| {
5772 for (range, text) in edits {
5773 buffer.edit([(range, text)], None, cx);
5774 }
5775 });
5776 this.fold_ranges(refold_ranges, true, cx);
5777 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5778 s.select(new_selections);
5779 })
5780 });
5781 }
5782
5783 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
5784 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5785 let buffer = self.buffer.read(cx).snapshot(cx);
5786
5787 let mut edits = Vec::new();
5788 let mut unfold_ranges = Vec::new();
5789 let mut refold_ranges = Vec::new();
5790
5791 let selections = self.selections.all::<Point>(cx);
5792 let mut selections = selections.iter().peekable();
5793 let mut contiguous_row_selections = Vec::new();
5794 let mut new_selections = Vec::new();
5795
5796 while let Some(selection) = selections.next() {
5797 // Find all the selections that span a contiguous row range
5798 let (start_row, end_row) = consume_contiguous_rows(
5799 &mut contiguous_row_selections,
5800 selection,
5801 &display_map,
5802 &mut selections,
5803 );
5804
5805 // Move the text spanned by the row range to be after the last line of the row range
5806 if end_row.0 <= buffer.max_point().row {
5807 let range_to_move =
5808 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
5809 let insertion_point = display_map
5810 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
5811 .0;
5812
5813 // Don't move lines across excerpt boundaries
5814 if buffer
5815 .excerpt_boundaries_in_range((
5816 Bound::Excluded(range_to_move.start),
5817 Bound::Included(insertion_point),
5818 ))
5819 .next()
5820 .is_none()
5821 {
5822 let mut text = String::from("\n");
5823 text.extend(buffer.text_for_range(range_to_move.clone()));
5824 text.pop(); // Drop trailing newline
5825 edits.push((
5826 buffer.anchor_after(range_to_move.start)
5827 ..buffer.anchor_before(range_to_move.end),
5828 String::new(),
5829 ));
5830 let insertion_anchor = buffer.anchor_after(insertion_point);
5831 edits.push((insertion_anchor..insertion_anchor, text));
5832
5833 let row_delta = insertion_point.row - range_to_move.end.row + 1;
5834
5835 // Move selections down
5836 new_selections.extend(contiguous_row_selections.drain(..).map(
5837 |mut selection| {
5838 selection.start.row += row_delta;
5839 selection.end.row += row_delta;
5840 selection
5841 },
5842 ));
5843
5844 // Move folds down
5845 unfold_ranges.push(range_to_move.clone());
5846 for fold in display_map.folds_in_range(
5847 buffer.anchor_before(range_to_move.start)
5848 ..buffer.anchor_after(range_to_move.end),
5849 ) {
5850 let mut start = fold.range.start.to_point(&buffer);
5851 let mut end = fold.range.end.to_point(&buffer);
5852 start.row += row_delta;
5853 end.row += row_delta;
5854 refold_ranges.push(start..end);
5855 }
5856 }
5857 }
5858
5859 // If we didn't move line(s), preserve the existing selections
5860 new_selections.append(&mut contiguous_row_selections);
5861 }
5862
5863 self.transact(cx, |this, cx| {
5864 this.unfold_ranges(unfold_ranges, true, true, cx);
5865 this.buffer.update(cx, |buffer, cx| {
5866 for (range, text) in edits {
5867 buffer.edit([(range, text)], None, cx);
5868 }
5869 });
5870 this.fold_ranges(refold_ranges, true, cx);
5871 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
5872 });
5873 }
5874
5875 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
5876 let text_layout_details = &self.text_layout_details(cx);
5877 self.transact(cx, |this, cx| {
5878 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5879 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5880 let line_mode = s.line_mode;
5881 s.move_with(|display_map, selection| {
5882 if !selection.is_empty() || line_mode {
5883 return;
5884 }
5885
5886 let mut head = selection.head();
5887 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5888 if head.column() == display_map.line_len(head.row()) {
5889 transpose_offset = display_map
5890 .buffer_snapshot
5891 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5892 }
5893
5894 if transpose_offset == 0 {
5895 return;
5896 }
5897
5898 *head.column_mut() += 1;
5899 head = display_map.clip_point(head, Bias::Right);
5900 let goal = SelectionGoal::HorizontalPosition(
5901 display_map
5902 .x_for_display_point(head, &text_layout_details)
5903 .into(),
5904 );
5905 selection.collapse_to(head, goal);
5906
5907 let transpose_start = display_map
5908 .buffer_snapshot
5909 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5910 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5911 let transpose_end = display_map
5912 .buffer_snapshot
5913 .clip_offset(transpose_offset + 1, Bias::Right);
5914 if let Some(ch) =
5915 display_map.buffer_snapshot.chars_at(transpose_start).next()
5916 {
5917 edits.push((transpose_start..transpose_offset, String::new()));
5918 edits.push((transpose_end..transpose_end, ch.to_string()));
5919 }
5920 }
5921 });
5922 edits
5923 });
5924 this.buffer
5925 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5926 let selections = this.selections.all::<usize>(cx);
5927 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5928 s.select(selections);
5929 });
5930 });
5931 }
5932
5933 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5934 let mut text = String::new();
5935 let buffer = self.buffer.read(cx).snapshot(cx);
5936 let mut selections = self.selections.all::<Point>(cx);
5937 let mut clipboard_selections = Vec::with_capacity(selections.len());
5938 {
5939 let max_point = buffer.max_point();
5940 let mut is_first = true;
5941 for selection in &mut selections {
5942 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5943 if is_entire_line {
5944 selection.start = Point::new(selection.start.row, 0);
5945 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5946 selection.goal = SelectionGoal::None;
5947 }
5948 if is_first {
5949 is_first = false;
5950 } else {
5951 text += "\n";
5952 }
5953 let mut len = 0;
5954 for chunk in buffer.text_for_range(selection.start..selection.end) {
5955 text.push_str(chunk);
5956 len += chunk.len();
5957 }
5958 clipboard_selections.push(ClipboardSelection {
5959 len,
5960 is_entire_line,
5961 first_line_indent: buffer
5962 .indent_size_for_line(MultiBufferRow(selection.start.row))
5963 .len,
5964 });
5965 }
5966 }
5967
5968 self.transact(cx, |this, cx| {
5969 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5970 s.select(selections);
5971 });
5972 this.insert("", cx);
5973 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5974 });
5975 }
5976
5977 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5978 let selections = self.selections.all::<Point>(cx);
5979 let buffer = self.buffer.read(cx).read(cx);
5980 let mut text = String::new();
5981
5982 let mut clipboard_selections = Vec::with_capacity(selections.len());
5983 {
5984 let max_point = buffer.max_point();
5985 let mut is_first = true;
5986 for selection in selections.iter() {
5987 let mut start = selection.start;
5988 let mut end = selection.end;
5989 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5990 if is_entire_line {
5991 start = Point::new(start.row, 0);
5992 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5993 }
5994 if is_first {
5995 is_first = false;
5996 } else {
5997 text += "\n";
5998 }
5999 let mut len = 0;
6000 for chunk in buffer.text_for_range(start..end) {
6001 text.push_str(chunk);
6002 len += chunk.len();
6003 }
6004 clipboard_selections.push(ClipboardSelection {
6005 len,
6006 is_entire_line,
6007 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6008 });
6009 }
6010 }
6011
6012 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6013 }
6014
6015 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6016 if self.read_only(cx) {
6017 return;
6018 }
6019
6020 self.transact(cx, |this, cx| {
6021 if let Some(item) = cx.read_from_clipboard() {
6022 let clipboard_text = Cow::Borrowed(item.text());
6023 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
6024 let old_selections = this.selections.all::<usize>(cx);
6025 let all_selections_were_entire_line =
6026 clipboard_selections.iter().all(|s| s.is_entire_line);
6027 let first_selection_indent_column =
6028 clipboard_selections.first().map(|s| s.first_line_indent);
6029 if clipboard_selections.len() != old_selections.len() {
6030 clipboard_selections.drain(..);
6031 }
6032
6033 this.buffer.update(cx, |buffer, cx| {
6034 let snapshot = buffer.read(cx);
6035 let mut start_offset = 0;
6036 let mut edits = Vec::new();
6037 let mut original_indent_columns = Vec::new();
6038 let line_mode = this.selections.line_mode;
6039 for (ix, selection) in old_selections.iter().enumerate() {
6040 let to_insert;
6041 let entire_line;
6042 let original_indent_column;
6043 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6044 let end_offset = start_offset + clipboard_selection.len;
6045 to_insert = &clipboard_text[start_offset..end_offset];
6046 entire_line = clipboard_selection.is_entire_line;
6047 start_offset = end_offset + 1;
6048 original_indent_column =
6049 Some(clipboard_selection.first_line_indent);
6050 } else {
6051 to_insert = clipboard_text.as_str();
6052 entire_line = all_selections_were_entire_line;
6053 original_indent_column = first_selection_indent_column
6054 }
6055
6056 // If the corresponding selection was empty when this slice of the
6057 // clipboard text was written, then the entire line containing the
6058 // selection was copied. If this selection is also currently empty,
6059 // then paste the line before the current line of the buffer.
6060 let range = if selection.is_empty() && !line_mode && entire_line {
6061 let column = selection.start.to_point(&snapshot).column as usize;
6062 let line_start = selection.start - column;
6063 line_start..line_start
6064 } else {
6065 selection.range()
6066 };
6067
6068 edits.push((range, to_insert));
6069 original_indent_columns.extend(original_indent_column);
6070 }
6071 drop(snapshot);
6072
6073 buffer.edit(
6074 edits,
6075 Some(AutoindentMode::Block {
6076 original_indent_columns,
6077 }),
6078 cx,
6079 );
6080 });
6081
6082 let selections = this.selections.all::<usize>(cx);
6083 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6084 } else {
6085 this.insert(&clipboard_text, cx);
6086 }
6087 }
6088 });
6089 }
6090
6091 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6092 if self.read_only(cx) {
6093 return;
6094 }
6095
6096 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6097 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
6098 self.change_selections(None, cx, |s| {
6099 s.select_anchors(selections.to_vec());
6100 });
6101 }
6102 self.request_autoscroll(Autoscroll::fit(), cx);
6103 self.unmark_text(cx);
6104 self.refresh_inline_completion(true, cx);
6105 cx.emit(EditorEvent::Edited);
6106 cx.emit(EditorEvent::TransactionUndone {
6107 transaction_id: tx_id,
6108 });
6109 }
6110 }
6111
6112 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6113 if self.read_only(cx) {
6114 return;
6115 }
6116
6117 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6118 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
6119 {
6120 self.change_selections(None, cx, |s| {
6121 s.select_anchors(selections.to_vec());
6122 });
6123 }
6124 self.request_autoscroll(Autoscroll::fit(), cx);
6125 self.unmark_text(cx);
6126 self.refresh_inline_completion(true, cx);
6127 cx.emit(EditorEvent::Edited);
6128 }
6129 }
6130
6131 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6132 self.buffer
6133 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6134 }
6135
6136 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6137 self.buffer
6138 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6139 }
6140
6141 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6142 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6143 let line_mode = s.line_mode;
6144 s.move_with(|map, selection| {
6145 let cursor = if selection.is_empty() && !line_mode {
6146 movement::left(map, selection.start)
6147 } else {
6148 selection.start
6149 };
6150 selection.collapse_to(cursor, SelectionGoal::None);
6151 });
6152 })
6153 }
6154
6155 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6156 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6157 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6158 })
6159 }
6160
6161 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6162 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6163 let line_mode = s.line_mode;
6164 s.move_with(|map, selection| {
6165 let cursor = if selection.is_empty() && !line_mode {
6166 movement::right(map, selection.end)
6167 } else {
6168 selection.end
6169 };
6170 selection.collapse_to(cursor, SelectionGoal::None)
6171 });
6172 })
6173 }
6174
6175 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6176 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6177 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6178 })
6179 }
6180
6181 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6182 if self.take_rename(true, cx).is_some() {
6183 return;
6184 }
6185
6186 if matches!(self.mode, EditorMode::SingleLine) {
6187 cx.propagate();
6188 return;
6189 }
6190
6191 let text_layout_details = &self.text_layout_details(cx);
6192
6193 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6194 let line_mode = s.line_mode;
6195 s.move_with(|map, selection| {
6196 if !selection.is_empty() && !line_mode {
6197 selection.goal = SelectionGoal::None;
6198 }
6199 let (cursor, goal) = movement::up(
6200 map,
6201 selection.start,
6202 selection.goal,
6203 false,
6204 &text_layout_details,
6205 );
6206 selection.collapse_to(cursor, goal);
6207 });
6208 })
6209 }
6210
6211 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6212 if self.take_rename(true, cx).is_some() {
6213 return;
6214 }
6215
6216 if matches!(self.mode, EditorMode::SingleLine) {
6217 cx.propagate();
6218 return;
6219 }
6220
6221 let text_layout_details = &self.text_layout_details(cx);
6222
6223 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6224 let line_mode = s.line_mode;
6225 s.move_with(|map, selection| {
6226 if !selection.is_empty() && !line_mode {
6227 selection.goal = SelectionGoal::None;
6228 }
6229 let (cursor, goal) = movement::up_by_rows(
6230 map,
6231 selection.start,
6232 action.lines,
6233 selection.goal,
6234 false,
6235 &text_layout_details,
6236 );
6237 selection.collapse_to(cursor, goal);
6238 });
6239 })
6240 }
6241
6242 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6243 if self.take_rename(true, cx).is_some() {
6244 return;
6245 }
6246
6247 if matches!(self.mode, EditorMode::SingleLine) {
6248 cx.propagate();
6249 return;
6250 }
6251
6252 let text_layout_details = &self.text_layout_details(cx);
6253
6254 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6255 let line_mode = s.line_mode;
6256 s.move_with(|map, selection| {
6257 if !selection.is_empty() && !line_mode {
6258 selection.goal = SelectionGoal::None;
6259 }
6260 let (cursor, goal) = movement::down_by_rows(
6261 map,
6262 selection.start,
6263 action.lines,
6264 selection.goal,
6265 false,
6266 &text_layout_details,
6267 );
6268 selection.collapse_to(cursor, goal);
6269 });
6270 })
6271 }
6272
6273 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6274 let text_layout_details = &self.text_layout_details(cx);
6275 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6276 s.move_heads_with(|map, head, goal| {
6277 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6278 })
6279 })
6280 }
6281
6282 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6283 let text_layout_details = &self.text_layout_details(cx);
6284 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6285 s.move_heads_with(|map, head, goal| {
6286 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6287 })
6288 })
6289 }
6290
6291 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6292 if self.take_rename(true, cx).is_some() {
6293 return;
6294 }
6295
6296 if matches!(self.mode, EditorMode::SingleLine) {
6297 cx.propagate();
6298 return;
6299 }
6300
6301 let row_count = if let Some(row_count) = self.visible_line_count() {
6302 row_count as u32 - 1
6303 } else {
6304 return;
6305 };
6306
6307 let autoscroll = if action.center_cursor {
6308 Autoscroll::center()
6309 } else {
6310 Autoscroll::fit()
6311 };
6312
6313 let text_layout_details = &self.text_layout_details(cx);
6314
6315 self.change_selections(Some(autoscroll), cx, |s| {
6316 let line_mode = s.line_mode;
6317 s.move_with(|map, selection| {
6318 if !selection.is_empty() && !line_mode {
6319 selection.goal = SelectionGoal::None;
6320 }
6321 let (cursor, goal) = movement::up_by_rows(
6322 map,
6323 selection.end,
6324 row_count,
6325 selection.goal,
6326 false,
6327 &text_layout_details,
6328 );
6329 selection.collapse_to(cursor, goal);
6330 });
6331 });
6332 }
6333
6334 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6335 let text_layout_details = &self.text_layout_details(cx);
6336 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6337 s.move_heads_with(|map, head, goal| {
6338 movement::up(map, head, goal, false, &text_layout_details)
6339 })
6340 })
6341 }
6342
6343 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6344 self.take_rename(true, cx);
6345
6346 if self.mode == EditorMode::SingleLine {
6347 cx.propagate();
6348 return;
6349 }
6350
6351 let text_layout_details = &self.text_layout_details(cx);
6352 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6353 let line_mode = s.line_mode;
6354 s.move_with(|map, selection| {
6355 if !selection.is_empty() && !line_mode {
6356 selection.goal = SelectionGoal::None;
6357 }
6358 let (cursor, goal) = movement::down(
6359 map,
6360 selection.end,
6361 selection.goal,
6362 false,
6363 &text_layout_details,
6364 );
6365 selection.collapse_to(cursor, goal);
6366 });
6367 });
6368 }
6369
6370 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
6371 if self.take_rename(true, cx).is_some() {
6372 return;
6373 }
6374
6375 if self
6376 .context_menu
6377 .write()
6378 .as_mut()
6379 .map(|menu| menu.select_last(self.project.as_ref(), cx))
6380 .unwrap_or(false)
6381 {
6382 return;
6383 }
6384
6385 if matches!(self.mode, EditorMode::SingleLine) {
6386 cx.propagate();
6387 return;
6388 }
6389
6390 let row_count = if let Some(row_count) = self.visible_line_count() {
6391 row_count as u32 - 1
6392 } else {
6393 return;
6394 };
6395
6396 let autoscroll = if action.center_cursor {
6397 Autoscroll::center()
6398 } else {
6399 Autoscroll::fit()
6400 };
6401
6402 let text_layout_details = &self.text_layout_details(cx);
6403 self.change_selections(Some(autoscroll), cx, |s| {
6404 let line_mode = s.line_mode;
6405 s.move_with(|map, selection| {
6406 if !selection.is_empty() && !line_mode {
6407 selection.goal = SelectionGoal::None;
6408 }
6409 let (cursor, goal) = movement::down_by_rows(
6410 map,
6411 selection.end,
6412 row_count,
6413 selection.goal,
6414 false,
6415 &text_layout_details,
6416 );
6417 selection.collapse_to(cursor, goal);
6418 });
6419 });
6420 }
6421
6422 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
6423 let text_layout_details = &self.text_layout_details(cx);
6424 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6425 s.move_heads_with(|map, head, goal| {
6426 movement::down(map, head, goal, false, &text_layout_details)
6427 })
6428 });
6429 }
6430
6431 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
6432 if let Some(context_menu) = self.context_menu.write().as_mut() {
6433 context_menu.select_first(self.project.as_ref(), cx);
6434 }
6435 }
6436
6437 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
6438 if let Some(context_menu) = self.context_menu.write().as_mut() {
6439 context_menu.select_prev(self.project.as_ref(), cx);
6440 }
6441 }
6442
6443 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
6444 if let Some(context_menu) = self.context_menu.write().as_mut() {
6445 context_menu.select_next(self.project.as_ref(), cx);
6446 }
6447 }
6448
6449 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
6450 if let Some(context_menu) = self.context_menu.write().as_mut() {
6451 context_menu.select_last(self.project.as_ref(), cx);
6452 }
6453 }
6454
6455 pub fn move_to_previous_word_start(
6456 &mut self,
6457 _: &MoveToPreviousWordStart,
6458 cx: &mut ViewContext<Self>,
6459 ) {
6460 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6461 s.move_cursors_with(|map, head, _| {
6462 (
6463 movement::previous_word_start(map, head),
6464 SelectionGoal::None,
6465 )
6466 });
6467 })
6468 }
6469
6470 pub fn move_to_previous_subword_start(
6471 &mut self,
6472 _: &MoveToPreviousSubwordStart,
6473 cx: &mut ViewContext<Self>,
6474 ) {
6475 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6476 s.move_cursors_with(|map, head, _| {
6477 (
6478 movement::previous_subword_start(map, head),
6479 SelectionGoal::None,
6480 )
6481 });
6482 })
6483 }
6484
6485 pub fn select_to_previous_word_start(
6486 &mut self,
6487 _: &SelectToPreviousWordStart,
6488 cx: &mut ViewContext<Self>,
6489 ) {
6490 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6491 s.move_heads_with(|map, head, _| {
6492 (
6493 movement::previous_word_start(map, head),
6494 SelectionGoal::None,
6495 )
6496 });
6497 })
6498 }
6499
6500 pub fn select_to_previous_subword_start(
6501 &mut self,
6502 _: &SelectToPreviousSubwordStart,
6503 cx: &mut ViewContext<Self>,
6504 ) {
6505 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6506 s.move_heads_with(|map, head, _| {
6507 (
6508 movement::previous_subword_start(map, head),
6509 SelectionGoal::None,
6510 )
6511 });
6512 })
6513 }
6514
6515 pub fn delete_to_previous_word_start(
6516 &mut self,
6517 _: &DeleteToPreviousWordStart,
6518 cx: &mut ViewContext<Self>,
6519 ) {
6520 self.transact(cx, |this, cx| {
6521 this.select_autoclose_pair(cx);
6522 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6523 let line_mode = s.line_mode;
6524 s.move_with(|map, selection| {
6525 if selection.is_empty() && !line_mode {
6526 let cursor = movement::previous_word_start(map, selection.head());
6527 selection.set_head(cursor, SelectionGoal::None);
6528 }
6529 });
6530 });
6531 this.insert("", cx);
6532 });
6533 }
6534
6535 pub fn delete_to_previous_subword_start(
6536 &mut self,
6537 _: &DeleteToPreviousSubwordStart,
6538 cx: &mut ViewContext<Self>,
6539 ) {
6540 self.transact(cx, |this, cx| {
6541 this.select_autoclose_pair(cx);
6542 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6543 let line_mode = s.line_mode;
6544 s.move_with(|map, selection| {
6545 if selection.is_empty() && !line_mode {
6546 let cursor = movement::previous_subword_start(map, selection.head());
6547 selection.set_head(cursor, SelectionGoal::None);
6548 }
6549 });
6550 });
6551 this.insert("", cx);
6552 });
6553 }
6554
6555 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
6556 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6557 s.move_cursors_with(|map, head, _| {
6558 (movement::next_word_end(map, head), SelectionGoal::None)
6559 });
6560 })
6561 }
6562
6563 pub fn move_to_next_subword_end(
6564 &mut self,
6565 _: &MoveToNextSubwordEnd,
6566 cx: &mut ViewContext<Self>,
6567 ) {
6568 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6569 s.move_cursors_with(|map, head, _| {
6570 (movement::next_subword_end(map, head), SelectionGoal::None)
6571 });
6572 })
6573 }
6574
6575 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
6576 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6577 s.move_heads_with(|map, head, _| {
6578 (movement::next_word_end(map, head), SelectionGoal::None)
6579 });
6580 })
6581 }
6582
6583 pub fn select_to_next_subword_end(
6584 &mut self,
6585 _: &SelectToNextSubwordEnd,
6586 cx: &mut ViewContext<Self>,
6587 ) {
6588 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6589 s.move_heads_with(|map, head, _| {
6590 (movement::next_subword_end(map, head), SelectionGoal::None)
6591 });
6592 })
6593 }
6594
6595 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
6596 self.transact(cx, |this, cx| {
6597 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6598 let line_mode = s.line_mode;
6599 s.move_with(|map, selection| {
6600 if selection.is_empty() && !line_mode {
6601 let cursor = movement::next_word_end(map, selection.head());
6602 selection.set_head(cursor, SelectionGoal::None);
6603 }
6604 });
6605 });
6606 this.insert("", cx);
6607 });
6608 }
6609
6610 pub fn delete_to_next_subword_end(
6611 &mut self,
6612 _: &DeleteToNextSubwordEnd,
6613 cx: &mut ViewContext<Self>,
6614 ) {
6615 self.transact(cx, |this, cx| {
6616 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6617 s.move_with(|map, selection| {
6618 if selection.is_empty() {
6619 let cursor = movement::next_subword_end(map, selection.head());
6620 selection.set_head(cursor, SelectionGoal::None);
6621 }
6622 });
6623 });
6624 this.insert("", cx);
6625 });
6626 }
6627
6628 pub fn move_to_beginning_of_line(
6629 &mut self,
6630 action: &MoveToBeginningOfLine,
6631 cx: &mut ViewContext<Self>,
6632 ) {
6633 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6634 s.move_cursors_with(|map, head, _| {
6635 (
6636 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6637 SelectionGoal::None,
6638 )
6639 });
6640 })
6641 }
6642
6643 pub fn select_to_beginning_of_line(
6644 &mut self,
6645 action: &SelectToBeginningOfLine,
6646 cx: &mut ViewContext<Self>,
6647 ) {
6648 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6649 s.move_heads_with(|map, head, _| {
6650 (
6651 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
6652 SelectionGoal::None,
6653 )
6654 });
6655 });
6656 }
6657
6658 pub fn delete_to_beginning_of_line(
6659 &mut self,
6660 _: &DeleteToBeginningOfLine,
6661 cx: &mut ViewContext<Self>,
6662 ) {
6663 self.transact(cx, |this, cx| {
6664 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6665 s.move_with(|_, selection| {
6666 selection.reversed = true;
6667 });
6668 });
6669
6670 this.select_to_beginning_of_line(
6671 &SelectToBeginningOfLine {
6672 stop_at_soft_wraps: false,
6673 },
6674 cx,
6675 );
6676 this.backspace(&Backspace, cx);
6677 });
6678 }
6679
6680 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
6681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6682 s.move_cursors_with(|map, head, _| {
6683 (
6684 movement::line_end(map, head, action.stop_at_soft_wraps),
6685 SelectionGoal::None,
6686 )
6687 });
6688 })
6689 }
6690
6691 pub fn select_to_end_of_line(
6692 &mut self,
6693 action: &SelectToEndOfLine,
6694 cx: &mut ViewContext<Self>,
6695 ) {
6696 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6697 s.move_heads_with(|map, head, _| {
6698 (
6699 movement::line_end(map, head, action.stop_at_soft_wraps),
6700 SelectionGoal::None,
6701 )
6702 });
6703 })
6704 }
6705
6706 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
6707 self.transact(cx, |this, cx| {
6708 this.select_to_end_of_line(
6709 &SelectToEndOfLine {
6710 stop_at_soft_wraps: false,
6711 },
6712 cx,
6713 );
6714 this.delete(&Delete, cx);
6715 });
6716 }
6717
6718 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
6719 self.transact(cx, |this, cx| {
6720 this.select_to_end_of_line(
6721 &SelectToEndOfLine {
6722 stop_at_soft_wraps: false,
6723 },
6724 cx,
6725 );
6726 this.cut(&Cut, cx);
6727 });
6728 }
6729
6730 pub fn move_to_start_of_paragraph(
6731 &mut self,
6732 _: &MoveToStartOfParagraph,
6733 cx: &mut ViewContext<Self>,
6734 ) {
6735 if matches!(self.mode, EditorMode::SingleLine) {
6736 cx.propagate();
6737 return;
6738 }
6739
6740 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6741 s.move_with(|map, selection| {
6742 selection.collapse_to(
6743 movement::start_of_paragraph(map, selection.head(), 1),
6744 SelectionGoal::None,
6745 )
6746 });
6747 })
6748 }
6749
6750 pub fn move_to_end_of_paragraph(
6751 &mut self,
6752 _: &MoveToEndOfParagraph,
6753 cx: &mut ViewContext<Self>,
6754 ) {
6755 if matches!(self.mode, EditorMode::SingleLine) {
6756 cx.propagate();
6757 return;
6758 }
6759
6760 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6761 s.move_with(|map, selection| {
6762 selection.collapse_to(
6763 movement::end_of_paragraph(map, selection.head(), 1),
6764 SelectionGoal::None,
6765 )
6766 });
6767 })
6768 }
6769
6770 pub fn select_to_start_of_paragraph(
6771 &mut self,
6772 _: &SelectToStartOfParagraph,
6773 cx: &mut ViewContext<Self>,
6774 ) {
6775 if matches!(self.mode, EditorMode::SingleLine) {
6776 cx.propagate();
6777 return;
6778 }
6779
6780 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6781 s.move_heads_with(|map, head, _| {
6782 (
6783 movement::start_of_paragraph(map, head, 1),
6784 SelectionGoal::None,
6785 )
6786 });
6787 })
6788 }
6789
6790 pub fn select_to_end_of_paragraph(
6791 &mut self,
6792 _: &SelectToEndOfParagraph,
6793 cx: &mut ViewContext<Self>,
6794 ) {
6795 if matches!(self.mode, EditorMode::SingleLine) {
6796 cx.propagate();
6797 return;
6798 }
6799
6800 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6801 s.move_heads_with(|map, head, _| {
6802 (
6803 movement::end_of_paragraph(map, head, 1),
6804 SelectionGoal::None,
6805 )
6806 });
6807 })
6808 }
6809
6810 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
6811 if matches!(self.mode, EditorMode::SingleLine) {
6812 cx.propagate();
6813 return;
6814 }
6815
6816 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6817 s.select_ranges(vec![0..0]);
6818 });
6819 }
6820
6821 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
6822 let mut selection = self.selections.last::<Point>(cx);
6823 selection.set_head(Point::zero(), SelectionGoal::None);
6824
6825 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6826 s.select(vec![selection]);
6827 });
6828 }
6829
6830 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
6831 if matches!(self.mode, EditorMode::SingleLine) {
6832 cx.propagate();
6833 return;
6834 }
6835
6836 let cursor = self.buffer.read(cx).read(cx).len();
6837 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6838 s.select_ranges(vec![cursor..cursor])
6839 });
6840 }
6841
6842 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
6843 self.nav_history = nav_history;
6844 }
6845
6846 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
6847 self.nav_history.as_ref()
6848 }
6849
6850 fn push_to_nav_history(
6851 &mut self,
6852 cursor_anchor: Anchor,
6853 new_position: Option<Point>,
6854 cx: &mut ViewContext<Self>,
6855 ) {
6856 if let Some(nav_history) = self.nav_history.as_mut() {
6857 let buffer = self.buffer.read(cx).read(cx);
6858 let cursor_position = cursor_anchor.to_point(&buffer);
6859 let scroll_state = self.scroll_manager.anchor();
6860 let scroll_top_row = scroll_state.top_row(&buffer);
6861 drop(buffer);
6862
6863 if let Some(new_position) = new_position {
6864 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
6865 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
6866 return;
6867 }
6868 }
6869
6870 nav_history.push(
6871 Some(NavigationData {
6872 cursor_anchor,
6873 cursor_position,
6874 scroll_anchor: scroll_state,
6875 scroll_top_row,
6876 }),
6877 cx,
6878 );
6879 }
6880 }
6881
6882 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
6883 let buffer = self.buffer.read(cx).snapshot(cx);
6884 let mut selection = self.selections.first::<usize>(cx);
6885 selection.set_head(buffer.len(), SelectionGoal::None);
6886 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6887 s.select(vec![selection]);
6888 });
6889 }
6890
6891 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
6892 let end = self.buffer.read(cx).read(cx).len();
6893 self.change_selections(None, cx, |s| {
6894 s.select_ranges(vec![0..end]);
6895 });
6896 }
6897
6898 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
6899 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6900 let mut selections = self.selections.all::<Point>(cx);
6901 let max_point = display_map.buffer_snapshot.max_point();
6902 for selection in &mut selections {
6903 let rows = selection.spanned_rows(true, &display_map);
6904 selection.start = Point::new(rows.start.0, 0);
6905 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
6906 selection.reversed = false;
6907 }
6908 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6909 s.select(selections);
6910 });
6911 }
6912
6913 pub fn split_selection_into_lines(
6914 &mut self,
6915 _: &SplitSelectionIntoLines,
6916 cx: &mut ViewContext<Self>,
6917 ) {
6918 let mut to_unfold = Vec::new();
6919 let mut new_selection_ranges = Vec::new();
6920 {
6921 let selections = self.selections.all::<Point>(cx);
6922 let buffer = self.buffer.read(cx).read(cx);
6923 for selection in selections {
6924 for row in selection.start.row..selection.end.row {
6925 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
6926 new_selection_ranges.push(cursor..cursor);
6927 }
6928 new_selection_ranges.push(selection.end..selection.end);
6929 to_unfold.push(selection.start..selection.end);
6930 }
6931 }
6932 self.unfold_ranges(to_unfold, true, true, cx);
6933 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6934 s.select_ranges(new_selection_ranges);
6935 });
6936 }
6937
6938 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
6939 self.add_selection(true, cx);
6940 }
6941
6942 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
6943 self.add_selection(false, cx);
6944 }
6945
6946 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
6947 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6948 let mut selections = self.selections.all::<Point>(cx);
6949 let text_layout_details = self.text_layout_details(cx);
6950 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
6951 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
6952 let range = oldest_selection.display_range(&display_map).sorted();
6953
6954 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
6955 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
6956 let positions = start_x.min(end_x)..start_x.max(end_x);
6957
6958 selections.clear();
6959 let mut stack = Vec::new();
6960 for row in range.start.row().0..=range.end.row().0 {
6961 if let Some(selection) = self.selections.build_columnar_selection(
6962 &display_map,
6963 DisplayRow(row),
6964 &positions,
6965 oldest_selection.reversed,
6966 &text_layout_details,
6967 ) {
6968 stack.push(selection.id);
6969 selections.push(selection);
6970 }
6971 }
6972
6973 if above {
6974 stack.reverse();
6975 }
6976
6977 AddSelectionsState { above, stack }
6978 });
6979
6980 let last_added_selection = *state.stack.last().unwrap();
6981 let mut new_selections = Vec::new();
6982 if above == state.above {
6983 let end_row = if above {
6984 DisplayRow(0)
6985 } else {
6986 display_map.max_point().row()
6987 };
6988
6989 'outer: for selection in selections {
6990 if selection.id == last_added_selection {
6991 let range = selection.display_range(&display_map).sorted();
6992 debug_assert_eq!(range.start.row(), range.end.row());
6993 let mut row = range.start.row();
6994 let positions =
6995 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
6996 px(start)..px(end)
6997 } else {
6998 let start_x =
6999 display_map.x_for_display_point(range.start, &text_layout_details);
7000 let end_x =
7001 display_map.x_for_display_point(range.end, &text_layout_details);
7002 start_x.min(end_x)..start_x.max(end_x)
7003 };
7004
7005 while row != end_row {
7006 if above {
7007 row.0 -= 1;
7008 } else {
7009 row.0 += 1;
7010 }
7011
7012 if let Some(new_selection) = self.selections.build_columnar_selection(
7013 &display_map,
7014 row,
7015 &positions,
7016 selection.reversed,
7017 &text_layout_details,
7018 ) {
7019 state.stack.push(new_selection.id);
7020 if above {
7021 new_selections.push(new_selection);
7022 new_selections.push(selection);
7023 } else {
7024 new_selections.push(selection);
7025 new_selections.push(new_selection);
7026 }
7027
7028 continue 'outer;
7029 }
7030 }
7031 }
7032
7033 new_selections.push(selection);
7034 }
7035 } else {
7036 new_selections = selections;
7037 new_selections.retain(|s| s.id != last_added_selection);
7038 state.stack.pop();
7039 }
7040
7041 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7042 s.select(new_selections);
7043 });
7044 if state.stack.len() > 1 {
7045 self.add_selections_state = Some(state);
7046 }
7047 }
7048
7049 pub fn select_next_match_internal(
7050 &mut self,
7051 display_map: &DisplaySnapshot,
7052 replace_newest: bool,
7053 autoscroll: Option<Autoscroll>,
7054 cx: &mut ViewContext<Self>,
7055 ) -> Result<()> {
7056 fn select_next_match_ranges(
7057 this: &mut Editor,
7058 range: Range<usize>,
7059 replace_newest: bool,
7060 auto_scroll: Option<Autoscroll>,
7061 cx: &mut ViewContext<Editor>,
7062 ) {
7063 this.unfold_ranges([range.clone()], false, true, cx);
7064 this.change_selections(auto_scroll, cx, |s| {
7065 if replace_newest {
7066 s.delete(s.newest_anchor().id);
7067 }
7068 s.insert_range(range.clone());
7069 });
7070 }
7071
7072 let buffer = &display_map.buffer_snapshot;
7073 let mut selections = self.selections.all::<usize>(cx);
7074 if let Some(mut select_next_state) = self.select_next_state.take() {
7075 let query = &select_next_state.query;
7076 if !select_next_state.done {
7077 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7078 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7079 let mut next_selected_range = None;
7080
7081 let bytes_after_last_selection =
7082 buffer.bytes_in_range(last_selection.end..buffer.len());
7083 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7084 let query_matches = query
7085 .stream_find_iter(bytes_after_last_selection)
7086 .map(|result| (last_selection.end, result))
7087 .chain(
7088 query
7089 .stream_find_iter(bytes_before_first_selection)
7090 .map(|result| (0, result)),
7091 );
7092
7093 for (start_offset, query_match) in query_matches {
7094 let query_match = query_match.unwrap(); // can only fail due to I/O
7095 let offset_range =
7096 start_offset + query_match.start()..start_offset + query_match.end();
7097 let display_range = offset_range.start.to_display_point(&display_map)
7098 ..offset_range.end.to_display_point(&display_map);
7099
7100 if !select_next_state.wordwise
7101 || (!movement::is_inside_word(&display_map, display_range.start)
7102 && !movement::is_inside_word(&display_map, display_range.end))
7103 {
7104 // TODO: This is n^2, because we might check all the selections
7105 if !selections
7106 .iter()
7107 .any(|selection| selection.range().overlaps(&offset_range))
7108 {
7109 next_selected_range = Some(offset_range);
7110 break;
7111 }
7112 }
7113 }
7114
7115 if let Some(next_selected_range) = next_selected_range {
7116 select_next_match_ranges(
7117 self,
7118 next_selected_range,
7119 replace_newest,
7120 autoscroll,
7121 cx,
7122 );
7123 } else {
7124 select_next_state.done = true;
7125 }
7126 }
7127
7128 self.select_next_state = Some(select_next_state);
7129 } else {
7130 let mut only_carets = true;
7131 let mut same_text_selected = true;
7132 let mut selected_text = None;
7133
7134 let mut selections_iter = selections.iter().peekable();
7135 while let Some(selection) = selections_iter.next() {
7136 if selection.start != selection.end {
7137 only_carets = false;
7138 }
7139
7140 if same_text_selected {
7141 if selected_text.is_none() {
7142 selected_text =
7143 Some(buffer.text_for_range(selection.range()).collect::<String>());
7144 }
7145
7146 if let Some(next_selection) = selections_iter.peek() {
7147 if next_selection.range().len() == selection.range().len() {
7148 let next_selected_text = buffer
7149 .text_for_range(next_selection.range())
7150 .collect::<String>();
7151 if Some(next_selected_text) != selected_text {
7152 same_text_selected = false;
7153 selected_text = None;
7154 }
7155 } else {
7156 same_text_selected = false;
7157 selected_text = None;
7158 }
7159 }
7160 }
7161 }
7162
7163 if only_carets {
7164 for selection in &mut selections {
7165 let word_range = movement::surrounding_word(
7166 &display_map,
7167 selection.start.to_display_point(&display_map),
7168 );
7169 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7170 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7171 selection.goal = SelectionGoal::None;
7172 selection.reversed = false;
7173 select_next_match_ranges(
7174 self,
7175 selection.start..selection.end,
7176 replace_newest,
7177 autoscroll,
7178 cx,
7179 );
7180 }
7181
7182 if selections.len() == 1 {
7183 let selection = selections
7184 .last()
7185 .expect("ensured that there's only one selection");
7186 let query = buffer
7187 .text_for_range(selection.start..selection.end)
7188 .collect::<String>();
7189 let is_empty = query.is_empty();
7190 let select_state = SelectNextState {
7191 query: AhoCorasick::new(&[query])?,
7192 wordwise: true,
7193 done: is_empty,
7194 };
7195 self.select_next_state = Some(select_state);
7196 } else {
7197 self.select_next_state = None;
7198 }
7199 } else if let Some(selected_text) = selected_text {
7200 self.select_next_state = Some(SelectNextState {
7201 query: AhoCorasick::new(&[selected_text])?,
7202 wordwise: false,
7203 done: false,
7204 });
7205 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7206 }
7207 }
7208 Ok(())
7209 }
7210
7211 pub fn select_all_matches(
7212 &mut self,
7213 _action: &SelectAllMatches,
7214 cx: &mut ViewContext<Self>,
7215 ) -> Result<()> {
7216 self.push_to_selection_history();
7217 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7218
7219 self.select_next_match_internal(&display_map, false, None, cx)?;
7220 let Some(select_next_state) = self.select_next_state.as_mut() else {
7221 return Ok(());
7222 };
7223 if select_next_state.done {
7224 return Ok(());
7225 }
7226
7227 let mut new_selections = self.selections.all::<usize>(cx);
7228
7229 let buffer = &display_map.buffer_snapshot;
7230 let query_matches = select_next_state
7231 .query
7232 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7233
7234 for query_match in query_matches {
7235 let query_match = query_match.unwrap(); // can only fail due to I/O
7236 let offset_range = query_match.start()..query_match.end();
7237 let display_range = offset_range.start.to_display_point(&display_map)
7238 ..offset_range.end.to_display_point(&display_map);
7239
7240 if !select_next_state.wordwise
7241 || (!movement::is_inside_word(&display_map, display_range.start)
7242 && !movement::is_inside_word(&display_map, display_range.end))
7243 {
7244 self.selections.change_with(cx, |selections| {
7245 new_selections.push(Selection {
7246 id: selections.new_selection_id(),
7247 start: offset_range.start,
7248 end: offset_range.end,
7249 reversed: false,
7250 goal: SelectionGoal::None,
7251 });
7252 });
7253 }
7254 }
7255
7256 new_selections.sort_by_key(|selection| selection.start);
7257 let mut ix = 0;
7258 while ix + 1 < new_selections.len() {
7259 let current_selection = &new_selections[ix];
7260 let next_selection = &new_selections[ix + 1];
7261 if current_selection.range().overlaps(&next_selection.range()) {
7262 if current_selection.id < next_selection.id {
7263 new_selections.remove(ix + 1);
7264 } else {
7265 new_selections.remove(ix);
7266 }
7267 } else {
7268 ix += 1;
7269 }
7270 }
7271
7272 select_next_state.done = true;
7273 self.unfold_ranges(
7274 new_selections.iter().map(|selection| selection.range()),
7275 false,
7276 false,
7277 cx,
7278 );
7279 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7280 selections.select(new_selections)
7281 });
7282
7283 Ok(())
7284 }
7285
7286 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7287 self.push_to_selection_history();
7288 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7289 self.select_next_match_internal(
7290 &display_map,
7291 action.replace_newest,
7292 Some(Autoscroll::newest()),
7293 cx,
7294 )?;
7295 Ok(())
7296 }
7297
7298 pub fn select_previous(
7299 &mut self,
7300 action: &SelectPrevious,
7301 cx: &mut ViewContext<Self>,
7302 ) -> Result<()> {
7303 self.push_to_selection_history();
7304 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7305 let buffer = &display_map.buffer_snapshot;
7306 let mut selections = self.selections.all::<usize>(cx);
7307 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7308 let query = &select_prev_state.query;
7309 if !select_prev_state.done {
7310 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7311 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7312 let mut next_selected_range = None;
7313 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7314 let bytes_before_last_selection =
7315 buffer.reversed_bytes_in_range(0..last_selection.start);
7316 let bytes_after_first_selection =
7317 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7318 let query_matches = query
7319 .stream_find_iter(bytes_before_last_selection)
7320 .map(|result| (last_selection.start, result))
7321 .chain(
7322 query
7323 .stream_find_iter(bytes_after_first_selection)
7324 .map(|result| (buffer.len(), result)),
7325 );
7326 for (end_offset, query_match) in query_matches {
7327 let query_match = query_match.unwrap(); // can only fail due to I/O
7328 let offset_range =
7329 end_offset - query_match.end()..end_offset - query_match.start();
7330 let display_range = offset_range.start.to_display_point(&display_map)
7331 ..offset_range.end.to_display_point(&display_map);
7332
7333 if !select_prev_state.wordwise
7334 || (!movement::is_inside_word(&display_map, display_range.start)
7335 && !movement::is_inside_word(&display_map, display_range.end))
7336 {
7337 next_selected_range = Some(offset_range);
7338 break;
7339 }
7340 }
7341
7342 if let Some(next_selected_range) = next_selected_range {
7343 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
7344 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7345 if action.replace_newest {
7346 s.delete(s.newest_anchor().id);
7347 }
7348 s.insert_range(next_selected_range);
7349 });
7350 } else {
7351 select_prev_state.done = true;
7352 }
7353 }
7354
7355 self.select_prev_state = Some(select_prev_state);
7356 } else {
7357 let mut only_carets = true;
7358 let mut same_text_selected = true;
7359 let mut selected_text = None;
7360
7361 let mut selections_iter = selections.iter().peekable();
7362 while let Some(selection) = selections_iter.next() {
7363 if selection.start != selection.end {
7364 only_carets = false;
7365 }
7366
7367 if same_text_selected {
7368 if selected_text.is_none() {
7369 selected_text =
7370 Some(buffer.text_for_range(selection.range()).collect::<String>());
7371 }
7372
7373 if let Some(next_selection) = selections_iter.peek() {
7374 if next_selection.range().len() == selection.range().len() {
7375 let next_selected_text = buffer
7376 .text_for_range(next_selection.range())
7377 .collect::<String>();
7378 if Some(next_selected_text) != selected_text {
7379 same_text_selected = false;
7380 selected_text = None;
7381 }
7382 } else {
7383 same_text_selected = false;
7384 selected_text = None;
7385 }
7386 }
7387 }
7388 }
7389
7390 if only_carets {
7391 for selection in &mut selections {
7392 let word_range = movement::surrounding_word(
7393 &display_map,
7394 selection.start.to_display_point(&display_map),
7395 );
7396 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7397 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7398 selection.goal = SelectionGoal::None;
7399 selection.reversed = false;
7400 }
7401 if selections.len() == 1 {
7402 let selection = selections
7403 .last()
7404 .expect("ensured that there's only one selection");
7405 let query = buffer
7406 .text_for_range(selection.start..selection.end)
7407 .collect::<String>();
7408 let is_empty = query.is_empty();
7409 let select_state = SelectNextState {
7410 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
7411 wordwise: true,
7412 done: is_empty,
7413 };
7414 self.select_prev_state = Some(select_state);
7415 } else {
7416 self.select_prev_state = None;
7417 }
7418
7419 self.unfold_ranges(
7420 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
7421 false,
7422 true,
7423 cx,
7424 );
7425 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7426 s.select(selections);
7427 });
7428 } else if let Some(selected_text) = selected_text {
7429 self.select_prev_state = Some(SelectNextState {
7430 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
7431 wordwise: false,
7432 done: false,
7433 });
7434 self.select_previous(action, cx)?;
7435 }
7436 }
7437 Ok(())
7438 }
7439
7440 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
7441 let text_layout_details = &self.text_layout_details(cx);
7442 self.transact(cx, |this, cx| {
7443 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7444 let mut edits = Vec::new();
7445 let mut selection_edit_ranges = Vec::new();
7446 let mut last_toggled_row = None;
7447 let snapshot = this.buffer.read(cx).read(cx);
7448 let empty_str: Arc<str> = "".into();
7449 let mut suffixes_inserted = Vec::new();
7450
7451 fn comment_prefix_range(
7452 snapshot: &MultiBufferSnapshot,
7453 row: MultiBufferRow,
7454 comment_prefix: &str,
7455 comment_prefix_whitespace: &str,
7456 ) -> Range<Point> {
7457 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
7458
7459 let mut line_bytes = snapshot
7460 .bytes_in_range(start..snapshot.max_point())
7461 .flatten()
7462 .copied();
7463
7464 // If this line currently begins with the line comment prefix, then record
7465 // the range containing the prefix.
7466 if line_bytes
7467 .by_ref()
7468 .take(comment_prefix.len())
7469 .eq(comment_prefix.bytes())
7470 {
7471 // Include any whitespace that matches the comment prefix.
7472 let matching_whitespace_len = line_bytes
7473 .zip(comment_prefix_whitespace.bytes())
7474 .take_while(|(a, b)| a == b)
7475 .count() as u32;
7476 let end = Point::new(
7477 start.row,
7478 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
7479 );
7480 start..end
7481 } else {
7482 start..start
7483 }
7484 }
7485
7486 fn comment_suffix_range(
7487 snapshot: &MultiBufferSnapshot,
7488 row: MultiBufferRow,
7489 comment_suffix: &str,
7490 comment_suffix_has_leading_space: bool,
7491 ) -> Range<Point> {
7492 let end = Point::new(row.0, snapshot.line_len(row));
7493 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
7494
7495 let mut line_end_bytes = snapshot
7496 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
7497 .flatten()
7498 .copied();
7499
7500 let leading_space_len = if suffix_start_column > 0
7501 && line_end_bytes.next() == Some(b' ')
7502 && comment_suffix_has_leading_space
7503 {
7504 1
7505 } else {
7506 0
7507 };
7508
7509 // If this line currently begins with the line comment prefix, then record
7510 // the range containing the prefix.
7511 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
7512 let start = Point::new(end.row, suffix_start_column - leading_space_len);
7513 start..end
7514 } else {
7515 end..end
7516 }
7517 }
7518
7519 // TODO: Handle selections that cross excerpts
7520 for selection in &mut selections {
7521 let start_column = snapshot
7522 .indent_size_for_line(MultiBufferRow(selection.start.row))
7523 .len;
7524 let language = if let Some(language) =
7525 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
7526 {
7527 language
7528 } else {
7529 continue;
7530 };
7531
7532 selection_edit_ranges.clear();
7533
7534 // If multiple selections contain a given row, avoid processing that
7535 // row more than once.
7536 let mut start_row = MultiBufferRow(selection.start.row);
7537 if last_toggled_row == Some(start_row) {
7538 start_row = start_row.next_row();
7539 }
7540 let end_row =
7541 if selection.end.row > selection.start.row && selection.end.column == 0 {
7542 MultiBufferRow(selection.end.row - 1)
7543 } else {
7544 MultiBufferRow(selection.end.row)
7545 };
7546 last_toggled_row = Some(end_row);
7547
7548 if start_row > end_row {
7549 continue;
7550 }
7551
7552 // If the language has line comments, toggle those.
7553 let full_comment_prefixes = language.line_comment_prefixes();
7554 if !full_comment_prefixes.is_empty() {
7555 let first_prefix = full_comment_prefixes
7556 .first()
7557 .expect("prefixes is non-empty");
7558 let prefix_trimmed_lengths = full_comment_prefixes
7559 .iter()
7560 .map(|p| p.trim_end_matches(' ').len())
7561 .collect::<SmallVec<[usize; 4]>>();
7562
7563 let mut all_selection_lines_are_comments = true;
7564
7565 for row in start_row.0..=end_row.0 {
7566 let row = MultiBufferRow(row);
7567 if start_row < end_row && snapshot.is_line_blank(row) {
7568 continue;
7569 }
7570
7571 let prefix_range = full_comment_prefixes
7572 .iter()
7573 .zip(prefix_trimmed_lengths.iter().copied())
7574 .map(|(prefix, trimmed_prefix_len)| {
7575 comment_prefix_range(
7576 snapshot.deref(),
7577 row,
7578 &prefix[..trimmed_prefix_len],
7579 &prefix[trimmed_prefix_len..],
7580 )
7581 })
7582 .max_by_key(|range| range.end.column - range.start.column)
7583 .expect("prefixes is non-empty");
7584
7585 if prefix_range.is_empty() {
7586 all_selection_lines_are_comments = false;
7587 }
7588
7589 selection_edit_ranges.push(prefix_range);
7590 }
7591
7592 if all_selection_lines_are_comments {
7593 edits.extend(
7594 selection_edit_ranges
7595 .iter()
7596 .cloned()
7597 .map(|range| (range, empty_str.clone())),
7598 );
7599 } else {
7600 let min_column = selection_edit_ranges
7601 .iter()
7602 .map(|range| range.start.column)
7603 .min()
7604 .unwrap_or(0);
7605 edits.extend(selection_edit_ranges.iter().map(|range| {
7606 let position = Point::new(range.start.row, min_column);
7607 (position..position, first_prefix.clone())
7608 }));
7609 }
7610 } else if let Some((full_comment_prefix, comment_suffix)) =
7611 language.block_comment_delimiters()
7612 {
7613 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
7614 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
7615 let prefix_range = comment_prefix_range(
7616 snapshot.deref(),
7617 start_row,
7618 comment_prefix,
7619 comment_prefix_whitespace,
7620 );
7621 let suffix_range = comment_suffix_range(
7622 snapshot.deref(),
7623 end_row,
7624 comment_suffix.trim_start_matches(' '),
7625 comment_suffix.starts_with(' '),
7626 );
7627
7628 if prefix_range.is_empty() || suffix_range.is_empty() {
7629 edits.push((
7630 prefix_range.start..prefix_range.start,
7631 full_comment_prefix.clone(),
7632 ));
7633 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
7634 suffixes_inserted.push((end_row, comment_suffix.len()));
7635 } else {
7636 edits.push((prefix_range, empty_str.clone()));
7637 edits.push((suffix_range, empty_str.clone()));
7638 }
7639 } else {
7640 continue;
7641 }
7642 }
7643
7644 drop(snapshot);
7645 this.buffer.update(cx, |buffer, cx| {
7646 buffer.edit(edits, None, cx);
7647 });
7648
7649 // Adjust selections so that they end before any comment suffixes that
7650 // were inserted.
7651 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
7652 let mut selections = this.selections.all::<Point>(cx);
7653 let snapshot = this.buffer.read(cx).read(cx);
7654 for selection in &mut selections {
7655 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
7656 match row.cmp(&MultiBufferRow(selection.end.row)) {
7657 Ordering::Less => {
7658 suffixes_inserted.next();
7659 continue;
7660 }
7661 Ordering::Greater => break,
7662 Ordering::Equal => {
7663 if selection.end.column == snapshot.line_len(row) {
7664 if selection.is_empty() {
7665 selection.start.column -= suffix_len as u32;
7666 }
7667 selection.end.column -= suffix_len as u32;
7668 }
7669 break;
7670 }
7671 }
7672 }
7673 }
7674
7675 drop(snapshot);
7676 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7677
7678 let selections = this.selections.all::<Point>(cx);
7679 let selections_on_single_row = selections.windows(2).all(|selections| {
7680 selections[0].start.row == selections[1].start.row
7681 && selections[0].end.row == selections[1].end.row
7682 && selections[0].start.row == selections[0].end.row
7683 });
7684 let selections_selecting = selections
7685 .iter()
7686 .any(|selection| selection.start != selection.end);
7687 let advance_downwards = action.advance_downwards
7688 && selections_on_single_row
7689 && !selections_selecting
7690 && this.mode != EditorMode::SingleLine;
7691
7692 if advance_downwards {
7693 let snapshot = this.buffer.read(cx).snapshot(cx);
7694
7695 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7696 s.move_cursors_with(|display_snapshot, display_point, _| {
7697 let mut point = display_point.to_point(display_snapshot);
7698 point.row += 1;
7699 point = snapshot.clip_point(point, Bias::Left);
7700 let display_point = point.to_display_point(display_snapshot);
7701 let goal = SelectionGoal::HorizontalPosition(
7702 display_snapshot
7703 .x_for_display_point(display_point, &text_layout_details)
7704 .into(),
7705 );
7706 (display_point, goal)
7707 })
7708 });
7709 }
7710 });
7711 }
7712
7713 pub fn select_larger_syntax_node(
7714 &mut self,
7715 _: &SelectLargerSyntaxNode,
7716 cx: &mut ViewContext<Self>,
7717 ) {
7718 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7719 let buffer = self.buffer.read(cx).snapshot(cx);
7720 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
7721
7722 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7723 let mut selected_larger_node = false;
7724 let new_selections = old_selections
7725 .iter()
7726 .map(|selection| {
7727 let old_range = selection.start..selection.end;
7728 let mut new_range = old_range.clone();
7729 while let Some(containing_range) =
7730 buffer.range_for_syntax_ancestor(new_range.clone())
7731 {
7732 new_range = containing_range;
7733 if !display_map.intersects_fold(new_range.start)
7734 && !display_map.intersects_fold(new_range.end)
7735 {
7736 break;
7737 }
7738 }
7739
7740 selected_larger_node |= new_range != old_range;
7741 Selection {
7742 id: selection.id,
7743 start: new_range.start,
7744 end: new_range.end,
7745 goal: SelectionGoal::None,
7746 reversed: selection.reversed,
7747 }
7748 })
7749 .collect::<Vec<_>>();
7750
7751 if selected_larger_node {
7752 stack.push(old_selections);
7753 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7754 s.select(new_selections);
7755 });
7756 }
7757 self.select_larger_syntax_node_stack = stack;
7758 }
7759
7760 pub fn select_smaller_syntax_node(
7761 &mut self,
7762 _: &SelectSmallerSyntaxNode,
7763 cx: &mut ViewContext<Self>,
7764 ) {
7765 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
7766 if let Some(selections) = stack.pop() {
7767 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7768 s.select(selections.to_vec());
7769 });
7770 }
7771 self.select_larger_syntax_node_stack = stack;
7772 }
7773
7774 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
7775 let project = self.project.clone();
7776 cx.spawn(|this, mut cx| async move {
7777 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
7778 this.display_map.update(cx, |map, cx| map.snapshot(cx))
7779 }) else {
7780 return;
7781 };
7782
7783 let Some(project) = project else {
7784 return;
7785 };
7786 if project
7787 .update(&mut cx, |this, _| this.is_remote())
7788 .unwrap_or(true)
7789 {
7790 // Do not display any test indicators in remote projects.
7791 return;
7792 }
7793 let new_rows =
7794 cx.background_executor()
7795 .spawn({
7796 let snapshot = display_snapshot.clone();
7797 async move {
7798 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
7799 }
7800 })
7801 .await;
7802 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
7803
7804 this.update(&mut cx, |this, _| {
7805 this.clear_tasks();
7806 for (key, value) in rows {
7807 this.insert_tasks(key, value);
7808 }
7809 })
7810 .ok();
7811 })
7812 }
7813 fn fetch_runnable_ranges(
7814 snapshot: &DisplaySnapshot,
7815 range: Range<Anchor>,
7816 ) -> Vec<language::RunnableRange> {
7817 snapshot.buffer_snapshot.runnable_ranges(range).collect()
7818 }
7819
7820 fn runnable_rows(
7821 project: Model<Project>,
7822 snapshot: DisplaySnapshot,
7823 runnable_ranges: Vec<RunnableRange>,
7824 mut cx: AsyncWindowContext,
7825 ) -> Vec<((BufferId, u32), (usize, RunnableTasks))> {
7826 runnable_ranges
7827 .into_iter()
7828 .filter_map(|mut runnable| {
7829 let (tasks, _) = cx
7830 .update(|cx| {
7831 Self::resolve_runnable(project.clone(), &mut runnable.runnable, cx)
7832 })
7833 .ok()?;
7834 if tasks.is_empty() {
7835 return None;
7836 }
7837
7838 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
7839
7840 let row = snapshot
7841 .buffer_snapshot
7842 .buffer_line_for_row(MultiBufferRow(point.row))?
7843 .1
7844 .start
7845 .row;
7846
7847 Some((
7848 (runnable.buffer_id, row),
7849 (
7850 runnable.run_range.start,
7851 RunnableTasks {
7852 templates: tasks,
7853 column: point.column,
7854 extra_variables: runnable.extra_captures,
7855 },
7856 ),
7857 ))
7858 })
7859 .collect()
7860 }
7861
7862 fn resolve_runnable(
7863 project: Model<Project>,
7864 runnable: &mut Runnable,
7865 cx: &WindowContext<'_>,
7866 ) -> (Vec<(TaskSourceKind, TaskTemplate)>, Option<WorktreeId>) {
7867 let (inventory, worktree_id) = project.read_with(cx, |project, cx| {
7868 let worktree_id = project
7869 .buffer_for_id(runnable.buffer)
7870 .and_then(|buffer| buffer.read(cx).file())
7871 .map(|file| WorktreeId::from_usize(file.worktree_id()));
7872
7873 (project.task_inventory().clone(), worktree_id)
7874 });
7875
7876 let inventory = inventory.read(cx);
7877 let tags = mem::take(&mut runnable.tags);
7878 let mut tags: Vec<_> = tags
7879 .into_iter()
7880 .flat_map(|tag| {
7881 let tag = tag.0.clone();
7882 inventory
7883 .list_tasks(Some(runnable.language.clone()), worktree_id)
7884 .into_iter()
7885 .filter(move |(_, template)| {
7886 template.tags.iter().any(|source_tag| source_tag == &tag)
7887 })
7888 })
7889 .sorted_by_key(|(kind, _)| kind.to_owned())
7890 .collect();
7891 if let Some((leading_tag_source, _)) = tags.first() {
7892 // Strongest source wins; if we have worktree tag binding, prefer that to
7893 // global and language bindings;
7894 // if we have a global binding, prefer that to language binding.
7895 let first_mismatch = tags
7896 .iter()
7897 .position(|(tag_source, _)| tag_source != leading_tag_source);
7898 if let Some(index) = first_mismatch {
7899 tags.truncate(index);
7900 }
7901 }
7902
7903 (tags, worktree_id)
7904 }
7905
7906 pub fn move_to_enclosing_bracket(
7907 &mut self,
7908 _: &MoveToEnclosingBracket,
7909 cx: &mut ViewContext<Self>,
7910 ) {
7911 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7912 s.move_offsets_with(|snapshot, selection| {
7913 let Some(enclosing_bracket_ranges) =
7914 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
7915 else {
7916 return;
7917 };
7918
7919 let mut best_length = usize::MAX;
7920 let mut best_inside = false;
7921 let mut best_in_bracket_range = false;
7922 let mut best_destination = None;
7923 for (open, close) in enclosing_bracket_ranges {
7924 let close = close.to_inclusive();
7925 let length = close.end() - open.start;
7926 let inside = selection.start >= open.end && selection.end <= *close.start();
7927 let in_bracket_range = open.to_inclusive().contains(&selection.head())
7928 || close.contains(&selection.head());
7929
7930 // If best is next to a bracket and current isn't, skip
7931 if !in_bracket_range && best_in_bracket_range {
7932 continue;
7933 }
7934
7935 // Prefer smaller lengths unless best is inside and current isn't
7936 if length > best_length && (best_inside || !inside) {
7937 continue;
7938 }
7939
7940 best_length = length;
7941 best_inside = inside;
7942 best_in_bracket_range = in_bracket_range;
7943 best_destination = Some(
7944 if close.contains(&selection.start) && close.contains(&selection.end) {
7945 if inside {
7946 open.end
7947 } else {
7948 open.start
7949 }
7950 } else {
7951 if inside {
7952 *close.start()
7953 } else {
7954 *close.end()
7955 }
7956 },
7957 );
7958 }
7959
7960 if let Some(destination) = best_destination {
7961 selection.collapse_to(destination, SelectionGoal::None);
7962 }
7963 })
7964 });
7965 }
7966
7967 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
7968 self.end_selection(cx);
7969 self.selection_history.mode = SelectionHistoryMode::Undoing;
7970 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
7971 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7972 self.select_next_state = entry.select_next_state;
7973 self.select_prev_state = entry.select_prev_state;
7974 self.add_selections_state = entry.add_selections_state;
7975 self.request_autoscroll(Autoscroll::newest(), cx);
7976 }
7977 self.selection_history.mode = SelectionHistoryMode::Normal;
7978 }
7979
7980 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
7981 self.end_selection(cx);
7982 self.selection_history.mode = SelectionHistoryMode::Redoing;
7983 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
7984 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
7985 self.select_next_state = entry.select_next_state;
7986 self.select_prev_state = entry.select_prev_state;
7987 self.add_selections_state = entry.add_selections_state;
7988 self.request_autoscroll(Autoscroll::newest(), cx);
7989 }
7990 self.selection_history.mode = SelectionHistoryMode::Normal;
7991 }
7992
7993 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
7994 let selections = self.selections.disjoint_anchors();
7995
7996 let lines = if action.lines == 0 { 3 } else { action.lines };
7997
7998 self.buffer.update(cx, |buffer, cx| {
7999 buffer.expand_excerpts(
8000 selections
8001 .into_iter()
8002 .map(|selection| selection.head().excerpt_id)
8003 .dedup(),
8004 lines,
8005 cx,
8006 )
8007 })
8008 }
8009
8010 pub fn expand_excerpt(&mut self, excerpt: ExcerptId, cx: &mut ViewContext<Self>) {
8011 self.buffer
8012 .update(cx, |buffer, cx| buffer.expand_excerpts([excerpt], 3, cx))
8013 }
8014
8015 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8016 self.go_to_diagnostic_impl(Direction::Next, cx)
8017 }
8018
8019 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8020 self.go_to_diagnostic_impl(Direction::Prev, cx)
8021 }
8022
8023 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8024 let buffer = self.buffer.read(cx).snapshot(cx);
8025 let selection = self.selections.newest::<usize>(cx);
8026
8027 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8028 if direction == Direction::Next {
8029 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8030 let (group_id, jump_to) = popover.activation_info();
8031 if self.activate_diagnostics(group_id, cx) {
8032 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8033 let mut new_selection = s.newest_anchor().clone();
8034 new_selection.collapse_to(jump_to, SelectionGoal::None);
8035 s.select_anchors(vec![new_selection.clone()]);
8036 });
8037 }
8038 return;
8039 }
8040 }
8041
8042 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8043 active_diagnostics
8044 .primary_range
8045 .to_offset(&buffer)
8046 .to_inclusive()
8047 });
8048 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8049 if active_primary_range.contains(&selection.head()) {
8050 *active_primary_range.start()
8051 } else {
8052 selection.head()
8053 }
8054 } else {
8055 selection.head()
8056 };
8057 let snapshot = self.snapshot(cx);
8058 loop {
8059 let diagnostics = if direction == Direction::Prev {
8060 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8061 } else {
8062 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8063 }
8064 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8065 let group = diagnostics
8066 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8067 // be sorted in a stable way
8068 // skip until we are at current active diagnostic, if it exists
8069 .skip_while(|entry| {
8070 (match direction {
8071 Direction::Prev => entry.range.start >= search_start,
8072 Direction::Next => entry.range.start <= search_start,
8073 }) && self
8074 .active_diagnostics
8075 .as_ref()
8076 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8077 })
8078 .find_map(|entry| {
8079 if entry.diagnostic.is_primary
8080 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8081 && !entry.range.is_empty()
8082 // if we match with the active diagnostic, skip it
8083 && Some(entry.diagnostic.group_id)
8084 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8085 {
8086 Some((entry.range, entry.diagnostic.group_id))
8087 } else {
8088 None
8089 }
8090 });
8091
8092 if let Some((primary_range, group_id)) = group {
8093 if self.activate_diagnostics(group_id, cx) {
8094 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8095 s.select(vec![Selection {
8096 id: selection.id,
8097 start: primary_range.start,
8098 end: primary_range.start,
8099 reversed: false,
8100 goal: SelectionGoal::None,
8101 }]);
8102 });
8103 }
8104 break;
8105 } else {
8106 // Cycle around to the start of the buffer, potentially moving back to the start of
8107 // the currently active diagnostic.
8108 active_primary_range.take();
8109 if direction == Direction::Prev {
8110 if search_start == buffer.len() {
8111 break;
8112 } else {
8113 search_start = buffer.len();
8114 }
8115 } else if search_start == 0 {
8116 break;
8117 } else {
8118 search_start = 0;
8119 }
8120 }
8121 }
8122 }
8123
8124 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8125 let snapshot = self
8126 .display_map
8127 .update(cx, |display_map, cx| display_map.snapshot(cx));
8128 let selection = self.selections.newest::<Point>(cx);
8129
8130 if !self.seek_in_direction(
8131 &snapshot,
8132 selection.head(),
8133 false,
8134 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8135 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8136 ),
8137 cx,
8138 ) {
8139 let wrapped_point = Point::zero();
8140 self.seek_in_direction(
8141 &snapshot,
8142 wrapped_point,
8143 true,
8144 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8145 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8146 ),
8147 cx,
8148 );
8149 }
8150 }
8151
8152 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8153 let snapshot = self
8154 .display_map
8155 .update(cx, |display_map, cx| display_map.snapshot(cx));
8156 let selection = self.selections.newest::<Point>(cx);
8157
8158 if !self.seek_in_direction(
8159 &snapshot,
8160 selection.head(),
8161 false,
8162 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8163 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8164 ),
8165 cx,
8166 ) {
8167 let wrapped_point = snapshot.buffer_snapshot.max_point();
8168 self.seek_in_direction(
8169 &snapshot,
8170 wrapped_point,
8171 true,
8172 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8173 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8174 ),
8175 cx,
8176 );
8177 }
8178 }
8179
8180 fn seek_in_direction(
8181 &mut self,
8182 snapshot: &DisplaySnapshot,
8183 initial_point: Point,
8184 is_wrapped: bool,
8185 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8186 cx: &mut ViewContext<Editor>,
8187 ) -> bool {
8188 let display_point = initial_point.to_display_point(snapshot);
8189 let mut hunks = hunks
8190 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8191 .filter(|hunk| {
8192 if is_wrapped {
8193 true
8194 } else {
8195 !hunk.contains_display_row(display_point.row())
8196 }
8197 })
8198 .dedup();
8199
8200 if let Some(hunk) = hunks.next() {
8201 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8202 let row = hunk.start_display_row();
8203 let point = DisplayPoint::new(row, 0);
8204 s.select_display_ranges([point..point]);
8205 });
8206
8207 true
8208 } else {
8209 false
8210 }
8211 }
8212
8213 pub fn go_to_definition(
8214 &mut self,
8215 _: &GoToDefinition,
8216 cx: &mut ViewContext<Self>,
8217 ) -> Task<Result<bool>> {
8218 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8219 }
8220
8221 pub fn go_to_implementation(
8222 &mut self,
8223 _: &GoToImplementation,
8224 cx: &mut ViewContext<Self>,
8225 ) -> Task<Result<bool>> {
8226 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8227 }
8228
8229 pub fn go_to_implementation_split(
8230 &mut self,
8231 _: &GoToImplementationSplit,
8232 cx: &mut ViewContext<Self>,
8233 ) -> Task<Result<bool>> {
8234 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8235 }
8236
8237 pub fn go_to_type_definition(
8238 &mut self,
8239 _: &GoToTypeDefinition,
8240 cx: &mut ViewContext<Self>,
8241 ) -> Task<Result<bool>> {
8242 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8243 }
8244
8245 pub fn go_to_definition_split(
8246 &mut self,
8247 _: &GoToDefinitionSplit,
8248 cx: &mut ViewContext<Self>,
8249 ) -> Task<Result<bool>> {
8250 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
8251 }
8252
8253 pub fn go_to_type_definition_split(
8254 &mut self,
8255 _: &GoToTypeDefinitionSplit,
8256 cx: &mut ViewContext<Self>,
8257 ) -> Task<Result<bool>> {
8258 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
8259 }
8260
8261 fn go_to_definition_of_kind(
8262 &mut self,
8263 kind: GotoDefinitionKind,
8264 split: bool,
8265 cx: &mut ViewContext<Self>,
8266 ) -> Task<Result<bool>> {
8267 let Some(workspace) = self.workspace() else {
8268 return Task::ready(Ok(false));
8269 };
8270 let buffer = self.buffer.read(cx);
8271 let head = self.selections.newest::<usize>(cx).head();
8272 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
8273 text_anchor
8274 } else {
8275 return Task::ready(Ok(false));
8276 };
8277
8278 let project = workspace.read(cx).project().clone();
8279 let definitions = project.update(cx, |project, cx| match kind {
8280 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
8281 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
8282 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
8283 });
8284
8285 cx.spawn(|editor, mut cx| async move {
8286 let definitions = definitions.await?;
8287 let navigated = editor
8288 .update(&mut cx, |editor, cx| {
8289 editor.navigate_to_hover_links(
8290 Some(kind),
8291 definitions
8292 .into_iter()
8293 .filter(|location| {
8294 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
8295 })
8296 .map(HoverLink::Text)
8297 .collect::<Vec<_>>(),
8298 split,
8299 cx,
8300 )
8301 })?
8302 .await?;
8303 anyhow::Ok(navigated)
8304 })
8305 }
8306
8307 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
8308 let position = self.selections.newest_anchor().head();
8309 let Some((buffer, buffer_position)) =
8310 self.buffer.read(cx).text_anchor_for_position(position, cx)
8311 else {
8312 return;
8313 };
8314
8315 cx.spawn(|editor, mut cx| async move {
8316 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
8317 editor.update(&mut cx, |_, cx| {
8318 cx.open_url(&url);
8319 })
8320 } else {
8321 Ok(())
8322 }
8323 })
8324 .detach();
8325 }
8326
8327 pub(crate) fn navigate_to_hover_links(
8328 &mut self,
8329 kind: Option<GotoDefinitionKind>,
8330 mut definitions: Vec<HoverLink>,
8331 split: bool,
8332 cx: &mut ViewContext<Editor>,
8333 ) -> Task<Result<bool>> {
8334 // If there is one definition, just open it directly
8335 if definitions.len() == 1 {
8336 let definition = definitions.pop().unwrap();
8337 let target_task = match definition {
8338 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8339 HoverLink::InlayHint(lsp_location, server_id) => {
8340 self.compute_target_location(lsp_location, server_id, cx)
8341 }
8342 HoverLink::Url(url) => {
8343 cx.open_url(&url);
8344 Task::ready(Ok(None))
8345 }
8346 };
8347 cx.spawn(|editor, mut cx| async move {
8348 let target = target_task.await.context("target resolution task")?;
8349 if let Some(target) = target {
8350 editor.update(&mut cx, |editor, cx| {
8351 let Some(workspace) = editor.workspace() else {
8352 return false;
8353 };
8354 let pane = workspace.read(cx).active_pane().clone();
8355
8356 let range = target.range.to_offset(target.buffer.read(cx));
8357 let range = editor.range_for_match(&range);
8358 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
8359 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
8360 s.select_ranges([range]);
8361 });
8362 } else {
8363 cx.window_context().defer(move |cx| {
8364 let target_editor: View<Self> =
8365 workspace.update(cx, |workspace, cx| {
8366 let pane = if split {
8367 workspace.adjacent_pane(cx)
8368 } else {
8369 workspace.active_pane().clone()
8370 };
8371
8372 workspace.open_project_item(pane, target.buffer.clone(), cx)
8373 });
8374 target_editor.update(cx, |target_editor, cx| {
8375 // When selecting a definition in a different buffer, disable the nav history
8376 // to avoid creating a history entry at the previous cursor location.
8377 pane.update(cx, |pane, _| pane.disable_history());
8378 target_editor.change_selections(
8379 Some(Autoscroll::focused()),
8380 cx,
8381 |s| {
8382 s.select_ranges([range]);
8383 },
8384 );
8385 pane.update(cx, |pane, _| pane.enable_history());
8386 });
8387 });
8388 }
8389 true
8390 })
8391 } else {
8392 Ok(false)
8393 }
8394 })
8395 } else if !definitions.is_empty() {
8396 let replica_id = self.replica_id(cx);
8397 cx.spawn(|editor, mut cx| async move {
8398 let (title, location_tasks, workspace) = editor
8399 .update(&mut cx, |editor, cx| {
8400 let tab_kind = match kind {
8401 Some(GotoDefinitionKind::Implementation) => "Implementations",
8402 _ => "Definitions",
8403 };
8404 let title = definitions
8405 .iter()
8406 .find_map(|definition| match definition {
8407 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
8408 let buffer = origin.buffer.read(cx);
8409 format!(
8410 "{} for {}",
8411 tab_kind,
8412 buffer
8413 .text_for_range(origin.range.clone())
8414 .collect::<String>()
8415 )
8416 }),
8417 HoverLink::InlayHint(_, _) => None,
8418 HoverLink::Url(_) => None,
8419 })
8420 .unwrap_or(tab_kind.to_string());
8421 let location_tasks = definitions
8422 .into_iter()
8423 .map(|definition| match definition {
8424 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8425 HoverLink::InlayHint(lsp_location, server_id) => {
8426 editor.compute_target_location(lsp_location, server_id, cx)
8427 }
8428 HoverLink::Url(_) => Task::ready(Ok(None)),
8429 })
8430 .collect::<Vec<_>>();
8431 (title, location_tasks, editor.workspace().clone())
8432 })
8433 .context("location tasks preparation")?;
8434
8435 let locations = futures::future::join_all(location_tasks)
8436 .await
8437 .into_iter()
8438 .filter_map(|location| location.transpose())
8439 .collect::<Result<_>>()
8440 .context("location tasks")?;
8441
8442 let Some(workspace) = workspace else {
8443 return Ok(false);
8444 };
8445 let opened = workspace
8446 .update(&mut cx, |workspace, cx| {
8447 Self::open_locations_in_multibuffer(
8448 workspace, locations, replica_id, title, split, cx,
8449 )
8450 })
8451 .ok();
8452
8453 anyhow::Ok(opened.is_some())
8454 })
8455 } else {
8456 Task::ready(Ok(false))
8457 }
8458 }
8459
8460 fn compute_target_location(
8461 &self,
8462 lsp_location: lsp::Location,
8463 server_id: LanguageServerId,
8464 cx: &mut ViewContext<Editor>,
8465 ) -> Task<anyhow::Result<Option<Location>>> {
8466 let Some(project) = self.project.clone() else {
8467 return Task::Ready(Some(Ok(None)));
8468 };
8469
8470 cx.spawn(move |editor, mut cx| async move {
8471 let location_task = editor.update(&mut cx, |editor, cx| {
8472 project.update(cx, |project, cx| {
8473 let language_server_name =
8474 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
8475 project
8476 .language_server_for_buffer(buffer.read(cx), server_id, cx)
8477 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
8478 });
8479 language_server_name.map(|language_server_name| {
8480 project.open_local_buffer_via_lsp(
8481 lsp_location.uri.clone(),
8482 server_id,
8483 language_server_name,
8484 cx,
8485 )
8486 })
8487 })
8488 })?;
8489 let location = match location_task {
8490 Some(task) => Some({
8491 let target_buffer_handle = task.await.context("open local buffer")?;
8492 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
8493 let target_start = target_buffer
8494 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
8495 let target_end = target_buffer
8496 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
8497 target_buffer.anchor_after(target_start)
8498 ..target_buffer.anchor_before(target_end)
8499 })?;
8500 Location {
8501 buffer: target_buffer_handle,
8502 range,
8503 }
8504 }),
8505 None => None,
8506 };
8507 Ok(location)
8508 })
8509 }
8510
8511 pub fn find_all_references(
8512 &mut self,
8513 _: &FindAllReferences,
8514 cx: &mut ViewContext<Self>,
8515 ) -> Option<Task<Result<()>>> {
8516 let multi_buffer = self.buffer.read(cx);
8517 let selection = self.selections.newest::<usize>(cx);
8518 let head = selection.head();
8519
8520 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
8521 let head_anchor = multi_buffer_snapshot.anchor_at(
8522 head,
8523 if head < selection.tail() {
8524 Bias::Right
8525 } else {
8526 Bias::Left
8527 },
8528 );
8529
8530 match self
8531 .find_all_references_task_sources
8532 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
8533 {
8534 Ok(_) => {
8535 log::info!(
8536 "Ignoring repeated FindAllReferences invocation with the position of already running task"
8537 );
8538 return None;
8539 }
8540 Err(i) => {
8541 self.find_all_references_task_sources.insert(i, head_anchor);
8542 }
8543 }
8544
8545 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
8546 let replica_id = self.replica_id(cx);
8547 let workspace = self.workspace()?;
8548 let project = workspace.read(cx).project().clone();
8549 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
8550 Some(cx.spawn(|editor, mut cx| async move {
8551 let _cleanup = defer({
8552 let mut cx = cx.clone();
8553 move || {
8554 let _ = editor.update(&mut cx, |editor, _| {
8555 if let Ok(i) =
8556 editor
8557 .find_all_references_task_sources
8558 .binary_search_by(|anchor| {
8559 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
8560 })
8561 {
8562 editor.find_all_references_task_sources.remove(i);
8563 }
8564 });
8565 }
8566 });
8567
8568 let locations = references.await?;
8569 if locations.is_empty() {
8570 return anyhow::Ok(());
8571 }
8572
8573 workspace.update(&mut cx, |workspace, cx| {
8574 let title = locations
8575 .first()
8576 .as_ref()
8577 .map(|location| {
8578 let buffer = location.buffer.read(cx);
8579 format!(
8580 "References to `{}`",
8581 buffer
8582 .text_for_range(location.range.clone())
8583 .collect::<String>()
8584 )
8585 })
8586 .unwrap();
8587 Self::open_locations_in_multibuffer(
8588 workspace, locations, replica_id, title, false, cx,
8589 );
8590 })
8591 }))
8592 }
8593
8594 /// Opens a multibuffer with the given project locations in it
8595 pub fn open_locations_in_multibuffer(
8596 workspace: &mut Workspace,
8597 mut locations: Vec<Location>,
8598 replica_id: ReplicaId,
8599 title: String,
8600 split: bool,
8601 cx: &mut ViewContext<Workspace>,
8602 ) {
8603 // If there are multiple definitions, open them in a multibuffer
8604 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
8605 let mut locations = locations.into_iter().peekable();
8606 let mut ranges_to_highlight = Vec::new();
8607 let capability = workspace.project().read(cx).capability();
8608
8609 let excerpt_buffer = cx.new_model(|cx| {
8610 let mut multibuffer = MultiBuffer::new(replica_id, capability);
8611 while let Some(location) = locations.next() {
8612 let buffer = location.buffer.read(cx);
8613 let mut ranges_for_buffer = Vec::new();
8614 let range = location.range.to_offset(buffer);
8615 ranges_for_buffer.push(range.clone());
8616
8617 while let Some(next_location) = locations.peek() {
8618 if next_location.buffer == location.buffer {
8619 ranges_for_buffer.push(next_location.range.to_offset(buffer));
8620 locations.next();
8621 } else {
8622 break;
8623 }
8624 }
8625
8626 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
8627 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
8628 location.buffer.clone(),
8629 ranges_for_buffer,
8630 DEFAULT_MULTIBUFFER_CONTEXT,
8631 cx,
8632 ))
8633 }
8634
8635 multibuffer.with_title(title)
8636 });
8637
8638 let editor = cx.new_view(|cx| {
8639 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
8640 });
8641 editor.update(cx, |editor, cx| {
8642 editor.highlight_background::<Self>(
8643 &ranges_to_highlight,
8644 |theme| theme.editor_highlighted_line_background,
8645 cx,
8646 );
8647 });
8648
8649 let item = Box::new(editor);
8650 let item_id = item.item_id();
8651
8652 if split {
8653 workspace.split_item(SplitDirection::Right, item.clone(), cx);
8654 } else {
8655 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
8656 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
8657 pane.close_current_preview_item(cx)
8658 } else {
8659 None
8660 }
8661 });
8662 workspace.add_item_to_active_pane(item.clone(), destination_index, cx);
8663 }
8664 workspace.active_pane().update(cx, |pane, cx| {
8665 pane.set_preview_item_id(Some(item_id), cx);
8666 });
8667 }
8668
8669 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
8670 use language::ToOffset as _;
8671
8672 let project = self.project.clone()?;
8673 let selection = self.selections.newest_anchor().clone();
8674 let (cursor_buffer, cursor_buffer_position) = self
8675 .buffer
8676 .read(cx)
8677 .text_anchor_for_position(selection.head(), cx)?;
8678 let (tail_buffer, cursor_buffer_position_end) = self
8679 .buffer
8680 .read(cx)
8681 .text_anchor_for_position(selection.tail(), cx)?;
8682 if tail_buffer != cursor_buffer {
8683 return None;
8684 }
8685
8686 let snapshot = cursor_buffer.read(cx).snapshot();
8687 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
8688 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
8689 let prepare_rename = project.update(cx, |project, cx| {
8690 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
8691 });
8692 drop(snapshot);
8693
8694 Some(cx.spawn(|this, mut cx| async move {
8695 let rename_range = if let Some(range) = prepare_rename.await? {
8696 Some(range)
8697 } else {
8698 this.update(&mut cx, |this, cx| {
8699 let buffer = this.buffer.read(cx).snapshot(cx);
8700 let mut buffer_highlights = this
8701 .document_highlights_for_position(selection.head(), &buffer)
8702 .filter(|highlight| {
8703 highlight.start.excerpt_id == selection.head().excerpt_id
8704 && highlight.end.excerpt_id == selection.head().excerpt_id
8705 });
8706 buffer_highlights
8707 .next()
8708 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
8709 })?
8710 };
8711 if let Some(rename_range) = rename_range {
8712 this.update(&mut cx, |this, cx| {
8713 let snapshot = cursor_buffer.read(cx).snapshot();
8714 let rename_buffer_range = rename_range.to_offset(&snapshot);
8715 let cursor_offset_in_rename_range =
8716 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
8717 let cursor_offset_in_rename_range_end =
8718 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
8719
8720 this.take_rename(false, cx);
8721 let buffer = this.buffer.read(cx).read(cx);
8722 let cursor_offset = selection.head().to_offset(&buffer);
8723 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
8724 let rename_end = rename_start + rename_buffer_range.len();
8725 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
8726 let mut old_highlight_id = None;
8727 let old_name: Arc<str> = buffer
8728 .chunks(rename_start..rename_end, true)
8729 .map(|chunk| {
8730 if old_highlight_id.is_none() {
8731 old_highlight_id = chunk.syntax_highlight_id;
8732 }
8733 chunk.text
8734 })
8735 .collect::<String>()
8736 .into();
8737
8738 drop(buffer);
8739
8740 // Position the selection in the rename editor so that it matches the current selection.
8741 this.show_local_selections = false;
8742 let rename_editor = cx.new_view(|cx| {
8743 let mut editor = Editor::single_line(cx);
8744 editor.buffer.update(cx, |buffer, cx| {
8745 buffer.edit([(0..0, old_name.clone())], None, cx)
8746 });
8747 let rename_selection_range = match cursor_offset_in_rename_range
8748 .cmp(&cursor_offset_in_rename_range_end)
8749 {
8750 Ordering::Equal => {
8751 editor.select_all(&SelectAll, cx);
8752 return editor;
8753 }
8754 Ordering::Less => {
8755 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
8756 }
8757 Ordering::Greater => {
8758 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
8759 }
8760 };
8761 if rename_selection_range.end > old_name.len() {
8762 editor.select_all(&SelectAll, cx);
8763 } else {
8764 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
8765 s.select_ranges([rename_selection_range]);
8766 });
8767 }
8768 editor
8769 });
8770
8771 let write_highlights =
8772 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
8773 let read_highlights =
8774 this.clear_background_highlights::<DocumentHighlightRead>(cx);
8775 let ranges = write_highlights
8776 .iter()
8777 .flat_map(|(_, ranges)| ranges.iter())
8778 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
8779 .cloned()
8780 .collect();
8781
8782 this.highlight_text::<Rename>(
8783 ranges,
8784 HighlightStyle {
8785 fade_out: Some(0.6),
8786 ..Default::default()
8787 },
8788 cx,
8789 );
8790 let rename_focus_handle = rename_editor.focus_handle(cx);
8791 cx.focus(&rename_focus_handle);
8792 let block_id = this.insert_blocks(
8793 [BlockProperties {
8794 style: BlockStyle::Flex,
8795 position: range.start,
8796 height: 1,
8797 render: Box::new({
8798 let rename_editor = rename_editor.clone();
8799 move |cx: &mut BlockContext| {
8800 let mut text_style = cx.editor_style.text.clone();
8801 if let Some(highlight_style) = old_highlight_id
8802 .and_then(|h| h.style(&cx.editor_style.syntax))
8803 {
8804 text_style = text_style.highlight(highlight_style);
8805 }
8806 div()
8807 .pl(cx.anchor_x)
8808 .child(EditorElement::new(
8809 &rename_editor,
8810 EditorStyle {
8811 background: cx.theme().system().transparent,
8812 local_player: cx.editor_style.local_player,
8813 text: text_style,
8814 scrollbar_width: cx.editor_style.scrollbar_width,
8815 syntax: cx.editor_style.syntax.clone(),
8816 status: cx.editor_style.status.clone(),
8817 inlay_hints_style: HighlightStyle {
8818 color: Some(cx.theme().status().hint),
8819 font_weight: Some(FontWeight::BOLD),
8820 ..HighlightStyle::default()
8821 },
8822 suggestions_style: HighlightStyle {
8823 color: Some(cx.theme().status().predictive),
8824 ..HighlightStyle::default()
8825 },
8826 },
8827 ))
8828 .into_any_element()
8829 }
8830 }),
8831 disposition: BlockDisposition::Below,
8832 }],
8833 Some(Autoscroll::fit()),
8834 cx,
8835 )[0];
8836 this.pending_rename = Some(RenameState {
8837 range,
8838 old_name,
8839 editor: rename_editor,
8840 block_id,
8841 });
8842 })?;
8843 }
8844
8845 Ok(())
8846 }))
8847 }
8848
8849 pub fn confirm_rename(
8850 &mut self,
8851 _: &ConfirmRename,
8852 cx: &mut ViewContext<Self>,
8853 ) -> Option<Task<Result<()>>> {
8854 let rename = self.take_rename(false, cx)?;
8855 let workspace = self.workspace()?;
8856 let (start_buffer, start) = self
8857 .buffer
8858 .read(cx)
8859 .text_anchor_for_position(rename.range.start, cx)?;
8860 let (end_buffer, end) = self
8861 .buffer
8862 .read(cx)
8863 .text_anchor_for_position(rename.range.end, cx)?;
8864 if start_buffer != end_buffer {
8865 return None;
8866 }
8867
8868 let buffer = start_buffer;
8869 let range = start..end;
8870 let old_name = rename.old_name;
8871 let new_name = rename.editor.read(cx).text(cx);
8872
8873 let rename = workspace
8874 .read(cx)
8875 .project()
8876 .clone()
8877 .update(cx, |project, cx| {
8878 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
8879 });
8880 let workspace = workspace.downgrade();
8881
8882 Some(cx.spawn(|editor, mut cx| async move {
8883 let project_transaction = rename.await?;
8884 Self::open_project_transaction(
8885 &editor,
8886 workspace,
8887 project_transaction,
8888 format!("Rename: {} → {}", old_name, new_name),
8889 cx.clone(),
8890 )
8891 .await?;
8892
8893 editor.update(&mut cx, |editor, cx| {
8894 editor.refresh_document_highlights(cx);
8895 })?;
8896 Ok(())
8897 }))
8898 }
8899
8900 fn take_rename(
8901 &mut self,
8902 moving_cursor: bool,
8903 cx: &mut ViewContext<Self>,
8904 ) -> Option<RenameState> {
8905 let rename = self.pending_rename.take()?;
8906 if rename.editor.focus_handle(cx).is_focused(cx) {
8907 cx.focus(&self.focus_handle);
8908 }
8909
8910 self.remove_blocks(
8911 [rename.block_id].into_iter().collect(),
8912 Some(Autoscroll::fit()),
8913 cx,
8914 );
8915 self.clear_highlights::<Rename>(cx);
8916 self.show_local_selections = true;
8917
8918 if moving_cursor {
8919 let rename_editor = rename.editor.read(cx);
8920 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
8921
8922 // Update the selection to match the position of the selection inside
8923 // the rename editor.
8924 let snapshot = self.buffer.read(cx).read(cx);
8925 let rename_range = rename.range.to_offset(&snapshot);
8926 let cursor_in_editor = snapshot
8927 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
8928 .min(rename_range.end);
8929 drop(snapshot);
8930
8931 self.change_selections(None, cx, |s| {
8932 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
8933 });
8934 } else {
8935 self.refresh_document_highlights(cx);
8936 }
8937
8938 Some(rename)
8939 }
8940
8941 pub fn pending_rename(&self) -> Option<&RenameState> {
8942 self.pending_rename.as_ref()
8943 }
8944
8945 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
8946 let project = match &self.project {
8947 Some(project) => project.clone(),
8948 None => return None,
8949 };
8950
8951 Some(self.perform_format(project, FormatTrigger::Manual, cx))
8952 }
8953
8954 fn perform_format(
8955 &mut self,
8956 project: Model<Project>,
8957 trigger: FormatTrigger,
8958 cx: &mut ViewContext<Self>,
8959 ) -> Task<Result<()>> {
8960 let buffer = self.buffer().clone();
8961 let mut buffers = buffer.read(cx).all_buffers();
8962 if trigger == FormatTrigger::Save {
8963 buffers.retain(|buffer| buffer.read(cx).is_dirty());
8964 }
8965
8966 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
8967 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
8968
8969 cx.spawn(|_, mut cx| async move {
8970 let transaction = futures::select_biased! {
8971 () = timeout => {
8972 log::warn!("timed out waiting for formatting");
8973 None
8974 }
8975 transaction = format.log_err().fuse() => transaction,
8976 };
8977
8978 buffer
8979 .update(&mut cx, |buffer, cx| {
8980 if let Some(transaction) = transaction {
8981 if !buffer.is_singleton() {
8982 buffer.push_transaction(&transaction.0, cx);
8983 }
8984 }
8985
8986 cx.notify();
8987 })
8988 .ok();
8989
8990 Ok(())
8991 })
8992 }
8993
8994 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
8995 if let Some(project) = self.project.clone() {
8996 self.buffer.update(cx, |multi_buffer, cx| {
8997 project.update(cx, |project, cx| {
8998 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
8999 });
9000 })
9001 }
9002 }
9003
9004 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9005 cx.show_character_palette();
9006 }
9007
9008 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9009 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9010 let buffer = self.buffer.read(cx).snapshot(cx);
9011 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9012 let is_valid = buffer
9013 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9014 .any(|entry| {
9015 entry.diagnostic.is_primary
9016 && !entry.range.is_empty()
9017 && entry.range.start == primary_range_start
9018 && entry.diagnostic.message == active_diagnostics.primary_message
9019 });
9020
9021 if is_valid != active_diagnostics.is_valid {
9022 active_diagnostics.is_valid = is_valid;
9023 let mut new_styles = HashMap::default();
9024 for (block_id, diagnostic) in &active_diagnostics.blocks {
9025 new_styles.insert(
9026 *block_id,
9027 diagnostic_block_renderer(diagnostic.clone(), is_valid),
9028 );
9029 }
9030 self.display_map
9031 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
9032 }
9033 }
9034 }
9035
9036 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9037 self.dismiss_diagnostics(cx);
9038 let snapshot = self.snapshot(cx);
9039 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9040 let buffer = self.buffer.read(cx).snapshot(cx);
9041
9042 let mut primary_range = None;
9043 let mut primary_message = None;
9044 let mut group_end = Point::zero();
9045 let diagnostic_group = buffer
9046 .diagnostic_group::<MultiBufferPoint>(group_id)
9047 .filter_map(|entry| {
9048 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9049 && (entry.range.start.row == entry.range.end.row
9050 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9051 {
9052 return None;
9053 }
9054 if entry.range.end > group_end {
9055 group_end = entry.range.end;
9056 }
9057 if entry.diagnostic.is_primary {
9058 primary_range = Some(entry.range.clone());
9059 primary_message = Some(entry.diagnostic.message.clone());
9060 }
9061 Some(entry)
9062 })
9063 .collect::<Vec<_>>();
9064 let primary_range = primary_range?;
9065 let primary_message = primary_message?;
9066 let primary_range =
9067 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9068
9069 let blocks = display_map
9070 .insert_blocks(
9071 diagnostic_group.iter().map(|entry| {
9072 let diagnostic = entry.diagnostic.clone();
9073 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9074 BlockProperties {
9075 style: BlockStyle::Fixed,
9076 position: buffer.anchor_after(entry.range.start),
9077 height: message_height,
9078 render: diagnostic_block_renderer(diagnostic, true),
9079 disposition: BlockDisposition::Below,
9080 }
9081 }),
9082 cx,
9083 )
9084 .into_iter()
9085 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9086 .collect();
9087
9088 Some(ActiveDiagnosticGroup {
9089 primary_range,
9090 primary_message,
9091 group_id,
9092 blocks,
9093 is_valid: true,
9094 })
9095 });
9096 self.active_diagnostics.is_some()
9097 }
9098
9099 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9100 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9101 self.display_map.update(cx, |display_map, cx| {
9102 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9103 });
9104 cx.notify();
9105 }
9106 }
9107
9108 pub fn set_selections_from_remote(
9109 &mut self,
9110 selections: Vec<Selection<Anchor>>,
9111 pending_selection: Option<Selection<Anchor>>,
9112 cx: &mut ViewContext<Self>,
9113 ) {
9114 let old_cursor_position = self.selections.newest_anchor().head();
9115 self.selections.change_with(cx, |s| {
9116 s.select_anchors(selections);
9117 if let Some(pending_selection) = pending_selection {
9118 s.set_pending(pending_selection, SelectMode::Character);
9119 } else {
9120 s.clear_pending();
9121 }
9122 });
9123 self.selections_did_change(false, &old_cursor_position, cx);
9124 }
9125
9126 fn push_to_selection_history(&mut self) {
9127 self.selection_history.push(SelectionHistoryEntry {
9128 selections: self.selections.disjoint_anchors(),
9129 select_next_state: self.select_next_state.clone(),
9130 select_prev_state: self.select_prev_state.clone(),
9131 add_selections_state: self.add_selections_state.clone(),
9132 });
9133 }
9134
9135 pub fn transact(
9136 &mut self,
9137 cx: &mut ViewContext<Self>,
9138 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9139 ) -> Option<TransactionId> {
9140 self.start_transaction_at(Instant::now(), cx);
9141 update(self, cx);
9142 self.end_transaction_at(Instant::now(), cx)
9143 }
9144
9145 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9146 self.end_selection(cx);
9147 if let Some(tx_id) = self
9148 .buffer
9149 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9150 {
9151 self.selection_history
9152 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9153 cx.emit(EditorEvent::TransactionBegun {
9154 transaction_id: tx_id,
9155 })
9156 }
9157 }
9158
9159 fn end_transaction_at(
9160 &mut self,
9161 now: Instant,
9162 cx: &mut ViewContext<Self>,
9163 ) -> Option<TransactionId> {
9164 if let Some(tx_id) = self
9165 .buffer
9166 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9167 {
9168 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
9169 *end_selections = Some(self.selections.disjoint_anchors());
9170 } else {
9171 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9172 }
9173
9174 cx.emit(EditorEvent::Edited);
9175 Some(tx_id)
9176 } else {
9177 None
9178 }
9179 }
9180
9181 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9182 let mut fold_ranges = Vec::new();
9183
9184 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9185
9186 let selections = self.selections.all_adjusted(cx);
9187 for selection in selections {
9188 let range = selection.range().sorted();
9189 let buffer_start_row = range.start.row;
9190
9191 for row in (0..=range.end.row).rev() {
9192 let fold_range = display_map.foldable_range(MultiBufferRow(row));
9193
9194 if let Some(fold_range) = fold_range {
9195 if fold_range.end.row >= buffer_start_row {
9196 fold_ranges.push(fold_range);
9197 if row <= range.start.row {
9198 break;
9199 }
9200 }
9201 }
9202 }
9203 }
9204
9205 self.fold_ranges(fold_ranges, true, cx);
9206 }
9207
9208 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
9209 let buffer_row = fold_at.buffer_row;
9210 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9211
9212 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
9213 let autoscroll = self
9214 .selections
9215 .all::<Point>(cx)
9216 .iter()
9217 .any(|selection| fold_range.overlaps(&selection.range()));
9218
9219 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
9220 }
9221 }
9222
9223 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
9224 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9225 let buffer = &display_map.buffer_snapshot;
9226 let selections = self.selections.all::<Point>(cx);
9227 let ranges = selections
9228 .iter()
9229 .map(|s| {
9230 let range = s.display_range(&display_map).sorted();
9231 let mut start = range.start.to_point(&display_map);
9232 let mut end = range.end.to_point(&display_map);
9233 start.column = 0;
9234 end.column = buffer.line_len(MultiBufferRow(end.row));
9235 start..end
9236 })
9237 .collect::<Vec<_>>();
9238
9239 self.unfold_ranges(ranges, true, true, cx);
9240 }
9241
9242 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
9243 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9244
9245 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
9246 ..Point::new(
9247 unfold_at.buffer_row.0,
9248 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
9249 );
9250
9251 let autoscroll = self
9252 .selections
9253 .all::<Point>(cx)
9254 .iter()
9255 .any(|selection| selection.range().overlaps(&intersection_range));
9256
9257 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
9258 }
9259
9260 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
9261 let selections = self.selections.all::<Point>(cx);
9262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9263 let line_mode = self.selections.line_mode;
9264 let ranges = selections.into_iter().map(|s| {
9265 if line_mode {
9266 let start = Point::new(s.start.row, 0);
9267 let end = Point::new(
9268 s.end.row,
9269 display_map
9270 .buffer_snapshot
9271 .line_len(MultiBufferRow(s.end.row)),
9272 );
9273 start..end
9274 } else {
9275 s.start..s.end
9276 }
9277 });
9278 self.fold_ranges(ranges, true, cx);
9279 }
9280
9281 pub fn fold_ranges<T: ToOffset + Clone>(
9282 &mut self,
9283 ranges: impl IntoIterator<Item = Range<T>>,
9284 auto_scroll: bool,
9285 cx: &mut ViewContext<Self>,
9286 ) {
9287 let mut fold_ranges = Vec::new();
9288 let mut buffers_affected = HashMap::default();
9289 let multi_buffer = self.buffer().read(cx);
9290 for range in ranges {
9291 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
9292 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9293 };
9294 fold_ranges.push(range);
9295 }
9296
9297 let mut ranges = fold_ranges.into_iter().peekable();
9298 if ranges.peek().is_some() {
9299 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
9300
9301 if auto_scroll {
9302 self.request_autoscroll(Autoscroll::fit(), cx);
9303 }
9304
9305 for buffer in buffers_affected.into_values() {
9306 self.sync_expanded_diff_hunks(buffer, cx);
9307 }
9308
9309 cx.notify();
9310
9311 if let Some(active_diagnostics) = self.active_diagnostics.take() {
9312 // Clear diagnostics block when folding a range that contains it.
9313 let snapshot = self.snapshot(cx);
9314 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
9315 drop(snapshot);
9316 self.active_diagnostics = Some(active_diagnostics);
9317 self.dismiss_diagnostics(cx);
9318 } else {
9319 self.active_diagnostics = Some(active_diagnostics);
9320 }
9321 }
9322
9323 self.scrollbar_marker_state.dirty = true;
9324 }
9325 }
9326
9327 pub fn unfold_ranges<T: ToOffset + Clone>(
9328 &mut self,
9329 ranges: impl IntoIterator<Item = Range<T>>,
9330 inclusive: bool,
9331 auto_scroll: bool,
9332 cx: &mut ViewContext<Self>,
9333 ) {
9334 let mut unfold_ranges = Vec::new();
9335 let mut buffers_affected = HashMap::default();
9336 let multi_buffer = self.buffer().read(cx);
9337 for range in ranges {
9338 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
9339 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9340 };
9341 unfold_ranges.push(range);
9342 }
9343
9344 let mut ranges = unfold_ranges.into_iter().peekable();
9345 if ranges.peek().is_some() {
9346 self.display_map
9347 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
9348 if auto_scroll {
9349 self.request_autoscroll(Autoscroll::fit(), cx);
9350 }
9351
9352 for buffer in buffers_affected.into_values() {
9353 self.sync_expanded_diff_hunks(buffer, cx);
9354 }
9355
9356 cx.notify();
9357 self.scrollbar_marker_state.dirty = true;
9358 }
9359 }
9360
9361 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
9362 if hovered != self.gutter_hovered {
9363 self.gutter_hovered = hovered;
9364 cx.notify();
9365 }
9366 }
9367
9368 pub fn insert_blocks(
9369 &mut self,
9370 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
9371 autoscroll: Option<Autoscroll>,
9372 cx: &mut ViewContext<Self>,
9373 ) -> Vec<BlockId> {
9374 let blocks = self
9375 .display_map
9376 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
9377 if let Some(autoscroll) = autoscroll {
9378 self.request_autoscroll(autoscroll, cx);
9379 }
9380 blocks
9381 }
9382
9383 pub fn replace_blocks(
9384 &mut self,
9385 blocks: HashMap<BlockId, RenderBlock>,
9386 autoscroll: Option<Autoscroll>,
9387 cx: &mut ViewContext<Self>,
9388 ) {
9389 self.display_map
9390 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
9391 if let Some(autoscroll) = autoscroll {
9392 self.request_autoscroll(autoscroll, cx);
9393 }
9394 }
9395
9396 pub fn remove_blocks(
9397 &mut self,
9398 block_ids: HashSet<BlockId>,
9399 autoscroll: Option<Autoscroll>,
9400 cx: &mut ViewContext<Self>,
9401 ) {
9402 self.display_map.update(cx, |display_map, cx| {
9403 display_map.remove_blocks(block_ids, cx)
9404 });
9405 if let Some(autoscroll) = autoscroll {
9406 self.request_autoscroll(autoscroll, cx);
9407 }
9408 }
9409
9410 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
9411 self.display_map
9412 .update(cx, |map, cx| map.snapshot(cx))
9413 .longest_row()
9414 }
9415
9416 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
9417 self.display_map
9418 .update(cx, |map, cx| map.snapshot(cx))
9419 .max_point()
9420 }
9421
9422 pub fn text(&self, cx: &AppContext) -> String {
9423 self.buffer.read(cx).read(cx).text()
9424 }
9425
9426 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
9427 let text = self.text(cx);
9428 let text = text.trim();
9429
9430 if text.is_empty() {
9431 return None;
9432 }
9433
9434 Some(text.to_string())
9435 }
9436
9437 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
9438 self.transact(cx, |this, cx| {
9439 this.buffer
9440 .read(cx)
9441 .as_singleton()
9442 .expect("you can only call set_text on editors for singleton buffers")
9443 .update(cx, |buffer, cx| buffer.set_text(text, cx));
9444 });
9445 }
9446
9447 pub fn display_text(&self, cx: &mut AppContext) -> String {
9448 self.display_map
9449 .update(cx, |map, cx| map.snapshot(cx))
9450 .text()
9451 }
9452
9453 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
9454 let mut wrap_guides = smallvec::smallvec![];
9455
9456 if self.show_wrap_guides == Some(false) {
9457 return wrap_guides;
9458 }
9459
9460 let settings = self.buffer.read(cx).settings_at(0, cx);
9461 if settings.show_wrap_guides {
9462 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
9463 wrap_guides.push((soft_wrap as usize, true));
9464 }
9465 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
9466 }
9467
9468 wrap_guides
9469 }
9470
9471 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
9472 let settings = self.buffer.read(cx).settings_at(0, cx);
9473 let mode = self
9474 .soft_wrap_mode_override
9475 .unwrap_or_else(|| settings.soft_wrap);
9476 match mode {
9477 language_settings::SoftWrap::None => SoftWrap::None,
9478 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
9479 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
9480 language_settings::SoftWrap::PreferredLineLength => {
9481 SoftWrap::Column(settings.preferred_line_length)
9482 }
9483 }
9484 }
9485
9486 pub fn set_soft_wrap_mode(
9487 &mut self,
9488 mode: language_settings::SoftWrap,
9489 cx: &mut ViewContext<Self>,
9490 ) {
9491 self.soft_wrap_mode_override = Some(mode);
9492 cx.notify();
9493 }
9494
9495 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
9496 let rem_size = cx.rem_size();
9497 self.display_map.update(cx, |map, cx| {
9498 map.set_font(
9499 style.text.font(),
9500 style.text.font_size.to_pixels(rem_size),
9501 cx,
9502 )
9503 });
9504 self.style = Some(style);
9505 }
9506
9507 pub fn style(&self) -> Option<&EditorStyle> {
9508 self.style.as_ref()
9509 }
9510
9511 // Called by the element. This method is not designed to be called outside of the editor
9512 // element's layout code because it does not notify when rewrapping is computed synchronously.
9513 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
9514 self.display_map
9515 .update(cx, |map, cx| map.set_wrap_width(width, cx))
9516 }
9517
9518 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
9519 if self.soft_wrap_mode_override.is_some() {
9520 self.soft_wrap_mode_override.take();
9521 } else {
9522 let soft_wrap = match self.soft_wrap_mode(cx) {
9523 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
9524 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
9525 language_settings::SoftWrap::PreferLine
9526 }
9527 };
9528 self.soft_wrap_mode_override = Some(soft_wrap);
9529 }
9530 cx.notify();
9531 }
9532
9533 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
9534 let mut editor_settings = EditorSettings::get_global(cx).clone();
9535 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
9536 EditorSettings::override_global(editor_settings, cx);
9537 }
9538
9539 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
9540 self.show_gutter = show_gutter;
9541 cx.notify();
9542 }
9543
9544 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
9545 self.show_wrap_guides = Some(show_gutter);
9546 cx.notify();
9547 }
9548
9549 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
9550 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
9551 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
9552 cx.reveal_path(&file.abs_path(cx));
9553 }
9554 }
9555 }
9556
9557 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
9558 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
9559 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
9560 if let Some(path) = file.abs_path(cx).to_str() {
9561 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
9562 }
9563 }
9564 }
9565 }
9566
9567 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
9568 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
9569 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
9570 if let Some(path) = file.path().to_str() {
9571 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
9572 }
9573 }
9574 }
9575 }
9576
9577 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
9578 self.show_git_blame_gutter = !self.show_git_blame_gutter;
9579
9580 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
9581 self.start_git_blame(true, cx);
9582 }
9583
9584 cx.notify();
9585 }
9586
9587 pub fn toggle_git_blame_inline(
9588 &mut self,
9589 _: &ToggleGitBlameInline,
9590 cx: &mut ViewContext<Self>,
9591 ) {
9592 self.toggle_git_blame_inline_internal(true, cx);
9593 cx.notify();
9594 }
9595
9596 pub fn git_blame_inline_enabled(&self) -> bool {
9597 self.git_blame_inline_enabled
9598 }
9599
9600 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
9601 if let Some(project) = self.project.as_ref() {
9602 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
9603 return;
9604 };
9605
9606 if buffer.read(cx).file().is_none() {
9607 return;
9608 }
9609
9610 let focused = self.focus_handle(cx).contains_focused(cx);
9611
9612 let project = project.clone();
9613 let blame =
9614 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
9615 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
9616 self.blame = Some(blame);
9617 }
9618 }
9619
9620 fn toggle_git_blame_inline_internal(
9621 &mut self,
9622 user_triggered: bool,
9623 cx: &mut ViewContext<Self>,
9624 ) {
9625 if self.git_blame_inline_enabled {
9626 self.git_blame_inline_enabled = false;
9627 self.show_git_blame_inline = false;
9628 self.show_git_blame_inline_delay_task.take();
9629 } else {
9630 self.git_blame_inline_enabled = true;
9631 self.start_git_blame_inline(user_triggered, cx);
9632 }
9633
9634 cx.notify();
9635 }
9636
9637 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
9638 self.start_git_blame(user_triggered, cx);
9639
9640 if ProjectSettings::get_global(cx)
9641 .git
9642 .inline_blame_delay()
9643 .is_some()
9644 {
9645 self.start_inline_blame_timer(cx);
9646 } else {
9647 self.show_git_blame_inline = true
9648 }
9649 }
9650
9651 pub fn blame(&self) -> Option<&Model<GitBlame>> {
9652 self.blame.as_ref()
9653 }
9654
9655 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
9656 self.show_git_blame_gutter && self.has_blame_entries(cx)
9657 }
9658
9659 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
9660 self.show_git_blame_inline
9661 && self.focus_handle.is_focused(cx)
9662 && !self.newest_selection_head_on_empty_line(cx)
9663 && self.has_blame_entries(cx)
9664 }
9665
9666 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
9667 self.blame()
9668 .map_or(false, |blame| blame.read(cx).has_generated_entries())
9669 }
9670
9671 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
9672 let cursor_anchor = self.selections.newest_anchor().head();
9673
9674 let snapshot = self.buffer.read(cx).snapshot(cx);
9675 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
9676
9677 snapshot.line_len(buffer_row) == 0
9678 }
9679
9680 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
9681 let (path, repo) = maybe!({
9682 let project_handle = self.project.as_ref()?.clone();
9683 let project = project_handle.read(cx);
9684 let buffer = self.buffer().read(cx).as_singleton()?;
9685 let path = buffer
9686 .read(cx)
9687 .file()?
9688 .as_local()?
9689 .path()
9690 .to_str()?
9691 .to_string();
9692 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
9693 Some((path, repo))
9694 })
9695 .ok_or_else(|| anyhow!("unable to open git repository"))?;
9696
9697 const REMOTE_NAME: &str = "origin";
9698 let origin_url = repo
9699 .lock()
9700 .remote_url(REMOTE_NAME)
9701 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
9702 let sha = repo
9703 .lock()
9704 .head_sha()
9705 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
9706 let selections = self.selections.all::<Point>(cx);
9707 let selection = selections.iter().peekable().next();
9708
9709 let (provider, remote) =
9710 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
9711 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
9712
9713 Ok(provider.build_permalink(
9714 remote,
9715 BuildPermalinkParams {
9716 sha: &sha,
9717 path: &path,
9718 selection: selection.map(|selection| {
9719 let range = selection.range();
9720 let start = range.start.row;
9721 let end = range.end.row;
9722 start..end
9723 }),
9724 },
9725 ))
9726 }
9727
9728 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
9729 let permalink = self.get_permalink_to_line(cx);
9730
9731 match permalink {
9732 Ok(permalink) => {
9733 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
9734 }
9735 Err(err) => {
9736 let message = format!("Failed to copy permalink: {err}");
9737
9738 Err::<(), anyhow::Error>(err).log_err();
9739
9740 if let Some(workspace) = self.workspace() {
9741 workspace.update(cx, |workspace, cx| {
9742 struct CopyPermalinkToLine;
9743
9744 workspace.show_toast(
9745 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
9746 cx,
9747 )
9748 })
9749 }
9750 }
9751 }
9752 }
9753
9754 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
9755 let permalink = self.get_permalink_to_line(cx);
9756
9757 match permalink {
9758 Ok(permalink) => {
9759 cx.open_url(permalink.as_ref());
9760 }
9761 Err(err) => {
9762 let message = format!("Failed to open permalink: {err}");
9763
9764 Err::<(), anyhow::Error>(err).log_err();
9765
9766 if let Some(workspace) = self.workspace() {
9767 workspace.update(cx, |workspace, cx| {
9768 struct OpenPermalinkToLine;
9769
9770 workspace.show_toast(
9771 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
9772 cx,
9773 )
9774 })
9775 }
9776 }
9777 }
9778 }
9779
9780 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
9781 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
9782 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
9783 pub fn highlight_rows<T: 'static>(
9784 &mut self,
9785 rows: RangeInclusive<Anchor>,
9786 color: Option<Hsla>,
9787 cx: &mut ViewContext<Self>,
9788 ) {
9789 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
9790 match self.highlighted_rows.entry(TypeId::of::<T>()) {
9791 hash_map::Entry::Occupied(o) => {
9792 let row_highlights = o.into_mut();
9793 let existing_highlight_index =
9794 row_highlights.binary_search_by(|(_, highlight_range, _)| {
9795 highlight_range
9796 .start()
9797 .cmp(&rows.start(), &multi_buffer_snapshot)
9798 .then(
9799 highlight_range
9800 .end()
9801 .cmp(&rows.end(), &multi_buffer_snapshot),
9802 )
9803 });
9804 match color {
9805 Some(color) => {
9806 let insert_index = match existing_highlight_index {
9807 Ok(i) => i,
9808 Err(i) => i,
9809 };
9810 row_highlights.insert(
9811 insert_index,
9812 (post_inc(&mut self.highlight_order), rows, Some(color)),
9813 );
9814 }
9815 None => match existing_highlight_index {
9816 Ok(i) => {
9817 row_highlights.remove(i);
9818 }
9819 Err(i) => {
9820 row_highlights
9821 .insert(i, (post_inc(&mut self.highlight_order), rows, None));
9822 }
9823 },
9824 }
9825 }
9826 hash_map::Entry::Vacant(v) => {
9827 v.insert(vec![(post_inc(&mut self.highlight_order), rows, color)]);
9828 }
9829 }
9830 }
9831
9832 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
9833 pub fn clear_row_highlights<T: 'static>(&mut self) {
9834 self.highlighted_rows.remove(&TypeId::of::<T>());
9835 }
9836
9837 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
9838 pub fn highlighted_rows<T: 'static>(
9839 &self,
9840 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
9841 Some(
9842 self.highlighted_rows
9843 .get(&TypeId::of::<T>())?
9844 .iter()
9845 .map(|(_, range, color)| (range, color.as_ref())),
9846 )
9847 }
9848
9849 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
9850 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
9851 /// Allows to ignore certain kinds of highlights.
9852 pub fn highlighted_display_rows(
9853 &mut self,
9854 exclude_highlights: HashSet<TypeId>,
9855 cx: &mut WindowContext,
9856 ) -> BTreeMap<DisplayRow, Hsla> {
9857 let snapshot = self.snapshot(cx);
9858 let mut used_highlight_orders = HashMap::default();
9859 self.highlighted_rows
9860 .iter()
9861 .filter(|(type_id, _)| !exclude_highlights.contains(type_id))
9862 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
9863 .fold(
9864 BTreeMap::<DisplayRow, Hsla>::new(),
9865 |mut unique_rows, (highlight_order, anchor_range, hsla)| {
9866 let start_row = anchor_range.start().to_display_point(&snapshot).row();
9867 let end_row = anchor_range.end().to_display_point(&snapshot).row();
9868 for row in start_row.0..=end_row.0 {
9869 let used_index =
9870 used_highlight_orders.entry(row).or_insert(*highlight_order);
9871 if highlight_order >= used_index {
9872 *used_index = *highlight_order;
9873 match hsla {
9874 Some(hsla) => {
9875 unique_rows.insert(DisplayRow(row), *hsla);
9876 }
9877 None => {
9878 unique_rows.remove(&DisplayRow(row));
9879 }
9880 }
9881 }
9882 }
9883 unique_rows
9884 },
9885 )
9886 }
9887
9888 pub fn set_search_within_ranges(
9889 &mut self,
9890 ranges: &[Range<Anchor>],
9891 cx: &mut ViewContext<Self>,
9892 ) {
9893 self.highlight_background::<SearchWithinRange>(
9894 ranges,
9895 |colors| colors.editor_document_highlight_read_background,
9896 cx,
9897 )
9898 }
9899
9900 pub fn highlight_background<T: 'static>(
9901 &mut self,
9902 ranges: &[Range<Anchor>],
9903 color_fetcher: fn(&ThemeColors) -> Hsla,
9904 cx: &mut ViewContext<Self>,
9905 ) {
9906 let snapshot = self.snapshot(cx);
9907 // this is to try and catch a panic sooner
9908 for range in ranges {
9909 snapshot
9910 .buffer_snapshot
9911 .summary_for_anchor::<usize>(&range.start);
9912 snapshot
9913 .buffer_snapshot
9914 .summary_for_anchor::<usize>(&range.end);
9915 }
9916
9917 self.background_highlights
9918 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
9919 self.scrollbar_marker_state.dirty = true;
9920 cx.notify();
9921 }
9922
9923 pub fn clear_background_highlights<T: 'static>(
9924 &mut self,
9925 cx: &mut ViewContext<Self>,
9926 ) -> Option<BackgroundHighlight> {
9927 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
9928 if !text_highlights.1.is_empty() {
9929 self.scrollbar_marker_state.dirty = true;
9930 cx.notify();
9931 }
9932 Some(text_highlights)
9933 }
9934
9935 #[cfg(feature = "test-support")]
9936 pub fn all_text_background_highlights(
9937 &mut self,
9938 cx: &mut ViewContext<Self>,
9939 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9940 let snapshot = self.snapshot(cx);
9941 let buffer = &snapshot.buffer_snapshot;
9942 let start = buffer.anchor_before(0);
9943 let end = buffer.anchor_after(buffer.len());
9944 let theme = cx.theme().colors();
9945 self.background_highlights_in_range(start..end, &snapshot, theme)
9946 }
9947
9948 fn document_highlights_for_position<'a>(
9949 &'a self,
9950 position: Anchor,
9951 buffer: &'a MultiBufferSnapshot,
9952 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
9953 let read_highlights = self
9954 .background_highlights
9955 .get(&TypeId::of::<DocumentHighlightRead>())
9956 .map(|h| &h.1);
9957 let write_highlights = self
9958 .background_highlights
9959 .get(&TypeId::of::<DocumentHighlightWrite>())
9960 .map(|h| &h.1);
9961 let left_position = position.bias_left(buffer);
9962 let right_position = position.bias_right(buffer);
9963 read_highlights
9964 .into_iter()
9965 .chain(write_highlights)
9966 .flat_map(move |ranges| {
9967 let start_ix = match ranges.binary_search_by(|probe| {
9968 let cmp = probe.end.cmp(&left_position, buffer);
9969 if cmp.is_ge() {
9970 Ordering::Greater
9971 } else {
9972 Ordering::Less
9973 }
9974 }) {
9975 Ok(i) | Err(i) => i,
9976 };
9977
9978 ranges[start_ix..]
9979 .iter()
9980 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
9981 })
9982 }
9983
9984 pub fn has_background_highlights<T: 'static>(&self) -> bool {
9985 self.background_highlights
9986 .get(&TypeId::of::<T>())
9987 .map_or(false, |(_, highlights)| !highlights.is_empty())
9988 }
9989
9990 pub fn background_highlights_in_range(
9991 &self,
9992 search_range: Range<Anchor>,
9993 display_snapshot: &DisplaySnapshot,
9994 theme: &ThemeColors,
9995 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
9996 let mut results = Vec::new();
9997 for (color_fetcher, ranges) in self.background_highlights.values() {
9998 let color = color_fetcher(theme);
9999 let start_ix = match ranges.binary_search_by(|probe| {
10000 let cmp = probe
10001 .end
10002 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10003 if cmp.is_gt() {
10004 Ordering::Greater
10005 } else {
10006 Ordering::Less
10007 }
10008 }) {
10009 Ok(i) | Err(i) => i,
10010 };
10011 for range in &ranges[start_ix..] {
10012 if range
10013 .start
10014 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10015 .is_ge()
10016 {
10017 break;
10018 }
10019
10020 let start = range.start.to_display_point(&display_snapshot);
10021 let end = range.end.to_display_point(&display_snapshot);
10022 results.push((start..end, color))
10023 }
10024 }
10025 results
10026 }
10027
10028 pub fn background_highlight_row_ranges<T: 'static>(
10029 &self,
10030 search_range: Range<Anchor>,
10031 display_snapshot: &DisplaySnapshot,
10032 count: usize,
10033 ) -> Vec<RangeInclusive<DisplayPoint>> {
10034 let mut results = Vec::new();
10035 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
10036 return vec![];
10037 };
10038
10039 let start_ix = match ranges.binary_search_by(|probe| {
10040 let cmp = probe
10041 .end
10042 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10043 if cmp.is_gt() {
10044 Ordering::Greater
10045 } else {
10046 Ordering::Less
10047 }
10048 }) {
10049 Ok(i) | Err(i) => i,
10050 };
10051 let mut push_region = |start: Option<Point>, end: Option<Point>| {
10052 if let (Some(start_display), Some(end_display)) = (start, end) {
10053 results.push(
10054 start_display.to_display_point(display_snapshot)
10055 ..=end_display.to_display_point(display_snapshot),
10056 );
10057 }
10058 };
10059 let mut start_row: Option<Point> = None;
10060 let mut end_row: Option<Point> = None;
10061 if ranges.len() > count {
10062 return Vec::new();
10063 }
10064 for range in &ranges[start_ix..] {
10065 if range
10066 .start
10067 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10068 .is_ge()
10069 {
10070 break;
10071 }
10072 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
10073 if let Some(current_row) = &end_row {
10074 if end.row == current_row.row {
10075 continue;
10076 }
10077 }
10078 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
10079 if start_row.is_none() {
10080 assert_eq!(end_row, None);
10081 start_row = Some(start);
10082 end_row = Some(end);
10083 continue;
10084 }
10085 if let Some(current_end) = end_row.as_mut() {
10086 if start.row > current_end.row + 1 {
10087 push_region(start_row, end_row);
10088 start_row = Some(start);
10089 end_row = Some(end);
10090 } else {
10091 // Merge two hunks.
10092 *current_end = end;
10093 }
10094 } else {
10095 unreachable!();
10096 }
10097 }
10098 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
10099 push_region(start_row, end_row);
10100 results
10101 }
10102
10103 /// Get the text ranges corresponding to the redaction query
10104 pub fn redacted_ranges(
10105 &self,
10106 search_range: Range<Anchor>,
10107 display_snapshot: &DisplaySnapshot,
10108 cx: &WindowContext,
10109 ) -> Vec<Range<DisplayPoint>> {
10110 display_snapshot
10111 .buffer_snapshot
10112 .redacted_ranges(search_range, |file| {
10113 if let Some(file) = file {
10114 file.is_private()
10115 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
10116 } else {
10117 false
10118 }
10119 })
10120 .map(|range| {
10121 range.start.to_display_point(display_snapshot)
10122 ..range.end.to_display_point(display_snapshot)
10123 })
10124 .collect()
10125 }
10126
10127 pub fn highlight_text<T: 'static>(
10128 &mut self,
10129 ranges: Vec<Range<Anchor>>,
10130 style: HighlightStyle,
10131 cx: &mut ViewContext<Self>,
10132 ) {
10133 self.display_map.update(cx, |map, _| {
10134 map.highlight_text(TypeId::of::<T>(), ranges, style)
10135 });
10136 cx.notify();
10137 }
10138
10139 pub(crate) fn highlight_inlays<T: 'static>(
10140 &mut self,
10141 highlights: Vec<InlayHighlight>,
10142 style: HighlightStyle,
10143 cx: &mut ViewContext<Self>,
10144 ) {
10145 self.display_map.update(cx, |map, _| {
10146 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
10147 });
10148 cx.notify();
10149 }
10150
10151 pub fn text_highlights<'a, T: 'static>(
10152 &'a self,
10153 cx: &'a AppContext,
10154 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
10155 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
10156 }
10157
10158 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
10159 let cleared = self
10160 .display_map
10161 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
10162 if cleared {
10163 cx.notify();
10164 }
10165 }
10166
10167 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
10168 (self.read_only(cx) || self.blink_manager.read(cx).visible())
10169 && self.focus_handle.is_focused(cx)
10170 }
10171
10172 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
10173 cx.notify();
10174 }
10175
10176 fn on_buffer_event(
10177 &mut self,
10178 multibuffer: Model<MultiBuffer>,
10179 event: &multi_buffer::Event,
10180 cx: &mut ViewContext<Self>,
10181 ) {
10182 match event {
10183 multi_buffer::Event::Edited {
10184 singleton_buffer_edited,
10185 } => {
10186 self.scrollbar_marker_state.dirty = true;
10187 self.refresh_active_diagnostics(cx);
10188 self.refresh_code_actions(cx);
10189 if self.has_active_inline_completion(cx) {
10190 self.update_visible_inline_completion(cx);
10191 }
10192 cx.emit(EditorEvent::BufferEdited);
10193 cx.emit(SearchEvent::MatchesInvalidated);
10194
10195 if *singleton_buffer_edited {
10196 if let Some(project) = &self.project {
10197 let project = project.read(cx);
10198 let languages_affected = multibuffer
10199 .read(cx)
10200 .all_buffers()
10201 .into_iter()
10202 .filter_map(|buffer| {
10203 let buffer = buffer.read(cx);
10204 let language = buffer.language()?;
10205 if project.is_local()
10206 && project.language_servers_for_buffer(buffer, cx).count() == 0
10207 {
10208 None
10209 } else {
10210 Some(language)
10211 }
10212 })
10213 .cloned()
10214 .collect::<HashSet<_>>();
10215 if !languages_affected.is_empty() {
10216 self.refresh_inlay_hints(
10217 InlayHintRefreshReason::BufferEdited(languages_affected),
10218 cx,
10219 );
10220 }
10221 }
10222 }
10223
10224 let Some(project) = &self.project else { return };
10225 let telemetry = project.read(cx).client().telemetry().clone();
10226 telemetry.log_edit_event("editor");
10227 }
10228 multi_buffer::Event::ExcerptsAdded {
10229 buffer,
10230 predecessor,
10231 excerpts,
10232 } => {
10233 self.tasks_update_task = Some(self.refresh_runnables(cx));
10234 cx.emit(EditorEvent::ExcerptsAdded {
10235 buffer: buffer.clone(),
10236 predecessor: *predecessor,
10237 excerpts: excerpts.clone(),
10238 });
10239 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
10240 }
10241 multi_buffer::Event::ExcerptsRemoved { ids } => {
10242 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
10243 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
10244 }
10245 multi_buffer::Event::Reparsed => {
10246 self.tasks_update_task = Some(self.refresh_runnables(cx));
10247
10248 cx.emit(EditorEvent::Reparsed);
10249 }
10250 multi_buffer::Event::LanguageChanged => {
10251 cx.emit(EditorEvent::Reparsed);
10252 cx.notify();
10253 }
10254 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
10255 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
10256 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
10257 cx.emit(EditorEvent::TitleChanged)
10258 }
10259 multi_buffer::Event::DiffBaseChanged => {
10260 self.scrollbar_marker_state.dirty = true;
10261 cx.emit(EditorEvent::DiffBaseChanged);
10262 cx.notify();
10263 }
10264 multi_buffer::Event::DiffUpdated { buffer } => {
10265 self.sync_expanded_diff_hunks(buffer.clone(), cx);
10266 cx.notify();
10267 }
10268 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
10269 multi_buffer::Event::DiagnosticsUpdated => {
10270 self.refresh_active_diagnostics(cx);
10271 self.scrollbar_marker_state.dirty = true;
10272 cx.notify();
10273 }
10274 _ => {}
10275 };
10276 }
10277
10278 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
10279 cx.notify();
10280 }
10281
10282 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
10283 self.refresh_inline_completion(true, cx);
10284 self.refresh_inlay_hints(
10285 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
10286 self.selections.newest_anchor().head(),
10287 &self.buffer.read(cx).snapshot(cx),
10288 cx,
10289 )),
10290 cx,
10291 );
10292 let editor_settings = EditorSettings::get_global(cx);
10293 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
10294 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
10295 self.current_line_highlight = editor_settings.current_line_highlight;
10296
10297 if self.mode == EditorMode::Full {
10298 let inline_blame_enabled = ProjectSettings::get_global(cx).git.inline_blame_enabled();
10299 if self.git_blame_inline_enabled != inline_blame_enabled {
10300 self.toggle_git_blame_inline_internal(false, cx);
10301 }
10302 }
10303
10304 cx.notify();
10305 }
10306
10307 pub fn set_searchable(&mut self, searchable: bool) {
10308 self.searchable = searchable;
10309 }
10310
10311 pub fn searchable(&self) -> bool {
10312 self.searchable
10313 }
10314
10315 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
10316 self.open_excerpts_common(true, cx)
10317 }
10318
10319 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
10320 self.open_excerpts_common(false, cx)
10321 }
10322
10323 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
10324 let buffer = self.buffer.read(cx);
10325 if buffer.is_singleton() {
10326 cx.propagate();
10327 return;
10328 }
10329
10330 let Some(workspace) = self.workspace() else {
10331 cx.propagate();
10332 return;
10333 };
10334
10335 let mut new_selections_by_buffer = HashMap::default();
10336 for selection in self.selections.all::<usize>(cx) {
10337 for (buffer, mut range, _) in
10338 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
10339 {
10340 if selection.reversed {
10341 mem::swap(&mut range.start, &mut range.end);
10342 }
10343 new_selections_by_buffer
10344 .entry(buffer)
10345 .or_insert(Vec::new())
10346 .push(range)
10347 }
10348 }
10349
10350 // We defer the pane interaction because we ourselves are a workspace item
10351 // and activating a new item causes the pane to call a method on us reentrantly,
10352 // which panics if we're on the stack.
10353 cx.window_context().defer(move |cx| {
10354 workspace.update(cx, |workspace, cx| {
10355 let pane = if split {
10356 workspace.adjacent_pane(cx)
10357 } else {
10358 workspace.active_pane().clone()
10359 };
10360
10361 for (buffer, ranges) in new_selections_by_buffer {
10362 let editor = workspace.open_project_item::<Self>(pane.clone(), buffer, cx);
10363 editor.update(cx, |editor, cx| {
10364 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
10365 s.select_ranges(ranges);
10366 });
10367 });
10368 }
10369 })
10370 });
10371 }
10372
10373 fn jump(
10374 &mut self,
10375 path: ProjectPath,
10376 position: Point,
10377 anchor: language::Anchor,
10378 offset_from_top: u32,
10379 cx: &mut ViewContext<Self>,
10380 ) {
10381 let workspace = self.workspace();
10382 cx.spawn(|_, mut cx| async move {
10383 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
10384 let editor = workspace.update(&mut cx, |workspace, cx| {
10385 // Reset the preview item id before opening the new item
10386 workspace.active_pane().update(cx, |pane, cx| {
10387 pane.set_preview_item_id(None, cx);
10388 });
10389 workspace.open_path_preview(path, None, true, true, cx)
10390 })?;
10391 let editor = editor
10392 .await?
10393 .downcast::<Editor>()
10394 .ok_or_else(|| anyhow!("opened item was not an editor"))?
10395 .downgrade();
10396 editor.update(&mut cx, |editor, cx| {
10397 let buffer = editor
10398 .buffer()
10399 .read(cx)
10400 .as_singleton()
10401 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
10402 let buffer = buffer.read(cx);
10403 let cursor = if buffer.can_resolve(&anchor) {
10404 language::ToPoint::to_point(&anchor, buffer)
10405 } else {
10406 buffer.clip_point(position, Bias::Left)
10407 };
10408
10409 let nav_history = editor.nav_history.take();
10410 editor.change_selections(
10411 Some(Autoscroll::top_relative(offset_from_top as usize)),
10412 cx,
10413 |s| {
10414 s.select_ranges([cursor..cursor]);
10415 },
10416 );
10417 editor.nav_history = nav_history;
10418
10419 anyhow::Ok(())
10420 })??;
10421
10422 anyhow::Ok(())
10423 })
10424 .detach_and_log_err(cx);
10425 }
10426
10427 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
10428 let snapshot = self.buffer.read(cx).read(cx);
10429 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
10430 Some(
10431 ranges
10432 .iter()
10433 .map(move |range| {
10434 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
10435 })
10436 .collect(),
10437 )
10438 }
10439
10440 fn selection_replacement_ranges(
10441 &self,
10442 range: Range<OffsetUtf16>,
10443 cx: &AppContext,
10444 ) -> Vec<Range<OffsetUtf16>> {
10445 let selections = self.selections.all::<OffsetUtf16>(cx);
10446 let newest_selection = selections
10447 .iter()
10448 .max_by_key(|selection| selection.id)
10449 .unwrap();
10450 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
10451 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
10452 let snapshot = self.buffer.read(cx).read(cx);
10453 selections
10454 .into_iter()
10455 .map(|mut selection| {
10456 selection.start.0 =
10457 (selection.start.0 as isize).saturating_add(start_delta) as usize;
10458 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
10459 snapshot.clip_offset_utf16(selection.start, Bias::Left)
10460 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
10461 })
10462 .collect()
10463 }
10464
10465 fn report_editor_event(
10466 &self,
10467 operation: &'static str,
10468 file_extension: Option<String>,
10469 cx: &AppContext,
10470 ) {
10471 if cfg!(any(test, feature = "test-support")) {
10472 return;
10473 }
10474
10475 let Some(project) = &self.project else { return };
10476
10477 // If None, we are in a file without an extension
10478 let file = self
10479 .buffer
10480 .read(cx)
10481 .as_singleton()
10482 .and_then(|b| b.read(cx).file());
10483 let file_extension = file_extension.or(file
10484 .as_ref()
10485 .and_then(|file| Path::new(file.file_name(cx)).extension())
10486 .and_then(|e| e.to_str())
10487 .map(|a| a.to_string()));
10488
10489 let vim_mode = cx
10490 .global::<SettingsStore>()
10491 .raw_user_settings()
10492 .get("vim_mode")
10493 == Some(&serde_json::Value::Bool(true));
10494
10495 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
10496 == language::language_settings::InlineCompletionProvider::Copilot;
10497 let copilot_enabled_for_language = self
10498 .buffer
10499 .read(cx)
10500 .settings_at(0, cx)
10501 .show_inline_completions;
10502
10503 let telemetry = project.read(cx).client().telemetry().clone();
10504 telemetry.report_editor_event(
10505 file_extension,
10506 vim_mode,
10507 operation,
10508 copilot_enabled,
10509 copilot_enabled_for_language,
10510 )
10511 }
10512
10513 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
10514 /// with each line being an array of {text, highlight} objects.
10515 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
10516 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
10517 return;
10518 };
10519
10520 #[derive(Serialize)]
10521 struct Chunk<'a> {
10522 text: String,
10523 highlight: Option<&'a str>,
10524 }
10525
10526 let snapshot = buffer.read(cx).snapshot();
10527 let range = self
10528 .selected_text_range(cx)
10529 .and_then(|selected_range| {
10530 if selected_range.is_empty() {
10531 None
10532 } else {
10533 Some(selected_range)
10534 }
10535 })
10536 .unwrap_or_else(|| 0..snapshot.len());
10537
10538 let chunks = snapshot.chunks(range, true);
10539 let mut lines = Vec::new();
10540 let mut line: VecDeque<Chunk> = VecDeque::new();
10541
10542 let Some(style) = self.style.as_ref() else {
10543 return;
10544 };
10545
10546 for chunk in chunks {
10547 let highlight = chunk
10548 .syntax_highlight_id
10549 .and_then(|id| id.name(&style.syntax));
10550 let mut chunk_lines = chunk.text.split('\n').peekable();
10551 while let Some(text) = chunk_lines.next() {
10552 let mut merged_with_last_token = false;
10553 if let Some(last_token) = line.back_mut() {
10554 if last_token.highlight == highlight {
10555 last_token.text.push_str(text);
10556 merged_with_last_token = true;
10557 }
10558 }
10559
10560 if !merged_with_last_token {
10561 line.push_back(Chunk {
10562 text: text.into(),
10563 highlight,
10564 });
10565 }
10566
10567 if chunk_lines.peek().is_some() {
10568 if line.len() > 1 && line.front().unwrap().text.is_empty() {
10569 line.pop_front();
10570 }
10571 if line.len() > 1 && line.back().unwrap().text.is_empty() {
10572 line.pop_back();
10573 }
10574
10575 lines.push(mem::take(&mut line));
10576 }
10577 }
10578 }
10579
10580 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
10581 return;
10582 };
10583 cx.write_to_clipboard(ClipboardItem::new(lines));
10584 }
10585
10586 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
10587 &self.inlay_hint_cache
10588 }
10589
10590 pub fn replay_insert_event(
10591 &mut self,
10592 text: &str,
10593 relative_utf16_range: Option<Range<isize>>,
10594 cx: &mut ViewContext<Self>,
10595 ) {
10596 if !self.input_enabled {
10597 cx.emit(EditorEvent::InputIgnored { text: text.into() });
10598 return;
10599 }
10600 if let Some(relative_utf16_range) = relative_utf16_range {
10601 let selections = self.selections.all::<OffsetUtf16>(cx);
10602 self.change_selections(None, cx, |s| {
10603 let new_ranges = selections.into_iter().map(|range| {
10604 let start = OffsetUtf16(
10605 range
10606 .head()
10607 .0
10608 .saturating_add_signed(relative_utf16_range.start),
10609 );
10610 let end = OffsetUtf16(
10611 range
10612 .head()
10613 .0
10614 .saturating_add_signed(relative_utf16_range.end),
10615 );
10616 start..end
10617 });
10618 s.select_ranges(new_ranges);
10619 });
10620 }
10621
10622 self.handle_input(text, cx);
10623 }
10624
10625 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
10626 let Some(project) = self.project.as_ref() else {
10627 return false;
10628 };
10629 let project = project.read(cx);
10630
10631 let mut supports = false;
10632 self.buffer().read(cx).for_each_buffer(|buffer| {
10633 if !supports {
10634 supports = project
10635 .language_servers_for_buffer(buffer.read(cx), cx)
10636 .any(
10637 |(_, server)| match server.capabilities().inlay_hint_provider {
10638 Some(lsp::OneOf::Left(enabled)) => enabled,
10639 Some(lsp::OneOf::Right(_)) => true,
10640 None => false,
10641 },
10642 )
10643 }
10644 });
10645 supports
10646 }
10647
10648 pub fn focus(&self, cx: &mut WindowContext) {
10649 cx.focus(&self.focus_handle)
10650 }
10651
10652 pub fn is_focused(&self, cx: &WindowContext) -> bool {
10653 self.focus_handle.is_focused(cx)
10654 }
10655
10656 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
10657 cx.emit(EditorEvent::Focused);
10658
10659 if let Some(rename) = self.pending_rename.as_ref() {
10660 let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
10661 cx.focus(&rename_editor_focus_handle);
10662 } else {
10663 if let Some(blame) = self.blame.as_ref() {
10664 blame.update(cx, GitBlame::focus)
10665 }
10666
10667 self.blink_manager.update(cx, BlinkManager::enable);
10668 self.show_cursor_names(cx);
10669 self.buffer.update(cx, |buffer, cx| {
10670 buffer.finalize_last_transaction(cx);
10671 if self.leader_peer_id.is_none() {
10672 buffer.set_active_selections(
10673 &self.selections.disjoint_anchors(),
10674 self.selections.line_mode,
10675 self.cursor_shape,
10676 cx,
10677 );
10678 }
10679 });
10680 }
10681 }
10682
10683 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
10684 self.blink_manager.update(cx, BlinkManager::disable);
10685 self.buffer
10686 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
10687
10688 if let Some(blame) = self.blame.as_ref() {
10689 blame.update(cx, GitBlame::blur)
10690 }
10691 self.hide_context_menu(cx);
10692 hide_hover(self, cx);
10693 cx.emit(EditorEvent::Blurred);
10694 cx.notify();
10695 }
10696
10697 pub fn register_action<A: Action>(
10698 &mut self,
10699 listener: impl Fn(&A, &mut WindowContext) + 'static,
10700 ) -> &mut Self {
10701 let listener = Arc::new(listener);
10702
10703 self.editor_actions.push(Box::new(move |cx| {
10704 let _view = cx.view().clone();
10705 let cx = cx.window_context();
10706 let listener = listener.clone();
10707 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
10708 let action = action.downcast_ref().unwrap();
10709 if phase == DispatchPhase::Bubble {
10710 listener(action, cx)
10711 }
10712 })
10713 }));
10714 self
10715 }
10716}
10717
10718fn hunks_for_selections(
10719 multi_buffer_snapshot: &MultiBufferSnapshot,
10720 selections: &[Selection<Anchor>],
10721) -> Vec<DiffHunk<MultiBufferRow>> {
10722 let mut hunks = Vec::with_capacity(selections.len());
10723 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
10724 HashMap::default();
10725 let buffer_rows_for_selections = selections.iter().map(|selection| {
10726 let head = selection.head();
10727 let tail = selection.tail();
10728 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
10729 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
10730 if start > end {
10731 end..start
10732 } else {
10733 start..end
10734 }
10735 });
10736
10737 for selected_multi_buffer_rows in buffer_rows_for_selections {
10738 let query_rows =
10739 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
10740 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
10741 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
10742 // when the caret is just above or just below the deleted hunk.
10743 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
10744 let related_to_selection = if allow_adjacent {
10745 hunk.associated_range.overlaps(&query_rows)
10746 || hunk.associated_range.start == query_rows.end
10747 || hunk.associated_range.end == query_rows.start
10748 } else {
10749 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
10750 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
10751 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
10752 || selected_multi_buffer_rows.end == hunk.associated_range.start
10753 };
10754 if related_to_selection {
10755 if !processed_buffer_rows
10756 .entry(hunk.buffer_id)
10757 .or_default()
10758 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
10759 {
10760 continue;
10761 }
10762 hunks.push(hunk);
10763 }
10764 }
10765 }
10766
10767 hunks
10768}
10769
10770pub trait CollaborationHub {
10771 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
10772 fn user_participant_indices<'a>(
10773 &self,
10774 cx: &'a AppContext,
10775 ) -> &'a HashMap<u64, ParticipantIndex>;
10776 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
10777}
10778
10779impl CollaborationHub for Model<Project> {
10780 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
10781 self.read(cx).collaborators()
10782 }
10783
10784 fn user_participant_indices<'a>(
10785 &self,
10786 cx: &'a AppContext,
10787 ) -> &'a HashMap<u64, ParticipantIndex> {
10788 self.read(cx).user_store().read(cx).participant_indices()
10789 }
10790
10791 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
10792 let this = self.read(cx);
10793 let user_ids = this.collaborators().values().map(|c| c.user_id);
10794 this.user_store().read_with(cx, |user_store, cx| {
10795 user_store.participant_names(user_ids, cx)
10796 })
10797 }
10798}
10799
10800pub trait CompletionProvider {
10801 fn completions(
10802 &self,
10803 buffer: &Model<Buffer>,
10804 buffer_position: text::Anchor,
10805 cx: &mut ViewContext<Editor>,
10806 ) -> Task<Result<Vec<Completion>>>;
10807
10808 fn resolve_completions(
10809 &self,
10810 buffer: Model<Buffer>,
10811 completion_indices: Vec<usize>,
10812 completions: Arc<RwLock<Box<[Completion]>>>,
10813 cx: &mut ViewContext<Editor>,
10814 ) -> Task<Result<bool>>;
10815
10816 fn apply_additional_edits_for_completion(
10817 &self,
10818 buffer: Model<Buffer>,
10819 completion: Completion,
10820 push_to_history: bool,
10821 cx: &mut ViewContext<Editor>,
10822 ) -> Task<Result<Option<language::Transaction>>>;
10823}
10824
10825impl CompletionProvider for Model<Project> {
10826 fn completions(
10827 &self,
10828 buffer: &Model<Buffer>,
10829 buffer_position: text::Anchor,
10830 cx: &mut ViewContext<Editor>,
10831 ) -> Task<Result<Vec<Completion>>> {
10832 self.update(cx, |project, cx| {
10833 project.completions(&buffer, buffer_position, cx)
10834 })
10835 }
10836
10837 fn resolve_completions(
10838 &self,
10839 buffer: Model<Buffer>,
10840 completion_indices: Vec<usize>,
10841 completions: Arc<RwLock<Box<[Completion]>>>,
10842 cx: &mut ViewContext<Editor>,
10843 ) -> Task<Result<bool>> {
10844 self.update(cx, |project, cx| {
10845 project.resolve_completions(buffer, completion_indices, completions, cx)
10846 })
10847 }
10848
10849 fn apply_additional_edits_for_completion(
10850 &self,
10851 buffer: Model<Buffer>,
10852 completion: Completion,
10853 push_to_history: bool,
10854 cx: &mut ViewContext<Editor>,
10855 ) -> Task<Result<Option<language::Transaction>>> {
10856 self.update(cx, |project, cx| {
10857 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
10858 })
10859 }
10860}
10861
10862fn inlay_hint_settings(
10863 location: Anchor,
10864 snapshot: &MultiBufferSnapshot,
10865 cx: &mut ViewContext<'_, Editor>,
10866) -> InlayHintSettings {
10867 let file = snapshot.file_at(location);
10868 let language = snapshot.language_at(location);
10869 let settings = all_language_settings(file, cx);
10870 settings
10871 .language(language.map(|l| l.name()).as_deref())
10872 .inlay_hints
10873}
10874
10875fn consume_contiguous_rows(
10876 contiguous_row_selections: &mut Vec<Selection<Point>>,
10877 selection: &Selection<Point>,
10878 display_map: &DisplaySnapshot,
10879 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
10880) -> (MultiBufferRow, MultiBufferRow) {
10881 contiguous_row_selections.push(selection.clone());
10882 let start_row = MultiBufferRow(selection.start.row);
10883 let mut end_row = ending_row(selection, display_map);
10884
10885 while let Some(next_selection) = selections.peek() {
10886 if next_selection.start.row <= end_row.0 {
10887 end_row = ending_row(next_selection, display_map);
10888 contiguous_row_selections.push(selections.next().unwrap().clone());
10889 } else {
10890 break;
10891 }
10892 }
10893 (start_row, end_row)
10894}
10895
10896fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
10897 if next_selection.end.column > 0 || next_selection.is_empty() {
10898 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
10899 } else {
10900 MultiBufferRow(next_selection.end.row)
10901 }
10902}
10903
10904impl EditorSnapshot {
10905 pub fn remote_selections_in_range<'a>(
10906 &'a self,
10907 range: &'a Range<Anchor>,
10908 collaboration_hub: &dyn CollaborationHub,
10909 cx: &'a AppContext,
10910 ) -> impl 'a + Iterator<Item = RemoteSelection> {
10911 let participant_names = collaboration_hub.user_names(cx);
10912 let participant_indices = collaboration_hub.user_participant_indices(cx);
10913 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
10914 let collaborators_by_replica_id = collaborators_by_peer_id
10915 .iter()
10916 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
10917 .collect::<HashMap<_, _>>();
10918 self.buffer_snapshot
10919 .remote_selections_in_range(range)
10920 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
10921 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
10922 let participant_index = participant_indices.get(&collaborator.user_id).copied();
10923 let user_name = participant_names.get(&collaborator.user_id).cloned();
10924 Some(RemoteSelection {
10925 replica_id,
10926 selection,
10927 cursor_shape,
10928 line_mode,
10929 participant_index,
10930 peer_id: collaborator.peer_id,
10931 user_name,
10932 })
10933 })
10934 }
10935
10936 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
10937 self.display_snapshot.buffer_snapshot.language_at(position)
10938 }
10939
10940 pub fn is_focused(&self) -> bool {
10941 self.is_focused
10942 }
10943
10944 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
10945 self.placeholder_text.as_ref()
10946 }
10947
10948 pub fn scroll_position(&self) -> gpui::Point<f32> {
10949 self.scroll_anchor.scroll_position(&self.display_snapshot)
10950 }
10951
10952 pub fn gutter_dimensions(
10953 &self,
10954 font_id: FontId,
10955 font_size: Pixels,
10956 em_width: Pixels,
10957 max_line_number_width: Pixels,
10958 cx: &AppContext,
10959 ) -> GutterDimensions {
10960 if !self.show_gutter {
10961 return GutterDimensions::default();
10962 }
10963 let descent = cx.text_system().descent(font_id, font_size);
10964
10965 let show_git_gutter = matches!(
10966 ProjectSettings::get_global(cx).git.git_gutter,
10967 Some(GitGutterSetting::TrackedFiles)
10968 );
10969 let gutter_settings = EditorSettings::get_global(cx).gutter;
10970 let gutter_lines_enabled = gutter_settings.line_numbers;
10971 let line_gutter_width = if gutter_lines_enabled {
10972 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
10973 let min_width_for_number_on_gutter = em_width * 4.0;
10974 max_line_number_width.max(min_width_for_number_on_gutter)
10975 } else {
10976 0.0.into()
10977 };
10978
10979 let git_blame_entries_width = self
10980 .render_git_blame_gutter
10981 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
10982
10983 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
10984 left_padding += if gutter_settings.code_actions {
10985 em_width * 3.0
10986 } else if show_git_gutter && gutter_lines_enabled {
10987 em_width * 2.0
10988 } else if show_git_gutter || gutter_lines_enabled {
10989 em_width
10990 } else {
10991 px(0.)
10992 };
10993
10994 let right_padding = if gutter_settings.folds && gutter_lines_enabled {
10995 em_width * 4.0
10996 } else if gutter_settings.folds {
10997 em_width * 3.0
10998 } else if gutter_lines_enabled {
10999 em_width
11000 } else {
11001 px(0.)
11002 };
11003
11004 GutterDimensions {
11005 left_padding,
11006 right_padding,
11007 width: line_gutter_width + left_padding + right_padding,
11008 margin: -descent,
11009 git_blame_entries_width,
11010 }
11011 }
11012}
11013
11014impl Deref for EditorSnapshot {
11015 type Target = DisplaySnapshot;
11016
11017 fn deref(&self) -> &Self::Target {
11018 &self.display_snapshot
11019 }
11020}
11021
11022#[derive(Clone, Debug, PartialEq, Eq)]
11023pub enum EditorEvent {
11024 InputIgnored {
11025 text: Arc<str>,
11026 },
11027 InputHandled {
11028 utf16_range_to_replace: Option<Range<isize>>,
11029 text: Arc<str>,
11030 },
11031 ExcerptsAdded {
11032 buffer: Model<Buffer>,
11033 predecessor: ExcerptId,
11034 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
11035 },
11036 ExcerptsRemoved {
11037 ids: Vec<ExcerptId>,
11038 },
11039 BufferEdited,
11040 Edited,
11041 Reparsed,
11042 Focused,
11043 Blurred,
11044 DirtyChanged,
11045 Saved,
11046 TitleChanged,
11047 DiffBaseChanged,
11048 SelectionsChanged {
11049 local: bool,
11050 },
11051 ScrollPositionChanged {
11052 local: bool,
11053 autoscroll: bool,
11054 },
11055 Closed,
11056 TransactionUndone {
11057 transaction_id: clock::Lamport,
11058 },
11059 TransactionBegun {
11060 transaction_id: clock::Lamport,
11061 },
11062}
11063
11064impl EventEmitter<EditorEvent> for Editor {}
11065
11066impl FocusableView for Editor {
11067 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
11068 self.focus_handle.clone()
11069 }
11070}
11071
11072impl Render for Editor {
11073 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
11074 let settings = ThemeSettings::get_global(cx);
11075
11076 let text_style = match self.mode {
11077 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
11078 color: cx.theme().colors().editor_foreground,
11079 font_family: settings.ui_font.family.clone(),
11080 font_features: settings.ui_font.features.clone(),
11081 font_size: rems(0.875).into(),
11082 font_weight: FontWeight::NORMAL,
11083 font_style: FontStyle::Normal,
11084 line_height: relative(settings.buffer_line_height.value()),
11085 background_color: None,
11086 underline: None,
11087 strikethrough: None,
11088 white_space: WhiteSpace::Normal,
11089 },
11090 EditorMode::Full => TextStyle {
11091 color: cx.theme().colors().editor_foreground,
11092 font_family: settings.buffer_font.family.clone(),
11093 font_features: settings.buffer_font.features.clone(),
11094 font_size: settings.buffer_font_size(cx).into(),
11095 font_weight: FontWeight::NORMAL,
11096 font_style: FontStyle::Normal,
11097 line_height: relative(settings.buffer_line_height.value()),
11098 background_color: None,
11099 underline: None,
11100 strikethrough: None,
11101 white_space: WhiteSpace::Normal,
11102 },
11103 };
11104
11105 let background = match self.mode {
11106 EditorMode::SingleLine => cx.theme().system().transparent,
11107 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
11108 EditorMode::Full => cx.theme().colors().editor_background,
11109 };
11110
11111 EditorElement::new(
11112 cx.view(),
11113 EditorStyle {
11114 background,
11115 local_player: cx.theme().players().local(),
11116 text: text_style,
11117 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
11118 syntax: cx.theme().syntax().clone(),
11119 status: cx.theme().status().clone(),
11120 inlay_hints_style: HighlightStyle {
11121 color: Some(cx.theme().status().hint),
11122 ..HighlightStyle::default()
11123 },
11124 suggestions_style: HighlightStyle {
11125 color: Some(cx.theme().status().predictive),
11126 ..HighlightStyle::default()
11127 },
11128 },
11129 )
11130 }
11131}
11132
11133impl ViewInputHandler for Editor {
11134 fn text_for_range(
11135 &mut self,
11136 range_utf16: Range<usize>,
11137 cx: &mut ViewContext<Self>,
11138 ) -> Option<String> {
11139 Some(
11140 self.buffer
11141 .read(cx)
11142 .read(cx)
11143 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
11144 .collect(),
11145 )
11146 }
11147
11148 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
11149 // Prevent the IME menu from appearing when holding down an alphabetic key
11150 // while input is disabled.
11151 if !self.input_enabled {
11152 return None;
11153 }
11154
11155 let range = self.selections.newest::<OffsetUtf16>(cx).range();
11156 Some(range.start.0..range.end.0)
11157 }
11158
11159 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
11160 let snapshot = self.buffer.read(cx).read(cx);
11161 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
11162 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
11163 }
11164
11165 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
11166 self.clear_highlights::<InputComposition>(cx);
11167 self.ime_transaction.take();
11168 }
11169
11170 fn replace_text_in_range(
11171 &mut self,
11172 range_utf16: Option<Range<usize>>,
11173 text: &str,
11174 cx: &mut ViewContext<Self>,
11175 ) {
11176 if !self.input_enabled {
11177 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11178 return;
11179 }
11180
11181 self.transact(cx, |this, cx| {
11182 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
11183 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
11184 Some(this.selection_replacement_ranges(range_utf16, cx))
11185 } else {
11186 this.marked_text_ranges(cx)
11187 };
11188
11189 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
11190 let newest_selection_id = this.selections.newest_anchor().id;
11191 this.selections
11192 .all::<OffsetUtf16>(cx)
11193 .iter()
11194 .zip(ranges_to_replace.iter())
11195 .find_map(|(selection, range)| {
11196 if selection.id == newest_selection_id {
11197 Some(
11198 (range.start.0 as isize - selection.head().0 as isize)
11199 ..(range.end.0 as isize - selection.head().0 as isize),
11200 )
11201 } else {
11202 None
11203 }
11204 })
11205 });
11206
11207 cx.emit(EditorEvent::InputHandled {
11208 utf16_range_to_replace: range_to_replace,
11209 text: text.into(),
11210 });
11211
11212 if let Some(new_selected_ranges) = new_selected_ranges {
11213 this.change_selections(None, cx, |selections| {
11214 selections.select_ranges(new_selected_ranges)
11215 });
11216 this.backspace(&Default::default(), cx);
11217 }
11218
11219 this.handle_input(text, cx);
11220 });
11221
11222 if let Some(transaction) = self.ime_transaction {
11223 self.buffer.update(cx, |buffer, cx| {
11224 buffer.group_until_transaction(transaction, cx);
11225 });
11226 }
11227
11228 self.unmark_text(cx);
11229 }
11230
11231 fn replace_and_mark_text_in_range(
11232 &mut self,
11233 range_utf16: Option<Range<usize>>,
11234 text: &str,
11235 new_selected_range_utf16: Option<Range<usize>>,
11236 cx: &mut ViewContext<Self>,
11237 ) {
11238 if !self.input_enabled {
11239 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11240 return;
11241 }
11242
11243 let transaction = self.transact(cx, |this, cx| {
11244 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
11245 let snapshot = this.buffer.read(cx).read(cx);
11246 if let Some(relative_range_utf16) = range_utf16.as_ref() {
11247 for marked_range in &mut marked_ranges {
11248 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
11249 marked_range.start.0 += relative_range_utf16.start;
11250 marked_range.start =
11251 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
11252 marked_range.end =
11253 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
11254 }
11255 }
11256 Some(marked_ranges)
11257 } else if let Some(range_utf16) = range_utf16 {
11258 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
11259 Some(this.selection_replacement_ranges(range_utf16, cx))
11260 } else {
11261 None
11262 };
11263
11264 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
11265 let newest_selection_id = this.selections.newest_anchor().id;
11266 this.selections
11267 .all::<OffsetUtf16>(cx)
11268 .iter()
11269 .zip(ranges_to_replace.iter())
11270 .find_map(|(selection, range)| {
11271 if selection.id == newest_selection_id {
11272 Some(
11273 (range.start.0 as isize - selection.head().0 as isize)
11274 ..(range.end.0 as isize - selection.head().0 as isize),
11275 )
11276 } else {
11277 None
11278 }
11279 })
11280 });
11281
11282 cx.emit(EditorEvent::InputHandled {
11283 utf16_range_to_replace: range_to_replace,
11284 text: text.into(),
11285 });
11286
11287 if let Some(ranges) = ranges_to_replace {
11288 this.change_selections(None, cx, |s| s.select_ranges(ranges));
11289 }
11290
11291 let marked_ranges = {
11292 let snapshot = this.buffer.read(cx).read(cx);
11293 this.selections
11294 .disjoint_anchors()
11295 .iter()
11296 .map(|selection| {
11297 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
11298 })
11299 .collect::<Vec<_>>()
11300 };
11301
11302 if text.is_empty() {
11303 this.unmark_text(cx);
11304 } else {
11305 this.highlight_text::<InputComposition>(
11306 marked_ranges.clone(),
11307 HighlightStyle {
11308 underline: Some(UnderlineStyle {
11309 thickness: px(1.),
11310 color: None,
11311 wavy: false,
11312 }),
11313 ..Default::default()
11314 },
11315 cx,
11316 );
11317 }
11318
11319 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
11320 let use_autoclose = this.use_autoclose;
11321 this.set_use_autoclose(false);
11322 this.handle_input(text, cx);
11323 this.set_use_autoclose(use_autoclose);
11324
11325 if let Some(new_selected_range) = new_selected_range_utf16 {
11326 let snapshot = this.buffer.read(cx).read(cx);
11327 let new_selected_ranges = marked_ranges
11328 .into_iter()
11329 .map(|marked_range| {
11330 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
11331 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
11332 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
11333 snapshot.clip_offset_utf16(new_start, Bias::Left)
11334 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
11335 })
11336 .collect::<Vec<_>>();
11337
11338 drop(snapshot);
11339 this.change_selections(None, cx, |selections| {
11340 selections.select_ranges(new_selected_ranges)
11341 });
11342 }
11343 });
11344
11345 self.ime_transaction = self.ime_transaction.or(transaction);
11346 if let Some(transaction) = self.ime_transaction {
11347 self.buffer.update(cx, |buffer, cx| {
11348 buffer.group_until_transaction(transaction, cx);
11349 });
11350 }
11351
11352 if self.text_highlights::<InputComposition>(cx).is_none() {
11353 self.ime_transaction.take();
11354 }
11355 }
11356
11357 fn bounds_for_range(
11358 &mut self,
11359 range_utf16: Range<usize>,
11360 element_bounds: gpui::Bounds<Pixels>,
11361 cx: &mut ViewContext<Self>,
11362 ) -> Option<gpui::Bounds<Pixels>> {
11363 let text_layout_details = self.text_layout_details(cx);
11364 let style = &text_layout_details.editor_style;
11365 let font_id = cx.text_system().resolve_font(&style.text.font());
11366 let font_size = style.text.font_size.to_pixels(cx.rem_size());
11367 let line_height = style.text.line_height_in_pixels(cx.rem_size());
11368 let em_width = cx
11369 .text_system()
11370 .typographic_bounds(font_id, font_size, 'm')
11371 .unwrap()
11372 .size
11373 .width;
11374
11375 let snapshot = self.snapshot(cx);
11376 let scroll_position = snapshot.scroll_position();
11377 let scroll_left = scroll_position.x * em_width;
11378
11379 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
11380 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
11381 + self.gutter_dimensions.width;
11382 let y = line_height * (start.row().as_f32() - scroll_position.y);
11383
11384 Some(Bounds {
11385 origin: element_bounds.origin + point(x, y),
11386 size: size(em_width, line_height),
11387 })
11388 }
11389}
11390
11391trait SelectionExt {
11392 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
11393 fn spanned_rows(
11394 &self,
11395 include_end_if_at_line_start: bool,
11396 map: &DisplaySnapshot,
11397 ) -> Range<MultiBufferRow>;
11398}
11399
11400impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
11401 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
11402 let start = self
11403 .start
11404 .to_point(&map.buffer_snapshot)
11405 .to_display_point(map);
11406 let end = self
11407 .end
11408 .to_point(&map.buffer_snapshot)
11409 .to_display_point(map);
11410 if self.reversed {
11411 end..start
11412 } else {
11413 start..end
11414 }
11415 }
11416
11417 fn spanned_rows(
11418 &self,
11419 include_end_if_at_line_start: bool,
11420 map: &DisplaySnapshot,
11421 ) -> Range<MultiBufferRow> {
11422 let start = self.start.to_point(&map.buffer_snapshot);
11423 let mut end = self.end.to_point(&map.buffer_snapshot);
11424 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
11425 end.row -= 1;
11426 }
11427
11428 let buffer_start = map.prev_line_boundary(start).0;
11429 let buffer_end = map.next_line_boundary(end).0;
11430 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
11431 }
11432}
11433
11434impl<T: InvalidationRegion> InvalidationStack<T> {
11435 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
11436 where
11437 S: Clone + ToOffset,
11438 {
11439 while let Some(region) = self.last() {
11440 let all_selections_inside_invalidation_ranges =
11441 if selections.len() == region.ranges().len() {
11442 selections
11443 .iter()
11444 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
11445 .all(|(selection, invalidation_range)| {
11446 let head = selection.head().to_offset(buffer);
11447 invalidation_range.start <= head && invalidation_range.end >= head
11448 })
11449 } else {
11450 false
11451 };
11452
11453 if all_selections_inside_invalidation_ranges {
11454 break;
11455 } else {
11456 self.pop();
11457 }
11458 }
11459 }
11460}
11461
11462impl<T> Default for InvalidationStack<T> {
11463 fn default() -> Self {
11464 Self(Default::default())
11465 }
11466}
11467
11468impl<T> Deref for InvalidationStack<T> {
11469 type Target = Vec<T>;
11470
11471 fn deref(&self) -> &Self::Target {
11472 &self.0
11473 }
11474}
11475
11476impl<T> DerefMut for InvalidationStack<T> {
11477 fn deref_mut(&mut self) -> &mut Self::Target {
11478 &mut self.0
11479 }
11480}
11481
11482impl InvalidationRegion for SnippetState {
11483 fn ranges(&self) -> &[Range<Anchor>] {
11484 &self.ranges[self.active_index]
11485 }
11486}
11487
11488pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
11489 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
11490
11491 Box::new(move |cx: &mut BlockContext| {
11492 let group_id: SharedString = cx.block_id.to_string().into();
11493
11494 let mut text_style = cx.text_style().clone();
11495 text_style.color = diagnostic_style(diagnostic.severity, true, cx.theme().status());
11496 let theme_settings = ThemeSettings::get_global(cx);
11497 text_style.font_family = theme_settings.buffer_font.family.clone();
11498 text_style.font_style = theme_settings.buffer_font.style;
11499 text_style.font_features = theme_settings.buffer_font.features.clone();
11500 text_style.font_weight = theme_settings.buffer_font.weight;
11501
11502 let multi_line_diagnostic = diagnostic.message.contains('\n');
11503
11504 let buttons = |diagnostic: &Diagnostic, block_id: usize| {
11505 if multi_line_diagnostic {
11506 v_flex()
11507 } else {
11508 h_flex()
11509 }
11510 .children(diagnostic.is_primary.then(|| {
11511 IconButton::new(("close-block", block_id), IconName::XCircle)
11512 .icon_color(Color::Muted)
11513 .size(ButtonSize::Compact)
11514 .style(ButtonStyle::Transparent)
11515 .visible_on_hover(group_id.clone())
11516 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
11517 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
11518 }))
11519 .child(
11520 IconButton::new(("copy-block", block_id), IconName::Copy)
11521 .icon_color(Color::Muted)
11522 .size(ButtonSize::Compact)
11523 .style(ButtonStyle::Transparent)
11524 .visible_on_hover(group_id.clone())
11525 .on_click({
11526 let message = diagnostic.message.clone();
11527 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
11528 })
11529 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
11530 )
11531 };
11532
11533 let icon_size = buttons(&diagnostic, cx.block_id)
11534 .into_any_element()
11535 .layout_as_root(AvailableSpace::min_size(), cx);
11536
11537 h_flex()
11538 .id(cx.block_id)
11539 .group(group_id.clone())
11540 .relative()
11541 .size_full()
11542 .pl(cx.gutter_dimensions.width)
11543 .w(cx.max_width + cx.gutter_dimensions.width)
11544 .child(
11545 div()
11546 .flex()
11547 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
11548 .flex_shrink(),
11549 )
11550 .child(buttons(&diagnostic, cx.block_id))
11551 .child(div().flex().flex_shrink_0().child(
11552 StyledText::new(text_without_backticks.clone()).with_highlights(
11553 &text_style,
11554 code_ranges.iter().map(|range| {
11555 (
11556 range.clone(),
11557 HighlightStyle {
11558 font_weight: Some(FontWeight::BOLD),
11559 ..Default::default()
11560 },
11561 )
11562 }),
11563 ),
11564 ))
11565 .into_any_element()
11566 })
11567}
11568
11569pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
11570 let mut text_without_backticks = String::new();
11571 let mut code_ranges = Vec::new();
11572
11573 if let Some(source) = &diagnostic.source {
11574 text_without_backticks.push_str(&source);
11575 code_ranges.push(0..source.len());
11576 text_without_backticks.push_str(": ");
11577 }
11578
11579 let mut prev_offset = 0;
11580 let mut in_code_block = false;
11581 for (ix, _) in diagnostic
11582 .message
11583 .match_indices('`')
11584 .chain([(diagnostic.message.len(), "")])
11585 {
11586 let prev_len = text_without_backticks.len();
11587 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
11588 prev_offset = ix + 1;
11589 if in_code_block {
11590 code_ranges.push(prev_len..text_without_backticks.len());
11591 in_code_block = false;
11592 } else {
11593 in_code_block = true;
11594 }
11595 }
11596
11597 (text_without_backticks.into(), code_ranges)
11598}
11599
11600fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
11601 match (severity, valid) {
11602 (DiagnosticSeverity::ERROR, true) => colors.error,
11603 (DiagnosticSeverity::ERROR, false) => colors.error,
11604 (DiagnosticSeverity::WARNING, true) => colors.warning,
11605 (DiagnosticSeverity::WARNING, false) => colors.warning,
11606 (DiagnosticSeverity::INFORMATION, true) => colors.info,
11607 (DiagnosticSeverity::INFORMATION, false) => colors.info,
11608 (DiagnosticSeverity::HINT, true) => colors.info,
11609 (DiagnosticSeverity::HINT, false) => colors.info,
11610 _ => colors.ignored,
11611 }
11612}
11613
11614pub fn styled_runs_for_code_label<'a>(
11615 label: &'a CodeLabel,
11616 syntax_theme: &'a theme::SyntaxTheme,
11617) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
11618 let fade_out = HighlightStyle {
11619 fade_out: Some(0.35),
11620 ..Default::default()
11621 };
11622
11623 let mut prev_end = label.filter_range.end;
11624 label
11625 .runs
11626 .iter()
11627 .enumerate()
11628 .flat_map(move |(ix, (range, highlight_id))| {
11629 let style = if let Some(style) = highlight_id.style(syntax_theme) {
11630 style
11631 } else {
11632 return Default::default();
11633 };
11634 let mut muted_style = style;
11635 muted_style.highlight(fade_out);
11636
11637 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
11638 if range.start >= label.filter_range.end {
11639 if range.start > prev_end {
11640 runs.push((prev_end..range.start, fade_out));
11641 }
11642 runs.push((range.clone(), muted_style));
11643 } else if range.end <= label.filter_range.end {
11644 runs.push((range.clone(), style));
11645 } else {
11646 runs.push((range.start..label.filter_range.end, style));
11647 runs.push((label.filter_range.end..range.end, muted_style));
11648 }
11649 prev_end = cmp::max(prev_end, range.end);
11650
11651 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
11652 runs.push((prev_end..label.text.len(), fade_out));
11653 }
11654
11655 runs
11656 })
11657}
11658
11659pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
11660 let mut prev_index = 0;
11661 let mut prev_codepoint: Option<char> = None;
11662 text.char_indices()
11663 .chain([(text.len(), '\0')])
11664 .filter_map(move |(index, codepoint)| {
11665 let prev_codepoint = prev_codepoint.replace(codepoint)?;
11666 let is_boundary = index == text.len()
11667 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
11668 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
11669 if is_boundary {
11670 let chunk = &text[prev_index..index];
11671 prev_index = index;
11672 Some(chunk)
11673 } else {
11674 None
11675 }
11676 })
11677}
11678
11679trait RangeToAnchorExt {
11680 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
11681}
11682
11683impl<T: ToOffset> RangeToAnchorExt for Range<T> {
11684 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
11685 let start_offset = self.start.to_offset(snapshot);
11686 let end_offset = self.end.to_offset(snapshot);
11687 if start_offset == end_offset {
11688 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
11689 } else {
11690 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
11691 }
11692 }
11693}
11694
11695pub trait RowExt {
11696 fn as_f32(&self) -> f32;
11697
11698 fn next_row(&self) -> Self;
11699
11700 fn previous_row(&self) -> Self;
11701
11702 fn minus(&self, other: Self) -> u32;
11703}
11704
11705impl RowExt for DisplayRow {
11706 fn as_f32(&self) -> f32 {
11707 self.0 as f32
11708 }
11709
11710 fn next_row(&self) -> Self {
11711 Self(self.0 + 1)
11712 }
11713
11714 fn previous_row(&self) -> Self {
11715 Self(self.0.saturating_sub(1))
11716 }
11717
11718 fn minus(&self, other: Self) -> u32 {
11719 self.0 - other.0
11720 }
11721}
11722
11723impl RowExt for MultiBufferRow {
11724 fn as_f32(&self) -> f32 {
11725 self.0 as f32
11726 }
11727
11728 fn next_row(&self) -> Self {
11729 Self(self.0 + 1)
11730 }
11731
11732 fn previous_row(&self) -> Self {
11733 Self(self.0.saturating_sub(1))
11734 }
11735
11736 fn minus(&self, other: Self) -> u32 {
11737 self.0 - other.0
11738 }
11739}
11740
11741trait RowRangeExt {
11742 type Row;
11743
11744 fn len(&self) -> usize;
11745
11746 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
11747}
11748
11749impl RowRangeExt for Range<MultiBufferRow> {
11750 type Row = MultiBufferRow;
11751
11752 fn len(&self) -> usize {
11753 (self.end.0 - self.start.0) as usize
11754 }
11755
11756 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
11757 (self.start.0..self.end.0).map(MultiBufferRow)
11758 }
11759}
11760
11761impl RowRangeExt for Range<DisplayRow> {
11762 type Row = DisplayRow;
11763
11764 fn len(&self) -> usize {
11765 (self.end.0 - self.start.0) as usize
11766 }
11767
11768 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
11769 (self.start.0..self.end.0).map(DisplayRow)
11770 }
11771}
11772
11773fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
11774 if hunk.diff_base_byte_range.is_empty() {
11775 DiffHunkStatus::Added
11776 } else if hunk.associated_range.is_empty() {
11777 DiffHunkStatus::Removed
11778 } else {
11779 DiffHunkStatus::Modified
11780 }
11781}