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