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