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