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::{point_to_lsp, 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::{
99 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
100 LanguageServerId,
101};
102use mouse_context_menu::MouseContextMenu;
103use movement::TextLayoutDetails;
104pub use multi_buffer::{
105 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
106 ToPoint,
107};
108use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
109use ordered_float::OrderedFloat;
110use parking_lot::{Mutex, RwLock};
111use project::project_settings::{GitGutterSetting, ProjectSettings};
112use project::{
113 CodeAction, Completion, FormatTrigger, Item, Location, Project, ProjectPath,
114 ProjectTransaction, TaskSourceKind, WorktreeId,
115};
116use rand::prelude::*;
117use rpc::{proto::*, ErrorExt};
118use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
119use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
120use serde::{Deserialize, Serialize};
121use settings::{update_settings_file, Settings, SettingsStore};
122use smallvec::SmallVec;
123use snippet::Snippet;
124use std::{
125 any::TypeId,
126 borrow::Cow,
127 cell::RefCell,
128 cmp::{self, Ordering, Reverse},
129 mem,
130 num::NonZeroU32,
131 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
132 path::Path,
133 rc::Rc,
134 sync::Arc,
135 time::{Duration, Instant},
136};
137pub use sum_tree::Bias;
138use sum_tree::TreeMap;
139use text::{BufferId, OffsetUtf16, Rope};
140use theme::{
141 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
142 ThemeColors, ThemeSettings,
143};
144use ui::{
145 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
146 ListItem, Popover, Tooltip,
147};
148use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
149use workspace::item::{ItemHandle, PreviewTabsSettings};
150use workspace::notifications::{DetachAndPromptErr, NotificationId};
151use workspace::{
152 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
153};
154use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
155
156use crate::hover_links::find_url;
157
158pub const FILE_HEADER_HEIGHT: u8 = 1;
159pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
160pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
161pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
162const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
163const MAX_LINE_LEN: usize = 1024;
164const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
165const MAX_SELECTION_HISTORY_LEN: usize = 1024;
166pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
167#[doc(hidden)]
168pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
169#[doc(hidden)]
170pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
171
172pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
173
174pub fn render_parsed_markdown(
175 element_id: impl Into<ElementId>,
176 parsed: &language::ParsedMarkdown,
177 editor_style: &EditorStyle,
178 workspace: Option<WeakView<Workspace>>,
179 cx: &mut WindowContext,
180) -> InteractiveText {
181 let code_span_background_color = cx
182 .theme()
183 .colors()
184 .editor_document_highlight_read_background;
185
186 let highlights = gpui::combine_highlights(
187 parsed.highlights.iter().filter_map(|(range, highlight)| {
188 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
189 Some((range.clone(), highlight))
190 }),
191 parsed
192 .regions
193 .iter()
194 .zip(&parsed.region_ranges)
195 .filter_map(|(region, range)| {
196 if region.code {
197 Some((
198 range.clone(),
199 HighlightStyle {
200 background_color: Some(code_span_background_color),
201 ..Default::default()
202 },
203 ))
204 } else {
205 None
206 }
207 }),
208 );
209
210 let mut links = Vec::new();
211 let mut link_ranges = Vec::new();
212 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
213 if let Some(link) = region.link.clone() {
214 links.push(link);
215 link_ranges.push(range.clone());
216 }
217 }
218
219 InteractiveText::new(
220 element_id,
221 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
222 )
223 .on_click(link_ranges, move |clicked_range_ix, cx| {
224 match &links[clicked_range_ix] {
225 markdown::Link::Web { url } => cx.open_url(url),
226 markdown::Link::Path { path } => {
227 if let Some(workspace) = &workspace {
228 _ = workspace.update(cx, |workspace, cx| {
229 workspace.open_abs_path(path.clone(), false, cx).detach();
230 });
231 }
232 }
233 }
234 })
235}
236
237#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
238pub(crate) enum InlayId {
239 Suggestion(usize),
240 Hint(usize),
241}
242
243impl InlayId {
244 fn id(&self) -> usize {
245 match self {
246 Self::Suggestion(id) => *id,
247 Self::Hint(id) => *id,
248 }
249 }
250}
251
252enum DiffRowHighlight {}
253enum DocumentHighlightRead {}
254enum DocumentHighlightWrite {}
255enum InputComposition {}
256
257#[derive(Copy, Clone, PartialEq, Eq)]
258pub enum Direction {
259 Prev,
260 Next,
261}
262
263pub fn init_settings(cx: &mut AppContext) {
264 EditorSettings::register(cx);
265}
266
267pub fn init(cx: &mut AppContext) {
268 init_settings(cx);
269
270 workspace::register_project_item::<Editor>(cx);
271 workspace::register_followable_item::<Editor>(cx);
272 workspace::register_deserializable_item::<Editor>(cx);
273 cx.observe_new_views(
274 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
275 workspace.register_action(Editor::new_file);
276 workspace.register_action(Editor::new_file_in_direction);
277 },
278 )
279 .detach();
280
281 cx.on_action(move |_: &workspace::NewFile, cx| {
282 let app_state = workspace::AppState::global(cx);
283 if let Some(app_state) = app_state.upgrade() {
284 workspace::open_new(app_state, cx, |workspace, cx| {
285 Editor::new_file(workspace, &Default::default(), cx)
286 })
287 .detach();
288 }
289 });
290 cx.on_action(move |_: &workspace::NewWindow, cx| {
291 let app_state = workspace::AppState::global(cx);
292 if let Some(app_state) = app_state.upgrade() {
293 workspace::open_new(app_state, cx, |workspace, cx| {
294 Editor::new_file(workspace, &Default::default(), cx)
295 })
296 .detach();
297 }
298 });
299}
300
301pub struct SearchWithinRange;
302
303trait InvalidationRegion {
304 fn ranges(&self) -> &[Range<Anchor>];
305}
306
307#[derive(Clone, Debug, PartialEq)]
308pub enum SelectPhase {
309 Begin {
310 position: DisplayPoint,
311 add: bool,
312 click_count: usize,
313 },
314 BeginColumnar {
315 position: DisplayPoint,
316 reset: bool,
317 goal_column: u32,
318 },
319 Extend {
320 position: DisplayPoint,
321 click_count: usize,
322 },
323 Update {
324 position: DisplayPoint,
325 goal_column: u32,
326 scroll_delta: gpui::Point<f32>,
327 },
328 End,
329}
330
331#[derive(Clone, Debug)]
332pub enum SelectMode {
333 Character,
334 Word(Range<Anchor>),
335 Line(Range<Anchor>),
336 All,
337}
338
339#[derive(Copy, Clone, PartialEq, Eq, Debug)]
340pub enum EditorMode {
341 SingleLine { auto_width: bool },
342 AutoHeight { max_lines: usize },
343 Full,
344}
345
346#[derive(Clone, Debug)]
347pub enum SoftWrap {
348 None,
349 PreferLine,
350 EditorWidth,
351 Column(u32),
352}
353
354#[derive(Clone)]
355pub struct EditorStyle {
356 pub background: Hsla,
357 pub local_player: PlayerColor,
358 pub text: TextStyle,
359 pub scrollbar_width: Pixels,
360 pub syntax: Arc<SyntaxTheme>,
361 pub status: StatusColors,
362 pub inlay_hints_style: HighlightStyle,
363 pub suggestions_style: HighlightStyle,
364}
365
366impl Default for EditorStyle {
367 fn default() -> Self {
368 Self {
369 background: Hsla::default(),
370 local_player: PlayerColor::default(),
371 text: TextStyle::default(),
372 scrollbar_width: Pixels::default(),
373 syntax: Default::default(),
374 // HACK: Status colors don't have a real default.
375 // We should look into removing the status colors from the editor
376 // style and retrieve them directly from the theme.
377 status: StatusColors::dark(),
378 inlay_hints_style: HighlightStyle::default(),
379 suggestions_style: HighlightStyle::default(),
380 }
381 }
382}
383
384type CompletionId = usize;
385
386#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
387struct EditorActionId(usize);
388
389impl EditorActionId {
390 pub fn post_inc(&mut self) -> Self {
391 let answer = self.0;
392
393 *self = Self(answer + 1);
394
395 Self(answer)
396 }
397}
398
399// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
400// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
401
402type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
403type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
404
405struct ScrollbarMarkerState {
406 scrollbar_size: Size<Pixels>,
407 dirty: bool,
408 markers: Arc<[PaintQuad]>,
409 pending_refresh: Option<Task<Result<()>>>,
410}
411
412impl ScrollbarMarkerState {
413 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
414 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
415 }
416}
417
418impl Default for ScrollbarMarkerState {
419 fn default() -> Self {
420 Self {
421 scrollbar_size: Size::default(),
422 dirty: false,
423 markers: Arc::from([]),
424 pending_refresh: None,
425 }
426 }
427}
428
429#[derive(Clone, Debug)]
430struct RunnableTasks {
431 templates: Vec<(TaskSourceKind, TaskTemplate)>,
432 offset: MultiBufferOffset,
433 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
434 column: u32,
435 // Values of all named captures, including those starting with '_'
436 extra_variables: HashMap<String, String>,
437 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
438 context_range: Range<BufferOffset>,
439}
440
441#[derive(Clone)]
442struct ResolvedTasks {
443 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
444 position: Anchor,
445}
446#[derive(Copy, Clone, Debug)]
447struct MultiBufferOffset(usize);
448#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
449struct BufferOffset(usize);
450/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
451///
452/// See the [module level documentation](self) for more information.
453pub struct Editor {
454 focus_handle: FocusHandle,
455 last_focused_descendant: Option<WeakFocusHandle>,
456 /// The text buffer being edited
457 buffer: Model<MultiBuffer>,
458 /// Map of how text in the buffer should be displayed.
459 /// Handles soft wraps, folds, fake inlay text insertions, etc.
460 pub display_map: Model<DisplayMap>,
461 pub selections: SelectionsCollection,
462 pub scroll_manager: ScrollManager,
463 /// When inline assist editors are linked, they all render cursors because
464 /// typing enters text into each of them, even the ones that aren't focused.
465 pub(crate) show_cursor_when_unfocused: bool,
466 columnar_selection_tail: Option<Anchor>,
467 add_selections_state: Option<AddSelectionsState>,
468 select_next_state: Option<SelectNextState>,
469 select_prev_state: Option<SelectNextState>,
470 selection_history: SelectionHistory,
471 autoclose_regions: Vec<AutocloseRegion>,
472 snippet_stack: InvalidationStack<SnippetState>,
473 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
474 ime_transaction: Option<TransactionId>,
475 active_diagnostics: Option<ActiveDiagnosticGroup>,
476 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
477 project: Option<Model<Project>>,
478 completion_provider: Option<Box<dyn CompletionProvider>>,
479 collaboration_hub: Option<Box<dyn CollaborationHub>>,
480 blink_manager: Model<BlinkManager>,
481 show_cursor_names: bool,
482 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
483 pub show_local_selections: bool,
484 mode: EditorMode,
485 show_breadcrumbs: bool,
486 show_gutter: bool,
487 show_line_numbers: Option<bool>,
488 show_git_diff_gutter: Option<bool>,
489 show_code_actions: Option<bool>,
490 show_runnables: Option<bool>,
491 show_wrap_guides: Option<bool>,
492 show_indent_guides: Option<bool>,
493 placeholder_text: Option<Arc<str>>,
494 highlight_order: usize,
495 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
496 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
497 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
498 scrollbar_marker_state: ScrollbarMarkerState,
499 active_indent_guides_state: ActiveIndentGuidesState,
500 nav_history: Option<ItemNavHistory>,
501 context_menu: RwLock<Option<ContextMenu>>,
502 mouse_context_menu: Option<MouseContextMenu>,
503 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
504 find_all_references_task_sources: Vec<Anchor>,
505 next_completion_id: CompletionId,
506 completion_documentation_pre_resolve_debounce: DebouncedDelay,
507 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
508 code_actions_task: Option<Task<()>>,
509 document_highlights_task: Option<Task<()>>,
510 linked_editing_range_task: Option<Task<Option<()>>>,
511 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
512 pending_rename: Option<RenameState>,
513 searchable: bool,
514 cursor_shape: CursorShape,
515 current_line_highlight: Option<CurrentLineHighlight>,
516 collapse_matches: bool,
517 autoindent_mode: Option<AutoindentMode>,
518 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
519 keymap_context_layers: BTreeMap<TypeId, KeyContext>,
520 input_enabled: bool,
521 use_modal_editing: bool,
522 read_only: bool,
523 leader_peer_id: Option<PeerId>,
524 remote_id: Option<ViewId>,
525 hover_state: HoverState,
526 gutter_hovered: bool,
527 hovered_link_state: Option<HoveredLinkState>,
528 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
529 active_inline_completion: Option<Inlay>,
530 show_inline_completions: bool,
531 inlay_hint_cache: InlayHintCache,
532 expanded_hunks: ExpandedHunks,
533 next_inlay_id: usize,
534 _subscriptions: Vec<Subscription>,
535 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
536 gutter_dimensions: GutterDimensions,
537 pub vim_replace_map: HashMap<Range<usize>, String>,
538 style: Option<EditorStyle>,
539 next_editor_action_id: EditorActionId,
540 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
541 use_autoclose: bool,
542 use_auto_surround: bool,
543 auto_replace_emoji_shortcode: bool,
544 show_git_blame_gutter: bool,
545 show_git_blame_inline: bool,
546 show_git_blame_inline_delay_task: Option<Task<()>>,
547 git_blame_inline_enabled: bool,
548 show_selection_menu: Option<bool>,
549 blame: Option<Model<GitBlame>>,
550 blame_subscription: Option<Subscription>,
551 custom_context_menu: Option<
552 Box<
553 dyn 'static
554 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
555 >,
556 >,
557 last_bounds: Option<Bounds<Pixels>>,
558 expect_bounds_change: Option<Bounds<Pixels>>,
559 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
560 tasks_update_task: Option<Task<()>>,
561 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
562 file_header_size: u8,
563 breadcrumb_header: Option<String>,
564}
565
566#[derive(Clone)]
567pub struct EditorSnapshot {
568 pub mode: EditorMode,
569 show_gutter: bool,
570 show_line_numbers: Option<bool>,
571 show_git_diff_gutter: Option<bool>,
572 show_code_actions: Option<bool>,
573 show_runnables: Option<bool>,
574 render_git_blame_gutter: bool,
575 pub display_snapshot: DisplaySnapshot,
576 pub placeholder_text: Option<Arc<str>>,
577 is_focused: bool,
578 scroll_anchor: ScrollAnchor,
579 ongoing_scroll: OngoingScroll,
580 current_line_highlight: CurrentLineHighlight,
581 gutter_hovered: bool,
582}
583
584const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
585
586#[derive(Debug, Clone, Copy)]
587pub struct GutterDimensions {
588 pub left_padding: Pixels,
589 pub right_padding: Pixels,
590 pub width: Pixels,
591 pub margin: Pixels,
592 pub git_blame_entries_width: Option<Pixels>,
593}
594
595impl GutterDimensions {
596 /// The full width of the space taken up by the gutter.
597 pub fn full_width(&self) -> Pixels {
598 self.margin + self.width
599 }
600
601 /// The width of the space reserved for the fold indicators,
602 /// use alongside 'justify_end' and `gutter_width` to
603 /// right align content with the line numbers
604 pub fn fold_area_width(&self) -> Pixels {
605 self.margin + self.right_padding
606 }
607}
608
609impl Default for GutterDimensions {
610 fn default() -> Self {
611 Self {
612 left_padding: Pixels::ZERO,
613 right_padding: Pixels::ZERO,
614 width: Pixels::ZERO,
615 margin: Pixels::ZERO,
616 git_blame_entries_width: None,
617 }
618 }
619}
620
621#[derive(Debug)]
622pub struct RemoteSelection {
623 pub replica_id: ReplicaId,
624 pub selection: Selection<Anchor>,
625 pub cursor_shape: CursorShape,
626 pub peer_id: PeerId,
627 pub line_mode: bool,
628 pub participant_index: Option<ParticipantIndex>,
629 pub user_name: Option<SharedString>,
630}
631
632#[derive(Clone, Debug)]
633struct SelectionHistoryEntry {
634 selections: Arc<[Selection<Anchor>]>,
635 select_next_state: Option<SelectNextState>,
636 select_prev_state: Option<SelectNextState>,
637 add_selections_state: Option<AddSelectionsState>,
638}
639
640enum SelectionHistoryMode {
641 Normal,
642 Undoing,
643 Redoing,
644}
645
646#[derive(Clone, PartialEq, Eq, Hash)]
647struct HoveredCursor {
648 replica_id: u16,
649 selection_id: usize,
650}
651
652impl Default for SelectionHistoryMode {
653 fn default() -> Self {
654 Self::Normal
655 }
656}
657
658#[derive(Default)]
659struct SelectionHistory {
660 #[allow(clippy::type_complexity)]
661 selections_by_transaction:
662 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
663 mode: SelectionHistoryMode,
664 undo_stack: VecDeque<SelectionHistoryEntry>,
665 redo_stack: VecDeque<SelectionHistoryEntry>,
666}
667
668impl SelectionHistory {
669 fn insert_transaction(
670 &mut self,
671 transaction_id: TransactionId,
672 selections: Arc<[Selection<Anchor>]>,
673 ) {
674 self.selections_by_transaction
675 .insert(transaction_id, (selections, None));
676 }
677
678 #[allow(clippy::type_complexity)]
679 fn transaction(
680 &self,
681 transaction_id: TransactionId,
682 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
683 self.selections_by_transaction.get(&transaction_id)
684 }
685
686 #[allow(clippy::type_complexity)]
687 fn transaction_mut(
688 &mut self,
689 transaction_id: TransactionId,
690 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
691 self.selections_by_transaction.get_mut(&transaction_id)
692 }
693
694 fn push(&mut self, entry: SelectionHistoryEntry) {
695 if !entry.selections.is_empty() {
696 match self.mode {
697 SelectionHistoryMode::Normal => {
698 self.push_undo(entry);
699 self.redo_stack.clear();
700 }
701 SelectionHistoryMode::Undoing => self.push_redo(entry),
702 SelectionHistoryMode::Redoing => self.push_undo(entry),
703 }
704 }
705 }
706
707 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
708 if self
709 .undo_stack
710 .back()
711 .map_or(true, |e| e.selections != entry.selections)
712 {
713 self.undo_stack.push_back(entry);
714 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
715 self.undo_stack.pop_front();
716 }
717 }
718 }
719
720 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
721 if self
722 .redo_stack
723 .back()
724 .map_or(true, |e| e.selections != entry.selections)
725 {
726 self.redo_stack.push_back(entry);
727 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
728 self.redo_stack.pop_front();
729 }
730 }
731 }
732}
733
734struct RowHighlight {
735 index: usize,
736 range: RangeInclusive<Anchor>,
737 color: Option<Hsla>,
738 should_autoscroll: bool,
739}
740
741#[derive(Clone, Debug)]
742struct AddSelectionsState {
743 above: bool,
744 stack: Vec<usize>,
745}
746
747#[derive(Clone)]
748struct SelectNextState {
749 query: AhoCorasick,
750 wordwise: bool,
751 done: bool,
752}
753
754impl std::fmt::Debug for SelectNextState {
755 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
756 f.debug_struct(std::any::type_name::<Self>())
757 .field("wordwise", &self.wordwise)
758 .field("done", &self.done)
759 .finish()
760 }
761}
762
763#[derive(Debug)]
764struct AutocloseRegion {
765 selection_id: usize,
766 range: Range<Anchor>,
767 pair: BracketPair,
768}
769
770#[derive(Debug)]
771struct SnippetState {
772 ranges: Vec<Vec<Range<Anchor>>>,
773 active_index: usize,
774}
775
776#[doc(hidden)]
777pub struct RenameState {
778 pub range: Range<Anchor>,
779 pub old_name: Arc<str>,
780 pub editor: View<Editor>,
781 block_id: BlockId,
782}
783
784struct InvalidationStack<T>(Vec<T>);
785
786struct RegisteredInlineCompletionProvider {
787 provider: Arc<dyn InlineCompletionProviderHandle>,
788 _subscription: Subscription,
789}
790
791enum ContextMenu {
792 Completions(CompletionsMenu),
793 CodeActions(CodeActionsMenu),
794}
795
796impl ContextMenu {
797 fn select_first(
798 &mut self,
799 project: Option<&Model<Project>>,
800 cx: &mut ViewContext<Editor>,
801 ) -> bool {
802 if self.visible() {
803 match self {
804 ContextMenu::Completions(menu) => menu.select_first(project, cx),
805 ContextMenu::CodeActions(menu) => menu.select_first(cx),
806 }
807 true
808 } else {
809 false
810 }
811 }
812
813 fn select_prev(
814 &mut self,
815 project: Option<&Model<Project>>,
816 cx: &mut ViewContext<Editor>,
817 ) -> bool {
818 if self.visible() {
819 match self {
820 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
821 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
822 }
823 true
824 } else {
825 false
826 }
827 }
828
829 fn select_next(
830 &mut self,
831 project: Option<&Model<Project>>,
832 cx: &mut ViewContext<Editor>,
833 ) -> bool {
834 if self.visible() {
835 match self {
836 ContextMenu::Completions(menu) => menu.select_next(project, cx),
837 ContextMenu::CodeActions(menu) => menu.select_next(cx),
838 }
839 true
840 } else {
841 false
842 }
843 }
844
845 fn select_last(
846 &mut self,
847 project: Option<&Model<Project>>,
848 cx: &mut ViewContext<Editor>,
849 ) -> bool {
850 if self.visible() {
851 match self {
852 ContextMenu::Completions(menu) => menu.select_last(project, cx),
853 ContextMenu::CodeActions(menu) => menu.select_last(cx),
854 }
855 true
856 } else {
857 false
858 }
859 }
860
861 fn visible(&self) -> bool {
862 match self {
863 ContextMenu::Completions(menu) => menu.visible(),
864 ContextMenu::CodeActions(menu) => menu.visible(),
865 }
866 }
867
868 fn render(
869 &self,
870 cursor_position: DisplayPoint,
871 style: &EditorStyle,
872 max_height: Pixels,
873 workspace: Option<WeakView<Workspace>>,
874 cx: &mut ViewContext<Editor>,
875 ) -> (ContextMenuOrigin, AnyElement) {
876 match self {
877 ContextMenu::Completions(menu) => (
878 ContextMenuOrigin::EditorPoint(cursor_position),
879 menu.render(style, max_height, workspace, cx),
880 ),
881 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
882 }
883 }
884}
885
886enum ContextMenuOrigin {
887 EditorPoint(DisplayPoint),
888 GutterIndicator(DisplayRow),
889}
890
891#[derive(Clone)]
892struct CompletionsMenu {
893 id: CompletionId,
894 initial_position: Anchor,
895 buffer: Model<Buffer>,
896 completions: Arc<RwLock<Box<[Completion]>>>,
897 match_candidates: Arc<[StringMatchCandidate]>,
898 matches: Arc<[StringMatch]>,
899 selected_item: usize,
900 scroll_handle: UniformListScrollHandle,
901 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
902}
903
904impl CompletionsMenu {
905 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
906 self.selected_item = 0;
907 self.scroll_handle.scroll_to_item(self.selected_item);
908 self.attempt_resolve_selected_completion_documentation(project, cx);
909 cx.notify();
910 }
911
912 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
913 if self.selected_item > 0 {
914 self.selected_item -= 1;
915 } else {
916 self.selected_item = self.matches.len() - 1;
917 }
918 self.scroll_handle.scroll_to_item(self.selected_item);
919 self.attempt_resolve_selected_completion_documentation(project, cx);
920 cx.notify();
921 }
922
923 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
924 if self.selected_item + 1 < self.matches.len() {
925 self.selected_item += 1;
926 } else {
927 self.selected_item = 0;
928 }
929 self.scroll_handle.scroll_to_item(self.selected_item);
930 self.attempt_resolve_selected_completion_documentation(project, cx);
931 cx.notify();
932 }
933
934 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
935 self.selected_item = self.matches.len() - 1;
936 self.scroll_handle.scroll_to_item(self.selected_item);
937 self.attempt_resolve_selected_completion_documentation(project, cx);
938 cx.notify();
939 }
940
941 fn pre_resolve_completion_documentation(
942 buffer: Model<Buffer>,
943 completions: Arc<RwLock<Box<[Completion]>>>,
944 matches: Arc<[StringMatch]>,
945 editor: &Editor,
946 cx: &mut ViewContext<Editor>,
947 ) -> Task<()> {
948 let settings = EditorSettings::get_global(cx);
949 if !settings.show_completion_documentation {
950 return Task::ready(());
951 }
952
953 let Some(provider) = editor.completion_provider.as_ref() else {
954 return Task::ready(());
955 };
956
957 let resolve_task = provider.resolve_completions(
958 buffer,
959 matches.iter().map(|m| m.candidate_id).collect(),
960 completions.clone(),
961 cx,
962 );
963
964 return cx.spawn(move |this, mut cx| async move {
965 if let Some(true) = resolve_task.await.log_err() {
966 this.update(&mut cx, |_, cx| cx.notify()).ok();
967 }
968 });
969 }
970
971 fn attempt_resolve_selected_completion_documentation(
972 &mut self,
973 project: Option<&Model<Project>>,
974 cx: &mut ViewContext<Editor>,
975 ) {
976 let settings = EditorSettings::get_global(cx);
977 if !settings.show_completion_documentation {
978 return;
979 }
980
981 let completion_index = self.matches[self.selected_item].candidate_id;
982 let Some(project) = project else {
983 return;
984 };
985
986 let resolve_task = project.update(cx, |project, cx| {
987 project.resolve_completions(
988 self.buffer.clone(),
989 vec![completion_index],
990 self.completions.clone(),
991 cx,
992 )
993 });
994
995 let delay_ms =
996 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
997 let delay = Duration::from_millis(delay_ms);
998
999 self.selected_completion_documentation_resolve_debounce
1000 .lock()
1001 .fire_new(delay, cx, |_, cx| {
1002 cx.spawn(move |this, mut cx| async move {
1003 if let Some(true) = resolve_task.await.log_err() {
1004 this.update(&mut cx, |_, cx| cx.notify()).ok();
1005 }
1006 })
1007 });
1008 }
1009
1010 fn visible(&self) -> bool {
1011 !self.matches.is_empty()
1012 }
1013
1014 fn render(
1015 &self,
1016 style: &EditorStyle,
1017 max_height: Pixels,
1018 workspace: Option<WeakView<Workspace>>,
1019 cx: &mut ViewContext<Editor>,
1020 ) -> AnyElement {
1021 let settings = EditorSettings::get_global(cx);
1022 let show_completion_documentation = settings.show_completion_documentation;
1023
1024 let widest_completion_ix = self
1025 .matches
1026 .iter()
1027 .enumerate()
1028 .max_by_key(|(_, mat)| {
1029 let completions = self.completions.read();
1030 let completion = &completions[mat.candidate_id];
1031 let documentation = &completion.documentation;
1032
1033 let mut len = completion.label.text.chars().count();
1034 if let Some(Documentation::SingleLine(text)) = documentation {
1035 if show_completion_documentation {
1036 len += text.chars().count();
1037 }
1038 }
1039
1040 len
1041 })
1042 .map(|(ix, _)| ix);
1043
1044 let completions = self.completions.clone();
1045 let matches = self.matches.clone();
1046 let selected_item = self.selected_item;
1047 let style = style.clone();
1048
1049 let multiline_docs = if show_completion_documentation {
1050 let mat = &self.matches[selected_item];
1051 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1052 Some(Documentation::MultiLinePlainText(text)) => {
1053 Some(div().child(SharedString::from(text.clone())))
1054 }
1055 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1056 Some(div().child(render_parsed_markdown(
1057 "completions_markdown",
1058 parsed,
1059 &style,
1060 workspace,
1061 cx,
1062 )))
1063 }
1064 _ => None,
1065 };
1066 multiline_docs.map(|div| {
1067 div.id("multiline_docs")
1068 .max_h(max_height)
1069 .flex_1()
1070 .px_1p5()
1071 .py_1()
1072 .min_w(px(260.))
1073 .max_w(px(640.))
1074 .w(px(500.))
1075 .overflow_y_scroll()
1076 .occlude()
1077 })
1078 } else {
1079 None
1080 };
1081
1082 let list = uniform_list(
1083 cx.view().clone(),
1084 "completions",
1085 matches.len(),
1086 move |_editor, range, cx| {
1087 let start_ix = range.start;
1088 let completions_guard = completions.read();
1089
1090 matches[range]
1091 .iter()
1092 .enumerate()
1093 .map(|(ix, mat)| {
1094 let item_ix = start_ix + ix;
1095 let candidate_id = mat.candidate_id;
1096 let completion = &completions_guard[candidate_id];
1097
1098 let documentation = if show_completion_documentation {
1099 &completion.documentation
1100 } else {
1101 &None
1102 };
1103
1104 let highlights = gpui::combine_highlights(
1105 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1106 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1107 |(range, mut highlight)| {
1108 // Ignore font weight for syntax highlighting, as we'll use it
1109 // for fuzzy matches.
1110 highlight.font_weight = None;
1111
1112 if completion.lsp_completion.deprecated.unwrap_or(false) {
1113 highlight.strikethrough = Some(StrikethroughStyle {
1114 thickness: 1.0.into(),
1115 ..Default::default()
1116 });
1117 highlight.color = Some(cx.theme().colors().text_muted);
1118 }
1119
1120 (range, highlight)
1121 },
1122 ),
1123 );
1124 let completion_label = StyledText::new(completion.label.text.clone())
1125 .with_highlights(&style.text, highlights);
1126 let documentation_label =
1127 if let Some(Documentation::SingleLine(text)) = documentation {
1128 if text.trim().is_empty() {
1129 None
1130 } else {
1131 Some(
1132 h_flex().ml_4().child(
1133 Label::new(text.clone())
1134 .size(LabelSize::Small)
1135 .color(Color::Muted),
1136 ),
1137 )
1138 }
1139 } else {
1140 None
1141 };
1142
1143 div().min_w(px(220.)).max_w(px(540.)).child(
1144 ListItem::new(mat.candidate_id)
1145 .inset(true)
1146 .selected(item_ix == selected_item)
1147 .on_click(cx.listener(move |editor, _event, cx| {
1148 cx.stop_propagation();
1149 if let Some(task) = editor.confirm_completion(
1150 &ConfirmCompletion {
1151 item_ix: Some(item_ix),
1152 },
1153 cx,
1154 ) {
1155 task.detach_and_log_err(cx)
1156 }
1157 }))
1158 .child(h_flex().overflow_hidden().child(completion_label))
1159 .end_slot::<Div>(documentation_label),
1160 )
1161 })
1162 .collect()
1163 },
1164 )
1165 .occlude()
1166 .max_h(max_height)
1167 .track_scroll(self.scroll_handle.clone())
1168 .with_width_from_item(widest_completion_ix)
1169 .with_sizing_behavior(ListSizingBehavior::Infer);
1170
1171 Popover::new()
1172 .child(list)
1173 .when_some(multiline_docs, |popover, multiline_docs| {
1174 popover.aside(multiline_docs)
1175 })
1176 .into_any_element()
1177 }
1178
1179 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1180 let mut matches = if let Some(query) = query {
1181 fuzzy::match_strings(
1182 &self.match_candidates,
1183 query,
1184 query.chars().any(|c| c.is_uppercase()),
1185 100,
1186 &Default::default(),
1187 executor,
1188 )
1189 .await
1190 } else {
1191 self.match_candidates
1192 .iter()
1193 .enumerate()
1194 .map(|(candidate_id, candidate)| StringMatch {
1195 candidate_id,
1196 score: Default::default(),
1197 positions: Default::default(),
1198 string: candidate.string.clone(),
1199 })
1200 .collect()
1201 };
1202
1203 // Remove all candidates where the query's start does not match the start of any word in the candidate
1204 if let Some(query) = query {
1205 if let Some(query_start) = query.chars().next() {
1206 matches.retain(|string_match| {
1207 split_words(&string_match.string).any(|word| {
1208 // Check that the first codepoint of the word as lowercase matches the first
1209 // codepoint of the query as lowercase
1210 word.chars()
1211 .flat_map(|codepoint| codepoint.to_lowercase())
1212 .zip(query_start.to_lowercase())
1213 .all(|(word_cp, query_cp)| word_cp == query_cp)
1214 })
1215 });
1216 }
1217 }
1218
1219 let completions = self.completions.read();
1220 matches.sort_unstable_by_key(|mat| {
1221 // We do want to strike a balance here between what the language server tells us
1222 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1223 // `Creat` and there is a local variable called `CreateComponent`).
1224 // So what we do is: we bucket all matches into two buckets
1225 // - Strong matches
1226 // - Weak matches
1227 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1228 // and the Weak matches are the rest.
1229 //
1230 // For the strong matches, we sort by the language-servers score first and for the weak
1231 // matches, we prefer our fuzzy finder first.
1232 //
1233 // The thinking behind that: it's useless to take the sort_text the language-server gives
1234 // us into account when it's obviously a bad match.
1235
1236 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1237 enum MatchScore<'a> {
1238 Strong {
1239 sort_text: Option<&'a str>,
1240 score: Reverse<OrderedFloat<f64>>,
1241 sort_key: (usize, &'a str),
1242 },
1243 Weak {
1244 score: Reverse<OrderedFloat<f64>>,
1245 sort_text: Option<&'a str>,
1246 sort_key: (usize, &'a str),
1247 },
1248 }
1249
1250 let completion = &completions[mat.candidate_id];
1251 let sort_key = completion.sort_key();
1252 let sort_text = completion.lsp_completion.sort_text.as_deref();
1253 let score = Reverse(OrderedFloat(mat.score));
1254
1255 if mat.score >= 0.2 {
1256 MatchScore::Strong {
1257 sort_text,
1258 score,
1259 sort_key,
1260 }
1261 } else {
1262 MatchScore::Weak {
1263 score,
1264 sort_text,
1265 sort_key,
1266 }
1267 }
1268 });
1269
1270 for mat in &mut matches {
1271 let completion = &completions[mat.candidate_id];
1272 mat.string.clone_from(&completion.label.text);
1273 for position in &mut mat.positions {
1274 *position += completion.label.filter_range.start;
1275 }
1276 }
1277 drop(completions);
1278
1279 self.matches = matches.into();
1280 self.selected_item = 0;
1281 }
1282}
1283
1284#[derive(Clone)]
1285struct CodeActionContents {
1286 tasks: Option<Arc<ResolvedTasks>>,
1287 actions: Option<Arc<[CodeAction]>>,
1288}
1289
1290impl CodeActionContents {
1291 fn len(&self) -> usize {
1292 match (&self.tasks, &self.actions) {
1293 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1294 (Some(tasks), None) => tasks.templates.len(),
1295 (None, Some(actions)) => actions.len(),
1296 (None, None) => 0,
1297 }
1298 }
1299
1300 fn is_empty(&self) -> bool {
1301 match (&self.tasks, &self.actions) {
1302 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1303 (Some(tasks), None) => tasks.templates.is_empty(),
1304 (None, Some(actions)) => actions.is_empty(),
1305 (None, None) => true,
1306 }
1307 }
1308
1309 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1310 self.tasks
1311 .iter()
1312 .flat_map(|tasks| {
1313 tasks
1314 .templates
1315 .iter()
1316 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1317 })
1318 .chain(self.actions.iter().flat_map(|actions| {
1319 actions
1320 .iter()
1321 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1322 }))
1323 }
1324 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1325 match (&self.tasks, &self.actions) {
1326 (Some(tasks), Some(actions)) => {
1327 if index < tasks.templates.len() {
1328 tasks
1329 .templates
1330 .get(index)
1331 .cloned()
1332 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1333 } else {
1334 actions
1335 .get(index - tasks.templates.len())
1336 .cloned()
1337 .map(CodeActionsItem::CodeAction)
1338 }
1339 }
1340 (Some(tasks), None) => tasks
1341 .templates
1342 .get(index)
1343 .cloned()
1344 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1345 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1346 (None, None) => None,
1347 }
1348 }
1349}
1350
1351#[allow(clippy::large_enum_variant)]
1352#[derive(Clone)]
1353enum CodeActionsItem {
1354 Task(TaskSourceKind, ResolvedTask),
1355 CodeAction(CodeAction),
1356}
1357
1358impl CodeActionsItem {
1359 fn as_task(&self) -> Option<&ResolvedTask> {
1360 let Self::Task(_, task) = self else {
1361 return None;
1362 };
1363 Some(task)
1364 }
1365 fn as_code_action(&self) -> Option<&CodeAction> {
1366 let Self::CodeAction(action) = self else {
1367 return None;
1368 };
1369 Some(action)
1370 }
1371 fn label(&self) -> String {
1372 match self {
1373 Self::CodeAction(action) => action.lsp_action.title.clone(),
1374 Self::Task(_, task) => task.resolved_label.clone(),
1375 }
1376 }
1377}
1378
1379struct CodeActionsMenu {
1380 actions: CodeActionContents,
1381 buffer: Model<Buffer>,
1382 selected_item: usize,
1383 scroll_handle: UniformListScrollHandle,
1384 deployed_from_indicator: Option<DisplayRow>,
1385}
1386
1387impl CodeActionsMenu {
1388 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1389 self.selected_item = 0;
1390 self.scroll_handle.scroll_to_item(self.selected_item);
1391 cx.notify()
1392 }
1393
1394 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1395 if self.selected_item > 0 {
1396 self.selected_item -= 1;
1397 } else {
1398 self.selected_item = self.actions.len() - 1;
1399 }
1400 self.scroll_handle.scroll_to_item(self.selected_item);
1401 cx.notify();
1402 }
1403
1404 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1405 if self.selected_item + 1 < self.actions.len() {
1406 self.selected_item += 1;
1407 } else {
1408 self.selected_item = 0;
1409 }
1410 self.scroll_handle.scroll_to_item(self.selected_item);
1411 cx.notify();
1412 }
1413
1414 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1415 self.selected_item = self.actions.len() - 1;
1416 self.scroll_handle.scroll_to_item(self.selected_item);
1417 cx.notify()
1418 }
1419
1420 fn visible(&self) -> bool {
1421 !self.actions.is_empty()
1422 }
1423
1424 fn render(
1425 &self,
1426 cursor_position: DisplayPoint,
1427 _style: &EditorStyle,
1428 max_height: Pixels,
1429 cx: &mut ViewContext<Editor>,
1430 ) -> (ContextMenuOrigin, AnyElement) {
1431 let actions = self.actions.clone();
1432 let selected_item = self.selected_item;
1433 let element = uniform_list(
1434 cx.view().clone(),
1435 "code_actions_menu",
1436 self.actions.len(),
1437 move |_this, range, cx| {
1438 actions
1439 .iter()
1440 .skip(range.start)
1441 .take(range.end - range.start)
1442 .enumerate()
1443 .map(|(ix, action)| {
1444 let item_ix = range.start + ix;
1445 let selected = selected_item == item_ix;
1446 let colors = cx.theme().colors();
1447 div()
1448 .px_2()
1449 .text_color(colors.text)
1450 .when(selected, |style| {
1451 style
1452 .bg(colors.element_active)
1453 .text_color(colors.text_accent)
1454 })
1455 .hover(|style| {
1456 style
1457 .bg(colors.element_hover)
1458 .text_color(colors.text_accent)
1459 })
1460 .whitespace_nowrap()
1461 .when_some(action.as_code_action(), |this, action| {
1462 this.on_mouse_down(
1463 MouseButton::Left,
1464 cx.listener(move |editor, _, cx| {
1465 cx.stop_propagation();
1466 if let Some(task) = editor.confirm_code_action(
1467 &ConfirmCodeAction {
1468 item_ix: Some(item_ix),
1469 },
1470 cx,
1471 ) {
1472 task.detach_and_log_err(cx)
1473 }
1474 }),
1475 )
1476 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1477 .child(SharedString::from(action.lsp_action.title.clone()))
1478 })
1479 .when_some(action.as_task(), |this, task| {
1480 this.on_mouse_down(
1481 MouseButton::Left,
1482 cx.listener(move |editor, _, cx| {
1483 cx.stop_propagation();
1484 if let Some(task) = editor.confirm_code_action(
1485 &ConfirmCodeAction {
1486 item_ix: Some(item_ix),
1487 },
1488 cx,
1489 ) {
1490 task.detach_and_log_err(cx)
1491 }
1492 }),
1493 )
1494 .child(SharedString::from(task.resolved_label.clone()))
1495 })
1496 })
1497 .collect()
1498 },
1499 )
1500 .elevation_1(cx)
1501 .px_2()
1502 .py_1()
1503 .max_h(max_height)
1504 .occlude()
1505 .track_scroll(self.scroll_handle.clone())
1506 .with_width_from_item(
1507 self.actions
1508 .iter()
1509 .enumerate()
1510 .max_by_key(|(_, action)| match action {
1511 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1512 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1513 })
1514 .map(|(ix, _)| ix),
1515 )
1516 .with_sizing_behavior(ListSizingBehavior::Infer)
1517 .into_any_element();
1518
1519 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1520 ContextMenuOrigin::GutterIndicator(row)
1521 } else {
1522 ContextMenuOrigin::EditorPoint(cursor_position)
1523 };
1524
1525 (cursor_position, element)
1526 }
1527}
1528
1529#[derive(Debug)]
1530struct ActiveDiagnosticGroup {
1531 primary_range: Range<Anchor>,
1532 primary_message: String,
1533 group_id: usize,
1534 blocks: HashMap<BlockId, Diagnostic>,
1535 is_valid: bool,
1536}
1537
1538#[derive(Serialize, Deserialize, Clone, Debug)]
1539pub struct ClipboardSelection {
1540 pub len: usize,
1541 pub is_entire_line: bool,
1542 pub first_line_indent: u32,
1543}
1544
1545#[derive(Debug)]
1546pub(crate) struct NavigationData {
1547 cursor_anchor: Anchor,
1548 cursor_position: Point,
1549 scroll_anchor: ScrollAnchor,
1550 scroll_top_row: u32,
1551}
1552
1553enum GotoDefinitionKind {
1554 Symbol,
1555 Type,
1556 Implementation,
1557}
1558
1559#[derive(Debug, Clone)]
1560enum InlayHintRefreshReason {
1561 Toggle(bool),
1562 SettingsChange(InlayHintSettings),
1563 NewLinesShown,
1564 BufferEdited(HashSet<Arc<Language>>),
1565 RefreshRequested,
1566 ExcerptsRemoved(Vec<ExcerptId>),
1567}
1568
1569impl InlayHintRefreshReason {
1570 fn description(&self) -> &'static str {
1571 match self {
1572 Self::Toggle(_) => "toggle",
1573 Self::SettingsChange(_) => "settings change",
1574 Self::NewLinesShown => "new lines shown",
1575 Self::BufferEdited(_) => "buffer edited",
1576 Self::RefreshRequested => "refresh requested",
1577 Self::ExcerptsRemoved(_) => "excerpts removed",
1578 }
1579 }
1580}
1581
1582impl Editor {
1583 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1584 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1585 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1586 Self::new(
1587 EditorMode::SingleLine { auto_width: false },
1588 buffer,
1589 None,
1590 false,
1591 cx,
1592 )
1593 }
1594
1595 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1596 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1597 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1598 Self::new(EditorMode::Full, buffer, None, false, cx)
1599 }
1600
1601 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1602 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1603 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1604 Self::new(
1605 EditorMode::SingleLine { auto_width: true },
1606 buffer,
1607 None,
1608 false,
1609 cx,
1610 )
1611 }
1612
1613 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1614 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1615 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1616 Self::new(
1617 EditorMode::AutoHeight { max_lines },
1618 buffer,
1619 None,
1620 false,
1621 cx,
1622 )
1623 }
1624
1625 pub fn for_buffer(
1626 buffer: Model<Buffer>,
1627 project: Option<Model<Project>>,
1628 cx: &mut ViewContext<Self>,
1629 ) -> Self {
1630 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1631 Self::new(EditorMode::Full, buffer, project, false, cx)
1632 }
1633
1634 pub fn for_multibuffer(
1635 buffer: Model<MultiBuffer>,
1636 project: Option<Model<Project>>,
1637 show_excerpt_controls: bool,
1638 cx: &mut ViewContext<Self>,
1639 ) -> Self {
1640 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1641 }
1642
1643 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1644 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1645 let mut clone = Self::new(
1646 self.mode,
1647 self.buffer.clone(),
1648 self.project.clone(),
1649 show_excerpt_controls,
1650 cx,
1651 );
1652 self.display_map.update(cx, |display_map, cx| {
1653 let snapshot = display_map.snapshot(cx);
1654 clone.display_map.update(cx, |display_map, cx| {
1655 display_map.set_state(&snapshot, cx);
1656 });
1657 });
1658 clone.selections.clone_state(&self.selections);
1659 clone.scroll_manager.clone_state(&self.scroll_manager);
1660 clone.searchable = self.searchable;
1661 clone
1662 }
1663
1664 pub fn new(
1665 mode: EditorMode,
1666 buffer: Model<MultiBuffer>,
1667 project: Option<Model<Project>>,
1668 show_excerpt_controls: bool,
1669 cx: &mut ViewContext<Self>,
1670 ) -> Self {
1671 let style = cx.text_style();
1672 let font_size = style.font_size.to_pixels(cx.rem_size());
1673 let editor = cx.view().downgrade();
1674 let fold_placeholder = FoldPlaceholder {
1675 constrain_width: true,
1676 render: Arc::new(move |fold_id, fold_range, cx| {
1677 let editor = editor.clone();
1678 div()
1679 .id(fold_id)
1680 .bg(cx.theme().colors().ghost_element_background)
1681 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1682 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1683 .rounded_sm()
1684 .size_full()
1685 .cursor_pointer()
1686 .child("⋯")
1687 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1688 .on_click(move |_, cx| {
1689 editor
1690 .update(cx, |editor, cx| {
1691 editor.unfold_ranges(
1692 [fold_range.start..fold_range.end],
1693 true,
1694 false,
1695 cx,
1696 );
1697 cx.stop_propagation();
1698 })
1699 .ok();
1700 })
1701 .into_any()
1702 }),
1703 merge_adjacent: true,
1704 };
1705 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1706 let display_map = cx.new_model(|cx| {
1707 DisplayMap::new(
1708 buffer.clone(),
1709 style.font(),
1710 font_size,
1711 None,
1712 show_excerpt_controls,
1713 file_header_size,
1714 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1715 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1716 fold_placeholder,
1717 cx,
1718 )
1719 });
1720
1721 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1722
1723 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1724
1725 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1726 .then(|| language_settings::SoftWrap::PreferLine);
1727
1728 let mut project_subscriptions = Vec::new();
1729 if mode == EditorMode::Full {
1730 if let Some(project) = project.as_ref() {
1731 if buffer.read(cx).is_singleton() {
1732 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1733 cx.emit(EditorEvent::TitleChanged);
1734 }));
1735 }
1736 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1737 if let project::Event::RefreshInlayHints = event {
1738 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1739 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1740 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1741 let focus_handle = editor.focus_handle(cx);
1742 if focus_handle.is_focused(cx) {
1743 let snapshot = buffer.read(cx).snapshot();
1744 for (range, snippet) in snippet_edits {
1745 let editor_range =
1746 language::range_from_lsp(*range).to_offset(&snapshot);
1747 editor
1748 .insert_snippet(&[editor_range], snippet.clone(), cx)
1749 .ok();
1750 }
1751 }
1752 }
1753 }
1754 }));
1755 let task_inventory = project.read(cx).task_inventory().clone();
1756 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1757 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1758 }));
1759 }
1760 }
1761
1762 let inlay_hint_settings = inlay_hint_settings(
1763 selections.newest_anchor().head(),
1764 &buffer.read(cx).snapshot(cx),
1765 cx,
1766 );
1767 let focus_handle = cx.focus_handle();
1768 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1769 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1770 .detach();
1771 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1772
1773 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1774 Some(false)
1775 } else {
1776 None
1777 };
1778
1779 let mut this = Self {
1780 focus_handle,
1781 show_cursor_when_unfocused: false,
1782 last_focused_descendant: None,
1783 buffer: buffer.clone(),
1784 display_map: display_map.clone(),
1785 selections,
1786 scroll_manager: ScrollManager::new(cx),
1787 columnar_selection_tail: None,
1788 add_selections_state: None,
1789 select_next_state: None,
1790 select_prev_state: None,
1791 selection_history: Default::default(),
1792 autoclose_regions: Default::default(),
1793 snippet_stack: Default::default(),
1794 select_larger_syntax_node_stack: Vec::new(),
1795 ime_transaction: Default::default(),
1796 active_diagnostics: None,
1797 soft_wrap_mode_override,
1798 completion_provider: project.clone().map(|project| Box::new(project) as _),
1799 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1800 project,
1801 blink_manager: blink_manager.clone(),
1802 show_local_selections: true,
1803 mode,
1804 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1805 show_gutter: mode == EditorMode::Full,
1806 show_line_numbers: None,
1807 show_git_diff_gutter: None,
1808 show_code_actions: None,
1809 show_runnables: None,
1810 show_wrap_guides: None,
1811 show_indent_guides,
1812 placeholder_text: None,
1813 highlight_order: 0,
1814 highlighted_rows: HashMap::default(),
1815 background_highlights: Default::default(),
1816 gutter_highlights: TreeMap::default(),
1817 scrollbar_marker_state: ScrollbarMarkerState::default(),
1818 active_indent_guides_state: ActiveIndentGuidesState::default(),
1819 nav_history: None,
1820 context_menu: RwLock::new(None),
1821 mouse_context_menu: None,
1822 completion_tasks: Default::default(),
1823 find_all_references_task_sources: Vec::new(),
1824 next_completion_id: 0,
1825 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1826 next_inlay_id: 0,
1827 available_code_actions: Default::default(),
1828 code_actions_task: Default::default(),
1829 document_highlights_task: Default::default(),
1830 linked_editing_range_task: Default::default(),
1831 pending_rename: Default::default(),
1832 searchable: true,
1833 cursor_shape: Default::default(),
1834 current_line_highlight: None,
1835 autoindent_mode: Some(AutoindentMode::EachLine),
1836 collapse_matches: false,
1837 workspace: None,
1838 keymap_context_layers: Default::default(),
1839 input_enabled: true,
1840 use_modal_editing: mode == EditorMode::Full,
1841 read_only: false,
1842 use_autoclose: true,
1843 use_auto_surround: true,
1844 auto_replace_emoji_shortcode: false,
1845 leader_peer_id: None,
1846 remote_id: None,
1847 hover_state: Default::default(),
1848 hovered_link_state: Default::default(),
1849 inline_completion_provider: None,
1850 active_inline_completion: None,
1851 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1852 expanded_hunks: ExpandedHunks::default(),
1853 gutter_hovered: false,
1854 pixel_position_of_newest_cursor: None,
1855 last_bounds: None,
1856 expect_bounds_change: None,
1857 gutter_dimensions: GutterDimensions::default(),
1858 style: None,
1859 show_cursor_names: false,
1860 hovered_cursors: Default::default(),
1861 next_editor_action_id: EditorActionId::default(),
1862 editor_actions: Rc::default(),
1863 vim_replace_map: Default::default(),
1864 show_inline_completions: mode == EditorMode::Full,
1865 custom_context_menu: None,
1866 show_git_blame_gutter: false,
1867 show_git_blame_inline: false,
1868 show_selection_menu: None,
1869 show_git_blame_inline_delay_task: None,
1870 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1871 blame: None,
1872 blame_subscription: None,
1873 file_header_size,
1874 tasks: Default::default(),
1875 _subscriptions: vec![
1876 cx.observe(&buffer, Self::on_buffer_changed),
1877 cx.subscribe(&buffer, Self::on_buffer_event),
1878 cx.observe(&display_map, Self::on_display_map_changed),
1879 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1880 cx.observe_global::<SettingsStore>(Self::settings_changed),
1881 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1882 cx.observe_window_activation(|editor, cx| {
1883 let active = cx.is_window_active();
1884 editor.blink_manager.update(cx, |blink_manager, cx| {
1885 if active {
1886 blink_manager.enable(cx);
1887 } else {
1888 blink_manager.show_cursor(cx);
1889 blink_manager.disable(cx);
1890 }
1891 });
1892 }),
1893 ],
1894 tasks_update_task: None,
1895 linked_edit_ranges: Default::default(),
1896 previous_search_ranges: None,
1897 breadcrumb_header: None,
1898 };
1899 this.tasks_update_task = Some(this.refresh_runnables(cx));
1900 this._subscriptions.extend(project_subscriptions);
1901
1902 this.end_selection(cx);
1903 this.scroll_manager.show_scrollbar(cx);
1904
1905 if mode == EditorMode::Full {
1906 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1907 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1908
1909 if this.git_blame_inline_enabled {
1910 this.git_blame_inline_enabled = true;
1911 this.start_git_blame_inline(false, cx);
1912 }
1913 }
1914
1915 this.report_editor_event("open", None, cx);
1916 this
1917 }
1918
1919 pub fn mouse_menu_is_focused(&self, cx: &mut WindowContext) -> bool {
1920 self.mouse_context_menu
1921 .as_ref()
1922 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1923 }
1924
1925 fn key_context(&self, cx: &AppContext) -> KeyContext {
1926 let mut key_context = KeyContext::new_with_defaults();
1927 key_context.add("Editor");
1928 let mode = match self.mode {
1929 EditorMode::SingleLine { .. } => "single_line",
1930 EditorMode::AutoHeight { .. } => "auto_height",
1931 EditorMode::Full => "full",
1932 };
1933
1934 if EditorSettings::get_global(cx).jupyter.enabled {
1935 key_context.add("jupyter");
1936 }
1937
1938 key_context.set("mode", mode);
1939 if self.pending_rename.is_some() {
1940 key_context.add("renaming");
1941 }
1942 if self.context_menu_visible() {
1943 match self.context_menu.read().as_ref() {
1944 Some(ContextMenu::Completions(_)) => {
1945 key_context.add("menu");
1946 key_context.add("showing_completions")
1947 }
1948 Some(ContextMenu::CodeActions(_)) => {
1949 key_context.add("menu");
1950 key_context.add("showing_code_actions")
1951 }
1952 None => {}
1953 }
1954 }
1955
1956 for layer in self.keymap_context_layers.values() {
1957 key_context.extend(layer);
1958 }
1959
1960 if let Some(extension) = self
1961 .buffer
1962 .read(cx)
1963 .as_singleton()
1964 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1965 {
1966 key_context.set("extension", extension.to_string());
1967 }
1968
1969 if self.has_active_inline_completion(cx) {
1970 key_context.add("copilot_suggestion");
1971 key_context.add("inline_completion");
1972 }
1973
1974 key_context
1975 }
1976
1977 pub fn new_file(
1978 workspace: &mut Workspace,
1979 _: &workspace::NewFile,
1980 cx: &mut ViewContext<Workspace>,
1981 ) {
1982 let project = workspace.project().clone();
1983 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1984
1985 cx.spawn(|workspace, mut cx| async move {
1986 let buffer = create.await?;
1987 workspace.update(&mut cx, |workspace, cx| {
1988 workspace.add_item_to_active_pane(
1989 Box::new(
1990 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1991 ),
1992 None,
1993 cx,
1994 )
1995 })
1996 })
1997 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1998 ErrorCode::RemoteUpgradeRequired => Some(format!(
1999 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2000 e.error_tag("required").unwrap_or("the latest version")
2001 )),
2002 _ => None,
2003 });
2004 }
2005
2006 pub fn new_file_in_direction(
2007 workspace: &mut Workspace,
2008 action: &workspace::NewFileInDirection,
2009 cx: &mut ViewContext<Workspace>,
2010 ) {
2011 let project = workspace.project().clone();
2012 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2013 let direction = action.0;
2014
2015 cx.spawn(|workspace, mut cx| async move {
2016 let buffer = create.await?;
2017 workspace.update(&mut cx, move |workspace, cx| {
2018 workspace.split_item(
2019 direction,
2020 Box::new(
2021 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2022 ),
2023 cx,
2024 )
2025 })?;
2026 anyhow::Ok(())
2027 })
2028 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2029 ErrorCode::RemoteUpgradeRequired => Some(format!(
2030 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2031 e.error_tag("required").unwrap_or("the latest version")
2032 )),
2033 _ => None,
2034 });
2035 }
2036
2037 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2038 self.buffer.read(cx).replica_id()
2039 }
2040
2041 pub fn leader_peer_id(&self) -> Option<PeerId> {
2042 self.leader_peer_id
2043 }
2044
2045 pub fn buffer(&self) -> &Model<MultiBuffer> {
2046 &self.buffer
2047 }
2048
2049 pub fn workspace(&self) -> Option<View<Workspace>> {
2050 self.workspace.as_ref()?.0.upgrade()
2051 }
2052
2053 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2054 self.buffer().read(cx).title(cx)
2055 }
2056
2057 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2058 EditorSnapshot {
2059 mode: self.mode,
2060 show_gutter: self.show_gutter,
2061 show_line_numbers: self.show_line_numbers,
2062 show_git_diff_gutter: self.show_git_diff_gutter,
2063 show_code_actions: self.show_code_actions,
2064 show_runnables: self.show_runnables,
2065 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2066 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2067 scroll_anchor: self.scroll_manager.anchor(),
2068 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2069 placeholder_text: self.placeholder_text.clone(),
2070 is_focused: self.focus_handle.is_focused(cx),
2071 current_line_highlight: self
2072 .current_line_highlight
2073 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2074 gutter_hovered: self.gutter_hovered,
2075 }
2076 }
2077
2078 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2079 self.buffer.read(cx).language_at(point, cx)
2080 }
2081
2082 pub fn file_at<T: ToOffset>(
2083 &self,
2084 point: T,
2085 cx: &AppContext,
2086 ) -> Option<Arc<dyn language::File>> {
2087 self.buffer.read(cx).read(cx).file_at(point).cloned()
2088 }
2089
2090 pub fn active_excerpt(
2091 &self,
2092 cx: &AppContext,
2093 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2094 self.buffer
2095 .read(cx)
2096 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2097 }
2098
2099 pub fn mode(&self) -> EditorMode {
2100 self.mode
2101 }
2102
2103 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2104 self.collaboration_hub.as_deref()
2105 }
2106
2107 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2108 self.collaboration_hub = Some(hub);
2109 }
2110
2111 pub fn set_custom_context_menu(
2112 &mut self,
2113 f: impl 'static
2114 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2115 ) {
2116 self.custom_context_menu = Some(Box::new(f))
2117 }
2118
2119 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2120 self.completion_provider = Some(provider);
2121 }
2122
2123 pub fn set_inline_completion_provider<T>(
2124 &mut self,
2125 provider: Option<Model<T>>,
2126 cx: &mut ViewContext<Self>,
2127 ) where
2128 T: InlineCompletionProvider,
2129 {
2130 self.inline_completion_provider =
2131 provider.map(|provider| RegisteredInlineCompletionProvider {
2132 _subscription: cx.observe(&provider, |this, _, cx| {
2133 if this.focus_handle.is_focused(cx) {
2134 this.update_visible_inline_completion(cx);
2135 }
2136 }),
2137 provider: Arc::new(provider),
2138 });
2139 self.refresh_inline_completion(false, cx);
2140 }
2141
2142 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2143 self.placeholder_text.as_deref()
2144 }
2145
2146 pub fn set_placeholder_text(
2147 &mut self,
2148 placeholder_text: impl Into<Arc<str>>,
2149 cx: &mut ViewContext<Self>,
2150 ) {
2151 let placeholder_text = Some(placeholder_text.into());
2152 if self.placeholder_text != placeholder_text {
2153 self.placeholder_text = placeholder_text;
2154 cx.notify();
2155 }
2156 }
2157
2158 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2159 self.cursor_shape = cursor_shape;
2160 cx.notify();
2161 }
2162
2163 pub fn set_current_line_highlight(
2164 &mut self,
2165 current_line_highlight: Option<CurrentLineHighlight>,
2166 ) {
2167 self.current_line_highlight = current_line_highlight;
2168 }
2169
2170 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2171 self.collapse_matches = collapse_matches;
2172 }
2173
2174 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2175 if self.collapse_matches {
2176 return range.start..range.start;
2177 }
2178 range.clone()
2179 }
2180
2181 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2182 if self.display_map.read(cx).clip_at_line_ends != clip {
2183 self.display_map
2184 .update(cx, |map, _| map.clip_at_line_ends = clip);
2185 }
2186 }
2187
2188 pub fn set_keymap_context_layer<Tag: 'static>(
2189 &mut self,
2190 context: KeyContext,
2191 cx: &mut ViewContext<Self>,
2192 ) {
2193 self.keymap_context_layers
2194 .insert(TypeId::of::<Tag>(), context);
2195 cx.notify();
2196 }
2197
2198 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
2199 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
2200 cx.notify();
2201 }
2202
2203 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2204 self.input_enabled = input_enabled;
2205 }
2206
2207 pub fn set_autoindent(&mut self, autoindent: bool) {
2208 if autoindent {
2209 self.autoindent_mode = Some(AutoindentMode::EachLine);
2210 } else {
2211 self.autoindent_mode = None;
2212 }
2213 }
2214
2215 pub fn read_only(&self, cx: &AppContext) -> bool {
2216 self.read_only || self.buffer.read(cx).read_only()
2217 }
2218
2219 pub fn set_read_only(&mut self, read_only: bool) {
2220 self.read_only = read_only;
2221 }
2222
2223 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2224 self.use_autoclose = autoclose;
2225 }
2226
2227 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2228 self.use_auto_surround = auto_surround;
2229 }
2230
2231 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2232 self.auto_replace_emoji_shortcode = auto_replace;
2233 }
2234
2235 pub fn set_show_inline_completions(&mut self, show_inline_completions: bool) {
2236 self.show_inline_completions = show_inline_completions;
2237 }
2238
2239 pub fn set_use_modal_editing(&mut self, to: bool) {
2240 self.use_modal_editing = to;
2241 }
2242
2243 pub fn use_modal_editing(&self) -> bool {
2244 self.use_modal_editing
2245 }
2246
2247 fn selections_did_change(
2248 &mut self,
2249 local: bool,
2250 old_cursor_position: &Anchor,
2251 show_completions: bool,
2252 cx: &mut ViewContext<Self>,
2253 ) {
2254 // Copy selections to primary selection buffer
2255 #[cfg(target_os = "linux")]
2256 if local {
2257 let selections = self.selections.all::<usize>(cx);
2258 let buffer_handle = self.buffer.read(cx).read(cx);
2259
2260 let mut text = String::new();
2261 for (index, selection) in selections.iter().enumerate() {
2262 let text_for_selection = buffer_handle
2263 .text_for_range(selection.start..selection.end)
2264 .collect::<String>();
2265
2266 text.push_str(&text_for_selection);
2267 if index != selections.len() - 1 {
2268 text.push('\n');
2269 }
2270 }
2271
2272 if !text.is_empty() {
2273 cx.write_to_primary(ClipboardItem::new(text));
2274 }
2275 }
2276
2277 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2278 self.buffer.update(cx, |buffer, cx| {
2279 buffer.set_active_selections(
2280 &self.selections.disjoint_anchors(),
2281 self.selections.line_mode,
2282 self.cursor_shape,
2283 cx,
2284 )
2285 });
2286 }
2287 let display_map = self
2288 .display_map
2289 .update(cx, |display_map, cx| display_map.snapshot(cx));
2290 let buffer = &display_map.buffer_snapshot;
2291 self.add_selections_state = None;
2292 self.select_next_state = None;
2293 self.select_prev_state = None;
2294 self.select_larger_syntax_node_stack.clear();
2295 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2296 self.snippet_stack
2297 .invalidate(&self.selections.disjoint_anchors(), buffer);
2298 self.take_rename(false, cx);
2299
2300 let new_cursor_position = self.selections.newest_anchor().head();
2301
2302 self.push_to_nav_history(
2303 *old_cursor_position,
2304 Some(new_cursor_position.to_point(buffer)),
2305 cx,
2306 );
2307
2308 if local {
2309 let new_cursor_position = self.selections.newest_anchor().head();
2310 let mut context_menu = self.context_menu.write();
2311 let completion_menu = match context_menu.as_ref() {
2312 Some(ContextMenu::Completions(menu)) => Some(menu),
2313
2314 _ => {
2315 *context_menu = None;
2316 None
2317 }
2318 };
2319
2320 if let Some(completion_menu) = completion_menu {
2321 let cursor_position = new_cursor_position.to_offset(buffer);
2322 let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
2323 if kind == Some(CharKind::Word)
2324 && word_range.to_inclusive().contains(&cursor_position)
2325 {
2326 let mut completion_menu = completion_menu.clone();
2327 drop(context_menu);
2328
2329 let query = Self::completion_query(buffer, cursor_position);
2330 cx.spawn(move |this, mut cx| async move {
2331 completion_menu
2332 .filter(query.as_deref(), cx.background_executor().clone())
2333 .await;
2334
2335 this.update(&mut cx, |this, cx| {
2336 let mut context_menu = this.context_menu.write();
2337 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2338 return;
2339 };
2340
2341 if menu.id > completion_menu.id {
2342 return;
2343 }
2344
2345 *context_menu = Some(ContextMenu::Completions(completion_menu));
2346 drop(context_menu);
2347 cx.notify();
2348 })
2349 })
2350 .detach();
2351
2352 if show_completions {
2353 self.show_completions(&ShowCompletions { trigger: None }, cx);
2354 }
2355 } else {
2356 drop(context_menu);
2357 self.hide_context_menu(cx);
2358 }
2359 } else {
2360 drop(context_menu);
2361 }
2362
2363 hide_hover(self, cx);
2364
2365 if old_cursor_position.to_display_point(&display_map).row()
2366 != new_cursor_position.to_display_point(&display_map).row()
2367 {
2368 self.available_code_actions.take();
2369 }
2370 self.refresh_code_actions(cx);
2371 self.refresh_document_highlights(cx);
2372 refresh_matching_bracket_highlights(self, cx);
2373 self.discard_inline_completion(false, cx);
2374 linked_editing_ranges::refresh_linked_ranges(self, cx);
2375 if self.git_blame_inline_enabled {
2376 self.start_inline_blame_timer(cx);
2377 }
2378 }
2379
2380 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2381 cx.emit(EditorEvent::SelectionsChanged { local });
2382
2383 if self.selections.disjoint_anchors().len() == 1 {
2384 cx.emit(SearchEvent::ActiveMatchChanged)
2385 }
2386 cx.notify();
2387 }
2388
2389 pub fn change_selections<R>(
2390 &mut self,
2391 autoscroll: Option<Autoscroll>,
2392 cx: &mut ViewContext<Self>,
2393 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2394 ) -> R {
2395 self.change_selections_inner(autoscroll, true, cx, change)
2396 }
2397
2398 pub fn change_selections_inner<R>(
2399 &mut self,
2400 autoscroll: Option<Autoscroll>,
2401 request_completions: bool,
2402 cx: &mut ViewContext<Self>,
2403 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2404 ) -> R {
2405 let old_cursor_position = self.selections.newest_anchor().head();
2406 self.push_to_selection_history();
2407
2408 let (changed, result) = self.selections.change_with(cx, change);
2409
2410 if changed {
2411 if let Some(autoscroll) = autoscroll {
2412 self.request_autoscroll(autoscroll, cx);
2413 }
2414 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2415 }
2416
2417 result
2418 }
2419
2420 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2421 where
2422 I: IntoIterator<Item = (Range<S>, T)>,
2423 S: ToOffset,
2424 T: Into<Arc<str>>,
2425 {
2426 if self.read_only(cx) {
2427 return;
2428 }
2429
2430 self.buffer
2431 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2432 }
2433
2434 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2435 where
2436 I: IntoIterator<Item = (Range<S>, T)>,
2437 S: ToOffset,
2438 T: Into<Arc<str>>,
2439 {
2440 if self.read_only(cx) {
2441 return;
2442 }
2443
2444 self.buffer.update(cx, |buffer, cx| {
2445 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2446 });
2447 }
2448
2449 pub fn edit_with_block_indent<I, S, T>(
2450 &mut self,
2451 edits: I,
2452 original_indent_columns: Vec<u32>,
2453 cx: &mut ViewContext<Self>,
2454 ) where
2455 I: IntoIterator<Item = (Range<S>, T)>,
2456 S: ToOffset,
2457 T: Into<Arc<str>>,
2458 {
2459 if self.read_only(cx) {
2460 return;
2461 }
2462
2463 self.buffer.update(cx, |buffer, cx| {
2464 buffer.edit(
2465 edits,
2466 Some(AutoindentMode::Block {
2467 original_indent_columns,
2468 }),
2469 cx,
2470 )
2471 });
2472 }
2473
2474 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2475 self.hide_context_menu(cx);
2476
2477 match phase {
2478 SelectPhase::Begin {
2479 position,
2480 add,
2481 click_count,
2482 } => self.begin_selection(position, add, click_count, cx),
2483 SelectPhase::BeginColumnar {
2484 position,
2485 goal_column,
2486 reset,
2487 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2488 SelectPhase::Extend {
2489 position,
2490 click_count,
2491 } => self.extend_selection(position, click_count, cx),
2492 SelectPhase::Update {
2493 position,
2494 goal_column,
2495 scroll_delta,
2496 } => self.update_selection(position, goal_column, scroll_delta, cx),
2497 SelectPhase::End => self.end_selection(cx),
2498 }
2499 }
2500
2501 fn extend_selection(
2502 &mut self,
2503 position: DisplayPoint,
2504 click_count: usize,
2505 cx: &mut ViewContext<Self>,
2506 ) {
2507 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2508 let tail = self.selections.newest::<usize>(cx).tail();
2509 self.begin_selection(position, false, click_count, cx);
2510
2511 let position = position.to_offset(&display_map, Bias::Left);
2512 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2513
2514 let mut pending_selection = self
2515 .selections
2516 .pending_anchor()
2517 .expect("extend_selection not called with pending selection");
2518 if position >= tail {
2519 pending_selection.start = tail_anchor;
2520 } else {
2521 pending_selection.end = tail_anchor;
2522 pending_selection.reversed = true;
2523 }
2524
2525 let mut pending_mode = self.selections.pending_mode().unwrap();
2526 match &mut pending_mode {
2527 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2528 _ => {}
2529 }
2530
2531 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2532 s.set_pending(pending_selection, pending_mode)
2533 });
2534 }
2535
2536 fn begin_selection(
2537 &mut self,
2538 position: DisplayPoint,
2539 add: bool,
2540 click_count: usize,
2541 cx: &mut ViewContext<Self>,
2542 ) {
2543 if !self.focus_handle.is_focused(cx) {
2544 self.last_focused_descendant = None;
2545 cx.focus(&self.focus_handle);
2546 }
2547
2548 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2549 let buffer = &display_map.buffer_snapshot;
2550 let newest_selection = self.selections.newest_anchor().clone();
2551 let position = display_map.clip_point(position, Bias::Left);
2552
2553 let start;
2554 let end;
2555 let mode;
2556 let auto_scroll;
2557 match click_count {
2558 1 => {
2559 start = buffer.anchor_before(position.to_point(&display_map));
2560 end = start;
2561 mode = SelectMode::Character;
2562 auto_scroll = true;
2563 }
2564 2 => {
2565 let range = movement::surrounding_word(&display_map, position);
2566 start = buffer.anchor_before(range.start.to_point(&display_map));
2567 end = buffer.anchor_before(range.end.to_point(&display_map));
2568 mode = SelectMode::Word(start..end);
2569 auto_scroll = true;
2570 }
2571 3 => {
2572 let position = display_map
2573 .clip_point(position, Bias::Left)
2574 .to_point(&display_map);
2575 let line_start = display_map.prev_line_boundary(position).0;
2576 let next_line_start = buffer.clip_point(
2577 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2578 Bias::Left,
2579 );
2580 start = buffer.anchor_before(line_start);
2581 end = buffer.anchor_before(next_line_start);
2582 mode = SelectMode::Line(start..end);
2583 auto_scroll = true;
2584 }
2585 _ => {
2586 start = buffer.anchor_before(0);
2587 end = buffer.anchor_before(buffer.len());
2588 mode = SelectMode::All;
2589 auto_scroll = false;
2590 }
2591 }
2592
2593 let point_to_delete: Option<usize> = {
2594 let selected_points: Vec<Selection<Point>> =
2595 self.selections.disjoint_in_range(start..end, cx);
2596
2597 if !add || click_count > 1 {
2598 None
2599 } else if selected_points.len() > 0 {
2600 Some(selected_points[0].id)
2601 } else {
2602 let clicked_point_already_selected =
2603 self.selections.disjoint.iter().find(|selection| {
2604 selection.start.to_point(buffer) == start.to_point(buffer)
2605 || selection.end.to_point(buffer) == end.to_point(buffer)
2606 });
2607
2608 if let Some(selection) = clicked_point_already_selected {
2609 Some(selection.id)
2610 } else {
2611 None
2612 }
2613 }
2614 };
2615
2616 let selections_count = self.selections.count();
2617
2618 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
2619 if let Some(point_to_delete) = point_to_delete {
2620 s.delete(point_to_delete);
2621
2622 if selections_count == 1 {
2623 s.set_pending_anchor_range(start..end, mode);
2624 }
2625 } else {
2626 if !add {
2627 s.clear_disjoint();
2628 } else if click_count > 1 {
2629 s.delete(newest_selection.id)
2630 }
2631
2632 s.set_pending_anchor_range(start..end, mode);
2633 }
2634 });
2635 }
2636
2637 fn begin_columnar_selection(
2638 &mut self,
2639 position: DisplayPoint,
2640 goal_column: u32,
2641 reset: bool,
2642 cx: &mut ViewContext<Self>,
2643 ) {
2644 if !self.focus_handle.is_focused(cx) {
2645 self.last_focused_descendant = None;
2646 cx.focus(&self.focus_handle);
2647 }
2648
2649 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2650
2651 if reset {
2652 let pointer_position = display_map
2653 .buffer_snapshot
2654 .anchor_before(position.to_point(&display_map));
2655
2656 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2657 s.clear_disjoint();
2658 s.set_pending_anchor_range(
2659 pointer_position..pointer_position,
2660 SelectMode::Character,
2661 );
2662 });
2663 }
2664
2665 let tail = self.selections.newest::<Point>(cx).tail();
2666 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2667
2668 if !reset {
2669 self.select_columns(
2670 tail.to_display_point(&display_map),
2671 position,
2672 goal_column,
2673 &display_map,
2674 cx,
2675 );
2676 }
2677 }
2678
2679 fn update_selection(
2680 &mut self,
2681 position: DisplayPoint,
2682 goal_column: u32,
2683 scroll_delta: gpui::Point<f32>,
2684 cx: &mut ViewContext<Self>,
2685 ) {
2686 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2687
2688 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2689 let tail = tail.to_display_point(&display_map);
2690 self.select_columns(tail, position, goal_column, &display_map, cx);
2691 } else if let Some(mut pending) = self.selections.pending_anchor() {
2692 let buffer = self.buffer.read(cx).snapshot(cx);
2693 let head;
2694 let tail;
2695 let mode = self.selections.pending_mode().unwrap();
2696 match &mode {
2697 SelectMode::Character => {
2698 head = position.to_point(&display_map);
2699 tail = pending.tail().to_point(&buffer);
2700 }
2701 SelectMode::Word(original_range) => {
2702 let original_display_range = original_range.start.to_display_point(&display_map)
2703 ..original_range.end.to_display_point(&display_map);
2704 let original_buffer_range = original_display_range.start.to_point(&display_map)
2705 ..original_display_range.end.to_point(&display_map);
2706 if movement::is_inside_word(&display_map, position)
2707 || original_display_range.contains(&position)
2708 {
2709 let word_range = movement::surrounding_word(&display_map, position);
2710 if word_range.start < original_display_range.start {
2711 head = word_range.start.to_point(&display_map);
2712 } else {
2713 head = word_range.end.to_point(&display_map);
2714 }
2715 } else {
2716 head = position.to_point(&display_map);
2717 }
2718
2719 if head <= original_buffer_range.start {
2720 tail = original_buffer_range.end;
2721 } else {
2722 tail = original_buffer_range.start;
2723 }
2724 }
2725 SelectMode::Line(original_range) => {
2726 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2727
2728 let position = display_map
2729 .clip_point(position, Bias::Left)
2730 .to_point(&display_map);
2731 let line_start = display_map.prev_line_boundary(position).0;
2732 let next_line_start = buffer.clip_point(
2733 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2734 Bias::Left,
2735 );
2736
2737 if line_start < original_range.start {
2738 head = line_start
2739 } else {
2740 head = next_line_start
2741 }
2742
2743 if head <= original_range.start {
2744 tail = original_range.end;
2745 } else {
2746 tail = original_range.start;
2747 }
2748 }
2749 SelectMode::All => {
2750 return;
2751 }
2752 };
2753
2754 if head < tail {
2755 pending.start = buffer.anchor_before(head);
2756 pending.end = buffer.anchor_before(tail);
2757 pending.reversed = true;
2758 } else {
2759 pending.start = buffer.anchor_before(tail);
2760 pending.end = buffer.anchor_before(head);
2761 pending.reversed = false;
2762 }
2763
2764 self.change_selections(None, cx, |s| {
2765 s.set_pending(pending, mode);
2766 });
2767 } else {
2768 log::error!("update_selection dispatched with no pending selection");
2769 return;
2770 }
2771
2772 self.apply_scroll_delta(scroll_delta, cx);
2773 cx.notify();
2774 }
2775
2776 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2777 self.columnar_selection_tail.take();
2778 if self.selections.pending_anchor().is_some() {
2779 let selections = self.selections.all::<usize>(cx);
2780 self.change_selections(None, cx, |s| {
2781 s.select(selections);
2782 s.clear_pending();
2783 });
2784 }
2785 }
2786
2787 fn select_columns(
2788 &mut self,
2789 tail: DisplayPoint,
2790 head: DisplayPoint,
2791 goal_column: u32,
2792 display_map: &DisplaySnapshot,
2793 cx: &mut ViewContext<Self>,
2794 ) {
2795 let start_row = cmp::min(tail.row(), head.row());
2796 let end_row = cmp::max(tail.row(), head.row());
2797 let start_column = cmp::min(tail.column(), goal_column);
2798 let end_column = cmp::max(tail.column(), goal_column);
2799 let reversed = start_column < tail.column();
2800
2801 let selection_ranges = (start_row.0..=end_row.0)
2802 .map(DisplayRow)
2803 .filter_map(|row| {
2804 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2805 let start = display_map
2806 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2807 .to_point(display_map);
2808 let end = display_map
2809 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2810 .to_point(display_map);
2811 if reversed {
2812 Some(end..start)
2813 } else {
2814 Some(start..end)
2815 }
2816 } else {
2817 None
2818 }
2819 })
2820 .collect::<Vec<_>>();
2821
2822 self.change_selections(None, cx, |s| {
2823 s.select_ranges(selection_ranges);
2824 });
2825 cx.notify();
2826 }
2827
2828 pub fn has_pending_nonempty_selection(&self) -> bool {
2829 let pending_nonempty_selection = match self.selections.pending_anchor() {
2830 Some(Selection { start, end, .. }) => start != end,
2831 None => false,
2832 };
2833
2834 pending_nonempty_selection
2835 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2836 }
2837
2838 pub fn has_pending_selection(&self) -> bool {
2839 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2840 }
2841
2842 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2843 self.clear_expanded_diff_hunks(cx);
2844 if self.dismiss_menus_and_popups(true, cx) {
2845 return;
2846 }
2847
2848 if self.mode == EditorMode::Full {
2849 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2850 return;
2851 }
2852 }
2853
2854 cx.propagate();
2855 }
2856
2857 pub fn dismiss_menus_and_popups(
2858 &mut self,
2859 should_report_inline_completion_event: bool,
2860 cx: &mut ViewContext<Self>,
2861 ) -> bool {
2862 if self.take_rename(false, cx).is_some() {
2863 return true;
2864 }
2865
2866 if hide_hover(self, cx) {
2867 return true;
2868 }
2869
2870 if self.hide_context_menu(cx).is_some() {
2871 return true;
2872 }
2873
2874 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2875 return true;
2876 }
2877
2878 if self.snippet_stack.pop().is_some() {
2879 return true;
2880 }
2881
2882 if self.mode == EditorMode::Full {
2883 if self.active_diagnostics.is_some() {
2884 self.dismiss_diagnostics(cx);
2885 return true;
2886 }
2887 }
2888
2889 false
2890 }
2891
2892 fn linked_editing_ranges_for(
2893 &self,
2894 selection: Range<text::Anchor>,
2895 cx: &AppContext,
2896 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2897 if self.linked_edit_ranges.is_empty() {
2898 return None;
2899 }
2900 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2901 selection.end.buffer_id.and_then(|end_buffer_id| {
2902 if selection.start.buffer_id != Some(end_buffer_id) {
2903 return None;
2904 }
2905 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2906 let snapshot = buffer.read(cx).snapshot();
2907 self.linked_edit_ranges
2908 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2909 .map(|ranges| (ranges, snapshot, buffer))
2910 })?;
2911 use text::ToOffset as TO;
2912 // find offset from the start of current range to current cursor position
2913 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2914
2915 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2916 let start_difference = start_offset - start_byte_offset;
2917 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2918 let end_difference = end_offset - start_byte_offset;
2919 // Current range has associated linked ranges.
2920 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2921 for range in linked_ranges.iter() {
2922 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2923 let end_offset = start_offset + end_difference;
2924 let start_offset = start_offset + start_difference;
2925 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2926 continue;
2927 }
2928 let start = buffer_snapshot.anchor_after(start_offset);
2929 let end = buffer_snapshot.anchor_after(end_offset);
2930 linked_edits
2931 .entry(buffer.clone())
2932 .or_default()
2933 .push(start..end);
2934 }
2935 Some(linked_edits)
2936 }
2937
2938 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2939 let text: Arc<str> = text.into();
2940
2941 if self.read_only(cx) {
2942 return;
2943 }
2944
2945 let selections = self.selections.all_adjusted(cx);
2946 let mut brace_inserted = false;
2947 let mut edits = Vec::new();
2948 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2949 let mut new_selections = Vec::with_capacity(selections.len());
2950 let mut new_autoclose_regions = Vec::new();
2951 let snapshot = self.buffer.read(cx).read(cx);
2952
2953 for (selection, autoclose_region) in
2954 self.selections_with_autoclose_regions(selections, &snapshot)
2955 {
2956 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2957 // Determine if the inserted text matches the opening or closing
2958 // bracket of any of this language's bracket pairs.
2959 let mut bracket_pair = None;
2960 let mut is_bracket_pair_start = false;
2961 let mut is_bracket_pair_end = false;
2962 if !text.is_empty() {
2963 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2964 // and they are removing the character that triggered IME popup.
2965 for (pair, enabled) in scope.brackets() {
2966 if !pair.close && !pair.surround {
2967 continue;
2968 }
2969
2970 if enabled && pair.start.ends_with(text.as_ref()) {
2971 bracket_pair = Some(pair.clone());
2972 is_bracket_pair_start = true;
2973 break;
2974 }
2975 if pair.end.as_str() == text.as_ref() {
2976 bracket_pair = Some(pair.clone());
2977 is_bracket_pair_end = true;
2978 break;
2979 }
2980 }
2981 }
2982
2983 if let Some(bracket_pair) = bracket_pair {
2984 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2985 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2986 let auto_surround =
2987 self.use_auto_surround && snapshot_settings.use_auto_surround;
2988 if selection.is_empty() {
2989 if is_bracket_pair_start {
2990 let prefix_len = bracket_pair.start.len() - text.len();
2991
2992 // If the inserted text is a suffix of an opening bracket and the
2993 // selection is preceded by the rest of the opening bracket, then
2994 // insert the closing bracket.
2995 let following_text_allows_autoclose = snapshot
2996 .chars_at(selection.start)
2997 .next()
2998 .map_or(true, |c| scope.should_autoclose_before(c));
2999 let preceding_text_matches_prefix = prefix_len == 0
3000 || (selection.start.column >= (prefix_len as u32)
3001 && snapshot.contains_str_at(
3002 Point::new(
3003 selection.start.row,
3004 selection.start.column - (prefix_len as u32),
3005 ),
3006 &bracket_pair.start[..prefix_len],
3007 ));
3008 if autoclose
3009 && bracket_pair.close
3010 && following_text_allows_autoclose
3011 && preceding_text_matches_prefix
3012 {
3013 let anchor = snapshot.anchor_before(selection.end);
3014 new_selections.push((selection.map(|_| anchor), text.len()));
3015 new_autoclose_regions.push((
3016 anchor,
3017 text.len(),
3018 selection.id,
3019 bracket_pair.clone(),
3020 ));
3021 edits.push((
3022 selection.range(),
3023 format!("{}{}", text, bracket_pair.end).into(),
3024 ));
3025 brace_inserted = true;
3026 continue;
3027 }
3028 }
3029
3030 if let Some(region) = autoclose_region {
3031 // If the selection is followed by an auto-inserted closing bracket,
3032 // then don't insert that closing bracket again; just move the selection
3033 // past the closing bracket.
3034 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3035 && text.as_ref() == region.pair.end.as_str();
3036 if should_skip {
3037 let anchor = snapshot.anchor_after(selection.end);
3038 new_selections
3039 .push((selection.map(|_| anchor), region.pair.end.len()));
3040 continue;
3041 }
3042 }
3043
3044 let always_treat_brackets_as_autoclosed = snapshot
3045 .settings_at(selection.start, cx)
3046 .always_treat_brackets_as_autoclosed;
3047 if always_treat_brackets_as_autoclosed
3048 && is_bracket_pair_end
3049 && snapshot.contains_str_at(selection.end, text.as_ref())
3050 {
3051 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3052 // and the inserted text is a closing bracket and the selection is followed
3053 // by the closing bracket then move the selection past the closing bracket.
3054 let anchor = snapshot.anchor_after(selection.end);
3055 new_selections.push((selection.map(|_| anchor), text.len()));
3056 continue;
3057 }
3058 }
3059 // If an opening bracket is 1 character long and is typed while
3060 // text is selected, then surround that text with the bracket pair.
3061 else if auto_surround
3062 && bracket_pair.surround
3063 && is_bracket_pair_start
3064 && bracket_pair.start.chars().count() == 1
3065 {
3066 edits.push((selection.start..selection.start, text.clone()));
3067 edits.push((
3068 selection.end..selection.end,
3069 bracket_pair.end.as_str().into(),
3070 ));
3071 brace_inserted = true;
3072 new_selections.push((
3073 Selection {
3074 id: selection.id,
3075 start: snapshot.anchor_after(selection.start),
3076 end: snapshot.anchor_before(selection.end),
3077 reversed: selection.reversed,
3078 goal: selection.goal,
3079 },
3080 0,
3081 ));
3082 continue;
3083 }
3084 }
3085 }
3086
3087 if self.auto_replace_emoji_shortcode
3088 && selection.is_empty()
3089 && text.as_ref().ends_with(':')
3090 {
3091 if let Some(possible_emoji_short_code) =
3092 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3093 {
3094 if !possible_emoji_short_code.is_empty() {
3095 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3096 let emoji_shortcode_start = Point::new(
3097 selection.start.row,
3098 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3099 );
3100
3101 // Remove shortcode from buffer
3102 edits.push((
3103 emoji_shortcode_start..selection.start,
3104 "".to_string().into(),
3105 ));
3106 new_selections.push((
3107 Selection {
3108 id: selection.id,
3109 start: snapshot.anchor_after(emoji_shortcode_start),
3110 end: snapshot.anchor_before(selection.start),
3111 reversed: selection.reversed,
3112 goal: selection.goal,
3113 },
3114 0,
3115 ));
3116
3117 // Insert emoji
3118 let selection_start_anchor = snapshot.anchor_after(selection.start);
3119 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3120 edits.push((selection.start..selection.end, emoji.to_string().into()));
3121
3122 continue;
3123 }
3124 }
3125 }
3126 }
3127
3128 // If not handling any auto-close operation, then just replace the selected
3129 // text with the given input and move the selection to the end of the
3130 // newly inserted text.
3131 let anchor = snapshot.anchor_after(selection.end);
3132 if !self.linked_edit_ranges.is_empty() {
3133 let start_anchor = snapshot.anchor_before(selection.start);
3134
3135 let is_word_char = text.chars().next().map_or(true, |char| {
3136 let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
3137 let kind = char_kind(&scope, char);
3138
3139 kind == CharKind::Word
3140 });
3141
3142 if is_word_char {
3143 if let Some(ranges) = self
3144 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3145 {
3146 for (buffer, edits) in ranges {
3147 linked_edits
3148 .entry(buffer.clone())
3149 .or_default()
3150 .extend(edits.into_iter().map(|range| (range, text.clone())));
3151 }
3152 }
3153 }
3154 }
3155
3156 new_selections.push((selection.map(|_| anchor), 0));
3157 edits.push((selection.start..selection.end, text.clone()));
3158 }
3159
3160 drop(snapshot);
3161
3162 self.transact(cx, |this, cx| {
3163 this.buffer.update(cx, |buffer, cx| {
3164 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3165 });
3166 for (buffer, edits) in linked_edits {
3167 buffer.update(cx, |buffer, cx| {
3168 let snapshot = buffer.snapshot();
3169 let edits = edits
3170 .into_iter()
3171 .map(|(range, text)| {
3172 use text::ToPoint as TP;
3173 let end_point = TP::to_point(&range.end, &snapshot);
3174 let start_point = TP::to_point(&range.start, &snapshot);
3175 (start_point..end_point, text)
3176 })
3177 .sorted_by_key(|(range, _)| range.start)
3178 .collect::<Vec<_>>();
3179 buffer.edit(edits, None, cx);
3180 })
3181 }
3182 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3183 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3184 let snapshot = this.buffer.read(cx).read(cx);
3185 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3186 .zip(new_selection_deltas)
3187 .map(|(selection, delta)| Selection {
3188 id: selection.id,
3189 start: selection.start + delta,
3190 end: selection.end + delta,
3191 reversed: selection.reversed,
3192 goal: SelectionGoal::None,
3193 })
3194 .collect::<Vec<_>>();
3195
3196 let mut i = 0;
3197 for (position, delta, selection_id, pair) in new_autoclose_regions {
3198 let position = position.to_offset(&snapshot) + delta;
3199 let start = snapshot.anchor_before(position);
3200 let end = snapshot.anchor_after(position);
3201 while let Some(existing_state) = this.autoclose_regions.get(i) {
3202 match existing_state.range.start.cmp(&start, &snapshot) {
3203 Ordering::Less => i += 1,
3204 Ordering::Greater => break,
3205 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3206 Ordering::Less => i += 1,
3207 Ordering::Equal => break,
3208 Ordering::Greater => break,
3209 },
3210 }
3211 }
3212 this.autoclose_regions.insert(
3213 i,
3214 AutocloseRegion {
3215 selection_id,
3216 range: start..end,
3217 pair,
3218 },
3219 );
3220 }
3221
3222 drop(snapshot);
3223 let had_active_inline_completion = this.has_active_inline_completion(cx);
3224 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3225 s.select(new_selections)
3226 });
3227
3228 if !brace_inserted && EditorSettings::get_global(cx).use_on_type_format {
3229 if let Some(on_type_format_task) =
3230 this.trigger_on_type_formatting(text.to_string(), cx)
3231 {
3232 on_type_format_task.detach_and_log_err(cx);
3233 }
3234 }
3235
3236 let trigger_in_words = !had_active_inline_completion;
3237 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3238 linked_editing_ranges::refresh_linked_ranges(this, cx);
3239 this.refresh_inline_completion(true, cx);
3240 });
3241 }
3242
3243 fn find_possible_emoji_shortcode_at_position(
3244 snapshot: &MultiBufferSnapshot,
3245 position: Point,
3246 ) -> Option<String> {
3247 let mut chars = Vec::new();
3248 let mut found_colon = false;
3249 for char in snapshot.reversed_chars_at(position).take(100) {
3250 // Found a possible emoji shortcode in the middle of the buffer
3251 if found_colon {
3252 if char.is_whitespace() {
3253 chars.reverse();
3254 return Some(chars.iter().collect());
3255 }
3256 // If the previous character is not a whitespace, we are in the middle of a word
3257 // and we only want to complete the shortcode if the word is made up of other emojis
3258 let mut containing_word = String::new();
3259 for ch in snapshot
3260 .reversed_chars_at(position)
3261 .skip(chars.len() + 1)
3262 .take(100)
3263 {
3264 if ch.is_whitespace() {
3265 break;
3266 }
3267 containing_word.push(ch);
3268 }
3269 let containing_word = containing_word.chars().rev().collect::<String>();
3270 if util::word_consists_of_emojis(containing_word.as_str()) {
3271 chars.reverse();
3272 return Some(chars.iter().collect());
3273 }
3274 }
3275
3276 if char.is_whitespace() || !char.is_ascii() {
3277 return None;
3278 }
3279 if char == ':' {
3280 found_colon = true;
3281 } else {
3282 chars.push(char);
3283 }
3284 }
3285 // Found a possible emoji shortcode at the beginning of the buffer
3286 chars.reverse();
3287 Some(chars.iter().collect())
3288 }
3289
3290 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3291 self.transact(cx, |this, cx| {
3292 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3293 let selections = this.selections.all::<usize>(cx);
3294 let multi_buffer = this.buffer.read(cx);
3295 let buffer = multi_buffer.snapshot(cx);
3296 selections
3297 .iter()
3298 .map(|selection| {
3299 let start_point = selection.start.to_point(&buffer);
3300 let mut indent =
3301 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3302 indent.len = cmp::min(indent.len, start_point.column);
3303 let start = selection.start;
3304 let end = selection.end;
3305 let selection_is_empty = start == end;
3306 let language_scope = buffer.language_scope_at(start);
3307 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3308 &language_scope
3309 {
3310 let leading_whitespace_len = buffer
3311 .reversed_chars_at(start)
3312 .take_while(|c| c.is_whitespace() && *c != '\n')
3313 .map(|c| c.len_utf8())
3314 .sum::<usize>();
3315
3316 let trailing_whitespace_len = buffer
3317 .chars_at(end)
3318 .take_while(|c| c.is_whitespace() && *c != '\n')
3319 .map(|c| c.len_utf8())
3320 .sum::<usize>();
3321
3322 let insert_extra_newline =
3323 language.brackets().any(|(pair, enabled)| {
3324 let pair_start = pair.start.trim_end();
3325 let pair_end = pair.end.trim_start();
3326
3327 enabled
3328 && pair.newline
3329 && buffer.contains_str_at(
3330 end + trailing_whitespace_len,
3331 pair_end,
3332 )
3333 && buffer.contains_str_at(
3334 (start - leading_whitespace_len)
3335 .saturating_sub(pair_start.len()),
3336 pair_start,
3337 )
3338 });
3339
3340 // Comment extension on newline is allowed only for cursor selections
3341 let comment_delimiter = maybe!({
3342 if !selection_is_empty {
3343 return None;
3344 }
3345
3346 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3347 return None;
3348 }
3349
3350 let delimiters = language.line_comment_prefixes();
3351 let max_len_of_delimiter =
3352 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3353 let (snapshot, range) =
3354 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3355
3356 let mut index_of_first_non_whitespace = 0;
3357 let comment_candidate = snapshot
3358 .chars_for_range(range)
3359 .skip_while(|c| {
3360 let should_skip = c.is_whitespace();
3361 if should_skip {
3362 index_of_first_non_whitespace += 1;
3363 }
3364 should_skip
3365 })
3366 .take(max_len_of_delimiter)
3367 .collect::<String>();
3368 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3369 comment_candidate.starts_with(comment_prefix.as_ref())
3370 })?;
3371 let cursor_is_placed_after_comment_marker =
3372 index_of_first_non_whitespace + comment_prefix.len()
3373 <= start_point.column as usize;
3374 if cursor_is_placed_after_comment_marker {
3375 Some(comment_prefix.clone())
3376 } else {
3377 None
3378 }
3379 });
3380 (comment_delimiter, insert_extra_newline)
3381 } else {
3382 (None, false)
3383 };
3384
3385 let capacity_for_delimiter = comment_delimiter
3386 .as_deref()
3387 .map(str::len)
3388 .unwrap_or_default();
3389 let mut new_text =
3390 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3391 new_text.push_str("\n");
3392 new_text.extend(indent.chars());
3393 if let Some(delimiter) = &comment_delimiter {
3394 new_text.push_str(&delimiter);
3395 }
3396 if insert_extra_newline {
3397 new_text = new_text.repeat(2);
3398 }
3399
3400 let anchor = buffer.anchor_after(end);
3401 let new_selection = selection.map(|_| anchor);
3402 (
3403 (start..end, new_text),
3404 (insert_extra_newline, new_selection),
3405 )
3406 })
3407 .unzip()
3408 };
3409
3410 this.edit_with_autoindent(edits, cx);
3411 let buffer = this.buffer.read(cx).snapshot(cx);
3412 let new_selections = selection_fixup_info
3413 .into_iter()
3414 .map(|(extra_newline_inserted, new_selection)| {
3415 let mut cursor = new_selection.end.to_point(&buffer);
3416 if extra_newline_inserted {
3417 cursor.row -= 1;
3418 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3419 }
3420 new_selection.map(|_| cursor)
3421 })
3422 .collect();
3423
3424 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3425 this.refresh_inline_completion(true, cx);
3426 });
3427 }
3428
3429 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3430 let buffer = self.buffer.read(cx);
3431 let snapshot = buffer.snapshot(cx);
3432
3433 let mut edits = Vec::new();
3434 let mut rows = Vec::new();
3435
3436 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3437 let cursor = selection.head();
3438 let row = cursor.row;
3439
3440 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3441
3442 let newline = "\n".to_string();
3443 edits.push((start_of_line..start_of_line, newline));
3444
3445 rows.push(row + rows_inserted as u32);
3446 }
3447
3448 self.transact(cx, |editor, cx| {
3449 editor.edit(edits, cx);
3450
3451 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3452 let mut index = 0;
3453 s.move_cursors_with(|map, _, _| {
3454 let row = rows[index];
3455 index += 1;
3456
3457 let point = Point::new(row, 0);
3458 let boundary = map.next_line_boundary(point).1;
3459 let clipped = map.clip_point(boundary, Bias::Left);
3460
3461 (clipped, SelectionGoal::None)
3462 });
3463 });
3464
3465 let mut indent_edits = Vec::new();
3466 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3467 for row in rows {
3468 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3469 for (row, indent) in indents {
3470 if indent.len == 0 {
3471 continue;
3472 }
3473
3474 let text = match indent.kind {
3475 IndentKind::Space => " ".repeat(indent.len as usize),
3476 IndentKind::Tab => "\t".repeat(indent.len as usize),
3477 };
3478 let point = Point::new(row.0, 0);
3479 indent_edits.push((point..point, text));
3480 }
3481 }
3482 editor.edit(indent_edits, cx);
3483 });
3484 }
3485
3486 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3487 let buffer = self.buffer.read(cx);
3488 let snapshot = buffer.snapshot(cx);
3489
3490 let mut edits = Vec::new();
3491 let mut rows = Vec::new();
3492 let mut rows_inserted = 0;
3493
3494 for selection in self.selections.all_adjusted(cx) {
3495 let cursor = selection.head();
3496 let row = cursor.row;
3497
3498 let point = Point::new(row + 1, 0);
3499 let start_of_line = snapshot.clip_point(point, Bias::Left);
3500
3501 let newline = "\n".to_string();
3502 edits.push((start_of_line..start_of_line, newline));
3503
3504 rows_inserted += 1;
3505 rows.push(row + rows_inserted);
3506 }
3507
3508 self.transact(cx, |editor, cx| {
3509 editor.edit(edits, cx);
3510
3511 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3512 let mut index = 0;
3513 s.move_cursors_with(|map, _, _| {
3514 let row = rows[index];
3515 index += 1;
3516
3517 let point = Point::new(row, 0);
3518 let boundary = map.next_line_boundary(point).1;
3519 let clipped = map.clip_point(boundary, Bias::Left);
3520
3521 (clipped, SelectionGoal::None)
3522 });
3523 });
3524
3525 let mut indent_edits = Vec::new();
3526 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3527 for row in rows {
3528 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3529 for (row, indent) in indents {
3530 if indent.len == 0 {
3531 continue;
3532 }
3533
3534 let text = match indent.kind {
3535 IndentKind::Space => " ".repeat(indent.len as usize),
3536 IndentKind::Tab => "\t".repeat(indent.len as usize),
3537 };
3538 let point = Point::new(row.0, 0);
3539 indent_edits.push((point..point, text));
3540 }
3541 }
3542 editor.edit(indent_edits, cx);
3543 });
3544 }
3545
3546 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3547 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3548 original_indent_columns: Vec::new(),
3549 });
3550 self.insert_with_autoindent_mode(text, autoindent, cx);
3551 }
3552
3553 fn insert_with_autoindent_mode(
3554 &mut self,
3555 text: &str,
3556 autoindent_mode: Option<AutoindentMode>,
3557 cx: &mut ViewContext<Self>,
3558 ) {
3559 if self.read_only(cx) {
3560 return;
3561 }
3562
3563 let text: Arc<str> = text.into();
3564 self.transact(cx, |this, cx| {
3565 let old_selections = this.selections.all_adjusted(cx);
3566 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3567 let anchors = {
3568 let snapshot = buffer.read(cx);
3569 old_selections
3570 .iter()
3571 .map(|s| {
3572 let anchor = snapshot.anchor_after(s.head());
3573 s.map(|_| anchor)
3574 })
3575 .collect::<Vec<_>>()
3576 };
3577 buffer.edit(
3578 old_selections
3579 .iter()
3580 .map(|s| (s.start..s.end, text.clone())),
3581 autoindent_mode,
3582 cx,
3583 );
3584 anchors
3585 });
3586
3587 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3588 s.select_anchors(selection_anchors);
3589 })
3590 });
3591 }
3592
3593 fn trigger_completion_on_input(
3594 &mut self,
3595 text: &str,
3596 trigger_in_words: bool,
3597 cx: &mut ViewContext<Self>,
3598 ) {
3599 if self.is_completion_trigger(text, trigger_in_words, cx) {
3600 self.show_completions(
3601 &ShowCompletions {
3602 trigger: text.chars().last(),
3603 },
3604 cx,
3605 );
3606 } else {
3607 self.hide_context_menu(cx);
3608 }
3609 }
3610
3611 fn is_completion_trigger(
3612 &self,
3613 text: &str,
3614 trigger_in_words: bool,
3615 cx: &mut ViewContext<Self>,
3616 ) -> bool {
3617 let position = self.selections.newest_anchor().head();
3618 let multibuffer = self.buffer.read(cx);
3619 let Some(buffer) = position
3620 .buffer_id
3621 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3622 else {
3623 return false;
3624 };
3625
3626 if let Some(completion_provider) = &self.completion_provider {
3627 completion_provider.is_completion_trigger(
3628 &buffer,
3629 position.text_anchor,
3630 text,
3631 trigger_in_words,
3632 cx,
3633 )
3634 } else {
3635 false
3636 }
3637 }
3638
3639 /// If any empty selections is touching the start of its innermost containing autoclose
3640 /// region, expand it to select the brackets.
3641 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3642 let selections = self.selections.all::<usize>(cx);
3643 let buffer = self.buffer.read(cx).read(cx);
3644 let new_selections = self
3645 .selections_with_autoclose_regions(selections, &buffer)
3646 .map(|(mut selection, region)| {
3647 if !selection.is_empty() {
3648 return selection;
3649 }
3650
3651 if let Some(region) = region {
3652 let mut range = region.range.to_offset(&buffer);
3653 if selection.start == range.start && range.start >= region.pair.start.len() {
3654 range.start -= region.pair.start.len();
3655 if buffer.contains_str_at(range.start, ®ion.pair.start)
3656 && buffer.contains_str_at(range.end, ®ion.pair.end)
3657 {
3658 range.end += region.pair.end.len();
3659 selection.start = range.start;
3660 selection.end = range.end;
3661
3662 return selection;
3663 }
3664 }
3665 }
3666
3667 let always_treat_brackets_as_autoclosed = buffer
3668 .settings_at(selection.start, cx)
3669 .always_treat_brackets_as_autoclosed;
3670
3671 if !always_treat_brackets_as_autoclosed {
3672 return selection;
3673 }
3674
3675 if let Some(scope) = buffer.language_scope_at(selection.start) {
3676 for (pair, enabled) in scope.brackets() {
3677 if !enabled || !pair.close {
3678 continue;
3679 }
3680
3681 if buffer.contains_str_at(selection.start, &pair.end) {
3682 let pair_start_len = pair.start.len();
3683 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3684 {
3685 selection.start -= pair_start_len;
3686 selection.end += pair.end.len();
3687
3688 return selection;
3689 }
3690 }
3691 }
3692 }
3693
3694 selection
3695 })
3696 .collect();
3697
3698 drop(buffer);
3699 self.change_selections(None, cx, |selections| selections.select(new_selections));
3700 }
3701
3702 /// Iterate the given selections, and for each one, find the smallest surrounding
3703 /// autoclose region. This uses the ordering of the selections and the autoclose
3704 /// regions to avoid repeated comparisons.
3705 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3706 &'a self,
3707 selections: impl IntoIterator<Item = Selection<D>>,
3708 buffer: &'a MultiBufferSnapshot,
3709 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3710 let mut i = 0;
3711 let mut regions = self.autoclose_regions.as_slice();
3712 selections.into_iter().map(move |selection| {
3713 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3714
3715 let mut enclosing = None;
3716 while let Some(pair_state) = regions.get(i) {
3717 if pair_state.range.end.to_offset(buffer) < range.start {
3718 regions = ®ions[i + 1..];
3719 i = 0;
3720 } else if pair_state.range.start.to_offset(buffer) > range.end {
3721 break;
3722 } else {
3723 if pair_state.selection_id == selection.id {
3724 enclosing = Some(pair_state);
3725 }
3726 i += 1;
3727 }
3728 }
3729
3730 (selection.clone(), enclosing)
3731 })
3732 }
3733
3734 /// Remove any autoclose regions that no longer contain their selection.
3735 fn invalidate_autoclose_regions(
3736 &mut self,
3737 mut selections: &[Selection<Anchor>],
3738 buffer: &MultiBufferSnapshot,
3739 ) {
3740 self.autoclose_regions.retain(|state| {
3741 let mut i = 0;
3742 while let Some(selection) = selections.get(i) {
3743 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3744 selections = &selections[1..];
3745 continue;
3746 }
3747 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3748 break;
3749 }
3750 if selection.id == state.selection_id {
3751 return true;
3752 } else {
3753 i += 1;
3754 }
3755 }
3756 false
3757 });
3758 }
3759
3760 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3761 let offset = position.to_offset(buffer);
3762 let (word_range, kind) = buffer.surrounding_word(offset);
3763 if offset > word_range.start && kind == Some(CharKind::Word) {
3764 Some(
3765 buffer
3766 .text_for_range(word_range.start..offset)
3767 .collect::<String>(),
3768 )
3769 } else {
3770 None
3771 }
3772 }
3773
3774 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3775 self.refresh_inlay_hints(
3776 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3777 cx,
3778 );
3779 }
3780
3781 pub fn inlay_hints_enabled(&self) -> bool {
3782 self.inlay_hint_cache.enabled
3783 }
3784
3785 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3786 if self.project.is_none() || self.mode != EditorMode::Full {
3787 return;
3788 }
3789
3790 let reason_description = reason.description();
3791 let ignore_debounce = matches!(
3792 reason,
3793 InlayHintRefreshReason::SettingsChange(_)
3794 | InlayHintRefreshReason::Toggle(_)
3795 | InlayHintRefreshReason::ExcerptsRemoved(_)
3796 );
3797 let (invalidate_cache, required_languages) = match reason {
3798 InlayHintRefreshReason::Toggle(enabled) => {
3799 self.inlay_hint_cache.enabled = enabled;
3800 if enabled {
3801 (InvalidationStrategy::RefreshRequested, None)
3802 } else {
3803 self.inlay_hint_cache.clear();
3804 self.splice_inlays(
3805 self.visible_inlay_hints(cx)
3806 .iter()
3807 .map(|inlay| inlay.id)
3808 .collect(),
3809 Vec::new(),
3810 cx,
3811 );
3812 return;
3813 }
3814 }
3815 InlayHintRefreshReason::SettingsChange(new_settings) => {
3816 match self.inlay_hint_cache.update_settings(
3817 &self.buffer,
3818 new_settings,
3819 self.visible_inlay_hints(cx),
3820 cx,
3821 ) {
3822 ControlFlow::Break(Some(InlaySplice {
3823 to_remove,
3824 to_insert,
3825 })) => {
3826 self.splice_inlays(to_remove, to_insert, cx);
3827 return;
3828 }
3829 ControlFlow::Break(None) => return,
3830 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3831 }
3832 }
3833 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3834 if let Some(InlaySplice {
3835 to_remove,
3836 to_insert,
3837 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3838 {
3839 self.splice_inlays(to_remove, to_insert, cx);
3840 }
3841 return;
3842 }
3843 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3844 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3845 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3846 }
3847 InlayHintRefreshReason::RefreshRequested => {
3848 (InvalidationStrategy::RefreshRequested, None)
3849 }
3850 };
3851
3852 if let Some(InlaySplice {
3853 to_remove,
3854 to_insert,
3855 }) = self.inlay_hint_cache.spawn_hint_refresh(
3856 reason_description,
3857 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3858 invalidate_cache,
3859 ignore_debounce,
3860 cx,
3861 ) {
3862 self.splice_inlays(to_remove, to_insert, cx);
3863 }
3864 }
3865
3866 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
3867 self.display_map
3868 .read(cx)
3869 .current_inlays()
3870 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3871 .cloned()
3872 .collect()
3873 }
3874
3875 pub fn excerpts_for_inlay_hints_query(
3876 &self,
3877 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3878 cx: &mut ViewContext<Editor>,
3879 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3880 let Some(project) = self.project.as_ref() else {
3881 return HashMap::default();
3882 };
3883 let project = project.read(cx);
3884 let multi_buffer = self.buffer().read(cx);
3885 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3886 let multi_buffer_visible_start = self
3887 .scroll_manager
3888 .anchor()
3889 .anchor
3890 .to_point(&multi_buffer_snapshot);
3891 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3892 multi_buffer_visible_start
3893 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3894 Bias::Left,
3895 );
3896 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3897 multi_buffer
3898 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3899 .into_iter()
3900 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3901 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3902 let buffer = buffer_handle.read(cx);
3903 let buffer_file = project::File::from_dyn(buffer.file())?;
3904 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3905 let worktree_entry = buffer_worktree
3906 .read(cx)
3907 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3908 if worktree_entry.is_ignored {
3909 return None;
3910 }
3911
3912 let language = buffer.language()?;
3913 if let Some(restrict_to_languages) = restrict_to_languages {
3914 if !restrict_to_languages.contains(language) {
3915 return None;
3916 }
3917 }
3918 Some((
3919 excerpt_id,
3920 (
3921 buffer_handle,
3922 buffer.version().clone(),
3923 excerpt_visible_range,
3924 ),
3925 ))
3926 })
3927 .collect()
3928 }
3929
3930 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3931 TextLayoutDetails {
3932 text_system: cx.text_system().clone(),
3933 editor_style: self.style.clone().unwrap(),
3934 rem_size: cx.rem_size(),
3935 scroll_anchor: self.scroll_manager.anchor(),
3936 visible_rows: self.visible_line_count(),
3937 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3938 }
3939 }
3940
3941 fn splice_inlays(
3942 &self,
3943 to_remove: Vec<InlayId>,
3944 to_insert: Vec<Inlay>,
3945 cx: &mut ViewContext<Self>,
3946 ) {
3947 self.display_map.update(cx, |display_map, cx| {
3948 display_map.splice_inlays(to_remove, to_insert, cx);
3949 });
3950 cx.notify();
3951 }
3952
3953 fn trigger_on_type_formatting(
3954 &self,
3955 input: String,
3956 cx: &mut ViewContext<Self>,
3957 ) -> Option<Task<Result<()>>> {
3958 if input.len() != 1 {
3959 return None;
3960 }
3961
3962 let project = self.project.as_ref()?;
3963 let position = self.selections.newest_anchor().head();
3964 let (buffer, buffer_position) = self
3965 .buffer
3966 .read(cx)
3967 .text_anchor_for_position(position, cx)?;
3968
3969 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3970 // hence we do LSP request & edit on host side only — add formats to host's history.
3971 let push_to_lsp_host_history = true;
3972 // If this is not the host, append its history with new edits.
3973 let push_to_client_history = project.read(cx).is_remote();
3974
3975 let on_type_formatting = project.update(cx, |project, cx| {
3976 project.on_type_format(
3977 buffer.clone(),
3978 buffer_position,
3979 input,
3980 push_to_lsp_host_history,
3981 cx,
3982 )
3983 });
3984 Some(cx.spawn(|editor, mut cx| async move {
3985 if let Some(transaction) = on_type_formatting.await? {
3986 if push_to_client_history {
3987 buffer
3988 .update(&mut cx, |buffer, _| {
3989 buffer.push_transaction(transaction, Instant::now());
3990 })
3991 .ok();
3992 }
3993 editor.update(&mut cx, |editor, cx| {
3994 editor.refresh_document_highlights(cx);
3995 })?;
3996 }
3997 Ok(())
3998 }))
3999 }
4000
4001 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4002 if self.pending_rename.is_some() {
4003 return;
4004 }
4005
4006 let Some(provider) = self.completion_provider.as_ref() else {
4007 return;
4008 };
4009
4010 let position = self.selections.newest_anchor().head();
4011 let (buffer, buffer_position) =
4012 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4013 output
4014 } else {
4015 return;
4016 };
4017
4018 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4019 let is_followup_invoke = {
4020 let context_menu_state = self.context_menu.read();
4021 matches!(
4022 context_menu_state.deref(),
4023 Some(ContextMenu::Completions(_))
4024 )
4025 };
4026 let trigger_kind = match (options.trigger, is_followup_invoke) {
4027 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4028 (Some(_), _) => CompletionTriggerKind::TRIGGER_CHARACTER,
4029 _ => CompletionTriggerKind::INVOKED,
4030 };
4031 let completion_context = CompletionContext {
4032 trigger_character: options.trigger.and_then(|c| {
4033 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4034 Some(String::from(c))
4035 } else {
4036 None
4037 }
4038 }),
4039 trigger_kind,
4040 };
4041 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4042
4043 let id = post_inc(&mut self.next_completion_id);
4044 let task = cx.spawn(|this, mut cx| {
4045 async move {
4046 this.update(&mut cx, |this, _| {
4047 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4048 })?;
4049 let completions = completions.await.log_err();
4050 let menu = if let Some(completions) = completions {
4051 let mut menu = CompletionsMenu {
4052 id,
4053 initial_position: position,
4054 match_candidates: completions
4055 .iter()
4056 .enumerate()
4057 .map(|(id, completion)| {
4058 StringMatchCandidate::new(
4059 id,
4060 completion.label.text[completion.label.filter_range.clone()]
4061 .into(),
4062 )
4063 })
4064 .collect(),
4065 buffer: buffer.clone(),
4066 completions: Arc::new(RwLock::new(completions.into())),
4067 matches: Vec::new().into(),
4068 selected_item: 0,
4069 scroll_handle: UniformListScrollHandle::new(),
4070 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4071 DebouncedDelay::new(),
4072 )),
4073 };
4074 menu.filter(query.as_deref(), cx.background_executor().clone())
4075 .await;
4076
4077 if menu.matches.is_empty() {
4078 None
4079 } else {
4080 this.update(&mut cx, |editor, cx| {
4081 let completions = menu.completions.clone();
4082 let matches = menu.matches.clone();
4083
4084 let delay_ms = EditorSettings::get_global(cx)
4085 .completion_documentation_secondary_query_debounce;
4086 let delay = Duration::from_millis(delay_ms);
4087 editor
4088 .completion_documentation_pre_resolve_debounce
4089 .fire_new(delay, cx, |editor, cx| {
4090 CompletionsMenu::pre_resolve_completion_documentation(
4091 buffer,
4092 completions,
4093 matches,
4094 editor,
4095 cx,
4096 )
4097 });
4098 })
4099 .ok();
4100 Some(menu)
4101 }
4102 } else {
4103 None
4104 };
4105
4106 this.update(&mut cx, |this, cx| {
4107 let mut context_menu = this.context_menu.write();
4108 match context_menu.as_ref() {
4109 None => {}
4110
4111 Some(ContextMenu::Completions(prev_menu)) => {
4112 if prev_menu.id > id {
4113 return;
4114 }
4115 }
4116
4117 _ => return,
4118 }
4119
4120 if this.focus_handle.is_focused(cx) && menu.is_some() {
4121 let menu = menu.unwrap();
4122 *context_menu = Some(ContextMenu::Completions(menu));
4123 drop(context_menu);
4124 this.discard_inline_completion(false, cx);
4125 cx.notify();
4126 } else if this.completion_tasks.len() <= 1 {
4127 // If there are no more completion tasks and the last menu was
4128 // empty, we should hide it. If it was already hidden, we should
4129 // also show the copilot completion when available.
4130 drop(context_menu);
4131 if this.hide_context_menu(cx).is_none() {
4132 this.update_visible_inline_completion(cx);
4133 }
4134 }
4135 })?;
4136
4137 Ok::<_, anyhow::Error>(())
4138 }
4139 .log_err()
4140 });
4141
4142 self.completion_tasks.push((id, task));
4143 }
4144
4145 pub fn confirm_completion(
4146 &mut self,
4147 action: &ConfirmCompletion,
4148 cx: &mut ViewContext<Self>,
4149 ) -> Option<Task<Result<()>>> {
4150 use language::ToOffset as _;
4151
4152 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4153 menu
4154 } else {
4155 return None;
4156 };
4157
4158 let mat = completions_menu
4159 .matches
4160 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
4161 let buffer_handle = completions_menu.buffer;
4162 let completions = completions_menu.completions.read();
4163 let completion = completions.get(mat.candidate_id)?;
4164 cx.stop_propagation();
4165
4166 let snippet;
4167 let text;
4168
4169 if completion.is_snippet() {
4170 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4171 text = snippet.as_ref().unwrap().text.clone();
4172 } else {
4173 snippet = None;
4174 text = completion.new_text.clone();
4175 };
4176 let selections = self.selections.all::<usize>(cx);
4177 let buffer = buffer_handle.read(cx);
4178 let old_range = completion.old_range.to_offset(buffer);
4179 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4180
4181 let newest_selection = self.selections.newest_anchor();
4182 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4183 return None;
4184 }
4185
4186 let lookbehind = newest_selection
4187 .start
4188 .text_anchor
4189 .to_offset(buffer)
4190 .saturating_sub(old_range.start);
4191 let lookahead = old_range
4192 .end
4193 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4194 let mut common_prefix_len = old_text
4195 .bytes()
4196 .zip(text.bytes())
4197 .take_while(|(a, b)| a == b)
4198 .count();
4199
4200 let snapshot = self.buffer.read(cx).snapshot(cx);
4201 let mut range_to_replace: Option<Range<isize>> = None;
4202 let mut ranges = Vec::new();
4203 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4204 for selection in &selections {
4205 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4206 let start = selection.start.saturating_sub(lookbehind);
4207 let end = selection.end + lookahead;
4208 if selection.id == newest_selection.id {
4209 range_to_replace = Some(
4210 ((start + common_prefix_len) as isize - selection.start as isize)
4211 ..(end as isize - selection.start as isize),
4212 );
4213 }
4214 ranges.push(start + common_prefix_len..end);
4215 } else {
4216 common_prefix_len = 0;
4217 ranges.clear();
4218 ranges.extend(selections.iter().map(|s| {
4219 if s.id == newest_selection.id {
4220 range_to_replace = Some(
4221 old_range.start.to_offset_utf16(&snapshot).0 as isize
4222 - selection.start as isize
4223 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4224 - selection.start as isize,
4225 );
4226 old_range.clone()
4227 } else {
4228 s.start..s.end
4229 }
4230 }));
4231 break;
4232 }
4233 if !self.linked_edit_ranges.is_empty() {
4234 let start_anchor = snapshot.anchor_before(selection.head());
4235 let end_anchor = snapshot.anchor_after(selection.tail());
4236 if let Some(ranges) = self
4237 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4238 {
4239 for (buffer, edits) in ranges {
4240 linked_edits.entry(buffer.clone()).or_default().extend(
4241 edits
4242 .into_iter()
4243 .map(|range| (range, text[common_prefix_len..].to_owned())),
4244 );
4245 }
4246 }
4247 }
4248 }
4249 let text = &text[common_prefix_len..];
4250
4251 cx.emit(EditorEvent::InputHandled {
4252 utf16_range_to_replace: range_to_replace,
4253 text: text.into(),
4254 });
4255
4256 self.transact(cx, |this, cx| {
4257 if let Some(mut snippet) = snippet {
4258 snippet.text = text.to_string();
4259 for tabstop in snippet.tabstops.iter_mut().flatten() {
4260 tabstop.start -= common_prefix_len as isize;
4261 tabstop.end -= common_prefix_len as isize;
4262 }
4263
4264 this.insert_snippet(&ranges, snippet, cx).log_err();
4265 } else {
4266 this.buffer.update(cx, |buffer, cx| {
4267 buffer.edit(
4268 ranges.iter().map(|range| (range.clone(), text)),
4269 this.autoindent_mode.clone(),
4270 cx,
4271 );
4272 });
4273 }
4274 for (buffer, edits) in linked_edits {
4275 buffer.update(cx, |buffer, cx| {
4276 let snapshot = buffer.snapshot();
4277 let edits = edits
4278 .into_iter()
4279 .map(|(range, text)| {
4280 use text::ToPoint as TP;
4281 let end_point = TP::to_point(&range.end, &snapshot);
4282 let start_point = TP::to_point(&range.start, &snapshot);
4283 (start_point..end_point, text)
4284 })
4285 .sorted_by_key(|(range, _)| range.start)
4286 .collect::<Vec<_>>();
4287 buffer.edit(edits, None, cx);
4288 })
4289 }
4290
4291 this.refresh_inline_completion(true, cx);
4292 });
4293
4294 if let Some(confirm) = completion.confirm.as_ref() {
4295 (confirm)(cx);
4296 }
4297
4298 if completion.show_new_completions_on_confirm {
4299 self.show_completions(&ShowCompletions { trigger: None }, cx);
4300 }
4301
4302 let provider = self.completion_provider.as_ref()?;
4303 let apply_edits = provider.apply_additional_edits_for_completion(
4304 buffer_handle,
4305 completion.clone(),
4306 true,
4307 cx,
4308 );
4309 Some(cx.foreground_executor().spawn(async move {
4310 apply_edits.await?;
4311 Ok(())
4312 }))
4313 }
4314
4315 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4316 let mut context_menu = self.context_menu.write();
4317 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4318 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4319 // Toggle if we're selecting the same one
4320 *context_menu = None;
4321 cx.notify();
4322 return;
4323 } else {
4324 // Otherwise, clear it and start a new one
4325 *context_menu = None;
4326 cx.notify();
4327 }
4328 }
4329 drop(context_menu);
4330 let snapshot = self.snapshot(cx);
4331 let deployed_from_indicator = action.deployed_from_indicator;
4332 let mut task = self.code_actions_task.take();
4333 let action = action.clone();
4334 cx.spawn(|editor, mut cx| async move {
4335 while let Some(prev_task) = task {
4336 prev_task.await;
4337 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4338 }
4339
4340 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4341 if editor.focus_handle.is_focused(cx) {
4342 let multibuffer_point = action
4343 .deployed_from_indicator
4344 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4345 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4346 let (buffer, buffer_row) = snapshot
4347 .buffer_snapshot
4348 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4349 .and_then(|(buffer_snapshot, range)| {
4350 editor
4351 .buffer
4352 .read(cx)
4353 .buffer(buffer_snapshot.remote_id())
4354 .map(|buffer| (buffer, range.start.row))
4355 })?;
4356 let (_, code_actions) = editor
4357 .available_code_actions
4358 .clone()
4359 .and_then(|(location, code_actions)| {
4360 let snapshot = location.buffer.read(cx).snapshot();
4361 let point_range = location.range.to_point(&snapshot);
4362 let point_range = point_range.start.row..=point_range.end.row;
4363 if point_range.contains(&buffer_row) {
4364 Some((location, code_actions))
4365 } else {
4366 None
4367 }
4368 })
4369 .unzip();
4370 let buffer_id = buffer.read(cx).remote_id();
4371 let tasks = editor
4372 .tasks
4373 .get(&(buffer_id, buffer_row))
4374 .map(|t| Arc::new(t.to_owned()));
4375 if tasks.is_none() && code_actions.is_none() {
4376 return None;
4377 }
4378
4379 editor.completion_tasks.clear();
4380 editor.discard_inline_completion(false, cx);
4381 let task_context =
4382 tasks
4383 .as_ref()
4384 .zip(editor.project.clone())
4385 .map(|(tasks, project)| {
4386 let position = Point::new(buffer_row, tasks.column);
4387 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4388 let location = Location {
4389 buffer: buffer.clone(),
4390 range: range_start..range_start,
4391 };
4392 // Fill in the environmental variables from the tree-sitter captures
4393 let mut captured_task_variables = TaskVariables::default();
4394 for (capture_name, value) in tasks.extra_variables.clone() {
4395 captured_task_variables.insert(
4396 task::VariableName::Custom(capture_name.into()),
4397 value.clone(),
4398 );
4399 }
4400 project.update(cx, |project, cx| {
4401 project.task_context_for_location(
4402 captured_task_variables,
4403 location,
4404 cx,
4405 )
4406 })
4407 });
4408
4409 Some(cx.spawn(|editor, mut cx| async move {
4410 let task_context = match task_context {
4411 Some(task_context) => task_context.await,
4412 None => None,
4413 };
4414 let resolved_tasks =
4415 tasks.zip(task_context).map(|(tasks, task_context)| {
4416 Arc::new(ResolvedTasks {
4417 templates: tasks
4418 .templates
4419 .iter()
4420 .filter_map(|(kind, template)| {
4421 template
4422 .resolve_task(&kind.to_id_base(), &task_context)
4423 .map(|task| (kind.clone(), task))
4424 })
4425 .collect(),
4426 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4427 multibuffer_point.row,
4428 tasks.column,
4429 )),
4430 })
4431 });
4432 let spawn_straight_away = resolved_tasks
4433 .as_ref()
4434 .map_or(false, |tasks| tasks.templates.len() == 1)
4435 && code_actions
4436 .as_ref()
4437 .map_or(true, |actions| actions.is_empty());
4438 if let Some(task) = editor
4439 .update(&mut cx, |editor, cx| {
4440 *editor.context_menu.write() =
4441 Some(ContextMenu::CodeActions(CodeActionsMenu {
4442 buffer,
4443 actions: CodeActionContents {
4444 tasks: resolved_tasks,
4445 actions: code_actions,
4446 },
4447 selected_item: Default::default(),
4448 scroll_handle: UniformListScrollHandle::default(),
4449 deployed_from_indicator,
4450 }));
4451 if spawn_straight_away {
4452 if let Some(task) = editor.confirm_code_action(
4453 &ConfirmCodeAction { item_ix: Some(0) },
4454 cx,
4455 ) {
4456 cx.notify();
4457 return task;
4458 }
4459 }
4460 cx.notify();
4461 Task::ready(Ok(()))
4462 })
4463 .ok()
4464 {
4465 task.await
4466 } else {
4467 Ok(())
4468 }
4469 }))
4470 } else {
4471 Some(Task::ready(Ok(())))
4472 }
4473 })?;
4474 if let Some(task) = spawned_test_task {
4475 task.await?;
4476 }
4477
4478 Ok::<_, anyhow::Error>(())
4479 })
4480 .detach_and_log_err(cx);
4481 }
4482
4483 pub fn confirm_code_action(
4484 &mut self,
4485 action: &ConfirmCodeAction,
4486 cx: &mut ViewContext<Self>,
4487 ) -> Option<Task<Result<()>>> {
4488 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4489 menu
4490 } else {
4491 return None;
4492 };
4493 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4494 let action = actions_menu.actions.get(action_ix)?;
4495 let title = action.label();
4496 let buffer = actions_menu.buffer;
4497 let workspace = self.workspace()?;
4498
4499 match action {
4500 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4501 workspace.update(cx, |workspace, cx| {
4502 workspace::tasks::schedule_resolved_task(
4503 workspace,
4504 task_source_kind,
4505 resolved_task,
4506 false,
4507 cx,
4508 );
4509
4510 Some(Task::ready(Ok(())))
4511 })
4512 }
4513 CodeActionsItem::CodeAction(action) => {
4514 let apply_code_actions = workspace
4515 .read(cx)
4516 .project()
4517 .clone()
4518 .update(cx, |project, cx| {
4519 project.apply_code_action(buffer, action, true, cx)
4520 });
4521 let workspace = workspace.downgrade();
4522 Some(cx.spawn(|editor, cx| async move {
4523 let project_transaction = apply_code_actions.await?;
4524 Self::open_project_transaction(
4525 &editor,
4526 workspace,
4527 project_transaction,
4528 title,
4529 cx,
4530 )
4531 .await
4532 }))
4533 }
4534 }
4535 }
4536
4537 pub async fn open_project_transaction(
4538 this: &WeakView<Editor>,
4539 workspace: WeakView<Workspace>,
4540 transaction: ProjectTransaction,
4541 title: String,
4542 mut cx: AsyncWindowContext,
4543 ) -> Result<()> {
4544 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4545
4546 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4547 cx.update(|cx| {
4548 entries.sort_unstable_by_key(|(buffer, _)| {
4549 buffer.read(cx).file().map(|f| f.path().clone())
4550 });
4551 })?;
4552
4553 // If the project transaction's edits are all contained within this editor, then
4554 // avoid opening a new editor to display them.
4555
4556 if let Some((buffer, transaction)) = entries.first() {
4557 if entries.len() == 1 {
4558 let excerpt = this.update(&mut cx, |editor, cx| {
4559 editor
4560 .buffer()
4561 .read(cx)
4562 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4563 })?;
4564 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4565 if excerpted_buffer == *buffer {
4566 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4567 let excerpt_range = excerpt_range.to_offset(buffer);
4568 buffer
4569 .edited_ranges_for_transaction::<usize>(transaction)
4570 .all(|range| {
4571 excerpt_range.start <= range.start
4572 && excerpt_range.end >= range.end
4573 })
4574 })?;
4575
4576 if all_edits_within_excerpt {
4577 return Ok(());
4578 }
4579 }
4580 }
4581 }
4582 } else {
4583 return Ok(());
4584 }
4585
4586 let mut ranges_to_highlight = Vec::new();
4587 let excerpt_buffer = cx.new_model(|cx| {
4588 let mut multibuffer =
4589 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4590 for (buffer_handle, transaction) in &entries {
4591 let buffer = buffer_handle.read(cx);
4592 ranges_to_highlight.extend(
4593 multibuffer.push_excerpts_with_context_lines(
4594 buffer_handle.clone(),
4595 buffer
4596 .edited_ranges_for_transaction::<usize>(transaction)
4597 .collect(),
4598 DEFAULT_MULTIBUFFER_CONTEXT,
4599 cx,
4600 ),
4601 );
4602 }
4603 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4604 multibuffer
4605 })?;
4606
4607 workspace.update(&mut cx, |workspace, cx| {
4608 let project = workspace.project().clone();
4609 let editor =
4610 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4611 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, cx);
4612 editor.update(cx, |editor, cx| {
4613 editor.highlight_background::<Self>(
4614 &ranges_to_highlight,
4615 |theme| theme.editor_highlighted_line_background,
4616 cx,
4617 );
4618 });
4619 })?;
4620
4621 Ok(())
4622 }
4623
4624 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4625 let project = self.project.clone()?;
4626 let buffer = self.buffer.read(cx);
4627 let newest_selection = self.selections.newest_anchor().clone();
4628 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4629 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4630 if start_buffer != end_buffer {
4631 return None;
4632 }
4633
4634 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4635 cx.background_executor()
4636 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4637 .await;
4638
4639 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4640 project.code_actions(&start_buffer, start..end, cx)
4641 }) {
4642 code_actions.await
4643 } else {
4644 Vec::new()
4645 };
4646
4647 this.update(&mut cx, |this, cx| {
4648 this.available_code_actions = if actions.is_empty() {
4649 None
4650 } else {
4651 Some((
4652 Location {
4653 buffer: start_buffer,
4654 range: start..end,
4655 },
4656 actions.into(),
4657 ))
4658 };
4659 cx.notify();
4660 })
4661 .log_err();
4662 }));
4663 None
4664 }
4665
4666 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4667 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4668 self.show_git_blame_inline = false;
4669
4670 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4671 cx.background_executor().timer(delay).await;
4672
4673 this.update(&mut cx, |this, cx| {
4674 this.show_git_blame_inline = true;
4675 cx.notify();
4676 })
4677 .log_err();
4678 }));
4679 }
4680 }
4681
4682 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4683 if self.pending_rename.is_some() {
4684 return None;
4685 }
4686
4687 let project = self.project.clone()?;
4688 let buffer = self.buffer.read(cx);
4689 let newest_selection = self.selections.newest_anchor().clone();
4690 let cursor_position = newest_selection.head();
4691 let (cursor_buffer, cursor_buffer_position) =
4692 buffer.text_anchor_for_position(cursor_position, cx)?;
4693 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4694 if cursor_buffer != tail_buffer {
4695 return None;
4696 }
4697
4698 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4699 cx.background_executor()
4700 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4701 .await;
4702
4703 let highlights = if let Some(highlights) = project
4704 .update(&mut cx, |project, cx| {
4705 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4706 })
4707 .log_err()
4708 {
4709 highlights.await.log_err()
4710 } else {
4711 None
4712 };
4713
4714 if let Some(highlights) = highlights {
4715 this.update(&mut cx, |this, cx| {
4716 if this.pending_rename.is_some() {
4717 return;
4718 }
4719
4720 let buffer_id = cursor_position.buffer_id;
4721 let buffer = this.buffer.read(cx);
4722 if !buffer
4723 .text_anchor_for_position(cursor_position, cx)
4724 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4725 {
4726 return;
4727 }
4728
4729 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4730 let mut write_ranges = Vec::new();
4731 let mut read_ranges = Vec::new();
4732 for highlight in highlights {
4733 for (excerpt_id, excerpt_range) in
4734 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4735 {
4736 let start = highlight
4737 .range
4738 .start
4739 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4740 let end = highlight
4741 .range
4742 .end
4743 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4744 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4745 continue;
4746 }
4747
4748 let range = Anchor {
4749 buffer_id,
4750 excerpt_id: excerpt_id,
4751 text_anchor: start,
4752 }..Anchor {
4753 buffer_id,
4754 excerpt_id,
4755 text_anchor: end,
4756 };
4757 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4758 write_ranges.push(range);
4759 } else {
4760 read_ranges.push(range);
4761 }
4762 }
4763 }
4764
4765 this.highlight_background::<DocumentHighlightRead>(
4766 &read_ranges,
4767 |theme| theme.editor_document_highlight_read_background,
4768 cx,
4769 );
4770 this.highlight_background::<DocumentHighlightWrite>(
4771 &write_ranges,
4772 |theme| theme.editor_document_highlight_write_background,
4773 cx,
4774 );
4775 cx.notify();
4776 })
4777 .log_err();
4778 }
4779 }));
4780 None
4781 }
4782
4783 fn refresh_inline_completion(
4784 &mut self,
4785 debounce: bool,
4786 cx: &mut ViewContext<Self>,
4787 ) -> Option<()> {
4788 let provider = self.inline_completion_provider()?;
4789 let cursor = self.selections.newest_anchor().head();
4790 let (buffer, cursor_buffer_position) =
4791 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4792 if !self.show_inline_completions
4793 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4794 {
4795 self.discard_inline_completion(false, cx);
4796 return None;
4797 }
4798
4799 self.update_visible_inline_completion(cx);
4800 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4801 Some(())
4802 }
4803
4804 fn cycle_inline_completion(
4805 &mut self,
4806 direction: Direction,
4807 cx: &mut ViewContext<Self>,
4808 ) -> Option<()> {
4809 let provider = self.inline_completion_provider()?;
4810 let cursor = self.selections.newest_anchor().head();
4811 let (buffer, cursor_buffer_position) =
4812 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4813 if !self.show_inline_completions
4814 || !provider.is_enabled(&buffer, cursor_buffer_position, cx)
4815 {
4816 return None;
4817 }
4818
4819 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4820 self.update_visible_inline_completion(cx);
4821
4822 Some(())
4823 }
4824
4825 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4826 if !self.has_active_inline_completion(cx) {
4827 self.refresh_inline_completion(false, cx);
4828 return;
4829 }
4830
4831 self.update_visible_inline_completion(cx);
4832 }
4833
4834 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4835 self.show_cursor_names(cx);
4836 }
4837
4838 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4839 self.show_cursor_names = true;
4840 cx.notify();
4841 cx.spawn(|this, mut cx| async move {
4842 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4843 this.update(&mut cx, |this, cx| {
4844 this.show_cursor_names = false;
4845 cx.notify()
4846 })
4847 .ok()
4848 })
4849 .detach();
4850 }
4851
4852 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4853 if self.has_active_inline_completion(cx) {
4854 self.cycle_inline_completion(Direction::Next, cx);
4855 } else {
4856 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4857 if is_copilot_disabled {
4858 cx.propagate();
4859 }
4860 }
4861 }
4862
4863 pub fn previous_inline_completion(
4864 &mut self,
4865 _: &PreviousInlineCompletion,
4866 cx: &mut ViewContext<Self>,
4867 ) {
4868 if self.has_active_inline_completion(cx) {
4869 self.cycle_inline_completion(Direction::Prev, cx);
4870 } else {
4871 let is_copilot_disabled = self.refresh_inline_completion(false, cx).is_none();
4872 if is_copilot_disabled {
4873 cx.propagate();
4874 }
4875 }
4876 }
4877
4878 pub fn accept_inline_completion(
4879 &mut self,
4880 _: &AcceptInlineCompletion,
4881 cx: &mut ViewContext<Self>,
4882 ) {
4883 let Some(completion) = self.take_active_inline_completion(cx) else {
4884 return;
4885 };
4886 if let Some(provider) = self.inline_completion_provider() {
4887 provider.accept(cx);
4888 }
4889
4890 cx.emit(EditorEvent::InputHandled {
4891 utf16_range_to_replace: None,
4892 text: completion.text.to_string().into(),
4893 });
4894 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
4895 self.refresh_inline_completion(true, cx);
4896 cx.notify();
4897 }
4898
4899 pub fn accept_partial_inline_completion(
4900 &mut self,
4901 _: &AcceptPartialInlineCompletion,
4902 cx: &mut ViewContext<Self>,
4903 ) {
4904 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
4905 if let Some(completion) = self.take_active_inline_completion(cx) {
4906 let mut partial_completion = completion
4907 .text
4908 .chars()
4909 .by_ref()
4910 .take_while(|c| c.is_alphabetic())
4911 .collect::<String>();
4912 if partial_completion.is_empty() {
4913 partial_completion = completion
4914 .text
4915 .chars()
4916 .by_ref()
4917 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4918 .collect::<String>();
4919 }
4920
4921 cx.emit(EditorEvent::InputHandled {
4922 utf16_range_to_replace: None,
4923 text: partial_completion.clone().into(),
4924 });
4925 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4926 self.refresh_inline_completion(true, cx);
4927 cx.notify();
4928 }
4929 }
4930 }
4931
4932 fn discard_inline_completion(
4933 &mut self,
4934 should_report_inline_completion_event: bool,
4935 cx: &mut ViewContext<Self>,
4936 ) -> bool {
4937 if let Some(provider) = self.inline_completion_provider() {
4938 provider.discard(should_report_inline_completion_event, cx);
4939 }
4940
4941 self.take_active_inline_completion(cx).is_some()
4942 }
4943
4944 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
4945 if let Some(completion) = self.active_inline_completion.as_ref() {
4946 let buffer = self.buffer.read(cx).read(cx);
4947 completion.position.is_valid(&buffer)
4948 } else {
4949 false
4950 }
4951 }
4952
4953 fn take_active_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
4954 let completion = self.active_inline_completion.take()?;
4955 self.display_map.update(cx, |map, cx| {
4956 map.splice_inlays(vec![completion.id], Default::default(), cx);
4957 });
4958 let buffer = self.buffer.read(cx).read(cx);
4959
4960 if completion.position.is_valid(&buffer) {
4961 Some(completion)
4962 } else {
4963 None
4964 }
4965 }
4966
4967 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
4968 let selection = self.selections.newest_anchor();
4969 let cursor = selection.head();
4970
4971 if self.context_menu.read().is_none()
4972 && self.completion_tasks.is_empty()
4973 && selection.start == selection.end
4974 {
4975 if let Some(provider) = self.inline_completion_provider() {
4976 if let Some((buffer, cursor_buffer_position)) =
4977 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4978 {
4979 if let Some(text) =
4980 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
4981 {
4982 let text = Rope::from(text);
4983 let mut to_remove = Vec::new();
4984 if let Some(completion) = self.active_inline_completion.take() {
4985 to_remove.push(completion.id);
4986 }
4987
4988 let completion_inlay =
4989 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
4990 self.active_inline_completion = Some(completion_inlay.clone());
4991 self.display_map.update(cx, move |map, cx| {
4992 map.splice_inlays(to_remove, vec![completion_inlay], cx)
4993 });
4994 cx.notify();
4995 return;
4996 }
4997 }
4998 }
4999 }
5000
5001 self.discard_inline_completion(false, cx);
5002 }
5003
5004 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5005 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5006 }
5007
5008 fn render_code_actions_indicator(
5009 &self,
5010 _style: &EditorStyle,
5011 row: DisplayRow,
5012 is_active: bool,
5013 cx: &mut ViewContext<Self>,
5014 ) -> Option<IconButton> {
5015 if self.available_code_actions.is_some() {
5016 Some(
5017 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5018 .shape(ui::IconButtonShape::Square)
5019 .icon_size(IconSize::XSmall)
5020 .icon_color(Color::Muted)
5021 .selected(is_active)
5022 .on_click(cx.listener(move |editor, _e, cx| {
5023 editor.focus(cx);
5024 editor.toggle_code_actions(
5025 &ToggleCodeActions {
5026 deployed_from_indicator: Some(row),
5027 },
5028 cx,
5029 );
5030 })),
5031 )
5032 } else {
5033 None
5034 }
5035 }
5036
5037 fn clear_tasks(&mut self) {
5038 self.tasks.clear()
5039 }
5040
5041 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5042 if let Some(_) = self.tasks.insert(key, value) {
5043 // This case should hopefully be rare, but just in case...
5044 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5045 }
5046 }
5047
5048 fn render_run_indicator(
5049 &self,
5050 _style: &EditorStyle,
5051 is_active: bool,
5052 row: DisplayRow,
5053 cx: &mut ViewContext<Self>,
5054 ) -> IconButton {
5055 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5056 .shape(ui::IconButtonShape::Square)
5057 .icon_size(IconSize::XSmall)
5058 .icon_color(Color::Muted)
5059 .selected(is_active)
5060 .on_click(cx.listener(move |editor, _e, cx| {
5061 editor.focus(cx);
5062 editor.toggle_code_actions(
5063 &ToggleCodeActions {
5064 deployed_from_indicator: Some(row),
5065 },
5066 cx,
5067 );
5068 }))
5069 }
5070
5071 pub fn context_menu_visible(&self) -> bool {
5072 self.context_menu
5073 .read()
5074 .as_ref()
5075 .map_or(false, |menu| menu.visible())
5076 }
5077
5078 fn render_context_menu(
5079 &self,
5080 cursor_position: DisplayPoint,
5081 style: &EditorStyle,
5082 max_height: Pixels,
5083 cx: &mut ViewContext<Editor>,
5084 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5085 self.context_menu.read().as_ref().map(|menu| {
5086 menu.render(
5087 cursor_position,
5088 style,
5089 max_height,
5090 self.workspace.as_ref().map(|(w, _)| w.clone()),
5091 cx,
5092 )
5093 })
5094 }
5095
5096 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5097 cx.notify();
5098 self.completion_tasks.clear();
5099 let context_menu = self.context_menu.write().take();
5100 if context_menu.is_some() {
5101 self.update_visible_inline_completion(cx);
5102 }
5103 context_menu
5104 }
5105
5106 pub fn insert_snippet(
5107 &mut self,
5108 insertion_ranges: &[Range<usize>],
5109 snippet: Snippet,
5110 cx: &mut ViewContext<Self>,
5111 ) -> Result<()> {
5112 struct Tabstop<T> {
5113 is_end_tabstop: bool,
5114 ranges: Vec<Range<T>>,
5115 }
5116
5117 let tabstops = self.buffer.update(cx, |buffer, cx| {
5118 let snippet_text: Arc<str> = snippet.text.clone().into();
5119 buffer.edit(
5120 insertion_ranges
5121 .iter()
5122 .cloned()
5123 .map(|range| (range, snippet_text.clone())),
5124 Some(AutoindentMode::EachLine),
5125 cx,
5126 );
5127
5128 let snapshot = &*buffer.read(cx);
5129 let snippet = &snippet;
5130 snippet
5131 .tabstops
5132 .iter()
5133 .map(|tabstop| {
5134 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5135 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5136 });
5137 let mut tabstop_ranges = tabstop
5138 .iter()
5139 .flat_map(|tabstop_range| {
5140 let mut delta = 0_isize;
5141 insertion_ranges.iter().map(move |insertion_range| {
5142 let insertion_start = insertion_range.start as isize + delta;
5143 delta +=
5144 snippet.text.len() as isize - insertion_range.len() as isize;
5145
5146 let start = ((insertion_start + tabstop_range.start) as usize)
5147 .min(snapshot.len());
5148 let end = ((insertion_start + tabstop_range.end) as usize)
5149 .min(snapshot.len());
5150 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5151 })
5152 })
5153 .collect::<Vec<_>>();
5154 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5155
5156 Tabstop {
5157 is_end_tabstop,
5158 ranges: tabstop_ranges,
5159 }
5160 })
5161 .collect::<Vec<_>>()
5162 });
5163 if let Some(tabstop) = tabstops.first() {
5164 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5165 s.select_ranges(tabstop.ranges.iter().cloned());
5166 });
5167
5168 // If we're already at the last tabstop and it's at the end of the snippet,
5169 // we're done, we don't need to keep the state around.
5170 if !tabstop.is_end_tabstop {
5171 let ranges = tabstops
5172 .into_iter()
5173 .map(|tabstop| tabstop.ranges)
5174 .collect::<Vec<_>>();
5175 self.snippet_stack.push(SnippetState {
5176 active_index: 0,
5177 ranges,
5178 });
5179 }
5180
5181 // Check whether the just-entered snippet ends with an auto-closable bracket.
5182 if self.autoclose_regions.is_empty() {
5183 let snapshot = self.buffer.read(cx).snapshot(cx);
5184 for selection in &mut self.selections.all::<Point>(cx) {
5185 let selection_head = selection.head();
5186 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5187 continue;
5188 };
5189
5190 let mut bracket_pair = None;
5191 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5192 let prev_chars = snapshot
5193 .reversed_chars_at(selection_head)
5194 .collect::<String>();
5195 for (pair, enabled) in scope.brackets() {
5196 if enabled
5197 && pair.close
5198 && prev_chars.starts_with(pair.start.as_str())
5199 && next_chars.starts_with(pair.end.as_str())
5200 {
5201 bracket_pair = Some(pair.clone());
5202 break;
5203 }
5204 }
5205 if let Some(pair) = bracket_pair {
5206 let start = snapshot.anchor_after(selection_head);
5207 let end = snapshot.anchor_after(selection_head);
5208 self.autoclose_regions.push(AutocloseRegion {
5209 selection_id: selection.id,
5210 range: start..end,
5211 pair,
5212 });
5213 }
5214 }
5215 }
5216 }
5217 Ok(())
5218 }
5219
5220 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5221 self.move_to_snippet_tabstop(Bias::Right, cx)
5222 }
5223
5224 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5225 self.move_to_snippet_tabstop(Bias::Left, cx)
5226 }
5227
5228 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5229 if let Some(mut snippet) = self.snippet_stack.pop() {
5230 match bias {
5231 Bias::Left => {
5232 if snippet.active_index > 0 {
5233 snippet.active_index -= 1;
5234 } else {
5235 self.snippet_stack.push(snippet);
5236 return false;
5237 }
5238 }
5239 Bias::Right => {
5240 if snippet.active_index + 1 < snippet.ranges.len() {
5241 snippet.active_index += 1;
5242 } else {
5243 self.snippet_stack.push(snippet);
5244 return false;
5245 }
5246 }
5247 }
5248 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5250 s.select_anchor_ranges(current_ranges.iter().cloned())
5251 });
5252 // If snippet state is not at the last tabstop, push it back on the stack
5253 if snippet.active_index + 1 < snippet.ranges.len() {
5254 self.snippet_stack.push(snippet);
5255 }
5256 return true;
5257 }
5258 }
5259
5260 false
5261 }
5262
5263 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5264 self.transact(cx, |this, cx| {
5265 this.select_all(&SelectAll, cx);
5266 this.insert("", cx);
5267 });
5268 }
5269
5270 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5271 self.transact(cx, |this, cx| {
5272 this.select_autoclose_pair(cx);
5273 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5274 if !this.linked_edit_ranges.is_empty() {
5275 let selections = this.selections.all::<MultiBufferPoint>(cx);
5276 let snapshot = this.buffer.read(cx).snapshot(cx);
5277
5278 for selection in selections.iter() {
5279 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5280 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5281 if selection_start.buffer_id != selection_end.buffer_id {
5282 continue;
5283 }
5284 if let Some(ranges) =
5285 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5286 {
5287 for (buffer, entries) in ranges {
5288 linked_ranges.entry(buffer).or_default().extend(entries);
5289 }
5290 }
5291 }
5292 }
5293
5294 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5295 if !this.selections.line_mode {
5296 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5297 for selection in &mut selections {
5298 if selection.is_empty() {
5299 let old_head = selection.head();
5300 let mut new_head =
5301 movement::left(&display_map, old_head.to_display_point(&display_map))
5302 .to_point(&display_map);
5303 if let Some((buffer, line_buffer_range)) = display_map
5304 .buffer_snapshot
5305 .buffer_line_for_row(MultiBufferRow(old_head.row))
5306 {
5307 let indent_size =
5308 buffer.indent_size_for_line(line_buffer_range.start.row);
5309 let indent_len = match indent_size.kind {
5310 IndentKind::Space => {
5311 buffer.settings_at(line_buffer_range.start, cx).tab_size
5312 }
5313 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5314 };
5315 if old_head.column <= indent_size.len && old_head.column > 0 {
5316 let indent_len = indent_len.get();
5317 new_head = cmp::min(
5318 new_head,
5319 MultiBufferPoint::new(
5320 old_head.row,
5321 ((old_head.column - 1) / indent_len) * indent_len,
5322 ),
5323 );
5324 }
5325 }
5326
5327 selection.set_head(new_head, SelectionGoal::None);
5328 }
5329 }
5330 }
5331
5332 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5333 this.insert("", cx);
5334 let empty_str: Arc<str> = Arc::from("");
5335 for (buffer, edits) in linked_ranges {
5336 let snapshot = buffer.read(cx).snapshot();
5337 use text::ToPoint as TP;
5338
5339 let edits = edits
5340 .into_iter()
5341 .map(|range| {
5342 let end_point = TP::to_point(&range.end, &snapshot);
5343 let mut start_point = TP::to_point(&range.start, &snapshot);
5344
5345 if end_point == start_point {
5346 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5347 .saturating_sub(1);
5348 start_point = TP::to_point(&offset, &snapshot);
5349 };
5350
5351 (start_point..end_point, empty_str.clone())
5352 })
5353 .sorted_by_key(|(range, _)| range.start)
5354 .collect::<Vec<_>>();
5355 buffer.update(cx, |this, cx| {
5356 this.edit(edits, None, cx);
5357 })
5358 }
5359 this.refresh_inline_completion(true, cx);
5360 linked_editing_ranges::refresh_linked_ranges(this, cx);
5361 });
5362 }
5363
5364 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5365 self.transact(cx, |this, cx| {
5366 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5367 let line_mode = s.line_mode;
5368 s.move_with(|map, selection| {
5369 if selection.is_empty() && !line_mode {
5370 let cursor = movement::right(map, selection.head());
5371 selection.end = cursor;
5372 selection.reversed = true;
5373 selection.goal = SelectionGoal::None;
5374 }
5375 })
5376 });
5377 this.insert("", cx);
5378 this.refresh_inline_completion(true, cx);
5379 });
5380 }
5381
5382 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5383 if self.move_to_prev_snippet_tabstop(cx) {
5384 return;
5385 }
5386
5387 self.outdent(&Outdent, cx);
5388 }
5389
5390 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5391 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5392 return;
5393 }
5394
5395 let mut selections = self.selections.all_adjusted(cx);
5396 let buffer = self.buffer.read(cx);
5397 let snapshot = buffer.snapshot(cx);
5398 let rows_iter = selections.iter().map(|s| s.head().row);
5399 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5400
5401 let mut edits = Vec::new();
5402 let mut prev_edited_row = 0;
5403 let mut row_delta = 0;
5404 for selection in &mut selections {
5405 if selection.start.row != prev_edited_row {
5406 row_delta = 0;
5407 }
5408 prev_edited_row = selection.end.row;
5409
5410 // If the selection is non-empty, then increase the indentation of the selected lines.
5411 if !selection.is_empty() {
5412 row_delta =
5413 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5414 continue;
5415 }
5416
5417 // If the selection is empty and the cursor is in the leading whitespace before the
5418 // suggested indentation, then auto-indent the line.
5419 let cursor = selection.head();
5420 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5421 if let Some(suggested_indent) =
5422 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5423 {
5424 if cursor.column < suggested_indent.len
5425 && cursor.column <= current_indent.len
5426 && current_indent.len <= suggested_indent.len
5427 {
5428 selection.start = Point::new(cursor.row, suggested_indent.len);
5429 selection.end = selection.start;
5430 if row_delta == 0 {
5431 edits.extend(Buffer::edit_for_indent_size_adjustment(
5432 cursor.row,
5433 current_indent,
5434 suggested_indent,
5435 ));
5436 row_delta = suggested_indent.len - current_indent.len;
5437 }
5438 continue;
5439 }
5440 }
5441
5442 // Otherwise, insert a hard or soft tab.
5443 let settings = buffer.settings_at(cursor, cx);
5444 let tab_size = if settings.hard_tabs {
5445 IndentSize::tab()
5446 } else {
5447 let tab_size = settings.tab_size.get();
5448 let char_column = snapshot
5449 .text_for_range(Point::new(cursor.row, 0)..cursor)
5450 .flat_map(str::chars)
5451 .count()
5452 + row_delta as usize;
5453 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5454 IndentSize::spaces(chars_to_next_tab_stop)
5455 };
5456 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5457 selection.end = selection.start;
5458 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5459 row_delta += tab_size.len;
5460 }
5461
5462 self.transact(cx, |this, cx| {
5463 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5464 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5465 this.refresh_inline_completion(true, cx);
5466 });
5467 }
5468
5469 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5470 if self.read_only(cx) {
5471 return;
5472 }
5473 let mut selections = self.selections.all::<Point>(cx);
5474 let mut prev_edited_row = 0;
5475 let mut row_delta = 0;
5476 let mut edits = Vec::new();
5477 let buffer = self.buffer.read(cx);
5478 let snapshot = buffer.snapshot(cx);
5479 for selection in &mut selections {
5480 if selection.start.row != prev_edited_row {
5481 row_delta = 0;
5482 }
5483 prev_edited_row = selection.end.row;
5484
5485 row_delta =
5486 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5487 }
5488
5489 self.transact(cx, |this, cx| {
5490 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5491 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5492 });
5493 }
5494
5495 fn indent_selection(
5496 buffer: &MultiBuffer,
5497 snapshot: &MultiBufferSnapshot,
5498 selection: &mut Selection<Point>,
5499 edits: &mut Vec<(Range<Point>, String)>,
5500 delta_for_start_row: u32,
5501 cx: &AppContext,
5502 ) -> u32 {
5503 let settings = buffer.settings_at(selection.start, cx);
5504 let tab_size = settings.tab_size.get();
5505 let indent_kind = if settings.hard_tabs {
5506 IndentKind::Tab
5507 } else {
5508 IndentKind::Space
5509 };
5510 let mut start_row = selection.start.row;
5511 let mut end_row = selection.end.row + 1;
5512
5513 // If a selection ends at the beginning of a line, don't indent
5514 // that last line.
5515 if selection.end.column == 0 && selection.end.row > selection.start.row {
5516 end_row -= 1;
5517 }
5518
5519 // Avoid re-indenting a row that has already been indented by a
5520 // previous selection, but still update this selection's column
5521 // to reflect that indentation.
5522 if delta_for_start_row > 0 {
5523 start_row += 1;
5524 selection.start.column += delta_for_start_row;
5525 if selection.end.row == selection.start.row {
5526 selection.end.column += delta_for_start_row;
5527 }
5528 }
5529
5530 let mut delta_for_end_row = 0;
5531 let has_multiple_rows = start_row + 1 != end_row;
5532 for row in start_row..end_row {
5533 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5534 let indent_delta = match (current_indent.kind, indent_kind) {
5535 (IndentKind::Space, IndentKind::Space) => {
5536 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5537 IndentSize::spaces(columns_to_next_tab_stop)
5538 }
5539 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5540 (_, IndentKind::Tab) => IndentSize::tab(),
5541 };
5542
5543 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5544 0
5545 } else {
5546 selection.start.column
5547 };
5548 let row_start = Point::new(row, start);
5549 edits.push((
5550 row_start..row_start,
5551 indent_delta.chars().collect::<String>(),
5552 ));
5553
5554 // Update this selection's endpoints to reflect the indentation.
5555 if row == selection.start.row {
5556 selection.start.column += indent_delta.len;
5557 }
5558 if row == selection.end.row {
5559 selection.end.column += indent_delta.len;
5560 delta_for_end_row = indent_delta.len;
5561 }
5562 }
5563
5564 if selection.start.row == selection.end.row {
5565 delta_for_start_row + delta_for_end_row
5566 } else {
5567 delta_for_end_row
5568 }
5569 }
5570
5571 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5572 if self.read_only(cx) {
5573 return;
5574 }
5575 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5576 let selections = self.selections.all::<Point>(cx);
5577 let mut deletion_ranges = Vec::new();
5578 let mut last_outdent = None;
5579 {
5580 let buffer = self.buffer.read(cx);
5581 let snapshot = buffer.snapshot(cx);
5582 for selection in &selections {
5583 let settings = buffer.settings_at(selection.start, cx);
5584 let tab_size = settings.tab_size.get();
5585 let mut rows = selection.spanned_rows(false, &display_map);
5586
5587 // Avoid re-outdenting a row that has already been outdented by a
5588 // previous selection.
5589 if let Some(last_row) = last_outdent {
5590 if last_row == rows.start {
5591 rows.start = rows.start.next_row();
5592 }
5593 }
5594 let has_multiple_rows = rows.len() > 1;
5595 for row in rows.iter_rows() {
5596 let indent_size = snapshot.indent_size_for_line(row);
5597 if indent_size.len > 0 {
5598 let deletion_len = match indent_size.kind {
5599 IndentKind::Space => {
5600 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5601 if columns_to_prev_tab_stop == 0 {
5602 tab_size
5603 } else {
5604 columns_to_prev_tab_stop
5605 }
5606 }
5607 IndentKind::Tab => 1,
5608 };
5609 let start = if has_multiple_rows
5610 || deletion_len > selection.start.column
5611 || indent_size.len < selection.start.column
5612 {
5613 0
5614 } else {
5615 selection.start.column - deletion_len
5616 };
5617 deletion_ranges.push(
5618 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5619 );
5620 last_outdent = Some(row);
5621 }
5622 }
5623 }
5624 }
5625
5626 self.transact(cx, |this, cx| {
5627 this.buffer.update(cx, |buffer, cx| {
5628 let empty_str: Arc<str> = "".into();
5629 buffer.edit(
5630 deletion_ranges
5631 .into_iter()
5632 .map(|range| (range, empty_str.clone())),
5633 None,
5634 cx,
5635 );
5636 });
5637 let selections = this.selections.all::<usize>(cx);
5638 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5639 });
5640 }
5641
5642 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5643 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5644 let selections = self.selections.all::<Point>(cx);
5645
5646 let mut new_cursors = Vec::new();
5647 let mut edit_ranges = Vec::new();
5648 let mut selections = selections.iter().peekable();
5649 while let Some(selection) = selections.next() {
5650 let mut rows = selection.spanned_rows(false, &display_map);
5651 let goal_display_column = selection.head().to_display_point(&display_map).column();
5652
5653 // Accumulate contiguous regions of rows that we want to delete.
5654 while let Some(next_selection) = selections.peek() {
5655 let next_rows = next_selection.spanned_rows(false, &display_map);
5656 if next_rows.start <= rows.end {
5657 rows.end = next_rows.end;
5658 selections.next().unwrap();
5659 } else {
5660 break;
5661 }
5662 }
5663
5664 let buffer = &display_map.buffer_snapshot;
5665 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5666 let edit_end;
5667 let cursor_buffer_row;
5668 if buffer.max_point().row >= rows.end.0 {
5669 // If there's a line after the range, delete the \n from the end of the row range
5670 // and position the cursor on the next line.
5671 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5672 cursor_buffer_row = rows.end;
5673 } else {
5674 // If there isn't a line after the range, delete the \n from the line before the
5675 // start of the row range and position the cursor there.
5676 edit_start = edit_start.saturating_sub(1);
5677 edit_end = buffer.len();
5678 cursor_buffer_row = rows.start.previous_row();
5679 }
5680
5681 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5682 *cursor.column_mut() =
5683 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5684
5685 new_cursors.push((
5686 selection.id,
5687 buffer.anchor_after(cursor.to_point(&display_map)),
5688 ));
5689 edit_ranges.push(edit_start..edit_end);
5690 }
5691
5692 self.transact(cx, |this, cx| {
5693 let buffer = this.buffer.update(cx, |buffer, cx| {
5694 let empty_str: Arc<str> = "".into();
5695 buffer.edit(
5696 edit_ranges
5697 .into_iter()
5698 .map(|range| (range, empty_str.clone())),
5699 None,
5700 cx,
5701 );
5702 buffer.snapshot(cx)
5703 });
5704 let new_selections = new_cursors
5705 .into_iter()
5706 .map(|(id, cursor)| {
5707 let cursor = cursor.to_point(&buffer);
5708 Selection {
5709 id,
5710 start: cursor,
5711 end: cursor,
5712 reversed: false,
5713 goal: SelectionGoal::None,
5714 }
5715 })
5716 .collect();
5717
5718 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5719 s.select(new_selections);
5720 });
5721 });
5722 }
5723
5724 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5725 if self.read_only(cx) {
5726 return;
5727 }
5728 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5729 for selection in self.selections.all::<Point>(cx) {
5730 let start = MultiBufferRow(selection.start.row);
5731 let end = if selection.start.row == selection.end.row {
5732 MultiBufferRow(selection.start.row + 1)
5733 } else {
5734 MultiBufferRow(selection.end.row)
5735 };
5736
5737 if let Some(last_row_range) = row_ranges.last_mut() {
5738 if start <= last_row_range.end {
5739 last_row_range.end = end;
5740 continue;
5741 }
5742 }
5743 row_ranges.push(start..end);
5744 }
5745
5746 let snapshot = self.buffer.read(cx).snapshot(cx);
5747 let mut cursor_positions = Vec::new();
5748 for row_range in &row_ranges {
5749 let anchor = snapshot.anchor_before(Point::new(
5750 row_range.end.previous_row().0,
5751 snapshot.line_len(row_range.end.previous_row()),
5752 ));
5753 cursor_positions.push(anchor..anchor);
5754 }
5755
5756 self.transact(cx, |this, cx| {
5757 for row_range in row_ranges.into_iter().rev() {
5758 for row in row_range.iter_rows().rev() {
5759 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5760 let next_line_row = row.next_row();
5761 let indent = snapshot.indent_size_for_line(next_line_row);
5762 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5763
5764 let replace = if snapshot.line_len(next_line_row) > indent.len {
5765 " "
5766 } else {
5767 ""
5768 };
5769
5770 this.buffer.update(cx, |buffer, cx| {
5771 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5772 });
5773 }
5774 }
5775
5776 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5777 s.select_anchor_ranges(cursor_positions)
5778 });
5779 });
5780 }
5781
5782 pub fn sort_lines_case_sensitive(
5783 &mut self,
5784 _: &SortLinesCaseSensitive,
5785 cx: &mut ViewContext<Self>,
5786 ) {
5787 self.manipulate_lines(cx, |lines| lines.sort())
5788 }
5789
5790 pub fn sort_lines_case_insensitive(
5791 &mut self,
5792 _: &SortLinesCaseInsensitive,
5793 cx: &mut ViewContext<Self>,
5794 ) {
5795 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
5796 }
5797
5798 pub fn unique_lines_case_insensitive(
5799 &mut self,
5800 _: &UniqueLinesCaseInsensitive,
5801 cx: &mut ViewContext<Self>,
5802 ) {
5803 self.manipulate_lines(cx, |lines| {
5804 let mut seen = HashSet::default();
5805 lines.retain(|line| seen.insert(line.to_lowercase()));
5806 })
5807 }
5808
5809 pub fn unique_lines_case_sensitive(
5810 &mut self,
5811 _: &UniqueLinesCaseSensitive,
5812 cx: &mut ViewContext<Self>,
5813 ) {
5814 self.manipulate_lines(cx, |lines| {
5815 let mut seen = HashSet::default();
5816 lines.retain(|line| seen.insert(*line));
5817 })
5818 }
5819
5820 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
5821 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
5822 if !revert_changes.is_empty() {
5823 self.transact(cx, |editor, cx| {
5824 editor.buffer().update(cx, |multi_buffer, cx| {
5825 for (buffer_id, changes) in revert_changes {
5826 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
5827 buffer.update(cx, |buffer, cx| {
5828 buffer.edit(
5829 changes.into_iter().map(|(range, text)| {
5830 (range, text.to_string().map(Arc::<str>::from))
5831 }),
5832 None,
5833 cx,
5834 );
5835 });
5836 }
5837 }
5838 });
5839 editor.change_selections(None, cx, |selections| selections.refresh());
5840 });
5841 }
5842 }
5843
5844 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
5845 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
5846 let project_path = buffer.read(cx).project_path(cx)?;
5847 let project = self.project.as_ref()?.read(cx);
5848 let entry = project.entry_for_path(&project_path, cx)?;
5849 let abs_path = project.absolute_path(&project_path, cx)?;
5850 let parent = if entry.is_symlink {
5851 abs_path.canonicalize().ok()?
5852 } else {
5853 abs_path
5854 }
5855 .parent()?
5856 .to_path_buf();
5857 Some(parent)
5858 }) {
5859 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
5860 }
5861 }
5862
5863 fn gather_revert_changes(
5864 &mut self,
5865 selections: &[Selection<Anchor>],
5866 cx: &mut ViewContext<'_, Editor>,
5867 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
5868 let mut revert_changes = HashMap::default();
5869 self.buffer.update(cx, |multi_buffer, cx| {
5870 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5871 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
5872 Self::prepare_revert_change(&mut revert_changes, &multi_buffer, &hunk, cx);
5873 }
5874 });
5875 revert_changes
5876 }
5877
5878 fn prepare_revert_change(
5879 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
5880 multi_buffer: &MultiBuffer,
5881 hunk: &DiffHunk<MultiBufferRow>,
5882 cx: &mut AppContext,
5883 ) -> Option<()> {
5884 let buffer = multi_buffer.buffer(hunk.buffer_id)?;
5885 let buffer = buffer.read(cx);
5886 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
5887 let buffer_snapshot = buffer.snapshot();
5888 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
5889 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
5890 probe
5891 .0
5892 .start
5893 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
5894 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
5895 }) {
5896 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
5897 Some(())
5898 } else {
5899 None
5900 }
5901 }
5902
5903 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
5904 self.manipulate_lines(cx, |lines| lines.reverse())
5905 }
5906
5907 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
5908 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
5909 }
5910
5911 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
5912 where
5913 Fn: FnMut(&mut Vec<&str>),
5914 {
5915 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5916 let buffer = self.buffer.read(cx).snapshot(cx);
5917
5918 let mut edits = Vec::new();
5919
5920 let selections = self.selections.all::<Point>(cx);
5921 let mut selections = selections.iter().peekable();
5922 let mut contiguous_row_selections = Vec::new();
5923 let mut new_selections = Vec::new();
5924 let mut added_lines = 0;
5925 let mut removed_lines = 0;
5926
5927 while let Some(selection) = selections.next() {
5928 let (start_row, end_row) = consume_contiguous_rows(
5929 &mut contiguous_row_selections,
5930 selection,
5931 &display_map,
5932 &mut selections,
5933 );
5934
5935 let start_point = Point::new(start_row.0, 0);
5936 let end_point = Point::new(
5937 end_row.previous_row().0,
5938 buffer.line_len(end_row.previous_row()),
5939 );
5940 let text = buffer
5941 .text_for_range(start_point..end_point)
5942 .collect::<String>();
5943
5944 let mut lines = text.split('\n').collect_vec();
5945
5946 let lines_before = lines.len();
5947 callback(&mut lines);
5948 let lines_after = lines.len();
5949
5950 edits.push((start_point..end_point, lines.join("\n")));
5951
5952 // Selections must change based on added and removed line count
5953 let start_row =
5954 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
5955 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
5956 new_selections.push(Selection {
5957 id: selection.id,
5958 start: start_row,
5959 end: end_row,
5960 goal: SelectionGoal::None,
5961 reversed: selection.reversed,
5962 });
5963
5964 if lines_after > lines_before {
5965 added_lines += lines_after - lines_before;
5966 } else if lines_before > lines_after {
5967 removed_lines += lines_before - lines_after;
5968 }
5969 }
5970
5971 self.transact(cx, |this, cx| {
5972 let buffer = this.buffer.update(cx, |buffer, cx| {
5973 buffer.edit(edits, None, cx);
5974 buffer.snapshot(cx)
5975 });
5976
5977 // Recalculate offsets on newly edited buffer
5978 let new_selections = new_selections
5979 .iter()
5980 .map(|s| {
5981 let start_point = Point::new(s.start.0, 0);
5982 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
5983 Selection {
5984 id: s.id,
5985 start: buffer.point_to_offset(start_point),
5986 end: buffer.point_to_offset(end_point),
5987 goal: s.goal,
5988 reversed: s.reversed,
5989 }
5990 })
5991 .collect();
5992
5993 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5994 s.select(new_selections);
5995 });
5996
5997 this.request_autoscroll(Autoscroll::fit(), cx);
5998 });
5999 }
6000
6001 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6002 self.manipulate_text(cx, |text| text.to_uppercase())
6003 }
6004
6005 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6006 self.manipulate_text(cx, |text| text.to_lowercase())
6007 }
6008
6009 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6010 self.manipulate_text(cx, |text| {
6011 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6012 // https://github.com/rutrum/convert-case/issues/16
6013 text.split('\n')
6014 .map(|line| line.to_case(Case::Title))
6015 .join("\n")
6016 })
6017 }
6018
6019 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6020 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6021 }
6022
6023 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6024 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6025 }
6026
6027 pub fn convert_to_upper_camel_case(
6028 &mut self,
6029 _: &ConvertToUpperCamelCase,
6030 cx: &mut ViewContext<Self>,
6031 ) {
6032 self.manipulate_text(cx, |text| {
6033 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6034 // https://github.com/rutrum/convert-case/issues/16
6035 text.split('\n')
6036 .map(|line| line.to_case(Case::UpperCamel))
6037 .join("\n")
6038 })
6039 }
6040
6041 pub fn convert_to_lower_camel_case(
6042 &mut self,
6043 _: &ConvertToLowerCamelCase,
6044 cx: &mut ViewContext<Self>,
6045 ) {
6046 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6047 }
6048
6049 pub fn convert_to_opposite_case(
6050 &mut self,
6051 _: &ConvertToOppositeCase,
6052 cx: &mut ViewContext<Self>,
6053 ) {
6054 self.manipulate_text(cx, |text| {
6055 text.chars()
6056 .fold(String::with_capacity(text.len()), |mut t, c| {
6057 if c.is_uppercase() {
6058 t.extend(c.to_lowercase());
6059 } else {
6060 t.extend(c.to_uppercase());
6061 }
6062 t
6063 })
6064 })
6065 }
6066
6067 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6068 where
6069 Fn: FnMut(&str) -> String,
6070 {
6071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6072 let buffer = self.buffer.read(cx).snapshot(cx);
6073
6074 let mut new_selections = Vec::new();
6075 let mut edits = Vec::new();
6076 let mut selection_adjustment = 0i32;
6077
6078 for selection in self.selections.all::<usize>(cx) {
6079 let selection_is_empty = selection.is_empty();
6080
6081 let (start, end) = if selection_is_empty {
6082 let word_range = movement::surrounding_word(
6083 &display_map,
6084 selection.start.to_display_point(&display_map),
6085 );
6086 let start = word_range.start.to_offset(&display_map, Bias::Left);
6087 let end = word_range.end.to_offset(&display_map, Bias::Left);
6088 (start, end)
6089 } else {
6090 (selection.start, selection.end)
6091 };
6092
6093 let text = buffer.text_for_range(start..end).collect::<String>();
6094 let old_length = text.len() as i32;
6095 let text = callback(&text);
6096
6097 new_selections.push(Selection {
6098 start: (start as i32 - selection_adjustment) as usize,
6099 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6100 goal: SelectionGoal::None,
6101 ..selection
6102 });
6103
6104 selection_adjustment += old_length - text.len() as i32;
6105
6106 edits.push((start..end, text));
6107 }
6108
6109 self.transact(cx, |this, cx| {
6110 this.buffer.update(cx, |buffer, cx| {
6111 buffer.edit(edits, None, cx);
6112 });
6113
6114 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6115 s.select(new_selections);
6116 });
6117
6118 this.request_autoscroll(Autoscroll::fit(), cx);
6119 });
6120 }
6121
6122 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6123 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6124 let buffer = &display_map.buffer_snapshot;
6125 let selections = self.selections.all::<Point>(cx);
6126
6127 let mut edits = Vec::new();
6128 let mut selections_iter = selections.iter().peekable();
6129 while let Some(selection) = selections_iter.next() {
6130 // Avoid duplicating the same lines twice.
6131 let mut rows = selection.spanned_rows(false, &display_map);
6132
6133 while let Some(next_selection) = selections_iter.peek() {
6134 let next_rows = next_selection.spanned_rows(false, &display_map);
6135 if next_rows.start < rows.end {
6136 rows.end = next_rows.end;
6137 selections_iter.next().unwrap();
6138 } else {
6139 break;
6140 }
6141 }
6142
6143 // Copy the text from the selected row region and splice it either at the start
6144 // or end of the region.
6145 let start = Point::new(rows.start.0, 0);
6146 let end = Point::new(
6147 rows.end.previous_row().0,
6148 buffer.line_len(rows.end.previous_row()),
6149 );
6150 let text = buffer
6151 .text_for_range(start..end)
6152 .chain(Some("\n"))
6153 .collect::<String>();
6154 let insert_location = if upwards {
6155 Point::new(rows.end.0, 0)
6156 } else {
6157 start
6158 };
6159 edits.push((insert_location..insert_location, text));
6160 }
6161
6162 self.transact(cx, |this, cx| {
6163 this.buffer.update(cx, |buffer, cx| {
6164 buffer.edit(edits, None, cx);
6165 });
6166
6167 this.request_autoscroll(Autoscroll::fit(), cx);
6168 });
6169 }
6170
6171 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6172 self.duplicate_line(true, cx);
6173 }
6174
6175 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6176 self.duplicate_line(false, cx);
6177 }
6178
6179 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6180 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6181 let buffer = self.buffer.read(cx).snapshot(cx);
6182
6183 let mut edits = Vec::new();
6184 let mut unfold_ranges = Vec::new();
6185 let mut refold_ranges = Vec::new();
6186
6187 let selections = self.selections.all::<Point>(cx);
6188 let mut selections = selections.iter().peekable();
6189 let mut contiguous_row_selections = Vec::new();
6190 let mut new_selections = Vec::new();
6191
6192 while let Some(selection) = selections.next() {
6193 // Find all the selections that span a contiguous row range
6194 let (start_row, end_row) = consume_contiguous_rows(
6195 &mut contiguous_row_selections,
6196 selection,
6197 &display_map,
6198 &mut selections,
6199 );
6200
6201 // Move the text spanned by the row range to be before the line preceding the row range
6202 if start_row.0 > 0 {
6203 let range_to_move = Point::new(
6204 start_row.previous_row().0,
6205 buffer.line_len(start_row.previous_row()),
6206 )
6207 ..Point::new(
6208 end_row.previous_row().0,
6209 buffer.line_len(end_row.previous_row()),
6210 );
6211 let insertion_point = display_map
6212 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6213 .0;
6214
6215 // Don't move lines across excerpts
6216 if buffer
6217 .excerpt_boundaries_in_range((
6218 Bound::Excluded(insertion_point),
6219 Bound::Included(range_to_move.end),
6220 ))
6221 .next()
6222 .is_none()
6223 {
6224 let text = buffer
6225 .text_for_range(range_to_move.clone())
6226 .flat_map(|s| s.chars())
6227 .skip(1)
6228 .chain(['\n'])
6229 .collect::<String>();
6230
6231 edits.push((
6232 buffer.anchor_after(range_to_move.start)
6233 ..buffer.anchor_before(range_to_move.end),
6234 String::new(),
6235 ));
6236 let insertion_anchor = buffer.anchor_after(insertion_point);
6237 edits.push((insertion_anchor..insertion_anchor, text));
6238
6239 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6240
6241 // Move selections up
6242 new_selections.extend(contiguous_row_selections.drain(..).map(
6243 |mut selection| {
6244 selection.start.row -= row_delta;
6245 selection.end.row -= row_delta;
6246 selection
6247 },
6248 ));
6249
6250 // Move folds up
6251 unfold_ranges.push(range_to_move.clone());
6252 for fold in display_map.folds_in_range(
6253 buffer.anchor_before(range_to_move.start)
6254 ..buffer.anchor_after(range_to_move.end),
6255 ) {
6256 let mut start = fold.range.start.to_point(&buffer);
6257 let mut end = fold.range.end.to_point(&buffer);
6258 start.row -= row_delta;
6259 end.row -= row_delta;
6260 refold_ranges.push((start..end, fold.placeholder.clone()));
6261 }
6262 }
6263 }
6264
6265 // If we didn't move line(s), preserve the existing selections
6266 new_selections.append(&mut contiguous_row_selections);
6267 }
6268
6269 self.transact(cx, |this, cx| {
6270 this.unfold_ranges(unfold_ranges, true, true, cx);
6271 this.buffer.update(cx, |buffer, cx| {
6272 for (range, text) in edits {
6273 buffer.edit([(range, text)], None, cx);
6274 }
6275 });
6276 this.fold_ranges(refold_ranges, true, cx);
6277 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6278 s.select(new_selections);
6279 })
6280 });
6281 }
6282
6283 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6284 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6285 let buffer = self.buffer.read(cx).snapshot(cx);
6286
6287 let mut edits = Vec::new();
6288 let mut unfold_ranges = Vec::new();
6289 let mut refold_ranges = Vec::new();
6290
6291 let selections = self.selections.all::<Point>(cx);
6292 let mut selections = selections.iter().peekable();
6293 let mut contiguous_row_selections = Vec::new();
6294 let mut new_selections = Vec::new();
6295
6296 while let Some(selection) = selections.next() {
6297 // Find all the selections that span a contiguous row range
6298 let (start_row, end_row) = consume_contiguous_rows(
6299 &mut contiguous_row_selections,
6300 selection,
6301 &display_map,
6302 &mut selections,
6303 );
6304
6305 // Move the text spanned by the row range to be after the last line of the row range
6306 if end_row.0 <= buffer.max_point().row {
6307 let range_to_move =
6308 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6309 let insertion_point = display_map
6310 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6311 .0;
6312
6313 // Don't move lines across excerpt boundaries
6314 if buffer
6315 .excerpt_boundaries_in_range((
6316 Bound::Excluded(range_to_move.start),
6317 Bound::Included(insertion_point),
6318 ))
6319 .next()
6320 .is_none()
6321 {
6322 let mut text = String::from("\n");
6323 text.extend(buffer.text_for_range(range_to_move.clone()));
6324 text.pop(); // Drop trailing newline
6325 edits.push((
6326 buffer.anchor_after(range_to_move.start)
6327 ..buffer.anchor_before(range_to_move.end),
6328 String::new(),
6329 ));
6330 let insertion_anchor = buffer.anchor_after(insertion_point);
6331 edits.push((insertion_anchor..insertion_anchor, text));
6332
6333 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6334
6335 // Move selections down
6336 new_selections.extend(contiguous_row_selections.drain(..).map(
6337 |mut selection| {
6338 selection.start.row += row_delta;
6339 selection.end.row += row_delta;
6340 selection
6341 },
6342 ));
6343
6344 // Move folds down
6345 unfold_ranges.push(range_to_move.clone());
6346 for fold in display_map.folds_in_range(
6347 buffer.anchor_before(range_to_move.start)
6348 ..buffer.anchor_after(range_to_move.end),
6349 ) {
6350 let mut start = fold.range.start.to_point(&buffer);
6351 let mut end = fold.range.end.to_point(&buffer);
6352 start.row += row_delta;
6353 end.row += row_delta;
6354 refold_ranges.push((start..end, fold.placeholder.clone()));
6355 }
6356 }
6357 }
6358
6359 // If we didn't move line(s), preserve the existing selections
6360 new_selections.append(&mut contiguous_row_selections);
6361 }
6362
6363 self.transact(cx, |this, cx| {
6364 this.unfold_ranges(unfold_ranges, true, true, cx);
6365 this.buffer.update(cx, |buffer, cx| {
6366 for (range, text) in edits {
6367 buffer.edit([(range, text)], None, cx);
6368 }
6369 });
6370 this.fold_ranges(refold_ranges, true, cx);
6371 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6372 });
6373 }
6374
6375 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6376 let text_layout_details = &self.text_layout_details(cx);
6377 self.transact(cx, |this, cx| {
6378 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6379 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6380 let line_mode = s.line_mode;
6381 s.move_with(|display_map, selection| {
6382 if !selection.is_empty() || line_mode {
6383 return;
6384 }
6385
6386 let mut head = selection.head();
6387 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6388 if head.column() == display_map.line_len(head.row()) {
6389 transpose_offset = display_map
6390 .buffer_snapshot
6391 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6392 }
6393
6394 if transpose_offset == 0 {
6395 return;
6396 }
6397
6398 *head.column_mut() += 1;
6399 head = display_map.clip_point(head, Bias::Right);
6400 let goal = SelectionGoal::HorizontalPosition(
6401 display_map
6402 .x_for_display_point(head, &text_layout_details)
6403 .into(),
6404 );
6405 selection.collapse_to(head, goal);
6406
6407 let transpose_start = display_map
6408 .buffer_snapshot
6409 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6410 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6411 let transpose_end = display_map
6412 .buffer_snapshot
6413 .clip_offset(transpose_offset + 1, Bias::Right);
6414 if let Some(ch) =
6415 display_map.buffer_snapshot.chars_at(transpose_start).next()
6416 {
6417 edits.push((transpose_start..transpose_offset, String::new()));
6418 edits.push((transpose_end..transpose_end, ch.to_string()));
6419 }
6420 }
6421 });
6422 edits
6423 });
6424 this.buffer
6425 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6426 let selections = this.selections.all::<usize>(cx);
6427 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6428 s.select(selections);
6429 });
6430 });
6431 }
6432
6433 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6434 let mut text = String::new();
6435 let buffer = self.buffer.read(cx).snapshot(cx);
6436 let mut selections = self.selections.all::<Point>(cx);
6437 let mut clipboard_selections = Vec::with_capacity(selections.len());
6438 {
6439 let max_point = buffer.max_point();
6440 let mut is_first = true;
6441 for selection in &mut selections {
6442 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6443 if is_entire_line {
6444 selection.start = Point::new(selection.start.row, 0);
6445 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6446 selection.goal = SelectionGoal::None;
6447 }
6448 if is_first {
6449 is_first = false;
6450 } else {
6451 text += "\n";
6452 }
6453 let mut len = 0;
6454 for chunk in buffer.text_for_range(selection.start..selection.end) {
6455 text.push_str(chunk);
6456 len += chunk.len();
6457 }
6458 clipboard_selections.push(ClipboardSelection {
6459 len,
6460 is_entire_line,
6461 first_line_indent: buffer
6462 .indent_size_for_line(MultiBufferRow(selection.start.row))
6463 .len,
6464 });
6465 }
6466 }
6467
6468 self.transact(cx, |this, cx| {
6469 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6470 s.select(selections);
6471 });
6472 this.insert("", cx);
6473 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6474 });
6475 }
6476
6477 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6478 let selections = self.selections.all::<Point>(cx);
6479 let buffer = self.buffer.read(cx).read(cx);
6480 let mut text = String::new();
6481
6482 let mut clipboard_selections = Vec::with_capacity(selections.len());
6483 {
6484 let max_point = buffer.max_point();
6485 let mut is_first = true;
6486 for selection in selections.iter() {
6487 let mut start = selection.start;
6488 let mut end = selection.end;
6489 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6490 if is_entire_line {
6491 start = Point::new(start.row, 0);
6492 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6493 }
6494 if is_first {
6495 is_first = false;
6496 } else {
6497 text += "\n";
6498 }
6499 let mut len = 0;
6500 for chunk in buffer.text_for_range(start..end) {
6501 text.push_str(chunk);
6502 len += chunk.len();
6503 }
6504 clipboard_selections.push(ClipboardSelection {
6505 len,
6506 is_entire_line,
6507 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6508 });
6509 }
6510 }
6511
6512 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
6513 }
6514
6515 pub fn do_paste(
6516 &mut self,
6517 text: &String,
6518 clipboard_selections: Option<Vec<ClipboardSelection>>,
6519 handle_entire_lines: bool,
6520 cx: &mut ViewContext<Self>,
6521 ) {
6522 if self.read_only(cx) {
6523 return;
6524 }
6525
6526 let clipboard_text = Cow::Borrowed(text);
6527
6528 self.transact(cx, |this, cx| {
6529 if let Some(mut clipboard_selections) = clipboard_selections {
6530 let old_selections = this.selections.all::<usize>(cx);
6531 let all_selections_were_entire_line =
6532 clipboard_selections.iter().all(|s| s.is_entire_line);
6533 let first_selection_indent_column =
6534 clipboard_selections.first().map(|s| s.first_line_indent);
6535 if clipboard_selections.len() != old_selections.len() {
6536 clipboard_selections.drain(..);
6537 }
6538
6539 this.buffer.update(cx, |buffer, cx| {
6540 let snapshot = buffer.read(cx);
6541 let mut start_offset = 0;
6542 let mut edits = Vec::new();
6543 let mut original_indent_columns = Vec::new();
6544 for (ix, selection) in old_selections.iter().enumerate() {
6545 let to_insert;
6546 let entire_line;
6547 let original_indent_column;
6548 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6549 let end_offset = start_offset + clipboard_selection.len;
6550 to_insert = &clipboard_text[start_offset..end_offset];
6551 entire_line = clipboard_selection.is_entire_line;
6552 start_offset = end_offset + 1;
6553 original_indent_column = Some(clipboard_selection.first_line_indent);
6554 } else {
6555 to_insert = clipboard_text.as_str();
6556 entire_line = all_selections_were_entire_line;
6557 original_indent_column = first_selection_indent_column
6558 }
6559
6560 // If the corresponding selection was empty when this slice of the
6561 // clipboard text was written, then the entire line containing the
6562 // selection was copied. If this selection is also currently empty,
6563 // then paste the line before the current line of the buffer.
6564 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6565 let column = selection.start.to_point(&snapshot).column as usize;
6566 let line_start = selection.start - column;
6567 line_start..line_start
6568 } else {
6569 selection.range()
6570 };
6571
6572 edits.push((range, to_insert));
6573 original_indent_columns.extend(original_indent_column);
6574 }
6575 drop(snapshot);
6576
6577 buffer.edit(
6578 edits,
6579 Some(AutoindentMode::Block {
6580 original_indent_columns,
6581 }),
6582 cx,
6583 );
6584 });
6585
6586 let selections = this.selections.all::<usize>(cx);
6587 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6588 } else {
6589 this.insert(&clipboard_text, cx);
6590 }
6591 });
6592 }
6593
6594 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6595 if let Some(item) = cx.read_from_clipboard() {
6596 self.do_paste(
6597 item.text(),
6598 item.metadata::<Vec<ClipboardSelection>>(),
6599 true,
6600 cx,
6601 )
6602 };
6603 }
6604
6605 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
6606 if self.read_only(cx) {
6607 return;
6608 }
6609
6610 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
6611 if let Some((selections, _)) =
6612 self.selection_history.transaction(transaction_id).cloned()
6613 {
6614 self.change_selections(None, cx, |s| {
6615 s.select_anchors(selections.to_vec());
6616 });
6617 }
6618 self.request_autoscroll(Autoscroll::fit(), cx);
6619 self.unmark_text(cx);
6620 self.refresh_inline_completion(true, cx);
6621 cx.emit(EditorEvent::Edited { transaction_id });
6622 cx.emit(EditorEvent::TransactionUndone { transaction_id });
6623 }
6624 }
6625
6626 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
6627 if self.read_only(cx) {
6628 return;
6629 }
6630
6631 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
6632 if let Some((_, Some(selections))) =
6633 self.selection_history.transaction(transaction_id).cloned()
6634 {
6635 self.change_selections(None, cx, |s| {
6636 s.select_anchors(selections.to_vec());
6637 });
6638 }
6639 self.request_autoscroll(Autoscroll::fit(), cx);
6640 self.unmark_text(cx);
6641 self.refresh_inline_completion(true, cx);
6642 cx.emit(EditorEvent::Edited { transaction_id });
6643 }
6644 }
6645
6646 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
6647 self.buffer
6648 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
6649 }
6650
6651 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
6652 self.buffer
6653 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
6654 }
6655
6656 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
6657 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6658 let line_mode = s.line_mode;
6659 s.move_with(|map, selection| {
6660 let cursor = if selection.is_empty() && !line_mode {
6661 movement::left(map, selection.start)
6662 } else {
6663 selection.start
6664 };
6665 selection.collapse_to(cursor, SelectionGoal::None);
6666 });
6667 })
6668 }
6669
6670 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
6671 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6672 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
6673 })
6674 }
6675
6676 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
6677 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6678 let line_mode = s.line_mode;
6679 s.move_with(|map, selection| {
6680 let cursor = if selection.is_empty() && !line_mode {
6681 movement::right(map, selection.end)
6682 } else {
6683 selection.end
6684 };
6685 selection.collapse_to(cursor, SelectionGoal::None)
6686 });
6687 })
6688 }
6689
6690 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
6691 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6692 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
6693 })
6694 }
6695
6696 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
6697 if self.take_rename(true, cx).is_some() {
6698 return;
6699 }
6700
6701 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6702 cx.propagate();
6703 return;
6704 }
6705
6706 let text_layout_details = &self.text_layout_details(cx);
6707 let selection_count = self.selections.count();
6708 let first_selection = self.selections.first_anchor();
6709
6710 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6711 let line_mode = s.line_mode;
6712 s.move_with(|map, selection| {
6713 if !selection.is_empty() && !line_mode {
6714 selection.goal = SelectionGoal::None;
6715 }
6716 let (cursor, goal) = movement::up(
6717 map,
6718 selection.start,
6719 selection.goal,
6720 false,
6721 &text_layout_details,
6722 );
6723 selection.collapse_to(cursor, goal);
6724 });
6725 });
6726
6727 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6728 {
6729 cx.propagate();
6730 }
6731 }
6732
6733 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
6734 if self.take_rename(true, cx).is_some() {
6735 return;
6736 }
6737
6738 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6739 cx.propagate();
6740 return;
6741 }
6742
6743 let text_layout_details = &self.text_layout_details(cx);
6744
6745 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6746 let line_mode = s.line_mode;
6747 s.move_with(|map, selection| {
6748 if !selection.is_empty() && !line_mode {
6749 selection.goal = SelectionGoal::None;
6750 }
6751 let (cursor, goal) = movement::up_by_rows(
6752 map,
6753 selection.start,
6754 action.lines,
6755 selection.goal,
6756 false,
6757 &text_layout_details,
6758 );
6759 selection.collapse_to(cursor, goal);
6760 });
6761 })
6762 }
6763
6764 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
6765 if self.take_rename(true, cx).is_some() {
6766 return;
6767 }
6768
6769 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6770 cx.propagate();
6771 return;
6772 }
6773
6774 let text_layout_details = &self.text_layout_details(cx);
6775
6776 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6777 let line_mode = s.line_mode;
6778 s.move_with(|map, selection| {
6779 if !selection.is_empty() && !line_mode {
6780 selection.goal = SelectionGoal::None;
6781 }
6782 let (cursor, goal) = movement::down_by_rows(
6783 map,
6784 selection.start,
6785 action.lines,
6786 selection.goal,
6787 false,
6788 &text_layout_details,
6789 );
6790 selection.collapse_to(cursor, goal);
6791 });
6792 })
6793 }
6794
6795 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
6796 let text_layout_details = &self.text_layout_details(cx);
6797 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6798 s.move_heads_with(|map, head, goal| {
6799 movement::down_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6800 })
6801 })
6802 }
6803
6804 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
6805 let text_layout_details = &self.text_layout_details(cx);
6806 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6807 s.move_heads_with(|map, head, goal| {
6808 movement::up_by_rows(map, head, action.lines, goal, false, &text_layout_details)
6809 })
6810 })
6811 }
6812
6813 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
6814 let Some(row_count) = self.visible_row_count() else {
6815 return;
6816 };
6817
6818 let text_layout_details = &self.text_layout_details(cx);
6819
6820 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6821 s.move_heads_with(|map, head, goal| {
6822 movement::up_by_rows(map, head, row_count, goal, false, &text_layout_details)
6823 })
6824 })
6825 }
6826
6827 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
6828 if self.take_rename(true, cx).is_some() {
6829 return;
6830 }
6831
6832 if self
6833 .context_menu
6834 .write()
6835 .as_mut()
6836 .map(|menu| menu.select_first(self.project.as_ref(), cx))
6837 .unwrap_or(false)
6838 {
6839 return;
6840 }
6841
6842 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6843 cx.propagate();
6844 return;
6845 }
6846
6847 let Some(row_count) = self.visible_row_count() else {
6848 return;
6849 };
6850
6851 let autoscroll = if action.center_cursor {
6852 Autoscroll::center()
6853 } else {
6854 Autoscroll::fit()
6855 };
6856
6857 let text_layout_details = &self.text_layout_details(cx);
6858
6859 self.change_selections(Some(autoscroll), cx, |s| {
6860 let line_mode = s.line_mode;
6861 s.move_with(|map, selection| {
6862 if !selection.is_empty() && !line_mode {
6863 selection.goal = SelectionGoal::None;
6864 }
6865 let (cursor, goal) = movement::up_by_rows(
6866 map,
6867 selection.end,
6868 row_count,
6869 selection.goal,
6870 false,
6871 &text_layout_details,
6872 );
6873 selection.collapse_to(cursor, goal);
6874 });
6875 });
6876 }
6877
6878 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
6879 let text_layout_details = &self.text_layout_details(cx);
6880 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6881 s.move_heads_with(|map, head, goal| {
6882 movement::up(map, head, goal, false, &text_layout_details)
6883 })
6884 })
6885 }
6886
6887 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
6888 self.take_rename(true, cx);
6889
6890 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6891 cx.propagate();
6892 return;
6893 }
6894
6895 let text_layout_details = &self.text_layout_details(cx);
6896 let selection_count = self.selections.count();
6897 let first_selection = self.selections.first_anchor();
6898
6899 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6900 let line_mode = s.line_mode;
6901 s.move_with(|map, selection| {
6902 if !selection.is_empty() && !line_mode {
6903 selection.goal = SelectionGoal::None;
6904 }
6905 let (cursor, goal) = movement::down(
6906 map,
6907 selection.end,
6908 selection.goal,
6909 false,
6910 &text_layout_details,
6911 );
6912 selection.collapse_to(cursor, goal);
6913 });
6914 });
6915
6916 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
6917 {
6918 cx.propagate();
6919 }
6920 }
6921
6922 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
6923 let Some(row_count) = self.visible_row_count() else {
6924 return;
6925 };
6926
6927 let text_layout_details = &self.text_layout_details(cx);
6928
6929 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6930 s.move_heads_with(|map, head, goal| {
6931 movement::down_by_rows(map, head, row_count, goal, false, &text_layout_details)
6932 })
6933 })
6934 }
6935
6936 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
6937 if self.take_rename(true, cx).is_some() {
6938 return;
6939 }
6940
6941 if self
6942 .context_menu
6943 .write()
6944 .as_mut()
6945 .map(|menu| menu.select_last(self.project.as_ref(), cx))
6946 .unwrap_or(false)
6947 {
6948 return;
6949 }
6950
6951 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6952 cx.propagate();
6953 return;
6954 }
6955
6956 let Some(row_count) = self.visible_row_count() else {
6957 return;
6958 };
6959
6960 let autoscroll = if action.center_cursor {
6961 Autoscroll::center()
6962 } else {
6963 Autoscroll::fit()
6964 };
6965
6966 let text_layout_details = &self.text_layout_details(cx);
6967 self.change_selections(Some(autoscroll), cx, |s| {
6968 let line_mode = s.line_mode;
6969 s.move_with(|map, selection| {
6970 if !selection.is_empty() && !line_mode {
6971 selection.goal = SelectionGoal::None;
6972 }
6973 let (cursor, goal) = movement::down_by_rows(
6974 map,
6975 selection.end,
6976 row_count,
6977 selection.goal,
6978 false,
6979 &text_layout_details,
6980 );
6981 selection.collapse_to(cursor, goal);
6982 });
6983 });
6984 }
6985
6986 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
6987 let text_layout_details = &self.text_layout_details(cx);
6988 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6989 s.move_heads_with(|map, head, goal| {
6990 movement::down(map, head, goal, false, &text_layout_details)
6991 })
6992 });
6993 }
6994
6995 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
6996 if let Some(context_menu) = self.context_menu.write().as_mut() {
6997 context_menu.select_first(self.project.as_ref(), cx);
6998 }
6999 }
7000
7001 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7002 if let Some(context_menu) = self.context_menu.write().as_mut() {
7003 context_menu.select_prev(self.project.as_ref(), cx);
7004 }
7005 }
7006
7007 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7008 if let Some(context_menu) = self.context_menu.write().as_mut() {
7009 context_menu.select_next(self.project.as_ref(), cx);
7010 }
7011 }
7012
7013 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7014 if let Some(context_menu) = self.context_menu.write().as_mut() {
7015 context_menu.select_last(self.project.as_ref(), cx);
7016 }
7017 }
7018
7019 pub fn move_to_previous_word_start(
7020 &mut self,
7021 _: &MoveToPreviousWordStart,
7022 cx: &mut ViewContext<Self>,
7023 ) {
7024 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7025 s.move_cursors_with(|map, head, _| {
7026 (
7027 movement::previous_word_start(map, head),
7028 SelectionGoal::None,
7029 )
7030 });
7031 })
7032 }
7033
7034 pub fn move_to_previous_subword_start(
7035 &mut self,
7036 _: &MoveToPreviousSubwordStart,
7037 cx: &mut ViewContext<Self>,
7038 ) {
7039 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7040 s.move_cursors_with(|map, head, _| {
7041 (
7042 movement::previous_subword_start(map, head),
7043 SelectionGoal::None,
7044 )
7045 });
7046 })
7047 }
7048
7049 pub fn select_to_previous_word_start(
7050 &mut self,
7051 _: &SelectToPreviousWordStart,
7052 cx: &mut ViewContext<Self>,
7053 ) {
7054 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7055 s.move_heads_with(|map, head, _| {
7056 (
7057 movement::previous_word_start(map, head),
7058 SelectionGoal::None,
7059 )
7060 });
7061 })
7062 }
7063
7064 pub fn select_to_previous_subword_start(
7065 &mut self,
7066 _: &SelectToPreviousSubwordStart,
7067 cx: &mut ViewContext<Self>,
7068 ) {
7069 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7070 s.move_heads_with(|map, head, _| {
7071 (
7072 movement::previous_subword_start(map, head),
7073 SelectionGoal::None,
7074 )
7075 });
7076 })
7077 }
7078
7079 pub fn delete_to_previous_word_start(
7080 &mut self,
7081 _: &DeleteToPreviousWordStart,
7082 cx: &mut ViewContext<Self>,
7083 ) {
7084 self.transact(cx, |this, cx| {
7085 this.select_autoclose_pair(cx);
7086 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7087 let line_mode = s.line_mode;
7088 s.move_with(|map, selection| {
7089 if selection.is_empty() && !line_mode {
7090 let cursor = movement::previous_word_start(map, selection.head());
7091 selection.set_head(cursor, SelectionGoal::None);
7092 }
7093 });
7094 });
7095 this.insert("", cx);
7096 });
7097 }
7098
7099 pub fn delete_to_previous_subword_start(
7100 &mut self,
7101 _: &DeleteToPreviousSubwordStart,
7102 cx: &mut ViewContext<Self>,
7103 ) {
7104 self.transact(cx, |this, cx| {
7105 this.select_autoclose_pair(cx);
7106 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7107 let line_mode = s.line_mode;
7108 s.move_with(|map, selection| {
7109 if selection.is_empty() && !line_mode {
7110 let cursor = movement::previous_subword_start(map, selection.head());
7111 selection.set_head(cursor, SelectionGoal::None);
7112 }
7113 });
7114 });
7115 this.insert("", cx);
7116 });
7117 }
7118
7119 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7120 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7121 s.move_cursors_with(|map, head, _| {
7122 (movement::next_word_end(map, head), SelectionGoal::None)
7123 });
7124 })
7125 }
7126
7127 pub fn move_to_next_subword_end(
7128 &mut self,
7129 _: &MoveToNextSubwordEnd,
7130 cx: &mut ViewContext<Self>,
7131 ) {
7132 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7133 s.move_cursors_with(|map, head, _| {
7134 (movement::next_subword_end(map, head), SelectionGoal::None)
7135 });
7136 })
7137 }
7138
7139 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7140 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7141 s.move_heads_with(|map, head, _| {
7142 (movement::next_word_end(map, head), SelectionGoal::None)
7143 });
7144 })
7145 }
7146
7147 pub fn select_to_next_subword_end(
7148 &mut self,
7149 _: &SelectToNextSubwordEnd,
7150 cx: &mut ViewContext<Self>,
7151 ) {
7152 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7153 s.move_heads_with(|map, head, _| {
7154 (movement::next_subword_end(map, head), SelectionGoal::None)
7155 });
7156 })
7157 }
7158
7159 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
7160 self.transact(cx, |this, cx| {
7161 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7162 let line_mode = s.line_mode;
7163 s.move_with(|map, selection| {
7164 if selection.is_empty() && !line_mode {
7165 let cursor = movement::next_word_end(map, selection.head());
7166 selection.set_head(cursor, SelectionGoal::None);
7167 }
7168 });
7169 });
7170 this.insert("", cx);
7171 });
7172 }
7173
7174 pub fn delete_to_next_subword_end(
7175 &mut self,
7176 _: &DeleteToNextSubwordEnd,
7177 cx: &mut ViewContext<Self>,
7178 ) {
7179 self.transact(cx, |this, cx| {
7180 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7181 s.move_with(|map, selection| {
7182 if selection.is_empty() {
7183 let cursor = movement::next_subword_end(map, selection.head());
7184 selection.set_head(cursor, SelectionGoal::None);
7185 }
7186 });
7187 });
7188 this.insert("", cx);
7189 });
7190 }
7191
7192 pub fn move_to_beginning_of_line(
7193 &mut self,
7194 action: &MoveToBeginningOfLine,
7195 cx: &mut ViewContext<Self>,
7196 ) {
7197 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7198 s.move_cursors_with(|map, head, _| {
7199 (
7200 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7201 SelectionGoal::None,
7202 )
7203 });
7204 })
7205 }
7206
7207 pub fn select_to_beginning_of_line(
7208 &mut self,
7209 action: &SelectToBeginningOfLine,
7210 cx: &mut ViewContext<Self>,
7211 ) {
7212 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7213 s.move_heads_with(|map, head, _| {
7214 (
7215 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7216 SelectionGoal::None,
7217 )
7218 });
7219 });
7220 }
7221
7222 pub fn delete_to_beginning_of_line(
7223 &mut self,
7224 _: &DeleteToBeginningOfLine,
7225 cx: &mut ViewContext<Self>,
7226 ) {
7227 self.transact(cx, |this, cx| {
7228 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7229 s.move_with(|_, selection| {
7230 selection.reversed = true;
7231 });
7232 });
7233
7234 this.select_to_beginning_of_line(
7235 &SelectToBeginningOfLine {
7236 stop_at_soft_wraps: false,
7237 },
7238 cx,
7239 );
7240 this.backspace(&Backspace, cx);
7241 });
7242 }
7243
7244 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7245 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7246 s.move_cursors_with(|map, head, _| {
7247 (
7248 movement::line_end(map, head, action.stop_at_soft_wraps),
7249 SelectionGoal::None,
7250 )
7251 });
7252 })
7253 }
7254
7255 pub fn select_to_end_of_line(
7256 &mut self,
7257 action: &SelectToEndOfLine,
7258 cx: &mut ViewContext<Self>,
7259 ) {
7260 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7261 s.move_heads_with(|map, head, _| {
7262 (
7263 movement::line_end(map, head, action.stop_at_soft_wraps),
7264 SelectionGoal::None,
7265 )
7266 });
7267 })
7268 }
7269
7270 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7271 self.transact(cx, |this, cx| {
7272 this.select_to_end_of_line(
7273 &SelectToEndOfLine {
7274 stop_at_soft_wraps: false,
7275 },
7276 cx,
7277 );
7278 this.delete(&Delete, cx);
7279 });
7280 }
7281
7282 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7283 self.transact(cx, |this, cx| {
7284 this.select_to_end_of_line(
7285 &SelectToEndOfLine {
7286 stop_at_soft_wraps: false,
7287 },
7288 cx,
7289 );
7290 this.cut(&Cut, cx);
7291 });
7292 }
7293
7294 pub fn move_to_start_of_paragraph(
7295 &mut self,
7296 _: &MoveToStartOfParagraph,
7297 cx: &mut ViewContext<Self>,
7298 ) {
7299 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7300 cx.propagate();
7301 return;
7302 }
7303
7304 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7305 s.move_with(|map, selection| {
7306 selection.collapse_to(
7307 movement::start_of_paragraph(map, selection.head(), 1),
7308 SelectionGoal::None,
7309 )
7310 });
7311 })
7312 }
7313
7314 pub fn move_to_end_of_paragraph(
7315 &mut self,
7316 _: &MoveToEndOfParagraph,
7317 cx: &mut ViewContext<Self>,
7318 ) {
7319 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7320 cx.propagate();
7321 return;
7322 }
7323
7324 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7325 s.move_with(|map, selection| {
7326 selection.collapse_to(
7327 movement::end_of_paragraph(map, selection.head(), 1),
7328 SelectionGoal::None,
7329 )
7330 });
7331 })
7332 }
7333
7334 pub fn select_to_start_of_paragraph(
7335 &mut self,
7336 _: &SelectToStartOfParagraph,
7337 cx: &mut ViewContext<Self>,
7338 ) {
7339 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7340 cx.propagate();
7341 return;
7342 }
7343
7344 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7345 s.move_heads_with(|map, head, _| {
7346 (
7347 movement::start_of_paragraph(map, head, 1),
7348 SelectionGoal::None,
7349 )
7350 });
7351 })
7352 }
7353
7354 pub fn select_to_end_of_paragraph(
7355 &mut self,
7356 _: &SelectToEndOfParagraph,
7357 cx: &mut ViewContext<Self>,
7358 ) {
7359 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7360 cx.propagate();
7361 return;
7362 }
7363
7364 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7365 s.move_heads_with(|map, head, _| {
7366 (
7367 movement::end_of_paragraph(map, head, 1),
7368 SelectionGoal::None,
7369 )
7370 });
7371 })
7372 }
7373
7374 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7375 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7376 cx.propagate();
7377 return;
7378 }
7379
7380 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7381 s.select_ranges(vec![0..0]);
7382 });
7383 }
7384
7385 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7386 let mut selection = self.selections.last::<Point>(cx);
7387 selection.set_head(Point::zero(), SelectionGoal::None);
7388
7389 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7390 s.select(vec![selection]);
7391 });
7392 }
7393
7394 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7395 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7396 cx.propagate();
7397 return;
7398 }
7399
7400 let cursor = self.buffer.read(cx).read(cx).len();
7401 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7402 s.select_ranges(vec![cursor..cursor])
7403 });
7404 }
7405
7406 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7407 self.nav_history = nav_history;
7408 }
7409
7410 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7411 self.nav_history.as_ref()
7412 }
7413
7414 fn push_to_nav_history(
7415 &mut self,
7416 cursor_anchor: Anchor,
7417 new_position: Option<Point>,
7418 cx: &mut ViewContext<Self>,
7419 ) {
7420 if let Some(nav_history) = self.nav_history.as_mut() {
7421 let buffer = self.buffer.read(cx).read(cx);
7422 let cursor_position = cursor_anchor.to_point(&buffer);
7423 let scroll_state = self.scroll_manager.anchor();
7424 let scroll_top_row = scroll_state.top_row(&buffer);
7425 drop(buffer);
7426
7427 if let Some(new_position) = new_position {
7428 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7429 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7430 return;
7431 }
7432 }
7433
7434 nav_history.push(
7435 Some(NavigationData {
7436 cursor_anchor,
7437 cursor_position,
7438 scroll_anchor: scroll_state,
7439 scroll_top_row,
7440 }),
7441 cx,
7442 );
7443 }
7444 }
7445
7446 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7447 let buffer = self.buffer.read(cx).snapshot(cx);
7448 let mut selection = self.selections.first::<usize>(cx);
7449 selection.set_head(buffer.len(), SelectionGoal::None);
7450 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7451 s.select(vec![selection]);
7452 });
7453 }
7454
7455 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7456 let end = self.buffer.read(cx).read(cx).len();
7457 self.change_selections(None, cx, |s| {
7458 s.select_ranges(vec![0..end]);
7459 });
7460 }
7461
7462 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7463 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7464 let mut selections = self.selections.all::<Point>(cx);
7465 let max_point = display_map.buffer_snapshot.max_point();
7466 for selection in &mut selections {
7467 let rows = selection.spanned_rows(true, &display_map);
7468 selection.start = Point::new(rows.start.0, 0);
7469 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7470 selection.reversed = false;
7471 }
7472 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7473 s.select(selections);
7474 });
7475 }
7476
7477 pub fn split_selection_into_lines(
7478 &mut self,
7479 _: &SplitSelectionIntoLines,
7480 cx: &mut ViewContext<Self>,
7481 ) {
7482 let mut to_unfold = Vec::new();
7483 let mut new_selection_ranges = Vec::new();
7484 {
7485 let selections = self.selections.all::<Point>(cx);
7486 let buffer = self.buffer.read(cx).read(cx);
7487 for selection in selections {
7488 for row in selection.start.row..selection.end.row {
7489 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7490 new_selection_ranges.push(cursor..cursor);
7491 }
7492 new_selection_ranges.push(selection.end..selection.end);
7493 to_unfold.push(selection.start..selection.end);
7494 }
7495 }
7496 self.unfold_ranges(to_unfold, true, true, cx);
7497 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7498 s.select_ranges(new_selection_ranges);
7499 });
7500 }
7501
7502 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7503 self.add_selection(true, cx);
7504 }
7505
7506 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7507 self.add_selection(false, cx);
7508 }
7509
7510 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7511 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7512 let mut selections = self.selections.all::<Point>(cx);
7513 let text_layout_details = self.text_layout_details(cx);
7514 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7515 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7516 let range = oldest_selection.display_range(&display_map).sorted();
7517
7518 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7519 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7520 let positions = start_x.min(end_x)..start_x.max(end_x);
7521
7522 selections.clear();
7523 let mut stack = Vec::new();
7524 for row in range.start.row().0..=range.end.row().0 {
7525 if let Some(selection) = self.selections.build_columnar_selection(
7526 &display_map,
7527 DisplayRow(row),
7528 &positions,
7529 oldest_selection.reversed,
7530 &text_layout_details,
7531 ) {
7532 stack.push(selection.id);
7533 selections.push(selection);
7534 }
7535 }
7536
7537 if above {
7538 stack.reverse();
7539 }
7540
7541 AddSelectionsState { above, stack }
7542 });
7543
7544 let last_added_selection = *state.stack.last().unwrap();
7545 let mut new_selections = Vec::new();
7546 if above == state.above {
7547 let end_row = if above {
7548 DisplayRow(0)
7549 } else {
7550 display_map.max_point().row()
7551 };
7552
7553 'outer: for selection in selections {
7554 if selection.id == last_added_selection {
7555 let range = selection.display_range(&display_map).sorted();
7556 debug_assert_eq!(range.start.row(), range.end.row());
7557 let mut row = range.start.row();
7558 let positions =
7559 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7560 px(start)..px(end)
7561 } else {
7562 let start_x =
7563 display_map.x_for_display_point(range.start, &text_layout_details);
7564 let end_x =
7565 display_map.x_for_display_point(range.end, &text_layout_details);
7566 start_x.min(end_x)..start_x.max(end_x)
7567 };
7568
7569 while row != end_row {
7570 if above {
7571 row.0 -= 1;
7572 } else {
7573 row.0 += 1;
7574 }
7575
7576 if let Some(new_selection) = self.selections.build_columnar_selection(
7577 &display_map,
7578 row,
7579 &positions,
7580 selection.reversed,
7581 &text_layout_details,
7582 ) {
7583 state.stack.push(new_selection.id);
7584 if above {
7585 new_selections.push(new_selection);
7586 new_selections.push(selection);
7587 } else {
7588 new_selections.push(selection);
7589 new_selections.push(new_selection);
7590 }
7591
7592 continue 'outer;
7593 }
7594 }
7595 }
7596
7597 new_selections.push(selection);
7598 }
7599 } else {
7600 new_selections = selections;
7601 new_selections.retain(|s| s.id != last_added_selection);
7602 state.stack.pop();
7603 }
7604
7605 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7606 s.select(new_selections);
7607 });
7608 if state.stack.len() > 1 {
7609 self.add_selections_state = Some(state);
7610 }
7611 }
7612
7613 pub fn select_next_match_internal(
7614 &mut self,
7615 display_map: &DisplaySnapshot,
7616 replace_newest: bool,
7617 autoscroll: Option<Autoscroll>,
7618 cx: &mut ViewContext<Self>,
7619 ) -> Result<()> {
7620 fn select_next_match_ranges(
7621 this: &mut Editor,
7622 range: Range<usize>,
7623 replace_newest: bool,
7624 auto_scroll: Option<Autoscroll>,
7625 cx: &mut ViewContext<Editor>,
7626 ) {
7627 this.unfold_ranges([range.clone()], false, true, cx);
7628 this.change_selections(auto_scroll, cx, |s| {
7629 if replace_newest {
7630 s.delete(s.newest_anchor().id);
7631 }
7632 s.insert_range(range.clone());
7633 });
7634 }
7635
7636 let buffer = &display_map.buffer_snapshot;
7637 let mut selections = self.selections.all::<usize>(cx);
7638 if let Some(mut select_next_state) = self.select_next_state.take() {
7639 let query = &select_next_state.query;
7640 if !select_next_state.done {
7641 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7642 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7643 let mut next_selected_range = None;
7644
7645 let bytes_after_last_selection =
7646 buffer.bytes_in_range(last_selection.end..buffer.len());
7647 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
7648 let query_matches = query
7649 .stream_find_iter(bytes_after_last_selection)
7650 .map(|result| (last_selection.end, result))
7651 .chain(
7652 query
7653 .stream_find_iter(bytes_before_first_selection)
7654 .map(|result| (0, result)),
7655 );
7656
7657 for (start_offset, query_match) in query_matches {
7658 let query_match = query_match.unwrap(); // can only fail due to I/O
7659 let offset_range =
7660 start_offset + query_match.start()..start_offset + query_match.end();
7661 let display_range = offset_range.start.to_display_point(&display_map)
7662 ..offset_range.end.to_display_point(&display_map);
7663
7664 if !select_next_state.wordwise
7665 || (!movement::is_inside_word(&display_map, display_range.start)
7666 && !movement::is_inside_word(&display_map, display_range.end))
7667 {
7668 // TODO: This is n^2, because we might check all the selections
7669 if !selections
7670 .iter()
7671 .any(|selection| selection.range().overlaps(&offset_range))
7672 {
7673 next_selected_range = Some(offset_range);
7674 break;
7675 }
7676 }
7677 }
7678
7679 if let Some(next_selected_range) = next_selected_range {
7680 select_next_match_ranges(
7681 self,
7682 next_selected_range,
7683 replace_newest,
7684 autoscroll,
7685 cx,
7686 );
7687 } else {
7688 select_next_state.done = true;
7689 }
7690 }
7691
7692 self.select_next_state = Some(select_next_state);
7693 } else {
7694 let mut only_carets = true;
7695 let mut same_text_selected = true;
7696 let mut selected_text = None;
7697
7698 let mut selections_iter = selections.iter().peekable();
7699 while let Some(selection) = selections_iter.next() {
7700 if selection.start != selection.end {
7701 only_carets = false;
7702 }
7703
7704 if same_text_selected {
7705 if selected_text.is_none() {
7706 selected_text =
7707 Some(buffer.text_for_range(selection.range()).collect::<String>());
7708 }
7709
7710 if let Some(next_selection) = selections_iter.peek() {
7711 if next_selection.range().len() == selection.range().len() {
7712 let next_selected_text = buffer
7713 .text_for_range(next_selection.range())
7714 .collect::<String>();
7715 if Some(next_selected_text) != selected_text {
7716 same_text_selected = false;
7717 selected_text = None;
7718 }
7719 } else {
7720 same_text_selected = false;
7721 selected_text = None;
7722 }
7723 }
7724 }
7725 }
7726
7727 if only_carets {
7728 for selection in &mut selections {
7729 let word_range = movement::surrounding_word(
7730 &display_map,
7731 selection.start.to_display_point(&display_map),
7732 );
7733 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7734 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7735 selection.goal = SelectionGoal::None;
7736 selection.reversed = false;
7737 select_next_match_ranges(
7738 self,
7739 selection.start..selection.end,
7740 replace_newest,
7741 autoscroll,
7742 cx,
7743 );
7744 }
7745
7746 if selections.len() == 1 {
7747 let selection = selections
7748 .last()
7749 .expect("ensured that there's only one selection");
7750 let query = buffer
7751 .text_for_range(selection.start..selection.end)
7752 .collect::<String>();
7753 let is_empty = query.is_empty();
7754 let select_state = SelectNextState {
7755 query: AhoCorasick::new(&[query])?,
7756 wordwise: true,
7757 done: is_empty,
7758 };
7759 self.select_next_state = Some(select_state);
7760 } else {
7761 self.select_next_state = None;
7762 }
7763 } else if let Some(selected_text) = selected_text {
7764 self.select_next_state = Some(SelectNextState {
7765 query: AhoCorasick::new(&[selected_text])?,
7766 wordwise: false,
7767 done: false,
7768 });
7769 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
7770 }
7771 }
7772 Ok(())
7773 }
7774
7775 pub fn select_all_matches(
7776 &mut self,
7777 _action: &SelectAllMatches,
7778 cx: &mut ViewContext<Self>,
7779 ) -> Result<()> {
7780 self.push_to_selection_history();
7781 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7782
7783 self.select_next_match_internal(&display_map, false, None, cx)?;
7784 let Some(select_next_state) = self.select_next_state.as_mut() else {
7785 return Ok(());
7786 };
7787 if select_next_state.done {
7788 return Ok(());
7789 }
7790
7791 let mut new_selections = self.selections.all::<usize>(cx);
7792
7793 let buffer = &display_map.buffer_snapshot;
7794 let query_matches = select_next_state
7795 .query
7796 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
7797
7798 for query_match in query_matches {
7799 let query_match = query_match.unwrap(); // can only fail due to I/O
7800 let offset_range = query_match.start()..query_match.end();
7801 let display_range = offset_range.start.to_display_point(&display_map)
7802 ..offset_range.end.to_display_point(&display_map);
7803
7804 if !select_next_state.wordwise
7805 || (!movement::is_inside_word(&display_map, display_range.start)
7806 && !movement::is_inside_word(&display_map, display_range.end))
7807 {
7808 self.selections.change_with(cx, |selections| {
7809 new_selections.push(Selection {
7810 id: selections.new_selection_id(),
7811 start: offset_range.start,
7812 end: offset_range.end,
7813 reversed: false,
7814 goal: SelectionGoal::None,
7815 });
7816 });
7817 }
7818 }
7819
7820 new_selections.sort_by_key(|selection| selection.start);
7821 let mut ix = 0;
7822 while ix + 1 < new_selections.len() {
7823 let current_selection = &new_selections[ix];
7824 let next_selection = &new_selections[ix + 1];
7825 if current_selection.range().overlaps(&next_selection.range()) {
7826 if current_selection.id < next_selection.id {
7827 new_selections.remove(ix + 1);
7828 } else {
7829 new_selections.remove(ix);
7830 }
7831 } else {
7832 ix += 1;
7833 }
7834 }
7835
7836 select_next_state.done = true;
7837 self.unfold_ranges(
7838 new_selections.iter().map(|selection| selection.range()),
7839 false,
7840 false,
7841 cx,
7842 );
7843 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
7844 selections.select(new_selections)
7845 });
7846
7847 Ok(())
7848 }
7849
7850 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
7851 self.push_to_selection_history();
7852 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7853 self.select_next_match_internal(
7854 &display_map,
7855 action.replace_newest,
7856 Some(Autoscroll::newest()),
7857 cx,
7858 )?;
7859 Ok(())
7860 }
7861
7862 pub fn select_previous(
7863 &mut self,
7864 action: &SelectPrevious,
7865 cx: &mut ViewContext<Self>,
7866 ) -> Result<()> {
7867 self.push_to_selection_history();
7868 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7869 let buffer = &display_map.buffer_snapshot;
7870 let mut selections = self.selections.all::<usize>(cx);
7871 if let Some(mut select_prev_state) = self.select_prev_state.take() {
7872 let query = &select_prev_state.query;
7873 if !select_prev_state.done {
7874 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
7875 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
7876 let mut next_selected_range = None;
7877 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
7878 let bytes_before_last_selection =
7879 buffer.reversed_bytes_in_range(0..last_selection.start);
7880 let bytes_after_first_selection =
7881 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
7882 let query_matches = query
7883 .stream_find_iter(bytes_before_last_selection)
7884 .map(|result| (last_selection.start, result))
7885 .chain(
7886 query
7887 .stream_find_iter(bytes_after_first_selection)
7888 .map(|result| (buffer.len(), result)),
7889 );
7890 for (end_offset, query_match) in query_matches {
7891 let query_match = query_match.unwrap(); // can only fail due to I/O
7892 let offset_range =
7893 end_offset - query_match.end()..end_offset - query_match.start();
7894 let display_range = offset_range.start.to_display_point(&display_map)
7895 ..offset_range.end.to_display_point(&display_map);
7896
7897 if !select_prev_state.wordwise
7898 || (!movement::is_inside_word(&display_map, display_range.start)
7899 && !movement::is_inside_word(&display_map, display_range.end))
7900 {
7901 next_selected_range = Some(offset_range);
7902 break;
7903 }
7904 }
7905
7906 if let Some(next_selected_range) = next_selected_range {
7907 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
7908 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7909 if action.replace_newest {
7910 s.delete(s.newest_anchor().id);
7911 }
7912 s.insert_range(next_selected_range);
7913 });
7914 } else {
7915 select_prev_state.done = true;
7916 }
7917 }
7918
7919 self.select_prev_state = Some(select_prev_state);
7920 } else {
7921 let mut only_carets = true;
7922 let mut same_text_selected = true;
7923 let mut selected_text = None;
7924
7925 let mut selections_iter = selections.iter().peekable();
7926 while let Some(selection) = selections_iter.next() {
7927 if selection.start != selection.end {
7928 only_carets = false;
7929 }
7930
7931 if same_text_selected {
7932 if selected_text.is_none() {
7933 selected_text =
7934 Some(buffer.text_for_range(selection.range()).collect::<String>());
7935 }
7936
7937 if let Some(next_selection) = selections_iter.peek() {
7938 if next_selection.range().len() == selection.range().len() {
7939 let next_selected_text = buffer
7940 .text_for_range(next_selection.range())
7941 .collect::<String>();
7942 if Some(next_selected_text) != selected_text {
7943 same_text_selected = false;
7944 selected_text = None;
7945 }
7946 } else {
7947 same_text_selected = false;
7948 selected_text = None;
7949 }
7950 }
7951 }
7952 }
7953
7954 if only_carets {
7955 for selection in &mut selections {
7956 let word_range = movement::surrounding_word(
7957 &display_map,
7958 selection.start.to_display_point(&display_map),
7959 );
7960 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
7961 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
7962 selection.goal = SelectionGoal::None;
7963 selection.reversed = false;
7964 }
7965 if selections.len() == 1 {
7966 let selection = selections
7967 .last()
7968 .expect("ensured that there's only one selection");
7969 let query = buffer
7970 .text_for_range(selection.start..selection.end)
7971 .collect::<String>();
7972 let is_empty = query.is_empty();
7973 let select_state = SelectNextState {
7974 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
7975 wordwise: true,
7976 done: is_empty,
7977 };
7978 self.select_prev_state = Some(select_state);
7979 } else {
7980 self.select_prev_state = None;
7981 }
7982
7983 self.unfold_ranges(
7984 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
7985 false,
7986 true,
7987 cx,
7988 );
7989 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
7990 s.select(selections);
7991 });
7992 } else if let Some(selected_text) = selected_text {
7993 self.select_prev_state = Some(SelectNextState {
7994 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
7995 wordwise: false,
7996 done: false,
7997 });
7998 self.select_previous(action, cx)?;
7999 }
8000 }
8001 Ok(())
8002 }
8003
8004 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8005 let text_layout_details = &self.text_layout_details(cx);
8006 self.transact(cx, |this, cx| {
8007 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8008 let mut edits = Vec::new();
8009 let mut selection_edit_ranges = Vec::new();
8010 let mut last_toggled_row = None;
8011 let snapshot = this.buffer.read(cx).read(cx);
8012 let empty_str: Arc<str> = "".into();
8013 let mut suffixes_inserted = Vec::new();
8014
8015 fn comment_prefix_range(
8016 snapshot: &MultiBufferSnapshot,
8017 row: MultiBufferRow,
8018 comment_prefix: &str,
8019 comment_prefix_whitespace: &str,
8020 ) -> Range<Point> {
8021 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8022
8023 let mut line_bytes = snapshot
8024 .bytes_in_range(start..snapshot.max_point())
8025 .flatten()
8026 .copied();
8027
8028 // If this line currently begins with the line comment prefix, then record
8029 // the range containing the prefix.
8030 if line_bytes
8031 .by_ref()
8032 .take(comment_prefix.len())
8033 .eq(comment_prefix.bytes())
8034 {
8035 // Include any whitespace that matches the comment prefix.
8036 let matching_whitespace_len = line_bytes
8037 .zip(comment_prefix_whitespace.bytes())
8038 .take_while(|(a, b)| a == b)
8039 .count() as u32;
8040 let end = Point::new(
8041 start.row,
8042 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8043 );
8044 start..end
8045 } else {
8046 start..start
8047 }
8048 }
8049
8050 fn comment_suffix_range(
8051 snapshot: &MultiBufferSnapshot,
8052 row: MultiBufferRow,
8053 comment_suffix: &str,
8054 comment_suffix_has_leading_space: bool,
8055 ) -> Range<Point> {
8056 let end = Point::new(row.0, snapshot.line_len(row));
8057 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8058
8059 let mut line_end_bytes = snapshot
8060 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8061 .flatten()
8062 .copied();
8063
8064 let leading_space_len = if suffix_start_column > 0
8065 && line_end_bytes.next() == Some(b' ')
8066 && comment_suffix_has_leading_space
8067 {
8068 1
8069 } else {
8070 0
8071 };
8072
8073 // If this line currently begins with the line comment prefix, then record
8074 // the range containing the prefix.
8075 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8076 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8077 start..end
8078 } else {
8079 end..end
8080 }
8081 }
8082
8083 // TODO: Handle selections that cross excerpts
8084 for selection in &mut selections {
8085 let start_column = snapshot
8086 .indent_size_for_line(MultiBufferRow(selection.start.row))
8087 .len;
8088 let language = if let Some(language) =
8089 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8090 {
8091 language
8092 } else {
8093 continue;
8094 };
8095
8096 selection_edit_ranges.clear();
8097
8098 // If multiple selections contain a given row, avoid processing that
8099 // row more than once.
8100 let mut start_row = MultiBufferRow(selection.start.row);
8101 if last_toggled_row == Some(start_row) {
8102 start_row = start_row.next_row();
8103 }
8104 let end_row =
8105 if selection.end.row > selection.start.row && selection.end.column == 0 {
8106 MultiBufferRow(selection.end.row - 1)
8107 } else {
8108 MultiBufferRow(selection.end.row)
8109 };
8110 last_toggled_row = Some(end_row);
8111
8112 if start_row > end_row {
8113 continue;
8114 }
8115
8116 // If the language has line comments, toggle those.
8117 let full_comment_prefixes = language.line_comment_prefixes();
8118 if !full_comment_prefixes.is_empty() {
8119 let first_prefix = full_comment_prefixes
8120 .first()
8121 .expect("prefixes is non-empty");
8122 let prefix_trimmed_lengths = full_comment_prefixes
8123 .iter()
8124 .map(|p| p.trim_end_matches(' ').len())
8125 .collect::<SmallVec<[usize; 4]>>();
8126
8127 let mut all_selection_lines_are_comments = true;
8128
8129 for row in start_row.0..=end_row.0 {
8130 let row = MultiBufferRow(row);
8131 if start_row < end_row && snapshot.is_line_blank(row) {
8132 continue;
8133 }
8134
8135 let prefix_range = full_comment_prefixes
8136 .iter()
8137 .zip(prefix_trimmed_lengths.iter().copied())
8138 .map(|(prefix, trimmed_prefix_len)| {
8139 comment_prefix_range(
8140 snapshot.deref(),
8141 row,
8142 &prefix[..trimmed_prefix_len],
8143 &prefix[trimmed_prefix_len..],
8144 )
8145 })
8146 .max_by_key(|range| range.end.column - range.start.column)
8147 .expect("prefixes is non-empty");
8148
8149 if prefix_range.is_empty() {
8150 all_selection_lines_are_comments = false;
8151 }
8152
8153 selection_edit_ranges.push(prefix_range);
8154 }
8155
8156 if all_selection_lines_are_comments {
8157 edits.extend(
8158 selection_edit_ranges
8159 .iter()
8160 .cloned()
8161 .map(|range| (range, empty_str.clone())),
8162 );
8163 } else {
8164 let min_column = selection_edit_ranges
8165 .iter()
8166 .map(|range| range.start.column)
8167 .min()
8168 .unwrap_or(0);
8169 edits.extend(selection_edit_ranges.iter().map(|range| {
8170 let position = Point::new(range.start.row, min_column);
8171 (position..position, first_prefix.clone())
8172 }));
8173 }
8174 } else if let Some((full_comment_prefix, comment_suffix)) =
8175 language.block_comment_delimiters()
8176 {
8177 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8178 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8179 let prefix_range = comment_prefix_range(
8180 snapshot.deref(),
8181 start_row,
8182 comment_prefix,
8183 comment_prefix_whitespace,
8184 );
8185 let suffix_range = comment_suffix_range(
8186 snapshot.deref(),
8187 end_row,
8188 comment_suffix.trim_start_matches(' '),
8189 comment_suffix.starts_with(' '),
8190 );
8191
8192 if prefix_range.is_empty() || suffix_range.is_empty() {
8193 edits.push((
8194 prefix_range.start..prefix_range.start,
8195 full_comment_prefix.clone(),
8196 ));
8197 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8198 suffixes_inserted.push((end_row, comment_suffix.len()));
8199 } else {
8200 edits.push((prefix_range, empty_str.clone()));
8201 edits.push((suffix_range, empty_str.clone()));
8202 }
8203 } else {
8204 continue;
8205 }
8206 }
8207
8208 drop(snapshot);
8209 this.buffer.update(cx, |buffer, cx| {
8210 buffer.edit(edits, None, cx);
8211 });
8212
8213 // Adjust selections so that they end before any comment suffixes that
8214 // were inserted.
8215 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8216 let mut selections = this.selections.all::<Point>(cx);
8217 let snapshot = this.buffer.read(cx).read(cx);
8218 for selection in &mut selections {
8219 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8220 match row.cmp(&MultiBufferRow(selection.end.row)) {
8221 Ordering::Less => {
8222 suffixes_inserted.next();
8223 continue;
8224 }
8225 Ordering::Greater => break,
8226 Ordering::Equal => {
8227 if selection.end.column == snapshot.line_len(row) {
8228 if selection.is_empty() {
8229 selection.start.column -= suffix_len as u32;
8230 }
8231 selection.end.column -= suffix_len as u32;
8232 }
8233 break;
8234 }
8235 }
8236 }
8237 }
8238
8239 drop(snapshot);
8240 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8241
8242 let selections = this.selections.all::<Point>(cx);
8243 let selections_on_single_row = selections.windows(2).all(|selections| {
8244 selections[0].start.row == selections[1].start.row
8245 && selections[0].end.row == selections[1].end.row
8246 && selections[0].start.row == selections[0].end.row
8247 });
8248 let selections_selecting = selections
8249 .iter()
8250 .any(|selection| selection.start != selection.end);
8251 let advance_downwards = action.advance_downwards
8252 && selections_on_single_row
8253 && !selections_selecting
8254 && !matches!(this.mode, EditorMode::SingleLine { .. });
8255
8256 if advance_downwards {
8257 let snapshot = this.buffer.read(cx).snapshot(cx);
8258
8259 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8260 s.move_cursors_with(|display_snapshot, display_point, _| {
8261 let mut point = display_point.to_point(display_snapshot);
8262 point.row += 1;
8263 point = snapshot.clip_point(point, Bias::Left);
8264 let display_point = point.to_display_point(display_snapshot);
8265 let goal = SelectionGoal::HorizontalPosition(
8266 display_snapshot
8267 .x_for_display_point(display_point, &text_layout_details)
8268 .into(),
8269 );
8270 (display_point, goal)
8271 })
8272 });
8273 }
8274 });
8275 }
8276
8277 pub fn select_enclosing_symbol(
8278 &mut self,
8279 _: &SelectEnclosingSymbol,
8280 cx: &mut ViewContext<Self>,
8281 ) {
8282 let buffer = self.buffer.read(cx).snapshot(cx);
8283 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8284
8285 fn update_selection(
8286 selection: &Selection<usize>,
8287 buffer_snap: &MultiBufferSnapshot,
8288 ) -> Option<Selection<usize>> {
8289 let cursor = selection.head();
8290 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8291 for symbol in symbols.iter().rev() {
8292 let start = symbol.range.start.to_offset(&buffer_snap);
8293 let end = symbol.range.end.to_offset(&buffer_snap);
8294 let new_range = start..end;
8295 if start < selection.start || end > selection.end {
8296 return Some(Selection {
8297 id: selection.id,
8298 start: new_range.start,
8299 end: new_range.end,
8300 goal: SelectionGoal::None,
8301 reversed: selection.reversed,
8302 });
8303 }
8304 }
8305 None
8306 }
8307
8308 let mut selected_larger_symbol = false;
8309 let new_selections = old_selections
8310 .iter()
8311 .map(|selection| match update_selection(selection, &buffer) {
8312 Some(new_selection) => {
8313 if new_selection.range() != selection.range() {
8314 selected_larger_symbol = true;
8315 }
8316 new_selection
8317 }
8318 None => selection.clone(),
8319 })
8320 .collect::<Vec<_>>();
8321
8322 if selected_larger_symbol {
8323 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8324 s.select(new_selections);
8325 });
8326 }
8327 }
8328
8329 pub fn select_larger_syntax_node(
8330 &mut self,
8331 _: &SelectLargerSyntaxNode,
8332 cx: &mut ViewContext<Self>,
8333 ) {
8334 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8335 let buffer = self.buffer.read(cx).snapshot(cx);
8336 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8337
8338 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8339 let mut selected_larger_node = false;
8340 let new_selections = old_selections
8341 .iter()
8342 .map(|selection| {
8343 let old_range = selection.start..selection.end;
8344 let mut new_range = old_range.clone();
8345 while let Some(containing_range) =
8346 buffer.range_for_syntax_ancestor(new_range.clone())
8347 {
8348 new_range = containing_range;
8349 if !display_map.intersects_fold(new_range.start)
8350 && !display_map.intersects_fold(new_range.end)
8351 {
8352 break;
8353 }
8354 }
8355
8356 selected_larger_node |= new_range != old_range;
8357 Selection {
8358 id: selection.id,
8359 start: new_range.start,
8360 end: new_range.end,
8361 goal: SelectionGoal::None,
8362 reversed: selection.reversed,
8363 }
8364 })
8365 .collect::<Vec<_>>();
8366
8367 if selected_larger_node {
8368 stack.push(old_selections);
8369 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8370 s.select(new_selections);
8371 });
8372 }
8373 self.select_larger_syntax_node_stack = stack;
8374 }
8375
8376 pub fn select_smaller_syntax_node(
8377 &mut self,
8378 _: &SelectSmallerSyntaxNode,
8379 cx: &mut ViewContext<Self>,
8380 ) {
8381 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8382 if let Some(selections) = stack.pop() {
8383 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8384 s.select(selections.to_vec());
8385 });
8386 }
8387 self.select_larger_syntax_node_stack = stack;
8388 }
8389
8390 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8391 if !EditorSettings::get_global(cx).gutter.runnables {
8392 self.clear_tasks();
8393 return Task::ready(());
8394 }
8395 let project = self.project.clone();
8396 cx.spawn(|this, mut cx| async move {
8397 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8398 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8399 }) else {
8400 return;
8401 };
8402
8403 let Some(project) = project else {
8404 return;
8405 };
8406
8407 let hide_runnables = project
8408 .update(&mut cx, |project, cx| {
8409 // Do not display any test indicators in non-dev server remote projects.
8410 project.is_remote() && project.ssh_connection_string(cx).is_none()
8411 })
8412 .unwrap_or(true);
8413 if hide_runnables {
8414 return;
8415 }
8416 let new_rows =
8417 cx.background_executor()
8418 .spawn({
8419 let snapshot = display_snapshot.clone();
8420 async move {
8421 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8422 }
8423 })
8424 .await;
8425 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8426
8427 this.update(&mut cx, |this, _| {
8428 this.clear_tasks();
8429 for (key, value) in rows {
8430 this.insert_tasks(key, value);
8431 }
8432 })
8433 .ok();
8434 })
8435 }
8436 fn fetch_runnable_ranges(
8437 snapshot: &DisplaySnapshot,
8438 range: Range<Anchor>,
8439 ) -> Vec<language::RunnableRange> {
8440 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8441 }
8442
8443 fn runnable_rows(
8444 project: Model<Project>,
8445 snapshot: DisplaySnapshot,
8446 runnable_ranges: Vec<RunnableRange>,
8447 mut cx: AsyncWindowContext,
8448 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8449 runnable_ranges
8450 .into_iter()
8451 .filter_map(|mut runnable| {
8452 let tasks = cx
8453 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8454 .ok()?;
8455 if tasks.is_empty() {
8456 return None;
8457 }
8458
8459 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8460
8461 let row = snapshot
8462 .buffer_snapshot
8463 .buffer_line_for_row(MultiBufferRow(point.row))?
8464 .1
8465 .start
8466 .row;
8467
8468 let context_range =
8469 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8470 Some((
8471 (runnable.buffer_id, row),
8472 RunnableTasks {
8473 templates: tasks,
8474 offset: MultiBufferOffset(runnable.run_range.start),
8475 context_range,
8476 column: point.column,
8477 extra_variables: runnable.extra_captures,
8478 },
8479 ))
8480 })
8481 .collect()
8482 }
8483
8484 fn templates_with_tags(
8485 project: &Model<Project>,
8486 runnable: &mut Runnable,
8487 cx: &WindowContext<'_>,
8488 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8489 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8490 let (worktree_id, file) = project
8491 .buffer_for_id(runnable.buffer)
8492 .and_then(|buffer| buffer.read(cx).file())
8493 .map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
8494 .unzip();
8495
8496 (project.task_inventory().clone(), worktree_id, file)
8497 });
8498
8499 let inventory = inventory.read(cx);
8500 let tags = mem::take(&mut runnable.tags);
8501 let mut tags: Vec<_> = tags
8502 .into_iter()
8503 .flat_map(|tag| {
8504 let tag = tag.0.clone();
8505 inventory
8506 .list_tasks(
8507 file.clone(),
8508 Some(runnable.language.clone()),
8509 worktree_id,
8510 cx,
8511 )
8512 .into_iter()
8513 .filter(move |(_, template)| {
8514 template.tags.iter().any(|source_tag| source_tag == &tag)
8515 })
8516 })
8517 .sorted_by_key(|(kind, _)| kind.to_owned())
8518 .collect();
8519 if let Some((leading_tag_source, _)) = tags.first() {
8520 // Strongest source wins; if we have worktree tag binding, prefer that to
8521 // global and language bindings;
8522 // if we have a global binding, prefer that to language binding.
8523 let first_mismatch = tags
8524 .iter()
8525 .position(|(tag_source, _)| tag_source != leading_tag_source);
8526 if let Some(index) = first_mismatch {
8527 tags.truncate(index);
8528 }
8529 }
8530
8531 tags
8532 }
8533
8534 pub fn move_to_enclosing_bracket(
8535 &mut self,
8536 _: &MoveToEnclosingBracket,
8537 cx: &mut ViewContext<Self>,
8538 ) {
8539 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8540 s.move_offsets_with(|snapshot, selection| {
8541 let Some(enclosing_bracket_ranges) =
8542 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8543 else {
8544 return;
8545 };
8546
8547 let mut best_length = usize::MAX;
8548 let mut best_inside = false;
8549 let mut best_in_bracket_range = false;
8550 let mut best_destination = None;
8551 for (open, close) in enclosing_bracket_ranges {
8552 let close = close.to_inclusive();
8553 let length = close.end() - open.start;
8554 let inside = selection.start >= open.end && selection.end <= *close.start();
8555 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8556 || close.contains(&selection.head());
8557
8558 // If best is next to a bracket and current isn't, skip
8559 if !in_bracket_range && best_in_bracket_range {
8560 continue;
8561 }
8562
8563 // Prefer smaller lengths unless best is inside and current isn't
8564 if length > best_length && (best_inside || !inside) {
8565 continue;
8566 }
8567
8568 best_length = length;
8569 best_inside = inside;
8570 best_in_bracket_range = in_bracket_range;
8571 best_destination = Some(
8572 if close.contains(&selection.start) && close.contains(&selection.end) {
8573 if inside {
8574 open.end
8575 } else {
8576 open.start
8577 }
8578 } else {
8579 if inside {
8580 *close.start()
8581 } else {
8582 *close.end()
8583 }
8584 },
8585 );
8586 }
8587
8588 if let Some(destination) = best_destination {
8589 selection.collapse_to(destination, SelectionGoal::None);
8590 }
8591 })
8592 });
8593 }
8594
8595 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
8596 self.end_selection(cx);
8597 self.selection_history.mode = SelectionHistoryMode::Undoing;
8598 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
8599 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8600 self.select_next_state = entry.select_next_state;
8601 self.select_prev_state = entry.select_prev_state;
8602 self.add_selections_state = entry.add_selections_state;
8603 self.request_autoscroll(Autoscroll::newest(), cx);
8604 }
8605 self.selection_history.mode = SelectionHistoryMode::Normal;
8606 }
8607
8608 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
8609 self.end_selection(cx);
8610 self.selection_history.mode = SelectionHistoryMode::Redoing;
8611 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
8612 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
8613 self.select_next_state = entry.select_next_state;
8614 self.select_prev_state = entry.select_prev_state;
8615 self.add_selections_state = entry.add_selections_state;
8616 self.request_autoscroll(Autoscroll::newest(), cx);
8617 }
8618 self.selection_history.mode = SelectionHistoryMode::Normal;
8619 }
8620
8621 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
8622 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
8623 }
8624
8625 pub fn expand_excerpts_down(
8626 &mut self,
8627 action: &ExpandExcerptsDown,
8628 cx: &mut ViewContext<Self>,
8629 ) {
8630 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
8631 }
8632
8633 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
8634 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
8635 }
8636
8637 pub fn expand_excerpts_for_direction(
8638 &mut self,
8639 lines: u32,
8640 direction: ExpandExcerptDirection,
8641 cx: &mut ViewContext<Self>,
8642 ) {
8643 let selections = self.selections.disjoint_anchors();
8644
8645 let lines = if lines == 0 {
8646 EditorSettings::get_global(cx).expand_excerpt_lines
8647 } else {
8648 lines
8649 };
8650
8651 self.buffer.update(cx, |buffer, cx| {
8652 buffer.expand_excerpts(
8653 selections
8654 .into_iter()
8655 .map(|selection| selection.head().excerpt_id)
8656 .dedup(),
8657 lines,
8658 direction,
8659 cx,
8660 )
8661 })
8662 }
8663
8664 pub fn expand_excerpt(
8665 &mut self,
8666 excerpt: ExcerptId,
8667 direction: ExpandExcerptDirection,
8668 cx: &mut ViewContext<Self>,
8669 ) {
8670 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
8671 self.buffer.update(cx, |buffer, cx| {
8672 buffer.expand_excerpts([excerpt], lines, direction, cx)
8673 })
8674 }
8675
8676 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
8677 self.go_to_diagnostic_impl(Direction::Next, cx)
8678 }
8679
8680 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
8681 self.go_to_diagnostic_impl(Direction::Prev, cx)
8682 }
8683
8684 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
8685 let buffer = self.buffer.read(cx).snapshot(cx);
8686 let selection = self.selections.newest::<usize>(cx);
8687
8688 // If there is an active Diagnostic Popover jump to its diagnostic instead.
8689 if direction == Direction::Next {
8690 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
8691 let (group_id, jump_to) = popover.activation_info();
8692 if self.activate_diagnostics(group_id, cx) {
8693 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8694 let mut new_selection = s.newest_anchor().clone();
8695 new_selection.collapse_to(jump_to, SelectionGoal::None);
8696 s.select_anchors(vec![new_selection.clone()]);
8697 });
8698 }
8699 return;
8700 }
8701 }
8702
8703 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
8704 active_diagnostics
8705 .primary_range
8706 .to_offset(&buffer)
8707 .to_inclusive()
8708 });
8709 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
8710 if active_primary_range.contains(&selection.head()) {
8711 *active_primary_range.start()
8712 } else {
8713 selection.head()
8714 }
8715 } else {
8716 selection.head()
8717 };
8718 let snapshot = self.snapshot(cx);
8719 loop {
8720 let diagnostics = if direction == Direction::Prev {
8721 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
8722 } else {
8723 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
8724 }
8725 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
8726 let group = diagnostics
8727 // relies on diagnostics_in_range to return diagnostics with the same starting range to
8728 // be sorted in a stable way
8729 // skip until we are at current active diagnostic, if it exists
8730 .skip_while(|entry| {
8731 (match direction {
8732 Direction::Prev => entry.range.start >= search_start,
8733 Direction::Next => entry.range.start <= search_start,
8734 }) && self
8735 .active_diagnostics
8736 .as_ref()
8737 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
8738 })
8739 .find_map(|entry| {
8740 if entry.diagnostic.is_primary
8741 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
8742 && !entry.range.is_empty()
8743 // if we match with the active diagnostic, skip it
8744 && Some(entry.diagnostic.group_id)
8745 != self.active_diagnostics.as_ref().map(|d| d.group_id)
8746 {
8747 Some((entry.range, entry.diagnostic.group_id))
8748 } else {
8749 None
8750 }
8751 });
8752
8753 if let Some((primary_range, group_id)) = group {
8754 if self.activate_diagnostics(group_id, cx) {
8755 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8756 s.select(vec![Selection {
8757 id: selection.id,
8758 start: primary_range.start,
8759 end: primary_range.start,
8760 reversed: false,
8761 goal: SelectionGoal::None,
8762 }]);
8763 });
8764 }
8765 break;
8766 } else {
8767 // Cycle around to the start of the buffer, potentially moving back to the start of
8768 // the currently active diagnostic.
8769 active_primary_range.take();
8770 if direction == Direction::Prev {
8771 if search_start == buffer.len() {
8772 break;
8773 } else {
8774 search_start = buffer.len();
8775 }
8776 } else if search_start == 0 {
8777 break;
8778 } else {
8779 search_start = 0;
8780 }
8781 }
8782 }
8783 }
8784
8785 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
8786 let snapshot = self
8787 .display_map
8788 .update(cx, |display_map, cx| display_map.snapshot(cx));
8789 let selection = self.selections.newest::<Point>(cx);
8790
8791 if !self.seek_in_direction(
8792 &snapshot,
8793 selection.head(),
8794 false,
8795 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8796 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
8797 ),
8798 cx,
8799 ) {
8800 let wrapped_point = Point::zero();
8801 self.seek_in_direction(
8802 &snapshot,
8803 wrapped_point,
8804 true,
8805 snapshot.buffer_snapshot.git_diff_hunks_in_range(
8806 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
8807 ),
8808 cx,
8809 );
8810 }
8811 }
8812
8813 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
8814 let snapshot = self
8815 .display_map
8816 .update(cx, |display_map, cx| display_map.snapshot(cx));
8817 let selection = self.selections.newest::<Point>(cx);
8818
8819 if !self.seek_in_direction(
8820 &snapshot,
8821 selection.head(),
8822 false,
8823 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8824 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
8825 ),
8826 cx,
8827 ) {
8828 let wrapped_point = snapshot.buffer_snapshot.max_point();
8829 self.seek_in_direction(
8830 &snapshot,
8831 wrapped_point,
8832 true,
8833 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
8834 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
8835 ),
8836 cx,
8837 );
8838 }
8839 }
8840
8841 fn seek_in_direction(
8842 &mut self,
8843 snapshot: &DisplaySnapshot,
8844 initial_point: Point,
8845 is_wrapped: bool,
8846 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
8847 cx: &mut ViewContext<Editor>,
8848 ) -> bool {
8849 let display_point = initial_point.to_display_point(snapshot);
8850 let mut hunks = hunks
8851 .map(|hunk| diff_hunk_to_display(&hunk, &snapshot))
8852 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
8853 .dedup();
8854
8855 if let Some(hunk) = hunks.next() {
8856 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8857 let row = hunk.start_display_row();
8858 let point = DisplayPoint::new(row, 0);
8859 s.select_display_ranges([point..point]);
8860 });
8861
8862 true
8863 } else {
8864 false
8865 }
8866 }
8867
8868 pub fn go_to_definition(
8869 &mut self,
8870 _: &GoToDefinition,
8871 cx: &mut ViewContext<Self>,
8872 ) -> Task<Result<bool>> {
8873 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx)
8874 }
8875
8876 pub fn go_to_implementation(
8877 &mut self,
8878 _: &GoToImplementation,
8879 cx: &mut ViewContext<Self>,
8880 ) -> Task<Result<bool>> {
8881 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
8882 }
8883
8884 pub fn go_to_implementation_split(
8885 &mut self,
8886 _: &GoToImplementationSplit,
8887 cx: &mut ViewContext<Self>,
8888 ) -> Task<Result<bool>> {
8889 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
8890 }
8891
8892 pub fn go_to_type_definition(
8893 &mut self,
8894 _: &GoToTypeDefinition,
8895 cx: &mut ViewContext<Self>,
8896 ) -> Task<Result<bool>> {
8897 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
8898 }
8899
8900 pub fn go_to_definition_split(
8901 &mut self,
8902 _: &GoToDefinitionSplit,
8903 cx: &mut ViewContext<Self>,
8904 ) -> Task<Result<bool>> {
8905 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
8906 }
8907
8908 pub fn go_to_type_definition_split(
8909 &mut self,
8910 _: &GoToTypeDefinitionSplit,
8911 cx: &mut ViewContext<Self>,
8912 ) -> Task<Result<bool>> {
8913 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
8914 }
8915
8916 fn go_to_definition_of_kind(
8917 &mut self,
8918 kind: GotoDefinitionKind,
8919 split: bool,
8920 cx: &mut ViewContext<Self>,
8921 ) -> Task<Result<bool>> {
8922 let Some(workspace) = self.workspace() else {
8923 return Task::ready(Ok(false));
8924 };
8925 let buffer = self.buffer.read(cx);
8926 let head = self.selections.newest::<usize>(cx).head();
8927 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
8928 text_anchor
8929 } else {
8930 return Task::ready(Ok(false));
8931 };
8932
8933 let project = workspace.read(cx).project().clone();
8934 let definitions = project.update(cx, |project, cx| match kind {
8935 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
8936 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
8937 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
8938 });
8939
8940 cx.spawn(|editor, mut cx| async move {
8941 let definitions = definitions.await?;
8942 let navigated = editor
8943 .update(&mut cx, |editor, cx| {
8944 editor.navigate_to_hover_links(
8945 Some(kind),
8946 definitions
8947 .into_iter()
8948 .filter(|location| {
8949 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
8950 })
8951 .map(HoverLink::Text)
8952 .collect::<Vec<_>>(),
8953 split,
8954 cx,
8955 )
8956 })?
8957 .await?;
8958 anyhow::Ok(navigated)
8959 })
8960 }
8961
8962 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
8963 let position = self.selections.newest_anchor().head();
8964 let Some((buffer, buffer_position)) =
8965 self.buffer.read(cx).text_anchor_for_position(position, cx)
8966 else {
8967 return;
8968 };
8969
8970 cx.spawn(|editor, mut cx| async move {
8971 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
8972 editor.update(&mut cx, |_, cx| {
8973 cx.open_url(&url);
8974 })
8975 } else {
8976 Ok(())
8977 }
8978 })
8979 .detach();
8980 }
8981
8982 pub(crate) fn navigate_to_hover_links(
8983 &mut self,
8984 kind: Option<GotoDefinitionKind>,
8985 mut definitions: Vec<HoverLink>,
8986 split: bool,
8987 cx: &mut ViewContext<Editor>,
8988 ) -> Task<Result<bool>> {
8989 // If there is one definition, just open it directly
8990 if definitions.len() == 1 {
8991 let definition = definitions.pop().unwrap();
8992 let target_task = match definition {
8993 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
8994 HoverLink::InlayHint(lsp_location, server_id) => {
8995 self.compute_target_location(lsp_location, server_id, cx)
8996 }
8997 HoverLink::Url(url) => {
8998 cx.open_url(&url);
8999 Task::ready(Ok(None))
9000 }
9001 };
9002 cx.spawn(|editor, mut cx| async move {
9003 let target = target_task.await.context("target resolution task")?;
9004 if let Some(target) = target {
9005 editor.update(&mut cx, |editor, cx| {
9006 let Some(workspace) = editor.workspace() else {
9007 return false;
9008 };
9009 let pane = workspace.read(cx).active_pane().clone();
9010
9011 let range = target.range.to_offset(target.buffer.read(cx));
9012 let range = editor.range_for_match(&range);
9013
9014 /// If select range has more than one line, we
9015 /// just point the cursor to range.start.
9016 fn check_multiline_range(
9017 buffer: &Buffer,
9018 range: Range<usize>,
9019 ) -> Range<usize> {
9020 if buffer.offset_to_point(range.start).row
9021 == buffer.offset_to_point(range.end).row
9022 {
9023 range
9024 } else {
9025 range.start..range.start
9026 }
9027 }
9028
9029 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9030 let buffer = target.buffer.read(cx);
9031 let range = check_multiline_range(buffer, range);
9032 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9033 s.select_ranges([range]);
9034 });
9035 } else {
9036 cx.window_context().defer(move |cx| {
9037 let target_editor: View<Self> =
9038 workspace.update(cx, |workspace, cx| {
9039 let pane = if split {
9040 workspace.adjacent_pane(cx)
9041 } else {
9042 workspace.active_pane().clone()
9043 };
9044
9045 workspace.open_project_item(pane, target.buffer.clone(), cx)
9046 });
9047 target_editor.update(cx, |target_editor, cx| {
9048 // When selecting a definition in a different buffer, disable the nav history
9049 // to avoid creating a history entry at the previous cursor location.
9050 pane.update(cx, |pane, _| pane.disable_history());
9051 let buffer = target.buffer.read(cx);
9052 let range = check_multiline_range(buffer, range);
9053 target_editor.change_selections(
9054 Some(Autoscroll::focused()),
9055 cx,
9056 |s| {
9057 s.select_ranges([range]);
9058 },
9059 );
9060 pane.update(cx, |pane, _| pane.enable_history());
9061 });
9062 });
9063 }
9064 true
9065 })
9066 } else {
9067 Ok(false)
9068 }
9069 })
9070 } else if !definitions.is_empty() {
9071 let replica_id = self.replica_id(cx);
9072 cx.spawn(|editor, mut cx| async move {
9073 let (title, location_tasks, workspace) = editor
9074 .update(&mut cx, |editor, cx| {
9075 let tab_kind = match kind {
9076 Some(GotoDefinitionKind::Implementation) => "Implementations",
9077 _ => "Definitions",
9078 };
9079 let title = definitions
9080 .iter()
9081 .find_map(|definition| match definition {
9082 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9083 let buffer = origin.buffer.read(cx);
9084 format!(
9085 "{} for {}",
9086 tab_kind,
9087 buffer
9088 .text_for_range(origin.range.clone())
9089 .collect::<String>()
9090 )
9091 }),
9092 HoverLink::InlayHint(_, _) => None,
9093 HoverLink::Url(_) => None,
9094 })
9095 .unwrap_or(tab_kind.to_string());
9096 let location_tasks = definitions
9097 .into_iter()
9098 .map(|definition| match definition {
9099 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9100 HoverLink::InlayHint(lsp_location, server_id) => {
9101 editor.compute_target_location(lsp_location, server_id, cx)
9102 }
9103 HoverLink::Url(_) => Task::ready(Ok(None)),
9104 })
9105 .collect::<Vec<_>>();
9106 (title, location_tasks, editor.workspace().clone())
9107 })
9108 .context("location tasks preparation")?;
9109
9110 let locations = futures::future::join_all(location_tasks)
9111 .await
9112 .into_iter()
9113 .filter_map(|location| location.transpose())
9114 .collect::<Result<_>>()
9115 .context("location tasks")?;
9116
9117 let Some(workspace) = workspace else {
9118 return Ok(false);
9119 };
9120 let opened = workspace
9121 .update(&mut cx, |workspace, cx| {
9122 Self::open_locations_in_multibuffer(
9123 workspace, locations, replica_id, title, split, cx,
9124 )
9125 })
9126 .ok();
9127
9128 anyhow::Ok(opened.is_some())
9129 })
9130 } else {
9131 Task::ready(Ok(false))
9132 }
9133 }
9134
9135 fn compute_target_location(
9136 &self,
9137 lsp_location: lsp::Location,
9138 server_id: LanguageServerId,
9139 cx: &mut ViewContext<Editor>,
9140 ) -> Task<anyhow::Result<Option<Location>>> {
9141 let Some(project) = self.project.clone() else {
9142 return Task::Ready(Some(Ok(None)));
9143 };
9144
9145 cx.spawn(move |editor, mut cx| async move {
9146 let location_task = editor.update(&mut cx, |editor, cx| {
9147 project.update(cx, |project, cx| {
9148 let language_server_name =
9149 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9150 project
9151 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9152 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9153 });
9154 language_server_name.map(|language_server_name| {
9155 project.open_local_buffer_via_lsp(
9156 lsp_location.uri.clone(),
9157 server_id,
9158 language_server_name,
9159 cx,
9160 )
9161 })
9162 })
9163 })?;
9164 let location = match location_task {
9165 Some(task) => Some({
9166 let target_buffer_handle = task.await.context("open local buffer")?;
9167 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9168 let target_start = target_buffer
9169 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9170 let target_end = target_buffer
9171 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9172 target_buffer.anchor_after(target_start)
9173 ..target_buffer.anchor_before(target_end)
9174 })?;
9175 Location {
9176 buffer: target_buffer_handle,
9177 range,
9178 }
9179 }),
9180 None => None,
9181 };
9182 Ok(location)
9183 })
9184 }
9185
9186 pub fn find_all_references(
9187 &mut self,
9188 _: &FindAllReferences,
9189 cx: &mut ViewContext<Self>,
9190 ) -> Option<Task<Result<()>>> {
9191 let multi_buffer = self.buffer.read(cx);
9192 let selection = self.selections.newest::<usize>(cx);
9193 let head = selection.head();
9194
9195 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9196 let head_anchor = multi_buffer_snapshot.anchor_at(
9197 head,
9198 if head < selection.tail() {
9199 Bias::Right
9200 } else {
9201 Bias::Left
9202 },
9203 );
9204
9205 match self
9206 .find_all_references_task_sources
9207 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9208 {
9209 Ok(_) => {
9210 log::info!(
9211 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9212 );
9213 return None;
9214 }
9215 Err(i) => {
9216 self.find_all_references_task_sources.insert(i, head_anchor);
9217 }
9218 }
9219
9220 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9221 let replica_id = self.replica_id(cx);
9222 let workspace = self.workspace()?;
9223 let project = workspace.read(cx).project().clone();
9224 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9225 Some(cx.spawn(|editor, mut cx| async move {
9226 let _cleanup = defer({
9227 let mut cx = cx.clone();
9228 move || {
9229 let _ = editor.update(&mut cx, |editor, _| {
9230 if let Ok(i) =
9231 editor
9232 .find_all_references_task_sources
9233 .binary_search_by(|anchor| {
9234 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9235 })
9236 {
9237 editor.find_all_references_task_sources.remove(i);
9238 }
9239 });
9240 }
9241 });
9242
9243 let locations = references.await?;
9244 if locations.is_empty() {
9245 return anyhow::Ok(());
9246 }
9247
9248 workspace.update(&mut cx, |workspace, cx| {
9249 let title = locations
9250 .first()
9251 .as_ref()
9252 .map(|location| {
9253 let buffer = location.buffer.read(cx);
9254 format!(
9255 "References to `{}`",
9256 buffer
9257 .text_for_range(location.range.clone())
9258 .collect::<String>()
9259 )
9260 })
9261 .unwrap();
9262 Self::open_locations_in_multibuffer(
9263 workspace, locations, replica_id, title, false, cx,
9264 );
9265 })
9266 }))
9267 }
9268
9269 /// Opens a multibuffer with the given project locations in it
9270 pub fn open_locations_in_multibuffer(
9271 workspace: &mut Workspace,
9272 mut locations: Vec<Location>,
9273 replica_id: ReplicaId,
9274 title: String,
9275 split: bool,
9276 cx: &mut ViewContext<Workspace>,
9277 ) {
9278 // If there are multiple definitions, open them in a multibuffer
9279 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9280 let mut locations = locations.into_iter().peekable();
9281 let mut ranges_to_highlight = Vec::new();
9282 let capability = workspace.project().read(cx).capability();
9283
9284 let excerpt_buffer = cx.new_model(|cx| {
9285 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9286 while let Some(location) = locations.next() {
9287 let buffer = location.buffer.read(cx);
9288 let mut ranges_for_buffer = Vec::new();
9289 let range = location.range.to_offset(buffer);
9290 ranges_for_buffer.push(range.clone());
9291
9292 while let Some(next_location) = locations.peek() {
9293 if next_location.buffer == location.buffer {
9294 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9295 locations.next();
9296 } else {
9297 break;
9298 }
9299 }
9300
9301 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9302 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9303 location.buffer.clone(),
9304 ranges_for_buffer,
9305 DEFAULT_MULTIBUFFER_CONTEXT,
9306 cx,
9307 ))
9308 }
9309
9310 multibuffer.with_title(title)
9311 });
9312
9313 let editor = cx.new_view(|cx| {
9314 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9315 });
9316 editor.update(cx, |editor, cx| {
9317 if let Some(first_range) = ranges_to_highlight.first() {
9318 editor.change_selections(None, cx, |selections| {
9319 selections.clear_disjoint();
9320 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9321 });
9322 }
9323 editor.highlight_background::<Self>(
9324 &ranges_to_highlight,
9325 |theme| theme.editor_highlighted_line_background,
9326 cx,
9327 );
9328 });
9329
9330 let item = Box::new(editor);
9331 let item_id = item.item_id();
9332
9333 if split {
9334 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9335 } else {
9336 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9337 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9338 pane.close_current_preview_item(cx)
9339 } else {
9340 None
9341 }
9342 });
9343 workspace.add_item_to_active_pane(item.clone(), destination_index, cx);
9344 }
9345 workspace.active_pane().update(cx, |pane, cx| {
9346 pane.set_preview_item_id(Some(item_id), cx);
9347 });
9348 }
9349
9350 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9351 use language::ToOffset as _;
9352
9353 let project = self.project.clone()?;
9354 let selection = self.selections.newest_anchor().clone();
9355 let (cursor_buffer, cursor_buffer_position) = self
9356 .buffer
9357 .read(cx)
9358 .text_anchor_for_position(selection.head(), cx)?;
9359 let (tail_buffer, cursor_buffer_position_end) = self
9360 .buffer
9361 .read(cx)
9362 .text_anchor_for_position(selection.tail(), cx)?;
9363 if tail_buffer != cursor_buffer {
9364 return None;
9365 }
9366
9367 let snapshot = cursor_buffer.read(cx).snapshot();
9368 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9369 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9370 let prepare_rename = project.update(cx, |project, cx| {
9371 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9372 });
9373 drop(snapshot);
9374
9375 Some(cx.spawn(|this, mut cx| async move {
9376 let rename_range = if let Some(range) = prepare_rename.await? {
9377 Some(range)
9378 } else {
9379 this.update(&mut cx, |this, cx| {
9380 let buffer = this.buffer.read(cx).snapshot(cx);
9381 let mut buffer_highlights = this
9382 .document_highlights_for_position(selection.head(), &buffer)
9383 .filter(|highlight| {
9384 highlight.start.excerpt_id == selection.head().excerpt_id
9385 && highlight.end.excerpt_id == selection.head().excerpt_id
9386 });
9387 buffer_highlights
9388 .next()
9389 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9390 })?
9391 };
9392 if let Some(rename_range) = rename_range {
9393 this.update(&mut cx, |this, cx| {
9394 let snapshot = cursor_buffer.read(cx).snapshot();
9395 let rename_buffer_range = rename_range.to_offset(&snapshot);
9396 let cursor_offset_in_rename_range =
9397 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9398 let cursor_offset_in_rename_range_end =
9399 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9400
9401 this.take_rename(false, cx);
9402 let buffer = this.buffer.read(cx).read(cx);
9403 let cursor_offset = selection.head().to_offset(&buffer);
9404 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9405 let rename_end = rename_start + rename_buffer_range.len();
9406 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9407 let mut old_highlight_id = None;
9408 let old_name: Arc<str> = buffer
9409 .chunks(rename_start..rename_end, true)
9410 .map(|chunk| {
9411 if old_highlight_id.is_none() {
9412 old_highlight_id = chunk.syntax_highlight_id;
9413 }
9414 chunk.text
9415 })
9416 .collect::<String>()
9417 .into();
9418
9419 drop(buffer);
9420
9421 // Position the selection in the rename editor so that it matches the current selection.
9422 this.show_local_selections = false;
9423 let rename_editor = cx.new_view(|cx| {
9424 let mut editor = Editor::single_line(cx);
9425 editor.buffer.update(cx, |buffer, cx| {
9426 buffer.edit([(0..0, old_name.clone())], None, cx)
9427 });
9428 let rename_selection_range = match cursor_offset_in_rename_range
9429 .cmp(&cursor_offset_in_rename_range_end)
9430 {
9431 Ordering::Equal => {
9432 editor.select_all(&SelectAll, cx);
9433 return editor;
9434 }
9435 Ordering::Less => {
9436 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9437 }
9438 Ordering::Greater => {
9439 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9440 }
9441 };
9442 if rename_selection_range.end > old_name.len() {
9443 editor.select_all(&SelectAll, cx);
9444 } else {
9445 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9446 s.select_ranges([rename_selection_range]);
9447 });
9448 }
9449 editor
9450 });
9451
9452 let write_highlights =
9453 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9454 let read_highlights =
9455 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9456 let ranges = write_highlights
9457 .iter()
9458 .flat_map(|(_, ranges)| ranges.iter())
9459 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9460 .cloned()
9461 .collect();
9462
9463 this.highlight_text::<Rename>(
9464 ranges,
9465 HighlightStyle {
9466 fade_out: Some(0.6),
9467 ..Default::default()
9468 },
9469 cx,
9470 );
9471 let rename_focus_handle = rename_editor.focus_handle(cx);
9472 cx.focus(&rename_focus_handle);
9473 let block_id = this.insert_blocks(
9474 [BlockProperties {
9475 style: BlockStyle::Flex,
9476 position: range.start,
9477 height: 1,
9478 render: Box::new({
9479 let rename_editor = rename_editor.clone();
9480 move |cx: &mut BlockContext| {
9481 let mut text_style = cx.editor_style.text.clone();
9482 if let Some(highlight_style) = old_highlight_id
9483 .and_then(|h| h.style(&cx.editor_style.syntax))
9484 {
9485 text_style = text_style.highlight(highlight_style);
9486 }
9487 div()
9488 .pl(cx.anchor_x)
9489 .child(EditorElement::new(
9490 &rename_editor,
9491 EditorStyle {
9492 background: cx.theme().system().transparent,
9493 local_player: cx.editor_style.local_player,
9494 text: text_style,
9495 scrollbar_width: cx.editor_style.scrollbar_width,
9496 syntax: cx.editor_style.syntax.clone(),
9497 status: cx.editor_style.status.clone(),
9498 inlay_hints_style: HighlightStyle {
9499 color: Some(cx.theme().status().hint),
9500 font_weight: Some(FontWeight::BOLD),
9501 ..HighlightStyle::default()
9502 },
9503 suggestions_style: HighlightStyle {
9504 color: Some(cx.theme().status().predictive),
9505 ..HighlightStyle::default()
9506 },
9507 },
9508 ))
9509 .into_any_element()
9510 }
9511 }),
9512 disposition: BlockDisposition::Below,
9513 }],
9514 Some(Autoscroll::fit()),
9515 cx,
9516 )[0];
9517 this.pending_rename = Some(RenameState {
9518 range,
9519 old_name,
9520 editor: rename_editor,
9521 block_id,
9522 });
9523 })?;
9524 }
9525
9526 Ok(())
9527 }))
9528 }
9529
9530 pub fn confirm_rename(
9531 &mut self,
9532 _: &ConfirmRename,
9533 cx: &mut ViewContext<Self>,
9534 ) -> Option<Task<Result<()>>> {
9535 let rename = self.take_rename(false, cx)?;
9536 let workspace = self.workspace()?;
9537 let (start_buffer, start) = self
9538 .buffer
9539 .read(cx)
9540 .text_anchor_for_position(rename.range.start, cx)?;
9541 let (end_buffer, end) = self
9542 .buffer
9543 .read(cx)
9544 .text_anchor_for_position(rename.range.end, cx)?;
9545 if start_buffer != end_buffer {
9546 return None;
9547 }
9548
9549 let buffer = start_buffer;
9550 let range = start..end;
9551 let old_name = rename.old_name;
9552 let new_name = rename.editor.read(cx).text(cx);
9553
9554 let rename = workspace
9555 .read(cx)
9556 .project()
9557 .clone()
9558 .update(cx, |project, cx| {
9559 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
9560 });
9561 let workspace = workspace.downgrade();
9562
9563 Some(cx.spawn(|editor, mut cx| async move {
9564 let project_transaction = rename.await?;
9565 Self::open_project_transaction(
9566 &editor,
9567 workspace,
9568 project_transaction,
9569 format!("Rename: {} → {}", old_name, new_name),
9570 cx.clone(),
9571 )
9572 .await?;
9573
9574 editor.update(&mut cx, |editor, cx| {
9575 editor.refresh_document_highlights(cx);
9576 })?;
9577 Ok(())
9578 }))
9579 }
9580
9581 fn take_rename(
9582 &mut self,
9583 moving_cursor: bool,
9584 cx: &mut ViewContext<Self>,
9585 ) -> Option<RenameState> {
9586 let rename = self.pending_rename.take()?;
9587 if rename.editor.focus_handle(cx).is_focused(cx) {
9588 cx.focus(&self.focus_handle);
9589 }
9590
9591 self.remove_blocks(
9592 [rename.block_id].into_iter().collect(),
9593 Some(Autoscroll::fit()),
9594 cx,
9595 );
9596 self.clear_highlights::<Rename>(cx);
9597 self.show_local_selections = true;
9598
9599 if moving_cursor {
9600 let rename_editor = rename.editor.read(cx);
9601 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
9602
9603 // Update the selection to match the position of the selection inside
9604 // the rename editor.
9605 let snapshot = self.buffer.read(cx).read(cx);
9606 let rename_range = rename.range.to_offset(&snapshot);
9607 let cursor_in_editor = snapshot
9608 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
9609 .min(rename_range.end);
9610 drop(snapshot);
9611
9612 self.change_selections(None, cx, |s| {
9613 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
9614 });
9615 } else {
9616 self.refresh_document_highlights(cx);
9617 }
9618
9619 Some(rename)
9620 }
9621
9622 pub fn pending_rename(&self) -> Option<&RenameState> {
9623 self.pending_rename.as_ref()
9624 }
9625
9626 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9627 let project = match &self.project {
9628 Some(project) => project.clone(),
9629 None => return None,
9630 };
9631
9632 Some(self.perform_format(project, FormatTrigger::Manual, cx))
9633 }
9634
9635 fn perform_format(
9636 &mut self,
9637 project: Model<Project>,
9638 trigger: FormatTrigger,
9639 cx: &mut ViewContext<Self>,
9640 ) -> Task<Result<()>> {
9641 let buffer = self.buffer().clone();
9642 let mut buffers = buffer.read(cx).all_buffers();
9643 if trigger == FormatTrigger::Save {
9644 buffers.retain(|buffer| buffer.read(cx).is_dirty());
9645 }
9646
9647 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
9648 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
9649
9650 cx.spawn(|_, mut cx| async move {
9651 let transaction = futures::select_biased! {
9652 () = timeout => {
9653 log::warn!("timed out waiting for formatting");
9654 None
9655 }
9656 transaction = format.log_err().fuse() => transaction,
9657 };
9658
9659 buffer
9660 .update(&mut cx, |buffer, cx| {
9661 if let Some(transaction) = transaction {
9662 if !buffer.is_singleton() {
9663 buffer.push_transaction(&transaction.0, cx);
9664 }
9665 }
9666
9667 cx.notify();
9668 })
9669 .ok();
9670
9671 Ok(())
9672 })
9673 }
9674
9675 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
9676 if let Some(project) = self.project.clone() {
9677 self.buffer.update(cx, |multi_buffer, cx| {
9678 project.update(cx, |project, cx| {
9679 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
9680 });
9681 })
9682 }
9683 }
9684
9685 fn cancel_language_server_work(
9686 &mut self,
9687 _: &CancelLanguageServerWork,
9688 cx: &mut ViewContext<Self>,
9689 ) {
9690 if let Some(project) = self.project.clone() {
9691 self.buffer.update(cx, |multi_buffer, cx| {
9692 project.update(cx, |project, cx| {
9693 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
9694 });
9695 })
9696 }
9697 }
9698
9699 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
9700 cx.show_character_palette();
9701 }
9702
9703 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
9704 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
9705 let buffer = self.buffer.read(cx).snapshot(cx);
9706 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
9707 let is_valid = buffer
9708 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
9709 .any(|entry| {
9710 entry.diagnostic.is_primary
9711 && !entry.range.is_empty()
9712 && entry.range.start == primary_range_start
9713 && entry.diagnostic.message == active_diagnostics.primary_message
9714 });
9715
9716 if is_valid != active_diagnostics.is_valid {
9717 active_diagnostics.is_valid = is_valid;
9718 let mut new_styles = HashMap::default();
9719 for (block_id, diagnostic) in &active_diagnostics.blocks {
9720 new_styles.insert(
9721 *block_id,
9722 (
9723 None,
9724 diagnostic_block_renderer(diagnostic.clone(), is_valid),
9725 ),
9726 );
9727 }
9728 self.display_map.update(cx, |display_map, cx| {
9729 display_map.replace_blocks(new_styles, cx)
9730 });
9731 }
9732 }
9733 }
9734
9735 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
9736 self.dismiss_diagnostics(cx);
9737 let snapshot = self.snapshot(cx);
9738 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
9739 let buffer = self.buffer.read(cx).snapshot(cx);
9740
9741 let mut primary_range = None;
9742 let mut primary_message = None;
9743 let mut group_end = Point::zero();
9744 let diagnostic_group = buffer
9745 .diagnostic_group::<MultiBufferPoint>(group_id)
9746 .filter_map(|entry| {
9747 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
9748 && (entry.range.start.row == entry.range.end.row
9749 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
9750 {
9751 return None;
9752 }
9753 if entry.range.end > group_end {
9754 group_end = entry.range.end;
9755 }
9756 if entry.diagnostic.is_primary {
9757 primary_range = Some(entry.range.clone());
9758 primary_message = Some(entry.diagnostic.message.clone());
9759 }
9760 Some(entry)
9761 })
9762 .collect::<Vec<_>>();
9763 let primary_range = primary_range?;
9764 let primary_message = primary_message?;
9765 let primary_range =
9766 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
9767
9768 let blocks = display_map
9769 .insert_blocks(
9770 diagnostic_group.iter().map(|entry| {
9771 let diagnostic = entry.diagnostic.clone();
9772 let message_height = diagnostic.message.matches('\n').count() as u8 + 1;
9773 BlockProperties {
9774 style: BlockStyle::Fixed,
9775 position: buffer.anchor_after(entry.range.start),
9776 height: message_height,
9777 render: diagnostic_block_renderer(diagnostic, true),
9778 disposition: BlockDisposition::Below,
9779 }
9780 }),
9781 cx,
9782 )
9783 .into_iter()
9784 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
9785 .collect();
9786
9787 Some(ActiveDiagnosticGroup {
9788 primary_range,
9789 primary_message,
9790 group_id,
9791 blocks,
9792 is_valid: true,
9793 })
9794 });
9795 self.active_diagnostics.is_some()
9796 }
9797
9798 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
9799 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
9800 self.display_map.update(cx, |display_map, cx| {
9801 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
9802 });
9803 cx.notify();
9804 }
9805 }
9806
9807 pub fn set_selections_from_remote(
9808 &mut self,
9809 selections: Vec<Selection<Anchor>>,
9810 pending_selection: Option<Selection<Anchor>>,
9811 cx: &mut ViewContext<Self>,
9812 ) {
9813 let old_cursor_position = self.selections.newest_anchor().head();
9814 self.selections.change_with(cx, |s| {
9815 s.select_anchors(selections);
9816 if let Some(pending_selection) = pending_selection {
9817 s.set_pending(pending_selection, SelectMode::Character);
9818 } else {
9819 s.clear_pending();
9820 }
9821 });
9822 self.selections_did_change(false, &old_cursor_position, true, cx);
9823 }
9824
9825 fn push_to_selection_history(&mut self) {
9826 self.selection_history.push(SelectionHistoryEntry {
9827 selections: self.selections.disjoint_anchors(),
9828 select_next_state: self.select_next_state.clone(),
9829 select_prev_state: self.select_prev_state.clone(),
9830 add_selections_state: self.add_selections_state.clone(),
9831 });
9832 }
9833
9834 pub fn transact(
9835 &mut self,
9836 cx: &mut ViewContext<Self>,
9837 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
9838 ) -> Option<TransactionId> {
9839 self.start_transaction_at(Instant::now(), cx);
9840 update(self, cx);
9841 self.end_transaction_at(Instant::now(), cx)
9842 }
9843
9844 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
9845 self.end_selection(cx);
9846 if let Some(tx_id) = self
9847 .buffer
9848 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
9849 {
9850 self.selection_history
9851 .insert_transaction(tx_id, self.selections.disjoint_anchors());
9852 cx.emit(EditorEvent::TransactionBegun {
9853 transaction_id: tx_id,
9854 })
9855 }
9856 }
9857
9858 fn end_transaction_at(
9859 &mut self,
9860 now: Instant,
9861 cx: &mut ViewContext<Self>,
9862 ) -> Option<TransactionId> {
9863 if let Some(transaction_id) = self
9864 .buffer
9865 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
9866 {
9867 if let Some((_, end_selections)) =
9868 self.selection_history.transaction_mut(transaction_id)
9869 {
9870 *end_selections = Some(self.selections.disjoint_anchors());
9871 } else {
9872 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
9873 }
9874
9875 cx.emit(EditorEvent::Edited { transaction_id });
9876 Some(transaction_id)
9877 } else {
9878 None
9879 }
9880 }
9881
9882 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
9883 let mut fold_ranges = Vec::new();
9884
9885 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9886
9887 let selections = self.selections.all_adjusted(cx);
9888 for selection in selections {
9889 let range = selection.range().sorted();
9890 let buffer_start_row = range.start.row;
9891
9892 for row in (0..=range.end.row).rev() {
9893 if let Some((foldable_range, fold_text)) =
9894 display_map.foldable_range(MultiBufferRow(row))
9895 {
9896 if foldable_range.end.row >= buffer_start_row {
9897 fold_ranges.push((foldable_range, fold_text));
9898 if row <= range.start.row {
9899 break;
9900 }
9901 }
9902 }
9903 }
9904 }
9905
9906 self.fold_ranges(fold_ranges, true, cx);
9907 }
9908
9909 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
9910 let buffer_row = fold_at.buffer_row;
9911 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9912
9913 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
9914 let autoscroll = self
9915 .selections
9916 .all::<Point>(cx)
9917 .iter()
9918 .any(|selection| fold_range.overlaps(&selection.range()));
9919
9920 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
9921 }
9922 }
9923
9924 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
9925 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9926 let buffer = &display_map.buffer_snapshot;
9927 let selections = self.selections.all::<Point>(cx);
9928 let ranges = selections
9929 .iter()
9930 .map(|s| {
9931 let range = s.display_range(&display_map).sorted();
9932 let mut start = range.start.to_point(&display_map);
9933 let mut end = range.end.to_point(&display_map);
9934 start.column = 0;
9935 end.column = buffer.line_len(MultiBufferRow(end.row));
9936 start..end
9937 })
9938 .collect::<Vec<_>>();
9939
9940 self.unfold_ranges(ranges, true, true, cx);
9941 }
9942
9943 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
9944 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9945
9946 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
9947 ..Point::new(
9948 unfold_at.buffer_row.0,
9949 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
9950 );
9951
9952 let autoscroll = self
9953 .selections
9954 .all::<Point>(cx)
9955 .iter()
9956 .any(|selection| selection.range().overlaps(&intersection_range));
9957
9958 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
9959 }
9960
9961 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
9962 let selections = self.selections.all::<Point>(cx);
9963 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9964 let line_mode = self.selections.line_mode;
9965 let ranges = selections.into_iter().map(|s| {
9966 if line_mode {
9967 let start = Point::new(s.start.row, 0);
9968 let end = Point::new(
9969 s.end.row,
9970 display_map
9971 .buffer_snapshot
9972 .line_len(MultiBufferRow(s.end.row)),
9973 );
9974 (start..end, display_map.fold_placeholder.clone())
9975 } else {
9976 (s.start..s.end, display_map.fold_placeholder.clone())
9977 }
9978 });
9979 self.fold_ranges(ranges, true, cx);
9980 }
9981
9982 pub fn fold_ranges<T: ToOffset + Clone>(
9983 &mut self,
9984 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
9985 auto_scroll: bool,
9986 cx: &mut ViewContext<Self>,
9987 ) {
9988 let mut fold_ranges = Vec::new();
9989 let mut buffers_affected = HashMap::default();
9990 let multi_buffer = self.buffer().read(cx);
9991 for (fold_range, fold_text) in ranges {
9992 if let Some((_, buffer, _)) =
9993 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
9994 {
9995 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
9996 };
9997 fold_ranges.push((fold_range, fold_text));
9998 }
9999
10000 let mut ranges = fold_ranges.into_iter().peekable();
10001 if ranges.peek().is_some() {
10002 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10003
10004 if auto_scroll {
10005 self.request_autoscroll(Autoscroll::fit(), cx);
10006 }
10007
10008 for buffer in buffers_affected.into_values() {
10009 self.sync_expanded_diff_hunks(buffer, cx);
10010 }
10011
10012 cx.notify();
10013
10014 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10015 // Clear diagnostics block when folding a range that contains it.
10016 let snapshot = self.snapshot(cx);
10017 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10018 drop(snapshot);
10019 self.active_diagnostics = Some(active_diagnostics);
10020 self.dismiss_diagnostics(cx);
10021 } else {
10022 self.active_diagnostics = Some(active_diagnostics);
10023 }
10024 }
10025
10026 self.scrollbar_marker_state.dirty = true;
10027 }
10028 }
10029
10030 pub fn unfold_ranges<T: ToOffset + Clone>(
10031 &mut self,
10032 ranges: impl IntoIterator<Item = Range<T>>,
10033 inclusive: bool,
10034 auto_scroll: bool,
10035 cx: &mut ViewContext<Self>,
10036 ) {
10037 let mut unfold_ranges = Vec::new();
10038 let mut buffers_affected = HashMap::default();
10039 let multi_buffer = self.buffer().read(cx);
10040 for range in ranges {
10041 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10042 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10043 };
10044 unfold_ranges.push(range);
10045 }
10046
10047 let mut ranges = unfold_ranges.into_iter().peekable();
10048 if ranges.peek().is_some() {
10049 self.display_map
10050 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10051 if auto_scroll {
10052 self.request_autoscroll(Autoscroll::fit(), cx);
10053 }
10054
10055 for buffer in buffers_affected.into_values() {
10056 self.sync_expanded_diff_hunks(buffer, cx);
10057 }
10058
10059 cx.notify();
10060 self.scrollbar_marker_state.dirty = true;
10061 self.active_indent_guides_state.dirty = true;
10062 }
10063 }
10064
10065 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10066 if hovered != self.gutter_hovered {
10067 self.gutter_hovered = hovered;
10068 cx.notify();
10069 }
10070 }
10071
10072 pub fn insert_blocks(
10073 &mut self,
10074 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10075 autoscroll: Option<Autoscroll>,
10076 cx: &mut ViewContext<Self>,
10077 ) -> Vec<BlockId> {
10078 let blocks = self
10079 .display_map
10080 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10081 if let Some(autoscroll) = autoscroll {
10082 self.request_autoscroll(autoscroll, cx);
10083 }
10084 blocks
10085 }
10086
10087 pub fn replace_blocks(
10088 &mut self,
10089 blocks: HashMap<BlockId, (Option<u8>, RenderBlock)>,
10090 autoscroll: Option<Autoscroll>,
10091 cx: &mut ViewContext<Self>,
10092 ) {
10093 self.display_map
10094 .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
10095 if let Some(autoscroll) = autoscroll {
10096 self.request_autoscroll(autoscroll, cx);
10097 }
10098 }
10099
10100 pub fn remove_blocks(
10101 &mut self,
10102 block_ids: HashSet<BlockId>,
10103 autoscroll: Option<Autoscroll>,
10104 cx: &mut ViewContext<Self>,
10105 ) {
10106 self.display_map.update(cx, |display_map, cx| {
10107 display_map.remove_blocks(block_ids, cx)
10108 });
10109 if let Some(autoscroll) = autoscroll {
10110 self.request_autoscroll(autoscroll, cx);
10111 }
10112 }
10113
10114 pub fn row_for_block(
10115 &self,
10116 block_id: BlockId,
10117 cx: &mut ViewContext<Self>,
10118 ) -> Option<DisplayRow> {
10119 self.display_map
10120 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10121 }
10122
10123 pub fn insert_creases(
10124 &mut self,
10125 creases: impl IntoIterator<Item = Crease>,
10126 cx: &mut ViewContext<Self>,
10127 ) -> Vec<CreaseId> {
10128 self.display_map
10129 .update(cx, |map, cx| map.insert_creases(creases, cx))
10130 }
10131
10132 pub fn remove_creases(
10133 &mut self,
10134 ids: impl IntoIterator<Item = CreaseId>,
10135 cx: &mut ViewContext<Self>,
10136 ) {
10137 self.display_map
10138 .update(cx, |map, cx| map.remove_creases(ids, cx));
10139 }
10140
10141 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10142 self.display_map
10143 .update(cx, |map, cx| map.snapshot(cx))
10144 .longest_row()
10145 }
10146
10147 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10148 self.display_map
10149 .update(cx, |map, cx| map.snapshot(cx))
10150 .max_point()
10151 }
10152
10153 pub fn text(&self, cx: &AppContext) -> String {
10154 self.buffer.read(cx).read(cx).text()
10155 }
10156
10157 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10158 let text = self.text(cx);
10159 let text = text.trim();
10160
10161 if text.is_empty() {
10162 return None;
10163 }
10164
10165 Some(text.to_string())
10166 }
10167
10168 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10169 self.transact(cx, |this, cx| {
10170 this.buffer
10171 .read(cx)
10172 .as_singleton()
10173 .expect("you can only call set_text on editors for singleton buffers")
10174 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10175 });
10176 }
10177
10178 pub fn display_text(&self, cx: &mut AppContext) -> String {
10179 self.display_map
10180 .update(cx, |map, cx| map.snapshot(cx))
10181 .text()
10182 }
10183
10184 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10185 let mut wrap_guides = smallvec::smallvec![];
10186
10187 if self.show_wrap_guides == Some(false) {
10188 return wrap_guides;
10189 }
10190
10191 let settings = self.buffer.read(cx).settings_at(0, cx);
10192 if settings.show_wrap_guides {
10193 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10194 wrap_guides.push((soft_wrap as usize, true));
10195 }
10196 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10197 }
10198
10199 wrap_guides
10200 }
10201
10202 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10203 let settings = self.buffer.read(cx).settings_at(0, cx);
10204 let mode = self
10205 .soft_wrap_mode_override
10206 .unwrap_or_else(|| settings.soft_wrap);
10207 match mode {
10208 language_settings::SoftWrap::None => SoftWrap::None,
10209 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10210 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10211 language_settings::SoftWrap::PreferredLineLength => {
10212 SoftWrap::Column(settings.preferred_line_length)
10213 }
10214 }
10215 }
10216
10217 pub fn set_soft_wrap_mode(
10218 &mut self,
10219 mode: language_settings::SoftWrap,
10220 cx: &mut ViewContext<Self>,
10221 ) {
10222 self.soft_wrap_mode_override = Some(mode);
10223 cx.notify();
10224 }
10225
10226 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10227 let rem_size = cx.rem_size();
10228 self.display_map.update(cx, |map, cx| {
10229 map.set_font(
10230 style.text.font(),
10231 style.text.font_size.to_pixels(rem_size),
10232 cx,
10233 )
10234 });
10235 self.style = Some(style);
10236 }
10237
10238 pub fn style(&self) -> Option<&EditorStyle> {
10239 self.style.as_ref()
10240 }
10241
10242 // Called by the element. This method is not designed to be called outside of the editor
10243 // element's layout code because it does not notify when rewrapping is computed synchronously.
10244 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10245 self.display_map
10246 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10247 }
10248
10249 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10250 if self.soft_wrap_mode_override.is_some() {
10251 self.soft_wrap_mode_override.take();
10252 } else {
10253 let soft_wrap = match self.soft_wrap_mode(cx) {
10254 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10255 SoftWrap::EditorWidth | SoftWrap::Column(_) => {
10256 language_settings::SoftWrap::PreferLine
10257 }
10258 };
10259 self.soft_wrap_mode_override = Some(soft_wrap);
10260 }
10261 cx.notify();
10262 }
10263
10264 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10265 let Some(workspace) = self.workspace() else {
10266 return;
10267 };
10268 let fs = workspace.read(cx).app_state().fs.clone();
10269 let current_show = TabBarSettings::get_global(cx).show;
10270 update_settings_file::<TabBarSettings>(fs, cx, move |setting| {
10271 setting.show = Some(!current_show);
10272 });
10273 }
10274
10275 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10276 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10277 self.buffer
10278 .read(cx)
10279 .settings_at(0, cx)
10280 .indent_guides
10281 .enabled
10282 });
10283 self.show_indent_guides = Some(!currently_enabled);
10284 cx.notify();
10285 }
10286
10287 fn should_show_indent_guides(&self) -> Option<bool> {
10288 self.show_indent_guides
10289 }
10290
10291 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10292 let mut editor_settings = EditorSettings::get_global(cx).clone();
10293 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10294 EditorSettings::override_global(editor_settings, cx);
10295 }
10296
10297 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10298 self.show_gutter = show_gutter;
10299 cx.notify();
10300 }
10301
10302 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10303 self.show_line_numbers = Some(show_line_numbers);
10304 cx.notify();
10305 }
10306
10307 pub fn set_show_git_diff_gutter(
10308 &mut self,
10309 show_git_diff_gutter: bool,
10310 cx: &mut ViewContext<Self>,
10311 ) {
10312 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10313 cx.notify();
10314 }
10315
10316 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10317 self.show_code_actions = Some(show_code_actions);
10318 cx.notify();
10319 }
10320
10321 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10322 self.show_runnables = Some(show_runnables);
10323 cx.notify();
10324 }
10325
10326 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10327 self.show_wrap_guides = Some(show_wrap_guides);
10328 cx.notify();
10329 }
10330
10331 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10332 self.show_indent_guides = Some(show_indent_guides);
10333 cx.notify();
10334 }
10335
10336 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10337 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10338 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10339 cx.reveal_path(&file.abs_path(cx));
10340 }
10341 }
10342 }
10343
10344 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10345 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10346 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10347 if let Some(path) = file.abs_path(cx).to_str() {
10348 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10349 }
10350 }
10351 }
10352 }
10353
10354 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10355 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10356 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10357 if let Some(path) = file.path().to_str() {
10358 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
10359 }
10360 }
10361 }
10362 }
10363
10364 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10365 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10366
10367 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10368 self.start_git_blame(true, cx);
10369 }
10370
10371 cx.notify();
10372 }
10373
10374 pub fn toggle_git_blame_inline(
10375 &mut self,
10376 _: &ToggleGitBlameInline,
10377 cx: &mut ViewContext<Self>,
10378 ) {
10379 self.toggle_git_blame_inline_internal(true, cx);
10380 cx.notify();
10381 }
10382
10383 pub fn git_blame_inline_enabled(&self) -> bool {
10384 self.git_blame_inline_enabled
10385 }
10386
10387 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10388 self.show_selection_menu = self
10389 .show_selection_menu
10390 .map(|show_selections_menu| !show_selections_menu)
10391 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10392
10393 cx.notify();
10394 }
10395
10396 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10397 self.show_selection_menu
10398 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10399 }
10400
10401 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10402 if let Some(project) = self.project.as_ref() {
10403 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10404 return;
10405 };
10406
10407 if buffer.read(cx).file().is_none() {
10408 return;
10409 }
10410
10411 let focused = self.focus_handle(cx).contains_focused(cx);
10412
10413 let project = project.clone();
10414 let blame =
10415 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10416 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10417 self.blame = Some(blame);
10418 }
10419 }
10420
10421 fn toggle_git_blame_inline_internal(
10422 &mut self,
10423 user_triggered: bool,
10424 cx: &mut ViewContext<Self>,
10425 ) {
10426 if self.git_blame_inline_enabled {
10427 self.git_blame_inline_enabled = false;
10428 self.show_git_blame_inline = false;
10429 self.show_git_blame_inline_delay_task.take();
10430 } else {
10431 self.git_blame_inline_enabled = true;
10432 self.start_git_blame_inline(user_triggered, cx);
10433 }
10434
10435 cx.notify();
10436 }
10437
10438 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10439 self.start_git_blame(user_triggered, cx);
10440
10441 if ProjectSettings::get_global(cx)
10442 .git
10443 .inline_blame_delay()
10444 .is_some()
10445 {
10446 self.start_inline_blame_timer(cx);
10447 } else {
10448 self.show_git_blame_inline = true
10449 }
10450 }
10451
10452 pub fn blame(&self) -> Option<&Model<GitBlame>> {
10453 self.blame.as_ref()
10454 }
10455
10456 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
10457 self.show_git_blame_gutter && self.has_blame_entries(cx)
10458 }
10459
10460 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
10461 self.show_git_blame_inline
10462 && self.focus_handle.is_focused(cx)
10463 && !self.newest_selection_head_on_empty_line(cx)
10464 && self.has_blame_entries(cx)
10465 }
10466
10467 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
10468 self.blame()
10469 .map_or(false, |blame| blame.read(cx).has_generated_entries())
10470 }
10471
10472 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
10473 let cursor_anchor = self.selections.newest_anchor().head();
10474
10475 let snapshot = self.buffer.read(cx).snapshot(cx);
10476 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
10477
10478 snapshot.line_len(buffer_row) == 0
10479 }
10480
10481 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
10482 let (path, selection, repo) = maybe!({
10483 let project_handle = self.project.as_ref()?.clone();
10484 let project = project_handle.read(cx);
10485
10486 let selection = self.selections.newest::<Point>(cx);
10487 let selection_range = selection.range();
10488
10489 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10490 (buffer, selection_range.start.row..selection_range.end.row)
10491 } else {
10492 let buffer_ranges = self
10493 .buffer()
10494 .read(cx)
10495 .range_to_buffer_ranges(selection_range, cx);
10496
10497 let (buffer, range, _) = if selection.reversed {
10498 buffer_ranges.first()
10499 } else {
10500 buffer_ranges.last()
10501 }?;
10502
10503 let snapshot = buffer.read(cx).snapshot();
10504 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
10505 ..text::ToPoint::to_point(&range.end, &snapshot).row;
10506 (buffer.clone(), selection)
10507 };
10508
10509 let path = buffer
10510 .read(cx)
10511 .file()?
10512 .as_local()?
10513 .path()
10514 .to_str()?
10515 .to_string();
10516 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
10517 Some((path, selection, repo))
10518 })
10519 .ok_or_else(|| anyhow!("unable to open git repository"))?;
10520
10521 const REMOTE_NAME: &str = "origin";
10522 let origin_url = repo
10523 .remote_url(REMOTE_NAME)
10524 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
10525 let sha = repo
10526 .head_sha()
10527 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
10528
10529 let (provider, remote) =
10530 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
10531 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
10532
10533 Ok(provider.build_permalink(
10534 remote,
10535 BuildPermalinkParams {
10536 sha: &sha,
10537 path: &path,
10538 selection: Some(selection),
10539 },
10540 ))
10541 }
10542
10543 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
10544 let permalink = self.get_permalink_to_line(cx);
10545
10546 match permalink {
10547 Ok(permalink) => {
10548 cx.write_to_clipboard(ClipboardItem::new(permalink.to_string()));
10549 }
10550 Err(err) => {
10551 let message = format!("Failed to copy permalink: {err}");
10552
10553 Err::<(), anyhow::Error>(err).log_err();
10554
10555 if let Some(workspace) = self.workspace() {
10556 workspace.update(cx, |workspace, cx| {
10557 struct CopyPermalinkToLine;
10558
10559 workspace.show_toast(
10560 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
10561 cx,
10562 )
10563 })
10564 }
10565 }
10566 }
10567 }
10568
10569 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
10570 let permalink = self.get_permalink_to_line(cx);
10571
10572 match permalink {
10573 Ok(permalink) => {
10574 cx.open_url(permalink.as_ref());
10575 }
10576 Err(err) => {
10577 let message = format!("Failed to open permalink: {err}");
10578
10579 Err::<(), anyhow::Error>(err).log_err();
10580
10581 if let Some(workspace) = self.workspace() {
10582 workspace.update(cx, |workspace, cx| {
10583 struct OpenPermalinkToLine;
10584
10585 workspace.show_toast(
10586 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
10587 cx,
10588 )
10589 })
10590 }
10591 }
10592 }
10593 }
10594
10595 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
10596 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
10597 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
10598 pub fn highlight_rows<T: 'static>(
10599 &mut self,
10600 rows: RangeInclusive<Anchor>,
10601 color: Option<Hsla>,
10602 should_autoscroll: bool,
10603 cx: &mut ViewContext<Self>,
10604 ) {
10605 let snapshot = self.buffer().read(cx).snapshot(cx);
10606 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
10607 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
10608 highlight
10609 .range
10610 .start()
10611 .cmp(&rows.start(), &snapshot)
10612 .then(highlight.range.end().cmp(&rows.end(), &snapshot))
10613 });
10614 match (color, existing_highlight_index) {
10615 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
10616 ix,
10617 RowHighlight {
10618 index: post_inc(&mut self.highlight_order),
10619 range: rows,
10620 should_autoscroll,
10621 color,
10622 },
10623 ),
10624 (None, Ok(i)) => {
10625 row_highlights.remove(i);
10626 }
10627 }
10628 }
10629
10630 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
10631 pub fn clear_row_highlights<T: 'static>(&mut self) {
10632 self.highlighted_rows.remove(&TypeId::of::<T>());
10633 }
10634
10635 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
10636 pub fn highlighted_rows<T: 'static>(
10637 &self,
10638 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
10639 Some(
10640 self.highlighted_rows
10641 .get(&TypeId::of::<T>())?
10642 .iter()
10643 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
10644 )
10645 }
10646
10647 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
10648 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
10649 /// Allows to ignore certain kinds of highlights.
10650 pub fn highlighted_display_rows(
10651 &mut self,
10652 cx: &mut WindowContext,
10653 ) -> BTreeMap<DisplayRow, Hsla> {
10654 let snapshot = self.snapshot(cx);
10655 let mut used_highlight_orders = HashMap::default();
10656 self.highlighted_rows
10657 .iter()
10658 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
10659 .fold(
10660 BTreeMap::<DisplayRow, Hsla>::new(),
10661 |mut unique_rows, highlight| {
10662 let start_row = highlight.range.start().to_display_point(&snapshot).row();
10663 let end_row = highlight.range.end().to_display_point(&snapshot).row();
10664 for row in start_row.0..=end_row.0 {
10665 let used_index =
10666 used_highlight_orders.entry(row).or_insert(highlight.index);
10667 if highlight.index >= *used_index {
10668 *used_index = highlight.index;
10669 match highlight.color {
10670 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
10671 None => unique_rows.remove(&DisplayRow(row)),
10672 };
10673 }
10674 }
10675 unique_rows
10676 },
10677 )
10678 }
10679
10680 pub fn highlighted_display_row_for_autoscroll(
10681 &self,
10682 snapshot: &DisplaySnapshot,
10683 ) -> Option<DisplayRow> {
10684 self.highlighted_rows
10685 .values()
10686 .flat_map(|highlighted_rows| highlighted_rows.iter())
10687 .filter_map(|highlight| {
10688 if highlight.color.is_none() || !highlight.should_autoscroll {
10689 return None;
10690 }
10691 Some(highlight.range.start().to_display_point(&snapshot).row())
10692 })
10693 .min()
10694 }
10695
10696 pub fn set_search_within_ranges(
10697 &mut self,
10698 ranges: &[Range<Anchor>],
10699 cx: &mut ViewContext<Self>,
10700 ) {
10701 self.highlight_background::<SearchWithinRange>(
10702 ranges,
10703 |colors| colors.editor_document_highlight_read_background,
10704 cx,
10705 )
10706 }
10707
10708 pub fn set_breadcrumb_header(&mut self, new_header: String) {
10709 self.breadcrumb_header = Some(new_header);
10710 }
10711
10712 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
10713 self.clear_background_highlights::<SearchWithinRange>(cx);
10714 }
10715
10716 pub fn highlight_background<T: 'static>(
10717 &mut self,
10718 ranges: &[Range<Anchor>],
10719 color_fetcher: fn(&ThemeColors) -> Hsla,
10720 cx: &mut ViewContext<Self>,
10721 ) {
10722 let snapshot = self.snapshot(cx);
10723 // this is to try and catch a panic sooner
10724 for range in ranges {
10725 snapshot
10726 .buffer_snapshot
10727 .summary_for_anchor::<usize>(&range.start);
10728 snapshot
10729 .buffer_snapshot
10730 .summary_for_anchor::<usize>(&range.end);
10731 }
10732
10733 self.background_highlights
10734 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10735 self.scrollbar_marker_state.dirty = true;
10736 cx.notify();
10737 }
10738
10739 pub fn clear_background_highlights<T: 'static>(
10740 &mut self,
10741 cx: &mut ViewContext<Self>,
10742 ) -> Option<BackgroundHighlight> {
10743 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
10744 if !text_highlights.1.is_empty() {
10745 self.scrollbar_marker_state.dirty = true;
10746 cx.notify();
10747 }
10748 Some(text_highlights)
10749 }
10750
10751 pub fn highlight_gutter<T: 'static>(
10752 &mut self,
10753 ranges: &[Range<Anchor>],
10754 color_fetcher: fn(&AppContext) -> Hsla,
10755 cx: &mut ViewContext<Self>,
10756 ) {
10757 self.gutter_highlights
10758 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
10759 cx.notify();
10760 }
10761
10762 pub fn clear_gutter_highlights<T: 'static>(
10763 &mut self,
10764 cx: &mut ViewContext<Self>,
10765 ) -> Option<GutterHighlight> {
10766 cx.notify();
10767 self.gutter_highlights.remove(&TypeId::of::<T>())
10768 }
10769
10770 #[cfg(feature = "test-support")]
10771 pub fn all_text_background_highlights(
10772 &mut self,
10773 cx: &mut ViewContext<Self>,
10774 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10775 let snapshot = self.snapshot(cx);
10776 let buffer = &snapshot.buffer_snapshot;
10777 let start = buffer.anchor_before(0);
10778 let end = buffer.anchor_after(buffer.len());
10779 let theme = cx.theme().colors();
10780 self.background_highlights_in_range(start..end, &snapshot, theme)
10781 }
10782
10783 #[cfg(feature = "test-support")]
10784 pub fn search_background_highlights(
10785 &mut self,
10786 cx: &mut ViewContext<Self>,
10787 ) -> Vec<Range<Point>> {
10788 let snapshot = self.buffer().read(cx).snapshot(cx);
10789
10790 let highlights = self
10791 .background_highlights
10792 .get(&TypeId::of::<items::BufferSearchHighlights>());
10793
10794 if let Some((_color, ranges)) = highlights {
10795 ranges
10796 .iter()
10797 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
10798 .collect_vec()
10799 } else {
10800 vec![]
10801 }
10802 }
10803
10804 fn document_highlights_for_position<'a>(
10805 &'a self,
10806 position: Anchor,
10807 buffer: &'a MultiBufferSnapshot,
10808 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
10809 let read_highlights = self
10810 .background_highlights
10811 .get(&TypeId::of::<DocumentHighlightRead>())
10812 .map(|h| &h.1);
10813 let write_highlights = self
10814 .background_highlights
10815 .get(&TypeId::of::<DocumentHighlightWrite>())
10816 .map(|h| &h.1);
10817 let left_position = position.bias_left(buffer);
10818 let right_position = position.bias_right(buffer);
10819 read_highlights
10820 .into_iter()
10821 .chain(write_highlights)
10822 .flat_map(move |ranges| {
10823 let start_ix = match ranges.binary_search_by(|probe| {
10824 let cmp = probe.end.cmp(&left_position, buffer);
10825 if cmp.is_ge() {
10826 Ordering::Greater
10827 } else {
10828 Ordering::Less
10829 }
10830 }) {
10831 Ok(i) | Err(i) => i,
10832 };
10833
10834 ranges[start_ix..]
10835 .iter()
10836 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
10837 })
10838 }
10839
10840 pub fn has_background_highlights<T: 'static>(&self) -> bool {
10841 self.background_highlights
10842 .get(&TypeId::of::<T>())
10843 .map_or(false, |(_, highlights)| !highlights.is_empty())
10844 }
10845
10846 pub fn background_highlights_in_range(
10847 &self,
10848 search_range: Range<Anchor>,
10849 display_snapshot: &DisplaySnapshot,
10850 theme: &ThemeColors,
10851 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10852 let mut results = Vec::new();
10853 for (color_fetcher, ranges) in self.background_highlights.values() {
10854 let color = color_fetcher(theme);
10855 let start_ix = match ranges.binary_search_by(|probe| {
10856 let cmp = probe
10857 .end
10858 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10859 if cmp.is_gt() {
10860 Ordering::Greater
10861 } else {
10862 Ordering::Less
10863 }
10864 }) {
10865 Ok(i) | Err(i) => i,
10866 };
10867 for range in &ranges[start_ix..] {
10868 if range
10869 .start
10870 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10871 .is_ge()
10872 {
10873 break;
10874 }
10875
10876 let start = range.start.to_display_point(&display_snapshot);
10877 let end = range.end.to_display_point(&display_snapshot);
10878 results.push((start..end, color))
10879 }
10880 }
10881 results
10882 }
10883
10884 pub fn background_highlight_row_ranges<T: 'static>(
10885 &self,
10886 search_range: Range<Anchor>,
10887 display_snapshot: &DisplaySnapshot,
10888 count: usize,
10889 ) -> Vec<RangeInclusive<DisplayPoint>> {
10890 let mut results = Vec::new();
10891 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
10892 return vec![];
10893 };
10894
10895 let start_ix = match ranges.binary_search_by(|probe| {
10896 let cmp = probe
10897 .end
10898 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10899 if cmp.is_gt() {
10900 Ordering::Greater
10901 } else {
10902 Ordering::Less
10903 }
10904 }) {
10905 Ok(i) | Err(i) => i,
10906 };
10907 let mut push_region = |start: Option<Point>, end: Option<Point>| {
10908 if let (Some(start_display), Some(end_display)) = (start, end) {
10909 results.push(
10910 start_display.to_display_point(display_snapshot)
10911 ..=end_display.to_display_point(display_snapshot),
10912 );
10913 }
10914 };
10915 let mut start_row: Option<Point> = None;
10916 let mut end_row: Option<Point> = None;
10917 if ranges.len() > count {
10918 return Vec::new();
10919 }
10920 for range in &ranges[start_ix..] {
10921 if range
10922 .start
10923 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10924 .is_ge()
10925 {
10926 break;
10927 }
10928 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
10929 if let Some(current_row) = &end_row {
10930 if end.row == current_row.row {
10931 continue;
10932 }
10933 }
10934 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
10935 if start_row.is_none() {
10936 assert_eq!(end_row, None);
10937 start_row = Some(start);
10938 end_row = Some(end);
10939 continue;
10940 }
10941 if let Some(current_end) = end_row.as_mut() {
10942 if start.row > current_end.row + 1 {
10943 push_region(start_row, end_row);
10944 start_row = Some(start);
10945 end_row = Some(end);
10946 } else {
10947 // Merge two hunks.
10948 *current_end = end;
10949 }
10950 } else {
10951 unreachable!();
10952 }
10953 }
10954 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
10955 push_region(start_row, end_row);
10956 results
10957 }
10958
10959 pub fn gutter_highlights_in_range(
10960 &self,
10961 search_range: Range<Anchor>,
10962 display_snapshot: &DisplaySnapshot,
10963 cx: &AppContext,
10964 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
10965 let mut results = Vec::new();
10966 for (color_fetcher, ranges) in self.gutter_highlights.values() {
10967 let color = color_fetcher(cx);
10968 let start_ix = match ranges.binary_search_by(|probe| {
10969 let cmp = probe
10970 .end
10971 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
10972 if cmp.is_gt() {
10973 Ordering::Greater
10974 } else {
10975 Ordering::Less
10976 }
10977 }) {
10978 Ok(i) | Err(i) => i,
10979 };
10980 for range in &ranges[start_ix..] {
10981 if range
10982 .start
10983 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
10984 .is_ge()
10985 {
10986 break;
10987 }
10988
10989 let start = range.start.to_display_point(&display_snapshot);
10990 let end = range.end.to_display_point(&display_snapshot);
10991 results.push((start..end, color))
10992 }
10993 }
10994 results
10995 }
10996
10997 /// Get the text ranges corresponding to the redaction query
10998 pub fn redacted_ranges(
10999 &self,
11000 search_range: Range<Anchor>,
11001 display_snapshot: &DisplaySnapshot,
11002 cx: &WindowContext,
11003 ) -> Vec<Range<DisplayPoint>> {
11004 display_snapshot
11005 .buffer_snapshot
11006 .redacted_ranges(search_range, |file| {
11007 if let Some(file) = file {
11008 file.is_private()
11009 && EditorSettings::get(Some(file.as_ref().into()), cx).redact_private_values
11010 } else {
11011 false
11012 }
11013 })
11014 .map(|range| {
11015 range.start.to_display_point(display_snapshot)
11016 ..range.end.to_display_point(display_snapshot)
11017 })
11018 .collect()
11019 }
11020
11021 pub fn highlight_text<T: 'static>(
11022 &mut self,
11023 ranges: Vec<Range<Anchor>>,
11024 style: HighlightStyle,
11025 cx: &mut ViewContext<Self>,
11026 ) {
11027 self.display_map.update(cx, |map, _| {
11028 map.highlight_text(TypeId::of::<T>(), ranges, style)
11029 });
11030 cx.notify();
11031 }
11032
11033 pub(crate) fn highlight_inlays<T: 'static>(
11034 &mut self,
11035 highlights: Vec<InlayHighlight>,
11036 style: HighlightStyle,
11037 cx: &mut ViewContext<Self>,
11038 ) {
11039 self.display_map.update(cx, |map, _| {
11040 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11041 });
11042 cx.notify();
11043 }
11044
11045 pub fn text_highlights<'a, T: 'static>(
11046 &'a self,
11047 cx: &'a AppContext,
11048 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11049 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11050 }
11051
11052 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11053 let cleared = self
11054 .display_map
11055 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11056 if cleared {
11057 cx.notify();
11058 }
11059 }
11060
11061 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11062 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11063 && self.focus_handle.is_focused(cx)
11064 }
11065
11066 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11067 self.show_cursor_when_unfocused = is_enabled;
11068 cx.notify();
11069 }
11070
11071 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11072 cx.notify();
11073 }
11074
11075 fn on_buffer_event(
11076 &mut self,
11077 multibuffer: Model<MultiBuffer>,
11078 event: &multi_buffer::Event,
11079 cx: &mut ViewContext<Self>,
11080 ) {
11081 match event {
11082 multi_buffer::Event::Edited {
11083 singleton_buffer_edited,
11084 } => {
11085 self.scrollbar_marker_state.dirty = true;
11086 self.active_indent_guides_state.dirty = true;
11087 self.refresh_active_diagnostics(cx);
11088 self.refresh_code_actions(cx);
11089 if self.has_active_inline_completion(cx) {
11090 self.update_visible_inline_completion(cx);
11091 }
11092 cx.emit(EditorEvent::BufferEdited);
11093 cx.emit(SearchEvent::MatchesInvalidated);
11094 if *singleton_buffer_edited {
11095 if let Some(project) = &self.project {
11096 let project = project.read(cx);
11097 let languages_affected = multibuffer
11098 .read(cx)
11099 .all_buffers()
11100 .into_iter()
11101 .filter_map(|buffer| {
11102 let buffer = buffer.read(cx);
11103 let language = buffer.language()?;
11104 if project.is_local()
11105 && project.language_servers_for_buffer(buffer, cx).count() == 0
11106 {
11107 None
11108 } else {
11109 Some(language)
11110 }
11111 })
11112 .cloned()
11113 .collect::<HashSet<_>>();
11114 if !languages_affected.is_empty() {
11115 self.refresh_inlay_hints(
11116 InlayHintRefreshReason::BufferEdited(languages_affected),
11117 cx,
11118 );
11119 }
11120 }
11121 }
11122
11123 let Some(project) = &self.project else { return };
11124 let telemetry = project.read(cx).client().telemetry().clone();
11125 refresh_linked_ranges(self, cx);
11126 telemetry.log_edit_event("editor");
11127 }
11128 multi_buffer::Event::ExcerptsAdded {
11129 buffer,
11130 predecessor,
11131 excerpts,
11132 } => {
11133 self.tasks_update_task = Some(self.refresh_runnables(cx));
11134 cx.emit(EditorEvent::ExcerptsAdded {
11135 buffer: buffer.clone(),
11136 predecessor: *predecessor,
11137 excerpts: excerpts.clone(),
11138 });
11139 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11140 }
11141 multi_buffer::Event::ExcerptsRemoved { ids } => {
11142 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11143 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11144 }
11145 multi_buffer::Event::ExcerptsEdited { ids } => {
11146 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11147 }
11148 multi_buffer::Event::ExcerptsExpanded { ids } => {
11149 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11150 }
11151 multi_buffer::Event::Reparsed(buffer_id) => {
11152 self.tasks_update_task = Some(self.refresh_runnables(cx));
11153
11154 cx.emit(EditorEvent::Reparsed(*buffer_id));
11155 }
11156 multi_buffer::Event::LanguageChanged(buffer_id) => {
11157 linked_editing_ranges::refresh_linked_ranges(self, cx);
11158 cx.emit(EditorEvent::Reparsed(*buffer_id));
11159 cx.notify();
11160 }
11161 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11162 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11163 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11164 cx.emit(EditorEvent::TitleChanged)
11165 }
11166 multi_buffer::Event::DiffBaseChanged => {
11167 self.scrollbar_marker_state.dirty = true;
11168 cx.emit(EditorEvent::DiffBaseChanged);
11169 cx.notify();
11170 }
11171 multi_buffer::Event::DiffUpdated { buffer } => {
11172 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11173 cx.notify();
11174 }
11175 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11176 multi_buffer::Event::DiagnosticsUpdated => {
11177 self.refresh_active_diagnostics(cx);
11178 self.scrollbar_marker_state.dirty = true;
11179 cx.notify();
11180 }
11181 _ => {}
11182 };
11183 }
11184
11185 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11186 cx.notify();
11187 }
11188
11189 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11190 self.tasks_update_task = Some(self.refresh_runnables(cx));
11191 self.refresh_inline_completion(true, cx);
11192 self.refresh_inlay_hints(
11193 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11194 self.selections.newest_anchor().head(),
11195 &self.buffer.read(cx).snapshot(cx),
11196 cx,
11197 )),
11198 cx,
11199 );
11200 let editor_settings = EditorSettings::get_global(cx);
11201 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11202 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11203
11204 if self.mode == EditorMode::Full {
11205 let inline_blame_enabled = ProjectSettings::get_global(cx).git.inline_blame_enabled();
11206 if self.git_blame_inline_enabled != inline_blame_enabled {
11207 self.toggle_git_blame_inline_internal(false, cx);
11208 }
11209 }
11210
11211 cx.notify();
11212 }
11213
11214 pub fn set_searchable(&mut self, searchable: bool) {
11215 self.searchable = searchable;
11216 }
11217
11218 pub fn searchable(&self) -> bool {
11219 self.searchable
11220 }
11221
11222 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11223 self.open_excerpts_common(true, cx)
11224 }
11225
11226 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11227 self.open_excerpts_common(false, cx)
11228 }
11229
11230 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11231 let buffer = self.buffer.read(cx);
11232 if buffer.is_singleton() {
11233 cx.propagate();
11234 return;
11235 }
11236
11237 let Some(workspace) = self.workspace() else {
11238 cx.propagate();
11239 return;
11240 };
11241
11242 let mut new_selections_by_buffer = HashMap::default();
11243 for selection in self.selections.all::<usize>(cx) {
11244 for (buffer, mut range, _) in
11245 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11246 {
11247 if selection.reversed {
11248 mem::swap(&mut range.start, &mut range.end);
11249 }
11250 new_selections_by_buffer
11251 .entry(buffer)
11252 .or_insert(Vec::new())
11253 .push(range)
11254 }
11255 }
11256
11257 // We defer the pane interaction because we ourselves are a workspace item
11258 // and activating a new item causes the pane to call a method on us reentrantly,
11259 // which panics if we're on the stack.
11260 cx.window_context().defer(move |cx| {
11261 workspace.update(cx, |workspace, cx| {
11262 let pane = if split {
11263 workspace.adjacent_pane(cx)
11264 } else {
11265 workspace.active_pane().clone()
11266 };
11267
11268 for (buffer, ranges) in new_selections_by_buffer {
11269 let editor = workspace.open_project_item::<Self>(pane.clone(), buffer, cx);
11270 editor.update(cx, |editor, cx| {
11271 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11272 s.select_ranges(ranges);
11273 });
11274 });
11275 }
11276 })
11277 });
11278 }
11279
11280 fn jump(
11281 &mut self,
11282 path: ProjectPath,
11283 position: Point,
11284 anchor: language::Anchor,
11285 offset_from_top: u32,
11286 cx: &mut ViewContext<Self>,
11287 ) {
11288 let workspace = self.workspace();
11289 cx.spawn(|_, mut cx| async move {
11290 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11291 let editor = workspace.update(&mut cx, |workspace, cx| {
11292 // Reset the preview item id before opening the new item
11293 workspace.active_pane().update(cx, |pane, cx| {
11294 pane.set_preview_item_id(None, cx);
11295 });
11296 workspace.open_path_preview(path, None, true, true, cx)
11297 })?;
11298 let editor = editor
11299 .await?
11300 .downcast::<Editor>()
11301 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11302 .downgrade();
11303 editor.update(&mut cx, |editor, cx| {
11304 let buffer = editor
11305 .buffer()
11306 .read(cx)
11307 .as_singleton()
11308 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11309 let buffer = buffer.read(cx);
11310 let cursor = if buffer.can_resolve(&anchor) {
11311 language::ToPoint::to_point(&anchor, buffer)
11312 } else {
11313 buffer.clip_point(position, Bias::Left)
11314 };
11315
11316 let nav_history = editor.nav_history.take();
11317 editor.change_selections(
11318 Some(Autoscroll::top_relative(offset_from_top as usize)),
11319 cx,
11320 |s| {
11321 s.select_ranges([cursor..cursor]);
11322 },
11323 );
11324 editor.nav_history = nav_history;
11325
11326 anyhow::Ok(())
11327 })??;
11328
11329 anyhow::Ok(())
11330 })
11331 .detach_and_log_err(cx);
11332 }
11333
11334 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11335 let snapshot = self.buffer.read(cx).read(cx);
11336 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11337 Some(
11338 ranges
11339 .iter()
11340 .map(move |range| {
11341 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11342 })
11343 .collect(),
11344 )
11345 }
11346
11347 fn selection_replacement_ranges(
11348 &self,
11349 range: Range<OffsetUtf16>,
11350 cx: &AppContext,
11351 ) -> Vec<Range<OffsetUtf16>> {
11352 let selections = self.selections.all::<OffsetUtf16>(cx);
11353 let newest_selection = selections
11354 .iter()
11355 .max_by_key(|selection| selection.id)
11356 .unwrap();
11357 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11358 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11359 let snapshot = self.buffer.read(cx).read(cx);
11360 selections
11361 .into_iter()
11362 .map(|mut selection| {
11363 selection.start.0 =
11364 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11365 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11366 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11367 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11368 })
11369 .collect()
11370 }
11371
11372 fn report_editor_event(
11373 &self,
11374 operation: &'static str,
11375 file_extension: Option<String>,
11376 cx: &AppContext,
11377 ) {
11378 if cfg!(any(test, feature = "test-support")) {
11379 return;
11380 }
11381
11382 let Some(project) = &self.project else { return };
11383
11384 // If None, we are in a file without an extension
11385 let file = self
11386 .buffer
11387 .read(cx)
11388 .as_singleton()
11389 .and_then(|b| b.read(cx).file());
11390 let file_extension = file_extension.or(file
11391 .as_ref()
11392 .and_then(|file| Path::new(file.file_name(cx)).extension())
11393 .and_then(|e| e.to_str())
11394 .map(|a| a.to_string()));
11395
11396 let vim_mode = cx
11397 .global::<SettingsStore>()
11398 .raw_user_settings()
11399 .get("vim_mode")
11400 == Some(&serde_json::Value::Bool(true));
11401
11402 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11403 == language::language_settings::InlineCompletionProvider::Copilot;
11404 let copilot_enabled_for_language = self
11405 .buffer
11406 .read(cx)
11407 .settings_at(0, cx)
11408 .show_inline_completions;
11409
11410 let telemetry = project.read(cx).client().telemetry().clone();
11411 telemetry.report_editor_event(
11412 file_extension,
11413 vim_mode,
11414 operation,
11415 copilot_enabled,
11416 copilot_enabled_for_language,
11417 )
11418 }
11419
11420 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
11421 /// with each line being an array of {text, highlight} objects.
11422 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
11423 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
11424 return;
11425 };
11426
11427 #[derive(Serialize)]
11428 struct Chunk<'a> {
11429 text: String,
11430 highlight: Option<&'a str>,
11431 }
11432
11433 let snapshot = buffer.read(cx).snapshot();
11434 let range = self
11435 .selected_text_range(cx)
11436 .and_then(|selected_range| {
11437 if selected_range.is_empty() {
11438 None
11439 } else {
11440 Some(selected_range)
11441 }
11442 })
11443 .unwrap_or_else(|| 0..snapshot.len());
11444
11445 let chunks = snapshot.chunks(range, true);
11446 let mut lines = Vec::new();
11447 let mut line: VecDeque<Chunk> = VecDeque::new();
11448
11449 let Some(style) = self.style.as_ref() else {
11450 return;
11451 };
11452
11453 for chunk in chunks {
11454 let highlight = chunk
11455 .syntax_highlight_id
11456 .and_then(|id| id.name(&style.syntax));
11457 let mut chunk_lines = chunk.text.split('\n').peekable();
11458 while let Some(text) = chunk_lines.next() {
11459 let mut merged_with_last_token = false;
11460 if let Some(last_token) = line.back_mut() {
11461 if last_token.highlight == highlight {
11462 last_token.text.push_str(text);
11463 merged_with_last_token = true;
11464 }
11465 }
11466
11467 if !merged_with_last_token {
11468 line.push_back(Chunk {
11469 text: text.into(),
11470 highlight,
11471 });
11472 }
11473
11474 if chunk_lines.peek().is_some() {
11475 if line.len() > 1 && line.front().unwrap().text.is_empty() {
11476 line.pop_front();
11477 }
11478 if line.len() > 1 && line.back().unwrap().text.is_empty() {
11479 line.pop_back();
11480 }
11481
11482 lines.push(mem::take(&mut line));
11483 }
11484 }
11485 }
11486
11487 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
11488 return;
11489 };
11490 cx.write_to_clipboard(ClipboardItem::new(lines));
11491 }
11492
11493 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
11494 &self.inlay_hint_cache
11495 }
11496
11497 pub fn replay_insert_event(
11498 &mut self,
11499 text: &str,
11500 relative_utf16_range: Option<Range<isize>>,
11501 cx: &mut ViewContext<Self>,
11502 ) {
11503 if !self.input_enabled {
11504 cx.emit(EditorEvent::InputIgnored { text: text.into() });
11505 return;
11506 }
11507 if let Some(relative_utf16_range) = relative_utf16_range {
11508 let selections = self.selections.all::<OffsetUtf16>(cx);
11509 self.change_selections(None, cx, |s| {
11510 let new_ranges = selections.into_iter().map(|range| {
11511 let start = OffsetUtf16(
11512 range
11513 .head()
11514 .0
11515 .saturating_add_signed(relative_utf16_range.start),
11516 );
11517 let end = OffsetUtf16(
11518 range
11519 .head()
11520 .0
11521 .saturating_add_signed(relative_utf16_range.end),
11522 );
11523 start..end
11524 });
11525 s.select_ranges(new_ranges);
11526 });
11527 }
11528
11529 self.handle_input(text, cx);
11530 }
11531
11532 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
11533 let Some(project) = self.project.as_ref() else {
11534 return false;
11535 };
11536 let project = project.read(cx);
11537
11538 let mut supports = false;
11539 self.buffer().read(cx).for_each_buffer(|buffer| {
11540 if !supports {
11541 supports = project
11542 .language_servers_for_buffer(buffer.read(cx), cx)
11543 .any(
11544 |(_, server)| match server.capabilities().inlay_hint_provider {
11545 Some(lsp::OneOf::Left(enabled)) => enabled,
11546 Some(lsp::OneOf::Right(_)) => true,
11547 None => false,
11548 },
11549 )
11550 }
11551 });
11552 supports
11553 }
11554
11555 pub fn focus(&self, cx: &mut WindowContext) {
11556 cx.focus(&self.focus_handle)
11557 }
11558
11559 pub fn is_focused(&self, cx: &WindowContext) -> bool {
11560 self.focus_handle.is_focused(cx)
11561 }
11562
11563 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
11564 cx.emit(EditorEvent::Focused);
11565
11566 if let Some(descendant) = self
11567 .last_focused_descendant
11568 .take()
11569 .and_then(|descendant| descendant.upgrade())
11570 {
11571 cx.focus(&descendant);
11572 } else {
11573 if let Some(blame) = self.blame.as_ref() {
11574 blame.update(cx, GitBlame::focus)
11575 }
11576
11577 self.blink_manager.update(cx, BlinkManager::enable);
11578 self.show_cursor_names(cx);
11579 self.buffer.update(cx, |buffer, cx| {
11580 buffer.finalize_last_transaction(cx);
11581 if self.leader_peer_id.is_none() {
11582 buffer.set_active_selections(
11583 &self.selections.disjoint_anchors(),
11584 self.selections.line_mode,
11585 self.cursor_shape,
11586 cx,
11587 );
11588 }
11589 });
11590 }
11591 }
11592
11593 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
11594 if event.blurred != self.focus_handle {
11595 self.last_focused_descendant = Some(event.blurred);
11596 }
11597 }
11598
11599 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
11600 self.blink_manager.update(cx, BlinkManager::disable);
11601 self.buffer
11602 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
11603
11604 if let Some(blame) = self.blame.as_ref() {
11605 blame.update(cx, GitBlame::blur)
11606 }
11607 self.hide_context_menu(cx);
11608 hide_hover(self, cx);
11609 cx.emit(EditorEvent::Blurred);
11610 cx.notify();
11611 }
11612
11613 pub fn register_action<A: Action>(
11614 &mut self,
11615 listener: impl Fn(&A, &mut WindowContext) + 'static,
11616 ) -> Subscription {
11617 let id = self.next_editor_action_id.post_inc();
11618 let listener = Arc::new(listener);
11619 self.editor_actions.borrow_mut().insert(
11620 id,
11621 Box::new(move |cx| {
11622 let _view = cx.view().clone();
11623 let cx = cx.window_context();
11624 let listener = listener.clone();
11625 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
11626 let action = action.downcast_ref().unwrap();
11627 if phase == DispatchPhase::Bubble {
11628 listener(action, cx)
11629 }
11630 })
11631 }),
11632 );
11633
11634 let editor_actions = self.editor_actions.clone();
11635 Subscription::new(move || {
11636 editor_actions.borrow_mut().remove(&id);
11637 })
11638 }
11639
11640 pub fn file_header_size(&self) -> u8 {
11641 self.file_header_size
11642 }
11643}
11644
11645fn hunks_for_selections(
11646 multi_buffer_snapshot: &MultiBufferSnapshot,
11647 selections: &[Selection<Anchor>],
11648) -> Vec<DiffHunk<MultiBufferRow>> {
11649 let mut hunks = Vec::with_capacity(selections.len());
11650 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
11651 HashMap::default();
11652 let buffer_rows_for_selections = selections.iter().map(|selection| {
11653 let head = selection.head();
11654 let tail = selection.tail();
11655 let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
11656 let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
11657 if start > end {
11658 end..start
11659 } else {
11660 start..end
11661 }
11662 });
11663
11664 for selected_multi_buffer_rows in buffer_rows_for_selections {
11665 let query_rows =
11666 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
11667 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
11668 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
11669 // when the caret is just above or just below the deleted hunk.
11670 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
11671 let related_to_selection = if allow_adjacent {
11672 hunk.associated_range.overlaps(&query_rows)
11673 || hunk.associated_range.start == query_rows.end
11674 || hunk.associated_range.end == query_rows.start
11675 } else {
11676 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
11677 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
11678 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
11679 || selected_multi_buffer_rows.end == hunk.associated_range.start
11680 };
11681 if related_to_selection {
11682 if !processed_buffer_rows
11683 .entry(hunk.buffer_id)
11684 .or_default()
11685 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
11686 {
11687 continue;
11688 }
11689 hunks.push(hunk);
11690 }
11691 }
11692 }
11693
11694 hunks
11695}
11696
11697pub trait CollaborationHub {
11698 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
11699 fn user_participant_indices<'a>(
11700 &self,
11701 cx: &'a AppContext,
11702 ) -> &'a HashMap<u64, ParticipantIndex>;
11703 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
11704}
11705
11706impl CollaborationHub for Model<Project> {
11707 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
11708 self.read(cx).collaborators()
11709 }
11710
11711 fn user_participant_indices<'a>(
11712 &self,
11713 cx: &'a AppContext,
11714 ) -> &'a HashMap<u64, ParticipantIndex> {
11715 self.read(cx).user_store().read(cx).participant_indices()
11716 }
11717
11718 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
11719 let this = self.read(cx);
11720 let user_ids = this.collaborators().values().map(|c| c.user_id);
11721 this.user_store().read_with(cx, |user_store, cx| {
11722 user_store.participant_names(user_ids, cx)
11723 })
11724 }
11725}
11726
11727pub trait CompletionProvider {
11728 fn completions(
11729 &self,
11730 buffer: &Model<Buffer>,
11731 buffer_position: text::Anchor,
11732 trigger: CompletionContext,
11733 cx: &mut ViewContext<Editor>,
11734 ) -> Task<Result<Vec<Completion>>>;
11735
11736 fn resolve_completions(
11737 &self,
11738 buffer: Model<Buffer>,
11739 completion_indices: Vec<usize>,
11740 completions: Arc<RwLock<Box<[Completion]>>>,
11741 cx: &mut ViewContext<Editor>,
11742 ) -> Task<Result<bool>>;
11743
11744 fn apply_additional_edits_for_completion(
11745 &self,
11746 buffer: Model<Buffer>,
11747 completion: Completion,
11748 push_to_history: bool,
11749 cx: &mut ViewContext<Editor>,
11750 ) -> Task<Result<Option<language::Transaction>>>;
11751
11752 fn is_completion_trigger(
11753 &self,
11754 buffer: &Model<Buffer>,
11755 position: language::Anchor,
11756 text: &str,
11757 trigger_in_words: bool,
11758 cx: &mut ViewContext<Editor>,
11759 ) -> bool;
11760}
11761
11762fn snippet_completions(
11763 project: &Project,
11764 buffer: &Model<Buffer>,
11765 buffer_position: text::Anchor,
11766 cx: &mut AppContext,
11767) -> Vec<Completion> {
11768 let language = buffer.read(cx).language_at(buffer_position);
11769 let language_name = language.as_ref().map(|language| language.lsp_id());
11770 let snippet_store = project.snippets().read(cx);
11771 let snippets = snippet_store.snippets_for(language_name);
11772
11773 if snippets.is_empty() {
11774 return vec![];
11775 }
11776 let snapshot = buffer.read(cx).text_snapshot();
11777 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
11778
11779 let mut lines = chunks.lines();
11780 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
11781 return vec![];
11782 };
11783
11784 let scope = language.map(|language| language.default_scope());
11785 let mut last_word = line_at
11786 .chars()
11787 .rev()
11788 .take_while(|c| char_kind(&scope, *c) == CharKind::Word)
11789 .collect::<String>();
11790 last_word = last_word.chars().rev().collect();
11791 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
11792 let to_lsp = |point: &text::Anchor| {
11793 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
11794 point_to_lsp(end)
11795 };
11796 let lsp_end = to_lsp(&buffer_position);
11797 snippets
11798 .into_iter()
11799 .filter_map(|snippet| {
11800 let matching_prefix = snippet
11801 .prefix
11802 .iter()
11803 .find(|prefix| prefix.starts_with(&last_word))?;
11804 let start = as_offset - last_word.len();
11805 let start = snapshot.anchor_before(start);
11806 let range = start..buffer_position;
11807 let lsp_start = to_lsp(&start);
11808 let lsp_range = lsp::Range {
11809 start: lsp_start,
11810 end: lsp_end,
11811 };
11812 Some(Completion {
11813 old_range: range,
11814 new_text: snippet.body.clone(),
11815 label: CodeLabel {
11816 text: matching_prefix.clone(),
11817 runs: vec![],
11818 filter_range: 0..matching_prefix.len(),
11819 },
11820 server_id: LanguageServerId(usize::MAX),
11821 documentation: snippet
11822 .description
11823 .clone()
11824 .map(|description| Documentation::SingleLine(description)),
11825 lsp_completion: lsp::CompletionItem {
11826 label: snippet.prefix.first().unwrap().clone(),
11827 kind: Some(CompletionItemKind::SNIPPET),
11828 label_details: snippet.description.as_ref().map(|description| {
11829 lsp::CompletionItemLabelDetails {
11830 detail: Some(description.clone()),
11831 description: None,
11832 }
11833 }),
11834 insert_text_format: Some(InsertTextFormat::SNIPPET),
11835 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
11836 lsp::InsertReplaceEdit {
11837 new_text: snippet.body.clone(),
11838 insert: lsp_range,
11839 replace: lsp_range,
11840 },
11841 )),
11842 filter_text: Some(snippet.body.clone()),
11843 sort_text: Some(char::MAX.to_string()),
11844 ..Default::default()
11845 },
11846 confirm: None,
11847 show_new_completions_on_confirm: false,
11848 })
11849 })
11850 .collect()
11851}
11852
11853impl CompletionProvider for Model<Project> {
11854 fn completions(
11855 &self,
11856 buffer: &Model<Buffer>,
11857 buffer_position: text::Anchor,
11858 options: CompletionContext,
11859 cx: &mut ViewContext<Editor>,
11860 ) -> Task<Result<Vec<Completion>>> {
11861 self.update(cx, |project, cx| {
11862 let snippets = snippet_completions(project, buffer, buffer_position, cx);
11863 let project_completions = project.completions(&buffer, buffer_position, options, cx);
11864 cx.background_executor().spawn(async move {
11865 let mut completions = project_completions.await?;
11866 //let snippets = snippets.into_iter().;
11867 completions.extend(snippets);
11868 Ok(completions)
11869 })
11870 })
11871 }
11872
11873 fn resolve_completions(
11874 &self,
11875 buffer: Model<Buffer>,
11876 completion_indices: Vec<usize>,
11877 completions: Arc<RwLock<Box<[Completion]>>>,
11878 cx: &mut ViewContext<Editor>,
11879 ) -> Task<Result<bool>> {
11880 self.update(cx, |project, cx| {
11881 project.resolve_completions(buffer, completion_indices, completions, cx)
11882 })
11883 }
11884
11885 fn apply_additional_edits_for_completion(
11886 &self,
11887 buffer: Model<Buffer>,
11888 completion: Completion,
11889 push_to_history: bool,
11890 cx: &mut ViewContext<Editor>,
11891 ) -> Task<Result<Option<language::Transaction>>> {
11892 self.update(cx, |project, cx| {
11893 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
11894 })
11895 }
11896
11897 fn is_completion_trigger(
11898 &self,
11899 buffer: &Model<Buffer>,
11900 position: language::Anchor,
11901 text: &str,
11902 trigger_in_words: bool,
11903 cx: &mut ViewContext<Editor>,
11904 ) -> bool {
11905 if !EditorSettings::get_global(cx).show_completions_on_input {
11906 return false;
11907 }
11908
11909 let mut chars = text.chars();
11910 let char = if let Some(char) = chars.next() {
11911 char
11912 } else {
11913 return false;
11914 };
11915 if chars.next().is_some() {
11916 return false;
11917 }
11918
11919 let buffer = buffer.read(cx);
11920 let scope = buffer.snapshot().language_scope_at(position);
11921 if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
11922 return true;
11923 }
11924
11925 buffer
11926 .completion_triggers()
11927 .iter()
11928 .any(|string| string == text)
11929 }
11930}
11931
11932fn inlay_hint_settings(
11933 location: Anchor,
11934 snapshot: &MultiBufferSnapshot,
11935 cx: &mut ViewContext<'_, Editor>,
11936) -> InlayHintSettings {
11937 let file = snapshot.file_at(location);
11938 let language = snapshot.language_at(location);
11939 let settings = all_language_settings(file, cx);
11940 settings
11941 .language(language.map(|l| l.name()).as_deref())
11942 .inlay_hints
11943}
11944
11945fn consume_contiguous_rows(
11946 contiguous_row_selections: &mut Vec<Selection<Point>>,
11947 selection: &Selection<Point>,
11948 display_map: &DisplaySnapshot,
11949 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
11950) -> (MultiBufferRow, MultiBufferRow) {
11951 contiguous_row_selections.push(selection.clone());
11952 let start_row = MultiBufferRow(selection.start.row);
11953 let mut end_row = ending_row(selection, display_map);
11954
11955 while let Some(next_selection) = selections.peek() {
11956 if next_selection.start.row <= end_row.0 {
11957 end_row = ending_row(next_selection, display_map);
11958 contiguous_row_selections.push(selections.next().unwrap().clone());
11959 } else {
11960 break;
11961 }
11962 }
11963 (start_row, end_row)
11964}
11965
11966fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
11967 if next_selection.end.column > 0 || next_selection.is_empty() {
11968 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
11969 } else {
11970 MultiBufferRow(next_selection.end.row)
11971 }
11972}
11973
11974impl EditorSnapshot {
11975 pub fn remote_selections_in_range<'a>(
11976 &'a self,
11977 range: &'a Range<Anchor>,
11978 collaboration_hub: &dyn CollaborationHub,
11979 cx: &'a AppContext,
11980 ) -> impl 'a + Iterator<Item = RemoteSelection> {
11981 let participant_names = collaboration_hub.user_names(cx);
11982 let participant_indices = collaboration_hub.user_participant_indices(cx);
11983 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
11984 let collaborators_by_replica_id = collaborators_by_peer_id
11985 .iter()
11986 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
11987 .collect::<HashMap<_, _>>();
11988 self.buffer_snapshot
11989 .selections_in_range(range, false)
11990 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
11991 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
11992 let participant_index = participant_indices.get(&collaborator.user_id).copied();
11993 let user_name = participant_names.get(&collaborator.user_id).cloned();
11994 Some(RemoteSelection {
11995 replica_id,
11996 selection,
11997 cursor_shape,
11998 line_mode,
11999 participant_index,
12000 peer_id: collaborator.peer_id,
12001 user_name,
12002 })
12003 })
12004 }
12005
12006 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12007 self.display_snapshot.buffer_snapshot.language_at(position)
12008 }
12009
12010 pub fn is_focused(&self) -> bool {
12011 self.is_focused
12012 }
12013
12014 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12015 self.placeholder_text.as_ref()
12016 }
12017
12018 pub fn scroll_position(&self) -> gpui::Point<f32> {
12019 self.scroll_anchor.scroll_position(&self.display_snapshot)
12020 }
12021
12022 pub fn gutter_dimensions(
12023 &self,
12024 font_id: FontId,
12025 font_size: Pixels,
12026 em_width: Pixels,
12027 max_line_number_width: Pixels,
12028 cx: &AppContext,
12029 ) -> GutterDimensions {
12030 if !self.show_gutter {
12031 return GutterDimensions::default();
12032 }
12033 let descent = cx.text_system().descent(font_id, font_size);
12034
12035 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12036 matches!(
12037 ProjectSettings::get_global(cx).git.git_gutter,
12038 Some(GitGutterSetting::TrackedFiles)
12039 )
12040 });
12041 let gutter_settings = EditorSettings::get_global(cx).gutter;
12042 let show_line_numbers = self
12043 .show_line_numbers
12044 .unwrap_or(gutter_settings.line_numbers);
12045 let line_gutter_width = if show_line_numbers {
12046 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12047 let min_width_for_number_on_gutter = em_width * 4.0;
12048 max_line_number_width.max(min_width_for_number_on_gutter)
12049 } else {
12050 0.0.into()
12051 };
12052
12053 let show_code_actions = self
12054 .show_code_actions
12055 .unwrap_or(gutter_settings.code_actions);
12056
12057 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12058
12059 let git_blame_entries_width = self
12060 .render_git_blame_gutter
12061 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12062
12063 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12064 left_padding += if show_code_actions || show_runnables {
12065 em_width * 3.0
12066 } else if show_git_gutter && show_line_numbers {
12067 em_width * 2.0
12068 } else if show_git_gutter || show_line_numbers {
12069 em_width
12070 } else {
12071 px(0.)
12072 };
12073
12074 let right_padding = if gutter_settings.folds && show_line_numbers {
12075 em_width * 4.0
12076 } else if gutter_settings.folds {
12077 em_width * 3.0
12078 } else if show_line_numbers {
12079 em_width
12080 } else {
12081 px(0.)
12082 };
12083
12084 GutterDimensions {
12085 left_padding,
12086 right_padding,
12087 width: line_gutter_width + left_padding + right_padding,
12088 margin: -descent,
12089 git_blame_entries_width,
12090 }
12091 }
12092
12093 pub fn render_fold_toggle(
12094 &self,
12095 buffer_row: MultiBufferRow,
12096 row_contains_cursor: bool,
12097 editor: View<Editor>,
12098 cx: &mut WindowContext,
12099 ) -> Option<AnyElement> {
12100 let folded = self.is_line_folded(buffer_row);
12101
12102 if let Some(crease) = self
12103 .crease_snapshot
12104 .query_row(buffer_row, &self.buffer_snapshot)
12105 {
12106 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12107 if folded {
12108 editor.update(cx, |editor, cx| {
12109 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12110 });
12111 } else {
12112 editor.update(cx, |editor, cx| {
12113 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12114 });
12115 }
12116 });
12117
12118 Some((crease.render_toggle)(
12119 buffer_row,
12120 folded,
12121 toggle_callback,
12122 cx,
12123 ))
12124 } else if folded
12125 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12126 {
12127 Some(
12128 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12129 .selected(folded)
12130 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12131 if folded {
12132 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12133 } else {
12134 this.fold_at(&FoldAt { buffer_row }, cx);
12135 }
12136 }))
12137 .into_any_element(),
12138 )
12139 } else {
12140 None
12141 }
12142 }
12143
12144 pub fn render_crease_trailer(
12145 &self,
12146 buffer_row: MultiBufferRow,
12147 cx: &mut WindowContext,
12148 ) -> Option<AnyElement> {
12149 let folded = self.is_line_folded(buffer_row);
12150 let crease = self
12151 .crease_snapshot
12152 .query_row(buffer_row, &self.buffer_snapshot)?;
12153 Some((crease.render_trailer)(buffer_row, folded, cx))
12154 }
12155}
12156
12157impl Deref for EditorSnapshot {
12158 type Target = DisplaySnapshot;
12159
12160 fn deref(&self) -> &Self::Target {
12161 &self.display_snapshot
12162 }
12163}
12164
12165#[derive(Clone, Debug, PartialEq, Eq)]
12166pub enum EditorEvent {
12167 InputIgnored {
12168 text: Arc<str>,
12169 },
12170 InputHandled {
12171 utf16_range_to_replace: Option<Range<isize>>,
12172 text: Arc<str>,
12173 },
12174 ExcerptsAdded {
12175 buffer: Model<Buffer>,
12176 predecessor: ExcerptId,
12177 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12178 },
12179 ExcerptsRemoved {
12180 ids: Vec<ExcerptId>,
12181 },
12182 ExcerptsEdited {
12183 ids: Vec<ExcerptId>,
12184 },
12185 ExcerptsExpanded {
12186 ids: Vec<ExcerptId>,
12187 },
12188 BufferEdited,
12189 Edited {
12190 transaction_id: clock::Lamport,
12191 },
12192 Reparsed(BufferId),
12193 Focused,
12194 Blurred,
12195 DirtyChanged,
12196 Saved,
12197 TitleChanged,
12198 DiffBaseChanged,
12199 SelectionsChanged {
12200 local: bool,
12201 },
12202 ScrollPositionChanged {
12203 local: bool,
12204 autoscroll: bool,
12205 },
12206 Closed,
12207 TransactionUndone {
12208 transaction_id: clock::Lamport,
12209 },
12210 TransactionBegun {
12211 transaction_id: clock::Lamport,
12212 },
12213}
12214
12215impl EventEmitter<EditorEvent> for Editor {}
12216
12217impl FocusableView for Editor {
12218 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12219 self.focus_handle.clone()
12220 }
12221}
12222
12223impl Render for Editor {
12224 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12225 let settings = ThemeSettings::get_global(cx);
12226
12227 let text_style = match self.mode {
12228 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12229 color: cx.theme().colors().editor_foreground,
12230 font_family: settings.ui_font.family.clone(),
12231 font_features: settings.ui_font.features.clone(),
12232 font_size: rems(0.875).into(),
12233 font_weight: settings.ui_font.weight,
12234 font_style: FontStyle::Normal,
12235 line_height: relative(settings.buffer_line_height.value()),
12236 background_color: None,
12237 underline: None,
12238 strikethrough: None,
12239 white_space: WhiteSpace::Normal,
12240 },
12241 EditorMode::Full => TextStyle {
12242 color: cx.theme().colors().editor_foreground,
12243 font_family: settings.buffer_font.family.clone(),
12244 font_features: settings.buffer_font.features.clone(),
12245 font_size: settings.buffer_font_size(cx).into(),
12246 font_weight: settings.buffer_font.weight,
12247 font_style: FontStyle::Normal,
12248 line_height: relative(settings.buffer_line_height.value()),
12249 background_color: None,
12250 underline: None,
12251 strikethrough: None,
12252 white_space: WhiteSpace::Normal,
12253 },
12254 };
12255
12256 let background = match self.mode {
12257 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12258 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12259 EditorMode::Full => cx.theme().colors().editor_background,
12260 };
12261
12262 EditorElement::new(
12263 cx.view(),
12264 EditorStyle {
12265 background,
12266 local_player: cx.theme().players().local(),
12267 text: text_style,
12268 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12269 syntax: cx.theme().syntax().clone(),
12270 status: cx.theme().status().clone(),
12271 inlay_hints_style: HighlightStyle {
12272 color: Some(cx.theme().status().hint),
12273 ..HighlightStyle::default()
12274 },
12275 suggestions_style: HighlightStyle {
12276 color: Some(cx.theme().status().predictive),
12277 ..HighlightStyle::default()
12278 },
12279 },
12280 )
12281 }
12282}
12283
12284impl ViewInputHandler for Editor {
12285 fn text_for_range(
12286 &mut self,
12287 range_utf16: Range<usize>,
12288 cx: &mut ViewContext<Self>,
12289 ) -> Option<String> {
12290 Some(
12291 self.buffer
12292 .read(cx)
12293 .read(cx)
12294 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12295 .collect(),
12296 )
12297 }
12298
12299 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12300 // Prevent the IME menu from appearing when holding down an alphabetic key
12301 // while input is disabled.
12302 if !self.input_enabled {
12303 return None;
12304 }
12305
12306 let range = self.selections.newest::<OffsetUtf16>(cx).range();
12307 Some(range.start.0..range.end.0)
12308 }
12309
12310 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
12311 let snapshot = self.buffer.read(cx).read(cx);
12312 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
12313 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
12314 }
12315
12316 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
12317 self.clear_highlights::<InputComposition>(cx);
12318 self.ime_transaction.take();
12319 }
12320
12321 fn replace_text_in_range(
12322 &mut self,
12323 range_utf16: Option<Range<usize>>,
12324 text: &str,
12325 cx: &mut ViewContext<Self>,
12326 ) {
12327 if !self.input_enabled {
12328 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12329 return;
12330 }
12331
12332 self.transact(cx, |this, cx| {
12333 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
12334 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12335 Some(this.selection_replacement_ranges(range_utf16, cx))
12336 } else {
12337 this.marked_text_ranges(cx)
12338 };
12339
12340 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
12341 let newest_selection_id = this.selections.newest_anchor().id;
12342 this.selections
12343 .all::<OffsetUtf16>(cx)
12344 .iter()
12345 .zip(ranges_to_replace.iter())
12346 .find_map(|(selection, range)| {
12347 if selection.id == newest_selection_id {
12348 Some(
12349 (range.start.0 as isize - selection.head().0 as isize)
12350 ..(range.end.0 as isize - selection.head().0 as isize),
12351 )
12352 } else {
12353 None
12354 }
12355 })
12356 });
12357
12358 cx.emit(EditorEvent::InputHandled {
12359 utf16_range_to_replace: range_to_replace,
12360 text: text.into(),
12361 });
12362
12363 if let Some(new_selected_ranges) = new_selected_ranges {
12364 this.change_selections(None, cx, |selections| {
12365 selections.select_ranges(new_selected_ranges)
12366 });
12367 this.backspace(&Default::default(), cx);
12368 }
12369
12370 this.handle_input(text, cx);
12371 });
12372
12373 if let Some(transaction) = self.ime_transaction {
12374 self.buffer.update(cx, |buffer, cx| {
12375 buffer.group_until_transaction(transaction, cx);
12376 });
12377 }
12378
12379 self.unmark_text(cx);
12380 }
12381
12382 fn replace_and_mark_text_in_range(
12383 &mut self,
12384 range_utf16: Option<Range<usize>>,
12385 text: &str,
12386 new_selected_range_utf16: Option<Range<usize>>,
12387 cx: &mut ViewContext<Self>,
12388 ) {
12389 if !self.input_enabled {
12390 return;
12391 }
12392
12393 let transaction = self.transact(cx, |this, cx| {
12394 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
12395 let snapshot = this.buffer.read(cx).read(cx);
12396 if let Some(relative_range_utf16) = range_utf16.as_ref() {
12397 for marked_range in &mut marked_ranges {
12398 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
12399 marked_range.start.0 += relative_range_utf16.start;
12400 marked_range.start =
12401 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
12402 marked_range.end =
12403 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
12404 }
12405 }
12406 Some(marked_ranges)
12407 } else if let Some(range_utf16) = range_utf16 {
12408 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
12409 Some(this.selection_replacement_ranges(range_utf16, cx))
12410 } else {
12411 None
12412 };
12413
12414 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
12415 let newest_selection_id = this.selections.newest_anchor().id;
12416 this.selections
12417 .all::<OffsetUtf16>(cx)
12418 .iter()
12419 .zip(ranges_to_replace.iter())
12420 .find_map(|(selection, range)| {
12421 if selection.id == newest_selection_id {
12422 Some(
12423 (range.start.0 as isize - selection.head().0 as isize)
12424 ..(range.end.0 as isize - selection.head().0 as isize),
12425 )
12426 } else {
12427 None
12428 }
12429 })
12430 });
12431
12432 cx.emit(EditorEvent::InputHandled {
12433 utf16_range_to_replace: range_to_replace,
12434 text: text.into(),
12435 });
12436
12437 if let Some(ranges) = ranges_to_replace {
12438 this.change_selections(None, cx, |s| s.select_ranges(ranges));
12439 }
12440
12441 let marked_ranges = {
12442 let snapshot = this.buffer.read(cx).read(cx);
12443 this.selections
12444 .disjoint_anchors()
12445 .iter()
12446 .map(|selection| {
12447 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
12448 })
12449 .collect::<Vec<_>>()
12450 };
12451
12452 if text.is_empty() {
12453 this.unmark_text(cx);
12454 } else {
12455 this.highlight_text::<InputComposition>(
12456 marked_ranges.clone(),
12457 HighlightStyle {
12458 underline: Some(UnderlineStyle {
12459 thickness: px(1.),
12460 color: None,
12461 wavy: false,
12462 }),
12463 ..Default::default()
12464 },
12465 cx,
12466 );
12467 }
12468
12469 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
12470 let use_autoclose = this.use_autoclose;
12471 let use_auto_surround = this.use_auto_surround;
12472 this.set_use_autoclose(false);
12473 this.set_use_auto_surround(false);
12474 this.handle_input(text, cx);
12475 this.set_use_autoclose(use_autoclose);
12476 this.set_use_auto_surround(use_auto_surround);
12477
12478 if let Some(new_selected_range) = new_selected_range_utf16 {
12479 let snapshot = this.buffer.read(cx).read(cx);
12480 let new_selected_ranges = marked_ranges
12481 .into_iter()
12482 .map(|marked_range| {
12483 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
12484 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
12485 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
12486 snapshot.clip_offset_utf16(new_start, Bias::Left)
12487 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
12488 })
12489 .collect::<Vec<_>>();
12490
12491 drop(snapshot);
12492 this.change_selections(None, cx, |selections| {
12493 selections.select_ranges(new_selected_ranges)
12494 });
12495 }
12496 });
12497
12498 self.ime_transaction = self.ime_transaction.or(transaction);
12499 if let Some(transaction) = self.ime_transaction {
12500 self.buffer.update(cx, |buffer, cx| {
12501 buffer.group_until_transaction(transaction, cx);
12502 });
12503 }
12504
12505 if self.text_highlights::<InputComposition>(cx).is_none() {
12506 self.ime_transaction.take();
12507 }
12508 }
12509
12510 fn bounds_for_range(
12511 &mut self,
12512 range_utf16: Range<usize>,
12513 element_bounds: gpui::Bounds<Pixels>,
12514 cx: &mut ViewContext<Self>,
12515 ) -> Option<gpui::Bounds<Pixels>> {
12516 let text_layout_details = self.text_layout_details(cx);
12517 let style = &text_layout_details.editor_style;
12518 let font_id = cx.text_system().resolve_font(&style.text.font());
12519 let font_size = style.text.font_size.to_pixels(cx.rem_size());
12520 let line_height = style.text.line_height_in_pixels(cx.rem_size());
12521
12522 let em_width = cx
12523 .text_system()
12524 .typographic_bounds(font_id, font_size, 'm')
12525 .unwrap()
12526 .size
12527 .width;
12528
12529 let snapshot = self.snapshot(cx);
12530 let scroll_position = snapshot.scroll_position();
12531 let scroll_left = scroll_position.x * em_width;
12532
12533 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
12534 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
12535 + self.gutter_dimensions.width;
12536 let y = line_height * (start.row().as_f32() - scroll_position.y);
12537
12538 Some(Bounds {
12539 origin: element_bounds.origin + point(x, y),
12540 size: size(em_width, line_height),
12541 })
12542 }
12543}
12544
12545trait SelectionExt {
12546 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
12547 fn spanned_rows(
12548 &self,
12549 include_end_if_at_line_start: bool,
12550 map: &DisplaySnapshot,
12551 ) -> Range<MultiBufferRow>;
12552}
12553
12554impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
12555 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
12556 let start = self
12557 .start
12558 .to_point(&map.buffer_snapshot)
12559 .to_display_point(map);
12560 let end = self
12561 .end
12562 .to_point(&map.buffer_snapshot)
12563 .to_display_point(map);
12564 if self.reversed {
12565 end..start
12566 } else {
12567 start..end
12568 }
12569 }
12570
12571 fn spanned_rows(
12572 &self,
12573 include_end_if_at_line_start: bool,
12574 map: &DisplaySnapshot,
12575 ) -> Range<MultiBufferRow> {
12576 let start = self.start.to_point(&map.buffer_snapshot);
12577 let mut end = self.end.to_point(&map.buffer_snapshot);
12578 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
12579 end.row -= 1;
12580 }
12581
12582 let buffer_start = map.prev_line_boundary(start).0;
12583 let buffer_end = map.next_line_boundary(end).0;
12584 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
12585 }
12586}
12587
12588impl<T: InvalidationRegion> InvalidationStack<T> {
12589 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
12590 where
12591 S: Clone + ToOffset,
12592 {
12593 while let Some(region) = self.last() {
12594 let all_selections_inside_invalidation_ranges =
12595 if selections.len() == region.ranges().len() {
12596 selections
12597 .iter()
12598 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
12599 .all(|(selection, invalidation_range)| {
12600 let head = selection.head().to_offset(buffer);
12601 invalidation_range.start <= head && invalidation_range.end >= head
12602 })
12603 } else {
12604 false
12605 };
12606
12607 if all_selections_inside_invalidation_ranges {
12608 break;
12609 } else {
12610 self.pop();
12611 }
12612 }
12613 }
12614}
12615
12616impl<T> Default for InvalidationStack<T> {
12617 fn default() -> Self {
12618 Self(Default::default())
12619 }
12620}
12621
12622impl<T> Deref for InvalidationStack<T> {
12623 type Target = Vec<T>;
12624
12625 fn deref(&self) -> &Self::Target {
12626 &self.0
12627 }
12628}
12629
12630impl<T> DerefMut for InvalidationStack<T> {
12631 fn deref_mut(&mut self) -> &mut Self::Target {
12632 &mut self.0
12633 }
12634}
12635
12636impl InvalidationRegion for SnippetState {
12637 fn ranges(&self) -> &[Range<Anchor>] {
12638 &self.ranges[self.active_index]
12639 }
12640}
12641
12642pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
12643 let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
12644
12645 Box::new(move |cx: &mut BlockContext| {
12646 let group_id: SharedString = cx.block_id.to_string().into();
12647
12648 let mut text_style = cx.text_style().clone();
12649 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
12650 let theme_settings = ThemeSettings::get_global(cx);
12651 text_style.font_family = theme_settings.buffer_font.family.clone();
12652 text_style.font_style = theme_settings.buffer_font.style;
12653 text_style.font_features = theme_settings.buffer_font.features.clone();
12654 text_style.font_weight = theme_settings.buffer_font.weight;
12655
12656 let multi_line_diagnostic = diagnostic.message.contains('\n');
12657
12658 let buttons = |diagnostic: &Diagnostic, block_id: usize| {
12659 if multi_line_diagnostic {
12660 v_flex()
12661 } else {
12662 h_flex()
12663 }
12664 .children(diagnostic.is_primary.then(|| {
12665 IconButton::new(("close-block", block_id), IconName::XCircle)
12666 .icon_color(Color::Muted)
12667 .size(ButtonSize::Compact)
12668 .style(ButtonStyle::Transparent)
12669 .visible_on_hover(group_id.clone())
12670 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
12671 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
12672 }))
12673 .child(
12674 IconButton::new(("copy-block", block_id), IconName::Copy)
12675 .icon_color(Color::Muted)
12676 .size(ButtonSize::Compact)
12677 .style(ButtonStyle::Transparent)
12678 .visible_on_hover(group_id.clone())
12679 .on_click({
12680 let message = diagnostic.message.clone();
12681 move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
12682 })
12683 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
12684 )
12685 };
12686
12687 let icon_size = buttons(&diagnostic, cx.block_id)
12688 .into_any_element()
12689 .layout_as_root(AvailableSpace::min_size(), cx);
12690
12691 h_flex()
12692 .id(cx.block_id)
12693 .group(group_id.clone())
12694 .relative()
12695 .size_full()
12696 .pl(cx.gutter_dimensions.width)
12697 .w(cx.max_width + cx.gutter_dimensions.width)
12698 .child(
12699 div()
12700 .flex()
12701 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
12702 .flex_shrink(),
12703 )
12704 .child(buttons(&diagnostic, cx.block_id))
12705 .child(div().flex().flex_shrink_0().child(
12706 StyledText::new(text_without_backticks.clone()).with_highlights(
12707 &text_style,
12708 code_ranges.iter().map(|range| {
12709 (
12710 range.clone(),
12711 HighlightStyle {
12712 font_weight: Some(FontWeight::BOLD),
12713 ..Default::default()
12714 },
12715 )
12716 }),
12717 ),
12718 ))
12719 .into_any_element()
12720 })
12721}
12722
12723pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
12724 let mut text_without_backticks = String::new();
12725 let mut code_ranges = Vec::new();
12726
12727 if let Some(source) = &diagnostic.source {
12728 text_without_backticks.push_str(&source);
12729 code_ranges.push(0..source.len());
12730 text_without_backticks.push_str(": ");
12731 }
12732
12733 let mut prev_offset = 0;
12734 let mut in_code_block = false;
12735 for (ix, _) in diagnostic
12736 .message
12737 .match_indices('`')
12738 .chain([(diagnostic.message.len(), "")])
12739 {
12740 let prev_len = text_without_backticks.len();
12741 text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
12742 prev_offset = ix + 1;
12743 if in_code_block {
12744 code_ranges.push(prev_len..text_without_backticks.len());
12745 }
12746 in_code_block = !in_code_block;
12747 }
12748
12749 (text_without_backticks.into(), code_ranges)
12750}
12751
12752fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
12753 match severity {
12754 DiagnosticSeverity::ERROR => colors.error,
12755 DiagnosticSeverity::WARNING => colors.warning,
12756 DiagnosticSeverity::INFORMATION => colors.info,
12757 DiagnosticSeverity::HINT => colors.info,
12758 _ => colors.ignored,
12759 }
12760}
12761
12762pub fn styled_runs_for_code_label<'a>(
12763 label: &'a CodeLabel,
12764 syntax_theme: &'a theme::SyntaxTheme,
12765) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
12766 let fade_out = HighlightStyle {
12767 fade_out: Some(0.35),
12768 ..Default::default()
12769 };
12770
12771 let mut prev_end = label.filter_range.end;
12772 label
12773 .runs
12774 .iter()
12775 .enumerate()
12776 .flat_map(move |(ix, (range, highlight_id))| {
12777 let style = if let Some(style) = highlight_id.style(syntax_theme) {
12778 style
12779 } else {
12780 return Default::default();
12781 };
12782 let mut muted_style = style;
12783 muted_style.highlight(fade_out);
12784
12785 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
12786 if range.start >= label.filter_range.end {
12787 if range.start > prev_end {
12788 runs.push((prev_end..range.start, fade_out));
12789 }
12790 runs.push((range.clone(), muted_style));
12791 } else if range.end <= label.filter_range.end {
12792 runs.push((range.clone(), style));
12793 } else {
12794 runs.push((range.start..label.filter_range.end, style));
12795 runs.push((label.filter_range.end..range.end, muted_style));
12796 }
12797 prev_end = cmp::max(prev_end, range.end);
12798
12799 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
12800 runs.push((prev_end..label.text.len(), fade_out));
12801 }
12802
12803 runs
12804 })
12805}
12806
12807pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
12808 let mut prev_index = 0;
12809 let mut prev_codepoint: Option<char> = None;
12810 text.char_indices()
12811 .chain([(text.len(), '\0')])
12812 .filter_map(move |(index, codepoint)| {
12813 let prev_codepoint = prev_codepoint.replace(codepoint)?;
12814 let is_boundary = index == text.len()
12815 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
12816 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
12817 if is_boundary {
12818 let chunk = &text[prev_index..index];
12819 prev_index = index;
12820 Some(chunk)
12821 } else {
12822 None
12823 }
12824 })
12825}
12826
12827pub trait RangeToAnchorExt {
12828 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
12829}
12830
12831impl<T: ToOffset> RangeToAnchorExt for Range<T> {
12832 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
12833 let start_offset = self.start.to_offset(snapshot);
12834 let end_offset = self.end.to_offset(snapshot);
12835 if start_offset == end_offset {
12836 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
12837 } else {
12838 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
12839 }
12840 }
12841}
12842
12843pub trait RowExt {
12844 fn as_f32(&self) -> f32;
12845
12846 fn next_row(&self) -> Self;
12847
12848 fn previous_row(&self) -> Self;
12849
12850 fn minus(&self, other: Self) -> u32;
12851}
12852
12853impl RowExt for DisplayRow {
12854 fn as_f32(&self) -> f32 {
12855 self.0 as f32
12856 }
12857
12858 fn next_row(&self) -> Self {
12859 Self(self.0 + 1)
12860 }
12861
12862 fn previous_row(&self) -> Self {
12863 Self(self.0.saturating_sub(1))
12864 }
12865
12866 fn minus(&self, other: Self) -> u32 {
12867 self.0 - other.0
12868 }
12869}
12870
12871impl RowExt for MultiBufferRow {
12872 fn as_f32(&self) -> f32 {
12873 self.0 as f32
12874 }
12875
12876 fn next_row(&self) -> Self {
12877 Self(self.0 + 1)
12878 }
12879
12880 fn previous_row(&self) -> Self {
12881 Self(self.0.saturating_sub(1))
12882 }
12883
12884 fn minus(&self, other: Self) -> u32 {
12885 self.0 - other.0
12886 }
12887}
12888
12889trait RowRangeExt {
12890 type Row;
12891
12892 fn len(&self) -> usize;
12893
12894 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
12895}
12896
12897impl RowRangeExt for Range<MultiBufferRow> {
12898 type Row = MultiBufferRow;
12899
12900 fn len(&self) -> usize {
12901 (self.end.0 - self.start.0) as usize
12902 }
12903
12904 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
12905 (self.start.0..self.end.0).map(MultiBufferRow)
12906 }
12907}
12908
12909impl RowRangeExt for Range<DisplayRow> {
12910 type Row = DisplayRow;
12911
12912 fn len(&self) -> usize {
12913 (self.end.0 - self.start.0) as usize
12914 }
12915
12916 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
12917 (self.start.0..self.end.0).map(DisplayRow)
12918 }
12919}
12920
12921fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
12922 if hunk.diff_base_byte_range.is_empty() {
12923 DiffHunkStatus::Added
12924 } else if hunk.associated_range.is_empty() {
12925 DiffHunkStatus::Removed
12926 } else {
12927 DiffHunkStatus::Modified
12928 }
12929}