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