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