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 let project = self.project.clone();
8251 cx.spawn(|this, mut cx| async move {
8252 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8253 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8254 }) else {
8255 return;
8256 };
8257
8258 let Some(project) = project else {
8259 return;
8260 };
8261
8262 let hide_runnables = project
8263 .update(&mut cx, |project, cx| {
8264 // Do not display any test indicators in non-dev server remote projects.
8265 project.is_remote() && project.ssh_connection_string(cx).is_none()
8266 })
8267 .unwrap_or(true);
8268 if hide_runnables {
8269 return;
8270 }
8271 let new_rows =
8272 cx.background_executor()
8273 .spawn({
8274 let snapshot = display_snapshot.clone();
8275 async move {
8276 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8277 }
8278 })
8279 .await;
8280 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8281
8282 this.update(&mut cx, |this, _| {
8283 this.clear_tasks();
8284 for (key, value) in rows {
8285 this.insert_tasks(key, value);
8286 }
8287 })
8288 .ok();
8289 })
8290 }
8291 fn fetch_runnable_ranges(
8292 snapshot: &DisplaySnapshot,
8293 range: Range<Anchor>,
8294 ) -> Vec<language::RunnableRange> {
8295 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8296 }
8297
8298 fn runnable_rows(
8299 project: Model<Project>,
8300 snapshot: DisplaySnapshot,
8301 runnable_ranges: Vec<RunnableRange>,
8302 mut cx: AsyncWindowContext,
8303 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8304 runnable_ranges
8305 .into_iter()
8306 .filter_map(|mut runnable| {
8307 let tasks = cx
8308 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8309 .ok()?;
8310 if tasks.is_empty() {
8311 return None;
8312 }
8313
8314 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8315
8316 let row = snapshot
8317 .buffer_snapshot
8318 .buffer_line_for_row(MultiBufferRow(point.row))?
8319 .1
8320 .start
8321 .row;
8322
8323 let context_range =
8324 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8325 Some((
8326 (runnable.buffer_id, row),
8327 RunnableTasks {
8328 templates: tasks,
8329 offset: MultiBufferOffset(runnable.run_range.start),
8330 context_range,
8331 column: point.column,
8332 extra_variables: runnable.extra_captures,
8333 },
8334 ))
8335 })
8336 .collect()
8337 }
8338
8339 fn templates_with_tags(
8340 project: &Model<Project>,
8341 runnable: &mut Runnable,
8342 cx: &WindowContext<'_>,
8343 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8344 let (inventory, worktree_id) = project.read_with(cx, |project, cx| {
8345 let worktree_id = project
8346 .buffer_for_id(runnable.buffer)
8347 .and_then(|buffer| buffer.read(cx).file())
8348 .map(|file| WorktreeId::from_usize(file.worktree_id()));
8349
8350 (project.task_inventory().clone(), worktree_id)
8351 });
8352
8353 let inventory = inventory.read(cx);
8354 let tags = mem::take(&mut runnable.tags);
8355 let mut tags: Vec<_> = tags
8356 .into_iter()
8357 .flat_map(|tag| {
8358 let tag = tag.0.clone();
8359 inventory
8360 .list_tasks(Some(runnable.language.clone()), worktree_id)
8361 .into_iter()
8362 .filter(move |(_, template)| {
8363 template.tags.iter().any(|source_tag| source_tag == &tag)
8364 })
8365 })
8366 .sorted_by_key(|(kind, _)| kind.to_owned())
8367 .collect();
8368 if let Some((leading_tag_source, _)) = tags.first() {
8369 // Strongest source wins; if we have worktree tag binding, prefer that to
8370 // global and language bindings;
8371 // if we have a global binding, prefer that to language binding.
8372 let first_mismatch = tags
8373 .iter()
8374 .position(|(tag_source, _)| tag_source != leading_tag_source);
8375 if let Some(index) = first_mismatch {
8376 tags.truncate(index);
8377 }
8378 }
8379
8380 tags
8381 }
8382
8383 pub fn move_to_enclosing_bracket(
8384 &mut self,
8385 _: &MoveToEnclosingBracket,
8386 cx: &mut ViewContext<Self>,
8387 ) {
8388 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8389 s.move_offsets_with(|snapshot, selection| {
8390 let Some(enclosing_bracket_ranges) =
8391 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8392 else {
8393 return;
8394 };
8395
8396 let mut best_length = usize::MAX;
8397 let mut best_inside = false;
8398 let mut best_in_bracket_range = false;
8399 let mut best_destination = None;
8400 for (open, close) in enclosing_bracket_ranges {
8401 let close = close.to_inclusive();
8402 let length = close.end() - open.start;
8403 let inside = selection.start >= open.end && selection.end <= *close.start();
8404 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8405 || close.contains(&selection.head());
8406
8407 // If best is next to a bracket and current isn't, skip
8408 if !in_bracket_range && best_in_bracket_range {
8409 continue;
8410 }
8411
8412 // Prefer smaller lengths unless best is inside and current isn't
8413 if length > best_length && (best_inside || !inside) {
8414 continue;
8415 }
8416
8417 best_length = length;
8418 best_inside = inside;
8419 best_in_bracket_range = in_bracket_range;
8420 best_destination = Some(
8421 if close.contains(&selection.start) && close.contains(&selection.end) {
8422 if inside {
8423 open.end
8424 } else {
8425 open.start
8426 }
8427 } else {
8428 if inside {
8429 *close.start()
8430 } else {
8431 *close.end()
8432 }
8433 },
8434 );
8435 }
8436
8437 if let Some(destination) = best_destination {
8438 selection.collapse_to(destination, SelectionGoal::None);
8439 }
8440 })
8441 });
8442 }
8443
8444 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8445 self.end_selection(cx);
8446 self.selection_history.mode = SelectionHistoryMode::Undoing;
8447 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8448 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8449 self.select_next_state = entry.select_next_state;
8450 self.select_prev_state = entry.select_prev_state;
8451 self.add_selections_state = entry.add_selections_state;
8452 self.request_autoscroll(Autoscroll::newest(), cx);
8453 }
8454 self.selection_history.mode = SelectionHistoryMode::Normal;
8455 }
8456
8457 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8458 self.end_selection(cx);
8459 self.selection_history.mode = SelectionHistoryMode::Redoing;
8460 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8461 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8462 self.select_next_state = entry.select_next_state;
8463 self.select_prev_state = entry.select_prev_state;
8464 self.add_selections_state = entry.add_selections_state;
8465 self.request_autoscroll(Autoscroll::newest(), cx);
8466 }
8467 self.selection_history.mode = SelectionHistoryMode::Normal;
8468 }
8469
8470 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8471 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8472 }
8473
8474 pub fn expand_excerpts_down(
8475 &mut self,
8476 action: &ExpandExcerptsDown,
8477 cx: &mut ViewContext<Self>,
8478 ) {
8479 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8480 }
8481
8482 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8483 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8484 }
8485
8486 pub fn expand_excerpts_for_direction(
8487 &mut self,
8488 lines: u32,
8489 direction: ExpandExcerptDirection,
8490 cx: &mut ViewContext<Self>,
8491 ) {
8492 let selections = self.selections.disjoint_anchors();
8493
8494 let lines = if lines == 0 {
8495 EditorSettings::get_global(cx).expand_excerpt_lines
8496 } else {
8497 lines
8498 };
8499
8500 self.buffer.update(cx, |buffer, cx| {
8501 buffer.expand_excerpts(
8502 selections
8503 .into_iter()
8504 .map(|selection| selection.head().excerpt_id)
8505 .dedup(),
8506 lines,
8507 direction,
8508 cx,
8509 )
8510 })
8511 }
8512
8513 pub fn expand_excerpt(
8514 &mut self,
8515 excerpt: ExcerptId,
8516 direction: ExpandExcerptDirection,
8517 cx: &mut ViewContext<Self>,
8518 ) {
8519 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8520 self.buffer.update(cx, |buffer, cx| {
8521 buffer.expand_excerpts([excerpt], lines, direction, cx)
8522 })
8523 }
8524
8525 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8526 self.go_to_diagnostic_impl(Direction::Next, cx)
8527 }
8528
8529 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8530 self.go_to_diagnostic_impl(Direction::Prev, cx)
8531 }
8532
8533 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8534 let buffer = self.buffer.read(cx).snapshot(cx);
8535 let selection = self.selections.newest::<usize>(cx);
8536
8537 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8538 if direction == Direction::Next {
8539 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8540 let (group_id, jump_to) = popover.activation_info();
8541 if self.activate_diagnostics(group_id, cx) {
8542 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8543 let mut new_selection = s.newest_anchor().clone();
8544 new_selection.collapse_to(jump_to, SelectionGoal::None);
8545 s.select_anchors(vec![new_selection.clone()]);
8546 });
8547 }
8548 return;
8549 }
8550 }
8551
8552 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8553 active_diagnostics
8554 .primary_range
8555 .to_offset(&buffer)
8556 .to_inclusive()
8557 });
8558 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8559 if active_primary_range.contains(&selection.head()) {
8560 *active_primary_range.start()
8561 } else {
8562 selection.head()
8563 }
8564 } else {
8565 selection.head()
8566 };
8567 let snapshot = self.snapshot(cx);
8568 loop {
8569 let diagnostics = if direction == Direction::Prev {
8570 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8571 } else {
8572 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8573 }
8574 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8575 let group = diagnostics
8576 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8577 // be sorted in a stable way
8578 // skip until we are at current active diagnostic, if it exists
8579 .skip_while(|entry| {
8580 (match direction {
8581 Direction::Prev => entry.range.start >= search_start,
8582 Direction::Next => entry.range.start <= search_start,
8583 }) && self
8584 .active_diagnostics
8585 .as_ref()
8586 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8587 })
8588 .find_map(|entry| {
8589 if entry.diagnostic.is_primary
8590 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8591 && !entry.range.is_empty()
8592 // if we match with the active diagnostic, skip it
8593 && Some(entry.diagnostic.group_id)
8594 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8595 {
8596 Some((entry.range, entry.diagnostic.group_id))
8597 } else {
8598 None
8599 }
8600 });
8601
8602 if let Some((primary_range, group_id)) = group {
8603 if self.activate_diagnostics(group_id, cx) {
8604 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8605 s.select(vec![Selection {
8606 id: selection.id,
8607 start: primary_range.start,
8608 end: primary_range.start,
8609 reversed: false,
8610 goal: SelectionGoal::None,
8611 }]);
8612 });
8613 }
8614 break;
8615 } else {
8616 // Cycle around to the start of the buffer, potentially moving back to the start of
8617 // the currently active diagnostic.
8618 active_primary_range.take();
8619 if direction == Direction::Prev {
8620 if search_start == buffer.len() {
8621 break;
8622 } else {
8623 search_start = buffer.len();
8624 }
8625 } else if search_start == 0 {
8626 break;
8627 } else {
8628 search_start = 0;
8629 }
8630 }
8631 }
8632 }
8633
8634 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8635 let snapshot = self
8636 .display_map
8637 .update(cx, |display_map, cx| display_map.snapshot(cx));
8638 let selection = self.selections.newest::<Point>(cx);
8639
8640 if !self.seek_in_direction(
8641 &snapshot,
8642 selection.head(),
8643 false,
8644 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8645 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8646 ),
8647 cx,
8648 ) {
8649 let wrapped_point = Point::zero();
8650 self.seek_in_direction(
8651 &snapshot,
8652 wrapped_point,
8653 true,
8654 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8655 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8656 ),
8657 cx,
8658 );
8659 }
8660 }
8661
8662 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8663 let snapshot = self
8664 .display_map
8665 .update(cx, |display_map, cx| display_map.snapshot(cx));
8666 let selection = self.selections.newest::<Point>(cx);
8667
8668 if !self.seek_in_direction(
8669 &snapshot,
8670 selection.head(),
8671 false,
8672 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8673 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8674 ),
8675 cx,
8676 ) {
8677 let wrapped_point = snapshot.buffer_snapshot.max_point();
8678 self.seek_in_direction(
8679 &snapshot,
8680 wrapped_point,
8681 true,
8682 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8683 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8684 ),
8685 cx,
8686 );
8687 }
8688 }
8689
8690 fn seek_in_direction(
8691 &mut self,
8692 snapshot: &DisplaySnapshot,
8693 initial_point: Point,
8694 is_wrapped: bool,
8695 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8696 cx: &mut ViewContext<Editor>,
8697 ) -> bool {
8698 let display_point = initial_point.to_display_point(snapshot);
8699 let mut hunks = hunks
8700 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8701 .filter(|hunk| {
8702 if is_wrapped {
8703 true
8704 } else {
8705 !hunk.contains_display_row(display_point.row())
8706 }
8707 })
8708 .dedup();
8709
8710 if let Some(hunk) = hunks.next() {
8711 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8712 let row = hunk.start_display_row();
8713 let point = DisplayPoint::new(row, 0);
8714 s.select_display_ranges([point..point]);
8715 });
8716
8717 true
8718 } else {
8719 false
8720 }
8721 }
8722
8723 pub fn go_to_definition(
8724 &mut self,
8725 _: &GoToDefinition,
8726 cx: &mut ViewContext<Self>,
8727 ) -> Task<Result<bool>> {
8728 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8729 }
8730
8731 pub fn go_to_implementation(
8732 &mut self,
8733 _: &GoToImplementation,
8734 cx: &mut ViewContext<Self>,
8735 ) -> Task<Result<bool>> {
8736 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8737 }
8738
8739 pub fn go_to_implementation_split(
8740 &mut self,
8741 _: &GoToImplementationSplit,
8742 cx: &mut ViewContext<Self>,
8743 ) -> Task<Result<bool>> {
8744 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8745 }
8746
8747 pub fn go_to_type_definition(
8748 &mut self,
8749 _: &GoToTypeDefinition,
8750 cx: &mut ViewContext<Self>,
8751 ) -> Task<Result<bool>> {
8752 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8753 }
8754
8755 pub fn go_to_definition_split(
8756 &mut self,
8757 _: &GoToDefinitionSplit,
8758 cx: &mut ViewContext<Self>,
8759 ) -> Task<Result<bool>> {
8760 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
8761 }
8762
8763 pub fn go_to_type_definition_split(
8764 &mut self,
8765 _: &GoToTypeDefinitionSplit,
8766 cx: &mut ViewContext<Self>,
8767 ) -> Task<Result<bool>> {
8768 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
8769 }
8770
8771 fn go_to_definition_of_kind(
8772 &mut self,
8773 kind: GotoDefinitionKind,
8774 split: bool,
8775 cx: &mut ViewContext<Self>,
8776 ) -> Task<Result<bool>> {
8777 let Some(workspace) = self.workspace() else {
8778 return Task::ready(Ok(false));
8779 };
8780 let buffer = self.buffer.read(cx);
8781 let head = self.selections.newest::<usize>(cx).head();
8782 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
8783 text_anchor
8784 } else {
8785 return Task::ready(Ok(false));
8786 };
8787
8788 let project = workspace.read(cx).project().clone();
8789 let definitions = project.update(cx, |project, cx| match kind {
8790 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
8791 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
8792 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
8793 });
8794
8795 cx.spawn(|editor, mut cx| async move {
8796 let definitions = definitions.await?;
8797 let navigated = editor
8798 .update(&mut cx, |editor, cx| {
8799 editor.navigate_to_hover_links(
8800 Some(kind),
8801 definitions
8802 .into_iter()
8803 .filter(|location| {
8804 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
8805 })
8806 .map(HoverLink::Text)
8807 .collect::<Vec<_>>(),
8808 split,
8809 cx,
8810 )
8811 })?
8812 .await?;
8813 anyhow::Ok(navigated)
8814 })
8815 }
8816
8817 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
8818 let position = self.selections.newest_anchor().head();
8819 let Some((buffer, buffer_position)) =
8820 self.buffer.read(cx).text_anchor_for_position(position, cx)
8821 else {
8822 return;
8823 };
8824
8825 cx.spawn(|editor, mut cx| async move {
8826 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
8827 editor.update(&mut cx, |_, cx| {
8828 cx.open_url(&url);
8829 })
8830 } else {
8831 Ok(())
8832 }
8833 })
8834 .detach();
8835 }
8836
8837 pub(crate) fn navigate_to_hover_links(
8838 &mut self,
8839 kind: Option<GotoDefinitionKind>,
8840 mut definitions: Vec<HoverLink>,
8841 split: bool,
8842 cx: &mut ViewContext<Editor>,
8843 ) -> Task<Result<bool>> {
8844 // If there is one definition, just open it directly
8845 if definitions.len() == 1 {
8846 let definition = definitions.pop().unwrap();
8847 let target_task = match definition {
8848 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8849 HoverLink::InlayHint(lsp_location, server_id) => {
8850 self.compute_target_location(lsp_location, server_id, cx)
8851 }
8852 HoverLink::Url(url) => {
8853 cx.open_url(&url);
8854 Task::ready(Ok(None))
8855 }
8856 };
8857 cx.spawn(|editor, mut cx| async move {
8858 let target = target_task.await.context("target resolution task")?;
8859 if let Some(target) = target {
8860 editor.update(&mut cx, |editor, cx| {
8861 let Some(workspace) = editor.workspace() else {
8862 return false;
8863 };
8864 let pane = workspace.read(cx).active_pane().clone();
8865
8866 let range = target.range.to_offset(target.buffer.read(cx));
8867 let range = editor.range_for_match(&range);
8868
8869 /// If select range has more than one line, we
8870 /// just point the cursor to range.start.
8871 fn check_multiline_range(
8872 buffer: &Buffer,
8873 range: Range<usize>,
8874 ) -> Range<usize> {
8875 if buffer.offset_to_point(range.start).row
8876 == buffer.offset_to_point(range.end).row
8877 {
8878 range
8879 } else {
8880 range.start..range.start
8881 }
8882 }
8883
8884 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
8885 let buffer = target.buffer.read(cx);
8886 let range = check_multiline_range(buffer, range);
8887 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
8888 s.select_ranges([range]);
8889 });
8890 } else {
8891 cx.window_context().defer(move |cx| {
8892 let target_editor: View<Self> =
8893 workspace.update(cx, |workspace, cx| {
8894 let pane = if split {
8895 workspace.adjacent_pane(cx)
8896 } else {
8897 workspace.active_pane().clone()
8898 };
8899
8900 workspace.open_project_item(pane, target.buffer.clone(), cx)
8901 });
8902 target_editor.update(cx, |target_editor, cx| {
8903 // When selecting a definition in a different buffer, disable the nav history
8904 // to avoid creating a history entry at the previous cursor location.
8905 pane.update(cx, |pane, _| pane.disable_history());
8906 let buffer = target.buffer.read(cx);
8907 let range = check_multiline_range(buffer, range);
8908 target_editor.change_selections(
8909 Some(Autoscroll::focused()),
8910 cx,
8911 |s| {
8912 s.select_ranges([range]);
8913 },
8914 );
8915 pane.update(cx, |pane, _| pane.enable_history());
8916 });
8917 });
8918 }
8919 true
8920 })
8921 } else {
8922 Ok(false)
8923 }
8924 })
8925 } else if !definitions.is_empty() {
8926 let replica_id = self.replica_id(cx);
8927 cx.spawn(|editor, mut cx| async move {
8928 let (title, location_tasks, workspace) = editor
8929 .update(&mut cx, |editor, cx| {
8930 let tab_kind = match kind {
8931 Some(GotoDefinitionKind::Implementation) => "Implementations",
8932 _ => "Definitions",
8933 };
8934 let title = definitions
8935 .iter()
8936 .find_map(|definition| match definition {
8937 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
8938 let buffer = origin.buffer.read(cx);
8939 format!(
8940 "{} for {}",
8941 tab_kind,
8942 buffer
8943 .text_for_range(origin.range.clone())
8944 .collect::<String>()
8945 )
8946 }),
8947 HoverLink::InlayHint(_, _) => None,
8948 HoverLink::Url(_) => None,
8949 })
8950 .unwrap_or(tab_kind.to_string());
8951 let location_tasks = definitions
8952 .into_iter()
8953 .map(|definition| match definition {
8954 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8955 HoverLink::InlayHint(lsp_location, server_id) => {
8956 editor.compute_target_location(lsp_location, server_id, cx)
8957 }
8958 HoverLink::Url(_) => Task::ready(Ok(None)),
8959 })
8960 .collect::<Vec<_>>();
8961 (title, location_tasks, editor.workspace().clone())
8962 })
8963 .context("location tasks preparation")?;
8964
8965 let locations = futures::future::join_all(location_tasks)
8966 .await
8967 .into_iter()
8968 .filter_map(|location| location.transpose())
8969 .collect::<Result<_>>()
8970 .context("location tasks")?;
8971
8972 let Some(workspace) = workspace else {
8973 return Ok(false);
8974 };
8975 let opened = workspace
8976 .update(&mut cx, |workspace, cx| {
8977 Self::open_locations_in_multibuffer(
8978 workspace, locations, replica_id, title, split, cx,
8979 )
8980 })
8981 .ok();
8982
8983 anyhow::Ok(opened.is_some())
8984 })
8985 } else {
8986 Task::ready(Ok(false))
8987 }
8988 }
8989
8990 fn compute_target_location(
8991 &self,
8992 lsp_location: lsp::Location,
8993 server_id: LanguageServerId,
8994 cx: &mut ViewContext<Editor>,
8995 ) -> Task<anyhow::Result<Option<Location>>> {
8996 let Some(project) = self.project.clone() else {
8997 return Task::Ready(Some(Ok(None)));
8998 };
8999
9000 cx.spawn(move |editor, mut cx| async move {
9001 let location_task = editor.update(&mut cx, |editor, cx| {
9002 project.update(cx, |project, cx| {
9003 let language_server_name =
9004 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9005 project
9006 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9007 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9008 });
9009 language_server_name.map(|language_server_name| {
9010 project.open_local_buffer_via_lsp(
9011 lsp_location.uri.clone(),
9012 server_id,
9013 language_server_name,
9014 cx,
9015 )
9016 })
9017 })
9018 })?;
9019 let location = match location_task {
9020 Some(task) => Some({
9021 let target_buffer_handle = task.await.context("open local buffer")?;
9022 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9023 let target_start = target_buffer
9024 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9025 let target_end = target_buffer
9026 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9027 target_buffer.anchor_after(target_start)
9028 ..target_buffer.anchor_before(target_end)
9029 })?;
9030 Location {
9031 buffer: target_buffer_handle,
9032 range,
9033 }
9034 }),
9035 None => None,
9036 };
9037 Ok(location)
9038 })
9039 }
9040
9041 pub fn find_all_references(
9042 &mut self,
9043 _: &FindAllReferences,
9044 cx: &mut ViewContext<Self>,
9045 ) -> Option<Task<Result<()>>> {
9046 let multi_buffer = self.buffer.read(cx);
9047 let selection = self.selections.newest::<usize>(cx);
9048 let head = selection.head();
9049
9050 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9051 let head_anchor = multi_buffer_snapshot.anchor_at(
9052 head,
9053 if head < selection.tail() {
9054 Bias::Right
9055 } else {
9056 Bias::Left
9057 },
9058 );
9059
9060 match self
9061 .find_all_references_task_sources
9062 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9063 {
9064 Ok(_) => {
9065 log::info!(
9066 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9067 );
9068 return None;
9069 }
9070 Err(i) => {
9071 self.find_all_references_task_sources.insert(i, head_anchor);
9072 }
9073 }
9074
9075 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9076 let replica_id = self.replica_id(cx);
9077 let workspace = self.workspace()?;
9078 let project = workspace.read(cx).project().clone();
9079 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9080 Some(cx.spawn(|editor, mut cx| async move {
9081 let _cleanup = defer({
9082 let mut cx = cx.clone();
9083 move || {
9084 let _ = editor.update(&mut cx, |editor, _| {
9085 if let Ok(i) =
9086 editor
9087 .find_all_references_task_sources
9088 .binary_search_by(|anchor| {
9089 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9090 })
9091 {
9092 editor.find_all_references_task_sources.remove(i);
9093 }
9094 });
9095 }
9096 });
9097
9098 let locations = references.await?;
9099 if locations.is_empty() {
9100 return anyhow::Ok(());
9101 }
9102
9103 workspace.update(&mut cx, |workspace, cx| {
9104 let title = locations
9105 .first()
9106 .as_ref()
9107 .map(|location| {
9108 let buffer = location.buffer.read(cx);
9109 format!(
9110 "References to `{}`",
9111 buffer
9112 .text_for_range(location.range.clone())
9113 .collect::<String>()
9114 )
9115 })
9116 .unwrap();
9117 Self::open_locations_in_multibuffer(
9118 workspace, locations, replica_id, title, false, cx,
9119 );
9120 })
9121 }))
9122 }
9123
9124 /// Opens a multibuffer with the given project locations in it
9125 pub fn open_locations_in_multibuffer(
9126 workspace: &mut Workspace,
9127 mut locations: Vec<Location>,
9128 replica_id: ReplicaId,
9129 title: String,
9130 split: bool,
9131 cx: &mut ViewContext<Workspace>,
9132 ) {
9133 // If there are multiple definitions, open them in a multibuffer
9134 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9135 let mut locations = locations.into_iter().peekable();
9136 let mut ranges_to_highlight = Vec::new();
9137 let capability = workspace.project().read(cx).capability();
9138
9139 let excerpt_buffer = cx.new_model(|cx| {
9140 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9141 while let Some(location) = locations.next() {
9142 let buffer = location.buffer.read(cx);
9143 let mut ranges_for_buffer = Vec::new();
9144 let range = location.range.to_offset(buffer);
9145 ranges_for_buffer.push(range.clone());
9146
9147 while let Some(next_location) = locations.peek() {
9148 if next_location.buffer == location.buffer {
9149 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9150 locations.next();
9151 } else {
9152 break;
9153 }
9154 }
9155
9156 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9157 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9158 location.buffer.clone(),
9159 ranges_for_buffer,
9160 DEFAULT_MULTIBUFFER_CONTEXT,
9161 cx,
9162 ))
9163 }
9164
9165 multibuffer.with_title(title)
9166 });
9167
9168 let editor = cx.new_view(|cx| {
9169 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9170 });
9171 editor.update(cx, |editor, cx| {
9172 editor.highlight_background::<Self>(
9173 &ranges_to_highlight,
9174 |theme| theme.editor_highlighted_line_background,
9175 cx,
9176 );
9177 });
9178
9179 let item = Box::new(editor);
9180 let item_id = item.item_id();
9181
9182 if split {
9183 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9184 } else {
9185 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9186 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9187 pane.close_current_preview_item(cx)
9188 } else {
9189 None
9190 }
9191 });
9192 workspace.add_item_to_active_pane(item.clone(), destination_index, cx);
9193 }
9194 workspace.active_pane().update(cx, |pane, cx| {
9195 pane.set_preview_item_id(Some(item_id), cx);
9196 });
9197 }
9198
9199 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9200 use language::ToOffset as _;
9201
9202 let project = self.project.clone()?;
9203 let selection = self.selections.newest_anchor().clone();
9204 let (cursor_buffer, cursor_buffer_position) = self
9205 .buffer
9206 .read(cx)
9207 .text_anchor_for_position(selection.head(), cx)?;
9208 let (tail_buffer, cursor_buffer_position_end) = self
9209 .buffer
9210 .read(cx)
9211 .text_anchor_for_position(selection.tail(), cx)?;
9212 if tail_buffer != cursor_buffer {
9213 return None;
9214 }
9215
9216 let snapshot = cursor_buffer.read(cx).snapshot();
9217 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9218 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9219 let prepare_rename = project.update(cx, |project, cx| {
9220 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9221 });
9222 drop(snapshot);
9223
9224 Some(cx.spawn(|this, mut cx| async move {
9225 let rename_range = if let Some(range) = prepare_rename.await? {
9226 Some(range)
9227 } else {
9228 this.update(&mut cx, |this, cx| {
9229 let buffer = this.buffer.read(cx).snapshot(cx);
9230 let mut buffer_highlights = this
9231 .document_highlights_for_position(selection.head(), &buffer)
9232 .filter(|highlight| {
9233 highlight.start.excerpt_id == selection.head().excerpt_id
9234 && highlight.end.excerpt_id == selection.head().excerpt_id
9235 });
9236 buffer_highlights
9237 .next()
9238 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9239 })?
9240 };
9241 if let Some(rename_range) = rename_range {
9242 this.update(&mut cx, |this, cx| {
9243 let snapshot = cursor_buffer.read(cx).snapshot();
9244 let rename_buffer_range = rename_range.to_offset(&snapshot);
9245 let cursor_offset_in_rename_range =
9246 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9247 let cursor_offset_in_rename_range_end =
9248 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9249
9250 this.take_rename(false, cx);
9251 let buffer = this.buffer.read(cx).read(cx);
9252 let cursor_offset = selection.head().to_offset(&buffer);
9253 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9254 let rename_end = rename_start + rename_buffer_range.len();
9255 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9256 let mut old_highlight_id = None;
9257 let old_name: Arc<str> = buffer
9258 .chunks(rename_start..rename_end, true)
9259 .map(|chunk| {
9260 if old_highlight_id.is_none() {
9261 old_highlight_id = chunk.syntax_highlight_id;
9262 }
9263 chunk.text
9264 })
9265 .collect::<String>()
9266 .into();
9267
9268 drop(buffer);
9269
9270 // Position the selection in the rename editor so that it matches the current selection.
9271 this.show_local_selections = false;
9272 let rename_editor = cx.new_view(|cx| {
9273 let mut editor = Editor::single_line(cx);
9274 editor.buffer.update(cx, |buffer, cx| {
9275 buffer.edit([(0..0, old_name.clone())], None, cx)
9276 });
9277 let rename_selection_range = match cursor_offset_in_rename_range
9278 .cmp(&cursor_offset_in_rename_range_end)
9279 {
9280 Ordering::Equal => {
9281 editor.select_all(&SelectAll, cx);
9282 return editor;
9283 }
9284 Ordering::Less => {
9285 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9286 }
9287 Ordering::Greater => {
9288 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9289 }
9290 };
9291 if rename_selection_range.end > old_name.len() {
9292 editor.select_all(&SelectAll, cx);
9293 } else {
9294 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9295 s.select_ranges([rename_selection_range]);
9296 });
9297 }
9298 editor
9299 });
9300
9301 let write_highlights =
9302 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9303 let read_highlights =
9304 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9305 let ranges = write_highlights
9306 .iter()
9307 .flat_map(|(_, ranges)| ranges.iter())
9308 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9309 .cloned()
9310 .collect();
9311
9312 this.highlight_text::<Rename>(
9313 ranges,
9314 HighlightStyle {
9315 fade_out: Some(0.6),
9316 ..Default::default()
9317 },
9318 cx,
9319 );
9320 let rename_focus_handle = rename_editor.focus_handle(cx);
9321 cx.focus(&rename_focus_handle);
9322 let block_id = this.insert_blocks(
9323 [BlockProperties {
9324 style: BlockStyle::Flex,
9325 position: range.start,
9326 height: 1,
9327 render: Box::new({
9328 let rename_editor = rename_editor.clone();
9329 move |cx: &mut BlockContext| {
9330 let mut text_style = cx.editor_style.text.clone();
9331 if let Some(highlight_style) = old_highlight_id
9332 .and_then(|h| h.style(&cx.editor_style.syntax))
9333 {
9334 text_style = text_style.highlight(highlight_style);
9335 }
9336 div()
9337 .pl(cx.anchor_x)
9338 .child(EditorElement::new(
9339 &rename_editor,
9340 EditorStyle {
9341 background: cx.theme().system().transparent,
9342 local_player: cx.editor_style.local_player,
9343 text: text_style,
9344 scrollbar_width: cx.editor_style.scrollbar_width,
9345 syntax: cx.editor_style.syntax.clone(),
9346 status: cx.editor_style.status.clone(),
9347 inlay_hints_style: HighlightStyle {
9348 color: Some(cx.theme().status().hint),
9349 font_weight: Some(FontWeight::BOLD),
9350 ..HighlightStyle::default()
9351 },
9352 suggestions_style: HighlightStyle {
9353 color: Some(cx.theme().status().predictive),
9354 ..HighlightStyle::default()
9355 },
9356 },
9357 ))
9358 .into_any_element()
9359 }
9360 }),
9361 disposition: BlockDisposition::Below,
9362 }],
9363 Some(Autoscroll::fit()),
9364 cx,
9365 )[0];
9366 this.pending_rename = Some(RenameState {
9367 range,
9368 old_name,
9369 editor: rename_editor,
9370 block_id,
9371 });
9372 })?;
9373 }
9374
9375 Ok(())
9376 }))
9377 }
9378
9379 pub fn confirm_rename(
9380 &mut self,
9381 _: &ConfirmRename,
9382 cx: &mut ViewContext<Self>,
9383 ) -> Option<Task<Result<()>>> {
9384 let rename = self.take_rename(false, cx)?;
9385 let workspace = self.workspace()?;
9386 let (start_buffer, start) = self
9387 .buffer
9388 .read(cx)
9389 .text_anchor_for_position(rename.range.start, cx)?;
9390 let (end_buffer, end) = self
9391 .buffer
9392 .read(cx)
9393 .text_anchor_for_position(rename.range.end, cx)?;
9394 if start_buffer != end_buffer {
9395 return None;
9396 }
9397
9398 let buffer = start_buffer;
9399 let range = start..end;
9400 let old_name = rename.old_name;
9401 let new_name = rename.editor.read(cx).text(cx);
9402
9403 let rename = workspace
9404 .read(cx)
9405 .project()
9406 .clone()
9407 .update(cx, |project, cx| {
9408 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9409 });
9410 let workspace = workspace.downgrade();
9411
9412 Some(cx.spawn(|editor, mut cx| async move {
9413 let project_transaction = rename.await?;
9414 Self::open_project_transaction(
9415 &editor,
9416 workspace,
9417 project_transaction,
9418 format!("Rename: {} → {}", old_name, new_name),
9419 cx.clone(),
9420 )
9421 .await?;
9422
9423 editor.update(&mut cx, |editor, cx| {
9424 editor.refresh_document_highlights(cx);
9425 })?;
9426 Ok(())
9427 }))
9428 }
9429
9430 fn take_rename(
9431 &mut self,
9432 moving_cursor: bool,
9433 cx: &mut ViewContext<Self>,
9434 ) -> Option<RenameState> {
9435 let rename = self.pending_rename.take()?;
9436 if rename.editor.focus_handle(cx).is_focused(cx) {
9437 cx.focus(&self.focus_handle);
9438 }
9439
9440 self.remove_blocks(
9441 [rename.block_id].into_iter().collect(),
9442 Some(Autoscroll::fit()),
9443 cx,
9444 );
9445 self.clear_highlights::<Rename>(cx);
9446 self.show_local_selections = true;
9447
9448 if moving_cursor {
9449 let rename_editor = rename.editor.read(cx);
9450 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9451
9452 // Update the selection to match the position of the selection inside
9453 // the rename editor.
9454 let snapshot = self.buffer.read(cx).read(cx);
9455 let rename_range = rename.range.to_offset(&snapshot);
9456 let cursor_in_editor = snapshot
9457 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9458 .min(rename_range.end);
9459 drop(snapshot);
9460
9461 self.change_selections(None, cx, |s| {
9462 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9463 });
9464 } else {
9465 self.refresh_document_highlights(cx);
9466 }
9467
9468 Some(rename)
9469 }
9470
9471 pub fn pending_rename(&self) -> Option<&RenameState> {
9472 self.pending_rename.as_ref()
9473 }
9474
9475 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9476 let project = match &self.project {
9477 Some(project) => project.clone(),
9478 None => return None,
9479 };
9480
9481 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9482 }
9483
9484 fn perform_format(
9485 &mut self,
9486 project: Model<Project>,
9487 trigger: FormatTrigger,
9488 cx: &mut ViewContext<Self>,
9489 ) -> Task<Result<()>> {
9490 let buffer = self.buffer().clone();
9491 let mut buffers = buffer.read(cx).all_buffers();
9492 if trigger == FormatTrigger::Save {
9493 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9494 }
9495
9496 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9497 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9498
9499 cx.spawn(|_, mut cx| async move {
9500 let transaction = futures::select_biased! {
9501 () = timeout => {
9502 log::warn!("timed out waiting for formatting");
9503 None
9504 }
9505 transaction = format.log_err().fuse() => transaction,
9506 };
9507
9508 buffer
9509 .update(&mut cx, |buffer, cx| {
9510 if let Some(transaction) = transaction {
9511 if !buffer.is_singleton() {
9512 buffer.push_transaction(&transaction.0, cx);
9513 }
9514 }
9515
9516 cx.notify();
9517 })
9518 .ok();
9519
9520 Ok(())
9521 })
9522 }
9523
9524 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9525 if let Some(project) = self.project.clone() {
9526 self.buffer.update(cx, |multi_buffer, cx| {
9527 project.update(cx, |project, cx| {
9528 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9529 });
9530 })
9531 }
9532 }
9533
9534 fn cancel_language_server_work(
9535 &mut self,
9536 _: &CancelLanguageServerWork,
9537 cx: &mut ViewContext<Self>,
9538 ) {
9539 if let Some(project) = self.project.clone() {
9540 self.buffer.update(cx, |multi_buffer, cx| {
9541 project.update(cx, |project, cx| {
9542 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9543 });
9544 })
9545 }
9546 }
9547
9548 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9549 cx.show_character_palette();
9550 }
9551
9552 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9553 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9554 let buffer = self.buffer.read(cx).snapshot(cx);
9555 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9556 let is_valid = buffer
9557 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9558 .any(|entry| {
9559 entry.diagnostic.is_primary
9560 && !entry.range.is_empty()
9561 && entry.range.start == primary_range_start
9562 && entry.diagnostic.message == active_diagnostics.primary_message
9563 });
9564
9565 if is_valid != active_diagnostics.is_valid {
9566 active_diagnostics.is_valid = is_valid;
9567 let mut new_styles = HashMap::default();
9568 for (block_id, diagnostic) in &active_diagnostics.blocks {
9569 new_styles.insert(
9570 *block_id,
9571 (
9572 None,
9573 diagnostic_block_renderer(diagnostic.clone(), is_valid),
9574 ),
9575 );
9576 }
9577 self.display_map.update(cx, |display_map, cx| {
9578 display_map.replace_blocks(new_styles, cx)
9579 });
9580 }
9581 }
9582 }
9583
9584 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9585 self.dismiss_diagnostics(cx);
9586 let snapshot = self.snapshot(cx);
9587 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9588 let buffer = self.buffer.read(cx).snapshot(cx);
9589
9590 let mut primary_range = None;
9591 let mut primary_message = None;
9592 let mut group_end = Point::zero();
9593 let diagnostic_group = buffer
9594 .diagnostic_group::<MultiBufferPoint>(group_id)
9595 .filter_map(|entry| {
9596 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9597 && (entry.range.start.row == entry.range.end.row
9598 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9599 {
9600 return None;
9601 }
9602 if entry.range.end > group_end {
9603 group_end = entry.range.end;
9604 }
9605 if entry.diagnostic.is_primary {
9606 primary_range = Some(entry.range.clone());
9607 primary_message = Some(entry.diagnostic.message.clone());
9608 }
9609 Some(entry)
9610 })
9611 .collect::<Vec<_>>();
9612 let primary_range = primary_range?;
9613 let primary_message = primary_message?;
9614 let primary_range =
9615 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9616
9617 let blocks = display_map
9618 .insert_blocks(
9619 diagnostic_group.iter().map(|entry| {
9620 let diagnostic = entry.diagnostic.clone();
9621 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9622 BlockProperties {
9623 style: BlockStyle::Fixed,
9624 position: buffer.anchor_after(entry.range.start),
9625 height: message_height,
9626 render: diagnostic_block_renderer(diagnostic, true),
9627 disposition: BlockDisposition::Below,
9628 }
9629 }),
9630 cx,
9631 )
9632 .into_iter()
9633 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9634 .collect();
9635
9636 Some(ActiveDiagnosticGroup {
9637 primary_range,
9638 primary_message,
9639 group_id,
9640 blocks,
9641 is_valid: true,
9642 })
9643 });
9644 self.active_diagnostics.is_some()
9645 }
9646
9647 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9648 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9649 self.display_map.update(cx, |display_map, cx| {
9650 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9651 });
9652 cx.notify();
9653 }
9654 }
9655
9656 pub fn set_selections_from_remote(
9657 &mut self,
9658 selections: Vec<Selection<Anchor>>,
9659 pending_selection: Option<Selection<Anchor>>,
9660 cx: &mut ViewContext<Self>,
9661 ) {
9662 let old_cursor_position = self.selections.newest_anchor().head();
9663 self.selections.change_with(cx, |s| {
9664 s.select_anchors(selections);
9665 if let Some(pending_selection) = pending_selection {
9666 s.set_pending(pending_selection, SelectMode::Character);
9667 } else {
9668 s.clear_pending();
9669 }
9670 });
9671 self.selections_did_change(false, &old_cursor_position, true, cx);
9672 }
9673
9674 fn push_to_selection_history(&mut self) {
9675 self.selection_history.push(SelectionHistoryEntry {
9676 selections: self.selections.disjoint_anchors(),
9677 select_next_state: self.select_next_state.clone(),
9678 select_prev_state: self.select_prev_state.clone(),
9679 add_selections_state: self.add_selections_state.clone(),
9680 });
9681 }
9682
9683 pub fn transact(
9684 &mut self,
9685 cx: &mut ViewContext<Self>,
9686 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9687 ) -> Option<TransactionId> {
9688 self.start_transaction_at(Instant::now(), cx);
9689 update(self, cx);
9690 self.end_transaction_at(Instant::now(), cx)
9691 }
9692
9693 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9694 self.end_selection(cx);
9695 if let Some(tx_id) = self
9696 .buffer
9697 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9698 {
9699 self.selection_history
9700 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9701 cx.emit(EditorEvent::TransactionBegun {
9702 transaction_id: tx_id,
9703 })
9704 }
9705 }
9706
9707 fn end_transaction_at(
9708 &mut self,
9709 now: Instant,
9710 cx: &mut ViewContext<Self>,
9711 ) -> Option<TransactionId> {
9712 if let Some(transaction_id) = self
9713 .buffer
9714 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9715 {
9716 if let Some((_, end_selections)) =
9717 self.selection_history.transaction_mut(transaction_id)
9718 {
9719 *end_selections = Some(self.selections.disjoint_anchors());
9720 } else {
9721 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9722 }
9723
9724 cx.emit(EditorEvent::Edited { transaction_id });
9725 Some(transaction_id)
9726 } else {
9727 None
9728 }
9729 }
9730
9731 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9732 let mut fold_ranges = Vec::new();
9733
9734 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9735
9736 let selections = self.selections.all_adjusted(cx);
9737 for selection in selections {
9738 let range = selection.range().sorted();
9739 let buffer_start_row = range.start.row;
9740
9741 for row in (0..=range.end.row).rev() {
9742 if let Some((foldable_range, fold_text)) =
9743 display_map.foldable_range(MultiBufferRow(row))
9744 {
9745 if foldable_range.end.row >= buffer_start_row {
9746 fold_ranges.push((foldable_range, fold_text));
9747 if row <= range.start.row {
9748 break;
9749 }
9750 }
9751 }
9752 }
9753 }
9754
9755 self.fold_ranges(fold_ranges, true, cx);
9756 }
9757
9758 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
9759 let buffer_row = fold_at.buffer_row;
9760 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9761
9762 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
9763 let autoscroll = self
9764 .selections
9765 .all::<Point>(cx)
9766 .iter()
9767 .any(|selection| fold_range.overlaps(&selection.range()));
9768
9769 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
9770 }
9771 }
9772
9773 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
9774 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9775 let buffer = &display_map.buffer_snapshot;
9776 let selections = self.selections.all::<Point>(cx);
9777 let ranges = selections
9778 .iter()
9779 .map(|s| {
9780 let range = s.display_range(&display_map).sorted();
9781 let mut start = range.start.to_point(&display_map);
9782 let mut end = range.end.to_point(&display_map);
9783 start.column = 0;
9784 end.column = buffer.line_len(MultiBufferRow(end.row));
9785 start..end
9786 })
9787 .collect::<Vec<_>>();
9788
9789 self.unfold_ranges(ranges, true, true, cx);
9790 }
9791
9792 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
9793 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9794
9795 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
9796 ..Point::new(
9797 unfold_at.buffer_row.0,
9798 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
9799 );
9800
9801 let autoscroll = self
9802 .selections
9803 .all::<Point>(cx)
9804 .iter()
9805 .any(|selection| selection.range().overlaps(&intersection_range));
9806
9807 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
9808 }
9809
9810 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
9811 let selections = self.selections.all::<Point>(cx);
9812 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9813 let line_mode = self.selections.line_mode;
9814 let ranges = selections.into_iter().map(|s| {
9815 if line_mode {
9816 let start = Point::new(s.start.row, 0);
9817 let end = Point::new(
9818 s.end.row,
9819 display_map
9820 .buffer_snapshot
9821 .line_len(MultiBufferRow(s.end.row)),
9822 );
9823 (start..end, display_map.fold_placeholder.clone())
9824 } else {
9825 (s.start..s.end, display_map.fold_placeholder.clone())
9826 }
9827 });
9828 self.fold_ranges(ranges, true, cx);
9829 }
9830
9831 pub fn fold_ranges<T: ToOffset + Clone>(
9832 &mut self,
9833 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
9834 auto_scroll: bool,
9835 cx: &mut ViewContext<Self>,
9836 ) {
9837 let mut fold_ranges = Vec::new();
9838 let mut buffers_affected = HashMap::default();
9839 let multi_buffer = self.buffer().read(cx);
9840 for (fold_range, fold_text) in ranges {
9841 if let Some((_, buffer, _)) =
9842 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
9843 {
9844 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9845 };
9846 fold_ranges.push((fold_range, fold_text));
9847 }
9848
9849 let mut ranges = fold_ranges.into_iter().peekable();
9850 if ranges.peek().is_some() {
9851 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
9852
9853 if auto_scroll {
9854 self.request_autoscroll(Autoscroll::fit(), cx);
9855 }
9856
9857 for buffer in buffers_affected.into_values() {
9858 self.sync_expanded_diff_hunks(buffer, cx);
9859 }
9860
9861 cx.notify();
9862
9863 if let Some(active_diagnostics) = self.active_diagnostics.take() {
9864 // Clear diagnostics block when folding a range that contains it.
9865 let snapshot = self.snapshot(cx);
9866 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
9867 drop(snapshot);
9868 self.active_diagnostics = Some(active_diagnostics);
9869 self.dismiss_diagnostics(cx);
9870 } else {
9871 self.active_diagnostics = Some(active_diagnostics);
9872 }
9873 }
9874
9875 self.scrollbar_marker_state.dirty = true;
9876 }
9877 }
9878
9879 pub fn unfold_ranges<T: ToOffset + Clone>(
9880 &mut self,
9881 ranges: impl IntoIterator<Item = Range<T>>,
9882 inclusive: bool,
9883 auto_scroll: bool,
9884 cx: &mut ViewContext<Self>,
9885 ) {
9886 let mut unfold_ranges = Vec::new();
9887 let mut buffers_affected = HashMap::default();
9888 let multi_buffer = self.buffer().read(cx);
9889 for range in ranges {
9890 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
9891 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9892 };
9893 unfold_ranges.push(range);
9894 }
9895
9896 let mut ranges = unfold_ranges.into_iter().peekable();
9897 if ranges.peek().is_some() {
9898 self.display_map
9899 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
9900 if auto_scroll {
9901 self.request_autoscroll(Autoscroll::fit(), cx);
9902 }
9903
9904 for buffer in buffers_affected.into_values() {
9905 self.sync_expanded_diff_hunks(buffer, cx);
9906 }
9907
9908 cx.notify();
9909 self.scrollbar_marker_state.dirty = true;
9910 self.active_indent_guides_state.dirty = true;
9911 }
9912 }
9913
9914 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
9915 if hovered != self.gutter_hovered {
9916 self.gutter_hovered = hovered;
9917 cx.notify();
9918 }
9919 }
9920
9921 pub fn insert_blocks(
9922 &mut self,
9923 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
9924 autoscroll: Option<Autoscroll>,
9925 cx: &mut ViewContext<Self>,
9926 ) -> Vec<BlockId> {
9927 let blocks = self
9928 .display_map
9929 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
9930 if let Some(autoscroll) = autoscroll {
9931 self.request_autoscroll(autoscroll, cx);
9932 }
9933 blocks
9934 }
9935
9936 pub fn replace_blocks(
9937 &mut self,
9938 blocks: HashMap<BlockId, (Option<u8>, RenderBlock)>,
9939 autoscroll: Option<Autoscroll>,
9940 cx: &mut ViewContext<Self>,
9941 ) {
9942 self.display_map
9943 .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
9944 if let Some(autoscroll) = autoscroll {
9945 self.request_autoscroll(autoscroll, cx);
9946 }
9947 }
9948
9949 pub fn remove_blocks(
9950 &mut self,
9951 block_ids: HashSet<BlockId>,
9952 autoscroll: Option<Autoscroll>,
9953 cx: &mut ViewContext<Self>,
9954 ) {
9955 self.display_map.update(cx, |display_map, cx| {
9956 display_map.remove_blocks(block_ids, cx)
9957 });
9958 if let Some(autoscroll) = autoscroll {
9959 self.request_autoscroll(autoscroll, cx);
9960 }
9961 }
9962
9963 pub fn insert_creases(
9964 &mut self,
9965 creases: impl IntoIterator<Item = Crease>,
9966 cx: &mut ViewContext<Self>,
9967 ) -> Vec<CreaseId> {
9968 self.display_map
9969 .update(cx, |map, cx| map.insert_creases(creases, cx))
9970 }
9971
9972 pub fn remove_creases(
9973 &mut self,
9974 ids: impl IntoIterator<Item = CreaseId>,
9975 cx: &mut ViewContext<Self>,
9976 ) {
9977 self.display_map
9978 .update(cx, |map, cx| map.remove_creases(ids, cx));
9979 }
9980
9981 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
9982 self.display_map
9983 .update(cx, |map, cx| map.snapshot(cx))
9984 .longest_row()
9985 }
9986
9987 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
9988 self.display_map
9989 .update(cx, |map, cx| map.snapshot(cx))
9990 .max_point()
9991 }
9992
9993 pub fn text(&self, cx: &AppContext) -> String {
9994 self.buffer.read(cx).read(cx).text()
9995 }
9996
9997 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
9998 let text = self.text(cx);
9999 let text = text.trim();
10000
10001 if text.is_empty() {
10002 return None;
10003 }
10004
10005 Some(text.to_string())
10006 }
10007
10008 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10009 self.transact(cx, |this, cx| {
10010 this.buffer
10011 .read(cx)
10012 .as_singleton()
10013 .expect("you can only call set_text on editors for singleton buffers")
10014 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10015 });
10016 }
10017
10018 pub fn display_text(&self, cx: &mut AppContext) -> String {
10019 self.display_map
10020 .update(cx, |map, cx| map.snapshot(cx))
10021 .text()
10022 }
10023
10024 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10025 let mut wrap_guides = smallvec::smallvec![];
10026
10027 if self.show_wrap_guides == Some(false) {
10028 return wrap_guides;
10029 }
10030
10031 let settings = self.buffer.read(cx).settings_at(0, cx);
10032 if settings.show_wrap_guides {
10033 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10034 wrap_guides.push((soft_wrap as usize, true));
10035 }
10036 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10037 }
10038
10039 wrap_guides
10040 }
10041
10042 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10043 let settings = self.buffer.read(cx).settings_at(0, cx);
10044 let mode = self
10045 .soft_wrap_mode_override
10046 .unwrap_or_else(|| settings.soft_wrap);
10047 match mode {
10048 language_settings::SoftWrap::None => SoftWrap::None,
10049 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10050 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10051 language_settings::SoftWrap::PreferredLineLength => {
10052 SoftWrap::Column(settings.preferred_line_length)
10053 }
10054 }
10055 }
10056
10057 pub fn set_soft_wrap_mode(
10058 &mut self,
10059 mode: language_settings::SoftWrap,
10060 cx: &mut ViewContext<Self>,
10061 ) {
10062 self.soft_wrap_mode_override = Some(mode);
10063 cx.notify();
10064 }
10065
10066 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10067 let rem_size = cx.rem_size();
10068 self.display_map.update(cx, |map, cx| {
10069 map.set_font(
10070 style.text.font(),
10071 style.text.font_size.to_pixels(rem_size),
10072 cx,
10073 )
10074 });
10075 self.style = Some(style);
10076 }
10077
10078 pub fn style(&self) -> Option<&EditorStyle> {
10079 self.style.as_ref()
10080 }
10081
10082 // Called by the element. This method is not designed to be called outside of the editor
10083 // element's layout code because it does not notify when rewrapping is computed synchronously.
10084 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10085 self.display_map
10086 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10087 }
10088
10089 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10090 if self.soft_wrap_mode_override.is_some() {
10091 self.soft_wrap_mode_override.take();
10092 } else {
10093 let soft_wrap = match self.soft_wrap_mode(cx) {
10094 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10095 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10096 language_settings::SoftWrap::PreferLine
10097 }
10098 };
10099 self.soft_wrap_mode_override = Some(soft_wrap);
10100 }
10101 cx.notify();
10102 }
10103
10104 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10105 let Some(workspace) = self.workspace() else {
10106 return;
10107 };
10108 let fs = workspace.read(cx).app_state().fs.clone();
10109 let current_show = TabBarSettings::get_global(cx).show;
10110 update_settings_file::<TabBarSettings>(fs, cx, move |setting| {
10111 setting.show = Some(!current_show);
10112 });
10113 }
10114
10115 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10116 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10117 self.buffer
10118 .read(cx)
10119 .settings_at(0, cx)
10120 .indent_guides
10121 .enabled
10122 });
10123 self.show_indent_guides = Some(!currently_enabled);
10124 cx.notify();
10125 }
10126
10127 fn should_show_indent_guides(&self) -> Option<bool> {
10128 self.show_indent_guides
10129 }
10130
10131 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10132 let mut editor_settings = EditorSettings::get_global(cx).clone();
10133 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10134 EditorSettings::override_global(editor_settings, cx);
10135 }
10136
10137 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10138 self.show_gutter = show_gutter;
10139 cx.notify();
10140 }
10141
10142 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10143 self.show_line_numbers = Some(show_line_numbers);
10144 cx.notify();
10145 }
10146
10147 pub fn set_show_git_diff_gutter(
10148 &mut self,
10149 show_git_diff_gutter: bool,
10150 cx: &mut ViewContext<Self>,
10151 ) {
10152 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10153 cx.notify();
10154 }
10155
10156 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10157 self.show_code_actions = Some(show_code_actions);
10158 cx.notify();
10159 }
10160
10161 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10162 self.show_wrap_guides = Some(show_wrap_guides);
10163 cx.notify();
10164 }
10165
10166 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10167 self.show_indent_guides = Some(show_indent_guides);
10168 cx.notify();
10169 }
10170
10171 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
10172 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10173 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10174 cx.reveal_path(&file.abs_path(cx));
10175 }
10176 }
10177 }
10178
10179 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10180 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10181 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10182 if let Some(path) = file.abs_path(cx).to_str() {
10183 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10184 }
10185 }
10186 }
10187 }
10188
10189 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10190 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10191 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10192 if let Some(path) = file.path().to_str() {
10193 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10194 }
10195 }
10196 }
10197 }
10198
10199 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10200 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10201
10202 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10203 self.start_git_blame(true, cx);
10204 }
10205
10206 cx.notify();
10207 }
10208
10209 pub fn toggle_git_blame_inline(
10210 &mut self,
10211 _: &ToggleGitBlameInline,
10212 cx: &mut ViewContext<Self>,
10213 ) {
10214 self.toggle_git_blame_inline_internal(true, cx);
10215 cx.notify();
10216 }
10217
10218 pub fn git_blame_inline_enabled(&self) -> bool {
10219 self.git_blame_inline_enabled
10220 }
10221
10222 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10223 self.show_selection_menu = self
10224 .show_selection_menu
10225 .map(|show_selections_menu| !show_selections_menu)
10226 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10227
10228 cx.notify();
10229 }
10230
10231 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10232 self.show_selection_menu
10233 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10234 }
10235
10236 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10237 if let Some(project) = self.project.as_ref() {
10238 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10239 return;
10240 };
10241
10242 if buffer.read(cx).file().is_none() {
10243 return;
10244 }
10245
10246 let focused = self.focus_handle(cx).contains_focused(cx);
10247
10248 let project = project.clone();
10249 let blame =
10250 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10251 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10252 self.blame = Some(blame);
10253 }
10254 }
10255
10256 fn toggle_git_blame_inline_internal(
10257 &mut self,
10258 user_triggered: bool,
10259 cx: &mut ViewContext<Self>,
10260 ) {
10261 if self.git_blame_inline_enabled {
10262 self.git_blame_inline_enabled = false;
10263 self.show_git_blame_inline = false;
10264 self.show_git_blame_inline_delay_task.take();
10265 } else {
10266 self.git_blame_inline_enabled = true;
10267 self.start_git_blame_inline(user_triggered, cx);
10268 }
10269
10270 cx.notify();
10271 }
10272
10273 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10274 self.start_git_blame(user_triggered, cx);
10275
10276 if ProjectSettings::get_global(cx)
10277 .git
10278 .inline_blame_delay()
10279 .is_some()
10280 {
10281 self.start_inline_blame_timer(cx);
10282 } else {
10283 self.show_git_blame_inline = true
10284 }
10285 }
10286
10287 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10288 self.blame.as_ref()
10289 }
10290
10291 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10292 self.show_git_blame_gutter && self.has_blame_entries(cx)
10293 }
10294
10295 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10296 self.show_git_blame_inline
10297 && self.focus_handle.is_focused(cx)
10298 && !self.newest_selection_head_on_empty_line(cx)
10299 && self.has_blame_entries(cx)
10300 }
10301
10302 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10303 self.blame()
10304 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10305 }
10306
10307 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10308 let cursor_anchor = self.selections.newest_anchor().head();
10309
10310 let snapshot = self.buffer.read(cx).snapshot(cx);
10311 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10312
10313 snapshot.line_len(buffer_row) == 0
10314 }
10315
10316 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10317 let (path, selection, repo) = maybe!({
10318 let project_handle = self.project.as_ref()?.clone();
10319 let project = project_handle.read(cx);
10320
10321 let selection = self.selections.newest::<Point>(cx);
10322 let selection_range = selection.range();
10323
10324 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10325 (buffer, selection_range.start.row..selection_range.end.row)
10326 } else {
10327 let buffer_ranges = self
10328 .buffer()
10329 .read(cx)
10330 .range_to_buffer_ranges(selection_range, cx);
10331
10332 let (buffer, range, _) = if selection.reversed {
10333 buffer_ranges.first()
10334 } else {
10335 buffer_ranges.last()
10336 }?;
10337
10338 let snapshot = buffer.read(cx).snapshot();
10339 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10340 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10341 (buffer.clone(), selection)
10342 };
10343
10344 let path = buffer
10345 .read(cx)
10346 .file()?
10347 .as_local()?
10348 .path()
10349 .to_str()?
10350 .to_string();
10351 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10352 Some((path, selection, repo))
10353 })
10354 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10355
10356 const REMOTE_NAME: &str = "origin";
10357 let origin_url = repo
10358 .remote_url(REMOTE_NAME)
10359 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10360 let sha = repo
10361 .head_sha()
10362 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10363
10364 let (provider, remote) =
10365 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10366 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10367
10368 Ok(provider.build_permalink(
10369 remote,
10370 BuildPermalinkParams {
10371 sha: &sha,
10372 path: &path,
10373 selection: Some(selection),
10374 },
10375 ))
10376 }
10377
10378 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10379 let permalink = self.get_permalink_to_line(cx);
10380
10381 match permalink {
10382 Ok(permalink) => {
10383 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10384 }
10385 Err(err) => {
10386 let message = format!("Failed to copy permalink: {err}");
10387
10388 Err::<(), anyhow::Error>(err).log_err();
10389
10390 if let Some(workspace) = self.workspace() {
10391 workspace.update(cx, |workspace, cx| {
10392 struct CopyPermalinkToLine;
10393
10394 workspace.show_toast(
10395 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10396 cx,
10397 )
10398 })
10399 }
10400 }
10401 }
10402 }
10403
10404 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10405 let permalink = self.get_permalink_to_line(cx);
10406
10407 match permalink {
10408 Ok(permalink) => {
10409 cx.open_url(permalink.as_ref());
10410 }
10411 Err(err) => {
10412 let message = format!("Failed to open permalink: {err}");
10413
10414 Err::<(), anyhow::Error>(err).log_err();
10415
10416 if let Some(workspace) = self.workspace() {
10417 workspace.update(cx, |workspace, cx| {
10418 struct OpenPermalinkToLine;
10419
10420 workspace.show_toast(
10421 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10422 cx,
10423 )
10424 })
10425 }
10426 }
10427 }
10428 }
10429
10430 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10431 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10432 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10433 pub fn highlight_rows<T: 'static>(
10434 &mut self,
10435 rows: RangeInclusive<Anchor>,
10436 color: Option<Hsla>,
10437 should_autoscroll: bool,
10438 cx: &mut ViewContext<Self>,
10439 ) {
10440 let snapshot = self.buffer().read(cx).snapshot(cx);
10441 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10442 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10443 highlight
10444 .range
10445 .start()
10446 .cmp(&rows.start(), &snapshot)
10447 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10448 });
10449 match (color, existing_highlight_index) {
10450 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10451 ix,
10452 RowHighlight {
10453 index: post_inc(&mut self.highlight_order),
10454 range: rows,
10455 should_autoscroll,
10456 color,
10457 },
10458 ),
10459 (None, Ok(i)) => {
10460 row_highlights.remove(i);
10461 }
10462 }
10463 }
10464
10465 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10466 pub fn clear_row_highlights<T: 'static>(&mut self) {
10467 self.highlighted_rows.remove(&TypeId::of::<T>());
10468 }
10469
10470 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10471 pub fn highlighted_rows<T: 'static>(
10472 &self,
10473 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10474 Some(
10475 self.highlighted_rows
10476 .get(&TypeId::of::<T>())?
10477 .iter()
10478 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10479 )
10480 }
10481
10482 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10483 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10484 /// Allows to ignore certain kinds of highlights.
10485 pub fn highlighted_display_rows(
10486 &mut self,
10487 cx: &mut WindowContext,
10488 ) -> BTreeMap<DisplayRow, Hsla> {
10489 let snapshot = self.snapshot(cx);
10490 let mut used_highlight_orders = HashMap::default();
10491 self.highlighted_rows
10492 .iter()
10493 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10494 .fold(
10495 BTreeMap::<DisplayRow, Hsla>::new(),
10496 |mut unique_rows, highlight| {
10497 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10498 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10499 for row in start_row.0..=end_row.0 {
10500 let used_index =
10501 used_highlight_orders.entry(row).or_insert(highlight.index);
10502 if highlight.index >= *used_index {
10503 *used_index = highlight.index;
10504 match highlight.color {
10505 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10506 None => unique_rows.remove(&DisplayRow(row)),
10507 };
10508 }
10509 }
10510 unique_rows
10511 },
10512 )
10513 }
10514
10515 pub fn highlighted_display_row_for_autoscroll(
10516 &self,
10517 snapshot: &DisplaySnapshot,
10518 ) -> Option<DisplayRow> {
10519 self.highlighted_rows
10520 .values()
10521 .flat_map(|highlighted_rows| highlighted_rows.iter())
10522 .filter_map(|highlight| {
10523 if highlight.color.is_none() || !highlight.should_autoscroll {
10524 return None;
10525 }
10526 Some(highlight.range.start().to_display_point(&snapshot).row())
10527 })
10528 .min()
10529 }
10530
10531 pub fn set_search_within_ranges(
10532 &mut self,
10533 ranges: &[Range<Anchor>],
10534 cx: &mut ViewContext<Self>,
10535 ) {
10536 self.highlight_background::<SearchWithinRange>(
10537 ranges,
10538 |colors| colors.editor_document_highlight_read_background,
10539 cx,
10540 )
10541 }
10542
10543 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10544 self.breadcrumb_header = Some(new_header);
10545 }
10546
10547 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10548 self.clear_background_highlights::<SearchWithinRange>(cx);
10549 }
10550
10551 pub fn highlight_background<T: 'static>(
10552 &mut self,
10553 ranges: &[Range<Anchor>],
10554 color_fetcher: fn(&ThemeColors) -> Hsla,
10555 cx: &mut ViewContext<Self>,
10556 ) {
10557 let snapshot = self.snapshot(cx);
10558 // this is to try and catch a panic sooner
10559 for range in ranges {
10560 snapshot
10561 .buffer_snapshot
10562 .summary_for_anchor::<usize>(&range.start);
10563 snapshot
10564 .buffer_snapshot
10565 .summary_for_anchor::<usize>(&range.end);
10566 }
10567
10568 self.background_highlights
10569 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10570 self.scrollbar_marker_state.dirty = true;
10571 cx.notify();
10572 }
10573
10574 pub fn clear_background_highlights<T: 'static>(
10575 &mut self,
10576 cx: &mut ViewContext<Self>,
10577 ) -> Option<BackgroundHighlight> {
10578 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10579 if !text_highlights.1.is_empty() {
10580 self.scrollbar_marker_state.dirty = true;
10581 cx.notify();
10582 }
10583 Some(text_highlights)
10584 }
10585
10586 pub fn highlight_gutter<T: 'static>(
10587 &mut self,
10588 ranges: &[Range<Anchor>],
10589 color_fetcher: fn(&AppContext) -> Hsla,
10590 cx: &mut ViewContext<Self>,
10591 ) {
10592 self.gutter_highlights
10593 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10594 cx.notify();
10595 }
10596
10597 pub fn clear_gutter_highlights<T: 'static>(
10598 &mut self,
10599 cx: &mut ViewContext<Self>,
10600 ) -> Option<GutterHighlight> {
10601 cx.notify();
10602 self.gutter_highlights.remove(&TypeId::of::<T>())
10603 }
10604
10605 #[cfg(feature = "test-support")]
10606 pub fn all_text_background_highlights(
10607 &mut self,
10608 cx: &mut ViewContext<Self>,
10609 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10610 let snapshot = self.snapshot(cx);
10611 let buffer = &snapshot.buffer_snapshot;
10612 let start = buffer.anchor_before(0);
10613 let end = buffer.anchor_after(buffer.len());
10614 let theme = cx.theme().colors();
10615 self.background_highlights_in_range(start..end, &snapshot, theme)
10616 }
10617
10618 #[cfg(feature = "test-support")]
10619 pub fn search_background_highlights(
10620 &mut self,
10621 cx: &mut ViewContext<Self>,
10622 ) -> Vec<Range<Point>> {
10623 let snapshot = self.buffer().read(cx).snapshot(cx);
10624
10625 let highlights = self
10626 .background_highlights
10627 .get(&TypeId::of::<items::BufferSearchHighlights>());
10628
10629 if let Some((_color, ranges)) = highlights {
10630 ranges
10631 .iter()
10632 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10633 .collect_vec()
10634 } else {
10635 vec![]
10636 }
10637 }
10638
10639 fn document_highlights_for_position<'a>(
10640 &'a self,
10641 position: Anchor,
10642 buffer: &'a MultiBufferSnapshot,
10643 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10644 let read_highlights = self
10645 .background_highlights
10646 .get(&TypeId::of::<DocumentHighlightRead>())
10647 .map(|h| &h.1);
10648 let write_highlights = self
10649 .background_highlights
10650 .get(&TypeId::of::<DocumentHighlightWrite>())
10651 .map(|h| &h.1);
10652 let left_position = position.bias_left(buffer);
10653 let right_position = position.bias_right(buffer);
10654 read_highlights
10655 .into_iter()
10656 .chain(write_highlights)
10657 .flat_map(move |ranges| {
10658 let start_ix = match ranges.binary_search_by(|probe| {
10659 let cmp = probe.end.cmp(&left_position, buffer);
10660 if cmp.is_ge() {
10661 Ordering::Greater
10662 } else {
10663 Ordering::Less
10664 }
10665 }) {
10666 Ok(i) | Err(i) => i,
10667 };
10668
10669 ranges[start_ix..]
10670 .iter()
10671 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
10672 })
10673 }
10674
10675 pub fn has_background_highlights<T: 'static>(&self) -> bool {
10676 self.background_highlights
10677 .get(&TypeId::of::<T>())
10678 .map_or(false, |(_, highlights)| !highlights.is_empty())
10679 }
10680
10681 pub fn background_highlights_in_range(
10682 &self,
10683 search_range: Range<Anchor>,
10684 display_snapshot: &DisplaySnapshot,
10685 theme: &ThemeColors,
10686 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10687 let mut results = Vec::new();
10688 for (color_fetcher, ranges) in self.background_highlights.values() {
10689 let color = color_fetcher(theme);
10690 let start_ix = match ranges.binary_search_by(|probe| {
10691 let cmp = probe
10692 .end
10693 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10694 if cmp.is_gt() {
10695 Ordering::Greater
10696 } else {
10697 Ordering::Less
10698 }
10699 }) {
10700 Ok(i) | Err(i) => i,
10701 };
10702 for range in &ranges[start_ix..] {
10703 if range
10704 .start
10705 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10706 .is_ge()
10707 {
10708 break;
10709 }
10710
10711 let start = range.start.to_display_point(&display_snapshot);
10712 let end = range.end.to_display_point(&display_snapshot);
10713 results.push((start..end, color))
10714 }
10715 }
10716 results
10717 }
10718
10719 pub fn background_highlight_row_ranges<T: 'static>(
10720 &self,
10721 search_range: Range<Anchor>,
10722 display_snapshot: &DisplaySnapshot,
10723 count: usize,
10724 ) -> Vec<RangeInclusive<DisplayPoint>> {
10725 let mut results = Vec::new();
10726 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
10727 return vec![];
10728 };
10729
10730 let start_ix = match ranges.binary_search_by(|probe| {
10731 let cmp = probe
10732 .end
10733 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10734 if cmp.is_gt() {
10735 Ordering::Greater
10736 } else {
10737 Ordering::Less
10738 }
10739 }) {
10740 Ok(i) | Err(i) => i,
10741 };
10742 let mut push_region = |start: Option<Point>, end: Option<Point>| {
10743 if let (Some(start_display), Some(end_display)) = (start, end) {
10744 results.push(
10745 start_display.to_display_point(display_snapshot)
10746 ..=end_display.to_display_point(display_snapshot),
10747 );
10748 }
10749 };
10750 let mut start_row: Option<Point> = None;
10751 let mut end_row: Option<Point> = None;
10752 if ranges.len() > count {
10753 return Vec::new();
10754 }
10755 for range in &ranges[start_ix..] {
10756 if range
10757 .start
10758 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10759 .is_ge()
10760 {
10761 break;
10762 }
10763 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
10764 if let Some(current_row) = &end_row {
10765 if end.row == current_row.row {
10766 continue;
10767 }
10768 }
10769 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
10770 if start_row.is_none() {
10771 assert_eq!(end_row, None);
10772 start_row = Some(start);
10773 end_row = Some(end);
10774 continue;
10775 }
10776 if let Some(current_end) = end_row.as_mut() {
10777 if start.row > current_end.row + 1 {
10778 push_region(start_row, end_row);
10779 start_row = Some(start);
10780 end_row = Some(end);
10781 } else {
10782 // Merge two hunks.
10783 *current_end = end;
10784 }
10785 } else {
10786 unreachable!();
10787 }
10788 }
10789 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
10790 push_region(start_row, end_row);
10791 results
10792 }
10793
10794 pub fn gutter_highlights_in_range(
10795 &self,
10796 search_range: Range<Anchor>,
10797 display_snapshot: &DisplaySnapshot,
10798 cx: &AppContext,
10799 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10800 let mut results = Vec::new();
10801 for (color_fetcher, ranges) in self.gutter_highlights.values() {
10802 let color = color_fetcher(cx);
10803 let start_ix = match ranges.binary_search_by(|probe| {
10804 let cmp = probe
10805 .end
10806 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10807 if cmp.is_gt() {
10808 Ordering::Greater
10809 } else {
10810 Ordering::Less
10811 }
10812 }) {
10813 Ok(i) | Err(i) => i,
10814 };
10815 for range in &ranges[start_ix..] {
10816 if range
10817 .start
10818 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10819 .is_ge()
10820 {
10821 break;
10822 }
10823
10824 let start = range.start.to_display_point(&display_snapshot);
10825 let end = range.end.to_display_point(&display_snapshot);
10826 results.push((start..end, color))
10827 }
10828 }
10829 results
10830 }
10831
10832 /// Get the text ranges corresponding to the redaction query
10833 pub fn redacted_ranges(
10834 &self,
10835 search_range: Range<Anchor>,
10836 display_snapshot: &DisplaySnapshot,
10837 cx: &WindowContext,
10838 ) -> Vec<Range<DisplayPoint>> {
10839 display_snapshot
10840 .buffer_snapshot
10841 .redacted_ranges(search_range, |file| {
10842 if let Some(file) = file {
10843 file.is_private()
10844 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
10845 } else {
10846 false
10847 }
10848 })
10849 .map(|range| {
10850 range.start.to_display_point(display_snapshot)
10851 ..range.end.to_display_point(display_snapshot)
10852 })
10853 .collect()
10854 }
10855
10856 pub fn highlight_text<T: 'static>(
10857 &mut self,
10858 ranges: Vec<Range<Anchor>>,
10859 style: HighlightStyle,
10860 cx: &mut ViewContext<Self>,
10861 ) {
10862 self.display_map.update(cx, |map, _| {
10863 map.highlight_text(TypeId::of::<T>(), ranges, style)
10864 });
10865 cx.notify();
10866 }
10867
10868 pub(crate) fn highlight_inlays<T: 'static>(
10869 &mut self,
10870 highlights: Vec<InlayHighlight>,
10871 style: HighlightStyle,
10872 cx: &mut ViewContext<Self>,
10873 ) {
10874 self.display_map.update(cx, |map, _| {
10875 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
10876 });
10877 cx.notify();
10878 }
10879
10880 pub fn text_highlights<'a, T: 'static>(
10881 &'a self,
10882 cx: &'a AppContext,
10883 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
10884 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
10885 }
10886
10887 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
10888 let cleared = self
10889 .display_map
10890 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
10891 if cleared {
10892 cx.notify();
10893 }
10894 }
10895
10896 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
10897 (self.read_only(cx) || self.blink_manager.read(cx).visible())
10898 && self.focus_handle.is_focused(cx)
10899 }
10900
10901 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
10902 cx.notify();
10903 }
10904
10905 fn on_buffer_event(
10906 &mut self,
10907 multibuffer: Model<MultiBuffer>,
10908 event: &multi_buffer::Event,
10909 cx: &mut ViewContext<Self>,
10910 ) {
10911 match event {
10912 multi_buffer::Event::Edited {
10913 singleton_buffer_edited,
10914 } => {
10915 self.scrollbar_marker_state.dirty = true;
10916 self.active_indent_guides_state.dirty = true;
10917 self.refresh_active_diagnostics(cx);
10918 self.refresh_code_actions(cx);
10919 if self.has_active_inline_completion(cx) {
10920 self.update_visible_inline_completion(cx);
10921 }
10922 cx.emit(EditorEvent::BufferEdited);
10923 cx.emit(SearchEvent::MatchesInvalidated);
10924 if *singleton_buffer_edited {
10925 if let Some(project) = &self.project {
10926 let project = project.read(cx);
10927 let languages_affected = multibuffer
10928 .read(cx)
10929 .all_buffers()
10930 .into_iter()
10931 .filter_map(|buffer| {
10932 let buffer = buffer.read(cx);
10933 let language = buffer.language()?;
10934 if project.is_local()
10935 && project.language_servers_for_buffer(buffer, cx).count() == 0
10936 {
10937 None
10938 } else {
10939 Some(language)
10940 }
10941 })
10942 .cloned()
10943 .collect::<HashSet<_>>();
10944 if !languages_affected.is_empty() {
10945 self.refresh_inlay_hints(
10946 InlayHintRefreshReason::BufferEdited(languages_affected),
10947 cx,
10948 );
10949 }
10950 }
10951 }
10952
10953 let Some(project) = &self.project else { return };
10954 let telemetry = project.read(cx).client().telemetry().clone();
10955 refresh_linked_ranges(self, cx);
10956 telemetry.log_edit_event("editor");
10957 }
10958 multi_buffer::Event::ExcerptsAdded {
10959 buffer,
10960 predecessor,
10961 excerpts,
10962 } => {
10963 self.tasks_update_task = Some(self.refresh_runnables(cx));
10964 cx.emit(EditorEvent::ExcerptsAdded {
10965 buffer: buffer.clone(),
10966 predecessor: *predecessor,
10967 excerpts: excerpts.clone(),
10968 });
10969 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
10970 }
10971 multi_buffer::Event::ExcerptsRemoved { ids } => {
10972 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
10973 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
10974 }
10975 multi_buffer::Event::ExcerptsEdited { ids } => {
10976 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
10977 }
10978 multi_buffer::Event::ExcerptsExpanded { ids } => {
10979 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
10980 }
10981 multi_buffer::Event::Reparsed(buffer_id) => {
10982 self.tasks_update_task = Some(self.refresh_runnables(cx));
10983
10984 cx.emit(EditorEvent::Reparsed(*buffer_id));
10985 }
10986 multi_buffer::Event::LanguageChanged(buffer_id) => {
10987 linked_editing_ranges::refresh_linked_ranges(self, cx);
10988 cx.emit(EditorEvent::Reparsed(*buffer_id));
10989 cx.notify();
10990 }
10991 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
10992 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
10993 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
10994 cx.emit(EditorEvent::TitleChanged)
10995 }
10996 multi_buffer::Event::DiffBaseChanged => {
10997 self.scrollbar_marker_state.dirty = true;
10998 cx.emit(EditorEvent::DiffBaseChanged);
10999 cx.notify();
11000 }
11001 multi_buffer::Event::DiffUpdated { buffer } => {
11002 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11003 cx.notify();
11004 }
11005 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11006 multi_buffer::Event::DiagnosticsUpdated => {
11007 self.refresh_active_diagnostics(cx);
11008 self.scrollbar_marker_state.dirty = true;
11009 cx.notify();
11010 }
11011 _ => {}
11012 };
11013 }
11014
11015 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11016 cx.notify();
11017 }
11018
11019 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11020 self.refresh_inline_completion(true, cx);
11021 self.refresh_inlay_hints(
11022 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11023 self.selections.newest_anchor().head(),
11024 &self.buffer.read(cx).snapshot(cx),
11025 cx,
11026 )),
11027 cx,
11028 );
11029 let editor_settings = EditorSettings::get_global(cx);
11030 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11031 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11032
11033 if self.mode == EditorMode::Full {
11034 let inline_blame_enabled = ProjectSettings::get_global(cx).git.inline_blame_enabled();
11035 if self.git_blame_inline_enabled != inline_blame_enabled {
11036 self.toggle_git_blame_inline_internal(false, cx);
11037 }
11038 }
11039
11040 cx.notify();
11041 }
11042
11043 pub fn set_searchable(&mut self, searchable: bool) {
11044 self.searchable = searchable;
11045 }
11046
11047 pub fn searchable(&self) -> bool {
11048 self.searchable
11049 }
11050
11051 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11052 self.open_excerpts_common(true, cx)
11053 }
11054
11055 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11056 self.open_excerpts_common(false, cx)
11057 }
11058
11059 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11060 let buffer = self.buffer.read(cx);
11061 if buffer.is_singleton() {
11062 cx.propagate();
11063 return;
11064 }
11065
11066 let Some(workspace) = self.workspace() else {
11067 cx.propagate();
11068 return;
11069 };
11070
11071 let mut new_selections_by_buffer = HashMap::default();
11072 for selection in self.selections.all::<usize>(cx) {
11073 for (buffer, mut range, _) in
11074 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11075 {
11076 if selection.reversed {
11077 mem::swap(&mut range.start, &mut range.end);
11078 }
11079 new_selections_by_buffer
11080 .entry(buffer)
11081 .or_insert(Vec::new())
11082 .push(range)
11083 }
11084 }
11085
11086 // We defer the pane interaction because we ourselves are a workspace item
11087 // and activating a new item causes the pane to call a method on us reentrantly,
11088 // which panics if we're on the stack.
11089 cx.window_context().defer(move |cx| {
11090 workspace.update(cx, |workspace, cx| {
11091 let pane = if split {
11092 workspace.adjacent_pane(cx)
11093 } else {
11094 workspace.active_pane().clone()
11095 };
11096
11097 for (buffer, ranges) in new_selections_by_buffer {
11098 let editor = workspace.open_project_item::<Self>(pane.clone(), buffer, cx);
11099 editor.update(cx, |editor, cx| {
11100 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11101 s.select_ranges(ranges);
11102 });
11103 });
11104 }
11105 })
11106 });
11107 }
11108
11109 fn jump(
11110 &mut self,
11111 path: ProjectPath,
11112 position: Point,
11113 anchor: language::Anchor,
11114 offset_from_top: u32,
11115 cx: &mut ViewContext<Self>,
11116 ) {
11117 let workspace = self.workspace();
11118 cx.spawn(|_, mut cx| async move {
11119 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11120 let editor = workspace.update(&mut cx, |workspace, cx| {
11121 // Reset the preview item id before opening the new item
11122 workspace.active_pane().update(cx, |pane, cx| {
11123 pane.set_preview_item_id(None, cx);
11124 });
11125 workspace.open_path_preview(path, None, true, true, cx)
11126 })?;
11127 let editor = editor
11128 .await?
11129 .downcast::<Editor>()
11130 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11131 .downgrade();
11132 editor.update(&mut cx, |editor, cx| {
11133 let buffer = editor
11134 .buffer()
11135 .read(cx)
11136 .as_singleton()
11137 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11138 let buffer = buffer.read(cx);
11139 let cursor = if buffer.can_resolve(&anchor) {
11140 language::ToPoint::to_point(&anchor, buffer)
11141 } else {
11142 buffer.clip_point(position, Bias::Left)
11143 };
11144
11145 let nav_history = editor.nav_history.take();
11146 editor.change_selections(
11147 Some(Autoscroll::top_relative(offset_from_top as usize)),
11148 cx,
11149 |s| {
11150 s.select_ranges([cursor..cursor]);
11151 },
11152 );
11153 editor.nav_history = nav_history;
11154
11155 anyhow::Ok(())
11156 })??;
11157
11158 anyhow::Ok(())
11159 })
11160 .detach_and_log_err(cx);
11161 }
11162
11163 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11164 let snapshot = self.buffer.read(cx).read(cx);
11165 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11166 Some(
11167 ranges
11168 .iter()
11169 .map(move |range| {
11170 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11171 })
11172 .collect(),
11173 )
11174 }
11175
11176 fn selection_replacement_ranges(
11177 &self,
11178 range: Range<OffsetUtf16>,
11179 cx: &AppContext,
11180 ) -> Vec<Range<OffsetUtf16>> {
11181 let selections = self.selections.all::<OffsetUtf16>(cx);
11182 let newest_selection = selections
11183 .iter()
11184 .max_by_key(|selection| selection.id)
11185 .unwrap();
11186 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11187 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11188 let snapshot = self.buffer.read(cx).read(cx);
11189 selections
11190 .into_iter()
11191 .map(|mut selection| {
11192 selection.start.0 =
11193 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11194 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11195 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11196 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11197 })
11198 .collect()
11199 }
11200
11201 fn report_editor_event(
11202 &self,
11203 operation: &'static str,
11204 file_extension: Option<String>,
11205 cx: &AppContext,
11206 ) {
11207 if cfg!(any(test, feature = "test-support")) {
11208 return;
11209 }
11210
11211 let Some(project) = &self.project else { return };
11212
11213 // If None, we are in a file without an extension
11214 let file = self
11215 .buffer
11216 .read(cx)
11217 .as_singleton()
11218 .and_then(|b| b.read(cx).file());
11219 let file_extension = file_extension.or(file
11220 .as_ref()
11221 .and_then(|file| Path::new(file.file_name(cx)).extension())
11222 .and_then(|e| e.to_str())
11223 .map(|a| a.to_string()));
11224
11225 let vim_mode = cx
11226 .global::<SettingsStore>()
11227 .raw_user_settings()
11228 .get("vim_mode")
11229 == Some(&serde_json::Value::Bool(true));
11230
11231 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11232 == language::language_settings::InlineCompletionProvider::Copilot;
11233 let copilot_enabled_for_language = self
11234 .buffer
11235 .read(cx)
11236 .settings_at(0, cx)
11237 .show_inline_completions;
11238
11239 let telemetry = project.read(cx).client().telemetry().clone();
11240 telemetry.report_editor_event(
11241 file_extension,
11242 vim_mode,
11243 operation,
11244 copilot_enabled,
11245 copilot_enabled_for_language,
11246 )
11247 }
11248
11249 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11250 /// with each line being an array of {text, highlight} objects.
11251 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11252 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11253 return;
11254 };
11255
11256 #[derive(Serialize)]
11257 struct Chunk<'a> {
11258 text: String,
11259 highlight: Option<&'a str>,
11260 }
11261
11262 let snapshot = buffer.read(cx).snapshot();
11263 let range = self
11264 .selected_text_range(cx)
11265 .and_then(|selected_range| {
11266 if selected_range.is_empty() {
11267 None
11268 } else {
11269 Some(selected_range)
11270 }
11271 })
11272 .unwrap_or_else(|| 0..snapshot.len());
11273
11274 let chunks = snapshot.chunks(range, true);
11275 let mut lines = Vec::new();
11276 let mut line: VecDeque<Chunk> = VecDeque::new();
11277
11278 let Some(style) = self.style.as_ref() else {
11279 return;
11280 };
11281
11282 for chunk in chunks {
11283 let highlight = chunk
11284 .syntax_highlight_id
11285 .and_then(|id| id.name(&style.syntax));
11286 let mut chunk_lines = chunk.text.split('\n').peekable();
11287 while let Some(text) = chunk_lines.next() {
11288 let mut merged_with_last_token = false;
11289 if let Some(last_token) = line.back_mut() {
11290 if last_token.highlight == highlight {
11291 last_token.text.push_str(text);
11292 merged_with_last_token = true;
11293 }
11294 }
11295
11296 if !merged_with_last_token {
11297 line.push_back(Chunk {
11298 text: text.into(),
11299 highlight,
11300 });
11301 }
11302
11303 if chunk_lines.peek().is_some() {
11304 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11305 line.pop_front();
11306 }
11307 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11308 line.pop_back();
11309 }
11310
11311 lines.push(mem::take(&mut line));
11312 }
11313 }
11314 }
11315
11316 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11317 return;
11318 };
11319 cx.write_to_clipboard(ClipboardItem::new(lines));
11320 }
11321
11322 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11323 &self.inlay_hint_cache
11324 }
11325
11326 pub fn replay_insert_event(
11327 &mut self,
11328 text: &str,
11329 relative_utf16_range: Option<Range<isize>>,
11330 cx: &mut ViewContext<Self>,
11331 ) {
11332 if !self.input_enabled {
11333 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11334 return;
11335 }
11336 if let Some(relative_utf16_range) = relative_utf16_range {
11337 let selections = self.selections.all::<OffsetUtf16>(cx);
11338 self.change_selections(None, cx, |s| {
11339 let new_ranges = selections.into_iter().map(|range| {
11340 let start = OffsetUtf16(
11341 range
11342 .head()
11343 .0
11344 .saturating_add_signed(relative_utf16_range.start),
11345 );
11346 let end = OffsetUtf16(
11347 range
11348 .head()
11349 .0
11350 .saturating_add_signed(relative_utf16_range.end),
11351 );
11352 start..end
11353 });
11354 s.select_ranges(new_ranges);
11355 });
11356 }
11357
11358 self.handle_input(text, cx);
11359 }
11360
11361 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11362 let Some(project) = self.project.as_ref() else {
11363 return false;
11364 };
11365 let project = project.read(cx);
11366
11367 let mut supports = false;
11368 self.buffer().read(cx).for_each_buffer(|buffer| {
11369 if !supports {
11370 supports = project
11371 .language_servers_for_buffer(buffer.read(cx), cx)
11372 .any(
11373 |(_, server)| match server.capabilities().inlay_hint_provider {
11374 Some(lsp::OneOf::Left(enabled)) => enabled,
11375 Some(lsp::OneOf::Right(_)) => true,
11376 None => false,
11377 },
11378 )
11379 }
11380 });
11381 supports
11382 }
11383
11384 pub fn focus(&self, cx: &mut WindowContext) {
11385 cx.focus(&self.focus_handle)
11386 }
11387
11388 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11389 self.focus_handle.is_focused(cx)
11390 }
11391
11392 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11393 cx.emit(EditorEvent::Focused);
11394
11395 if let Some(descendant) = self
11396 .last_focused_descendant
11397 .take()
11398 .and_then(|descendant| descendant.upgrade())
11399 {
11400 cx.focus(&descendant);
11401 } else {
11402 if let Some(blame) = self.blame.as_ref() {
11403 blame.update(cx, GitBlame::focus)
11404 }
11405
11406 self.blink_manager.update(cx, BlinkManager::enable);
11407 self.show_cursor_names(cx);
11408 self.buffer.update(cx, |buffer, cx| {
11409 buffer.finalize_last_transaction(cx);
11410 if self.leader_peer_id.is_none() {
11411 buffer.set_active_selections(
11412 &self.selections.disjoint_anchors(),
11413 self.selections.line_mode,
11414 self.cursor_shape,
11415 cx,
11416 );
11417 }
11418 });
11419 }
11420 }
11421
11422 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11423 if event.blurred != self.focus_handle {
11424 self.last_focused_descendant = Some(event.blurred);
11425 }
11426 }
11427
11428 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11429 self.blink_manager.update(cx, BlinkManager::disable);
11430 self.buffer
11431 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11432
11433 if let Some(blame) = self.blame.as_ref() {
11434 blame.update(cx, GitBlame::blur)
11435 }
11436 self.hide_context_menu(cx);
11437 hide_hover(self, cx);
11438 cx.emit(EditorEvent::Blurred);
11439 cx.notify();
11440 }
11441
11442 pub fn register_action<A: Action>(
11443 &mut self,
11444 listener: impl Fn(&A, &mut WindowContext) + 'static,
11445 ) -> Subscription {
11446 let id = self.next_editor_action_id.post_inc();
11447 let listener = Arc::new(listener);
11448 self.editor_actions.borrow_mut().insert(
11449 id,
11450 Box::new(move |cx| {
11451 let _view = cx.view().clone();
11452 let cx = cx.window_context();
11453 let listener = listener.clone();
11454 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11455 let action = action.downcast_ref().unwrap();
11456 if phase == DispatchPhase::Bubble {
11457 listener(action, cx)
11458 }
11459 })
11460 }),
11461 );
11462
11463 let editor_actions = self.editor_actions.clone();
11464 Subscription::new(move || {
11465 editor_actions.borrow_mut().remove(&id);
11466 })
11467 }
11468
11469 pub fn file_header_size(&self) -> u8 {
11470 self.file_header_size
11471 }
11472}
11473
11474fn hunks_for_selections(
11475 multi_buffer_snapshot: &MultiBufferSnapshot,
11476 selections: &[Selection<Anchor>],
11477) -> Vec<DiffHunk<MultiBufferRow>> {
11478 let mut hunks = Vec::with_capacity(selections.len());
11479 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11480 HashMap::default();
11481 let buffer_rows_for_selections = selections.iter().map(|selection| {
11482 let head = selection.head();
11483 let tail = selection.tail();
11484 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11485 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11486 if start > end {
11487 end..start
11488 } else {
11489 start..end
11490 }
11491 });
11492
11493 for selected_multi_buffer_rows in buffer_rows_for_selections {
11494 let query_rows =
11495 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11496 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11497 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11498 // when the caret is just above or just below the deleted hunk.
11499 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11500 let related_to_selection = if allow_adjacent {
11501 hunk.associated_range.overlaps(&query_rows)
11502 || hunk.associated_range.start == query_rows.end
11503 || hunk.associated_range.end == query_rows.start
11504 } else {
11505 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11506 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11507 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11508 || selected_multi_buffer_rows.end == hunk.associated_range.start
11509 };
11510 if related_to_selection {
11511 if !processed_buffer_rows
11512 .entry(hunk.buffer_id)
11513 .or_default()
11514 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11515 {
11516 continue;
11517 }
11518 hunks.push(hunk);
11519 }
11520 }
11521 }
11522
11523 hunks
11524}
11525
11526pub trait CollaborationHub {
11527 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11528 fn user_participant_indices<'a>(
11529 &self,
11530 cx: &'a AppContext,
11531 ) -> &'a HashMap<u64, ParticipantIndex>;
11532 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11533}
11534
11535impl CollaborationHub for Model<Project> {
11536 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11537 self.read(cx).collaborators()
11538 }
11539
11540 fn user_participant_indices<'a>(
11541 &self,
11542 cx: &'a AppContext,
11543 ) -> &'a HashMap<u64, ParticipantIndex> {
11544 self.read(cx).user_store().read(cx).participant_indices()
11545 }
11546
11547 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11548 let this = self.read(cx);
11549 let user_ids = this.collaborators().values().map(|c| c.user_id);
11550 this.user_store().read_with(cx, |user_store, cx| {
11551 user_store.participant_names(user_ids, cx)
11552 })
11553 }
11554}
11555
11556pub trait CompletionProvider {
11557 fn completions(
11558 &self,
11559 buffer: &Model<Buffer>,
11560 buffer_position: text::Anchor,
11561 trigger: CompletionContext,
11562 cx: &mut ViewContext<Editor>,
11563 ) -> Task<Result<Vec<Completion>>>;
11564
11565 fn resolve_completions(
11566 &self,
11567 buffer: Model<Buffer>,
11568 completion_indices: Vec<usize>,
11569 completions: Arc<RwLock<Box<[Completion]>>>,
11570 cx: &mut ViewContext<Editor>,
11571 ) -> Task<Result<bool>>;
11572
11573 fn apply_additional_edits_for_completion(
11574 &self,
11575 buffer: Model<Buffer>,
11576 completion: Completion,
11577 push_to_history: bool,
11578 cx: &mut ViewContext<Editor>,
11579 ) -> Task<Result<Option<language::Transaction>>>;
11580
11581 fn is_completion_trigger(
11582 &self,
11583 buffer: &Model<Buffer>,
11584 position: language::Anchor,
11585 text: &str,
11586 trigger_in_words: bool,
11587 cx: &mut ViewContext<Editor>,
11588 ) -> bool;
11589}
11590
11591impl CompletionProvider for Model<Project> {
11592 fn completions(
11593 &self,
11594 buffer: &Model<Buffer>,
11595 buffer_position: text::Anchor,
11596 options: CompletionContext,
11597 cx: &mut ViewContext<Editor>,
11598 ) -> Task<Result<Vec<Completion>>> {
11599 self.update(cx, |project, cx| {
11600 project.completions(&buffer, buffer_position, options, cx)
11601 })
11602 }
11603
11604 fn resolve_completions(
11605 &self,
11606 buffer: Model<Buffer>,
11607 completion_indices: Vec<usize>,
11608 completions: Arc<RwLock<Box<[Completion]>>>,
11609 cx: &mut ViewContext<Editor>,
11610 ) -> Task<Result<bool>> {
11611 self.update(cx, |project, cx| {
11612 project.resolve_completions(buffer, completion_indices, completions, cx)
11613 })
11614 }
11615
11616 fn apply_additional_edits_for_completion(
11617 &self,
11618 buffer: Model<Buffer>,
11619 completion: Completion,
11620 push_to_history: bool,
11621 cx: &mut ViewContext<Editor>,
11622 ) -> Task<Result<Option<language::Transaction>>> {
11623 self.update(cx, |project, cx| {
11624 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
11625 })
11626 }
11627
11628 fn is_completion_trigger(
11629 &self,
11630 buffer: &Model<Buffer>,
11631 position: language::Anchor,
11632 text: &str,
11633 trigger_in_words: bool,
11634 cx: &mut ViewContext<Editor>,
11635 ) -> bool {
11636 if !EditorSettings::get_global(cx).show_completions_on_input {
11637 return false;
11638 }
11639
11640 let mut chars = text.chars();
11641 let char = if let Some(char) = chars.next() {
11642 char
11643 } else {
11644 return false;
11645 };
11646 if chars.next().is_some() {
11647 return false;
11648 }
11649
11650 let buffer = buffer.read(cx);
11651 let scope = buffer.snapshot().language_scope_at(position);
11652 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
11653 return true;
11654 }
11655
11656 buffer
11657 .completion_triggers()
11658 .iter()
11659 .any(|string| string == text)
11660 }
11661}
11662
11663fn inlay_hint_settings(
11664 location: Anchor,
11665 snapshot: &MultiBufferSnapshot,
11666 cx: &mut ViewContext<'_, Editor>,
11667) -> InlayHintSettings {
11668 let file = snapshot.file_at(location);
11669 let language = snapshot.language_at(location);
11670 let settings = all_language_settings(file, cx);
11671 settings
11672 .language(language.map(|l| l.name()).as_deref())
11673 .inlay_hints
11674}
11675
11676fn consume_contiguous_rows(
11677 contiguous_row_selections: &mut Vec<Selection<Point>>,
11678 selection: &Selection<Point>,
11679 display_map: &DisplaySnapshot,
11680 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
11681) -> (MultiBufferRow, MultiBufferRow) {
11682 contiguous_row_selections.push(selection.clone());
11683 let start_row = MultiBufferRow(selection.start.row);
11684 let mut end_row = ending_row(selection, display_map);
11685
11686 while let Some(next_selection) = selections.peek() {
11687 if next_selection.start.row <= end_row.0 {
11688 end_row = ending_row(next_selection, display_map);
11689 contiguous_row_selections.push(selections.next().unwrap().clone());
11690 } else {
11691 break;
11692 }
11693 }
11694 (start_row, end_row)
11695}
11696
11697fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
11698 if next_selection.end.column > 0 || next_selection.is_empty() {
11699 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
11700 } else {
11701 MultiBufferRow(next_selection.end.row)
11702 }
11703}
11704
11705impl EditorSnapshot {
11706 pub fn remote_selections_in_range<'a>(
11707 &'a self,
11708 range: &'a Range<Anchor>,
11709 collaboration_hub: &dyn CollaborationHub,
11710 cx: &'a AppContext,
11711 ) -> impl 'a + Iterator<Item = RemoteSelection> {
11712 let participant_names = collaboration_hub.user_names(cx);
11713 let participant_indices = collaboration_hub.user_participant_indices(cx);
11714 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
11715 let collaborators_by_replica_id = collaborators_by_peer_id
11716 .iter()
11717 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
11718 .collect::<HashMap<_, _>>();
11719 self.buffer_snapshot
11720 .remote_selections_in_range(range)
11721 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
11722 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
11723 let participant_index = participant_indices.get(&collaborator.user_id).copied();
11724 let user_name = participant_names.get(&collaborator.user_id).cloned();
11725 Some(RemoteSelection {
11726 replica_id,
11727 selection,
11728 cursor_shape,
11729 line_mode,
11730 participant_index,
11731 peer_id: collaborator.peer_id,
11732 user_name,
11733 })
11734 })
11735 }
11736
11737 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
11738 self.display_snapshot.buffer_snapshot.language_at(position)
11739 }
11740
11741 pub fn is_focused(&self) -> bool {
11742 self.is_focused
11743 }
11744
11745 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
11746 self.placeholder_text.as_ref()
11747 }
11748
11749 pub fn scroll_position(&self) -> gpui::Point<f32> {
11750 self.scroll_anchor.scroll_position(&self.display_snapshot)
11751 }
11752
11753 pub fn gutter_dimensions(
11754 &self,
11755 font_id: FontId,
11756 font_size: Pixels,
11757 em_width: Pixels,
11758 max_line_number_width: Pixels,
11759 cx: &AppContext,
11760 ) -> GutterDimensions {
11761 if !self.show_gutter {
11762 return GutterDimensions::default();
11763 }
11764 let descent = cx.text_system().descent(font_id, font_size);
11765
11766 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
11767 matches!(
11768 ProjectSettings::get_global(cx).git.git_gutter,
11769 Some(GitGutterSetting::TrackedFiles)
11770 )
11771 });
11772 let gutter_settings = EditorSettings::get_global(cx).gutter;
11773 let show_line_numbers = self
11774 .show_line_numbers
11775 .unwrap_or_else(|| gutter_settings.line_numbers);
11776 let line_gutter_width = if show_line_numbers {
11777 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
11778 let min_width_for_number_on_gutter = em_width * 4.0;
11779 max_line_number_width.max(min_width_for_number_on_gutter)
11780 } else {
11781 0.0.into()
11782 };
11783
11784 let show_code_actions = self
11785 .show_code_actions
11786 .unwrap_or_else(|| gutter_settings.code_actions);
11787
11788 let git_blame_entries_width = self
11789 .render_git_blame_gutter
11790 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
11791
11792 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
11793 left_padding += if show_code_actions {
11794 em_width * 3.0
11795 } else if show_git_gutter && show_line_numbers {
11796 em_width * 2.0
11797 } else if show_git_gutter || show_line_numbers {
11798 em_width
11799 } else {
11800 px(0.)
11801 };
11802
11803 let right_padding = if gutter_settings.folds && show_line_numbers {
11804 em_width * 4.0
11805 } else if gutter_settings.folds {
11806 em_width * 3.0
11807 } else if show_line_numbers {
11808 em_width
11809 } else {
11810 px(0.)
11811 };
11812
11813 GutterDimensions {
11814 left_padding,
11815 right_padding,
11816 width: line_gutter_width + left_padding + right_padding,
11817 margin: -descent,
11818 git_blame_entries_width,
11819 }
11820 }
11821
11822 pub fn render_fold_toggle(
11823 &self,
11824 buffer_row: MultiBufferRow,
11825 row_contains_cursor: bool,
11826 editor: View<Editor>,
11827 cx: &mut WindowContext,
11828 ) -> Option<AnyElement> {
11829 let folded = self.is_line_folded(buffer_row);
11830
11831 if let Some(crease) = self
11832 .crease_snapshot
11833 .query_row(buffer_row, &self.buffer_snapshot)
11834 {
11835 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
11836 if folded {
11837 editor.update(cx, |editor, cx| {
11838 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
11839 });
11840 } else {
11841 editor.update(cx, |editor, cx| {
11842 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
11843 });
11844 }
11845 });
11846
11847 Some((crease.render_toggle)(
11848 buffer_row,
11849 folded,
11850 toggle_callback,
11851 cx,
11852 ))
11853 } else if folded
11854 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
11855 {
11856 Some(
11857 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
11858 .selected(folded)
11859 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
11860 if folded {
11861 this.unfold_at(&UnfoldAt { buffer_row }, cx);
11862 } else {
11863 this.fold_at(&FoldAt { buffer_row }, cx);
11864 }
11865 }))
11866 .into_any_element(),
11867 )
11868 } else {
11869 None
11870 }
11871 }
11872
11873 pub fn render_crease_trailer(
11874 &self,
11875 buffer_row: MultiBufferRow,
11876 cx: &mut WindowContext,
11877 ) -> Option<AnyElement> {
11878 let folded = self.is_line_folded(buffer_row);
11879 let crease = self
11880 .crease_snapshot
11881 .query_row(buffer_row, &self.buffer_snapshot)?;
11882 Some((crease.render_trailer)(buffer_row, folded, cx))
11883 }
11884}
11885
11886impl Deref for EditorSnapshot {
11887 type Target = DisplaySnapshot;
11888
11889 fn deref(&self) -> &Self::Target {
11890 &self.display_snapshot
11891 }
11892}
11893
11894#[derive(Clone, Debug, PartialEq, Eq)]
11895pub enum EditorEvent {
11896 InputIgnored {
11897 text: Arc<str>,
11898 },
11899 InputHandled {
11900 utf16_range_to_replace: Option<Range<isize>>,
11901 text: Arc<str>,
11902 },
11903 ExcerptsAdded {
11904 buffer: Model<Buffer>,
11905 predecessor: ExcerptId,
11906 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
11907 },
11908 ExcerptsRemoved {
11909 ids: Vec<ExcerptId>,
11910 },
11911 ExcerptsEdited {
11912 ids: Vec<ExcerptId>,
11913 },
11914 ExcerptsExpanded {
11915 ids: Vec<ExcerptId>,
11916 },
11917 BufferEdited,
11918 Edited {
11919 transaction_id: clock::Lamport,
11920 },
11921 Reparsed(BufferId),
11922 Focused,
11923 Blurred,
11924 DirtyChanged,
11925 Saved,
11926 TitleChanged,
11927 DiffBaseChanged,
11928 SelectionsChanged {
11929 local: bool,
11930 },
11931 ScrollPositionChanged {
11932 local: bool,
11933 autoscroll: bool,
11934 },
11935 Closed,
11936 TransactionUndone {
11937 transaction_id: clock::Lamport,
11938 },
11939 TransactionBegun {
11940 transaction_id: clock::Lamport,
11941 },
11942}
11943
11944impl EventEmitter<EditorEvent> for Editor {}
11945
11946impl FocusableView for Editor {
11947 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
11948 self.focus_handle.clone()
11949 }
11950}
11951
11952impl Render for Editor {
11953 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
11954 let settings = ThemeSettings::get_global(cx);
11955
11956 let text_style = match self.mode {
11957 EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
11958 color: cx.theme().colors().editor_foreground,
11959 font_family: settings.ui_font.family.clone(),
11960 font_features: settings.ui_font.features.clone(),
11961 font_size: rems(0.875).into(),
11962 font_weight: settings.ui_font.weight,
11963 font_style: FontStyle::Normal,
11964 line_height: relative(settings.buffer_line_height.value()),
11965 background_color: None,
11966 underline: None,
11967 strikethrough: None,
11968 white_space: WhiteSpace::Normal,
11969 },
11970 EditorMode::Full => TextStyle {
11971 color: cx.theme().colors().editor_foreground,
11972 font_family: settings.buffer_font.family.clone(),
11973 font_features: settings.buffer_font.features.clone(),
11974 font_size: settings.buffer_font_size(cx).into(),
11975 font_weight: settings.buffer_font.weight,
11976 font_style: FontStyle::Normal,
11977 line_height: relative(settings.buffer_line_height.value()),
11978 background_color: None,
11979 underline: None,
11980 strikethrough: None,
11981 white_space: WhiteSpace::Normal,
11982 },
11983 };
11984
11985 let background = match self.mode {
11986 EditorMode::SingleLine => cx.theme().system().transparent,
11987 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
11988 EditorMode::Full => cx.theme().colors().editor_background,
11989 };
11990
11991 EditorElement::new(
11992 cx.view(),
11993 EditorStyle {
11994 background,
11995 local_player: cx.theme().players().local(),
11996 text: text_style,
11997 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
11998 syntax: cx.theme().syntax().clone(),
11999 status: cx.theme().status().clone(),
12000 inlay_hints_style: HighlightStyle {
12001 color: Some(cx.theme().status().hint),
12002 ..HighlightStyle::default()
12003 },
12004 suggestions_style: HighlightStyle {
12005 color: Some(cx.theme().status().predictive),
12006 ..HighlightStyle::default()
12007 },
12008 },
12009 )
12010 }
12011}
12012
12013impl ViewInputHandler for Editor {
12014 fn text_for_range(
12015 &mut self,
12016 range_utf16: Range<usize>,
12017 cx: &mut ViewContext<Self>,
12018 ) -> Option<String> {
12019 Some(
12020 self.buffer
12021 .read(cx)
12022 .read(cx)
12023 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12024 .collect(),
12025 )
12026 }
12027
12028 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12029 // Prevent the IME menu from appearing when holding down an alphabetic key
12030 // while input is disabled.
12031 if !self.input_enabled {
12032 return None;
12033 }
12034
12035 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12036 Some(range.start.0..range.end.0)
12037 }
12038
12039 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12040 let snapshot = self.buffer.read(cx).read(cx);
12041 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12042 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12043 }
12044
12045 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12046 self.clear_highlights::<InputComposition>(cx);
12047 self.ime_transaction.take();
12048 }
12049
12050 fn replace_text_in_range(
12051 &mut self,
12052 range_utf16: Option<Range<usize>>,
12053 text: &str,
12054 cx: &mut ViewContext<Self>,
12055 ) {
12056 if !self.input_enabled {
12057 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12058 return;
12059 }
12060
12061 self.transact(cx, |this, cx| {
12062 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12063 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12064 Some(this.selection_replacement_ranges(range_utf16, cx))
12065 } else {
12066 this.marked_text_ranges(cx)
12067 };
12068
12069 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12070 let newest_selection_id = this.selections.newest_anchor().id;
12071 this.selections
12072 .all::<OffsetUtf16>(cx)
12073 .iter()
12074 .zip(ranges_to_replace.iter())
12075 .find_map(|(selection, range)| {
12076 if selection.id == newest_selection_id {
12077 Some(
12078 (range.start.0 as isize - selection.head().0 as isize)
12079 ..(range.end.0 as isize - selection.head().0 as isize),
12080 )
12081 } else {
12082 None
12083 }
12084 })
12085 });
12086
12087 cx.emit(EditorEvent::InputHandled {
12088 utf16_range_to_replace: range_to_replace,
12089 text: text.into(),
12090 });
12091
12092 if let Some(new_selected_ranges) = new_selected_ranges {
12093 this.change_selections(None, cx, |selections| {
12094 selections.select_ranges(new_selected_ranges)
12095 });
12096 this.backspace(&Default::default(), cx);
12097 }
12098
12099 this.handle_input(text, cx);
12100 });
12101
12102 if let Some(transaction) = self.ime_transaction {
12103 self.buffer.update(cx, |buffer, cx| {
12104 buffer.group_until_transaction(transaction, cx);
12105 });
12106 }
12107
12108 self.unmark_text(cx);
12109 }
12110
12111 fn replace_and_mark_text_in_range(
12112 &mut self,
12113 range_utf16: Option<Range<usize>>,
12114 text: &str,
12115 new_selected_range_utf16: Option<Range<usize>>,
12116 cx: &mut ViewContext<Self>,
12117 ) {
12118 if !self.input_enabled {
12119 return;
12120 }
12121
12122 let transaction = self.transact(cx, |this, cx| {
12123 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12124 let snapshot = this.buffer.read(cx).read(cx);
12125 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12126 for marked_range in &mut marked_ranges {
12127 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12128 marked_range.start.0 += relative_range_utf16.start;
12129 marked_range.start =
12130 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12131 marked_range.end =
12132 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12133 }
12134 }
12135 Some(marked_ranges)
12136 } else if let Some(range_utf16) = range_utf16 {
12137 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12138 Some(this.selection_replacement_ranges(range_utf16, cx))
12139 } else {
12140 None
12141 };
12142
12143 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12144 let newest_selection_id = this.selections.newest_anchor().id;
12145 this.selections
12146 .all::<OffsetUtf16>(cx)
12147 .iter()
12148 .zip(ranges_to_replace.iter())
12149 .find_map(|(selection, range)| {
12150 if selection.id == newest_selection_id {
12151 Some(
12152 (range.start.0 as isize - selection.head().0 as isize)
12153 ..(range.end.0 as isize - selection.head().0 as isize),
12154 )
12155 } else {
12156 None
12157 }
12158 })
12159 });
12160
12161 cx.emit(EditorEvent::InputHandled {
12162 utf16_range_to_replace: range_to_replace,
12163 text: text.into(),
12164 });
12165
12166 if let Some(ranges) = ranges_to_replace {
12167 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12168 }
12169
12170 let marked_ranges = {
12171 let snapshot = this.buffer.read(cx).read(cx);
12172 this.selections
12173 .disjoint_anchors()
12174 .iter()
12175 .map(|selection| {
12176 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12177 })
12178 .collect::<Vec<_>>()
12179 };
12180
12181 if text.is_empty() {
12182 this.unmark_text(cx);
12183 } else {
12184 this.highlight_text::<InputComposition>(
12185 marked_ranges.clone(),
12186 HighlightStyle {
12187 underline: Some(UnderlineStyle {
12188 thickness: px(1.),
12189 color: None,
12190 wavy: false,
12191 }),
12192 ..Default::default()
12193 },
12194 cx,
12195 );
12196 }
12197
12198 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12199 let use_autoclose = this.use_autoclose;
12200 let use_auto_surround = this.use_auto_surround;
12201 this.set_use_autoclose(false);
12202 this.set_use_auto_surround(false);
12203 this.handle_input(text, cx);
12204 this.set_use_autoclose(use_autoclose);
12205 this.set_use_auto_surround(use_auto_surround);
12206
12207 if let Some(new_selected_range) = new_selected_range_utf16 {
12208 let snapshot = this.buffer.read(cx).read(cx);
12209 let new_selected_ranges = marked_ranges
12210 .into_iter()
12211 .map(|marked_range| {
12212 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12213 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12214 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12215 snapshot.clip_offset_utf16(new_start, Bias::Left)
12216 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12217 })
12218 .collect::<Vec<_>>();
12219
12220 drop(snapshot);
12221 this.change_selections(None, cx, |selections| {
12222 selections.select_ranges(new_selected_ranges)
12223 });
12224 }
12225 });
12226
12227 self.ime_transaction = self.ime_transaction.or(transaction);
12228 if let Some(transaction) = self.ime_transaction {
12229 self.buffer.update(cx, |buffer, cx| {
12230 buffer.group_until_transaction(transaction, cx);
12231 });
12232 }
12233
12234 if self.text_highlights::<InputComposition>(cx).is_none() {
12235 self.ime_transaction.take();
12236 }
12237 }
12238
12239 fn bounds_for_range(
12240 &mut self,
12241 range_utf16: Range<usize>,
12242 element_bounds: gpui::Bounds<Pixels>,
12243 cx: &mut ViewContext<Self>,
12244 ) -> Option<gpui::Bounds<Pixels>> {
12245 let text_layout_details = self.text_layout_details(cx);
12246 let style = &text_layout_details.editor_style;
12247 let font_id = cx.text_system().resolve_font(&style.text.font());
12248 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12249 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12250 let em_width = cx
12251 .text_system()
12252 .typographic_bounds(font_id, font_size, 'm')
12253 .unwrap()
12254 .size
12255 .width;
12256
12257 let snapshot = self.snapshot(cx);
12258 let scroll_position = snapshot.scroll_position();
12259 let scroll_left = scroll_position.x * em_width;
12260
12261 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12262 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12263 + self.gutter_dimensions.width;
12264 let y = line_height * (start.row().as_f32() - scroll_position.y);
12265
12266 Some(Bounds {
12267 origin: element_bounds.origin + point(x, y),
12268 size: size(em_width, line_height),
12269 })
12270 }
12271}
12272
12273trait SelectionExt {
12274 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12275 fn spanned_rows(
12276 &self,
12277 include_end_if_at_line_start: bool,
12278 map: &DisplaySnapshot,
12279 ) -> Range<MultiBufferRow>;
12280}
12281
12282impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12283 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12284 let start = self
12285 .start
12286 .to_point(&map.buffer_snapshot)
12287 .to_display_point(map);
12288 let end = self
12289 .end
12290 .to_point(&map.buffer_snapshot)
12291 .to_display_point(map);
12292 if self.reversed {
12293 end..start
12294 } else {
12295 start..end
12296 }
12297 }
12298
12299 fn spanned_rows(
12300 &self,
12301 include_end_if_at_line_start: bool,
12302 map: &DisplaySnapshot,
12303 ) -> Range<MultiBufferRow> {
12304 let start = self.start.to_point(&map.buffer_snapshot);
12305 let mut end = self.end.to_point(&map.buffer_snapshot);
12306 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12307 end.row -= 1;
12308 }
12309
12310 let buffer_start = map.prev_line_boundary(start).0;
12311 let buffer_end = map.next_line_boundary(end).0;
12312 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12313 }
12314}
12315
12316impl<T: InvalidationRegion> InvalidationStack<T> {
12317 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12318 where
12319 S: Clone + ToOffset,
12320 {
12321 while let Some(region) = self.last() {
12322 let all_selections_inside_invalidation_ranges =
12323 if selections.len() == region.ranges().len() {
12324 selections
12325 .iter()
12326 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12327 .all(|(selection, invalidation_range)| {
12328 let head = selection.head().to_offset(buffer);
12329 invalidation_range.start <= head && invalidation_range.end >= head
12330 })
12331 } else {
12332 false
12333 };
12334
12335 if all_selections_inside_invalidation_ranges {
12336 break;
12337 } else {
12338 self.pop();
12339 }
12340 }
12341 }
12342}
12343
12344impl<T> Default for InvalidationStack<T> {
12345 fn default() -> Self {
12346 Self(Default::default())
12347 }
12348}
12349
12350impl<T> Deref for InvalidationStack<T> {
12351 type Target = Vec<T>;
12352
12353 fn deref(&self) -> &Self::Target {
12354 &self.0
12355 }
12356}
12357
12358impl<T> DerefMut for InvalidationStack<T> {
12359 fn deref_mut(&mut self) -> &mut Self::Target {
12360 &mut self.0
12361 }
12362}
12363
12364impl InvalidationRegion for SnippetState {
12365 fn ranges(&self) -> &[Range<Anchor>] {
12366 &self.ranges[self.active_index]
12367 }
12368}
12369
12370pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
12371 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
12372
12373 Box::new(move |cx: &mut BlockContext| {
12374 let group_id: SharedString = cx.block_id.to_string().into();
12375
12376 let mut text_style = cx.text_style().clone();
12377 text_style.color = diagnostic_style(diagnostic.severity, true, cx.theme().status());
12378 let theme_settings = ThemeSettings::get_global(cx);
12379 text_style.font_family = theme_settings.buffer_font.family.clone();
12380 text_style.font_style = theme_settings.buffer_font.style;
12381 text_style.font_features = theme_settings.buffer_font.features.clone();
12382 text_style.font_weight = theme_settings.buffer_font.weight;
12383
12384 let multi_line_diagnostic = diagnostic.message.contains('\n');
12385
12386 let buttons = |diagnostic: &Diagnostic, block_id: usize| {
12387 if multi_line_diagnostic {
12388 v_flex()
12389 } else {
12390 h_flex()
12391 }
12392 .children(diagnostic.is_primary.then(|| {
12393 IconButton::new(("close-block", block_id), IconName::XCircle)
12394 .icon_color(Color::Muted)
12395 .size(ButtonSize::Compact)
12396 .style(ButtonStyle::Transparent)
12397 .visible_on_hover(group_id.clone())
12398 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12399 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12400 }))
12401 .child(
12402 IconButton::new(("copy-block", block_id), IconName::Copy)
12403 .icon_color(Color::Muted)
12404 .size(ButtonSize::Compact)
12405 .style(ButtonStyle::Transparent)
12406 .visible_on_hover(group_id.clone())
12407 .on_click({
12408 let message = diagnostic.message.clone();
12409 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12410 })
12411 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12412 )
12413 };
12414
12415 let icon_size = buttons(&diagnostic, cx.block_id)
12416 .into_any_element()
12417 .layout_as_root(AvailableSpace::min_size(), cx);
12418
12419 h_flex()
12420 .id(cx.block_id)
12421 .group(group_id.clone())
12422 .relative()
12423 .size_full()
12424 .pl(cx.gutter_dimensions.width)
12425 .w(cx.max_width + cx.gutter_dimensions.width)
12426 .child(
12427 div()
12428 .flex()
12429 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12430 .flex_shrink(),
12431 )
12432 .child(buttons(&diagnostic, cx.block_id))
12433 .child(div().flex().flex_shrink_0().child(
12434 StyledText::new(text_without_backticks.clone()).with_highlights(
12435 &text_style,
12436 code_ranges.iter().map(|range| {
12437 (
12438 range.clone(),
12439 HighlightStyle {
12440 font_weight: Some(FontWeight::BOLD),
12441 ..Default::default()
12442 },
12443 )
12444 }),
12445 ),
12446 ))
12447 .into_any_element()
12448 })
12449}
12450
12451pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
12452 let mut text_without_backticks = String::new();
12453 let mut code_ranges = Vec::new();
12454
12455 if let Some(source) = &diagnostic.source {
12456 text_without_backticks.push_str(&source);
12457 code_ranges.push(0..source.len());
12458 text_without_backticks.push_str(": ");
12459 }
12460
12461 let mut prev_offset = 0;
12462 let mut in_code_block = false;
12463 for (ix, _) in diagnostic
12464 .message
12465 .match_indices('`')
12466 .chain([(diagnostic.message.len(), "")])
12467 {
12468 let prev_len = text_without_backticks.len();
12469 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
12470 prev_offset = ix + 1;
12471 if in_code_block {
12472 code_ranges.push(prev_len..text_without_backticks.len());
12473 in_code_block = false;
12474 } else {
12475 in_code_block = true;
12476 }
12477 }
12478
12479 (text_without_backticks.into(), code_ranges)
12480}
12481
12482fn diagnostic_style(severity: DiagnosticSeverity, valid: bool, colors: &StatusColors) -> Hsla {
12483 match (severity, valid) {
12484 (DiagnosticSeverity::ERROR, true) => colors.error,
12485 (DiagnosticSeverity::ERROR, false) => colors.error,
12486 (DiagnosticSeverity::WARNING, true) => colors.warning,
12487 (DiagnosticSeverity::WARNING, false) => colors.warning,
12488 (DiagnosticSeverity::INFORMATION, true) => colors.info,
12489 (DiagnosticSeverity::INFORMATION, false) => colors.info,
12490 (DiagnosticSeverity::HINT, true) => colors.info,
12491 (DiagnosticSeverity::HINT, false) => colors.info,
12492 _ => colors.ignored,
12493 }
12494}
12495
12496pub fn styled_runs_for_code_label<'a>(
12497 label: &'a CodeLabel,
12498 syntax_theme: &'a theme::SyntaxTheme,
12499) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
12500 let fade_out = HighlightStyle {
12501 fade_out: Some(0.35),
12502 ..Default::default()
12503 };
12504
12505 let mut prev_end = label.filter_range.end;
12506 label
12507 .runs
12508 .iter()
12509 .enumerate()
12510 .flat_map(move |(ix, (range, highlight_id))| {
12511 let style = if let Some(style) = highlight_id.style(syntax_theme) {
12512 style
12513 } else {
12514 return Default::default();
12515 };
12516 let mut muted_style = style;
12517 muted_style.highlight(fade_out);
12518
12519 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
12520 if range.start >= label.filter_range.end {
12521 if range.start > prev_end {
12522 runs.push((prev_end..range.start, fade_out));
12523 }
12524 runs.push((range.clone(), muted_style));
12525 } else if range.end <= label.filter_range.end {
12526 runs.push((range.clone(), style));
12527 } else {
12528 runs.push((range.start..label.filter_range.end, style));
12529 runs.push((label.filter_range.end..range.end, muted_style));
12530 }
12531 prev_end = cmp::max(prev_end, range.end);
12532
12533 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
12534 runs.push((prev_end..label.text.len(), fade_out));
12535 }
12536
12537 runs
12538 })
12539}
12540
12541pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
12542 let mut prev_index = 0;
12543 let mut prev_codepoint: Option<char> = None;
12544 text.char_indices()
12545 .chain([(text.len(), '\0')])
12546 .filter_map(move |(index, codepoint)| {
12547 let prev_codepoint = prev_codepoint.replace(codepoint)?;
12548 let is_boundary = index == text.len()
12549 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
12550 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
12551 if is_boundary {
12552 let chunk = &text[prev_index..index];
12553 prev_index = index;
12554 Some(chunk)
12555 } else {
12556 None
12557 }
12558 })
12559}
12560
12561trait RangeToAnchorExt {
12562 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
12563}
12564
12565impl<T: ToOffset> RangeToAnchorExt for Range<T> {
12566 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
12567 let start_offset = self.start.to_offset(snapshot);
12568 let end_offset = self.end.to_offset(snapshot);
12569 if start_offset == end_offset {
12570 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
12571 } else {
12572 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
12573 }
12574 }
12575}
12576
12577pub trait RowExt {
12578 fn as_f32(&self) -> f32;
12579
12580 fn next_row(&self) -> Self;
12581
12582 fn previous_row(&self) -> Self;
12583
12584 fn minus(&self, other: Self) -> u32;
12585}
12586
12587impl RowExt for DisplayRow {
12588 fn as_f32(&self) -> f32 {
12589 self.0 as f32
12590 }
12591
12592 fn next_row(&self) -> Self {
12593 Self(self.0 + 1)
12594 }
12595
12596 fn previous_row(&self) -> Self {
12597 Self(self.0.saturating_sub(1))
12598 }
12599
12600 fn minus(&self, other: Self) -> u32 {
12601 self.0 - other.0
12602 }
12603}
12604
12605impl RowExt for MultiBufferRow {
12606 fn as_f32(&self) -> f32 {
12607 self.0 as f32
12608 }
12609
12610 fn next_row(&self) -> Self {
12611 Self(self.0 + 1)
12612 }
12613
12614 fn previous_row(&self) -> Self {
12615 Self(self.0.saturating_sub(1))
12616 }
12617
12618 fn minus(&self, other: Self) -> u32 {
12619 self.0 - other.0
12620 }
12621}
12622
12623trait RowRangeExt {
12624 type Row;
12625
12626 fn len(&self) -> usize;
12627
12628 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
12629}
12630
12631impl RowRangeExt for Range<MultiBufferRow> {
12632 type Row = MultiBufferRow;
12633
12634 fn len(&self) -> usize {
12635 (self.end.0 - self.start.0) as usize
12636 }
12637
12638 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
12639 (self.start.0..self.end.0).map(MultiBufferRow)
12640 }
12641}
12642
12643impl RowRangeExt for Range<DisplayRow> {
12644 type Row = DisplayRow;
12645
12646 fn len(&self) -> usize {
12647 (self.end.0 - self.start.0) as usize
12648 }
12649
12650 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
12651 (self.start.0..self.end.0).map(DisplayRow)
12652 }
12653}
12654
12655fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
12656 if hunk.diff_base_byte_range.is_empty() {
12657 DiffHunkStatus::Added
12658 } else if hunk.associated_range.is_empty() {
12659 DiffHunkStatus::Removed
12660 } else {
12661 DiffHunkStatus::Modified
12662 }
12663}