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