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