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