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