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