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