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